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

6.8 KiB

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

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:

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:

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:

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:

Approved → payment available

but the target architecture is:

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:

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.