From 6e683e3eeb5e14955ae2ac442b8067d8b41e838a Mon Sep 17 00:00:00 2001 From: "Yassine R." Date: Tue, 28 Jan 2025 02:02:57 +0100 Subject: [PATCH] fix(backend): fix search for birth date & phone --- .../delete-structure/test.ref.html | 2 +- .../new-structure/test.ref.html | 2 +- ...ersImportValidator.service.invalid.test.ts | 2 +- .../controllers/search-usagers.controller.ts | 10 --- .../src/usagers/dto/search-usager.dto.ts | 10 ++- .../services/cerfa/tests/CERFA_MOCKS.mock.ts | 6 +- .../CUSTOM_DOC_ATTESTATION_POSTALE.const.ts | 2 +- .../tests/CUSTOM_DOC_COURRIER_REFUS.ts | 2 +- .../tests/FIRST_SHEET_USAGERS.mock.ts | 4 +- .../utils/validate-search-field.decorator.ts | 1 - .../src/util/phone/phoneUtils.service.spec.ts | 2 +- .../src/util/test/AppTestHelper.service.ts | 5 ++ packages/common/src/search/functions/index.ts | 1 + .../src/search/functions/parseBirthDate.ts | 15 ++++ .../functions/tests/parseBirthDate.spec.ts | 71 +++++++++++++++++++ .../manage-filters.component.html | 38 +++++++--- .../manage-filters.component.ts | 15 ++-- .../manage-usagers-page.component.html | 1 + .../manage-usagers-page.component.ts | 23 +++--- .../usager-filter/UsagersFilterCriteria.ts | 5 +- .../usagersSearchStringFilter.service.ts | 4 +- .../usager-filter/usagersFilter.service.ts | 1 + .../user-profil/user-profil.component.ts | 23 ++---- .../services/manage-users.service.ts | 45 ++++++++---- ...display-etat-civil-decision.component.html | 2 +- .../components/step-rdv/step-rdv.component.ts | 7 +- .../display-etat-civil.component.html | 2 +- .../profil-general-section.component.html | 2 +- .../etat-civil-parent-form.component.ts | 8 ++- .../usager-shared/pipes/referrer-name.pipe.ts | 16 +++-- 30 files changed, 223 insertions(+), 104 deletions(-) create mode 100644 packages/common/src/search/functions/parseBirthDate.ts create mode 100644 packages/common/src/search/functions/tests/parseBirthDate.spec.ts diff --git a/packages/backend/src/_static/email-templates/delete-structure/test.ref.html b/packages/backend/src/_static/email-templates/delete-structure/test.ref.html index 3ed65c5f3c..cbd4d9f311 100644 --- a/packages/backend/src/_static/email-templates/delete-structure/test.ref.html +++ b/packages/backend/src/_static/email-templates/delete-structure/test.ref.html @@ -443,7 +443,7 @@ margin-left: 5px; " > - +33 1 02 03 04 05 + 01 02 03 04 05

- +33 1 02 03 04 05 + 01 02 03 04 05

{ rowNumber: 10, value: { countryCode: "fr", - numero: "0102030405 et des lettres", + numero: "0102030405", }, }, diff --git a/packages/backend/src/usagers/controllers/search-usagers.controller.ts b/packages/backend/src/usagers/controllers/search-usagers.controller.ts index ac64a37cdb..9d4d950c03 100644 --- a/packages/backend/src/usagers/controllers/search-usagers.controller.ts +++ b/packages/backend/src/usagers/controllers/search-usagers.controller.ts @@ -120,7 +120,6 @@ export class SearchUsagersController { structureId: user.structureId, }); - console.log(search); if (search.searchString?.length > 0) { if (search.searchStringField === CriteriaSearchField.DEFAULT) { query.andWhere("nom_prenom_surnom_ref ILIKE :str", { @@ -137,7 +136,6 @@ export class SearchUsagersController { } else if ( search.searchStringField === CriteriaSearchField.PHONE_NUMBER ) { - console.log(search.searchString); query.andWhere(`telephone->>'numero' ILIKE :phone`, { phone: `%${search.searchString}%`, }); @@ -199,14 +197,6 @@ export class SearchUsagersController { } } - console.log({ - searchString: !search.searchString, - echeance: !search?.echeance, - entretien: !search?.entretien, - referrerId: typeof search?.referrerId === "undefined", - lastInteractionDate: !search?.lastInteractionDate, - }); - if ( !search.searchString && !search?.echeance && diff --git a/packages/backend/src/usagers/dto/search-usager.dto.ts b/packages/backend/src/usagers/dto/search-usager.dto.ts index b0612832d0..c9924280cb 100644 --- a/packages/backend/src/usagers/dto/search-usager.dto.ts +++ b/packages/backend/src/usagers/dto/search-usager.dto.ts @@ -3,6 +3,7 @@ import { IsIn, IsNumber, IsOptional, ValidateIf } from "class-validator"; import { CriteriaSearchField, normalizeString, + parseBirthDate, UsagersFilterCriteriaDernierPassage, UsagersFilterCriteriaEcheance, UsagersFilterCriteriaEntretien, @@ -20,13 +21,10 @@ export class SearchUsagerDto { return null; } - if ( - [ - CriteriaSearchField.PHONE_NUMBER, - CriteriaSearchField.BIRTH_DATE, - ].includes(obj.searchStringField) - ) { + if (CriteriaSearchField.PHONE_NUMBER === obj.searchStringField) { return value.replace(/\D/g, ""); + } else if (CriteriaSearchField.BIRTH_DATE === obj.searchStringField) { + return parseBirthDate(value); } return normalizeString(value).trim(); diff --git a/packages/backend/src/usagers/services/cerfa/tests/CERFA_MOCKS.mock.ts b/packages/backend/src/usagers/services/cerfa/tests/CERFA_MOCKS.mock.ts index 7d72585b22..41744623dd 100644 --- a/packages/backend/src/usagers/services/cerfa/tests/CERFA_MOCKS.mock.ts +++ b/packages/backend/src/usagers/services/cerfa/tests/CERFA_MOCKS.mock.ts @@ -59,8 +59,8 @@ export const CERFA_MOCK_USAGER_ACTIF: UsagerCerfaFields = { signature1A: "ASNIERES-SUR-SEINE", signature1B: "ASNIERES-SUR-SEINE", signature2: "ASNIERES-SUR-SEINE", - telephone: "+33 6 06 06 06 06", - telephoneOrga: "+33 6 02 03 04 05", + telephone: "06 06 06 06 06", + telephoneOrga: "06 02 03 04 05", typeDemande: "2", }; @@ -124,6 +124,6 @@ export const CERFA_MOCK_USAGER_REFUS: UsagerCerfaFields = { signature1B: "ASNIERES-SUR-SEINE", signature2: "ASNIERES-SUR-SEINE", telephone: "", - telephoneOrga: "+33 6 02 03 04 05", + telephoneOrga: "06 02 03 04 05", typeDemande: "1", }; diff --git a/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts b/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts index 1ee33960a3..aba31222ee 100644 --- a/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts +++ b/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts @@ -27,7 +27,7 @@ export const CUSTOM_DOC_ATTESTATION_POSTALE: StructureCustomDocTags = { USAGER_SURNOM: "TEST", USAGER_DATE_NAISSANCE: "02/11/1988", USAGER_LIEU_NAISSANCE: "Paris", - USAGER_PHONE: "+33 6 06 06 06 06", + USAGER_PHONE: "06 06 06 06 06", USAGER_EMAIL: "fake-mail@yopmail.com", USAGER_NUMERO_DISTRIBUTION_SPECIALE: "", STATUT_DOM: "Actif", diff --git a/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts b/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts index 75eba7b418..c6d9acbfb6 100644 --- a/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts +++ b/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts @@ -28,7 +28,7 @@ export const CUSTOM_DOC_COURRIER_REFUS: StructureCustomDocTags = { USAGER_SURNOM: "", USAGER_DATE_NAISSANCE: "07/08/1998", USAGER_LIEU_NAISSANCE: "Bouaké, Côte d'Ivoire", - USAGER_PHONE: "+33 6 06 06 06 06", + USAGER_PHONE: "06 06 06 06 06", USAGER_EMAIL: "domicilie2@yopmail.com", AYANTS_DROITS_LISTE: "Karamoko Mauricette né(e) le 20/12/1978", STATUT_DOM: "Refusé", diff --git a/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/FIRST_SHEET_USAGERS.mock.ts b/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/FIRST_SHEET_USAGERS.mock.ts index 09341a0d27..c9574a9b8b 100644 --- a/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/FIRST_SHEET_USAGERS.mock.ts +++ b/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/FIRST_SHEET_USAGERS.mock.ts @@ -21,7 +21,7 @@ export const FIRST_SHEET_USAGERS = [ USAGER_NATIONALITE: "", USAGER_NOM: "NOUVEAU", USAGER_NUMERO_DISTRIBUTION_SPECIALE: "", - USAGER_PHONE: "+33 6 06 06 06 06", + USAGER_PHONE: "06 06 06 06 06", USAGER_PRENOM: "DOSSIER", USAGER_CUSTOM_REF: "6", USAGER_SURNOM: "TEST", @@ -62,7 +62,7 @@ export const FIRST_SHEET_USAGERS = [ USAGER_NATIONALITE: "", USAGER_NOM: "Martine", USAGER_NUMERO_DISTRIBUTION_SPECIALE: "", - USAGER_PHONE: "+33 6 06 06 06 06", + USAGER_PHONE: "06 06 06 06 06", USAGER_PRENOM: "Sembat", USAGER_CUSTOM_REF: "5", USAGER_SURNOM: "", diff --git a/packages/backend/src/usagers/utils/validate-search-field.decorator.ts b/packages/backend/src/usagers/utils/validate-search-field.decorator.ts index 5c64b30074..42809a8a77 100644 --- a/packages/backend/src/usagers/utils/validate-search-field.decorator.ts +++ b/packages/backend/src/usagers/utils/validate-search-field.decorator.ts @@ -14,7 +14,6 @@ export function ValidateSearchField(validationOptions?: ValidationOptions) { options: validationOptions, validator: { validate(value: string, args: ValidationArguments) { - console.log({ value }); const searchStringField = args.object["searchStringField"]; if (!searchStringField) { return false; diff --git a/packages/backend/src/util/phone/phoneUtils.service.spec.ts b/packages/backend/src/util/phone/phoneUtils.service.spec.ts index 82ca7be54f..9809c464cb 100644 --- a/packages/backend/src/util/phone/phoneUtils.service.spec.ts +++ b/packages/backend/src/util/phone/phoneUtils.service.spec.ts @@ -15,7 +15,7 @@ describe("Telephone utils", () => { it("getPhoneString shoud return string with countryCode and numero", () => { expect(getPhoneString({ countryCode: "fr", numero: "0622062206" })).toEqual( - "0622062206" + "06 22 06 22 06" ); }); }); diff --git a/packages/backend/src/util/test/AppTestHelper.service.ts b/packages/backend/src/util/test/AppTestHelper.service.ts index 6e6e3c78cd..7fff666d66 100644 --- a/packages/backend/src/util/test/AppTestHelper.service.ts +++ b/packages/backend/src/util/test/AppTestHelper.service.ts @@ -94,6 +94,11 @@ async function bootstrapTestApp( context.app.useGlobalPipes( new ValidationPipe({ whitelist: true, + stopAtFirstError: true, + transform: true, + transformOptions: { + enableImplicitConversion: false, + }, }) ); await context.app.init(); diff --git a/packages/common/src/search/functions/index.ts b/packages/common/src/search/functions/index.ts index 0b4f872473..a705d68131 100644 --- a/packages/common/src/search/functions/index.ts +++ b/packages/common/src/search/functions/index.ts @@ -1,3 +1,4 @@ //@index('./*.ts', f => `export * from '${f.path}'`) export * from "./getUsagerDeadlines"; export * from "./normalize-string"; +export * from "./parseBirthDate"; diff --git a/packages/common/src/search/functions/parseBirthDate.ts b/packages/common/src/search/functions/parseBirthDate.ts new file mode 100644 index 0000000000..9cfb194046 --- /dev/null +++ b/packages/common/src/search/functions/parseBirthDate.ts @@ -0,0 +1,15 @@ +import { format, isValid, parse } from "date-fns"; + +export const parseBirthDate = (text: string): string | null => { + const parsedDate = parse(text.trim(), "dd/MM/yyyy", new Date()); + if (!isValid(parsedDate)) { + return null; + } + const minDate = new Date(1900, 0, 1); + const today = new Date(); + + if (parsedDate < minDate || parsedDate > today) { + return null; + } + return format(parsedDate, "ddMMyyyy"); +}; diff --git a/packages/common/src/search/functions/tests/parseBirthDate.spec.ts b/packages/common/src/search/functions/tests/parseBirthDate.spec.ts new file mode 100644 index 0000000000..13cbfdb054 --- /dev/null +++ b/packages/common/src/search/functions/tests/parseBirthDate.spec.ts @@ -0,0 +1,71 @@ +import { parseBirthDate } from "../parseBirthDate"; + +describe("parseBirthDate", () => { + describe("Valid Date Formats", () => { + it("should parse a valid date", () => { + const result = parseBirthDate("15/08/1990"); + expect(result).toBe("15081990"); + }); + + it("should handle initial zeros", () => { + const result = parseBirthDate("01/02/2000"); + expect(result).toBe("01022000"); + }); + }); + + describe("Date Range Validation: 1900 < date > now", () => { + it("should return null for a date before 1900", () => { + const result = parseBirthDate("01/01/1899"); + expect(result).toBeNull(); + }); + + it("should return null for a future date", () => { + const result = parseBirthDate("01/01/2045"); + expect(result).toBeNull(); + }); + }); + + describe("Invalid Date Formats", () => { + it("should return null for incorrect date format", () => { + const result = parseBirthDate("2023-01-15"); + expect(result).toBeNull(); + }); + + it("should return null for a non-existent date", () => { + const result = parseBirthDate("31/02/2000"); + expect(result).toBeNull(); + }); + + it("should return null for a non-numeric string", () => { + const result = parseBirthDate("abc/def/ghij"); + expect(result).toBeNull(); + }); + }); + + // Spaces handling tests + describe("Spaces Handling", () => { + it("should remove spaces before and after the date", () => { + const result = parseBirthDate(" 15/08/1990 "); + expect(result).toBe("15081990"); + }); + }); + + // Boundary cases tests + describe("Boundary Cases", () => { + it("should accept the minimum allowed date", () => { + const result = parseBirthDate("01/01/1900"); + expect(result).toBe("01011900"); + }); + + it("should accept the maximum allowed date (today)", () => { + const today = new Date(); + const formattedToday = `${today.getDate().toString().padStart(2, "0")}/${( + today.getMonth() + 1 + ) + .toString() + .padStart(2, "0")}/${today.getFullYear()}`; + const result = parseBirthDate(formattedToday); + expect(result).not.toBeNull(); + }); + }); +}); diff --git a/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html b/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html index a89f6d22dd..06f67f1d11 100644 --- a/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html +++ b/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html @@ -29,6 +29,7 @@ class="d-md-inline-block d-block me-2 text-md-start text-center my-1" >

@@ -389,6 +402,7 @@ #dropdownSort="ngbDropdown" >