From 4075ed0fde3fa228934001ee91ecb29a3f2d3b6c Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Fri, 13 Dec 2024 13:15:03 +0100 Subject: [PATCH 01/11] Extract decos-service to its own service --- src/server/routing/router-protected.ts | 2 +- .../{vergunningen-v2 => decos}/decos-service.test.ts | 2 +- .../services/{vergunningen-v2 => decos}/decos-service.ts | 4 ++-- .../services/{vergunningen-v2 => decos}/helpers.test.ts | 4 ++-- src/server/services/{vergunningen-v2 => decos}/helpers.ts | 4 ++-- .../toeristische-verhuur-notifications.ts | 2 +- src/server/services/vergunningen-v2/decos-zaken.ts | 2 +- .../services/vergunningen-v2/vergunningen-notifications.ts | 2 +- .../vergunningen-v2/vergunningen-route-handlers.ts | 2 +- src/server/services/vergunningen-v2/vergunningen.ts | 7 +++++-- 10 files changed, 17 insertions(+), 14 deletions(-) rename src/server/services/{vergunningen-v2 => decos}/decos-service.test.ts (99%) rename src/server/services/{vergunningen-v2 => decos}/decos-service.ts (99%) rename src/server/services/{vergunningen-v2 => decos}/helpers.test.ts (98%) rename src/server/services/{vergunningen-v2 => decos}/helpers.ts (97%) diff --git a/src/server/routing/router-protected.ts b/src/server/routing/router-protected.ts index 6e18bcae6d..206b65647b 100644 --- a/src/server/routing/router-protected.ts +++ b/src/server/routing/router-protected.ts @@ -37,7 +37,7 @@ import { import { attachDocumentDownloadRoute } from '../services/shared/document-download-route-handler'; import { fetchErfpachtV2DossiersDetail } from '../services/simple-connect/erfpacht'; import { fetchBBDocument } from '../services/toeristische-verhuur/toeristische-verhuur-powerbrowser-bb-vergunning'; -import { fetchDecosDocument } from '../services/vergunningen-v2/decos-service'; +import { fetchDecosDocument } from '../services/decos/decos-service'; import { fetchVergunningDetail, fetchZakenFromSource, diff --git a/src/server/services/vergunningen-v2/decos-service.test.ts b/src/server/services/decos/decos-service.test.ts similarity index 99% rename from src/server/services/vergunningen-v2/decos-service.test.ts rename to src/server/services/decos/decos-service.test.ts index 2168398d28..1ff448688d 100644 --- a/src/server/services/vergunningen-v2/decos-service.test.ts +++ b/src/server/services/decos/decos-service.test.ts @@ -4,7 +4,7 @@ import { DecosDocumentSource, DecosZaakSource, DecosZakenResponse, -} from './config-and-types'; +} from '../vergunningen-v2/config-and-types'; import { fetchDecosDocumentList, fetchDecosVergunningen, diff --git a/src/server/services/vergunningen-v2/decos-service.ts b/src/server/services/decos/decos-service.ts similarity index 99% rename from src/server/services/vergunningen-v2/decos-service.ts rename to src/server/services/decos/decos-service.ts index ba26aca920..32319b1dc1 100644 --- a/src/server/services/vergunningen-v2/decos-service.ts +++ b/src/server/services/decos/decos-service.ts @@ -13,12 +13,12 @@ import { VergunningDocument, VergunningV2, adresBoekenByProfileType, -} from './config-and-types'; +} from '../vergunningen-v2/config-and-types'; import { SELECT_FIELDS_META, SELECT_FIELDS_TRANSFORM_BASE, decosZaakTransformers, -} from './decos-zaken'; +} from '../vergunningen-v2/decos-zaken'; import { getDecosZaakTypeFromSource, getUserKeysSearchQuery, diff --git a/src/server/services/vergunningen-v2/helpers.test.ts b/src/server/services/decos/helpers.test.ts similarity index 98% rename from src/server/services/vergunningen-v2/helpers.test.ts rename to src/server/services/decos/helpers.test.ts index 06f53f50e3..bb386c126f 100644 --- a/src/server/services/vergunningen-v2/helpers.test.ts +++ b/src/server/services/decos/helpers.test.ts @@ -1,8 +1,8 @@ import { DecosZaakSource, TouringcarDagontheffing, -} from './config-and-types'; -import { decosZaakTransformers } from './decos-zaken'; +} from '../vergunningen-v2/config-and-types'; +import { decosZaakTransformers } from '../vergunningen-v2/decos-zaken'; import { getCustomTitleForVergunningWithLicensePlates, getDecosZaakTypeFromSource, diff --git a/src/server/services/vergunningen-v2/helpers.ts b/src/server/services/decos/helpers.ts similarity index 97% rename from src/server/services/vergunningen-v2/helpers.ts rename to src/server/services/decos/helpers.ts index b057b1abc3..c37e795963 100644 --- a/src/server/services/vergunningen-v2/helpers.ts +++ b/src/server/services/decos/helpers.ts @@ -8,8 +8,8 @@ import { DecosZaakTypeTransformer, NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END, VergunningV2, -} from './config-and-types'; -import { decosZaakTransformers } from './decos-zaken'; +} from '../vergunningen-v2/config-and-types'; +import { decosZaakTransformers } from '../vergunningen-v2/decos-zaken'; import { defaultDateFormat, isDateInPast, diff --git a/src/server/services/toeristische-verhuur/toeristische-verhuur-notifications.ts b/src/server/services/toeristische-verhuur/toeristische-verhuur-notifications.ts index 7375a65f45..4b6939a8a8 100644 --- a/src/server/services/toeristische-verhuur/toeristische-verhuur-notifications.ts +++ b/src/server/services/toeristische-verhuur/toeristische-verhuur-notifications.ts @@ -14,7 +14,7 @@ import { isRecentNotification } from '../../../universal/helpers/utils'; import { MyNotification } from '../../../universal/types'; import { AuthProfileAndToken } from '../../auth/auth-types'; import { NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END } from '../vergunningen-v2/config-and-types'; -import { isNearEndDate } from '../vergunningen-v2/helpers'; +import { isNearEndDate } from '../decos/helpers'; export function createToeristischeVerhuurNotification( vergunning: ToeristischeVerhuurVergunning, diff --git a/src/server/services/vergunningen-v2/decos-zaken.ts b/src/server/services/vergunningen-v2/decos-zaken.ts index 7bf70d3b51..7a839cb92f 100644 --- a/src/server/services/vergunningen-v2/decos-zaken.ts +++ b/src/server/services/vergunningen-v2/decos-zaken.ts @@ -41,7 +41,7 @@ import { getCustomTitleForVergunningWithLicensePlates, transformBoolean, transformKenteken, -} from './helpers'; +} from '../decos/helpers'; import { caseNotificationLabelsDefault, caseNotificationLabelsExpirables, diff --git a/src/server/services/vergunningen-v2/vergunningen-notifications.ts b/src/server/services/vergunningen-v2/vergunningen-notifications.ts index 732d4a75a8..a2d6d6882e 100644 --- a/src/server/services/vergunningen-v2/vergunningen-notifications.ts +++ b/src/server/services/vergunningen-v2/vergunningen-notifications.ts @@ -10,7 +10,7 @@ import { VergunningFrontendV2, } from './config-and-types'; import { decosZaakTransformers } from './decos-zaken'; -import { isNearEndDate } from './helpers'; +import { isNearEndDate } from '../decos/helpers'; import { FILTER_VERGUNNINGEN_DEFAULT, fetchVergunningenV2, diff --git a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts index 0fb9e8b385..d64952432e 100644 --- a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts +++ b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts @@ -4,7 +4,7 @@ import { DecosZaakSource } from './config-and-types'; import { fetchDecosZaakFromSource, fetchDecosZakenFromSource, -} from './decos-service'; +} from '../decos/decos-service'; import { fetchVergunningV2 } from './vergunningen'; import { apiSuccessResult } from '../../../universal/helpers/api'; import { getAuth } from '../../auth/auth-helpers'; diff --git a/src/server/services/vergunningen-v2/vergunningen.ts b/src/server/services/vergunningen-v2/vergunningen.ts index d7d9d6d5d0..c594c4ff5a 100644 --- a/src/server/services/vergunningen-v2/vergunningen.ts +++ b/src/server/services/vergunningen-v2/vergunningen.ts @@ -10,8 +10,11 @@ import { VergunningFrontendV2, VergunningV2, } from './config-and-types'; -import { fetchDecosVergunning, fetchDecosVergunningen } from './decos-service'; -import { isExpired, toDateFormatted } from './helpers'; +import { + fetchDecosVergunning, + fetchDecosVergunningen, +} from '../decos/decos-service'; +import { isExpired, toDateFormatted } from '../decos/helpers'; import { getStatusSteps } from './vergunningen-status-steps'; import { AppRoute, AppRoutes } from '../../../universal/config/routes'; import { apiSuccessResult } from '../../../universal/helpers/api'; From 08008f75f03e8ccb5b7149e924170041d29346f5 Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Tue, 17 Dec 2024 11:01:52 +0100 Subject: [PATCH 02/11] Accept decosZaakTransformers as input in decosService functions --- .../services/decos/decos-service.test.ts | 28 +- src/server/services/decos/decos-service.ts | 201 ++++++----- src/server/services/decos/decos-types.ts | 214 +++++++++++ src/server/services/decos/helpers.test.ts | 80 ++--- src/server/services/decos/helpers.ts | 73 ++-- .../vergunningen-v2/config-and-types.ts | 335 +++--------------- .../services/vergunningen-v2/decos-zaken.ts | 218 ++++++------ .../vergunningen-notification-labels.ts | 6 +- .../vergunningen-notifications.ts | 10 +- .../vergunningen-route-handlers.ts | 2 +- .../services/vergunningen-v2/vergunningen.ts | 77 ++-- 11 files changed, 633 insertions(+), 611 deletions(-) create mode 100644 src/server/services/decos/decos-types.ts diff --git a/src/server/services/decos/decos-service.test.ts b/src/server/services/decos/decos-service.test.ts index 1ff448688d..2b78a33f74 100644 --- a/src/server/services/decos/decos-service.test.ts +++ b/src/server/services/decos/decos-service.test.ts @@ -1,20 +1,21 @@ import uid from 'uid-safe'; -import { - DecosDocumentSource, - DecosZaakSource, - DecosZakenResponse, -} from '../vergunningen-v2/config-and-types'; import { fetchDecosDocumentList, - fetchDecosVergunningen, + fetchDecosZaken, fetchDecosWorkflowDate, fetchDecosZakenFromSource, forTesting, } from './decos-service'; +import { + DecosDocumentSource, + DecosZaakSource, + DecosZakenResponse, +} from './decos-types'; import { remoteApi } from '../../../testing/utils'; import { jsonCopy, range } from '../../../universal/helpers/utils'; import { AuthProfileAndToken } from '../../auth/auth-types'; +import { decosZaakTransformers } from '../vergunningen-v2/decos-zaken'; const zakenSource = { count: 1, @@ -321,9 +322,10 @@ describe('decos-service', () => { .times(numberOfAddressBooksToSearch) .replyWithError('Booksearch failed'); - const responseData = await fetchDecosVergunningen( + const responseData = await fetchDecosZaken( reqID, - authProfileAndToken + authProfileAndToken, + decosZaakTransformers ); expect(responseData).toMatchInlineSnapshot(` @@ -350,9 +352,10 @@ describe('decos-service', () => { .times(numberOfAddressBooksToSearch) .reply(200, zakenSource); - const responseData = await fetchDecosVergunningen( + const responseData = await fetchDecosZaken( reqID, - authProfileAndToken + authProfileAndToken, + decosZaakTransformers ); expect(responseData.status).toBe('OK'); @@ -598,6 +601,7 @@ describe('decos-service', () => { const transformed = await forTesting.transformDecosZaakResponse( reqID, + decosZaakTransformers, zakenSource.content[0] ); @@ -629,6 +633,7 @@ describe('decos-service', () => { const transformed = await forTesting.transformDecosZaakResponse( reqID, + decosZaakTransformers, zaak ); expect(transformed).toBe(null); @@ -641,6 +646,7 @@ describe('decos-service', () => { const transformed = await forTesting.transformDecosZaakResponse( reqID, + decosZaakTransformers, zaak ); expect(transformed).not.toBe(null); @@ -662,6 +668,7 @@ describe('decos-service', () => { const transformed = await forTesting.transformDecosZaakResponse( reqID, + decosZaakTransformers, zaak ); expect(transformed?.decision).toBe('Zie besluit'); @@ -682,6 +689,7 @@ describe('decos-service', () => { }); const zakenTransformed = await forTesting.transformDecosZakenResponse( reqID, + decosZaakTransformers, zaken ); expect( diff --git a/src/server/services/decos/decos-service.ts b/src/server/services/decos/decos-service.ts index 32319b1dc1..7e77afc375 100644 --- a/src/server/services/decos/decos-service.ts +++ b/src/server/services/decos/decos-service.ts @@ -1,30 +1,29 @@ import memoizee from 'memoizee'; import { - AddressBookEntry, + DecosZaakBase, + DecosZaakTransformer, + MA_DECISION_DEFAULT, +} from './decos-types'; +import { adresBoekenByProfileType } from './decos-types'; +import { AddressBookEntry } from './decos-types'; +import { DecosFieldValue } from './decos-types'; +import { DecosZaakDocument } from './decos-types'; +import { DecosWorkflowStepDate, DecosWorkflowStepTitle } from './decos-types'; +import { DecosDocumentBlobSource, DecosDocumentSource, - DecosFieldValue, - DecosWorkflowStepDate, - DecosWorkflowStepTitle, DecosZaakSource, DecosZakenResponse, - MA_DECISION_DEFAULT, - VergunningDocument, - VergunningV2, - adresBoekenByProfileType, -} from '../vergunningen-v2/config-and-types'; -import { - SELECT_FIELDS_META, - SELECT_FIELDS_TRANSFORM_BASE, - decosZaakTransformers, -} from '../vergunningen-v2/decos-zaken'; +} from './decos-types'; import { getDecosZaakTypeFromSource, getUserKeysSearchQuery, isExcludedFromTransformation, } from './helpers'; import { + ApiErrorResponse, + ApiSuccessResponse, apiSuccessResult, getSettledResult, } from '../../../universal/helpers/api'; @@ -38,10 +37,15 @@ import { getApiConfig } from '../../helpers/source-api-helpers'; import { requestData } from '../../helpers/source-api-request'; import { captureException, captureMessage } from '../monitoring'; import { DocumentDownloadData } from '../shared/document-download-route-handler'; +import { + SELECT_FIELDS_META, + SELECT_FIELDS_TRANSFORM_BASE, + decosCaseToZaakTransformers, +} from '../vergunningen-v2/decos-zaken'; /** - * The Decos service ties responses of various api calls together and produces a set of transformed set of vergunningen. + * The Decos service ties responses of various api calls together and produces a set of transformed set of decosZaken. * - * Service: **fetchDecosVergunningen** + * Service: **fetchDecosZaken** * * First we query Decos for a `key` to see if a certain userID (BSN or KVKNUMBER) is known in the system. We look for the values of the userID in so called AddressBooks. * The id's (keys) of the addressbooks are static values. So we put together a search query that looks for an AddressBook entry in a @@ -59,13 +63,13 @@ import { DocumentDownloadData } from '../shared/document-download-route-handler' * After transformation of the api attribute names and possibly values we apply another, optional, transform to the data. * At this point we can fetch other services to enrich the data we retrieved initially or maybe assign values to some properties based on business logic. * - * Service: **fetchDecosVergunning** + * Service: **fetchDecosZaak** * - * Retrieves one zaak based on a key found in the fetchDecosVergunningen call. This service is used to provide the data for the detailed view of a vergunning. + * Retrieves one zaak based on a key found in the fetchDecosZaken call. This service is used to provide the data for the detailed view of a decos zaak. * This api call to Decos does not specify any fields to be selected and instead retrieves all the fields related to the requested zaak. Source validity checks / filtering is - * not performed at this stage because we should only have the keys of individual zaken that we got from the fetchDecosVergunningen service. + * not performed at this stage because we should only have the keys of individual zaken that we got from the fetchDecosZaken service. * - * Event hough we retrieve all source fields/values for a specific case, only ones specified (transformFields) end up in the data sent to the user. + * Event though we retrieve all source fields/values for a specific case, only ones specified (transformFields) end up in the data sent to the user. * * Service: **fetchDecosWorkflowDate** * @@ -77,7 +81,7 @@ import { DocumentDownloadData } from '../shared/document-download-route-handler' * ZaakID is the value of the `key` attribute in Decos. `Identifier` is the attribute name we use in Mijn Amsterdam as `Zaaknummer / Kenmerk`, a value readable to the user. * * - * Servuce: **fetchDecosDocument** + * Service: **fetchDecosDocument** * * Retrieves the document (binary) data which can be presented to the user as Document download. * @@ -129,19 +133,25 @@ async function getUserKeys( return apiSuccessResult(userKeys); } -async function transformDecosZaakResponse( +async function transformDecosZaakResponse< + T extends DecosZaakTransformer, + DZ extends DecosZaakBase = NestedType, +>( requestID: RequestID, + decosZaakTransformers: T[], decosZaakSource: DecosZaakSource -) { +): Promise { const zaakType = getDecosZaakTypeFromSource(decosZaakSource); - const zaakTypeTransformer = decosZaakTransformers[zaakType]; + const decosZaakTransformer = decosZaakTransformers.find( + (transformer) => transformer.caseType == zaakType + ); - if (!zaakTypeTransformer || !zaakTypeTransformer.transformFields) { + if (!decosZaakTransformer || !decosZaakTransformer.transformFields) { captureMessage(`No transformer for zaakType ${zaakType}`); return null; } - if (isExcludedFromTransformation(decosZaakSource, zaakTypeTransformer)) { + if (isExcludedFromTransformation(decosZaakSource, decosZaakTransformer)) { return null; } @@ -152,7 +162,7 @@ async function transformDecosZaakResponse( // Iterates over the desired data fields (key=>value pairs) and transforms values if necessary. const transformedFieldEntries = Object.entries( - zaakTypeTransformer.transformFields + decosZaakTransformer.transformFields ).map(([fieldNameSource, fieldTransformer]) => { const fieldNameTransformed = typeof fieldTransformer === 'object' @@ -168,7 +178,7 @@ async function transformDecosZaakResponse( typeof fieldTransformer === 'object' && typeof fieldTransformer.transform === 'function' ? fieldTransformer.transform(value, { - zaakTypeTransformer, + decosZaakTransformer, fetchDecosWorkflowDate: fetchWorkflowDate, }) : value; @@ -182,97 +192,103 @@ async function transformDecosZaakResponse( // Create an object from the transformed fieldNames and values const transformedFields = Object.fromEntries(transformedFieldEntries); - // Create the base data for the vergunning. This object is not guaranteed to have all fields defined in the type for a specific vergunning. - // It depends on the query and resturned result to the decos api which field value ends up in the vergunning. - // For example, if we selected only the sourcefield `mark` we'd have a vergunning with a value for `identifier`.. - let vergunning: VergunningV2 = { + // Create the base data for the decosZaak. This object is not guaranteed to have all fields defined in the type for a specific decosZaak. + // It depends on the query and resturned result to the decos api which field value ends up in the decosZaak. + // For example, if we selected only the sourcefield `mark` we'd have a decosZaak with a value for `identifier`.. + let decosZaak: DZ = { id: transformedFields.identifier?.replace(/\//g, '-') ?? - 'unknown-vergunning-id', + 'unknown-decoszaak-id', key: decosZaakSource.key, - title: zaakTypeTransformer.title, + title: decosZaakTransformer.title, dateInBehandeling: null, // Serves as placeholder, value for this property will be added async below. ...transformedFields, }; // Try to fetch and assign a specific date on which the zaak was "In behandeling" - if (zaakTypeTransformer.dateInBehandelingWorkflowStepTitle) { + if (decosZaakTransformer.dateInBehandelingWorkflowStepTitle) { const { content: dateInBehandeling } = await fetchWorkflowDate( - zaakTypeTransformer.dateInBehandelingWorkflowStepTitle + decosZaakTransformer.dateInBehandelingWorkflowStepTitle ); if (dateInBehandeling) { - vergunning.dateInBehandeling = dateInBehandeling; + decosZaak.dateInBehandeling = dateInBehandeling; } } - if (vergunning.processed && !vergunning.decision) { - vergunning.decision = MA_DECISION_DEFAULT; + if (decosZaak.processed && !decosZaak.decision) { + decosZaak.decision = MA_DECISION_DEFAULT; } // After initial transformation of the data is done, perform a Post transform action. // It's possible to handle some data quality improvements and/or business logic operations in the after transform function. - if (zaakTypeTransformer.afterTransform) { - vergunning = await zaakTypeTransformer.afterTransform( - vergunning, + if (decosZaakTransformer.afterTransform) { + decosZaak = await decosZaakTransformer.afterTransform( + decosZaak, decosZaakSource, { fetchDecosWorkflowDate: fetchWorkflowDate, - zaakTypeTransformer, + decosZaakTransformer, } ); } - return vergunning; + return decosZaak; } -async function transformDecosZakenResponse( +async function transformDecosZakenResponse< + T extends DecosZaakTransformer, + DZ extends DecosZaakBase = NestedType, +>( requestID: RequestID, + decosZaakTransformers: T[], decosZakenSource: DecosZaakSource[] ) { - const zakenToBeTransformed = []; - + const zakenToBeTransformed: [T, DecosZaakSource][] = []; for (const decosZaakSource of decosZakenSource) { const zaakType = getDecosZaakTypeFromSource(decosZaakSource); - const zaakTypeTransformer = decosZaakTransformers[zaakType]; + const decosZaakTransformer = decosZaakTransformers.find( + (transformer) => transformer.caseType == zaakType + ); - if (!zaakTypeTransformer) { - captureMessage(`Decos: ${zaakType} transformer not implemented`); + if (!decosZaakTransformer) { + captureMessage(`Decos: ${zaakType} transformer not passed`); continue; } // Exclude zaken that match the following conditions - if (isExcludedFromTransformation(decosZaakSource, zaakTypeTransformer)) { + if (isExcludedFromTransformation(decosZaakSource, decosZaakTransformer)) { continue; } - zakenToBeTransformed.push(decosZaakSource); + zakenToBeTransformed.push([decosZaakTransformer, decosZaakSource]); } - let vergunningen: Array = []; + let decosZaken: Array = []; try { - vergunningen = await Promise.all( - zakenToBeTransformed.map((decosZaak) => { - return transformDecosZaakResponse(requestID, decosZaak); + decosZaken = await Promise.all( + zakenToBeTransformed.map(([decosZaakTransformer, decosZaak]) => { + return transformDecosZaakResponse( + requestID, + [decosZaakTransformer], + decosZaak + ); }) ); } catch (err) { captureException(err); } - return vergunningen - .filter( - (vergunning: VergunningV2 | null): vergunning is VergunningV2 => - vergunning !== null - ) + return decosZaken + .filter((decosZaak: DZ | null): decosZaak is DZ => decosZaak !== null) .sort(sortAlpha('identifier', 'desc')); } async function getZakenByUserKey(requestID: RequestID, userKey: string) { const selectFieldsAllCases = Object.keys(SELECT_FIELDS_TRANSFORM_BASE); - const additionalSelectFields = Object.values(decosZaakTransformers).flatMap( - (zaakTransformer) => zaakTransformer.addToSelectFieldsBase ?? [] - ); + const additionalSelectFields = Object.values( + decosCaseToZaakTransformers + ).flatMap((zaakTransformer) => zaakTransformer.addToSelectFieldsBase ?? []); const selectFields = uniqueArray([ ...SELECT_FIELDS_META, @@ -331,28 +347,33 @@ export async function fetchDecosZakenFromSource( return apiSuccessResult(zakenSource); } -async function fetchDecosVergunningen_( +export async function fetchDecosZaken_< + T extends DecosZaakTransformer, + DZ extends DecosZaakBase = NestedType, +>( requestID: RequestID, - authProfileAndToken: AuthProfileAndToken -) { + authProfileAndToken: AuthProfileAndToken, + zaakTypeTransformers: T[] +): Promise | ApiErrorResponse> { const zakenSourceResponse = await fetchDecosZakenFromSource( requestID, authProfileAndToken ); if (zakenSourceResponse.status === 'OK') { - const vergunningen = await transformDecosZakenResponse( + const decosZakenSource = zakenSourceResponse.content; + const zaken = await transformDecosZakenResponse( requestID, - zakenSourceResponse.content + zaakTypeTransformers, + decosZakenSource ); - - return apiSuccessResult(vergunningen); + return apiSuccessResult(zaken); } return zakenSourceResponse; } -export const fetchDecosVergunningen = memoizee(fetchDecosVergunningen_, { +export const fetchDecosZaken = memoizee(fetchDecosZaken_, { maxAge: DEFAULT_API_CACHE_TTL_MS, }); @@ -381,7 +402,7 @@ function transformDecosWorkflowDateResponse( export async function fetchDecosWorkflowDate( requestID: RequestID, - zaakID: VergunningV2['key'], + zaakID: DecosZaakBase['key'], stepTitle: DecosWorkflowStepTitle ) { const apiConfigWorkflows = getApiConfig('DECOS_API', { @@ -413,7 +434,7 @@ export async function fetchDecosWorkflowDate( async function fetchIsPdfDocument( requestID: RequestID, - documentKey: VergunningDocument['key'] + documentKey: DecosZaakDocument['key'] ) { // items / { document_id } / blob ? select = bol10 const apiConfigDocuments = getApiConfig('DECOS_API', { @@ -456,20 +477,20 @@ async function transformDecosDocumentListResponse( .map(async ({ fields: documentMetadata, key }) => { const isPdfResponse = await fetchIsPdfDocument(requestID, key); if (isPdfResponse.status === 'OK' && isPdfResponse.content.isPDF) { - const vergunningDocument: VergunningDocument = { + const decosZaakDocument: DecosZaakDocument = { id: documentMetadata.mark, key: isPdfResponse.content.key, title: documentMetadata.text41, datePublished: documentMetadata.received_date, url: '', // Url is constructed in vergunningen.ts }; - return vergunningDocument; + return decosZaakDocument; } return null; }); const documents = (await Promise.all(documentsSourceFiltered)).filter( - (document: VergunningDocument | null): document is VergunningDocument => + (document: DecosZaakDocument | null): document is DecosZaakDocument => document !== null ); return documents; @@ -480,7 +501,7 @@ async function transformDecosDocumentListResponse( export async function fetchDecosDocumentList( requestID: RequestID, - zaakID: VergunningV2['key'] + zaakID: DecosZaakBase['key'] ) { const apiConfigDocuments = getApiConfig('DECOS_API', { formatUrl: (config) => { @@ -504,7 +525,7 @@ export async function fetchDecosDocumentList( export async function fetchDecosZaakFromSource( requestID: RequestID, - zaakID: VergunningV2['key'], + zaakID: DecosZaakBase['key'], includeProperties: boolean = false ) { // Fetch the zaak from Decos, this request will return all the fieldNames, no need to specify the ?select= query. @@ -523,9 +544,15 @@ export async function fetchDecosZaakFromSource( return requestData(apiConfig, requestID); } -export async function fetchDecosVergunning( +type NestedType = T extends DecosZaakTransformer ? R : never; + +export async function fetchDecosZaak< + T extends DecosZaakTransformer, + DZ extends DecosZaakBase = NestedType, +>( requestID: RequestID, - zaakID: VergunningV2['key'] + decosZaakTransformers: T[], + zaakID: DecosZaakBase['key'] ) { const decosZaakSourceRequest = fetchDecosZaakFromSource(requestID, zaakID); const decosZaakDocumentsRequest = fetchDecosDocumentList(requestID, zaakID); @@ -539,16 +566,16 @@ export async function fetchDecosVergunning( const zaakSourceResponse = getSettledResult(zaakSourceResponseSettled); const documentsResponse = getSettledResult(documentsResponseSettled); - let documents: VergunningDocument[] = []; - let vergunning: VergunningV2 | null = null; + let documents: DecosZaakDocument[] = []; + let decosZaak: DecosZaakBase | null = null; if (zaakSourceResponse.status == 'OK') { const decosZaakResponseData = zaakSourceResponse.content; - if (decosZaakResponseData) { try { - vergunning = await transformDecosZaakResponse( + decosZaak = await transformDecosZaakResponse( requestID, + decosZaakTransformers, decosZaakResponseData ); } catch (error) { @@ -556,12 +583,12 @@ export async function fetchDecosVergunning( } } - if (documentsResponse.status === 'OK' && vergunning !== null) { + if (documentsResponse.status === 'OK' && decosZaak !== null) { documents = documentsResponse.content; } return apiSuccessResult({ - vergunning, + decosZaak: decosZaak as DZ, documents, }); } diff --git a/src/server/services/decos/decos-types.ts b/src/server/services/decos/decos-types.ts new file mode 100644 index 0000000000..bb205753a5 --- /dev/null +++ b/src/server/services/decos/decos-types.ts @@ -0,0 +1,214 @@ +import { ApiResponse } from '../../../universal/helpers/api'; +import { GenericDocument } from '../../../universal/types'; +import { DecosCaseType } from '../../../universal/types/vergunningen'; +import { NotificationLabelByType } from '../vergunningen-v2/config-and-types'; + +type DecosDocumentBase = { + text39: string; + text40: string; + text41: string; + // identifier / zaaknummer + mark: string; + // datePublished + received_date: string; +}; +type DecosDocumentBlobBase = { + // IS PDF + bol10: boolean; +}; + +export type DecosFieldsObject = Record< + DecosFieldNameSource, + string | boolean | null | number +>; + +export type DecosFieldTransformerObject< + T extends DecosZaakBase = DecosZaakBase, +> = Record | keyof T>; + +export type DecosZaakSource = { + key: DecosZaakID; + links: string[]; + fields: DecosZaakFieldsSource & DecosFieldsObject; +}; + +export type DecosDocumentSource = { + key: DecosZaakID; + links: string[]; + fields: DecosDocumentBase & DecosFieldsObject; +}; + +export type DecosDocumentBlobSource = { + key: DecosZaakID; + links: string[]; + fields: DecosDocumentBlobBase & DecosFieldsObject; +}; + +export type DecosZakenResponse = { + count: number; + content: T; +}; + +export type DecosResponse = { + itemDataResultSet: { + content: T[]; + }; +}; +export type DecosWorkflowStepTitle = string; +export type DecosWorkflowStepDate = string; +export type DecosZaakDocument = GenericDocument & { key: string }; +export type DecosZaakID = string; +export type DecosFieldNameSource = string; +export type DecosFieldValue = string | number | boolean | null; +export type DecosZaakFieldsSource = { + // status + title: string; + // caseType + text45: DecosCaseType | string; + // decision + dfunction?: string | null; + // identifier / zaaknummer + mark: string; + // processed + processed: boolean; + // dateDecision + date5?: string | null; + // dateRequest + document_date: string; + // dateStart + date6?: string | null; + // dateEnd + date7?: string | null; + + subject1?: string; + // Info regarding possible payment + text11?: string | null; + // Info regarding possible payment + text12?: string | null; +}; +export type AddressBookEntry = { + key: string; +}; +export const adresBoekenBSN = + process.env.BFF_DECOS_API_ADRES_BOEKEN_BSN?.split(',') ?? []; + +export const adresBoekenKVK = + process.env.BFF_DECOS_API_ADRES_BOEKEN_KVK?.split(',') ?? []; + +export const adresBoekenByProfileType: Record = { + private: adresBoekenBSN, + commercial: adresBoekenKVK, + 'private-attributes': [], +}; + +export const MA_DECISION_DEFAULT = 'Zie besluit'; +export type DecosFieldTransformer = { + name: keyof T; + transform?: ( + input: any, + options?: DecosTransformerOptions + ) => DecosFieldValue; +}; +export type DecosTransformerOptions = { + decosZaakTransformer?: DecosZaakTransformer; + fetchDecosWorkflowDate?: ( + stepTitle: DecosWorkflowStepTitle + ) => Promise>; +}; + +export type DecosZaakTransformer = { + // The caseType (zaaktype) of the sourceData. + caseType: DecosCaseType; + // Title of the DecosZaakBase, mostly a slightly different variant of the $caseType + title: string; + // A mapping object that can be used to assign a readable attribute to the data sent to the frontend. + // For example: date6 becomes dateStart. Additionally a function can be provided to perform some compute on the value assigned to the sourceField. + // For example String operations like, trim, split, uppercase etc. + transformFields?: Partial>; + // After transform is used to perform additional transformations after the initial transform. + // Business logic is implemented at this point, also async calls to other services to enrich the data can be done here. + afterTransform?: ( + decosZaak: T, + decosZaakSource: DecosZaakSource, + options?: DecosTransformerOptions + ) => Promise; + // A function to check if the source data quality and/or prerequisites for showing the data to the user are valid. + // This function is run before transformation of the zaak. + hasValidSourceData?: (decosZaakSource: DecosZaakSource) => boolean; + // Indicate if the zaak requires payment to be processed and complete. This function is run before transformation of the zaak. + requirePayment?: boolean; + // Decision (resultaat) values are generalized here. For example. The sourceValues can be one of: `Toegekend met borden`, `Toegekend zonder dingen` which we want to show to the user as `Toegekend`. + decisionTranslations?: Record; + // The title of the workflow step that is used to find a date for the InBehandeling status. + dateInBehandelingWorkflowStepTitle?: string; + // Indicates if the Zaak should be shown to the user / is expected to be transformed. + isActive: boolean; + // Initially we request a set of fields to be included in the responseData (?select=). For some cases we need a (few) custom field(s) included in the initial response. + // For example to show a kenteken in the Notifications. Sadly the requested fields cannot be specified on a case basis. + // This means even though we do not want, for example, date7 for case A we will receive it anyway. + // We select a specific set of fields because otherwise we receive all the fields of a zaak which are bloated and mostly unused. + addToSelectFieldsBase?: string[]; + // Notifications for this specific + notificationLabels?: Partial; +}; +export type MADecision = string; +export type DecosDecision = string; +export type ZakenFilter = (zaak: DecosZaakBase) => boolean; +export type DecosZakenSourceFilter = ( + decosZaakSource: DecosZaakSource +) => boolean; +export interface DecosZaakBase { + caseType: DecosCaseType; + dateDecision: string | null; + dateInBehandeling: string | null; + dateRequest: string; + + // DateStart and DateEnd are not applicable to every single decosZaak but general enough to but in base Type. + dateStart: string | null; + dateEnd: string | null; + + decision: string | null; + description: string; + + // Url to BFF Detail paga api + fetchUrl: string; + + identifier: ZaakKenmerk; + title: string; + id: string; + + // Decos key (uuid) used as primary id's in api communication. + key: string; + + processed: boolean; + status: ZaakStatus; + + paymentStatus: string | null; + paymentMethod: string | null; +} +export type ZaakKenmerk = `Z/${number}/${number}`; // Z/23/2230346 +export type ZaakStatus = + | 'Ontvangen' + | 'In behandeling' + | 'Afgehandeld' + | string; +export interface DecosZaakWithLocation extends DecosZaakBase { + location: string | null; +} + +export interface DecosZaakWithKentekens extends DecosZaakBase { + kentekens: string | null; +} + +export interface DecosZaakWithDateRange extends DecosZaakBase { + dateStart: string | null; + dateEnd: string | null; +} + +export interface DecosZaakWithTimeRange extends DecosZaakBase { + timeStart: string | null; + timeEnd: string | null; +} +export interface DecosZaakWithDateTimeRange + extends DecosZaakWithDateRange, + DecosZaakWithTimeRange {} diff --git a/src/server/services/decos/helpers.test.ts b/src/server/services/decos/helpers.test.ts index bb386c126f..6e774d2078 100644 --- a/src/server/services/decos/helpers.test.ts +++ b/src/server/services/decos/helpers.test.ts @@ -1,13 +1,9 @@ +import { DecosZaakSource } from './decos-types'; import { - DecosZaakSource, - TouringcarDagontheffing, -} from '../vergunningen-v2/config-and-types'; -import { decosZaakTransformers } from '../vergunningen-v2/decos-zaken'; -import { - getCustomTitleForVergunningWithLicensePlates, + getCustomTitleForDecosZaakWithLicensePlates, getDecosZaakTypeFromSource, hasInvalidDecision, - hasOtherActualVergunningOfSameType, + hasOtherActualDecosZaakOfSameType, isExcludedFromTransformation, isExpired, isNearEndDate, @@ -18,103 +14,105 @@ import { transformKenteken, } from './helpers'; import { CaseTypeV2 } from '../../../universal/types/vergunningen'; +import { TouringcarDagontheffing } from '../vergunningen-v2/config-and-types'; +import { decosCaseToZaakTransformers } from '../vergunningen-v2/decos-zaken'; -describe('helpers/Vergunningen', () => { +describe('helpers/decos', () => { vi.useFakeTimers(); vi.setSystemTime(new Date('2022-10-06')); test('isNearEndDate', () => { { // No end date - const vergunning: any = { + const decosZaak: any = { dateEnd: null, }; - expect(isNearEndDate(vergunning)).toBe(false); + expect(isNearEndDate(decosZaak)).toBe(false); } { // Near end - const vergunning: any = { + const decosZaak: any = { dateEnd: '2022-10-28', }; - expect(isNearEndDate(vergunning)).toBe(true); + expect(isNearEndDate(decosZaak)).toBe(true); } { // Not near end - const vergunning: any = { + const decosZaak: any = { dateEnd: '2023-10-28', }; - expect(isNearEndDate(vergunning)).toBe(false); + expect(isNearEndDate(decosZaak)).toBe(false); } }); test('isExpired', () => { { // Not expired - const vergunning: any = { + const decosZaak: any = { dateEnd: '2023-10-28', }; - expect(isExpired(vergunning)).toBe(false); + expect(isExpired(decosZaak)).toBe(false); } { - const vergunning: any = { + const decosZaak: any = { dateEnd: '2022-10-07', }; - expect(isExpired(vergunning)).toBe(false); + expect(isExpired(decosZaak)).toBe(false); } { // Is expired - const vergunning: any = { + const decosZaak: any = { dateEnd: '2022-10-06', }; - expect(isExpired(vergunning)).toBe(true); + expect(isExpired(decosZaak)).toBe(true); } { - const vergunning: any = { + const decosZaak: any = { dateEnd: '2022-10-06', }; - expect(isExpired(vergunning)).toBe(true); + expect(isExpired(decosZaak)).toBe(true); } { - const vergunning: any = { + const decosZaak: any = { dateEnd: '2022-09-28', }; - expect(isExpired(vergunning)).toBe(true); + expect(isExpired(decosZaak)).toBe(true); } }); - test('hasOtherActualVergunningOfSameType', () => { - const vergunning: any = { + test('hasOtherActualDecosZaakOfSameType', () => { + const decosZaak: any = { caseType: 'test1', dateEnd: null, identifier: 'xx1', }; { - const vergunningen: any = [ + const decosZaken: any = [ { caseType: 'test1', dateEnd: null, identifier: 'xx2' }, - vergunning, + decosZaak, ]; - expect(hasOtherActualVergunningOfSameType(vergunningen, vergunning)).toBe( + expect(hasOtherActualDecosZaakOfSameType(decosZaken, decosZaak)).toBe( true ); } { - const vergunningen: any = [vergunning]; + const decosZaken: any = [decosZaak]; - expect(hasOtherActualVergunningOfSameType(vergunningen, vergunning)).toBe( + expect(hasOtherActualDecosZaakOfSameType(decosZaken, decosZaak)).toBe( false ); } { - const vergunningen: any = [ + const decosZaken: any = [ { caseType: 'test1', dateEnd: '2022-05-06', identifier: 'xx2' }, - vergunning, + decosZaak, ]; - expect(hasOtherActualVergunningOfSameType(vergunningen, vergunning)).toBe( + expect(hasOtherActualDecosZaakOfSameType(decosZaken, decosZaak)).toBe( false ); } @@ -234,7 +232,7 @@ describe('helpers/Vergunningen', () => { expect( isExcludedFromTransformation( { fields: { subject1: '*verwijder' } } as DecosZaakSource, - decosZaakTransformers[CaseTypeV2.AanbiedenDiensten] + decosCaseToZaakTransformers[CaseTypeV2.AanbiedenDiensten] ) ).toBe(true); }); @@ -243,7 +241,7 @@ describe('helpers/Vergunningen', () => { expect( isExcludedFromTransformation( { fields: { dfunction: 'buiten behandeling' } } as DecosZaakSource, - decosZaakTransformers[CaseTypeV2.AanbiedenDiensten] + decosCaseToZaakTransformers[CaseTypeV2.AanbiedenDiensten] ) ).toBe(true); }); @@ -258,7 +256,7 @@ describe('helpers/Vergunningen', () => { text12: 'wacht op online betaling', }, } as DecosZaakSource, - decosZaakTransformers[CaseTypeV2.WVOS] + decosCaseToZaakTransformers[CaseTypeV2.WVOS] ) ).toBe(true); }); @@ -271,7 +269,7 @@ describe('helpers/Vergunningen', () => { text45: CaseTypeV2.WVOS, }, } as DecosZaakSource, - { ...decosZaakTransformers[CaseTypeV2.WVOS], isActive: false } + { ...decosCaseToZaakTransformers[CaseTypeV2.WVOS], isActive: false } ) ).toBe(true); }); @@ -284,7 +282,7 @@ describe('helpers/Vergunningen', () => { text45: CaseTypeV2.WVOS, }, } as DecosZaakSource, - { ...decosZaakTransformers[CaseTypeV2.WVOS] } + { ...decosCaseToZaakTransformers[CaseTypeV2.WVOS] } ) ).toBe(false); }); @@ -304,7 +302,7 @@ describe('helpers/Vergunningen', () => { describe('getCustomTitleForVergunningWithLicensePlates', () => { test('Single kenteken title', () => { expect( - getCustomTitleForVergunningWithLicensePlates({ + getCustomTitleForDecosZaakWithLicensePlates({ title: 'blaap', kentekens: 'AA-BB-CC', } as TouringcarDagontheffing) @@ -313,7 +311,7 @@ describe('helpers/Vergunningen', () => { test('Multiple kenteken title', () => { expect( - getCustomTitleForVergunningWithLicensePlates({ + getCustomTitleForDecosZaakWithLicensePlates({ title: 'blaap', kentekens: 'AA-BB-CC | DDD-EE-F | ZZ-XX-00 | THJ-789-I', } as TouringcarDagontheffing) diff --git a/src/server/services/decos/helpers.ts b/src/server/services/decos/helpers.ts index c37e795963..f211b9e7d2 100644 --- a/src/server/services/decos/helpers.ts +++ b/src/server/services/decos/helpers.ts @@ -1,15 +1,9 @@ import { - DECOS_EXCLUDE_CASES_WITH_INVALID_DFUNCTION, - DECOS_EXCLUDE_CASES_WITH_PENDING_PAYMENT_CONFIRMATION_SUBJECT1, - DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT11, - DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT12, - DECOS_PENDING_REMOVAL_DFUNCTION, + DecosZaakTransformer, DecosZaakSource, - DecosZaakTypeTransformer, - NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END, - VergunningV2, -} from '../vergunningen-v2/config-and-types'; -import { decosZaakTransformers } from '../vergunningen-v2/decos-zaken'; + DecosZaakBase, + DecosZaakWithKentekens, +} from './decos-types'; import { defaultDateFormat, isDateInPast, @@ -17,6 +11,15 @@ import { } from '../../../universal/helpers/date'; import { DecosCaseType } from '../../../universal/types/vergunningen'; import { AuthProfileAndToken } from '../../auth/auth-types'; +import { + DECOS_EXCLUDE_CASES_WITH_INVALID_DFUNCTION, + DECOS_EXCLUDE_CASES_WITH_PENDING_PAYMENT_CONFIRMATION_SUBJECT1, + DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT11, + DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT12, + DECOS_PENDING_REMOVAL_DFUNCTION, + NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END, +} from '../vergunningen-v2/config-and-types'; +import { decosCaseToZaakTransformers } from '../vergunningen-v2/decos-zaken'; // Checks to see if a payment was not processed correctly/completely yet. export function isWaitingForPaymentConfirmation( @@ -37,7 +40,7 @@ export function isWaitingForPaymentConfirmation( ); return ( - !!decosZaakTransformers[zaakType]?.requirePayment && + !!decosCaseToZaakTransformers[zaakType]?.requirePayment && (isWaitingForPaymentConfirmation || isWaitingForPaymentConfirmation2) ); } @@ -60,7 +63,7 @@ export function isScheduledForRemoval(decosZaakSource: DecosZaakSource) { export function isExcludedFromTransformation( zaakSource: DecosZaakSource, - zaakTypeTransformer: DecosZaakTypeTransformer + zaakTypeTransformer: DecosZaakTransformer ) { return ( isScheduledForRemoval(zaakSource) || @@ -89,21 +92,23 @@ export function transformKenteken(kentekenSource: string | null) { return kentekenSource; } -export function getCustomTitleForVergunningWithLicensePlates( - vergunning: VergunningV2 +export function getCustomTitleForDecosZaakWithLicensePlates( + decosZaak: DecosZaakWithKentekens ) { - if ('kentekens' in vergunning) { - const plates = vergunning.kentekens?.split(' | '); + if ('kentekens' in decosZaak) { + const plates = decosZaak.kentekens?.split(' | '); if (plates?.length === 1) { - return `${vergunning.title} (${vergunning.kentekens})`; + return `${decosZaak.title} (${decosZaak.kentekens})`; } else if (!!plates && plates.length > 1) { - return `${vergunning.title} (${plates[0]}... +${plates.length - 1})`; + return `${decosZaak.title} (${plates[0]}... +${plates.length - 1})`; } } - return vergunning.title; + return decosZaak.title; } -export function getDecosZaakTypeFromSource(decosZaakSource: DecosZaakSource) { +export function getDecosZaakTypeFromSource( + decosZaakSource: T +) { return decosZaakSource.fields.text45 as DecosCaseType; } @@ -130,42 +135,42 @@ export function getUserKeysSearchQuery( } export function isNearEndDate( - vergunning: { dateEnd: string | null }, + decosZaak: { dateEnd: string | null }, dateNow: Date = new Date() ) { - if (!vergunning.dateEnd) { + if (!decosZaak.dateEnd) { return false; } - const monthsTillEnd = monthsFromNow(vergunning.dateEnd, dateNow); + const monthsTillEnd = monthsFromNow(decosZaak.dateEnd, dateNow); return ( - !isExpired(vergunning, dateNow) && + !isExpired(decosZaak, dateNow) && monthsTillEnd < NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END && monthsTillEnd >= 0 // Only show the notification if we have a long-running permit validity ); } export function isExpired( - vergunning: { dateEnd: string | null }, + decosZaak: { dateEnd: string | null }, dateNow: Date = new Date() ) { - if (!vergunning.dateEnd) { + if (!decosZaak.dateEnd) { return false; } - return isDateInPast(vergunning.dateEnd, dateNow); + return isDateInPast(decosZaak.dateEnd, dateNow); } -export function hasOtherActualVergunningOfSameType( - items: Array, - item: VergunningV2 +export function hasOtherActualDecosZaakOfSameType( + items: Array, + item: DecosZaakBase ): boolean { return items.some( - (otherVergunning: VergunningV2) => - otherVergunning.caseType === item.caseType && - otherVergunning.identifier !== item.identifier && - !isExpired(otherVergunning) + (otherDecosZaak: DecosZaakBase) => + otherDecosZaak.caseType === item.caseType && + otherDecosZaak.identifier !== item.identifier && + !isExpired(otherDecosZaak) ); } diff --git a/src/server/services/vergunningen-v2/config-and-types.ts b/src/server/services/vergunningen-v2/config-and-types.ts index 3f8a231da5..3d245c1504 100644 --- a/src/server/services/vergunningen-v2/config-and-types.ts +++ b/src/server/services/vergunningen-v2/config-and-types.ts @@ -1,14 +1,18 @@ -import { ApiResponse } from '../../../universal/helpers/api'; -import { - GenericDocument, - LinkProps, - ZaakDetail, -} from '../../../universal/types'; +import { LinkProps, ZaakDetail } from '../../../universal/types'; import { CaseTypeV2, DecosCaseType, GetCaseType, } from '../../../universal/types/vergunningen'; +import { + DecosZaakBase, + DecosZaakWithDateRange, + DecosZaakWithKentekens, + DecosZaakWithLocation, + DecosZaakWithDateTimeRange, + ZaakStatus, + ZakenFilter, +} from '../decos/decos-types'; export const NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END = 3; export const NOTIFICATION_MAX_MONTHS_TO_SHOW_EXPIRED = 3; @@ -39,233 +43,16 @@ export const DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT11 = 'nogniet'; export const DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT12 = 'wacht op online betaling'; -const adresBoekenBSN = - process.env.BFF_DECOS_API_ADRES_BOEKEN_BSN?.split(',') ?? []; - -const adresBoekenKVK = - process.env.BFF_DECOS_API_ADRES_BOEKEN_KVK?.split(',') ?? []; - -export const adresBoekenByProfileType: Record = { - private: adresBoekenBSN, - commercial: adresBoekenKVK, - 'private-attributes': [], -}; - -export const MA_DECISION_DEFAULT = 'Zie besluit'; - -export type ZaakKenmerk = `Z/${number}/${number}`; // Z/23/2230346 -export type DecosZaakID = string; -export type ZaakStatus = - | 'Ontvangen' - | 'In behandeling' - | 'Afgehandeld' - | string; - -type MADecision = string; -type DecosDecision = string; - -export type DecosFieldNameSource = string; -export type DecosFieldValue = string | number | boolean | null; - -type DecosTransformerOptions = { - zaakTypeTransformer?: DecosZaakTypeTransformer; - fetchDecosWorkflowDate?: ( - stepTitle: DecosWorkflowStepTitle - ) => Promise>; -}; - -export type DecosFieldTransformer = { - name: keyof T; - transform?: ( - input: any, - options?: DecosTransformerOptions - ) => DecosFieldValue; -}; - -export type DecosZaakTypeTransformer = { - // The caseType (zaaktype) of the sourceData. - caseType: DecosCaseType; - // Title of the VergunningV2, mostly a slightly different variant of the $caseType - title: string; - // A mapping object that can be used to assign a readable attribute to the data sent to the frontend. - // For example: date6 becomes dateStart. Additionally a function can be provided to perform some compute on the value assigned to the sourceField. - // For example String operations like, trim, split, uppercase etc. - transformFields?: Partial>; - // After transform is used to perform additional transformations after the initial transform. - // Business logic is implemented at this point, also async calls to other services to enrich the data can be done here. - afterTransform?: ( - vergunning: T, - decosZaakSource: DecosZaakSource, - options?: DecosTransformerOptions - ) => Promise; - // A function to check if the source data quality and/or prerequisites for showing the data to the user are valid. - // This function is run before transformation of the zaak. - hasValidSourceData?: (decosZaakSource: DecosZaakSource) => boolean; - // Indicate if the zaak requires payment to be processed and complete. This function is run before transformation of the zaak. - requirePayment?: boolean; - // Decision (resultaat) values are generalized here. For example. The sourceValues can be one of: `Toegekend met borden`, `Toegekend zonder dingen` which we want to show to the user as `Toegekend`. - decisionTranslations?: Record; - // The title of the workflow step that is used to find a date for the InBehandeling status. - dateInBehandelingWorkflowStepTitle?: string; - // Indicates if the Zaak should be shown to the user / is expected to be transformed. - isActive: boolean; - // Initially we request a set of fields to be included in the responseData (?select=). For some cases we need a (few) custom field(s) included in the initial response. - // For example to show a kenteken in the Notifications. Sadly the requested fields cannot be specified on a case basis. - // This means even though we do not want, for example, date7 for case A we will receive it anyway. - // We select a specific set of fields because otherwise we receive all the fields of a zaak which are bloated and mostly unused. - addToSelectFieldsBase?: string[]; - // Notifications for this specific - notificationLabels?: Partial; -}; - -type DecosZaakBase = { - // status - title: string; - // caseType - text45: DecosCaseType | string; - // decision - dfunction?: string | null; - // identifier / zaaknummer - mark: string; - // processed - processed: boolean; - // dateDecision - date5?: string | null; - // dateRequest - document_date: string; - // dateStart - date6?: string | null; - // dateEnd - date7?: string | null; - - subject1?: string; - // Info regarding possible payment - text11?: string | null; - // Info regarding possible payment - text12?: string | null; -}; - -type DecosDocumentBase = { - text39: string; - text40: string; - text41: string; - // identifier / zaaknummer - mark: string; - // datePublished - received_date: string; -}; - -type DecosDocumentBlobBase = { - // IS PDF - bol10: boolean; -}; - -export type DecosFieldsObject = Record< - DecosFieldNameSource, - string | boolean | null | number ->; - -export type DecosFieldTransformerObject = - Record | keyof T>; - -export type DecosZaakSource = { - key: DecosZaakID; - links: string[]; - fields: DecosZaakBase & DecosFieldsObject; -}; - -export type DecosDocumentSource = { - key: DecosZaakID; - links: string[]; - fields: DecosDocumentBase & DecosFieldsObject; -}; - -export type DecosDocumentBlobSource = { - key: DecosZaakID; - links: string[]; - fields: DecosDocumentBlobBase & DecosFieldsObject; -}; - -export type DecosZakenResponse = { - count: number; - content: T; -}; - -export type DecosResponse = { - itemDataResultSet: { - content: T[]; - }; -}; - -export type VergunningCaseTypeFilter = (vergunning: VergunningV2) => boolean; - -export type AddressBookEntry = { - key: string; -}; - -export type DecosWorkflowStepTitle = string; -export type DecosWorkflowStepDate = string; - -export interface VergunningBase { - caseType: DecosCaseType; - dateDecision: string | null; - dateInBehandeling: string | null; - dateRequest: string; - - // DateStart and DateEnd are not applicable to every single vergunning but general enough to but in base Type. - dateStart: string | null; - dateEnd: string | null; - - decision: string | null; - description: string; - - // Url to BFF Detail paga api - fetchUrl: string; - - identifier: ZaakKenmerk; - title: string; - id: string; - - // Decos key (uuid) used as primary id's in api communication. - key: string; - - processed: boolean; - status: ZaakStatus; - - paymentStatus: string | null; - paymentMethod: string | null; -} - -export interface VergunningWithLocation extends VergunningBase { - location: string | null; -} - -export interface VergunningWithKentekens extends VergunningBase { - kentekens: string | null; -} - -export interface VergunningWithDateRange extends VergunningBase { - dateStart: string | null; - dateEnd: string | null; -} - -export interface VergunningWithTimeRange extends VergunningBase { - timeStart: string | null; - timeEnd: string | null; -} - -export interface VergunningWithDateTimeRange - extends VergunningWithDateRange, - VergunningWithTimeRange {} +export type VergunningBase = DecosZaakBase; export interface TVMRVVObject - extends VergunningWithLocation, - VergunningWithDateTimeRange, - VergunningWithKentekens { + extends DecosZaakWithLocation, + DecosZaakWithDateTimeRange, + DecosZaakWithKentekens { caseType: GetCaseType<'TVMRVVObject'>; } -export interface GPK extends VergunningWithLocation { +export interface GPK extends DecosZaakWithLocation { caseType: GetCaseType<'GPK'>; cardType: 'driver' | 'passenger'; cardNumber: number | null; @@ -273,44 +60,44 @@ export interface GPK extends VergunningWithLocation { requestReason: string | null; } -export interface GPP extends VergunningWithLocation { +export interface GPP extends DecosZaakWithLocation { caseType: GetCaseType<'GPP'>; kentekens: string | null; } export interface EvenementMelding - extends VergunningWithLocation, - VergunningWithDateTimeRange { + extends DecosZaakWithLocation, + DecosZaakWithDateTimeRange { caseType: GetCaseType<'EvenementMelding'>; } export interface EvenementVergunning - extends VergunningWithLocation, - VergunningWithDateTimeRange { + extends DecosZaakWithLocation, + DecosZaakWithDateTimeRange { caseType: GetCaseType<'EvenementVergunning'>; } -export interface Omzettingsvergunning extends VergunningWithLocation { +export interface Omzettingsvergunning extends DecosZaakWithLocation { caseType: GetCaseType<'Omzettingsvergunning'>; dateInBehandeling: string | null; } export interface ERVV - extends VergunningWithLocation, - VergunningWithDateTimeRange { + extends DecosZaakWithLocation, + DecosZaakWithDateTimeRange { caseType: GetCaseType<'ERVV'>; } export interface VakantieverhuurVergunningaanvraag - extends VergunningWithLocation, - VergunningWithDateRange { + extends DecosZaakWithLocation, + DecosZaakWithDateRange { caseType: GetCaseType<'VakantieverhuurVergunningaanvraag'>; title: 'VergunningV2 vakantieverhuur'; decision: 'Verleend' | 'Ingetrokken'; } // BZB is short for Parkeerontheffingen Blauwe zone bedrijven -export interface BZB extends VergunningWithDateRange { +export interface BZB extends DecosZaakWithDateRange { caseType: GetCaseType<'BZB'>; companyName: string | null; numberOfPermits: string | null; @@ -318,47 +105,47 @@ export interface BZB extends VergunningWithDateRange { } // BZP is short for Parkeerontheffingen Blauwe zone particulieren -export interface BZP extends VergunningWithDateRange, VergunningWithKentekens { +export interface BZP extends DecosZaakWithDateRange, DecosZaakWithKentekens { caseType: GetCaseType<'BZP'>; kentekens: string | null; decision: string | null; } export interface Flyeren - extends VergunningWithLocation, - VergunningWithDateTimeRange { + extends DecosZaakWithLocation, + DecosZaakWithDateTimeRange { caseType: GetCaseType<'Flyeren'>; decision: 'Verleend' | 'Niet verleend' | 'Ingetrokken'; } export interface AanbiedenDiensten - extends VergunningWithLocation, - VergunningWithDateRange { + extends DecosZaakWithLocation, + DecosZaakWithDateRange { caseType: GetCaseType<'AanbiedenDiensten'>; } export interface Nachtwerkontheffing - extends VergunningWithLocation, - VergunningWithDateTimeRange { + extends DecosZaakWithLocation, + DecosZaakWithDateTimeRange { caseType: GetCaseType<'NachtwerkOntheffing'>; } export interface ZwaarVerkeer - extends VergunningWithKentekens, - VergunningWithDateRange { + extends DecosZaakWithKentekens, + DecosZaakWithDateRange { caseType: GetCaseType<'ZwaarVerkeer'>; exemptionKind: string | null; } export interface RVVHeleStad - extends VergunningWithKentekens, - VergunningWithDateRange { + extends DecosZaakWithKentekens, + DecosZaakWithDateRange { caseType: GetCaseType<'RVVHeleStad'>; } export interface RVVSloterweg - extends VergunningWithKentekens, - VergunningWithDateRange { + extends DecosZaakWithKentekens, + DecosZaakWithDateRange { caseType: GetCaseType<'RVVSloterweg'>; vorigeKentekens: string | null; dateWorkflowVerleend: string | null; @@ -369,49 +156,49 @@ export interface RVVSloterweg } export interface TouringcarDagontheffing - extends VergunningWithKentekens, - VergunningWithDateTimeRange { + extends DecosZaakWithKentekens, + DecosZaakWithDateTimeRange { caseType: GetCaseType<'TouringcarDagontheffing'>; destination: string | null; } export interface TouringcarJaarontheffing - extends VergunningWithKentekens, - VergunningWithDateRange { + extends DecosZaakWithKentekens, + DecosZaakWithDateRange { caseType: GetCaseType<'TouringcarJaarontheffing'>; destination: string | null; routetest: boolean; } -export interface Samenvoegingsvergunning extends VergunningWithLocation { +export interface Samenvoegingsvergunning extends DecosZaakWithLocation { caseType: GetCaseType<'Samenvoegingsvergunning'>; } -export interface Onttrekkingsvergunning extends VergunningWithLocation { +export interface Onttrekkingsvergunning extends DecosZaakWithLocation { caseType: GetCaseType<'Onttrekkingsvergunning'>; } -export interface OnttrekkingsvergunningSloop extends VergunningWithLocation { +export interface OnttrekkingsvergunningSloop extends DecosZaakWithLocation { caseType: GetCaseType<'OnttrekkingsvergunningSloop'>; } -export interface VormenVanWoonruimte extends VergunningWithLocation { +export interface VormenVanWoonruimte extends DecosZaakWithLocation { caseType: GetCaseType<'VormenVanWoonruimte'>; } -export interface Splitsingsvergunning extends VergunningWithLocation { +export interface Splitsingsvergunning extends DecosZaakWithLocation { caseType: GetCaseType<'Splitsingsvergunning'>; } export interface ExploitatieHorecabedrijf - extends VergunningWithLocation, - VergunningWithDateRange { + extends DecosZaakWithLocation, + DecosZaakWithDateRange { caseType: GetCaseType<'ExploitatieHorecabedrijf'>; dateStartPermit: string | null; numberOfPermits: string | null; } -export interface Ligplaatsvergunning extends VergunningWithLocation { +export interface Ligplaatsvergunning extends DecosZaakWithLocation { caseType: GetCaseType<'VOB'>; requestKind: string | null; reason: string | null; @@ -436,8 +223,8 @@ export type EigenParkeerplaatsRequestType = export interface EigenParkeerplaats extends VergunningBase, - VergunningWithKentekens, - VergunningWithDateRange { + DecosZaakWithKentekens, + DecosZaakWithDateRange { caseType: GetCaseType<'EigenParkeerplaats'>; vorigeKentekens: string | null; requestTypes: EigenParkeerplaatsRequestType[]; @@ -462,9 +249,9 @@ export type WVOSActiviteit = | 'Filmen'; export interface WerkzaamhedenEnVervoerOpStraat - extends VergunningWithLocation, - VergunningWithDateRange, - VergunningWithKentekens { + extends DecosZaakWithLocation, + DecosZaakWithDateRange, + DecosZaakWithKentekens { caseType: GetCaseType<'WVOS'>; werkzaamheden: WVOSActiviteit[]; } @@ -506,14 +293,10 @@ export type VergunningenSourceData = { status: 'OK' | 'ERROR'; }; -export type VergunningExpirable = VergunningV2 & { dateEnd?: string | null }; - -export type VergunningDocument = GenericDocument & { key: string }; - -export type VergunningenData = VergunningV2[]; +export type DecosZaakExpirable = VergunningV2 & { dateEnd?: string | null }; export interface VergunningOptions { - filter?: (vergunning: VergunningV2) => boolean; + filter?: ZakenFilter; appRoute: string | ((vergunning: VergunningV2) => string); } @@ -526,8 +309,6 @@ export type VergunningFrontendV2 = T & { isExpired?: boolean; } & ZaakDetail; -export type VergunningFilter = (vergunning: VergunningV2) => boolean; - export type NotificationProperty = | 'title' | 'description' diff --git a/src/server/services/vergunningen-v2/decos-zaken.ts b/src/server/services/vergunningen-v2/decos-zaken.ts index 7a839cb92f..f5cee67a15 100644 --- a/src/server/services/vergunningen-v2/decos-zaken.ts +++ b/src/server/services/vergunningen-v2/decos-zaken.ts @@ -4,10 +4,6 @@ import { AanbiedenDiensten as AanbiedenDienstenType, BZB as BZBType, BZP as BZPType, - DecosFieldNameSource, - DecosFieldTransformer, - DecosFieldTransformerObject, - DecosZaakTypeTransformer, ERVV, EigenParkeerplaatsOpheffen as EigenParkeerplaatsOpheffenType, EigenParkeerplaatsRequestType, @@ -31,17 +27,11 @@ import { TouringcarDagontheffing as TouringcarDagontheffingType, TouringcarJaarontheffing as TouringcarJaarontheffingType, VakantieverhuurVergunningaanvraag as VakantieverhuurVergunningaanvraagType, - VergunningV2, VormenVanWoonruimte as VormenVanWoonruimteType, WVOSActiviteit as WVOSActiviteitType, WerkzaamhedenEnVervoerOpStraat as WerkzaamhedenEnVervoerOpStraatType, ZwaarVerkeer as ZwaarVerkeerType, } from './config-and-types'; -import { - getCustomTitleForVergunningWithLicensePlates, - transformBoolean, - transformKenteken, -} from '../decos/helpers'; import { caseNotificationLabelsDefault, caseNotificationLabelsExpirables, @@ -53,12 +43,24 @@ import { CaseTypeV2, DecosCaseType, } from '../../../universal/types/vergunningen'; +import { + DecosZaakTransformer, + DecosFieldTransformer, + DecosFieldNameSource, + DecosFieldTransformerObject, + DecosZaakWithKentekens, +} from '../decos/decos-types'; +import { + getCustomTitleForDecosZaakWithLicensePlates, + transformBoolean, + transformKenteken, +} from '../decos/helpers'; const decision: DecosFieldTransformer = { name: 'decision', transform: (decision: string, options) => { const decisionTranslations = - options?.zaakTypeTransformer?.decisionTranslations; + options?.decosZaakTransformer?.decisionTranslations; if (decisionTranslations) { const maDecision = Object.entries(decisionTranslations).find( @@ -89,7 +91,7 @@ const description = 'description'; // 1 or multiple kenteken(s) const kentekens = { - name: 'kentekens' as keyof VergunningV2, // TODO: Can this be typed stricter without casting? + name: 'kentekens' as keyof DecosZaakWithKentekens, // TODO: Can this be typed stricter without casting? transform: transformKenteken, }; @@ -112,7 +114,7 @@ export const SELECT_FIELDS_TRANSFORM_BASE: Partial date7: dateEnd, }; -export const TVMRVVObject: DecosZaakTypeTransformer = { +export const TVMRVVObject: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.TVMRVVObject, title: 'Tijdelijke verkeersmaatregel (TVM-RVV-Object)', @@ -144,14 +146,14 @@ export const TVMRVVObject: DecosZaakTypeTransformer = { vergunning.dateEnd = vergunning.dateStart; } - vergunning.title = getCustomTitleForVergunningWithLicensePlates(vergunning); + vergunning.title = getCustomTitleForDecosZaakWithLicensePlates(vergunning); return vergunning; }, notificationLabels: caseNotificationLabelsExpirables, }; -export const VakantieverhuurVergunningaanvraag: DecosZaakTypeTransformer = +export const VakantieverhuurVergunningaanvraag: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.VakantieverhuurVergunningaanvraag, @@ -191,7 +193,7 @@ export const VakantieverhuurVergunningaanvraag: DecosZaakTypeTransformer = { +export const GPP: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.GPP, title: 'Vaste parkeerplaats voor gehandicapten (GPP)', @@ -201,7 +203,7 @@ export const GPP: DecosZaakTypeTransformer = { text8: location, }, async afterTransform(vergunning, decosZaakSource, options) { - vergunning.title = getCustomTitleForVergunningWithLicensePlates(vergunning); + vergunning.title = getCustomTitleForDecosZaakWithLicensePlates(vergunning); return vergunning; }, decisionTranslations: { @@ -211,7 +213,7 @@ export const GPP: DecosZaakTypeTransformer = { notificationLabels: caseNotificationLabelsDefault, }; -export const GPK: DecosZaakTypeTransformer = { +export const GPK: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.GPK, title: 'Europese gehandicaptenparkeerkaart (GPK)', @@ -242,32 +244,31 @@ export const GPK: DecosZaakTypeTransformer = { notificationLabels: caseNotificationLabelsExpirables, }; -export const EvenementMelding: DecosZaakTypeTransformer = - { - isActive: true, - caseType: CaseTypeV2.EvenementMelding, - title: 'Evenement melding', - transformFields: { - ...SELECT_FIELDS_TRANSFORM_BASE, - date6: dateStart, - date7: dateEnd, - text6: location, - text7: timeStart, - text8: timeEnd, - }, - decisionTranslations: { - Toegestaan: [ - 'Verleend', - 'Verleend (Bijzonder/Bewaren)', - 'Verleend zonder borden', - ], - 'Niet toegestaan': ['Niet verleend'], - '': ['Nog niet bekend', 'Nog niet bekend'], - }, - notificationLabels: caseNotificationLabelsDefault, - }; +export const EvenementMelding: DecosZaakTransformer = { + isActive: true, + caseType: CaseTypeV2.EvenementMelding, + title: 'Evenement melding', + transformFields: { + ...SELECT_FIELDS_TRANSFORM_BASE, + date6: dateStart, + date7: dateEnd, + text6: location, + text7: timeStart, + text8: timeEnd, + }, + decisionTranslations: { + Toegestaan: [ + 'Verleend', + 'Verleend (Bijzonder/Bewaren)', + 'Verleend zonder borden', + ], + 'Niet toegestaan': ['Niet verleend'], + '': ['Nog niet bekend', 'Nog niet bekend'], + }, + notificationLabels: caseNotificationLabelsDefault, +}; -export const EvenementVergunning: DecosZaakTypeTransformer = +export const EvenementVergunning: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.EvenementVergunning, @@ -287,7 +288,7 @@ export const EvenementVergunning: DecosZaakTypeTransformer = +export const Omzettingsvergunning: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.Omzettingsvergunning, @@ -304,7 +305,7 @@ export const Omzettingsvergunning: DecosZaakTypeTransformer = { +export const ERVV_TVM: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.Omzettingsvergunning, title: 'e-RVV (Gratis verkeersontheffing voor elektrisch goederenvervoer)', @@ -329,7 +330,7 @@ export const ERVV_TVM: DecosZaakTypeTransformer = { notificationLabels: caseNotificationLabelsExpirables, }; -export const BZP: DecosZaakTypeTransformer = { +export const BZP: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.BZP, title: CaseTypeV2.BZP, @@ -341,13 +342,13 @@ export const BZP: DecosZaakTypeTransformer = { text8: kentekens, }, async afterTransform(vergunning, decosZaakSource, options) { - vergunning.title = getCustomTitleForVergunningWithLicensePlates(vergunning); + vergunning.title = getCustomTitleForDecosZaakWithLicensePlates(vergunning); return vergunning; }, notificationLabels: caseNotificationLabelsExpirables, }; -export const BZB: DecosZaakTypeTransformer = { +export const BZB: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.BZB, title: CaseTypeV2.BZB, @@ -361,7 +362,7 @@ export const BZB: DecosZaakTypeTransformer = { notificationLabels: caseNotificationLabelsDefault, }; -export const Flyeren: DecosZaakTypeTransformer = { +export const Flyeren: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.Flyeren, title: 'Verspreiden reclamemateriaal (sampling)', @@ -377,21 +378,20 @@ export const Flyeren: DecosZaakTypeTransformer = { notificationLabels: caseNotificationLabelsDefault, }; -export const AanbiedenDiensten: DecosZaakTypeTransformer = - { - isActive: true, - caseType: CaseTypeV2.AanbiedenDiensten, - title: CaseTypeV2.AanbiedenDiensten, - transformFields: { - ...SELECT_FIELDS_TRANSFORM_BASE, - date6: dateStart, - date7: dateEnd, - text6: location, - }, - notificationLabels: caseNotificationLabelsDefault, - }; +export const AanbiedenDiensten: DecosZaakTransformer = { + isActive: true, + caseType: CaseTypeV2.AanbiedenDiensten, + title: CaseTypeV2.AanbiedenDiensten, + transformFields: { + ...SELECT_FIELDS_TRANSFORM_BASE, + date6: dateStart, + date7: dateEnd, + text6: location, + }, + notificationLabels: caseNotificationLabelsDefault, +}; -export const NachtwerkOntheffing: DecosZaakTypeTransformer = +export const NachtwerkOntheffing: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.NachtwerkOntheffing, @@ -413,7 +413,7 @@ export const NachtwerkOntheffing: DecosZaakTypeTransformer = { +export const ZwaarVerkeer: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.ZwaarVerkeer, decisionTranslations: { @@ -473,13 +473,13 @@ export const ZwaarVerkeer: DecosZaakTypeTransformer = { }, addToSelectFieldsBase: ['text49'], // kenteken, async afterTransform(vergunning, decosZaakSource, options) { - vergunning.title = getCustomTitleForVergunningWithLicensePlates(vergunning); + vergunning.title = getCustomTitleForDecosZaakWithLicensePlates(vergunning); return vergunning; }, notificationLabels: caseNotificationLabelsDefault, }; -export const Samenvoegingsvergunning: DecosZaakTypeTransformer = +export const Samenvoegingsvergunning: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.Samenvoegingsvergunning, @@ -493,7 +493,7 @@ export const Samenvoegingsvergunning: DecosZaakTypeTransformer = +export const Onttrekkingsvergunning: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.Onttrekkingsvergunning, @@ -507,7 +507,7 @@ export const Onttrekkingsvergunning: DecosZaakTypeTransformer = +export const OnttrekkingsvergunningSloop: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.OnttrekkingsvergunningSloop, @@ -521,7 +521,7 @@ export const OnttrekkingsvergunningSloop: DecosZaakTypeTransformer = +export const VormenVanWoonruimte: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.VormenVanWoonruimte, @@ -535,7 +535,7 @@ export const VormenVanWoonruimte: DecosZaakTypeTransformer = +export const Splitsingsvergunning: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.Splitsingsvergunning, @@ -548,24 +548,23 @@ export const Splitsingsvergunning: DecosZaakTypeTransformer = - { - isActive: true, - caseType: CaseTypeV2.VOB, - title: 'Ligplaatsvergunning', - dateInBehandelingWorkflowStepTitle: 'VOB - Beoordelen en besluiten', - transformFields: { - ...SELECT_FIELDS_TRANSFORM_BASE, - text9: { name: 'requestKind' }, - text18: { name: 'reason' }, - text6: location, - text10: { name: 'vesselKind' }, // soort vaartuig - text14: { name: 'vesselName' }, // naam vaartuig - }, - notificationLabels: caseNotificationLabelsDefault, - }; +export const VOBvergunning: DecosZaakTransformer = { + isActive: true, + caseType: CaseTypeV2.VOB, + title: 'Ligplaatsvergunning', + dateInBehandelingWorkflowStepTitle: 'VOB - Beoordelen en besluiten', + transformFields: { + ...SELECT_FIELDS_TRANSFORM_BASE, + text9: { name: 'requestKind' }, + text18: { name: 'reason' }, + text6: location, + text10: { name: 'vesselKind' }, // soort vaartuig + text14: { name: 'vesselName' }, // naam vaartuig + }, + notificationLabels: caseNotificationLabelsDefault, +}; -export const ExploitatieHorecabedrijf: DecosZaakTypeTransformer = +export const ExploitatieHorecabedrijf: DecosZaakTransformer = { isActive: FeatureToggle.horecaActive, caseType: CaseTypeV2.ExploitatieHorecabedrijf, @@ -581,7 +580,7 @@ export const ExploitatieHorecabedrijf: DecosZaakTypeTransformer = { +export const RVVHeleStad: DecosZaakTransformer = { isActive: !IS_PRODUCTION, caseType: CaseTypeV2.RVVHeleStad, title: 'RVV-verkeersontheffing', @@ -596,13 +595,13 @@ export const RVVHeleStad: DecosZaakTypeTransformer = { }, addToSelectFieldsBase: ['text49'], // Kenteken, async afterTransform(vergunning, decosZaakSource, options) { - vergunning.title = getCustomTitleForVergunningWithLicensePlates(vergunning); + vergunning.title = getCustomTitleForDecosZaakWithLicensePlates(vergunning); return vergunning; }, notificationLabels: caseNotificationLabelsExpirables, }; -export const RVVSloterweg: DecosZaakTypeTransformer = { +export const RVVSloterweg: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.RVVSloterweg, title: 'RVV ontheffing Sloterweg', @@ -629,7 +628,7 @@ export const RVVSloterweg: DecosZaakTypeTransformer = { await options?.fetchDecosWorkflowDate?.('Status naar actief'); if (dateVerleend) { - vergunning['processed'] = true; + vergunning.processed = true; // if the workflow verleend has run but there is no decision then its actually Verleend. // this decision (verleend) is not set by decos eventhough the actual permit is granted. // This is some hack to have an overview of active permits in the Decos back-office. @@ -643,7 +642,7 @@ export const RVVSloterweg: DecosZaakTypeTransformer = { !vergunning.processed && (vergunning.dateDecision || vergunning.decision) ) { - vergunning['processed'] = true; + vergunning.processed = true; } // Add zone to title @@ -658,7 +657,7 @@ export const RVVSloterweg: DecosZaakTypeTransformer = { notificationLabels: caseNotificationLabelsRevoke, }; -export const EigenParkeerplaats: DecosZaakTypeTransformer = +export const EigenParkeerplaats: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.EigenParkeerplaats, @@ -682,7 +681,7 @@ export const EigenParkeerplaats: DecosZaakTypeTransformer = +export const EigenParkeerplaatsOpheffen: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.EigenParkeerplaatsOpheffen, @@ -761,7 +760,7 @@ export const EigenParkeerplaatsOpheffen: DecosZaakTypeTransformer = +export const TouringcarDagontheffing: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.TouringcarDagontheffing, @@ -780,13 +779,13 @@ export const TouringcarDagontheffing: DecosZaakTypeTransformer = +export const TouringcarJaarontheffing: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.TouringcarJaarontheffing, @@ -807,14 +806,14 @@ export const TouringcarJaarontheffing: DecosZaakTypeTransformer = +export const WerkEnVervoerOpStraat: DecosZaakTransformer = { isActive: !IS_PRODUCTION, caseType: CaseTypeV2.WVOS, @@ -862,14 +861,14 @@ export const WerkEnVervoerOpStraat: DecosZaakTypeTransformer { - return [zaakTransformer.caseType, zaakTransformer] as const; -}); - -export const decosZaakTransformers = Object.fromEntries( - zaakTransformerEntries -) as Record; +]; +export const decosCaseToZaakTransformers = decosZaakTransformers.reduce( + (acc, zaakTransformer) => ({ + ...acc, + [zaakTransformer.caseType]: zaakTransformer, + }), + {} as Record> +); diff --git a/src/server/services/vergunningen-v2/vergunningen-notification-labels.ts b/src/server/services/vergunningen-v2/vergunningen-notification-labels.ts index 7219dfc94e..4ad6721947 100644 --- a/src/server/services/vergunningen-v2/vergunningen-notification-labels.ts +++ b/src/server/services/vergunningen-v2/vergunningen-notification-labels.ts @@ -3,7 +3,7 @@ import { subMonths } from 'date-fns'; import { NotificationLabels, RVVSloterweg, - VergunningExpirable, + DecosZaakExpirable, VergunningFrontendV2, } from './config-and-types'; import { dateFormat } from '../../../universal/helpers/date'; @@ -44,7 +44,7 @@ const statusAfgehandeld: NotificationLabels = { const verlooptBinnenkort: NotificationLabels = { title: (vergunning) => `Uw ${vergunning.title} loopt af`, description: (vergunning) => `Uw ${vergunning.title} loopt binnenkort af.`, - datePublished: (vergunning: VergunningExpirable) => + datePublished: (vergunning: DecosZaakExpirable) => vergunning.dateEnd ? dateFormat( subMonths( @@ -63,7 +63,7 @@ const verlooptBinnenkort: NotificationLabels = { const isVerlopen: NotificationLabels = { title: (vergunning) => `Uw ${vergunning.caseType} is verlopen`, description: (vergunning) => `Uw ${vergunning.title} is verlopen.`, - datePublished: (vergunning: VergunningExpirable) => vergunning.dateEnd, + datePublished: (vergunning: DecosZaakExpirable) => vergunning.dateEnd, link: (vergunning) => ({ title: `Vraag zonodig een nieuwe vergunning aan`, to: vergunning.link.to, diff --git a/src/server/services/vergunningen-v2/vergunningen-notifications.ts b/src/server/services/vergunningen-v2/vergunningen-notifications.ts index a2d6d6882e..be937ad51e 100644 --- a/src/server/services/vergunningen-v2/vergunningen-notifications.ts +++ b/src/server/services/vergunningen-v2/vergunningen-notifications.ts @@ -6,10 +6,10 @@ import { NotificationLabelByType, NotificationLabels, NotificationProperty, - VergunningFilter, VergunningFrontendV2, } from './config-and-types'; -import { decosZaakTransformers } from './decos-zaken'; +import { ZakenFilter } from '../decos/decos-types'; +import { decosCaseToZaakTransformers } from './decos-zaken'; import { isNearEndDate } from '../decos/helpers'; import { FILTER_VERGUNNINGEN_DEFAULT, @@ -86,7 +86,7 @@ export function createVergunningNotification( vergunningen: VergunningFrontendV2[], thema: Thema ): MyNotification | null { - const zaakTypeTransformer = decosZaakTransformers[vergunning.caseType]; + const zaakTypeTransformer = decosCaseToZaakTransformers[vergunning.caseType]; const labels = zaakTypeTransformer.notificationLabels; if (labels) { @@ -126,14 +126,12 @@ async function fetchVergunningenV2Notifications_( requestID: RequestID, authProfileAndToken: AuthProfileAndToken, appRoute: AppRoute = AppRoutes['VERGUNNINGEN/DETAIL'], - filter: VergunningFilter = FILTER_VERGUNNINGEN_DEFAULT, thema: Thema = Themas.VERGUNNINGEN ) { const VERGUNNINGEN = await fetchVergunningenV2( requestID, authProfileAndToken, - appRoute, - filter + appRoute ); if (VERGUNNINGEN.status === 'OK') { diff --git a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts index d64952432e..2dfe60be8a 100644 --- a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts +++ b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts @@ -1,6 +1,6 @@ import { Request, Response } from 'express'; -import { DecosZaakSource } from './config-and-types'; +import { DecosZaakSource } from '../decos/decos-types'; import { fetchDecosZaakFromSource, fetchDecosZakenFromSource, diff --git a/src/server/services/vergunningen-v2/vergunningen.ts b/src/server/services/vergunningen-v2/vergunningen.ts index c594c4ff5a..87f9488c98 100644 --- a/src/server/services/vergunningen-v2/vergunningen.ts +++ b/src/server/services/vergunningen-v2/vergunningen.ts @@ -4,15 +4,15 @@ import slug from 'slugme'; import { EXCLUDE_CASE_TYPES_FROM_VERGUNNINGEN_THEMA, - VergunningCaseTypeFilter, - VergunningDocument, - VergunningFilter, + VergunningBase, VergunningFrontendV2, - VergunningV2, } from './config-and-types'; +import { DecosZaakDocument, ZakenFilter } from '../decos/decos-types'; +import { VergunningV2 } from './config-and-types'; import { - fetchDecosVergunning, - fetchDecosVergunningen, + fetchDecosZaak, + fetchDecosZaken, + fetchDecosZaken_, } from '../decos/decos-service'; import { isExpired, toDateFormatted } from '../decos/helpers'; import { getStatusSteps } from './vergunningen-status-steps'; @@ -25,9 +25,13 @@ import { encryptSessionIdWithRouteIdParam } from '../../helpers/encrypt-decrypt' import { BffEndpoints } from '../../routing/bff-routes'; import { generateFullApiUrlBFF } from '../../routing/route-helpers'; import { decryptEncryptedRouteParamAndValidateSessionID } from '../shared/decrypt-route-param'; +import { + decosCaseToZaakTransformers, + decosZaakTransformers, +} from './decos-zaken'; -export const FILTER_VERGUNNINGEN_DEFAULT: VergunningFilter = ( - vergunning: VergunningV2 +export const FILTER_VERGUNNINGEN_DEFAULT: ZakenFilter = ( + vergunning: VergunningBase ) => { return !EXCLUDE_CASE_TYPES_FROM_VERGUNNINGEN_THEMA.includes( vergunning.caseType @@ -85,19 +89,19 @@ function transformVergunningFrontend( return vergunningFrontend; } -async function fetchAndFilterVergunningenV2_( +async function fetchVergunningenV2_( requestID: RequestID, authProfileAndToken: AuthProfileAndToken, - appRoute: AppRoute, - caseTypeFilter?: VergunningCaseTypeFilter + appRoute: AppRoute = AppRoutes['VERGUNNINGEN/DETAIL'] ) { - const response = await fetchDecosVergunningen(requestID, authProfileAndToken); + const response = await fetchDecosZaken_( + requestID, + authProfileAndToken, + decosZaakTransformers + ); if (response.status === 'OK') { let decosVergunningen = response.content; - if (caseTypeFilter) { - decosVergunningen = decosVergunningen.filter(caseTypeFilter); - } const vergunningenFrontend: VergunningFrontendV2[] = decosVergunningen.map( (vergunning) => transformVergunningFrontend( @@ -112,31 +116,14 @@ async function fetchAndFilterVergunningenV2_( return response; } -export const fetchAndFilterVergunningenV2 = memoizee( - fetchAndFilterVergunningenV2_, - { - maxAge: DEFAULT_API_CACHE_TTL_MS, - length: 4, - } -); +export const fetchVergunningenV2 = memoizee(fetchVergunningenV2_, { + maxAge: DEFAULT_API_CACHE_TTL_MS, + length: 4, +}); -export async function fetchVergunningenV2( - requestID: RequestID, - authProfileAndToken: AuthProfileAndToken, - appRoute: AppRoute = AppRoutes['VERGUNNINGEN/DETAIL'], - filter: VergunningFilter = FILTER_VERGUNNINGEN_DEFAULT -) { - return fetchAndFilterVergunningenV2( - requestID, - authProfileAndToken, - appRoute, - filter - ); -} - -function addEncryptedDocumentIdToUrl( +function setEncryptedDocumentDownloadUrl( sessionID: SessionID, - document: VergunningDocument + document: DecosZaakDocument ) { const documentIdEncrypted = encryptSessionIdWithRouteIdParam( sessionID, @@ -163,20 +150,24 @@ export async function fetchVergunningV2( ); if (decryptResult.status === 'OK') { - const response = await fetchDecosVergunning( + const response = await fetchDecosZaak( requestID, + decosZaakTransformers, decryptResult.content ); - if (response.status === 'OK' && response.content?.vergunning) { - const { vergunning, documents } = response.content; + if (response.status === 'OK' && response.content?.decosZaak) { + const { decosZaak, documents } = response.content; const documentsTransformed = documents.map((document) => - addEncryptedDocumentIdToUrl(authProfileAndToken.profile.sid, document) + setEncryptedDocumentDownloadUrl( + authProfileAndToken.profile.sid, + document + ) ); return apiSuccessResult({ vergunning: transformVergunningFrontend( authProfileAndToken.profile.id, - vergunning, + decosZaak, AppRoutes['VERGUNNINGEN/DETAIL'] ), documents: documentsTransformed, From 2efbd661397c10a04563aa5c3275a9e614a85ced Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Tue, 17 Dec 2024 12:11:53 +0100 Subject: [PATCH 03/11] Add tests for when no valid transformer is provided --- .../services/decos/decos-service.test.ts | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/server/services/decos/decos-service.test.ts b/src/server/services/decos/decos-service.test.ts index 2b78a33f74..09342edccf 100644 --- a/src/server/services/decos/decos-service.test.ts +++ b/src/server/services/decos/decos-service.test.ts @@ -650,12 +650,8 @@ describe('decos-service', () => { zaak ); expect(transformed).not.toBe(null); - expect(transformed !== null && 'werkzaamheden' in transformed).toBe(true); - expect( - transformed !== null && 'werkzaamheden' in transformed - ? transformed.werkzaamheden - : [] - ).toStrictEqual([ + expect(transformed!).toHaveProperty('werkzaamheden'); + expect((transformed as any).werkzaamheden).toStrictEqual([ 'Werkzaamheden verrichten in de nacht', 'Verhuizing tussen twee locaties binnen Amsterdam', ]); @@ -673,6 +669,16 @@ describe('decos-service', () => { ); expect(transformed?.decision).toBe('Zie besluit'); }); + + test('Null response when no valid transformer', async () => { + const zaak: DecosZaakSource = jsonCopy(zakenSource.content[0]); + const transformed = await forTesting.transformDecosZaakResponse( + reqID, + [], + zaak + ); + expect(transformed).toBe(null); + }); }); describe('transformDecosZakenResponse', () => { @@ -696,5 +702,15 @@ describe('decos-service', () => { zakenTransformed.map(({ identifier }) => identifier) ).toStrictEqual(['3 - xxx', '2 - xxx', '1 - xxx', '0 - xxx']); }); + + test('Empty response when no valid transformer', async () => { + const zaak: DecosZaakSource = jsonCopy(zakenSource.content[0]); + const zakenTransformed = await forTesting.transformDecosZakenResponse( + reqID, + [], + [zaak] + ); + expect(zakenTransformed).toStrictEqual([]); + }); }); }); From bcf9696459efe64fb064c11db7fd4be4b4c90d04 Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Tue, 17 Dec 2024 12:28:04 +0100 Subject: [PATCH 04/11] Replace a captureMessage for a comment for case that is no longer considered an error --- src/server/services/decos/decos-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/services/decos/decos-service.ts b/src/server/services/decos/decos-service.ts index 7e77afc375..42216c4bcd 100644 --- a/src/server/services/decos/decos-service.ts +++ b/src/server/services/decos/decos-service.ts @@ -250,8 +250,8 @@ async function transformDecosZakenResponse< (transformer) => transformer.caseType == zaakType ); + // exclude decosZaakSources that do not have a matching decosZaakTransformer if (!decosZaakTransformer) { - captureMessage(`Decos: ${zaakType} transformer not passed`); continue; } From ce1f5811a5be7747a4f2bafccb5eafaf16caaa47 Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Wed, 18 Dec 2024 16:06:42 +0100 Subject: [PATCH 05/11] Fetch decos zaken filtered by caseType --- .../services/decos/decos-service.test.ts | 21 ++++++ src/server/services/decos/decos-service.ts | 50 +++++++++++---- src/server/services/decos/decos-types.ts | 51 ++++++++++++++- .../services/vergunningen-v2/decos-zaken.ts | 64 +++---------------- .../vergunningen-route-handlers.ts | 4 +- .../services/vergunningen-v2/vergunningen.ts | 14 +--- 6 files changed, 122 insertions(+), 82 deletions(-) diff --git a/src/server/services/decos/decos-service.test.ts b/src/server/services/decos/decos-service.test.ts index 09342edccf..ee6598e2e0 100644 --- a/src/server/services/decos/decos-service.test.ts +++ b/src/server/services/decos/decos-service.test.ts @@ -501,6 +501,27 @@ describe('decos-service', () => { `); }); + test('Called with filter based on caseTypes', async () => { + remoteApi + .get(/\/decos\/items\/123456789\/folders/) + .query((queryObject) => { + return ( + queryObject.filter === + 'text45 eq Aanbieden van diensten or text45 eq GPK' + ); + }) + .times(numberOfAddressBooksToSearch) + .reply(200, zakenSource); + + const responseData = await forTesting.getZakenByUserKey( + reqID, + '123456789', + ['Aanbieden van diensten', 'GPK'] + ); + + expect(responseData.content?.length).toBe(1); + }); + test('Success', async () => { remoteApi .get(/\/decos\/items\/123456789\/folders/) diff --git a/src/server/services/decos/decos-service.ts b/src/server/services/decos/decos-service.ts index 42216c4bcd..a37aeeebc0 100644 --- a/src/server/services/decos/decos-service.ts +++ b/src/server/services/decos/decos-service.ts @@ -1,6 +1,8 @@ import memoizee from 'memoizee'; +import assert from 'assert'; import { + caseType, DecosZaakBase, DecosZaakTransformer, MA_DECISION_DEFAULT, @@ -37,11 +39,12 @@ import { getApiConfig } from '../../helpers/source-api-helpers'; import { requestData } from '../../helpers/source-api-request'; import { captureException, captureMessage } from '../monitoring'; import { DocumentDownloadData } from '../shared/document-download-route-handler'; +import { decosZaakTransformers } from '../vergunningen-v2/decos-zaken'; import { SELECT_FIELDS_META, SELECT_FIELDS_TRANSFORM_BASE, - decosCaseToZaakTransformers, -} from '../vergunningen-v2/decos-zaken'; +} from './decos-types'; +import { DecosCaseType } from '../../../universal/types/vergunningen'; /** * The Decos service ties responses of various api calls together and produces a set of transformed set of decosZaken. * @@ -284,21 +287,38 @@ async function transformDecosZakenResponse< .sort(sortAlpha('identifier', 'desc')); } -async function getZakenByUserKey(requestID: RequestID, userKey: string) { - const selectFieldsAllCases = Object.keys(SELECT_FIELDS_TRANSFORM_BASE); - const additionalSelectFields = Object.values( - decosCaseToZaakTransformers - ).flatMap((zaakTransformer) => zaakTransformer.addToSelectFieldsBase ?? []); +async function getZakenByUserKey( + requestID: RequestID, + userKey: string, + decosCaseTypes: DecosCaseType[] = [] +) { + const caseField = 'text45'; + assert( + SELECT_FIELDS_TRANSFORM_BASE[caseField] == caseType, + `getZakenByUserKey expects field ${caseField} to be the caseType` + ); - const selectFields = uniqueArray([ + const fields = uniqueArray([ ...SELECT_FIELDS_META, - ...selectFieldsAllCases, - ...additionalSelectFields, + ...Object.keys(SELECT_FIELDS_TRANSFORM_BASE), + ...decosZaakTransformers.flatMap( + (zaakTransformer) => zaakTransformer.addToSelectFieldsBase ?? [] + ), ]).join(','); + const caseTypes = decosCaseTypes + .map((caseType) => `${caseField} eq ${caseType}`) + .join(' or '); + + const decosUrlParams = [ + 'top=50', + fields ? `select=${fields}` : '', + caseTypes ? `filter=${caseTypes}` : '', + ].join('&'); + const apiConfig = getApiConfig('DECOS_API', { formatUrl: (config) => { - return `${config.url}/items/${userKey}/folders?top=50&select=${selectFields}`; + return `${config.url}/items/${userKey}/folders?${decosUrlParams}`; }, transformResponse: (responseData: DecosZakenResponse) => { if (!Array.isArray(responseData?.content)) { @@ -318,7 +338,8 @@ async function getZakenByUserKey(requestID: RequestID, userKey: string) { export async function fetchDecosZakenFromSource( requestID: RequestID, - authProfileAndToken: AuthProfileAndToken + authProfileAndToken: AuthProfileAndToken, + decosCaseTypes: DecosCaseType[] = [] ) { const userKeysResponse = await getUserKeys(requestID, authProfileAndToken); @@ -328,7 +349,7 @@ export async function fetchDecosZakenFromSource( const zakenSourceResponses = await Promise.allSettled( userKeysResponse.content.map((userKey) => - getZakenByUserKey(requestID, userKey) + getZakenByUserKey(requestID, userKey, decosCaseTypes) ) ); @@ -357,7 +378,8 @@ export async function fetchDecosZaken_< ): Promise | ApiErrorResponse> { const zakenSourceResponse = await fetchDecosZakenFromSource( requestID, - authProfileAndToken + authProfileAndToken, + zaakTypeTransformers.map((transformer) => transformer.caseType) ); if (zakenSourceResponse.status === 'OK') { diff --git a/src/server/services/decos/decos-types.ts b/src/server/services/decos/decos-types.ts index bb205753a5..9026328bd4 100644 --- a/src/server/services/decos/decos-types.ts +++ b/src/server/services/decos/decos-types.ts @@ -211,4 +211,53 @@ export interface DecosZaakWithTimeRange extends DecosZaakBase { } export interface DecosZaakWithDateTimeRange extends DecosZaakWithDateRange, - DecosZaakWithTimeRange {} + DecosZaakWithTimeRange {} // A list of common readable api attributes +const status = 'status'; +export const caseType = 'caseType'; +const identifier = 'identifier'; +const processed = 'processed'; +const dateDecision = 'dateDecision'; +const dateRequest = 'dateRequest'; +export const dateStart = 'dateStart'; +export const dateEnd = 'dateEnd'; +export const location = 'location'; +export const timeStart = 'timeStart'; +export const timeEnd = 'timeEnd'; +export const destination = 'destination'; +export const description = 'description'; +// Fields are selected per case initially but don't end up in the data we send to front end. +// These fields are fore example used to determine payment status. + +export const SELECT_FIELDS_META = ['text11', 'text12', 'subject1']; +// The set of field transforms that applies to every case. +// { $api_attribute_name_source: $api_attribute_name_mijn_amsterdam } + +export const decision: DecosFieldTransformer = { + name: 'decision', + transform: (decision: string, options) => { + const decisionTranslations = + options?.decosZaakTransformer?.decisionTranslations; + + if (decisionTranslations) { + const maDecision = Object.entries(decisionTranslations).find( + ([maDecision, decosDecisions]) => { + return decosDecisions.includes(decision); + } + )?.[0]; + return maDecision ?? decision; + } + return decision; + }, +}; + +export const SELECT_FIELDS_TRANSFORM_BASE: DecosFieldTransformerObject = { + title: status, + text45: caseType, + dfunction: decision, + mark: identifier, + processed: processed, + date5: dateDecision, + document_date: dateRequest, + date6: dateStart, + date7: dateEnd, +}; diff --git a/src/server/services/vergunningen-v2/decos-zaken.ts b/src/server/services/vergunningen-v2/decos-zaken.ts index f5cee67a15..3e8541c4b5 100644 --- a/src/server/services/vergunningen-v2/decos-zaken.ts +++ b/src/server/services/vergunningen-v2/decos-zaken.ts @@ -44,11 +44,17 @@ import { DecosCaseType, } from '../../../universal/types/vergunningen'; import { - DecosZaakTransformer, - DecosFieldTransformer, + dateEnd, + dateStart, DecosFieldNameSource, - DecosFieldTransformerObject, + DecosZaakTransformer, DecosZaakWithKentekens, + description, + destination, + location, + SELECT_FIELDS_TRANSFORM_BASE, + timeEnd, + timeStart, } from '../decos/decos-types'; import { getCustomTitleForDecosZaakWithLicensePlates, @@ -56,64 +62,12 @@ import { transformKenteken, } from '../decos/helpers'; -const decision: DecosFieldTransformer = { - name: 'decision', - transform: (decision: string, options) => { - const decisionTranslations = - options?.decosZaakTransformer?.decisionTranslations; - - if (decisionTranslations) { - const maDecision = Object.entries(decisionTranslations).find( - ([maDecision, decosDecisions]) => { - return decosDecisions.includes(decision); - } - )?.[0]; - return maDecision ?? decision; - } - return decision; - }, -}; - -// A list of common readable api attributes -const status = 'status'; -const caseType = 'caseType'; -const identifier = 'identifier'; -const processed = 'processed'; -const dateDecision = 'dateDecision'; -const dateRequest = 'dateRequest'; -const dateStart = 'dateStart'; -const dateEnd = 'dateEnd'; -const location = 'location'; -const timeStart = 'timeStart'; -const timeEnd = 'timeEnd'; -const destination = 'destination'; -const description = 'description'; - // 1 or multiple kenteken(s) const kentekens = { name: 'kentekens' as keyof DecosZaakWithKentekens, // TODO: Can this be typed stricter without casting? transform: transformKenteken, }; -// Fields are selected per case initially but don't end up in the data we send to front end. -// These fields are fore example used to determine payment status. -export const SELECT_FIELDS_META = ['text11', 'text12', 'subject1']; - -// The set of field transforms that applies to every case. -// { $api_attribute_name_source: $api_attribute_name_mijn_amsterdam } -export const SELECT_FIELDS_TRANSFORM_BASE: Partial = - { - title: status, - text45: caseType, - dfunction: decision, - mark: identifier, - processed: processed, - date5: dateDecision, - document_date: dateRequest, - date6: dateStart, - date7: dateEnd, - }; - export const TVMRVVObject: DecosZaakTransformer = { isActive: true, caseType: CaseTypeV2.TVMRVVObject, diff --git a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts index 2dfe60be8a..e4a8d787cd 100644 --- a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts +++ b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts @@ -13,6 +13,7 @@ import { generateFullApiUrlBFF, sendUnauthorized, } from '../../routing/route-helpers'; +import { decosZaakTransformers } from './decos-zaken'; export async function fetchVergunningDetail(req: Request, res: Response) { const authProfileAndToken = getAuth(req); @@ -63,7 +64,8 @@ export async function fetchZakenFromSource( const zakenResponseData = await fetchDecosZakenFromSource( res.locals.requestID, - authProfileAndToken + authProfileAndToken, + decosZaakTransformers.map((transformer) => transformer.caseType) ); if (zakenResponseData.status === 'OK') { diff --git a/src/server/services/vergunningen-v2/vergunningen.ts b/src/server/services/vergunningen-v2/vergunningen.ts index 87f9488c98..cb8243fb1d 100644 --- a/src/server/services/vergunningen-v2/vergunningen.ts +++ b/src/server/services/vergunningen-v2/vergunningen.ts @@ -9,11 +9,7 @@ import { } from './config-and-types'; import { DecosZaakDocument, ZakenFilter } from '../decos/decos-types'; import { VergunningV2 } from './config-and-types'; -import { - fetchDecosZaak, - fetchDecosZaken, - fetchDecosZaken_, -} from '../decos/decos-service'; +import { fetchDecosZaak, fetchDecosZaken } from '../decos/decos-service'; import { isExpired, toDateFormatted } from '../decos/helpers'; import { getStatusSteps } from './vergunningen-status-steps'; import { AppRoute, AppRoutes } from '../../../universal/config/routes'; @@ -25,10 +21,7 @@ import { encryptSessionIdWithRouteIdParam } from '../../helpers/encrypt-decrypt' import { BffEndpoints } from '../../routing/bff-routes'; import { generateFullApiUrlBFF } from '../../routing/route-helpers'; import { decryptEncryptedRouteParamAndValidateSessionID } from '../shared/decrypt-route-param'; -import { - decosCaseToZaakTransformers, - decosZaakTransformers, -} from './decos-zaken'; +import { decosZaakTransformers } from './decos-zaken'; export const FILTER_VERGUNNINGEN_DEFAULT: ZakenFilter = ( vergunning: VergunningBase @@ -94,7 +87,7 @@ async function fetchVergunningenV2_( authProfileAndToken: AuthProfileAndToken, appRoute: AppRoute = AppRoutes['VERGUNNINGEN/DETAIL'] ) { - const response = await fetchDecosZaken_( + const response = await fetchDecosZaken( requestID, authProfileAndToken, decosZaakTransformers @@ -118,7 +111,6 @@ async function fetchVergunningenV2_( export const fetchVergunningenV2 = memoizee(fetchVergunningenV2_, { maxAge: DEFAULT_API_CACHE_TTL_MS, - length: 4, }); function setEncryptedDocumentDownloadUrl( From da56e8306fcf9bac3ade9850980e53f357bb42cb Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Wed, 18 Dec 2024 16:20:45 +0100 Subject: [PATCH 06/11] Remove last reference to vergunningen-v2 from decos-service --- .../services/decos/decos-service.test.ts | 10 ++++- src/server/services/decos/decos-service.ts | 45 ++++++++++--------- .../vergunningen-route-handlers.ts | 2 +- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/server/services/decos/decos-service.test.ts b/src/server/services/decos/decos-service.test.ts index ee6598e2e0..ef0eba23fe 100644 --- a/src/server/services/decos/decos-service.test.ts +++ b/src/server/services/decos/decos-service.test.ts @@ -15,7 +15,10 @@ import { import { remoteApi } from '../../../testing/utils'; import { jsonCopy, range } from '../../../universal/helpers/utils'; import { AuthProfileAndToken } from '../../auth/auth-types'; -import { decosZaakTransformers } from '../vergunningen-v2/decos-zaken'; +import { + decosCaseToZaakTransformers, + decosZaakTransformers, +} from '../vergunningen-v2/decos-zaken'; const zakenSource = { count: 1, @@ -516,7 +519,10 @@ describe('decos-service', () => { const responseData = await forTesting.getZakenByUserKey( reqID, '123456789', - ['Aanbieden van diensten', 'GPK'] + [ + decosCaseToZaakTransformers['Aanbieden van diensten'], + decosCaseToZaakTransformers.GPK, + ] ); expect(responseData.content?.length).toBe(1); diff --git a/src/server/services/decos/decos-service.ts b/src/server/services/decos/decos-service.ts index a37aeeebc0..1cdec4c4d0 100644 --- a/src/server/services/decos/decos-service.ts +++ b/src/server/services/decos/decos-service.ts @@ -1,22 +1,24 @@ +import assert from 'assert'; + import memoizee from 'memoizee'; -import assert from 'assert'; import { caseType, DecosZaakBase, DecosZaakTransformer, MA_DECISION_DEFAULT, -} from './decos-types'; -import { adresBoekenByProfileType } from './decos-types'; -import { AddressBookEntry } from './decos-types'; -import { DecosFieldValue } from './decos-types'; -import { DecosZaakDocument } from './decos-types'; -import { DecosWorkflowStepDate, DecosWorkflowStepTitle } from './decos-types'; -import { + adresBoekenByProfileType, + AddressBookEntry, + DecosFieldValue, + DecosZaakDocument, + DecosWorkflowStepDate, + DecosWorkflowStepTitle, DecosDocumentBlobSource, DecosDocumentSource, DecosZaakSource, DecosZakenResponse, + SELECT_FIELDS_META, + SELECT_FIELDS_TRANSFORM_BASE, } from './decos-types'; import { getDecosZaakTypeFromSource, @@ -30,6 +32,7 @@ import { getSettledResult, } from '../../../universal/helpers/api'; import { sortAlpha, uniqueArray } from '../../../universal/helpers/utils'; +import { DecosCaseType } from '../../../universal/types/vergunningen'; import { AuthProfileAndToken } from '../../auth/auth-types'; import { DataRequestConfig, @@ -39,12 +42,6 @@ import { getApiConfig } from '../../helpers/source-api-helpers'; import { requestData } from '../../helpers/source-api-request'; import { captureException, captureMessage } from '../monitoring'; import { DocumentDownloadData } from '../shared/document-download-route-handler'; -import { decosZaakTransformers } from '../vergunningen-v2/decos-zaken'; -import { - SELECT_FIELDS_META, - SELECT_FIELDS_TRANSFORM_BASE, -} from './decos-types'; -import { DecosCaseType } from '../../../universal/types/vergunningen'; /** * The Decos service ties responses of various api calls together and produces a set of transformed set of decosZaken. * @@ -290,7 +287,10 @@ async function transformDecosZakenResponse< async function getZakenByUserKey( requestID: RequestID, userKey: string, - decosCaseTypes: DecosCaseType[] = [] + zaakTypeTransformers: Pick< + DecosZaakTransformer, + 'addToSelectFieldsBase' | 'caseType' + >[] = [] ) { const caseField = 'text45'; assert( @@ -301,13 +301,13 @@ async function getZakenByUserKey( const fields = uniqueArray([ ...SELECT_FIELDS_META, ...Object.keys(SELECT_FIELDS_TRANSFORM_BASE), - ...decosZaakTransformers.flatMap( + ...zaakTypeTransformers.flatMap( (zaakTransformer) => zaakTransformer.addToSelectFieldsBase ?? [] ), ]).join(','); - const caseTypes = decosCaseTypes - .map((caseType) => `${caseField} eq ${caseType}`) + const caseTypes = zaakTypeTransformers + .map((transformer) => `${caseField} eq ${transformer.caseType}`) .join(' or '); const decosUrlParams = [ @@ -339,7 +339,10 @@ async function getZakenByUserKey( export async function fetchDecosZakenFromSource( requestID: RequestID, authProfileAndToken: AuthProfileAndToken, - decosCaseTypes: DecosCaseType[] = [] + zaakTypeTransformers: Pick< + DecosZaakTransformer, + 'addToSelectFieldsBase' | 'caseType' + >[] = [] ) { const userKeysResponse = await getUserKeys(requestID, authProfileAndToken); @@ -349,7 +352,7 @@ export async function fetchDecosZakenFromSource( const zakenSourceResponses = await Promise.allSettled( userKeysResponse.content.map((userKey) => - getZakenByUserKey(requestID, userKey, decosCaseTypes) + getZakenByUserKey(requestID, userKey, zaakTypeTransformers) ) ); @@ -379,7 +382,7 @@ export async function fetchDecosZaken_< const zakenSourceResponse = await fetchDecosZakenFromSource( requestID, authProfileAndToken, - zaakTypeTransformers.map((transformer) => transformer.caseType) + zaakTypeTransformers ); if (zakenSourceResponse.status === 'OK') { diff --git a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts index e4a8d787cd..eae8fa6a2f 100644 --- a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts +++ b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts @@ -65,7 +65,7 @@ export async function fetchZakenFromSource( const zakenResponseData = await fetchDecosZakenFromSource( res.locals.requestID, authProfileAndToken, - decosZaakTransformers.map((transformer) => transformer.caseType) + decosZaakTransformers ); if (zakenResponseData.status === 'OK') { From 5939ebbba6f450eb8cb3269c5338f6431408719a Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Wed, 18 Dec 2024 16:22:43 +0100 Subject: [PATCH 07/11] Remove unused imports and apply lintfixes for decos and vergunninge-v2 --- src/server/services/decos/decos-service.ts | 1 - .../vergunningen-v2/vergunningen-notifications.ts | 4 +--- .../vergunningen-v2/vergunningen-route-handlers.ts | 12 ++++++------ src/server/services/vergunningen-v2/vergunningen.ts | 10 +++++----- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/server/services/decos/decos-service.ts b/src/server/services/decos/decos-service.ts index 1cdec4c4d0..e23b166398 100644 --- a/src/server/services/decos/decos-service.ts +++ b/src/server/services/decos/decos-service.ts @@ -32,7 +32,6 @@ import { getSettledResult, } from '../../../universal/helpers/api'; import { sortAlpha, uniqueArray } from '../../../universal/helpers/utils'; -import { DecosCaseType } from '../../../universal/types/vergunningen'; import { AuthProfileAndToken } from '../../auth/auth-types'; import { DataRequestConfig, diff --git a/src/server/services/vergunningen-v2/vergunningen-notifications.ts b/src/server/services/vergunningen-v2/vergunningen-notifications.ts index be937ad51e..9075ae5a99 100644 --- a/src/server/services/vergunningen-v2/vergunningen-notifications.ts +++ b/src/server/services/vergunningen-v2/vergunningen-notifications.ts @@ -8,11 +8,8 @@ import { NotificationProperty, VergunningFrontendV2, } from './config-and-types'; -import { ZakenFilter } from '../decos/decos-types'; import { decosCaseToZaakTransformers } from './decos-zaken'; -import { isNearEndDate } from '../decos/helpers'; import { - FILTER_VERGUNNINGEN_DEFAULT, fetchVergunningenV2, } from './vergunningen'; import { AppRoute, AppRoutes } from '../../../universal/config/routes'; @@ -25,6 +22,7 @@ import { isRecentNotification } from '../../../universal/helpers/utils'; import { MyNotification } from '../../../universal/types'; import { AuthProfileAndToken } from '../../auth/auth-types'; import { DEFAULT_API_CACHE_TTL_MS } from '../../config/source-api'; +import { isNearEndDate } from '../decos/helpers'; // prettier-ignore export function getNotificationLabels( diff --git a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts index eae8fa6a2f..2ad8102167 100644 --- a/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts +++ b/src/server/services/vergunningen-v2/vergunningen-route-handlers.ts @@ -1,10 +1,6 @@ import { Request, Response } from 'express'; -import { DecosZaakSource } from '../decos/decos-types'; -import { - fetchDecosZaakFromSource, - fetchDecosZakenFromSource, -} from '../decos/decos-service'; +import { decosZaakTransformers } from './decos-zaken'; import { fetchVergunningV2 } from './vergunningen'; import { apiSuccessResult } from '../../../universal/helpers/api'; import { getAuth } from '../../auth/auth-helpers'; @@ -13,7 +9,11 @@ import { generateFullApiUrlBFF, sendUnauthorized, } from '../../routing/route-helpers'; -import { decosZaakTransformers } from './decos-zaken'; +import { + fetchDecosZaakFromSource, + fetchDecosZakenFromSource, +} from '../decos/decos-service'; +import { DecosZaakSource } from '../decos/decos-types'; export async function fetchVergunningDetail(req: Request, res: Response) { const authProfileAndToken = getAuth(req); diff --git a/src/server/services/vergunningen-v2/vergunningen.ts b/src/server/services/vergunningen-v2/vergunningen.ts index cb8243fb1d..fb5e646713 100644 --- a/src/server/services/vergunningen-v2/vergunningen.ts +++ b/src/server/services/vergunningen-v2/vergunningen.ts @@ -7,10 +7,8 @@ import { VergunningBase, VergunningFrontendV2, } from './config-and-types'; -import { DecosZaakDocument, ZakenFilter } from '../decos/decos-types'; import { VergunningV2 } from './config-and-types'; -import { fetchDecosZaak, fetchDecosZaken } from '../decos/decos-service'; -import { isExpired, toDateFormatted } from '../decos/helpers'; +import { decosZaakTransformers } from './decos-zaken'; import { getStatusSteps } from './vergunningen-status-steps'; import { AppRoute, AppRoutes } from '../../../universal/config/routes'; import { apiSuccessResult } from '../../../universal/helpers/api'; @@ -20,8 +18,10 @@ import { DEFAULT_API_CACHE_TTL_MS } from '../../config/source-api'; import { encryptSessionIdWithRouteIdParam } from '../../helpers/encrypt-decrypt'; import { BffEndpoints } from '../../routing/bff-routes'; import { generateFullApiUrlBFF } from '../../routing/route-helpers'; +import { fetchDecosZaak, fetchDecosZaken } from '../decos/decos-service'; +import { DecosZaakDocument, ZakenFilter } from '../decos/decos-types'; +import { isExpired, toDateFormatted } from '../decos/helpers'; import { decryptEncryptedRouteParamAndValidateSessionID } from '../shared/decrypt-route-param'; -import { decosZaakTransformers } from './decos-zaken'; export const FILTER_VERGUNNINGEN_DEFAULT: ZakenFilter = ( vergunning: VergunningBase @@ -94,7 +94,7 @@ async function fetchVergunningenV2_( ); if (response.status === 'OK') { - let decosVergunningen = response.content; + const decosVergunningen = response.content; const vergunningenFrontend: VergunningFrontendV2[] = decosVergunningen.map( (vergunning) => transformVergunningFrontend( From 71c63f295b0e6ced4265dd6656d1a0c91d1db76f Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Thu, 19 Dec 2024 11:26:51 +0100 Subject: [PATCH 08/11] Remove anys and pass decosTypeTransformer to isWaitingForPaymentConfirmation --- .../services/decos/decos-service.test.ts | 32 ++++--- src/server/services/decos/decos-types.ts | 21 +++++ src/server/services/decos/helpers.test.ts | 86 ++++++++++++------- src/server/services/decos/helpers.ts | 28 +++--- .../toeristische-verhuur-notifications.ts | 2 +- .../vergunningen-v2/config-and-types.ts | 22 ----- 6 files changed, 106 insertions(+), 85 deletions(-) diff --git a/src/server/services/decos/decos-service.test.ts b/src/server/services/decos/decos-service.test.ts index ef0eba23fe..2aa757c9d5 100644 --- a/src/server/services/decos/decos-service.test.ts +++ b/src/server/services/decos/decos-service.test.ts @@ -15,6 +15,7 @@ import { import { remoteApi } from '../../../testing/utils'; import { jsonCopy, range } from '../../../universal/helpers/utils'; import { AuthProfileAndToken } from '../../auth/auth-types'; +import type { WerkzaamhedenEnVervoerOpStraat } from '../vergunningen-v2/config-and-types'; import { decosCaseToZaakTransformers, decosZaakTransformers, @@ -241,8 +242,9 @@ describe('decos-service', () => { const workflowInstance2: typeof workflowInstance = jsonCopy(workflowInstance); - const [instance1] = workflowInstance2.content; - delete (instance1 as any).fields.text7; + const instance1 = workflowInstance2.content[0]; + const fields = instance1.fields as Partial; + delete fields.text7; remoteApi .get(/\/decos\/items\/123-abc-000\/workflowlinkinstances/) @@ -421,25 +423,25 @@ describe('decos-service', () => { test('Niet definitief', () => { const isValid = forTesting.filterValidDocument({ fields: { text39: 'foo.bar' }, - } as any); + } as unknown as DecosDocumentSource); expect(isValid).toBe(false); }); test('Niet openbaar', () => { const isValid = forTesting.filterValidDocument({ fields: { text39: 'definitief', text40: 'geheim' }, - } as any); + } as unknown as DecosDocumentSource); expect(isValid).toBe(false); }); test('Niet van toepassing', () => { const isValid = forTesting.filterValidDocument({ fields: { text39: 'definitief', text40: 'openbaar', text41: 'nvt' }, - } as any); + } as unknown as DecosDocumentSource); expect(isValid).toBe(false); }); test('Alles ok!', () => { const isValid = forTesting.filterValidDocument({ fields: { text39: 'definitief', text40: 'openbaar', text41: 'ok' }, - } as any); + } as unknown as DecosDocumentSource); expect(isValid).toBe(true); }); }); @@ -566,7 +568,8 @@ describe('decos-service', () => { test('No date', () => { const workflowInstance2: typeof workflowInstance = jsonCopy(workflowInstance); - delete (workflowInstance2 as any).content[0].fields.date1; + const fields = workflowInstance2.content[0].fields; + delete (fields as Partial).date1; const date = forTesting.transformDecosWorkflowDateResponse( 'Zaak - behandelen', @@ -671,14 +674,15 @@ describe('decos-service', () => { zaak.fields.bol17 = true; zaak.fields.bol8 = true; - const transformed = await forTesting.transformDecosZaakResponse( - reqID, - decosZaakTransformers, - zaak - ); + const transformed: WerkzaamhedenEnVervoerOpStraat | null = + await forTesting.transformDecosZaakResponse( + reqID, + decosZaakTransformers, + zaak + ); expect(transformed).not.toBe(null); - expect(transformed!).toHaveProperty('werkzaamheden'); - expect((transformed as any).werkzaamheden).toStrictEqual([ + expect(transformed).toHaveProperty('werkzaamheden'); + expect(transformed?.werkzaamheden).toStrictEqual([ 'Werkzaamheden verrichten in de nacht', 'Verhuizing tussen twee locaties binnen Amsterdam', ]); diff --git a/src/server/services/decos/decos-types.ts b/src/server/services/decos/decos-types.ts index 9026328bd4..b56266dee4 100644 --- a/src/server/services/decos/decos-types.ts +++ b/src/server/services/decos/decos-types.ts @@ -261,3 +261,24 @@ export const SELECT_FIELDS_TRANSFORM_BASE: DecosFieldTransformerObject = { date6: dateStart, date7: dateEnd, }; +// Cases with this one of these dfunction values will not be included in the cases shown to the user. + +export const DECOS_EXCLUDE_CASES_WITH_INVALID_DFUNCTION = [ + 'buiten behandeling', + 'geannuleerd', + 'geen aanvraag of dubbel', +]; +// Cases with one of these subject1 values will not be included in the cases shown to the user. Payment is not yet processed or failed. + +export const DECOS_EXCLUDE_CASES_WITH_PENDING_PAYMENT_CONFIRMATION_SUBJECT1 = [ + 'wacht op online betaling', + 'wacht op ideal betaling', +]; +// Cases with this dfunction value will not be included in the cases shown to the user. + +export const DECOS_PENDING_REMOVAL_DFUNCTION = '*verwijder'; +// Cases with this text11 value will not be included in the cases shown to the user. Payment is not yet processed or failed. +export const DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT11 = 'nogniet'; +// Cases with this text12 value will not be included in the cases shown to the user. Payment is not yet processed or failed. +export const DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT12 = + 'wacht op online betaling'; diff --git a/src/server/services/decos/helpers.test.ts b/src/server/services/decos/helpers.test.ts index 6e774d2078..e056afbad1 100644 --- a/src/server/services/decos/helpers.test.ts +++ b/src/server/services/decos/helpers.test.ts @@ -1,4 +1,8 @@ -import { DecosZaakSource } from './decos-types'; +import { + DecosZaakBase, + DecosZaakSource, + DecosZaakTransformer, +} from './decos-types'; import { getCustomTitleForDecosZaakWithLicensePlates, getDecosZaakTypeFromSource, @@ -24,21 +28,21 @@ describe('helpers/decos', () => { test('isNearEndDate', () => { { // No end date - const decosZaak: any = { + const decosZaak = { dateEnd: null, }; expect(isNearEndDate(decosZaak)).toBe(false); } { // Near end - const decosZaak: any = { + const decosZaak = { dateEnd: '2022-10-28', }; expect(isNearEndDate(decosZaak)).toBe(true); } { // Not near end - const decosZaak: any = { + const decosZaak = { dateEnd: '2023-10-28', }; expect(isNearEndDate(decosZaak)).toBe(false); @@ -48,32 +52,32 @@ describe('helpers/decos', () => { test('isExpired', () => { { // Not expired - const decosZaak: any = { + const decosZaak = { dateEnd: '2023-10-28', }; expect(isExpired(decosZaak)).toBe(false); } { - const decosZaak: any = { + const decosZaak = { dateEnd: '2022-10-07', }; expect(isExpired(decosZaak)).toBe(false); } { // Is expired - const decosZaak: any = { + const decosZaak = { dateEnd: '2022-10-06', }; expect(isExpired(decosZaak)).toBe(true); } { - const decosZaak: any = { + const decosZaak = { dateEnd: '2022-10-06', }; expect(isExpired(decosZaak)).toBe(true); } { - const decosZaak: any = { + const decosZaak = { dateEnd: '2022-09-28', }; expect(isExpired(decosZaak)).toBe(true); @@ -81,17 +85,17 @@ describe('helpers/decos', () => { }); test('hasOtherActualDecosZaakOfSameType', () => { - const decosZaak: any = { + const decosZaak = { caseType: 'test1', dateEnd: null, identifier: 'xx1', - }; + } as unknown as DecosZaakBase; { - const decosZaken: any = [ + const decosZaken = [ { caseType: 'test1', dateEnd: null, identifier: 'xx2' }, decosZaak, - ]; + ] as unknown as DecosZaakBase[]; expect(hasOtherActualDecosZaakOfSameType(decosZaken, decosZaak)).toBe( true @@ -99,7 +103,7 @@ describe('helpers/decos', () => { } { - const decosZaken: any = [decosZaak]; + const decosZaken = [decosZaak]; expect(hasOtherActualDecosZaakOfSameType(decosZaken, decosZaak)).toBe( false @@ -107,10 +111,10 @@ describe('helpers/decos', () => { } { - const decosZaken: any = [ + const decosZaken = [ { caseType: 'test1', dateEnd: '2022-05-06', identifier: 'xx2' }, decosZaak, - ]; + ] as unknown as DecosZaakBase[]; expect(hasOtherActualDecosZaakOfSameType(decosZaken, decosZaak)).toBe( false @@ -128,7 +132,12 @@ describe('helpers/decos', () => { }, } as DecosZaakSource; - expect(isWaitingForPaymentConfirmation(zaak)).toBe(true); + expect( + isWaitingForPaymentConfirmation( + zaak, + decosCaseToZaakTransformers['Werk en vervoer op straat'] + ) + ).toBe(true); }); test('Is not waiting: wrong casetype', () => { @@ -140,7 +149,12 @@ describe('helpers/decos', () => { }, } as DecosZaakSource; - expect(isWaitingForPaymentConfirmation(zaak)).toBe(false); + expect( + isWaitingForPaymentConfirmation( + zaak, + {} as unknown as DecosZaakTransformer + ) + ).toBe(false); }); test('Is not waiting', () => { @@ -152,7 +166,12 @@ describe('helpers/decos', () => { }, } as DecosZaakSource; - expect(isWaitingForPaymentConfirmation(zaak)).toBe(false); + expect( + isWaitingForPaymentConfirmation( + zaak, + decosCaseToZaakTransformers['Werk en vervoer op straat'] + ) + ).toBe(false); }); test('Is still waiting', () => { @@ -163,7 +182,12 @@ describe('helpers/decos', () => { }, } as DecosZaakSource; - expect(isWaitingForPaymentConfirmation(zaak)).toBe(true); + expect( + isWaitingForPaymentConfirmation( + zaak, + decosCaseToZaakTransformers['Werk en vervoer op straat'] + ) + ).toBe(true); }); }); @@ -323,7 +347,7 @@ describe('helpers/decos', () => { expect( getDecosZaakTypeFromSource({ fields: { text45: 'Werk en vervoer op straat' }, - } as any) + } as unknown as DecosZaakSource) ).toBe(CaseTypeV2.WVOS); }); @@ -340,21 +364,21 @@ describe('helpers/decos', () => { const d = new Date(); d.getDate(); d.setDate(d.getDate() + 30); - expect(isNearEndDate({ dateEnd: d.toISOString() } as any)).toBe(true); + expect(isNearEndDate({ dateEnd: d.toISOString() })).toBe(true); }); test('Not near', () => { const d = new Date(); d.getDate(); d.setDate(d.getDate() + 120); - expect(isNearEndDate({ dateEnd: d.toISOString() } as any)).toBe(false); + expect(isNearEndDate({ dateEnd: d.toISOString() })).toBe(false); }); test('In past', () => { const d = new Date(); d.getDate(); d.setDate(d.getDate() - 120); - expect(isNearEndDate({ dateEnd: d.toISOString() } as any)).toBe(false); + expect(isNearEndDate({ dateEnd: d.toISOString() })).toBe(false); }); }); @@ -363,24 +387,20 @@ describe('helpers/decos', () => { const d = new Date(); d.getDate(); d.setDate(d.getDate() + 1); - expect(isExpired({ dateEnd: new Date().toISOString() } as any, d)).toBe( - true - ); + expect(isExpired({ dateEnd: new Date().toISOString() }, d)).toBe(true); }); test('Is not expired', () => { const d = new Date(); d.getDate(); d.setDate(d.getDate() - 1); - expect(isExpired({ dateEnd: new Date().toISOString() } as any, d)).toBe( - false - ); + expect(isExpired({ dateEnd: new Date().toISOString() }, d)).toBe(false); }); test('Is expired same date', () => { - expect( - isExpired({ dateEnd: new Date().toISOString() } as any, new Date()) - ).toBe(true); + expect(isExpired({ dateEnd: new Date().toISOString() }, new Date())).toBe( + true + ); }); }); diff --git a/src/server/services/decos/helpers.ts b/src/server/services/decos/helpers.ts index f211b9e7d2..6175766b5d 100644 --- a/src/server/services/decos/helpers.ts +++ b/src/server/services/decos/helpers.ts @@ -1,9 +1,17 @@ -import { +import type { DecosZaakTransformer, DecosZaakSource, DecosZaakBase, DecosZaakWithKentekens, } from './decos-types'; +import { + DECOS_EXCLUDE_CASES_WITH_INVALID_DFUNCTION, + DECOS_EXCLUDE_CASES_WITH_PENDING_PAYMENT_CONFIRMATION_SUBJECT1, + DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT11, + DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT12, + DECOS_PENDING_REMOVAL_DFUNCTION, +} from './decos-types'; +import { NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END } from '../../../universal/helpers/vergunningen'; import { defaultDateFormat, isDateInPast, @@ -11,22 +19,12 @@ import { } from '../../../universal/helpers/date'; import { DecosCaseType } from '../../../universal/types/vergunningen'; import { AuthProfileAndToken } from '../../auth/auth-types'; -import { - DECOS_EXCLUDE_CASES_WITH_INVALID_DFUNCTION, - DECOS_EXCLUDE_CASES_WITH_PENDING_PAYMENT_CONFIRMATION_SUBJECT1, - DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT11, - DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT12, - DECOS_PENDING_REMOVAL_DFUNCTION, - NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END, -} from '../vergunningen-v2/config-and-types'; -import { decosCaseToZaakTransformers } from '../vergunningen-v2/decos-zaken'; // Checks to see if a payment was not processed correctly/completely yet. export function isWaitingForPaymentConfirmation( - decosZaakSource: DecosZaakSource + decosZaakSource: DecosZaakSource, + zaakTypeTransformer: DecosZaakTransformer ) { - const zaakType = getDecosZaakTypeFromSource(decosZaakSource); - const isWaitingForPaymentConfirmation = decosZaakSource.fields.text11?.toLowerCase() == DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT11 && @@ -40,7 +38,7 @@ export function isWaitingForPaymentConfirmation( ); return ( - !!decosCaseToZaakTransformers[zaakType]?.requirePayment && + !!zaakTypeTransformer.requirePayment && (isWaitingForPaymentConfirmation || isWaitingForPaymentConfirmation2) ); } @@ -67,7 +65,7 @@ export function isExcludedFromTransformation( ) { return ( isScheduledForRemoval(zaakSource) || - isWaitingForPaymentConfirmation(zaakSource) || + isWaitingForPaymentConfirmation(zaakSource, zaakTypeTransformer) || hasInvalidDecision(zaakSource) || !zaakTypeTransformer.isActive || // Check if we have data we want to transform or not. diff --git a/src/server/services/toeristische-verhuur/toeristische-verhuur-notifications.ts b/src/server/services/toeristische-verhuur/toeristische-verhuur-notifications.ts index 4b6939a8a8..e0e97faf6b 100644 --- a/src/server/services/toeristische-verhuur/toeristische-verhuur-notifications.ts +++ b/src/server/services/toeristische-verhuur/toeristische-verhuur-notifications.ts @@ -13,7 +13,7 @@ import { dateFormat, isDateInPast } from '../../../universal/helpers/date'; import { isRecentNotification } from '../../../universal/helpers/utils'; import { MyNotification } from '../../../universal/types'; import { AuthProfileAndToken } from '../../auth/auth-types'; -import { NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END } from '../vergunningen-v2/config-and-types'; +import { NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END } from '../../../universal/helpers/vergunningen'; import { isNearEndDate } from '../decos/helpers'; export function createToeristischeVerhuurNotification( diff --git a/src/server/services/vergunningen-v2/config-and-types.ts b/src/server/services/vergunningen-v2/config-and-types.ts index 3d245c1504..b71b0e5a77 100644 --- a/src/server/services/vergunningen-v2/config-and-types.ts +++ b/src/server/services/vergunningen-v2/config-and-types.ts @@ -14,7 +14,6 @@ import { ZakenFilter, } from '../decos/decos-types'; -export const NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END = 3; export const NOTIFICATION_MAX_MONTHS_TO_SHOW_EXPIRED = 3; export const EXCLUDE_CASE_TYPES_FROM_VERGUNNINGEN_THEMA: DecosCaseType[] = [ @@ -22,27 +21,6 @@ export const EXCLUDE_CASE_TYPES_FROM_VERGUNNINGEN_THEMA: DecosCaseType[] = [ CaseTypeV2.ExploitatieHorecabedrijf, ]; -// Cases with this one of these dfunction values will not be included in the cases shown to the user. -export const DECOS_EXCLUDE_CASES_WITH_INVALID_DFUNCTION = [ - 'buiten behandeling', - 'geannuleerd', - 'geen aanvraag of dubbel', -]; - -// Cases with one of these subject1 values will not be included in the cases shown to the user. Payment is not yet processed or failed. -export const DECOS_EXCLUDE_CASES_WITH_PENDING_PAYMENT_CONFIRMATION_SUBJECT1 = [ - 'wacht op online betaling', - 'wacht op ideal betaling', -]; - -// Cases with this dfunction value will not be included in the cases shown to the user. -export const DECOS_PENDING_REMOVAL_DFUNCTION = '*verwijder'; -// Cases with this text11 value will not be included in the cases shown to the user. Payment is not yet processed or failed. -export const DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT11 = 'nogniet'; -// Cases with this text12 value will not be included in the cases shown to the user. Payment is not yet processed or failed. -export const DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT12 = - 'wacht op online betaling'; - export type VergunningBase = DecosZaakBase; export interface TVMRVVObject From 11695b49e260dda535f61d2e6d713748906bcd30 Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Thu, 19 Dec 2024 13:05:22 +0100 Subject: [PATCH 09/11] Replace any with decoszaakbase --- src/server/services/decos/decos-service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/services/decos/decos-service.ts b/src/server/services/decos/decos-service.ts index e23b166398..baf784d47d 100644 --- a/src/server/services/decos/decos-service.ts +++ b/src/server/services/decos/decos-service.ts @@ -287,7 +287,7 @@ async function getZakenByUserKey( requestID: RequestID, userKey: string, zaakTypeTransformers: Pick< - DecosZaakTransformer, + DecosZaakTransformer, 'addToSelectFieldsBase' | 'caseType' >[] = [] ) { @@ -339,7 +339,7 @@ export async function fetchDecosZakenFromSource( requestID: RequestID, authProfileAndToken: AuthProfileAndToken, zaakTypeTransformers: Pick< - DecosZaakTransformer, + DecosZaakTransformer, 'addToSelectFieldsBase' | 'caseType' >[] = [] ) { From 187f710739b673dc3ffb6f049f79b8c5f5875692 Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Thu, 19 Dec 2024 13:11:27 +0100 Subject: [PATCH 10/11] Exclude empty params in decosUrlParams in getZakenByUserKey --- src/server/services/decos/decos-service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/services/decos/decos-service.ts b/src/server/services/decos/decos-service.ts index baf784d47d..e27d6cc643 100644 --- a/src/server/services/decos/decos-service.ts +++ b/src/server/services/decos/decos-service.ts @@ -309,11 +309,11 @@ async function getZakenByUserKey( .map((transformer) => `${caseField} eq ${transformer.caseType}`) .join(' or '); - const decosUrlParams = [ - 'top=50', - fields ? `select=${fields}` : '', - caseTypes ? `filter=${caseTypes}` : '', - ].join('&'); + const decosUrlParams = new URLSearchParams({ + top: '50', + ...(fields && { select: fields }), + ...(caseTypes && { filter: caseTypes }), + }); const apiConfig = getApiConfig('DECOS_API', { formatUrl: (config) => { From e181ee66f6503cc33a6070cd97c1a959e4dad0d6 Mon Sep 17 00:00:00 2001 From: Terry van Walen Date: Thu, 19 Dec 2024 13:13:39 +0100 Subject: [PATCH 11/11] Reorder import based on linter --- src/server/services/decos/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/services/decos/helpers.ts b/src/server/services/decos/helpers.ts index 6175766b5d..2aa740caba 100644 --- a/src/server/services/decos/helpers.ts +++ b/src/server/services/decos/helpers.ts @@ -11,12 +11,12 @@ import { DECOS_PENDING_PAYMENT_CONFIRMATION_TEXT12, DECOS_PENDING_REMOVAL_DFUNCTION, } from './decos-types'; -import { NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END } from '../../../universal/helpers/vergunningen'; import { defaultDateFormat, isDateInPast, monthsFromNow, } from '../../../universal/helpers/date'; +import { NOTIFICATION_REMINDER_FROM_MONTHS_NEAR_END } from '../../../universal/helpers/vergunningen'; import { DecosCaseType } from '../../../universal/types/vergunningen'; import { AuthProfileAndToken } from '../../auth/auth-types';