Skip to content

Commit

Permalink
Préavis - Permettre la création d'un préavis manuel à partir d'un pré…
Browse files Browse the repository at this point in the history
…avis logbook (#3572)

## Linked issues

- Resolve #3564
- Resolve #3495

----

- [x] Tests E2E (Cypress)
  • Loading branch information
ivangabriele authored Aug 28, 2024
2 parents c6ef9c1 + 8a34df8 commit fdd0a5b
Show file tree
Hide file tree
Showing 31 changed files with 479 additions and 306 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ data class LogbookMessage(
var isDeleted: Boolean = false,
val isEnriched: Boolean = false,
var isSentByFailoverSoftware: Boolean = false,
// TODO Rename to `value` to help distinguish `message` (`value`) from `LogbookMessage`.
val message: LogbookMessageValue? = null,
val messageType: String? = null,
val operationType: LogbookOperationType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ data class PriorNotification(
* See /adrs/0006-prior-notification-states-specifications.md for more details.
*/
get() = run {
val pnoMessage = logbookMessageAndValue.value
val pnoValue = logbookMessageAndValue.value

val isInVerificationScope = pnoMessage.isInVerificationScope
val isVerified = pnoMessage.isVerified
val isSent = pnoMessage.isSent
val isBeingSent = pnoMessage.isBeingSent
val isInVerificationScope = pnoValue.isInVerificationScope
val isVerified = pnoValue.isVerified
val isSent = pnoValue.isSent
val isBeingSent = pnoValue.isBeingSent

return when {
isInVerificationScope == null || isVerified == null || isSent == null || isBeingSent == null -> null
Expand Down Expand Up @@ -78,9 +78,9 @@ data class PriorNotification(
isManuallyCreated: Boolean,
) {
val logbookMessage = logbookMessageAndValue.logbookMessage
val pnoMessage = logbookMessageAndValue.value
val pnoValue = logbookMessageAndValue.value

port = pnoMessage.port?.let { portLocode ->
port = pnoValue.port?.let { portLocode ->
allPorts.find { it.locode == portLocode }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,25 +135,6 @@ class PriorNotificationController(
)
}

@GetMapping("/logbook/{reportId}/form")
@Operation(summary = "Get a logbook prior notification form data by its `reportId`")
fun getLogbookFormData(
@PathParam("Logbook message `reportId`")
@PathVariable(name = "reportId")
reportId: String,
@Parameter(description = "Operation date (to optimize SQL query via Timescale).")
@RequestParam(name = "operationDate")
operationDate: ZonedDateTime,
): LogbookPriorNotificationFormDataOutput {
return LogbookPriorNotificationFormDataOutput.fromPriorNotification(
getPriorNotification.execute(
reportId,
operationDate,
false,
),
)
}

@PutMapping("/logbook/{reportId}")
@Operation(summary = "Update a logbook prior notification by its `reportId`")
fun updateLogbook(
Expand Down Expand Up @@ -198,25 +179,6 @@ class PriorNotificationController(
.fromManualPriorNotificationComputedValues(manualPriorNotificationComputedValues)
}

@GetMapping("/manual/{reportId}/form")
@Operation(summary = "Get a manual prior notification form data by its `reportId`")
fun getManualData(
@PathParam("Logbook message `reportId`")
@PathVariable(name = "reportId")
reportId: String,
@Parameter(description = "Operation date (to optimize SQL query via Timescale).")
@RequestParam(name = "operationDate")
operationDate: ZonedDateTime,
): ManualPriorNotificationFormDataOutput {
return ManualPriorNotificationFormDataOutput.fromPriorNotification(
getPriorNotification.execute(
reportId,
operationDate,
true,
),
)
}

@PostMapping("/manual")
@Operation(summary = "Create a new manual prior notification")
fun createManual(
Expand Down Expand Up @@ -300,8 +262,8 @@ class PriorNotificationController(
@Parameter(description = "Operation date (to optimize SQL query via Timescale).")
@RequestParam(name = "operationDate")
operationDate: ZonedDateTime,
): PriorNotificationDetailDataOutput {
return PriorNotificationDetailDataOutput
): PriorNotificationDataOutput {
return PriorNotificationDataOutput
.fromPriorNotification(getPriorNotification.execute(reportId, operationDate, isManuallyCreated))
}

Expand Down Expand Up @@ -352,8 +314,8 @@ class PriorNotificationController(
@Parameter(description = "Is the prior notification manually created?")
@RequestParam(name = "isManuallyCreated")
isManuallyCreated: Boolean,
): PriorNotificationDetailDataOutput {
return PriorNotificationDetailDataOutput
): PriorNotificationDataOutput {
return PriorNotificationDataOutput
.fromPriorNotification(verifyAndSendPriorNotification.execute(reportId, operationDate, isManuallyCreated))
}

Expand All @@ -369,13 +331,13 @@ class PriorNotificationController(
@Parameter(description = "Is the prior notification manually created?")
@RequestParam(name = "isManuallyCreated")
isManuallyCreated: Boolean,
): PriorNotificationDetailDataOutput {
): PriorNotificationDataOutput {
val updatedPriorNotification = invalidatePriorNotification.execute(
reportId = reportId,
operationDate = operationDate,
isManuallyCreated = isManuallyCreated,
)

return PriorNotificationDetailDataOutput.fromPriorNotification(updatedPriorNotification)
return PriorNotificationDataOutput.fromPriorNotification(updatedPriorNotification)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@ import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotifica
data class LogbookPriorNotificationFormDataOutput(
val authorTrigram: String?,
val note: String?,
val reportId: String,
) {
companion object {
fun fromPriorNotification(priorNotification: PriorNotification): LogbookPriorNotificationFormDataOutput {
val reportId = requireNotNull(priorNotification.reportId) { "`priorNotification.reportId` is null." }

return LogbookPriorNotificationFormDataOutput(
authorTrigram = priorNotification.logbookMessageAndValue.value.authorTrigram,
note = priorNotification.logbookMessageAndValue.value.note,
reportId = reportId,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.outputs

import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessagePurpose
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification
import fr.gouv.cnsp.monitorfish.utils.CustomZonedDateTime
import java.time.ZonedDateTime

data class ManualPriorNotificationDraftDataOutput(
val hasPortEntranceAuthorization: Boolean,
val hasPortLandingAuthorization: Boolean,
val authorTrigram: String?,
val didNotFishAfterZeroNotice: Boolean,
val expectedArrivalDate: String?,
val expectedLandingDate: String?,
val fishingCatches: List<ManualPriorNotificationFishingCatchDataOutput>,
val globalFaoArea: String?,
val note: String?,
val portLocode: String?,
val sentAt: ZonedDateTime?,
val purpose: LogbookMessagePurpose?,
val tripGearCodes: List<String>,
val vesselId: Int?,
) {
companion object {
/**
* Used to duplicate a logbook prior notification as a manual one in Frontend.
*/
fun fromPriorNotification(priorNotification: PriorNotification): ManualPriorNotificationDraftDataOutput {
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val pnoValue = priorNotification.logbookMessageAndValue.value

val expectedArrivalDate = pnoValue.predictedArrivalDatetimeUtc
?.let { CustomZonedDateTime.fromZonedDateTime(it).toString() }
val expectedLandingDate = pnoValue.predictedLandingDatetimeUtc
?.let { CustomZonedDateTime.fromZonedDateTime(it).toString() }
val tripGearCodes = logbookMessage.tripGears
?.let { tripGears ->
tripGears
.map { tripGear -> requireNotNull(tripGear.gear) { "`it.gear` is null." } }
} ?: emptyList()

val hasPortEntranceAuthorization = pnoValue.hasPortEntranceAuthorization ?: true
val hasPortLandingAuthorization = pnoValue.hasPortLandingAuthorization ?: true
// In Frontend form, manual prior notifications can:
// - either have a single global FAO area field
// - or have an FAO area field per fishing catch
// while in Backend, we always have an FAO area field per fishing catch.
// So we need to check if all fishing catches have the same FAO area to know which case we are in.
val hasGlobalFaoArea = pnoValue.catchOnboard.mapNotNull { it.faoZone }.distinct().size == 1
val globalFaoArea = if (hasGlobalFaoArea) {
pnoValue.catchOnboard.first().faoZone
} else {
null
}
val fishingCatchDataOutputs = pnoValue.catchOnboard.map {
ManualPriorNotificationFishingCatchDataOutput.fromLogbookFishingCatch(it, !hasGlobalFaoArea)
}

return ManualPriorNotificationDraftDataOutput(
authorTrigram = pnoValue.authorTrigram,
didNotFishAfterZeroNotice = priorNotification.didNotFishAfterZeroNotice,
expectedArrivalDate = expectedArrivalDate,
expectedLandingDate = expectedLandingDate,
fishingCatches = fishingCatchDataOutputs,
globalFaoArea = globalFaoArea,
hasPortEntranceAuthorization = hasPortEntranceAuthorization,
hasPortLandingAuthorization = hasPortLandingAuthorization,
note = pnoValue.note,
portLocode = pnoValue.port,
sentAt = priorNotification.sentAt,
purpose = pnoValue.purpose,
tripGearCodes = tripGearCodes,
vesselId = priorNotification.vessel?.id,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.time.ZoneOffset
import java.time.ZonedDateTime

data class ManualPriorNotificationFormDataOutput(
val reportId: String,
val hasPortEntranceAuthorization: Boolean,
val hasPortLandingAuthorization: Boolean,
val authorTrigram: String,
Expand All @@ -17,7 +18,6 @@ data class ManualPriorNotificationFormDataOutput(
val globalFaoArea: String?,
val note: String?,
val portLocode: String,
val reportId: String,
val sentAt: ZonedDateTime,
val purpose: LogbookMessagePurpose,
val tripGearCodes: List<String>,
Expand All @@ -27,23 +27,23 @@ data class ManualPriorNotificationFormDataOutput(
companion object {
fun fromPriorNotification(priorNotification: PriorNotification): ManualPriorNotificationFormDataOutput {
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val pnoMessage = priorNotification.logbookMessageAndValue.value
val pnoValue = priorNotification.logbookMessageAndValue.value

val authorTrigram = requireNotNull(pnoMessage.authorTrigram) {
"`pnoMessage.authorTrigram` is null."
val authorTrigram = requireNotNull(pnoValue.authorTrigram) {
"`pnoValue.authorTrigram` is null."
}
val expectedArrivalDate = CustomZonedDateTime.fromZonedDateTime(
requireNotNull(pnoMessage.predictedArrivalDatetimeUtc) {
requireNotNull(pnoValue.predictedArrivalDatetimeUtc) {
"`message.predictedArrivalDatetimeUtc` is null."
},
).toString()
val expectedLandingDate = CustomZonedDateTime.fromZonedDateTime(
requireNotNull(pnoMessage.predictedLandingDatetimeUtc) {
requireNotNull(pnoValue.predictedLandingDatetimeUtc) {
"`message.predictedLandingDatetimeUtc` is null."
},
).toString()
val portLocode = requireNotNull(pnoMessage.port) { "`pnoMessage.port` is null." }
val purpose = requireNotNull(pnoMessage.purpose) { "`pnoMessage.purpose` is null." }
val portLocode = requireNotNull(pnoValue.port) { "`pnoValue.port` is null." }
val purpose = requireNotNull(pnoValue.purpose) { "`pnoValue.purpose` is null." }
val reportId = requireNotNull(priorNotification.reportId) { "`priorNotification.reportId` is null." }
val sentAt = requireNotNull(priorNotification.sentAt) { "`priorNotification.sentAt` is null." }
val tripGearCodes = requireNotNull(logbookMessage.tripGears) {
Expand All @@ -56,36 +56,36 @@ data class ManualPriorNotificationFormDataOutput(
val vesselId = requireNotNull(priorNotification.vessel) {
"`priorNotification.vessel` is null."
}.id
val hasPortEntranceAuthorization = pnoMessage.hasPortEntranceAuthorization ?: true
val hasPortLandingAuthorization = pnoMessage.hasPortLandingAuthorization ?: true

val hasPortEntranceAuthorization = pnoValue.hasPortEntranceAuthorization ?: true
val hasPortLandingAuthorization = pnoValue.hasPortLandingAuthorization ?: true
// In Frontend form, manual prior notifications can:
// - either have a single global FAO area field
// - or have an FAO area field per fishing catch
// while in Backend, we always have an FAO area field per fishing catch.
// So we need to check if all fishing catches have the same FAO area to know which case we are in.
val hasGlobalFaoArea = pnoMessage.catchOnboard.mapNotNull { it.faoZone }.distinct().size == 1
val hasGlobalFaoArea = pnoValue.catchOnboard.mapNotNull { it.faoZone }.distinct().size == 1
val globalFaoArea = if (hasGlobalFaoArea) {
pnoMessage.catchOnboard.first().faoZone
pnoValue.catchOnboard.first().faoZone
} else {
null
}
val fishingCatchDataOutputs = pnoMessage.catchOnboard.map {
val fishingCatchDataOutputs = pnoValue.catchOnboard.map {
ManualPriorNotificationFishingCatchDataOutput.fromLogbookFishingCatch(it, !hasGlobalFaoArea)
}

return ManualPriorNotificationFormDataOutput(
hasPortEntranceAuthorization = hasPortEntranceAuthorization,
hasPortLandingAuthorization = hasPortLandingAuthorization,
reportId = reportId,
authorTrigram = authorTrigram,
didNotFishAfterZeroNotice = priorNotification.didNotFishAfterZeroNotice,
expectedArrivalDate = expectedArrivalDate,
expectedLandingDate = expectedLandingDate,
globalFaoArea = globalFaoArea,
fishingCatches = fishingCatchDataOutputs,
note = pnoMessage.note,
globalFaoArea = globalFaoArea,
hasPortEntranceAuthorization = hasPortEntranceAuthorization,
hasPortLandingAuthorization = hasPortLandingAuthorization,
note = pnoValue.note,
portLocode = portLocode,
reportId = reportId,
sentAt = sentAt,
purpose = purpose,
tripGearCodes = tripGearCodes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotifica
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationState
import java.time.ZonedDateTime

class PriorNotificationDetailDataOutput(
class PriorNotificationDataOutput(
/** Reference logbook message (report) `reportId`. */
val reportId: String,
val asLogbookForm: LogbookPriorNotificationFormDataOutput?,
val asManualDraft: ManualPriorNotificationDraftDataOutput?,
val asManualForm: ManualPriorNotificationFormDataOutput?,
/** Unique identifier concatenating all the DAT, COR, RET & DEL operations `id` used for data consolidation. */
val fingerprint: String,
val isLessThanTwelveMetersVessel: Boolean,
Expand All @@ -18,11 +21,27 @@ class PriorNotificationDetailDataOutput(
val riskFactor: Double?,
) {
companion object {
fun fromPriorNotification(priorNotification: PriorNotification): PriorNotificationDetailDataOutput {
fun fromPriorNotification(priorNotification: PriorNotification): PriorNotificationDataOutput {
val reportId = requireNotNull(priorNotification.reportId) {
"`reportId` is null."
}

val asLogbookForm = if (!priorNotification.isManuallyCreated) {
LogbookPriorNotificationFormDataOutput.fromPriorNotification(priorNotification)
} else {
null
}
val asManualDraft = if (!priorNotification.isManuallyCreated) {
ManualPriorNotificationDraftDataOutput.fromPriorNotification(priorNotification)
} else {
null
}
val asManualForm = if (priorNotification.isManuallyCreated) {
ManualPriorNotificationFormDataOutput.fromPriorNotification(priorNotification)
} else {
null
}

val isLessThanTwelveMetersVessel = requireNotNull(priorNotification.vessel) {
"`priorNotification.vessel` is null."
}.isLessThanTwelveMetersVessel()
Expand All @@ -33,8 +52,11 @@ class PriorNotificationDetailDataOutput(

val logbookMessageDataOutput = LogbookMessageDataOutput.fromLogbookMessage(logbookMessage)

return PriorNotificationDetailDataOutput(
reportId,
return PriorNotificationDataOutput(
reportId = reportId,
asLogbookForm = asLogbookForm,
asManualDraft = asManualDraft,
asManualForm = asManualForm,
fingerprint = priorNotification.fingerprint,
isLessThanTwelveMetersVessel = isLessThanTwelveMetersVessel,
isManuallyCreated = priorNotification.isManuallyCreated,
Expand Down
Loading

0 comments on commit fdd0a5b

Please sign in to comment.