diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/PriorNotificationPdfDocumentRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/PriorNotificationPdfDocumentRepository.kt
index 5166cf0b1a..307ff1656c 100644
--- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/PriorNotificationPdfDocumentRepository.kt
+++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/PriorNotificationPdfDocumentRepository.kt
@@ -4,4 +4,5 @@ import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PdfDocument
interface PriorNotificationPdfDocumentRepository {
fun findByReportId(reportId: String): PdfDocument
+ fun deleteByReportId(reportId: String)
}
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 e428a27cd5..fdc9d5e446 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
@@ -7,10 +7,9 @@ import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.ManualPriorNotificationComputedValues
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationType
-import fr.gouv.cnsp.monitorfish.domain.repositories.GearRepository
-import fr.gouv.cnsp.monitorfish.domain.repositories.ManualPriorNotificationRepository
-import fr.gouv.cnsp.monitorfish.domain.repositories.PortRepository
-import fr.gouv.cnsp.monitorfish.domain.repositories.VesselRepository
+import fr.gouv.cnsp.monitorfish.domain.repositories.*
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import java.time.ZonedDateTime
@UseCase
@@ -20,8 +19,11 @@ class CreateOrUpdateManualPriorNotification(
private val portRepository: PortRepository,
private val vesselRepository: VesselRepository,
private val computeManualPriorNotification: ComputeManualPriorNotification,
+ private val priorNotificationPdfDocumentRepository: PriorNotificationPdfDocumentRepository,
private val getPriorNotification: GetPriorNotification,
) {
+ private val logger: Logger = LoggerFactory.getLogger(CreateOrUpdateManualPriorNotification::class.java)
+
fun execute(
hasPortEntranceAuthorization: Boolean,
hasPortLandingAuthorization: Boolean,
@@ -126,6 +128,14 @@ class CreateOrUpdateManualPriorNotification(
updatedAt = null,
)
+ if (reportId !== null) {
+ try {
+ priorNotificationPdfDocumentRepository.deleteByReportId(reportId)
+ } catch (e: Exception) {
+ logger.warn("Could not delete existing PDF document", e)
+ }
+ }
+
val newOrCurrentReportId = manualPriorNotificationRepository.save(newOrNextPriorNotification)
val createdOrUpdatedPriorNotification = getPriorNotification.execute(newOrCurrentReportId, true)
@@ -164,10 +174,10 @@ class CreateOrUpdateManualPriorNotification(
// so we transform that single FAO area into an FAO area per fishing catch.
// This means we don't need to set a global PNO message FAO area here.
this.faoZone = null
- this.isBeingSent = isInVerificationScope
+ this.isBeingSent = false
this.isInVerificationScope = isInVerificationScope
this.isSent = false
- this.isVerified = existingPnoValue?.isVerified ?: false
+ this.isVerified = false
this.latitude = null
this.longitude = null
this.note = note
diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNote.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNote.kt
index da1bb3e251..317b4a7801 100644
--- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNote.kt
+++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNote.kt
@@ -3,16 +3,28 @@ 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.PriorNotificationPdfDocumentRepository
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
@UseCase
class UpdatePriorNotificationNote(
private val logbookReportRepository: LogbookReportRepository,
+ private val priorNotificationPdfDocumentRepository: PriorNotificationPdfDocumentRepository,
private val getPriorNotification: GetPriorNotification,
) {
+ private val logger: Logger = LoggerFactory.getLogger(CreateOrUpdateManualPriorNotification::class.java)
+
fun execute(
note: String?,
reportId: String,
): PriorNotification {
+ try {
+ priorNotificationPdfDocumentRepository.deleteByReportId(reportId)
+ } catch (e: Exception) {
+ logger.warn("Could not delete existing PDF document", e)
+ }
+
logbookReportRepository.updatePriorNotificationNote(
reportId = reportId,
note = note,
diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/PriorNotificationPdfDocumentEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/PriorNotificationPdfDocumentEntity.kt
index 6a9b909cd2..494e739cf2 100644
--- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/PriorNotificationPdfDocumentEntity.kt
+++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/PriorNotificationPdfDocumentEntity.kt
@@ -14,10 +14,12 @@ data class PriorNotificationPdfDocumentEntity(
@Id
@Column(name = "report_id")
val reportId: String,
+
@Column(name = "source", columnDefinition = "prior_notification_source")
@Enumerated(EnumType.STRING)
@JdbcType(PostgreSQLEnumJdbcType::class)
val source: PriorNotificationSource,
+
@Column(name = "generation_datetime_utc")
val generationDatetimeUtc: ZonedDateTime,
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 930020212a..2370784d67 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
@@ -385,6 +385,15 @@ class JpaLogbookReportRepository(
val pnoMessage = objectMapper.readValue(logbookReportEntity.message, PNO::class.java)
pnoMessage.note = note
+ /**
+ * The PNO states are re-initialized,
+ * - the PDF will be generated
+ * - the PNO will require another verification before sending
+ */
+ pnoMessage.isBeingSent = false
+ pnoMessage.isVerified = false
+ pnoMessage.isSent = false
+
val nextMessage = objectMapper.writeValueAsString(pnoMessage)
val updatedEntity = logbookReportEntity.copy(message = nextMessage)
diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaPriorNotificationPdfDocumentRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaPriorNotificationPdfDocumentRepository.kt
index b6eb7e8f5c..8f5d44e27b 100644
--- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaPriorNotificationPdfDocumentRepository.kt
+++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaPriorNotificationPdfDocumentRepository.kt
@@ -6,6 +6,7 @@ import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageException
import fr.gouv.cnsp.monitorfish.domain.repositories.PriorNotificationPdfDocumentRepository
import fr.gouv.cnsp.monitorfish.infrastructure.database.repositories.interfaces.DBPriorNotificationPdfDocumentRepository
import org.springframework.dao.EmptyResultDataAccessException
+import org.springframework.data.jpa.repository.Modifying
import org.springframework.stereotype.Repository
@Repository
@@ -19,4 +20,13 @@ class JpaPriorNotificationPdfDocumentRepository(
throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND)
}
}
+
+ @Modifying(clearAutomatically = true)
+ override fun deleteByReportId(reportId: String) {
+ return try {
+ dbPriorNotificationPdfDocumentRepository.deleteById(reportId)
+ } catch (e: EmptyResultDataAccessException) {
+ throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND)
+ }
+ }
}
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 ef9de8974b..dd7a259f4a 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
@@ -4,10 +4,7 @@ import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.given
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessagePurpose
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.ManualPriorNotificationComputedValues
-import fr.gouv.cnsp.monitorfish.domain.repositories.GearRepository
-import fr.gouv.cnsp.monitorfish.domain.repositories.ManualPriorNotificationRepository
-import fr.gouv.cnsp.monitorfish.domain.repositories.PortRepository
-import fr.gouv.cnsp.monitorfish.domain.repositories.VesselRepository
+import fr.gouv.cnsp.monitorfish.domain.repositories.*
import fr.gouv.cnsp.monitorfish.fakers.PriorNotificationFaker
import fr.gouv.cnsp.monitorfish.fakers.VesselFaker
import org.assertj.core.api.Assertions.assertThat
@@ -37,6 +34,9 @@ class CreateOrUpdateManualPriorNotificationUTests {
@MockBean
private lateinit var getPriorNotification: GetPriorNotification
+ @MockBean
+ private lateinit var priorNotificationPdfDocumentRepository: PriorNotificationPdfDocumentRepository
+
@Test
fun `execute Should update a manual prior notification`() {
val fakePriorNotification = PriorNotificationFaker.fakePriorNotification()
@@ -62,6 +62,7 @@ class CreateOrUpdateManualPriorNotificationUTests {
portRepository,
vesselRepository,
computeManualPriorNotification,
+ priorNotificationPdfDocumentRepository,
getPriorNotification,
).execute(
hasPortEntranceAuthorization = true,
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
new file mode 100644
index 0000000000..1a5c781dfd
--- /dev/null
+++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/UpdatePriorNotificationNoteUTests.kt
@@ -0,0 +1,44 @@
+package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification
+
+import com.nhaarman.mockitokotlin2.given
+import fr.gouv.cnsp.monitorfish.domain.repositories.LogbookReportRepository
+import fr.gouv.cnsp.monitorfish.domain.repositories.PriorNotificationPdfDocumentRepository
+import fr.gouv.cnsp.monitorfish.fakers.PriorNotificationFaker
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.springframework.boot.test.mock.mockito.MockBean
+import org.springframework.test.context.junit.jupiter.SpringExtension
+
+@ExtendWith(SpringExtension::class)
+class UpdatePriorNotificationNoteUTests {
+ @MockBean
+ private lateinit var logbookReportRepository: LogbookReportRepository
+
+ @MockBean
+ private lateinit var getPriorNotification: GetPriorNotification
+
+ @MockBean
+ private lateinit var priorNotificationPdfDocumentRepository: PriorNotificationPdfDocumentRepository
+
+ @Test
+ fun `execute Should update a prior notification note`() {
+ val fakePriorNotification = PriorNotificationFaker.fakePriorNotification()
+
+ // Given
+ given(getPriorNotification.execute(fakePriorNotification.reportId!!, false)).willReturn(fakePriorNotification)
+
+ // When
+ val result = UpdatePriorNotificationNote(
+ logbookReportRepository,
+ priorNotificationPdfDocumentRepository,
+ getPriorNotification,
+ ).execute(
+ note = null,
+ reportId = fakePriorNotification.reportId!!,
+ )
+
+ // Then
+ assertThat(result.reportId).isEqualTo(fakePriorNotification.reportId)
+ }
+}
diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt
index a4b922c1e8..1cc5a82e4a 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
@@ -1174,8 +1174,14 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() {
// Then
val updatedDatReport = jpaLogbookReportRepository.findById(109)
assertThat((updatedDatReport.message as PNO).note).isEqualTo("A wonderful note")
+ assertThat((updatedDatReport.message as PNO).isBeingSent).isEqualTo(false)
+ assertThat((updatedDatReport.message as PNO).isVerified).isEqualTo(false)
+ assertThat((updatedDatReport.message as PNO).isSent).isEqualTo(false)
val updatedCorReport = jpaLogbookReportRepository.findById(1109)
assertThat((updatedCorReport.message as PNO).note).isEqualTo("A wonderful note")
+ assertThat((updatedCorReport.message as PNO).isBeingSent).isEqualTo(false)
+ assertThat((updatedCorReport.message as PNO).isVerified).isEqualTo(false)
+ assertThat((updatedCorReport.message as PNO).isSent).isEqualTo(false)
}
companion object {
diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaPriorNotificationPdfDocumentRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaPriorNotificationPdfDocumentRepositoryITests.kt
index 7d8a5dc381..dbdce51c43 100644
--- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaPriorNotificationPdfDocumentRepositoryITests.kt
+++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaPriorNotificationPdfDocumentRepositoryITests.kt
@@ -1,7 +1,10 @@
package fr.gouv.cnsp.monitorfish.infrastructure.database.repositories
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationSource
+import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageErrorCode
+import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageException
import org.assertj.core.api.Assertions.assertThat
+import org.assertj.core.api.Assertions.catchThrowable
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.transaction.annotation.Transactional
@@ -24,4 +27,24 @@ class JpaPriorNotificationPdfDocumentRepositoryITests : AbstractDBTests() {
assertThat(pdfDocument.generationDatetimeUtc).isEqualTo(ZonedDateTime.parse("2024-07-03T14:45:00Z"))
assertThat(pdfDocument.pdfDocument).isNotNull()
}
+
+ @Test
+ @Transactional
+ fun `deleteByReportId Should delete a pdf document`() {
+ // Given
+ val existingPdfDocument = jpaPriorNotificationPdfDocumentRepository.findByReportId("FAKE_OPERATION_102")
+ assertThat(existingPdfDocument.reportId).isEqualTo("FAKE_OPERATION_102")
+
+ // When
+ jpaPriorNotificationPdfDocumentRepository.deleteByReportId("FAKE_OPERATION_102")
+
+ // Then
+ val throwable = catchThrowable {
+ jpaPriorNotificationPdfDocumentRepository.findByReportId("FAKE_OPERATION_102")
+ }
+
+ // Then
+ assertThat(throwable).isNotNull()
+ assertThat((throwable as BackendUsageException).code).isEqualTo(BackendUsageErrorCode.NOT_FOUND)
+ }
}
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 a6723d5400..6de8e6b4d1 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
@@ -184,7 +184,7 @@ context('Side Window > Prior Notification Card > Card', () => {
})
})
- it('Should update a note', () => {
+ it('Should update a note and delete the current PDF', () => {
// Given
openSideWindowPriorNotification(`CALAMARO`)
cy.get('*[name="note"]').should('have.value', '')
@@ -195,7 +195,11 @@ context('Side Window > Prior Notification Card > Card', () => {
cy.get('*[name="note"]').should('have.value', "Un point d'attention.")
cy.wait('@updatePriorNotificationNote')
- // Then, the note is saved
+ // Then, the PDF is deleted
+ cy.clickButton('Télécharger les documents')
+ cy.get('li[aria-disabled="true"]').contains('Préavis de débarquement (Document non généré)')
+
+ // The note is saved
openSideWindowPriorNotification(`CALAMARO`)
cy.get('*[name="note"]').should('have.value', "Un point d'attention.")
})
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 739bfc68f8..2c0fc1d0d7 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
@@ -323,6 +323,9 @@ context('Side Window > Prior Notification Form > Form', () => {
reportId: createdPriorNotification.reportId
})
+ cy.clickButton('Télécharger les documents')
+ cy.get('li[aria-disabled="true"]').contains('Préavis de débarquement (Document non généré)')
+
// -----------------------------------------------------------------------
// List
@@ -429,6 +432,24 @@ context('Side Window > Prior Notification Form > Form', () => {
cy.countRequestsByAlias('@computePriorNotification', 1500).should('be.equal', 6)
})
+ it('Should permit the resending of an updated manual PNO', () => {
+ // Given
+ cy.intercept(
+ 'POST',
+ '/bff/v1/prior_notifications/00000000-0000-4000-0000-000000000002/verify_and_send?isManuallyCreated=true'
+ ).as('verifyAndSendPriorNotification')
+ editSideWindowPriorNotification('DOS FIN', '00000000-0000-4000-0000-000000000002')
+ cy.get('button').contains('Diffusé')
+ cy.fill('Saisi par', 'BOB')
+
+ // When
+ cy.clickButton('Enregistrer')
+
+ // Then
+ cy.clickButton('Diffuser')
+ cy.wait('@verifyAndSendPriorNotification')
+ })
+
it('Should verify and send a manual prior notification', () => {
// -------------------------------------------------------------------------
// Add
@@ -484,7 +505,7 @@ context('Side Window > Prior Notification Form > Form', () => {
cy.get('.Element-Tag').contains('Hors diffusion').should('exist')
// -----------------------------------------------------------------------
- // Veryify and send
+ // Verify and send
cy.intercept(
'POST',
diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts
index 257ee7e8fa..b91d160d2b 100644
--- a/frontend/src/api/types.ts
+++ b/frontend/src/api/types.ts
@@ -7,7 +7,7 @@ export type RTKBaseQueryArgs =
// Mutation
| {
body?: AnyObject
- method: 'DELETE' | 'POST' | 'PUT'
+ method: 'GET' | 'DELETE' | 'POST' | 'PUT'
/** URL Path (and not full URL). */
url: string
}
diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx
index d1fba2e6b5..5dcdc21a41 100644
--- a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx
+++ b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx
@@ -56,7 +56,6 @@ export function PriorNotificationCard() {
state => state.displayedError.sideWindowPriorNotificationCardError
)
const [isLoading, setIsLoading] = useState(false)
- const isPendingSend = priorNotificationDetail?.state === PriorNotification.State.PENDING_SEND
const isSent = [PriorNotification.State.SENT, PriorNotification.State.VERIFIED_AND_SENT].includes(
priorNotificationDetail?.state as any
)
@@ -180,7 +179,7 @@ export function PriorNotificationCard() {
>
@@ -193,14 +192,13 @@ export function PriorNotificationCard() {