diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationDraftDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationDraftDataOutput.kt index 48d362f06d..d96d867664 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationDraftDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/ManualPriorNotificationDraftDataOutput.kt @@ -58,7 +58,12 @@ data class ManualPriorNotificationDraftDataOutput( } val fishingCatchDataOutputs = pnoValue.catchOnboard.map { - ManualPriorNotificationFishingCatchDataOutput.fromLogbookFishingCatch(it, !hasGlobalFaoArea) + val catchWithNormalizedWeight = it.copy(weight = it.weight ?: 0.0) + + ManualPriorNotificationFishingCatchDataOutput.fromLogbookFishingCatch( + catchWithNormalizedWeight, + !hasGlobalFaoArea, + ) } return ManualPriorNotificationDraftDataOutput( diff --git a/backend/src/main/resources/db/testdata/V666.5.1__Insert_more_pno_logbook_reports.sql b/backend/src/main/resources/db/testdata/V666.5.1__Insert_more_pno_logbook_reports.sql index 66a17932a7..6e4a4e13c1 100644 --- a/backend/src/main/resources/db/testdata/V666.5.1__Insert_more_pno_logbook_reports.sql +++ b/backend/src/main/resources/db/testdata/V666.5.1__Insert_more_pno_logbook_reports.sql @@ -241,7 +241,7 @@ UPDATE logbook_reports SET value = JSONB_SET(value, '{tripStartDate}', TO_JSONB( INSERT INTO logbook_reports (id, report_id, referenced_report_id, integration_datetime_utc, operation_datetime_utc, operation_number, operation_type, transmission_format, value) VALUES (1119, NULL, 'FAKE_OPERATION_119', NOW() AT TIME ZONE 'UTC' - INTERVAL '14 minutes', NOW() AT TIME ZONE 'UTC' - INTERVAL '14 minutes', 'FAKE_OPERATION_119_RET', 'RET', 'ERS', '{"returnStatus":"000"}'); -INSERT INTO logbook_reports (id, report_id, referenced_report_id, cfr, enriched, flag_state, integration_datetime_utc, log_type, operation_datetime_utc, operation_number, operation_type, report_datetime_utc, software, transmission_format, trip_gears, trip_segments, vessel_name, value) VALUES (120, 'FAKE_OPERATION_120', NULL, 'CFR129', true, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'PNO', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'FAKE_OPERATION_120', 'DAT', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'TurboCatch (3.7-1)', 'ERS', NULL, NULL, 'BON VENT', '{"authorTrigram":null,"riskFactor":2.9,"catchOnboard":[{"weight":150,"nbFish":null,"species":"ANF","faoZone":"27.8.a","effortZone":"C","economicZone":"FRA","statisticalRectangle":"23E6"}],"pnoTypes":[{"pnoTypeName":"Préavis type Z","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"BROIA","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null,"updatedAt":null,"updatedBy":null}'); +INSERT INTO logbook_reports (id, report_id, referenced_report_id, cfr, enriched, flag_state, integration_datetime_utc, log_type, operation_datetime_utc, operation_number, operation_type, report_datetime_utc, software, transmission_format, trip_gears, trip_segments, vessel_name, value) VALUES (120, 'FAKE_OPERATION_120', NULL, 'CFR129', true, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'PNO', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'FAKE_OPERATION_120', 'DAT', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 minutes', 'TurboCatch (3.7-1)', 'ERS', NULL, NULL, 'BON VENT', '{"authorTrigram":null,"riskFactor":2.9,"catchOnboard":[{"economicZone":"FRA","effortZone":"C","faoZone":"27.8.a","nbFish":null,"species":"ANF","statisticalRectangle":"23E6","weight":null},{"economicZone":"FRA","effortZone":"C","faoZone":"27.8.a","nbFish":null,"species":"COD","statisticalRectangle":"23E6","weight":25}],"pnoTypes":[{"pnoTypeName":"Préavis type Z","minimumNotificationPeriod":4,"hasDesignatedPorts":false}],"port":"BROIA","predictedArrivalDatetimeUtc":null,"predictedLandingDatetimeUtc":null,"purpose":"LAN","tripStartDate":null,"updatedAt":null,"updatedBy":null}'); UPDATE logbook_reports 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 id = 120; UPDATE logbook_reports 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 id = 120; UPDATE logbook_reports 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 id = 120; 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 fbc2f05779..8da898aaf1 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 @@ -1510,6 +1510,7 @@ // - Vessel: BON VENT // - Flag state: FR // - French vessel landing in a foreign port + // - `null` weight in fishing catches { "id": 120, "report_id": "FAKE_OPERATION_120", @@ -1533,13 +1534,22 @@ "riskFactor": 2.9, "catchOnboard": [ { - "weight": 150.0, + "economicZone": "FRA", + "effortZone": "C", + "faoZone": "27.8.a", "nbFish": null, "species": "ANF", - "faoZone": "27.8.a", - "effortZone": "C", + "statisticalRectangle": "23E6", + "weight": null + }, + { "economicZone": "FRA", - "statisticalRectangle": "23E6" + "effortZone": "C", + "faoZone": "27.8.a", + "nbFish": null, + "species": "COD", + "statisticalRectangle": "23E6", + "weight": 25.0 } ], "pnoTypes": [ diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/LogbookMessageFaker.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/LogbookMessageFaker.kt index 1fedbabaa7..e3b2876b7c 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/LogbookMessageFaker.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/LogbookMessageFaker.kt @@ -1,19 +1,20 @@ package fr.gouv.cnsp.monitorfish.fakers import fr.gouv.cnsp.monitorfish.domain.entities.logbook.* +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.Acknowledgment import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO import java.time.ZonedDateTime class LogbookMessageFaker { companion object { - fun fakePnoLogbookMessage(index: Int = 1): LogbookMessage { + fun fakePnoLogbookMessage(index: Int): LogbookMessage { return LogbookMessage( id = index.toLong(), reportId = "FAKE_REPORT_ID_$index", referencedReportId = null, - isDeleted = false, integrationDateTime = ZonedDateTime.now(), isCorrectedByNewerMessage = false, + isDeleted = false, isEnriched = true, message = fakePnoMessage(), messageType = "PNO", @@ -25,21 +26,97 @@ class LogbookMessageFaker { ) } - private fun fakeLogbookFishingCatch(specyCode: String = "HKE"): LogbookFishingCatch { + fun fakePnoLogbookMessage( + id: Long? = null, + reportId: String? = null, + referencedReportId: String? = null, + acknowledgment: Acknowledgment? = null, + externalReferenceNumber: String? = null, + flagState: String? = null, + imo: String? = null, + integrationDateTime: ZonedDateTime = ZonedDateTime.now(), + internalReferenceNumber: String? = null, + ircs: String? = null, + isCorrectedByNewerMessage: Boolean = false, + isDeleted: Boolean = false, + isEnriched: Boolean = false, + isSentByFailoverSoftware: Boolean = false, + message: PNO = fakePnoMessage(), + messageType: String? = null, + operationDateTime: ZonedDateTime = ZonedDateTime.now(), + operationNumber: String? = null, + operationType: LogbookOperationType = LogbookOperationType.DAT, + rawMessage: String? = null, + reportDateTime: ZonedDateTime? = null, + software: String? = null, + transmissionFormat: LogbookTransmissionFormat? = null, + tripGears: List? = emptyList(), + tripNumber: String? = null, + tripSegments: List? = emptyList(), + vesselId: Int? = null, + vesselName: String? = null, + ): LogbookMessage { + return LogbookMessage( + id = id, + reportId = reportId, + referencedReportId = referencedReportId, + acknowledgment = acknowledgment, + externalReferenceNumber = externalReferenceNumber, + flagState = flagState, + imo = imo, + integrationDateTime = integrationDateTime, + internalReferenceNumber = internalReferenceNumber, + ircs = ircs, + isCorrectedByNewerMessage = isCorrectedByNewerMessage, + isDeleted = isDeleted, + isEnriched = isEnriched, + isSentByFailoverSoftware = isSentByFailoverSoftware, + message = message, + messageType = messageType, + operationDateTime = operationDateTime, + operationNumber = operationNumber, + operationType = operationType, + rawMessage = rawMessage, + reportDateTime = reportDateTime, + software = software, + transmissionFormat = transmissionFormat, + tripGears = tripGears, + tripNumber = tripNumber, + tripSegments = tripSegments, + vesselId = vesselId, + vesselName = vesselName, + ) + } + + fun fakeLogbookFishingCatch( + conversionFactor: Double? = null, + economicZone: String? = null, + effortZone: String? = null, + faoZone: String? = "FAO AREA 51", + freshness: String? = null, + nbFish: Double? = null, + packaging: String? = null, + presentation: String? = null, + preservationState: String? = null, + species: String? = "HKE", + speciesName: String? = "Fake Species HKE", + statisticalRectangle: String? = null, + weight: Double? = 42.0, + ): LogbookFishingCatch { return LogbookFishingCatch( - conversionFactor = null, - economicZone = null, - effortZone = null, - faoZone = "FAO AREA 51", - freshness = null, - nbFish = null, - packaging = null, - presentation = null, - preservationState = null, - species = specyCode, - speciesName = "Fake Species $specyCode", - statisticalRectangle = null, - weight = 42.0, + conversionFactor = conversionFactor, + economicZone = economicZone, + effortZone = effortZone, + faoZone = faoZone, + freshness = freshness, + nbFish = nbFish, + packaging = packaging, + presentation = presentation, + preservationState = preservationState, + species = species, + speciesName = speciesName, + statisticalRectangle = statisticalRectangle, + weight = weight, ) } diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerUTests.kt index 71300945a0..e853e200e2 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerUTests.kt @@ -5,10 +5,13 @@ import com.nhaarman.mockitokotlin2.any 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.LogbookMessageAndValue import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessagePurpose +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.* import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.* import fr.gouv.cnsp.monitorfish.domain.utils.PaginatedList +import fr.gouv.cnsp.monitorfish.fakers.LogbookMessageFaker import fr.gouv.cnsp.monitorfish.fakers.PriorNotificationFaker import fr.gouv.cnsp.monitorfish.infrastructure.api.input.LogbookPriorNotificationFormDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationComputeDataInput @@ -366,6 +369,59 @@ class PriorNotificationControllerUTests { .andExpect(jsonPath("$.reportId", equalTo(fakePriorNotification.reportId))) } + // Non-regression test + @Test + fun `getOne Should get a logbook prior notification including a null weight in its fishing catches`() { + val fakePriorNotification = + PriorNotificationFaker.fakePriorNotification().copy( + logbookMessageAndValue = + LogbookMessageAndValue( + logbookMessage = + LogbookMessageFaker.fakePnoLogbookMessage( + reportId = "FAKE_OPERATION_001", + message = + LogbookMessageFaker.fakePnoMessage().apply { + catchOnboard = + listOf( + LogbookMessageFaker.fakeLogbookFishingCatch(species = "COD", weight = 12.0), + LogbookMessageFaker.fakeLogbookFishingCatch(species = "HKE", weight = null), + ) + catchToLand = + listOf( + LogbookMessageFaker.fakeLogbookFishingCatch(species = "COD", weight = 12.0), + LogbookMessageFaker.fakeLogbookFishingCatch(species = "HKE", weight = null), + ) + }, + ), + clazz = PNO::class.java, + ), + ) + + // Given + given( + getPriorNotification.execute( + fakePriorNotification.reportId!!, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, + false, + ), + ) + .willReturn(fakePriorNotification) + + // When + api.perform( + get( + "/bff/v1/prior_notifications/${fakePriorNotification.reportId!!}?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}&isManuallyCreated=false", + ), + ) + // Then + .andExpect(status().isOk) + .andExpect(jsonPath("$.reportId", equalTo(fakePriorNotification.reportId!!))) + .andExpect(jsonPath("$.asManualDraft.fishingCatches[0].weight", equalTo(12.0))) + .andExpect(jsonPath("$.asManualDraft.fishingCatches[1].weight", equalTo(0.0))) + .andExpect(jsonPath("$.logbookMessage.message.catchOnboard[0].weight", equalTo(12.0))) + .andExpect(jsonPath("$.logbookMessage.message.catchToLand[1].weight", equalTo(null))) + } + @Test fun `getPdf Should get the PDF of a prior notification`() { val dummyPdfContent = diff --git a/frontend/src/features/Logbook/components/VesselLogbook/LogbookMessages/messages/common/SpecyCatch.tsx b/frontend/src/features/Logbook/components/VesselLogbook/LogbookMessages/messages/common/SpecyCatch.tsx index ae8b7ab37d..f8b0edc17b 100644 --- a/frontend/src/features/Logbook/components/VesselLogbook/LogbookMessages/messages/common/SpecyCatch.tsx +++ b/frontend/src/features/Logbook/components/VesselLogbook/LogbookMessages/messages/common/SpecyCatch.tsx @@ -38,7 +38,9 @@ export function SpecyCatch({ {specyFullName} Poids total ({weightType}) - {specyCatch.weight || -} kg + + {specyCatch.weight ? `${specyCatch.weight} kg` : -} + {specyCatch.nbFish > 0 && ( @@ -161,6 +163,5 @@ const NoValue = styled.span` color: ${p => p.theme.color.slateGray}; font-weight: 300; line-height: normal; - width: 50px; display: inline-block; `