feat(F-189): completed feature

This commit is contained in:
chattie
2026-08-22 23:01:59 +02:00
parent 0e3c488c85
commit 6b93e91ef4
24 changed files with 1535 additions and 63 deletions

View File

@@ -4,6 +4,18 @@ import { AppError } from '../../../shared/errors.js';
type Queryable = Pick<pg.Pool, 'query'> | Pick<pg.PoolClient, 'query'>;
interface ReturnedItemRow {
id: string;
quantity: number;
returned_quantity: number;
unit_price_cents: number;
discount_cents: number;
tax_cents: number;
name: string;
sku: string;
is_free_item: boolean;
}
interface ReceiptOrderRow {
id: string;
receipt_number: string | null;
@@ -165,3 +177,75 @@ function integerOrNull(value: unknown): number | null {
function integerOrZero(value: unknown): number {
return typeof value === 'number' && Number.isInteger(value) ? value : 0;
}
/**
* Build a return/reversal receipt based on the original POS order.
*
* The returned payload uses negative line totals and prices `R-<original>` as
* its receipt number without persisting a separate sequence. The `paidCents`
* block is replaced with a `refundedCents` flag and the receipt is flagged
* `isReturn=true` so renderers can style it accordingly.
*/
export async function buildPosReturnReceipt(
queryable: Queryable,
orderId: string,
refundedCents: number,
): Promise<PosReceipt> {
const original = await buildPosReceipt(queryable, orderId);
const itemResult = await queryable.query<ReturnedItemRow>(
`SELECT id, quantity, returned_quantity, unit_price_cents, discount_cents, tax_cents,
name, sku, is_free_item
FROM orders_items WHERE order_id = $1 ORDER BY created_at, id`,
[orderId],
);
const items = itemResult.rows
.filter((row) => row.returned_quantity > 0)
.map((row) => {
const returnedQuantity = Number(row.returned_quantity);
const unitPrice = Number(row.unit_price_cents);
const discount = Number(row.discount_cents);
const tax = Number(row.tax_cents);
const subtotal = unitPrice * returnedQuantity;
const discountCents = discount * returnedQuantity;
const taxCents = tax * returnedQuantity;
const total = subtotal - discountCents + taxCents;
return {
name: row.name,
sku: row.sku,
quantity: returnedQuantity,
unitPriceCents: unitPrice,
subtotalCents: subtotal,
discountCents,
taxCents,
totalCents: -total,
freeItem: row.is_free_item,
};
});
const subtotal = items.reduce((sum, item) => sum + item.subtotalCents, 0);
const discount = items.reduce((sum, item) => sum + item.discountCents, 0);
const tax = items.reduce((sum, item) => sum + item.taxCents, 0);
return {
receiptNumber: original.receiptNumber.startsWith('R-')
? original.receiptNumber
: `R-${original.receiptNumber}`,
orderId: original.orderId,
issuedAt: original.issuedAt,
company: original.company,
terminal: original.terminal,
cashier: original.cashier,
sessionId: original.sessionId,
customerEmail: original.customerEmail,
items,
subtotalCents: -subtotal,
discountCents: -discount,
taxCents: -tax,
totalCents: -refundedCents,
payments: [],
changeCents: 0,
header: original.header,
returnPolicy: original.returnPolicy,
footer: original.footer,
originalReceiptNumber: original.receiptNumber,
isReturn: true,
};
}