kell_creations/kell_creations_apps/packages/feature_orders
Mike Kell 86c630d670 feat(stage-11a): custom order domain and data
Adds the custom/made-to-order commission workflow to feature_orders at the
domain and data layers. No UI in this slice — Stage 11B/11C cover the
CustomOrderFormPage, confirmation, and tracking surfaces.

Custom orders extend the existing Order aggregate rather than forming a
parallel system, so they flow through the same repository contract and the
same order management pipeline as standard orders.

Domain:

- OrderType enum (standard, custom) with wireValue/fromWire; fromWire defaults
  to standard so an order written by an older client is never mis-classified
  as a commission.
- CustomOrderSpec value object — material, color, size, dimensions,
  unmodifiable reference image URLs, free-text notes, estimated completion.
  Immutable with copyWith, list-aware ==/hashCode, and a toString that reports
  the reference image count rather than the URLs.
- DepositInfo value object — all money as integer cents, consistent with
  PaymentResult.amountCents, so repeated deposit/balance/settlement arithmetic
  cannot drift. Derived balanceDueCents clamps at zero rather than reporting a
  credit when a deposit exceeds the total.
- FulfillmentStatus pipeline: quoted -> depositReceived -> inProduction ->
  qualityCheck -> readyForPickup -> completed, plus cancelled from any
  non-terminal stage. FulfillmentTransition enforces legality in the domain —
  illegal jumps, same-stage advances, and any move out of a terminal stage are
  rejected with an explanatory reason rather than silently applied.
  qualityCheck -> inProduction is permitted: failed inspection sending a piece
  back to the bench is a real workshop operation.
- Order extended with orderType (defaults to standard), nullable
  customOrderSpec, depositInfo, and fulfillmentStatus, plus isCustom,
  hasDeposit, and balanceDueCents. Every existing field, default, and copyWith
  semantic is preserved.
- OrdersRepository extended with createCustomOrder, updateCustomOrderSpec,
  recordDeposit, and advanceFulfillmentStatus.

Data:

- FakeOrdersRepository implements all four methods and seeds five custom
  orders (KC-C2001..KC-C2005) covering every non-terminal pipeline stage. The
  six standard orders are unchanged.
- WooCommerceOrdersRepository implements all four methods. Custom order state
  round-trips through 13 _kc_-prefixed order meta keys; the full mapping table
  is documented on WooCommerceOrderMapper. Spec edits and stage advances PUT
  only meta, so they can never mutate a price, a line item, or a customer
  record. balanceDueCents is derived on read, never persisted, to avoid a
  second source of truth.
- WooCommerceApiClient.updateOrder added in feature_wordpress — only
  updateOrderStatus existed, which cannot carry meta.

Observability (Stage 16A layer):

- KcLogger injected into both repositories, defaulting to KcLogger.silent().
  Domain rejections log at warning; API failures log at error with the status
  code and originating exception.
- No silent catch (_) {} blocks — every swallowed error produces both a log
  record and a failure result.
- Log context is limited to identifiers, operation names, and status names.
  Customer identity, addresses, money amounts, and payment identifiers are
  never logged, and all context additionally passes through KcRedactor.
- The deposit transaction ID is deliberately absent from both DepositInfo and
  the WooCommerce meta mapping — it lives on PaymentResult alone, because
  order meta is readable by any store administrator. Asserted by tests on both
  the object and the outgoing request body.

Also closes P3.8: adds .metadata, CHANGELOG.md, LICENSE, and README.md to
feature_orders so it matches every other package (.gitignore already existed).
The README documents the pipeline, the meta mapping, and the privacy rules.

Deviations from the brief, recorded in the Stage 11A section:

- estimatedCompletionDate lives on CustomOrderSpec rather than Order, and
  there is no separate updateEstimatedCompletion — a date revision goes
  through updateCustomOrderSpec. Deferred for review in Stage 11C.
- internalNotes not added; Order.notes already serves that purpose.

Validation:

- 423 new tests in feature_orders (234 -> 657), including an exhaustive 7x7
  legal/illegal transition matrix and coverage of every new repository method
  on both implementations.
- 1,898/1,898 passing across all packages; no regression from the 1,475
  baseline at 666abd2.
- dart analyze --fatal-infos clean across all 11 packages and apps.
- dart format --line-length 100 clean on every file touched by this slice.

Closes: P2.2 (domain + data portion), P3.8
2026-08-29 15:19:29 -04:00
..
build feat(orders): Stage 9A - WooCommerce Orders API integration 2026-07-11 09:45:36 -04:00
lib feat(stage-11a): custom order domain and data 2026-08-29 15:19:29 -04:00
test feat(stage-11a): custom order domain and data 2026-08-29 15:19:29 -04:00
.gitignore chore(feature_orders): add .gitignore, untrack .dart_tool and pubspec.lock 2026-05-30 10:09:40 -04:00
.metadata feat(stage-11a): custom order domain and data 2026-08-29 15:19:29 -04:00
CHANGELOG.md feat(stage-11a): custom order domain and data 2026-08-29 15:19:29 -04:00
LICENSE feat(stage-11a): custom order domain and data 2026-08-29 15:19:29 -04:00
README.md feat(stage-11a): custom order domain and data 2026-08-29 15:19:29 -04:00
analysis_options.yaml feat(orders): add orders workspace vertical slice 2026-04-04 13:57:27 -04:00
pubspec.yaml feat(stage-11a): custom order domain and data 2026-08-29 15:19:29 -04:00

README.md

Order management for the Kell Creations operations platform. Covers both standard catalogue orders and made-to-order custom commissions behind a single Order aggregate and one OrdersRepository contract.

Features

  • Domain model — Order, OrderItem, OrderStatus, OrderSource, OrderType, PaymentMethod, PaymentResult.
  • Custom orders — CustomOrderSpec (material, colour, size, dimensions, reference images, notes, estimated completion) and DepositInfo (deposit and total amounts in integer cents, deposit/settlement dates, derived balance).
  • Fulfillment pipeline — FulfillmentStatus with enforced transitions: quoted → depositReceived → inProduction → qualityCheck → readyForPickup → completed, plus cancelled from any non-terminal stage and a qualityCheck → inProduction rework path. Illegal jumps are rejected by the domain, not silently applied.
  • Repository contract — OrdersRepository with read, status, note, and create operations plus createCustomOrder, updateCustomOrderSpec, recordDeposit, and advanceFulfillmentStatus.
  • Implementations — FakeOrdersRepository (seeded standard and custom orders across every pipeline stage) and WooCommerceOrdersRepository (REST API v3, custom order state round-tripped through _kc_-prefixed order meta-data).
  • Application layer — OrdersController, CheckoutController, and the GetOrders / UpdateOrderStatus / AddOrderNote / CreateOrder use cases.
  • Presentation — OrdersPage, OrderCard, OrderDetailPanel, OrderStatusChip.

Getting started

Add the package as a path dependency and import the barrel:

dependencies:
  feature_orders:
    path: ../feature_orders
import 'package:feature_orders/feature_orders.dart';

Usage

final repository = FakeOrdersRepository(logger: observability.logger);

final result = await repository.createCustomOrder(
  order,
  spec: CustomOrderSpec(
    material: 'Organic cotton',
    color: 'Sage green',
    size: 'Large',
  ),
  deposit: const DepositInfo.quoted(
    totalAmountCents: 7200,
    depositAmountCents: 2400,
  ),
);

// Advance through the pipeline — illegal jumps are rejected.
await repository.recordDeposit(
  order.id,
  amountCents: 2400,
  paidAt: DateTime.now(),
);
await repository.advanceFulfillmentStatus(order.id, FulfillmentStatus.inProduction);

Observability and privacy

Repository implementations accept an optional KcLogger from core. Failed and rejected write operations are logged with the operation name, order ID, and status names only. Customer identity, addresses, money amounts, and deposit payment identifiers are never logged, and every context map is additionally passed through KcRedactor. There are no silent catch (_) {} blocks.

The WooCommerce meta-data mapping deliberately excludes the deposit transaction ID — it lives on PaymentResult alone. See the class-level documentation on WooCommerceOrderMapper for the complete key table.

Additional information

Part of the kell_creations_apps monorepo. Run the package tests with flutter test from this directory, or the whole suite with ./tools/run_all_tests.sh --analyze from kell_creations_apps/.