199 lines
5.1 KiB
TypeScript
199 lines
5.1 KiB
TypeScript
import {
|
|
Entity,
|
|
PrimaryGeneratedColumn,
|
|
Column,
|
|
CreateDateColumn,
|
|
ManyToOne,
|
|
JoinColumn,
|
|
Index,
|
|
} from 'typeorm';
|
|
import { EcommerceOrder } from './ecommerce-order.entity';
|
|
|
|
export enum OrderLineStatus {
|
|
PENDING = 'pending',
|
|
RESERVED = 'reserved',
|
|
FULFILLED = 'fulfilled',
|
|
SHIPPED = 'shipped',
|
|
DELIVERED = 'delivered',
|
|
CANCELLED = 'cancelled',
|
|
RETURNED = 'returned',
|
|
REFUNDED = 'refunded',
|
|
}
|
|
|
|
@Entity('ecommerce_order_lines', { schema: 'retail' })
|
|
@Index(['tenantId', 'orderId'])
|
|
@Index(['tenantId', 'productId'])
|
|
export class EcommerceOrderLine {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id: string;
|
|
|
|
@Column({ name: 'tenant_id', type: 'uuid' })
|
|
@Index()
|
|
tenantId: string;
|
|
|
|
@Column({ name: 'order_id', type: 'uuid' })
|
|
orderId: string;
|
|
|
|
@Column({ name: 'line_number', type: 'int' })
|
|
lineNumber: number;
|
|
|
|
@Column({
|
|
type: 'enum',
|
|
enum: OrderLineStatus,
|
|
default: OrderLineStatus.PENDING,
|
|
})
|
|
status: OrderLineStatus;
|
|
|
|
// Product (from erp-core)
|
|
@Column({ name: 'product_id', type: 'uuid' })
|
|
productId: string;
|
|
|
|
@Column({ name: 'product_code', length: 50 })
|
|
productCode: string;
|
|
|
|
@Column({ name: 'product_name', length: 200 })
|
|
productName: string;
|
|
|
|
@Column({ name: 'product_slug', length: 255, nullable: true })
|
|
productSlug: string;
|
|
|
|
@Column({ name: 'product_image', length: 255, nullable: true })
|
|
productImage: string;
|
|
|
|
// Variant
|
|
@Column({ name: 'variant_id', type: 'uuid', nullable: true })
|
|
variantId: string;
|
|
|
|
@Column({ name: 'variant_name', length: 200, nullable: true })
|
|
variantName: string;
|
|
|
|
@Column({ name: 'variant_sku', length: 50, nullable: true })
|
|
variantSku: string;
|
|
|
|
@Column({ name: 'variant_options', type: 'jsonb', nullable: true })
|
|
variantOptions: {
|
|
name: string;
|
|
value: string;
|
|
}[];
|
|
|
|
// Quantity
|
|
@Column({ type: 'decimal', precision: 15, scale: 4, default: 1 })
|
|
quantity: number;
|
|
|
|
@Column({ name: 'quantity_fulfilled', type: 'decimal', precision: 15, scale: 4, default: 0 })
|
|
quantityFulfilled: number;
|
|
|
|
@Column({ name: 'quantity_refunded', type: 'decimal', precision: 15, scale: 4, default: 0 })
|
|
quantityRefunded: number;
|
|
|
|
// UOM
|
|
@Column({ name: 'uom_id', type: 'uuid', nullable: true })
|
|
uomId: string;
|
|
|
|
@Column({ name: 'uom_name', length: 20, default: 'PZA' })
|
|
uomName: string;
|
|
|
|
// Pricing
|
|
@Column({ name: 'unit_price', type: 'decimal', precision: 15, scale: 4 })
|
|
unitPrice: number;
|
|
|
|
@Column({ name: 'original_price', type: 'decimal', precision: 15, scale: 4 })
|
|
originalPrice: number;
|
|
|
|
// Discounts
|
|
@Column({ name: 'discount_percent', type: 'decimal', precision: 5, scale: 2, default: 0 })
|
|
discountPercent: number;
|
|
|
|
@Column({ name: 'discount_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
|
discountAmount: number;
|
|
|
|
@Column({ name: 'promotion_id', type: 'uuid', nullable: true })
|
|
promotionId: string;
|
|
|
|
@Column({ name: 'promotion_name', length: 100, nullable: true })
|
|
promotionName: string;
|
|
|
|
// Tax
|
|
@Column({ name: 'tax_id', type: 'uuid', nullable: true })
|
|
taxId: string;
|
|
|
|
@Column({ name: 'tax_rate', type: 'decimal', precision: 5, scale: 4, default: 0 })
|
|
taxRate: number;
|
|
|
|
@Column({ name: 'tax_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
|
taxAmount: number;
|
|
|
|
@Column({ name: 'tax_included', type: 'boolean', default: true })
|
|
taxIncluded: boolean;
|
|
|
|
// Totals
|
|
@Column({ type: 'decimal', precision: 15, scale: 2, default: 0 })
|
|
subtotal: number;
|
|
|
|
@Column({ type: 'decimal', precision: 15, scale: 2, default: 0 })
|
|
total: number;
|
|
|
|
// Cost
|
|
@Column({ name: 'unit_cost', type: 'decimal', precision: 15, scale: 4, nullable: true })
|
|
unitCost: number;
|
|
|
|
// Weight (for shipping calculation)
|
|
@Column({ type: 'decimal', precision: 10, scale: 4, nullable: true })
|
|
weight: number;
|
|
|
|
@Column({ name: 'weight_unit', length: 5, default: 'kg' })
|
|
weightUnit: string;
|
|
|
|
// Warehouse
|
|
@Column({ name: 'warehouse_id', type: 'uuid', nullable: true })
|
|
warehouseId: string;
|
|
|
|
// Lot/Serial
|
|
@Column({ name: 'lot_number', length: 50, nullable: true })
|
|
lotNumber: string;
|
|
|
|
@Column({ name: 'serial_number', length: 50, nullable: true })
|
|
serialNumber: string;
|
|
|
|
// Custom options
|
|
@Column({ name: 'custom_options', type: 'jsonb', nullable: true })
|
|
customOptions: {
|
|
name: string;
|
|
value: string;
|
|
price?: number;
|
|
}[];
|
|
|
|
// Gift
|
|
@Column({ name: 'is_gift', type: 'boolean', default: false })
|
|
isGift: boolean;
|
|
|
|
@Column({ name: 'gift_message', type: 'text', nullable: true })
|
|
giftMessage: string;
|
|
|
|
// Refund
|
|
@Column({ name: 'refunded_at', type: 'timestamp with time zone', nullable: true })
|
|
refundedAt: Date;
|
|
|
|
@Column({ name: 'refund_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
|
refundAmount: number;
|
|
|
|
@Column({ name: 'refund_reason', length: 255, nullable: true })
|
|
refundReason: string;
|
|
|
|
// Notes
|
|
@Column({ type: 'text', nullable: true })
|
|
notes: string;
|
|
|
|
// Metadata
|
|
@Column({ type: 'jsonb', nullable: true })
|
|
metadata: Record<string, any>;
|
|
|
|
@CreateDateColumn({ name: 'created_at' })
|
|
createdAt: Date;
|
|
|
|
// Relations
|
|
@ManyToOne(() => EcommerceOrder, (order) => order.lines)
|
|
@JoinColumn({ name: 'order_id' })
|
|
order: EcommerceOrder;
|
|
}
|