From 4d70c1d29892998456468abafc25132f89ebbbd2 Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Wed, 31 Jul 2024 15:49:24 +0200 Subject: [PATCH 01/41] Add auto prior notification computation route in Backend --- .../AutoPriorNotificationComputedValues.kt | 9 +++++ .../ManualPriorNotificationComputedValues.kt | 9 ++--- .../ComputeAutoPriorNotification.kt | 29 ++++++++++++++++ .../ComputeManualPriorNotification.kt | 4 --- .../api/bff/PriorNotificationController.kt | 20 ++++++++++- .../AutoPriorNotificationComputeDataInput.kt | 8 +++++ ...iorNotificationComputedValuesDataOutput.kt | 11 +++++++ ...iorNotificationComputedValuesDataOutput.kt | 4 +-- .../bff/PriorNotificationControllerITests.kt | 33 +++++++++++++++++++ 9 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/AutoPriorNotificationComputedValues.kt create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeAutoPriorNotification.kt create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/AutoPriorNotificationComputeDataInput.kt create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/AutoPriorNotificationComputedValuesDataOutput.kt diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/AutoPriorNotificationComputedValues.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/AutoPriorNotificationComputedValues.kt new file mode 100644 index 0000000000..8aa3d79a23 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/AutoPriorNotificationComputedValues.kt @@ -0,0 +1,9 @@ +package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification + +/** + * Real-time computed values displayed within an auto prior notification form. + */ +data class AutoPriorNotificationComputedValues( + /** Next initial state of the auto prior notification once it will be created or updated. */ + val nextState: PriorNotificationState, +) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/ManualPriorNotificationComputedValues.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/ManualPriorNotificationComputedValues.kt index b930bf4723..b6acef7cca 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/ManualPriorNotificationComputedValues.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/ManualPriorNotificationComputedValues.kt @@ -2,15 +2,16 @@ package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification import com.neovisionaries.i18n.CountryCode import fr.gouv.cnsp.monitorfish.domain.entities.fleet_segment.FleetSegment -import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.VESSEL_FLAG_COUNTRY_CODES_WITHOUT_SYSTEMATIC_VERIFICATION -import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.VESSEL_RISK_FACTOR_VERIFICATION_THRESHOLD + +val VESSEL_FLAG_COUNTRY_CODES_WITHOUT_SYSTEMATIC_VERIFICATION: List = listOf(CountryCode.FR) +const val VESSEL_RISK_FACTOR_VERIFICATION_THRESHOLD: Double = 2.3 /** - * Real-time computed values displayed within a prior notification form. + * Real-time computed values displayed within a manual prior notification form. */ data class ManualPriorNotificationComputedValues( val isVesselUnderCharter: Boolean?, - /** Next initial state of the prior notification once it will be created or updated. */ + /** Next initial state of the manual prior notification once it will be created or updated. */ val nextState: PriorNotificationState, val tripSegments: List, val types: List, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeAutoPriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeAutoPriorNotification.kt new file mode 100644 index 0000000000..547526e4f7 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeAutoPriorNotification.kt @@ -0,0 +1,29 @@ +package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification + +import fr.gouv.cnsp.monitorfish.config.UseCase +import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.AutoPriorNotificationComputedValues +import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification +import fr.gouv.cnsp.monitorfish.domain.repositories.PnoPortSubscriptionRepository +import fr.gouv.cnsp.monitorfish.domain.repositories.PnoSegmentSubscriptionRepository +import fr.gouv.cnsp.monitorfish.domain.repositories.PnoVesselSubscriptionRepository + +@UseCase +class ComputeAutoPriorNotification( + private val pnoPortSubscriptionRepository: PnoPortSubscriptionRepository, + private val pnoSegmentSubscriptionRepository: PnoSegmentSubscriptionRepository, + private val pnoVesselSubscriptionRepository: PnoVesselSubscriptionRepository, +) { + fun execute( + isInVerificationScope: Boolean, + portLocode: String, + segmentCodes: List, + vesselId: Int, + ): AutoPriorNotificationComputedValues { + val isPartOfControlUnitSubscriptions = pnoPortSubscriptionRepository.has(portLocode) + || pnoVesselSubscriptionRepository.has(vesselId) + || pnoSegmentSubscriptionRepository.has(portLocode, segmentCodes) + val nextState = PriorNotification.getNextState(isInVerificationScope, isPartOfControlUnitSubscriptions) + + return AutoPriorNotificationComputedValues(nextState) + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeManualPriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeManualPriorNotification.kt index c04f544b7c..8dc44d2d0c 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeManualPriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeManualPriorNotification.kt @@ -1,6 +1,5 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification -import com.neovisionaries.i18n.CountryCode import fr.gouv.cnsp.monitorfish.config.UseCase import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookFishingCatch import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.ManualPriorNotificationComputedValues @@ -11,9 +10,6 @@ import fr.gouv.cnsp.monitorfish.domain.repositories.PnoVesselSubscriptionReposit import fr.gouv.cnsp.monitorfish.domain.repositories.VesselRepository import fr.gouv.cnsp.monitorfish.domain.use_cases.fleet_segment.ComputeFleetSegments -val VESSEL_FLAG_COUNTRY_CODES_WITHOUT_SYSTEMATIC_VERIFICATION: List = listOf(CountryCode.FR) -const val VESSEL_RISK_FACTOR_VERIFICATION_THRESHOLD: Double = 2.3 - @UseCase class ComputeManualPriorNotification( private val pnoPortSubscriptionRepository: PnoPortSubscriptionRepository, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt index dd3b42206f..f781e6e946 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt @@ -5,6 +5,7 @@ import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotifica import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.filters.PriorNotificationsFilter import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.sorters.PriorNotificationsSortColumn import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.* +import fr.gouv.cnsp.monitorfish.infrastructure.api.input.AutoPriorNotificationComputeDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationComputeDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.PriorNotificationDataInput @@ -21,6 +22,7 @@ import java.time.ZonedDateTime @RequestMapping("/bff/v1/prior_notifications") @Tag(name = "Prior notifications endpoints") class PriorNotificationController( + private val computeAutoPriorNotification: ComputeAutoPriorNotification, private val computeManualPriorNotification: ComputeManualPriorNotification, private val createOrUpdateManualPriorNotification: CreateOrUpdateManualPriorNotification, private val getPriorNotification: GetPriorNotification, @@ -135,8 +137,24 @@ class PriorNotificationController( return PriorNotificationsExtraDataOutput.fromPriorNotificationStats(priorNotificationStats) } + @PostMapping("/auto/compute") + @Operation(summary = "Calculate auto prior notification next state") + fun getAutoComputation( + @RequestBody + autoPriorNotificationComputeDataInput: AutoPriorNotificationComputeDataInput, + ): AutoPriorNotificationComputedValuesDataOutput { + val manualPriorNotificationComputedValues = computeAutoPriorNotification.execute( + isInVerificationScope = autoPriorNotificationComputeDataInput.isInVerificationScope, + portLocode = autoPriorNotificationComputeDataInput.portLocode, + segmentCodes = autoPriorNotificationComputeDataInput.segmentCodes, + vesselId = autoPriorNotificationComputeDataInput.vesselId, + ) + + return AutoPriorNotificationComputedValuesDataOutput(manualPriorNotificationComputedValues.nextState) + } + @PostMapping("/manual/compute") - @Operation(summary = "Calculate manual prior notification fleet segments, prior notification types and risk factor") + @Operation(summary = "Calculate manual prior notification fleet segments, prior notification types, risk factor and next state") fun getManualComputation( @RequestBody manualPriorNotificationComputeDataInput: ManualPriorNotificationComputeDataInput, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/AutoPriorNotificationComputeDataInput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/AutoPriorNotificationComputeDataInput.kt new file mode 100644 index 0000000000..1946cf5639 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/AutoPriorNotificationComputeDataInput.kt @@ -0,0 +1,8 @@ +package fr.gouv.cnsp.monitorfish.infrastructure.api.input + +data class AutoPriorNotificationComputeDataInput( + val isInVerificationScope: Boolean, + val portLocode: String, + val segmentCodes: List, + val vesselId: Int, +) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/AutoPriorNotificationComputedValuesDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/AutoPriorNotificationComputedValuesDataOutput.kt new file mode 100644 index 0000000000..c6633ed238 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/AutoPriorNotificationComputedValuesDataOutput.kt @@ -0,0 +1,11 @@ +package fr.gouv.cnsp.monitorfish.infrastructure.api.outputs + +import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationState + +/** + * Output data for the real-time computed values displayed within an auto prior notification form. + */ +data class AutoPriorNotificationComputedValuesDataOutput( + /** Next initial state of the auto prior notification once it will be created or updated. */ + val nextState: PriorNotificationState, +) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationComputedValuesDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationComputedValuesDataOutput.kt index 919a3459ac..1846a8a2d4 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationComputedValuesDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationComputedValuesDataOutput.kt @@ -4,10 +4,10 @@ import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.ManualPriorNo import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationState /** - * Output data for the real-time computed values displayed within a prior notification form. + * Output data for the real-time computed values displayed within a manual prior notification form. */ data class ManualPriorNotificationComputedValuesDataOutput( - /** Next initial state of the prior notification once it will be created or updated. */ + /** Next initial state of the manual prior notification once it will be created or updated. */ val nextState: PriorNotificationState, val isVesselUnderCharter: Boolean?, val tripSegments: List, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt index 6f3192f5b3..1700a06863 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt @@ -6,12 +6,14 @@ import com.nhaarman.mockitokotlin2.anyOrNull import fr.gouv.cnsp.monitorfish.config.SentryConfig import fr.gouv.cnsp.monitorfish.domain.entities.facade.SeafrontGroup import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessagePurpose +import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.AutoPriorNotificationComputedValues import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.ManualPriorNotificationComputedValues import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationState import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationStats import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.* import fr.gouv.cnsp.monitorfish.domain.utils.PaginatedList import fr.gouv.cnsp.monitorfish.fakers.PriorNotificationFaker +import fr.gouv.cnsp.monitorfish.infrastructure.api.input.AutoPriorNotificationComputeDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationComputeDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.PriorNotificationDataInput @@ -37,6 +39,9 @@ class PriorNotificationControllerITests { @Autowired private lateinit var api: MockMvc + @MockBean + private lateinit var computeAutoPriorNotification: ComputeAutoPriorNotification + @MockBean private lateinit var computeManualPriorNotification: ComputeManualPriorNotification @@ -120,6 +125,34 @@ class PriorNotificationControllerITests { .andExpect(jsonPath("$.perSeafrontGroupCount['ALL']", equalTo(2))) } + fun `getAutoComputation Should get an auto prior notification computed values`() { + // Given + given(this.computeAutoPriorNotification.execute(any(), any(), any(), any())) + .willReturn( + AutoPriorNotificationComputedValues( + nextState = PriorNotificationState.OUT_OF_VERIFICATION_SCOPE, + ), + ) + + // When + val requestBody = objectMapper.writeValueAsString( + AutoPriorNotificationComputeDataInput( + isInVerificationScope = false, + portLocode = "FRABC", + segmentCodes = emptyList(), + vesselId = 42, + ), + ) + api.perform( + post("/bff/v1/prior_notifications/auto/compute") + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody), + ) + // Then + .andExpect(status().isOk) + .andExpect(jsonPath("$.nextState", equalTo("OUT_OF_VERIFICATION_SCOPE"))) + } + @Test fun `getManualComputation Should get a manual prior notification computed values`() { // Given From d8c1cb0d739b73565c1a7d71a9123b41f6c5fd80 Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Wed, 31 Jul 2024 18:06:09 +0200 Subject: [PATCH 02/41] Remove tags max width in prior notification card & form --- .../components/PriorNotificationList/styles.ts | 6 ++++-- .../features/PriorNotification/components/shared/TagBar.tsx | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/styles.ts b/frontend/src/features/PriorNotification/components/PriorNotificationList/styles.ts index db28ee0bf1..89a9c9d865 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/styles.ts +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/styles.ts @@ -3,11 +3,13 @@ import { Tag } from '@mtes-mct/monitor-ui' import styled from 'styled-components' // TODO Update that in monitor-ui. -export const FixedTag = styled(Tag)` +export const FixedTag = styled(Tag)<{ + $isFullWidth?: boolean +}>` align-items: baseline; display: inline-block; line-height: 22px; - max-width: 130px; + max-width: ${p => (p.$isFullWidth ? 'auto' : '130px')}; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/frontend/src/features/PriorNotification/components/shared/TagBar.tsx b/frontend/src/features/PriorNotification/components/shared/TagBar.tsx index bf319c3e60..6d756457b6 100644 --- a/frontend/src/features/PriorNotification/components/shared/TagBar.tsx +++ b/frontend/src/features/PriorNotification/components/shared/TagBar.tsx @@ -40,7 +40,7 @@ export function TagBar({ )} {tripSegments?.map(tripSegment => ( - + {`${tripSegment.code} – ${tripSegment.name}`} ))} @@ -71,6 +71,7 @@ export function TagBar({ {!!state && ( type.hasDesignatedPorts && ( - + {type.name} ) From 315eccd1cb0ddc1e14db8149167c6e08207e84f0 Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Thu, 1 Aug 2024 20:47:05 +0200 Subject: [PATCH 03/41] Add author trigram field in auto prior notification form --- .../domain/entities/logbook/messages/PNO.kt | 1 + .../prior_notification/PriorNotification.kt | 1 - .../repositories/LogbookReportRepository.kt | 8 +- .../ComputeAutoPriorNotification.kt | 6 +- .../CreateOrUpdateManualPriorNotification.kt | 25 +- ...Note.kt => UpdateAutoPriorNotification.kt} | 10 +- .../api/bff/PriorNotificationController.kt | 108 ++++++--- ...t.kt => AutoPriorNotificationDataInput.kt} | 3 +- .../AutoPriorNotificationDataOutput.kt | 21 ++ .../ManualPriorNotificationDataOutput.kt | 4 +- .../PriorNotificationDetailDataOutput.kt | 10 +- .../database/entities/LogbookReportEntity.kt | 1 - .../entities/ManualPriorNotificationEntity.kt | 20 +- .../JpaLogbookReportRepository.kt | 16 +- ...or_notifications_author_trigram_column.sql | 2 + .../V666.11__Insert_dummy_risk_factors.sql | 2 - ...nsert_dummy_manual_prior_notifications.sql | 22 +- .../V666.11__Insert_dummy_risk_factors.jsonc | 35 --- ...5.1__Insert_more_pno_logbook_reports.jsonc | 1 - ...ert_dummy_manual_prior_notifications.jsonc | 22 +- .../GetNumberToVerifyUTests.kt | 2 - .../GetPriorNotificationsUTests.kt | 2 - ...t => UpdateAutoPriorNotificationUTests.kt} | 5 +- .../fakers/PriorNotificationFaker.kt | 1 - .../bff/PriorNotificationControllerITests.kt | 118 ++++++++-- .../JpaLogbookReportRepositoryITests.kt | 12 +- ...ManualPriorNotificationRepositoryITests.kt | 2 +- frontend/src/constants/index.ts | 1 + .../PriorNotification.types.ts | 27 ++- .../AutoPriorNotificationForm/Footer.tsx | 40 ++++ .../AutoPriorNotificationForm/Form.tsx | 118 ++++++++++ .../AutoPriorNotificationForm/index.tsx | 28 +++ .../Card.tsx | 80 ++----- .../Form.tsx | 8 +- .../Header.tsx | 0 .../constants.ts | 6 +- .../FormikFishingCatchesMultiSelect/index.tsx | 0 .../FormikFishingCatchesMultiSelect/styles.ts | 0 .../FormikFishingCatchesMultiSelect/types.ts | 0 .../FormikFishingCatchesMultiSelect/utils.tsx | 0 .../fields/FormikVesselSelect.tsx | 0 .../index.tsx | 36 +-- .../types.ts | 2 +- .../utils.tsx | 6 +- .../PriorNotificationCard/index.tsx | 216 ++---------------- .../components/PriorNotificationList/Row.tsx | 6 +- .../cells/ActionButtonsCell.tsx | 22 +- .../PriorNotificationList/index.tsx | 19 +- .../PriorNotification/priorNotificationApi.ts | 76 +++--- .../src/features/PriorNotification/slice.ts | 69 +++--- .../createOrUpdateManualPriorNotification.ts | 10 +- .../useCases/openAutoPriorNotificationForm.ts | 77 +++++++ ....ts => openManualPriorNotificationForm.ts} | 31 ++- .../useCases/openPriorNotificationCard.ts | 11 +- ...Note.ts => updateAutoPriorNotification.ts} | 8 +- ...teEditedPriorNotificationComputedValues.ts | 8 +- .../verifyAndSendPriorNotification.ts | 5 +- .../src/features/PriorNotification/utils.ts | 13 ++ 58 files changed, 814 insertions(+), 569 deletions(-) rename backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/{UpdatePriorNotificationNote.kt => UpdateAutoPriorNotification.kt} (73%) rename backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/{PriorNotificationDataInput.kt => AutoPriorNotificationDataInput.kt} (52%) create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/AutoPriorNotificationDataOutput.kt create mode 100644 backend/src/main/resources/db/migration/internal/V0.268__Drop_manual_prior_notifications_author_trigram_column.sql rename backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/{UpdatePriorNotificationNoteUTests.kt => UpdateAutoPriorNotificationUTests.kt} (93%) create mode 100644 frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx create mode 100644 frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Form.tsx create mode 100644 frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/Card.tsx (77%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/Form.tsx (94%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/Header.tsx (100%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/constants.ts (91%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/fields/FormikFishingCatchesMultiSelect/index.tsx (100%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/fields/FormikFishingCatchesMultiSelect/styles.ts (100%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/fields/FormikFishingCatchesMultiSelect/types.ts (100%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/fields/FormikFishingCatchesMultiSelect/utils.tsx (100%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/fields/FormikVesselSelect.tsx (100%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/index.tsx (71%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/types.ts (83%) rename frontend/src/features/PriorNotification/components/{PriorNotificationForm => ManualPriorNotificationForm}/utils.tsx (83%) create mode 100644 frontend/src/features/PriorNotification/useCases/openAutoPriorNotificationForm.ts rename frontend/src/features/PriorNotification/useCases/{openPriorNotificationForm.ts => openManualPriorNotificationForm.ts} (74%) rename frontend/src/features/PriorNotification/useCases/{updatePriorNotificationNote.ts => updateAutoPriorNotification.ts} (84%) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/messages/PNO.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/messages/PNO.kt index 76a8bf5a64..912a432889 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/messages/PNO.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/messages/PNO.kt @@ -14,6 +14,7 @@ class PNO() : LogbookMessageValue { var hasPortEntranceAuthorization: Boolean? = null var hasPortLandingAuthorization: Boolean? = null + var authorTrigram: String? = null var catchOnboard: List = emptyList() var catchToLand: List = emptyList() var economicZone: String? = null diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt index ef0e2dd88f..0a146b458b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt @@ -21,7 +21,6 @@ import java.time.ZonedDateTime data class PriorNotification( val reportId: String?, - val authorTrigram: String?, val createdAt: ZonedDateTime?, val didNotFishAfterZeroNotice: Boolean, val isManuallyCreated: Boolean, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt index d1a25f0034..60e2fe0773 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt @@ -75,7 +75,13 @@ interface LogbookReportRepository { fun findAllPriorNotificationsToVerify(): List - fun updatePriorNotificationNote(reportId: String, operationDate: ZonedDateTime, note: String?) + fun updatePriorNotificationData( + reportId: String, + operationDate: ZonedDateTime, + + authorTrigram: String?, + note: String?, + ) fun invalidate(reportId: String, operationDate: ZonedDateTime) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeAutoPriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeAutoPriorNotification.kt index 547526e4f7..1ad42407d0 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeAutoPriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/ComputeAutoPriorNotification.kt @@ -19,9 +19,9 @@ class ComputeAutoPriorNotification( segmentCodes: List, vesselId: Int, ): AutoPriorNotificationComputedValues { - val isPartOfControlUnitSubscriptions = pnoPortSubscriptionRepository.has(portLocode) - || pnoVesselSubscriptionRepository.has(vesselId) - || pnoSegmentSubscriptionRepository.has(portLocode, segmentCodes) + val isPartOfControlUnitSubscriptions = pnoPortSubscriptionRepository.has(portLocode) || + pnoVesselSubscriptionRepository.has(vesselId) || + pnoSegmentSubscriptionRepository.has(portLocode, segmentCodes) val nextState = PriorNotification.getNextState(isInVerificationScope, isPartOfControlUnitSubscriptions) return AutoPriorNotificationComputedValues(nextState) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotification.kt index 09ecd7e44f..2437c47a4b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotification.kt @@ -29,17 +29,18 @@ class CreateOrUpdateManualPriorNotification( private val logger: Logger = LoggerFactory.getLogger(CreateOrUpdateManualPriorNotification::class.java) fun execute( - hasPortEntranceAuthorization: Boolean, - hasPortLandingAuthorization: Boolean, + reportId: String?, + authorTrigram: String, didNotFishAfterZeroNotice: Boolean, expectedArrivalDate: ZonedDateTime, expectedLandingDate: ZonedDateTime, faoArea: String, fishingCatches: List, + hasPortEntranceAuthorization: Boolean, + hasPortLandingAuthorization: Boolean, note: String?, portLocode: String, - reportId: String?, sentAt: ZonedDateTime, purpose: LogbookMessagePurpose, tripGearCodes: List, @@ -65,13 +66,14 @@ class CreateOrUpdateManualPriorNotification( val vessel = vesselRepository.findVesselById(vesselId) val priorNotificationTypes = computedValues.types.map { it.toPriorNotificationType() } val message = getMessage( - hasPortEntranceAuthorization = hasPortEntranceAuthorization, - hasPortLandingAuthorization = hasPortLandingAuthorization, + authorTrigram = authorTrigram, expectedArrivalDate = expectedArrivalDate, expectedLandingDate = expectedLandingDate, // At the moment, manual prior notifications only have a single global FAO area field in Frontend, // so we transform that single FAO area into an FAO area per fishing catch. fishingCatches = fishingCatchesWithFaoArea, + hasPortEntranceAuthorization = hasPortEntranceAuthorization, + hasPortLandingAuthorization = hasPortLandingAuthorization, note = note, pnoTypes = priorNotificationTypes, portLocode = portLocode, @@ -115,7 +117,6 @@ class CreateOrUpdateManualPriorNotification( val newOrNextPriorNotification = PriorNotification( reportId = reportId, - authorTrigram = authorTrigram, didNotFishAfterZeroNotice = didNotFishAfterZeroNotice, isManuallyCreated = true, logbookMessageAndValue = logbookMessageAndValue, @@ -131,6 +132,8 @@ class CreateOrUpdateManualPriorNotification( updatedAt = null, ) + // Any update must trigger a new PDF generation, so we delete the existing PDF document + // which is re-generated by the Pipeline each time a PDF is deleted if (reportId !== null) { try { priorNotificationPdfDocumentRepository.deleteByReportId(reportId) @@ -151,12 +154,13 @@ class CreateOrUpdateManualPriorNotification( } private fun getMessage( - hasPortEntranceAuthorization: Boolean, - hasPortLandingAuthorization: Boolean, + authorTrigram: String, purpose: LogbookMessagePurpose, expectedArrivalDate: ZonedDateTime, expectedLandingDate: ZonedDateTime, fishingCatches: List, + hasPortEntranceAuthorization: Boolean, + hasPortLandingAuthorization: Boolean, note: String?, pnoTypes: List, portLocode: String, @@ -174,8 +178,7 @@ class CreateOrUpdateManualPriorNotification( val portName = allPorts.find { it.locode == portLocode }?.name return PNO().apply { - this.hasPortEntranceAuthorization = hasPortEntranceAuthorization - this.hasPortLandingAuthorization = hasPortLandingAuthorization + this.authorTrigram = authorTrigram this.catchOnboard = fishingCatches this.catchToLand = fishingCatches this.economicZone = null @@ -184,6 +187,8 @@ class CreateOrUpdateManualPriorNotification( // so we transform that single FAO area into an FAO area per fishing catch. // This means we don't need to set a global PNO message FAO area here. this.faoZone = null + this.hasPortEntranceAuthorization = hasPortEntranceAuthorization + this.hasPortLandingAuthorization = hasPortLandingAuthorization // If the prior notification is not in verification scope, // we pass `isBeingSent` as `true` in order to ask the workflow to send it. this.isBeingSent = isBeingSent diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNote.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdateAutoPriorNotification.kt similarity index 73% rename from backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNote.kt rename to backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdateAutoPriorNotification.kt index 6ec5df5f43..fb8273f26b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNote.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdateAutoPriorNotification.kt @@ -9,27 +9,31 @@ import org.slf4j.LoggerFactory import java.time.ZonedDateTime @UseCase -class UpdatePriorNotificationNote( +class UpdateAutoPriorNotification( private val logbookReportRepository: LogbookReportRepository, private val priorNotificationPdfDocumentRepository: PriorNotificationPdfDocumentRepository, private val getPriorNotification: GetPriorNotification, ) { - private val logger: Logger = LoggerFactory.getLogger(CreateOrUpdateManualPriorNotification::class.java) + private val logger: Logger = LoggerFactory.getLogger(UpdateAutoPriorNotification::class.java) fun execute( reportId: String, operationDate: ZonedDateTime, + authorTrigram: String?, note: String?, ): PriorNotification { + // Any update must trigger a new PDF generation, so we delete the existing PDF document + // which is re-generated by the Pipeline each time a PDF is deleted try { priorNotificationPdfDocumentRepository.deleteByReportId(reportId) } catch (e: Exception) { logger.warn("Could not delete existing PDF document", e) } - logbookReportRepository.updatePriorNotificationNote( + logbookReportRepository.updatePriorNotificationData( reportId = reportId, operationDate = operationDate, + authorTrigram = authorTrigram, note = note, ) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt index f781e6e946..a282443a8b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt @@ -6,9 +6,9 @@ import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.filters.Prior import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.sorters.PriorNotificationsSortColumn import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.* import fr.gouv.cnsp.monitorfish.infrastructure.api.input.AutoPriorNotificationComputeDataInput +import fr.gouv.cnsp.monitorfish.infrastructure.api.input.AutoPriorNotificationDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationComputeDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationDataInput -import fr.gouv.cnsp.monitorfish.infrastructure.api.input.PriorNotificationDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.* import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter @@ -29,7 +29,7 @@ class PriorNotificationController( private val getPriorNotifications: GetPriorNotifications, private val getNumberToVerify: GetNumberToVerify, private val getPriorNotificationTypes: GetPriorNotificationTypes, - private val updatePriorNotificationNote: UpdatePriorNotificationNote, + private val updateAutoPriorNotification: UpdateAutoPriorNotification, private val verifyAndSendPriorNotification: VerifyAndSendPriorNotification, private val invalidatePriorNotification: InvalidatePriorNotification, ) { @@ -129,14 +129,6 @@ class PriorNotificationController( ) } - @GetMapping("/to_verify") - @Operation(summary = "Get number of prior notifications to verify") - fun getNumberToVerify(): PriorNotificationsExtraDataOutput { - val priorNotificationStats = getNumberToVerify.execute() - - return PriorNotificationsExtraDataOutput.fromPriorNotificationStats(priorNotificationStats) - } - @PostMapping("/auto/compute") @Operation(summary = "Calculate auto prior notification next state") fun getAutoComputation( @@ -153,8 +145,51 @@ class PriorNotificationController( return AutoPriorNotificationComputedValuesDataOutput(manualPriorNotificationComputedValues.nextState) } + @GetMapping("/auto/{reportId}/data") + @Operation(summary = "Get an auto prior notification form data by its `reportId`") + fun getAutoData( + @PathParam("Logbook message `reportId`") + @PathVariable(name = "reportId") + reportId: String, + @Parameter(description = "Operation date (to optimize SQL query via Timescale).") + @RequestParam(name = "operationDate") + operationDate: ZonedDateTime, + ): AutoPriorNotificationDataOutput { + return AutoPriorNotificationDataOutput.fromPriorNotification( + getPriorNotification.execute( + reportId, + operationDate, + false, + ), + ) + } + + @PutMapping("/auto/{reportId}") + @Operation(summary = "Update an auto prior notification by its `reportId`") + fun updateAuto( + @PathParam("Logbook message `reportId`") + @PathVariable(name = "reportId") + reportId: String, + @Parameter(description = "Operation date (to optimize SQL query via Timescale).") + @RequestParam(name = "operationDate") + operationDate: ZonedDateTime, + @RequestBody + autoPriorNotificationDataInput: AutoPriorNotificationDataInput, + ): AutoPriorNotificationDataOutput { + val updatedPriorNotification = updateAutoPriorNotification.execute( + reportId = reportId, + operationDate = operationDate, + authorTrigram = autoPriorNotificationDataInput.authorTrigram, + note = autoPriorNotificationDataInput.note, + ) + + return AutoPriorNotificationDataOutput.fromPriorNotification(updatedPriorNotification) + } + @PostMapping("/manual/compute") - @Operation(summary = "Calculate manual prior notification fleet segments, prior notification types, risk factor and next state") + @Operation( + summary = "Calculate manual prior notification fleet segments, prior notification types, risk factor and next state", + ) fun getManualComputation( @RequestBody manualPriorNotificationComputeDataInput: ManualPriorNotificationComputeDataInput, @@ -173,9 +208,9 @@ class PriorNotificationController( .fromManualPriorNotificationComputedValues(manualPriorNotificationComputedValues) } - @GetMapping("/manual/{reportId}") + @GetMapping("/manual/{reportId}/data") @Operation(summary = "Get a manual prior notification form data by its `reportId`") - fun getOneManual( + fun getManualData( @PathParam("Logbook message `reportId`") @PathVariable(name = "reportId") reportId: String, @@ -192,16 +227,13 @@ class PriorNotificationController( ) } - @PutMapping("/manual/{reportId}") - @Operation(summary = "Update a manual prior notification by its `reportId`") - fun updateManual( - @PathParam("Logbook message `reportId`") - @PathVariable(name = "reportId") - reportId: String, + @PostMapping("/manual") + @Operation(summary = "Create a new manual prior notification") + fun createManual( @RequestBody manualPriorNotificationDataInput: ManualPriorNotificationDataInput, ): ManualPriorNotificationDataOutput { - val updatedPriorNotification = createOrUpdateManualPriorNotification.execute( + val createdPriorNotification = createOrUpdateManualPriorNotification.execute( hasPortEntranceAuthorization = manualPriorNotificationDataInput.hasPortEntranceAuthorization, hasPortLandingAuthorization = manualPriorNotificationDataInput.hasPortLandingAuthorization, authorTrigram = manualPriorNotificationDataInput.authorTrigram, @@ -212,23 +244,26 @@ class PriorNotificationController( fishingCatches = manualPriorNotificationDataInput.fishingCatches.map { it.toLogbookFishingCatch() }, note = manualPriorNotificationDataInput.note, portLocode = manualPriorNotificationDataInput.portLocode, - reportId = reportId, + reportId = null, sentAt = manualPriorNotificationDataInput.sentAt, purpose = manualPriorNotificationDataInput.purpose, tripGearCodes = manualPriorNotificationDataInput.tripGearCodes, vesselId = manualPriorNotificationDataInput.vesselId, ) - return ManualPriorNotificationDataOutput.fromPriorNotification(updatedPriorNotification) + return ManualPriorNotificationDataOutput.fromPriorNotification(createdPriorNotification) } - @PostMapping("/manual") - @Operation(summary = "Create a new manual prior notification") - fun createManual( + @PutMapping("/manual/{reportId}") + @Operation(summary = "Update a manual prior notification by its `reportId`") + fun updateManual( + @PathParam("Logbook message `reportId`") + @PathVariable(name = "reportId") + reportId: String, @RequestBody manualPriorNotificationDataInput: ManualPriorNotificationDataInput, ): ManualPriorNotificationDataOutput { - val createdPriorNotification = createOrUpdateManualPriorNotification.execute( + val updatedPriorNotification = createOrUpdateManualPriorNotification.execute( hasPortEntranceAuthorization = manualPriorNotificationDataInput.hasPortEntranceAuthorization, hasPortLandingAuthorization = manualPriorNotificationDataInput.hasPortLandingAuthorization, authorTrigram = manualPriorNotificationDataInput.authorTrigram, @@ -239,14 +274,22 @@ class PriorNotificationController( fishingCatches = manualPriorNotificationDataInput.fishingCatches.map { it.toLogbookFishingCatch() }, note = manualPriorNotificationDataInput.note, portLocode = manualPriorNotificationDataInput.portLocode, - reportId = null, + reportId = reportId, sentAt = manualPriorNotificationDataInput.sentAt, purpose = manualPriorNotificationDataInput.purpose, tripGearCodes = manualPriorNotificationDataInput.tripGearCodes, vesselId = manualPriorNotificationDataInput.vesselId, ) - return ManualPriorNotificationDataOutput.fromPriorNotification(createdPriorNotification) + return ManualPriorNotificationDataOutput.fromPriorNotification(updatedPriorNotification) + } + + @GetMapping("/to_verify") + @Operation(summary = "Get number of prior notifications to verify") + fun getNumberToVerify(): PriorNotificationsExtraDataOutput { + val priorNotificationStats = getNumberToVerify.execute() + + return PriorNotificationsExtraDataOutput.fromPriorNotificationStats(priorNotificationStats) } @GetMapping("/types") @@ -299,12 +342,13 @@ class PriorNotificationController( @RequestParam(name = "operationDate") operationDate: ZonedDateTime, @RequestBody - priorNotificationDataInput: PriorNotificationDataInput, + autoPriorNotificationDataInput: AutoPriorNotificationDataInput, ): PriorNotificationDetailDataOutput { - val updatedPriorNotification = updatePriorNotificationNote.execute( - note = priorNotificationDataInput.note, - operationDate = operationDate, + val updatedPriorNotification = updateAutoPriorNotification.execute( reportId = reportId, + operationDate = operationDate, + authorTrigram = autoPriorNotificationDataInput.authorTrigram, + note = autoPriorNotificationDataInput.note, ) return PriorNotificationDetailDataOutput.fromPriorNotification(updatedPriorNotification) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/PriorNotificationDataInput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/AutoPriorNotificationDataInput.kt similarity index 52% rename from backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/PriorNotificationDataInput.kt rename to backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/AutoPriorNotificationDataInput.kt index f76c1bf9cb..2517b9ce25 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/PriorNotificationDataInput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/AutoPriorNotificationDataInput.kt @@ -1,5 +1,6 @@ package fr.gouv.cnsp.monitorfish.infrastructure.api.input -data class PriorNotificationDataInput( +data class AutoPriorNotificationDataInput( + val authorTrigram: String?, val note: String?, ) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/AutoPriorNotificationDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/AutoPriorNotificationDataOutput.kt new file mode 100644 index 0000000000..cf5a214ef2 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/AutoPriorNotificationDataOutput.kt @@ -0,0 +1,21 @@ +package fr.gouv.cnsp.monitorfish.infrastructure.api.outputs + +import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification + +data class AutoPriorNotificationDataOutput( + val authorTrigram: String?, + val note: String?, + val reportId: String, +) { + companion object { + fun fromPriorNotification(priorNotification: PriorNotification): AutoPriorNotificationDataOutput { + val reportId = requireNotNull(priorNotification.reportId) { "`priorNotification.reportId` is null." } + + return AutoPriorNotificationDataOutput( + authorTrigram = priorNotification.logbookMessageAndValue.value.authorTrigram, + note = priorNotification.logbookMessageAndValue.value.note, + reportId = reportId, + ) + } + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationDataOutput.kt index 6a6571951e..47cf586ecf 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationDataOutput.kt @@ -29,8 +29,8 @@ data class ManualPriorNotificationDataOutput( val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage val pnoMessage = priorNotification.logbookMessageAndValue.value - val authorTrigram = requireNotNull(priorNotification.authorTrigram) { - "`priorNotification.authorTrigram` is null." + val authorTrigram = requireNotNull(pnoMessage.authorTrigram) { + "`pnoMessage.authorTrigram` is null." } val expectedArrivalDate = CustomZonedDateTime.fromZonedDateTime( requireNotNull(pnoMessage.predictedArrivalDatetimeUtc) { diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDetailDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDetailDataOutput.kt index 301c3a5942..206d5d5847 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDetailDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDetailDataOutput.kt @@ -2,16 +2,18 @@ package fr.gouv.cnsp.monitorfish.infrastructure.api.outputs import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationState +import java.time.ZonedDateTime class PriorNotificationDetailDataOutput( - // TODO Rename that to `reportId`. /** Reference logbook message (report) `reportId`. */ - val id: String, + val reportId: String, /** Unique identifier concatenating all the DAT, COR, RET & DEL operations `id` used for data consolidation. */ val fingerprint: String, val isLessThanTwelveMetersVessel: Boolean, + val isManuallyCreated: Boolean, val isVesselUnderCharter: Boolean?, val logbookMessage: LogbookMessageDataOutput, + val operationDate: ZonedDateTime, val state: PriorNotificationState?, val riskFactor: Double?, ) { @@ -30,11 +32,13 @@ class PriorNotificationDetailDataOutput( val logbookMessageDataOutput = LogbookMessageDataOutput.fromLogbookMessage(logbookMessage) return PriorNotificationDetailDataOutput( - id = referenceReportId, + reportId = referenceReportId, fingerprint = priorNotification.fingerprint, isLessThanTwelveMetersVessel = isLessThanTwelveMetersVessel, + isManuallyCreated = priorNotification.isManuallyCreated, isVesselUnderCharter, logbookMessage = logbookMessageDataOutput, + operationDate = logbookMessage.operationDateTime, state = priorNotification.state, riskFactor = priorNotification.logbookMessageAndValue.value.riskFactor, ) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt index 0e60e31d3f..f6bbc2236b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt @@ -150,7 +150,6 @@ data class LogbookReportEntity( return PriorNotification( reportId = reportId, - authorTrigram = null, createdAt = operationDateTime.atZone(UTC), didNotFishAfterZeroNotice = false, isManuallyCreated = false, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/ManualPriorNotificationEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/ManualPriorNotificationEntity.kt index a9a7ad9442..d5bee5dd35 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/ManualPriorNotificationEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/ManualPriorNotificationEntity.kt @@ -18,9 +18,6 @@ data class ManualPriorNotificationEntity( @GeneratedValue(strategy = GenerationType.IDENTITY) val reportId: String?, - @Column(name = "author_trigram") - val authorTrigram: String, - @Column(name = "vessel_id", nullable = false) val vesselId: Int, @@ -73,27 +70,23 @@ data class ManualPriorNotificationEntity( priorNotification.updatedAt } - requireNotNull(pnoLogbookMessage.vesselId) { - "vesselId must be not null" - } - requireNotNull(priorNotification.sentAt) { - "sentAt must be not null" - } + val cfr = requireNotNull(pnoLogbookMessage.internalReferenceNumber) { "`cfr` is null." } + val sentAt = requireNotNull(priorNotification.sentAt) { "`sentAt` is null." } + val vesselId = requireNotNull(pnoLogbookMessage.vesselId) { "`vesselId` is null." } return ManualPriorNotificationEntity( reportId = pnoLogbookMessage.reportId, - authorTrigram = requireNotNull(priorNotification.authorTrigram), - cfr = requireNotNull(pnoLogbookMessage.internalReferenceNumber), + cfr = cfr, createdAt = createdAt, didNotFishAfterZeroNotice = priorNotification.didNotFishAfterZeroNotice, flagState = pnoLogbookMessage.flagState, - sentAt = priorNotification.sentAt, + sentAt = sentAt, tripGears = pnoLogbookMessage.tripGears, tripSegments = pnoLogbookMessage.tripSegments, updatedAt = updatedAt, value = pnoLogbookMessageValue, vesselName = pnoLogbookMessage.vesselName, - vesselId = pnoLogbookMessage.vesselId, + vesselId = vesselId, ) } catch (e: IllegalArgumentException) { throw BackendInternalException( @@ -132,7 +125,6 @@ data class ManualPriorNotificationEntity( val logbookMessageAndValue = LogbookMessageAndValue(pnoLogbookMessage, PNO::class.java) return PriorNotification( - authorTrigram = authorTrigram, createdAt = createdAt, didNotFishAfterZeroNotice = didNotFishAfterZeroNotice, isManuallyCreated = true, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt index 9a35e273d8..b9c5538fe0 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt @@ -389,7 +389,13 @@ class JpaLogbookReportRepository( } @Transactional - override fun updatePriorNotificationNote(reportId: String, operationDate: ZonedDateTime, note: String?) { + override fun updatePriorNotificationData( + reportId: String, + operationDate: ZonedDateTime, + + authorTrigram: String?, + note: String?, + ) { val logbookReportEntities = dbLogbookReportRepository.findEnrichedPnoReferenceAndRelatedOperationsByReportId( reportId, @@ -404,12 +410,16 @@ class JpaLogbookReportRepository( .filter { it.operationType in listOf(LogbookOperationType.DAT, LogbookOperationType.COR) } .map { logbookReportEntity -> val pnoMessage = objectMapper.readValue(logbookReportEntity.message, PNO::class.java) + pnoMessage.authorTrigram = authorTrigram pnoMessage.note = note /** - * The PNO states are re-initialized, - * - the PDF will be generated + * The PNO states are re-initialized: + * - the PDF will be re-generated (done in the use case by deleting the old one) * - the PNO will require another verification before sending + * + * Note: We will lose the distinction between `AUTO_SEND_DONE` and `OUT_OF_VERIFICATION_SCOPE` + * if it was in one of these states before the update. But it's an acceptable trade-off. */ pnoMessage.isBeingSent = false pnoMessage.isVerified = false diff --git a/backend/src/main/resources/db/migration/internal/V0.268__Drop_manual_prior_notifications_author_trigram_column.sql b/backend/src/main/resources/db/migration/internal/V0.268__Drop_manual_prior_notifications_author_trigram_column.sql new file mode 100644 index 0000000000..0bd02751f7 --- /dev/null +++ b/backend/src/main/resources/db/migration/internal/V0.268__Drop_manual_prior_notifications_author_trigram_column.sql @@ -0,0 +1,2 @@ +ALTER TABLE public.manual_prior_notifications + DROP COLUMN author_trigram; diff --git a/backend/src/main/resources/db/testdata/V666.11__Insert_dummy_risk_factors.sql b/backend/src/main/resources/db/testdata/V666.11__Insert_dummy_risk_factors.sql index fde60fb433..3071bec9a3 100644 --- a/backend/src/main/resources/db/testdata/V666.11__Insert_dummy_risk_factors.sql +++ b/backend/src/main/resources/db/testdata/V666.11__Insert_dummy_risk_factors.sql @@ -7,8 +7,6 @@ INSERT INTO risk_factors (cfr, control_priority_level, control_rate_risk_factor, INSERT INTO risk_factors (cfr, control_priority_level, control_rate_risk_factor, departure_datetime_utc, detectability_risk_factor, external_immatriculation, impact_risk_factor, infraction_rate_risk_factor, infraction_score, ircs, last_control_datetime_utc, last_control_infraction, last_logbook_message_datetime_utc, number_controls_last_3_years, number_controls_last_5_years, number_gear_seizures_last_5_years, number_infractions_last_5_years, number_recent_controls, number_species_seizures_last_5_years, number_vessel_seizures_last_5_years, post_control_comments, probability_risk_factor, risk_factor, segment_highest_impact, segment_highest_priority, segments, total_weight_onboard, trip_number, vessel_id, gear_onboard, species_onboard) VALUES ('CFR102', 4, 5, NOW() - INTERVAL '1 day', 5, 'EXTIMM103', 4, 3, NULL, 'IRCS103', NULL, true, NOW(), 0, 0, 4, 5, 0, 3, 2, '', 4, 4, 'NWW10', 'PEL 03', '{"NWW10", "PEL 03"}', 12345.67, 123103, 102, '[{"gear":"OTB","mesh":70,"dimensions":45}]', '[{"gear":"OTB","faoZone":"27.8.b","species":"BLI","weight":13.46},{"gear":"OTB","faoZone":"27.8.c","species":"HKE","weight":235.6},{"gear":"OTB","faoZone":"27.8.b","species":"HKE","weight":235.6}]'); -INSERT INTO risk_factors (cfr, control_priority_level, control_rate_risk_factor, departure_datetime_utc, detectability_risk_factor, external_immatriculation, impact_risk_factor, infraction_rate_risk_factor, infraction_score, ircs, last_control_datetime_utc, last_control_infraction, last_logbook_message_datetime_utc, number_controls_last_3_years, number_controls_last_5_years, number_gear_seizures_last_5_years, number_infractions_last_5_years, number_recent_controls, number_species_seizures_last_5_years, number_vessel_seizures_last_5_years, post_control_comments, probability_risk_factor, risk_factor, segment_highest_impact, segment_highest_priority, segments, total_weight_onboard, trip_number, vessel_id, gear_onboard, species_onboard) VALUES ('CFR109', 4, 5, '2023-12-31 14:00:00', 5, 'EXTIMM109', 4, 3, NULL, 'IRCS109', '2024-02-01 00:00:00', true, NOW(), 0, 0, 4, 5, 0, 3, 2, '', 4, 4, 'NWW10', 'PEL 03', '{"NWW10", "PEL 03"}', 12345.67, 123109, 109, '[]', '[]'); - INSERT INTO risk_factors (cfr, control_priority_level, control_rate_risk_factor, departure_datetime_utc, detectability_risk_factor, external_immatriculation, impact_risk_factor, infraction_rate_risk_factor, infraction_score, ircs, last_control_datetime_utc, last_control_infraction, last_logbook_message_datetime_utc, number_controls_last_3_years, number_controls_last_5_years, number_gear_seizures_last_5_years, number_infractions_last_5_years, number_recent_controls, number_species_seizures_last_5_years, number_vessel_seizures_last_5_years, post_control_comments, probability_risk_factor, risk_factor, segment_highest_impact, segment_highest_priority, segments, total_weight_onboard, trip_number, vessel_id, gear_onboard, species_onboard) VALUES ('CFR109', 4, 5, '2024-03-31 14:00:00', 5, 'EXTIMM109', 4, 3, NULL, 'IRCS109', '2024-04-01 00:00:00', true, NOW(), 0, 0, 4, 5, 0, 3, 2, '', 4, 4, 'NWW10', 'PEL 03', '{"NWW10", "PEL 03"}', 12345.67, 123109, 109, '[]', '[]'); INSERT INTO risk_factors (cfr, control_priority_level, control_rate_risk_factor, departure_datetime_utc, detectability_risk_factor, external_immatriculation, impact_risk_factor, infraction_rate_risk_factor, infraction_score, ircs, last_control_datetime_utc, last_control_infraction, last_logbook_message_datetime_utc, number_controls_last_3_years, number_controls_last_5_years, number_gear_seizures_last_5_years, number_infractions_last_5_years, number_recent_controls, number_species_seizures_last_5_years, number_vessel_seizures_last_5_years, post_control_comments, probability_risk_factor, risk_factor, segment_highest_impact, segment_highest_priority, segments, total_weight_onboard, trip_number, vessel_id, gear_onboard, species_onboard) VALUES ('CFR115', 4, 5, NOW() - INTERVAL '1 day', 5, 'EXTIMM115', 4, 3, NULL, 'IRCS115', '2024-05-01 00:00:00', true, NOW(), 0, 0, 4, 5, 0, 3, 2, '', 4, 2.5, 'NWW10', 'PEL 03', '{"NWW10", "PEL 03"}', 12345.67, 123102, 115, '[{"gear":"OTB","mesh":70,"dimensions":45}]', '[{"gear":"OTB","faoZone":"27.8.b","species":"BLI","weight":13.46},{"gear":"OTB","faoZone":"27.8.c","species":"HKE","weight":235.6},{"gear":"OTB","faoZone":"27.8.b","species":"HKE","weight":235.6}]'); diff --git a/backend/src/main/resources/db/testdata/V666.5.2__Insert_dummy_manual_prior_notifications.sql b/backend/src/main/resources/db/testdata/V666.5.2__Insert_dummy_manual_prior_notifications.sql index 7442118925..a131cee683 100644 --- a/backend/src/main/resources/db/testdata/V666.5.2__Insert_dummy_manual_prior_notifications.sql +++ b/backend/src/main/resources/db/testdata/V666.5.2__Insert_dummy_manual_prior_notifications.sql @@ -1,54 +1,54 @@ -- /!\ This file is automatically generated by a local script. -- Do NOT update it directly, update the associated .jsonc file in /backend/src/main/resources/db/testdata/json/. -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000001', 'ABC', 'CFR112', 112, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"LNP"}]', '[{"segment":"NWW09","segmentName":"Lignes"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'POISSON PAS NET', '{"riskFactor":2.1,"catchOnboard":[{"faoZone":"21.1.A","weight":72,"nbFish":null,"species":"SOS"}],"catchToLand":[{"faoZone":"21.1.A","weight":72,"nbFish":null,"species":"SOS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isSent":false,"isVerified":false,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRVNE","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000001', 'CFR112', 112, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"LNP"}]', '[{"segment":"NWW09","segmentName":"Lignes"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'POISSON PAS NET', '{"authorTrigram":"ABC","riskFactor":2.1,"catchOnboard":[{"faoZone":"21.1.A","weight":72,"nbFish":null,"species":"SOS"}],"catchToLand":[{"faoZone":"21.1.A","weight":72,"nbFish":null,"species":"SOS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isSent":false,"isVerified":false,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRVNE","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000001'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3.5 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000001'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000001'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000002', 'ABC', 'CFR115', 115, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'BEL', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"TB"},{"gear":"TBS"}]', '[{"segment":"NWW03","segmentName":"Chalut de fond en eau profonde"},{"segment":"NWW05","segmentName":"Chalut à perche"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'DOS FIN', '{"riskFactor":2.7,"catchOnboard":[{"faoZone":"21.1.B","weight":300,"nbFish":10,"species":"BF1"},{"faoZone":"21.1.B","weight":100,"nbFish":20,"species":"BF3"},{"faoZone":"21.1.B","weight":400,"nbFish":80,"species":"SWO"},{"faoZone":"21.1.B","weight":600,"nbFish":null,"species":"BFT"},{"faoZone":"21.1.B","weight":200,"nbFish":25,"species":"BF2"}],"catchToLand":[{"faoZone":"21.1.B","weight":600,"nbFish":null,"species":"BFT"},{"faoZone":"21.1.B","weight":300,"nbFish":10,"species":"BF1"},{"faoZone":"21.1.B","weight":200,"nbFish":25,"species":"BF2"},{"faoZone":"21.1.B","weight":100,"nbFish":20,"species":"BF3"},{"faoZone":"21.1.B","weight":400,"nbFish":80,"species":"SWO"}],"faoZone":null,"isBeingSent":true,"isInVerificationScope":false,"isSent":false,"isVerified":false,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type B","minimumNotificationPeriod":4,"hasDesignatedPorts":false},{"pnoTypeName":"Préavis type C","minimumNotificationPeriod":8,"hasDesignatedPorts":true}],"port":"FRVNE","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000002', 'CFR115', 115, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'BEL', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"TB"},{"gear":"TBS"}]', '[{"segment":"NWW03","segmentName":"Chalut de fond en eau profonde"},{"segment":"NWW05","segmentName":"Chalut à perche"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'DOS FIN', '{"authorTrigram":"ABC","riskFactor":2.7,"catchOnboard":[{"faoZone":"21.1.B","weight":300,"nbFish":10,"species":"BF1"},{"faoZone":"21.1.B","weight":100,"nbFish":20,"species":"BF3"},{"faoZone":"21.1.B","weight":400,"nbFish":80,"species":"SWO"},{"faoZone":"21.1.B","weight":600,"nbFish":null,"species":"BFT"},{"faoZone":"21.1.B","weight":200,"nbFish":25,"species":"BF2"}],"catchToLand":[{"faoZone":"21.1.B","weight":600,"nbFish":null,"species":"BFT"},{"faoZone":"21.1.B","weight":300,"nbFish":10,"species":"BF1"},{"faoZone":"21.1.B","weight":200,"nbFish":25,"species":"BF2"},{"faoZone":"21.1.B","weight":100,"nbFish":20,"species":"BF3"},{"faoZone":"21.1.B","weight":400,"nbFish":80,"species":"SWO"}],"faoZone":null,"isBeingSent":true,"isInVerificationScope":false,"isSent":false,"isVerified":false,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type B","minimumNotificationPeriod":4,"hasDesignatedPorts":false},{"pnoTypeName":"Préavis type C","minimumNotificationPeriod":8,"hasDesignatedPorts":true}],"port":"FRVNE","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000002'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '4 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000002'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000002'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000003', 'ABC', 'CFR117', 117, NOW() AT TIME ZONE 'UTC' - INTERVAL '50 minutes', true, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"TBS"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"},{"segment":"MED02","segmentName":"All Trawls 2"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '5 minutes', 'QUEUE DE POISSON', '{"riskFactor":3.1,"catchOnboard":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"BIB"}],"catchToLand":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"BIB"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isSent":true,"isVerified":false,"note":"Pêche abandonnée pour cause de météo défavorable.","pnoTypes":[{"pnoTypeName":"Préavis type E","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000003', 'CFR117', 117, NOW() AT TIME ZONE 'UTC' - INTERVAL '50 minutes', true, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"TBS"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"},{"segment":"MED02","segmentName":"All Trawls 2"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '5 minutes', 'QUEUE DE POISSON', '{"authorTrigram":"ABC","riskFactor":3.1,"catchOnboard":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"BIB"}],"catchToLand":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"BIB"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isSent":true,"isVerified":false,"note":"Pêche abandonnée pour cause de météo défavorable.","pnoTypes":[{"pnoTypeName":"Préavis type E","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000003'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3.5 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000003'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000003'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000004', 'ABC', 'CFR116', 116, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'NAVIRE RENOMMÉ (ANCIEN NOM)', '{"riskFactor":1.5,"catchOnboard":[{"faoZone":"21.1.C","weight":24.3,"nbFish":null,"species":"ALV"}],"catchToLand":[{"faoZone":"21.1.C","weight":24.3,"nbFish":null,"species":"ALV"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isInvalidated":true,"isSent":false,"isVerified":true,"note":0,"pnoTypes":[{"pnoTypeName":"Préavis type C","minimumNotificationPeriod":8,"hasDesignatedPorts":true}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000004', 'CFR116', 116, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'NAVIRE RENOMMÉ (ANCIEN NOM)', '{"authorTrigram":"ABC","riskFactor":1.5,"catchOnboard":[{"faoZone":"21.1.C","weight":24.3,"nbFish":null,"species":"ALV"}],"catchToLand":[{"faoZone":"21.1.C","weight":24.3,"nbFish":null,"species":"ALV"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isInvalidated":true,"isSent":false,"isVerified":true,"note":0,"pnoTypes":[{"pnoTypeName":"Préavis type C","minimumNotificationPeriod":8,"hasDesignatedPorts":true}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000004'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3.5 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000004'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000004'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000005', 'ABC', 'CFR120', 120, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'ITA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'VIVA L''ITALIA', '{"riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":true,"isInVerificationScope":false,"isSent":false,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type C","minimumNotificationPeriod":8,"hasDesignatedPorts":true}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000005', 'CFR120', 120, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'ITA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'VIVA L''ITALIA', '{"authorTrigram":"ABC","riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":true,"isInVerificationScope":false,"isSent":false,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type C","minimumNotificationPeriod":8,"hasDesignatedPorts":true}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000005'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000005'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000005'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000006', 'ABC', 'CFR121', 121, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'MARE ET BASS', '{"riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isSent":true,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000006', 'CFR121', 121, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'MARE ET BASS', '{"authorTrigram":"ABC","riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isSent":true,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000006'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3.5 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000006'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000006'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000007', 'ABC', 'CFR122', 122, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'FILET DOUX', '{"riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":true,"isSent":false,"isVerified":false,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000007', 'CFR122', 122, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'FILET DOUX', '{"authorTrigram":"ABC","riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":true,"isSent":false,"isVerified":false,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000007'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3.5 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000007'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000007'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000008', 'ABC', 'CFR123', 123, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'DÉVOILÉ', '{"riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":true,"isSent":false,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000008', 'CFR123', 123, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'DÉVOILÉ', '{"authorTrigram":"ABC","riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":true,"isSent":false,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000008'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3.5 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000008'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000008'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000009', 'ABC', 'CFR124', 124, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'MAT QUILLE', '{"riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":true,"isInVerificationScope":true,"isSent":false,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000009', 'CFR124', 124, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'MAT QUILLE', '{"authorTrigram":"ABC","riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":true,"isInVerificationScope":true,"isSent":false,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000009'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3.5 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000009'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000009'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000010', 'ABC', 'CFR125', 125, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'BEAU SÉANT', '{"riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":true,"isSent":true,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000010', 'CFR125', 125, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '30 minutes', '[{"gear":"OTT"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'BEAU SÉANT', '{"authorTrigram":"ABC","riskFactor":3.2,"catchOnboard":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"catchToLand":[{"faoZone":"21.1.C","weight":50,"nbFish":null,"species":"AGS"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":true,"isSent":true,"isVerified":true,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type A","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRMRS","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null}'); UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArrivalDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000010'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedLandingDatetimeUtc}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' + INTERVAL '3.5 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000010'; UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB(TO_CHAR(NOW() AT TIME ZONE 'UTC' - INTERVAL '10 hours', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')), true) WHERE report_id = '00000000-0000-4000-0000-000000000010'; -INSERT INTO manual_prior_notifications (report_id, author_trigram, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000011', 'ABC', 'CFR118', 118, '2023-01-01T08:45:00', true, 'FRA', '2023-01-01T08:30:00', '[{"gear":"OTB"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', '2023-01-01T08:45:00', 'GOUJON BOUGON', '{"riskFactor":3.8,"catchOnboard":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"BIB"}],"catchToLand":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"BIB"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isSent":false,"isVerified":false,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type F","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRNCE","predictedArrivalDatetimeUtc":"2023-01-01T10:00:00Z","predictedLandingDatetimeUtc":"2023-01-01T10:30:00Z","purpose":"LAN","tripStartDate":"2023-01-01T08:00:00Z"}'); +INSERT INTO manual_prior_notifications (report_id, cfr, vessel_id, created_at, did_not_fish_after_zero_notice, flag_state, sent_at, trip_gears, trip_segments, updated_at, vessel_name, value) VALUES ('00000000-0000-4000-0000-000000000011', 'CFR118', 118, '2023-01-01T08:45:00', true, 'FRA', '2023-01-01T08:30:00', '[{"gear":"OTB"}]', '[{"segment":"MED01","segmentName":"All Trawls 1"}]', '2023-01-01T08:45:00', 'GOUJON BOUGON', '{"authorTrigram":"ABC","riskFactor":3.8,"catchOnboard":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"BIB"}],"catchToLand":[{"faoZone":"21.1.C","weight":0,"nbFish":null,"species":"BIB"}],"faoZone":null,"isBeingSent":false,"isInVerificationScope":false,"isSent":false,"isVerified":false,"note":null,"pnoTypes":[{"pnoTypeName":"Préavis type F","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"FRNCE","predictedArrivalDatetimeUtc":"2023-01-01T10:00:00Z","predictedLandingDatetimeUtc":"2023-01-01T10:30:00Z","purpose":"LAN","tripStartDate":"2023-01-01T08:00:00Z"}'); diff --git a/backend/src/main/resources/db/testdata/json/V666.11__Insert_dummy_risk_factors.jsonc b/backend/src/main/resources/db/testdata/json/V666.11__Insert_dummy_risk_factors.jsonc index e048c12c79..f1a1f4a1d5 100644 --- a/backend/src/main/resources/db/testdata/json/V666.11__Insert_dummy_risk_factors.jsonc +++ b/backend/src/main/resources/db/testdata/json/V666.11__Insert_dummy_risk_factors.jsonc @@ -186,41 +186,6 @@ }, // - Vessel: LE POISSON D'AVRIL - // - Last control date: 2024-02-01 - // - DUPLICATE CFR (= 2 rows with the same `cfr` value) - { - "cfr": "CFR109", - "control_priority_level": 4, - "control_rate_risk_factor": 5, - "departure_datetime_utc": "2023-12-31 14:00:00", - "detectability_risk_factor": 5, - "external_immatriculation": "EXTIMM109", - "impact_risk_factor": 4, - "infraction_rate_risk_factor": 3, - "infraction_score": null, - "ircs": "IRCS109", - "last_control_datetime_utc": "2024-02-01 00:00:00", - "last_control_infraction": true, - "last_logbook_message_datetime_utc:sql": "NOW()", - "number_controls_last_3_years": 0, - "number_controls_last_5_years": 0, - "number_gear_seizures_last_5_years": 4, - "number_infractions_last_5_years": 5, - "number_recent_controls": 0, - "number_species_seizures_last_5_years": 3, - "number_vessel_seizures_last_5_years": 2, - "post_control_comments": "", - "probability_risk_factor": 4, - "risk_factor": 4, - "segment_highest_impact": "NWW10", - "segment_highest_priority": "PEL 03", - "segments": ["NWW10", "PEL 03"], - "total_weight_onboard": 12345.67, - "trip_number": 123109, - "vessel_id": 109, - "gear_onboard:jsonb": [], - "species_onboard:jsonb": [] - }, // - Last control date: 2024-04-01 { "cfr": "CFR109", diff --git a/backend/src/main/resources/db/testdata/json/V666.5.1__Insert_more_pno_logbook_reports.jsonc b/backend/src/main/resources/db/testdata/json/V666.5.1__Insert_more_pno_logbook_reports.jsonc index 133c2ef28f..17ac1a95ab 100644 --- a/backend/src/main/resources/db/testdata/json/V666.5.1__Insert_more_pno_logbook_reports.jsonc +++ b/backend/src/main/resources/db/testdata/json/V666.5.1__Insert_more_pno_logbook_reports.jsonc @@ -813,7 +813,6 @@ }, // - Vessel: LE POISSON D'AVRIL - // - DUPLICATE Risk Factor CFR (= 2 `risk_factors` table rows with the same `cfr` value) // - DUPLICATE Vessel CFR (= 2 `vessels` table rows with the same `cfr` value) { "id": 113, diff --git a/backend/src/main/resources/db/testdata/json/V666.5.2__Insert_dummy_manual_prior_notifications.jsonc b/backend/src/main/resources/db/testdata/json/V666.5.2__Insert_dummy_manual_prior_notifications.jsonc index 12134101c5..1b33f63707 100644 --- a/backend/src/main/resources/db/testdata/json/V666.5.2__Insert_dummy_manual_prior_notifications.jsonc +++ b/backend/src/main/resources/db/testdata/json/V666.5.2__Insert_dummy_manual_prior_notifications.jsonc @@ -12,7 +12,6 @@ // - Vessel: POISSON PAS NET { "report_id": "00000000-0000-4000-0000-000000000001", - "author_trigram": "ABC", "cfr": "CFR112", "vessel_id": 112, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -24,6 +23,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "POISSON PAS NET", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 2.1, "catchOnboard": [ { @@ -71,7 +71,6 @@ // - Flag state: BEL { "report_id": "00000000-0000-4000-0000-000000000002", - "author_trigram": "ABC", "cfr": "CFR115", "vessel_id": 115, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -86,6 +85,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "DOS FIN", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 2.7, // Unsorted on purpose "catchOnboard": [ @@ -187,7 +187,6 @@ // - With risk factor { "report_id": "00000000-0000-4000-0000-000000000003", - "author_trigram": "ABC", "cfr": "CFR117", "vessel_id": 117, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '50 minutes'", @@ -202,6 +201,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '5 minutes'", "vessel_name": "QUEUE DE POISSON", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 3.1, "catchOnboard": [ { @@ -245,7 +245,6 @@ // - RENAMED TO: NAVIRE RENOMMÉ (NOUVEAU NOM) { "report_id": "00000000-0000-4000-0000-000000000004", - "author_trigram": "ABC", "cfr": "CFR116", "vessel_id": 116, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -257,6 +256,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "NAVIRE RENOMMÉ (ANCIEN NOM)", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 1.5, "catchOnboard": [ { @@ -303,7 +303,6 @@ // - Flag state: ITA { "report_id": "00000000-0000-4000-0000-000000000005", - "author_trigram": "ABC", "cfr": "CFR120", "vessel_id": 120, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -315,6 +314,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "VIVA L'ITALIA", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 3.2, "catchOnboard": [ { @@ -357,7 +357,6 @@ // - Vessel: MARE ET BASS { "report_id": "00000000-0000-4000-0000-000000000006", - "author_trigram": "ABC", "cfr": "CFR121", "vessel_id": 121, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -369,6 +368,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "MARE ET BASS", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 3.2, "catchOnboard": [ { @@ -411,7 +411,6 @@ // - Vessel: FILET DOUX { "report_id": "00000000-0000-4000-0000-000000000007", - "author_trigram": "ABC", "cfr": "CFR122", "vessel_id": 122, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -423,6 +422,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "FILET DOUX", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 3.2, "catchOnboard": [ { @@ -465,7 +465,6 @@ // - Vessel: DÉVOILÉ { "report_id": "00000000-0000-4000-0000-000000000008", - "author_trigram": "ABC", "cfr": "CFR123", "vessel_id": 123, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -477,6 +476,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "DÉVOILÉ", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 3.2, "catchOnboard": [ { @@ -519,7 +519,6 @@ // - Vessel: MAT QUILLE { "report_id": "00000000-0000-4000-0000-000000000009", - "author_trigram": "ABC", "cfr": "CFR124", "vessel_id": 124, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -531,6 +530,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "MAT QUILLE", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 3.2, "catchOnboard": [ { @@ -573,7 +573,6 @@ // - Vessel: BEAU SÉANT { "report_id": "00000000-0000-4000-0000-000000000010", - "author_trigram": "ABC", "cfr": "CFR125", "vessel_id": 125, "created_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", @@ -585,6 +584,7 @@ "updated_at:sql": "NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes'", "vessel_name": "BEAU SÉANT", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 3.2, "catchOnboard": [ { @@ -631,7 +631,6 @@ // - Arrival date: 2023 { "report_id": "00000000-0000-4000-0000-000000000011", - "author_trigram": "ABC", "cfr": "CFR118", "vessel_id": 118, "created_at": "2023-01-01T08:45:00", @@ -643,6 +642,7 @@ "updated_at": "2023-01-01T08:45:00", "vessel_name": "GOUJON BOUGON", "value:jsonb": { + "authorTrigram": "ABC", "riskFactor": 3.8, "catchOnboard": [ { diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerifyUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerifyUTests.kt index 4d6c0d6fc6..2289832008 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerifyUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerifyUTests.kt @@ -48,7 +48,6 @@ class GetNumberToVerifyUTests { listOf( PriorNotification( reportId = "FAKE_REPORT_ID_1", - authorTrigram = null, createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, @@ -81,7 +80,6 @@ class GetNumberToVerifyUTests { PriorNotification( reportId = "FAKE_REPORT_ID_2", - authorTrigram = null, createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsUTests.kt index 06b303d561..f3822d7632 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsUTests.kt @@ -63,7 +63,6 @@ class GetPriorNotificationsUTests { listOf( PriorNotification( reportId = "FAKE_REPORT_ID_1", - authorTrigram = null, createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, @@ -96,7 +95,6 @@ class GetPriorNotificationsUTests { PriorNotification( reportId = "FAKE_REPORT_ID_2", - authorTrigram = null, createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNoteUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdateAutoPriorNotificationUTests.kt similarity index 93% rename from backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNoteUTests.kt rename to backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdateAutoPriorNotificationUTests.kt index ddf7c865d9..2f5672d545 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNoteUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdateAutoPriorNotificationUTests.kt @@ -11,7 +11,7 @@ import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.test.context.junit.jupiter.SpringExtension @ExtendWith(SpringExtension::class) -class UpdatePriorNotificationNoteUTests { +class UpdateAutoPriorNotificationUTests { @MockBean private lateinit var logbookReportRepository: LogbookReportRepository @@ -35,13 +35,14 @@ class UpdatePriorNotificationNoteUTests { ).willReturn(fakePriorNotification) // When - val result = UpdatePriorNotificationNote( + val result = UpdateAutoPriorNotification( logbookReportRepository, priorNotificationPdfDocumentRepository, getPriorNotification, ).execute( reportId = fakePriorNotification.reportId!!, operationDate = fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, + authorTrigram = "ABC", note = null, ) diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/PriorNotificationFaker.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/PriorNotificationFaker.kt index 3f438508ce..85bc628139 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/PriorNotificationFaker.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/PriorNotificationFaker.kt @@ -10,7 +10,6 @@ class PriorNotificationFaker { fun fakePriorNotification(index: Int = 1): PriorNotification { return PriorNotification( reportId = "FAKE_REPORT_ID_$index", - authorTrigram = "ABC", createdAt = ZonedDateTime.now(), didNotFishAfterZeroNotice = false, isManuallyCreated = false, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt index 1700a06863..3479e0e77b 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt @@ -14,9 +14,9 @@ import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.* import fr.gouv.cnsp.monitorfish.domain.utils.PaginatedList import fr.gouv.cnsp.monitorfish.fakers.PriorNotificationFaker import fr.gouv.cnsp.monitorfish.infrastructure.api.input.AutoPriorNotificationComputeDataInput +import fr.gouv.cnsp.monitorfish.infrastructure.api.input.AutoPriorNotificationDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationComputeDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationDataInput -import fr.gouv.cnsp.monitorfish.infrastructure.api.input.PriorNotificationDataInput import org.hamcrest.Matchers.equalTo import org.junit.jupiter.api.Test import org.mockito.BDDMockito.given @@ -64,7 +64,7 @@ class PriorNotificationControllerITests { private lateinit var verifyAndSendPriorNotification: VerifyAndSendPriorNotification @MockBean - private lateinit var updatePriorNotificationNote: UpdatePriorNotificationNote + private lateinit var updateAutoPriorNotification: UpdateAutoPriorNotification @MockBean private lateinit var invalidatePriorNotification: InvalidatePriorNotification @@ -108,23 +108,6 @@ class PriorNotificationControllerITests { } @Test - fun `getNumberToVerify Should get the number of prior notification to verify`() { - // Given - given(getNumberToVerify.execute()).willReturn( - PriorNotificationStats(perSeafrontGroupCount = mapOf(Pair(SeafrontGroup.ALL, 2))), - ) - - // When - api.perform( - get( - "/bff/v1/prior_notifications/to_verify", - ), - ) - // Then - .andExpect(status().isOk) - .andExpect(jsonPath("$.perSeafrontGroupCount['ALL']", equalTo(2))) - } - fun `getAutoComputation Should get an auto prior notification computed values`() { // Given given(this.computeAutoPriorNotification.execute(any(), any(), any(), any())) @@ -153,6 +136,72 @@ class PriorNotificationControllerITests { .andExpect(jsonPath("$.nextState", equalTo("OUT_OF_VERIFICATION_SCOPE"))) } + @Test + fun `getAutoData Should get an auto prior notification form data by its reportId`() { + val fakePriorNotification = PriorNotificationFaker.fakePriorNotification() + + // Given + given( + getPriorNotification.execute( + fakePriorNotification.reportId!!, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, + false, + ), + ) + .willReturn(fakePriorNotification) + + // When + api.perform( + get( + "/bff/v1/prior_notifications/auto/${fakePriorNotification.reportId!!}/data?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}", + ), + ) + // Then + .andExpect(status().isOk) + .andExpect(jsonPath("$.reportId", equalTo(fakePriorNotification.reportId))) + } + + @Test + fun `updateAuto Should update an auto prior notification by its reportId`() { + val fakePriorNotification = PriorNotificationFaker.fakePriorNotification() + fakePriorNotification.logbookMessageAndValue.value.note = "Test !" + + // Given + given( + updateAutoPriorNotification.execute( + reportId = anyOrNull(), + operationDate = anyOrNull(), + authorTrigram = anyOrNull(), + note = anyOrNull(), + ), + ) + .willReturn(fakePriorNotification) + + // When + val requestBody = objectMapper.writeValueAsString( + AutoPriorNotificationDataInput( + authorTrigram = "ABC", + note = "Test !", + ), + ) + api.perform( + put( + "/bff/v1/prior_notifications/auto/${fakePriorNotification.reportId!!}?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}", + ) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody), + ) + // Then + .andExpect(status().isOk) + .andExpect(jsonPath("$.reportId", equalTo(fakePriorNotification.reportId))) + .andExpect( + jsonPath( + "$.logbookMessage.message.note", + equalTo(fakePriorNotification.logbookMessageAndValue.value.note), + ), + ) + } + @Test fun `getManualComputation Should get a manual prior notification computed values`() { // Given @@ -188,7 +237,7 @@ class PriorNotificationControllerITests { } @Test - fun `getOneManual Should get a manual prior notification form data by its reportId`() { + fun `getManualData Should get a manual prior notification form data by its reportId`() { val fakePriorNotification = PriorNotificationFaker.fakePriorNotification() // Given @@ -204,7 +253,7 @@ class PriorNotificationControllerITests { // When api.perform( get( - "/bff/v1/prior_notifications/manual/${fakePriorNotification.reportId!!}?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}", + "/bff/v1/prior_notifications/manual/${fakePriorNotification.reportId!!}/data?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}", ), ) // Then @@ -322,6 +371,24 @@ class PriorNotificationControllerITests { .andExpect(jsonPath("$.reportId", equalTo(fakePriorNotification.reportId))) } + @Test + fun `getNumberToVerify Should get the number of prior notification to verify`() { + // Given + given(getNumberToVerify.execute()).willReturn( + PriorNotificationStats(perSeafrontGroupCount = mapOf(Pair(SeafrontGroup.ALL, 2))), + ) + + // When + api.perform( + get( + "/bff/v1/prior_notifications/to_verify", + ), + ) + // Then + .andExpect(status().isOk) + .andExpect(jsonPath("$.perSeafrontGroupCount['ALL']", equalTo(2))) + } + @Test fun `getAllTypes Should get a list of prior notification types`() { // Given @@ -358,7 +425,7 @@ class PriorNotificationControllerITests { ) // Then .andExpect(status().isOk) - .andExpect(jsonPath("$.id", equalTo(fakePriorNotification.reportId))) + .andExpect(jsonPath("$.reportId", equalTo(fakePriorNotification.reportId))) } @Test @@ -393,9 +460,10 @@ class PriorNotificationControllerITests { // Given given( - updatePriorNotificationNote.execute( + updateAutoPriorNotification.execute( reportId = anyOrNull(), operationDate = anyOrNull(), + authorTrigram = anyOrNull(), note = anyOrNull(), ), ) @@ -403,7 +471,8 @@ class PriorNotificationControllerITests { // When val requestBody = objectMapper.writeValueAsString( - PriorNotificationDataInput( + AutoPriorNotificationDataInput( + authorTrigram = "ABC", note = "Test !", ), ) @@ -455,5 +524,6 @@ class PriorNotificationControllerITests { equalTo(fakePriorNotification.logbookMessageAndValue.value.isInvalidated), ), ) + .andExpect(jsonPath("$.reportId", equalTo(fakePriorNotification.reportId))) } } diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt index ee6e1db0c2..11c5ace15b 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt @@ -2,7 +2,10 @@ package fr.gouv.cnsp.monitorfish.infrastructure.database.repositories import com.neovisionaries.i18n.CountryCode import fr.gouv.cnsp.monitorfish.config.MapperConfiguration -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.* +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessagePurpose +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookRawMessage +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookTransmissionFormat import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.* import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.filters.PriorNotificationsFilter import fr.gouv.cnsp.monitorfish.domain.exceptions.NoLogbookFishingTripFound @@ -947,8 +950,8 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { // Then assertThat(result).hasSizeGreaterThan(0) - assertThat(result.filter { it.logbookMessageAndValue.value.isVerified == false }).hasSize(1) - assertThat(result.filter { it.logbookMessageAndValue.value.isInVerificationScope == true }).hasSize(1) + assertThat(result.all { it.logbookMessageAndValue.value.isVerified == false }).isTrue() + assertThat(result.all { it.logbookMessageAndValue.value.isInVerificationScope == true }).isTrue() } @Test @@ -1114,9 +1117,10 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat((currentCorReport.message as PNO).note).isNull() // When - jpaLogbookReportRepository.updatePriorNotificationNote( + jpaLogbookReportRepository.updatePriorNotificationData( "FAKE_OPERATION_109", ZonedDateTime.now().minusMinutes(15), + "ABC", "A wonderful note", ) diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepositoryITests.kt index 44965d0f7b..1239d9a7c1 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepositoryITests.kt @@ -422,7 +422,6 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { val newPriorNotification = PriorNotification( reportId = null, - authorTrigram = "ABC", createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, @@ -433,6 +432,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { // Replaced by the generated `createdAt` during the save operation. integrationDateTime = ZonedDateTime.now(), message = PNO().apply { + authorTrigram = "ABC" catchOnboard = emptyList() catchToLand = emptyList() economicZone = null diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts index 8ff0c3e2e9..afe8b247b8 100644 --- a/frontend/src/constants/index.ts +++ b/frontend/src/constants/index.ts @@ -10,6 +10,7 @@ export const BOOLEAN_AS_OPTIONS: Array> = [ { label: 'Non', value: false } ] +export const HALF_A_SECOND = 500 export const THIRTY_SECONDS = 30 * 1000 export const ONE_MINUTE = 60 * 1000 export const FIVE_MINUTES = 5 * 60 * 1000 diff --git a/frontend/src/features/PriorNotification/PriorNotification.types.ts b/frontend/src/features/PriorNotification/PriorNotification.types.ts index d96296589e..b377aad147 100644 --- a/frontend/src/features/PriorNotification/PriorNotification.types.ts +++ b/frontend/src/features/PriorNotification/PriorNotification.types.ts @@ -56,16 +56,21 @@ export namespace PriorNotification { export type PriorNotificationDetail = { /** Unique identifier concatenating all the DAT, COR, RET & DEL operations `id` used for data consolidation. */ fingerprint: string - /** Logbook message `reportId`. */ - id: string isLessThanTwelveMetersVessel: boolean + isManuallyCreated: boolean isVesselUnderCharter: boolean | undefined logbookMessage: LogbookMessage.PnoLogbookMessage + operationDate: string + /** Logbook message `reportId`. */ + reportId: string riskFactor: number | undefined state: State | undefined } - export type PriorNotificationUpdateNoteRequestData = Pick + export type AutoPriorNotificationData = { + authorTrigram: string | string + note: string | undefined + } export type ManualPriorNotificationData = { authorTrigram: string @@ -87,12 +92,24 @@ export namespace PriorNotification { } export type NewManualPriorNotificationData = Omit - export type PriorNotificationComputeRequestData = Pick< + export type AutoComputeRequestData = { + isInVerificationScope: boolean + portLocode: string + segmentCodes: string[] + vesselId: number + } + /** Real-time computed values displayed within a prior notification form. */ + export type AutoComputedValues = { + /** Next initial state of the prior notification once it will be created or updated. */ + nextState: State + } + + export type ManualComputeRequestData = Pick< ManualPriorNotificationData, 'faoArea' | 'fishingCatches' | 'portLocode' | 'tripGearCodes' | 'vesselId' > /** Real-time computed values displayed within a prior notification form. */ - export type ManualPriorNotificationComputedValues = Pick< + export type ManualComputedValues = Pick< PriorNotification, 'isVesselUnderCharter' | 'tripSegments' | 'types' | 'riskFactor' > & { diff --git a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx new file mode 100644 index 0000000000..a390d9eaa8 --- /dev/null +++ b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx @@ -0,0 +1,40 @@ +import { PriorNotification } from '@features/PriorNotification/PriorNotification.types' +import { verifyAndSendPriorNotification } from '@features/PriorNotification/useCases/verifyAndSendPriorNotification' +import { getPriorNotificationIdentifier } from '@features/PriorNotification/utils' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' +import { Accent, Button, Icon } from '@mtes-mct/monitor-ui' +import { assertNotNullish } from '@utils/assertNotNullish' + +type FooterProps = Readonly<{ + detail: PriorNotification.PriorNotificationDetail +}> +export function Footer({ detail }: FooterProps) { + const dispatch = useMainAppDispatch() + + const { isInvalidated } = detail.logbookMessage.message + const isPendingSend = + !!detail.state && + [PriorNotification.State.AUTO_SEND_IN_PROGRESS, PriorNotification.State.PENDING_SEND].includes(detail.state) + const isVerifiedAndSent = detail.state === PriorNotification.State.VERIFIED_AND_SENT + + const verifyAndSend = async () => { + const identifier = getPriorNotificationIdentifier(detail) + assertNotNullish(identifier) + + await dispatch(verifyAndSendPriorNotification(identifier, detail.fingerprint, false)) + } + + return ( + + ) +} diff --git a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Form.tsx b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Form.tsx new file mode 100644 index 0000000000..3c3c970eee --- /dev/null +++ b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Form.tsx @@ -0,0 +1,118 @@ +import { HALF_A_SECOND } from '@constants/index' +import { useInvalidatePriorNotificationMutation } from '@features/PriorNotification/priorNotificationApi' +import { updateAutoPriorNotification } from '@features/PriorNotification/useCases/updateAutoPriorNotification' +import { getPriorNotificationIdentifier } from '@features/PriorNotification/utils' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' +import { Accent, Button, FormikEffect, FormikTextarea, FormikTextInput, Icon } from '@mtes-mct/monitor-ui' +import { assertNotNullish } from '@utils/assertNotNullish' +import { useIsSuperUser } from 'auth/hooks/useIsSuperUser' +import { Formik } from 'formik' +import { noop } from 'lodash' +import { useCallback, useMemo, useState } from 'react' +import styled from 'styled-components' +import { useDebouncedCallback } from 'use-debounce' + +import { InvalidatePriorNotificationDialog } from '../InvalidatePriorNotificationDialog' + +import type { PriorNotification } from '@features/PriorNotification/PriorNotification.types' + +type FormProps = Readonly<{ + detail: PriorNotification.PriorNotificationDetail + initialFormValues: PriorNotification.AutoPriorNotificationData +}> +export function Form({ detail, initialFormValues }: FormProps) { + const dispatch = useMainAppDispatch() + const isSuperUser = useIsSuperUser() + + const [invalidatePriorNotification] = useInvalidatePriorNotificationMutation() + + const [isInvalidatingPriorNotificationDialog, setIsInvalidatingPriorNotificationDialog] = useState(false) + + const priorNotificationIdentifier = useMemo(() => getPriorNotificationIdentifier(detail), [detail]) + assertNotNullish(priorNotificationIdentifier) + + const { isInvalidated } = detail.logbookMessage.message + + const invalidate = async () => { + await invalidatePriorNotification({ + isManuallyCreated: detail.isManuallyCreated, + operationDate: priorNotificationIdentifier.operationDate, + reportId: priorNotificationIdentifier.reportId + }) + + setIsInvalidatingPriorNotificationDialog(false) + } + + const updateNoteCallback = useCallback( + async (nextValues: PriorNotification.AutoPriorNotificationData) => { + await dispatch(updateAutoPriorNotification(priorNotificationIdentifier, nextValues)) + }, + [dispatch, priorNotificationIdentifier] + ) + + const updateNote = useDebouncedCallback( + (nextValues: PriorNotification.AutoPriorNotificationData) => updateNoteCallback(nextValues), + HALF_A_SECOND + ) + + return ( + <> + + <> + + + + + + + + + + + {isSuperUser && !isInvalidated && ( + setIsInvalidatingPriorNotificationDialog(true)} + title="Invalider le préavis" + > + Invalider le préavis + + )} + + {isInvalidatingPriorNotificationDialog && ( + setIsInvalidatingPriorNotificationDialog(false)} + onConfirm={invalidate} + /> + )} + + ) +} + +const FieldGroup = styled.div.attrs({ className: 'FieldGroup' })` + display: flex; + flex-direction: column; + gap: 8px; + + .rs-checkbox { + > .rs-checkbox-checker { + > label { + line-height: 18px; + } + } + } + + textarea { + box-sizing: border-box !important; + } +` + +const AuthorTrigramInput = styled(FormikTextInput)` + width: 120px; +` + +const InvalidateButton = styled(Button)` + color: ${p => p.theme.color.maximumRed}; + margin-top: 48px; +` diff --git a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx new file mode 100644 index 0000000000..dbf6d074ae --- /dev/null +++ b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx @@ -0,0 +1,28 @@ +import { useMainAppSelector } from '@hooks/useMainAppSelector' + +import { Footer } from './Footer' +import { Form } from './Form' +import { PriorNotificationCard } from '../PriorNotificationCard' + +export function AutoPriorNotificationForm() { + const editedAutoPriorNotificationInitialFormValues = useMainAppSelector( + state => state.priorNotification.editedAutoPriorNotificationInitialFormValues + ) + const openedPriorNotificationDetail = useMainAppSelector( + state => state.priorNotification.openedPriorNotificationDetail + ) + + if (!editedAutoPriorNotificationInitialFormValues || !openedPriorNotificationDetail) { + return + } + + return ( + + } + footerChildren={
} + priorNotificationDetail={openedPriorNotificationDetail} + /> + ) +} diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationForm/Card.tsx b/frontend/src/features/PriorNotification/components/ManualPriorNotificationForm/Card.tsx similarity index 77% rename from frontend/src/features/PriorNotification/components/PriorNotificationForm/Card.tsx rename to frontend/src/features/PriorNotification/components/ManualPriorNotificationForm/Card.tsx index 762991f198..9bc546050e 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationForm/Card.tsx +++ b/frontend/src/features/PriorNotification/components/ManualPriorNotificationForm/Card.tsx @@ -1,18 +1,13 @@ -import { RTK_FORCE_REFETCH_QUERY_OPTIONS, RTK_ONE_MINUTE_POLLING_QUERY_OPTIONS } from '@api/constants' import { ConfirmationModal } from '@components/ConfirmationModal' import { FrontendErrorBoundary } from '@components/FrontendErrorBoundary' import { InvalidatePriorNotificationDialog } from '@features/PriorNotification/components/InvalidatePriorNotificationDialog' -import { - useGetPriorNotificationDetailQuery, - useInvalidatePriorNotificationMutation -} from '@features/PriorNotification/priorNotificationApi' +import { useInvalidatePriorNotificationMutation } from '@features/PriorNotification/priorNotificationApi' import { priorNotificationActions } from '@features/PriorNotification/slice' import { updateEditedPriorNotificationComputedValues } from '@features/PriorNotification/useCases/updateEditedPriorNotificationComputedValues' import { isZeroNotice } from '@features/PriorNotification/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { Accent, Banner, Button, FormikEffect, Icon, Level, usePrevious } from '@mtes-mct/monitor-ui' -import { skipToken } from '@reduxjs/toolkit/query' import { assertNotNullish } from '@utils/assertNotNullish' import { getDefinedObject } from '@utils/getDefinedObject' import { useFormikContext } from 'formik' @@ -28,40 +23,21 @@ import { PriorNotification } from '../../PriorNotification.types' import { DownloadButton } from '../shared/DownloadButton' import { TagBar } from '../shared/TagBar' -import type { FormValues } from './types' +import type { ManualPriorNotificationFormValues } from './types' import type { Promisable } from 'type-fest' type CardProps = Readonly<{ + detail: PriorNotification.PriorNotificationDetail | undefined isValidatingOnChange: boolean onClose: () => void onSubmit: () => Promisable onVerifyAndSend: () => Promisable - reportId: string | undefined }> -export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, reportId }: CardProps) { - const { dirty, isValid, submitForm, values } = useFormikContext() +export function Card({ detail, isValidatingOnChange, onClose, onSubmit, onVerifyAndSend }: CardProps) { + const { dirty, isValid, submitForm, values } = useFormikContext() const dispatch = useMainAppDispatch() const editedPriorNotificationComputedValues = useMainAppSelector( - store => store.priorNotification.editedPriorNotificationComputedValues - ) - const openedPriorNotificationIdentifier = useMainAppSelector( - store => store.priorNotification.openedPriorNotificationIdentifier - ) - const isOpenedPriorNotificationManuallyCreated = useMainAppSelector( - store => store.priorNotification.isOpenedPriorNotificationManuallyCreated - ) - - const { data: editedPriorNotificationDetail } = useGetPriorNotificationDetailQuery( - openedPriorNotificationIdentifier && typeof isOpenedPriorNotificationManuallyCreated === 'boolean' - ? { - ...openedPriorNotificationIdentifier, - isManuallyCreated: isOpenedPriorNotificationManuallyCreated - } - : skipToken, - { - ...RTK_ONE_MINUTE_POLLING_QUERY_OPTIONS, - ...RTK_FORCE_REFETCH_QUERY_OPTIONS - } + store => store.priorNotification.editedManualPriorNotificationComputedValues ) const [invalidatePriorNotification] = useInvalidatePriorNotificationMutation() @@ -69,16 +45,14 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, const [isClosingConfirmationDialog, setIsClosingConfirmationDialog] = useState(false) const previousPartialComputationRequestData = usePrevious(getPartialComputationRequestData(values)) - const applicableState = editedPriorNotificationComputedValues?.nextState ?? editedPriorNotificationDetail?.state - const isNewPriorNotification = !reportId - const isInvalidated = editedPriorNotificationDetail?.logbookMessage?.message?.isInvalidated + const applicableState = editedPriorNotificationComputedValues?.nextState ?? detail?.state + const isNewPriorNotification = !detail + const isInvalidated = detail?.logbookMessage?.message?.isInvalidated const isPendingSend = - !!editedPriorNotificationDetail?.state && - [PriorNotification.State.AUTO_SEND_IN_PROGRESS, PriorNotification.State.PENDING_SEND].includes( - editedPriorNotificationDetail?.state - ) - const isPendingVerification = editedPriorNotificationDetail?.state === PriorNotification.State.PENDING_VERIFICATION - const isVerifiedAndSent = editedPriorNotificationDetail?.state === PriorNotification.State.VERIFIED_AND_SENT + !!detail?.state && + [PriorNotification.State.AUTO_SEND_IN_PROGRESS, PriorNotification.State.PENDING_SEND].includes(detail?.state) + const isPendingVerification = detail?.state === PriorNotification.State.PENDING_VERIFICATION + const isVerifiedAndSent = detail?.state === PriorNotification.State.VERIFIED_AND_SENT const hasDesignatedPorts = editedPriorNotificationComputedValues?.types?.find(type => type.hasDesignatedPorts) const handleClose = () => { @@ -92,12 +66,12 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, } const invalidate = async () => { - assertNotNullish(editedPriorNotificationDetail) + assertNotNullish(detail) await invalidatePriorNotification({ isManuallyCreated: true, - operationDate: editedPriorNotificationDetail.logbookMessage.operationDateTime, - reportId: editedPriorNotificationDetail.logbookMessage.reportId + operationDate: detail.logbookMessage.operationDateTime, + reportId: detail.logbookMessage.reportId }) setIsInvalidatingPriorNotificationDialog(false) @@ -110,14 +84,14 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, } const updateComputedValues = useDebouncedCallback( - (nextComputationRequestData: PriorNotification.PriorNotificationComputeRequestData) => { + (nextComputationRequestData: PriorNotification.ManualComputeRequestData) => { dispatch(updateEditedPriorNotificationComputedValues(nextComputationRequestData)) }, 1000 ) // We need to check for equality outside the debounce to ensure `nextFormValues` is up-to-date. - const updateComputedValuesIfNecessary = (nextFormValues: FormValues) => { + const updateComputedValuesIfNecessary = (nextFormValues: ManualPriorNotificationFormValues) => { const nextPartialComputationRequestData = getPartialComputationRequestData(nextFormValues) // If nothing changed, we don't need to update the computed values @@ -148,7 +122,7 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, useEffect( () => () => { - dispatch(priorNotificationActions.setEditedPriorNotificationInitialFormValues(values)) + dispatch(priorNotificationActions.setEditedManualPriorNotificationInitialFormValues(values)) }, [dispatch, values] ) @@ -160,12 +134,12 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, - {editedPriorNotificationDetail?.state === PriorNotification.State.PENDING_SEND && ( + {detail?.state === PriorNotification.State.PENDING_SEND && ( Le préavis est en cours de diffusion. )} - {editedPriorNotificationDetail?.state === PriorNotification.State.AUTO_SEND_IN_PROGRESS && ( + {detail?.state === PriorNotification.State.AUTO_SEND_IN_PROGRESS && ( Le préavis est en cours d’envoi aux unités qui l’ont demandé. @@ -195,7 +169,7 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, {!isNewPriorNotification && isPendingVerification && ( Le préavis doit être vérifié par le CNSP avant sa diffusion. )} - {(!!editedPriorNotificationComputedValues || !!openedPriorNotificationIdentifier) && ( + {(!!editedPriorNotificationComputedValues || !!detail) && ( Le navire doit respecter un délai d’envoi{hasDesignatedPorts && ' et débarquer dans un port désigné'}. @@ -205,7 +179,7 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend,
- {!!editedPriorNotificationDetail && !isInvalidated && ( + {!!detail && !isInvalidated && ( - {!!editedPriorNotificationDetail && ( - + {!!detail && ( + )} + {footerChildren}
- {isInvalidatingPriorNotificationDialog && ( - setIsInvalidatingPriorNotificationDialog(false)} - onConfirm={invalidate} - /> - )} ) } -const InvalidateButton = styled(Button)` - color: ${p => p.theme.color.maximumRed}; - margin-top: 48px; -` - const Wrapper = styled.div` bottom: 0; display: flex; @@ -330,21 +176,3 @@ const Footer = styled.div` margin-left: 8px; } ` - -const FieldGroup = styled.div.attrs({ className: 'FieldGroup' })` - display: flex; - flex-direction: column; - gap: 8px; - - .rs-checkbox { - > .rs-checkbox-checker { - > label { - line-height: 18px; - } - } - } - - textarea { - box-sizing: border-box !important; - } -` diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/Row.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/Row.tsx index c7b82cfb22..95178c5215 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/Row.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/Row.tsx @@ -1,5 +1,3 @@ -import { openPriorNotificationCard } from '@features/PriorNotification/useCases/openPriorNotificationCard' -import { openPriorNotificationForm } from '@features/PriorNotification/useCases/openPriorNotificationForm' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { customDayjs, Icon, TableWithSelectableRows, Tag, THEME } from '@mtes-mct/monitor-ui' import { flexRender, type Row as RowType } from '@tanstack/react-table' @@ -9,6 +7,8 @@ import styled from 'styled-components' import { FixedTag, None } from './styles' import { getColorsFromState, getExpandableRowCellCustomStyle } from './utils' import { PriorNotification } from '../../PriorNotification.types' +import { openManualPriorNotificationForm } from '../../useCases/openManualPriorNotificationForm' +import { openPriorNotificationCard } from '../../useCases/openPriorNotificationCard' type RowProps = Readonly<{ row: RowType @@ -22,7 +22,7 @@ export function Row({ row }: RowProps) { const openCard = () => { if (priorNotification.isManuallyCreated) { dispatch( - openPriorNotificationForm( + openManualPriorNotificationForm( { operationDate: priorNotification.operationDate, reportId: priorNotification.id }, priorNotification.fingerprint ) diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/cells/ActionButtonsCell.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/cells/ActionButtonsCell.tsx index 98a732193a..ba0cc447c7 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/cells/ActionButtonsCell.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/cells/ActionButtonsCell.tsx @@ -1,4 +1,5 @@ -import { openPriorNotificationForm } from '@features/PriorNotification/useCases/openPriorNotificationForm' +import { openAutoPriorNotificationForm } from '@features/PriorNotification/useCases/openAutoPriorNotificationForm' +import { openManualPriorNotificationForm } from '@features/PriorNotification/useCases/openManualPriorNotificationForm' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { Accent, Icon, IconButton } from '@mtes-mct/monitor-ui' import { VesselIdentifier, type VesselIdentity } from 'domain/entities/vessel/types' @@ -17,16 +18,25 @@ export function ActionButtonsCell({ priorNotification }: ActionButtonsCellProps) const isSuperUser = useIsSuperUser() const dispatch = useMainAppDispatch() - const editPriorNotification = () => { + const editAutoPriorNotification = () => { dispatch( - openPriorNotificationForm( + openAutoPriorNotificationForm( { operationDate: priorNotification.operationDate, reportId: priorNotification.id }, priorNotification.fingerprint ) ) } - const openPriorNotificationDetail = () => { + const editManualPriorNotification = () => { + dispatch( + openManualPriorNotificationForm( + { operationDate: priorNotification.operationDate, reportId: priorNotification.id }, + priorNotification.fingerprint + ) + ) + } + + const open = () => { dispatch( openPriorNotificationCard( { @@ -70,7 +80,7 @@ export function ActionButtonsCell({ priorNotification }: ActionButtonsCellProps) @@ -79,7 +89,7 @@ export function ActionButtonsCell({ priorNotification }: ActionButtonsCellProps) diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx index 2cf5bb4b3b..6fa55e2e7f 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx @@ -2,7 +2,7 @@ import { BackendApi } from '@api/BackendApi.types' import { RTK_FORCE_REFETCH_QUERY_OPTIONS, RTK_ONE_MINUTE_POLLING_QUERY_OPTIONS, RtkCacheTagType } from '@api/constants' import { ErrorWall } from '@components/ErrorWall' import { LogbookMessage } from '@features/Logbook/LogbookMessage.types' -import { openPriorNotificationForm } from '@features/PriorNotification/useCases/openPriorNotificationForm' +import { openManualPriorNotificationForm } from '@features/PriorNotification/useCases/openManualPriorNotificationForm' import { Body } from '@features/SideWindow/components/Body' import { Header } from '@features/SideWindow/components/Header' import { Page } from '@features/SideWindow/components/Page' @@ -32,8 +32,9 @@ import { TableBodyLoader } from './TableBodyLoader' import { getTitle } from './utils' import { useGetPriorNotificationsQuery, useGetPriorNotificationsToVerifyQuery } from '../../priorNotificationApi' import { priorNotificationActions } from '../../slice' +import { AutoPriorNotificationForm } from '../AutoPriorNotificationForm' +import { ManualPriorNotificationForm } from '../ManualPriorNotificationForm' import { PriorNotificationCard } from '../PriorNotificationCard' -import { PriorNotificationForm } from '../PriorNotificationForm' import type { AllSeafrontGroup, NoSeafrontGroup, SeafrontGroup } from '@constants/seafront' @@ -43,6 +44,9 @@ type PriorNotificationListProps = Readonly<{ export function PriorNotificationList({ isFromUrl }: PriorNotificationListProps) { const dispatch = useMainAppDispatch() const listFilter = useMainAppSelector(state => state.priorNotification.listFilterValues) + const openedPriorNotificationDetail = useMainAppSelector( + state => state.priorNotification.openedPriorNotificationDetail + ) const isPriorNotificationCardOpen = useMainAppSelector(state => state.priorNotification.isPriorNotificationCardOpen) const isPriorNotificationFormOpen = useMainAppSelector(state => state.priorNotification.isPriorNotificationFormOpen) const isSuperUser = useIsSuperUser() @@ -162,7 +166,7 @@ export function PriorNotificationList({ isFromUrl }: PriorNotificationListProps) )} From 29b710481ee6a2d150de7e505cb674d634d8c6f2 Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Fri, 2 Aug 2024 14:10:16 +0200 Subject: [PATCH 09/41] Update prior notification controller unit tests --- .../api/bff/PriorNotificationControllerITests.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt index 3479e0e77b..88f325e0a0 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt @@ -196,7 +196,7 @@ class PriorNotificationControllerITests { .andExpect(jsonPath("$.reportId", equalTo(fakePriorNotification.reportId))) .andExpect( jsonPath( - "$.logbookMessage.message.note", + "$.note", equalTo(fakePriorNotification.logbookMessageAndValue.value.note), ), ) @@ -239,6 +239,7 @@ class PriorNotificationControllerITests { @Test fun `getManualData Should get a manual prior notification form data by its reportId`() { val fakePriorNotification = PriorNotificationFaker.fakePriorNotification() + fakePriorNotification.logbookMessageTyped.typedMessage.authorTrigram = "ABC" // Given given( @@ -264,6 +265,7 @@ class PriorNotificationControllerITests { @Test fun `updateManual Should create a manual prior notification`() { val fakePriorNotification = PriorNotificationFaker.fakePriorNotification() + fakePriorNotification.logbookMessageTyped.typedMessage.authorTrigram = "ABC" // Given given( @@ -319,21 +321,22 @@ class PriorNotificationControllerITests { @Test fun `updateManual Should update a manual prior notification by its reportId`() { val fakePriorNotification = PriorNotificationFaker.fakePriorNotification() + fakePriorNotification.logbookMessageTyped.typedMessage.authorTrigram = "ABC" // Given given( createOrUpdateManualPriorNotification.execute( - hasPortEntranceAuthorization = anyOrNull(), - hasPortLandingAuthorization = anyOrNull(), + reportId = any(), authorTrigram = anyOrNull(), didNotFishAfterZeroNotice = anyOrNull(), expectedArrivalDate = anyOrNull(), expectedLandingDate = anyOrNull(), faoArea = anyOrNull(), fishingCatches = anyOrNull(), + hasPortEntranceAuthorization = anyOrNull(), + hasPortLandingAuthorization = anyOrNull(), note = anyOrNull(), portLocode = anyOrNull(), - reportId = anyOrNull(), sentAt = anyOrNull(), purpose = anyOrNull(), tripGearCodes = anyOrNull(), From 51f21860117188a4e6a922d54e536b872e131997 Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Fri, 2 Aug 2024 14:31:21 +0200 Subject: [PATCH 10/41] Update prior notification state labels --- .../0006-prior-notification-states-specifications.md | 8 ++++---- .../prior_notification/PriorNotificationState.kt | 6 +++--- .../side_window/prior_notification_card/card.spec.ts | 4 ++-- .../side_window/prior_notification_form/form.spec.ts | 2 +- .../PriorNotification/PriorNotification.types.ts | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/adrs/0006-prior-notification-states-specifications.md b/adrs/0006-prior-notification-states-specifications.md index 8eba011735..846ed04f1d 100644 --- a/adrs/0006-prior-notification-states-specifications.md +++ b/adrs/0006-prior-notification-states-specifications.md @@ -24,7 +24,7 @@ _À rédiger._ | `FALSE` | `FALSE` | `FALSE` | `TRUE` | `FALSE` | `00010` | Le préavis auto ne fait pas partie du périmètre de vérification du CNSP et a été diffusé auprès des unités dont les paramètres de diffusion correspondent à ce préavis. | "Envoi auto. fait" | `AUTO_SEND_DONE` | | `FALSE` | `FALSE` | `FALSE` | `TRUE` | `TRUE` | `00011` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `FALSE` | `FALSE` | `TRUE` | `FALSE` | `FALSE` | `00100` | Le préavis a été vérifié / diffusé mais la diffusion n'a pas abouti à un envoi effectif. Causes possibles : pas d'unité abonnée au port, unités abonnées mais sans adresse de diffusion email ni sms, ou tous les envois ont échoué | "Échec de diffusion" | `FAILED_SEND` | -| `FALSE` | `FALSE` | `TRUE` | `FALSE` | `TRUE` | `00101` | L'utilisateur a vérifié et diffusé le préavis auto (bien que hors scope), la pipeline n'a pas encore fait l'envoi. | "En cours de diffusion" | `PENDING_SEND` | +| `FALSE` | `FALSE` | `TRUE` | `FALSE` | `TRUE` | `00101` | L'utilisateur a vérifié et diffusé le préavis auto (bien que hors scope), la pipeline n'a pas encore fait l'envoi. | "Diffusion en cours" | `PENDING_SEND` | | `FALSE` | `FALSE` | `TRUE` | `TRUE` | `FALSE` | `00110` | Le préavis auto a été vérifié et diffusé, bien que ne faisant pas partie du périmère de vérification du CNSP. | "Vérifié et diffusé" | `VERIFIED_AND_SENT` | | `FALSE` | `FALSE` | `TRUE` | `TRUE` | `TRUE` | `00111` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `FALSE` | `TRUE` | `FALSE` | `FALSE` | `FALSE` | `01000` | Le préavis auto est à vérifier et diffuser. | "À vérifier" | `PENDING_VERIFICATION` | @@ -32,7 +32,7 @@ _À rédiger._ | `FALSE` | `TRUE` | `FALSE` | `TRUE` | `FALSE` | `01010` | Impossible : un préavis faisant partie du périmètre de vérification du CNSP ne peut pas être envoyé sans avoir été vérifié. | | | | `FALSE` | `TRUE` | `FALSE` | `TRUE` | `TRUE` | `01011` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. Et il ne peut pas être envoyé sans avoir été vérifié. | | | | `FALSE` | `TRUE` | `TRUE` | `FALSE` | `FALSE` | `01100` | Le préavis a été vérifié / diffusé mais la diffusion n'a pas abouti à un envoi effectif. Causes possibles : pas d'unité abonnée au port, unités abonnées mais sans adresse de diffusion email ni sms, ou tous les envois ont échoué | "Échec de diffusion" | `FAILED_SEND` | -| `FALSE` | `TRUE` | `TRUE` | `FALSE` | `TRUE` | `01101` | L'utilisateur a vérifié et diffusé le préavis auto, la pipeline n'a pas encore fait l'envoi. | "En cours de diffusion" | `PENDING_SEND` | +| `FALSE` | `TRUE` | `TRUE` | `FALSE` | `TRUE` | `01101` | L'utilisateur a vérifié et diffusé le préavis auto, la pipeline n'a pas encore fait l'envoi. | "Diffusion en cours" | `PENDING_SEND` | | `FALSE` | `TRUE` | `TRUE` | `TRUE` | `FALSE` | `01110` | Le préavis auto a été vérifié et diffusé. | "Vérifié et diffusé" | `VERIFIED_AND_SENT` | | `FALSE` | `TRUE` | `TRUE` | `TRUE` | `TRUE` | `01111` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `TRUE` | `FALSE` | `FALSE` | `FALSE` | `FALSE` | `10000` | Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et n'a pas été diffusé car aucune unité n'a des paramètres de diffusion qui correspondent à ce préavis.

**FORMULAIRE**
Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et aucune unité n'a des paramètres de diffusion qui correspondent à ce préavis. | "Hors vérification" | `OUT_OF_VERIFICATION_SCOPE` | @@ -41,7 +41,7 @@ _À rédiger._ | `TRUE` | `FALSE` | `FALSE` | `TRUE` | `FALSE` | `10010` | Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et a été diffusé auprès des unités dont les paramètres de diffusion correspondent à ce préavis. | "Envoi auto. fait" | `AUTO_SEND_DONE` | | `TRUE` | `FALSE` | `FALSE` | `TRUE` | `TRUE` | `10011` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `TRUE` | `FALSE` | `TRUE` | `FALSE` | `FALSE` | `10100` | Le préavis a été vérifié / diffusé mais la diffusion n'a pas abouti à un envoi effectif. Causes possibles : pas d'unité abonnée au port, unités abonnées mais sans adresse de diffusion email ni sms, ou tous les envois ont échoué | "Échec de diffusion" | `FAILED_SEND` | -| `TRUE` | `FALSE` | `TRUE` | `FALSE` | `TRUE` | `10101` | Le préavis manuel a été vérifié et diffusé, bien que ne faisant pas partie du périmère de vérification du CNSP. La pipeline n'a pas encore fait l'envoi. | "En cours de diffusion" | `PENDING_SEND` | +| `TRUE` | `FALSE` | `TRUE` | `FALSE` | `TRUE` | `10101` | Le préavis manuel a été vérifié et diffusé, bien que ne faisant pas partie du périmère de vérification du CNSP. La pipeline n'a pas encore fait l'envoi. | "Diffusion en cours" | `PENDING_SEND` | | `TRUE` | `FALSE` | `TRUE` | `TRUE` | `FALSE` | `10110` | Le préavis manuel a été vérifié et diffusé, bien que ne faisant pas partie du périmère de vérification du CNSP. | "Vérifié et diffusé" | `VERIFIED_AND_SENT` | | `TRUE` | `FALSE` | `TRUE` | `TRUE` | `TRUE` | `10111` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `TRUE` | `TRUE` | `FALSE` | `FALSE` | `FALSE` | `11000` | Le préavis manuel est à vérifier et diffuser. | "À vérifier" | `PENDING_VERIFICATION` | @@ -49,6 +49,6 @@ _À rédiger._ | `TRUE` | `TRUE` | `FALSE` | `TRUE` | `FALSE` | `11010` | Impossible : un préavis faisant partie du périmètre de vérification du CNSP ne peut pas être envoyé sans avoir été vérifié. | | | | `TRUE` | `TRUE` | `FALSE` | `TRUE` | `TRUE` | `11011` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `TRUE` | `TRUE` | `TRUE` | `FALSE` | `FALSE` | `11100` | Le préavis a été vérifié / diffusé mais la diffusion n'a pas abouti à un envoi effectif. Causes possibles : pas d'unité abonnée au port, unités abonnées mais sans adresse de diffusion email ni sms, ou tous les envois ont échoué | "Échec de diffusion" | `FAILED_SEND` | -| `TRUE` | `TRUE` | `TRUE` | `FALSE` | `TRUE` | `11101` | L'utilisateur a vérifié/diffusé le préavis manuel, la pipeline n'a pas encore fait l'envoi. | "En cours de diffusion" | `PENDING_SEND` | +| `TRUE` | `TRUE` | `TRUE` | `FALSE` | `TRUE` | `11101` | L'utilisateur a vérifié/diffusé le préavis manuel, la pipeline n'a pas encore fait l'envoi. | "Diffusion en cours" | `PENDING_SEND` | | `TRUE` | `TRUE` | `TRUE` | `TRUE` | `FALSE` | `11110` | Le préavis manuel a été vérifié et diffusé. | "Vérifié et diffusé" | `VERIFIED_AND_SENT` | | `TRUE` | `TRUE` | `TRUE` | `TRUE` | `TRUE` | `11111` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotificationState.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotificationState.kt index eb54e56fec..c6512eba1b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotificationState.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotificationState.kt @@ -4,7 +4,7 @@ enum class PriorNotificationState { /** "Envoi auto. demandé". */ AUTO_SEND_REQUESTED, - /** "En cours d'envoi auto.". */ + /** "Envoi auto en cours". */ AUTO_SEND_IN_PROGRESS, /** "Envoi auto. fait". */ @@ -16,10 +16,10 @@ enum class PriorNotificationState { /** "Hors vérification". */ OUT_OF_VERIFICATION_SCOPE, - /** "En cours de d'envoi auto". */ + /** "Envoi auto en cours". */ PENDING_AUTO_SEND, - /** "En cours de diffusion". */ + /** "Diffusion en cours". */ PENDING_SEND, /** "À vérifier (CNSP)". */ diff --git a/frontend/cypress/e2e/side_window/prior_notification_card/card.spec.ts b/frontend/cypress/e2e/side_window/prior_notification_card/card.spec.ts index 5df16e4092..115d996bce 100644 --- a/frontend/cypress/e2e/side_window/prior_notification_card/card.spec.ts +++ b/frontend/cypress/e2e/side_window/prior_notification_card/card.spec.ts @@ -171,7 +171,7 @@ context('Side Window > Prior Notification Card > Card', () => { state: PriorNotification.State.PENDING_SEND }) - cy.contains('En cours de diffusion') + cy.contains('Diffusion en cours') // ----------------------------------------------------------------------- // List @@ -180,7 +180,7 @@ context('Side Window > Prior Notification Card > Card', () => { cy.fill('Rechercher un navire', 'LE POISSON AMBULANT') cy.getTableRowById('FAKE_OPERATION_111' as unknown as number) - .find('span[title="En cours de diffusion"]') + .find('span[title="Diffusion en cours"]') .should('be.visible') }) }) diff --git a/frontend/cypress/e2e/side_window/prior_notification_form/form.spec.ts b/frontend/cypress/e2e/side_window/prior_notification_form/form.spec.ts index b553634ead..31a77b9b2a 100644 --- a/frontend/cypress/e2e/side_window/prior_notification_form/form.spec.ts +++ b/frontend/cypress/e2e/side_window/prior_notification_form/form.spec.ts @@ -531,7 +531,7 @@ context('Side Window > Prior Notification Form > Form', () => { cy.fill('Rechercher un navire', 'IN-ARÊTE-ABLE') cy.getTableRowById(createdPriorNotification.reportId) - .find('span[title="En cours de diffusion"]') + .find('span[title="Diffusion en cours"]') .should('be.visible') }) }) diff --git a/frontend/src/features/PriorNotification/PriorNotification.types.ts b/frontend/src/features/PriorNotification/PriorNotification.types.ts index b377aad147..c11aa02935 100644 --- a/frontend/src/features/PriorNotification/PriorNotification.types.ts +++ b/frontend/src/features/PriorNotification/PriorNotification.types.ts @@ -171,7 +171,7 @@ export namespace PriorNotification { export enum State { /** "Envoi auto. fait". */ AUTO_SEND_DONE = 'AUTO_SEND_DONE', - /** "En cours d'envoi auto.". */ + /** "Envoi auto en cours". */ AUTO_SEND_IN_PROGRESS = 'AUTO_SEND_IN_PROGRESS', /** "Envoi auto. demandé". */ AUTO_SEND_REQUESTED = 'AUTO_SEND_REQUESTED', @@ -179,9 +179,9 @@ export namespace PriorNotification { FAILED_SEND = 'FAILED_SEND', /** "Hors vérification". */ OUT_OF_VERIFICATION_SCOPE = 'OUT_OF_VERIFICATION_SCOPE', - /** "En cours de d'envoi auto". */ + /** "Envoi auto en cours". */ PENDING_AUTO_SEND = 'PENDING_AUTO_SEND', - /** "En cours de diffusion". */ + /** "Diffusion en cours". */ PENDING_SEND = 'PENDING_SEND', /** "À vérifier (CNSP)". */ PENDING_VERIFICATION = 'PENDING_VERIFICATION', @@ -190,12 +190,12 @@ export namespace PriorNotification { } export const STATE_LABEL: Record = { AUTO_SEND_DONE: 'Envoi auto. fait', - AUTO_SEND_IN_PROGRESS: "En cours d'envoi auto.", + AUTO_SEND_IN_PROGRESS: 'Envoi auto en cours', AUTO_SEND_REQUESTED: 'Envoi auto. demandé', FAILED_SEND: 'Échec de diffusion', OUT_OF_VERIFICATION_SCOPE: 'Hors vérification', - PENDING_AUTO_SEND: "En cours de d'envoi auto", - PENDING_SEND: 'En cours de diffusion', + PENDING_AUTO_SEND: 'Envoi auto en cours', + PENDING_SEND: 'Diffusion en cours', PENDING_VERIFICATION: 'À vérifier (CNSP)', VERIFIED_AND_SENT: 'Vérifié et diffusé' } From 7d7bbdb597e93eaab94290d2bbb189c386244f97 Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Fri, 2 Aug 2024 14:35:45 +0200 Subject: [PATCH 11/41] Fix duplicate in prior notification state enum --- adrs/0006-prior-notification-states-specifications.md | 4 ++-- .../entities/prior_notification/PriorNotification.kt | 2 +- .../entities/prior_notification/PriorNotificationState.kt | 5 +---- .../features/PriorNotification/PriorNotification.types.ts | 7 ++----- .../components/AutoPriorNotificationForm/Footer.tsx | 2 +- .../components/ManualPriorNotificationForm/Card.tsx | 4 ++-- .../components/PriorNotificationList/cells/StateCell.tsx | 3 +-- .../components/PriorNotificationList/utils.ts | 2 +- 8 files changed, 11 insertions(+), 18 deletions(-) diff --git a/adrs/0006-prior-notification-states-specifications.md b/adrs/0006-prior-notification-states-specifications.md index 846ed04f1d..f9ecb911c0 100644 --- a/adrs/0006-prior-notification-states-specifications.md +++ b/adrs/0006-prior-notification-states-specifications.md @@ -20,7 +20,7 @@ _À rédiger._ | --------- | ------------------------ | ----------- | ------- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | --------------------------- | | `FALSE` | `FALSE` | `FALSE` | `FALSE` | `FALSE` | `00000` | Le préavis auto ne fait pas partie du périmètre de vérification du CNSP et n'a pas été diffusé car aucune unité n'a des paramètres de diffusion qui correspondent à ce préavis.

**FORMULAIRE**
Le préavis auto ne fait pas partie du périmètre de vérification du CNSP et aucune unité n'a des paramètres de diffusion qui correspondent à ce préavis. | "Hors vérification" | `OUT_OF_VERIFICATION_SCOPE` | | `FALSE` | `FALSE` | `FALSE` | `FALSE` | `FALSE` | `00000` | **FORMULAIRE**
Le préavis auto ne fait pas partie du périmètre de vérification du CNSP et au moins une unité a des paramètres de diffusion qui correspondent à ce préavis. | "Envoi auto. demandé" | `AUTO_SEND_REQUESTED` | -| `FALSE` | `FALSE` | `FALSE` | `FALSE` | `TRUE` | `00001` | Le préavis auto ne fait pas partie du périmètre de vérification du CNSP et est en cours de recherche d'unités dont les paramètres de diffusion correspondent à ce préavis. | "En cours d'envoi auto" | `AUTO_SEND_IN_PROGRESS` | +| `FALSE` | `FALSE` | `FALSE` | `FALSE` | `TRUE` | `00001` | Le préavis auto ne fait pas partie du périmètre de vérification du CNSP et est en cours de recherche d'unités dont les paramètres de diffusion correspondent à ce préavis. | "En cours d'envoi auto" | `PENDING_AUTO_SEND` | | `FALSE` | `FALSE` | `FALSE` | `TRUE` | `FALSE` | `00010` | Le préavis auto ne fait pas partie du périmètre de vérification du CNSP et a été diffusé auprès des unités dont les paramètres de diffusion correspondent à ce préavis. | "Envoi auto. fait" | `AUTO_SEND_DONE` | | `FALSE` | `FALSE` | `FALSE` | `TRUE` | `TRUE` | `00011` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `FALSE` | `FALSE` | `TRUE` | `FALSE` | `FALSE` | `00100` | Le préavis a été vérifié / diffusé mais la diffusion n'a pas abouti à un envoi effectif. Causes possibles : pas d'unité abonnée au port, unités abonnées mais sans adresse de diffusion email ni sms, ou tous les envois ont échoué | "Échec de diffusion" | `FAILED_SEND` | @@ -37,7 +37,7 @@ _À rédiger._ | `FALSE` | `TRUE` | `TRUE` | `TRUE` | `TRUE` | `01111` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `TRUE` | `FALSE` | `FALSE` | `FALSE` | `FALSE` | `10000` | Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et n'a pas été diffusé car aucune unité n'a des paramètres de diffusion qui correspondent à ce préavis.

**FORMULAIRE**
Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et aucune unité n'a des paramètres de diffusion qui correspondent à ce préavis. | "Hors vérification" | `OUT_OF_VERIFICATION_SCOPE` | | `TRUE` | `FALSE` | `FALSE` | `FALSE` | `FALSE` | `10000` | **FORMULAIRE**
Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et au moins une unité a des paramètres de diffusion qui correspondent à ce préavis. | "Envoi auto. demandé" | `AUTO_SEND_REQUESTED` | -| `TRUE` | `FALSE` | `FALSE` | `FALSE` | `TRUE` | `10001` | Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et est en cours de recherche d'unités dont les paramètres de diffusion correspondent à ce préavis. | "En cours d'envoi auto" | `AUTO_SEND_IN_PROGRESS` | +| `TRUE` | `FALSE` | `FALSE` | `FALSE` | `TRUE` | `10001` | Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et est en cours de recherche d'unités dont les paramètres de diffusion correspondent à ce préavis. | "En cours d'envoi auto" | `PENDING_AUTO_SEND` | | `TRUE` | `FALSE` | `FALSE` | `TRUE` | `FALSE` | `10010` | Le préavis manuel ne fait pas partie du périmètre de vérification du CNSP et a été diffusé auprès des unités dont les paramètres de diffusion correspondent à ce préavis. | "Envoi auto. fait" | `AUTO_SEND_DONE` | | `TRUE` | `FALSE` | `FALSE` | `TRUE` | `TRUE` | `10011` | Impossible : on ne peut avoir un préavis à la fois diffusé et en cours de diffusion. | | | | `TRUE` | `FALSE` | `TRUE` | `FALSE` | `FALSE` | `10100` | Le préavis a été vérifié / diffusé mais la diffusion n'a pas abouti à un envoi effectif. Causes possibles : pas d'unité abonnée au port, unités abonnées mais sans adresse de diffusion email ni sms, ou tous les envois ont échoué | "Échec de diffusion" | `FAILED_SEND` | diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt index 0a146b458b..15601c4b0a 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt @@ -52,7 +52,7 @@ data class PriorNotification( return when { isInVerificationScope == null || isVerified == null || isSent == null || isBeingSent == null -> null !isInVerificationScope && !isVerified && !isSent && !isBeingSent -> PriorNotificationState.OUT_OF_VERIFICATION_SCOPE - !isInVerificationScope && !isVerified && !isSent && isBeingSent -> PriorNotificationState.AUTO_SEND_IN_PROGRESS + !isInVerificationScope && !isVerified && !isSent && isBeingSent -> PriorNotificationState.PENDING_AUTO_SEND !isInVerificationScope && !isVerified && isSent && !isBeingSent -> PriorNotificationState.AUTO_SEND_DONE !isInVerificationScope && isVerified && !isSent && !isBeingSent -> PriorNotificationState.FAILED_SEND !isInVerificationScope && isVerified && !isSent && isBeingSent -> PriorNotificationState.PENDING_SEND diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotificationState.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotificationState.kt index c6512eba1b..da2812586d 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotificationState.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotificationState.kt @@ -4,9 +4,6 @@ enum class PriorNotificationState { /** "Envoi auto. demandé". */ AUTO_SEND_REQUESTED, - /** "Envoi auto en cours". */ - AUTO_SEND_IN_PROGRESS, - /** "Envoi auto. fait". */ AUTO_SEND_DONE, @@ -16,7 +13,7 @@ enum class PriorNotificationState { /** "Hors vérification". */ OUT_OF_VERIFICATION_SCOPE, - /** "Envoi auto en cours". */ + /** "Envoi auto. en cours". */ PENDING_AUTO_SEND, /** "Diffusion en cours". */ diff --git a/frontend/src/features/PriorNotification/PriorNotification.types.ts b/frontend/src/features/PriorNotification/PriorNotification.types.ts index c11aa02935..ec02cc56d5 100644 --- a/frontend/src/features/PriorNotification/PriorNotification.types.ts +++ b/frontend/src/features/PriorNotification/PriorNotification.types.ts @@ -171,15 +171,13 @@ export namespace PriorNotification { export enum State { /** "Envoi auto. fait". */ AUTO_SEND_DONE = 'AUTO_SEND_DONE', - /** "Envoi auto en cours". */ - AUTO_SEND_IN_PROGRESS = 'AUTO_SEND_IN_PROGRESS', /** "Envoi auto. demandé". */ AUTO_SEND_REQUESTED = 'AUTO_SEND_REQUESTED', /** "Échec de diffusion". */ FAILED_SEND = 'FAILED_SEND', /** "Hors vérification". */ OUT_OF_VERIFICATION_SCOPE = 'OUT_OF_VERIFICATION_SCOPE', - /** "Envoi auto en cours". */ + /** "Envoi auto. en cours". */ PENDING_AUTO_SEND = 'PENDING_AUTO_SEND', /** "Diffusion en cours". */ PENDING_SEND = 'PENDING_SEND', @@ -190,11 +188,10 @@ export namespace PriorNotification { } export const STATE_LABEL: Record = { AUTO_SEND_DONE: 'Envoi auto. fait', - AUTO_SEND_IN_PROGRESS: 'Envoi auto en cours', AUTO_SEND_REQUESTED: 'Envoi auto. demandé', FAILED_SEND: 'Échec de diffusion', OUT_OF_VERIFICATION_SCOPE: 'Hors vérification', - PENDING_AUTO_SEND: 'Envoi auto en cours', + PENDING_AUTO_SEND: 'Envoi auto. en cours', PENDING_SEND: 'Diffusion en cours', PENDING_VERIFICATION: 'À vérifier (CNSP)', VERIFIED_AND_SENT: 'Vérifié et diffusé' diff --git a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx index a390d9eaa8..c7dd4d5a4e 100644 --- a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx +++ b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx @@ -14,7 +14,7 @@ export function Footer({ detail }: FooterProps) { const { isInvalidated } = detail.logbookMessage.message const isPendingSend = !!detail.state && - [PriorNotification.State.AUTO_SEND_IN_PROGRESS, PriorNotification.State.PENDING_SEND].includes(detail.state) + [PriorNotification.State.PENDING_AUTO_SEND, PriorNotification.State.PENDING_SEND].includes(detail.state) const isVerifiedAndSent = detail.state === PriorNotification.State.VERIFIED_AND_SENT const verifyAndSend = async () => { diff --git a/frontend/src/features/PriorNotification/components/ManualPriorNotificationForm/Card.tsx b/frontend/src/features/PriorNotification/components/ManualPriorNotificationForm/Card.tsx index 9bc546050e..4cff6e315a 100644 --- a/frontend/src/features/PriorNotification/components/ManualPriorNotificationForm/Card.tsx +++ b/frontend/src/features/PriorNotification/components/ManualPriorNotificationForm/Card.tsx @@ -50,7 +50,7 @@ export function Card({ detail, isValidatingOnChange, onClose, onSubmit, onVerify const isInvalidated = detail?.logbookMessage?.message?.isInvalidated const isPendingSend = !!detail?.state && - [PriorNotification.State.AUTO_SEND_IN_PROGRESS, PriorNotification.State.PENDING_SEND].includes(detail?.state) + [PriorNotification.State.PENDING_AUTO_SEND, PriorNotification.State.PENDING_SEND].includes(detail?.state) const isPendingVerification = detail?.state === PriorNotification.State.PENDING_VERIFICATION const isVerifiedAndSent = detail?.state === PriorNotification.State.VERIFIED_AND_SENT const hasDesignatedPorts = editedPriorNotificationComputedValues?.types?.find(type => type.hasDesignatedPorts) @@ -139,7 +139,7 @@ export function Card({ detail, isValidatingOnChange, onClose, onSubmit, onVerify Le préavis est en cours de diffusion. )} - {detail?.state === PriorNotification.State.AUTO_SEND_IN_PROGRESS && ( + {detail?.state === PriorNotification.State.PENDING_AUTO_SEND && ( Le préavis est en cours d’envoi aux unités qui l’ont demandé. diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/cells/StateCell.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/cells/StateCell.tsx index 8c79388bd6..522dd0e927 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/cells/StateCell.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/cells/StateCell.tsx @@ -23,8 +23,7 @@ export function StateCell({ isInvalidated, state }: SendButtonCellProps) { return ( - {!!state && - [PriorNotification.State.AUTO_SEND_IN_PROGRESS, PriorNotification.State.PENDING_SEND].includes(state) ? ( + {!!state && [PriorNotification.State.PENDING_AUTO_SEND, PriorNotification.State.PENDING_SEND].includes(state) ? ( diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/utils.ts b/frontend/src/features/PriorNotification/components/PriorNotificationList/utils.ts index 846818d288..41fd4bf108 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/utils.ts +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/utils.ts @@ -128,7 +128,7 @@ export function getColorsFromState(state: PriorNotification.State | undefined): colors = [THEME.color.lightGray, THEME.color.lightGray, THEME.color.charcoal] break - case PriorNotification.State.AUTO_SEND_IN_PROGRESS: + case PriorNotification.State.PENDING_AUTO_SEND: case PriorNotification.State.PENDING_SEND: colors = [THEME.color.gainsboro, THEME.color.slateGray, THEME.color.slateGray] break From 8a000d59bf8e25c806282988738ddc2b8de5bae1 Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Fri, 2 Aug 2024 14:57:30 +0200 Subject: [PATCH 12/41] Fix prior notification card wrapper background left position for ext/ --- .../components/PriorNotificationCard/index.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx index bd3c49188d..005d9a9f1d 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx @@ -8,6 +8,7 @@ import { } from '@features/PriorNotification/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { Accent, Button } from '@mtes-mct/monitor-ui' +import { useIsSuperUser } from 'auth/hooks/useIsSuperUser' import styled from 'styled-components' import { LoadingSpinnerWall } from 'ui/LoadingSpinnerWall' @@ -31,6 +32,7 @@ export function PriorNotificationCard({ priorNotificationDetail }: PriorNotificationCardLayoutProps) { const dispatch = useMainAppDispatch() + const isSuperUser = useIsSuperUser() const isInvalidated = priorNotificationDetail?.logbookMessage?.message?.isInvalidated const isPendingVerification = priorNotificationDetail?.state === PriorNotification.State.PENDING_VERIFICATION @@ -45,7 +47,7 @@ export function PriorNotificationCard({ if (!priorNotificationDetail || isLoading) { return ( - + @@ -56,7 +58,7 @@ export function PriorNotificationCard({ } return ( - + @@ -116,11 +118,14 @@ export function PriorNotificationCard({ ) } -const Wrapper = styled.div` +const Wrapper = styled.div<{ + // TODO Pass this prop via a context? + $isSuperUser: boolean +}>` bottom: 0; display: flex; justify-content: flex-end; - left: 70px; + left: ${p => (p.$isSuperUser ? '70px' : 0)}; position: fixed; right: 0; top: 0; From d5c8f7c520bce7b290f9712ffbcc1681899c88a1 Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Fri, 2 Aug 2024 15:00:36 +0200 Subject: [PATCH 13/41] Add missing prior notification card data --- .../AutoPriorNotificationForm/index.tsx | 4 +- .../PriorNotificationCard/Header.tsx | 12 +++--- .../PriorNotificationCard/index.tsx | 39 ++++++++----------- .../PriorNotificationList/index.tsx | 7 +++- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx index dbf6d074ae..36eb4f2c18 100644 --- a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx +++ b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx @@ -13,7 +13,7 @@ export function AutoPriorNotificationForm() { ) if (!editedAutoPriorNotificationInitialFormValues || !openedPriorNotificationDetail) { - return + return } return ( @@ -21,8 +21,8 @@ export function AutoPriorNotificationForm() { bodyChildren={ } + detail={openedPriorNotificationDetail} footerChildren={
} - priorNotificationDetail={openedPriorNotificationDetail} /> ) } diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationCard/Header.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationCard/Header.tsx index 4bb72378d8..984b259b77 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationCard/Header.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationCard/Header.tsx @@ -5,10 +5,10 @@ import styled from 'styled-components' import type { PriorNotification } from '../../PriorNotification.types' type HeaderProps = Readonly<{ + detail: PriorNotification.PriorNotificationDetail onClose: () => void - priorNotificationDetail: PriorNotification.PriorNotificationDetail }> -export function Header({ onClose, priorNotificationDetail }: HeaderProps) { +export function Header({ detail, onClose }: HeaderProps) { return ( @@ -17,17 +17,17 @@ export function Header({ onClose, priorNotificationDetail }: HeaderProps) { <Icon.Fishery /> </TitleRowIconBox> - <span>{`Préavis navire ${priorNotificationDetail.isLessThanTwelveMetersVessel ? '< 12 M' : '≥ 12 M'}`}</span> + <span>{`Préavis navire ${detail.isLessThanTwelveMetersVessel ? '< 12 M' : '≥ 12 M'}`}</span> </TitleRow> <TitleRow> <TitleRowIconBox> - <CountryFlag countryCode={priorNotificationDetail.logbookMessage.flagState} size={[24, 18]} /> + <CountryFlag countryCode={detail.logbookMessage.flagState} size={[24, 18]} /> </TitleRowIconBox> <span> - <VesselName>{priorNotificationDetail.logbookMessage.vesselName}</VesselName> ( - {priorNotificationDetail.logbookMessage.internalReferenceNumber}) + <VesselName>{detail.logbookMessage.vesselName}</VesselName> ({detail.logbookMessage.internalReferenceNumber} + ) </span> </TitleRow> diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx index 005d9a9f1d..330a0b5884 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx @@ -20,32 +20,30 @@ import { TagBar } from '../shared/TagBar' type PriorNotificationCardLayoutProps = Readonly<{ applicableState?: PriorNotification.State bodyChildren?: React.ReactNode + detail: PriorNotification.PriorNotificationDetail | undefined footerChildren?: React.ReactNode isLoading?: boolean - priorNotificationDetail?: PriorNotification.PriorNotificationDetail }> export function PriorNotificationCard({ applicableState, bodyChildren, + detail, footerChildren, - isLoading = false, - priorNotificationDetail + isLoading = false }: PriorNotificationCardLayoutProps) { const dispatch = useMainAppDispatch() const isSuperUser = useIsSuperUser() - const isInvalidated = priorNotificationDetail?.logbookMessage?.message?.isInvalidated - const isPendingVerification = priorNotificationDetail?.state === PriorNotification.State.PENDING_VERIFICATION - const hasDesignatedPorts = priorNotificationDetail?.logbookMessage?.message?.pnoTypes?.find( - type => type.hasDesignatedPorts - ) + const isInvalidated = detail?.logbookMessage.message.isInvalidated + const hasDesignatedPorts = detail?.logbookMessage.message.pnoTypes?.find(type => type.hasDesignatedPorts) + const isPendingVerification = detail?.state === PriorNotification.State.PENDING_VERIFICATION const close = () => { dispatch(priorNotificationActions.closePriorNotificationCard()) dispatch(priorNotificationActions.closePriorNotificationForm()) } - if (!priorNotificationDetail || isLoading) { + if (!detail || isLoading) { return ( @@ -63,23 +61,21 @@ export function PriorNotificationCard({ -
+
{isPendingVerification && Le préavis doit être vérifié par le CNSP avant sa diffusion.} @@ -91,8 +87,8 @@ export function PriorNotificationCard({
@@ -105,10 +101,7 @@ export function PriorNotificationCard({ Fermer - + {footerChildren}
diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx index 6fa55e2e7f..5a76fa3e01 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx @@ -236,7 +236,12 @@ export function PriorNotificationList({ isFromUrl }: PriorNotificationListProps) - {isPriorNotificationCardOpen && } + {isPriorNotificationCardOpen && ( + + )} {isPriorNotificationFormOpen && !openedPriorNotificationDetail?.isManuallyCreated && ( )} From b1ebab23834d76ad2975700254fd4d1346638fcd Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Mon, 5 Aug 2024 11:36:16 +0200 Subject: [PATCH 14/41] Fix auto prior notification state update during data update in Backend --- .../repositories/LogbookReportRepository.kt | 2 + .../VerifyAndSendPriorNotification.kt | 1 + .../cnsp/monitorfish/infrastructure/Utils.kt | 17 ++++++++ .../JpaLogbookReportRepository.kt | 39 ++++++++++--------- ...r_notifications_author_trigram_column.sql} | 0 .../JpaLogbookReportRepositoryITests.kt | 1 + 6 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/Utils.kt rename backend/src/main/resources/db/migration/internal/{V0.268__Drop_manual_prior_notifications_author_trigram_column.sql => V0.270__Drop_manual_prior_notifications_author_trigram_column.sql} (100%) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt index 60e2fe0773..2649821662 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt @@ -69,7 +69,9 @@ interface LogbookReportRepository { fun updatePriorNotificationState( reportId: String, operationDate: ZonedDateTime, + isBeingSent: Boolean, + isSent: Boolean, isVerified: Boolean, ) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/VerifyAndSendPriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/VerifyAndSendPriorNotification.kt index 5775dad134..393e284596 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/VerifyAndSendPriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/VerifyAndSendPriorNotification.kt @@ -20,6 +20,7 @@ class VerifyAndSendPriorNotification( reportId, operationDate, isBeingSent = true, + isSent = false, isVerified = true, ) } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/Utils.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/Utils.kt new file mode 100644 index 0000000000..dd9eb8295b --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/Utils.kt @@ -0,0 +1,17 @@ +package fr.gouv.cnsp.monitorfish.infrastructure + +class Utils { + companion object { + /** + * Compares two strings by trimming them and checking if they are equivalent: + * + * If both strings are null or empty, they are considered equivalent. + */ + fun areStringsEquivalent(leftString: String?, rightString: String?): Boolean { + val normalizedLeftString = leftString?.trim().takeUnless { it.isNullOrEmpty() || it == " " } + val normalizedRightString = rightString?.trim().takeUnless { it.isNullOrEmpty() || it == " " } + + return normalizedLeftString == normalizedRightString + } + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt index b9c5538fe0..8180497b9b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt @@ -10,6 +10,7 @@ import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotifica import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.filters.PriorNotificationsFilter import fr.gouv.cnsp.monitorfish.domain.exceptions.* import fr.gouv.cnsp.monitorfish.domain.repositories.LogbookReportRepository +import fr.gouv.cnsp.monitorfish.infrastructure.Utils import fr.gouv.cnsp.monitorfish.infrastructure.database.entities.LogbookReportEntity import fr.gouv.cnsp.monitorfish.infrastructure.database.repositories.interfaces.DBLogbookReportRepository import fr.gouv.cnsp.monitorfish.infrastructure.database.repositories.utils.toSqlArrayString @@ -360,7 +361,9 @@ class JpaLogbookReportRepository( override fun updatePriorNotificationState( reportId: String, operationDate: ZonedDateTime, + isBeingSent: Boolean, + isSent: Boolean, isVerified: Boolean, ) { val logbookReportEntities = @@ -378,10 +381,10 @@ class JpaLogbookReportRepository( .map { logbookReportEntity -> val pnoMessage = objectMapper.readValue(logbookReportEntity.message, PNO::class.java) pnoMessage.isBeingSent = isBeingSent + pnoMessage.isSent = isSent pnoMessage.isVerified = isVerified val nextMessage = objectMapper.writeValueAsString(pnoMessage) - val updatedEntity = logbookReportEntity.copy(message = nextMessage) dbLogbookReportRepository.save(updatedEntity) @@ -410,26 +413,26 @@ class JpaLogbookReportRepository( .filter { it.operationType in listOf(LogbookOperationType.DAT, LogbookOperationType.COR) } .map { logbookReportEntity -> val pnoMessage = objectMapper.readValue(logbookReportEntity.message, PNO::class.java) - pnoMessage.authorTrigram = authorTrigram - pnoMessage.note = note - - /** - * The PNO states are re-initialized: - * - the PDF will be re-generated (done in the use case by deleting the old one) - * - the PNO will require another verification before sending - * - * Note: We will lose the distinction between `AUTO_SEND_DONE` and `OUT_OF_VERIFICATION_SCOPE` - * if it was in one of these states before the update. But it's an acceptable trade-off. - */ - pnoMessage.isBeingSent = false - pnoMessage.isVerified = false - pnoMessage.isSent = false + if ( + !Utils.areStringsEquivalent(authorTrigram, pnoMessage.authorTrigram) || + !Utils.areStringsEquivalent(note, pnoMessage.note) + ) { + pnoMessage.authorTrigram = authorTrigram + pnoMessage.note = note - val nextMessage = objectMapper.writeValueAsString(pnoMessage) + /** + * The PNO states are re-initialized: + * - the PDF will be re-generated (done in the use case by deleting the old one) + * - the PNO will require another verification before sending + */ + pnoMessage.isVerified = false - val updatedEntity = logbookReportEntity.copy(message = nextMessage) + val nextMessage = objectMapper.writeValueAsString(pnoMessage) - dbLogbookReportRepository.save(updatedEntity) + val updatedEntity = logbookReportEntity.copy(message = nextMessage) + + dbLogbookReportRepository.save(updatedEntity) + } } } diff --git a/backend/src/main/resources/db/migration/internal/V0.268__Drop_manual_prior_notifications_author_trigram_column.sql b/backend/src/main/resources/db/migration/internal/V0.270__Drop_manual_prior_notifications_author_trigram_column.sql similarity index 100% rename from backend/src/main/resources/db/migration/internal/V0.268__Drop_manual_prior_notifications_author_trigram_column.sql rename to backend/src/main/resources/db/migration/internal/V0.270__Drop_manual_prior_notifications_author_trigram_column.sql diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt index 11c5ace15b..c6ad3c087d 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt @@ -1095,6 +1095,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { "FAKE_OPERATION_109", ZonedDateTime.now().minusMinutes(15), isBeingSent = true, + isSent = false, isVerified = true, ) From d14cc48e12e883512a4c01cd471183480311dd5e Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Mon, 5 Aug 2024 11:38:30 +0200 Subject: [PATCH 15/41] Include state in prior notification fingerprint --- .../domain/entities/prior_notification/PriorNotification.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt index 15601c4b0a..6e4a04368e 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt @@ -34,7 +34,7 @@ data class PriorNotification( var lastControlDateTime: ZonedDateTime?, ) { /** Each prior notification and each of its updates have a unique fingerprint. */ - val fingerprint: String = listOf(reportId, updatedAt).joinToString(separator = ".") + val fingerprint: String = listOf(reportId, updatedAt, state).joinToString(separator = ".") private val logger = LoggerFactory.getLogger(PriorNotification::class.java) val state: PriorNotificationState? From 7d60cd1a921797d4b3c31d5ada2d34af3e39fe6b Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Mon, 5 Aug 2024 11:40:32 +0200 Subject: [PATCH 16/41] Add missing pending send banner in prior notification card --- .../AutoPriorNotificationForm/Footer.tsx | 18 ++--------- .../AutoPriorNotificationForm/index.tsx | 22 +++++++++++-- .../ManualPriorNotificationForm/Card.tsx | 32 ++++--------------- .../ManualPriorNotificationForm/index.tsx | 2 +- .../PriorNotificationCard/index.tsx | 15 ++++++++- .../cells/ActionButtonsCell.tsx | 22 +++++++------ .../components/shared/CardBanner.tsx | 23 +++++++++++++ .../verifyAndSendPriorNotification.ts | 7 ++-- 8 files changed, 82 insertions(+), 59 deletions(-) create mode 100644 frontend/src/features/PriorNotification/components/shared/CardBanner.tsx diff --git a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx index c7dd4d5a4e..d7f0ca985c 100644 --- a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx +++ b/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/Footer.tsx @@ -1,35 +1,23 @@ import { PriorNotification } from '@features/PriorNotification/PriorNotification.types' -import { verifyAndSendPriorNotification } from '@features/PriorNotification/useCases/verifyAndSendPriorNotification' -import { getPriorNotificationIdentifier } from '@features/PriorNotification/utils' -import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { Accent, Button, Icon } from '@mtes-mct/monitor-ui' -import { assertNotNullish } from '@utils/assertNotNullish' type FooterProps = Readonly<{ detail: PriorNotification.PriorNotificationDetail + onVerifyAndSend: () => void }> -export function Footer({ detail }: FooterProps) { - const dispatch = useMainAppDispatch() - +export function Footer({ detail, onVerifyAndSend }: FooterProps) { const { isInvalidated } = detail.logbookMessage.message const isPendingSend = !!detail.state && [PriorNotification.State.PENDING_AUTO_SEND, PriorNotification.State.PENDING_SEND].includes(detail.state) const isVerifiedAndSent = detail.state === PriorNotification.State.VERIFIED_AND_SENT - const verifyAndSend = async () => { - const identifier = getPriorNotificationIdentifier(detail) - assertNotNullish(identifier) - - await dispatch(verifyAndSendPriorNotification(identifier, detail.fingerprint, false)) - } - return ( + ) +} diff --git a/frontend/src/features/PriorNotification/components/LogbookPriorNotificationForm/Form.tsx b/frontend/src/features/PriorNotification/components/LogbookPriorNotificationForm/Form.tsx new file mode 100644 index 0000000000..8deef28092 --- /dev/null +++ b/frontend/src/features/PriorNotification/components/LogbookPriorNotificationForm/Form.tsx @@ -0,0 +1,118 @@ +import { HALF_A_SECOND } from '@constants/index' +import { useInvalidatePriorNotificationMutation } from '@features/PriorNotification/priorNotificationApi' +import { updateLogbookPriorNotification } from '@features/PriorNotification/useCases/updateLogbookPriorNotification' +import { getPriorNotificationIdentifier } from '@features/PriorNotification/utils' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' +import { Accent, Button, FormikEffect, FormikTextarea, FormikTextInput, Icon } from '@mtes-mct/monitor-ui' +import { assertNotNullish } from '@utils/assertNotNullish' +import { useIsSuperUser } from 'auth/hooks/useIsSuperUser' +import { Formik } from 'formik' +import { noop } from 'lodash' +import { useCallback, useMemo, useState } from 'react' +import styled from 'styled-components' +import { useDebouncedCallback } from 'use-debounce' + +import { InvalidatePriorNotificationDialog } from '../InvalidatePriorNotificationDialog' + +import type { PriorNotification } from '@features/PriorNotification/PriorNotification.types' + +type FormProps = Readonly<{ + detail: PriorNotification.PriorNotificationDetail + initialFormValues: PriorNotification.LogbookPriorNotificationData +}> +export function Form({ detail, initialFormValues }: FormProps) { + const dispatch = useMainAppDispatch() + const isSuperUser = useIsSuperUser() + + const [invalidatePriorNotification] = useInvalidatePriorNotificationMutation() + + const [isInvalidatingPriorNotificationDialog, setIsInvalidatingPriorNotificationDialog] = useState(false) + + const priorNotificationIdentifier = useMemo(() => getPriorNotificationIdentifier(detail), [detail]) + assertNotNullish(priorNotificationIdentifier) + + const { isInvalidated } = detail.logbookMessage.message + + const invalidate = async () => { + await invalidatePriorNotification({ + isManuallyCreated: detail.isManuallyCreated, + operationDate: priorNotificationIdentifier.operationDate, + reportId: priorNotificationIdentifier.reportId + }) + + setIsInvalidatingPriorNotificationDialog(false) + } + + const updateNoteCallback = useCallback( + async (nextValues: PriorNotification.LogbookPriorNotificationData) => { + await dispatch(updateLogbookPriorNotification(priorNotificationIdentifier, nextValues)) + }, + [dispatch, priorNotificationIdentifier] + ) + + const updateNote = useDebouncedCallback( + (nextValues: PriorNotification.LogbookPriorNotificationData) => updateNoteCallback(nextValues), + HALF_A_SECOND + ) + + return ( + <> + + <> + + + + + + + + + + + {isSuperUser && !isInvalidated && ( + setIsInvalidatingPriorNotificationDialog(true)} + title="Invalider le préavis" + > + Invalider le préavis + + )} + + {isInvalidatingPriorNotificationDialog && ( + setIsInvalidatingPriorNotificationDialog(false)} + onConfirm={invalidate} + /> + )} + + ) +} + +const FieldGroup = styled.div.attrs({ className: 'FieldGroup' })` + display: flex; + flex-direction: column; + gap: 8px; + + .rs-checkbox { + > .rs-checkbox-checker { + > label { + line-height: 18px; + } + } + } + + textarea { + box-sizing: border-box !important; + } +` + +const AuthorTrigramInput = styled(FormikTextInput)` + width: 120px; +` + +const InvalidateButton = styled(Button)` + color: ${p => p.theme.color.maximumRed}; + margin-top: 48px; +` diff --git a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx b/frontend/src/features/PriorNotification/components/LogbookPriorNotificationForm/index.tsx similarity index 74% rename from frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx rename to frontend/src/features/PriorNotification/components/LogbookPriorNotificationForm/index.tsx index 3f48ccfc13..64f9c7f5cc 100644 --- a/frontend/src/features/PriorNotification/components/AutoPriorNotificationForm/index.tsx +++ b/frontend/src/features/PriorNotification/components/LogbookPriorNotificationForm/index.tsx @@ -9,10 +9,10 @@ import { Footer } from './Footer' import { Form } from './Form' import { PriorNotificationCard } from '../PriorNotificationCard' -export function AutoPriorNotificationForm() { +export function LogbookPriorNotificationForm() { const dispatch = useMainAppDispatch() - const editedAutoPriorNotificationInitialFormValues = useMainAppSelector( - state => state.priorNotification.editedAutoPriorNotificationInitialFormValues + const editedLogbookPriorNotificationInitialFormValues = useMainAppSelector( + state => state.priorNotification.editedLogbookPriorNotificationInitialFormValues ) const openedPriorNotificationDetail = useMainAppSelector( state => state.priorNotification.openedPriorNotificationDetail @@ -30,14 +30,17 @@ export function AutoPriorNotificationForm() { await dispatch(verifyAndSendPriorNotification(identifier, false)) } - if (!editedAutoPriorNotificationInitialFormValues || !openedPriorNotificationDetail || isLoading) { + if (!editedLogbookPriorNotificationInitialFormValues || !openedPriorNotificationDetail || isLoading) { return } return ( + } detail={openedPriorNotificationDetail} footerChildren={