diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlEntity.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlEntity.kt index 4fd670e17..e58f3fdd0 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlEntity.kt @@ -12,6 +12,10 @@ data class EnvActionControlEntity( override val geom: Geometry? = null, override val facade: String? = null, override val department: String? = null, + override val isAdministrativeControl: Boolean? = null, + override val isComplianceWithWaterRegulationsControl: Boolean? = null, + override val isSafetyEquipmentAndStandardsComplianceControl: Boolean? = null, + override val isSeafarersControl: Boolean? = null, val themes: List? = listOf(), val observations: String? = null, val actionNumberOfControls: Int? = null, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlProperties.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlProperties.kt index 8492bea50..933e5b3c3 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlProperties.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlProperties.kt @@ -20,6 +20,10 @@ data class EnvActionControlProperties( facade: String?, department: String?, geom: Geometry?, + isAdministrativeControl: Boolean?, + isComplianceWithWaterRegulationsControl: Boolean?, + isSafetyEquipmentAndStandardsComplianceControl: Boolean?, + isSeafarersControl: Boolean?, ) = EnvActionControlEntity( id = id, actionStartDateTimeUtc = actionStartDateTimeUtc, @@ -33,7 +37,12 @@ data class EnvActionControlProperties( actionTargetType = actionTargetType, vehicleType = vehicleType, infractions = infractions, + isAdministrativeControl = isAdministrativeControl, + isComplianceWithWaterRegulationsControl = isComplianceWithWaterRegulationsControl, + isSafetyEquipmentAndStandardsComplianceControl = isSafetyEquipmentAndStandardsComplianceControl, + isSeafarersControl = isSeafarersControl, ) + companion object { fun fromEnvActionControlEntity(envAction: EnvActionControlEntity) = EnvActionControlProperties( themes = envAction.themes, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionEntity.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionEntity.kt index 1d493ca0b..a7abbe5c5 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionEntity.kt @@ -1,4 +1,5 @@ package fr.gouv.cacem.monitorenv.domain.entities.mission + import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import org.locationtech.jts.geom.Geometry @@ -24,4 +25,8 @@ abstract class EnvActionEntity( open val department: String? = null, open val facade: String? = null, open val geom: Geometry? = null, + open val isAdministrativeControl: Boolean? = null, + open val isComplianceWithWaterRegulationsControl: Boolean? = null, + open val isSafetyEquipmentAndStandardsComplianceControl: Boolean? = null, + open val isSeafarersControl: Boolean? = null, ) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/mappers/EnvActionMapper.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/mappers/EnvActionMapper.kt index 4d3dd7f58..4788457d7 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/mappers/EnvActionMapper.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/mappers/EnvActionMapper.kt @@ -29,6 +29,10 @@ object EnvActionMapper { facade: String?, department: String?, value: String?, + isAdministrativeControl: Boolean?, + isComplianceWithWaterRegulationsControl: Boolean?, + isSafetyEquipmentAndStandardsComplianceControl: Boolean?, + isSeafarersControl: Boolean?, ): EnvActionEntity { return try { if (!value.isNullOrEmpty() && value != jsonbNullString) { @@ -54,6 +58,10 @@ object EnvActionMapper { facade, department, geom, + isAdministrativeControl, + isComplianceWithWaterRegulationsControl, + isSafetyEquipmentAndStandardsComplianceControl, + isSeafarersControl, ) ActionTypeEnum.NOTE -> mapper.readValue( value, @@ -67,6 +75,7 @@ object EnvActionMapper { throw EntityConversionException("Error while converting 'action'. $value", e) } } + fun envActionEntityToJSON(mapper: ObjectMapper, envAction: EnvActionEntity): String { return try { when (envAction.actionType) { diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/CreateOrUpdateEnvActionDataInput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/CreateOrUpdateEnvActionDataInput.kt new file mode 100644 index 000000000..c918250a9 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/CreateOrUpdateEnvActionDataInput.kt @@ -0,0 +1,86 @@ +package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs + +import fr.gouv.cacem.monitorenv.domain.entities.VehicleTypeEnum +import fr.gouv.cacem.monitorenv.domain.entities.mission.ActionTargetTypeEnum +import fr.gouv.cacem.monitorenv.domain.entities.mission.ActionTypeEnum +import fr.gouv.cacem.monitorenv.domain.entities.mission.EnvActionControlEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.EnvActionEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.EnvActionNoteEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.EnvActionSurveillanceEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.InfractionEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.ThemeEntity +import org.locationtech.jts.geom.Geometry +import java.time.ZonedDateTime +import java.util.UUID + +data class CreateOrUpdateEnvActionDataInput( + val id: UUID, + val actionType: ActionTypeEnum, + val actionStartDateTimeUtc: ZonedDateTime? = null, + val actionEndDateTimeUtc: ZonedDateTime? = null, + val department: String? = null, + val facade: String? = null, + val geom: Geometry? = null, + val observations: String? = null, + val isAdministrativeControl: Boolean? = false, + val isComplianceWithWaterRegulationsControl: Boolean? = false, + val isSafetyEquipmentAndStandardsComplianceControl: Boolean? = false, + val isSeafarersControl: Boolean? = false, + val themes: List? = listOf(), + val actionNumberOfControls: Int? = null, + val actionTargetType: ActionTargetTypeEnum? = null, + val vehicleType: VehicleTypeEnum? = null, + val infractions: List? = listOf(), + val coverMissionZone: Boolean? = null, +) { + fun toEnvActionEntity(): EnvActionEntity { + if (this.actionType == null) throw IllegalArgumentException("actionType is required") + + when (actionType) { + ActionTypeEnum.CONTROL -> { + return EnvActionControlEntity( + id = id, + actionStartDateTimeUtc = actionStartDateTimeUtc, + actionEndDateTimeUtc = actionEndDateTimeUtc, + department = department, + facade = facade, + geom = geom, + isAdministrativeControl = isAdministrativeControl ?: false, + isComplianceWithWaterRegulationsControl = isComplianceWithWaterRegulationsControl ?: false, + isSafetyEquipmentAndStandardsComplianceControl = isSafetyEquipmentAndStandardsComplianceControl ?: false, + isSeafarersControl = isSeafarersControl ?: false, + themes = themes, + observations = observations, + actionNumberOfControls = actionNumberOfControls, + actionTargetType = actionTargetType, + vehicleType = vehicleType, + infractions = infractions, + ) + } + ActionTypeEnum.SURVEILLANCE -> { + return EnvActionSurveillanceEntity( + id = id, + actionStartDateTimeUtc = actionStartDateTimeUtc, + actionEndDateTimeUtc = actionEndDateTimeUtc, + department = department, + facade = facade, + geom = geom, + themes = themes, + observations = observations, + coverMissionZone = coverMissionZone, + ) + } + ActionTypeEnum.NOTE -> { + return EnvActionNoteEntity( + id = id, + actionStartDateTimeUtc = actionStartDateTimeUtc, + actionEndDateTimeUtc = actionEndDateTimeUtc, + observations = observations, + ) + } + else -> { + throw IllegalArgumentException("actionType $actionType is not valid") + } + } + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/EnvActionModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/EnvActionModel.kt index 0f3eabb67..ffe07c900 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/EnvActionModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/EnvActionModel.kt @@ -60,8 +60,10 @@ data class EnvActionModel( @Type(JsonBinaryType::class) @Column(name = "value", columnDefinition = "jsonb") val value: String, + @Column(name = "facade") val facade: String? = null, + @Column(name = "department") val department: String? = null, @@ -69,19 +71,35 @@ data class EnvActionModel( @JoinColumn(name = "mission_id") @JsonBackReference val mission: MissionModel, + + @Column(name = "is_administrative_control") + val isAdministrativeControl: Boolean? = null, + + @Column(name = "is_compliance_with_water_regulations_control") + val isComplianceWithWaterRegulationsControl: Boolean? = null, + + @Column(name = "is_safety_equipment_and_standards_compliance_control") + val isSafetyEquipmentAndStandardsComplianceControl: Boolean? = null, + + @Column(name = "is_seafarers_control") + val isSeafarersControl: Boolean? = null, ) { fun toActionEntity(mapper: ObjectMapper): EnvActionEntity { return EnvActionMapper.getEnvActionEntityFromJSON( - mapper, - id, - actionStartDateTime?.atZone(UTC), - actionEndDateTime?.atZone(UTC), - geom, - actionType, - facade, - department, - value, + mapper = mapper, + id = id, + actionStartDateTimeUtc = actionStartDateTime?.atZone(UTC), + actionEndDateTimeUtc = actionEndDateTime?.atZone(UTC), + geom = geom, + actionType = actionType, + facade = facade, + department = department, + value = value, + isAdministrativeControl = isAdministrativeControl, + isComplianceWithWaterRegulationsControl = isComplianceWithWaterRegulationsControl, + isSafetyEquipmentAndStandardsComplianceControl = isSafetyEquipmentAndStandardsComplianceControl, + isSeafarersControl = isSeafarersControl, ) } companion object { @@ -95,6 +113,10 @@ data class EnvActionModel( value = EnvActionMapper.envActionEntityToJSON(mapper, action), mission = mission, geom = action.geom, + isAdministrativeControl = action.isAdministrativeControl, + isComplianceWithWaterRegulationsControl = action.isComplianceWithWaterRegulationsControl, + isSafetyEquipmentAndStandardsComplianceControl = action.isSafetyEquipmentAndStandardsComplianceControl, + isSeafarersControl = action.isSeafarersControl, ) } @@ -110,6 +132,6 @@ data class EnvActionModel( @Override override fun toString(): String { - return this::class.simpleName + "(id = $id , geom = $geom , actionStartDateTime = $actionStartDateTime, actionEndDateTime = $actionEndDateTime, actionType = $actionType , value = $value )" + return this::class.simpleName + "(id = $id , geom = $geom , actionStartDateTime = $actionStartDateTime, actionEndDateTime = $actionEndDateTime, actionType = $actionType , value = $value, facade = $facade, department = $department, isAdministrativeControl = $isAdministrativeControl, isComplianceWithWaterRegulationsControl = $isComplianceWithWaterRegulationsControl, isSeafarersControl = $isSeafarersControl, isSafetyEquipmentAndStandardsComplianceControl = $isSafetyEquipmentAndStandardsComplianceControl )" } } diff --git a/backend/src/main/resources/db/migration/internal/V0.099__add_other_control_types_rapportnav.sql b/backend/src/main/resources/db/migration/internal/V0.099__add_other_control_types_rapportnav.sql new file mode 100644 index 000000000..33cb9fb7c --- /dev/null +++ b/backend/src/main/resources/db/migration/internal/V0.099__add_other_control_types_rapportnav.sql @@ -0,0 +1,5 @@ +ALTER TABLE env_actions + ADD COLUMN is_administrative_control boolean, + ADD COLUMN is_compliance_with_water_regulations_control boolean, + ADD COLUMN is_safety_equipment_and_standards_compliance_control boolean, + ADD COLUMN is_seafarers_control boolean; diff --git a/backend/src/main/resources/db/testdata/V666.4__insert_dummy_control_units.sql b/backend/src/main/resources/db/testdata/V666.4__insert_dummy_control_units.sql index 3384c3515..4d9f90938 100644 --- a/backend/src/main/resources/db/testdata/V666.4__insert_dummy_control_units.sql +++ b/backend/src/main/resources/db/testdata/V666.4__insert_dummy_control_units.sql @@ -12,8 +12,6 @@ VALUES (10006, 1005, false, 'SML 33'), (10007, 1005, false, 'SML 50'), (10008, 1005, false, 'Police de l''eau – DDTM 11'), - (10009, 1009, false, 'PAM Jeanne Barret'), - (10010, 1009, false, 'PAM Themis'), (10011, 1009, false, 'Cross Etel'), (10012, 1009, false, 'Cross Gris Nez'), (10013, 2, true, 'BGC Ajaccio'), @@ -37,7 +35,13 @@ VALUES (10031, 1004, false, 'Réserve Naturelle 7 Iles'), (10032, 1005, false, 'Cultures marines – DDTM 30'); -SELECT setval('control_units_id_seq', 10032, true); +INSERT INTO public.control_units( + id, administration_id, name) VALUES + ( 10121, 1009, 'PAM Jeanne Barret'), + ( 10080, 1009, 'PAM Themis'); + + +SELECT setval('control_units_id_seq', (SELECT max(id) FROM control_units), true); INSERT INTO public.control_unit_contacts ( id, control_unit_id, name) @@ -58,8 +62,8 @@ VALUES ( 5, 10002, 'Voiture', 3, 'CAR'), ( 6, 10003, 'AR VECHEN', 2, 'FRIGATE'), ( 7, 10003, 'Semi-rigide', 3, 'BARGE'), - ( 8, 10010, 'PAM Jeanne Barret', 3, 'FRIGATE'), - ( 9, 10011, 'PAM Themis', 3, 'FRIGATE'), + ( 8, 10121, 'PAM Jeanne Barret', 3, 'FRIGATE'), + ( 9, 10080, 'PAM Themis', 3, 'FRIGATE'), ( 10, 10018, 'ALTAIR', 3, 'FRIGATE'), ( 11, 10018, 'PHEROUSA', 3, 'FRIGATE'), ( 12, 10018, 'ARIOLA', 3, 'FRIGATE'); diff --git a/backend/src/main/resources/db/testdata/V666.5__insert_dummy_missions.sql b/backend/src/main/resources/db/testdata/V666.5__insert_dummy_missions.sql index fbdfd2b84..7454cbcc4 100644 --- a/backend/src/main/resources/db/testdata/V666.5__insert_dummy_missions.sql +++ b/backend/src/main/resources/db/testdata/V666.5__insert_dummy_missions.sql @@ -60,7 +60,7 @@ INSERT INTO missions_control_units (mission_id, control_unit_id) VALUES ( 0, 10002), - ( 1, 10011), + ( 1, 10080), ( 2, 10002), ( 3, 10004), ( 4, 10003), @@ -82,10 +82,10 @@ VALUES ( 19, 10018), ( 20, 10002), ( 21, 10018), - ( 22, 10010), - ( 23, 10010), + ( 22, 10121), + ( 23, 10121), ( 24, 10000), - ( 26, 10011), + ( 26, 10080), ( 27, 10003), ( 28, 10019), ( 29, 10018), @@ -105,7 +105,7 @@ VALUES ( 44, 10003), ( 45, 10014), ( 46, 10018), - ( 48, 10011), + ( 48, 10080), ( 49, 10002), ( 50, 10002), ( 51, 10002), diff --git a/backend/src/main/resources/db/testdata/V666.6__insert_dummy_env_actions.sql b/backend/src/main/resources/db/testdata/V666.6__insert_dummy_env_actions.sql index 7e882b68c..e598a2cbe 100644 --- a/backend/src/main/resources/db/testdata/V666.6__insert_dummy_env_actions.sql +++ b/backend/src/main/resources/db/testdata/V666.6__insert_dummy_env_actions.sql @@ -13,6 +13,8 @@ INSERT INTO public.env_actions VALUES ('6d4b7d0a-79ce-47cf-ac26-2024d2b27f28', 4 INSERT INTO public.env_actions VALUES ('b8007c8a-5135-4bc3-816f-c69c7b75d807', 34, 'SURVEILLANCE', '{"themes": [{"theme": "Police des espèces protégées et de leurs habitats (faune et flore)", "subThemes": ["Destruction, capture, arrachage", "Atteinte aux habitats d''espèces protégées"], "protectedSpecies": ["FLORA", "BIRDS"]}, {"theme": "Police des mouillages", "subThemes": ["Mouillage individuel", "ZMEL"], "protectedSpecies": []}], "duration": 0.0, "observations": "RAS", "coverMissionZone": true, "protectedSpecies": []}', '2022-07-16 10:03:12.588693', NULL, NULL, NULL, '2022-07-16 12:03:12.588693'); INSERT INTO public.env_actions VALUES ('c52c6f20-e495-4b29-b3df-d7edfb67fdd7', 34, 'CONTROL', '{"themes": [{"theme": "Police des mouillages", "subThemes": ["Mouillage individuel", "ZMEL"], "protectedSpecies": []}], "observations": "RAS", "infractions": [{"id": "5d5b7829-68cd-4436-8c0b-1cc8db7788a0", "natinf": ["10038","10231"], "toProcess": false, "vesselSize": "FROM_24_TO_46m", "vesselType": "COMMERCIAL", "companyName": null, "formalNotice": "PENDING", "observations": "Pas d''observations", "relevantCourt": "LOCAL_COURT", "infractionType": "WITH_REPORT", "registrationNumber": "BALTIK", "controlledPersonIdentity": "John Doe"}], "vehicleType": "VESSEL", "actionTargetType": "VEHICLE", "actionNumberOfControls": 1}', '2022-07-16 09:03:12.588693', '0104000020E610000001000000010100000047A07E6651E3DEBF044620AB65C54840', NULL, NULL, '2022-07-16 12:03:12.588693'); +INSERT INTO public.env_actions VALUES ('4d9a3139-6c60-49a5-b443-0e6238a6a120', 41, 'CONTROL', '{"themes": [{"theme": "Police des mouillages", "subThemes": ["Contrôle administratif"], "protectedSpecies": []}], "infractions": [], "vehicleType": null, "observations": "", "actionTargetType": null, "actionNumberOfControls": null}','2022-07-01 02:44:16.588693', NULL, NULL, NULL, NULL, TRUE, TRUE, TRUE, TRUE); + UPDATE public.env_actions SET action_start_datetime_utc = action_start_datetime_utc + (now() - '2022-06-01 23:00:00'), action_end_datetime_utc = action_end_datetime_utc + (now() - '2022-06-01 23:00:00') diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepositoryITests.kt index 36c1f7e72..8503c7351 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepositoryITests.kt @@ -93,7 +93,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { ControlUnitResourceEntity( id = 8, baseId = 3, - controlUnitId = 10010, + controlUnitId = 10121, name = "PAM Jeanne Barret", note = null, photo = null, @@ -102,7 +102,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { ControlUnitResourceEntity( id = 9, baseId = 3, - controlUnitId = 10011, + controlUnitId = 10080, name = "PAM Themis", note = null, photo = null, diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepositoryITests.kt index 0b50ac7df..4485688c4 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepositoryITests.kt @@ -158,21 +158,47 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { assertThat(foundFullControlUnits[32]).isEqualTo( FullControlUnitDTO( administration = AdministrationEntity( - id = 1005, + id = 1009, isArchived = false, - name = "DDTM", + name = "DIRM / DM", ), controlUnit = ControlUnitEntity( - id = 10032, - administrationId = 1005, + id = 10121, + administrationId = 1009, areaNote = null, departmentAreaInseeCode = null, isArchived = false, - name = "Cultures marines – DDTM 30", + name = "PAM Jeanne Barret", termsNote = null, ), controlUnitContacts = listOf(), - controlUnitResources = listOf(), + controlUnitResources = listOf( + FullControlUnitResourceDTO( + base = BaseEntity( + id = 3, + name = "Dunkerque", + latitude = 51.035534, + longitude = 2.372845, + ), + controlUnit = ControlUnitEntity( + id = 10121, + administrationId = 1009, + areaNote = null, + isArchived = false, + name = "PAM Jeanne Barret", + termsNote = null, + ), + controlUnitResource = ControlUnitResourceEntity( + id = 8, + baseId = 3, + controlUnitId = 10121, + name = "PAM Jeanne Barret", + note = null, + photo = null, + type = ControlUnitResourceType.FRIGATE, + ), + ), + ), ), ) } @@ -289,13 +315,13 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { val createdControlUnit = jpaControlUnitRepository.save(newControlUnit) - assertThat(createdControlUnit).isEqualTo(newControlUnit.copy(id = 10033)) + assertThat(createdControlUnit).isEqualTo(newControlUnit.copy(id = 10122)) // --------------------------------------------------------------------- // Update val nextControlUnit = ControlUnitEntity( - id = 10033, + id = 10122, administrationId = 1, areaNote = "Updated Area Note", departmentAreaInseeCode = "85", @@ -311,10 +337,10 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { // --------------------------------------------------------------------- // Delete - jpaControlUnitRepository.deleteById(10033) + jpaControlUnitRepository.deleteById(10122) val controlUnitIds = jpaControlUnitRepository.findAll().map { requireNotNull(it.controlUnit.id) }.sorted() - assertThat(controlUnitIds).doesNotContain(10033) + assertThat(controlUnitIds).doesNotContain(10122) } } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepositoryITests.kt index 46dd90990..c3ad6fed1 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepositoryITests.kt @@ -64,6 +64,12 @@ class JpaMissionRepositoryITests : AbstractDBTests() { facade = "Facade 1", department = "Department 1", geom = point, + vehicleType = VehicleTypeEnum.VEHICLE_LAND, + isAdministrativeControl = true, + isComplianceWithWaterRegulationsControl = true, + isSafetyEquipmentAndStandardsComplianceControl = true, + isSeafarersControl = true, + ), EnvActionSurveillanceEntity( id = UUID.fromString("a6c4bd17-eb45-4504-ab15-7a18ea714a10"), diff --git a/datascience/tests/test_data/remote_database/V666.1__Reset_test_controllers.sql b/datascience/tests/test_data/remote_database/V666.1__Reset_test_controllers.sql index a360e7dc0..01d6a6f1e 100644 --- a/datascience/tests/test_data/remote_database/V666.1__Reset_test_controllers.sql +++ b/datascience/tests/test_data/remote_database/V666.1__Reset_test_controllers.sql @@ -17,8 +17,6 @@ INSERT INTO public.control_units ( ( 1005, 'SML 33'), ( 1005, 'SML 50'), ( 1005, 'Police de l''eau – DDTM 11'), - ( 1009, 'PAM Jeanne Barret'), - ( 1009, 'PAM Themis'), ( 1009, 'Cross Etel'), ( 1009, 'Cross Gris Nez'), ( 2, 'BGC Ajaccio'), @@ -41,6 +39,13 @@ INSERT INTO public.control_units ( ( 1004, 'Réserve Naturelle de L''Ilot M''Bouzi'), ( 1004, 'Réserve Naturelle 7 Iles'); +INSERT INTO public.control_units( + id, administration_id, name) VALUES + ( 10121, 1009, 'PAM Jeanne Barret'), + ( 10080, 1009, 'PAM Themis'); + + + -- -- Add historic control units -- diff --git a/frontend/cypress/e2e/back_office/control_unit_form.spec.ts b/frontend/cypress/e2e/back_office/control_unit_form.spec.ts index ae06ede2e..1e1e3039a 100644 --- a/frontend/cypress/e2e/back_office/control_unit_form.spec.ts +++ b/frontend/cypress/e2e/back_office/control_unit_form.spec.ts @@ -43,11 +43,11 @@ context('Back Office > Control Unit Form', () => { // ------------------------------------------------------------------------- // Edit - cy.intercept('PUT', `/api/v2/control_units/10033`).as('updateControlUnit') + cy.intercept('PUT', `/api/v2/control_units/10122`).as('updateControlUnit') - cy.getTableRowById(10033).clickButton('Éditer cette unité de contrôle') + cy.getTableRowById(10122).clickButton('Éditer cette unité de contrôle') - expectPathToBe('/backoffice/control_units/10033') + expectPathToBe('/backoffice/control_units/10122') cy.fill('Administration', 'AFB') cy.fill('Nom', 'Unité 2') @@ -64,7 +64,7 @@ context('Back Office > Control Unit Form', () => { administrationId: 1002, areaNote: null, departmentAreaInseeCode: '85', - id: 10033, + id: 10122, isArchived: false, name: 'Unité 2', termsNote: null @@ -74,29 +74,29 @@ context('Back Office > Control Unit Form', () => { // ------------------------------------------------------------------------- // Archive - cy.intercept('POST', `/api/v2/control_units/10033/archive`).as('archiveControlUnit') + cy.intercept('POST', `/api/v2/control_units/10122/archive`).as('archiveControlUnit') - cy.getTableRowById(10033).clickButton('Archiver cette unité de contrôle') + cy.getTableRowById(10122).clickButton('Archiver cette unité de contrôle') cy.clickButton('Archiver') cy.wait('@archiveControlUnit') - cy.getTableRowById(10033).should('not.exist') + cy.getTableRowById(10122).should('not.exist') cy.clickButton('Unités archivées') - cy.getTableRowById(10033).should('exist') + cy.getTableRowById(10122).should('exist') // ------------------------------------------------------------------------- // Delete - cy.intercept('DELETE', `/api/v2/control_units/10033`).as('deleteControlUnit') + cy.intercept('DELETE', `/api/v2/control_units/10122`).as('deleteControlUnit') - cy.getTableRowById(10033).clickButton('Supprimer cette unité de contrôle') + cy.getTableRowById(10122).clickButton('Supprimer cette unité de contrôle') cy.clickButton('Supprimer') cy.wait('@deleteControlUnit') - cy.getTableRowById(10033).should('not.exist') + cy.getTableRowById(10122).should('not.exist') cy.clickButton('Unités actives') - cy.getTableRowById(10033).should('not.exist') + cy.getTableRowById(10122).should('not.exist') }) }) diff --git a/frontend/cypress/e2e/side_window/mission/mission_dates_validation.spec.ts b/frontend/cypress/e2e/side_window/mission/mission_dates_validation.spec.ts index c0c6d4356..9da3bb198 100644 --- a/frontend/cypress/e2e/side_window/mission/mission_dates_validation.spec.ts +++ b/frontend/cypress/e2e/side_window/mission/mission_dates_validation.spec.ts @@ -203,4 +203,52 @@ context('Mission dates', () => { expect(response && response.statusCode).equal(200) }) }) + + it('save other control actions', () => { + // Given + cy.get('*[data-cy="edit-mission-41"]').click({ force: true }) + cy.get('*[data-cy="action-card"]').eq(0).click() + + cy.get('*[data-cy="add-control-administration"]').click() + cy.get('.rs-picker-search-bar-input').type('DIRM{enter}') + + cy.get('*[data-cy="add-control-unit"]').click() + cy.get('*[data-key="10080"]').click() + cy.get('*[data-cy="control-unit-contact"]').type('Contact 012345') + + // When + cy.fill('Contrôle administratif', false) + cy.fill('Respect du code de la navigation sur le plan d’eau', false) + cy.fill('Gens de mer', false) + cy.fill('Equipement de sécurité et respect des normes', false) + + cy.intercept('PUT', `/bff/v1/missions/41`).as('updateMission') + cy.get('form').submit() + + // Then + cy.wait('@updateMission').then(({ request, response }) => { + expect(response && response.statusCode).equal(200) + + const controlActionRequest = request.body.envActions[0] + expect(controlActionRequest.isAdministrativeControl).equal(false) + expect(controlActionRequest.isComplianceWithWaterRegulationsControl).equal(false) + expect(controlActionRequest.isSeafarersControl).equal(false) + expect(controlActionRequest.isSafetyEquipmentAndStandardsComplianceControl).equal(false) + + const controlActionResponse = response?.body.envActions[0] + expect(controlActionResponse.isAdministrativeControl).equal(false) + expect(controlActionResponse.isComplianceWithWaterRegulationsControl).equal(false) + expect(controlActionResponse.isSeafarersControl).equal(false) + expect(controlActionResponse.isSafetyEquipmentAndStandardsComplianceControl).equal(false) + }) + + // Revert + cy.get('*[data-cy="edit-mission-41"]').click({ force: true }) + cy.get('*[data-cy="action-card"]').eq(0).click() + cy.fill('Contrôle administratif', true) + cy.fill('Respect du code de la navigation sur le plan d’eau', true) + cy.fill('Gens de mer', true) + cy.fill('Equipement de sécurité et respect des normes', true) + cy.get('form').submit() + }) }) diff --git a/frontend/cypress/e2e/side_window/mission_list/missions.spec.ts b/frontend/cypress/e2e/side_window/mission_list/missions.spec.ts index c245c9286..4b88d4f1f 100644 --- a/frontend/cypress/e2e/side_window/mission_list/missions.spec.ts +++ b/frontend/cypress/e2e/side_window/mission_list/missions.spec.ts @@ -41,7 +41,7 @@ context('Missions', () => { cy.log('Units should be filtered') cy.get('*[data-cy="edit-mission-38"]').should('exist') cy.get('*[data-cy="select-units-filter"]').click() - cy.get('div[role="option"]').find('label').contains('Cross Etel').click() + cy.get('div[role="option"]').find('label').contains('PAM Themis').click({ force: true }) cy.get('*[data-cy="edit-mission-48"]').should('exist') cy.get('*[data-cy="edit-mission-38"]').should('not.exist') diff --git a/frontend/src/domain/entities/controlUnit.ts b/frontend/src/domain/entities/controlUnit.ts index eab10e825..1025949a4 100644 --- a/frontend/src/domain/entities/controlUnit.ts +++ b/frontend/src/domain/entities/controlUnit.ts @@ -128,6 +128,14 @@ export namespace ControlUnit { TUGBOAT = 'Remorqueur' } + // List of PAM units identifiers + // 10141 PAM Gyptis + // 10404 PAM Iris + // 10121 PAM Jeanne Barret + // 10345 PAM Osiris + // 10080 PAM Themis + export const PAMControlUnitIds = [10141, 10404, 10121, 10345, 10080] + // --------------------------------------------------------------------------- // Types diff --git a/frontend/src/domain/entities/missions.ts b/frontend/src/domain/entities/missions.ts index fd843e313..2d1f8758d 100644 --- a/frontend/src/domain/entities/missions.ts +++ b/frontend/src/domain/entities/missions.ts @@ -292,6 +292,10 @@ export type NewEnvActionControl = EnvActionCommonProperties & { actionTargetType?: string actionType: ActionTypeEnum.CONTROL infractions: Infraction[] + isAdministrativeControl?: boolean + isComplianceWithWaterRegulationsControl?: boolean + isSafetyEquipmentAndStandardsComplianceControl?: boolean + isSeafarersControl?: boolean observations: string | null themes: EnvActionTheme[] vehicleType?: string diff --git a/frontend/src/features/missions/MissionForm/ActionForm/ControlForm/ControlForm.tsx b/frontend/src/features/missions/MissionForm/ActionForm/ControlForm/ControlForm.tsx index 32d2b40a6..1370ec34e 100644 --- a/frontend/src/features/missions/MissionForm/ActionForm/ControlForm/ControlForm.tsx +++ b/frontend/src/features/missions/MissionForm/ActionForm/ControlForm/ControlForm.tsx @@ -13,6 +13,7 @@ import { Form, IconButton } from 'rsuite' import styled from 'styled-components' import { InfractionsForm } from './InfractionsForm' +import { OtherControlTypesForm } from './OtherControlTypesForm' import { TargetTypeEnum, TargetTypeLabels } from '../../../../../domain/entities/targetType' import { VehicleTypeEnum } from '../../../../../domain/entities/vehicleType' import { ReactComponent as ControlIconSVG } from '../../../../../uiMonitor/icons/Control.svg' @@ -199,7 +200,12 @@ export function ControlForm({ )} validateOnChange={false} /> - + + ) @@ -212,6 +218,7 @@ const Header = styled.div` const FormBody = styled.div` display: flex; flex-direction: column; + padding-bottom: 48px; ` const Title = styled.h2` font-size: 16px; @@ -254,3 +261,7 @@ const DeleteIcon = styled(DeleteSVG)` const IconButtonRight = styled(IconButton)` margin-left: auto; ` + +const StyledFormikTextareaWithMargin = styled(FormikTextarea)` + margin-top: 24px; +` diff --git a/frontend/src/features/missions/MissionForm/ActionForm/ControlForm/OtherControlTypesForm.tsx b/frontend/src/features/missions/MissionForm/ActionForm/ControlForm/OtherControlTypesForm.tsx new file mode 100644 index 000000000..3829de99d --- /dev/null +++ b/frontend/src/features/missions/MissionForm/ActionForm/ControlForm/OtherControlTypesForm.tsx @@ -0,0 +1,56 @@ +import { FormikCheckbox } from '@mtes-mct/monitor-ui' +import { useFormikContext } from 'formik' +import styled from 'styled-components' + +import { ControlUnit } from '../../../../../domain/entities/controlUnit' + +import type { Mission } from '../../../../../domain/entities/missions' + +export function OtherControlTypesForm({ currentActionIndex }: { currentActionIndex: number }) { + const { + values: { controlUnits } + } = useFormikContext() + + const currentControlUnitIsPAM = controlUnits.some(controlUnit => + ControlUnit.PAMControlUnitIds.includes(controlUnit.id) + ) + + if (!currentControlUnitIsPAM) { + return null + } + + return ( + <> + + + + + + + ) +} + +const Label = styled.span` + margin-top: 24px; + font-size: 13px; + line-height: 22px; + display: inline-block; + color: ${p => p.theme.color.slateGray}; +` + +const StyledFormikCheckbox = styled(FormikCheckbox)` + height: 50px; + background-color: ${p => p.theme.color.white}; + margin-bottom: 4px; + justify-content: center; + padding: 16px; +` diff --git a/frontend/src/features/missions/MissionForm/MissionForm.tsx b/frontend/src/features/missions/MissionForm/MissionForm.tsx index 80bec4b2a..53b54653e 100644 --- a/frontend/src/features/missions/MissionForm/MissionForm.tsx +++ b/frontend/src/features/missions/MissionForm/MissionForm.tsx @@ -8,6 +8,7 @@ import { ActionForm } from './ActionForm/ActionForm' import { ActionsForm } from './ActionsForm' import { GeneralInformationsForm } from './GeneralInformationsForm' import { useSyncFormValuesWithRedux } from './hooks/useSyncFormValuesWithRedux' +import { useUpdateOtherControlTypes } from './hooks/useUpdateOtherControlTypes' import { useUpdateSurveillance } from './hooks/useUpdateSurveillance' import { MissionFormBottomBar } from './MissionFormBottomBar' import { CancelEditModal } from './modals/CancelEditModal' @@ -32,6 +33,7 @@ export function MissionForm({ id, isAlreadyClosed, isNewMission, selectedMission useSyncFormValuesWithRedux() useUpdateSurveillance() + useUpdateOtherControlTypes() useEffect(() => { if (selectedMission) { diff --git a/frontend/src/features/missions/MissionForm/hooks/useUpdateOtherControlTypes.ts b/frontend/src/features/missions/MissionForm/hooks/useUpdateOtherControlTypes.ts new file mode 100644 index 000000000..9d02cddbe --- /dev/null +++ b/frontend/src/features/missions/MissionForm/hooks/useUpdateOtherControlTypes.ts @@ -0,0 +1,38 @@ +import { usePrevious } from '@mtes-mct/monitor-ui' +import { useFormikContext } from 'formik' + +import { ControlUnit } from '../../../../domain/entities/controlUnit' +import { ActionTypeEnum, type Mission } from '../../../../domain/entities/missions' + +export const useUpdateOtherControlTypes = () => { + const { + setFieldValue, + values: { controlUnits, envActions } + } = useFormikContext() + + const currentControlUnitIsPAM = controlUnits.some(controlUnit => + ControlUnit.PAMControlUnitIds.includes(controlUnit.id) + ) + const previousControlUnitIsPAM = usePrevious(currentControlUnitIsPAM) + + if (previousControlUnitIsPAM && !currentControlUnitIsPAM) { + envActions.forEach((action, index) => { + if (action.actionType === ActionTypeEnum.CONTROL) { + setFieldValue(`envActions[${index}].isAdministrativeControl`, null, false) + setFieldValue(`envActions[${index}].isComplianceWithWaterRegulationsControl`, null, false) + setFieldValue(`envActions[${index}].isSafetyEquipmentAndStandardsComplianceControl`, null, false) + setFieldValue(`envActions[${index}].isSeafarersControl`, null, false) + } + }) + } + if (!previousControlUnitIsPAM && currentControlUnitIsPAM) { + envActions.forEach((action, index) => { + if (action.actionType === ActionTypeEnum.CONTROL) { + setFieldValue(`envActions[${index}].isAdministrativeControl`, false, false) + setFieldValue(`envActions[${index}].isComplianceWithWaterRegulationsControl`, false, false) + setFieldValue(`envActions[${index}].isSafetyEquipmentAndStandardsComplianceControl`, false, false) + setFieldValue(`envActions[${index}].isSeafarersControl`, false, false) + } + }) + } +}