From 43575f23dd968f10beff2bc7b5a146383567d0b6 Mon Sep 17 00:00:00 2001 From: Niels Thykier Date: Wed, 13 Dec 2023 10:36:57 +0100 Subject: [PATCH] DT-773: Create scenario with active reefer Signed-off-by: Niels Thykier --- .../standards/ebl/EblScenarioListBuilder.java | 17 +++-- ...arrier_SupplyScenarioParametersAction.java | 59 +++++++++++++---- .../standards/ebl/action/EblAction.java | 3 +- ...r_IssueAmendedTransportDocumentAction.java | 4 +- ...r_PublishDraftTransportDocumentAction.java | 4 +- .../standards/ebl/checks/ScenarioType.java | 7 ++ .../models/CarrierShippingInstructions.java | 29 +++++--- .../ebl/party/DynamicScenarioParameters.java | 66 +++++++++---------- .../standards/ebl/party/EblCarrier.java | 34 +++++++--- 9 files changed, 153 insertions(+), 70 deletions(-) create mode 100644 ebl/src/main/java/org/dcsa/conformance/standards/ebl/checks/ScenarioType.java diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/EblScenarioListBuilder.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/EblScenarioListBuilder.java index f2375ae0..3c51689e 100644 --- a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/EblScenarioListBuilder.java +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/EblScenarioListBuilder.java @@ -8,6 +8,7 @@ import org.dcsa.conformance.core.scenario.ConformanceAction; import org.dcsa.conformance.core.scenario.ScenarioListBuilder; import org.dcsa.conformance.standards.ebl.action.*; +import org.dcsa.conformance.standards.ebl.checks.ScenarioType; import org.dcsa.conformance.standards.ebl.party.ShippingInstructionsStatus; import org.dcsa.conformance.standards.ebl.party.TransportDocumentStatus; @@ -38,7 +39,10 @@ public static EblScenarioListBuilder buildTree( threadLocalComponentFactory.set(componentFactory); threadLocalCarrierPartyName.set(carrierPartyName); threadLocalShipperPartyName.set(shipperPartyName); - return carrier_SupplyScenarioParameters().thenAllPathsFrom(SI_START); + return noAction().thenEither( + carrier_SupplyScenarioParameters(ScenarioType.REGULAR).thenAllPathsFrom(SI_START), + carrier_SupplyScenarioParameters(ScenarioType.REEFER).thenHappyPathFrom(SI_START) + ); } private EblScenarioListBuilder thenAllPathsFrom( @@ -119,6 +123,11 @@ yield thenEither( private EblScenarioListBuilder thenHappyPathFrom( ShippingInstructionsStatus shippingInstructionsStatus) { return switch (shippingInstructionsStatus) { + case SI_START -> then( + uc1_shipper_submitShippingInstructions() + .then( + shipper_GetShippingInstructions(SI_RECEIVED, TD_START) + .thenHappyPathFrom(SI_RECEIVED))); case SI_RECEIVED -> then(uc6_carrier_publishDraftTransportDocument() .then( shipper_GetShippingInstructions(SI_RECEIVED, TD_DRAFT, true) @@ -134,7 +143,7 @@ private EblScenarioListBuilder thenHappyPathFrom( ); case SI_COMPLETED -> then(noAction()); case SI_CANCELLED, SI_DECLINED -> throw new AssertionError("Please use the black state rather than DECLINED"); - case SI_START, SI_ANY -> throw new AssertionError("Not a real/reachable state"); + case SI_ANY -> throw new AssertionError("Not a real/reachable state"); }; } @@ -255,10 +264,10 @@ private static EblScenarioListBuilder noAction() { return new EblScenarioListBuilder(null); } - private static EblScenarioListBuilder carrier_SupplyScenarioParameters() { + private static EblScenarioListBuilder carrier_SupplyScenarioParameters(ScenarioType scenarioType) { String carrierPartyName = threadLocalCarrierPartyName.get(); return new EblScenarioListBuilder( - previousAction -> new Carrier_SupplyScenarioParametersAction(carrierPartyName)); + previousAction -> new Carrier_SupplyScenarioParametersAction(carrierPartyName, scenarioType)); } diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/Carrier_SupplyScenarioParametersAction.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/Carrier_SupplyScenarioParametersAction.java index 63678389..72e68db8 100644 --- a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/Carrier_SupplyScenarioParametersAction.java +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/Carrier_SupplyScenarioParametersAction.java @@ -4,13 +4,29 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.function.Consumer; import java.util.function.Supplier; + +import lombok.NonNull; +import org.dcsa.conformance.standards.ebl.checks.ScenarioType; import org.dcsa.conformance.standards.ebl.party.CarrierScenarioParameters; +import org.dcsa.conformance.standards.ebl.party.DynamicScenarioParameters; public class Carrier_SupplyScenarioParametersAction extends EblAction { private CarrierScenarioParameters carrierScenarioParameters = null; - public Carrier_SupplyScenarioParametersAction(String carrierPartyName) { - super(carrierPartyName, null, null, "SupplyCSP", -1); + private ScenarioType scenarioType; + + public Carrier_SupplyScenarioParametersAction(String carrierPartyName, @NonNull ScenarioType scenarioType) { + super( + carrierPartyName, + null, + null, + switch (scenarioType) { + case REGULAR -> "SupplyCSP"; + case REEFER -> "SupplyCSP-AR"; + }, + -1); + this.scenarioType = scenarioType; + this.getDspConsumer().accept(getDspSupplier().get().withScenarioType(scenarioType)); } @Override @@ -19,13 +35,19 @@ public void reset() { carrierScenarioParameters = null; } + @Override + public ObjectNode asJsonNode() { + return super.asJsonNode() + .put("scenarioType", scenarioType.name()); + } + @Override public ObjectNode exportJsonState() { ObjectNode jsonState = super.exportJsonState(); if (carrierScenarioParameters != null) { jsonState.set("carrierScenarioParameters", carrierScenarioParameters.toJson()); } - return jsonState; + return jsonState.put("scenarioType", scenarioType.name()); } @Override @@ -35,6 +57,7 @@ public void importJsonState(JsonNode jsonState) { if (cspNode != null) { carrierScenarioParameters = CarrierScenarioParameters.fromJson(cspNode); } + this.scenarioType = ScenarioType.valueOf(jsonState.required("scenarioType").asText()); } @Override @@ -44,15 +67,27 @@ public String getHumanReadablePrompt() { @Override public JsonNode getJsonForHumanReadablePrompt() { - return new CarrierScenarioParameters( - "Booking ref X", - "Commodity Subref for X", - "APZU4812090", - "DKCPH", - "Consignment Item HS Code", - "Shoes" - - ).toJson(); + var csp = switch (scenarioType) { + case REGULAR -> new CarrierScenarioParameters( + "Booking Reference", + "Commodity subreference for regular (non-DG, non-reefer) cargo", + // Any valid regular equipment reference will do as an example. + "NARU3472484", + "DKCPH", + "851712", + "300 boxes of blue shoes size 47" + ); + case REEFER -> new CarrierScenarioParameters( + "Booking Reference", + "Commodity subreference for cargo requiring an *active* reefer", + // Any valid reefer equipment reference will do as an example. + "BBCU5200220", + "DKCPH", + "04052090", + "Dairy products" + ); + }; + return csp.toJson(); } @Override diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/EblAction.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/EblAction.java index ba89f9dc..f08ae6d1 100644 --- a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/EblAction.java +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/EblAction.java @@ -8,6 +8,7 @@ import org.dcsa.conformance.core.traffic.ConformanceExchange; import org.dcsa.conformance.core.traffic.HttpMessageType; import org.dcsa.conformance.standards.ebl.checks.EBLChecks; +import org.dcsa.conformance.standards.ebl.checks.ScenarioType; import org.dcsa.conformance.standards.ebl.party.*; import java.util.UUID; @@ -31,7 +32,7 @@ public EblAction( this.dspReference = previousAction == null ? new OverwritingReference<>( - null, new DynamicScenarioParameters(null, null, null, null, null)) + null, new DynamicScenarioParameters(ScenarioType.REGULAR, null, null, null, null, null)) : new OverwritingReference<>(previousAction.dspReference, null); } diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/UC11i_Carrier_IssueAmendedTransportDocumentAction.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/UC11i_Carrier_IssueAmendedTransportDocumentAction.java index 72a5a9c0..3c3a1b1c 100644 --- a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/UC11i_Carrier_IssueAmendedTransportDocumentAction.java +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/UC11i_Carrier_IssueAmendedTransportDocumentAction.java @@ -28,8 +28,10 @@ public String getHumanReadablePrompt() { @Override public ObjectNode asJsonNode() { + var dsp = getDspSupplier().get(); return super.asJsonNode() - .put("documentReference", getDspSupplier().get().transportDocumentReference()); + .put("documentReference", dsp.transportDocumentReference()) + .put("scenarioType", dsp.scenarioType().name()); } @Override diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/UC6_Carrier_PublishDraftTransportDocumentAction.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/UC6_Carrier_PublishDraftTransportDocumentAction.java index 725abe51..714fcdb2 100644 --- a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/UC6_Carrier_PublishDraftTransportDocumentAction.java +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/action/UC6_Carrier_PublishDraftTransportDocumentAction.java @@ -28,8 +28,10 @@ public String getHumanReadablePrompt() { @Override public ObjectNode asJsonNode() { + var dsp = getDspSupplier().get(); return super.asJsonNode() - .put("documentReference", getDspSupplier().get().shippingInstructionsReference()); + .put("documentReference", dsp.shippingInstructionsReference()) + .put("scenarioType", dsp.scenarioType().name()); } @Override diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/checks/ScenarioType.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/checks/ScenarioType.java new file mode 100644 index 00000000..5e1cc108 --- /dev/null +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/checks/ScenarioType.java @@ -0,0 +1,7 @@ +package org.dcsa.conformance.standards.ebl.checks; + +public enum ScenarioType { + REGULAR, + REEFER, + ; +} diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/models/CarrierShippingInstructions.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/models/CarrierShippingInstructions.java index b4bce5d7..d684b450 100644 --- a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/models/CarrierShippingInstructions.java +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/models/CarrierShippingInstructions.java @@ -11,6 +11,7 @@ import java.util.*; import java.util.function.*; import org.dcsa.conformance.core.state.JsonNodeMap; +import org.dcsa.conformance.standards.ebl.checks.ScenarioType; import org.dcsa.conformance.standards.ebl.party.ShippingInstructionsStatus; import org.dcsa.conformance.standards.ebl.party.TransportDocumentStatus; @@ -327,9 +328,9 @@ public void voidTransportDocument(String documentReference) { td.put(TRANSPORT_DOCUMENT_STATUS, TD_VOIDED.wireName()); } - public void issueAmendedTransportDocument(String documentReference) { + public void issueAmendedTransportDocument(String documentReference, ScenarioType scenarioType) { checkState(documentReference, getTransportDocumentState(), s -> s == TD_VOIDED); - this.generateDraftTD(); + this.generateDraftTD(scenarioType); updateTDForIssuance(); var tdData = getTransportDocument().orElseThrow(); var tdr = tdData.required(TRANSPORT_DOCUMENT_REFERENCE).asText(); @@ -354,13 +355,13 @@ public void rejectSurrenderForDelivery(String documentReference) { td.put(TRANSPORT_DOCUMENT_STATUS, TD_ISSUED.wireName()); } - public void publishDraftTransportDocument(String documentReference) { + public void publishDraftTransportDocument(String documentReference, ScenarioType scenarioType) { // We allow draft when: // 1) The original ("black") state is RECEIVED, *and* // 2) There is no update received (that is "grey" is not UPDATE_RECEIVED) checkState(documentReference, getOriginalShippingInstructionState(), s -> s == SI_RECEIVED); checkState(documentReference, getOriginalShippingInstructionState(), s -> s != SI_UPDATE_RECEIVED); - this.generateDraftTD(); + this.generateDraftTD(scenarioType); var tdData = getTransportDocument().orElseThrow(); var tdr = tdData.required(TRANSPORT_DOCUMENT_REFERENCE).asText(); mutateShippingInstructionsAndUpdate(si -> si.put(TRANSPORT_DOCUMENT_REFERENCE, tdr)); @@ -432,25 +433,37 @@ private void fixupConsignmentItems(ObjectNode transportDocument) { } } - private void fixupUtilizedTransportEquipments(ObjectNode transportDocument) { + private void fixupUtilizedTransportEquipments(ObjectNode transportDocument, ScenarioType scenarioType) { + var containerISOEquipmentCode = switch (scenarioType) { + case REEFER -> "22RB"; + case REGULAR -> "22G1"; + }; for (JsonNode node : transportDocument.path("utilizedTransportEquipments")) { if (!node.isObject()) { continue; } ObjectNode ute = (ObjectNode)node; var ref = ute.path("equipmentReference"); - ute.putObject("equipment").set("equipmentReference", ref); + ute.putObject("equipment") + .put("ISOEquipmentCode", containerISOEquipmentCode) + .set("equipmentReference", ref); ute.remove("equipmentReference"); + if (scenarioType == ScenarioType.REEFER) { + ute.put("isNonOperatingReefer", false) + .putObject("activeReeferSettings") + .put("temperatureSetpoint", -18) + .put("temperatureUnit", "CEL"); + } } } - private void generateDraftTD() { + private void generateDraftTD(ScenarioType scenarioType) { var td = OBJECT_MAPPER.createObjectNode(); var siData = getShippingInstructions(); var existingTd = getTransportDocument().orElse(null); copyFieldsWherePresent(siData, td, COPY_SI_INTO_TD_FIELDS); preserveOrGenerateCarrierFields(existingTd, td); - fixupUtilizedTransportEquipments(td); + fixupUtilizedTransportEquipments(td, scenarioType); fixupConsignmentItems(td); state.set(TD_DATA_FIELD, td); } diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/party/DynamicScenarioParameters.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/party/DynamicScenarioParameters.java index 24d4a731..6316ccd4 100644 --- a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/party/DynamicScenarioParameters.java +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/party/DynamicScenarioParameters.java @@ -3,55 +3,53 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.NonNull; import lombok.With; +import org.dcsa.conformance.standards.ebl.checks.ScenarioType; + +import java.util.function.Function; @With public record DynamicScenarioParameters( + @NonNull + ScenarioType scenarioType, String shippingInstructionsReference, String transportDocumentReference, ShippingInstructionsStatus shippingInstructionsStatus, ShippingInstructionsStatus updatedShippingInstructionsStatus, TransportDocumentStatus transportDocumentStatus) { public ObjectNode toJson() { - ObjectNode dspNode = new ObjectMapper().createObjectNode(); - if (shippingInstructionsReference != null) { - dspNode.put("shippingInstructionsReference", shippingInstructionsReference); - } - if (transportDocumentReference != null) { - dspNode.put("transportDocumentReference", transportDocumentReference); - } - if (shippingInstructionsStatus != null) { - dspNode.put("shippingInstructionsStatus", shippingInstructionsStatus.wireName()); - } - if (updatedShippingInstructionsStatus != null) { - dspNode.put( - "updatedShippingInstructionsStatus", updatedShippingInstructionsStatus.wireName()); + return new ObjectMapper().createObjectNode() + .put("scenarioType", scenarioType.name()) + .put("shippingInstructionsReference", shippingInstructionsReference) + .put("transportDocumentReference", transportDocumentReference) + .put("shippingInstructionsStatus", serializeEnum(shippingInstructionsStatus, ShippingInstructionsStatus::wireName)) + .put("updatedShippingInstructionsStatus", serializeEnum(updatedShippingInstructionsStatus, ShippingInstructionsStatus::wireName)) + .put("transportDocumentStatus", serializeEnum(transportDocumentStatus, TransportDocumentStatus::wireName)); + } + + private static > String serializeEnum(E v, Function mapper) { + if (v == null) { + return null; } - if (transportDocumentStatus != null) { - dspNode.put("transportDocumentStatus", transportDocumentStatus.wireName()); + return mapper.apply(v); + } + + private static E readEnum(String value, Function mapper) { + if (value == null) { + return null; } - return dspNode; + return mapper.apply(value); } public static DynamicScenarioParameters fromJson(JsonNode jsonNode) { - ObjectNode dspNode = (ObjectNode) jsonNode; return new DynamicScenarioParameters( - dspNode.has("shippingInstructionsReference") - ? dspNode.get("shippingInstructionsReference").asText() - : null, - dspNode.has("transportDocumentReference") - ? dspNode.get("transportDocumentReference").asText() - : null, - dspNode.has("shippingInstructionsStatus") - ? ShippingInstructionsStatus.fromWireName( - dspNode.get("shippingInstructionsStatus").asText()) - : null, - dspNode.has("updatedShippingInstructionsStatus") - ? ShippingInstructionsStatus.fromWireName( - dspNode.get("updatedShippingInstructionsStatus").asText()) - : null, - dspNode.has("transportDocumentStatus") - ? TransportDocumentStatus.fromWireName(dspNode.get("transportDocumentStatus").asText()) - : null); + readEnum(jsonNode.required("scenarioType").asText(), ScenarioType::valueOf), + jsonNode.path("shippingInstructionsReference").asText(null), + jsonNode.path("transportDocumentReference").asText(null), + readEnum(jsonNode.required("shippingInstructionsStatus").asText(null), ShippingInstructionsStatus::fromWireName), + readEnum(jsonNode.required("updatedShippingInstructionsStatus").asText(null), ShippingInstructionsStatus::fromWireName), + readEnum(jsonNode.required("transportDocumentStatus").asText(null), TransportDocumentStatus::fromWireName) + ); } } diff --git a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/party/EblCarrier.java b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/party/EblCarrier.java index 8883166f..1e925b13 100644 --- a/ebl/src/main/java/org/dcsa/conformance/standards/ebl/party/EblCarrier.java +++ b/ebl/src/main/java/org/dcsa/conformance/standards/ebl/party/EblCarrier.java @@ -24,6 +24,7 @@ import org.dcsa.conformance.core.traffic.ConformanceRequest; import org.dcsa.conformance.core.traffic.ConformanceResponse; import org.dcsa.conformance.standards.ebl.action.*; +import org.dcsa.conformance.standards.ebl.checks.ScenarioType; import org.dcsa.conformance.standards.ebl.models.CarrierShippingInstructions; @Slf4j @@ -86,15 +87,28 @@ protected Map, Consumer> getActionP private void supplyScenarioParameters(JsonNode actionPrompt) { log.info("Carrier.supplyScenarioParameters(%s)".formatted(actionPrompt.toPrettyString())); + var scenarioType = ScenarioType.valueOf(actionPrompt.required("scenarioType").asText()); CarrierScenarioParameters carrierScenarioParameters = - new CarrierScenarioParameters( - "CARRIER_BOOKING_REFX", - "COMMODITY_SUBREFERENCE_FOR_REFX", - "APZU4812090", - "DKCPH", - "851712", - "300 boxes of blue shoes size 47" + switch (scenarioType) { + case REGULAR -> new CarrierScenarioParameters( + "CBR_123_REGULAR", + "Some Commodity Subreference 123", + // A "22G1" container - keep aligned with the fixupUtilizedTransportEquipments() + "NARU3472484", + "DKCPH", + "851712", + "300 boxes of blue shoes size 47" ); + case REEFER -> new CarrierScenarioParameters( + "CBR_123_REEFER", + "Some reefer Commodity Subreference 123", + // A "22RB" container - keep aligned with the fixupUtilizedTransportEquipments() + "BBCU5200220", + "DKCPH", + "04052090", + "Dairy products" + ); + }; asyncOrchestratorPostPartyInput( OBJECT_MAPPER .createObjectNode() @@ -144,10 +158,11 @@ private void publishDraftTransportDocument(JsonNode actionPrompt) { log.info("Carrier.publishDraftTransportDocument(%s)".formatted(actionPrompt.toPrettyString())); var documentReference = actionPrompt.required("documentReference").asText(); + var scenarioType = ScenarioType.valueOf(actionPrompt.required("scenarioType").asText()); var sir = tdrToSir.getOrDefault(documentReference, documentReference); var si = CarrierShippingInstructions.fromPersistentStore(persistentMap, sir); - si.publishDraftTransportDocument(documentReference); + si.publishDraftTransportDocument(documentReference, scenarioType); si.save(persistentMap); tdrToSir.put(si.getTransportDocumentReference(), si.getShippingInstructionsReference()); generateAndEmitNotificationFromTransportDocument(actionPrompt, si, true); @@ -234,10 +249,11 @@ private void issueAmendedTransportDocument(JsonNode actionPrompt) { log.info("Carrier.issueAmendedTransportDocument(%s)".formatted(actionPrompt.toPrettyString())); var documentReference = actionPrompt.required("documentReference").asText(); + var scenarioType = ScenarioType.valueOf(actionPrompt.required("scenarioType").asText()); var sir = tdrToSir.getOrDefault(documentReference, documentReference); var si = CarrierShippingInstructions.fromPersistentStore(persistentMap, sir); - si.issueAmendedTransportDocument(documentReference); + si.issueAmendedTransportDocument(documentReference, scenarioType); si.save(persistentMap); tdrToSir.put(si.getTransportDocumentReference(), si.getShippingInstructionsReference()); generateAndEmitNotificationFromTransportDocument(actionPrompt, si, true);