diff --git a/.env.local.template b/.env.local.template index 58efb3d108..e64ad16b72 100644 --- a/.env.local.template +++ b/.env.local.template @@ -155,7 +155,13 @@ BFF_AFIS_API_BASE_URL=$BFF_MOCK_API_BASE_URL/afis/RESTAdapter BFF_AFIS_CLIENT_ID=mijnamsterdam BFF_AFIS_CLIENT_SECRET=$DEV_ENC_KEY_256 BFF_ENABLEU_AFIS_API_KEY=$DEV_API_KEY -BFF_AFIS_DIRECT_CONNECT_ENABLED=false + +# If we connect to AFIS directly, and bypassing Enable-U, we want to fetch the token directly from AFIS. +BFF_AFIS_ENABLE_DIRECT_TOKEN_FETCHING=false + +# Can be used to enable PostponeFetch in the AFIS api config. +# This way we can disable the Afis Thema via Backend. +BFF_AFIS_FEATURE_TOGGLE_ACTIVE=true # Belastingen BFF_BELASTINGEN_BEARER_TOKEN=$DEV_ENC_KEY_256 diff --git a/src/client/helpers/monitoring.ts b/src/client/helpers/monitoring.ts index f61953464e..032732d858 100644 --- a/src/client/helpers/monitoring.ts +++ b/src/client/helpers/monitoring.ts @@ -20,7 +20,7 @@ const severityMap: Record = { }; export type Properties = { - properties?: Record; + properties?: Record; severity?: Severity; }; @@ -58,9 +58,13 @@ export function captureException(error: unknown, properties?: Properties) { properties, }; - IS_DEVELOPMENT - ? MA_APP_MODE !== 'unittest' && console.log('Capture exception', payload) - : appInsights.trackException(payload); + if (IS_DEVELOPMENT) { + if (MA_APP_MODE !== 'unittest') { + console.log('Capture exception', payload); + } + } else { + appInsights.trackException(payload); + } } export function captureMessage(message: string, properties?: Properties) { @@ -70,12 +74,16 @@ export function captureMessage(message: string, properties?: Properties) { const payload = { message, severity, properties }; - IS_DEVELOPMENT - ? MA_APP_MODE !== 'unittest' && console.log('Capture message', payload) - : appInsights.trackTrace(payload); + if (IS_DEVELOPMENT) { + if (MA_APP_MODE !== 'unittest') { + console.log('Capture message', payload); + } + } else { + appInsights.trackTrace(payload); + } } -export function trackEvent(name: string, properties: Record) { +export function trackEvent(name: string, properties: Record) { return IS_DEVELOPMENT ? MA_APP_MODE !== 'unittest' && console.log('Track event', name, properties) : appInsights.trackEvent({ diff --git a/src/client/hooks/useTrackThemas.hook.ts b/src/client/hooks/useTrackThemas.hook.ts index fc088f0bb9..390125ed7f 100644 --- a/src/client/hooks/useTrackThemas.hook.ts +++ b/src/client/hooks/useTrackThemas.hook.ts @@ -1,14 +1,13 @@ import { useEffect } from 'react'; import { useSessionStorage } from './storage.hook'; -import { useAppStateGetter } from './useAppState'; import { useThemaMenuItems } from './useThemaMenuItems'; +import { ThemaMenuItemTransformed } from '../config/thema'; import { trackEvent } from '../helpers/monitoring'; -type ThemaTitleAndId = Record<'title' | 'id', string>; +type ThemaTitleAndId = Pick; export function useTrackThemas() { - const appState = useAppStateGetter(); const [storedThemas, setStoredThemas] = useSessionStorage('themas', null); const themasState = useThemaMenuItems(); @@ -17,10 +16,7 @@ export function useTrackThemas() { if (!storedThemas && !themasState.isLoading) { const themaTitlesAndIds: ThemaTitleAndId[] = themasState.items.map( (item) => ({ - title: - typeof item.title === 'function' - ? item.title(appState) - : item.title, + title: item.title, id: item.id, }) ); diff --git a/src/client/pages/Afis/Afis-thema-config.tsx b/src/client/pages/Afis/Afis-thema-config.tsx index 7adaa6feb2..337373419b 100644 --- a/src/client/pages/Afis/Afis-thema-config.tsx +++ b/src/client/pages/Afis/Afis-thema-config.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react'; -import { generatePath, LinkProps } from 'react-router-dom'; +import { generatePath } from 'react-router-dom'; import { AfisFacturenResponse, @@ -8,7 +8,7 @@ import { AfisFactuurState, } from '../../../server/services/afis/afis-types'; import { AppRoutes } from '../../../universal/config/routes'; -import { ZaakDetail } from '../../../universal/types'; +import { LinkProps, ZaakDetail } from '../../../universal/types'; import { DisplayProps } from '../../components/Table/TableV2'; import { MAX_TABLE_ROWS_ON_THEMA_PAGINA } from '../../config/app'; diff --git a/src/client/pages/Afis/Afis.tsx b/src/client/pages/Afis/Afis.tsx index ffc013e8da..8d9432ec1b 100644 --- a/src/client/pages/Afis/Afis.tsx +++ b/src/client/pages/Afis/Afis.tsx @@ -20,14 +20,28 @@ import ThemaPagina from '../ThemaPagina/ThemaPagina'; import ThemaPaginaTable from '../ThemaPagina/ThemaPaginaTable'; const pageContentTop = ( - - Hieronder ziet u een overzicht van uw facturen. U ziet hier niet de facturen - inzake Gemeentebelastingen. Deze vindt u terug bij{' '} - - Mijn Belastingen - - . - + <> + + Hieronder ziet u een overzicht van uw facturen. Mist u een factuur of + heeft u een vraag over één van uw facturen? Stuur een e-mail naar{' '} + + debiteurenadministratie@amsterdam.nl + {' '} + met de details van de factuur, zoals het factuurnummer of andere relevante + informatie, zodat zij u verder kunnen helpen. + + + U ziet hier niet de facturen over Gemeentebelastingen. Deze vindt u terug + bij{' '} + + Mijn Belastingen + + . + + ); export function AfisDisclaimer() { @@ -55,11 +69,11 @@ export function AfisDisclaimerOvergedragenFacturen() { return ( - Bij het uitblijven van een betaling, wordt uw factuur door Financiën - overgedragen naar de afdeling Incasso & Invordering van directie - Belastingen. Deze afdeling is vanaf dat moment verantwoordelijk voor de - invordering van uw factuur en daarmee uw aanspreekpunt. De status van uw - factuur wordt hier niet bijgewerkt. + Als u niet betaalt, wordt uw factuur door Financiën overgedragen naar de + afdeling Incasso & Invordering van directie Belastingen. Deze afdeling + is vanaf dat moment verantwoordelijk voor de invordering van uw factuur + en daarmee uw aanspreekpunt. De status van uw factuur vindt u terug bij + Mijn Belastingen - gemeente Amsterdam. Heeft u vragen? diff --git a/src/client/pages/Afis/AfisBetaalVoorkeuren.tsx b/src/client/pages/Afis/AfisBetaalVoorkeuren.tsx index ed134b8ae5..e34662f452 100644 --- a/src/client/pages/Afis/AfisBetaalVoorkeuren.tsx +++ b/src/client/pages/Afis/AfisBetaalVoorkeuren.tsx @@ -47,7 +47,10 @@ function AfisBusinessPartnerDetails({ return ( - + {isLoading && } {!isLoading && !!rows.length && ( @@ -112,43 +115,45 @@ export function AfisBetaalVoorkeuren() { const pageContentTop = ( <> - Hieronder kunt u uw betaalgegevens bekijken en een automatische incasso - instellen per afdeling van de gemeente. Wil u uw betaalgegevens - wijzigen, stuur dan een email naar{' '} + Hieronder kunt u uw facturatiegegevens bekijken en een automatische + incasso instellen per afdeling van de gemeente. Wil u uw + facturatiegegevens wijzigen, stuur dan een email naar{' '} debiteurenadministratie@amsterdam.nl . - - Via automatische incasso betalen - - - Download{' '} - - het machtigingsformulier. - {' '} - Kies een of meerdere producten waarvoor de gemeente automatisch mag - incasseren en vul uw gegevens in. Het debiteurennummer is niet - verplicht. Onderteken het formulier en stuur het naar: - - - Gemeente Amsterdam -
- Debiteurenadministratie -
- Antwoordnummer 47389 -
- 1070 WC -
- Amsterdam -
- Een postzegel is niet nodig. + {!FeatureToggle.afisEmandatesActive && ( + <> + + Via automatische incasso betalen + + + Download{' '} + + het machtigingsformulier. + {' '} + Kies een of meerdere producten waarvoor de gemeente automatisch mag + incasseren en vul uw gegevens in. Het debiteurennummer is niet + verplicht. Onderteken het formulier en stuur het naar: + + + Gemeente Amsterdam +
+ Debiteurenadministratie +
+ Antwoordnummer 47389 +
+ 1070 WC Amsterdam +
+ Een postzegel is niet nodig. + + )} ); @@ -193,7 +198,7 @@ export function AfisBetaalVoorkeuren() { )} {hasBusinessPartnerDetailsError && ( <> - Wij kunnen nu geen betaalgegevens laten zien. + Wij kunnen nu geen facturatiegegevens laten zien.
)} diff --git a/src/client/pages/Afis/__snapshots__/Afis.test.tsx.snap b/src/client/pages/Afis/__snapshots__/Afis.test.tsx.snap index 767ba62af7..888d081236 100644 --- a/src/client/pages/Afis/__snapshots__/Afis.test.tsx.snap +++ b/src/client/pages/Afis/__snapshots__/Afis.test.tsx.snap @@ -80,10 +80,22 @@ exports[` > Matches the Full Page snapshot 1`] = `
+

+ Hieronder ziet u een overzicht van uw facturen. Mist u een factuur of heeft u een vraag over één van uw facturen? Stuur een e-mail naar + + debiteurenadministratie@amsterdam.nl + + met de details van de factuur, zoals het factuurnummer of andere relevante informatie, zodat zij u verder kunnen helpen. +

- Hieronder ziet u een overzicht van uw facturen. U ziet hier niet de facturen inzake Gemeentebelastingen. Deze vindt u terug bij + U ziet hier niet de facturen over Gemeentebelastingen. Deze vindt u terug bij > Matches the Full Page snapshot 1`] = ` .

+
diff --git a/src/client/pages/Afis/useAfisThemaData.hook.tsx b/src/client/pages/Afis/useAfisThemaData.hook.tsx index e0d289169a..15ffa6fb26 100644 --- a/src/client/pages/Afis/useAfisThemaData.hook.tsx +++ b/src/client/pages/Afis/useAfisThemaData.hook.tsx @@ -176,6 +176,7 @@ export function useAfisThemaData() { isThemaPaginaError: isError(AFIS, false), isThemaPaginaLoading: isLoading(AFIS), listPageTitle, + linkListItems, routes, dependencyErrors: { open: hasFailedDependency(AFIS, 'open'), diff --git a/src/server/config/source-api.ts b/src/server/config/source-api.ts index e59f096544..a95cc73c01 100644 --- a/src/server/config/source-api.ts +++ b/src/server/config/source-api.ts @@ -96,6 +96,12 @@ export type SourceApiKey = type ApiDataRequestConfig = Record; +const afisFeatureToggle = getFromEnv('BFF_AFIS_FEATURE_TOGGLE_ACTIVE'); +const postponeFetchAfis = + typeof afisFeatureToggle !== 'undefined' + ? afisFeatureToggle === 'false' + : !FeatureToggle.afisActive; + const contactmomentenFeatureToggle = getFromEnv( 'BFF_CONTACTMOMENTEN_FEATURE_TOGGLE_ACTIVE' ); @@ -106,7 +112,7 @@ const postponeFetchContactmomenten = export const ApiConfig: ApiDataRequestConfig = { AFIS: { - postponeFetch: !FeatureToggle.afisActive, + postponeFetch: postponeFetchAfis, url: `${getFromEnv('BFF_AFIS_API_BASE_URL')}`, }, ZORGNED_JZD: { diff --git a/src/server/services/afis/afis-facturen.ts b/src/server/services/afis/afis-facturen.ts index 18eebab382..1462d172ad 100644 --- a/src/server/services/afis/afis-facturen.ts +++ b/src/server/services/afis/afis-facturen.ts @@ -23,7 +23,7 @@ import { encryptSessionIdWithRouteIdParam } from '../../helpers/encrypt-decrypt' import { requestData } from '../../helpers/source-api-request'; import { BffEndpoints } from '../../routing/bff-routes'; import { generateFullApiUrlBFF } from '../../routing/route-helpers'; -import { captureMessage } from '../monitoring'; +import { captureMessage, trackEvent } from '../monitoring'; import { getAfisApiConfig, getFeedEntryProperties } from './afis-helpers'; import { AccountingDocumentType, @@ -39,6 +39,7 @@ import { XmlNullable, } from './afis-types'; import { AppRoutes } from '../../../universal/config/routes'; +import { entries } from '../../../universal/helpers/utils'; const DEFAULT_PROFIT_CENTER_NAME = 'Gemeente Amsterdam'; const AFIS_MAX_FACTUREN_TOP = 2000; @@ -284,10 +285,13 @@ function transformFacturen( /** Checks if a factuur is available for download. * - * A factuur can only be downloaded after a certain time on the day it was posted. This because PDF's of invoices are generated by a batch job running at a later time. + * A factuur can only be downloaded after a certain time on the day it was posted. + * This because PDF's of invoices are generated by a batch job running at a later time. * */ function isDownloadAvailable(postingDate: string): boolean { if (!postingDate) { + // Return true if we cannot determine the posting date. + // This should not be the case. However if it does happen we at least show the factuur to the user. return true; } @@ -528,6 +532,25 @@ export async function fetchAfisFacturenOverview( return apiErrorResult('Facturen ophalen mislukt.', null); } + // Prepare data for monitoring + const countByState = entries(facturenOverview).reduce( + (acc, [state, content]) => { + if (content === null) { + return acc; + } + return { + ...acc, + [state]: content?.count ?? -1, + }; + }, + {} + ); + + if (Object.keys(countByState).length > 0) { + // Log the count of facturen per state in Application Insights + trackEvent('afis-facturen-per-categorie', countByState); + } + return apiSuccessResult(facturenOverview, failedDependencies); } diff --git a/src/server/services/afis/afis-helpers.ts b/src/server/services/afis/afis-helpers.ts index ba71d2929e..215105990b 100644 --- a/src/server/services/afis/afis-helpers.ts +++ b/src/server/services/afis/afis-helpers.ts @@ -24,7 +24,7 @@ export async function getAfisApiConfig( ): Promise { // If Afis EnableU is active, token fetching is taken care of by EnableU Gateway. const authHeader = - getFromEnv('BFF_AFIS_DIRECT_CONNECT_ENABLED') === 'true' + getFromEnv('BFF_AFIS_ENABLE_DIRECT_TOKEN_FETCHING') === 'true' ? await fetchAfisTokenHeader(requestID) : { apiKey: getFromEnv('BFF_ENABLEU_AFIS_API_KEY') }; diff --git a/src/server/services/afis/afis.ts b/src/server/services/afis/afis.ts index 64f28b6e97..2ea4c4e038 100644 --- a/src/server/services/afis/afis.ts +++ b/src/server/services/afis/afis.ts @@ -58,7 +58,7 @@ async function fetchAfisTokenHeader_( Authorization: string; } | null>(dataRequestConfig, requestID); - if (tokenHeaderResponse.status !== 'OK') { + if (tokenHeaderResponse.status === 'ERROR') { throw new Error('AFIS: Could not fetch token'); } diff --git a/src/server/services/monitoring.ts b/src/server/services/monitoring.ts index 37889e7bd8..41b7fc7670 100644 --- a/src/server/services/monitoring.ts +++ b/src/server/services/monitoring.ts @@ -83,6 +83,15 @@ export function captureMessage(message: string, properties?: Properties) { } } +export function trackEvent(name: string, properties: Record) { + return IS_DEVELOPMENT + ? MA_APP_MODE !== 'unittest' && console.log('Track event', name, properties) + : client?.trackEvent({ + name, + properties, + }); +} + interface TrackRequestProps { name: string; url: string; diff --git a/src/universal/config/feature-toggles.ts b/src/universal/config/feature-toggles.ts index eae578d86f..ac4f88567b 100644 --- a/src/universal/config/feature-toggles.ts +++ b/src/universal/config/feature-toggles.ts @@ -2,7 +2,7 @@ import { IS_AP, IS_OT, IS_PRODUCTION } from './env'; export const FeatureToggle = { // AFIS - afisActive: !IS_PRODUCTION, + afisActive: true, afisEmandatesActive: false, afisBusinesspartnerPhoneActive: false, // We don't filter out the undownloadable facturen for testing purposes.