ifnex/IFNEX Logistics.md
Kazem Alghasi 61a2643dd0 feat(api): implement shipment review and resubmission workflow
Introduces a complete shipment review system allowing staff to request
changes to customer orders and customers to resubmit corrected orders.

- Add `ReviewState` enum and `ShipmentReview` model to track review history.
- Implement `ShipmentReviewService` to handle approval and change
  request logic.
- Add `resubmit` endpoint for customers to update orders when
  `changes_requested` state is active.
- Add `request-changes` endpoint for staff to flag orders for correction.
- Update `ShipmentResource` in Filament to display review states and
  manage approvals.
- Implement WordPress bridge support for fetching and resubmitting
  orders via AJAX.
- Add database migrations for `shipment_reviews` table and `review_state`
  column on shipments.
- Add `StaffApiMiddleware` to secure staff-specific API routes.
2026-09-27 01:00:26 +03:30

278 lines
6.8 KiB
Markdown

# IFNEX Logistics — Architecture Decisions
## ADR-001 — Shipment Review Workflow
### Context
The current order workflow uses `Shipment.status` for operational, approval, and payment-related states simultaneously.
Current flow:
`pending_approval → approved → payment → processed → ...`
There is currently no independent representation for:
* review decision
* requested customer changes
* review history
* customer resubmission
### Decision
Introduce an independent Review State and Review History domain without immediately removing or redesigning the existing `ShipmentStatus` enum.
### Transitional model
`Shipment.status` remains backward-compatible:
* `pending_approval`
* `approved`
* `cancelled`
* operational statuses
* legacy `pending_payment`
A new `Shipment.review_state` represents:
* `pending`
* `changes_requested`
* `approved`
* `rejected`
### Target workflow
```text
Create Order
↓
review_state = pending
status = pending_approval
↓
Staff Review
├── Request Changes
│ ↓
│ review_state = changes_requested
│ ↓
│ Customer Edit
│ ↓
│ Resubmit
│ ↓
│ review_state = pending
│
├── Approve
│ ↓
│ review_state = approved
│ status = approved
│
└── Reject
↓
review_state = rejected
status = cancelled
```
### Consequences
This allows the current client workflow to work without immediately breaking existing code that depends on `ShipmentStatus`.
Long term, approval/review state can be fully separated from operational Shipment status.
---
## ADR-002 — Shipment Review History
### Decision
Create a dedicated `shipment_reviews` domain instead of storing review reasons and decisions directly on `shipments` or using `ShipmentStatusHistory` as a substitute.
A review record should contain at minimum:
```text
id
shipment_id
revision_no
decision
reason
notes
reviewed_by
reviewed_at
created_at
updated_at
```
The review record represents one submission/review cycle.
### Rationale
`ShipmentStatusHistory` records operational status transitions. Review decisions are a different domain concern and require reviewer identity, reason, notes, and revision context.
---
## ADR-003 — Customer Revision / Resubmission
### Decision
Customer correction must not be implemented as a blind PATCH against the Shipment.
A resubmission updates the current shipment aggregate transactionally while creating a new review revision.
The aggregate includes:
```text
Shipment
ShipmentPackage[]
ShipmentItem[]
```
### Required behavior
Resubmission must:
1. validate the complete order payload;
2. recalculate volumetric and chargeable weight server-side;
3. recalculate pricing server-side;
4. update Shipment fields;
5. synchronize Packages;
6. synchronize Items;
7. create a new review revision;
8. return the shipment in `pending` review state.
AWB remains unchanged because the customer is revising the same order.
---
## ADR-004 — Request Changes vs Reject
### Decision
These are distinct actions.
### Request Changes
* order remains active;
* customer may edit;
* customer may resubmit;
* reason is mandatory;
* shipment remains operationally pre-approval.
### Reject
* order is terminated;
* shipment becomes cancelled;
* customer cannot continue the same review cycle.
API concepts:
```text
POST /staff/orders/{shipment}/request-changes
POST /staff/orders/{shipment}/approve
POST /staff/orders/{shipment}/reject
POST /customer/orders/{shipment}/resubmit
```
---
## ADR-005 — Payment State
### Decision
Do not derive payment state from `Shipment.status`.
The current implementation remains temporarily compatible with:
```text
Approved → payment available
```
but the target architecture is:
```text
Shipment Operational Status
Review State
Payment State
```
as separate domains.
`ShipmentStatus::isPaid()` must eventually be removed or deprecated because states such as `cancelled`, `failed`, and `returned` cannot safely imply payment completion.
---
## ADR-006 — Order Detail API Contract
Laravel is the canonical source of the Order Detail API contract.
Target response structure:
```text
shipment
├── status
├── review
├── sender
├── receiver
├── packages[]
├── items[]
├── financial
├── documents[]
└── tracking_events[]
```
WordPress must consume the canonical Laravel structure rather than relying on legacy flattened fields.
Known current contract mismatches:
* Laravel returns `sender.*`, WordPress expects `sender_name`, `sender_phone`, etc.
* Laravel returns `receiver.*`, WordPress expects flattened receiver fields.
* Laravel returns tracking event keys `date`, `description`, `location`; WordPress expects `event_date`, `event_description`, `event_time`.
These mismatches must be corrected during API/UI hardening.
---
## ADR-007 — Commitment Documents
`CommitmentForm` is the reusable template.
`ShipmentCommitmentForm` represents the requirement/instance for a particular shipment.
For the current client:
* physical delivery is the primary process;
* online upload is optional;
* signed-document upload must not block approval/payment unless explicitly required by business policy.
Required documents should eventually be instantiated/snapshotted per shipment instead of dynamically resolving the current active templates.
---
## ADR-008 — Operational Finance Boundary
IFNEX is not intended to become a full accounting system.
IFNEX should provide logistics-relevant financial information:
* wallet
* customer receivable/debt status
* order financial status
* payment transactions
* credit/settlement information
* audit trail
A deeper accounting system should be integrated externally through API rather than recreated inside IFNEX.
---
## ADR-009 — Production Hardening Findings
The following findings require later hardening:
1. `ShipmentStatus::isPaid()` is semantically unsafe.
2. `Shipment::isDelivered()` compares an Enum-cast field with a string.
3. `StaffOrderController` lacks explicit role/permission authorization.
4. Order Detail sender/receiver API contract is inconsistent with WordPress.
5. Tracking event API contract is inconsistent with WordPress.
6. PDF download uses a different user-meta token key from the standard Bridge token.
7. Commitment-form shipment requirements are not currently snapshotted.
8. Customer signed-document uploads currently use public storage semantics.
9. `ShipmentPackage` and `ShipmentItem` are not included in the current detailed customer order response.
10. Payment state is coupled to Shipment status.
11. Price calculation and discount consumption require a clear distinction between preview and committed pricing.
12. Existing status/schema migration history should be preserved; do not rewrite historical migrations.