import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, OneToMany, JoinColumn, Index, } from 'typeorm'; import { POSSession } from './pos-session.entity'; import { POSOrderLine } from './pos-order-line.entity'; import { POSPayment } from './pos-payment.entity'; export enum OrderStatus { DRAFT = 'draft', CONFIRMED = 'confirmed', PAID = 'paid', PARTIALLY_PAID = 'partially_paid', VOIDED = 'voided', REFUNDED = 'refunded', } export enum OrderType { SALE = 'sale', REFUND = 'refund', EXCHANGE = 'exchange', } @Entity('pos_orders', { schema: 'retail' }) @Index(['tenantId', 'branchId', 'createdAt']) @Index(['tenantId', 'sessionId']) @Index(['tenantId', 'customerId']) @Index(['tenantId', 'number'], { unique: true }) export class POSOrder { @PrimaryGeneratedColumn('uuid') id: string; @Column({ name: 'tenant_id', type: 'uuid' }) @Index() tenantId: string; @Column({ name: 'branch_id', type: 'uuid' }) branchId: string; @Column({ name: 'session_id', type: 'uuid' }) sessionId: string; @Column({ name: 'register_id', type: 'uuid' }) registerId: string; @Column({ name: 'user_id', type: 'uuid' }) userId: string; @Column({ length: 30, unique: true }) number: string; @Column({ type: 'enum', enum: OrderType, default: OrderType.SALE, }) type: OrderType; @Column({ type: 'enum', enum: OrderStatus, default: OrderStatus.DRAFT, }) status: OrderStatus; // Customer (optional, from erp-core partners) @Column({ name: 'customer_id', type: 'uuid', nullable: true }) customerId: string; @Column({ name: 'customer_name', length: 200, nullable: true }) customerName: string; @Column({ name: 'customer_rfc', length: 13, nullable: true }) customerRfc: string; // Salesperson (for commission tracking) @Column({ name: 'salesperson_id', type: 'uuid', nullable: true }) salespersonId: string; // Amounts @Column({ type: 'decimal', precision: 15, scale: 2, default: 0 }) subtotal: number; @Column({ name: 'discount_amount', type: 'decimal', precision: 15, scale: 2, default: 0 }) discountAmount: number; @Column({ name: 'discount_percent', type: 'decimal', precision: 5, scale: 2, default: 0 }) discountPercent: number; @Column({ name: 'discount_reason', length: 255, nullable: true }) discountReason: string; @Column({ name: 'discount_authorized_by', type: 'uuid', nullable: true }) discountAuthorizedBy: string; @Column({ name: 'tax_amount', type: 'decimal', precision: 15, scale: 2, default: 0 }) taxAmount: number; @Column({ type: 'decimal', precision: 15, scale: 2, default: 0 }) total: number; @Column({ name: 'amount_paid', type: 'decimal', precision: 15, scale: 2, default: 0 }) amountPaid: number; @Column({ name: 'change_amount', type: 'decimal', precision: 15, scale: 2, default: 0 }) changeAmount: number; // Tax breakdown @Column({ name: 'tax_breakdown', type: 'jsonb', nullable: true }) taxBreakdown: { taxId: string; taxName: string; rate: number; base: number; amount: number; }[]; // Currency @Column({ name: 'currency_code', length: 3, default: 'MXN' }) currencyCode: string; @Column({ name: 'exchange_rate', type: 'decimal', precision: 10, scale: 6, default: 1 }) exchangeRate: number; // Coupon/Promotion @Column({ name: 'coupon_id', type: 'uuid', nullable: true }) couponId: string; @Column({ name: 'coupon_code', length: 50, nullable: true }) couponCode: string; @Column({ name: 'promotion_ids', type: 'jsonb', nullable: true }) promotionIds: string[]; // Loyalty points @Column({ name: 'loyalty_points_earned', type: 'int', default: 0 }) loyaltyPointsEarned: number; @Column({ name: 'loyalty_points_redeemed', type: 'int', default: 0 }) loyaltyPointsRedeemed: number; @Column({ name: 'loyalty_points_value', type: 'decimal', precision: 15, scale: 2, default: 0 }) loyaltyPointsValue: number; // Invoicing @Column({ name: 'requires_invoice', type: 'boolean', default: false }) requiresInvoice: boolean; @Column({ name: 'cfdi_id', type: 'uuid', nullable: true }) cfdiId: string; @Column({ name: 'invoice_status', length: 20, nullable: true }) invoiceStatus: 'pending' | 'issued' | 'cancelled'; // Reference to original order (for refunds/exchanges) @Column({ name: 'original_order_id', type: 'uuid', nullable: true }) originalOrderId: string; // Void info @Column({ name: 'voided_at', type: 'timestamp with time zone', nullable: true }) voidedAt: Date; @Column({ name: 'voided_by', type: 'uuid', nullable: true }) voidedBy: string; @Column({ name: 'void_reason', length: 255, nullable: true }) voidReason: string; // Notes @Column({ type: 'text', nullable: true }) notes: string; // Offline sync @Column({ name: 'is_offline_order', type: 'boolean', default: false }) isOfflineOrder: boolean; @Column({ name: 'offline_uuid', type: 'uuid', nullable: true }) offlineUuid: string; @Column({ name: 'synced_at', type: 'timestamp with time zone', nullable: true }) syncedAt: Date; // Receipt @Column({ name: 'receipt_printed', type: 'boolean', default: false }) receiptPrinted: boolean; @Column({ name: 'receipt_sent', type: 'boolean', default: false }) receiptSent: boolean; @Column({ name: 'receipt_email', length: 100, nullable: true }) receiptEmail: string; // Metadata @Column({ type: 'jsonb', nullable: true }) metadata: Record; @CreateDateColumn({ name: 'created_at' }) createdAt: Date; @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date; @Column({ name: 'completed_at', type: 'timestamp with time zone', nullable: true }) completedAt: Date; // Relations @ManyToOne(() => POSSession, (session) => session.orders) @JoinColumn({ name: 'session_id' }) session: POSSession; @OneToMany(() => POSOrderLine, (line) => line.order) lines: POSOrderLine[]; @OneToMany(() => POSPayment, (payment) => payment.order) payments: POSPayment[]; }