schema updates to rental statuses

This commit is contained in:
jackiettran
2025-11-24 18:08:12 -05:00
parent 42a5412582
commit 8e6af92cba
7 changed files with 19 additions and 34 deletions

View File

@@ -64,15 +64,15 @@ const Rental = sequelize.define("Rental", {
"damaged", "damaged",
"lost" "lost"
), ),
defaultValue: "pending", allowNull: false,
}, },
paymentStatus: { paymentStatus: {
type: DataTypes.ENUM("pending", "paid", "refunded", "not_required"), type: DataTypes.ENUM("pending", "paid", "refunded", "not_required"),
defaultValue: "pending", allowNull: false,
}, },
payoutStatus: { payoutStatus: {
type: DataTypes.ENUM("pending", "processing", "completed", "failed"), type: DataTypes.ENUM("pending", "completed", "failed"),
defaultValue: "pending", allowNull: true,
}, },
payoutProcessedAt: { payoutProcessedAt: {
type: DataTypes.DATE, type: DataTypes.DATE,

View File

@@ -823,7 +823,7 @@ router.post("/:id/mark-completed", authenticateToken, async (req, res) => {
}); });
} }
await rental.update({ status: "completed" }); await rental.update({ status: "completed", payoutStatus: "pending" });
const updatedRental = await Rental.findByPk(rental.id, { const updatedRental = await Rental.findByPk(rental.id, {
include: [ include: [
@@ -1131,6 +1131,7 @@ router.post("/:id/mark-return", authenticateToken, async (req, res) => {
// Item returned on time // Item returned on time
updatedRental = await rental.update({ updatedRental = await rental.update({
status: "completed", status: "completed",
payoutStatus: "pending",
actualReturnDateTime: actualReturnDateTime || rental.endDateTime, actualReturnDateTime: actualReturnDateTime || rental.endDateTime,
notes: notes || null, notes: notes || null,
}); });
@@ -1174,6 +1175,7 @@ router.post("/:id/mark-return", authenticateToken, async (req, res) => {
// Item returned damaged // Item returned damaged
const damageUpdates = { const damageUpdates = {
status: "damaged", status: "damaged",
payoutStatus: "pending",
actualReturnDateTime: actualReturnDateTime || rental.endDateTime, actualReturnDateTime: actualReturnDateTime || rental.endDateTime,
notes: notes || null, notes: notes || null,
}; };
@@ -1217,6 +1219,7 @@ router.post("/:id/mark-return", authenticateToken, async (req, res) => {
// Item reported as lost // Item reported as lost
updatedRental = await rental.update({ updatedRental = await rental.update({
status: "lost", status: "lost",
payoutStatus: "pending",
itemLostReportedAt: new Date(), itemLostReportedAt: new Date(),
notes: notes || null, notes: notes || null,
}); });

View File

@@ -81,6 +81,7 @@ class LateReturnService {
const updates = { const updates = {
actualReturnDateTime: new Date(actualReturnDateTime), actualReturnDateTime: new Date(actualReturnDateTime),
status: lateCalculation.isLate ? "returned_late" : "completed", status: lateCalculation.isLate ? "returned_late" : "completed",
payoutStatus: "pending",
}; };
if (notes) { if (notes) {

View File

@@ -50,11 +50,6 @@ class PayoutService {
throw new Error("Invalid payout amount"); throw new Error("Invalid payout amount");
} }
// Update status to processing
await rental.update({
payoutStatus: "processing",
});
// Create Stripe transfer // Create Stripe transfer
const transfer = await StripeService.createTransfer({ const transfer = await StripeService.createTransfer({
amount: rental.payoutAmount, amount: rental.payoutAmount,

View File

@@ -186,11 +186,6 @@ describe('PayoutService', () => {
it('should successfully process a rental payout', async () => { it('should successfully process a rental payout', async () => {
const result = await PayoutService.processRentalPayout(mockRental); const result = await PayoutService.processRentalPayout(mockRental);
// Verify status update to processing
expect(mockRental.update).toHaveBeenNthCalledWith(1, {
payoutStatus: 'processing'
});
// Verify Stripe transfer creation // Verify Stripe transfer creation
expect(mockCreateTransfer).toHaveBeenCalledWith({ expect(mockCreateTransfer).toHaveBeenCalledWith({
amount: 9500, amount: 9500,
@@ -206,7 +201,7 @@ describe('PayoutService', () => {
}); });
// Verify status update to completed // Verify status update to completed
expect(mockRental.update).toHaveBeenNthCalledWith(2, { expect(mockRental.update).toHaveBeenCalledWith({
payoutStatus: 'completed', payoutStatus: 'completed',
payoutProcessedAt: expect.any(Date), payoutProcessedAt: expect.any(Date),
stripeTransferId: 'tr_123456789' stripeTransferId: 'tr_123456789'
@@ -260,13 +255,8 @@ describe('PayoutService', () => {
await expect(PayoutService.processRentalPayout(mockRental)) await expect(PayoutService.processRentalPayout(mockRental))
.rejects.toThrow('Stripe transfer failed'); .rejects.toThrow('Stripe transfer failed');
// Verify processing status was set
expect(mockRental.update).toHaveBeenNthCalledWith(1, {
payoutStatus: 'processing'
});
// Verify failure status was set // Verify failure status was set
expect(mockRental.update).toHaveBeenNthCalledWith(2, { expect(mockRental.update).toHaveBeenCalledWith({
payoutStatus: 'failed' payoutStatus: 'failed'
}); });
@@ -296,9 +286,7 @@ describe('PayoutService', () => {
}); });
const dbError = new Error('Database completion update failed'); const dbError = new Error('Database completion update failed');
mockRental.update mockRental.update.mockRejectedValueOnce(dbError);
.mockResolvedValueOnce(true) // processing update succeeds
.mockRejectedValueOnce(dbError); // completion update fails
await expect(PayoutService.processRentalPayout(mockRental)) await expect(PayoutService.processRentalPayout(mockRental))
.rejects.toThrow('Database completion update failed'); .rejects.toThrow('Database completion update failed');
@@ -315,16 +303,14 @@ describe('PayoutService', () => {
const updateError = new Error('Update failed status failed'); const updateError = new Error('Update failed status failed');
mockCreateTransfer.mockRejectedValue(stripeError); mockCreateTransfer.mockRejectedValue(stripeError);
mockRental.update mockRental.update.mockRejectedValueOnce(updateError);
.mockResolvedValueOnce(true) // processing update succeeds
.mockRejectedValueOnce(updateError); // failed status update fails
// The service will throw the update error since it happens in the catch block // The service will throw the update error since it happens in the catch block
await expect(PayoutService.processRentalPayout(mockRental)) await expect(PayoutService.processRentalPayout(mockRental))
.rejects.toThrow('Update failed status failed'); .rejects.toThrow('Update failed status failed');
// Should still attempt to update to failed status // Should still attempt to update to failed status
expect(mockRental.update).toHaveBeenNthCalledWith(2, { expect(mockRental.update).toHaveBeenCalledWith({
payoutStatus: 'failed' payoutStatus: 'failed'
}); });
}); });

View File

@@ -211,15 +211,15 @@ const EarningsDashboard: React.FC = () => {
className={`badge ${ className={`badge ${
rental.payoutStatus === "completed" rental.payoutStatus === "completed"
? "bg-success" ? "bg-success"
: rental.payoutStatus === "processing" : rental.payoutStatus === "failed"
? "bg-warning" ? "bg-danger"
: "bg-secondary" : "bg-secondary"
}`} }`}
> >
{rental.payoutStatus === "completed" {rental.payoutStatus === "completed"
? "Paid" ? "Paid"
: rental.payoutStatus === "processing" : rental.payoutStatus === "failed"
? "Processing" ? "Failed"
: "Pending"} : "Pending"}
</span> </span>
</td> </td>

View File

@@ -147,7 +147,7 @@ export interface Rental {
stripePaymentIntentId?: string; stripePaymentIntentId?: string;
stripePaymentMethodId?: string; stripePaymentMethodId?: string;
// Payout status tracking // Payout status tracking
payoutStatus?: "pending" | "processing" | "completed" | "failed"; payoutStatus?: "pending" | "completed" | "failed" | null;
payoutProcessedAt?: string; payoutProcessedAt?: string;
stripeTransferId?: string; stripeTransferId?: string;
deliveryMethod: "pickup" | "delivery"; deliveryMethod: "pickup" | "delivery";