Files
musadeq/backend/src/modules/invoices/entities/invoice.entity.ts

132 lines
4.3 KiB
TypeScript

/**
* ════════════════════════════════════════════════════════════
* مُصادَق (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[];
}