diff --git a/apps/api/src/bank-transactions-file/bank-transactions-file.controller.ts b/apps/api/src/bank-transactions-file/bank-transactions-file.controller.ts index a30cbb7d7..563f8b759 100644 --- a/apps/api/src/bank-transactions-file/bank-transactions-file.controller.ts +++ b/apps/api/src/bank-transactions-file/bank-transactions-file.controller.ts @@ -53,15 +53,18 @@ export class BankTransactionsFileController { throw new NotFoundException('No person record with keycloak ID: ' + keycloakId) } - const allMovementsFromAllFiles: { payment: CreateManyBankPaymentsDto; paymentRef: string }[][] = - [] - let accountMovementsFromFile: { payment: CreateManyBankPaymentsDto; paymentRef: string }[] = [] + const allMovementsFromAllFiles: { payment: CreateManyBankPaymentsDto; paymentRef: string }[] = [] + const allMovementsWithErrors: { payment: CreateManyBankPaymentsDto; paymentRef: string, refError?: string }[] = [] //parse files and save them to S3 const promises = await Promise.all( files.map((file, key) => { - accountMovementsFromFile = parseBankTransactionsFile(file.buffer) - allMovementsFromAllFiles.push(accountMovementsFromFile) + let accountMovementsFromFile: { payment: CreateManyBankPaymentsDto; paymentRef: string, refError?: string }[] = parseBankTransactionsFile(file.buffer) + const validMovements = accountMovementsFromFile.filter(m => !m.refError) + const invalidMovements = accountMovementsFromFile.filter(m => m.refError) + + allMovementsFromAllFiles.push(...validMovements) + allMovementsWithErrors.push(...invalidMovements); const filesType = body.types return this.bankTransactionsFileService.create( Array.isArray(filesType) ? filesType[key] : filesType, @@ -76,25 +79,25 @@ export class BankTransactionsFileController { //now import the parsed donations const donations: CreateManyBankPaymentsDto[] = [] - for (const fileOfBankMovements of allMovementsFromAllFiles) { - for await (const movement of fileOfBankMovements) { - const campaign = await this.campaignService.getCampaignByPaymentReference( - movement.paymentRef, - ) - - if (!campaign) { - Logger.warn('No campaign with payment reference: ' + movement.paymentRef) - throw new NotFoundException('No campaign with payment reference: ' + movement.paymentRef) - } + for (const movement of allMovementsFromAllFiles) { + let campaign = await this.campaignService.getCampaignByPaymentReference(movement.paymentRef) + if (!campaign) { + campaign = await this.campaignService.getCampaignBySlug(movement.paymentRef) + } - const vault = await this.vaultService.findByCampaignId(campaign.id) - movement.payment.extPaymentMethodId = 'imported bank payment' - movement.payment.targetVaultId = vault[0].id - movement.payment.type = DonationType.donation - movement.payment.status = DonationStatus.succeeded - movement.payment.provider = PaymentProvider.bank - donations.push(movement.payment) + if (!campaign) { + Logger.warn('No campaign with payment reference: ' + movement.paymentRef) + allMovementsWithErrors.push({...movement, refError: `No campaign with payment reference: ${movement.paymentRef}`}) + continue; } + + const vault = await this.vaultService.findByCampaignId(campaign.id) + movement.payment.extPaymentMethodId = 'imported bank payment' + movement.payment.targetVaultId = vault[0].id + movement.payment.type = DonationType.donation + movement.payment.status = DonationStatus.succeeded + movement.payment.provider = PaymentProvider.bank + donations.push(movement.payment) } await this.donationsService.createManyBankPayments(donations) diff --git a/apps/api/src/bank-transactions-file/helpers/parser.ts b/apps/api/src/bank-transactions-file/helpers/parser.ts index 154c71d95..bbd7cc6b5 100644 --- a/apps/api/src/bank-transactions-file/helpers/parser.ts +++ b/apps/api/src/bank-transactions-file/helpers/parser.ts @@ -4,12 +4,13 @@ import { CreateManyBankPaymentsDto } from '../../donations/dto/create-many-bank- // eslint-disable-next-line @typescript-eslint/no-var-requires export const parseString = require('xml2js').parseString -const regexPaymentRef = /\b[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}\b/g +const uuidPaymentRefRegex = /\b[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}\b/g +const slugPaymentRefRegex = /[*#]{1}(.*)[*#]{1}/g export function parseBankTransactionsFile( fileBuffer, -): { payment: CreateManyBankPaymentsDto; paymentRef: string }[] { - const accountMovements: { payment: CreateManyBankPaymentsDto; paymentRef: string }[] = [] +): { payment: CreateManyBankPaymentsDto; paymentRef: string, refError?: string }[] { + const accountMovements: { payment: CreateManyBankPaymentsDto; paymentRef: string, refError?: string }[] = [] parseString(fileBuffer, function (err, items) { for (const item in items) { for (const movement in items[item].AccountMovement) { @@ -18,7 +19,7 @@ export function parseBankTransactionsFile( items[item].AccountMovement[movement].Status[0].Context[0] === 'success' ) { const payment = new CreateManyBankPaymentsDto() - const paymentRef = items[item].AccountMovement[movement].NarrativeI02[0] + const paymentRef: string = items[item].AccountMovement[movement].NarrativeI02[0] payment.amount = Number(items[item].AccountMovement[movement].Amount[0]) * 100 payment.currency = items[item].AccountMovement[movement].CCY[0] payment.extCustomerId = items[item].AccountMovement[movement].OppositeSideAccount[0] @@ -26,10 +27,19 @@ export function parseBankTransactionsFile( payment.createdAt = new Date(items[item].AccountMovement[movement].PaymentDateTime[0]) payment.billingName = items[item].AccountMovement[movement].OppositeSideName[0] - const matchedRef = paymentRef.replace(/[ _]+/g, '-').match(regexPaymentRef) - if (matchedRef) { - accountMovements.push({ payment, paymentRef: matchedRef[0] }) + const matchedUUIDRef = paymentRef.replace(/[ _]+/g, '-').match(uuidPaymentRefRegex) // leaving it for backwards-compatibility + const matchedSlugRef = paymentRef.match(slugPaymentRefRegex) + let validRef = "" + if (matchedUUIDRef) { + validRef = matchedUUIDRef[0] + } else if (matchedSlugRef) { + validRef = matchedSlugRef[0] + } + + if (validRef) { + accountMovements.push({ payment, paymentRef: validRef }) } else { + accountMovements.push({ payment, paymentRef: paymentRef, refError: 'cannot recognize paymentRef' }) Logger.warn('cannot recognize paymentRef from NarrativeI02 field: ' + paymentRef) } }