Files
mercadodevida/project/src/modules/pos/domain/pos-sale.ts

166 lines
4.0 KiB
TypeScript

export interface PosStockSaleLineInput {
kind?: 'stock';
variantId: string;
quantity: number;
discountCents?: number;
// Legacy snapshot fields are accepted at the API boundary but ignored.
productId?: string;
sku?: string;
ean?: string | null;
name?: string;
unitPriceCents?: number;
taxCents?: number;
}
export interface PosFreeSaleLineInput {
kind: 'free';
name: string;
unitPriceCents: number;
quantity: number;
}
export type PosSaleLineInput = PosStockSaleLineInput | PosFreeSaleLineInput;
export interface PosSaleLineItem {
kind: 'stock' | 'free';
variantId: string | null;
productId: string | null;
sku: string;
ean: string | null;
name: string;
unitPriceCents: number;
discountCents: number;
taxCents: number;
quantity: number;
vatRate: string | null;
}
export type PosPaymentKind = 'cash' | 'card' | 'other';
export interface PosPaymentInput {
/** Configured payment method code. */
methodCode?: string;
/** Legacy field; used only to resolve cash/card defaults. */
kind?: PosPaymentKind;
amountCents: number;
/** For cash payments: actual amount given by customer. */
tenderedCents?: number;
/** For card: last 4 digits if known. */
last4?: string;
}
export interface PosSaleInput {
idempotencyKey: string;
cashSessionId: string;
terminalId: string;
userId: string;
items: PosSaleLineInput[];
payments: PosPaymentInput[];
customerId?: string;
/** Optional label for pending sales without customer */
posLabel?: string;
/** Club member ID — if provided and sale completes, cashback is accumulated to the member's ledger. */
clubMemberId?: string;
}
export interface PosReceiptItem {
name: string;
sku: string;
quantity: number;
unitPriceCents: number;
subtotalCents: number;
discountCents: number;
taxCents: number;
totalCents: number;
freeItem: boolean;
}
export interface PosReceiptPayment {
methodCode: string;
methodLabel: string;
kind: PosPaymentKind;
amountCents: number;
tenderedCents: number | null;
changeCents: number;
}
export interface PosReceipt {
receiptNumber: string;
orderId: string;
issuedAt: Date;
company: {
name: string;
address: string | null;
taxId: string | null;
email: string | null;
phone: string | null;
};
/** URL del logo custom para el ticket. Si es null, usar /images/logo-main.png */
logoUrl: string | null;
terminal: { id: string; name: string };
cashier: string;
sessionId: string;
customerEmail: string | null;
items: PosReceiptItem[];
subtotalCents: number;
discountCents: number;
taxCents: number;
totalCents: number;
payments: PosReceiptPayment[];
changeCents: number;
header: string | null;
returnPolicy: string;
footer: string | null;
/** Present on return receipts. References the original ticket number. */
originalReceiptNumber?: string;
/** True for return/reversal receipts that should print negative totals. */
isReturn?: boolean;
}
export interface PosSaleResult {
orderId: string;
idempotencyKey: string;
receiptNumber: string;
state: 'PENDING' | 'COMPLETED';
totalCents: number;
paidCents: number;
outstandingCents: number;
changeCents: number;
items: PosSaleLineItem[];
payments: PosPaymentResult[];
receipt: PosReceipt;
createdAt: Date;
/** Cashback accumulated to the Club member's ledger (0 if no clubMemberId or sale is PENDING). */
clubEarnedCents: number;
/** Club member code when a clubMemberId was provided. */
clubMemberCode?: string;
}
export interface PosPaymentResult extends PosReceiptPayment {
id: string;
}
export interface PosReturnLineInput {
orderItemId: string;
returnedQuantity: number;
}
export interface PosReturnInput {
orderId: string;
idempotencyKey: string;
cashSessionId: string;
terminalId: string;
userId: string;
reason: string;
lines: PosReturnLineInput[];
}
export interface PosReturnResult {
orderId: string;
returnId: string;
idempotencyKey: string;
state: 'PARTIALLY_REFUNDED' | 'REFUNDED';
refundedCents: number;
receipt: PosReceipt;
}