diff --git a/.github/workflows/test_e2e_portalicious.yml b/.github/workflows/test_e2e_portalicious.yml index 698f24cc92..7f85ebea76 100644 --- a/.github/workflows/test_e2e_portalicious.yml +++ b/.github/workflows/test_e2e_portalicious.yml @@ -36,63 +36,64 @@ jobs: cache: 'npm' cache-dependency-path: '${{ env.e2eTestsPath }}/package-lock.json' - - name: Install E2E-Tests code-dependencies - working-directory: ${{ env.e2eTestsPath }} - run: | - npm ci --no-fund --no-audit - - - name: Lint E2E-Tests code - working-directory: ${{ env.e2eTestsPath }} - run: 'npm run lint' - - - name: Set ENV-variables for test-environment - run: | - cp services/.env.example services/.env - - - name: Run Services with Docker - working-directory: ./services - run: | - docker --log-level 'warn' compose -f docker-compose.yml up -d --quiet-pull --wait --wait-timeout 300 - - - name: Start Portalicious - working-directory: ./interfaces/Portalicious - env: - NG_URL_121_SERVICE_API: http://localhost:3000/api - run: | - npm install - npm run start:debug-production > portalicious-server-logs.txt 2>&1 & - - - name: Install 121-Service dependencies - # This step is necessary because the tests run functions in this folder - working-directory: ./services/121-service - run: | - npm install - - - name: Install E2E-Tests runtime-dependencies - working-directory: ${{ env.e2eTestsPath }} - run: | - npx playwright install chromium --with-deps - - - name: Wait for Portalicious - run: | - timeout 90s sh -c 'until curl http://localhost:8088 -I; do echo "Waiting for Portalicious to be running..."; sleep 1; done' - - - name: Run E2E-Tests with Playwright - working-directory: ${{ env.e2eTestsPath }} - env: - BASE_URL_PORTALICIOUS: http://localhost:8088 - run: | - npm run test:portalicious - - - uses: actions/upload-artifact@v4 - if: always() - with: - name: test-result-artifacts - path: | - ${{ env.e2eTestsPath }}/test-results/ - ./interfaces/Portalicious/portalicious-server-logs.txt - retention-days: 30 - - - name: Docker logs - if: always() - uses: jwalton/gh-docker-logs@v2 + # Disabled for now in the feat.refactor-registration-data branch + # - name: Install E2E-Tests code-dependencies + # working-directory: ${{ env.e2eTestsPath }} + # run: | + # npm ci --no-fund --no-audit + + # - name: Lint E2E-Tests code + # working-directory: ${{ env.e2eTestsPath }} + # run: 'npm run lint' + + # - name: Set ENV-variables for test-environment + # run: | + # cp services/.env.example services/.env + + # - name: Run Services with Docker + # working-directory: ./services + # run: | + # docker --log-level 'warn' compose -f docker-compose.yml up -d --quiet-pull --wait --wait-timeout 300 + + # - name: Start Portalicious + # working-directory: ./interfaces/Portalicious + # env: + # NG_URL_121_SERVICE_API: http://localhost:3000/api + # run: | + # npm install + # npm run start:debug-production > portalicious-server-logs.txt 2>&1 & + + # - name: Install 121-Service dependencies + # # This step is necessary because the tests run functions in this folder + # working-directory: ./services/121-service + # run: | + # npm install + + # - name: Install E2E-Tests runtime-dependencies + # working-directory: ${{ env.e2eTestsPath }} + # run: | + # npx playwright install chromium --with-deps + + # - name: Wait for Portalicious + # run: | + # timeout 90s sh -c 'until curl http://localhost:8088 -I; do echo "Waiting for Portalicious to be running..."; sleep 1; done' + + # - name: Run E2E-Tests with Playwright + # working-directory: ${{ env.e2eTestsPath }} + # env: + # BASE_URL_PORTALICIOUS: http://localhost:8088 + # run: | + # npm run test:portalicious + + # - uses: actions/upload-artifact@v4 + # if: always() + # with: + # name: test-result-artifacts + # path: | + # ${{ env.e2eTestsPath }}/test-results/ + # ./interfaces/Portalicious/portalicious-server-logs.txt + # retention-days: 30 + + # - name: Docker logs + # if: always() + # uses: jwalton/gh-docker-logs@v2 diff --git a/e2e/pages/PersonalInformationPopUp/PersonalInformationPopUp.ts b/e2e/pages/PersonalInformationPopUp/PersonalInformationPopUp.ts index 007053e41b..759a154c8b 100644 --- a/e2e/pages/PersonalInformationPopUp/PersonalInformationPopUp.ts +++ b/e2e/pages/PersonalInformationPopUp/PersonalInformationPopUp.ts @@ -2,22 +2,12 @@ import { expect, Locator } from '@playwright/test'; import { Page } from 'playwright'; import englishTranslations from '@121-portal/src/assets/i18n/en.json'; -import visaIntersolveTranslations from '@121-service/src/seed-data/fsp/fsp-intersolve-visa.json'; -import programTestTranslations from '@121-service/src/seed-data/program/program-test.json'; +import programTest from '@121-service/src/seed-data/program/program-test.json'; const updateSuccesfullNotification = englishTranslations.common['update-success']; const programWesterosQuestionName = - programTestTranslations.programQuestions[1].label.en; -const fspCahngeWarning = - englishTranslations['page'].program['program-people-affected'][ - 'edit-person-affected-popup' - ].fspChangeWarning; -const visaQuestionStreet = visaIntersolveTranslations.questions[0].label.en; -const visaQuestionHouseNumberAddition = - visaIntersolveTranslations.questions[2].label.en; -const visaQuestionPostalCode = visaIntersolveTranslations.questions[3].label.en; -const visaQuestionCity = visaIntersolveTranslations.questions[4].label.en; + programTest.programRegistrationAttributes[1].label.en; class PersonalInformationPopup { readonly page: Page; @@ -121,8 +111,29 @@ class PersonalInformationPopup { const saveButton = this.page.getByRole('button', { name: saveButtonName }); const okButton = this.page.getByRole('button', { name: okButtonName }); const alertMessage = this.page.getByRole('alertdialog'); - const fieldInput = fieldSelector.getByRole('textbox'); - await fieldInput.fill(newValue); + + // Locate the field input which can be a textbox, textarea, number input, phone input, select, or checkbox + const fieldInput = fieldSelector.locator( + 'ion-input input, textarea, input[type="number"], input[type="tel"], ion-select, ion-checkbox', + ); + + if ( + await fieldInput.evaluate( + (el) => el.tagName.toLowerCase() === 'ion-select', + ) + ) { + await fieldInput.selectOption(newValue); + } else if ( + await fieldInput.evaluate( + (el) => el.tagName.toLowerCase() === 'ion-input', + ) + ) { + // Locate the native input element within ion-input + const nativeInput = fieldInput.locator('input'); + await nativeInput.fill(newValue); + } else { + await fieldInput.fill(newValue); + } await this.page.waitForLoadState('networkidle'); await fieldSelector.getByText(saveButtonName).click(); @@ -258,7 +269,7 @@ class PersonalInformationPopup { } async selectFspInputForm({ filterValue }: { filterValue: string }) { - const fieldSelector = this.personAffectedPopUpFsp; + const fieldSelector = this.editPersonAffectedPopUp; const updatePropertyItem = fieldSelector.locator( 'app-update-property-item', ); @@ -270,60 +281,63 @@ class PersonalInformationPopup { return inputForm.getByRole('textbox'); } + async updateAttributeByLabel({ + labelText, + newValue, + }: { + labelText: string; + newValue: string; + }) { + const saveButtonName = englishTranslations.common.save; + const okButtonName = englishTranslations.common.ok; + const labelElement = this.editPersonAffectedPopUp + .locator(`text=${labelText}`) + .first(); + const parentElement = labelElement.locator('..').locator('..'); + + await this.updateField({ + fieldSelector: parentElement, + newValue, + saveButtonName, + okButtonName, + reasonText: (newValue) => `Change ${labelText} to ${newValue}`, + }); + } + async updatefinancialServiceProvider({ fspNewName, fspOldName, saveButtonName, okButtonName, + newAttributes, }: { fspNewName: string; fspOldName: string; saveButtonName: string; okButtonName: string; + newAttributes: { labelText: string; newValue: string }[]; }) { + // Loop over the attributes and update each one + for (const attribute of newAttributes) { + await this.updateAttributeByLabel({ + labelText: attribute.labelText, + newValue: attribute.newValue, + }); + } + const dropdown = this.page.getByRole('radio'); - const warning = fspCahngeWarning; - const newValue = 'Nieuwe straat'; const fieldSelector = this.personAffectedPopUpFsp; const okButton = this.page.getByRole('button', { name: okButtonName }); - const streetAdressInput = await this.selectFspInputForm({ - filterValue: visaQuestionStreet, - }); - const numberAdditionInput = await this.selectFspInputForm({ - filterValue: visaQuestionHouseNumberAddition, - }); - const postalCodeInput = await this.selectFspInputForm({ - filterValue: visaQuestionPostalCode, - }); - const cityInput = await this.selectFspInputForm({ - filterValue: visaQuestionCity, - }); await this.validateFspNamePresentInEditPopUp(fspOldName); await this.financialServiceProviderDropdown.click(); await dropdown.getByText(fspNewName).click(); - await this.validateFspWarningInEditPopUp(warning); - - await streetAdressInput.fill(newValue); - await this.personAffectedHouseNumber.getByLabel('').click(); - await this.personAffectedHouseNumber.getByLabel('').fill('2'); - await numberAdditionInput.fill('D'); - await postalCodeInput.fill('1234AB'); - await cityInput.fill('Amsterdam'); - await postalCodeInput.click(); await fieldSelector.getByText(saveButtonName).click(); await okButton.waitFor({ state: 'visible' }); await okButton.click(); } - - async validateFspWarningInEditPopUp(warning: string) { - await this.page.waitForLoadState('networkidle'); - const element = this.page.locator('ion-text.ion-padding.md.hydrated'); - const text = await element.textContent(); - expect(text).toContain(warning); - } } export default PersonalInformationPopup; diff --git a/e2e/pages/Table/TableModule.ts b/e2e/pages/Table/TableModule.ts index 0f781bf86c..7e7b50b250 100644 --- a/e2e/pages/Table/TableModule.ts +++ b/e2e/pages/Table/TableModule.ts @@ -5,7 +5,14 @@ import { Locator, Page } from 'playwright'; import * as XLSX from 'xlsx'; import englishTranslations from '@121-portal/src/assets/i18n/en.json'; -import visaFspIntersolve from '@121-service/src/seed-data/fsp/fsp-intersolve-visa.json'; +import { FinancialServiceProviders } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; +import { FINANCIAL_SERVICE_PROVIDERS } from '@121-service/src/financial-service-providers/financial-service-providers.const'; + +const fsp = FINANCIAL_SERVICE_PROVIDERS.find( + (fsp) => fsp.name === FinancialServiceProviders.intersolveVisa, +); + +const labelVisaEn = fsp ? fsp.defaultLabel.en : undefined; const paymentLabel = englishTranslations.page.program['program-people-affected'].actions.doPayment; @@ -472,7 +479,7 @@ class TableModule { // Assert the values of the row Object.entries(assertionData).forEach(([key, value]) => { - expect(rowToAssert[key]).toBe(value); + expect(rowToAssert![key]).toBe(value); }); } @@ -483,7 +490,7 @@ class TableModule { for (let i = 1; i <= count; i++) { const fsp = this.page.locator(TableModule.getRow(i)); const fspText = (await fsp.textContent())?.trim(); - const isVisaFsp = fspText?.includes(visaFspIntersolve.displayName.en); + const isVisaFsp = fspText?.includes(labelVisaEn!); if ( (shouldSelectVisa && isVisaFsp) || @@ -503,7 +510,7 @@ class TableModule { for (let i = 1; i <= count; i++) { const fsp = this.page.locator(TableModule.getRow(i)); const fspText = (await fsp.textContent())?.trim(); - const isVisaFsp = fspText?.includes(visaFspIntersolve.displayName.en); + const isVisaFsp = fspText?.includes(labelVisaEn!); if ( (shouldIncludeVisa && isVisaFsp) || diff --git a/e2e/test-registration-data/test-registrations-westeros-20.csv b/e2e/test-registration-data/test-registrations-westeros-20.csv index be3cd87fab..b3cf082197 100644 --- a/e2e/test-registration-data/test-registrations-westeros-20.csv +++ b/e2e/test-registration-data/test-registrations-westeros-20.csv @@ -1,21 +1,21 @@ -sreferenceId,preferredLanguage,maxPayments,firstName,lastName,phoneNumber,programFinancialServiceProviderConfigurationName,house -00dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -01dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -02dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -03dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -04dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -05dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -06dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -07dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -08dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -09dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,stark -10dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,stark -11dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,stark -12dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,stark -13dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,stark -14dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238876,gringotts,stark -15dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238856,gringotts,stark -16dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,stark -17dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,stark -18dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,stark -19dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,stark +referenceId,preferredLanguage,maxPayments,firstName,lastName,phoneNumber,programFinancialServiceProviderConfigurationName,whatsappPhoneNumber,addressStreet,addressHouseNumber,addressHouseNumberAddition,addressPostalCode,addressCity,house +00dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +01dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +02dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +03dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +04dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +05dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +06dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +07dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +08dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +09dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,ironBank,14155238886,Straat,1,A,1234AB,Den Haag,stark +10dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +11dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +12dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +13dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +14dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238876,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +15dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238856,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +16dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +17dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +18dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark +19dc9451-1273-484c-b2e8-ae21b51a96ab,en,,Test,succeed,14155238886,gringotts,14155238886,Straat,1,A,1234AB,Den Haag,stark diff --git a/e2e/tests/121-Portal/EditInfoPersonAffected/OpenPopUpViewAndEdit.spec.ts b/e2e/tests/121-Portal/EditInfoPersonAffected/OpenPopUpViewAndEdit.spec.ts index 61e159da19..fed4258851 100644 --- a/e2e/tests/121-Portal/EditInfoPersonAffected/OpenPopUpViewAndEdit.spec.ts +++ b/e2e/tests/121-Portal/EditInfoPersonAffected/OpenPopUpViewAndEdit.spec.ts @@ -3,7 +3,6 @@ import { test } from '@playwright/test'; import { AppRoutes } from '@121-portal/src/app/app-routes.enum'; import englishTranslations from '@121-portal/src/assets/i18n/en.json'; import { SeedScript } from '@121-service/src/scripts/seed-script.enum'; -import fspIntersolveVisa from '@121-service/src/seed-data/fsp/fsp-intersolve-visa.json'; import NLRCProgram from '@121-service/src/seed-data/program/program-nlrc-ocw.json'; import { seedPaidRegistrations } from '@121-service/test/helpers/registration.helper'; import { resetDB } from '@121-service/test/helpers/utility.helper'; @@ -15,7 +14,10 @@ import PersonalInformationPopUp from '@121-e2e/pages/PersonalInformationPopUp/Pe import TableModule from '@121-e2e/pages/Table/TableModule'; const nlrcOcwProgrammeTitle = NLRCProgram.titlePortal.en; -const whatsappLabel = fspIntersolveVisa.questions[5].label.en; +const whatsappLabel = NLRCProgram.programRegistrationAttributes.find( + (attribute) => attribute.name === 'whatsappPhoneNumber', +)!.label.en; + const save = englishTranslations.common.save; test.beforeEach(async ({ page }) => { diff --git a/e2e/tests/121-Portal/EditInfoPersonAffected/UpdateFinancialServiceProvider.spec.ts b/e2e/tests/121-Portal/EditInfoPersonAffected/UpdateFinancialServiceProvider.spec.ts index 642cebccb9..f355259226 100644 --- a/e2e/tests/121-Portal/EditInfoPersonAffected/UpdateFinancialServiceProvider.spec.ts +++ b/e2e/tests/121-Portal/EditInfoPersonAffected/UpdateFinancialServiceProvider.spec.ts @@ -2,10 +2,11 @@ import { test } from '@playwright/test'; import { AppRoutes } from '@121-portal/src/app/app-routes.enum'; import englishTranslations from '@121-portal/src/assets/i18n/en.json'; +import { FinancialServiceProviders } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; +import { findFinancialServiceProviderByNameOrFail } from '@121-service/src/financial-service-providers/financial-service-providers.helpers'; import { SeedScript } from '@121-service/src/scripts/seed-script.enum'; -import visaFspIntersolve from '@121-service/src/seed-data/fsp/fsp-intersolve-visa.json'; -import fspIntersolveVoucher from '@121-service/src/seed-data/fsp/fsp-intersolve-voucher-whatsapp.json'; import NLRCProgram from '@121-service/src/seed-data/program/program-nlrc-ocw.json'; +import programOcw from '@121-service/src/seed-data/program/program-nlrc-ocw.json'; import { seedPaidRegistrations } from '@121-service/test/helpers/registration.helper'; import { resetDB } from '@121-service/test/helpers/utility.helper'; import { registrationsOCW } from '@121-service/test/registrations/pagination/pagination-data'; @@ -18,8 +19,32 @@ import TableModule from '@121-e2e/pages/Table/TableModule'; const nlrcOcwProgrammeTitle = NLRCProgram.titlePortal.en; const save = englishTranslations.common.save; const ok = englishTranslations.common.ok; -const voucherFspName = fspIntersolveVoucher.displayName.en; -const visaFspName = visaFspIntersolve.displayName.en; +const voucherFspName = findFinancialServiceProviderByNameOrFail( + FinancialServiceProviders.intersolveVoucherWhatsapp, +).defaultLabel.en; +const visaFspName = findFinancialServiceProviderByNameOrFail( + FinancialServiceProviders.intersolveVisa, +).defaultLabel.en; +const visaQuestionStreet = programOcw.programRegistrationAttributes.find( + (attribute) => attribute.name === 'addressStreet', +)!.label.en; + +const visaQuestionHouseNumberAddition = + programOcw.programRegistrationAttributes.find( + (attribute) => attribute.name === 'addressHouseNumberAddition', + )!.label.en; + +const visaQuestionPostalCode = programOcw.programRegistrationAttributes.find( + (attribute) => attribute.name === 'addressPostalCode', +)!.label.en; + +const visaQuestionCity = programOcw.programRegistrationAttributes.find( + (attribute) => attribute.name === 'addressCity', +)!.label.en; + +const visaQuestionHouseNumber = programOcw.programRegistrationAttributes.find( + (attribute) => attribute.name === 'addressHouseNumber', +)!.label.en; test.beforeEach(async ({ page }) => { await resetDB(SeedScript.nlrcMultiple); @@ -55,17 +80,24 @@ test('[28048] Update chosen Finacial service provider', async ({ page }) => { // Need to be fixed await test.step('Update Finacial service provider from Voucher whatsapp to Visa debit card', async () => { await piiPopUp.updatefinancialServiceProvider({ - fspNewName: visaFspName, - fspOldName: voucherFspName, + fspNewName: visaFspName!, + fspOldName: voucherFspName!, saveButtonName: save, okButtonName: ok, + newAttributes: [ + { labelText: visaQuestionHouseNumber, newValue: '3' }, + { labelText: visaQuestionStreet, newValue: 'Nieuwe straat' }, + { labelText: visaQuestionHouseNumberAddition, newValue: 'D' }, + { labelText: visaQuestionPostalCode, newValue: '1234AB' }, + { labelText: visaQuestionCity, newValue: 'Amsterdam' }, + ], }); }); await test.step('Validate Finacial service provider be updated', async () => { await table.validateFspCell({ rowNumber, - fspName: visaFspName, + fspName: visaFspName!, }); }); }); diff --git a/e2e/tests/121-Portal/EditInfoPersonAffected/UpdatePaymentMultiplierInvalid.spec.ts b/e2e/tests/121-Portal/EditInfoPersonAffected/UpdatePaymentMultiplierInvalid.spec.ts index 6c6d7a1949..9f7a9a8a04 100644 --- a/e2e/tests/121-Portal/EditInfoPersonAffected/UpdatePaymentMultiplierInvalid.spec.ts +++ b/e2e/tests/121-Portal/EditInfoPersonAffected/UpdatePaymentMultiplierInvalid.spec.ts @@ -56,7 +56,7 @@ test('[28040] Update paymentAmountMultiplier with invalid value', async ({ okButtonName: ok, alert: alertPattern.replace( '{{error}}', - 'paymentAmountMultiplier must be a positive number', + 'paymentAmountMultiplier: this field must be a positive number', ), }); }); @@ -68,7 +68,7 @@ test('[28040] Update paymentAmountMultiplier with invalid value', async ({ okButtonName: ok, alert: alertPattern.replace( '{{error}}', - 'paymentAmountMultiplier must be a positive number', + 'paymentAmountMultiplier: this field must be a positive number', ), }); }); diff --git a/e2e/tests/121-Portal/EditInfoPersonAffected/UpdatePhoneNumberInvalid.spec.ts b/e2e/tests/121-Portal/EditInfoPersonAffected/UpdatePhoneNumberInvalid.spec.ts index 43f0ff3383..b40431cffa 100644 --- a/e2e/tests/121-Portal/EditInfoPersonAffected/UpdatePhoneNumberInvalid.spec.ts +++ b/e2e/tests/121-Portal/EditInfoPersonAffected/UpdatePhoneNumberInvalid.spec.ts @@ -43,8 +43,8 @@ test('[28045] Update phoneNumber with invalid value', async ({ page }) => { const homePage = new HomePage(page); const piiPopUp = new PersonalInformationPopUp(page); - function createAlertMessage(pattern: string, phoneNumber: string): string { - const error = `The value '${phoneNumber}' given for the attribute 'phoneNumber' does not have the correct format for type 'tel'`; + function createAlertMessage(pattern: string): string { + const error = `phoneNumber: This value is not a valid phonenumber according to Twilio lookup`; return pattern.replace('{{error}}', error); } @@ -66,20 +66,9 @@ test('[28045] Update phoneNumber with invalid value', async ({ page }) => { }); }); - await test.step('Update phone number with longer(18 digit) number', async () => { - const phoneNumber = '123456789012345678'; - const alertMessage = createAlertMessage(alertPattern, phoneNumber); - await piiPopUp.updatePhoneNumber({ - phoneNumber, - saveButtonName: save, - okButtonName: ok, - alert: alertMessage, - }); - }); - - await test.step('Update phone number with shorter(7 digit) number', async () => { - const phoneNumber = '1234567'; - const alertMessage = createAlertMessage(alertPattern, phoneNumber); + await test.step('Update phone number with invalid lookup number', async () => { + const phoneNumber = '16005550005'; + const alertMessage = createAlertMessage(alertPattern); await piiPopUp.updatePhoneNumber({ phoneNumber, saveButtonName: save, diff --git a/e2e/tests/121-Portal/MakeNewPayment/Safaricom/MissingNationalIdErrorSafaricom.spec.ts b/e2e/tests/121-Portal/MakeNewPayment/Safaricom/MakeFailedPaymentSafaricom.spec.ts similarity index 94% rename from e2e/tests/121-Portal/MakeNewPayment/Safaricom/MissingNationalIdErrorSafaricom.spec.ts rename to e2e/tests/121-Portal/MakeNewPayment/Safaricom/MakeFailedPaymentSafaricom.spec.ts index 93ac42bdca..f0da52014d 100644 --- a/e2e/tests/121-Portal/MakeNewPayment/Safaricom/MissingNationalIdErrorSafaricom.spec.ts +++ b/e2e/tests/121-Portal/MakeNewPayment/Safaricom/MakeFailedPaymentSafaricom.spec.ts @@ -25,13 +25,14 @@ const paymentStatus = englishTranslations.entity.payment.status.error; const paymentFilter = englishTranslations['registration-details']['activity-overview'].filters .payment; -const paymentErrorMessages = 'Property idNumber is undefined'; +const paymentErrorMessages = + 'Error Occurred - Invalid Access Token - mocked_access_token'; test.beforeEach(async ({ page }) => { await resetDB(SeedScript.krcsMultiple); const programIdBHA = 2; const bhaProgramId = programIdBHA; - registrationsSafaricom[0].nationalId = ''; + registrationsSafaricom[0].phoneNumber = '254000000000'; const accessToken = await getAccessToken(); await seedIncludedRegistrations( @@ -49,9 +50,7 @@ test.beforeEach(async ({ page }) => { ); }); -test('[30262] Safaricom: Error because of missing National ID', async ({ - page, -}) => { +test('[30262] Safaricom: Make failed payment', async ({ page }) => { const table = new TableModule(page); const navigationModule = new NavigationModule(page); const homePage = new HomePage(page); diff --git a/e2e/tests/121-Portal/MakeNewPayment/Safaricom/MakeSuccessfulPaymentSafaricom.spec.ts b/e2e/tests/121-Portal/MakeNewPayment/Safaricom/MakeSuccessfulPaymentSafaricom.spec.ts index e420a2eac4..7413980637 100644 --- a/e2e/tests/121-Portal/MakeNewPayment/Safaricom/MakeSuccessfulPaymentSafaricom.spec.ts +++ b/e2e/tests/121-Portal/MakeNewPayment/Safaricom/MakeSuccessfulPaymentSafaricom.spec.ts @@ -1,6 +1,6 @@ import { test } from '@playwright/test'; -import { FinancialServiceProviderName } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; +import { FinancialServiceProviders } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; import { SeedScript } from '@121-service/src/scripts/seed-script.enum'; import KRCSProgram from '@121-service/src/seed-data/program/program-krcs-turkana.json'; import { seedIncludedRegistrations } from '@121-service/test/helpers/registration.helper'; @@ -99,7 +99,7 @@ test('[30259] Safaricom: "Make Successful payment"', async ({ page }) => { }); await registrationPage.validatePaymentDetails({ transferAmount: formattedValue, - fspName: FinancialServiceProviderName.safaricom, + fspName: FinancialServiceProviders.safaricom, currency, }); }); diff --git a/e2e/tests/121-Portal/ViewPaProfilePage/ValidateFspInPaPopUp.spec.ts b/e2e/tests/121-Portal/ViewPaProfilePage/ValidateFspInPaPopUp.spec.ts index e5b3a277bc..d57c889744 100644 --- a/e2e/tests/121-Portal/ViewPaProfilePage/ValidateFspInPaPopUp.spec.ts +++ b/e2e/tests/121-Portal/ViewPaProfilePage/ValidateFspInPaPopUp.spec.ts @@ -2,8 +2,9 @@ import { test } from '@playwright/test'; import { AppRoutes } from '@121-portal/src/app/app-routes.enum'; import englishTranslations from '@121-portal/src/assets/i18n/en.json'; +import { FinancialServiceProviders } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; +import { findFinancialServiceProviderByNameOrFail } from '@121-service/src/financial-service-providers/financial-service-providers.helpers'; import { SeedScript } from '@121-service/src/scripts/seed-script.enum'; -import visaFspIntersolve from '@121-service/src/seed-data/fsp/fsp-intersolve-visa.json'; import NLRCProgram from '@121-service/src/seed-data/program/program-nlrc-ocw.json'; import { seedPaidRegistrations } from '@121-service/test/helpers/registration.helper'; import { resetDB } from '@121-service/test/helpers/utility.helper'; @@ -19,7 +20,9 @@ import TableModule from '../../../pages/Table/TableModule'; const nlrcOcwProgrammeTitle = NLRCProgram.titlePortal.en; const pageTitle = englishTranslations['registration-details'].pageTitle; -const visaFspName = visaFspIntersolve.displayName.en; +const visaFspName = findFinancialServiceProviderByNameOrFail( + FinancialServiceProviders.intersolveVisa, +).defaultLabel.en; test.beforeEach(async ({ page }) => { await resetDB(SeedScript.nlrcMultiple); @@ -59,6 +62,6 @@ test('[27659][27611] Open the edit PA popup', async ({ page }) => { await registration.validateHeaderToContainText(pageTitle); await registration.openEditPaPopUp(); await registration.validateEditPaPopUpOpened(); - await piiPopUp.validateFspNamePresentInEditPopUp(visaFspName); + await piiPopUp.validateFspNamePresentInEditPopUp(visaFspName!); }); }); diff --git a/e2e/tests/121-Portal/ViewPaProfilePage/ViewActivityFspOverview.spec.ts b/e2e/tests/121-Portal/ViewPaProfilePage/ViewActivityFspOverview.spec.ts index 25fd9bc254..a388b2e501 100644 --- a/e2e/tests/121-Portal/ViewPaProfilePage/ViewActivityFspOverview.spec.ts +++ b/e2e/tests/121-Portal/ViewPaProfilePage/ViewActivityFspOverview.spec.ts @@ -1,8 +1,8 @@ import { test } from '@playwright/test'; import { AppRoutes } from '@121-portal/src/app/app-routes.enum'; -import FspName from '@121-portal/src/app/enums/fsp-name.enum'; import englishTranslations from '@121-portal/src/assets/i18n/en.json'; +import { FinancialServiceProviders } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; import { TransactionStatusEnum } from '@121-service/src/payments/transactions/enums/transaction-status.enum'; import { RegistrationStatusEnum } from '@121-service/src/registration/enum/registration-status.enum'; import { SeedScript } from '@121-service/src/scripts/seed-script.enum'; @@ -16,12 +16,12 @@ import NLRCProgram from '@121-service/src/seed-data/program/program-nlrc-ocw.jso import { waitFor } from '@121-service/src/utils/waitFor.helper'; import { doPayment, - updateFinancialServiceProvider, waitForPaymentTransactionsToComplete, } from '@121-service/test/helpers/program.helper'; import { awaitChangePaStatus, importRegistrations, + updateRegistration, } from '@121-service/test/helpers/registration.helper'; import { getAccessToken, @@ -89,17 +89,15 @@ test.beforeEach(async ({ page }) => { Object.values(TransactionStatusEnum), ); - await updateFinancialServiceProvider( + await updateRegistration( programIdVisa, + registrationVisa.referenceId, + { + programFinancialServiceProviderConfigurationName: + FinancialServiceProviders.intersolveVoucherWhatsapp, + }, + 'automated test', accessToken, - paymentReferenceIds, - FspName.intersolveVoucherPaper, - '31600000000', - 'a', - '2', - '3', - '1234CH', - 'Waddinxveen', ); // Login diff --git a/e2e/tests/121-Portal/ViewPaProfilePage/ViewPersonalInformationTable.spec.ts b/e2e/tests/121-Portal/ViewPaProfilePage/ViewPersonalInformationTable.spec.ts index cddda500ac..a20f1adeaa 100644 --- a/e2e/tests/121-Portal/ViewPaProfilePage/ViewPersonalInformationTable.spec.ts +++ b/e2e/tests/121-Portal/ViewPaProfilePage/ViewPersonalInformationTable.spec.ts @@ -2,8 +2,9 @@ import { test } from '@playwright/test'; import { AppRoutes } from '@121-portal/src/app/app-routes.enum'; import englishTranslations from '@121-portal/src/assets/i18n/en.json'; +import { FinancialServiceProviders } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; +import { findFinancialServiceProviderByNameOrFail } from '@121-service/src/financial-service-providers/financial-service-providers.helpers'; import { SeedScript } from '@121-service/src/scripts/seed-script.enum'; -import visaFspTranslations from '@121-service/src/seed-data/fsp/fsp-intersolve-visa.json'; import NLRCProgram from '@121-service/src/seed-data/program/program-nlrc-ocw.json'; import { seedPaidRegistrations } from '@121-service/test/helpers/registration.helper'; import { resetDB } from '@121-service/test/helpers/utility.helper'; @@ -20,7 +21,9 @@ const pageTitle = englishTranslations['registration-details'].pageTitle; const status = englishTranslations.entity.registration.status.included; const language = englishTranslations.page.program['program-people-affected'].language.nl; -const visaFsp = visaFspTranslations.displayName.en; +const visaFsp = findFinancialServiceProviderByNameOrFail( + FinancialServiceProviders.intersolveVisa, +).defaultLabel.en; test.beforeEach(async ({ page }) => { await resetDB(SeedScript.nlrcMultiple); @@ -62,7 +65,7 @@ test('[27492] View Personal information table', async ({ page }) => { await Helpers.getTodaysDate(), language, '+14155235555', - visaFsp, + visaFsp!, ); }); }); diff --git a/interfaces/Portal/src/app/program/edit-person-affected-popup/edit-person-affected-popup.component.ts b/interfaces/Portal/src/app/program/edit-person-affected-popup/edit-person-affected-popup.component.ts index 69ec5f0661..656e1b2421 100644 --- a/interfaces/Portal/src/app/program/edit-person-affected-popup/edit-person-affected-popup.component.ts +++ b/interfaces/Portal/src/app/program/edit-person-affected-popup/edit-person-affected-popup.component.ts @@ -113,16 +113,10 @@ export class EditPersonAffectedPopupComponent implements OnInit { this.attributeValues.preferredLanguage = this.person?.preferredLanguage; if (this.program && this.program.editableAttributes) { - this.paTableAttributesInput = this.program.editableAttributes; - - const fspObject = this.programFspConfigList.find( - (f) => f.name === this.person?.financialServiceProviderName, + // Filter out the phoneNumber attribute here, because it always added to the edit PA popup, regardless of the program configuration as it is also a field in the RegistrationEntity + this.paTableAttributesInput = this.program.editableAttributes.filter( + (attribute) => attribute.name !== 'phoneNumber', ); - if (fspObject && fspObject.editableAttributes) { - this.paTableAttributesInput = fspObject.editableAttributes.concat( - this.paTableAttributesInput, - ); - } } if (this.canViewPersonalData) { diff --git a/interfaces/Portal/src/assets/i18n/ar.json b/interfaces/Portal/src/assets/i18n/ar.json index cd257ca91b..a41a6051d0 100644 --- a/interfaces/Portal/src/assets/i18n/ar.json +++ b/interfaces/Portal/src/assets/i18n/ar.json @@ -394,8 +394,6 @@ "error-alert": { "invalid-date": "تنسيق التاريخ الذي أدخلته غير صالح. مثال تنسيق التاريخ الصالح هو 31-12-1970 والذي يتبع اليوم والشهر والسنة ، DD-MM-YYYY" }, - "fspChangeWarning": "سيؤدي تحديد FSP هذا إلى حذف السمات التالية المتعلقة ب FSP القديم:", - "fspNewAttributesExplanation": "بالنسبة ل FSP هذا ، تكون السمات التالية مطلوبة:", "has-notes-title": "الملاحظات المتاحة", "popup-title": "{{pa}} معلومات مفصلة", "properties": { diff --git a/interfaces/Portal/src/assets/i18n/en.json b/interfaces/Portal/src/assets/i18n/en.json index 1f65c352b2..7e9ffb9c03 100644 --- a/interfaces/Portal/src/assets/i18n/en.json +++ b/interfaces/Portal/src/assets/i18n/en.json @@ -374,8 +374,6 @@ "error-alert": { "invalid-date": "The date format you inputted is not valid. A valid date format example is 31-12-1970 which follows day-month-year, DD-MM-YYYY" }, - "fspChangeWarning": "Selecting this FSP will delete the following attributes related to the old FSP:", - "fspNewAttributesExplanation": "For this FSP the following attributes are required:", "has-notes-title": "Notes available", "popup-title": "{{pa}} detailed info", "properties": { diff --git a/interfaces/Portal/src/assets/i18n/es.json b/interfaces/Portal/src/assets/i18n/es.json index ebe7a78c7f..fdde08db33 100644 --- a/interfaces/Portal/src/assets/i18n/es.json +++ b/interfaces/Portal/src/assets/i18n/es.json @@ -394,8 +394,6 @@ "error-alert": { "invalid-date": "El formato de fecha que ingresó no es válido. Un ejemplo de formato de fecha válido es 31-12-1970 que sigue día-mes-año, DD-MM-AAAA" }, - "fspChangeWarning": "Al seleccionar este FSP, se eliminarán los siguientes atributos relacionados con el FSP anterior:", - "fspNewAttributesExplanation": "Para este FSP se requieren los siguientes atributos:", "has-notes-title": "Notas disponibles", "popup-title": "{{pa}} información detallada", "properties": { diff --git a/interfaces/Portal/src/assets/i18n/fr.json b/interfaces/Portal/src/assets/i18n/fr.json index 46279cac75..55342d2960 100644 --- a/interfaces/Portal/src/assets/i18n/fr.json +++ b/interfaces/Portal/src/assets/i18n/fr.json @@ -405,8 +405,6 @@ "error-alert": { "invalid-date": "Le format de date que vous avez saisi n’est pas valide. Un exemple de format de date valide est 31-12-1970 qui suit jour-mois-année, JJ-MM-AAAA" }, - "fspChangeWarning": "La sélection de ce PSF supprimera les attributs suivants liés à l’ancien PSF :", - "fspNewAttributesExplanation": "Pour ce PSF, les attributs suivants sont requis :", "has-notes-title": "Notes disponibles", "popup-title": "{{pa}} info détaillée", "properties": { diff --git a/interfaces/Portal/src/assets/i18n/nl.json b/interfaces/Portal/src/assets/i18n/nl.json index 9f2572bbb5..2e2a5094ba 100644 --- a/interfaces/Portal/src/assets/i18n/nl.json +++ b/interfaces/Portal/src/assets/i18n/nl.json @@ -409,8 +409,6 @@ "error-alert": { "invalid-date": "De datumnotatie die u hebt ingevoerd, is niet geldig. Een geldig voorbeeld van een datumnotatie is 31-12-1970. Dus: dag-maand-jaar, DD-MM-JJJJ" }, - "fspChangeWarning": "Als u deze FSP selecteert, wordt de volgende informatie verwijderd die verband houden met de oude FSP:", - "fspNewAttributesExplanation": "Voor deze FSP is de volgende informatie vereist:", "has-notes-title": "Notities beschikbaar", "popup-title": "{{pa}} meer info", "properties": { diff --git a/package-lock.json b/package-lock.json index 3ae654f480..5feba45642 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "license": "Apache-2.0", "devDependencies": { "@prettier/plugin-xml": "^3.4.1", + "@types/multer": "^1.4.12", "husky": "^9.0.11", "lint-staged": "^15.2.2", "prettier": "3.3.3", @@ -27,6 +28,124 @@ "prettier": "^3.0.0" } }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/multer": { + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "22.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", + "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, "node_modules/@xml-tools/parser": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@xml-tools/parser/-/parser-1.0.11.tgz", @@ -826,6 +945,13 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index bbfa5b72a2..7f4c45fd35 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ }, "devDependencies": { "@prettier/plugin-xml": "^3.4.1", + "@types/multer": "^1.4.12", "husky": "^9.0.11", "lint-staged": "^15.2.2", "prettier": "3.3.3", diff --git a/services/121-service/.knip.json b/services/121-service/.knip.json index 29e07961b2..5f966d5a15 100644 --- a/services/121-service/.knip.json +++ b/services/121-service/.knip.json @@ -6,7 +6,8 @@ "!src/**/*.spec.{js,ts}", "!src/migration/**/*.{js,ts}", "!src/seed-data/**/*.{js,ts}", - "!src/datasource-manage-migrations.ts" + "!src/datasource-manage-migrations.ts", + "!src/payments/fsp-integration/intersolve-visa/entities/intersolve-visa-wallet.entity.ts" ], "rules": { "dependencies": "warn", diff --git a/services/121-service/jest.unit.config.js b/services/121-service/jest.unit.config.js index cb90152973..b4cc547d9c 100644 --- a/services/121-service/jest.unit.config.js +++ b/services/121-service/jest.unit.config.js @@ -3,9 +3,6 @@ module.exports = { preset: 'ts-jest', rootDir: '.', testMatch: ['/**/*.spec.ts'], - transform: { - '^.+\\.ts?$': ['ts-jest', { tsconfig: '/test/tsconfig.json' }], - }, coverageReporters: ['json', 'lcov'], modulePathIgnorePatterns: ['/dist/'], moduleNameMapper: { diff --git a/services/121-service/module-dependencies.md b/services/121-service/module-dependencies.md index d3f56348ec..e57485aa04 100644 --- a/services/121-service/module-dependencies.md +++ b/services/121-service/module-dependencies.md @@ -89,7 +89,6 @@ graph LR MetricsModule-->IntersolveVoucherModule MetricsModule-->EventsModule MetricsModule-->RegistrationDataModule - MigrateVisaModule-->UserModule MessageIncomingModule-->ImageCodeModule MessageIncomingModule-->UserModule MessageIncomingModule-->IntersolveVoucherModule diff --git a/services/121-service/package-lock.json b/services/121-service/package-lock.json index b51b069148..42ff77cd70 100644 --- a/services/121-service/package-lock.json +++ b/services/121-service/package-lock.json @@ -21,7 +21,6 @@ "@nestjs/typeorm": "^10.0.2", "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.7", - "@types/multer": "^1.4.12", "applicationinsights": "^2.9.5", "bull": "^4.16.2", "bwip-js": "^2.0.11", @@ -61,8 +60,8 @@ "@types/cookie-parser": "^1.4.7", "@types/jest": "^29.5.13", "@types/lodash": "^4.17.12", - "@types/node": "^20.x", "@types/multer": "^1.4.12", + "@types/node": "^20.x", "@types/passport-azure-ad": "^4.3.6", "@types/passport-jwt": "^4.0.1", "@types/supertest": "^6.0.2", @@ -5705,6 +5704,8 @@ "version": "1.4.12", "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "dev": true, + "license": "MIT", "dependencies": { "@types/express": "*" } diff --git a/services/121-service/package.json b/services/121-service/package.json index 9c1bb22df5..57f74e0833 100644 --- a/services/121-service/package.json +++ b/services/121-service/package.json @@ -54,7 +54,6 @@ "@nestjs/typeorm": "^10.0.2", "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.7", - "@types/multer": "^1.4.12", "applicationinsights": "^2.9.5", "bull": "^4.16.2", "bwip-js": "^2.0.11", @@ -94,8 +93,8 @@ "@types/cookie-parser": "^1.4.7", "@types/jest": "^29.5.13", "@types/lodash": "^4.17.12", - "@types/node": "^20.x", "@types/multer": "^1.4.12", + "@types/node": "^20.x", "@types/passport-azure-ad": "^4.3.6", "@types/passport-jwt": "^4.0.1", "@types/supertest": "^6.0.2", diff --git a/services/121-service/src/activities/interfaces/transaction-activity.interface.ts b/services/121-service/src/activities/interfaces/transaction-activity.interface.ts index 4510c1065e..296ea21252 100644 --- a/services/121-service/src/activities/interfaces/transaction-activity.interface.ts +++ b/services/121-service/src/activities/interfaces/transaction-activity.interface.ts @@ -1,6 +1,6 @@ import { ActivityTypeEnum } from '@121-service/src/activities/enum/activity-type.enum'; import { BaseActivity } from '@121-service/src/activities/interfaces/base-activity.interface'; -import { FinancialServiceProviderName } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; +import { FinancialServiceProviders } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; import { TransactionStatusEnum } from '@121-service/src/payments/transactions/enums/transaction-status.enum'; import { LocalizedString } from '@121-service/src/shared/types/localized-string.type'; @@ -11,7 +11,7 @@ export interface TransactionActivity extends BaseActivity { status: TransactionStatusEnum; amount: number; paymentDate: Date; - fsp: FinancialServiceProviderName; + fsp: FinancialServiceProviders; fspName: LocalizedString; errorMessage?: string; }; diff --git a/services/121-service/src/app.module.ts b/services/121-service/src/app.module.ts index dc941a1f9b..d3908d9aed 100644 --- a/services/121-service/src/app.module.ts +++ b/services/121-service/src/app.module.ts @@ -15,7 +15,6 @@ import { EmailsModule } from '@121-service/src/emails/emails.module'; import { FinancialServiceProviderCallbackJobProcessorsModule } from '@121-service/src/financial-service-provider-callback-job-processors/financial-service-provider-callback-job-processors.module'; import { HealthModule } from '@121-service/src/health/health.module'; import { MetricsModule } from '@121-service/src/metrics/metrics.module'; -import { MigrateVisaModule } from '@121-service/src/migrate-visa/migrate-visa.module'; import { NoteModule } from '@121-service/src/notes/notes.module'; import { MessageModule } from '@121-service/src/notifications/message.module'; import { MessageIncomingModule } from '@121-service/src/notifications/message-incoming/message-incoming.module'; @@ -37,7 +36,6 @@ import { TypeOrmModule } from '@121-service/src/typeorm.module'; OrganizationModule, MessageModule, MetricsModule, - MigrateVisaModule, MessageIncomingModule, NoteModule, EmailsModule, diff --git a/services/121-service/src/main.ts b/services/121-service/src/main.ts index 56ad36fe23..3c52020fcd 100644 --- a/services/121-service/src/main.ts +++ b/services/121-service/src/main.ts @@ -25,6 +25,8 @@ import { SWAGGER_CUSTOM_JS, } from '@121-service/src/config'; import { AzureLogService } from '@121-service/src/shared/services/azure-log.service'; + +import 'multer'; // This is import is required to prevent typing error on the MulterModule // eslint-disable-next-line @typescript-eslint/no-require-imports import appInsights = require('applicationinsights'); diff --git a/services/121-service/src/migrate-visa/migrate-visa.controller.ts b/services/121-service/src/migrate-visa/migrate-visa.controller.ts deleted file mode 100644 index d8309133ab..0000000000 --- a/services/121-service/src/migrate-visa/migrate-visa.controller.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { - Controller, - Post, - Query, - UploadedFile, - UseGuards, - UseInterceptors, -} from '@nestjs/common'; -import { FileInterceptor } from '@nestjs/platform-express'; -import { - ApiBody, - ApiConsumes, - ApiOperation, - ApiQuery, - ApiTags, -} from '@nestjs/swagger'; - -import { AuthenticatedUser } from '@121-service/src/guards/authenticated-user.decorator'; -import { AuthenticatedUserGuard } from '@121-service/src/guards/authenticated-user.guard'; -import { MigrateVisaService } from '@121-service/src/migrate-visa/migrate-visa.service'; -import { FILE_UPLOAD_API_FORMAT } from '@121-service/src/shared/file-upload-api-format'; - -@UseGuards(AuthenticatedUserGuard) -@ApiTags('migrate') -@Controller('migrate-visa') -export class MigrateVisaController { - public constructor(private readonly migrateVisaService: MigrateVisaService) {} - - @AuthenticatedUser({ isAdmin: true }) - @ApiQuery({ - name: 'limit', - required: false, - type: 'integer', - description: 'Optional limit for the number of records to migrate', - }) - @ApiOperation({ summary: 'Migrate visa data. One time use' }) - @ApiConsumes('multipart/form-data') - @ApiBody(FILE_UPLOAD_API_FORMAT) - @UseInterceptors(FileInterceptor('file')) - @Post('visa') - public async migrateVisaData( - @Query('limit') limit: number, - @UploadedFile() csvFileWithPreActivationValues: Blob, - ): Promise { - await this.migrateVisaService.migrateData( - limit, - csvFileWithPreActivationValues, - ); - } -} diff --git a/services/121-service/src/migrate-visa/migrate-visa.module.ts b/services/121-service/src/migrate-visa/migrate-visa.module.ts deleted file mode 100644 index 90256dfbcf..0000000000 --- a/services/121-service/src/migrate-visa/migrate-visa.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { HttpModule } from '@nestjs/axios'; -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { MigrateVisaController } from '@121-service/src/migrate-visa/migrate-visa.controller'; -import { MigrateVisaService } from '@121-service/src/migrate-visa/migrate-visa.service'; -import { CustomHttpService } from '@121-service/src/shared/services/custom-http.service'; -import { UserModule } from '@121-service/src/user/user.module'; -import { FileImportService } from '@121-service/src/utils/file-import/file-import.service'; - -@Module({ - imports: [TypeOrmModule.forFeature(), UserModule, HttpModule], - providers: [MigrateVisaService, CustomHttpService, FileImportService], - controllers: [MigrateVisaController], - exports: [MigrateVisaService], -}) -export class MigrateVisaModule {} diff --git a/services/121-service/src/migrate-visa/migrate-visa.service.ts b/services/121-service/src/migrate-visa/migrate-visa.service.ts deleted file mode 100644 index 8f820fa8ee..0000000000 --- a/services/121-service/src/migrate-visa/migrate-visa.service.ts +++ /dev/null @@ -1,696 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Issuer, TokenSet } from 'openid-client'; -import { DataSource, QueryRunner } from 'typeorm'; -import { v4 as uuid } from 'uuid'; - -import { - FinancialServiceProviderConfigurationProperties, - FinancialServiceProviders, -} from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; -import { IssueTokenRequestIntersolveApiDto } from '@121-service/src/payments/fsp-integration/intersolve-visa/dtos/intersolve-api/issue-token-request-intersolve-api.dto'; -import { IssueTokenResponseIntersolveApiDto } from '@121-service/src/payments/fsp-integration/intersolve-visa/dtos/intersolve-api/issue-token-response-intersolve-api.dto'; -import { ErrorsInResponseIntersolveApi } from '@121-service/src/payments/fsp-integration/intersolve-visa/dtos/intersolve-api/partials/error-in-response-intersolve-api'; -import { IntersolveVisaChildWalletEntity } from '@121-service/src/payments/fsp-integration/intersolve-visa/entities/intersolve-visa-child-wallet.entity'; -import { IntersolveVisaCustomerEntity } from '@121-service/src/payments/fsp-integration/intersolve-visa/entities/intersolve-visa-customer.entity'; -import { IntersolveVisaParentWalletEntity } from '@121-service/src/payments/fsp-integration/intersolve-visa/entities/intersolve-visa-parent-wallet.entity'; -import { - IntersolveVisaWalletEntity, - IntersolveVisaWalletStatus, -} from '@121-service/src/payments/fsp-integration/intersolve-visa/entities/intersolve-visa-wallet.entity'; -import { IntersolveBlockTokenReasonCodeEnum } from '@121-service/src/payments/fsp-integration/intersolve-visa/enums/intersolve-block-token-reason-code.enum'; -import { IntersolveVisaTokenStatus } from '@121-service/src/payments/fsp-integration/intersolve-visa/enums/intersolve-visa-token-status.enum'; -import { ProgramFinancialServiceProviderConfigurationEntity } from '@121-service/src/program-financial-service-provider-configurations/entities/program-financial-service-provider-configuration.entity'; -import { CustomHttpService } from '@121-service/src/shared/services/custom-http.service'; -import { FileImportService } from '@121-service/src/utils/file-import/file-import.service'; - -const intersolveVisaApiUrl = process.env.MOCK_INTERSOLVE - ? `${process.env.MOCK_SERVICE_URL}api/fsp/intersolve-visa` - : process.env.INTERSOLVE_VISA_API_URL; - -interface VisaCustomerWithReferenceId extends IntersolveVisaCustomerEntity { - referenceId: string; -} - -interface PreActivationValueRecord { - CardCode: string; - PreActivationValue: number; -} - -@Injectable() -export class MigrateVisaService { - constructor( - private readonly httpService: CustomHttpService, - private readonly fileImportService: FileImportService, - private readonly dataSource: DataSource, - ) {} - name = 'VisaMigrateChildParentWallet1717505534921'; - public tokenSet: TokenSet; - - public async migrateData( - limit: number, - csvFileWithPreActivationValues: Blob, - ): Promise { - const queryRunner = this.dataSource.createQueryRunner(); - await this.migrationTemplateData(queryRunner); - await this.insertProgramConfiguration(); - // await this.COMMENT_OUT_THIS_FUNCTION_FOR_TESTING_ONLY_CLEAR_DATA( - // queryRunner, - // ); - const programIds = - await this.selectProgramIdsForInstanceWithVisa(queryRunner); - for (const programId of programIds) { - await this.migrateProgramData( - queryRunner, - programId, - limit, - csvFileWithPreActivationValues, - ); - } - } - - private async insertProgramConfiguration(): Promise { - // this should not be set on production so it should not run on production - if (process.env.INTERSOLVE_VISA_FUNDINGTOKEN_CODE) { - // Insert program configuration - const fspVisaId = await this.dataSource.query( - `SELECT id FROM "121-service"."financial_service_provider" WHERE "fsp" = 'Intersolve-visa'`, - ); - const config2 = new ProgramFinancialServiceProviderConfigurationEntity(); - config2.fspId = fspVisaId[0].id; - config2.programId = 2; - config2.name = - FinancialServiceProviderConfigurationProperties.fundingTokenCode; - config2.value = process.env.INTERSOLVE_VISA_FUNDINGTOKEN_CODE; - const config3 = new ProgramFinancialServiceProviderConfigurationEntity(); - config3.fspId = fspVisaId[0].id; - config3.programId = 3; - config3.name = - FinancialServiceProviderConfigurationProperties.fundingTokenCode; - config3.value = process.env.INTERSOLVE_VISA_FUNDINGTOKEN_CODE; - - // save - try { - await this.dataSource.manager.save(config2); - } catch (e) { - console.log( - 'MigrateVisaService ~ inserProgramConfiguration program 2 ~ e', - e, - ); - } - try { - await this.dataSource.manager.save(config3); - } catch (e) { - console.log( - 'MigrateVisaService ~ inserProgramConfiguration program 3 ~ e', - e, - ); - } - } - } - - private async COMMENT_OUT_THIS_FUNCTION_FOR_TESTING_ONLY_CLEAR_DATA( - q: QueryRunner, - ): Promise { - // truncate - await q.query( - `TRUNCATE TABLE "121-service"."intersolve_visa_parent_wallet" CASCADE`, - ); - await q.query( - `TRUNCATE TABLE "121-service"."intersolve_visa_child_wallet" CASCADE`, - ); - await q.query( - `TRUNCATE TABLE "121-service"."intersolve_migration_progress"`, - ); - } - - private async migrationTemplateData(q: QueryRunner): Promise { - await q.query( - `UPDATE "121-service"."message_template" SET type = 'pauseVisaCard' WHERE type = 'blockVisaCard'`, - ); - await q.query( - `UPDATE "121-service"."message_template" SET type = 'unpauseVisaCard' WHERE type = 'unblockVisaCard'`, - ); - } - - private async createMigrationProgressTableAndIndex(queryRunner: QueryRunner) { - await queryRunner.query( - `CREATE TABLE IF NOT EXISTS "121-service"."intersolve_migration_progress" ("id" SERIAL NOT NULL, "referenceId" character varying NOT NULL, "completedOrCaught" boolean NOT NULL DEFAULT false, "error" json, "created" TIMESTAMP NOT NULL DEFAULT now())`, - ); - await queryRunner.query( - `CREATE INDEX IF NOT EXISTS "IDX_intersolve_migration_progress" ON "121-service"."intersolve_migration_progress" ("referenceId")`, - ); - console.log('Migration progress table and index created(IF NOT EXISTS)'); - } - - private async migrateProgramData( - queryRunner: QueryRunner, - programId: number, - limit: number, - csvFileWithPreActivationValues: Blob, - ): Promise { - await this.createMigrationProgressTableAndIndex(queryRunner); - - // Process pre-activation values of wallets to be able to transfer them to parent wallets in case of INACTIVE wallets - const preActivationValuesMap = await this.processPreActivationValuesCsvFile( - csvFileWithPreActivationValues, - ); - - console.time(`Migrating program ${programId}`); - const brandCode = await this.getBrandcodeForProgram(queryRunner, programId); - const visaCustomers = await this.selectVisaCustomers( - programId, - queryRunner, - limit, - ); - console.log( - `Migrating ${visaCustomers.length} customers for program ${programId}`, - ); - for (let i = 0; i < visaCustomers.length; i++) { - const visaCustomer = visaCustomers[i]; - if ((i + 1) % 10 === 0) { - console.log(`Migrating ${i + 1} of ${visaCustomers.length}`); - } - - let error: any; - - try { - await queryRunner.query( - `INSERT INTO "121-service".intersolve_migration_progress - ("referenceId", "completedOrCaught", "error", "created") - VALUES('${visaCustomer.referenceId}', false, NULL, now())`, - ); - - await this.migrateCustomerAndWalletData( - visaCustomer, - brandCode, - queryRunner, - preActivationValuesMap, - ); - } catch (e) { - // console.log('error:', e); - error = { - message: e.message, - stack: e.stack, - }; - } finally { - const errorString = error - ? JSON.stringify(error).replace(/'/g, "''") - : null; - const query = `UPDATE "121-service"."intersolve_migration_progress" SET "completedOrCaught" = true, "error" = ${errorString ? `'${errorString}'` : 'NULL'} WHERE "referenceId" = '${visaCustomer.referenceId}'`; - await queryRunner.query(query); - } - } - - /** - * When the migration is done, we want to query the migrations table for: - * - 'completedOrCaught' being false -> This means it stopped somewhere in the process, should be the edge case - * - 'completedOrCaught' being true and 'error' being defined -> This means an error occured during the process - * - * Those are the cases that should be investiged. - * - * */ - - console.timeEnd(`Migrating program ${programId}`); - } - - private async processPreActivationValuesCsvFile( - csvFileWithPreActivationValues: Blob, - ): Promise> { - const preActivationValueRecords = (await this.fileImportService.validateCsv( - csvFileWithPreActivationValues, - )) as PreActivationValueRecord[]; - // Convert to map for performance reasons - const preActivationValuesMap = new Map( - preActivationValueRecords.map((item) => [ - item['CardCode'], - item['PreActivationBudget'], - ]), - ); - return preActivationValuesMap; - } - - private async migrateCustomerAndWalletData( - visaCustomer: VisaCustomerWithReferenceId, - brandCode: string, - queryRunner: QueryRunner, - preActivationValuesMap: Map, - ): Promise { - const originalWalletsOfCustomer = await this.selectOriginalWallets( - visaCustomer, - queryRunner, - ); - - if (originalWalletsOfCustomer.length > 0) { - // create parent wallet - const newestOriginalWallet = originalWalletsOfCustomer[0]; - const parentWallet = await this.createParentWallet( - visaCustomer, - newestOriginalWallet, - brandCode, - queryRunner, - ); - - // migrate & link child wallets - await this.migrateAndLinkChildWallets( - originalWalletsOfCustomer, - parentWallet, - queryRunner, - preActivationValuesMap, - ); - } - } - - private async createParentWallet( - visaCustomer: VisaCustomerWithReferenceId, - newestOriginalWallet: IntersolveVisaWalletEntity, - brandCode: string, - queryRunner: QueryRunner, - ): Promise { - const createWalletReponse = await this.postCreateActiveWallet( - { - reference: uuid(), - activate: true, - }, - brandCode, - ); - if (!createWalletReponse?.data?.success) { - console.log( - 'MigrateVisaService ~ createWalletReponse.data.errors:', - createWalletReponse?.data?.errors, - ); - const errors = createWalletReponse?.data?.errors; - const formattedErrors = this.formatErrors(errors); - - throw new Error( - `Failed to create wallet for customer with referenceId ${visaCustomer.referenceId}. Errors: ${formattedErrors}`, - ); - } - const tokenCode = createWalletReponse.data?.data?.token?.code; - const linkToCustomerReponse = await this.postLinkCustomerToWallet( - { - holderId: visaCustomer.holderId, - }, - tokenCode, - ); - - if (!this.isSuccessResponseStatus(linkToCustomerReponse.status)) { - console.log( - 'MigrateVisaService ~ linkToCustomerReponse.data.errors:', - linkToCustomerReponse?.data?.errors, - ); - const errors = linkToCustomerReponse?.data?.errors; - const formattedErrors = this.formatErrors(errors); - - throw new Error( - `Failed to link wallet to customer with referenceId ${visaCustomer.referenceId}. Errors: ${formattedErrors}`, - ); - } - - const newParentWallet = new IntersolveVisaParentWalletEntity(); - newParentWallet.intersolveVisaCustomerId = visaCustomer.id; - newParentWallet.tokenCode = tokenCode; - newParentWallet.balance = newestOriginalWallet.balance - ? newestOriginalWallet.balance - : 0; // Deals with the factor that the old balance was nullable - newParentWallet.lastExternalUpdate = newestOriginalWallet.lastExternalUpdate - ? newestOriginalWallet.lastExternalUpdate - : new Date(); - newParentWallet.spentThisMonth = newestOriginalWallet.spentThisMonth; - newParentWallet.isLinkedToVisaCustomer = true; - return await queryRunner.manager.save(newParentWallet); - } - - private async migrateAndLinkChildWallets( - originalWallets: IntersolveVisaWalletEntity[], - parentWallet: IntersolveVisaParentWalletEntity, - queryRunner: QueryRunner, - preActivationValuesMap: Map, - ): Promise { - for (const originalWallet of originalWallets) { - const childWallet = new IntersolveVisaChildWalletEntity(); - childWallet.intersolveVisaParentWalletId = parentWallet.id; - childWallet.tokenCode = originalWallet.tokenCode!; // We assume that tokencode is never null in the old data - childWallet.isLinkedToParentWallet = false; - childWallet.isTokenBlocked = originalWallet.tokenBlocked ? true : false; // Deals with the factor that the old isTokenBlocked was nullable - childWallet.isDebitCardCreated = originalWallet.debitCardCreated; - childWallet.walletStatus = originalWallet.walletStatus - ? (originalWallet.walletStatus as unknown as IntersolveVisaTokenStatus) - : IntersolveVisaTokenStatus.Active; // Deals with the factor that the old walletStatus was nullable - childWallet.cardStatus = originalWallet.cardStatus; - childWallet.lastUsedDate = originalWallet.lastUsedDate; - childWallet.lastExternalUpdate = originalWallet.lastExternalUpdate; - childWallet.created = originalWallet.created; - const savedChildWallet = await queryRunner.manager.save(childWallet); - - await this.postToggleBlockWallet( - originalWallet.tokenCode, - { - reasonCode: IntersolveBlockTokenReasonCodeEnum.UNBLOCK_GENERAL, - }, - false, - ); - const postLinkTokenResult = await this.postLinkToken( - savedChildWallet.tokenCode, - parentWallet.tokenCode, - ); - if (!this.isSuccessResponseStatus(postLinkTokenResult.status)) { - const errors = postLinkTokenResult?.data?.errors; - const formattedErrors = this.formatErrors(errors); - - throw new Error( - `Failed to link child wallet ${childWallet.id} to parent wallet ${parentWallet.id}. Errors: ${formattedErrors}`, - ); - } else { - savedChildWallet.isLinkedToParentWallet = true; - await queryRunner.manager.save(savedChildWallet); - } - - if (originalWallet.walletStatus === IntersolveVisaWalletStatus.Inactive) { - // get pre-activation value per child wallet - const preActivationValue = preActivationValuesMap.get( - originalWallet.tokenCode as string, - ); - if (!preActivationValue) { - throw new Error( - `No pre-activation value found for wallet ${originalWallet.id} with tokenCode ${originalWallet.tokenCode}`, - ); - } - - // transfer pre-activation value to parent wallet - const postTransferResult = await this.postTransfer( - parentWallet.tokenCode, - preActivationValue as number, - ); - - if (!this.isSuccessResponseStatus(postTransferResult.status)) { - const errors = postTransferResult?.data?.errors; - const formattedErrors = this.formatErrors(errors); - throw new Error( - `Failed to transfer pre-activation value of inactive original wallet ${originalWallet.id} to parent wallet ${parentWallet.id}. Errors: ${formattedErrors}`, - ); - } - } - - if (originalWallet.tokenBlocked) { - // Block wallet again if original wallet was blocked - await this.postToggleBlockWallet( - originalWallet.tokenCode, - { - reasonCode: IntersolveBlockTokenReasonCodeEnum.BLOCK_GENERAL, - }, - true, - ); - } - } - } - - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////// QUERY HELPER FUNCTIONS //////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - private async getBrandcodeForProgram( - queryRunner: QueryRunner, - programId: number, - ): Promise { - const brandCodeConfig = await queryRunner.query( - ` - SELECT * - FROM "121-service"."program_fsp_configuration" pfc - INNER JOIN "121-service"."financial_service_provider" f ON pfc."fspId" = f."id" - WHERE pfc."programId" = $1 - AND pfc."name" = $2 - AND f."fsp" = $3 - `, - [ - programId, - FinancialServiceProviderConfigurationProperties.brandCode, - FinancialServiceProviders.intersolveVisa, - ], - ); - - if (!brandCodeConfig || brandCodeConfig.length === 0) { - throw new Error( - `No brandCode found for program ${programId}. Please update the program FSP cofinguration.`, - ); - } - return brandCodeConfig[0]?.value as string; - } - - private async selectProgramIdsForInstanceWithVisa( - queryRunner: QueryRunner, - ): Promise { - // if intersolve_visa is not enabled, return empty array - if (!process.env.INTERSOLVE_VISA_API_URL) { - return []; - } - const queryResult = await queryRunner.query( - `SELECT id FROM "121-service"."program"`, - ); - return queryResult.map((row: { id: number }) => row.id); - } - - private async selectVisaCustomers( - programId: number, - queryRunner: QueryRunner, - limit: number, - ): Promise { - return queryRunner.query( - `select - i.*, - r."referenceId" - from - "121-service"."intersolve_visa_customer" i - left join "121-service".registration r on - r.id = i."registrationId" - LEFT JOIN "121-service"."intersolve_migration_progress" imp ON r."referenceId" = imp."referenceId" - WHERE "programId" = ${programId} AND imp."referenceId" IS NULL - LIMIT ${limit ?? 'ALL'}`, - ); - } - - private async selectOriginalWallets( - visaCustomer: VisaCustomerWithReferenceId, - queryRunner: QueryRunner, - ): Promise { - return queryRunner.query( - `SELECT * FROM "121-service"."intersolve_visa_wallet" WHERE "intersolveVisaCustomerId" = ${visaCustomer.id} order by "created" desc`, - ); - } - - private formatErrors(errors: unknown): string { - return errors - ? Object.values(errors) - .map((value) => { - const code = value.code ? `${value.code}` : ''; - const field = value.field ? `(${value.field}) ` : ''; - const description = value.description - ? `: ${value.description}` - : ''; - return [code, field, description].filter(Boolean).join(' '); - }) - .join(', ') - : 'Unknown error'; - } - - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////// HTTPS HELPER FUNCTIONS //////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - public async getAuthenticationToken() { - if (process.env.MOCK_INTERSOLVE) { - return 'mocked-token'; - } - if (this.isTokenValid(this.tokenSet)) { - // Return cached token - return this.tokenSet.access_token; - } - // If not valid, request new token - const trustIssuer = await Issuer.discover( - `${process.env.INTERSOLVE_VISA_OIDC_ISSUER}/.well-known/openid-configuration`, - ); - const client = new trustIssuer.Client({ - client_id: process.env.INTERSOLVE_VISA_CLIENT_ID!, - client_secret: process.env.INTERSOLVE_VISA_CLIENT_SECRET!, - }); - const tokenSet = await client.grant({ - grant_type: 'client_credentials', - }); - // Cache tokenSet - this.tokenSet = tokenSet; - return tokenSet.access_token; - } - - private isTokenValid( - tokenSet: TokenSet, - ): tokenSet is TokenSet & Required> { - if (!tokenSet || !tokenSet.expires_at) { - return false; - } - // Convert expires_at to milliseconds - const expiresAtInMs = tokenSet.expires_at * 1000; - const timeLeftBeforeExpire = expiresAtInMs - Date.now(); - // If more than 1 minute left before expiration, the token is considered valid - return timeLeftBeforeExpire > 60000; - } - - public async postCreateActiveWallet( - payload: IssueTokenRequestIntersolveApiDto, - brandCode: string, - ): Promise { - const authToken = await this.getAuthenticationToken(); - - const apiPath = process.env.INTERSOLVE_VISA_PROD - ? 'pointofsale-payments' - : 'pointofsale'; - const url = `${intersolveVisaApiUrl}/${apiPath}/v1/brand-types/${brandCode}/issue-token?includeBalances=true`; - const headers = [ - { name: 'Authorization', value: `Bearer ${authToken}` }, - { name: 'Tenant-ID', value: process.env.INTERSOLVE_VISA_TENANT_ID }, - ]; - return await this.httpService.post( - url, - payload, - headers, - ); - } - - public async postLinkCustomerToWallet( - payload: { - holderId: string | null; - }, - tokenCode: string | null, - ): Promise<{ - data: { - success?: boolean; - errors?: ErrorsInResponseIntersolveApi[]; - code?: string; - correlationId?: string; - }; - status: number; - statusText?: string; - }> { - const authToken = await this.getAuthenticationToken(); - const apiPath = process.env.INTERSOLVE_VISA_PROD - ? 'wallet-payments' - : 'wallet'; - const url = `${intersolveVisaApiUrl}/${apiPath}/v1/tokens/${tokenCode}/register-holder`; - const headers = [ - { name: 'Authorization', value: `Bearer ${authToken}` }, - { name: 'Tenant-ID', value: process.env.INTERSOLVE_VISA_TENANT_ID }, - ]; - // On success this returns a 204 No Content - return await this.httpService.post(url, payload, headers); - } - - private isSuccessResponseStatus(status: number): boolean { - return status >= 200 && status < 300; - } - - public async postToggleBlockWallet( - tokenCode: string | null, - payload: { - reasonCode: IntersolveBlockTokenReasonCodeEnum; - }, - block: boolean, - ): Promise<{ - data: { - success?: boolean; - errors?: ErrorsInResponseIntersolveApi[]; - code?: string; - correlationId?: string; - }; - status: number; - statusText?: string; - }> { - const authToken = await this.getAuthenticationToken(); - const apiPath = process.env.INTERSOLVE_VISA_PROD - ? 'pointofsale-payments' - : 'pointofsale'; - const url = `${intersolveVisaApiUrl}/${apiPath}/v1/tokens/${tokenCode}/${ - block ? 'block' : 'unblock' - }`; - const headers = [ - { name: 'Authorization', value: `Bearer ${authToken}` }, - { name: 'Tenant-ID', value: process.env.INTERSOLVE_VISA_TENANT_ID }, - ]; - const blockResult = await this.httpService.post(url, payload, headers); - const result = { - status: blockResult.status, - statusText: blockResult.statusText, - data: blockResult.data, - }; - return result; - } - - public async postLinkToken( - childTokenCode: string | null, - parentTokenCode: string | null, - ): Promise<{ - data: { - success?: boolean; - errors?: ErrorsInResponseIntersolveApi[]; - code?: string; - correlationId?: string; - }; - status: number; - statusText?: string; - }> { - const authToken = await this.getAuthenticationToken(); - const apiPath = process.env.INTERSOLVE_VISA_PROD - ? 'wallet-payments' - : 'wallet'; - const url = `${intersolveVisaApiUrl}/${apiPath}/v1/tokens/${parentTokenCode}/link-token`; - const headers = [ - { name: 'Authorization', value: `Bearer ${authToken}` }, - { name: 'Tenant-ID', value: process.env.INTERSOLVE_VISA_TENANT_ID }, - ]; - const payload = { - tokenCode: childTokenCode, - }; - const linkResult = await this.httpService.post(url, payload, headers); - return linkResult; - } - - public async postTransfer( - parentTokenCode: string, - amountInCent: number, - ): Promise<{ - data: { - success?: boolean; - errors?: ErrorsInResponseIntersolveApi[]; - code?: string; - correlationId?: string; - }; - status: number; - statusText?: string; - }> { - const authToken = await this.getAuthenticationToken(); - const apiPath = process.env.INTERSOLVE_VISA_PROD - ? 'wallet-payments' - : 'wallet'; - const url = `${intersolveVisaApiUrl}/${apiPath}/v1/tokens/${process.env.INTERSOLVE_VISA_FUNDINGTOKEN_CODE}/transfer`; - const headers = [ - { name: 'Authorization', value: `Bearer ${authToken}` }, - { name: 'Tenant-ID', value: process.env.INTERSOLVE_VISA_TENANT_ID }, - ]; - - const payload = { - quantity: { - value: amountInCent, - assetCode: process.env.INTERSOLVE_VISA_ASSET_CODE!, - }, - creditor: { - tokenCode: parentTokenCode, - }, - reference: `ParentTokenCode=${parentTokenCode}`, // Should be 50 characters or less! - operationReference: uuid(), // Required to pass in a UUID, which needs be unique for all transfers. Is used as idempotency key. - }; - - const transferResult = await this.httpService.post( - url, - payload, - headers, - ); - return transferResult; - } -} diff --git a/services/121-service/src/payments/dto/fsp-instructions.dto.ts b/services/121-service/src/payments/dto/fsp-instructions.dto.ts index 45c81ca4a5..38414248d0 100644 --- a/services/121-service/src/payments/dto/fsp-instructions.dto.ts +++ b/services/121-service/src/payments/dto/fsp-instructions.dto.ts @@ -1,9 +1,7 @@ import { ExcelFspInstructions } from '@121-service/src/payments/fsp-integration/excel/dto/excel-fsp-instructions.dto'; -export type CsvInstructions = ExcelFspInstructions[]; - export class FspInstructions { - public data: CsvInstructions | string; + public data: ExcelFspInstructions[] | string; public fileType: ExportFileType; public fileNamePrefix: string; } diff --git a/services/121-service/src/payments/fsp-integration/excel/excel.service.spec.ts b/services/121-service/src/payments/fsp-integration/excel/excel.service.spec.ts index e31ab8f5a9..ceb05c32b1 100644 --- a/services/121-service/src/payments/fsp-integration/excel/excel.service.spec.ts +++ b/services/121-service/src/payments/fsp-integration/excel/excel.service.spec.ts @@ -6,10 +6,12 @@ import { ExcelService } from '@121-service/src/payments/fsp-integration/excel/ex import { TransactionReturnDto } from '@121-service/src/payments/transactions/dto/get-transaction.dto'; import { TransactionStatusEnum } from '@121-service/src/payments/transactions/enums/transaction-status.enum'; import { TransactionsService } from '@121-service/src/payments/transactions/transactions.service'; +import { ProgramFinancialServiceProviderConfigurationRepository } from '@121-service/src/program-financial-service-provider-configurations/program-financial-service-provider-configurations.repository'; import { ProgramEntity } from '@121-service/src/programs/program.entity'; import { RegistrationViewEntity } from '@121-service/src/registration/registration-view.entity'; import { RegistrationViewScopedRepository } from '@121-service/src/registration/repositories/registration-view-scoped.repository'; import { RegistrationsPaginationService } from '@121-service/src/registration/services/registrations-pagination.service'; +import { FileImportService } from '@121-service/src/utils/file-import/file-import.service'; // ##TODO: tests should be reenabled when the excel service is refactored to the new fsp config structure @@ -21,6 +23,14 @@ const mockRegistrationsPaginationService = { retrieveRegistrationsPaginationService: jest.fn(), }; +const mockFileImportService = { + importFile: jest.fn(), +}; + +const mockProgramFinancialServiceProviderConfigurationRepository = { + retrieveProgramFinancialServiceProviderConfigurationRepository: jest.fn(), +}; + const mockRegistrationViewScopedRepository = { retrieveRegistrationViewScopedRepository: jest.fn(), }; @@ -75,6 +85,14 @@ describe('ExcelService', () => { provide: RegistrationViewScopedRepository, useValue: mockRegistrationViewScopedRepository, }, + { + provide: ProgramFinancialServiceProviderConfigurationRepository, + useValue: mockProgramFinancialServiceProviderConfigurationRepository, + }, + { + provide: FileImportService, + useValue: mockFileImportService, + }, ], }).compile(); @@ -89,15 +107,22 @@ describe('ExcelService', () => { const expectedResult = [ { - paTransactionResult: { + feedback: { + importStatus: 'paymentSuccess', + message: null, + phoneNumber: '27883373741', + referenceId: 'referenceId1234', + status: 'success', + }, + programFinancialServiceProviderConfigurationId: 1, + transaction: { calculatedAmount: transactionAmount, fspName: FinancialServiceProviders.excel, + message: null, referenceId: referenceid, registrationId, status: transactionStatus, }, - phoneNumber, - status: transactionStatus, }, ]; diff --git a/services/121-service/src/payments/payments.controller.ts b/services/121-service/src/payments/payments.controller.ts index f8ac8d496d..77ec1b1d50 100644 --- a/services/121-service/src/payments/payments.controller.ts +++ b/services/121-service/src/payments/payments.controller.ts @@ -298,7 +298,7 @@ export class PaymentsController { @ApiBody(FILE_UPLOAD_API_FORMAT) @UseInterceptors(FileInterceptor('file')) public async importFspReconciliationData( - @UploadedFile() file: Blob, + @UploadedFile() file: Express.Multer.File, @Param('programId', ParseIntPipe) programId: number, @Param('payment', ParseIntPipe) diff --git a/services/121-service/src/payments/transactions/dto/get-audited-transaction.dto.ts b/services/121-service/src/payments/transactions/dto/get-audited-transaction.dto.ts index cee6c40de4..c0f9f811bc 100644 --- a/services/121-service/src/payments/transactions/dto/get-audited-transaction.dto.ts +++ b/services/121-service/src/payments/transactions/dto/get-audited-transaction.dto.ts @@ -1,4 +1,4 @@ -import { FinancialServiceProviderName } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; +import { FinancialServiceProviders } from '@121-service/src/financial-service-providers/enum/financial-service-provider-name.enum'; import { TransactionStatusEnum } from '@121-service/src/payments/transactions/enums/transaction-status.enum'; import { LocalizedString } from '@121-service/src/shared/types/localized-string.type'; @@ -11,7 +11,7 @@ export interface GetAuditedTransactionDto { errorMessage?: string; customData?: string; fspName: LocalizedString; - fsp: FinancialServiceProviderName; + fsp: FinancialServiceProviders; fspIntegrationType: string; userId: number; username: string; diff --git a/services/121-service/src/programs/dto/validation-info.dto.ts b/services/121-service/src/programs/dto/validation-info.dto.ts deleted file mode 100644 index cdd3996605..0000000000 --- a/services/121-service/src/programs/dto/validation-info.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { RegistrationAttributeTypes } from '@121-service/src/registration/enum/registration-attribute.enum'; - -export class ValidationInfo { - public readonly type?: RegistrationAttributeTypes; - public readonly options?: any[]; -} diff --git a/services/121-service/src/programs/utils/overwrite-fsp-display-name.helper.ts b/services/121-service/src/programs/utils/overwrite-fsp-display-name.helper.ts deleted file mode 100644 index 48de968117..0000000000 --- a/services/121-service/src/programs/utils/overwrite-fsp-display-name.helper.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ProgramEntity } from '@121-service/src/programs/program.entity'; -import { LocalizedString } from '@121-service/src/shared/types/localized-string.type'; - -export function getprogramFinancialServiceProviderConfigurationLabelMapping( - program: ProgramEntity, -): Record { - if (!program.programFinancialServiceProviderConfigurations) { - throw new Error( - `getprogramFinancialServiceProviderConfigurationLabelMapping: Should be used with a program entity relations ['programFinancialServiceProviderConfigurations']`, - ); - } - const map = {}; - - for (const fspConfig of program.programFinancialServiceProviderConfigurations) { - map[fspConfig.financialServiceProviderName] = fspConfig.label; - } - return map; -} diff --git a/services/121-service/src/registration/dto/bulk-import.dto.ts b/services/121-service/src/registration/dto/bulk-import.dto.ts index dc69e99ac1..335fe4efda 100644 --- a/services/121-service/src/registration/dto/bulk-import.dto.ts +++ b/services/121-service/src/registration/dto/bulk-import.dto.ts @@ -25,7 +25,7 @@ const fspArray = Object.values(FinancialServiceProviders).map((item) => String(item), ); const languageArray = Object.values(LanguageEnum).map((item) => String(item)); -export class BulkImportDto { +class BulkImportDto { @ApiProperty() @IsString() @IsOptional() @@ -58,7 +58,7 @@ export class BulkImportDto { public scope?: string; } -export class BulkImportResult extends BulkImportDto { +class BulkImportResult extends BulkImportDto { public importStatus: ImportStatus; public registrationStatus: RegistrationStatusEnum | string; } diff --git a/services/121-service/src/registration/dto/bulk-update.dto.ts b/services/121-service/src/registration/dto/bulk-update.dto.ts deleted file mode 100644 index ffaeddd9b1..0000000000 --- a/services/121-service/src/registration/dto/bulk-update.dto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsOptional, IsString } from 'class-validator'; - -import { BulkImportDto } from '@121-service/src/registration/dto/bulk-import.dto'; - -export class BulkUpdateDto extends BulkImportDto { - @ApiProperty() - @IsString() - public referenceId: string; - - @ApiProperty() - @IsOptional() - public declare scope?: string; -} diff --git a/services/121-service/src/registration/validators/registrations-input-validator.ts b/services/121-service/src/registration/validators/registrations-input-validator.ts index 5985ac0692..8d13229217 100644 --- a/services/121-service/src/registration/validators/registrations-input-validator.ts +++ b/services/121-service/src/registration/validators/registrations-input-validator.ts @@ -21,7 +21,6 @@ import { ValidatedRegistrationInput } from '@121-service/src/registration/interf import { RegistrationEntity } from '@121-service/src/registration/registration.entity'; import { RegistrationViewScopedRepository } from '@121-service/src/registration/repositories/registration-view-scoped.repository'; import { RegistrationsPaginationService } from '@121-service/src/registration/services/registrations-pagination.service'; -import { RegistrationsInputValidatorHelpers } from '@121-service/src/registration/validators/registrations-input.validator.helper'; import { LanguageEnum } from '@121-service/src/shared/enum/language.enums'; import { UserService } from '@121-service/src/user/user.service'; @@ -732,60 +731,14 @@ export class RegistrationsInputValidator { lineNumber: i + 1, column: 'phoneNumber', value, - error: 'PhoneNumber is not valid according to Twilio lookup', + error: + 'This value is not a valid phonenumber according to Twilio lookup', }; return { errorObj, sanitized }; } return { errorObj: undefined, sanitized }; } - private validateNonTelephoneDynamicAttribute({ - value, - type, - columnName, - i, - }: { - value: InputAttributeType; - type: string; - columnName: string; - i: number; - }): ValidateRegistrationErrorObject | undefined { - const cleanedValue = this.cleanNonTelephoneDynamicAttribute(value, type); - if (cleanedValue === null) { - const errorObj = { - lineNumber: i + 1, - column: columnName, - value, - error: `Value is not a valid ${type}`, - }; - return errorObj; - } - } - - private cleanNonTelephoneDynamicAttribute( - value: InputAttributeType, - type: string, - ): InputAttributeType { - switch (type) { - case RegistrationAttributeTypes.numeric: - if (value == null) { - return undefined; - } - // Convert the value to a number and return it - // If the value is not a number, return null - return isNaN(Number(value)) ? undefined : Number(value); - case RegistrationAttributeTypes.boolean: - // Convert the value to a boolean and return it - // If the value is not a boolean, return null - const convertedValue = - RegistrationsInputValidatorHelpers.inputToBoolean(value); - return convertedValue === undefined ? undefined : convertedValue; - default: - // If the type is neither numeric nor boolean, return the original value - return value as string; - } - } - private validateFspRequiredAttributes({ row, originalRegistration, @@ -1035,7 +988,7 @@ export class RegistrationsInputValidator { lineNumber: i + 1, column: GenericRegistrationAttributes.paymentAmountMultiplier, value, - error: 'PaymentAmountMultiplier must be a positive number', + error: 'this field must be a positive number', }, }; } @@ -1055,8 +1008,9 @@ export class RegistrationsInputValidator { validatedMaxPayments?: number | undefined; } { // It's always allowed to remove the maxPayments value - if (value == null) { - return { validatedMaxPayments: value }; + // When you upload a csv file, the value is an empty string + if (value == null || value === '') { + return { validatedMaxPayments: undefined }; } if (isNaN(+value) || +value <= 0) { return { @@ -1064,7 +1018,7 @@ export class RegistrationsInputValidator { lineNumber: i + 1, column: GenericRegistrationAttributes.maxPayments, value, - error: 'MaxPayments must be a positive number', + error: 'MaxPayments must be a positive number or left empty', }, }; } diff --git a/services/121-service/src/wrapper.type.ts b/services/121-service/src/wrapper.type.ts index 2acd7340a5..e1a683c35f 100644 --- a/services/121-service/src/wrapper.type.ts +++ b/services/121-service/src/wrapper.type.ts @@ -3,7 +3,3 @@ * caused by reflection metadata saving the type of the property. */ export type WrapperType = T; // WrapperType === Relation - -export function getEnumValue(enumValue: T): T { - return enumValue; -} diff --git a/services/121-service/test/helpers/program.helper.ts b/services/121-service/test/helpers/program.helper.ts index 7cf5e32c87..017e360cdf 100644 --- a/services/121-service/test/helpers/program.helper.ts +++ b/services/121-service/test/helpers/program.helper.ts @@ -193,34 +193,6 @@ export async function getFspInstructions( .query({ format: 'json' }); } -export async function updateFinancialServiceProvider( - programId: number, - accessToken: string, - paymentReferenceIds: string[], - newFspName: string, - whatsappPhoneNumber: string, - addressStreet: string, - addressHouseNumber: string, - addressHouseNumberAddition: string, - addressPostalCode: string, - addressCity: string, -): Promise { - return await getServer() - .put(`/programs/${programId}/registrations/${paymentReferenceIds}/fsp`) - .set('Cookie', [accessToken]) - .send({ - newFspName, - newFspAttributes: { - whatsappPhoneNumber, - addressStreet, - addressHouseNumber, - addressHouseNumberAddition, - addressPostalCode, - addressCity, - }, - }); -} - export async function importFspReconciliationData( programId: number, paymentNr: number, diff --git a/services/121-service/test/program/__snapshots__/create-program.test.ts.snap b/services/121-service/test/program/__snapshots__/create-program.test.ts.snap index 38b1c1e47f..ce1281933e 100644 --- a/services/121-service/test/program/__snapshots__/create-program.test.ts.snap +++ b/services/121-service/test/program/__snapshots__/create-program.test.ts.snap @@ -1322,7 +1322,6 @@ exports[`Create program should post a program: Create program response for progr "group": "payments", }, ], - "financialServiceProviderConfigurations": [], "fixedTransferValue": 25, "fullnameNamingConvention": [ "fullName", @@ -1396,7 +1395,6 @@ exports[`Create program should post a program: Create program response for progr }, ], "paymentAmountMultiplierFormula": null, - "programFinancialServiceProviderConfigurations": [], "programRegistrationAttributes": [ { "duplicateCheck": false, diff --git a/services/121-service/test/program/manage-program-financial-service-provider-configuration.test.ts b/services/121-service/test/program/manage-program-financial-service-provider-configuration.test.ts index 6be9fefd9b..c0a009ee41 100644 --- a/services/121-service/test/program/manage-program-financial-service-provider-configuration.test.ts +++ b/services/121-service/test/program/manage-program-financial-service-provider-configuration.test.ts @@ -185,7 +185,7 @@ describe('Manage financial service provider configurations', () => { (config) => config.name === name, ); // Assert - expect(result.statusCode).toBe(HttpStatus.OK); + expect(result.statusCode).toBe(HttpStatus.NO_CONTENT); expect(getResultConfig).toBeUndefined(); }); @@ -348,7 +348,7 @@ describe('Manage financial service provider configurations', () => { ); // Assert - expect(deleteResult.statusCode).toBe(HttpStatus.OK); + expect(deleteResult.statusCode).toBe(HttpStatus.NO_CONTENT); expect(usernamePropertyAfter).toBeUndefined(); expect(config?.properties.length).toBe( seededFspConfigVoucher.properties.length - 1, diff --git a/services/121-service/tsconfig.json b/services/121-service/tsconfig.json index bf1060e199..0e331ef452 100644 --- a/services/121-service/tsconfig.json +++ b/services/121-service/tsconfig.json @@ -25,7 +25,7 @@ "checkJs": true, "incremental": true, "outDir": "./dist", - "types": ["express", "node", "Multer", "jest"], + "types": ["express", "node", "jest"], "typeRoots": ["./node_modules/@types"], "resolveJsonModule": true, "baseUrl": "./",