Documentation

fsds/stack-and-shipment-creation-without-load-numbers.md

Plan: Allow Stack and Shipment Creation Without Load Numbers

Problem

When Oracle XML files don't have <load_nbr> elements, the entire stack AND shipment creation is skipped, causing all camera read OBLPN lookups to fail. This is overly restrictive since:

  • Stacks (OBLPNs) are physical containers that exist independently of load assignments
  • Shipments can be identified by OBLPN instead of load number
  • The validation docs treat missing load numbers as warnings, not failures

Solution Overview

Modify OracleProcessor.ProcessShippingInfoAsync to:

  1. Always create stacks when we have a valid OBLPN
  2. Create shipments using OBLPN as the primary identifier when load number is missing
  3. Add load number as an additional identifier when available

Files to Modify

Primary: engines/bbu/src/Acsis.Dynaplex.Engines.Bbu/Services/OracleProcessor.cs

Change 1: Restructure ProcessShippingInfoAsync (lines ~940-990)

Current flow:

if (loadNumber missing) {
    log warning, skip everything
} else {
    create shipment → create stack → allocate
}

New flow:

// Always create stack if we have OBLPN
stackItem = await UpsertStackItemAsync(...)

// Create shipment using OBLPN or LoadNumber as identifier
shipmentResult = await UpsertShipmentAsync(...)  // Modified method

// Link stack to shipment if both exist
if (stackItem != null && shipmentResult != null) {
    await EnsureStackAllocationAsync(...)
    await SyncStackProductItemsAsync(...)
}

Change 2: Modify UpsertShipmentForLoadAsync → UpsertShipmentAsync

Rename and modify the method to:

  • Accept OBLPN as a required parameter
  • Accept loadNumber as optional
  • Use OBLPN as TrackingNumber when loadNumber is missing
  • Look up existing shipments by OBLPN first, then by loadNumber
  • Store both identifiers when available

New signature:

private async Task<LoadShipmentResult?> UpsertShipmentAsync(
    string oblpn,                    // Required - always have this
    string? loadNumber,              // Optional
    string? manifestNumber,
    Guid? sourceLocationId,
    int itemQuantity,
    IReadOnlyList<DeliveryPlan> deliveryPlans,
    string? trackingNumber,
    string? carrierCode,
    CancellationToken cancellationToken
)

Lookup logic:

  1. First try to find by OBLPN identifier (PRIMARY_OBLPN)
  2. If not found and loadNumber provided, try by LOAD_NUMBER
  3. Create new if neither found

TrackingNumber assignment:

  • Always use OBLPN (consistent identifier regardless of loadNumber presence)

Change 3: Update UpsertStackItemAsync call site

Move the call outside the loadNumber check so it always executes when we have a valid OBLPN.

Change 4: Fix misleading success log (line ~983)

Move "Successfully imported entities for OBLPN" log inside the actual success path, not after the if/else block.

Implementation Steps

  1. Rename method: UpsertShipmentForLoadAsyncUpsertShipmentAsync

    • Update signature to take OBLPN as required, loadNumber as optional
    • Update all call sites
  2. Update lookup logic in UpsertShipmentAsync:

    • Add OBLPN-based lookup using IdentifierTypeCodes.PrimaryOblpn
    • Keep loadNumber lookup as fallback
    • Ensure both identifiers are stored
  3. Restructure ProcessShippingInfoAsync:

    • Move UpsertStackItemAsync call before/outside the shipment creation
    • Update shipment creation to use new method signature
    • Ensure stack allocation only happens when both stack and shipment exist
  4. Fix logging:

    • Add debug log showing which identifier was used
    • Fix the misleading "Successfully imported" log placement

Testing Considerations

  • Test with Oracle XML that has: OBLPN only, LoadNumber only, both, neither
  • Verify camera read lookups succeed when stacks are created
  • Verify shipment-stack allocation works correctly
  • Check that existing data with load numbers still works

Risks & Mitigations

Risk Mitigation
Duplicate shipments if same OBLPN processed twice Existing upsert pattern handles this via identifier lookup
Breaking existing data with load numbers Lookup checks both identifiers, existing data unaffected
ShipmentLifecycleProcessor expects certain data Stack and shipment allocation remain unchanged