# Indirect Goods Issue Parity and FOC-Only Return Upgrade Plan

## 1. Purpose

This document plans the upgrade of the existing Indirect Goods module so that indirect goods can be issued through the same operational choices as direct goods while continuing to use their own catalog and inventory ledgers.

The required end state is:

1. A material request may contain direct goods, indirect goods, or both.
2. Indirect goods can be fulfilled through warehouse inventory, supplier direct delivery, or indirect FOC stock.
3. Partial and multi-source issues are supported and auditable per request line.
4. Issued indirect goods are represented in project inventory with exact source provenance.
5. Indirect goods may be returned from a project, but every return goes to **Indirect FOC Inventory only**.
6. An indirect return must never increase normal `indirect_goods_inventory`.
7. Division, project, FOC, movement, issue, return, stock-on-project, damaged and transaction reports must remain consistent.

This is a planning document. It does not authorize implementation or data migration by itself.

---

## 2. Non-Negotiable Business Rules

### 2.1 Separate ledgers remain separate

Direct and indirect goods must not share item foreign keys or physical inventory rows:

- Direct catalog: `items`
- Indirect catalog: `indirect_goods`
- Direct warehouse inventory: `inventory`
- Indirect warehouse inventory: `indirect_goods_inventory`
- Direct project inventory: `project_inventory`
- Indirect project inventory: `indirect_goods_project_inventory`
- Direct FOC inventory: `foc_inventory`
- New indirect FOC inventory: `indirect_goods_foc_inventory`

The shared Material Issue interface may combine the workflows, but database mutations must remain type-safe.

### 2.2 Issue-method parity

“Issue indirect goods like direct goods” means indirect goods receive equivalent fulfillment behavior:

| Method | Indirect behavior |
|---|---|
| Warehouse inventory | Deduct indirect warehouse batches using FEFO/FIFO, add to indirect project inventory |
| Supplier direct delivery | Do not increase indirect warehouse inventory; add delivered quantity directly to indirect project inventory |
| FOC issue | Deduct indirect FOC stock and add zero-cost quantity to indirect project inventory |

### 2.3 Returns are FOC-only

All indirect returns, regardless of their original issue source or physical condition, must:

- reduce indirect project inventory;
- increase indirect FOC inventory;
- create an indirect return record;
- create indirect FOC adjustment and stock-movement history;
- never add stock back to normal indirect warehouse inventory.

Condition is still recorded:

- `serviceable`
- `repairable`
- `damaged`
- `expired`
- `scrap`

Condition controls availability and reporting, not the destination ledger.

### 2.4 Quantity conservation

For each indirect request item:

```text
requested = issued + remaining
issued = currently_on_project + returned_to_foc + consumed + transferred_out
```

For each source fulfillment:

```text
quantity_fulfilled >= quantity_returned + quantity_consumed + quantity_transferred
```

No operation may allow negative warehouse, project, FOC, fulfillment or request balances.

### 2.5 Idempotency

Approve, issue, deliver, return, cancel and retry actions must be safe against double submission. Every stock-changing operation must run in one database transaction and lock the affected request, fulfillment and inventory rows.

---

## 3. Current-State Problems the Upgrade Must Correct

1. Indirect quantities entered under inventory, supplier delivery and FOC on the shared issue screen are currently aggregated and all deducted from `indirect_goods_inventory`.
2. Indirect goods do not have fulfillment rows equivalent to `outgoing_fulfillments`.
3. There is no indirect supplier-direct-delivery document or line-item ledger.
4. There is no separate indirect FOC inventory.
5. Indirect goods have no return workflow.
6. Indirect issue provenance is stored only indirectly through invoice/project inventory rows.
7. Standalone indirect issue can draw stock across divisions when no explicit division is supplied.
8. Incoming indirect receipts with `project_id` currently add the same quantity to both warehouse and project inventory.
9. Indirect damaged/expired consumption does not feed the main damaged or FOC reporting model.
10. Division Deliveries reconstructs indirect values dynamically while direct goods use a persistent ledger.
11. Existing combined issue printing combines presentation but not equivalent operational records.

---

## 4. Target Architecture

### 4.1 Request layer

Keep:

- `indirect_goods_requests`
- `indirect_goods_request_items`

Add explicit status and quantity semantics so the request remains the business demand while fulfillments represent actual supply.

### 4.2 Fulfillment layer

Add `indirect_goods_fulfillments` as the authoritative issue ledger.

Suggested columns:

| Column | Purpose |
|---|---|
| `id` | Primary key |
| `indirect_goods_request_id` | Request header |
| `indirect_goods_request_item_id` | Exact requested line |
| `indirect_good_id` | Reporting and validation |
| `fulfillment_type` | `inventory`, `direct_delivery`, `foc_transfer` |
| `quantity_fulfilled` | Issued quantity |
| `quantity_returned` | Quantity returned to indirect FOC |
| `quantity_consumed` | Quantity consumed/written off at project |
| `quantity_transferred` | Reserved for future project transfer support |
| `unit_cost` | Actual source cost; zero for FOC |
| `total_cost` | Quantity multiplied by unit cost |
| `source_type` | Model/table discriminator |
| `source_id` | Source header or inventory record |
| `source_line_id` | Exact source batch/delivery/FOC line |
| `source_reference` | Human-readable reference |
| `project_inventory_id` | Destination indirect project inventory row |
| `division_id` | Source division |
| `fulfilled_at` | Actual issue date/time |
| `fulfilled_by` | User |
| `status` | `active`, `partially_returned`, `fully_returned`, `reversed` |
| `metadata` | Batch/invoice snapshot and non-key audit data |
| timestamps | Audit timestamps |

Indexes must cover request, request item, item, fulfillment type, source and division. Foreign keys should restrict deletion of issued history.

### 4.3 Source allocation layer

One issue can consume multiple warehouse or FOC batches. Add `indirect_goods_fulfillment_allocations` rather than hiding batch splits in JSON.

Each allocation records:

- fulfillment ID;
- source inventory type;
- source row ID;
- invoice and batch snapshot;
- quantity issued;
- quantity returned;
- quantity consumed;
- unit cost and total cost.

This table is required for correct FEFO deduction, valuation, returns, reconciliation and audit.

### 4.4 Supplier direct-delivery layer

Add:

- `indirect_goods_direct_deliveries`
- `indirect_goods_direct_delivery_items`

Header fields should include delivery number, request, project, division, supplier, LPO, invoice, delivery note, dates, receiver, documents, status, currency, VAT and totals.

Item fields should include request-item link, indirect good, ordered/delivered/accepted/rejected quantities, unit, unit price, batch, invoice, expiry, condition and quality fields.

Only accepted quantity becomes a fulfillment and project stock. Rejected quantity never enters project or warehouse inventory.

### 4.5 Indirect FOC layer

Add:

- `indirect_goods_foc_inventory`
- `indirect_goods_foc_adjustments`

FOC inventory must track:

- indirect good and division;
- available and reserved quantity;
- unit and zero/estimated valuation;
- source type and source reference;
- source return and source project;
- invoice, batch and expiry;
- condition and condition notes;
- quality/status fields;
- received/dispatched dates;
- created/updated users.

Adjustment types should include:

- `return_in`
- `dispatch`
- `reserve`
- `release`
- `condition_change`
- `stock_increase`
- `stock_decrease`
- `disposal`
- `reversal`

Only serviceable/approved rows should be issuable by default. Damaged, expired and scrap rows remain visible but unavailable unless explicitly reclassified.

### 4.6 Return layer

Add:

- `indirect_goods_returns`
- `indirect_goods_return_items`

Header fields:

- return number;
- return date;
- project and division;
- request/reference selection;
- receiver and inspector;
- status;
- documents and notes;
- created/approved/completed users and timestamps.

Line fields:

- indirect good;
- request item and fulfillment link;
- project inventory row;
- quantity submitted, accepted and rejected;
- condition;
- reason;
- invoice/batch snapshots;
- destination indirect FOC row;
- unit cost snapshot.

The return line must always point to a fulfillment or a controlled legacy allocation. Item-only unreferenced returns must require elevated permission and an audit reason.

### 4.7 Division ledger

Add `indirect_goods_division_deliveries` to provide parity with `division_deliveries` without mixing catalogs.

Track:

- total received into normal warehouse stock;
- warehouse available;
- total issued to projects;
- current quantity on projects;
- total returned to FOC;
- FOC available;
- total consumed;
- total disposed;
- average unit cost and value;
- first/last activity dates.

This is a summary ledger. Source tables and movements remain authoritative. A reconciliation command must be able to rebuild it.

### 4.8 Movement ledger

Extend `indirect_goods_stock_movements.movement_type` to include:

- `in`
- `out`
- `return`
- `foc_in`
- `foc_out`
- `consumption`
- `transfer`
- `adjustment`
- `reversal`

Add optional fields for project, request item, fulfillment, return item, source/destination ledger and source/destination inventory row.

Every movement must store real before/moved/after balances. Zero placeholders are not acceptable.

---

## 5. Required State Machines

### 5.1 Indirect request

```text
draft -> pending_approval -> approved -> partially_issued -> issued -> closed
   |             |             |
   +-> cancelled +-> rejected  +-> cancelled (only if nothing issued)
```

Rules:

- `issued` means every request line has zero remaining balance.
- A partially issued request remains issuable.
- Issued quantity cannot be edited directly.
- Cancellation after issue requires fulfillment reversal, not a status-only change.

### 5.2 Indirect direct delivery

```text
draft -> pending -> delivered -> completed
   |         |          |
   +------> cancelled    +-> partially_returned -> returned
```

Project stock changes only on delivery completion.

### 5.3 Indirect return

```text
draft -> submitted -> inspected -> completed
   |          |           |
   +------> cancelled     +-> rejected
```

Project and FOC stock change together only when the return completes.

---

## 6. Phase-by-Phase Implementation Plan

## Phase 0 — Business Decisions and Baseline Freeze

### Work

1. Confirm that “same as direct” includes warehouse, supplier direct delivery and FOC issue.
2. Confirm whether project-to-project transfer is in this release or explicitly deferred.
3. Confirm whether all indirect returns, including unopened/serviceable goods, must remain FOC forever.
4. Confirm FOC valuation policy: zero cost, recoverable value, or original cost retained for reporting.
5. Confirm who may create, inspect, complete, reverse and dispose returns.
6. Confirm whether consumption remains available after returns are introduced.
7. Capture production row counts, status counts and quantity/value totals for every indirect table.
8. Export duplicate, null-division, negative-balance and inconsistent request data.
9. Freeze the meaning of existing statuses and report formulas before schema work.

### Deliverables

- Signed business-rule checklist.
- Baseline reconciliation export.
- Decision log for deferred features.
- Rollback owner and deployment window.

### Exit criteria

No schema or workflow implementation begins until all seven business decisions are recorded.

---

## Phase 1 — Schema Foundation

### Work

Create additive migrations for:

1. `indirect_goods_fulfillments`
2. `indirect_goods_fulfillment_allocations`
3. `indirect_goods_direct_deliveries`
4. `indirect_goods_direct_delivery_items`
5. `indirect_goods_foc_inventory`
6. `indirect_goods_foc_adjustments`
7. `indirect_goods_returns`
8. `indirect_goods_return_items`
9. `indirect_goods_division_deliveries`
10. movement-ledger columns and enum expansion
11. request/request-item fields needed for fulfillment status and locking
12. project-inventory provenance/division indexes and uniqueness corrections

Use nullable foreign keys during backfill, then tighten constraints only after validation.

### Mandatory constraints

- Positive quantities on operational rows.
- Unique document numbers.
- Unique idempotency key for stock-changing submissions.
- Request item and indirect good consistency.
- Indexed project/division/item/source references.
- Soft deletes only where audit semantics allow them.
- Issued, fulfillment, return and movement rows cannot be hard-deleted through the UI.

### Exit criteria

- Migrations run forward and backward on a production-size copy.
- Existing screens continue working with new tables empty.
- No current indirect quantity or value changes.

---

## Phase 2 — Domain Services and Transaction Boundaries

### Work

Create dedicated services instead of placing more stock logic in controllers/models:

- `IndirectGoodsAvailabilityService`
- `IndirectGoodsIssueService`
- `IndirectGoodsDirectDeliveryService`
- `IndirectGoodsFocService`
- `IndirectGoodsReturnService`
- `IndirectGoodsConsumptionService`
- `IndirectGoodsDivisionLedgerService`
- `IndirectGoodsReconciliationService`

Each stock-changing method must:

1. start a database transaction;
2. lock request and inventory rows with `lockForUpdate()`;
3. revalidate status and available quantity inside the transaction;
4. update source stock;
5. update destination stock;
6. create fulfillment/allocation/return records;
7. create movement and FOC adjustment rows;
8. update request and division summaries;
9. commit as a unit;
10. roll back completely on any failure.

### Exit criteria

- Services have unit tests for success, shortage, retry and rollback.
- Controllers contain validation/orchestration, not balance calculations.

---

## Phase 3 — Existing Data Backfill and Reconciliation

### Work

1. Create fulfillment rows for historic issued indirect request quantities.
2. Allocate historic quantities to project inventory/invoice rows where determinable.
3. Mark uncertain source allocations as `legacy` with documented metadata.
4. Build indirect division-summary rows from incoming, warehouse, project and consumption ledgers.
5. Repair or flag project inventory rows missing division IDs.
6. Identify incoming receipts that added the same stock to warehouse and project inventories.
7. Do not auto-correct suspected duplicates without an approved reconciliation rule.
8. Backfill real movement references where possible.
9. Produce before/after totals by item, division, project, invoice and batch.

### Safety

- Backfill commands require `--dry-run` and `--commit` modes.
- Every change must be logged to a reconciliation table or export.
- Commands must be rerunnable without duplicating rows.

### Exit criteria

- Every issued request line has fulfillment coverage or an explicit exception.
- Warehouse/project/consumed totals reconcile to the approved baseline.

---

## Phase 4 — Warehouse Inventory Issue Parity

### Work

1. Replace the current aggregated indirect issue path with explicit per-method processing.
2. Require a division for every issued indirect line.
3. Validate availability by indirect good and division.
4. Deduct using FEFO by expiry, then FIFO by creation date.
5. Create one fulfillment and one or more allocation rows.
6. Add quantity to indirect project inventory by project, division, item, invoice and batch.
7. Create real-balance indirect movement rows.
8. Update request item released/balance quantities.
9. Update indirect division ledger.
10. Preserve partial issue behavior.
11. Prevent cross-division deduction unless a privileged explicit override is selected.

### Exit criteria

- Warehouse issue quantity is conserved across all affected tables.
- Repeated submission cannot issue twice.
- Partial and multi-batch issues reconcile exactly.

---

## Phase 5 — Supplier Direct Delivery for Indirect Goods

### Work

1. Add supplier direct-delivery forms and document upload.
2. Link each delivery item to an indirect request item.
3. Support delivered, accepted and rejected quantities.
4. Require supplier, division, project, date, unit price and delivery reference.
5. On completion, create fulfillment/allocation records.
6. Add accepted quantity directly to indirect project inventory.
7. Do not update normal indirect warehouse inventory.
8. Create movement events representing supplier receipt and project delivery without fabricating warehouse availability.
9. Update division issue/on-project summaries.
10. Include the delivery in combined issue details and printouts.

### Exit criteria

- Warehouse quantity remains unchanged.
- Project quantity and request fulfillment increase once.
- Rejected quantity is visible but never stocked.

---

## Phase 6 — Indirect FOC Inventory and FOC Issue

### Work

1. Implement indirect FOC inventory, adjustments, reservation and dispatch services.
2. Add FOC availability APIs by indirect good and division.
3. Allow FOC selection only from eligible condition/status rows.
4. Deduct FOC FEFO/FIFO using allocation rows.
5. Create zero-cost fulfillment unless the approved policy retains recoverable value.
6. Add issued FOC quantity to indirect project inventory.
7. Record FOC adjustment, movement, request and division-ledger effects.
8. Add FOC inventory screens, movement history and condition filters.

### Exit criteria

- FOC issue never changes normal indirect warehouse stock.
- Project stock, fulfillment and FOC balances reconcile.

---

## Phase 7 — FOC-Only Indirect Return Workflow

### Work

1. Add a dedicated Indirect Goods Return entry point.
2. Select project, original request and eligible fulfillment sources.
3. Calculate returnable quantity from fulfillment allocations, excluding consumed, already returned and transferred quantities.
4. Require quantity, condition, reason, receiver and division.
5. Support multiple source fulfillments in one return document.
6. On completion:
   - lock project inventory and fulfillment allocations;
   - reduce indirect project inventory;
   - increase `quantity_returned` on allocation and fulfillment;
   - create/increase indirect FOC inventory;
   - create an approved `return_in` FOC adjustment;
   - create project-out and FOC-in movement rows;
   - update return status and division summary.
7. Never call `IndirectGoodsInventory::updateQuantity(..., 'add')` from a return.
8. Route damaged, expired and scrap returns to non-issuable FOC conditions.
9. Allow disposal or condition change only as separate audited operations.
10. Add printable Indirect Material Return Note.

### Return validation

- Project must match the fulfillment destination.
- Item must match request item, fulfillment and project inventory.
- Division must match source allocation unless an audited override is used.
- Return quantity cannot exceed source-specific returnable quantity.
- Return cannot exceed current project quantity.
- Completed returns cannot be edited or deleted.
- Reversal must be a compensating transaction.

### Exit criteria

- Every indirect return increases only indirect FOC inventory.
- Normal indirect warehouse inventory is unchanged.
- Full and partial return histories reconcile to original issues.

---

## Phase 8 — Consumption, Damage and Expiry Integration

### Work

1. Update consumption to allocate against fulfillment sources.
2. Increment fulfillment/allocation `quantity_consumed`.
3. Continue reducing indirect project inventory and increasing project `quantity_consumed`.
4. Prevent consumption of quantities already returned.
5. Decide whether `damaged` and `expired` at project are consumption or physical FOC return:
   - if physically returned, use the return workflow;
   - if disposed at site, use consumption with evidence and approval.
6. Add condition/disposition reporting without inserting indirect IDs into direct `damaged_items`.
7. Keep consumption and return mutually exclusive at allocation level.

### Exit criteria

- Project balance equals fulfillment less returns, consumption and transfers.
- Damaged/expired quantities appear in the correct indirect report category.

---

## Phase 9 — Unified Material Issue UI and Documents

**Status:** Completed on 2026-07-11. See `docs/indirect-goods-upgrade-phase-9-unified-ui-documents-completion.md`.

### Work

1. Keep one Material Issue page for direct and indirect demand.
2. Show separate sections for:
   - direct warehouse/direct delivery/FOC;
   - indirect warehouse/direct delivery/FOC.
3. Remove the current behavior that merges all indirect methods into one quantity array.
4. Display source division availability beside each input.
5. Display requested, previously issued, remaining and proposed quantities.
6. Validate per-line totals client-side and server-side.
7. Show actual fulfillment type and source reference in issue details.
8. Update combined print to include both goods types and actual source remarks.
9. Add indirect direct-delivery and FOC references to the issue note.
10. Provide view/print actions for issue and return documents.
11. Preserve accessibility, mobile behavior and non-JavaScript validation.

### Exit criteria

- A mixed request can be partially issued across all allowed sources.
- UI labels exactly match database behavior.

---

## Phase 10 — Reports and Dashboards

**Status:** Completed on 2026-07-11. See `docs/indirect-goods-upgrade-phase-10-reports-dashboards-completion.md`.

### Reports requiring updates

1. Material Issued
2. Material Issue details and combined print
3. Transactions
4. Incoming
5. Outgoing
6. Returns
7. Stock on Project and item details
8. Division Deliveries and item details
9. Inventory and Materials
10. Stock Movements
11. Damaged/Expired Items
12. FOC Inventory and FOC on Project
13. Project and division dashboards
14. Low Stock and valuation exports

### Required report sources

| Metric | Authoritative source |
|---|---|
| Indirect requested | `indirect_goods_request_items.quantity_requested` |
| Indirect issued | `indirect_goods_fulfillments` |
| Warehouse available | `indirect_goods_inventory` |
| Project available | `indirect_goods_project_inventory` |
| Returned to FOC | `indirect_goods_return_items` / fulfillments |
| Indirect FOC available | `indirect_goods_foc_inventory` |
| Consumed | fulfillment allocations plus `indirect_goods_consumption` |
| Division summary | `indirect_goods_division_deliveries`, verified against source ledgers |
| Movement audit | `indirect_goods_stock_movements` |

### Anti-double-counting rules

- Do not count request item released quantity and fulfillment quantity as separate issues.
- Do not count direct delivery as warehouse outflow.
- Do not count a return in both project stock and FOC stock after completion.
- Do not add indirect FOC rows to direct `foc_inventory` totals.
- Combined reports must label goods type and use separate IDs.
- Legacy backfill fulfillments must not duplicate existing project inventory totals.

### Exit criteria

- Each report reconciles with source-table queries.
- Excel/PDF exports match on-screen filters and totals.

---

## Phase 11 — Permissions, Audit and Security

**Status:** Completed on 2026-07-11. See `docs/indirect-goods-upgrade-phase-11-permissions-audit-security-completion.md`.

### New permissions

- `warehouse.indirect-issue.inventory`
- `warehouse.indirect-issue.direct-delivery`
- `warehouse.indirect-issue.foc`
- `warehouse.indirect-issue.reverse`
- `warehouse.indirect-returns.view`
- `warehouse.indirect-returns.create`
- `warehouse.indirect-returns.inspect`
- `warehouse.indirect-returns.complete`
- `warehouse.indirect-returns.reverse`
- `warehouse.indirect-foc.view`
- `warehouse.indirect-foc.adjust`
- `warehouse.indirect-foc.dispose`
- `warehouse.indirect-reconciliation.run`

### Audit requirements

- User and timestamp on every state transition.
- Reason required for overrides, adjustments, reversals and disposal.
- Original and new values for manual changes.
- Uploaded supporting files retained according to policy.
- No hard deletion of completed financial/stock documents.

### Exit criteria

- Permission tests cover every mutation route.
- Unauthorized users cannot infer stock through APIs.

---

## Phase 12 — Automated Testing

**Status:** Completed on 2026-07-11. See `docs/indirect-goods-upgrade-phase-12-automated-testing-completion.md`.

### Unit tests

- FEFO/FIFO allocation.
- Weighted average and zero-cost valuation.
- Request status calculation.
- Fulfillment returnable balance.
- FOC eligibility by condition.
- Division ledger calculations.
- Reversal calculations.

### Feature tests

- Direct-only, indirect-only and mixed requests.
- Partial indirect warehouse issue.
- Multi-batch and multi-division issue.
- Supplier direct delivery with accepted/rejected quantities.
- Indirect FOC issue.
- Mixed-source issue in one submission.
- Serviceable, damaged, expired and scrap return to FOC.
- Partial and full returns.
- Consumption before/after partial return.
- Over-issue, over-return and negative-stock rejection.
- Double-click/idempotency protection.
- Concurrent issue against the same inventory batch.
- Transaction rollback after intermediate failure.
- Report and export totals.
- Permission and document-print tests.

### Reconciliation tests

For every item/division/project fixture:

```text
warehouse closing = opening + incoming - warehouse issues +/- adjustments
project closing = issues + direct deliveries + FOC issues - returns - consumption - transfers
FOC closing = opening + returns + increases - FOC issues - disposal - decreases
```

### Exit criteria

- All new tests pass.
- Existing direct-goods tests remain unchanged and pass.
- A production-sized reconciliation fixture has zero unexplained variance.

---

## Phase 13 — Deployment, Cutover and Rollback

**Status:** Application cutover implementation and current-environment readiness completed on 2026-07-12. External production backup, deployment and supervised acceptance must follow `docs/indirect-goods-upgrade-phase-13-deployment-cutover-rollback.md`.

### Deployment sequence

1. Take database and uploaded-document backups.
2. Deploy additive schema with features disabled.
3. Run dry-run backfill and review variances.
4. Run committed backfill.
5. Enable fulfillment recording for new indirect issues.
6. Reconcile for an agreed monitoring period.
7. Enable supplier direct delivery.
8. Enable indirect FOC issue.
9. Enable FOC-only returns.
10. Switch reports to new authoritative sources.
11. Remove legacy issue path only after successful reconciliation.

### Feature flags

- `indirect_issue_fulfillments_enabled`
- `indirect_direct_delivery_enabled`
- `indirect_foc_enabled`
- `indirect_returns_enabled`
- `indirect_reports_v2_enabled`

### Rollback

- Disable flags in reverse order.
- Do not drop new tables during an operational rollback.
- New transactions remain auditable.
- Use compensating stock operations for committed business transactions.
- Restore database only for a full disaster rollback approved by stakeholders.

### Exit criteria

- Post-deployment reconciliation passes.
- Users complete supervised warehouse issue, direct delivery, FOC issue and return scenarios.
- No unexplained direct-goods report changes.

---

## Phase 14 — Monitoring and Operational Controls

**Status:** Application monitoring controls completed on 2026-07-12. See `docs/indirect-goods-upgrade-phase-14-monitoring-operational-controls-completion.md`.

### Daily automated checks

- Negative warehouse/project/FOC quantities.
- Request released quantity versus fulfillment sum.
- Fulfillment quantity versus returned/consumed/transferred sum.
- Project inventory versus open fulfillment allocation balance.
- FOC inventory versus FOC adjustment ledger.
- Division summary versus source ledgers.
- Completed documents without movement rows.
- Duplicate idempotency keys or document numbers.
- Missing divisions, projects or source references.

### Operational tools

- Read-only reconciliation dashboard.
- Dry-run repair commands.
- Per-document audit timeline.
- Exception export with owner/status.
- Alerts for failed or rolled-back stock transactions.

---

## 7. Primary Files Expected to Change

### Existing application files

- `app/Http/Controllers/Warehouse/MaterialIssueController.php`
- `app/Http/Controllers/Warehouse/OutgoingTransactionController.php`
- `app/Http/Controllers/Warehouse/ReportController.php`
- `app/Http/Controllers/Warehouse/IndirectGoods/IndirectGoodsRequestController.php`
- `app/Http/Controllers/Warehouse/IndirectGoods/IndirectGoodsConsumptionController.php`
- `app/Http/Controllers/Warehouse/IndirectGoods/IndirectGoodsInventoryController.php`
- `app/Models/Warehouse/IndirectGoods/IndirectGoodsRequest.php`
- `app/Models/Warehouse/IndirectGoods/IndirectGoodsRequestItem.php`
- `app/Models/Warehouse/IndirectGoods/IndirectGoodsInventory.php`
- `app/Models/Warehouse/IndirectGoods/IndirectGoodsProjectInventory.php`
- `app/Models/Warehouse/IndirectGoods/IndirectGoodsStockMovement.php`
- `routes/warehouse.php`
- `resources/views/warehouse/material-issue/decision-multi.blade.php`
- `resources/views/warehouse/material-issue/index.blade.php`
- `resources/views/warehouse/material-issue/issued-details.blade.php`
- `resources/views/warehouse/inventory/material-issued/print.blade.php`
- indirect request, inventory, report and sidebar views

### New code areas

- Indirect fulfillment models/services.
- Indirect direct-delivery models/controller/views.
- Indirect FOC models/controller/views.
- Indirect return models/controller/views.
- Indirect division-ledger and reconciliation services/commands.
- Migrations, policies/permissions, factories and tests.

This list must be refined during implementation discovery; it is not permission to alter unrelated direct-goods logic.

---

## 8. Definition of Done

The upgrade is complete only when all conditions below are true:

1. Indirect warehouse, supplier direct-delivery and FOC issues each create distinct, correct fulfillment records.
2. Partial and multi-source issue quantities cannot exceed the request balance.
3. Every issued quantity has source-batch and destination-project provenance.
4. Indirect returns can be created against exact outstanding fulfillment quantities.
5. Every completed indirect return reduces project inventory and increases only indirect FOC inventory.
6. No indirect return changes normal indirect warehouse inventory.
7. Consumption and return cannot claim the same issued quantity.
8. Division summaries reconcile with warehouse, project and FOC ledgers.
9. All listed reports and exports include indirect activity without double counting.
10. Existing direct-goods workflows and totals are unchanged.
11. Historic issued indirect quantities are backfilled or explicitly listed as exceptions.
12. Permissions, audit trail, idempotency and concurrency controls are tested.
13. Forward migration, feature-flag rollback and reconciliation procedures are proven.
14. Business users sign off warehouse issue, direct delivery, FOC issue, partial issue, serviceable return, damaged return, consumption and report scenarios.

---

## 9. Recommended Delivery Slices

To reduce risk, implementation should be delivered in these deployable slices:

1. **Foundation:** schema, services, reconciliation and backfill with UI disabled.
2. **Warehouse parity:** fulfillment ledger plus indirect warehouse issue.
3. **Direct delivery:** supplier-to-project indirect fulfillment.
4. **FOC issue:** indirect FOC inventory and dispatch.
5. **FOC-only return:** project-to-indirect-FOC workflow.
6. **Reports and cutover:** report v2, exports, dashboards and legacy-path retirement.

Each slice requires reconciliation and acceptance before the next is enabled.
