feat(sat): add database models for SAT sync
Phase 1 - Database models: - Add FielCredential model for encrypted FIEL storage - Add SatSyncJob model for sync job tracking - Add SAT-related enums (SatSyncType, SatSyncStatus, CfdiSyncType) - Add TypeScript types in shared package - Relations: Tenant -> FielCredential (1:1), Tenant -> SatSyncJobs (1:N) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,9 @@ model Tenant {
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
expiresAt DateTime? @map("expires_at")
|
||||
|
||||
users User[]
|
||||
users User[]
|
||||
fielCredential FielCredential?
|
||||
satSyncJobs SatSyncJob[]
|
||||
|
||||
@@map("tenants")
|
||||
}
|
||||
@@ -62,3 +64,75 @@ enum Role {
|
||||
contador
|
||||
visor
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SAT Sync Models
|
||||
// ============================================
|
||||
|
||||
model FielCredential {
|
||||
id String @id @default(uuid())
|
||||
tenantId String @unique @map("tenant_id")
|
||||
rfc String @db.VarChar(13)
|
||||
cerData Bytes @map("cer_data")
|
||||
keyData Bytes @map("key_data")
|
||||
keyPasswordEncrypted Bytes @map("key_password_encrypted")
|
||||
encryptionIv Bytes @map("encryption_iv")
|
||||
encryptionTag Bytes @map("encryption_tag")
|
||||
serialNumber String? @map("serial_number") @db.VarChar(50)
|
||||
validFrom DateTime @map("valid_from")
|
||||
validUntil DateTime @map("valid_until")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("fiel_credentials")
|
||||
}
|
||||
|
||||
model SatSyncJob {
|
||||
id String @id @default(uuid())
|
||||
tenantId String @map("tenant_id")
|
||||
type SatSyncType
|
||||
status SatSyncStatus @default(pending)
|
||||
dateFrom DateTime @map("date_from") @db.Date
|
||||
dateTo DateTime @map("date_to") @db.Date
|
||||
cfdiType CfdiSyncType? @map("cfdi_type")
|
||||
satRequestId String? @map("sat_request_id") @db.VarChar(50)
|
||||
satPackageIds String[] @map("sat_package_ids")
|
||||
cfdisFound Int @default(0) @map("cfdis_found")
|
||||
cfdisDownloaded Int @default(0) @map("cfdis_downloaded")
|
||||
cfdisInserted Int @default(0) @map("cfdis_inserted")
|
||||
cfdisUpdated Int @default(0) @map("cfdis_updated")
|
||||
progressPercent Int @default(0) @map("progress_percent")
|
||||
errorMessage String? @map("error_message")
|
||||
startedAt DateTime? @map("started_at")
|
||||
completedAt DateTime? @map("completed_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
retryCount Int @default(0) @map("retry_count")
|
||||
nextRetryAt DateTime? @map("next_retry_at")
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@index([status])
|
||||
@@index([status, nextRetryAt])
|
||||
@@map("sat_sync_jobs")
|
||||
}
|
||||
|
||||
enum SatSyncType {
|
||||
initial
|
||||
daily
|
||||
}
|
||||
|
||||
enum SatSyncStatus {
|
||||
pending
|
||||
running
|
||||
completed
|
||||
failed
|
||||
}
|
||||
|
||||
enum CfdiSyncType {
|
||||
emitidos
|
||||
recibidos
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ export * from './types/impuestos';
|
||||
export * from './types/alertas';
|
||||
export * from './types/reportes';
|
||||
export * from './types/calendario';
|
||||
export * from './types/sat';
|
||||
|
||||
// Constants
|
||||
export * from './constants/plans';
|
||||
|
||||
132
packages/shared/src/types/sat.ts
Normal file
132
packages/shared/src/types/sat.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
// ============================================
|
||||
// FIEL (e.firma) Types
|
||||
// ============================================
|
||||
|
||||
export interface FielUploadRequest {
|
||||
cerFile: string; // Base64
|
||||
keyFile: string; // Base64
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface FielStatus {
|
||||
configured: boolean;
|
||||
rfc?: string;
|
||||
serialNumber?: string;
|
||||
validFrom?: string;
|
||||
validUntil?: string;
|
||||
isExpired?: boolean;
|
||||
daysUntilExpiration?: number;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SAT Sync Types
|
||||
// ============================================
|
||||
|
||||
export type SatSyncType = 'initial' | 'daily';
|
||||
export type SatSyncStatus = 'pending' | 'running' | 'completed' | 'failed';
|
||||
export type CfdiSyncType = 'emitidos' | 'recibidos';
|
||||
|
||||
export interface SatSyncJob {
|
||||
id: string;
|
||||
tenantId: string;
|
||||
type: SatSyncType;
|
||||
status: SatSyncStatus;
|
||||
dateFrom: string;
|
||||
dateTo: string;
|
||||
cfdiType?: CfdiSyncType;
|
||||
satRequestId?: string;
|
||||
satPackageIds: string[];
|
||||
cfdisFound: number;
|
||||
cfdisDownloaded: number;
|
||||
cfdisInserted: number;
|
||||
cfdisUpdated: number;
|
||||
progressPercent: number;
|
||||
errorMessage?: string;
|
||||
startedAt?: string;
|
||||
completedAt?: string;
|
||||
createdAt: string;
|
||||
retryCount: number;
|
||||
}
|
||||
|
||||
export interface SatSyncStatusResponse {
|
||||
hasActiveSync: boolean;
|
||||
currentJob?: SatSyncJob;
|
||||
lastCompletedJob?: SatSyncJob;
|
||||
totalCfdisSynced: number;
|
||||
}
|
||||
|
||||
export interface SatSyncHistoryResponse {
|
||||
jobs: SatSyncJob[];
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface StartSyncRequest {
|
||||
type?: SatSyncType;
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
}
|
||||
|
||||
export interface StartSyncResponse {
|
||||
jobId: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SAT Web Service Types
|
||||
// ============================================
|
||||
|
||||
export interface SatAuthResponse {
|
||||
token: string;
|
||||
expiresAt: Date;
|
||||
}
|
||||
|
||||
export interface SatDownloadRequest {
|
||||
rfcSolicitante: string;
|
||||
fechaInicio: Date;
|
||||
fechaFin: Date;
|
||||
tipoSolicitud: 'CFDI' | 'Metadata';
|
||||
tipoComprobante?: 'I' | 'E' | 'T' | 'N' | 'P';
|
||||
rfcEmisor?: string;
|
||||
rfcReceptor?: string;
|
||||
}
|
||||
|
||||
export interface SatDownloadRequestResponse {
|
||||
idSolicitud: string;
|
||||
codEstatus: string;
|
||||
mensaje: string;
|
||||
}
|
||||
|
||||
export interface SatVerifyResponse {
|
||||
codEstatus: string;
|
||||
estadoSolicitud: number; // 1=Aceptada, 2=EnProceso, 3=Terminada, 4=Error, 5=Rechazada, 6=Vencida
|
||||
codigoEstadoSolicitud: string;
|
||||
numeroCfdis: number;
|
||||
mensaje: string;
|
||||
paquetes: string[];
|
||||
}
|
||||
|
||||
export interface SatPackageResponse {
|
||||
paquete: string; // Base64 ZIP
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SAT Error Codes
|
||||
// ============================================
|
||||
|
||||
export const SAT_STATUS_CODES: Record<string, string> = {
|
||||
'5000': 'Solicitud recibida con éxito',
|
||||
'5002': 'Se agotó el límite de solicitudes',
|
||||
'5004': 'No se encontraron CFDIs',
|
||||
'5005': 'Solicitud duplicada',
|
||||
};
|
||||
|
||||
export const SAT_REQUEST_STATUS: Record<number, string> = {
|
||||
1: 'Aceptada',
|
||||
2: 'En proceso',
|
||||
3: 'Terminada',
|
||||
4: 'Error',
|
||||
5: 'Rechazada',
|
||||
6: 'Vencida',
|
||||
};
|
||||
Reference in New Issue
Block a user