diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt index 52121e8235..a3888b1eb0 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt @@ -51,10 +51,10 @@ data class LogbookMessage( return referencedReportId ?: reportId } - fun toConsolidatedLogbookMessageTyped( + fun toConsolidatedLogbookMessageAndValue( relatedLogbookMessages: List, clazz: Class, - ): LogbookMessageTyped { + ): LogbookMessageAndValue { if (reportId == null) { throw EntityConversionException( "Logbook report $id has no `reportId`. You can only enrich a DAT or an orphan COR operation with a `reportId`.", @@ -77,7 +77,7 @@ data class LogbookMessage( isDeleted = historicallySortedRelatedLogbookMessages.any { it.operationType == LogbookOperationType.DEL }, ) - return LogbookMessageTyped( + return LogbookMessageAndValue( logbookMessage = finalLogbookMessage, clazz = clazz, ) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageTyped.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageAndValue.kt similarity index 90% rename from backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageTyped.kt rename to backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageAndValue.kt index bb44c08ecd..eedf55e31b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageTyped.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageAndValue.kt @@ -3,12 +3,12 @@ package fr.gouv.cnsp.monitorfish.domain.entities.logbook import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.LogbookMessageValue import fr.gouv.cnsp.monitorfish.domain.exceptions.EntityConversionException -class LogbookMessageTyped( +class LogbookMessageAndValue( /** Logbook report DAT operation, or last COR one if any, enriched with RET & DEL information if any. */ val logbookMessage: LogbookMessage, private val clazz: Class, ) { - val typedMessage: T + val value: T get() = if (clazz.isInstance(logbookMessage.message)) { clazz.cast(logbookMessage.message) } else { 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 1d6a9d9745..76a8bf5a64 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 @@ -30,7 +30,7 @@ class PNO() : LogbookMessageValue { * Is it a prior notification requiring a manual verification? * * It should stay `true` even after the manual verification is done (`isVerified == true`) - * to differanciate mandatory-verification prior notifications from non-mandatory-verification prior notifications. + * to differentiate mandatory-verification prior notifications from non-mandatory-verification prior notifications. * * # Example * @@ -43,6 +43,7 @@ class PNO() : LogbookMessageValue { var isVerified: Boolean? = null var isBeingSent: Boolean? = null var isSent: Boolean? = null + var isInvalidated: Boolean? = null var latitude: Double? = null var longitude: Double? = null var note: 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 5ab4358e6b..ef0e2dd88f 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 @@ -2,7 +2,7 @@ package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification import fr.gouv.cnsp.monitorfish.domain.entities.facade.Seafront import fr.gouv.cnsp.monitorfish.domain.entities.gear.Gear -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO import fr.gouv.cnsp.monitorfish.domain.entities.port.Port import fr.gouv.cnsp.monitorfish.domain.entities.reporting.ReportingType @@ -25,7 +25,7 @@ data class PriorNotification( val createdAt: ZonedDateTime?, val didNotFishAfterZeroNotice: Boolean, val isManuallyCreated: Boolean, - var logbookMessageTyped: LogbookMessageTyped, + var logbookMessageAndValue: LogbookMessageAndValue, var port: Port?, var reportingCount: Int?, var seafront: Seafront?, @@ -43,7 +43,7 @@ data class PriorNotification( * See /adrs/0006-prior-notification-states-specifications.md for more details. */ get() = run { - val pnoMessage = logbookMessageTyped.typedMessage + val pnoMessage = logbookMessageAndValue.value val isInVerificationScope = pnoMessage.isInVerificationScope val isVerified = pnoMessage.isVerified @@ -79,8 +79,8 @@ data class PriorNotification( allVessels: List, isManuallyCreated: Boolean, ) { - val logbookMessage = logbookMessageTyped.logbookMessage - val pnoMessage = logbookMessageTyped.typedMessage + val logbookMessage = logbookMessageAndValue.logbookMessage + val pnoMessage = logbookMessageAndValue.value port = try { pnoMessage.port?.let { portLocode -> @@ -121,7 +121,7 @@ data class PriorNotification( allSpecies: List, logbookRawMessageRepository: LogbookRawMessageRepository, ) { - val logbookMessage = logbookMessageTyped.logbookMessage + val logbookMessage = logbookMessageAndValue.logbookMessage val logbookMessageWithRawMessage = logbookMessage.operationNumber?.let { operationNumber -> logbookMessage.copy( rawMessage = try { @@ -135,7 +135,7 @@ data class PriorNotification( } ?: logbookMessage logbookMessageWithRawMessage.enrichGearPortAndSpecyNames(allGears, allPorts, allSpecies) - logbookMessageTyped = LogbookMessageTyped( + logbookMessageAndValue = LogbookMessageAndValue( logbookMessageWithRawMessage, PNO::class.java, ) 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 72448f119d..d1a25f0034 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 @@ -1,7 +1,7 @@ package fr.gouv.cnsp.monitorfish.domain.repositories import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue import fr.gouv.cnsp.monitorfish.domain.entities.logbook.VoyageDatesAndTripNumber import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification @@ -64,7 +64,7 @@ interface LogbookReportRepository { // Only used in tests fun save(message: LogbookMessage) - fun savePriorNotification(logbookMessageTyped: LogbookMessageTyped): PriorNotification + fun savePriorNotification(logbookMessageAndValue: LogbookMessageAndValue): PriorNotification fun updatePriorNotificationState( reportId: String, @@ -77,6 +77,8 @@ interface LogbookReportRepository { fun updatePriorNotificationNote(reportId: String, operationDate: ZonedDateTime, note: String?) + fun invalidate(reportId: String, operationDate: ZonedDateTime) + // For test purpose fun deleteAll() } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/ManualPriorNotificationRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/ManualPriorNotificationRepository.kt index 050a6d66d8..aaf24e1263 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/ManualPriorNotificationRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/ManualPriorNotificationRepository.kt @@ -12,5 +12,7 @@ interface ManualPriorNotificationRepository { fun updateState(reportId: String, isBeingSent: Boolean, isVerified: Boolean) + fun invalidate(reportId: String) + fun findAllToVerify(): List } 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 c8bfb56dec..09ecd7e44f 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 @@ -111,14 +111,14 @@ class CreateOrUpdateManualPriorNotification( tripGears = tripGears, tripSegments = tripSegments, ) - val logbookMessageTyped = LogbookMessageTyped(pnoLogbookMessage, PNO::class.java) + val logbookMessageAndValue = LogbookMessageAndValue(pnoLogbookMessage, PNO::class.java) val newOrNextPriorNotification = PriorNotification( reportId = reportId, authorTrigram = authorTrigram, didNotFishAfterZeroNotice = didNotFishAfterZeroNotice, isManuallyCreated = true, - logbookMessageTyped = logbookMessageTyped, + logbookMessageAndValue = logbookMessageAndValue, sentAt = sentAt, // All these props are useless for the save operation. @@ -143,7 +143,7 @@ class CreateOrUpdateManualPriorNotification( manualPriorNotificationRepository.save(newOrNextPriorNotification) val createdOrUpdatedPriorNotification = getPriorNotification.execute( createdOrUpdatedIncompletePriorNotification.reportId!!, - createdOrUpdatedIncompletePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + createdOrUpdatedIncompletePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, true, ) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerify.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerify.kt index 1640f0b095..9bb48c0023 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerify.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerify.kt @@ -23,7 +23,7 @@ class GetNumberToVerify( val incompletePriorNotifications = automaticPriorNotifications + manualPriorNotifications val undeletedPriorNotifications = incompletePriorNotifications - .filter { !it.logbookMessageTyped.logbookMessage.isDeleted } + .filter { !it.logbookMessageAndValue.logbookMessage.isDeleted } val priorNotifications = undeletedPriorNotifications .map { priorNotification -> diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt index a581a6b5e0..f6b9f77887 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt @@ -42,12 +42,12 @@ class GetPriorNotifications( val incompletePriorNotifications = automaticPriorNotifications + manualPriorNotifications val undeletedPriorNotifications = incompletePriorNotifications - .filter { !it.logbookMessageTyped.logbookMessage.isDeleted } + .filter { !it.logbookMessageAndValue.logbookMessage.isDeleted } val priorNotifications = undeletedPriorNotifications .map { priorNotification -> priorNotification.enrich(allPorts, allRiskFactors, allVessels, priorNotification.isManuallyCreated) - priorNotification.logbookMessageTyped.logbookMessage + priorNotification.logbookMessageAndValue.logbookMessage .enrichGearPortAndSpecyNames(allGears, allPorts, allSpecies) priorNotification @@ -57,14 +57,14 @@ class GetPriorNotifications( Sort.Direction.ASC -> priorNotifications.sortedWith( compareBy( { getSortKey(it, sortColumn) }, - { it.logbookMessageTyped.logbookMessage.id }, // Tie-breaker + { it.logbookMessageAndValue.logbookMessage.id }, // Tie-breaker ), ) Sort.Direction.DESC -> priorNotifications.sortedWith( // Only solution found to fix typing issues compareByDescending { getSortKey(it, sortColumn) } - .thenByDescending { it.logbookMessageTyped.logbookMessage.id }, // Tie-breaker + .thenByDescending { it.logbookMessageAndValue.logbookMessage.id }, // Tie-breaker ) }.filter { seafrontGroup.hasSeafront(it.seafront) && (states.isNullOrEmpty() || states.contains(it.state)) } @@ -99,11 +99,11 @@ class GetPriorNotifications( sortColumn: PriorNotificationsSortColumn, ): Comparable<*>? { return when (sortColumn) { - PriorNotificationsSortColumn.EXPECTED_ARRIVAL_DATE -> priorNotification.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc - PriorNotificationsSortColumn.EXPECTED_LANDING_DATE -> priorNotification.logbookMessageTyped.typedMessage.predictedLandingDatetimeUtc + PriorNotificationsSortColumn.EXPECTED_ARRIVAL_DATE -> priorNotification.logbookMessageAndValue.value.predictedArrivalDatetimeUtc + PriorNotificationsSortColumn.EXPECTED_LANDING_DATE -> priorNotification.logbookMessageAndValue.value.predictedLandingDatetimeUtc PriorNotificationsSortColumn.PORT_NAME -> priorNotification.port?.name - PriorNotificationsSortColumn.VESSEL_NAME -> priorNotification.logbookMessageTyped.logbookMessage.vesselName - PriorNotificationsSortColumn.VESSEL_RISK_FACTOR -> priorNotification.logbookMessageTyped.typedMessage.riskFactor + PriorNotificationsSortColumn.VESSEL_NAME -> priorNotification.logbookMessageAndValue.logbookMessage.vesselName + PriorNotificationsSortColumn.VESSEL_RISK_FACTOR -> priorNotification.logbookMessageAndValue.value.riskFactor } } } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/InvalidatePriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/InvalidatePriorNotification.kt new file mode 100644 index 0000000000..b96c6586dc --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/InvalidatePriorNotification.kt @@ -0,0 +1,24 @@ +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.PriorNotification +import fr.gouv.cnsp.monitorfish.domain.repositories.LogbookReportRepository +import fr.gouv.cnsp.monitorfish.domain.repositories.ManualPriorNotificationRepository +import java.time.ZonedDateTime + +@UseCase +class InvalidatePriorNotification( + private val logbookReportRepository: LogbookReportRepository, + private val manualPriorNotificationRepository: ManualPriorNotificationRepository, + private val getPriorNotification: GetPriorNotification, +) { + fun execute(reportId: String, operationDate: ZonedDateTime, isManuallyCreated: Boolean): PriorNotification { + if (isManuallyCreated) { + manualPriorNotificationRepository.invalidate(reportId) + } else { + logbookReportRepository.invalidate(reportId, operationDate) + } + + return getPriorNotification.execute(reportId, operationDate, isManuallyCreated) + } +} 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 caaf81d19b..dd3b42206f 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 @@ -29,6 +29,7 @@ class PriorNotificationController( private val getPriorNotificationTypes: GetPriorNotificationTypes, private val updatePriorNotificationNote: UpdatePriorNotificationNote, private val verifyAndSendPriorNotification: VerifyAndSendPriorNotification, + private val invalidatePriorNotification: InvalidatePriorNotification, ) { @GetMapping("") @Operation(summary = "Get all prior notifications") @@ -290,4 +291,26 @@ class PriorNotificationController( return PriorNotificationDetailDataOutput.fromPriorNotification(updatedPriorNotification) } + + @PutMapping("/{reportId}/invalidate") + @Operation(summary = "Invalidate a prior notification by its `reportId`") + fun invalidate( + @PathParam("Logbook message `reportId`") + @PathVariable(name = "reportId") + reportId: String, + @Parameter(description = "Operation date (to optimize SQL query via Timescale).") + @RequestParam(name = "operationDate") + operationDate: ZonedDateTime, + @Parameter(description = "Is the prior notification manually created?") + @RequestParam(name = "isManuallyCreated") + isManuallyCreated: Boolean, + ): PriorNotificationDetailDataOutput { + val updatedPriorNotification = invalidatePriorNotification.execute( + reportId = reportId, + operationDate = operationDate, + isManuallyCreated = isManuallyCreated, + ) + + return PriorNotificationDetailDataOutput.fromPriorNotification(updatedPriorNotification) + } } 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 b0262a8915..6a6571951e 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 @@ -26,8 +26,8 @@ data class ManualPriorNotificationDataOutput( ) { companion object { fun fromPriorNotification(priorNotification: PriorNotification): ManualPriorNotificationDataOutput { - val logbookMessage = priorNotification.logbookMessageTyped.logbookMessage - val pnoMessage = priorNotification.logbookMessageTyped.typedMessage + val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage + val pnoMessage = priorNotification.logbookMessageAndValue.value val authorTrigram = requireNotNull(priorNotification.authorTrigram) { "`priorNotification.authorTrigram` is null." 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 f3d2ad94d4..301c3a5942 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 @@ -23,7 +23,7 @@ class PriorNotificationDetailDataOutput( val isVesselUnderCharter = requireNotNull(priorNotification.vessel) { "`priorNotification.vessel` is null." }.underCharter - val logbookMessage = priorNotification.logbookMessageTyped.logbookMessage + val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage val referenceReportId = requireNotNull(logbookMessage.getReferenceReportId()) { "`logbookMessage.getReferenceReportId()` returned null." } @@ -36,7 +36,7 @@ class PriorNotificationDetailDataOutput( isVesselUnderCharter, logbookMessage = logbookMessageDataOutput, state = priorNotification.state, - riskFactor = priorNotification.logbookMessageTyped.typedMessage.riskFactor, + riskFactor = priorNotification.logbookMessageAndValue.value.riskFactor, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationListItemDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationListItemDataOutput.kt index 6de4c91bc7..7f78e74564 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationListItemDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationListItemDataOutput.kt @@ -21,6 +21,7 @@ data class PriorNotificationListItemDataOutput( val isCorrection: Boolean, val isManuallyCreated: Boolean = false, val isVesselUnderCharter: Boolean?, + val isInvalidated: Boolean? = false, val onBoardCatches: List, // Used to optimize SQL query via Timescale when fetching a single prior notification from the list val operationDate: ZonedDateTime, @@ -50,14 +51,14 @@ data class PriorNotificationListItemDataOutput( val logger: Logger = LoggerFactory.getLogger(PriorNotificationListItemDataOutput::class.java) fun fromPriorNotification(priorNotification: PriorNotification): PriorNotificationListItemDataOutput? { - val logbookMessage = priorNotification.logbookMessageTyped.logbookMessage + val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage val referenceReportId = logbookMessage.getReferenceReportId() if (referenceReportId == null) { logger.warn("Prior notification has neither `reportId` nor `referencedReportId`: $priorNotification.") return null } - val message = priorNotification.logbookMessageTyped.typedMessage + val message = priorNotification.logbookMessageAndValue.value val acknowledgment = logbookMessage.acknowledgment?.let { AcknowledgmentDataOutput.fromAcknowledgment(it) } val onBoardCatches = message.catchOnboard @@ -80,6 +81,7 @@ data class PriorNotificationListItemDataOutput( fingerprint = priorNotification.fingerprint, isCorrection = logbookMessage.operationType === LogbookOperationType.COR, isManuallyCreated = priorNotification.isManuallyCreated, + isInvalidated = message.isInvalidated, isVesselUnderCharter = vessel.underCharter, onBoardCatches = onBoardCatches, operationDate = logbookMessage.operationDateTime, @@ -103,7 +105,7 @@ data class PriorNotificationListItemDataOutput( vesselLength = vessel.length, vesselMmsi = vessel.mmsi, vesselName = vessel.vesselName, - riskFactor = priorNotification.logbookMessageTyped.typedMessage.riskFactor, + riskFactor = priorNotification.logbookMessageAndValue.value.riskFactor, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/public_api/SentryController.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/public_api/SentryController.kt index e9ee076aa4..9926bbfb55 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/public_api/SentryController.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/public_api/SentryController.kt @@ -16,7 +16,7 @@ class SentryController(val sentryConfig: SentryConfig) { @GetMapping("/trigger_sentry_error") fun triggerError(): Map { try { - throw Exception("Sentry test error triggered from get request.") + throw IllegalArgumentException("Sentry test error triggered from get request.") } catch (e: Exception) { logger.error(e.message, e) } 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 0fad3cfd67..0e60e31d3f 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 @@ -142,8 +142,8 @@ data class LogbookReportEntity( val relatedLogbookMessages = relatedModels .map { it.toLogbookMessage(mapper) } .sortedBy { it.operationDateTime } - val consolidatedLogbookMessageTyped = referenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + val consolidatedLogbookMessageAndValue = referenceLogbookMessage + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) val updatedAt = relatedLogbookMessages.lastOrNull()?.operationDateTime ?: operationDateTime.atZone(UTC) // For practical reasons `vessel` can't be `null`, so we temporarily set it to "Navire inconnu" val vessel = UNKNOWN_VESSEL @@ -154,8 +154,8 @@ data class LogbookReportEntity( createdAt = operationDateTime.atZone(UTC), didNotFishAfterZeroNotice = false, isManuallyCreated = false, - logbookMessageTyped = consolidatedLogbookMessageTyped, - sentAt = consolidatedLogbookMessageTyped.logbookMessage.reportDateTime, + logbookMessageAndValue = consolidatedLogbookMessageAndValue, + sentAt = consolidatedLogbookMessageAndValue.logbookMessage.reportDateTime, updatedAt = updatedAt, // These props need to be calculated in the use case 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 8ce9479092..a9a7ad9442 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 @@ -64,8 +64,8 @@ data class ManualPriorNotificationEntity( isUpdate: Boolean = false, ): ManualPriorNotificationEntity { try { - val pnoLogbookMessage = priorNotification.logbookMessageTyped.logbookMessage - val pnoLogbookMessageValue = priorNotification.logbookMessageTyped.typedMessage + val pnoLogbookMessage = priorNotification.logbookMessageAndValue.logbookMessage + val pnoLogbookMessageValue = priorNotification.logbookMessageAndValue.value val createdAt = priorNotification.createdAt ?: ZonedDateTime.now() val updatedAt = if (isUpdate || priorNotification.updatedAt == null) { ZonedDateTime.now() @@ -129,14 +129,14 @@ data class ManualPriorNotificationEntity( ) // For practical reasons `vessel` can't be `null`, so we temporarily set it to "Navire inconnu" val vessel = UNKNOWN_VESSEL - val logbookMessageTyped = LogbookMessageTyped(pnoLogbookMessage, PNO::class.java) + val logbookMessageAndValue = LogbookMessageAndValue(pnoLogbookMessage, PNO::class.java) return PriorNotification( authorTrigram = authorTrigram, createdAt = createdAt, didNotFishAfterZeroNotice = didNotFishAfterZeroNotice, isManuallyCreated = true, - logbookMessageTyped = logbookMessageTyped, + logbookMessageAndValue = logbookMessageAndValue, reportId = reportId, sentAt = sentAt, updatedAt = updatedAt, 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 9ea54fdc59..9a35e273d8 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 @@ -2,7 +2,7 @@ package fr.gouv.cnsp.monitorfish.infrastructure.database.repositories import com.fasterxml.jackson.databind.ObjectMapper import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType import fr.gouv.cnsp.monitorfish.domain.entities.logbook.VoyageDatesAndTripNumber import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO @@ -22,7 +22,6 @@ import org.springframework.dao.EmptyResultDataAccessException import org.springframework.data.domain.PageRequest import org.springframework.data.jpa.repository.Modifying import org.springframework.stereotype.Repository -import java.time.ZoneOffset import java.time.ZoneOffset.UTC import java.time.ZonedDateTime @@ -96,8 +95,8 @@ class JpaLogbookReportRepository( null } }.filter { - it.logbookMessageTyped.typedMessage.isInVerificationScope == true && - it.logbookMessageTyped.typedMessage.isVerified == false + it.logbookMessageAndValue.value.isInVerificationScope == true && + it.logbookMessageAndValue.value.isVerified == false } } @@ -350,9 +349,9 @@ class JpaLogbookReportRepository( @Modifying @Transactional - override fun savePriorNotification(logbookMessageTyped: LogbookMessageTyped): PriorNotification { + override fun savePriorNotification(logbookMessageAndValue: LogbookMessageAndValue): PriorNotification { return dbLogbookReportRepository - .save(LogbookReportEntity.fromLogbookMessage(objectMapper, logbookMessageTyped.logbookMessage)) + .save(LogbookReportEntity.fromLogbookMessage(objectMapper, logbookMessageAndValue.logbookMessage)) .toPriorNotification(objectMapper, emptyList()) } @@ -367,7 +366,7 @@ class JpaLogbookReportRepository( val logbookReportEntities = dbLogbookReportRepository.findEnrichedPnoReferenceAndRelatedOperationsByReportId( reportId, - operationDate.withZoneSameInstant(ZoneOffset.UTC).toString(), + operationDate.withZoneSameInstant(UTC).toString(), ) if (logbookReportEntities.isEmpty()) { throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND) @@ -394,7 +393,7 @@ class JpaLogbookReportRepository( val logbookReportEntities = dbLogbookReportRepository.findEnrichedPnoReferenceAndRelatedOperationsByReportId( reportId, - operationDate.withZoneSameInstant(ZoneOffset.UTC).toString(), + operationDate.withZoneSameInstant(UTC).toString(), ) if (logbookReportEntities.isEmpty()) { throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND) @@ -424,6 +423,32 @@ class JpaLogbookReportRepository( } } + @Transactional + override fun invalidate(reportId: String, operationDate: ZonedDateTime) { + val logbookReportEntities = + dbLogbookReportRepository.findEnrichedPnoReferenceAndRelatedOperationsByReportId( + reportId, + operationDate.withZoneSameInstant(UTC).toString(), + ) + if (logbookReportEntities.isEmpty()) { + throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND) + } + + // We need to update both DAT and related COR operations (which also covers orphan COR cases) + logbookReportEntities + .filter { it.operationType in listOf(LogbookOperationType.DAT, LogbookOperationType.COR) } + .map { logbookReportEntity -> + val pnoMessage = objectMapper.readValue(logbookReportEntity.message, PNO::class.java) + pnoMessage.isInvalidated = true + + val nextMessage = objectMapper.writeValueAsString(pnoMessage) + + val updatedEntity = logbookReportEntity.copy(message = nextMessage) + + dbLogbookReportRepository.save(updatedEntity) + } + } + private fun getAllMessagesExceptionMessage(internalReferenceNumber: String) = "No messages found for the vessel. (internalReferenceNumber: \"$internalReferenceNumber\")" diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepository.kt index 6a4a0b5679..b7014bcd2b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepository.kt @@ -16,9 +16,6 @@ import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Repository import org.springframework.transaction.annotation.Transactional import java.time.ZonedDateTime -import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.jvm.isAccessible -import kotlin.reflect.jvm.javaField @Repository class JpaManualPriorNotificationRepository( @@ -93,25 +90,32 @@ class JpaManualPriorNotificationRepository( @Transactional @CacheEvict(value = ["manual_pno_to_verify"], allEntries = true) override fun updateState(reportId: String, isBeingSent: Boolean, isVerified: Boolean) { - val manualPriorNotificationEntity = + val manualPriorNotification = dbManualPriorNotificationRepository.findByReportId(reportId) ?: throw BackendUsageException( BackendUsageErrorCode.NOT_FOUND, ) - val nextPnoValue: PNO = manualPriorNotificationEntity.value + val nextPnoValue: PNO = manualPriorNotification.value nextPnoValue.isBeingSent = isBeingSent nextPnoValue.isVerified = isVerified - // We use a reflection to update the entity `value` prop since it's immutable - val pnoValueRefection = ManualPriorNotificationEntity::class.declaredMemberProperties - .find { it.name == "value" } - pnoValueRefection?.let { - it.isAccessible = true - val field = it.javaField - field?.isAccessible = true - field?.set(manualPriorNotificationEntity, nextPnoValue) - } + val updatedManualPriorNotification = manualPriorNotification.copy(value = nextPnoValue) + + dbManualPriorNotificationRepository.save(updatedManualPriorNotification) + } + + @Transactional + override fun invalidate(reportId: String) { + val manualPriorNotification = + dbManualPriorNotificationRepository.findByReportId(reportId) ?: throw BackendUsageException( + BackendUsageErrorCode.NOT_FOUND, + ) + + val nextPnoValue: PNO = manualPriorNotification.value + nextPnoValue.isInvalidated = true + + val updatedManualPriorNotification = manualPriorNotification.copy(value = nextPnoValue) - dbManualPriorNotificationRepository.save(manualPriorNotificationEntity) + dbManualPriorNotificationRepository.save(updatedManualPriorNotification) } } 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 e3bd516829..7442118925 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 @@ -16,7 +16,7 @@ UPDATE manual_prior_notifications SET value = JSONB_SET(value, '{predictedArriva 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,"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, 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}'); 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'; 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 d02fc02270..12134101c5 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 @@ -277,6 +277,7 @@ "faoZone": null, "isBeingSent": false, "isInVerificationScope": false, + "isInvalidated": true, "isSent": false, "isVerified": true, "note": 0, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageUTests.kt index f6799a3cac..fb40961f83 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessageUTests.kt @@ -40,7 +40,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should set acknowledge to successful with one successful RET message`() { + fun `toConsolidatedLogbookMessageAndValue Should set acknowledge to successful with one successful RET message`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -57,7 +57,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.acknowledgment?.isSuccess).isTrue() @@ -66,7 +66,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should set acknowledge to successful with multiple RET messages of which one is successful, no matter the (historical) order`() { + fun `toConsolidatedLogbookMessageAndValue Should set acknowledge to successful with multiple RET messages of which one is successful, no matter the (historical) order`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -95,7 +95,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.acknowledgment?.isSuccess).isTrue() @@ -104,7 +104,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should set acknowledge to not successful with one unsuccessful RET message`() { + fun `toConsolidatedLogbookMessageAndValue Should set acknowledge to not successful with one unsuccessful RET message`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -121,7 +121,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.acknowledgment?.isSuccess).isFalse() @@ -130,7 +130,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should set acknowledge to (most recent) not successful with multiple unsucessful RET message`() { + fun `toConsolidatedLogbookMessageAndValue Should set acknowledge to (most recent) not successful with multiple unsucessful RET message`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -153,7 +153,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.acknowledgment?.isSuccess).isFalse() @@ -164,7 +164,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessageReversed = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages.reversed(), PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages.reversed(), PNO::class.java) // Then assertThat(enrichedLogbookMessageReversed.logbookMessage.acknowledgment?.isSuccess).isFalse() @@ -175,7 +175,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should set acknowledge to successful when it comes from FLUX flow`() { + fun `toConsolidatedLogbookMessageAndValue Should set acknowledge to successful when it comes from FLUX flow`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -186,7 +186,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(emptyList(), PNO::class.java) + .toConsolidatedLogbookMessageAndValue(emptyList(), PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.acknowledgment?.isSuccess).isTrue() @@ -195,7 +195,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should set acknowledge to successful when it was generated via VISIOCaptures app`() { + fun `toConsolidatedLogbookMessageAndValue Should set acknowledge to successful when it was generated via VISIOCaptures app`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -206,7 +206,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(emptyList(), PNO::class.java) + .toConsolidatedLogbookMessageAndValue(emptyList(), PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.acknowledgment?.isSuccess).isTrue() @@ -215,7 +215,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should flag it as corrected from an orphan COR message`() { + fun `toConsolidatedLogbookMessageAndValue Should flag it as corrected from an orphan COR message`() { // Given val missingDatLogbookMessageReportId = UUID.randomUUID().toString() val refenceLogbookMessage = getFakeLogbookMessage( @@ -226,7 +226,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(emptyList(), PNO::class.java) + .toConsolidatedLogbookMessageAndValue(emptyList(), PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.reportDateTime) @@ -236,7 +236,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should use the most recent COR message as base and flag it corrected from a DAT with multiple COR messages`() { + fun `toConsolidatedLogbookMessageAndValue Should use the most recent COR message as base and flag it corrected from a DAT with multiple COR messages`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -262,7 +262,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.reportDateTime).isEqualTo( @@ -273,7 +273,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessageReversed = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages.reversed(), PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages.reversed(), PNO::class.java) // Then assertThat(enrichedLogbookMessageReversed.logbookMessage.reportDateTime) @@ -283,7 +283,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should flag it as deleted from a DAT with a DEL message`() { + fun `toConsolidatedLogbookMessageAndValue Should flag it as deleted from a DAT with a DEL message`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -299,7 +299,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.reportDateTime) @@ -309,7 +309,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should flag it as corrected and deleted from an orphan COR with a DEL message`() { + fun `toConsolidatedLogbookMessageAndValue Should flag it as corrected and deleted from an orphan COR with a DEL message`() { // Given val missingDatLogbookMessageReportId = UUID.randomUUID().toString() val refenceLogbookMessage = getFakeLogbookMessage( @@ -327,7 +327,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.reportDateTime) @@ -337,7 +337,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should use the most recent COR message as base and flag it corrected and deleted from a DAT with COR and DEL messages`() { + fun `toConsolidatedLogbookMessageAndValue Should use the most recent COR message as base and flag it corrected and deleted from a DAT with COR and DEL messages`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -363,7 +363,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.reportDateTime) @@ -373,7 +373,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should use the most recent COR message as base and associate its RET from a DAT, even with a later DAT-RET message`() { + fun `toConsolidatedLogbookMessageAndValue Should use the most recent COR message as base and associate its RET from a DAT, even with a later DAT-RET message`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -418,7 +418,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.reportDateTime) @@ -432,7 +432,7 @@ class LogbookMessageUTests { } @Test - fun `toEnrichedLogbookMessageTyped Should use the most recent COR message as base and skip acknowledgement flagging without a COR-related RET`() { + fun `toConsolidatedLogbookMessageAndValue Should use the most recent COR message as base and skip acknowledgement flagging without a COR-related RET`() { // Given val refenceLogbookMessage = getFakeLogbookMessage( LogbookOperationType.DAT, @@ -455,7 +455,7 @@ class LogbookMessageUTests { // When val enrichedLogbookMessage = refenceLogbookMessage - .toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java) + .toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java) // Then assertThat(enrichedLogbookMessage.logbookMessage.reportDateTime) diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotificationUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotificationUTests.kt index 6cb7c40e1e..b6af92652f 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotificationUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotificationUTests.kt @@ -66,7 +66,7 @@ class CreateOrUpdateManualPriorNotificationUTests { given( getPriorNotification.execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, true, ), ).willReturn(fakePriorNotification) 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 efd5fdf1d8..4d6c0d6fc6 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 @@ -3,7 +3,7 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification import com.nhaarman.mockitokotlin2.given import fr.gouv.cnsp.monitorfish.domain.entities.facade.SeafrontGroup import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookTransmissionFormat import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO @@ -52,7 +52,7 @@ class GetNumberToVerifyUTests { createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, - logbookMessageTyped = LogbookMessageTyped( + logbookMessageAndValue = LogbookMessageAndValue( clazz = PNO::class.java, logbookMessage = LogbookMessage( id = 1, @@ -85,7 +85,7 @@ class GetNumberToVerifyUTests { createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, - logbookMessageTyped = LogbookMessageTyped( + logbookMessageAndValue = LogbookMessageAndValue( clazz = PNO::class.java, logbookMessage = LogbookMessage( id = 1, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationUTests.kt index 82c9fc23d9..1585ea8421 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationUTests.kt @@ -2,7 +2,7 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification import com.nhaarman.mockitokotlin2.given import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookTransmissionFormat import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO @@ -52,7 +52,7 @@ class GetPriorNotificationUTests { given( logbookReportRepository.findPriorNotificationByReportId( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, ), ) .willReturn(fakePriorNotification) @@ -70,14 +70,14 @@ class GetPriorNotificationUTests { vesselRepository, ).execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, false, ) // Then - assertThat(result.logbookMessageTyped.logbookMessage.reportId) + assertThat(result.logbookMessageAndValue.logbookMessage.reportId) .isEqualTo(fakePriorNotification.reportId!!) - assertThat(result.logbookMessageTyped.logbookMessage.referencedReportId).isNull() + assertThat(result.logbookMessageAndValue.logbookMessage.referencedReportId).isNull() } @Test @@ -85,7 +85,7 @@ class GetPriorNotificationUTests { val fakeLogbookMessageReferenceReportId = "FAKE_REPORT_ID_1" val fakePriorNotification = PriorNotificationFaker.fakePriorNotification().copy( reportId = null, - logbookMessageTyped = LogbookMessageTyped( + logbookMessageAndValue = LogbookMessageAndValue( clazz = PNO::class.java, logbookMessage = LogbookMessage( id = 2, @@ -110,7 +110,7 @@ class GetPriorNotificationUTests { given( logbookReportRepository.findPriorNotificationByReportId( fakeLogbookMessageReferenceReportId, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, ), ) .willReturn(fakePriorNotification) @@ -128,14 +128,14 @@ class GetPriorNotificationUTests { vesselRepository, ).execute( fakeLogbookMessageReferenceReportId, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, false, ) // Then assertThat(result.reportId).isNull() - assertThat(result.logbookMessageTyped.logbookMessage.reportId).isNull() - assertThat(result.logbookMessageTyped.logbookMessage.referencedReportId) + assertThat(result.logbookMessageAndValue.logbookMessage.reportId).isNull() + assertThat(result.logbookMessageAndValue.logbookMessage.referencedReportId) .isEqualTo(fakeLogbookMessageReferenceReportId) } } diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsITests.kt index 3ed3321fe1..fb36e2dd96 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsITests.kt @@ -79,9 +79,9 @@ class GetPriorNotificationsITests : AbstractDBTests() { // Then val firstPriorNotificationWithNonNullArrivalDate = result.data - .first { it.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc != null } + .first { it.logbookMessageAndValue.value.predictedArrivalDatetimeUtc != null } assertThat( - firstPriorNotificationWithNonNullArrivalDate.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc, + firstPriorNotificationWithNonNullArrivalDate.logbookMessageAndValue.value.predictedArrivalDatetimeUtc, ) .isBefore(ZonedDateTime.parse("2024-01-01T00:00:00Z")) assertThat(result.data).hasSizeGreaterThan(0) @@ -108,9 +108,9 @@ class GetPriorNotificationsITests : AbstractDBTests() { // Then val firstPriorNotificationWithNonNullArrivalDate = result.data - .first { it.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc != null } + .first { it.logbookMessageAndValue.value.predictedArrivalDatetimeUtc != null } assertThat( - firstPriorNotificationWithNonNullArrivalDate.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc, + firstPriorNotificationWithNonNullArrivalDate.logbookMessageAndValue.value.predictedArrivalDatetimeUtc, ) .isAfter(ZonedDateTime.now().minusHours(1)) assertThat(result.data).hasSizeGreaterThan(0) @@ -137,9 +137,9 @@ class GetPriorNotificationsITests : AbstractDBTests() { // Then val firstPriorNotificationWithNonNullLandingDate = result.data - .first { it.logbookMessageTyped.typedMessage.predictedLandingDatetimeUtc != null } + .first { it.logbookMessageAndValue.value.predictedLandingDatetimeUtc != null } assertThat( - firstPriorNotificationWithNonNullLandingDate.logbookMessageTyped.typedMessage.predictedLandingDatetimeUtc, + firstPriorNotificationWithNonNullLandingDate.logbookMessageAndValue.value.predictedLandingDatetimeUtc, ) .isEqualTo(ZonedDateTime.parse("2023-01-01T10:30:00Z")) assertThat(result.data).hasSizeGreaterThan(0) @@ -166,9 +166,9 @@ class GetPriorNotificationsITests : AbstractDBTests() { // Then val firstPriorNotificationWithNonNullLandingDate = result.data - .first { it.logbookMessageTyped.typedMessage.predictedLandingDatetimeUtc != null } + .first { it.logbookMessageAndValue.value.predictedLandingDatetimeUtc != null } assertThat( - firstPriorNotificationWithNonNullLandingDate.logbookMessageTyped.typedMessage.predictedLandingDatetimeUtc, + firstPriorNotificationWithNonNullLandingDate.logbookMessageAndValue.value.predictedLandingDatetimeUtc, ) .isAfter(ZonedDateTime.now().plusHours(4)) assertThat(result.data).hasSizeGreaterThan(0) @@ -196,7 +196,7 @@ class GetPriorNotificationsITests : AbstractDBTests() { // Then val firstPriorNotificationWithNonNullPort = result.data.first { it.port != null } assertThat(firstPriorNotificationWithNonNullPort.port!!.name).isEqualTo("Al Jazeera Port") - assertThat(firstPriorNotificationWithNonNullPort.logbookMessageTyped.typedMessage.port).isEqualTo("AEJAZ") + assertThat(firstPriorNotificationWithNonNullPort.logbookMessageAndValue.value.port).isEqualTo("AEJAZ") assertThat(result.data).hasSizeGreaterThan(0) } @@ -222,7 +222,7 @@ class GetPriorNotificationsITests : AbstractDBTests() { // Then val firstPriorNotificationWithNonNullPort = result.data.first { it.port != null } assertThat(firstPriorNotificationWithNonNullPort.port!!.name).isEqualTo("Vannes") - assertThat(firstPriorNotificationWithNonNullPort.logbookMessageTyped.typedMessage.port).isEqualTo("FRVNE") + assertThat(firstPriorNotificationWithNonNullPort.logbookMessageAndValue.value.port).isEqualTo("FRVNE") assertThat(result.data).hasSizeGreaterThan(0) } @@ -250,9 +250,9 @@ class GetPriorNotificationsITests : AbstractDBTests() { // We don't test the `.vessel.VesselName` since in the real world, // the vessel name may have changed between the logbook message date and now assertThat(firstPriorNotificationWithKnownVessel.vessel!!.internalReferenceNumber).isEqualTo("CFR125") - assertThat(firstPriorNotificationWithKnownVessel.logbookMessageTyped.logbookMessage.internalReferenceNumber) + assertThat(firstPriorNotificationWithKnownVessel.logbookMessageAndValue.logbookMessage.internalReferenceNumber) .isEqualTo("CFR125") - assertThat(firstPriorNotificationWithKnownVessel.logbookMessageTyped.logbookMessage.vesselName) + assertThat(firstPriorNotificationWithKnownVessel.logbookMessageAndValue.logbookMessage.vesselName) .isEqualTo("BEAU SÉANT") assertThat(result.data).hasSizeGreaterThan(0) } @@ -281,9 +281,9 @@ class GetPriorNotificationsITests : AbstractDBTests() { // We don't test the `.vessel.VesselName` since in the real world, // the vessel name may have changed between the logbook message date and now assertThat(firstPriorNotificationWithKnownVessel.vessel!!.internalReferenceNumber).isEqualTo("CFR120") - assertThat(firstPriorNotificationWithKnownVessel.logbookMessageTyped.logbookMessage.internalReferenceNumber) + assertThat(firstPriorNotificationWithKnownVessel.logbookMessageAndValue.logbookMessage.internalReferenceNumber) .isEqualTo("CFR120") - assertThat(firstPriorNotificationWithKnownVessel.logbookMessageTyped.logbookMessage.vesselName) + assertThat(firstPriorNotificationWithKnownVessel.logbookMessageAndValue.logbookMessage.vesselName) .isEqualTo("VIVA L'ITALIA") assertThat(result.data).hasSizeGreaterThan(0) } @@ -309,8 +309,8 @@ class GetPriorNotificationsITests : AbstractDBTests() { // Then val firstPriorNotificationWithNonNullRiskFactor = - result.data.first { it.logbookMessageTyped.typedMessage.riskFactor != null } - assertThat(firstPriorNotificationWithNonNullRiskFactor.logbookMessageTyped.typedMessage.riskFactor!!).isEqualTo( + result.data.first { it.logbookMessageAndValue.value.riskFactor != null } + assertThat(firstPriorNotificationWithNonNullRiskFactor.logbookMessageAndValue.value.riskFactor!!).isEqualTo( 1.5, ) assertThat(result.data).hasSizeGreaterThan(0) @@ -337,8 +337,8 @@ class GetPriorNotificationsITests : AbstractDBTests() { // Then val firstPriorNotificationWithNonNullRiskFactor = - result.data.first { it.logbookMessageTyped.typedMessage.riskFactor != null } - assertThat(firstPriorNotificationWithNonNullRiskFactor.logbookMessageTyped.typedMessage.riskFactor!!).isEqualTo( + result.data.first { it.logbookMessageAndValue.value.riskFactor != null } + assertThat(firstPriorNotificationWithNonNullRiskFactor.logbookMessageAndValue.value.riskFactor!!).isEqualTo( 3.9, ) assertThat(result.data).hasSizeGreaterThan(0) 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 158f72e43c..06b303d561 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 @@ -3,7 +3,7 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification import com.nhaarman.mockitokotlin2.given import fr.gouv.cnsp.monitorfish.domain.entities.facade.SeafrontGroup import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookTransmissionFormat import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO @@ -67,7 +67,7 @@ class GetPriorNotificationsUTests { createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, - logbookMessageTyped = LogbookMessageTyped( + logbookMessageAndValue = LogbookMessageAndValue( clazz = PNO::class.java, logbookMessage = LogbookMessage( id = 1, @@ -100,7 +100,7 @@ class GetPriorNotificationsUTests { createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, - logbookMessageTyped = LogbookMessageTyped( + logbookMessageAndValue = LogbookMessageAndValue( clazz = PNO::class.java, logbookMessage = LogbookMessage( id = 1, @@ -151,10 +151,10 @@ class GetPriorNotificationsUTests { // Then assertThat(result.data).hasSize(2) - assertThat(result.data[0].logbookMessageTyped.logbookMessage.reportId).isEqualTo( + assertThat(result.data[0].logbookMessageAndValue.logbookMessage.reportId).isEqualTo( "FAKE_REPORT_ID_1", ) - assertThat(result.data[1].logbookMessageTyped.logbookMessage.reportId).isEqualTo( + assertThat(result.data[1].logbookMessageAndValue.logbookMessage.reportId).isEqualTo( "FAKE_REPORT_ID_2_COR", ) } 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/UpdatePriorNotificationNoteUTests.kt index e908ec7d6e..ddf7c865d9 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/UpdatePriorNotificationNoteUTests.kt @@ -29,7 +29,7 @@ class UpdatePriorNotificationNoteUTests { given( getPriorNotification.execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, false, ), ).willReturn(fakePriorNotification) @@ -41,7 +41,7 @@ class UpdatePriorNotificationNoteUTests { getPriorNotification, ).execute( reportId = fakePriorNotification.reportId!!, - operationDate = fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + operationDate = fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, note = null, ) diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/VerifyAndSendPriorNotificationUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/VerifyAndSendPriorNotificationUTests.kt index 58a286e870..f1db83be01 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/VerifyAndSendPriorNotificationUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/VerifyAndSendPriorNotificationUTests.kt @@ -29,7 +29,7 @@ class VerifyAndSendPriorNotificationUTests { given( logbookReportRepository.findPriorNotificationByReportId( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, ), ) .willReturn(fakePriorNotification) @@ -38,7 +38,7 @@ class VerifyAndSendPriorNotificationUTests { given( getPriorNotification.execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, false, ), ) @@ -51,7 +51,7 @@ class VerifyAndSendPriorNotificationUTests { getPriorNotification, ).execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, false, ) @@ -67,7 +67,7 @@ class VerifyAndSendPriorNotificationUTests { given( logbookReportRepository.findPriorNotificationByReportId( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, ), ) .willReturn(null) @@ -76,7 +76,7 @@ class VerifyAndSendPriorNotificationUTests { given( getPriorNotification.execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, true, ), ) @@ -89,7 +89,7 @@ class VerifyAndSendPriorNotificationUTests { getPriorNotification, ).execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, true, ) 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 450dfe1bdc..3f438508ce 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 @@ -1,6 +1,6 @@ package fr.gouv.cnsp.monitorfish.fakers -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification import java.time.ZonedDateTime @@ -14,7 +14,7 @@ class PriorNotificationFaker { createdAt = ZonedDateTime.now(), didNotFishAfterZeroNotice = false, isManuallyCreated = false, - logbookMessageTyped = LogbookMessageTyped( + logbookMessageAndValue = LogbookMessageAndValue( clazz = PNO::class.java, logbookMessage = LogbookMessageFaker.fakePnoLogbookMessage(index), ), 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 ea604344f4..6f3192f5b3 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 @@ -61,6 +61,9 @@ class PriorNotificationControllerITests { @MockBean private lateinit var updatePriorNotificationNote: UpdatePriorNotificationNote + @MockBean + private lateinit var invalidatePriorNotification: InvalidatePriorNotification + @Autowired private lateinit var objectMapper: ObjectMapper @@ -159,7 +162,7 @@ class PriorNotificationControllerITests { given( getPriorNotification.execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, true, ), ) @@ -168,7 +171,7 @@ class PriorNotificationControllerITests { // When api.perform( get( - "/bff/v1/prior_notifications/manual/${fakePriorNotification.reportId!!}?operationDate=${fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime}", + "/bff/v1/prior_notifications/manual/${fakePriorNotification.reportId!!}?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}", ), ) // Then @@ -308,7 +311,7 @@ class PriorNotificationControllerITests { given( getPriorNotification.execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, false, ), ) @@ -317,7 +320,7 @@ class PriorNotificationControllerITests { // When api.perform( get( - "/bff/v1/prior_notifications/${fakePriorNotification.reportId!!}?operationDate=${fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime}&isManuallyCreated=false", + "/bff/v1/prior_notifications/${fakePriorNotification.reportId!!}?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}&isManuallyCreated=false", ), ) // Then @@ -333,7 +336,7 @@ class PriorNotificationControllerITests { given( verifyAndSendPriorNotification.execute( fakePriorNotification.reportId!!, - fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime, + fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime, false, ), ) @@ -342,7 +345,7 @@ class PriorNotificationControllerITests { // When api.perform( post( - "/bff/v1/prior_notifications/${fakePriorNotification.reportId!!}/verify_and_send?operationDate=${fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime}&isManuallyCreated=false", + "/bff/v1/prior_notifications/${fakePriorNotification.reportId!!}/verify_and_send?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}&isManuallyCreated=false", ), ) // Then @@ -353,7 +356,7 @@ class PriorNotificationControllerITests { @Test fun `update Should update a prior notification note by its reportId`() { val fakePriorNotification = PriorNotificationFaker.fakePriorNotification() - fakePriorNotification.logbookMessageTyped.typedMessage.note = "Test !" + fakePriorNotification.logbookMessageAndValue.value.note = "Test !" // Given given( @@ -373,7 +376,7 @@ class PriorNotificationControllerITests { ) api.perform( put( - "/bff/v1/prior_notifications/${fakePriorNotification.reportId!!}/note?operationDate=${fakePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime}", + "/bff/v1/prior_notifications/${fakePriorNotification.reportId!!}/note?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}", ) .contentType(MediaType.APPLICATION_JSON) .content(requestBody), @@ -384,7 +387,39 @@ class PriorNotificationControllerITests { .andExpect( jsonPath( "$.logbookMessage.message.note", - equalTo(fakePriorNotification.logbookMessageTyped.typedMessage.note), + equalTo(fakePriorNotification.logbookMessageAndValue.value.note), + ), + ) + } + + @Test + fun `invalidate Should invalidate a prior notification by its reportId`() { + val fakePriorNotification = PriorNotificationFaker.fakePriorNotification() + fakePriorNotification.logbookMessageAndValue.value.isInvalidated = null + + // Given + given( + invalidatePriorNotification.execute( + reportId = anyOrNull(), + operationDate = anyOrNull(), + isManuallyCreated = anyOrNull(), + ), + ) + .willReturn(fakePriorNotification) + + // When + api.perform( + put( + "/bff/v1/prior_notifications/${fakePriorNotification.reportId!!}/invalidate?operationDate=${fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime}&isManuallyCreated=false", + ), + ) + // Then + .andExpect(status().isOk) + .andExpect(jsonPath("$.id", equalTo(fakePriorNotification.reportId))) + .andExpect( + jsonPath( + "$.logbookMessage.message.isInvalidated", + equalTo(fakePriorNotification.logbookMessageAndValue.value.isInvalidated), ), ) } 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 1e832f0cfb..ee6e1db0c2 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 @@ -556,7 +556,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeGreaterThan(0) val resultVessels = result.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(resultVessels).hasSize(result.size) @@ -582,7 +582,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(firstResult).hasSizeGreaterThan(0) assertThat( firstResult.all { - it.logbookMessageTyped.logbookMessage.id in expectedLogbookReportIdsWithOneOrMoreReportings + it.logbookMessageAndValue.logbookMessage.id in expectedLogbookReportIdsWithOneOrMoreReportings }, ).isTrue() @@ -600,7 +600,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(secondResult).hasSizeGreaterThan(0) assertThat( secondResult.none { - it.logbookMessageTyped.logbookMessage.id in expectedLogbookReportIdsWithOneOrMoreReportings + it.logbookMessageAndValue.logbookMessage.id in expectedLogbookReportIdsWithOneOrMoreReportings }, ).isTrue() } @@ -622,7 +622,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(firstResult).hasSizeGreaterThan(0) val firstResultVessels = firstResult.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(firstResultVessels).hasSize(firstResult.size) @@ -642,7 +642,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(secondResult).hasSizeGreaterThan(0) val secondResultVessels = secondResult.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(secondResultVessels).hasSize(secondResult.size) @@ -666,7 +666,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(firstResult).hasSizeGreaterThan(0) val firstResultRiskFactors = firstResult.mapNotNull { jpaRiskFactorRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(firstResultRiskFactors).hasSize(firstResult.size) @@ -690,7 +690,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(secondResult).hasSizeGreaterThan(0) val secondResultRiskFactors = secondResult.mapNotNull { jpaRiskFactorRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(secondResultRiskFactors).hasSize(secondResult.size) @@ -718,7 +718,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeGreaterThan(0) assertThat( result.all { - listOf("FRSML", "FRVNE").contains(it.logbookMessageTyped.typedMessage.port) + listOf("FRSML", "FRVNE").contains(it.logbookMessageAndValue.value.port) }, ).isTrue() } @@ -740,7 +740,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(firstResult).hasSizeGreaterThan(0) val firstResultVessels = firstResult.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(firstResultVessels).hasSize(firstResult.size) @@ -760,7 +760,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(secondResult).hasSizeGreaterThan(0) val secondResultVessels = secondResult.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(secondResultVessels).hasSize(secondResult.size) @@ -784,7 +784,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeGreaterThan(0) assertThat( result.all { - it.logbookMessageTyped.typedMessage.catchOnboard + it.logbookMessageAndValue.value.catchOnboard .any { catch -> listOf("COD", "HKE").contains(catch.species) } }, ).isTrue() @@ -807,7 +807,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeGreaterThan(0) assertThat( result.all { - it.logbookMessageTyped.typedMessage.pnoTypes + it.logbookMessageAndValue.value.pnoTypes .any { type -> listOf("Préavis type A", "Préavis type C").contains(type.name) } }, ).isTrue() @@ -830,7 +830,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeGreaterThan(0) assertThat( result.all { - it.logbookMessageTyped.logbookMessage.tripSegments!! + it.logbookMessageAndValue.logbookMessage.tripSegments!! .any { tripSegment -> listOf("SWW06", "NWW03").contains( tripSegment.code, @@ -857,7 +857,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeGreaterThan(0) assertThat( result.all { - it.logbookMessageTyped.logbookMessage.tripGears!! + it.logbookMessageAndValue.logbookMessage.tripGears!! .any { tripGear -> listOf("OTT", "TB").contains(tripGear.gear) } }, ).isTrue() @@ -879,7 +879,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(firstResult).hasSizeGreaterThan(0) assertThat( firstResult.all { - it.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc!! + it.logbookMessageAndValue.value.predictedArrivalDatetimeUtc!! .isAfter(ZonedDateTime.parse("2024-01-01T00:00:00Z")) }, ).isTrue() @@ -897,7 +897,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(secondResult).hasSizeGreaterThan(0) assertThat( secondResult.all { - it.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc!! + it.logbookMessageAndValue.value.predictedArrivalDatetimeUtc!! .isBefore(ZonedDateTime.parse("2024-01-01T00:00:00Z")) }, ).isTrue() @@ -921,19 +921,19 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeGreaterThan(0) assertThat( result.all { - it.logbookMessageTyped.typedMessage.pnoTypes + it.logbookMessageAndValue.value.pnoTypes .any { type -> listOf("Préavis type A", "Préavis type C").contains(type.name) } }, ).isTrue() assertThat( result.all { - it.logbookMessageTyped.logbookMessage.tripGears!! + it.logbookMessageAndValue.logbookMessage.tripGears!! .any { tripGear -> listOf("OTT", "TB").contains(tripGear.gear) } }, ).isTrue() assertThat( result.all { - it.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc!! + it.logbookMessageAndValue.value.predictedArrivalDatetimeUtc!! .isAfter(ZonedDateTime.parse("2024-01-01T00:00:00Z")) }, ).isTrue() @@ -947,8 +947,8 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { // Then assertThat(result).hasSizeGreaterThan(0) - assertThat(result.filter { it.logbookMessageTyped.typedMessage.isVerified == false }).hasSize(1) - assertThat(result.filter { it.logbookMessageTyped.typedMessage.isInVerificationScope == true }).hasSize(1) + assertThat(result.filter { it.logbookMessageAndValue.value.isVerified == false }).hasSize(1) + assertThat(result.filter { it.logbookMessageAndValue.value.isInVerificationScope == true }).hasSize(1) } @Test @@ -1133,6 +1133,28 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { assertThat((updatedCorReport.message as PNO).isSent).isEqualTo(false) } + @Test + @Transactional + fun `invalidate Should invalidate for an existing PNO logbook report`() { + // Given + val currentDatReport = jpaLogbookReportRepository.findById(109) + assertThat((currentDatReport.message as PNO).isInvalidated).isNull() + val currentCorReport = jpaLogbookReportRepository.findById(1109) + assertThat((currentCorReport.message as PNO).isInvalidated).isNull() + + // When + jpaLogbookReportRepository.invalidate( + "FAKE_OPERATION_109", + ZonedDateTime.now().minusMinutes(15), + ) + + // Then + val updatedDatReport = jpaLogbookReportRepository.findById(109) + assertThat((updatedDatReport.message as PNO).isInvalidated).isEqualTo(true) + val updatedCorReport = jpaLogbookReportRepository.findById(1109) + assertThat((updatedCorReport.message as PNO).isInvalidated).isEqualTo(true) + } + companion object { private fun getFakeLogbookReportModel( operationType: LogbookOperationType, 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 25db72cfab..44965d0f7b 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 @@ -2,8 +2,8 @@ package fr.gouv.cnsp.monitorfish.infrastructure.database.repositories import com.neovisionaries.i18n.CountryCode import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage +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.LogbookMessageTyped import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification @@ -62,7 +62,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeBetween(1, allManualPriorNotificationsLength - 1) val resultVessels = result.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(resultVessels).hasSize(result.size) @@ -116,7 +116,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(firstResult).hasSize(allManualPriorNotificationsLength) val firstResultVessels = firstResult.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(firstResultVessels).hasSize(firstResult.size) @@ -145,7 +145,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(firstResult).hasSizeBetween(1, allManualPriorNotificationsLength - 1) val firstResultRiskFactors = firstResult.mapNotNull { jpaRiskFactorRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(firstResultRiskFactors).hasSize(firstResult.size) @@ -165,7 +165,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(secondResult).hasSizeBetween(1, allManualPriorNotificationsLength - 1) val secondResultRiskFactors = secondResult.mapNotNull { jpaRiskFactorRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(secondResultRiskFactors).hasSize(secondResult.size) @@ -189,7 +189,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( result.all { - listOf("FRNCE", "FRVNE").contains(it.logbookMessageTyped.typedMessage.port) + listOf("FRNCE", "FRVNE").contains(it.logbookMessageAndValue.value.port) }, ).isTrue() } @@ -206,11 +206,11 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { // Then assertThat(firstResult).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( - firstResult.all { it.logbookMessageTyped.logbookMessage.vesselName == "NAVIRE RENOMMÉ (ANCIEN NOM)" }, + firstResult.all { it.logbookMessageAndValue.logbookMessage.vesselName == "NAVIRE RENOMMÉ (ANCIEN NOM)" }, ).isTrue() val firstResultVessels = firstResult.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(firstResultVessels).hasSize(firstResult.size) @@ -225,11 +225,11 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { // Then assertThat(secondResult).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( - secondResult.all { it.logbookMessageTyped.logbookMessage.vesselName == "NAVIRE RENOMMÉ (ANCIEN NOM)" }, + secondResult.all { it.logbookMessageAndValue.logbookMessage.vesselName == "NAVIRE RENOMMÉ (ANCIEN NOM)" }, ).isTrue() val secondResultVessels = secondResult.mapNotNull { jpaVesselRepository.findFirstByInternalReferenceNumber( - it.logbookMessageTyped.logbookMessage.internalReferenceNumber!!, + it.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!, ) } assertThat(secondResultVessels).hasSize(secondResult.size) @@ -249,13 +249,13 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( result.all { - it.logbookMessageTyped.typedMessage.catchOnboard + it.logbookMessageAndValue.value.catchOnboard .any { catch -> listOf("BIB", "BFT").contains(catch.species) } }, ).isTrue() assertThat( result.all { - it.logbookMessageTyped.typedMessage.catchToLand + it.logbookMessageAndValue.value.catchToLand .any { catch -> listOf("BIB", "BFT").contains(catch.species) } }, ).isTrue() @@ -275,7 +275,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( result.all { - it.logbookMessageTyped.typedMessage.pnoTypes + it.logbookMessageAndValue.value.pnoTypes .any { type -> listOf("Préavis type A", "Préavis type C").contains(type.name) } }, ).isTrue() @@ -294,7 +294,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( result.all { - it.logbookMessageTyped.logbookMessage.tripSegments!! + it.logbookMessageAndValue.logbookMessage.tripSegments!! .any { tripSegment -> listOf("NWW05", "NWW09").contains( tripSegment.code, @@ -317,7 +317,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( result.all { - it.logbookMessageTyped.logbookMessage.tripGears!! + it.logbookMessageAndValue.logbookMessage.tripGears!! .any { tripGear -> listOf("LNP", "TBS").contains(tripGear.gear) } }, ).isTrue() @@ -339,7 +339,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(firstResult).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( firstResult.all { - it.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc!! + it.logbookMessageAndValue.value.predictedArrivalDatetimeUtc!! .isAfter(ZonedDateTime.parse("2024-01-01T00:00:00Z")) }, ).isTrue() @@ -357,7 +357,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(secondResult).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( secondResult.all { - it.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc!! + it.logbookMessageAndValue.value.predictedArrivalDatetimeUtc!! .isBefore(ZonedDateTime.parse("2024-01-01T00:00:00Z")) }, ).isTrue() @@ -379,19 +379,19 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { assertThat(result).hasSizeBetween(1, allManualPriorNotificationsLength - 1) assertThat( result.all { - it.logbookMessageTyped.typedMessage.pnoTypes + it.logbookMessageAndValue.value.pnoTypes .any { type -> listOf("Préavis type A", "Préavis type C").contains(type.name) } }, ).isTrue() assertThat( result.all { - it.logbookMessageTyped.logbookMessage.tripGears!! + it.logbookMessageAndValue.logbookMessage.tripGears!! .any { tripGear -> listOf("OTT", "TB").contains(tripGear.gear) } }, ).isTrue() assertThat( result.all { - it.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc!! + it.logbookMessageAndValue.value.predictedArrivalDatetimeUtc!! .isAfter(ZonedDateTime.parse("2024-01-01T00:00:00Z")) }, ).isTrue() @@ -408,7 +408,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { // Then assertThat(result!!.reportId).isEqualTo("00000000-0000-4000-0000-000000000002") - assertThat(result.logbookMessageTyped.logbookMessage.vesselName).isEqualTo("DOS FIN") + assertThat(result.logbookMessageAndValue.logbookMessage.vesselName).isEqualTo("DOS FIN") } @Test @@ -426,7 +426,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { createdAt = null, didNotFishAfterZeroNotice = false, isManuallyCreated = false, - logbookMessageTyped = LogbookMessageTyped( + logbookMessageAndValue = LogbookMessageAndValue( LogbookMessage( id = null, internalReferenceNumber = "CFR123", @@ -485,9 +485,9 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { .usingRecursiveComparison() .ignoringFields("logbookMessageTyped") .isEqualTo(createdPriorNotification) - assertThat(lastPriorNotification.logbookMessageTyped.logbookMessage) - .isEqualTo(createdPriorNotification.logbookMessageTyped.logbookMessage) - assertThat(createdPriorNotification.logbookMessageTyped.typedMessage.riskFactor) + assertThat(lastPriorNotification.logbookMessageAndValue.logbookMessage) + .isEqualTo(createdPriorNotification.logbookMessageAndValue.logbookMessage) + assertThat(createdPriorNotification.logbookMessageAndValue.value.riskFactor) .isEqualTo(2.1) } @@ -497,8 +497,8 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { // Given val currentManualPriorNotification = jpaManualPriorNotificationRepository .findByReportId("00000000-0000-4000-0000-000000000001")!! - assertThat(currentManualPriorNotification.logbookMessageTyped.typedMessage.isBeingSent).isEqualTo(false) - assertThat(currentManualPriorNotification.logbookMessageTyped.typedMessage.isVerified).isEqualTo(false) + assertThat(currentManualPriorNotification.logbookMessageAndValue.value.isBeingSent).isEqualTo(false) + assertThat(currentManualPriorNotification.logbookMessageAndValue.value.isVerified).isEqualTo(false) // When jpaManualPriorNotificationRepository.updateState( @@ -510,8 +510,8 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { // Then val updatedManualPriorNotification = jpaManualPriorNotificationRepository .findByReportId("00000000-0000-4000-0000-000000000001")!! - assertThat(updatedManualPriorNotification.logbookMessageTyped.typedMessage.isBeingSent).isEqualTo(true) - assertThat(updatedManualPriorNotification.logbookMessageTyped.typedMessage.isVerified).isEqualTo(true) + assertThat(updatedManualPriorNotification.logbookMessageAndValue.value.isBeingSent).isEqualTo(true) + assertThat(updatedManualPriorNotification.logbookMessageAndValue.value.isVerified).isEqualTo(true) } @Test @@ -522,7 +522,26 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { // Then assertThat(result).hasSizeGreaterThan(0) - assertThat(result.filter { it.logbookMessageTyped.typedMessage.isVerified == false }).hasSize(1) - assertThat(result.filter { it.logbookMessageTyped.typedMessage.isInVerificationScope == true }).hasSize(1) + assertThat(result.filter { it.logbookMessageAndValue.value.isVerified == false }).hasSize(1) + assertThat(result.filter { it.logbookMessageAndValue.value.isInVerificationScope == true }).hasSize(1) + } + + @Test + @Transactional + fun `invalidate Should invalidate an existing PNO logbook report`() { + // Given + val currentManualPriorNotification = jpaManualPriorNotificationRepository + .findByReportId("00000000-0000-4000-0000-000000000001")!! + assertThat(currentManualPriorNotification.logbookMessageAndValue.value.isInvalidated).isNull() + + // When + jpaManualPriorNotificationRepository.invalidate( + "00000000-0000-4000-0000-000000000001", + ) + + // Then + val updatedManualPriorNotification = jpaManualPriorNotificationRepository + .findByReportId("00000000-0000-4000-0000-000000000001")!! + assertThat(updatedManualPriorNotification.logbookMessageAndValue.value.isInvalidated).isEqualTo(true) } } diff --git a/datascience/src/pipeline/queries/monitorfish/pnos_to_generate.sql b/datascience/src/pipeline/queries/monitorfish/pnos_to_generate.sql index eaa2cd6ed6..c16a99fef9 100644 --- a/datascience/src/pipeline/queries/monitorfish/pnos_to_generate.sql +++ b/datascience/src/pipeline/queries/monitorfish/pnos_to_generate.sql @@ -74,6 +74,10 @@ WHERE AND report_id IN (SELECT referenced_report_id FROM acknowledged_messages) ) ) + AND ( + (value->>'isInvalidated') IS NULL + OR (value->>'isInvalidated')::BOOLEAN IS false + ) ORDER BY id) UNION ALL @@ -118,6 +122,12 @@ ON rf.vessel_id = r.vessel_id LEFT JOIN ports p ON p.locode = r.value->>'port' WHERE - (value->>'isBeingSent')::BOOLEAN IS true - OR report_id NOT IN (SELECT report_id FROM prior_notification_pdf_documents) + ( + (value->>'isInvalidated') IS NULL + OR (value->>'isInvalidated')::BOOLEAN IS false + ) + AND ( + (value->>'isBeingSent')::BOOLEAN IS true + OR report_id NOT IN (SELECT report_id FROM prior_notification_pdf_documents) + ) ORDER BY report_id) \ No newline at end of file diff --git a/datascience/tests/test_data/remote_database/V666.38__Reset_test_manual_prior_notifications.sql b/datascience/tests/test_data/remote_database/V666.38__Reset_test_manual_prior_notifications.sql index 38b8b53607..bb53156416 100644 --- a/datascience/tests/test_data/remote_database/V666.38__Reset_test_manual_prior_notifications.sql +++ b/datascience/tests/test_data/remote_database/V666.38__Reset_test_manual_prior_notifications.sql @@ -6,4 +6,5 @@ INSERT INTO public.manual_prior_notifications ( ('00000000-0000-4000-0000-000000000002', 'AAA', 'ABC000055481', 3, NOW() AT TIME ZONE 'UTC' - INTERVAL '15 days', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 days', '[]', '[]', NOW() AT TIME ZONE 'UTC' - INTERVAL '15 days', 'PLACE SPECTACLE SUBIR', '{"riskFactor": 1.2, "isBeingSent": false, "isInVerificationScope": false, "isSent": true, "isVerified": false, "catchOnboard":[{"faoZone": "27.7.d","weight": 1080,"nbFish": null,"species": "SOL"}],"catchToLand":[{"faoZone": "21.1.A","weight": 72,"nbFish": null,"species": "SOS"}],"note": null,"pnoTypes":[{"pnoTypeName": "Préavis type A","minimumNotificationPeriod": 4,"hasDesignatedPorts": false}],"port": "FRZJZ","predictedArrivalDatetimeUtc": "2021-05-06T07:41:03.340Z","predictedLandingDatetimeUtc": "2021-05-06T11:41:03.340Z","purpose": "LAN","tripStartDate": "2021-05-04T11:41:03.340Z"}'), ('00000000-0000-4000-0000-000000000003', 'AAA', NULL, 6, NOW() AT TIME ZONE 'UTC' - INTERVAL '3 months', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 months', 'null', 'null', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 months', 'I DO 4H REPORT', '{"riskFactor": 2.8, "isBeingSent": true, "isInVerificationScope": false, "isSent": true, "isVerified": true, "catchOnboard":[{"faoZone": "37.1","weight": 172,"nbFish": 3,"species": "BFT"}],"catchToLand":[{"faoZone": "21.1.A","weight": 72,"nbFish": null,"species": "SOS"}],"note": null,"pnoTypes":[{"pnoTypeName": "Préavis type A","minimumNotificationPeriod": 4,"hasDesignatedPorts": false}],"port": "FRDPE","predictedArrivalDatetimeUtc": "2021-05-06T07:41:03.340Z","predictedLandingDatetimeUtc": "2021-05-06T11:41:03.340Z","purpose": "LAN","tripStartDate": "2021-05-04T11:41:03.340Z"}'), ('00000000-0000-4000-0000-000000000004', 'AAA', NULL, 6, NOW() AT TIME ZONE 'UTC' - INTERVAL '3 weeks', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 weeks', NULL, NULL, NOW() AT TIME ZONE 'UTC' - INTERVAL '3 weeks', 'I DO 4H REPORT', '{"riskFactor": 3.1, "isBeingSent": false, "isInVerificationScope": false, "isSent": true, "isVerified": false, "note": null,"pnoTypes":[{"pnoTypeName": "Préavis type A","minimumNotificationPeriod": 4,"hasDesignatedPorts": false}],"port": "FRDKK","predictedArrivalDatetimeUtc": "2021-05-06T07:41:03.340Z","predictedLandingDatetimeUtc": "2021-05-06T11:41:03.340Z","purpose": "LAN","tripStartDate": "2021-05-04T11:41:03.340Z"}'), - ('00000000-0000-4000-0000-000000000005', 'AAA', NULL, 6, NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', '[{"gear": "LNP"}, {"gear": "OTM", "mesh": 80}]', '[{"segment": "NWW09","segmentName": "Lignes"}, {"segment": "SWW01","segmentName": "Chaluts de fond"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', 'I DO 4H REPORT', '{"riskFactor": 3.8, "isBeingSent": true, "isInVerificationScope": true, "isSent": true, "isVerified": true, "catchOnboard":[{"faoZone": "21.1.a","weight": 72,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 172,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 72,"nbFish": 3,"species": "BFT"}, {"faoZone": "27.2.a","weight": 32,"nbFish": 2,"species": "BFT"}, {"faoZone": "27.2.a","weight" : 502,"nbFish": 2,"species": "SWO"}, {"faoZone": "27.2.a","weight": 202,"nbFish": 1,"species": "SWO"}],"catchToLand":[{"faoZone": "21.1.a","weight": 72,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 172,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 72,"nbFish": 3,"species": "BFT"}, {"faoZone": "27.2.a","weight": 32,"nbFish": 2,"species": "BFT"}, {"faoZone": "27.2.a","weight": 502,"nbFish": 2,"species": "SWO"}, {"faoZone": "27.2.a","weight": 202,"nbFish": 1,"species": "SWO"}],"note": "Ceci est une note de préavis manuel","pnoTypes":[{"pnoTypeName": "Préavis type A","minimumNotificationPeriod": 4,"hasDesignatedPorts": false}, {"pnoTypeName": "Préavis type B","minimumNotificationPeriod": 4,"hasDesignatedPorts": true}],"port": "FRLEH","predictedArrivalDatetimeUtc": "2021-05-06T07:41:03.340Z","predictedLandingDatetimeUtc": "2021-05-06T11:41:03.340Z","purpose": "LAN","tripStartDate": "2021-05-04T11:41:03.340Z"}'); \ No newline at end of file + ('00000000-0000-4000-0000-000000000005', 'AAA', NULL, 6, NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', '[{"gear": "LNP"}, {"gear": "OTM", "mesh": 80}]', '[{"segment": "NWW09","segmentName": "Lignes"}, {"segment": "SWW01","segmentName": "Chaluts de fond"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', 'I DO 4H REPORT', '{"riskFactor": 3.8, "isBeingSent": true, "isInVerificationScope": true, "isSent": true, "isVerified": true, "catchOnboard":[{"faoZone": "21.1.a","weight": 72,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 172,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 72,"nbFish": 3,"species": "BFT"}, {"faoZone": "27.2.a","weight": 32,"nbFish": 2,"species": "BFT"}, {"faoZone": "27.2.a","weight" : 502,"nbFish": 2,"species": "SWO"}, {"faoZone": "27.2.a","weight": 202,"nbFish": 1,"species": "SWO"}],"catchToLand":[{"faoZone": "21.1.a","weight": 72,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 172,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 72,"nbFish": 3,"species": "BFT"}, {"faoZone": "27.2.a","weight": 32,"nbFish": 2,"species": "BFT"}, {"faoZone": "27.2.a","weight": 502,"nbFish": 2,"species": "SWO"}, {"faoZone": "27.2.a","weight": 202,"nbFish": 1,"species": "SWO"}],"note": "Ceci est une note de préavis manuel","pnoTypes":[{"pnoTypeName": "Préavis type A","minimumNotificationPeriod": 4,"hasDesignatedPorts": false}, {"pnoTypeName": "Préavis type B","minimumNotificationPeriod": 4,"hasDesignatedPorts": true}],"port": "FRLEH","predictedArrivalDatetimeUtc": "2021-05-06T07:41:03.340Z","predictedLandingDatetimeUtc": "2021-05-06T11:41:03.340Z","purpose": "LAN","tripStartDate": "2021-05-04T11:41:03.340Z", "isInvalidated": false}'), + ('00000000-0000-4000-0000-000000000006', 'AAA', NULL, 6, NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', false, 'FRA', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', '[{"gear": "LNP"}, {"gear": "OTM", "mesh": 80}]', '[{"segment": "NWW09","segmentName": "Lignes"}, {"segment": "SWW01","segmentName": "Chaluts de fond"}]', NOW() AT TIME ZONE 'UTC' - INTERVAL '3 days', 'I DO 4H REPORT', '{"riskFactor": 3.8, "isBeingSent": true, "isInVerificationScope": true, "isSent": true, "isVerified": true, "catchOnboard":[{"faoZone": "21.1.a","weight": 72,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 172,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 72,"nbFish": 3,"species": "BFT"}, {"faoZone": "27.2.a","weight": 32,"nbFish": 2,"species": "BFT"}, {"faoZone": "27.2.a","weight" : 502,"nbFish": 2,"species": "SWO"}, {"faoZone": "27.2.a","weight": 202,"nbFish": 1,"species": "SWO"}],"catchToLand":[{"faoZone": "21.1.a","weight": 72,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 172,"nbFish": null,"species": "GHL"}, {"faoZone": "21.1.a","weight": 72,"nbFish": 3,"species": "BFT"}, {"faoZone": "27.2.a","weight": 32,"nbFish": 2,"species": "BFT"}, {"faoZone": "27.2.a","weight": 502,"nbFish": 2,"species": "SWO"}, {"faoZone": "27.2.a","weight": 202,"nbFish": 1,"species": "SWO"}],"note": "Ceci est une note de préavis manuel","pnoTypes":[{"pnoTypeName": "Préavis type A","minimumNotificationPeriod": 4,"hasDesignatedPorts": false}, {"pnoTypeName": "Préavis type B","minimumNotificationPeriod": 4,"hasDesignatedPorts": true}],"port": "FRLEH","predictedArrivalDatetimeUtc": "2021-05-06T07:41:03.340Z","predictedLandingDatetimeUtc": "2021-05-06T11:41:03.340Z","purpose": "LAN","tripStartDate": "2021-05-04T11:41:03.340Z", "isInvalidated": true}'); \ No newline at end of file diff --git a/datascience/tests/test_data/remote_database/V666.5__Reset_test_logbook.sql b/datascience/tests/test_data/remote_database/V666.5__Reset_test_logbook.sql index 474e150a8f..4b9a787f87 100644 --- a/datascience/tests/test_data/remote_database/V666.5__Reset_test_logbook.sql +++ b/datascience/tests/test_data/remote_database/V666.5__Reset_test_logbook.sql @@ -174,7 +174,9 @@ INSERT INTO logbook_raw_messages (operation_number, xml_message) VALUES ('17', 'Message ERS xml'), ('18', 'Message ERS xml'), ('19', 'Message ERS xml'), - ('20', 'Message ERS xml'); + ('20', 'Message ERS xml'), + ('21', 'Message ERS xml'), + ('22', 'Message ERS xml'); INSERT INTO logbook_reports ( operation_number, operation_country, operation_datetime_utc, operation_type, @@ -215,7 +217,7 @@ INSERT INTO logbook_reports ( '14', 'OOF', ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 52 minutes')::TIMESTAMP, 'DAT', '14', null, ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 54 minutes')::TIMESTAMP, 'ABC000306959', 'LLUK', 'RV348407', 'ÉTABLIR IMPRESSION LORSQUE', 'FRA', null, 'PNO', - '{"port": "FRLEH", "purpose": "LAN", "catchOnboard": [{"nbFish": null, "faoZone": "27.8.a", "weight": 150.0, "species": "GHL"}, {"nbFish": null, "faoZone": "27.8.a", "weight": 1450.0, "species": "HKE"}, {"nbFish": 2, "faoZone": "27.8.a", "weight": 150.0, "species": "BFT"}, {"nbFish": 2, "faoZone": "27.8.a", "weight": 70.0, "species": "SWO"}, {"nbFish": 2, "faoZone": "27.8.b", "weight": 150.0, "species": "BFT"}, {"nbFish": null, "faoZone": "27.8.b", "weight": 250.0, "species": "GHL"}], "tripStartDate": "2020-05-04T19:41:03.340Z", "predictedArrivalDatetimeUtc": "2020-05-06T11:41:03.340Z", "pnoTypes": [{"pnoTypeName": "Préavis type 1", "minimumNotificationPeriod": 4.0, "hasDesignatedPorts": true}, {"pnoTypeName": "Préavis type 2", "minimumNotificationPeriod": 4.0, "hasDesignatedPorts": true}], "isInVerificationScope": false, "isVerified": false, "isSent": false, "isBeingSent": true, "riskFactor": 2.14443662414848}', + '{"port": "FRLEH", "purpose": "LAN", "catchOnboard": [{"nbFish": null, "faoZone": "27.8.a", "weight": 150.0, "species": "GHL"}, {"nbFish": null, "faoZone": "27.8.a", "weight": 1450.0, "species": "HKE"}, {"nbFish": 2, "faoZone": "27.8.a", "weight": 150.0, "species": "BFT"}, {"nbFish": 2, "faoZone": "27.8.a", "weight": 70.0, "species": "SWO"}, {"nbFish": 2, "faoZone": "27.8.b", "weight": 150.0, "species": "BFT"}, {"nbFish": null, "faoZone": "27.8.b", "weight": 250.0, "species": "GHL"}], "tripStartDate": "2020-05-04T19:41:03.340Z", "predictedArrivalDatetimeUtc": "2020-05-06T11:41:03.340Z", "pnoTypes": [{"pnoTypeName": "Préavis type 1", "minimumNotificationPeriod": 4.0, "hasDesignatedPorts": true}, {"pnoTypeName": "Préavis type 2", "minimumNotificationPeriod": 4.0, "hasDesignatedPorts": true}], "isInVerificationScope": false, "isVerified": false, "isSent": false, "isBeingSent": true, "riskFactor": 2.14443662414848, "isInvalidated": false}', ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 50 minutes')::TIMESTAMP, '20510003', 'ERS', true, '[{"gear": "OTB", "mesh": 140, "dimensions": "250.0"}]', '[{"segment": "SWW01/02/03", "segmentName": "Segment ciblé par une unité"}]' ), @@ -267,5 +269,21 @@ INSERT INTO logbook_reports ( '{"returnStatus": "000"}', ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 27 minutes')::TIMESTAMP, NULL, 'ERS', false, NULL, NULL +), +( + '21', 'OOF', ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 12 minutes')::TIMESTAMP, 'DAT', + '21', null, ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 14 minutes')::TIMESTAMP, + 'INVA_PNO_VES', 'INVA', 'INVALID', 'NAVIRE AVEC PNO INVALIDE', 'FRA', null, 'PNO', + '{"port": "FRDPE", "purpose": "LAN", "catchOnboard": [{"nbFish": null, "faoZone": "27.8.a", "weight": 150.0, "species": "GHL"}, {"nbFish": null, "faoZone": "27.8.a", "weight": 1450.0, "species": "HKE"}, {"nbFish": 2, "faoZone": "27.8.a", "weight": 150.0, "species": "BFT"}, {"nbFish": 2, "faoZone": "27.8.a", "weight": 70.0, "species": "SWO"}, {"nbFish": 2, "faoZone": "27.8.b", "weight": 150.0, "species": "BFT"}, {"nbFish": null, "faoZone": "27.8.b", "weight": 250.0, "species": "GHL"}], "tripStartDate": "2020-05-04T19:41:03.340Z", "predictedArrivalDatetimeUtc": "2020-05-06T11:41:03.340Z", "pnoTypes": [], "isInVerificationScope": false, "isVerified": false, "isSent": false, "isBeingSent": true, "isInvalidated": true}', + ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 30 minutes')::TIMESTAMP, '20510003', 'ERS', + true, '[{"gear": "OTB", "mesh": 140, "dimensions": "250.0"}]', '[]' +), +( + '22', 'OOE', ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 10 minutes')::TIMESTAMP, 'RET', + NULL, '21', NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + '{"returnStatus": "000"}', + ((now() AT TIME ZONE 'UTC') - INTERVAL '1 month 10 minutes')::TIMESTAMP, NULL, 'ERS', + false, NULL, NULL ) ; \ No newline at end of file diff --git a/datascience/tests/test_pipeline/test_flows/test_current_segments.py b/datascience/tests/test_pipeline/test_flows/test_current_segments.py index feeb725a78..51dfaadb6d 100644 --- a/datascience/tests/test_pipeline/test_flows/test_current_segments.py +++ b/datascience/tests/test_pipeline/test_flows/test_current_segments.py @@ -28,11 +28,18 @@ def current_segments() -> pd.DataFrame: now = datetime.datetime.utcnow() return pd.DataFrame( { - "cfr": ["ABC000000000", "ABC000306959", "ABC000542519", "___TARGET___"], + "cfr": [ + "ABC000000000", + "ABC000306959", + "ABC000542519", + "INVA_PNO_VES", + "___TARGET___", + ], "last_logbook_message_datetime_utc": [ now - relativedelta.relativedelta(months=1, minutes=27), now - datetime.timedelta(days=1, hours=6), now - datetime.timedelta(weeks=1, days=3), + now - relativedelta.relativedelta(months=1, minutes=14), now - relativedelta.relativedelta(months=1, minutes=34), ], "departure_datetime_utc": [ @@ -40,13 +47,15 @@ def current_segments() -> pd.DataFrame: datetime.datetime(2018, 2, 27, 1, 5), now - datetime.timedelta(weeks=1, days=5), pd.NaT, + pd.NaT, ], - "trip_number": [None, "20210001", "20210002", None], + "trip_number": [None, "20210001", "20210002", None, None], "gear_onboard": [ None, [{"gear": "OTM", "mesh": 80, "dimensions": None}], [{"gear": "OTB", "mesh": 80, "dimensions": None}], None, + None, ], "species_onboard": [ None, @@ -73,29 +82,31 @@ def current_segments() -> pd.DataFrame: }, ], None, + None, ], - "segments": [[], ["SWW04"], ["SWW01/02/03"], []], - "total_weight_onboard": [0.0, 713.0, 2583.0, 0.0], - "probable_segments": [None, None, None, None], - "impact_risk_factor": [1.0, 2.1, 3.0, 1.0], - "control_priority_level": [1.0, 1.0, 1.0, 1.0], - "segment_highest_impact": [None, "SWW04", "SWW01/02/03", None], - "segment_highest_priority": [None, None, None, None], - "vessel_id": [None, 1.0, 2.0, 7.0], - "external_immatriculation": [None, "RV348407", "RO237719", None], - "ircs": [None, "LLUK", "FQ7058", None], + "segments": [[], ["SWW04"], ["SWW01/02/03"], [], []], + "total_weight_onboard": [0.0, 713.0, 2583.0, 0.0, 0.0], + "probable_segments": [None, None, None, None, None], + "impact_risk_factor": [1.0, 2.1, 3.0, 1.0, 1.0], + "control_priority_level": [1.0, 1.0, 1.0, 1.0, 1.0], + "segment_highest_impact": [None, "SWW04", "SWW01/02/03", None, None], + "segment_highest_priority": [None, None, None, None, None], + "vessel_id": [None, 1.0, 2.0, None, 7.0], + "external_immatriculation": [None, "RV348407", "RO237719", None, None], + "ircs": [None, "LLUK", "FQ7058", None, None], } ) def test_extract_catches(reset_test_data): catches = extract_catches.run() - assert len(catches) == 5 + assert len(catches) == 6 assert set(catches.cfr) == { "ABC000542519", "ABC000306959", "ABC000000000", "___TARGET___", + "INVA_PNO_VES", } assert set(catches.ircs) == {"LLUK", "FQ7058", None} assert set(catches.loc[catches.cfr == "ABC000542519", "trip_number"]) == { @@ -475,20 +486,14 @@ def test_test_current_segments_flow(reset_test_data, current_segments): computed_current_segments.drop(columns=datetime_columns), ) - # Data of these vessels is only for PNO distribution testing and should be ignored - # when testing this flow - excluded_cfrs = ["ABC000000000", "___TARGET___"] - assert ( ( ( - current_segments.loc[ - ~current_segments.cfr.isin(excluded_cfrs), datetime_columns - ] - - computed_current_segments.loc[ - ~computed_current_segments.cfr.isin(excluded_cfrs), datetime_columns - ] + current_segments[datetime_columns] + - computed_current_segments[datetime_columns] ) + .abs() + .fillna(datetime.timedelta(seconds=0)) < datetime.timedelta(seconds=10) ) .all() 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 1b518e31c5..5df16e4092 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 @@ -221,4 +221,34 @@ context('Side Window > Prior Notification Card > Card', () => { // Verify that window.open was called with the correct URL cy.get('@windowOpen').should('be.calledWith', '/api/v1/prior_notifications/pdf/FAKE_OPERATION_102', '_blank') }) + + it('Should invalidate a prior notification', () => { + // Given + openSideWindowPriorNotificationList() + cy.get('[data-cy="side-window-sub-menu-ALL"]').click() + cy.fill('Rechercher un navire', 'COURANT') + + cy.getTableRowById('FAKE_OPERATION_102' as any) + .find('[title="Préavis invalidé"]') + .should('not.exist') + + cy.getTableRowById('FAKE_OPERATION_102' as any).clickButton('Éditer le préavis') + if (document.querySelector('[data-cy="first-loader"]')) { + cy.getDataCy('first-loader').should('not.be.visible') + } + + // When + cy.clickButton('Invalider le préavis') + cy.clickButton('Confirmer l’invalidation') + + // Then + cy.get('.Wrapper').contains('Invalidé') + cy.get('[title="Invalider le préavis"]').should('not.exist') + + cy.clickButton('Fermer') + + cy.getTableRowById('FAKE_OPERATION_102' as any) + .find('[title="Préavis invalidé"]') + .should('exist') + }) }) 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 c584c50623..b553634ead 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 @@ -537,4 +537,26 @@ context('Side Window > Prior Notification Form > Form', () => { }) }) }) + + it('Should invalidate a manual prior notification', () => { + // Given + cy.intercept( + 'POST', + '/bff/v1/prior_notifications/00000000-0000-4000-0000-000000000001/invalidate?isManuallyCreated=true&operationDate=*' + ).as('verifyAndSendPriorNotification') + editSideWindowPriorNotification('POISSON PAS NET', '00000000-0000-4000-0000-000000000001') + cy.get('.Wrapper').should('not.contain', 'Invalidé') + + // When + cy.clickButton('Invalider le préavis') + cy.clickButton('Confirmer l’invalidation') + cy.get('.Wrapper').contains('Invalidé') + cy.get('[title="Invalider le préavis"]').should('not.exist') + + // Then + cy.clickButton('Fermer') + cy.getTableRowById('00000000-0000-4000-0000-000000000001' as any) + .find('[title="Préavis invalidé"]') + .should('exist') + }) }) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b4a0e0b547..bf5fc2c262 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@dnd-kit/core": "6.1.0", "@dnd-kit/modifiers": "6.0.1", - "@mtes-mct/monitor-ui": "18.9.0", + "@mtes-mct/monitor-ui": "18.10.0", "@reduxjs/toolkit": "2.2.6", "@sentry/react": "7.117.0", "@tanstack/react-table": "8.19.2", @@ -663,9 +663,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", - "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2815,12 +2815,12 @@ "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==" }, "node_modules/@mtes-mct/monitor-ui": { - "version": "18.9.0", - "resolved": "https://registry.npmjs.org/@mtes-mct/monitor-ui/-/monitor-ui-18.9.0.tgz", - "integrity": "sha512-qok6HoLlB8BVCzEc8pe8ztZ+YJTOBeXLJJkC9wEz9dkN5SQeT1ZJjd1kZxx46GqqB7oR40oxINMXeIdVSRDdpA==", + "version": "18.10.0", + "resolved": "https://registry.npmjs.org/@mtes-mct/monitor-ui/-/monitor-ui-18.10.0.tgz", + "integrity": "sha512-F53beFlQFng05CDi2+j9ylKn48pG0nLM7ZNdVITeIUsgKa3jXTawGE4yGrF8rZvsKlZQ0cUvS1qTxrn5bV6MEQ==", "dependencies": { - "@babel/runtime": "7.24.8", - "@tanstack/react-table": "8.19.3", + "@babel/runtime": "7.25.0", + "@tanstack/react-table": "8.20.1", "@tanstack/react-virtual": "beta", "prop-types": "15.8.1", "tslib": "2.6.3" @@ -2839,11 +2839,11 @@ } }, "node_modules/@mtes-mct/monitor-ui/node_modules/@tanstack/react-table": { - "version": "8.19.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.19.3.tgz", - "integrity": "sha512-MtgPZc4y+cCRtU16y1vh1myuyZ2OdkWgMEBzyjYsoMWMicKZGZvcDnub3Zwb6XF2pj9iRMvm1SO1n57lS0vXLw==", + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.1.tgz", + "integrity": "sha512-PJK+07qbengObe5l7c8vCdtefXm8cyR4i078acWrHbdm8JKw1ES7YpmOtVt9ALUVEEFAHscdVpGRhRgikgFMbQ==", "dependencies": { - "@tanstack/table-core": "8.19.3" + "@tanstack/table-core": "8.20.1" }, "engines": { "node": ">=12" @@ -2858,9 +2858,9 @@ } }, "node_modules/@mtes-mct/monitor-ui/node_modules/@tanstack/table-core": { - "version": "8.19.3", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.19.3.tgz", - "integrity": "sha512-IqREj9ADoml9zCAouIG/5kCGoyIxPFdqdyoxis9FisXFi5vT+iYfEfLosq4xkU/iDbMcEuAj+X8dWRLvKYDNoQ==", + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.1.tgz", + "integrity": "sha512-5Ly5TIRHnWH7vSDell9B/OVyV380qqIJVg7H7R7jU4fPEmOD4smqAX7VRflpYI09srWR8aj5OLD2Ccs1pI5mTg==", "engines": { "node": ">=12" }, diff --git a/frontend/package.json b/frontend/package.json index 393d3f3756..fa045c53e4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "dev-puppeteer": "FRONTEND_MONITORENV_URL=//localhost:9880 import-meta-env-prepare -u -x ./.env.local.defaults && vite --port 3000", "bundle-sw": "esbuild src/workers/serviceWorker.ts --bundle --outfile=public/service-worker.js", "prepare": "cd .. && ./frontend/node_modules/.bin/husky ./frontend/config/husky", + "generate:testdata": "node ./scripts/generate_test_data_seeds.mjs", "start": "import-meta-env-prepare -u -x ./.env.local.defaults && vite preview --port 3000", "start:emulate": "npm run clean && npm run build && import-meta-env -x ./.env.example -p ./build/index.html && import-meta-env-prepare -u -x ./.env.local.defaults && vite preview --port 3000", "test:e2e": "cypress run --browser firefox --config-file ./config/cypress.config.js --e2e", @@ -35,7 +36,7 @@ "dependencies": { "@dnd-kit/core": "6.1.0", "@dnd-kit/modifiers": "6.0.1", - "@mtes-mct/monitor-ui": "18.9.0", + "@mtes-mct/monitor-ui": "18.10.0", "@reduxjs/toolkit": "2.2.6", "@sentry/react": "7.117.0", "@tanstack/react-table": "8.19.2", diff --git a/frontend/src/features/Logbook/LogbookMessage.types.ts b/frontend/src/features/Logbook/LogbookMessage.types.ts index e6aadfc5cd..6338558da5 100644 --- a/frontend/src/features/Logbook/LogbookMessage.types.ts +++ b/frontend/src/features/Logbook/LogbookMessage.types.ts @@ -87,6 +87,7 @@ export namespace LogbookMessage { hasPortLandingAuthorization: boolean | undefined isBeingSent: boolean | undefined isInVerificationScope: boolean | undefined + isInvalidated: boolean | undefined isSent: boolean | undefined isVerified: boolean | undefined latitude: string | undefined diff --git a/frontend/src/features/PriorNotification/PriorNotification.types.ts b/frontend/src/features/PriorNotification/PriorNotification.types.ts index f9db75e0bd..d96296589e 100644 --- a/frontend/src/features/PriorNotification/PriorNotification.types.ts +++ b/frontend/src/features/PriorNotification/PriorNotification.types.ts @@ -17,6 +17,7 @@ export namespace PriorNotification { isBeingSent: boolean isCorrection: boolean isInVerificationScope: boolean + isInvalidated: boolean | undefined isManuallyCreated: boolean isSent: boolean isVerified: boolean diff --git a/frontend/src/features/PriorNotification/components/InvalidatePriorNotificationDialog.tsx b/frontend/src/features/PriorNotification/components/InvalidatePriorNotificationDialog.tsx new file mode 100644 index 0000000000..282cd8a46a --- /dev/null +++ b/frontend/src/features/PriorNotification/components/InvalidatePriorNotificationDialog.tsx @@ -0,0 +1,37 @@ +import { Accent, Button, Dialog } from '@mtes-mct/monitor-ui' +import styled from 'styled-components' + +import type { Promisable } from 'type-fest' + +type InvalidatePriorNotificationDialogProps = Readonly<{ + onCancel: () => Promisable + onConfirm: () => Promisable +}> +export function InvalidatePriorNotificationDialog({ onCancel, onConfirm }: InvalidatePriorNotificationDialogProps) { + return ( + + Invalider le préavis + +

+ Êtes-vous sûr de vouloir invalider ce préavis ? +

+

Vous ne pourrez plus le modifier ni le diffuser aux unités. Vous pourrez toujours le consulter.

+
+ + + + + +
+ ) +} + +// TODO Remove that once we get rid of global legacy CSS. +const StyledDialogTitle = styled(Dialog.Title)` + line-height: 48px; + margin: 0; +` diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx index 3543f955d3..60ee151208 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx @@ -2,8 +2,12 @@ import { RTK_FORCE_REFETCH_QUERY_OPTIONS, RTK_ONE_MINUTE_POLLING_QUERY_OPTIONS } import { ErrorWall } from '@components/ErrorWall' import { FrontendErrorBoundary } from '@components/FrontendErrorBoundary' import { LogbookMessage } from '@features/Logbook/components/VesselLogbook/LogbookMessages/messages/LogbookMessage' +import { InvalidatePriorNotificationDialog } from '@features/PriorNotification/components/InvalidatePriorNotificationDialog' import { PriorNotification } from '@features/PriorNotification/PriorNotification.types' -import { useGetPriorNotificationDetailQuery } from '@features/PriorNotification/priorNotificationApi' +import { + useGetPriorNotificationDetailQuery, + useInvalidatePriorNotificationMutation +} from '@features/PriorNotification/priorNotificationApi' import { updatePriorNotificationNote } from '@features/PriorNotification/useCases/updatePriorNotificationNote' import { verifyAndSendPriorNotification } from '@features/PriorNotification/useCases/verifyAndSendPriorNotification' import { @@ -52,10 +56,14 @@ export function PriorNotificationCard() { ...RTK_FORCE_REFETCH_QUERY_OPTIONS } ) + const [invalidatePriorNotification] = useInvalidatePriorNotificationMutation() const sideWindowPriorNotificationCardError = useMainAppSelector( state => state.displayedError.sideWindowPriorNotificationCardError ) + const [isLoading, setIsLoading] = useState(false) + const [isInvalidatingPriorNotificationDialog, setIsInvalidatingPriorNotificationDialog] = useState(false) + const isInvalidated = priorNotificationDetail?.logbookMessage?.message?.isInvalidated const isPendingSend = !!priorNotificationDetail?.state && [PriorNotification.State.AUTO_SEND_IN_PROGRESS, PriorNotification.State.PENDING_SEND].includes( @@ -81,6 +89,22 @@ export function PriorNotificationCard() { setIsLoading(false) } + const invalidate = async () => { + setIsLoading(true) + + assertNotNullish(openedPriorNotificationIdentity) + assertNotNullish(isOpenedPriorNotificationManuallyCreated) + + await invalidatePriorNotification({ + isManuallyCreated: isOpenedPriorNotificationManuallyCreated, + operationDate: openedPriorNotificationIdentity.operationDate, + reportId: openedPriorNotificationIdentity.reportId + }) + + setIsInvalidatingPriorNotificationDialog(false) + setIsLoading(false) + } + const updateNoteCallback = useCallback( async (nextNote: string | undefined) => { assertNotNullish(openedPriorNotificationIdentity) @@ -146,6 +170,7 @@ export function PriorNotificationCard() { + + {isSuperUser && !isInvalidated && ( + setIsInvalidatingPriorNotificationDialog(true)} + title="Invalider le préavis" + > + Invalider le préavis + + )}
@@ -205,19 +241,35 @@ export function PriorNotificationCard() {
+ {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; diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationForm/Card.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationForm/Card.tsx index 8a87709f6a..762991f198 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationForm/Card.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationForm/Card.tsx @@ -1,7 +1,11 @@ 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 { useGetPriorNotificationDetailQuery } from '@features/PriorNotification/priorNotificationApi' +import { InvalidatePriorNotificationDialog } from '@features/PriorNotification/components/InvalidatePriorNotificationDialog' +import { + useGetPriorNotificationDetailQuery, + useInvalidatePriorNotificationMutation +} from '@features/PriorNotification/priorNotificationApi' import { priorNotificationActions } from '@features/PriorNotification/slice' import { updateEditedPriorNotificationComputedValues } from '@features/PriorNotification/useCases/updateEditedPriorNotificationComputedValues' import { isZeroNotice } from '@features/PriorNotification/utils' @@ -9,6 +13,7 @@ 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' import { isEqual } from 'lodash' @@ -58,12 +63,15 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, ...RTK_FORCE_REFETCH_QUERY_OPTIONS } ) + const [invalidatePriorNotification] = useInvalidatePriorNotificationMutation() + const [isInvalidatingPriorNotificationDialog, setIsInvalidatingPriorNotificationDialog] = useState(false) 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 isPendingSend = !!editedPriorNotificationDetail?.state && [PriorNotification.State.AUTO_SEND_IN_PROGRESS, PriorNotification.State.PENDING_SEND].includes( @@ -83,6 +91,18 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, onClose() } + const invalidate = async () => { + assertNotNullish(editedPriorNotificationDetail) + + await invalidatePriorNotification({ + isManuallyCreated: true, + operationDate: editedPriorNotificationDetail.logbookMessage.operationDateTime, + reportId: editedPriorNotificationDetail.logbookMessage.reportId + }) + + setIsInvalidatingPriorNotificationDialog(false) + } + const handleSubmit = () => { onSubmit() @@ -157,6 +177,7 @@ export function Card({ isValidatingOnChange, onClose, onSubmit, onVerifyAndSend, -
+ + + {!!editedPriorNotificationDetail && !isInvalidated && ( + setIsInvalidatingPriorNotificationDialog(true)} + title="Invalider le préavis" + > + Invalider le préavis + + )}