---
id: WF-COM-CAT-015
title: Give promotion uses back
trigger: >-
  Voids, eligible full cancellations and authorized manual actions return promotion capacity. They preserve
  the discount evidence and do not reverse Accounting documents.
product_area: commerce
module: catalog
revision: 1
status: draft
design_intent: confirmed
implementation_evidence: observed
inference: []
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
  - CAP-COM-CAT-017
moves:
  - STATE-COM-CAT-010
crosses:
  - SVC-COM-CAT-promotions
idempotency: >-
  Only applied uses can restore capacity. Repeated staff release receives the already-released refusal instead
  of changing counters again.
concurrency: >-
  Release updates applied uses and takes counter locks in the same order used by placement, within the
  caller’s void or cancellation transaction.
hops:
  - 'n': 1
    actor_or_service: Order void (orders module)
    action: 'Within order voiding, release every applied promotion use with the void reason.'
    anchor: >-
      services-commerce:modules/promotions/services/redemptions/redemption-release.ts:releaseVoidedOrderRedemptions
    seam: sync
    on_failure: Roll back voiding if release fails.
    left_inconsistent: The void transaction retains no partial release.
    observer: The requesting person or service receives the result and any returned refusal.
    provenance: documented
    source: 'services-commerce:modules/orders/services/orders/void-order.ts'
  - 'n': 2
    actor_or_service: Cancellation workflow or enforcement handler (orders and fulfilment modules)
    action: >-
      Release a cancellation’s use only when every allocated line is fully cancelled and retained release
      policy permits it.
    anchor: >-
      services-commerce:modules/promotions/services/redemptions/redemption-release.ts:releaseRedemptionsForCancelledLines
    seam: sync
    on_failure: Roll back the caller’s cancellation transaction on release failure.
    left_inconsistent: The failed cancellation retains no partial capacity adjustment.
    observer: The requesting person or service receives the result and any returned refusal.
    provenance: documented
    source: 'services-commerce:modules/orders/services/cancellations/cancellation-workflow.ts'
  - 'n': 3
    actor_or_service: Promotions API
    action: >-
      Accept an override-authorized release reason and record release in campaign and order history within one
      transaction.
    anchor: 'services-commerce:modules/promotions/services/promotions/promotion-workflow.ts:releaseRedemption'
    seam: sync
    on_failure: 'Reject missing authority, missing reason or a use already released.'
    left_inconsistent: The refused request changes no usage or history.
    observer: The requesting person or service receives the result and any returned refusal.
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotion-redemptions.e2e.spec.js#refuses a release
      without a reason, 400
  - 'n': 4
    actor_or_service: Redemption persistence
    action: >-
      Move applied usage to released and restore code and campaign capacity without altering discount
      evidence.
    anchor: 'services-commerce:modules/promotions/services/persistence/promotion-redemptions.ts'
    seam: sync
    on_failure: Roll back the caller transaction if capacity restoration fails.
    left_inconsistent: Monetary allocations remain unchanged; failed restoration commits no counter changes.
    observer: The recorded release is visible through usage and performance reads.
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/integration/services/persistence/promotion-statistics.queries.integration.spec.js#sums
      the discount given of the applied redemptions only: $5.00 and $3.00, not the released $10.00
branches:
  - 'Partial cancellation, retained-line amendment and refund do not independently restore a use.'
waits: []
evidence:
  - >-
    services-commerce at c8b66a98: modules/promotions/services/redemptions/redemption-release.ts;
    services/persistence/promotion-redemptions.ts (releaseRedemptions);
    services/promotions/promotion-workflow.ts (releaseRedemption, ORDER_PROMOTION_RELEASED_EVENT).
  - >-
    services-commerce at c8b66a98: modules/orders/services/orders/void-order.ts,
    modules/orders/services/cancellations/cancellation-workflow.ts,
    modules/fulfillment/services/enforcement/enforcement-handler.ts (the callers);
    docs/milestones/COMM-M1-set-up-what-you-sell.md (D13).
controls: [TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-CRY-001, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002]
---

# Give promotion uses back

## What It Does

Voids, eligible full cancellations and authorized manual actions return promotion capacity. They preserve the discount evidence and do not reverse Accounting documents.

## The Flow

```mermaid
sequenceDiagram
  participant V as Void (orders)
  participant C as Cancellation (orders, fulfilment)
  participant S as Staff (override)
  participant R as Redemption persistence
  V->>R: release all applied (order voided)
  C->>R: release fully cancelled, if frozen setting on
  S->>R: release one (reason)
  R->>R: applied to released, counts minus one
  R-->>S: promotion history and order event
```

## When Something Fails

Roll back voiding if release fails. The void transaction retains no partial release. Roll back the caller’s cancellation transaction on release failure. The failed cancellation retains no partial capacity adjustment. Reject missing authority, missing reason or a use already released. The refused request changes no usage or history. Roll back the caller transaction if capacity restoration fails. Monetary allocations remain unchanged; failed restoration commits no counter changes.

## Open Design Questions

The implemented recovery and concurrency behaviour above defines the current boundary. Any change to transaction scope or product authority requires a recorded decision.
