RPAfelApi Integration - Required Updates Analysis
π Current State Analysisβ
β What's Already Goodβ
-
FelController - NO CHANGES NEEDED β
- Accepts
Invoiceobject (works for all document types) - Calls
felService.certifyDocument(service handles routing) - Error handling is solid
- Keep as-is for now
- Accepts
-
Database Migration - GOOD β
- Tables for
credit_note,debit_note,fel_cancellationexist - Proper indexes and relationships
- FEL fields included
- No changes needed
- Tables for
-
Architecture - SOLID β
- Clean separation: Controller β Service β Provider
- Event-driven design
- Structure is good
π§ Required Updatesβ
1. Invoice Interface - NEEDS EXTENSION β οΈβ
Current Issue:
- Only supports
type: "FACT" - Missing fields for NCRE, NDEB, ANULACION
Required Changes:
// apps/backend/src/fel/domain/fel.interface.ts
// Add union type for document types
export type FelDocumentType = 'FACT' | 'NCRE' | 'NDEB' | 'ANULACION';
// Extend Invoice interface
export interface Invoice {
// ... existing fields ...
generalData: {
type: FelDocumentType; // Changed from string
dateTimeIssue: string;
currencyCode: string;
};
// Add optional fields for NCRE/NDEB/ANULACION
originalInvoiceUuid?: string; // Required for NCRE, NDEB, ANULACION
adjustmentReason?: string; // Required for NCRE, NDEB
cancellationReason?: string; // Required for ANULACION
cancellationDate?: string; // For ANULACION
originalInvoiceDate?: string; // For ANULACION
}
Why:
- RPAfelApi expects these fields in DTE JSON
- Database tables already have these fields
- Need to support all document types
2. FelService - NEEDS ROUTING LOGIC β οΈβ
Current Issue:
- Always converts to XML
- Always uses direct certifiers
- No RPAfelApi path
Required Changes:
// apps/backend/src/fel/application/fel.service.ts
async certifyDocument({
taxId,
document,
}: ICertifyDocumentParameters): Promise<undefined> {
const { businessData, felProviderData } = document;
try {
// Step 1: Fetch business data
const business = await this.businessesRepository.findById(businessData.id);
if (!business) {
throw new Error('Business not found');
}
// Step 2: Check if using RPAfelApi
const useRpaFelApi =
process.env.USE_RPA_FEL_API === 'true' ||
felProviderData.providerName === 'rpafelapi';
if (useRpaFelApi) {
return await this.certifyViaRpaFelApi(document, business, taxId);
}
// Step 3: Use direct certifier (existing logic)
// ... existing code continues ...
} catch (error) {
// ... existing error handling ...
}
}
private async certifyViaRpaFelApi(
document: Invoice,
business: SelectableBusiness,
taxId: string
): Promise<undefined> {
// Extract certifier config
const { felCertifier, encryptedFelToken } =
this.extractFelCertifierConfig(business.felCertifierConfig);
// Get certifier credentials
const certifierUser = this.getCertifierUser(business, felCertifier);
const certifierPassword = this.cryptoService.decrypt(encryptedFelToken);
// Convert Invoice to DTE JSON (not XML!)
const rpaUUID = document.addendumData.internalReference; // sale.id
const dteJson = this.xmlToDteJsonService.convertInvoiceToDteJson(
document,
rpaUUID
);
// Add certifier credentials to DTE JSON
dteJson.DatosEmision.DatosGenerales.rpaCertificador_Usuario = certifierUser;
dteJson.DatosEmision.DatosGenerales.rpaCertificador_Clave = certifierPassword;
// Get RPAfelApi provider
const provider = this.providerService.getProvider('rpafelapi');
// Certify via RPAfelApi
const result = await provider.certifyDocument({
taxId,
xmlContent: '', // Not needed for RPAfelApi
apiUrl: '', // Not needed
token: '', // RPAfelApi handles auth internally via DTE JSON
});
return result;
}
Why:
- Need to route to RPAfelApi when enabled
- RPAfelApi uses DTE JSON, not XML
- Keep existing direct certifier path
3. ProviderService - NEEDS RPAfelApi PROVIDER β οΈβ
Current Issue:
- Only has
digifactandinfile - No
rpafelapiprovider
Required Changes:
// apps/backend/src/fel/application/provider.service.ts
import { ProviderRpaFelApiService } from '@/fel/infrastructure/provider-rpafelapi.service';
@Injectable()
export class ProviderService {
constructor(
private readonly providerDigifactService: ProviderDigifactService,
private readonly providerInfileService: ProviderInfileService,
private readonly providerRpaFelApiService: ProviderRpaFelApiService, // Add this
) {
this.providerMap = {
digifact: this.providerDigifactService,
infile: this.providerInfileService,
rpafelapi: this.providerRpaFelApiService, // Add this
};
}
}
Why:
- Need to register RPAfelApi provider
- ProviderService routes to correct provider
4. New Services Needed - CREATE NEW FILES β οΈβ
A. ProviderRpaFelApiServiceβ
File: apps/backend/src/fel/infrastructure/provider-rpafelapi.service.ts
Purpose:
- HTTP client for RPAfelApi
- Converts DTE JSON to RPAfelApi format
- Transforms responses
Status: Need to create (see implementation guide)
B. XmlToDteJsonServiceβ
File: apps/backend/src/fel/application/xml-to-dte-json.service.ts
Purpose:
- Converts
Invoiceobject to DTE JSON structure - Handles all document types (FACT, NCRE, NDEB, ANULACION)
- Maps fields correctly
Status: Need to create (see implementation guide)
5. FelModule - NEEDS NEW PROVIDERS β οΈβ
Required Changes:
// apps/backend/src/fel/fel.modules.ts
import { ProviderRpaFelApiService } from '@/fel/infrastructure/provider-rpafelapi.service';
import { XmlToDteJsonService } from '@/fel/application/xml-to-dte-json.service';
@Module({
// ... existing imports ...
providers: [
FelService,
ProviderService,
XmlConversionService,
XmlToDteJsonService, // Add this
ProviderDigifactService,
ProviderInfileService,
ProviderRpaFelApiService, // Add this
],
// ...
})
export class FelModule {}
Why:
- Register new services
- Dependency injection
π― Optional Enhancements (Not Required, But Recommended)β
1. New Controller Endpoints - OPTIONAL π‘β
Current: Controller works fine for basic certification
Could Add:
POST /fel/void-certificate- Void a certificatePOST /fel/query-payer-info- Query payer informationPOST /fel/send-email- Send FEL document via email
Why Optional:
- RPAfelApi has these endpoints
- But you might not need them in FlowPOS
- Can add later if needed
Example:
// apps/backend/src/fel/interfaces/fel.controller.ts
@Post('void-certificate')
async voidCertificate(@Body() request: VoidCertificateDto): Promise<any> {
// Call RPAfelApi void endpoint
// Or implement directly
}
@Post('query-payer-info')
async queryPayerInfo(@Body() request: QueryPayerInfoDto): Promise<any> {
// Call RPAfelApi query endpoint
// Or implement directly
}
2. Extended Invoice Interface for Credit/Debit Notes - RECOMMENDED π‘β
Current: Basic Invoice interface
Could Add:
- Separate interfaces for each document type
- Better type safety
Example:
// Base interface
export interface BaseFelDocument {
businessData: { id: string };
felProviderData: { providerName: string };
generalData: { type: FelDocumentType; dateTimeIssue: string; currencyCode: string };
issuerData: { /* ... */ };
receiverData: { /* ... */ };
phrases: Phrase[];
addendumData: { /* ... */ };
}
// Specific interfaces
export interface Invoice extends BaseFelDocument {
generalData: { type: 'FACT'; /* ... */ };
items: InvoiceItem[];
total: number;
}
export interface CreditNote extends BaseFelDocument {
generalData: { type: 'NCRE'; /* ... */ };
originalInvoiceUuid: string;
adjustmentReason: string;
items: InvoiceItem[];
total: number;
}
export interface DebitNote extends BaseFelDocument {
generalData: { type: 'NDEB'; /* ... */ };
originalInvoiceUuid: string;
adjustmentReason: string;
items: InvoiceItem[];
total: number;
}
export interface Cancellation extends BaseFelDocument {
generalData: { type: 'ANULACION'; /* ... */ };
originalInvoiceUuid: string;
cancellationReason: string;
cancellationDate: string;
originalInvoiceDate: string;
}
// Union type
export type FelDocument = Invoice | CreditNote | DebitNote | Cancellation;
Why Optional:
- More type safety
- Better IDE support
- Clearer intent
- But adds complexity
π Summary: Required vs Optionalβ
β Required Updatesβ
- Invoice Interface - Add optional fields for NCRE/NDEB/ANULACION
- FelService - Add routing logic for RPAfelApi
- ProviderService - Register RPAfelApi provider
- ProviderRpaFelApiService - Create new service
- XmlToDteJsonService - Create new service
- FelModule - Register new services
π‘ Optional Enhancementsβ
- New Controller Endpoints - Void, query, email
- Extended Interfaces - Separate types for each document
- Validation Service - SAT rules validation
- Cache Service - (RPAfelApi handles this, but could add local cache)
π― Answer to Your Questionβ
"Do we need to update the logic or structures?"β
YES, but minimal:
- Controller - β NO CHANGES (works fine as-is)
- Invoice Interface - β οΈ MINOR UPDATE (add optional fields)
- FelService - β οΈ ADD ROUTING (new method for RPAfelApi)
- ProviderService - β οΈ REGISTER PROVIDER (one line)
- New Services - β οΈ CREATE 2 NEW FILES (provider + converter)
- FelModule - β οΈ REGISTER SERVICES (add to providers array)
"The fel.controller is currently working fine. Do you think it needs to be modified or updated?"β
NO, controller is fine! β
Why:
- It accepts
Invoiceobject (works for all document types) - It calls
felService.certifyDocument(service handles routing) - Service layer abstraction means controller doesn't need to know about RPAfelApi
- Error handling is already good
Only add new endpoints if you need:
- Void certificate functionality
- Query payer info
- Send email
π Implementation Priorityβ
Phase 1: Core Integration (Required)β
- β Extend Invoice interface
- β Create ProviderRpaFelApiService
- β Create XmlToDteJsonService
- β Update FelService with routing
- β Update ProviderService
- β Update FelModule
Phase 2: Testingβ
- β Test with FACT documents
- β Test with NCRE documents
- β Test with NDEB documents
- β Test with ANULACION documents
Phase 3: Optional Enhancementsβ
- π‘ Add new controller endpoints (if needed)
- π‘ Add extended interfaces (if wanted)
- π‘ Add validation service (if needed)
π‘ Key Insightβ
The beauty of your architecture:
- Controller doesn't need to change because service layer handles routing
- Database is already ready (migration done)
- Just need to add RPAfelApi as another provider option
- Existing code continues to work
This is a clean integration! π
Last Updated: 2025-12-18
Status: Analysis Complete - Ready for Implementation