Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,13 @@ private static EblScenarioListBuilder requiredSiScenarios(
return carrierSupplyScenarioParameters(scenarioType, isTd)
.then(
uc1ShipperSubmitShippingInstructions()
.then(shipperGetShippingInstructions(SI_RECEIVED, false)));
.then(shipperGetShippingInstructions(SI_RECEIVED, SI_ANY, false)));
}

private static EblScenarioListBuilder shipperRequiredSiScenarios(
ScenarioType scenarioType, boolean isTd) {
return uc1ShipperSubmitShippingInstructionsStandalone(scenarioType, uc1TitleFor(scenarioType))
.then(shipperGetShippingInstructions(SI_RECEIVED, false));
.then(shipperGetShippingInstructions(SI_RECEIVED, SI_ANY, false));
}

private static EblScenarioListBuilder carrierOptionalSiScenarios(boolean isTd) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,11 @@ payload, inputScenarioType, isTd, getDspSupplier().get())
@Override
protected void doHandlePartyInput(JsonNode partyInput) {
JsonNode input = partyInput.get(INPUT);
ScenarioType effectiveScenarioType = inputScenarioType(input);
getCarrierPayloadConsumer().accept(input);
if (includeAmendment) {
if (includeAmendment || allowsAnySiTypeValidation()) {
getDspConsumer()
.accept(getDspSupplier().get().withScenarioType(inputScenarioType(input).name()));
.accept(getDspSupplier().get().withScenarioType(effectiveScenarioType.name()));
}
if (isTd && input.has("transportDocumentReference")) {
getDspConsumer()
Expand Down Expand Up @@ -295,7 +296,7 @@ private void initializeScenarioType() {
}

ScenarioType inputScenarioType(JsonNode input) {
if (!includeAmendment) {
if (!includeAmendment && !allowsAnySiTypeValidation()) {
return scenarioType;
}
return switch (input.path("transportDocumentTypeCode").asText()) {
Expand All @@ -308,6 +309,10 @@ ScenarioType inputScenarioType(JsonNode input) {
};
}

private boolean allowsAnySiTypeValidation() {
return allowAnySiType && !isTd;
}

Set<String> validateAmendedTransportDocument(JsonNode input) {
if (!includeAmendment) {
return Set.of();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.dcsa.conformance.standards.ebl.action;

import static org.dcsa.conformance.core.toolkit.JsonToolkit.OBJECT_MAPPER;
import static org.dcsa.conformance.standards.ebl.party.ShippingInstructionsStatus.SI_ANY;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
Expand Down Expand Up @@ -182,6 +183,7 @@ protected Stream<? extends ConformanceCheck> createSubChecks() {
expectedApiVersion,
notificationSchemaValidator,
ShippingInstructionsStatus.SI_RECEIVED,
SI_ANY,
EblChecks.SIR_OR_TDR_REQUIRED_IN_NOTIFICATION));
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ protected Stream<? extends ConformanceCheck> createSubChecks() {
buildChecksWithCondition(
UPDATED_SHIPPING_INSTRUCTIONS_LABEL,
UPDATED_SHIPPING_INSTRUCTIONS_PATH,
updatedShippingInstructionsStatus != null,
updatedShippingInstructionsStatus != null
&& updatedShippingInstructionsStatus != ShippingInstructionsStatus.SI_ANY,
Comment thread
palatsangeetha marked this conversation as resolved.
() ->
EblChecks.getSiPayloadChecks(
shippingInstructionsStatus,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1826,11 +1826,13 @@ public static List<JsonContentCheck> generateScenarioRelatedChecks(
List<JsonContentCheck> checks = new ArrayList<>();

checks.add(
JsonAttribute.mustEqual(
"[%s] Verify that the correct '%s' is used"
.formatted(SCENARIO, TRANSPORT_DOCUMENT_TYPE_CODE),
TRANSPORT_DOCUMENT_TYPE_CODE,
scenarioType::transportDocumentTypeCode));
isTD
? JsonAttribute.mustEqual(
"[%s] Verify that the correct '%s' is used"
.formatted(SCENARIO, TRANSPORT_DOCUMENT_TYPE_CODE),
TRANSPORT_DOCUMENT_TYPE_CODE,
scenarioType::transportDocumentTypeCode)
: siScopeCheck(scenarioType));

checks.add(
JsonAttribute.allIndividualMatchesMustBeValid(
Expand All @@ -1843,6 +1845,18 @@ public static List<JsonContentCheck> generateScenarioRelatedChecks(
return checks;
}

private static JsonContentCheck siScopeCheck(ScenarioType scenarioType) {
return describedSiCheck(
scenarioSpecificSiValidationMessage(scenarioType),
JsonAttribute.mustEqual(
JsonPointer.compile(S.formatted(TRANSPORT_DOCUMENT_TYPE_CODE)),
scenarioType.transportDocumentTypeCode()),
JsonAttribute.mustEqual(
"Validate scenario-specific '%s'.".formatted(IS_TO_ORDER),
JsonPointer.compile(S.formatted(IS_TO_ORDER)),
scenarioType.isToOrder()));
}

private static JsonContentMatchedValidation scenarioCustomsReferencesCheck(
ScenarioType scenarioType) {
return (nodeToValidate, contextPath) -> {
Expand Down Expand Up @@ -1936,22 +1950,16 @@ public static List<JsonContentCheck> getSiPayloadChecks(
Supplier<EblDynamicScenarioParameters> dspSupplier) {
var checks = new ArrayList<JsonContentCheck>();

checks.add(
JsonAttribute.mustEqual(
SI_REF_SIR_PTR, () -> dspSupplier.get().shippingInstructionsReference()));
Comment thread
palatsangeetha marked this conversation as resolved.

checks.add(
JsonAttribute.mustEqual(SI_REF_SI_STATUS_PTR, shippingInstructionsStatus.wireName()));
Comment thread
palatsangeetha marked this conversation as resolved.

if (updatedShippingInstructionsStatus != ShippingInstructionsStatus.SI_ANY) {
var updatedStatusCheck =
getUpdatedShippingInstructionsStatusCheck(updatedShippingInstructionsStatus);
checks.add(updatedStatusCheck);
checks.add(getUpdatedShippingInstructionsStatusCheck(updatedShippingInstructionsStatus));
checks.add(UPDATED_SI_STATUS_ALLOWED_VALUES_CHECK);
Comment thread
palatsangeetha marked this conversation as resolved.
Comment thread
palatsangeetha marked this conversation as resolved.
}

checks.addAll(STATIC_SI_CHECKS);
checks.add(SI_STATUS_ALLOWED_VALUES_CHECK);
checks.add(UPDATED_SI_STATUS_ALLOWED_VALUES_CHECK);

checks.add(FEEDBACKS_PRESENCE);

Expand Down Expand Up @@ -2036,11 +2044,12 @@ public static List<JsonContentCheck> getSiNotificationChecks(
jsonContentChecks.add(
JsonAttribute.mustEqual(SI_REF_SI_STATUS_PTR, shippingInstructionsStatus.wireName()));

jsonContentChecks.add(
getUpdatedShippingInstructionsStatusCheck(updatedShippingInstructionsStatus));
if (updatedShippingInstructionsStatus != ShippingInstructionsStatus.SI_ANY) {
jsonContentChecks.add(getUpdatedShippingInstructionsStatusCheck(updatedShippingInstructionsStatus));
jsonContentChecks.add(UPDATED_SI_STATUS_ALLOWED_VALUES_CHECK);
}

jsonContentChecks.add(SI_STATUS_ALLOWED_VALUES_CHECK);
jsonContentChecks.add(UPDATED_SI_STATUS_ALLOWED_VALUES_CHECK);
jsonContentChecks.add(FEEDBACKS_PRESENCE);
jsonContentChecks.add(VALID_FEEDBACKS_SEVERITY);
jsonContentChecks.add(VALID_FEEDBACKS_CODE);
Expand Down Expand Up @@ -2410,4 +2419,17 @@ private boolean isReeferContainerSizeTypeCode(String isoEquipmentCode) {
var codeChar = isoEquipmentCode.length() > 2 ? isoEquipmentCode.charAt(2) : '?';
return codeChar == 'R' || codeChar == 'H';
}

public static String scenarioSpecificSiValidationMessage(ScenarioType scenarioType) {
return switch (scenarioType.transportDocumentTypeCode()) {
case SWB ->
"For Sea Waybill: transportDocumentTypeCode must equal SWB and isToOrder must equal false.";
case BOL ->
scenarioType.isToOrder()
? "For Negotiable B/L: transportDocumentTypeCode must equal BOL and isToOrder must equal true."
: "For Straight B/L: transportDocumentTypeCode must equal BOL and isToOrder must equal false.";
default ->
"For this scenario: transportDocumentTypeCode and isToOrder must match the scope definition.";
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,6 @@
"descriptionOfGoods": [
"Refined Soybean Oil"
],
"nationalCommodityCodes": [
{
"type": "NCM",
"countryCode": "BR",
"values": [
"15152100"
]
}
],
"HSCodes": [
"15152100"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,6 @@
"descriptionOfGoods": [
"Refined Soybean Oil"
],
"nationalCommodityCodes": [
{
"type": "NCM",
"countryCode": "BR",
"values": [
"15152100"
]
}
],
"HSCodes": [
"15152100"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@
"descriptionOfGoods": [
"Refined Soybean Oil"
],
"nationalCommodityCodes": [
{
"type": "NCM",
"countryCode": "BR",
"values": [
"15152100"
]
}
],
"HSCodes": [
"15152100"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.dcsa.conformance.core.check.ConformanceCheck;
import org.dcsa.conformance.core.scenario.ConformanceAction;
import org.dcsa.conformance.core.scenario.ConformanceScenario;
import org.dcsa.conformance.core.scenario.ScenarioConformanceType;
import org.dcsa.conformance.standards.ebl.action.CarrierSupplyPayloadAction;
import org.dcsa.conformance.standards.ebl.action.Shipper_GetShippingInstructionsAction;
import org.dcsa.conformance.standards.ebl.action.UC1_Shipper_SubmitShippingInstructionsAction;
import org.dcsa.conformance.standards.ebl.action.UC17_Shipper_SubmitTransportDocumentAmendmentAction;
import org.dcsa.conformance.standards.ebl.checks.ScenarioType;
import org.dcsa.conformance.standards.ebl.party.ShippingInstructionsStatus;
import org.junit.jupiter.api.Test;

class EblScenarioListBuilderTest {
Expand Down Expand Up @@ -87,6 +92,29 @@ void carrierSiOptionalScenariosUseAnySiLabelAndDocumentedPaths() {
assertTrue(optionalTitles.stream().noneMatch(title -> title.contains("Sea Waybill")));
}

@Test
void requiredSiOnlyScenariosDoNotValidateUpdatedShippingInstructionsStatus() throws Exception {
Map<String, List<ConformanceScenario>> carrierModules =
buildModules(Set.of(CARRIER), EblScenarioListBuilder.SCENARIO_SUITE_CONFORMANCE_SI);
Map<String, List<ConformanceScenario>> shipperModules =
buildModules(Set.of(SHIPPER), EblScenarioListBuilder.SCENARIO_SUITE_CONFORMANCE_SI);

var carrierScenario = carrierModules.get("Required Sea Waybill scenario").getFirst();
var shipperScenario = shipperModules.get("Required Sea Waybill scenario").getFirst();
var carrierCheckTitles = allCheckTitles(carrierScenario);
var shipperCheckTitles = allCheckTitles(shipperScenario);

assertEquals(ShippingInstructionsStatus.SI_ANY, expectedUpdatedSiStatusOf(carrierScenario));
assertEquals(ShippingInstructionsStatus.SI_ANY, expectedUpdatedSiStatusOf(shipperScenario));

assertFalse(
carrierCheckTitles.stream().anyMatch(title -> title.contains("updatedShippingInstructionsStatus")),
carrierCheckTitles.toString());
Comment thread
palatsangeetha marked this conversation as resolved.
assertFalse(
shipperCheckTitles.stream().anyMatch(title -> title.contains("updatedShippingInstructionsStatus")),
shipperCheckTitles.toString());
}

@Test
void carrierTdOnlyScenariosMatchDocumentation() {
Map<String, List<ConformanceScenario>> modules =
Expand Down Expand Up @@ -275,6 +303,31 @@ private List<String> titles(List<ConformanceScenario> scenarios) {
return scenarios.stream().map(ConformanceScenario::getTitle).toList();
}

private ShippingInstructionsStatus expectedUpdatedSiStatusOf(ConformanceScenario scenario)
throws Exception {
ConformanceAction getSiAction =
scenario.allActionsStream()
.filter(Shipper_GetShippingInstructionsAction.class::isInstance)
.findFirst()
.orElseThrow();
var field = Shipper_GetShippingInstructionsAction.class.getDeclaredField("expectedAmendedSiStatus");
field.setAccessible(true);
return (ShippingInstructionsStatus) field.get(getSiAction);
}

private List<String> allCheckTitles(ConformanceScenario scenario) {
return scenario.allActionsStream()
.map(action -> action.createCheck("3.0.0"))
.filter(java.util.Objects::nonNull)
.flatMap(this::flattenCheckTitles)
.toList();
}

private Stream<String> flattenCheckTitles(ConformanceCheck check) {
return Stream.concat(
Stream.of(check.getTitle()), check.subChecksStream().flatMap(this::flattenCheckTitles));
}

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,52 @@ void missingOrNullInputProducesAUserFacingError() {
assertEquals(missingInputError.getMessage(), nullInputError.getMessage());
}

@Test
void anySiOptionalScenarioInfersEverySupportedShippingInstructionsScope() {
CarrierSupplyPayloadAction anySiAction =
new CarrierSupplyPayloadAction(
"Carrier", ScenarioType.REGULAR_STRAIGHT_BL, "3.0.0", null, false, false, true);

assertEquals(
ScenarioType.REGULAR_SWB,
anySiAction.inputScenarioType(shippingInstructionsFor(ScenarioType.REGULAR_SWB)));
assertEquals(
ScenarioType.REGULAR_STRAIGHT_BL,
anySiAction.inputScenarioType(shippingInstructionsFor(ScenarioType.REGULAR_STRAIGHT_BL)));
assertEquals(
ScenarioType.REGULAR_NEGOTIABLE_BL,
anySiAction.inputScenarioType(shippingInstructionsFor(ScenarioType.REGULAR_NEGOTIABLE_BL)));
}

@Test
void anySiOptionalScenarioAcceptsEverySupportedShippingInstructionsTypeAndUpdatesDsp() {
JsonSchemaValidator schemaValidator =
JsonSchemaValidator.getInstance(
"/standards/ebl/schemas/EBL_v3.0.0.yaml", "CreateShippingInstructions");

for (ScenarioType scenarioType :
Set.of(
ScenarioType.REGULAR_SWB,
ScenarioType.REGULAR_STRAIGHT_BL,
ScenarioType.REGULAR_NEGOTIABLE_BL)) {
CarrierSupplyPayloadAction anySiAction =
new CarrierSupplyPayloadAction(
"Carrier",
ScenarioType.REGULAR_STRAIGHT_BL,
"3.0.0",
schemaValidator,
false,
false,
true);
ObjectNode partyInput = OBJECT_MAPPER.createObjectNode();
partyInput.set("input", shippingInstructionsFor(scenarioType));

anySiAction.handlePartyInput(partyInput);

assertEquals(scenarioType.name(), anySiAction.getDSP().scenarioType(), scenarioType.name());
}
}

@Test
void generatedTransportDocumentsMatchLatestSchemaAndWorkbookRules() {
JsonSchemaValidator schemaValidator =
Expand Down Expand Up @@ -189,5 +235,10 @@ private static JsonNode amendedTd(
"""
.formatted(type, isToOrder, reference, status));
}

private static JsonNode shippingInstructionsFor(ScenarioType scenarioType) {
return new CarrierSupplyPayloadAction("Carrier", scenarioType, "3.0.0", null, false)
.getJsonForHumanReadablePrompt();
}
}

Loading
Loading