From e4e0653aa1de7a5f167d140d2448a0a1adfe2460 Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 12 Nov 2024 11:37:04 +0100 Subject: [PATCH] feat(invoices): accounting export fixes --- packages/vendure-plugin-invoices/CHANGELOG.md | 6 +++++ packages/vendure-plugin-invoices/package.json | 2 +- .../src/services/accounting.service.ts | 4 +++- .../accounting/xero-uk-export-strategy.ts | 23 +++++++++++-------- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/vendure-plugin-invoices/CHANGELOG.md b/packages/vendure-plugin-invoices/CHANGELOG.md index 59fd0c79..1edcb8e2 100644 --- a/packages/vendure-plugin-invoices/CHANGELOG.md +++ b/packages/vendure-plugin-invoices/CHANGELOG.md @@ -1,3 +1,9 @@ +# 4.1.1 (2024-11-12) + +- Better error message extraction from Xero API response +- Limit search term for looking up contacts in Xero by max 50 characters, to prevent Xero API errors +- Log unknown errors on invoice with timestamp when accounting export fails + # 4.1.0 (2024-08-30) - Exporting credit invoices via accounting strategies now have their own method interface diff --git a/packages/vendure-plugin-invoices/package.json b/packages/vendure-plugin-invoices/package.json index 012c1263..d66b6b36 100644 --- a/packages/vendure-plugin-invoices/package.json +++ b/packages/vendure-plugin-invoices/package.json @@ -1,6 +1,6 @@ { "name": "@vendure-hub/pinelab-invoice-plugin", - "version": "4.1.0", + "version": "4.1.1", "description": "Vendure plugin for PDF invoice generation", "author": "Martijn van de Brug ", "homepage": "https://pinelab-plugins.com/", diff --git a/packages/vendure-plugin-invoices/src/services/accounting.service.ts b/packages/vendure-plugin-invoices/src/services/accounting.service.ts index bde76f7b..da76c073 100644 --- a/packages/vendure-plugin-invoices/src/services/accounting.service.ts +++ b/packages/vendure-plugin-invoices/src/services/accounting.service.ts @@ -151,7 +151,9 @@ export class AccountingService implements OnModuleInit { } catch (e) { await invoiceRepository.update(invoice.id, { accountingReference: { - errorMessage: (e as Error)?.message, + errorMessage: + (e as Error)?.message || + `Unknown error occured at ${new Date().toISOString()}`, }, }); throw e; diff --git a/packages/vendure-plugin-invoices/src/strategies/accounting/xero-uk-export-strategy.ts b/packages/vendure-plugin-invoices/src/strategies/accounting/xero-uk-export-strategy.ts index 829e9ad0..765ddffa 100644 --- a/packages/vendure-plugin-invoices/src/strategies/accounting/xero-uk-export-strategy.ts +++ b/packages/vendure-plugin-invoices/src/strategies/accounting/xero-uk-export-strategy.ts @@ -217,7 +217,7 @@ export class XeroUKExportStrategy implements AccountingExportStrategy { order, invoice, isCreditInvoiceFor.invoiceNumber - ) || `Credit note for ${isCreditInvoiceFor}`; + ) || `Credit note for ${isCreditInvoiceFor.invoiceNumber}`; const creditNote: import('xero-node').CreditNote = { creditNoteNumber: `${invoice.invoiceNumber} (CN)`, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -268,7 +268,7 @@ export class XeroUKExportStrategy implements AccountingExportStrategy { ): Promise { await this.tokenCache.value(); // Always get a token before making a request // Find by contact name first - const contacName = this.getNormalizedContactName(customer, companyName); + const contactName = this.getNormalizedContactName(customer, companyName); let contacts = await this.xero.accountingApi.getContacts( this.tenantId, undefined, @@ -278,7 +278,7 @@ export class XeroUKExportStrategy implements AccountingExportStrategy { undefined, undefined, undefined, - this.getNormalizedContactName(customer, companyName) + contactName ); if (!contacts.body.contacts?.length) { // If no contacts, try to find by email @@ -321,7 +321,7 @@ export class XeroUKExportStrategy implements AccountingExportStrategy { ); const createdContact = createdContacts.body.contacts?.[0]; Logger.info( - `No contact found with name '${contacName}' or email '${customer.emailAddress}'. Created new contact with email "${createdContact?.emailAddress}" (${createdContact?.contactID})`, + `No contact found with name '${contactName}' or email '${customer.emailAddress}'. Created new contact with email "${createdContact?.emailAddress}" (${createdContact?.contactID})`, loggerCtx ); return createdContacts.body.contacts![0]; @@ -345,10 +345,14 @@ export class XeroUKExportStrategy implements AccountingExportStrategy { /** * Get the readable error message from the Xero response */ - private getErrorMessage(err: any): string { + private getErrorMessage(err: string): string { + const errorObj = JSON.parse(err); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return ( - JSON.parse(err)?.response?.body?.Elements?.[0]?.ValidationErrors?.[0] - ?.Message || JSON.parse(err)?.response?.body?.Message + errorObj?.response?.body?.Elements?.[0]?.ValidationErrors?.[0]?.Message || + errorObj?.response?.body?.Message || + errorObj?.response?.body || + errorObj?.body ); } @@ -426,7 +430,8 @@ export class XeroUKExportStrategy implements AccountingExportStrategy { /** * Get the normalized contact name. Uses company or other wise customers full name. - * Trims and replaces duplicate spaces/tabs/newlines + * Trims and replaces duplicate spaces/tabs/newlines. + * Shortens to max 50 characters, because that is the max allowed for the Xero API */ private getNormalizedContactName( customer: Customer, @@ -435,7 +440,7 @@ export class XeroUKExportStrategy implements AccountingExportStrategy { const contactName = companyName || [customer.firstName, customer.lastName].filter(Boolean).join(' '); - return contactName.trim().replace(/\s\s+/g, ' '); + return contactName.trim().replace(/\s\s+/g, ' ').substring(0, 50); } private getTaxType(