Sprint 0: Updated inventories (MASTER/BACKEND/DATABASE) with verified baseline Sprint 1: Fixed 8 P0 blockers - CFDI entities (schema cfdi→fiscal), auth base DDL, billing duplication (→operations), 5 project entities, PaymentInvoiceAllocation, core.companies DDL, recreate-database.sh array Sprint 2: 4 new auth entities, session/role/permission DDL reconciliation, CFDI PAC+StampQueue, partner address+contact alignment Sprint 3: CFDI service+controller+routes, mobile service+controller+routes, inventory extended DDL (7 tables) Sprint 4: timestamp→timestamptz (40 files), field divergences, token/roles/permissions service alignment with new DDL-aligned entities Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
142 lines
4.3 KiB
TypeScript
142 lines
4.3 KiB
TypeScript
import {
|
|
Entity,
|
|
PrimaryGeneratedColumn,
|
|
Column,
|
|
CreateDateColumn,
|
|
UpdateDateColumn,
|
|
Index,
|
|
ManyToOne,
|
|
JoinColumn,
|
|
} from 'typeorm';
|
|
import { CancellationReason } from '../enums/cancellation-reason.enum.js';
|
|
import { CfdiInvoice } from './cfdi-invoice.entity.js';
|
|
|
|
/**
|
|
* Cancellation request status aligned with DDL fiscal.cancellation_request_status
|
|
*/
|
|
export enum CancellationRequestStatus {
|
|
PENDING = 'pending',
|
|
SUBMITTED = 'submitted',
|
|
ACCEPTED = 'accepted',
|
|
REJECTED = 'rejected',
|
|
IN_PROCESS = 'in_process',
|
|
EXPIRED = 'expired',
|
|
CANCELLED = 'cancelled',
|
|
ERROR = 'error',
|
|
}
|
|
|
|
/**
|
|
* CFDI Cancellation Request Entity
|
|
* Aligned with DDL: fiscal.cfdi_cancellation_requests
|
|
*/
|
|
@Entity({ schema: 'fiscal', name: 'cfdi_cancellation_requests' })
|
|
@Index('idx_cfdi_cancellation_requests_tenant', ['tenantId'])
|
|
@Index('idx_cfdi_cancellation_requests_cfdi', ['cfdiInvoiceId'])
|
|
@Index('idx_cfdi_cancellation_requests_uuid', ['cfdiUuid'])
|
|
@Index('idx_cfdi_cancellation_requests_status', ['status'])
|
|
@Index('idx_cfdi_cancellation_requests_created', ['createdAt'])
|
|
export class CfdiCancellation {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id: string;
|
|
|
|
@Column({ type: 'uuid', nullable: false, name: 'tenant_id' })
|
|
tenantId: string;
|
|
|
|
@Column({ type: 'uuid', nullable: false, name: 'cfdi_invoice_id' })
|
|
cfdiInvoiceId: string;
|
|
|
|
@Column({ type: 'varchar', length: 36, nullable: false, name: 'cfdi_uuid' })
|
|
cfdiUuid: string;
|
|
|
|
@Column({
|
|
type: 'enum',
|
|
enum: CancellationReason,
|
|
nullable: false,
|
|
name: 'cancellation_reason',
|
|
})
|
|
cancellationReason: CancellationReason;
|
|
|
|
@Column({ type: 'varchar', length: 36, nullable: true, name: 'substitute_uuid' })
|
|
substituteUuid: string | null;
|
|
|
|
@Column({ type: 'uuid', nullable: true, name: 'substitute_cfdi_id' })
|
|
substituteCfdiId: string | null;
|
|
|
|
@Column({
|
|
type: 'enum',
|
|
enum: CancellationRequestStatus,
|
|
default: CancellationRequestStatus.PENDING,
|
|
nullable: false,
|
|
})
|
|
status: CancellationRequestStatus;
|
|
|
|
@Column({ type: 'text', nullable: true, name: 'sat_ack_xml' })
|
|
satAckXml: string | null;
|
|
|
|
@Column({ type: 'timestamptz', nullable: true, name: 'completed_at' })
|
|
completedAt: Date | null;
|
|
|
|
@Column({ type: 'text', nullable: true, name: 'error_message' })
|
|
errorMessage: string | null;
|
|
|
|
@Column({ type: 'varchar', length: 50, nullable: true, name: 'error_code' })
|
|
errorCode: string | null;
|
|
|
|
@Column({ type: 'jsonb', nullable: true, name: 'error_details' })
|
|
errorDetails: Record<string, any> | null;
|
|
|
|
@Column({ type: 'varchar', length: 10, nullable: true, name: 'sat_response_code' })
|
|
satResponseCode: string | null;
|
|
|
|
@Column({ type: 'text', nullable: true, name: 'sat_response_message' })
|
|
satResponseMessage: string | null;
|
|
|
|
@Column({ type: 'text', nullable: true, name: 'receiver_response_reason' })
|
|
receiverResponseReason: string | null;
|
|
|
|
@Column({ type: 'varchar', length: 20, nullable: true, name: 'receiver_response' })
|
|
receiverResponse: string | null;
|
|
|
|
@Column({ type: 'timestamptz', nullable: true, name: 'receiver_response_at' })
|
|
receiverResponseAt: Date | null;
|
|
|
|
@Column({ type: 'timestamptz', nullable: true, name: 'expires_at' })
|
|
expiresAt: Date | null;
|
|
|
|
@Column({ type: 'timestamptz', nullable: true, name: 'requested_at' })
|
|
requestedAt: Date | null;
|
|
|
|
@Column({ type: 'timestamptz', nullable: true, name: 'submitted_at' })
|
|
submittedAt: Date | null;
|
|
|
|
@Column({ type: 'timestamptz', nullable: true, name: 'response_at' })
|
|
responseAt: Date | null;
|
|
|
|
@Column({ type: 'text', nullable: true, name: 'reason_notes' })
|
|
reasonNotes: string | null;
|
|
|
|
@Column({ type: 'text', nullable: true, name: 'internal_notes' })
|
|
internalNotes: string | null;
|
|
|
|
@Column({ type: 'integer', default: 0, nullable: false, name: 'retry_count' })
|
|
retryCount: number;
|
|
|
|
// Relation
|
|
@ManyToOne(() => CfdiInvoice, (invoice) => invoice.cancellations)
|
|
@JoinColumn({ name: 'cfdi_invoice_id' })
|
|
cfdiInvoice: CfdiInvoice;
|
|
|
|
// Audit fields
|
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
|
createdAt: Date;
|
|
|
|
@Column({ type: 'uuid', nullable: true, name: 'created_by' })
|
|
createdBy: string | null;
|
|
|
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz', nullable: true })
|
|
updatedAt: Date | null;
|
|
|
|
@Column({ type: 'uuid', nullable: true, name: 'updated_by' })
|
|
updatedBy: string | null;
|
|
}
|