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.
278 lines
6.8 KiB
Markdown
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.
|