---
id: ATS-COM-FUL-018
scope: CAP-COM-FUL-018
product_area: commerce
module: fulfillment
revision: 2
status: proposed
approved_by: null
approved_at: null
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-COM-FUL-018-01
    title: Claims retain individual units and allocation order
    level: api
    proves:
      - BR-CAP-COM-FUL-018-01
    preconditions: Two sellable bins hold stock.
    steps: Claim two units for one line and repeat.
    expected: >-
      The first location supplies two numbered commitments and repetition
      creates no duplicates.
    negative: false
    data_needs: Fixtures for claims retain individual units and allocation order.
    bound_to:
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        ok with one committed claim for the SKU and the quantity, under the
        claim ref the line is stamped with
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#writes
        one committed unit per unit on the first location in allocation order,
        unit 0 and 1, each stamped with the order and the line
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        the same claim again for the same line without writing twice (idempotent
        on the line)
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#takes
        the location with the next pick priority once the first is empty
      - TST-COM-FUL-065
    provenance: test
    source: >-
      services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
      ok with one committed claim for the SKU and the quantity, under the claim
      ref the line is stamped with
    category: rule
  - id: ATS-COM-FUL-018-02
    title: Product policy decides shortage refusal or backorder
    level: api
    proves:
      - BR-CAP-COM-FUL-018-02
    preconditions: Products differ on backorder permission and have too little stock.
    steps: Claim excess quantity and exercise an absent location item.
    expected: >-
      The strict product fails, the permissive product commits and backorders,
      and the missing item is created where needed.
    negative: true
    data_needs: Fixtures for product policy decides shortage refusal or backorder.
    bound_to:
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#is
        refused insufficient_stock with nothing written when the product
        prohibits backorders (the last cap)
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#commits
        what exists and backorders the rest when the product allows it (A12),
        the shortfall counting as backordered, never against available
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#backorders
        a SKU no item holds yet by making its item at the first sellable
        location, when the product allows it
      - TST-COM-FUL-065
    provenance: test
    source: >-
      services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#is
      refused insufficient_stock with nothing written when the product prohibits
      backorders (the last cap)
    category: failure
  - id: ATS-COM-FUL-018-03
    title: Concurrent buyers cannot share the final stock
    level: api
    proves:
      - BR-CAP-COM-FUL-018-03
      - BR-CAP-COM-FUL-018-08
      - INV-DOM-COM-FUL-005-06
    preconditions: Ten carts compete for one unit and a shipment can consume it.
    steps: Race placements and separately race claim against shipment.
    expected: >-
      One placement wins, other carts conflict and a shipped unit is not claimed
      again.
    negative: true
    data_needs: Fixtures for concurrent buyers cannot share the final stock.
    bound_to:
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#lets
        exactly one placement win and refuses the nine others 409 claim_conflict
        naming the item, the SKU and insufficient_stock
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#refuses
        a placement made after the last cap was gone with the same 409
        claim_conflict and insufficient_stock, never a preparation refusal
        (D-P8I-21)
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        the second, once it gets the row, insufficient_stock, with nothing
        written for it
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        the claim, once it gets the row, insufficient_stock: the unit the
        shipment took is not claimed twice
      - TST-COM-FUL-058
      - TST-COM-FUL-065
    provenance: test
    source: >-
      services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#lets
      exactly one placement win and refuses the nine others 409 claim_conflict
      naming the item, the SKU and insufficient_stock
    category: concurrency
  - id: ATS-COM-FUL-018-04
    title: Early holds retain extension and release limits
    level: api
    proves:
      - BR-CAP-COM-FUL-018-04
      - BR-CAP-COM-FUL-018-09
    preconditions: Checkout holds two limited-stock units.
    steps: 'Extend within the permitted window, exceed it, place and void.'
    expected: >-
      The excessive extension fails, placement commits the units and void
      releases them.
    negative: true
    data_needs: Fixtures for early holds retain extension and release limits.
    bound_to:
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#holds
        the two caps for the item under hold:<item> (the units held with the
        product’s expiry), and the shelf shows one left
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#extends
        the hold within the window and refuses one past it, 409
        hold_not_extendable
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#converts
        the held units to committed on the line at placement, and the void
        releases them (reason void), the stock back
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#converts
        the held unit to committed on the line, answering the hold’s ref as the
        claim
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#refuses
        a hold that is not held (converted already, or never made):
        hold_not_found
      - TST-COM-FUL-058
      - TST-COM-FUL-065
    provenance: test
    source: >-
      services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#holds
      the two caps for the item under hold:<item> (the units held with the
      product’s expiry), and the shelf shows one left
    category: transition
  - id: ATS-COM-FUL-018-05
    title: Allocation constraints enforce one-unit state records
    level: api
    proves:
      - BR-CAP-COM-FUL-018-05
      - INV-DOM-COM-FUL-005-05
    preconditions: A stock item is available for direct ledger fixtures.
    steps: >-
      Try missing hold or commitment references, duplicate holds, multi-unit
      rows and item changes.
    expected: >-
      All invalid rows fail and valid state changes affect held or allocated
      balances.
    negative: true
    data_needs: Fixtures for allocation constraints enforce one-unit state records.
    bound_to:
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#counts
        a held row against held, a committed one against allocated and a
        released one against nothing, through the trigger
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        a held row without a checkout item or an expiry, and a committed row
        without a line, by inventory_allocations_holder_check and
        inventory_allocations_held_until_check
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        a second live hold of one checkout item and unit, by
        inventory_allocations_hold_unit_uidx, and a quantity other than one, by
        inventory_allocations_quantity_check
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        a hold the stock cannot cover, by inventory_items_balances_check:
        available never falls below zero
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        moving a row to another item, SQLSTATE RHC05 (the trigger), and a
        release reason outside the contract’s
      - TST-COM-FUL-059
    provenance: test
    source: >-
      services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#counts
      a held row against held, a committed one against allocated and a released
      one against nothing, through the trigger
    category: invariant
  - id: ATS-COM-FUL-018-06
    title: Availability uses consistent product states
    level: api
    proves:
      - BR-CAP-COM-FUL-018-06
    preconditions: 'Fixtures include limited stock, permitted backorders and untracked goods.'
    steps: Read storefront availability for each requested quantity.
    expected: >-
      Results distinguish limited, unavailable and backorder states while
      untracked goods remain available without counts.
    negative: false
    data_needs: Fixtures for availability uses consistent product states.
    bound_to:
      - >-
        services-commerce:modules/inventory/tests/e2e/inventory.e2e.spec.js#answers
        the availability in the storefront’s words: limited with what is left,
        unavailable for more than the shelf has, backorder for the shirt,
        available for a product it does not track
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/staff-movements.integration.spec.js#answers
        the storefront’s words: limited with what is left, backorder with the
        shortfall for the shirt, unavailable for the cap, available for a
        product it does not track
      - TST-COM-FUL-057
      - TST-COM-FUL-066
    provenance: test
    source: >-
      services-commerce:modules/inventory/tests/e2e/inventory.e2e.spec.js#answers
      the availability in the storefront’s words: limited with what is left,
      unavailable for more than the shelf has, backorder for the shirt,
      available for a product it does not track
    category: rule
  - id: ATS-COM-FUL-018-07
    title: Void and cancellation release only their owned units
    level: api
    proves:
      - BR-CAP-COM-FUL-018-07
    preconditions: Separate orders contain committed units and a partial cancellation.
    steps: Void one order and deliver the other's selected cancellation.
    expected: >-
      Void releases all its commitments and partial cancellation releases only
      its named unit.
    negative: false
    data_needs: Fixtures for void and cancellation release only their owned units.
    bound_to:
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#releases
        a line’s committed units on a void, giving the stock back; a release
        naming nothing live answers released: []
      - >-
        services-commerce:modules/shipping/tests/e2e/post-purchase-shipping.e2e.spec.js#delivers
        the cancel to the Merchandise participant: unit 2 released, the kept
        units unfenced and picked, the document completed, the memo and the
        refund after the domain
      - TST-COM-FUL-065
      - TST-COM-FUL-074
    provenance: test
    source: >-
      services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#releases
      a line’s committed units on a void, giving the stock back; a release
      naming nothing live answers released: []
    category: interaction
  - id: ATS-COM-FUL-018-08
    title: Warehouse departure supplies the physical application outcome
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-FUL-004-04
      - BR-CAP-COM-FUL-014-01
      - BR-CAP-COM-FUL-018-05
    exercises:
      - CAP-COM-FUL-004
      - CAP-COM-FUL-014
      - CAP-COM-FUL-018
    relies_on:
      - ATS-COM-FUL-004-03
      - ATS-COM-FUL-014-01
      - ATS-COM-FUL-018-05
    preconditions: >-
      Three picked units have current delivery authorization and a manifested
      shipment.
    steps: >-
      Hand the shipment to its carrier, then inspect allocation units and
      delivery detail.
    expected: >-
      The three units are shipped and their obligations are completed with the
      allocation references.
    negative: false
    data_needs: >-
      An isolated workspace with the products, records, authority and quantities
      described in the preconditions.
    bound_to:
      - >-
        services-commerce:modules/shipping/tests/integration/services/fulfillment/shipping-flow.integration.spec.js#hands
        off: the line handed to the carrier with its whole quantity shipped, the
        units shipped in the ledger, one shipment movement of minus three on the
        item, three fewer on hand
      - >-
        services-commerce:modules/shipping/tests/integration/services/fulfillment/shipping-flow.integration.spec.js#completes
        every unit’s obligation through the register (the allocation as the
        record), and emits one deferred cost relief fact per item
      - >-
        services-commerce:modules/shipping/tests/integration/services/fulfillment/shipping-flow.integration.spec.js#writes
        the shipped quantity on the order line, the order’s shipping_summary
        complete, and the order’s history
      - TST-COM-FUL-078
    provenance: test
    source:
      - >-
        services-commerce:modules/fulfillment/tests/integration/services/persistence/obligation-register.integration.spec.js#records
        partial completion as in progress with the units, then completes when
        the rest arrive
      - >-
        services-commerce:modules/shipping/tests/integration/services/fulfillment/shipping-flow.integration.spec.js#hands
        off: the line handed to the carrier with its whole quantity shipped, the
        units shipped in the ledger, one shipment movement of minus three on the
        item, three fewer on hand
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#counts
        a held row against held, a committed one against allocated and a
        released one against nothing, through the trigger
  - id: ATS-COM-FUL-018-09
    title: Replenishment creates the second dispatch wave
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-FUL-012-02
      - BR-CAP-COM-FUL-017-07
      - BR-CAP-COM-FUL-018-02
      - BR-CAP-COM-FUL-014-01
    exercises:
      - CAP-COM-FUL-012
      - CAP-COM-FUL-017
      - CAP-COM-FUL-018
      - CAP-COM-FUL-014
    relies_on:
      - ATS-COM-FUL-012-02
      - ATS-COM-FUL-017-04
      - ATS-COM-FUL-018-02
      - ATS-COM-FUL-014-01
    preconditions: A five-shirt order has three available units and two backorders.
    steps: >-
      Ship the first wave, receive two more shirts and create and ship the next
      wave.
    expected: >-
      The second wave contains only the two replenished units; all five
      obligations complete.
    negative: false
    data_needs: >-
      An isolated workspace with the products, records, authority and quantities
      described in the preconditions.
    bound_to:
      - >-
        services-commerce:modules/shipping/tests/e2e/shipping.e2e.spec.js#makes
        the first wave of the three picked units (0..2), charged what the order
        paid, and walks it to the handoff
      - >-
        services-commerce:modules/shipping/tests/e2e/shipping.e2e.spec.js#fills
        the two backorders at the receipt, and the second wave picks the filled
        units at its creation (3..4) and ships them
      - >-
        services-commerce:modules/shipping/tests/e2e/shipping.e2e.spec.js#completes
        the order’s shipping: every unit shipped, every obligation completed,
        the shelf down five, the cost facts emitted per wave
      - TST-COM-FUL-075
    provenance: test
    source:
      - >-
        services-commerce:modules/shipping/tests/e2e/shipping.e2e.spec.js#makes
        the first wave of the three picked units (0..2), charged what the order
        paid, and walks it to the handoff
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/staff-movements.integration.spec.js#converts
        both backordered units to committed in the receipt’s transaction, oldest
        order first, leaving one to sell
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#is
        refused insufficient_stock with nothing written when the product
        prohibits backorders (the last cap)
      - >-
        services-commerce:modules/shipping/tests/integration/services/fulfillment/shipping-flow.integration.spec.js#hands
        off: the line handed to the carrier with its whole quantity shipped, the
        units shipped in the ledger, one shipment movement of minus three on the
        item, three fewer on hand
  - id: ATS-COM-FUL-018-10
    title: >-
      Cancellation fencing releases selected allocations while retained units
      ship later
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-FUL-012-08
      - BR-CAP-COM-FUL-014-06
      - BR-CAP-COM-FUL-018-07
    exercises:
      - CAP-COM-FUL-012
      - CAP-COM-FUL-014
      - CAP-COM-FUL-018
    relies_on:
      - ATS-COM-FUL-012-06
      - ATS-COM-FUL-014-04
      - ATS-COM-FUL-018-07
    preconditions: >-
      Three picked shirts share a labelled shipment and cancellation selects the
      final unit.
    steps: >-
      Commit cancellation, deliver its instruction and create a new shipment for
      the retained units.
    expected: >-
      The selected unit releases, the old parcel is pulled, and two retained
      picked units use new shipment work.
    negative: false
    data_needs: >-
      An isolated workspace with the products, records, authority and quantities
      described in the preconditions.
    bound_to:
      - >-
        services-commerce:modules/shipping/tests/e2e/post-purchase-shipping.e2e.spec.js#fences
        and cancels the shipment line, pulls the package and voids the counter
        label, and refuses the handoff asked after (409)
      - >-
        services-commerce:modules/shipping/tests/e2e/post-purchase-shipping.e2e.spec.js#carries
        the two kept units on a new shipment (0..1)
      - >-
        services-commerce:modules/shipping/tests/e2e/post-purchase-shipping.e2e.spec.js#delivers
        the cancel to the Merchandise participant: unit 2 released, the kept
        units unfenced and picked, the document completed, the memo and the
        refund after the domain
      - TST-COM-FUL-074
    provenance: test
    source:
      - >-
        services-commerce:modules/shipping/tests/integration/services/fulfillment/shipping-flow.integration.spec.js#refuses
        a second handover (not packed any more) and the cancellation of a
        collected shipment
      - >-
        services-commerce:modules/shipping/tests/integration/services/fulfillment/shipping-flow.integration.spec.js#cancels
        the fenced line at the cancel step: the units unstamped (still the
        order’s, picked), the label voided and the package pulled, the shipment
        cancelled
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#releases
        a line’s committed units on a void, giving the stock back; a release
        naming nothing live answers released: []
  - id: ATS-COM-FUL-018-11
    title: Returned stock becomes available through the movement ledger
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-FUL-015-05
      - BR-CAP-COM-FUL-017-03
      - BR-CAP-COM-FUL-018-06
    exercises:
      - CAP-COM-FUL-015
      - CAP-COM-FUL-017
      - CAP-COM-FUL-018
    relies_on:
      - ATS-COM-FUL-015-04
      - ATS-COM-FUL-017-02
      - ATS-COM-FUL-018-06
    preconditions: Two shipped shirts return under a restock-permitting authorization.
    steps: >-
      Receive and inspect them, restock to the original sellable location and
      read availability and movements.
    expected: >-
      The returned quantity has a restock entry and is available to buyers;
      receiving alone adds no stock.
    negative: false
    data_needs: >-
      An isolated workspace with the products, records, authority and quantities
      described in the preconditions.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/shipping/tests/integration/services/returns/return-receiving.integration.spec.js#restocks
        the two units at the bin they shipped from: the return_restock movement
        naming the disposition, on hand up two, the two reliefs reversed at 1500
        with the restore_cogs fact of 3000
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        the application role a balance: UPDATE on on_hand is 42501 (the column
        grant), while its settings are its to change
      - >-
        services-commerce:modules/inventory/tests/e2e/inventory.e2e.spec.js#answers
        the availability in the storefront’s words: limited with what is left,
        unavailable for more than the shelf has, backorder for the shirt,
        available for a product it does not track
  - id: ATS-COM-FUL-018-12
    title: An inactive warehouse refuses receipt and allocation
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-FUL-018-01
      - INV-DOM-COM-FUL-005-12
    exercises:
      - CAP-COM-FUL-016
      - CAP-COM-FUL-017
      - CAP-COM-FUL-018
    relies_on:
      - ATS-COM-FUL-016-05
      - ATS-COM-FUL-017-01
      - ATS-COM-FUL-018-01
    preconditions: The only sellable location belongs to an inactive warehouse.
    steps: >-
      Attempt a receipt and a placement claim, then repeat after authorized
      activation.
    expected: >-
      Inactive-place work is refused; active-place work is considered under its
      normal stock guards.
    negative: false
    data_needs: >-
      An isolated workspace with the products, records, authority and quantities
      described in the preconditions.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/inventory/tests/e2e/inventory.e2e.spec.js#lets
        a reader list and read everything, and refuses it every write 403
        permission_missing
      - >-
        services-commerce:modules/inventory/tests/e2e/inventory.e2e.spec.js#moves
        quarantined beside on_hand and a scrap off it; refuses what the shelf
        cannot bear (400 naming the rule), a wrong sign (400) and a type staff
        do not record (400)
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        ok with one committed claim for the SKU and the quantity, under the
        claim ref the line is stamped with
  - id: ATS-COM-FUL-018-13
    title: Place ordering controls the units claimed from received stock
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-FUL-016-05
      - BR-CAP-COM-FUL-017-01
      - BR-CAP-COM-FUL-018-01
    exercises:
      - CAP-COM-FUL-016
      - CAP-COM-FUL-017
      - CAP-COM-FUL-018
    relies_on:
      - ATS-COM-FUL-016-04
      - ATS-COM-FUL-017-01
      - ATS-COM-FUL-018-01
    preconditions: >-
      Two active sellable bins have different pick priorities and receive the
      same SKU.
    steps: >-
      Receive stock in both and claim two units, then inspect their item and
      location references.
    expected: The claim uses the earlier sellable location before the later one.
    negative: false
    data_needs: >-
      An isolated workspace with the products, records, authority and quantities
      described in the preconditions.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/inventory/tests/integration/services/persistence/inventory-configuration.integration.spec.js#answers
        the sellable locations across warehouses in allocation order: the main
        warehouse (priority 1) before the depot (2), bins by pick priority,
        quarantine and returns left out
      - >-
        services-commerce:modules/inventory/tests/e2e/inventory.e2e.spec.js#moves
        quarantined beside on_hand and a scrap off it; refuses what the shelf
        cannot bear (400 naming the rule), a wrong sign (400) and a type staff
        do not record (400)
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        ok with one committed claim for the SKU and the quantity, under the
        claim ref the line is stamped with
  - id: ATS-COM-FUL-018-14
    title: A shelf count corrects the ledger and changes availability
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-FUL-019-02
      - BR-CAP-COM-FUL-017-03
      - BR-CAP-COM-FUL-018-06
    exercises:
      - CAP-COM-FUL-019
      - CAP-COM-FUL-017
      - CAP-COM-FUL-018
    relies_on:
      - ATS-COM-FUL-019-01
      - ATS-COM-FUL-017-02
      - ATS-COM-FUL-018-06
    preconditions: A sellable item holds ten units and a scoped count observes eight.
    steps: 'Complete the count, inspect its correction movement and read availability.'
    expected: >-
      The count retains one correction and buyers see the corrected stock;
      repeated completion changes nothing.
    negative: false
    data_needs: >-
      An isolated workspace with the products, records, authority and quantities
      described in the preconditions.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/staff-movements.integration.spec.js#opens
        the count open with no lines, records the lines with what the shelf
        expected, and completes it with one correction for the variance
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        the application role a balance: UPDATE on on_hand is 42501 (the column
        grant), while its settings are its to change
      - >-
        services-commerce:modules/inventory/tests/e2e/inventory.e2e.spec.js#answers
        the availability in the storefront’s words: limited with what is left,
        unavailable for more than the shelf has, backorder for the shirt,
        available for a product it does not track
  - id: ATS-COM-FUL-018-15
    title: Nightly expiry gives held stock back to buyers
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-FUL-019-04
      - BR-CAP-COM-FUL-018-04
    exercises:
      - CAP-COM-FUL-019
      - CAP-COM-FUL-018
    relies_on:
      - ATS-COM-FUL-019-03
      - ATS-COM-FUL-018-04
    preconditions: An expired checkout hold consumes one unit of tracked sellable stock.
    steps: >-
      Run nightly reconciliation, inspect the expiry finding and read the
      allocation and availability.
    expected: The hold releases with an expiry reason and its unit becomes available.
    negative: false
    data_needs: >-
      An isolated workspace with the products, records, authority and quantities
      described in the preconditions.
    bound_to:
      - >-
        services-commerce:modules/inventory/tests/integration/services/reconciliation/nightly-inventory.integration.spec.js#counts
        what it did: the tenant, one hold expired, three items checked, one
        mismatch, one stale unit, one low-stock SKU of two with a point, and the
        cost pass: three items whose stock no layer costs (the fixture’s bare
        receipts), no handoff without a relief
      - >-
        services-commerce:modules/inventory/tests/integration/services/reconciliation/nightly-inventory.integration.spec.js#releases
        the expired hold (reason expiry) and raises expired_hold on it, giving
        the unit back
      - >-
        services-commerce:modules/inventory/tests/integration/services/reconciliation/nightly-inventory.integration.spec.js#raises
        balance_mismatch on the item knocked out of line, with what was kept and
        what the ledgers say, and corrects nothing
      - >-
        services-commerce:modules/inventory/tests/integration/services/reconciliation/nightly-inventory.integration.spec.js#raises
        low_stock on shirt L at its own reorder point with the product’s reorder
        quantity, and nothing on the cap above its point
      - >-
        services-commerce:modules/inventory/tests/integration/services/reconciliation/nightly-inventory.integration.spec.js#raises
        stale_allocation on the committed unit whose obligation is cancelled
      - >-
        services-commerce:modules/inventory/tests/integration/services/reconciliation/nightly-inventory.integration.spec.js#finds
        the same on a second run and adds an occurrence to each open finding
        rather than another row
      - TST-COM-FUL-063
    provenance: test
    source:
      - >-
        services-commerce:modules/inventory/tests/integration/services/reconciliation/nightly-inventory.integration.spec.js#counts
        what it did: the tenant, one hold expired, three items checked, one
        mismatch, one stale unit, one low-stock SKU of two with a point, and the
        cost pass: three items whose stock no layer costs (the fixture’s bare
        receipts), no handoff without a relief
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#holds
        the two caps for the item under hold:<item> (the units held with the
        product’s expiry), and the shelf shows one left
evidence:
  - repository: services-commerce
    tests:
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        ok with one committed claim for the SKU and the quantity, under the
        claim ref the line is stamped with
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#writes
        one committed unit per unit on the first location in allocation order,
        unit 0 and 1, each stamped with the order and the line
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        the same claim again for the same line without writing twice (idempotent
        on the line)
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#takes
        the location with the next pick priority once the first is empty
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#is
        refused insufficient_stock with nothing written when the product
        prohibits backorders (the last cap)
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#commits
        what exists and backorders the rest when the product allows it (A12),
        the shortfall counting as backordered, never against available
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#backorders
        a SKU no item holds yet by making its item at the first sellable
        location, when the product allows it
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#lets
        exactly one placement win and refuses the nine others 409 claim_conflict
        naming the item, the SKU and insufficient_stock
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#refuses
        a placement made after the last cap was gone with the same 409
        claim_conflict and insufficient_stock, never a preparation refusal
        (D-P8I-21)
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        the second, once it gets the row, insufficient_stock, with nothing
        written for it
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#answers
        the claim, once it gets the row, insufficient_stock: the unit the
        shipment took is not claimed twice
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#holds
        the two caps for the item under hold:<item> (the units held with the
        product’s expiry), and the shelf shows one left
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#extends
        the hold within the window and refuses one past it, 409
        hold_not_extendable
      - >-
        services-commerce:modules/inventory/tests/e2e/merchandise-placement.e2e.spec.js#converts
        the held units to committed on the line at placement, and the void
        releases them (reason void), the stock back
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#converts
        the held unit to committed on the line, answering the hold’s ref as the
        claim
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#refuses
        a hold that is not held (converted already, or never made):
        hold_not_found
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#counts
        a held row against held, a committed one against allocated and a
        released one against nothing, through the trigger
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        a held row without a checkout item or an expiry, and a committed row
        without a line, by inventory_allocations_holder_check and
        inventory_allocations_held_until_check
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        a second live hold of one checkout item and unit, by
        inventory_allocations_hold_unit_uidx, and a quantity other than one, by
        inventory_allocations_quantity_check
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        a hold the stock cannot cover, by inventory_items_balances_check:
        available never falls below zero
      - >-
        services-commerce:modules/inventory/tests/integration/db/inventory-tables.table.integration.spec.js#refuses
        moving a row to another item, SQLSTATE RHC05 (the trigger), and a
        release reason outside the contract’s
      - >-
        services-commerce:modules/inventory/tests/e2e/inventory.e2e.spec.js#answers
        the availability in the storefront’s words: limited with what is left,
        unavailable for more than the shelf has, backorder for the shirt,
        available for a product it does not track
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/staff-movements.integration.spec.js#answers
        the storefront’s words: limited with what is left, backorder with the
        shortfall for the shirt, unavailable for the cap, available for a
        product it does not track
      - >-
        services-commerce:modules/inventory/tests/integration/services/stock/merchandise-claims.integration.spec.js#releases
        a line’s committed units on a void, giving the stock back; a release
        naming nothing live answers released: []
      - >-
        services-commerce:modules/shipping/tests/e2e/post-purchase-shipping.e2e.spec.js#delivers
        the cancel to the Merchandise participant: unit 2 released, the kept
        units unfenced and picked, the document completed, the memo and the
        refund after the domain
coverage:
  rule: covered
  transition: covered
  validation: 'gap: FND-COM-FUL-073'
  permission: 'gap: FND-COM-FUL-073'
  edge: 'gap: FND-COM-FUL-073'
  failure: covered
  boundary: 'gap: FND-COM-FUL-073'
  concurrency: covered
  invariant: covered
  interaction: covered
coverage_notes:
  validation: >-
    No scenario specifically isolates this dimension; the coverage finding
    retains the missing proof.
  permission: >-
    No scenario specifically isolates this dimension; the coverage finding
    retains the missing proof.
  edge: >-
    No scenario specifically isolates this dimension; the coverage finding
    retains the missing proof.
  boundary: >-
    No scenario specifically isolates this dimension; the coverage finding
    retains the missing proof.
transition_audit:
  - model: STATE-COM-FUL-009
    from: held
    to: committed
    source: >-
      services-commerce:modules/inventory/db/inventory-allocations.table.ts
      (commerce_commit_claims)
    coverage: 'gap: FND-COM-FUL-073'
    scenarios: []
  - model: STATE-COM-FUL-009
    from: held
    to: released
    source: >-
      services-commerce:modules/inventory/services/persistence/inventory-allocations.ts:expireHolds
    coverage: 'gap: FND-COM-FUL-073'
    scenarios: []
  - model: STATE-COM-FUL-009
    from: committed
    to: picked
    source: >-
      services-commerce:modules/inventory/services/persistence/inventory-allocations.ts:pickUnitsOfLine
    coverage: 'gap: FND-COM-FUL-073'
    scenarios: []
  - model: STATE-COM-FUL-009
    from: backordered
    to: committed
    source: >-
      services-commerce:modules/inventory/services/persistence/inventory-allocations.ts:fillBackorders
    coverage: 'gap: FND-COM-FUL-073'
    scenarios: []
  - model: STATE-COM-FUL-009
    from: picked
    to: shipped
    source: >-
      services-commerce:modules/inventory/services/persistence/inventory-allocations.ts:shipUnits
    coverage: 'gap: FND-COM-FUL-073'
    scenarios: []
  - model: STATE-COM-FUL-009
    from: committed
    to: released
    source: >-
      services-commerce:modules/inventory/services/persistence/inventory-allocations.ts:releaseFencedUnitsOfLine
    coverage: 'gap: FND-COM-FUL-073'
    scenarios: []
  - model: STATE-COM-FUL-009
    from: picked
    to: released
    source: >-
      services-commerce:docs/migrations/inventory/README.md (D-P7-24 amendment
      of D-P8I-13)
    coverage: 'gap: FND-COM-FUL-073'
    scenarios: []
  - model: STATE-COM-FUL-009
    from: backordered
    to: released
    source: >-
      services-commerce:modules/inventory/db/inventory-allocations.table.ts
      (commerce_release_claims)
    coverage: 'gap: FND-COM-FUL-073'
    scenarios: []
permission_audit:
  - key: commerce.inventory.read
    where_checked: api
    coverage: 'gap: FND-COM-FUL-073'
    note: >-
      Per-key positive and negative coverage is not separately mapped;
      permission scenarios retain the observed aggregate tests.
related_audit:
  - capability: CAP-COM-FUL-012
    scenarios:
      - ATS-COM-FUL-018-09
      - ATS-COM-FUL-018-10
  - capability: CAP-COM-FUL-017
    scenarios:
      - ATS-COM-FUL-018-09
      - ATS-COM-FUL-018-11
      - ATS-COM-FUL-018-12
      - ATS-COM-FUL-018-13
      - ATS-COM-FUL-018-14
  - capability: CAP-COM-FUL-019
    scenarios:
      - ATS-COM-FUL-018-14
      - ATS-COM-FUL-018-15
invariant_audit:
  - invariant: INV-DOM-COM-FUL-005-01
    scenarios:
      - ATS-COM-FUL-017-02
    coverage: covered
    provenance: observed
    source: >-
      services-commerce:modules/inventory/db/inventory-items.table.ts (column
      grants, apply_inventory_movement, apply_inventory_allocation)
  - invariant: INV-DOM-COM-FUL-005-02
    scenarios:
      - ATS-COM-FUL-017-02
    coverage: covered
    provenance: observed
    source: >-
      services-commerce:modules/inventory/db/inventory-items.table.ts:inventory_items_balances_check
  - invariant: INV-DOM-COM-FUL-005-03
    scenarios:
      - ATS-COM-FUL-017-01
      - ATS-COM-FUL-017-02
    coverage: covered
    provenance: observed
    source: >-
      services-commerce:modules/inventory/db/inventory-movements.table.ts:inventory_movements_quantity_sign_check
  - invariant: INV-DOM-COM-FUL-005-04
    scenarios:
      - ATS-COM-FUL-017-03
    coverage: covered
    provenance: documented
    source: 'services-commerce:docs/migrations/inventory/README.md#D-P8I-22'
  - invariant: INV-DOM-COM-FUL-005-05
    scenarios:
      - ATS-COM-FUL-018-05
    coverage: covered
    provenance: observed
    source: 'services-commerce:modules/inventory/db/inventory-allocations.table.ts'
  - invariant: INV-DOM-COM-FUL-005-06
    scenarios:
      - ATS-COM-FUL-018-03
    coverage: covered
    provenance: documented
    source: 'services-commerce:docs/migrations/inventory/README.md#D-P8I-21'
  - invariant: INV-DOM-COM-FUL-005-07
    scenarios:
      - ATS-COM-FUL-014-01
    coverage: covered
    provenance: observed
    source: >-
      services-commerce:modules/inventory/services/persistence/inventory-allocations.ts:shipUnits
  - invariant: INV-DOM-COM-FUL-005-08
    scenarios:
      - ATS-COM-FUL-017-05
    coverage: covered
    provenance: observed
    source: 'services-commerce:modules/inventory/services/stock/transfer-stock.ts'
  - invariant: INV-DOM-COM-FUL-005-09
    scenarios:
      - ATS-COM-FUL-017-04
    coverage: covered
    provenance: observed
    source: 'services-commerce:modules/inventory/services/stock/staff-movements.ts'
  - invariant: INV-DOM-COM-FUL-005-10
    scenarios:
      - ATS-COM-FUL-019-01
    coverage: covered
    provenance: observed
    source: >-
      services-commerce:modules/inventory/services/persistence/inventory-counts.ts:completeCount
  - invariant: INV-DOM-COM-FUL-005-11
    scenarios:
      - ATS-COM-FUL-019-02
    coverage: covered
    provenance: observed
    source: >-
      services-commerce:modules/inventory/db/inventory-exceptions.table.ts:inventory_exceptions_open_uidx
  - invariant: INV-DOM-COM-FUL-005-12
    scenarios:
      - ATS-COM-FUL-016-06
      - ATS-COM-FUL-017-09
      - ATS-COM-FUL-018-12
    coverage: covered
    provenance: observed
    source: >-
      services-commerce:modules/inventory/services/persistence/warehouses.ts;
      inventory-locations.ts
---

# Allocate stock and explain what buyers can obtain: Capability Test Specification

## Coverage

The category map distinguishes available cases from missing proof. A covered category has a scenario; the lifecycle and permission audits retain gaps in exhaustive coverage. Proposed cases describe the intended verification and carry no execution result.

| Rule | Scenarios |
|---|---|
| `BR-CAP-COM-FUL-018-01` | `ATS-COM-FUL-018-01`, `ATS-COM-FUL-018-12`, `ATS-COM-FUL-018-13` |
| `BR-CAP-COM-FUL-018-02` | `ATS-COM-FUL-018-02`, `ATS-COM-FUL-018-09` |
| `BR-CAP-COM-FUL-018-03` | `ATS-COM-FUL-018-03` |
| `BR-CAP-COM-FUL-018-04` | `ATS-COM-FUL-018-04`, `ATS-COM-FUL-018-15` |
| `BR-CAP-COM-FUL-018-05` | `ATS-COM-FUL-018-05`, `ATS-COM-FUL-018-08` |
| `BR-CAP-COM-FUL-018-06` | `ATS-COM-FUL-018-06`, `ATS-COM-FUL-018-11`, `ATS-COM-FUL-018-14` |
| `BR-CAP-COM-FUL-018-07` | `ATS-COM-FUL-018-07`, `ATS-COM-FUL-018-10` |
| `BR-CAP-COM-FUL-018-08` | `ATS-COM-FUL-018-03` |

## Interactions With Related Capabilities

The interaction cases exercise prepare an order's goods for dispatch; keep stock movements and transfers accountable; reconcile shelf counts and stock findings. They vary the upstream record or outcome and inspect the resulting work across the capability boundary. Workflow crossings also include stock, financial or application records when the flow depends on them. Existing test bindings retain their source anchors so a reviewer can distinguish supporting assertions from a complete workflow run.
