224 lines
5.9 KiB
TypeScript
224 lines
5.9 KiB
TypeScript
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<string, any>;
|
|
|
|
@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[];
|
|
}
|