/** * ════════════════════════════════════════════════════════════ * مُصادَق (Musadaq) — Invoice Entity * ════════════════════════════════════════════════════════════ */ import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, OneToMany, JoinColumn, Index, } from 'typeorm'; import { Tenant } from '../../tenants/entities/tenant.entity'; import { Company } from '../../companies/entities/company.entity'; import { InvoiceLine } from './invoice-line.entity'; export enum InvoiceStatus { UPLOADED = 'uploaded', EXTRACTING = 'extracting', EXTRACTED = 'extracted', VALIDATED = 'validated', VALIDATION_FAILED = 'validation_failed', SUBMITTING = 'submitting', APPROVED = 'approved', REJECTED = 'rejected', } @Entity('invoices') export class Invoice { @PrimaryGeneratedColumn('uuid') id!: string; @Column({ name: 'tenant_id', type: 'uuid' }) tenant_id!: string; @ManyToOne(() => Tenant, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'tenant_id' }) tenant!: Tenant; @Column({ name: 'company_id', type: 'uuid' }) company_id!: string; @ManyToOne(() => Company, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'company_id' }) company!: Company; // ── Invoice Identity ───────────────────────────────────── @Column({ type: 'varchar', length: 100, nullable: true }) invoice_number?: string; @Column({ type: 'date', nullable: true }) invoice_date?: Date; @Column({ type: 'enum', enum: ['cash', 'credit'], default: 'cash' }) invoice_type!: 'cash' | 'credit'; @Column({ type: 'varchar', length: 3, default: '388' }) // 388: Sales, 381: Credit Note ubl_type_code!: string; @Column({ type: 'varchar', length: 3, default: '013' }) // 013: Cash, 023: Credit payment_method_code!: string; // ── Parties ────────────────────────────────────────────── @Column({ type: 'varchar', length: 20, nullable: true }) supplier_tin?: string; @Column({ type: 'varchar', length: 255, nullable: true }) supplier_name?: string; @Column({ type: 'text', nullable: true }) supplier_address?: string; @Column({ type: 'varchar', length: 20, nullable: true }) buyer_tin?: string; @Column({ type: 'varchar', length: 20, nullable: true }) buyer_national_id?: string; @Column({ type: 'varchar', length: 255, nullable: true }) buyer_name?: string; // ── Totals (decimal 15,3) ──────────────────────────────── @Column({ type: 'decimal', precision: 15, scale: 3, default: 0 }) subtotal!: number; @Column({ type: 'decimal', precision: 15, scale: 3, default: 0 }) discount_total!: number; @Column({ type: 'decimal', precision: 15, scale: 3, default: 0 }) tax_amount!: number; @Column({ type: 'decimal', precision: 15, scale: 3, default: 0 }) grand_total!: number; @Column({ type: 'char', length: 3, default: 'JOD' }) currency_code!: string; // ── Processing Status ──────────────────────────────────── @Column({ type: 'enum', enum: InvoiceStatus, default: InvoiceStatus.UPLOADED }) status!: InvoiceStatus; @Column({ type: 'text', nullable: true }) original_file_path?: string; @Column({ type: 'varchar', length: 20, default: 'simplified' }) invoice_category!: string; @Column({ type: 'jsonb', nullable: true }) validation_errors?: string[]; @Column({ type: 'text', nullable: true }) qr_code?: string; @Column({ type: 'decimal', precision: 4, scale: 3, nullable: true }) ai_confidence_score?: number; @CreateDateColumn({ type: 'timestamp' }) created_at!: Date; @UpdateDateColumn({ type: 'timestamp' }) updated_at!: Date; // ── One-to-Many Relationship ───────────────────────────── @OneToMany(() => InvoiceLine, (line) => line.invoice, { cascade: true }) lines!: InvoiceLine[]; }