fix(entities): Sync SalesOrder, Product, Partner entities with DDL
GAP-ENT-001: SalesOrder - aligned with sales.sales_orders DDL - Renamed name -> orderNumber - Changed currencyId (UUID) -> currency (string) - Changed paymentTermId -> paymentTermDays (int) - Added: partnerName, partnerEmail, billingAddress, shippingAddress - Added: requestedDate, promisedDate, shippedDate, deliveredDate - Added: warehouseId, discountAmount, shippingAmount - Added: shippingMethod, trackingNumber, carrier, internalNotes - Removed Odoo-style fields not in DDL GAP-ENT-002: Product - aligned with products.products DDL - Renamed salePrice -> price, costPrice -> cost - Renamed satProductCode -> taxCode, conversionFactor -> uomConversion - Added: shortDescription, volume, leadTimeDays, attributes (JSONB) - Removed: inventoryProductId, shortName, satUnitCode, tags, notes - Removed tracking fields (trackLots, trackSerials, trackExpiry) GAP-ENT-003: Partner - aligned with partners.partners DDL - Added: name (separate from displayName) - Added: taxIdType, verifiedAt, settings (JSONB) - Removed fields that belong in partner_tax_info: taxRegime, cfdiUse - Removed: currentBalance, priceListId, discountPercent, salesRepId Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
390bdd3923
commit
0f7feff3f8
@ -6,11 +6,17 @@ import {
|
|||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
DeleteDateColumn,
|
DeleteDateColumn,
|
||||||
Index,
|
Index,
|
||||||
ManyToOne,
|
|
||||||
OneToMany,
|
|
||||||
JoinColumn,
|
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partner Entity
|
||||||
|
*
|
||||||
|
* Synchronized with DDL: database/ddl/16-partners.sql
|
||||||
|
* Table: partners.partners
|
||||||
|
*
|
||||||
|
* Represents customers, suppliers, and other business partners.
|
||||||
|
* Extended fiscal information is stored in partners.partner_tax_info (separate entity).
|
||||||
|
*/
|
||||||
@Entity({ name: 'partners', schema: 'partners' })
|
@Entity({ name: 'partners', schema: 'partners' })
|
||||||
export class Partner {
|
export class Partner {
|
||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
@ -20,34 +26,34 @@ export class Partner {
|
|||||||
@Column({ name: 'tenant_id', type: 'uuid' })
|
@Column({ name: 'tenant_id', type: 'uuid' })
|
||||||
tenantId: string;
|
tenantId: string;
|
||||||
|
|
||||||
// Identificacion
|
// Identification
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ type: 'varchar', length: 20, unique: true })
|
@Column({ type: 'varchar', length: 30 })
|
||||||
code: string;
|
code: string;
|
||||||
|
|
||||||
@Column({ name: 'display_name', type: 'varchar', length: 200 })
|
@Column({ type: 'varchar', length: 200 })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ name: 'display_name', type: 'varchar', length: 200, nullable: true })
|
||||||
displayName: string;
|
displayName: string;
|
||||||
|
|
||||||
|
// Partner type
|
||||||
|
@Index()
|
||||||
|
@Column({ name: 'partner_type', type: 'varchar', length: 20, default: 'customer' })
|
||||||
|
partnerType: 'customer' | 'supplier' | 'both' | 'contact';
|
||||||
|
|
||||||
|
// Fiscal data
|
||||||
|
@Index()
|
||||||
|
@Column({ name: 'tax_id', type: 'varchar', length: 50, nullable: true })
|
||||||
|
taxId: string;
|
||||||
|
|
||||||
|
@Column({ name: 'tax_id_type', type: 'varchar', length: 20, nullable: true })
|
||||||
|
taxIdType: string;
|
||||||
|
|
||||||
@Column({ name: 'legal_name', type: 'varchar', length: 200, nullable: true })
|
@Column({ name: 'legal_name', type: 'varchar', length: 200, nullable: true })
|
||||||
legalName: string;
|
legalName: string;
|
||||||
|
|
||||||
// Tipo de partner
|
// Primary contact
|
||||||
@Index()
|
|
||||||
@Column({ name: 'partner_type', type: 'varchar', length: 20, default: 'customer' })
|
|
||||||
partnerType: 'customer' | 'supplier' | 'both';
|
|
||||||
|
|
||||||
// Fiscal
|
|
||||||
@Index()
|
|
||||||
@Column({ name: 'tax_id', type: 'varchar', length: 20, nullable: true })
|
|
||||||
taxId: string;
|
|
||||||
|
|
||||||
@Column({ name: 'tax_regime', type: 'varchar', length: 100, nullable: true })
|
|
||||||
taxRegime: string;
|
|
||||||
|
|
||||||
@Column({ name: 'cfdi_use', type: 'varchar', length: 10, nullable: true })
|
|
||||||
cfdiUse: string;
|
|
||||||
|
|
||||||
// Contacto principal
|
|
||||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
@ -57,48 +63,43 @@ export class Partner {
|
|||||||
@Column({ type: 'varchar', length: 30, nullable: true })
|
@Column({ type: 'varchar', length: 30, nullable: true })
|
||||||
mobile: string;
|
mobile: string;
|
||||||
|
|
||||||
@Column({ type: 'varchar', length: 500, nullable: true })
|
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||||
website: string;
|
website: string;
|
||||||
|
|
||||||
// Terminos de pago
|
// Credit and payments
|
||||||
@Column({ name: 'payment_term_days', type: 'int', default: 0 })
|
|
||||||
paymentTermDays: number;
|
|
||||||
|
|
||||||
@Column({ name: 'credit_limit', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
@Column({ name: 'credit_limit', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
||||||
creditLimit: number;
|
creditLimit: number;
|
||||||
|
|
||||||
@Column({ name: 'current_balance', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
@Column({ name: 'payment_term_days', type: 'int', default: 0 })
|
||||||
currentBalance: number;
|
paymentTermDays: number;
|
||||||
|
|
||||||
// Lista de precios
|
@Column({ name: 'payment_method', type: 'varchar', length: 50, nullable: true })
|
||||||
@Column({ name: 'price_list_id', type: 'uuid', nullable: true })
|
paymentMethod: string;
|
||||||
priceListId: string;
|
|
||||||
|
|
||||||
// Descuentos
|
// Classification
|
||||||
@Column({ name: 'discount_percent', type: 'decimal', precision: 5, scale: 2, default: 0 })
|
|
||||||
discountPercent: number;
|
|
||||||
|
|
||||||
// Categoria
|
|
||||||
@Column({ type: 'varchar', length: 50, nullable: true })
|
@Column({ type: 'varchar', length: 50, nullable: true })
|
||||||
category: string;
|
category: string;
|
||||||
|
|
||||||
@Column({ type: 'text', array: true, default: '{}' })
|
@Column({ type: 'text', array: true, default: '{}' })
|
||||||
tags: string[];
|
tags: string[];
|
||||||
|
|
||||||
// Notas
|
// Status
|
||||||
@Column({ type: 'text', nullable: true })
|
|
||||||
notes: string;
|
|
||||||
|
|
||||||
// Estado
|
|
||||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
|
||||||
@Column({ name: 'is_verified', type: 'boolean', default: false })
|
@Column({ name: 'is_verified', type: 'boolean', default: false })
|
||||||
isVerified: boolean;
|
isVerified: boolean;
|
||||||
|
|
||||||
// Vendedor asignado
|
@Column({ name: 'verified_at', type: 'timestamptz', nullable: true })
|
||||||
@Column({ name: 'sales_rep_id', type: 'uuid', nullable: true })
|
verifiedAt: Date | null;
|
||||||
salesRepId: string;
|
|
||||||
|
// Settings (flexible JSONB)
|
||||||
|
@Column({ type: 'jsonb', default: '{}' })
|
||||||
|
settings: Record<string, unknown>;
|
||||||
|
|
||||||
|
// Notes
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
notes: string;
|
||||||
|
|
||||||
// Metadata
|
// Metadata
|
||||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
|||||||
@ -12,22 +12,21 @@ import {
|
|||||||
import { ProductCategory } from './product-category.entity';
|
import { ProductCategory } from './product-category.entity';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commerce Product Entity (schema: products.products)
|
* Commerce Product Entity
|
||||||
|
*
|
||||||
|
* Synchronized with DDL: database/ddl/17-products.sql
|
||||||
|
* Table: products.products
|
||||||
*
|
*
|
||||||
* NOTE: This is NOT a duplicate of inventory/entities/product.entity.ts
|
* NOTE: This is NOT a duplicate of inventory/entities/product.entity.ts
|
||||||
*
|
*
|
||||||
* Key differences:
|
* Key differences:
|
||||||
* - This entity: products.products - Commerce/retail focused
|
* - This entity: products.products - Commerce/retail focused
|
||||||
* - Has: SAT codes, tax rates, detailed dimensions, min/max stock, reorder points
|
* - Has: SAT codes, tax rates, pricing, attributes
|
||||||
* - Used by: Sales, purchases, invoicing, POS
|
* - Used by: Sales, purchases, invoicing, POS
|
||||||
*
|
*
|
||||||
* - Inventory Product: inventory.products - Warehouse/stock management focused (Odoo-style)
|
* - Inventory Product: inventory.products - Warehouse/stock management focused (Odoo-style)
|
||||||
* - Has: valuationMethod, tracking (lot/serial), isStorable, StockQuant/Lot relations
|
* - Has: valuationMethod, tracking (lot/serial), isStorable, StockQuant/Lot relations
|
||||||
* - Used by: Inventory module for stock tracking, valuation, picking operations
|
* - Used by: Inventory module for stock tracking, valuation, picking operations
|
||||||
*
|
|
||||||
* These are intentionally separate by domain. This commerce product entity handles
|
|
||||||
* pricing, tax compliance (SAT/CFDI), and business rules. For physical stock tracking,
|
|
||||||
* use the inventory module's product entity.
|
|
||||||
*/
|
*/
|
||||||
@Entity({ name: 'products', schema: 'products' })
|
@Entity({ name: 'products', schema: 'products' })
|
||||||
export class Product {
|
export class Product {
|
||||||
@ -46,19 +45,7 @@ export class Product {
|
|||||||
@JoinColumn({ name: 'category_id' })
|
@JoinColumn({ name: 'category_id' })
|
||||||
category: ProductCategory;
|
category: ProductCategory;
|
||||||
|
|
||||||
/**
|
// Identification
|
||||||
* Optional link to inventory.products for unified stock management.
|
|
||||||
* This allows the commerce product to be linked to its inventory counterpart
|
|
||||||
* for stock tracking, valuation (FIFO/AVERAGE), and warehouse operations.
|
|
||||||
*
|
|
||||||
* The inventory product handles: stock levels, lot/serial tracking, valuation layers
|
|
||||||
* This commerce product handles: pricing, taxes, SAT compliance, commercial data
|
|
||||||
*/
|
|
||||||
@Index()
|
|
||||||
@Column({ name: 'inventory_product_id', type: 'uuid', nullable: true })
|
|
||||||
inventoryProductId: string | null;
|
|
||||||
|
|
||||||
// Identificacion
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ type: 'varchar', length: 50 })
|
@Column({ type: 'varchar', length: 50 })
|
||||||
sku: string;
|
sku: string;
|
||||||
@ -70,55 +57,48 @@ export class Product {
|
|||||||
@Column({ type: 'varchar', length: 200 })
|
@Column({ type: 'varchar', length: 200 })
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@Column({ name: 'short_name', type: 'varchar', length: 50, nullable: true })
|
|
||||||
shortName: string;
|
|
||||||
|
|
||||||
@Column({ type: 'text', nullable: true })
|
@Column({ type: 'text', nullable: true })
|
||||||
description: string;
|
description: string;
|
||||||
|
|
||||||
// Tipo
|
@Column({ name: 'short_description', type: 'varchar', length: 500, nullable: true })
|
||||||
|
shortDescription: string;
|
||||||
|
|
||||||
|
// Type
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ name: 'product_type', type: 'varchar', length: 20, default: 'product' })
|
@Column({ name: 'product_type', type: 'varchar', length: 20, default: 'product' })
|
||||||
productType: 'product' | 'service' | 'consumable' | 'kit';
|
productType: 'product' | 'service' | 'consumable' | 'kit';
|
||||||
|
|
||||||
// Precios
|
// Pricing
|
||||||
@Column({ name: 'sale_price', type: 'decimal', precision: 15, scale: 4, default: 0 })
|
@Column({ type: 'decimal', precision: 15, scale: 4, default: 0 })
|
||||||
salePrice: number;
|
price: number;
|
||||||
|
|
||||||
@Column({ name: 'cost_price', type: 'decimal', precision: 15, scale: 4, default: 0 })
|
@Column({ type: 'decimal', precision: 15, scale: 4, default: 0 })
|
||||||
costPrice: number;
|
cost: number;
|
||||||
|
|
||||||
@Column({ name: 'min_sale_price', type: 'decimal', precision: 15, scale: 4, nullable: true })
|
|
||||||
minSalePrice: number;
|
|
||||||
|
|
||||||
@Column({ type: 'varchar', length: 3, default: 'MXN' })
|
@Column({ type: 'varchar', length: 3, default: 'MXN' })
|
||||||
currency: string;
|
currency: string;
|
||||||
|
|
||||||
// Impuestos
|
@Column({ name: 'tax_included', type: 'boolean', default: true })
|
||||||
|
taxIncluded: boolean;
|
||||||
|
|
||||||
|
// Taxes
|
||||||
@Column({ name: 'tax_rate', type: 'decimal', precision: 5, scale: 2, default: 16 })
|
@Column({ name: 'tax_rate', type: 'decimal', precision: 5, scale: 2, default: 16 })
|
||||||
taxRate: number;
|
taxRate: number;
|
||||||
|
|
||||||
@Column({ name: 'tax_included', type: 'boolean', default: false })
|
@Column({ name: 'tax_code', type: 'varchar', length: 20, nullable: true })
|
||||||
taxIncluded: boolean;
|
taxCode: string;
|
||||||
|
|
||||||
// SAT (Mexico)
|
// Unit of measure
|
||||||
@Column({ name: 'sat_product_code', type: 'varchar', length: 20, nullable: true })
|
|
||||||
satProductCode: string;
|
|
||||||
|
|
||||||
@Column({ name: 'sat_unit_code', type: 'varchar', length: 10, nullable: true })
|
|
||||||
satUnitCode: string;
|
|
||||||
|
|
||||||
// Unidad de medida
|
|
||||||
@Column({ type: 'varchar', length: 20, default: 'PZA' })
|
@Column({ type: 'varchar', length: 20, default: 'PZA' })
|
||||||
uom: string;
|
uom: string;
|
||||||
|
|
||||||
@Column({ name: 'uom_purchase', type: 'varchar', length: 20, nullable: true })
|
@Column({ name: 'uom_purchase', type: 'varchar', length: 20, nullable: true })
|
||||||
uomPurchase: string;
|
uomPurchase: string;
|
||||||
|
|
||||||
@Column({ name: 'conversion_factor', type: 'decimal', precision: 10, scale: 4, default: 1 })
|
@Column({ name: 'uom_conversion', type: 'decimal', precision: 10, scale: 4, default: 1 })
|
||||||
conversionFactor: number;
|
uomConversion: number;
|
||||||
|
|
||||||
// Inventario
|
// Inventory
|
||||||
@Column({ name: 'track_inventory', type: 'boolean', default: true })
|
@Column({ name: 'track_inventory', type: 'boolean', default: true })
|
||||||
trackInventory: boolean;
|
trackInventory: boolean;
|
||||||
|
|
||||||
@ -131,26 +111,13 @@ export class Product {
|
|||||||
@Column({ name: 'reorder_point', type: 'decimal', precision: 15, scale: 4, nullable: true })
|
@Column({ name: 'reorder_point', type: 'decimal', precision: 15, scale: 4, nullable: true })
|
||||||
reorderPoint: number;
|
reorderPoint: number;
|
||||||
|
|
||||||
@Column({ name: 'reorder_quantity', type: 'decimal', precision: 15, scale: 4, nullable: true })
|
@Column({ name: 'lead_time_days', type: 'int', default: 0 })
|
||||||
reorderQuantity: number;
|
leadTimeDays: number;
|
||||||
|
|
||||||
// Lotes y series
|
// Physical characteristics
|
||||||
@Column({ name: 'track_lots', type: 'boolean', default: false })
|
|
||||||
trackLots: boolean;
|
|
||||||
|
|
||||||
@Column({ name: 'track_serials', type: 'boolean', default: false })
|
|
||||||
trackSerials: boolean;
|
|
||||||
|
|
||||||
@Column({ name: 'track_expiry', type: 'boolean', default: false })
|
|
||||||
trackExpiry: boolean;
|
|
||||||
|
|
||||||
// Dimensiones
|
|
||||||
@Column({ type: 'decimal', precision: 10, scale: 4, nullable: true })
|
@Column({ type: 'decimal', precision: 10, scale: 4, nullable: true })
|
||||||
weight: number;
|
weight: number;
|
||||||
|
|
||||||
@Column({ name: 'weight_unit', type: 'varchar', length: 10, default: 'kg' })
|
|
||||||
weightUnit: string;
|
|
||||||
|
|
||||||
@Column({ type: 'decimal', precision: 10, scale: 4, nullable: true })
|
@Column({ type: 'decimal', precision: 10, scale: 4, nullable: true })
|
||||||
length: number;
|
length: number;
|
||||||
|
|
||||||
@ -160,25 +127,21 @@ export class Product {
|
|||||||
@Column({ type: 'decimal', precision: 10, scale: 4, nullable: true })
|
@Column({ type: 'decimal', precision: 10, scale: 4, nullable: true })
|
||||||
height: number;
|
height: number;
|
||||||
|
|
||||||
@Column({ name: 'dimension_unit', type: 'varchar', length: 10, default: 'cm' })
|
@Column({ type: 'decimal', precision: 10, scale: 4, nullable: true })
|
||||||
dimensionUnit: string;
|
volume: number;
|
||||||
|
|
||||||
// Imagenes
|
// Images
|
||||||
@Column({ name: 'image_url', type: 'varchar', length: 500, nullable: true })
|
@Column({ name: 'image_url', type: 'varchar', length: 500, nullable: true })
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
|
|
||||||
@Column({ type: 'text', array: true, default: '{}' })
|
@Column({ type: 'jsonb', default: '[]' })
|
||||||
images: string[];
|
images: string[];
|
||||||
|
|
||||||
// Tags
|
// Attributes (flexible JSONB for custom attributes like color, size, material)
|
||||||
@Column({ type: 'text', array: true, default: '{}' })
|
@Column({ type: 'jsonb', default: '{}' })
|
||||||
tags: string[];
|
attributes: Record<string, unknown>;
|
||||||
|
|
||||||
// Notas
|
// Status
|
||||||
@Column({ type: 'text', nullable: true })
|
|
||||||
notes: string;
|
|
||||||
|
|
||||||
// Estado
|
|
||||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,22 @@
|
|||||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, Index, ManyToOne, JoinColumn } from 'typeorm';
|
import {
|
||||||
import { PaymentTerm } from '../../core/entities/payment-term.entity.js';
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
DeleteDateColumn,
|
||||||
|
Index,
|
||||||
|
ManyToOne,
|
||||||
|
JoinColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sales Order Entity
|
* Sales Order Entity
|
||||||
*
|
*
|
||||||
* Aligned with SQL schema used by orders.service.ts
|
* Synchronized with DDL: database/ddl/22-sales.sql
|
||||||
* Supports full Order-to-Cash flow with:
|
* Table: sales.sales_orders
|
||||||
* - PaymentTerms integration
|
*
|
||||||
* - Automatic picking creation
|
* Represents confirmed sales orders with full order-to-delivery tracking.
|
||||||
* - Stock reservation
|
|
||||||
* - Invoice and delivery status tracking
|
|
||||||
*/
|
*/
|
||||||
@Entity({ name: 'sales_orders', schema: 'sales' })
|
@Entity({ name: 'sales_orders', schema: 'sales' })
|
||||||
export class SalesOrder {
|
export class SalesOrder {
|
||||||
@ -20,110 +27,104 @@ export class SalesOrder {
|
|||||||
@Column({ name: 'tenant_id', type: 'uuid' })
|
@Column({ name: 'tenant_id', type: 'uuid' })
|
||||||
tenantId: string;
|
tenantId: string;
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({ name: 'company_id', type: 'uuid' })
|
|
||||||
companyId: string;
|
|
||||||
|
|
||||||
// Order identification
|
// Order identification
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ type: 'varchar', length: 30 })
|
@Column({ name: 'order_number', type: 'varchar', length: 30 })
|
||||||
name: string; // Order number (e.g., SO-000001)
|
orderNumber: string;
|
||||||
|
|
||||||
@Column({ name: 'client_order_ref', type: 'varchar', length: 100, nullable: true })
|
|
||||||
clientOrderRef: string | null; // Customer's reference number
|
|
||||||
|
|
||||||
|
// Origin (from quotation)
|
||||||
@Column({ name: 'quotation_id', type: 'uuid', nullable: true })
|
@Column({ name: 'quotation_id', type: 'uuid', nullable: true })
|
||||||
quotationId: string | null;
|
quotationId: string | null;
|
||||||
|
|
||||||
// Partner/Customer
|
// Customer
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ name: 'partner_id', type: 'uuid' })
|
@Column({ name: 'partner_id', type: 'uuid' })
|
||||||
partnerId: string;
|
partnerId: string;
|
||||||
|
|
||||||
|
@Column({ name: 'partner_name', type: 'varchar', length: 200, nullable: true })
|
||||||
|
partnerName: string | null;
|
||||||
|
|
||||||
|
@Column({ name: 'partner_email', type: 'varchar', length: 255, nullable: true })
|
||||||
|
partnerEmail: string | null;
|
||||||
|
|
||||||
|
// Addresses (JSONB)
|
||||||
|
@Column({ name: 'billing_address', type: 'jsonb', nullable: true })
|
||||||
|
billingAddress: Record<string, unknown> | null;
|
||||||
|
|
||||||
|
@Column({ name: 'shipping_address', type: 'jsonb', nullable: true })
|
||||||
|
shippingAddress: Record<string, unknown> | null;
|
||||||
|
|
||||||
// Dates
|
// Dates
|
||||||
@Column({ name: 'order_date', type: 'date', default: () => 'CURRENT_DATE' })
|
@Column({ name: 'order_date', type: 'date', default: () => 'CURRENT_DATE' })
|
||||||
orderDate: Date;
|
orderDate: Date;
|
||||||
|
|
||||||
@Column({ name: 'validity_date', type: 'date', nullable: true })
|
@Column({ name: 'requested_date', type: 'date', nullable: true })
|
||||||
validityDate: Date | null;
|
requestedDate: Date | null;
|
||||||
|
|
||||||
@Column({ name: 'commitment_date', type: 'date', nullable: true })
|
@Column({ name: 'promised_date', type: 'date', nullable: true })
|
||||||
commitmentDate: Date | null; // Promised delivery date
|
promisedDate: Date | null;
|
||||||
|
|
||||||
// Currency and pricing
|
@Column({ name: 'shipped_date', type: 'date', nullable: true })
|
||||||
@Index()
|
shippedDate: Date | null;
|
||||||
@Column({ name: 'currency_id', type: 'uuid' })
|
|
||||||
currencyId: string;
|
|
||||||
|
|
||||||
@Column({ name: 'pricelist_id', type: 'uuid', nullable: true })
|
@Column({ name: 'delivered_date', type: 'date', nullable: true })
|
||||||
pricelistId: string | null;
|
deliveredDate: Date | null;
|
||||||
|
|
||||||
// Payment terms integration (TASK-003-01)
|
// Sales rep
|
||||||
@Index()
|
@Column({ name: 'sales_rep_id', type: 'uuid', nullable: true })
|
||||||
@Column({ name: 'payment_term_id', type: 'uuid', nullable: true })
|
salesRepId: string | null;
|
||||||
paymentTermId: string | null;
|
|
||||||
|
|
||||||
@ManyToOne(() => PaymentTerm)
|
// Warehouse
|
||||||
@JoinColumn({ name: 'payment_term_id' })
|
@Column({ name: 'warehouse_id', type: 'uuid', nullable: true })
|
||||||
paymentTerm: PaymentTerm;
|
warehouseId: string | null;
|
||||||
|
|
||||||
// Sales team
|
// Totals
|
||||||
@Column({ name: 'user_id', type: 'uuid', nullable: true })
|
@Column({ type: 'varchar', length: 3, default: 'MXN' })
|
||||||
userId: string | null; // Sales representative
|
currency: string;
|
||||||
|
|
||||||
@Column({ name: 'sales_team_id', type: 'uuid', nullable: true })
|
@Column({ type: 'decimal', precision: 15, scale: 2, default: 0 })
|
||||||
salesTeamId: string | null;
|
subtotal: number;
|
||||||
|
|
||||||
// Amounts
|
@Column({ name: 'tax_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
||||||
@Column({ name: 'amount_untaxed', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
taxAmount: number;
|
||||||
amountUntaxed: number;
|
|
||||||
|
|
||||||
@Column({ name: 'amount_tax', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
@Column({ name: 'discount_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
||||||
amountTax: number;
|
discountAmount: number;
|
||||||
|
|
||||||
@Column({ name: 'amount_total', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
@Column({ name: 'shipping_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
|
||||||
amountTotal: number;
|
shippingAmount: number;
|
||||||
|
|
||||||
// Status fields (Order-to-Cash tracking)
|
@Column({ type: 'decimal', precision: 15, scale: 2, default: 0 })
|
||||||
|
total: number;
|
||||||
|
|
||||||
|
// Payment terms
|
||||||
|
@Column({ name: 'payment_term_days', type: 'int', default: 0 })
|
||||||
|
paymentTermDays: number;
|
||||||
|
|
||||||
|
@Column({ name: 'payment_method', type: 'varchar', length: 50, nullable: true })
|
||||||
|
paymentMethod: string | null;
|
||||||
|
|
||||||
|
// Status
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ type: 'varchar', length: 20, default: 'draft' })
|
@Column({ type: 'varchar', length: 20, default: 'draft' })
|
||||||
status: 'draft' | 'sent' | 'sale' | 'done' | 'cancelled';
|
status: 'draft' | 'confirmed' | 'processing' | 'shipped' | 'delivered' | 'cancelled';
|
||||||
|
|
||||||
@Index()
|
// Shipping
|
||||||
@Column({ name: 'invoice_status', type: 'varchar', length: 20, default: 'pending' })
|
@Column({ name: 'shipping_method', type: 'varchar', length: 50, nullable: true })
|
||||||
invoiceStatus: 'pending' | 'partial' | 'invoiced';
|
shippingMethod: string | null;
|
||||||
|
|
||||||
@Index()
|
@Column({ name: 'tracking_number', type: 'varchar', length: 100, nullable: true })
|
||||||
@Column({ name: 'delivery_status', type: 'varchar', length: 20, default: 'pending' })
|
trackingNumber: string | null;
|
||||||
deliveryStatus: 'pending' | 'partial' | 'delivered';
|
|
||||||
|
|
||||||
@Column({ name: 'invoice_policy', type: 'varchar', length: 20, default: 'order' })
|
@Column({ type: 'varchar', length: 100, nullable: true })
|
||||||
invoicePolicy: 'order' | 'delivery';
|
carrier: string | null;
|
||||||
|
|
||||||
// Delivery/Picking integration (TASK-003-03)
|
|
||||||
@Column({ name: 'picking_id', type: 'uuid', nullable: true })
|
|
||||||
pickingId: string | null;
|
|
||||||
|
|
||||||
// Notes
|
// Notes
|
||||||
@Column({ type: 'text', nullable: true })
|
@Column({ type: 'text', nullable: true })
|
||||||
notes: string | null;
|
notes: string | null;
|
||||||
|
|
||||||
@Column({ name: 'terms_conditions', type: 'text', nullable: true })
|
@Column({ name: 'internal_notes', type: 'text', nullable: true })
|
||||||
termsConditions: string | null;
|
internalNotes: string | null;
|
||||||
|
|
||||||
// Confirmation tracking
|
|
||||||
@Column({ name: 'confirmed_at', type: 'timestamptz', nullable: true })
|
|
||||||
confirmedAt: Date | null;
|
|
||||||
|
|
||||||
@Column({ name: 'confirmed_by', type: 'uuid', nullable: true })
|
|
||||||
confirmedBy: string | null;
|
|
||||||
|
|
||||||
// Cancellation tracking
|
|
||||||
@Column({ name: 'cancelled_at', type: 'timestamptz', nullable: true })
|
|
||||||
cancelledAt: Date | null;
|
|
||||||
|
|
||||||
@Column({ name: 'cancelled_by', type: 'uuid', nullable: true })
|
|
||||||
cancelledBy: string | null;
|
|
||||||
|
|
||||||
// Audit fields
|
// Audit fields
|
||||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user