# Phase 3B — Parts and inventory audit

## Baseline and scope

Starting branch: `master`; frozen HEAD: `b90f236`. Starting regression baseline:
1,395 tests, zero failures and skips. Phase 3B.1 remains the global SparePart
master and compatibility authority. Inventory is a separate app because its
company-owned operational transactions and immutable physical history have a
different lifecycle from global part master data.

The only frozen service integrations are the explicitly approved serialization
policy guard in `apps/parts/services.py` and the unresolved-inventory closure
guard in `apps/service/handover_services.py`. No baseline test or historical
migration is changed. No Phase 3C functionality, staging, commit, or push is part
of this work.

## Sequential checkpoint evidence

| Checkpoint | Verification before proceeding |
| --- | --- |
| 3B.2 | 170 tests passed: 73 inventory tests and 97 unchanged parts tests; 16 inventory PostgreSQL concurrency tests |
| 3B.3 | 131 inventory tests passed; 31 concurrency tests |
| 3B.4 | 176 inventory tests passed; 43 concurrency tests |
| 3B.5 | 282 tests passed: 218 inventory and 64 unchanged handover tests; final additional Admin check passed 21 tests; inventory total reached 219, including 55 concurrency tests |
| 3B.6 | Combined inventory run passed 281 tests, followed by expanded control/Admin verification of 83 tests and final serial query/Admin verification of 32 tests; inventory total reached 288, including 71 concurrency tests |

Checks, migration-drift checks, and whitespace checks passed at the checkpoints.
All 11 new migrations were inspected and exercised by PostgreSQL test database
creation. Development database migrations `inventory.0001`–`0011` are applied.

## Models and migrations

The 24 concrete models are:

| Area | Models |
| --- | --- |
| Ledger | InventoryLocation, StockPosition, SerializedStockUnit, StockMovement, StockLedgerEntry, StockMovementUnit |
| Documents | InventoryDocumentSequence, GoodsReceipt, GoodsReceiptLine, GoodsReceiptIdentifier, StockTransfer, StockTransferLine, StockTransferUnit |
| Demand | PartsRequest, PartsRequestLine, PartsRequestEvent, StockReservation, ReservationUnit |
| Usage | PartsIssue, PartsDisposition, DefectiveRecovery |
| Control | StockCount, StockCountUnit, StockAdjustment |

`InventoryRecord` is abstract. StockPosition is a locking anchor, not an
authoritative mutable balance. No quantity counter was added to SparePart.

New migrations: `0001_initial`, `0002_ledger_integrity`,
`0003_receiving_transfer`, `0004_document_integrity`,
`0005_job_parts_reservations`, `0006_reservation_integrity`,
`0007_job_parts_usage`, `0008_usage_integrity`, `0009_defective_recovery`,
`0010_stock_control`, `0011_control_integrity`.

PostgreSQL checks, unique constraints, composite ownership references, immutable
history triggers, and deferred posting-coherence triggers complement service
validation. They cover enum/quantity/timestamp validity, document numbers,
permanent part/identifier uniqueness, active serial reservations, unit ownership,
typed ledger endpoints, lifecycle evidence, and count variance linkage.

## Public operational APIs

Every business command requires an actor and uses the existing scope-aware
authorization engine. Internal validation, revision, and locking helpers are
not alternate authorization entry points.

| Module | Business services |
| --- | --- |
| services | create_location, update_location, deactivate_location, reactivate_location, register_serialized_unit, receive_stock, move_stock |
| document_services | create_goods_receipt, update_goods_receipt, set_goods_receipt_lines, post_goods_receipt, cancel_goods_receipt; create_stock_transfer, update_stock_transfer, set_stock_transfer_lines, dispatch_stock_transfer, receive_stock_transfer, cancel_stock_transfer |
| request_services | create_parts_request, approve_parts_request, reject_parts_request, cancel_parts_request, reserve_parts, release_reservation |
| usage_services | issue_reserved_parts, consume_issued_parts, return_unused_parts, recover_defective_component |
| control_services | adjust_stock, create_stock_count, start_stock_count, record_stock_count, reconcile_stock_count, cancel_stock_count |

| Module | Scoped queries |
| --- | --- |
| queries | authorized_locations, stock_on_hand, available_stock, stock_positions_for_location, stock_positions_for_part, serialized_history |
| document_queries | goods_receipts, stock_transfers, goods_receipt_detail, stock_transfer_detail |
| request_queries | parts_requests, parts_request_detail, stock_reservations, request_fulfillment |
| usage_queries | parts_issues, parts_usage, issue_detail, defective_recoveries |
| control_queries | stock_counts, stock_adjustments, control_positions, inventory_anomalies |

The read-only `audit_inventory --actor <active-username>` command reports scoped
anomaly IDs and fails when anomalies exist or the actor has no visible scope.
It does not repair or rewrite inventory.

## State machines and physical accounting

| Record | Supported transitions |
| --- | --- |
| Receipt | DRAFT → POSTED or CANCELLED |
| Transfer | DRAFT → DISPATCHED → RECEIVED; DRAFT → CANCELLED |
| Request | REQUESTED → APPROVED or REJECTED/CANCELLED; APPROVED → FULFILLED or CANCELLED subject to resolution checks |
| Reservation | ACTIVE → RELEASED or ISSUED |
| Count | DRAFT → COUNTING → RECONCILED; nonterminal → CANCELLED |
| Serialized unit | REGISTERED → IN_STOCK; movement to IN_TRANSIT/IN_CUSTODY; consumption → CONSUMED; explicit loss → REMOVED; controlled found/count correction may restore REMOVED units |

Posted documents, movements, ledger entries, issues, dispositions, recoveries,
and adjustments retain immutable evidence. Corrections create new transactions.
Receipt and adjustment-in create positive entries; movement creates equal
negative/positive entries; consumption and adjustment-out create negative
entries. At each location:

`on_hand = receipts + inbound moves + positive adjustments - outbound moves - consumption - negative adjustments`.

An unused return is an inbound move from job custody. Dispatch is a move into
managed transit, receipt a move out of transit. Issue is a move into custody,
not consumption. Summing all locations cancels internal moves. The dedicated
end-to-end audit asserts the resulting location balances and company total.

Reservations do not create physical ledger entries. Available stock is eligible
usable on-hand less active reservations. Position and unit locks prevent
oversubscription; outward posting also preserves reservations and the anonymous
bucket of OPTIONAL_SERIAL stock. Original requested/issued quantities and
disposition history remain unchanged when remaining demand is cancelled.

REQUIRED_SERIAL requires an identifier for every unit; NOT_SERIALIZED excludes
units; OPTIONAL_SERIAL tracks identified and anonymous quantities separately.
Serial identity survives movements and returns. Consumed units cannot be found
back into inventory through adjustment. The approved 3B.1 integration permanently
locks serialization policy after any serialized unit or posted movement exists,
including after zero stock. Draft documents alone do not lock it. There is no
policy conversion, fabricated serial, or history rewrite.

## Service integration and security

Requests use frozen model/exact-variant compatibility APIs. Variant-only parts
are excluded when the device variant is unknown. Request, approval, reservation,
issue, and consumption validate fresh applicable state. Eligible assigned
engineers request and consume; consumption references a performed action in the
current open repair execution. QC rework preserves the earlier execution and
consumption while new consumption references the new attempt.

Returns resolve unused custody without fabricating repair use. Removed defective
customer components have a separate immutable recovery register linked to case,
device, repair action, and optional replacement consumption, at a defective or
quarantine location. They do not inflate usable replacement stock.

Final closure checks active reservations and unresolved issued quantities under
the existing ServiceCase lock. No alternate case status setter was introduced.
Cleanup operations permit explicit release/return where documented after case
cancellation or part deactivation; operational creation still requires active
dependencies.

Authorization uses existing permission/scope coupling at Company or ServiceCenter
targets. Both transfer endpoints and case/location operations require applicable
scope. Staff and native Django permissions do not create business scope. Existing
active-superuser behavior is retained, while ownership, compatibility, and active
operational hierarchy checks still apply. Query filtering is SQL-based. Tests
exercise cross-company references, IDOR, permission revocation, protected fields,
stale revisions, CSRF, readonly history, and deletion denial. Free text is bounded
and rendered with normal escaping; inventory notes/references are not credential
storage.

## Canonical locking discipline

Locks follow dependencies before mutable workflow rows:

1. Actor/recipient users in deterministic order, Company and authorization paths.
2. Applicable catalog/device/taxonomy dependencies, part categories and SpareParts.
3. Inventory locations in deterministic order.
4. ServiceCase, request/reservation or document workflow rows where applicable.
5. StockPosition anchors in deterministic location/part order; count row for count operations.
6. Serialized units in deterministic UUID order.

Readers participating in posting hold shared dependency locks; lifecycle/master
updates take conflicting exclusive locks. In particular, policy changes lock
SparePart exclusively before checking history; inventory posting holds the same
part shared before creating history. This serializes first posting versus policy
change. Case closure uses its existing case lock and reads resolution without
acquiring later dependency locks. Count writers never acquire case locks.
Managed transit/custody locations have no competing public lifecycle writer.

Real PostgreSQL tests observe blocking and exercise both operation orders where
relevant: policy/posting, permission revocation/posting, stock/reservation,
transfer/adjustment, found serial/receipt, and closure/resolution. These tests
provide evidence for tested schedules, not a formal proof of deadlock freedom.

## Admin and query performance

Admin transitions delegate to domain services, with signed actor/action/record
revision confirmations. Adjustment revisions also capture ledger/reservation
changes to reject balance ABA. Generated posting command IDs prevent duplicate
submission. Count approval explicitly displays expected, counted, and variance
values; serialized snapshots are readonly inlines. Posted records have no
arbitrary status, balance, reference edit, or delete path.

Representative query assertions cover lazy stock-position construction (zero
queries), position evaluation (one), stock balance (three), receipt/transfer
detail including lines/units (four), request and issue detail (six), and joined
usage/control/serialized-history evaluation (one where asserted). Aggregation
runs in SQL, with select_related/prefetch_related for displayed relationships.
These are fixture-based regression budgets, not production load benchmarks.

## Dedicated audit findings

The audit adds 16 tests, including five real PostgreSQL concurrency tests, and
reproduced three defects before correcting them:

1. Location lifecycle Admin actions lacked a reviewable signed confirmation.
   Added signed selected IDs, actor/action, and revisions; stale/tampered requests
   and a real lock-wait stale submission are rejected.
2. A partially issued request could not cancel remaining demand after all issued
   custody was resolved. Cancellation now checks active reservations and actual
   unresolved issue quantities, retaining original demand and issue history.
3. Count approval omitted an explicit variance label. Added the computed variance
   to the list and approval confirmation without a schema change.

Only new inventory implementation/tests were corrected during this audit.
Dedicated tests also cover full ledger accounting through closure, QC rework,
immutable history, draft-only policy editing, consumed serial protection, and
permission-revocation races.

## Known limits

Transfers dispatch/receive whole documents; no post-dispatch cancellation or
partial receipt is provided. Posted receipt correction uses explicit adjustment,
not history deletion. Counts cover one location/part position rather than a
warehouse-wide freeze. Defective recovery has no disposal, refurbishment, or
manufacturer settlement workflow. There is no automatic serialization conversion,
costing, procurement, billing, or Phase 3C feature.

Supported services enforce aggregate sufficiency under locks. Privileged raw SQL
is not an operational API: deliberately injected aggregate anomalies are detected
by read-only reconciliation, not automatically repaired. A scoped audit only
reports records visible to its actor.

## Final verification

Final focused audit/integration verification passed 85 tests in 100.150 seconds.
The final complete run used PostgreSQL 18.6 and
`python manage.py test --noinput --verbosity 2`, after the last code change.
It created a fresh test database, applied all migrations from zero, ran every
test, and destroyed the test database successfully.

| Result | Count |
| --- | ---: |
| Starting baseline | 1,395 |
| New inventory tests | 304 |
| Final total | 1,699 |
| Passed | 1,699 |
| Failed / errors | 0 |
| Skipped | 0 |
| New real PostgreSQL concurrency tests, included above | 76 |

The single complete run finished in **2,133.700 seconds**, returned `OK`, and
exited with code 0. The saved verbose log was independently checked for 1,699
successful test markers, including 304 inventory tests and all 76 inventory
concurrency tests, with no failure/error/skip markers. Local verification log:
`C:\Users\Subin\AppData\Local\Temp\c-care-phase3b-full-suite.log`.

After that run, `manage.py check` reported zero issues,
`manage.py makemigrations --check` reported no changes, and
`manage.py showmigrations` showed every migration applied, including all 11
inventory migrations. `git diff --check` passed. An additional whitespace scan
covered all untracked files, which ordinary `git diff --check` does not inspect.
Review found no baseline-test edits, historical-migration edits, unrelated files,
or credential signatures. `.env` remains ignored. HEAD is still `b90f236` on
`master`, and the index is unchanged. Only this report was finalized after the
complete test run; no application or test code changed afterward.

**PHASE 3B VERIFIED — READY TO FREEZE**

## Exact file inventory and Git state

Tracked modifications (5 files):

```text
apps/parts/services.py
apps/service/handover_services.py
config/settings/base.py
docs/ARCHITECTURE.md
docs/SERVICE_PARTS.md
```

New files (67 files, including this report):

```text
apps/inventory/__init__.py
apps/inventory/admin.py
apps/inventory/apps.py
apps/inventory/control_admin.py
apps/inventory/control_models.py
apps/inventory/control_queries.py
apps/inventory/control_services.py
apps/inventory/document_admin.py
apps/inventory/document_models.py
apps/inventory/document_queries.py
apps/inventory/document_services.py
apps/inventory/locking.py
apps/inventory/management/__init__.py
apps/inventory/management/commands/__init__.py
apps/inventory/management/commands/audit_inventory.py
apps/inventory/migrations/0001_initial.py
apps/inventory/migrations/0002_ledger_integrity.py
apps/inventory/migrations/0003_receiving_transfer.py
apps/inventory/migrations/0004_document_integrity.py
apps/inventory/migrations/0005_job_parts_reservations.py
apps/inventory/migrations/0006_reservation_integrity.py
apps/inventory/migrations/0007_job_parts_usage.py
apps/inventory/migrations/0008_usage_integrity.py
apps/inventory/migrations/0009_defective_recovery.py
apps/inventory/migrations/0010_stock_control.py
apps/inventory/migrations/0011_control_integrity.py
apps/inventory/migrations/__init__.py
apps/inventory/models.py
apps/inventory/policy.py
apps/inventory/queries.py
apps/inventory/request_admin.py
apps/inventory/request_models.py
apps/inventory/request_queries.py
apps/inventory/request_services.py
apps/inventory/services.py
apps/inventory/templates/admin/inventory/confirm_count_transition.html
apps/inventory/templates/admin/inventory/confirm_job_transition.html
apps/inventory/templates/admin/inventory/confirm_locations.html
apps/inventory/templates/admin/inventory/confirm_transition.html
apps/inventory/test_admin.py
apps/inventory/test_audit.py
apps/inventory/test_audit_concurrency.py
apps/inventory/test_concurrency.py
apps/inventory/test_control.py
apps/inventory/test_control_admin.py
apps/inventory/test_control_concurrency.py
apps/inventory/test_document_admin.py
apps/inventory/test_document_concurrency.py
apps/inventory/test_documents.py
apps/inventory/test_reconciliation.py
apps/inventory/test_request_admin.py
apps/inventory/test_request_concurrency.py
apps/inventory/test_requests.py
apps/inventory/test_usage.py
apps/inventory/test_usage_admin.py
apps/inventory/test_usage_concurrency.py
apps/inventory/tests.py
apps/inventory/usage_admin.py
apps/inventory/usage_models.py
apps/inventory/usage_queries.py
apps/inventory/usage_services.py
docs/INVENTORY_CONTROL.md
docs/INVENTORY_LEDGER.md
docs/INVENTORY_RECEIVING_TRANSFER.md
docs/PHASE_3B_AUDIT.md
docs/SERVICE_PARTS_REQUEST.md
docs/SERVICE_PARTS_USAGE.md
```

Tracked diff statistics (untracked new files are listed separately above):

```text
 apps/parts/services.py            |  3 +++
 apps/service/handover_services.py |  2 ++
 config/settings/base.py           |  1 +
 docs/ARCHITECTURE.md              |  8 ++++++++
 docs/SERVICE_PARTS.md             | 10 ++++++++++
 5 files changed, 24 insertions(+)
```

Git status:

```text
 M apps/parts/services.py
 M apps/service/handover_services.py
 M config/settings/base.py
 M docs/ARCHITECTURE.md
 M docs/SERVICE_PARTS.md
?? apps/inventory/
?? docs/INVENTORY_CONTROL.md
?? docs/INVENTORY_LEDGER.md
?? docs/INVENTORY_RECEIVING_TRANSFER.md
?? docs/PHASE_3B_AUDIT.md
?? docs/SERVICE_PARTS_REQUEST.md
?? docs/SERVICE_PARTS_USAGE.md
```

All modifications are unstaged; the index is unchanged. `.env` remains ignored.
No generated logs, temporary scripts, or bytecode are in the new-file inventory.

