From 248affc0f95be5ccb5f5c381a76de6f354a5a30c Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Wed, 22 Jul 2026 10:51:34 -0500 Subject: [PATCH] fix doc numer increase --- qbo_helper.js | 6 ++-- src/services/qbo-service.js | 58 +++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/qbo_helper.js b/qbo_helper.js index ec248ce..6ccb232 100644 --- a/qbo_helper.js +++ b/qbo_helper.js @@ -197,8 +197,10 @@ async function makeQboApiCall(requestOptions) { await doRefresh(); return await client.makeApiCall(requestOptions); } - throw new Error(`QBO API Error ${qboFault.code}: ${qboFault.message}${qboFault.detail ? ' - ' + qboFault.detail : ''}`); - } + const err = new Error(`QBO API Error ${qboFault.code}: ${qboFault.message}${qboFault.detail ? ' - ' + qboFault.detail : ''}`); + err.qboCode = qboFault.code; + err.qboFault = qboFault; + throw err; } // ── Kein saveTokens() hier – Token hat sich nicht geändert ── return response; diff --git a/src/services/qbo-service.js b/src/services/qbo-service.js index d3ac838..38ede43 100644 --- a/src/services/qbo-service.js +++ b/src/services/qbo-service.js @@ -82,36 +82,50 @@ async function exportInvoiceToQbo(invoiceId, dbClient) { // <-- Nutzt jetzt dbCl }; let qboInvoice = null; + const MAX_RETRIES = 20; - for (let attempt = 0; attempt < 5; attempt++) { + for (let attempt = 0; attempt < MAX_RETRIES; attempt++) { console.log(`📤 QBO Export Invoice (DocNumber: ${qboPayload.DocNumber})...`); - const response = await makeQboApiCall({ - url: `${baseUrl}/v3/company/${companyId}/invoice`, - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(qboPayload) - }); + try { + const response = await makeQboApiCall({ + url: `${baseUrl}/v3/company/${companyId}/invoice`, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(qboPayload) // innerhalb der Schleife → aktuelle DocNumber + }); - const data = response.getJson ? response.getJson() : response.json; + const data = response.getJson ? response.getJson() : response.json; - if (data.Fault?.Error?.[0]?.code === '6140') { - console.log(` ⚠️ DocNumber ${qboPayload.DocNumber} exists, retrying...`); - qboPayload.DocNumber = (parseInt(qboPayload.DocNumber) + 1).toString(); - continue; + // Fault-Fallback, falls makeQboApiCall doch mal nicht wirft + const fault = extractQboFault(data); + if (fault) { + const err = new Error(`QBO API Error ${fault.code}: ${fault.message}`); + err.qboCode = fault.code; + throw err; + } + + qboInvoice = data.Invoice || data; + if (qboInvoice.Id) break; + + throw new Error("QBO returned no ID: " + JSON.stringify(data).substring(0, 500)); + + } catch (err) { + if (err.qboCode === '6140') { + const oldNum = qboPayload.DocNumber; + qboPayload.DocNumber = (parseInt(oldNum) + 1).toString(); + console.log(` ⚠️ DocNumber ${oldNum} belegt (evtl. voided), versuche ${qboPayload.DocNumber}...`); + qboInvoice = null; + continue; + } + console.error(`❌ QBO Export Fault:`, err.message); + throw err; } - if (data.Fault) { - const errMsg = data.Fault.Error?.map(e => `${e.code}: ${e.Message} - ${e.Detail}`).join('; ') || JSON.stringify(data.Fault); - console.error(`❌ QBO Export Fault:`, errMsg); - throw new Error('QBO export failed: ' + errMsg); - } - qboInvoice = data.Invoice || data; - if (qboInvoice.Id) break; - - throw new Error("QBO returned no ID: " + JSON.stringify(data).substring(0, 500)); } - if (!qboInvoice?.Id) throw new Error('Could not find free DocNumber after 5 attempts.'); + if (!qboInvoice?.Id) { + throw new Error(`Keine freie DocNumber nach ${MAX_RETRIES} Versuchen (ab ${nextDocNumber}).`); + } await dbClient.query( 'UPDATE invoices SET qbo_id = $1, qbo_sync_token = $2, qbo_doc_number = $3, invoice_number = $4 WHERE id = $5',