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; }