@openade/fe
Version:
Fatturazione Elettronica - Electronic Invoicing for Sistema di Interscambio (SDI)
213 lines • 7.77 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BatchProcessor = void 0;
exports.createBatchProcessor = createBatchProcessor;
exports.processInvoiceBatch = processInvoiceBatch;
const invoice_xml_1 = require("./invoice.xml");
const sdicoop_client_1 = require("./sdicoop.client");
class BatchProcessor {
constructor(sdiConfig, batchConfig = {}) {
this.sdiClient = new sdicoop_client_1.SDICOOPClient(sdiConfig);
this.config = {
maxBatchSize: 100,
batchDelay: 1000,
maxConcurrentBatches: 3,
retry: {
maxRetries: 3,
retryDelay: 5000,
backoffMultiplier: 2,
},
...batchConfig,
};
}
async processBatch(items) {
const startTime = Date.now();
const results = [];
const errors = [];
let completed = 0;
let failed = 0;
const batches = this.splitIntoBatches(items);
const totalBatches = batches.length;
if (this.config.onProgress) {
this.config.onProgress({
total: items.length,
completed: 0,
failed: 0,
pending: items.length,
currentBatch: 0,
totalBatches,
percentage: 0,
});
}
const batchPromises = [];
const semaphore = new Semaphore(this.config.maxConcurrentBatches);
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
const batchNumber = i + 1;
const batchPromise = semaphore.acquire().then(async (release) => {
try {
const batchResults = await this.processSingleBatch(batch, batchNumber, totalBatches);
results.push(...batchResults);
for (const result of batchResults) {
if (result.success) {
completed++;
}
else {
failed++;
errors.push({
itemId: result.id,
message: result.error || 'Unknown error',
retryCount: result.retryCount,
timestamp: new Date().toISOString(),
});
}
}
if (this.config.onProgress) {
this.config.onProgress({
total: items.length,
completed,
failed,
pending: items.length - completed - failed,
currentBatch: batchNumber,
totalBatches,
percentage: Math.round(((completed + failed) / items.length) * 100),
estimatedTimeRemaining: this.calculateEstimatedTimeRemaining(startTime, completed + failed, items.length),
});
}
if (batchNumber < totalBatches && this.config.batchDelay > 0) {
await this.delay(this.config.batchDelay);
}
}
finally {
release();
}
});
batchPromises.push(batchPromise);
}
await Promise.all(batchPromises);
const processingTime = Date.now() - startTime;
const successful = results.filter((r) => r.success).length;
return {
total: items.length,
successful,
failed,
processingTime,
results,
summary: {
successRate: items.length > 0 ? (successful / items.length) * 100 : 0,
averageProcessingTime: results.length > 0 ? processingTime / results.length : 0,
errors,
},
};
}
async processSingleBatch(items, _batchNumber, _totalBatches) {
const results = [];
for (const item of items) {
let retryCount = 0;
let success = false;
let result;
let error;
while (retryCount <= this.config.retry.maxRetries && !success) {
try {
const xml = (0, invoice_xml_1.buildInvoiceXML)(item.invoice);
result = await this.sdiClient.sendInvoice(item.filename, xml);
success = result.success;
if (!success) {
error = result.error;
}
}
catch (err) {
error = err instanceof Error ? err.message : 'Unknown error';
}
if (!success && retryCount < this.config.retry.maxRetries) {
retryCount++;
const delay = this.config.retry.retryDelay *
Math.pow(this.config.retry.backoffMultiplier, retryCount - 1);
await this.delay(delay);
}
else {
break;
}
}
results.push({
id: item.id,
success,
result,
error,
retryCount,
});
if (!success && this.config.onError) {
this.config.onError({
itemId: item.id,
message: error || 'Unknown error',
retryCount,
timestamp: new Date().toISOString(),
});
}
}
return results;
}
splitIntoBatches(items) {
const batches = [];
const batchSize = this.config.maxBatchSize;
for (let i = 0; i < items.length; i += batchSize) {
batches.push(items.slice(i, i + batchSize));
}
return batches;
}
calculateEstimatedTimeRemaining(startTime, completed, total) {
if (completed === 0)
return 0;
const elapsed = Date.now() - startTime;
const rate = completed / elapsed;
const remaining = total - completed;
return Math.round(remaining / rate);
}
delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
exports.BatchProcessor = BatchProcessor;
class Semaphore {
constructor(permits) {
this.waiting = [];
this.permits = permits;
}
async acquire() {
return new Promise((resolve) => {
if (this.permits > 0) {
this.permits--;
resolve(() => this.release());
}
else {
this.waiting.push(() => {
this.permits--;
resolve(() => this.release());
});
}
});
}
release() {
this.permits++;
if (this.waiting.length > 0) {
const next = this.waiting.shift();
if (next)
next();
}
}
}
function createBatchProcessor(sdiConfig, batchConfig = {}) {
return new BatchProcessor(sdiConfig, batchConfig);
}
async function processInvoiceBatch(invoices, sdiConfig, batchConfig = {}) {
const processor = createBatchProcessor(sdiConfig, batchConfig);
const items = invoices.map((inv, index) => ({
id: `invoice_${index}_${Date.now()}`,
invoice: inv.invoice,
filename: inv.filename,
channel: inv.channel,
metadata: inv.metadata,
}));
return await processor.processBatch(items);
}
//# sourceMappingURL=batch.processor.js.map