Add payable filter to sales invoices and enhance invoice status handling

- Introduced a new `payable` filter in the `ListSalesInvoicesFilters` and `ListSalesInvoicesQuery` types to allow querying of invoices based on their payable status.
- Updated the `SalesInvoicesRepository` to incorporate logic for filtering invoices that are payable, checking both status and balance.
- Enhanced the `SalesInvoicesService` to support the new `payable` filter in query handling.
- Modified the `SalesInvoiceDto` to include the `payable` property for better API response representation.
- Added unit tests to validate the new filter functionality and ensure proper handling of invoice statuses during updates.
- Updated e2e tests to cover scenarios involving the new payable filter and status transitions for invoices.
This commit is contained in:
shancheas
2026-08-31 15:15:06 +07:00
parent afed5ff0f5
commit 5579cf6566
11 changed files with 141 additions and 9 deletions
@@ -22,6 +22,7 @@ import {
isValidImageUrl,
isAllowedStatusTransition,
parseCsvRecord,
PAYABLE_INVOICE_STATUSES,
SALES_PAYMENT_STATUSES,
SALES_PAYMENT_USER_TRANSITIONS,
} from '../shared/sales-fields';
@@ -341,7 +342,8 @@ export class SalesPaymentsService {
}
const result: SalesPaymentAllocationInput[] = [];
for (const line of lines) {
await this.salesInvoicesService.findById(line.invoiceId);
const invoice = await this.salesInvoicesService.findById(line.invoiceId);
this.assertInvoicePayable(invoice);
const amount = this.parseDecimal(line.amount);
if (!amount.isPositive()) {
throw new BadRequestException('Invalid amount');
@@ -351,6 +353,28 @@ export class SalesPaymentsService {
return result;
}
private assertInvoicePayable(invoice: {
status?: string;
balance?: string;
}): void {
if (
!invoice.status ||
!(PAYABLE_INVOICE_STATUSES as readonly string[]).includes(invoice.status)
) {
throw new BadRequestException('Invoice is not payable');
}
try {
if (!Decimal.create(invoice.balance ?? '0').isPositive()) {
throw new BadRequestException('Invoice is not payable');
}
} catch (error) {
if (error instanceof InvalidDecimalError) {
throw new BadRequestException('Invoice is not payable');
}
throw error;
}
}
private assertImage(image: SalesImageBody): SalesPaymentImageInput {
if (!isValidImageUrl(image.url)) {
throw new BadRequestException('Invalid image URL');