Skip to content

Commit

Permalink
Add creation date & author to pno form edit history
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangabriele committed Oct 17, 2024
1 parent b06e86a commit 7891d1b
Show file tree
Hide file tree
Showing 16 changed files with 210 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class PNO() : LogbookMessageValue {
var authorTrigram: String? = null
var catchOnboard: List<LogbookFishingCatch> = emptyList()
var catchToLand: List<LogbookFishingCatch> = emptyList()
var createdBy: String? = null
var economicZone: String? = null
var effortZone: String? = null

Expand Down Expand Up @@ -76,5 +77,8 @@ class PNO() : LogbookMessageValue {
@JsonSerialize(using = ZonedDateTimeSerializer::class)
var tripStartDate: ZonedDateTime? = null

@JsonDeserialize(using = ZonedDateTimeDeserializer::class)
@JsonSerialize(using = ZonedDateTimeSerializer::class)
var updatedAt: ZonedDateTime? = null
var updatedBy: String? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ data class PriorNotification(
isManuallyCreated = false,
logbookMessageAndValue = logbookMessageAndValue,
sentAt = logbookMessageAndValue.logbookMessage.reportDateTime,
updatedAt = logbookMessage.operationDateTime,
updatedAt = logbookMessageAndValue.value.updatedAt ?: logbookMessage.operationDateTime,
// These props need to be calculated in the use case
port = null,
reportingCount = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ class CreateOrUpdateManualPriorNotification(
updatedBy: String?,
vesselId: Int,
): PriorNotification {
val existingMessageValue: PNO? =
reportId?.let {
val manualPriorNotfication = manualPriorNotificationRepository.findByReportId(reportId)
manualPriorNotfication?.logbookMessageAndValue?.logbookMessage?.message as PNO
}

println("existingMessageValue: $existingMessageValue")

// /!\ Backend computed vessel risk factor is only used as a real time Frontend indicator.
// The Backend should NEVER update `risk_factors` DB table, only the pipeline is allowed to update it.
val computedValues =
Expand All @@ -72,8 +80,9 @@ class CreateOrUpdateManualPriorNotification(
val tripSegments = computedValues.tripSegments.map { it.toLogbookTripSegment() }
val vessel = vesselRepository.findVesselById(vesselId)
val priorNotificationTypes = computedValues.types.map { it.toPriorNotificationType() }
val message =
getMessage(
val messageValue =
getMessageValue(
existingMessageValue = existingMessageValue,
expectedArrivalDate = expectedArrivalDate,
expectedLandingDate = expectedLandingDate,
// At the moment, manual prior notifications only have a single global FAO area field in Frontend,
Expand Down Expand Up @@ -116,7 +125,7 @@ class CreateOrUpdateManualPriorNotification(
isDeleted = false,
isEnriched = true,
isSentByFailoverSoftware = false,
message = message,
message = messageValue,
messageType = LogbookMessageTypeMapping.PNO.name,
operationType = LogbookOperationType.DAT,
tripGears = tripGears,
Expand Down Expand Up @@ -163,7 +172,8 @@ class CreateOrUpdateManualPriorNotification(
return createdOrUpdatedPriorNotification
}

private fun getMessage(
private fun getMessageValue(
existingMessageValue: PNO?,
purpose: LogbookMessagePurpose,
expectedArrivalDate: ZonedDateTime,
expectedLandingDate: ZonedDateTime,
Expand All @@ -180,6 +190,8 @@ class CreateOrUpdateManualPriorNotification(
): PNO {
val allPorts = portRepository.findAll()

val authorTrigram = existingMessageValue?.authorTrigram
val createdBy = existingMessageValue?.createdBy ?: updatedBy
val isInVerificationScope =
ManualPriorNotificationComputedValues
.isInVerificationScope(computedVesselFlagCountryCode, computedVesselRiskFactor)
Expand All @@ -189,8 +201,10 @@ class CreateOrUpdateManualPriorNotification(
val portName = allPorts.find { it.locode == portLocode }?.name

return PNO().apply {
this.authorTrigram = authorTrigram
this.catchOnboard = fishingCatches
this.catchToLand = fishingCatches
this.createdBy = createdBy
this.economicZone = null
this.effortZone = null
// At the moment, manual prior notifications only have a single global FAO area field in Frontend,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class PriorNotificationDataOutput(
val asLogbookForm: LogbookPriorNotificationFormDataOutput?,
val asManualDraft: ManualPriorNotificationDraftDataOutput?,
val asManualForm: ManualPriorNotificationFormDataOutput?,
val createdAt: ZonedDateTime,
/** Unique identifier concatenating all the DAT, COR, RET & DEL operations `id` used for data consolidation. */
val fingerprint: String,
val isLessThanTwelveMetersVessel: Boolean,
Expand All @@ -19,6 +20,7 @@ class PriorNotificationDataOutput(
val operationDate: ZonedDateTime,
val state: PriorNotificationState?,
val riskFactor: Double?,
val updatedAt: ZonedDateTime,
val vesselId: Int,
val vesselIdentity: VesselIdentityDataOutput,
) {
Expand Down Expand Up @@ -52,9 +54,18 @@ class PriorNotificationDataOutput(
requireNotNull(priorNotification.vessel) {
"`priorNotification.vessel` is null."
}

val createdAt =
requireNotNull(priorNotification.createdAt) {
"`priorNotification.createdAt` is null."
}
val isLessThanTwelveMetersVessel = vessel.isLessThanTwelveMetersVessel()
val isVesselUnderCharter = vessel.underCharter
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val updatedAt =
requireNotNull(priorNotification.updatedAt) {
"`priorNotification.updatedAt` is null."
}
val vesselIdentity = VesselIdentityDataOutput.fromVessel(vessel)
val vesselId = vessel.id

Expand All @@ -65,6 +76,7 @@ class PriorNotificationDataOutput(
asLogbookForm = asLogbookForm,
asManualDraft = asManualDraft,
asManualForm = asManualForm,
createdAt = createdAt,
fingerprint = priorNotification.fingerprint,
isLessThanTwelveMetersVessel = isLessThanTwelveMetersVessel,
isManuallyCreated = priorNotification.isManuallyCreated,
Expand All @@ -73,6 +85,7 @@ class PriorNotificationDataOutput(
operationDate = logbookMessage.operationDateTime,
state = priorNotification.state,
riskFactor = priorNotification.logbookMessageAndValue.value.riskFactor,
updatedAt = updatedAt,
vesselId = vesselId,
vesselIdentity = vesselIdentity,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import java.time.ZonedDateTime
data class PriorNotificationListItemDataOutput(
val reportId: String,
val acknowledgment: AcknowledgmentDataOutput?,
val createdAt: ZonedDateTime?,
val expectedArrivalDate: ZonedDateTime?,
val expectedLandingDate: ZonedDateTime?,
/** Unique identifier concatenating all the DAT, COR, RET & DEL operations `id` used for data consolidation. */
Expand All @@ -36,7 +35,6 @@ data class PriorNotificationListItemDataOutput(
val tripGears: List<LogbookMessageGearDataOutput>,
val tripSegments: List<LogbookMessageTripSegmentDataOutput>,
val types: List<PriorNotificationTypeDataOutput>,
val updatedAt: ZonedDateTime?,
val vesselId: Int?,
val vesselExternalReferenceNumber: String?,
val vesselFlagCountryCode: CountryCode,
Expand Down Expand Up @@ -78,7 +76,6 @@ data class PriorNotificationListItemDataOutput(
return PriorNotificationListItemDataOutput(
reportId = priorNotification.reportId,
acknowledgment = acknowledgment,
createdAt = priorNotification.createdAt,
expectedArrivalDate = message.predictedArrivalDatetimeUtc,
expectedLandingDate = message.predictedLandingDatetimeUtc,
fingerprint = priorNotification.fingerprint,
Expand All @@ -99,7 +96,6 @@ data class PriorNotificationListItemDataOutput(
tripGears = tripGears,
tripSegments = tripSegments,
types = types,
updatedAt = priorNotification.updatedAt,
vesselId = vessel.id,
vesselExternalReferenceNumber = vessel.externalReferenceNumber,
vesselFlagCountryCode = vessel.flagState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ class JpaLogbookReportRepository(
val nextPnoValue =
pnoValue.apply {
this.note = note
this.updatedAt = ZonedDateTime.now()
this.updatedBy = updatedBy

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,74 @@ class CreateOrUpdateManualPriorNotificationUTests {
@MockBean
private lateinit var getPriorNotification: GetPriorNotification

@Test
fun `execute Should create a manual prior notification`() {
val newFakePriorNotification = PriorNotificationFaker.fakePriorNotification()

// Given
given(vesselRepository.findVesselById(any())).willReturn(VesselFaker.fakeVessel())
given(computeManualPriorNotification.execute(any(), any(), any(), any(), any())).willReturn(
ManualPriorNotificationComputedValues(
isVesselUnderCharter = null,
nextState = PriorNotificationState.OUT_OF_VERIFICATION_SCOPE,
tripSegments = emptyList(),
types = emptyList(),
vesselRiskFactor = null,
),
)
given(manualPriorNotificationRepository.save(any())).willReturn(newFakePriorNotification)
given(
getPriorNotification.execute(
newFakePriorNotification.reportId!!,
newFakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime,
true,
),
).willReturn(newFakePriorNotification)

// When
val result =
CreateOrUpdateManualPriorNotification(
gearRepository,
manualPriorNotificationRepository,
pnoPortSubscriptionRepository,
pnoSegmentSubscriptionRepository,
pnoVesselSubscriptionRepository,
portRepository,
priorNotificationPdfDocumentRepository,
vesselRepository,
computeManualPriorNotification,
getPriorNotification,
).execute(
purpose = LogbookMessagePurpose.LAN,
didNotFishAfterZeroNotice = false,
expectedArrivalDate = ZonedDateTime.parse("2024-01-01T00:00:00Z"),
expectedLandingDate = ZonedDateTime.parse("2024-01-01T00:00:00Z"),
fishingCatches = emptyList(),
globalFaoArea = "FAKE_FAO_AREA",
hasPortEntranceAuthorization = true,
hasPortLandingAuthorization = true,
note = null,
portLocode = "FAKE_PORT_LOCODE",
reportId = null,
sentAt = ZonedDateTime.parse("2024-01-01T00:00:00Z"),
tripGearCodes = emptyList(),
updatedBy = "editor@example.org",
vesselId = 1,
)

// Then
assertThat(result.reportId!!).isEqualTo(newFakePriorNotification.reportId!!)
}

@Test
fun `execute Should update a manual prior notification`() {
val fakePriorNotification = PriorNotificationFaker.fakePriorNotification()
val existingFakePriorNotification = PriorNotificationFaker.fakePriorNotification()
val updatedFakePriorNotification = existingFakePriorNotification.copy(updatedAt = ZonedDateTime.now())

// Given
given(manualPriorNotificationRepository.findByReportId(existingFakePriorNotification.reportId!!)).willReturn(
existingFakePriorNotification,
)
given(vesselRepository.findVesselById(any())).willReturn(VesselFaker.fakeVessel())
given(computeManualPriorNotification.execute(any(), any(), any(), any(), any())).willReturn(
ManualPriorNotificationComputedValues(
Expand All @@ -62,14 +125,14 @@ class CreateOrUpdateManualPriorNotificationUTests {
vesselRiskFactor = null,
),
)
given(manualPriorNotificationRepository.save(any())).willReturn(fakePriorNotification)
given(manualPriorNotificationRepository.save(any())).willReturn(updatedFakePriorNotification)
given(
getPriorNotification.execute(
fakePriorNotification.reportId!!,
fakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime,
existingFakePriorNotification.reportId!!,
existingFakePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime,
true,
),
).willReturn(fakePriorNotification)
).willReturn(updatedFakePriorNotification)

// When
val result =
Expand All @@ -95,14 +158,14 @@ class CreateOrUpdateManualPriorNotificationUTests {
hasPortLandingAuthorization = true,
note = null,
portLocode = "FAKE_PORT_LOCODE",
reportId = fakePriorNotification.reportId!!,
reportId = existingFakePriorNotification.reportId!!,
sentAt = ZonedDateTime.parse("2024-01-01T00:00:00Z"),
tripGearCodes = emptyList(),
updatedBy = "editor@example.org",
vesselId = 1,
)

// Then
assertThat(result.reportId!!).isEqualTo(fakePriorNotification.reportId!!)
assertThat(result.reportId!!).isEqualTo(existingFakePriorNotification.reportId!!)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,13 @@ class PriorNotificationControllerUTests {
val requestBody =
objectMapper.writeValueAsString(
ManualPriorNotificationFormDataInput(
hasPortEntranceAuthorization = true,
hasPortLandingAuthorization = true,
authorTrigram = "ABC",
didNotFishAfterZeroNotice = false,
expectedArrivalDate = ZonedDateTime.now(),
expectedLandingDate = ZonedDateTime.now(),
globalFaoArea = "FAO AREA 51",
fishingCatches = emptyList(),
globalFaoArea = "FAO AREA 51",
hasPortEntranceAuthorization = true,
hasPortLandingAuthorization = true,
note = null,
portLocode = "FRABVC",
sentAt = ZonedDateTime.now(),
Expand Down Expand Up @@ -283,14 +282,13 @@ class PriorNotificationControllerUTests {
val requestBody =
objectMapper.writeValueAsString(
ManualPriorNotificationFormDataInput(
hasPortEntranceAuthorization = true,
hasPortLandingAuthorization = true,
authorTrigram = "ABC",
didNotFishAfterZeroNotice = false,
expectedArrivalDate = ZonedDateTime.now(),
expectedLandingDate = ZonedDateTime.now(),
globalFaoArea = "FAO AREA 51",
fishingCatches = emptyList(),
globalFaoArea = "FAO AREA 51",
hasPortEntranceAuthorization = true,
hasPortLandingAuthorization = true,
note = null,
portLocode = "FRABVC",
sentAt = ZonedDateTime.now(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ export function getPriorNotificationsFakeResponse({
data: [
{
acknowledgment: null,
createdAt,
expectedArrivalDate,
expectedLandingDate: expectedArrivalDate,
fingerprint,
Expand Down Expand Up @@ -114,7 +113,6 @@ export function getPriorNotificationsFakeResponse({
tripGears: [{ dimensions: null, gear: 'OTT', gearName: null, mesh: null }],
tripSegments: [{ code: 'MED01', name: 'All Trawls 1' }],
types: [{ hasDesignatedPorts: false, minimumNotificationPeriod: 4.0, name: 'Préavis type A' }],
updatedAt,
vesselExternalReferenceNumber: 'EXTIMM121',
vesselFlagCountryCode: 'FR',
vesselId: 121,
Expand Down Expand Up @@ -163,6 +161,7 @@ export function getPriorNotificationFakeResponse({
const commonData: OrUndefinedToOrNull<
Omit<PriorNotification.Detail, 'asLogbookForm' | 'asManualDraft' | 'asManualForm' | 'isManuallyCreated'>
> = {
createdAt,
fingerprint,
isLessThanTwelveMetersVessel: true,
isVesselUnderCharter: false,
Expand Down Expand Up @@ -236,6 +235,7 @@ export function getPriorNotificationFakeResponse({
riskFactor: 3.2,
statisticalRectangle: null,
tripStartDate,
updatedAt,
updatedBy: 'editor@example.org'
},
messageType: Logbook.MessageType.PNO,
Expand All @@ -255,6 +255,7 @@ export function getPriorNotificationFakeResponse({
reportId,
riskFactor: 3.2,
state,
updatedAt,
vesselId: 121,
vesselIdentity: {
beaconNumber: null,
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/features/Logbook/Logbook.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,13 @@ export namespace Logbook {
/**
* @deprecated
* Kept because some historical messages used a manually entered trigram to identify the author of the message.
*
* It's now automated via `createdBy` and `updatedBy` fields.
*/
authorTrigram: string | undefined
catchOnboard: Catch[] | undefined
catchToLand: Catch[] | undefined
createdBy: string | undefined
economicZone: string | undefined
effortZone: string | undefined
faoZone: string | undefined
Expand All @@ -194,6 +196,13 @@ export namespace Logbook {
riskFactor: number | undefined
statisticalRectangle: string | undefined
tripStartDate: string | undefined
/**
* @internal
* This `updatedAt` field is only used internally for logbook PNOs. It's always `undefined` for manual ones.
*
* /!\ Use `PriorNotification.PriorNotification.updatedAt` or `PriorNotification.Detail.updatedAt` instead.
*/
updatedAt: string | undefined
updatedBy: string | undefined
}

Expand Down
Loading

0 comments on commit 7891d1b

Please sign in to comment.