# Stock Reservation — QA Test Scenarios Guide

**Module:** Materials Management (MM) — GB5 Backend
**Version:** GB5 v1.0
**Date:** June 2026

---

## Table of Contents

1. [Happy Path Test Scenarios](#1-happy-path-test-scenarios)
2. [Validation & Edge Case Tests](#2-validation--edge-case-tests)
3. [Integration Tests](#3-integration-tests)
4. [Lot/Batch Level Tests](#4-lotbatch-level-tests)
5. [Auto-Linked PO Tests](#5-auto-linked-po-tests)
6. [Subcontracting Tests](#6-subcontracting-tests)
7. [Performance Benchmarks](#7-performance-benchmarks)
8. [DB Verification Queries](#8-db-verification-queries)
9. [Acceptance Criteria Sign-Off](#9-acceptance-criteria-sign-off)

---

## Test Setup — Common Assumptions

- Base URL: `{MMSL_BASE_URL}` (e.g., `https://localhost:7001`)
- All requests include `Login` header (LoginDTO JSON string)
- Test data: TenantId=1 (ClientId=1), OUId=1, StoreId=1, ItemId=100, SKUId=0
- TSTOCKPOSITION: ItemId=100, OUId=1, StoreId=1, QUANTITY=1000, RESERVEDQUANTITY=0

---

## 1. Happy Path Test Scenarios

### RES-001 — Reserve 500 Units from Stock Against SO

**Endpoint:** `POST /StockReservation/SaveStockReservation`

**Request:**
```json
{
  "StockReservationId": 0,
  "OUId": 1,
  "ReservationMode": 3,
  "ReservationSource": 0,
  "ReservationStatus": 2,
  "AssignmentLevel": 2,
  "ReservationDate": "2026-06-10",
  "ForObjectHeaderTypeId": 200,
  "ForObjectHeaderId": 5001,
  "ForObjectTypeId": 201,
  "ForObjectId": 9001,
  "ItemId": 100,
  "SKUId": 0,
  "StoreId": 1,
  "ReservedQuantity": 500.0
}
```

**Expected Response:** HTTP 200
```json
{
  "IsSuccess": true,
  "Body": "\"Details have been saved successfully with id = {newId}\""
}
```

**DB Verification:**
```sql
-- 1. TRESERVATION row created
SELECT STOCKRESERVATIONID, RESERVATIONSTATUS, RESERVEDSQUANTITY, ISDELETED
FROM   TRESERVATION
WHERE  FOROBJECTHEADERID = 5001 AND FOROBJECTID = 9001 AND ITEMID = 100
-- Expected: 1 row, STATUS=2, RESERVEDQUANTITY=500, ISDELETED=0

-- 2. TSTOCKPOSITION updated
SELECT RESERVEDQUANTITY
FROM   TSTOCKPOSITION
WHERE  ITEMID=100 AND STOREID=1
-- Expected: RESERVEDQUANTITY = (previous + 500)
```

---

### RES-002 — Reserve 200 kg from PO Pipeline Against Work Indent

**Endpoint:** `POST /StockReservation/SaveStockReservation`

**Request:**
```json
{
  "StockReservationId": 0,
  "OUId": 1,
  "ReservationMode": 3,
  "ReservationSource": 1,
  "ReservationStatus": 2,
  "AssignmentLevel": 5,
  "ForObjectHeaderId": 6001,
  "ForObjectId": 9002,
  "ItemId": 101,
  "FromObjectHeaderId": 7001,
  "FromObjectId": 8001,
  "FromAllotedAllocationId": 4001,
  "ReservedQuantity": 200.0
}
```

**Expected Response:** HTTP 200

**DB Verification:**
```sql
-- 1. TRESERVATION created, Source=Document
SELECT RESERVATIONSOURCE, FROMALLOTEDALLOCATIONID, RESERVEDQUANTITY
FROM   TRESERVATION WHERE FOROBJECTID = 9002 AND ITEMID = 101
-- Expected: RESERVATIONSOURCE=1, FROMALLOTEDALLOCATIONID=4001, QTY=200

-- 2. TPENDINGALLOCATION reduced
SELECT PENDINGQUANTITY FROM TPENDINGALLOCATION WHERE ALLOTEDALLOCATIONID = 4001
-- Expected: reduced by 200 from original value
```

---

### RES-003 — Partial Unreserve (200 of 500 reserved, 0 issued)

**Setup:** Create reservation with 500 units (RES-001). Get StockReservationId and Version.

**Endpoint:** `POST /StockReservation/UnreserveStockReservation`

**Request:**
```json
{
  "StockReservationId": {id from RES-001},
  "Version": 1,
  "Reason": "Customer reduced order",
  "ReleaseQuantity": 200.0
}
```

**Expected Response:** HTTP 200, Body: `"Details have been deleted successfully"`

**DB Verification:**
```sql
SELECT RESERVEDQUANTITY, RESERVATIONSTATUS, ISDELETED
FROM   TRESERVATION WHERE STOCKRESERVATIONID = {id}
-- Expected: RESERVEDQUANTITY=300, STATUS=2 (still Confirmed), ISDELETED=0

SELECT RESERVEDQUANTITY FROM TSTOCKPOSITION WHERE ITEMID=100 AND STOREID=1
-- Expected: reduced by 200 from post-RES-001 value
```

---

### RES-004 — Full Unreserve

**Endpoint:** `POST /StockReservation/UnreserveStockReservation`

**Request:**
```json
{
  "StockReservationId": {id},
  "Version": 1,
  "Reason": "SO cancelled by customer",
  "ReleaseQuantity": null
}
```

**Expected Response:** HTTP 200

**DB Verification:**
```sql
SELECT RESERVATIONSTATUS, ISDELETED, DELETEREASON
FROM   TRESERVATION WHERE STOCKRESERVATIONID = {id}
-- Expected: STATUS=3 (Released), ISDELETED=1, DELETEREASON='SO cancelled by customer'

SELECT RESERVEDQUANTITY FROM TSTOCKPOSITION WHERE ITEMID=100 AND STOREID=1
-- Expected: restored to pre-reservation value
```

---

### RES-005 — Free-Track a Demand

**Endpoint:** `POST /StockReservation/FreeTrackDemand`

**Request:**
```json
{
  "OUId": 1,
  "ForObjectHeaderTypeId": 300,
  "ForObjectHeaderId": 5002,
  "ForObjectTypeId": 301,
  "ForObjectId": 9003,
  "ItemId": 100,
  "SKUId": 0,
  "Reason": "Internal R&D sample — no stock commitment"
}
```

**Expected Response:** HTTP 200, Body: `"Details Saved Successfully"`

**DB Verification:**
```sql
SELECT ISFREETRACKED, RESERVATIONSTATUS, RESERVEDQUANTITY
FROM   TRESERVATION WHERE FOROBJECTID = 9003 AND ITEMID = 100
-- Expected: ISFREETRACKED=1, STATUS=5 (FreeTracked), RESERVEDQUANTITY=0

SELECT RESERVEDQUANTITY FROM TSTOCKPOSITION WHERE ITEMID=100 AND STOREID=1
-- Expected: UNCHANGED — free-track does not block stock
```

---

### RES-006 — Reassign 500 Units from SO-A to SO-B

**Setup:** Active Confirmed reservation for ForObjectId=9001 (500 units).

**Endpoint:** `POST /StockReservation/ReassignReservation`

**Request:**
```json
{
  "OriginalReservationId": {id},
  "ReassignQuantity": 500.0,
  "Reason": "Priority escalation — SO-099 export shipment",
  "NewForObjectHeaderTypeId": 200,
  "NewForObjectHeaderId": 5003,
  "NewForObjectTypeId": 201,
  "NewForObjectId": 9099
}
```

**Expected Response:** HTTP 200

**DB Verification:**
```sql
-- Original: Reassigned
SELECT RESERVATIONSTATUS FROM TRESERVATION WHERE STOCKRESERVATIONID = {id}
-- Expected: STATUS=4 (Reassigned)

-- New reservation: Confirmed for new demand
SELECT STOCKRESERVATIONID, RESERVATIONSTATUS, FOROBJECTID, RESERVEDQUANTITY
FROM   TRESERVATION WHERE FOROBJECTID = 9099 AND ITEMID = 100
-- Expected: STATUS=2, RESERVEDQUANTITY=500

-- TREASSIGNMENTLOG entry
SELECT ORIGINALRESERVATIONID, NEWRESERVATIONID, REASSIGNEDQUANTITY, REASON
FROM   TREASSIGNMENTLOG WHERE ORIGINALRESERVATIONID = {id}
-- Expected: 1 row with correct values

-- TSTOCKPOSITION: UNCHANGED (supply side didn't move)
```

---

### RES-007 — Auto-Reserve MRP Run (All Demands Fully Coverable)

**Setup:** MRP run ID=10 has 5 demand lines, total 800 units. TSTOCKPOSITION has 1000 available for ItemId=100.

**Endpoint:** `POST /StockReservation/AutoReserveForMRPRun`

**Request:**
```json
{
  "MrpRunId": 10,
  "OUId": 1,
  "Mode": 1,
  "FallbackToDocument": false
}
```

**Expected Response:** HTTP 200
```json
{
  "MrpRunId": 10,
  "TotalDemands": 5,
  "FullyReservedCount": 5,
  "PartiallyReservedCount": 0,
  "NotReservedCount": 0,
  "Shortages": []
}
```

**DB Verification:**
```sql
SELECT COUNT(*), RESERVATIONSTATUS, MRPRUNID
FROM   TRESERVATION WHERE MRPRUNID = 10 AND ISDELETED = 0
GROUP  BY RESERVATIONSTATUS, MRPRUNID
-- Expected: 5 rows, STATUS=1 (Tentative)
```

---

### RES-008 — Confirm All Tentative for MRP Run

**Setup:** Tentative reservations from RES-007.

**Endpoint:** `POST /StockReservation/ConfirmTentativeReservations`

**Request body:** `{ "MrpRunId": 10 }`

**Expected Response:** HTTP 200, Body: `"Details Updated Successfully"`

**DB Verification:**
```sql
SELECT COUNT(*), RESERVATIONSTATUS
FROM   TRESERVATION WHERE MRPRUNID = 10 AND ISDELETED = 0
GROUP  BY RESERVATIONSTATUS
-- Expected: all rows STATUS=2 (Confirmed), none STATUS=1
```

---

## 2. Validation & Edge Case Tests

### RES-E01 — Reserve More Than Available Stock

**Setup:** TSTOCKPOSITION has 100 units available, request 500.

**Request:** SaveStockReservation with `ReservedQuantity: 500`

**Expected Response:** HTTP 400 (or exception response)
```json
{ "IsSuccess": false, "Body": "\"Insufficient available quantity.\"" }
```

**DB Verification:** No new TRESERVATION row created. TSTOCKPOSITION unchanged.

---

### RES-E02 — Unreserve Quantity Exceeding Balance

**Setup:** Reservation with 500 reserved, 300 issued (BalanceQuantity=200).

**Request:** Unreserve with `ReleaseQuantity: 300`

**Expected:** `"Release quantity exceeds balance (issued portion cannot be unreserved)."`

---

### RES-E03 — Duplicate Active Reservation

**Setup:** Active Confirmed reservation for ForObjectId=9001, ItemId=100.

**Request:** SaveStockReservation with same ForObjectHeaderId + ForObjectId + ItemId.

**Expected:** `"An active reservation already exists for this demand and item."`

---

### RES-E04 — Planning Mode via SaveReservation

**Request:** SaveStockReservation with `ReservationMode: 1`

**Expected:** `"Planning-mode reservations are created by AutoReserve only."`

---

### RES-E05 — Reservation Date Outside Open Period

**Request:** SaveStockReservation with a date in a closed financial period.

**Expected:** Validation error from period check (financial period validation).

---

### RES-E06 — Reassign to Same Demand

**Request:** ReassignReservation with `NewForObjectId` = same as original `ForObjectId`.

**Expected:** `"New demand target must differ from original."`

---

### RES-E07 — Reassign Non-Confirmed Reservation

**Setup:** Reservation in Tentative status (STATUS=1).

**Request:** ReassignReservation.

**Expected:** `"Only Confirmed reservations can be reassigned."`

---

### RES-E08 — Free-Track When Confirmed Reservation Exists

**Setup:** Active Confirmed reservation for ForObjectId=9001.

**Request:** FreeTrackDemand for the same ForObjectHeaderId + ForObjectId + ItemId.

**Expected:** `"Unreserve the existing reservation before marking as Free-Tracked."`

---

### RES-E09 — Confirm Non-Tentative Reservation

**Setup:** Reservation in Confirmed status.

**Request:** POST /ConfirmReservation with that StockReservationId.

**Expected:** `"Reservation is not in Tentative status or does not exist."`

---

### RES-E10 — Partial Auto-Reserve (50% Coverable)

**Setup:** MRP run with 10 demand lines totalling 1000 units. Stock only has 500 available.

**Request:** AutoReserveForMRPRun.

**Expected Response:** `PartiallyReservedCount > 0`, `Shortages` array populated.

```json
{
  "TotalDemands": 10,
  "FullyReservedCount": 5,
  "PartiallyReservedCount": 3,
  "NotReservedCount": 2,
  "Shortages": [...]
}
```

---

## 3. Integration Tests

### INT-001 — MRP → Auto-Reserve → Confirm → Issue Stock

```
1. Run AutoReserveForMRPRun → Tentative reservations created
2. ConfirmTentativeReservations → all STATUS=2 (Confirmed)
3. POST UpdateIssuedQuantity (via Goods Issue module) → ISSUEDQUANTITY += qty
4. Verify: TRESERVATION.ISSUEDQUANTITY = issued qty
5. Verify: STATUS = 6 (PartiallyFulfilled) if partial, 7 (FullyFulfilled) if complete
```

**DB Verification:**
```sql
SELECT RESERVEDQUANTITY, ISSUEDQUANTITY, RESERVATIONSTATUS
FROM   TRESERVATION WHERE MRPRUNID = {runId}
-- PartiallyFulfilled: ISSUEDQUANTITY > 0 AND ISSUEDQUANTITY < RESERVEDQUANTITY, STATUS=6
-- FullyFulfilled:     ISSUEDQUANTITY = RESERVEDQUANTITY, STATUS=7
```

---

### INT-002 — SO Cancelled → Unreserve → New SO Uses Same Stock

```
1. Reserve 500 units for SO-A: TSTOCKPOSITION.RESERVEDQUANTITY = 500
2. SO-A cancelled → UnreserveStockReservation (full)
   TSTOCKPOSITION.RESERVEDQUANTITY = 0, STATUS=3 (Released)
3. Create reservation for SO-B: 500 units
   TSTOCKPOSITION.RESERVEDQUANTITY = 500 again
4. Verify: SO-B reservation STATUS=2 (Confirmed)
```

---

### INT-003 — Reservation List Paging

**Endpoint:** `GET /StockReservation/GetStockReservationList?OUId=1&Page=1&Size=20`

**Expected:** HTTP 200, array of StockReservationListDTO, max 20 rows.

**Verify:** Soft-deleted rows (ISDELETED=1) do not appear in the list.

---

### INT-004 — Multi-Tenant Isolation

```
1. Create reservation with TenantId=1 (ClientId=1)
2. Query with LoginDTO.ClientId=2
3. Verify: reservation NOT returned (all queries filter WHERE TENANTID = @TenantId)
```

---

## 4. Lot/Batch Level Tests

### RES-L01 — Matrix Activated with Lot Assignment

**Setup:** Matrix M-001 created with TMATRIXINPUT.LotId = 50.

**Trigger:** Matrix activation call.

**DB Verification:**
```sql
SELECT ISLOTLOCKED, LOTID, RESERVATIONSTATUS
FROM   TRESERVATION WHERE FOROBJECTHEADERID = {matrixId}
-- Expected: ISLOTLOCKED=1, LOTID=50, STATUS=2 (Confirmed)
```

---

### RES-L02 — Issue Screen Shows Only Reserved Lot

**Test:** On Issue screen for Matrix M-001, available lots query must:
- Include `AND LOTID = (SELECT LOTID FROM TRESERVATION WHERE ... AND ISLOTLOCKED=1)`
- Return only Lot 50 for selection

**Verify:** Lot 50 visible; other lots of same item NOT selectable.

---

### RES-L03 — Attempt to Issue Different Lot for Lot-Locked Reservation

**Test:** Submit issue with LotId ≠ 50 for lot-locked reservation.

**Expected:** System rejects: issue line with wrong lot is blocked.

---

### RES-L04 — Lot-Level Unreserve and Re-Reserve from Different Lot

```
1. Lot-locked reservation: LotId=50, Qty=200
2. UnreserveStockReservation (full)
   TRESERVATION STATUS=3, ISLOTLOCKED becomes irrelevant
3. Create new reservation: LotId=51, Qty=200, ISLOTLOCKED=1
4. Verify: new reservation references Lot 51
```

---

## 5. Auto-Linked PO Tests

### RES-B01 — PO Created from SO (Trade Item), AutoLink=true

**Setup:** AutoReserveOnLinkedPO enabled for trade item BizTransactionType.

**Trigger:** Create PO from SO-2026-100 (300 units of trade item).

**DB Verification:**
```sql
SELECT ISAUTOLINKED, LINKEDPOID, RESERVATIONSOURCE, RESERVATIONSTATUS
FROM   TRESERVATION WHERE FOROBJECTHEADERID = {so_header_id}
-- Expected: ISAUTOLINKED=1, LINKEDPOID={po_id}, SOURCE=1 (Document), STATUS=2 (Confirmed)
```

---

### RES-B02 — PO Received → Goods Receipt Converts Pipeline to Stock

**Trigger:** Post goods receipt for PO with auto-linked reservation.

**DB Verification:**
```sql
-- Old pipeline reservation released
SELECT RESERVATIONSTATUS, ISDELETED FROM TRESERVATION WHERE LINKEDPOID = {po_id}
-- Expected: STATUS=3 (Released), ISDELETED=1

-- New stock reservation created
SELECT RESERVATIONSOURCE, RESERVATIONSTATUS, STOREID
FROM   TRESERVATION WHERE FOROBJECTHEADERID = {so_header_id} AND ISDELETED=0
-- Expected: SOURCE=0 (Stock), STATUS=2, STOREID = receiving store
```

---

### RES-B03 — Partial PO Receipt (200 of 300 ordered)

**Trigger:** GR for 200 of 300 ordered.

**Expected:**
- Stock reservation for 200 (Confirmed)
- Pipeline reservation reduced to 100 (still active) OR a new pipeline reservation for 100

**DB Verification:**
```sql
SELECT RESERVATIONSOURCE, RESERVEDQUANTITY, RESERVATIONSTATUS
FROM   TRESERVATION WHERE FOROBJECTHEADERID = {so_header_id} AND ISDELETED=0
-- Expected: 2 rows: SOURCE=0 QTY=200, and SOURCE=1 QTY=100 (remaining pipeline)
```

---

### RES-B04 — AutoLink=false — No Auto-Reservation

**Setup:** AutoReserveOnLinkedPO = false for this BizTransactionType.

**Trigger:** Create PO from SO.

**DB Verification:** No TRESERVATION row created automatically. Must reserve manually.

---

## 6. Subcontracting Tests

### RES-S01 — Job Work Dispatch — Material with Active Reservation

**Setup:** Confirmed reservation SR/202606/00042 for WO-2026-015 (500 units of Part A, Store A).

**Endpoint:** `POST /StockReservation/MarkSubconOut`

**Request:**
```json
{
  "StockReservationId": 42,
  "Version": 1,
  "SubconObjectHeaderTypeId": 400,
  "SubconObjectHeaderId": 8001,
  "SubconLocation": "HeatTech Pvt Ltd",
  "SentOutDate": "2026-06-10T00:00:00Z",
  "ExpectedReturnDate": "2026-06-17"
}
```

**Expected Response:** HTTP 200, Body: `"Details Saved Successfully"`

**DB Verification:**
```sql
SELECT RESERVATIONSTATUS, SUBCONOBJECTHEADERID, SUBCONLOCATION, SUBCONSENTOUT
FROM   TRESERVATION WHERE STOCKRESERVATIONID = 42
-- Expected: STATUS=8 (SubcontractingOut), SUBCONOBJECTHEADERID=8001,
--           SUBCONLOCATION='HeatTech Pvt Ltd', SUBCONSENTOUT populated
```

**Verify:** ItemId=100 in Store A does NOT appear in GetAvailabilityForReservation result.

---

### RES-S02 — Job Work Dispatch — Material WITHOUT Reservation

**Test:** Dispatch material from Store A, but no TRESERVATION row exists for that item.

**Expected:** No TRESERVATION changes. JW document processes normally without reservation impact.

---

### RES-S03 — Job Work Return — Full Quantity (490 of 500 returned)

**Endpoint:** `POST /StockReservation/MarkSubconReturned`

**Request:**
```json
{
  "StockReservationId": 42,
  "Version": 2,
  "ActualReturnedQty": 490.0
}
```

**Expected Response:** HTTP 200

**DB Verification:**
```sql
SELECT RESERVATIONSTATUS, RESERVEDQUANTITY
FROM   TRESERVATION WHERE STOCKRESERVATIONID = 42
-- Expected: STATUS=2 (Confirmed), RESERVEDQUANTITY=490
-- Process loss of 10 units recorded against JW document
```

---

### RES-S04 — Job Work Return — Partial (200 of 500 returned)

**Trigger:** 200 returned, 300 still at vendor.

**DB Verification:**
```sql
-- After partial return, two reservation rows expected:
SELECT STOCKRESERVATIONID, RESERVATIONSTATUS, RESERVEDQUANTITY
FROM   TRESERVATION WHERE FOROBJECTID = {wo_id} AND ISDELETED=0
-- Row 1: STATUS=2 (Confirmed), RESERVEDQUANTITY=200 (returned, in store)
-- Row 2: STATUS=8 (SubcontractingOut), RESERVEDQUANTITY=300 (still at vendor)
```

---

### RES-S05 — Past Expected Return Date

**Endpoint:** `GET /StockReservation/GetSubconTracking?OUId=1`

**Expected:** SubconTrackingReportDTO rows with `IsOverdue=true` for items where `SUBCONEXPECTEDRETURN < TODAY`.

---

### RES-S06 — Nested Subcontracting (JW1 → JW2)

**Setup:** Material sent to JW-2026-008 (HeatTech). Then dispatched from HeatTech to JW-2026-009 (PowerCoat).

**Trigger:** MarkSubconOut called again with SubconObjectHeaderId = JW-2026-009.

**DB Verification:**
```sql
SELECT SUBCONOBJECTHEADERID, SUBCONLOCATION
FROM   TRESERVATION WHERE STOCKRESERVATIONID = 42
-- Expected: SUBCONOBJECTHEADERID=JW-2026-009, SUBCONLOCATION='PowerCoat Inc'
-- Demand link (FOROBJECTID) preserved — still pointing to WO-2026-015
```

---

## 7. Performance Benchmarks

| Test | Tool | Setup | Acceptance Threshold |
|---|---|---|---|
| AutoReserve 1000 demand lines | Postman / k6 | 1000 demand lines, 20 stores with stock | Complete in **< 10 seconds** |
| GetAvailabilityForReservation | Postman | Normal DB load | Response **< 500ms** |
| GetStockReservationList paged (100 rows) | Postman | 100 rows per page | Response **< 300ms** |
| Concurrent save — 10 parallel requests | k6 | 10 simultaneous SaveReservation for same item | All succeed or fail gracefully; no deadlocks; no orphaned transactions |
| Optimistic concurrency retry | Postman | VERSION mismatch scenario | Retry loop executes ≤ 3 times; clean error on exhaust |

---

## 8. DB Verification Queries

### Reconciliation: TSTOCKPOSITION vs Active Reservations

Run after any bulk operation to verify consistency:

```sql
-- Should return 0 rows (discrepancies)
SELECT sp.ITEMID, sp.SKUID, sp.STOREID,
       sp.RESERVEDQUANTITY        AS PositionReserved,
       ISNULL(r.TotalReserved, 0) AS SumOfActiveReservations,
       sp.RESERVEDQUANTITY - ISNULL(r.TotalReserved, 0) AS Discrepancy
FROM   TSTOCKPOSITION sp
LEFT JOIN (
    SELECT ITEMID, SKUID, STOREID, SUM(RESERVEDQUANTITY) AS TotalReserved
    FROM   TRESERVATION
    WHERE  RESERVATIONSOURCE = 0   -- Stock source only
      AND  RESERVATIONSTATUS IN (2, 6, 8)  -- Confirmed, PartiallyFulfilled, SubcontractingOut
      AND  ISDELETED = 0
    GROUP  BY ITEMID, SKUID, STOREID
) r ON r.ITEMID=sp.ITEMID AND r.SKUID=sp.SKUID AND r.STOREID=sp.STOREID
WHERE  sp.RESERVEDQUANTITY <> ISNULL(r.TotalReserved, 0)
```

### All Active Reservations for an Item

```sql
SELECT r.STOCKRESERVATIONID, r.RESERVATIONNUMBER,
       r.RESERVATIONSTATUS, r.RESERVATIONMODE,
       r.RESERVEDQUANTITY, r.ISSUEDQUANTITY,
       r.RESERVEDQUANTITY - r.ISSUEDQUANTITY AS BalanceQty,
       r.FOROBJECTHEADERID, r.FOROBJECTID,
       r.STOREID, r.ISLOTLOCKED, r.LOTID
FROM   TRESERVATION r
WHERE  r.ITEMID    = @ItemId
  AND  r.TENANTID  = @TenantId
  AND  r.ISDELETED = 0
ORDER  BY r.CREATEDON DESC
```

### Reassignment Chain for a Reservation

```sql
-- All reassignments where this reservation was the original
SELECT l.REASSIGNMENTLOGID, l.ORIGINALRESERVATIONID,
       l.NEWRESERVATIONID, l.REASSIGNEDQUANTITY, l.REASON,
       l.REASSIGNEDON
FROM   TREASSIGNMENTLOG l
WHERE  l.ORIGINALRESERVATIONID = @StockReservationId
   OR  l.NEWRESERVATIONID      = @StockReservationId
ORDER  BY l.REASSIGNEDON
```

### Subcontracting Out Report

```sql
SELECT r.STOCKRESERVATIONID, r.RESERVATIONNUMBER,
       r.ITEMID, r.RESERVEDQUANTITY,
       r.FOROBJECTID,
       r.SUBCONLOCATION,
       r.SUBCONSENTOUT,
       r.SUBCONEXPECTEDRETURN,
       CASE WHEN r.SUBCONEXPECTEDRETURN < CAST(GETUTCDATE() AS DATE)
            THEN 1 ELSE 0 END AS IsOverdue
FROM   TRESERVATION r
WHERE  r.RESERVATIONSTATUS = 8   -- SubcontractingOut
  AND  r.OUID     = @OUId
  AND  r.TENANTID = @TenantId
  AND  r.ISDELETED = 0
ORDER  BY r.SUBCONEXPECTEDRETURN
```

### MEVENTTYPE Seed Verification

```sql
SELECT EVENTTYPEID, EVENTTYPENAME, ISAUDIT
FROM   MEVENTTYPE
WHERE  EVENTTYPEID BETWEEN -1799996009 AND -1799996001
ORDER  BY EVENTTYPEID DESC
-- Expected: 9 rows
```

---

## 9. Acceptance Criteria Sign-Off

### Happy Path

- [ ] RES-001: Stock reservation created, TSTOCKPOSITION updated
- [ ] RES-002: Pipeline reservation created, TPENDINGALLOCATION reduced
- [ ] RES-003: Partial unreserve — reserved qty reduced, not deleted
- [ ] RES-004: Full unreserve — STATUS=3 (Released), ISDELETED=1, stock restored
- [ ] RES-005: Free-track — STATUS=5 (FreeTracked), no stock block
- [ ] RES-006: Reassign — original STATUS=4, new STATUS=2, TREASSIGNMENTLOG populated
- [ ] RES-007: MRP auto-reserve — all Tentative reservations created
- [ ] RES-008: Confirm all Tentative → all Confirmed

### Validation & Edge Cases

- [ ] RES-E01 through RES-E10: all produce correct error messages, no unhandled exceptions

### Integration

- [ ] INT-001: MRP → Confirm → Issue flow end-to-end
- [ ] INT-002: Cancel → Unreserve → Re-reserve same stock succeeds
- [ ] INT-003: Paged list — no deleted rows returned
- [ ] INT-004: Multi-tenant isolation verified — cross-tenant data never returned

### Lot/Batch

- [ ] RES-L01 through RES-L04: lot-level lock, issue constraint, unreserve/re-reserve

### Auto-Linked PO

- [ ] RES-B01 through RES-B04: auto-link creation, GR conversion, partial receipt, disabled config

### Subcontracting

- [ ] RES-S01 through RES-S06: dispatch, return, partial return, overdue, nested

### System Criteria

- [ ] Soft-delete: ISDELETED=1 rows never appear in any list or availability query
- [ ] Audit trail: MEVENTTYPE has 9 seed rows; EventLog contains one entry per write operation
- [ ] Reconciliation: TSTOCKPOSITION.RESERVEDQUANTITY = sum of active stock-source reservations
- [ ] Concurrency: VERSION mismatch triggers retry (≤ 3); exhausted retry returns clean error
- [ ] Multi-tenant: TenantId filter on every query — verified by running cross-tenant request
- [ ] Performance: all benchmarks in Section 7 met
