feat(F-189): completed feature
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user