Add paymentStatus filter and field on order list API.

Support paid/unpaid/failed filtering from completed transactions for dashboard order tabs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-02 22:22:23 +03:30
co-authored by Cursor
parent 0d9e866a57
commit 7f6fa3672d
2 changed files with 45 additions and 0 deletions
+6
View File
@@ -2,6 +2,7 @@ import {
ArrayMinSize, ArrayMinSize,
IsDateString, IsDateString,
IsEnum, IsEnum,
IsIn,
IsInt, IsInt,
IsNumber, IsNumber,
IsOptional, IsOptional,
@@ -166,4 +167,9 @@ export class ListOrdersDto {
@Min(0) @Min(0)
@Type(() => Number) @Type(() => Number)
maxTotal?: number; maxTotal?: number;
/** Filter by payment outcome derived from transactions. */
@IsOptional()
@IsIn(['paid', 'unpaid', 'failed'])
paymentStatus?: 'paid' | 'unpaid' | 'failed';
} }
+39
View File
@@ -155,6 +155,7 @@ export class OrdersService {
customer: this.buildCustomerSearchFilter(query.customerQuery.trim()), customer: this.buildCustomerSearchFilter(query.customerQuery.trim()),
} }
: {}), : {}),
...this.paymentStatusWhere(query.paymentStatus),
}; };
const [items, total] = await Promise.all([ const [items, total] = await Promise.all([
@@ -762,6 +763,43 @@ export class OrdersService {
return { OR: or }; return { OR: or };
} }
private paymentStatusWhere(
paymentStatus: 'paid' | 'unpaid' | 'failed' | undefined,
): Prisma.OrderWhereInput {
if (paymentStatus === 'paid') {
return {
transactions: { some: { status: 'completed' } },
};
}
if (paymentStatus === 'failed') {
return {
AND: [
{ transactions: { some: { status: 'failed' } } },
{ transactions: { none: { status: 'completed' } } },
],
};
}
if (paymentStatus === 'unpaid') {
return {
transactions: { none: { status: 'completed' } },
};
}
return {};
}
private derivePaymentStatus(
transactions: Array<{ status: string }>,
): 'paid' | 'unpaid' | 'failed' {
if (transactions.some((tx) => tx.status === 'completed')) return 'paid';
if (
transactions.length > 0 &&
transactions.every((tx) => tx.status === 'failed')
) {
return 'failed';
}
return 'unpaid';
}
private async defaultProcessStepId(businessId: bigint) { private async defaultProcessStepId(businessId: bigint) {
const steps = await this.businessSettings.getOrderProcessSteps(businessId); const steps = await this.businessSettings.getOrderProcessSteps(businessId);
return steps[0]?.id ?? 'processing'; return steps[0]?.id ?? 'processing';
@@ -873,6 +911,7 @@ export class OrdersService {
processStepLabel: processStep?.label ?? null, processStepLabel: processStep?.label ?? null,
processStepLabelFa: processStep?.labelFa?.trim() || null, processStepLabelFa: processStep?.labelFa?.trim() || null,
processStepColor: processStep?.color ?? null, processStepColor: processStep?.color ?? null,
paymentStatus: this.derivePaymentStatus(order.transactions ?? []),
source: order.source, source: order.source,
subtotal: Number(order.subtotal), subtotal: Number(order.subtotal),
shippingTotal: Number(order.shippingTotal), shippingTotal: Number(order.shippingTotal),