erp-mecanicas-diesel-backen.../src/modules/invoices/entities/invoice.entity.ts
Adrian Flores Cortes 8c010221fb feat: Propagate core entities from erp-construccion
Modules added:
- audit (7 entities): audit logs, config changes, entity changes
- billing-usage (14 entities): subscriptions, invoices, coupons
- core (13 entities): countries, currencies, UoMs, sequences
- invoices (4 entities): invoices, payments, allocations
- notifications (6 entities): notifications, templates, channels

Total: 44 new entity files
Build: Clean (0 TypeScript errors)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 09:06:11 -06:00

188 lines
5.6 KiB
TypeScript

/**
* Unified Invoice Entity
* Combines commercial and SaaS billing invoices
* Compatible with erp-core invoice.entity
*
* @module Invoices
*/
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
DeleteDateColumn,
Index,
OneToMany,
} from 'typeorm';
import { InvoiceItem } from './invoice-item.entity';
export type InvoiceType = 'sale' | 'purchase' | 'credit_note' | 'debit_note';
export type InvoiceStatus = 'draft' | 'validated' | 'sent' | 'partial' | 'paid' | 'overdue' | 'void' | 'refunded' | 'cancelled' | 'voided';
export type InvoiceContext = 'commercial' | 'saas';
@Entity({ name: 'invoices', schema: 'billing' })
export class Invoice {
@PrimaryGeneratedColumn('uuid')
id: string;
@Index()
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
@Index({ unique: true })
@Column({ name: 'invoice_number', type: 'varchar', length: 30 })
invoiceNumber: string;
@Index()
@Column({ name: 'invoice_type', type: 'varchar', length: 20, default: 'sale' })
invoiceType: InvoiceType;
@Index()
@Column({ name: 'invoice_context', type: 'varchar', length: 20, default: 'commercial' })
invoiceContext: InvoiceContext;
// Commercial fields
@Column({ name: 'sales_order_id', type: 'uuid', nullable: true })
salesOrderId: string | null;
@Column({ name: 'purchase_order_id', type: 'uuid', nullable: true })
purchaseOrderId: string | null;
@Index()
@Column({ name: 'partner_id', type: 'uuid', nullable: true })
partnerId: string | null;
@Column({ name: 'partner_name', type: 'varchar', length: 200, nullable: true })
partnerName: string | null;
@Column({ name: 'partner_tax_id', type: 'varchar', length: 50, nullable: true })
partnerTaxId: string | null;
// SaaS billing fields
@Index()
@Column({ name: 'subscription_id', type: 'uuid', nullable: true })
subscriptionId: string | null;
@Column({ name: 'period_start', type: 'date', nullable: true })
periodStart: Date | null;
@Column({ name: 'period_end', type: 'date', nullable: true })
periodEnd: Date | null;
// Billing information
@Column({ name: 'billing_name', type: 'varchar', length: 200, nullable: true })
billingName: string | null;
@Column({ name: 'billing_email', type: 'varchar', length: 255, nullable: true })
billingEmail: string | null;
@Column({ name: 'billing_address', type: 'jsonb', nullable: true })
billingAddress: Record<string, any> | null;
@Column({ name: 'tax_id', type: 'varchar', length: 50, nullable: true })
taxId: string | null;
// Dates
@Index()
@Column({ name: 'invoice_date', type: 'date', default: () => 'CURRENT_DATE' })
invoiceDate: Date;
@Column({ name: 'due_date', type: 'date', nullable: true })
dueDate: Date | null;
@Column({ name: 'payment_date', type: 'date', nullable: true })
paymentDate: Date | null;
@Column({ name: 'paid_at', type: 'timestamptz', nullable: true })
paidAt: Date | null;
// Amounts
@Column({ type: 'varchar', length: 3, default: 'MXN' })
currency: string;
@Column({ name: 'exchange_rate', type: 'decimal', precision: 10, scale: 6, default: 1 })
exchangeRate: number;
@Column({ type: 'decimal', precision: 15, scale: 2, default: 0 })
subtotal: number;
@Column({ name: 'tax_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
taxAmount: number;
@Column({ name: 'withholding_tax', type: 'decimal', precision: 15, scale: 2, default: 0 })
withholdingTax: number;
@Column({ name: 'discount_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
discountAmount: 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: 'amount_due', type: 'decimal', precision: 15, scale: 2, insert: false, update: false, nullable: true })
amountDue: number | null;
@Column({ name: 'paid_amount', type: 'decimal', precision: 15, scale: 2, default: 0 })
paidAmount: number;
// Payment details
@Column({ name: 'payment_term_days', type: 'int', default: 0 })
paymentTermDays: number;
@Column({ name: 'payment_method', type: 'varchar', length: 50, nullable: true })
paymentMethod: string | null;
@Column({ name: 'payment_reference', type: 'varchar', length: 100, nullable: true })
paymentReference: string | null;
// Status
@Index()
@Column({ type: 'varchar', length: 20, default: 'draft' })
status: InvoiceStatus;
// CFDI (Mexico)
@Index()
@Column({ name: 'cfdi_uuid', type: 'varchar', length: 40, nullable: true })
cfdiUuid: string | null;
@Column({ name: 'cfdi_status', type: 'varchar', length: 20, nullable: true })
cfdiStatus: string | null;
@Column({ name: 'cfdi_xml', type: 'text', nullable: true })
cfdiXml: string | null;
@Column({ name: 'cfdi_pdf_url', type: 'varchar', length: 500, nullable: true })
cfdiPdfUrl: string | null;
// Notes
@Column({ type: 'text', nullable: true })
notes: string | null;
@Column({ name: 'internal_notes', type: 'text', nullable: true })
internalNotes: string | null;
// Audit
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
@Column({ name: 'created_by', type: 'uuid', nullable: true })
createdBy: string | null;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt: Date;
@Column({ name: 'updated_by', type: 'uuid', nullable: true })
updatedBy: string | null;
@DeleteDateColumn({ name: 'deleted_at', type: 'timestamptz', nullable: true })
deletedAt: Date | null;
// Relations
@OneToMany(() => InvoiceItem, (item) => item.invoice, { cascade: true })
items: InvoiceItem[];
}