toga-ai 1.0.1205 → 1.0.1206

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,7 +53,8 @@ same bridge topology, walked for a different payload (a scalar flag rather than
53
53
  ### Mechanism
54
54
  1. **Interceptor gate.** `postPost`/`postPut` call `propagateFulfillableWhenPayloadCarriesTheFlag`,
55
55
  which returns immediately unless the payload carried `isFulfillable`. Senders: the sync, the
56
- one-time backfill, and the toga25-supply item modal Fulfillable toggle (Compass USA + Canada).
56
+ one-time backfill, and the toga25-supply item modal Fulfillable toggle (Compass USA + Canada;
57
+ edit PUT and, since 2026-10-29, create POST — a new item has no order lines, so the walk is a no-op).
57
58
  **A UI caller must send the key ONLY when the user changed it** — `useItemEditForm` adds it to
58
59
  the PUT only when `dirtyFields.isFulfillable` is set. Sending it on every save would walk the
59
60
  whole SO/PO chain on every item edit for nothing.
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-10-21
9
+ updated: 2026-10-29
10
10
  owners: ["bala"]
11
11
  files:
12
12
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderUpdatedItemsTable/SalesOrderUpdatedItemsTable.tsx
@@ -17,6 +17,7 @@ related:
17
17
  - ./record-modals-and-nested-tables.md
18
18
  - ./bundle-item-pricing.md
19
19
  - ../../../clients/compass-usa/workflows/granting-persona-bundle-access.md
20
+ - ../../../clients/compass-usa/features/isfulfillable-data-quality-and-type-rule.md
20
21
  ---
21
22
  The Items table inside the Supply sales order / service request modal: an INTERIM hardcoded table (not Surface-driven) that flattens standalone lines, kits, kit add-ons, parts and fees into tiered rows; open when an order line is missing or mis-drawn in that table.
22
23
 
@@ -40,12 +41,14 @@ The Items table inside the Supply sales order / service request modal: an INTERI
40
41
  - **Add-on header key = the add-on's first order-line uuid**, not the bundle uuid. The same add-on bundle can appear twice on one order; a bundle-uuid key would collide.
41
42
  - **Add-on lines keep their real `isPrimary`.** The kit-level "Computer Support" badge and the child sort read only each child group's LEAD row; for an add-on that is the header row, which has no `isPrimary`. So an add-on's own primary part shows its "Computer Support" badge, and nothing leaks to the kit. Do not force `isPrimary = false` in `buildAddOnRows()`; it protects nothing and hides that badge.
42
43
  - The developer decision (TRUE-82981): the add-on must match the main kit layout exactly. Do not invent a different style for it.
44
+ - **Qty Fulfilled click → `ItemFulfillmentModal`.** `isQtyFulfilledClickable()` (copy of toga2-supply `OrderItemsTableSection`) makes the cell clickable only when `item.isFulfillable` is `true` or `"1"` AND qty fulfilled != 0. NULL and 0 are both dead. For hostname `COMPASS`, an `assetType.name` of `FEE` is never clickable. Kit parent rows are never clickable. Flag data: [isFulfillable data quality](../../../clients/compass-usa/features/isfulfillable-data-quality-and-type-rule.md).
43
45
 
44
46
  ## Gotchas
45
47
  - **⚠ Before 2026-10-21 add-on parts never showed.** From the first commit (`983686a`, 29 Jul 2026) `buildRows()` drew a `formatChildBundle` entry as one minimal row with "–" for part number / SKU / manufacturer and skipped `childBundle.bundleItems`. Fixed in PR agilantsolutions/toga25-supply#71 (`b2679f2`, `f4c604c`).
46
48
  - **An add-on that shows blank (no name, no Kit ID) is usually data, not this table.** If the persona has no `Personas_Bundles` grant on the add-on bundle, the API returns it without a uuid. See [granting persona bundle access](../../../clients/compass-usa/workflows/granting-persona-bundle-access.md#kit-add-on-bundles-need-their-own-personas_bundles-grant).
47
- - Test orders: copies of Compass SA139536 and SA139507 on client-sandbox carry kits with add-ons. How to copy a prod order: [Compass order lifecycle](../../../clients/compass-usa/workflows/order-lifecycle-and-data-integrity.md#copying-a-prod-order-into-client-sandbox-for-testing).
49
+ - Test orders: copies of Compass SA139536 and SA139507 on client-sandbox carry kits with add-ons. Clickable Qty Fulfilled: Compass SA118967 (laptop `C40QYUC`, 1 fulfilled) on client-sandbox. How to copy a prod order: [Compass order lifecycle](../../../clients/compass-usa/workflows/order-lifecycle-and-data-integrity.md#copying-a-prod-order-into-client-sandbox-for-testing).
48
50
 
49
51
  ## Change history
52
+ - 2026-10-29 — Documented the Qty Fulfilled click gate (`isFulfillable`, Compass FEE rule) (TRUE-82989). (bala)
50
53
  - 2026-10-21 — Added the `addOn` tier: add-ons render like the main kit with their parts listed (TRUE-82981, PR #71). (bala)
51
54
  - 2026-10-21 — `buildAddOnRows()` no longer forces `isPrimary = false` on add-on lines; badge propagation reads only group lead rows (TRUE-82981, commit `26ac427`, CodeRabbit review). (bala)
@@ -64,6 +64,10 @@ files:
64
64
  - toga25-supply/src/surface/SurfaceHeader.tsx
65
65
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/helpers/surfaceBundlesToDecisionFields.ts
66
66
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx
67
+ - toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordCreateModalLayout.tsx
68
+ - toga25-supply/src/layout/ItemRecordModalLayout/hooks/useItemCreateForm.tsx
69
+ - toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/COMPASS/itemRecordCreateFields.json
70
+ - toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/COMPASSCANADA/itemRecordCreateFields.json
67
71
  related:
68
72
  - ../workflows/cypress-testing.md
69
73
  - ../../_underscore/features/surface-resolver.md
@@ -165,6 +169,7 @@ The item-record modal's **VIEW** sections are now resolved from Surface for **ev
165
169
  - **Header Edit button** — an element in `item-record-header`, matched **id-agnostically** by `renderType='BUTTON'` + `config.kind='itemEdit'`. Core seeds it OFF; Compass / Compass Canada opt in.
166
170
  - **Vendor-items table** — a dedicated `item-record-vendor-items` **toggle surface** carrying a `config.role='sectionVisibility'` marker element; the FE reads it as **`vendorItemsEnabled`** and conditionally renders the vendor table in `ItemRecordView`. Core-OFF; Compass / Compass Canada opt in.
167
171
  - **Fulfillable toggle** (Attributes, after Store Visible; TRUE-82989) — Core element 236 on `item-record-attributes` (surface 17), Core-OFF; Compass / Compass Canada opt in. **Adding a field to this modal takes three edits:** the Surface element (view), the client's `itemRecordEditFields.json` (edit), and the client's `apiFields.json` delta (fetch). `isFulfillable` sits in the COMPASS / COMPASSCANADA deltas only ([why](client-api-fetch-fields.md)). Why it is sent only when dirty: [propagation](../../_underscore/features/fulfillable-item-propagation.md).
172
+ - **Create modal too (2026-10-29).** The create form is JSON, not Surface: the toggle is in COMPASS / COMPASSCANADA `itemRecordCreateFields.json` after Store Visible, starts **ON** (`emptyItemDefaults.isFulfillable: true` in `hooks/useItemCreateForm.tsx`). `ItemRecordCreateModalLayout.tsx` sets `isFulfillableOnCreateForm` when any **section** field has `valueKey: "isFulfillable"` (`headerFields` are not checked); only then does `useItemCreateForm` add `isFulfillable` (true or false) to the `POST /items` body. Other clients' roles have no write grant on 2434, so they never receive the key.
168
173
 
169
174
  #### `showNullAsEmpty` — show `—` for a NULL boolean instead of "No" (opt-in element config)
170
175
 
@@ -206,7 +211,7 @@ The item modal's **create** payload now takes its per-tenant defaults from the S
206
211
 
207
212
  #### Sending `catalog` by NAME does not trip the Compass POST interceptor
208
213
 
209
- `_Model_Compass_Item::postPost` throws *"Could not load catalog uuid"* when it receives a catalog with no uuid, which looks like a reason to send a uuid instead of a name. It is not. api2 invokes the POST payload interceptor with **`$outData` — the created record, not the submitted body** (`V2.php:6019`), and the created record always carries the resolved catalog uuid. Compass has an active POST/POST interceptor on `recordId 21` (Items); Compass Canada has one too, but there is no `_Model_CompassCanada_Item`, so it falls back to `_Model_Client_Item::postPost`, which returns early unless `isFulfillable` was sent. Background: [API payload interceptors](../../api2/features/api-payload-interceptors.md). The name→row match itself is safe because `Items.catalogId` is a `MATCH` field over a record whose `name` is an identifier — [nested-relationship writes](../../api2/features/nested-relationship-writes.md).
214
+ `_Model_Compass_Item::postPost` throws *"Could not load catalog uuid"* when it receives a catalog with no uuid, which looks like a reason to send a uuid instead of a name. It is not. api2 invokes the POST payload interceptor with **`$outData` — the created record, not the submitted body** (`V2.php:6019`), and the created record always carries the resolved catalog uuid. Compass has an active POST/POST interceptor on `recordId 21` (Items); Compass Canada has one too, but there is no `_Model_CompassCanada_Item`, so it falls back to `_Model_Client_Item::postPost`, which returns early unless `isFulfillable` was sent. Since 2026-10-29 the Compass / Canada create form always sends it; the chain walk on a brand-new item finds no order lines and changes nothing. Background: [API payload interceptors](../../api2/features/api-payload-interceptors.md). The name→row match itself is safe because `Items.catalogId` is a `MATCH` field over a record whose `name` is an identifier — [nested-relationship writes](../../api2/features/nested-relationship-writes.md).
210
215
 
211
216
  ### SalesOrders approval-decision modal (approve + deny) — migrated via an OVERLAY adapter seam
212
217
 
@@ -443,4 +448,5 @@ Related: [legacy page meta](../../_underscore/features/page-meta-context-field-s
443
448
  - **🚨 Header surfaces use `order.`-prefixed valueKeys; section surfaces use BARE keys.** The record modal top bar builds its surface record as `{ order }`, so header elements read `order.dateOrder`, `order._status`. Section surfaces (`order-details` etc.) resolve against the order **directly**, so their keys are bare: `contact._name`, `_purchaseOrders`, `description`. The same field therefore has two different valueKeys depending on which surface it lives in, and copying one into the other renders blank with no error.
444
449
 
445
450
  ## Change history
451
+ - 2026-10-29 — Fulfillable toggle added to the Compass USA + Canada item create modal, default ON (TRUE-82989). (bala)
446
452
  - 2026-10-28 — Item modal Fulfillable toggle (Compass USA + Canada) and the opt-in `showNullAsEmpty` element flag (TRUE-82989). (bala)
@@ -9,7 +9,7 @@
9
9
  | [Compass Canada: incoming MITS purchase-order monitor (Monitor/Compass/CompassCanadaIncomingPurchaseOrders)](features/oneuptime-incoming-purchase-orders-monitor.md) | 2.0 | worker2 OneUptime push monitor for Compass Canada sales orders sent to MITS that have not got their full purchase order back, plus how MITS links a Canada PO to |
10
10
  | [Compass Canada: PO-to-vendor transmission monitor (Monitor/Compass/CompassCanadaPurchaseOrderTransmissionsToVendors)](features/oneuptime-po-transmissions-to-vendors-monitor.md) | 2.0 | worker2 OneUptime push monitor for Compass Canada POs the 1.0 cron has not yet sent to the vendor (Grand & Toy), plus how the Canada PO transmit cron differs fr |
11
11
  | [Compass Canada: sales-order-to-MITS transmission monitor (Monitor/Compass/CompassCanadaSalesOrderTransmission)](features/oneuptime-sales-order-transmission-monitor.md) | 2.0 | worker2 OneUptime push monitor for approved Compass Canada sales orders the 1.0 cron has not yet sent to MITS, plus Canada MITS send-cron timing facts; open whe |
12
- | [Compass Canada — order fulfillment status (standard base rule, no Canada override)](features/order-fulfillment-status-per-line.md) | 2.0 | Canada-specific facts for the shared Compass order fulfillment status (standard base rule since TRUE-79669); open before touching Canada order status or Canada |
12
+ | [Compass Canada — order fulfillment status (deployed ASN per-line override; TRUE-79669 base rule pending)](features/order-fulfillment-status-per-line.md) | 2.0 | Canada-specific facts for Compass order fulfillment status: the deployed per-line ASN override that reads `Items.isFulfillable`, and the TRUE-79669 base rule th |
13
13
  | [Compass Canada Persona Model (sector-granted vs hand-assigned)](features/persona-model.md) | 2.0 | Where each `Client_CompassCanada` persona comes from: PEOPLE cron (only "All"), sector `Locations_Personas`, or hand-assigned with nothing keeping it in step. |
14
14
  | [Compass Canada](profile.md) | 2.0 | Compass Canada is the Canadian arm of the Compass account — a separate TOGA tenant, related to but distinct from Compass USA. |
15
15
  | [Copying a Compass USA user into the Canada tenant (cross-tenant hand-add)](workflows/copying-a-us-user-into-the-canada-tenant.md) | 2.0 | Giving a Compass **USA** person a login on the Canada TechHub portal: the guarded insert chain, the uuid role/persona mapping, and the worker2 guard that stops |
@@ -1,12 +1,12 @@
1
1
  ---
2
- title: Compass Canada — order fulfillment status (standard base rule, no Canada override)
2
+ title: Compass Canada — order fulfillment status (deployed ASN per-line override; TRUE-79669 base rule pending)
3
3
  framework: "2.0"
4
4
  repo: _underscore
5
5
  project: _Underscore
6
6
  client: compass-canada
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-10-15
9
+ updated: 2026-10-29
10
10
  owners: ["bala", "jcardinal"]
11
11
  files:
12
12
  - _underscore/Model/Client/SalesOrder.php
@@ -18,24 +18,41 @@ related:
18
18
  - ../../../2.0/apps/_underscore/features/item-fulfillment-stage-lifecycle-and-order-status.md
19
19
  - grand-and-toy-asn-import.md
20
20
  ---
21
- Canada-specific facts for the shared Compass order fulfillment status (standard base rule since TRUE-79669); open before touching Canada order status or Canada `isFulfillable`.
21
+ Canada-specific facts for Compass order fulfillment status: the deployed per-line ASN override that reads `Items.isFulfillable`, and the TRUE-79669 base rule that will replace it; open before touching Canada order status or Canada `isFulfillable`.
22
22
 
23
23
  ## Summary
24
- Since TRUE-79669 (2026-10-15, not yet deployed) Compass Canada has **no** `_fulfillmentStatus()` override. It runs the standard base per-line rule, same as Compass USA. The rule, gates and blast radius live in **[Compass USA + Canada — order fulfillment status](../../compass-usa/features/order-fulfillment-status-per-line.md)**. This page holds only what differs for Canada.
24
+ - **Deployed today** (verified 2026-10-29: `_underscore` `origin/_sandbox-client` and `_production`, same file): Canada has its **own** `_fulfillmentStatus()` override, a per-line rule on ASN quantity. It skips lines with `Items.isFulfillable = 0`. So the TOGa Supply Fulfillable toggle changes Canada order status **now**.
25
+ - **Decided, not merged** (TRUE-79669, 2026-10-15): delete the override and run the standard base per-line rule, same as USA. Rule, gates and blast radius: **[Compass USA + Canada — order fulfillment status](../../compass-usa/features/order-fulfillment-status-per-line.md)**.
25
26
 
26
27
  ## How it works
27
- - Shipped quantity comes from **ItemFulfillmentItems** (created from Grand & Toy ASNs — [G&T ASN Import](grand-and-toy-asn-import.md)), not from the ASN bridge the deleted override read.
28
- - Non-shipping lines are excluded by `Items.isFulfillable = 0`, set by `dbchanges2/Client_CompassCanada/2026-10-15a - NonShippableItemTypesNotFulfillable.sql` for item types `CONSULTING`, `SERVICE`, `SERVICES`, `WARRANTY` (PO-linked AppleCare / ABM SKUs such as `COMPASS-IPAD-ABM`, `SDU42Z/A` never ship).
28
+ ### Deployed override (`_Model_Compass_Canada_SalesOrder`)
29
+ ```
30
+ pendingFulfillment ASN qty on expected lines = 0
31
+ partiallyFulfilled any expected line has quantity > ASN qty
32
+ fulfilled otherwise
33
+ ```
34
+ - **Expected line** (`_isExpectedToShipLine`): `price > 0` AND has a `SalesOrderItems_PurchaseOrderItems` row AND `ItemTypes.name` not in `ITEM_TYPES__NOT_SHIPPABLE` (`SERVICE`, `SERVICES`, `CONSULTING`, `WARRANTY`) AND `COALESCE(Items.isFulfillable, 1) = 1`.
35
+ - **Shipped qty** (`_qtyShippedByAsn`): `SUM(AdvanceShippingNoticeItems.quantity)` through the SO→PO item bridge. ASN, not ItemFulfillmentItems.
36
+ - Effect of the toggle: switching an **unshipped** item OFF drops its line from the check, so the order can move Partially Fulfilled → Fulfilled. NULL counts as ON.
37
+
38
+ ### After TRUE-79669 (not deployed)
39
+ - Shipped qty comes from **ItemFulfillmentItems** (created from Grand & Toy ASNs — [G&T ASN Import](grand-and-toy-asn-import.md)).
40
+ - Non-shipping lines are excluded only by `Items.isFulfillable = 0` (no type list, no price / PO-link filter). `2026-10-15a - NonShippableItemTypesNotFulfillable.sql` sets CONSULTING / SERVICE / SERVICES / WARRANTY items to 0.
29
41
  - `ItemTypes` is the only usable type column in Canada (no `FEE` asset type).
30
42
 
43
+ ### Test orders (client-sandbox, 2026-10-29)
44
+ - `SAC100804`: `77-87476` shipped, `MXPN3CL/A` not shipped = Partially Fulfilled. Toggle `MXPN3CL/A` OFF → Fulfilled.
45
+ - `SAC100724`: Fulfilled.
46
+
31
47
  ## Gotchas
32
- - **⚠ Follow-up open: new Canada service-type items start with `isFulfillable` NULL**, which the base rule treats as fulfillable. A new SERVICE / CONSULTING / WARRANTY item will hold its orders at Partially Fulfilled until it is set to 0. The data fix only covers items that exist today.
33
- - **46 open Canada orders have an ASN but no IF** (measured locally 2026-10-15) — they now read **Pending Fulfillment**. Fix by creating the missing IF, not by changing the rule.
34
- - Superseded: the 2026-09-16 Canada ASN-bridge per-line rule (`ITEM_TYPES__NOT_SHIPPABLE`, "do NOT switch to local IF tables") — deleted in TRUE-79669.
48
+ - **⚠ New items created in TOGa Supply start `isFulfillable = 1`** (create toggle default ON, 2026-10-29). A new SERVICE / CONSULTING / WARRANTY item is still skipped by the deployed type list, but after TRUE-79669 it holds orders at Partially Fulfilled unless switched OFF.
49
+ - **46 open Canada orders have an ASN but no IF** (measured locally 2026-10-15). The deployed ASN rule counts them as shipped; they will read **Pending Fulfillment** after TRUE-79669. Fix by creating the missing IF, not by changing the rule.
50
+ - Do not trust "no Canada override" in older notes: the override is still live until TRUE-79669 merges. Check the deployed branch before reasoning about Canada status.
35
51
 
36
52
  ## Change history
37
- - 2026-10-15 — TRUE-79669: Canada override and `ITEM_TYPES__NOT_SHIPPABLE` deleted; Canada runs the standard base rule; service item types set to `isFulfillable = 0`. Not deployed. (jcardinal)
38
- - 2026-09-16 — Canada per-line ASN-bridge rule (90 orders corrected), now superseded. (bala)
53
+ - 2026-10-29 — Verified the deployed override still runs and reads `isFulfillable`; toggle test on SAC100804 (TRUE-82989). (bala)
54
+ - 2026-10-15 — TRUE-79669: Canada override and `ITEM_TYPES__NOT_SHIPPABLE` deleted on branch; service item types set to `isFulfillable = 0`. Not deployed. (jcardinal)
55
+ - 2026-09-16 — Canada per-line ASN-bridge rule (90 orders corrected). (bala)
39
56
 
40
57
  ## Related
41
58
  - [Compass USA + Canada — order fulfillment status](../../compass-usa/features/order-fulfillment-status-per-line.md)
@@ -248,14 +248,16 @@ to but distinct from Compass USA. Like Compass USA it spans the **2.0** commerce
248
248
  All **228** prod items were still NULL (never stamped; Canada has no NetSuite, so no sync ever
249
249
  writes it). `Client_CompassCanada/2026-10-28a` sets NULL rows to 0 for `Fee` / `Services` asset
250
250
  types and service / consulting / warranty item types (**14**), else 1 (**214**), and turns on the
251
- Fulfillable toggle in the toga25-supply item modal. New items still start NULL; set them with the
252
- toggle. Canada uses a **different type vocabulary** than USA (title-case asset types, its own
251
+ Fulfillable toggle in the toga25-supply item modal. Since 2026-10-29 the create modal has the
252
+ toggle too (default ON), so new items start at 1; switch it OFF for service items. The deployed
253
+ Canada status rule already skips `isFulfillable = 0` lines. Canada uses a **different type vocabulary** than USA (title-case asset types, its own
253
254
  item types). See
254
255
  [isFulfillable — Data Quality & the Type-Derived Rule](../compass-usa/features/isfulfillable-data-quality-and-type-rule.md).
255
256
  - The 2026-06-08 ASN → ItemFulfillment work was for **Compass USA**, not Compass Canada.
256
- - **Order status = the standard base per-line rule (TRUE-79669, 2026-10-15, not yet deployed).**
257
- Canada's `_fulfillmentStatus()` override and `ITEM_TYPES__NOT_SHIPPABLE` were deleted; service
258
- item types are set to `isFulfillable = 0` instead. IF stages (picked/packed/shipped) are seeded by
257
+ - **Order status: deployed = Canada's own per-line ASN override (reads `isFulfillable`, verified
258
+ 2026-10-29); decided = the standard base per-line rule (TRUE-79669, 2026-10-15, not merged).**
259
+ TRUE-79669 deletes the override and `ITEM_TYPES__NOT_SHIPPABLE`; service item types are set to
260
+ `isFulfillable = 0` instead. IF stages (picked/packed/shipped) are seeded by
259
261
  `dbchanges2/Client_CompassCanada/2026-06-30a - ItemFulfillmentLifecycleAndShippedBackfill.sql`.
260
262
  [Canada order fulfillment status](features/order-fulfillment-status-per-line.md).
261
263
  - **Item titles in customer emails** come from the `ItemTranslations` sidecar (prod: 220 fr-CA rows,
@@ -19,6 +19,8 @@ files:
19
19
  - dbchanges2/Client_Compass/2026-10-28a - ItemsIsFulfillableToggleVisible.sql
20
20
  - dbchanges2/Client_CompassCanada/2026-10-28a - ItemsIsFulfillableToggleAndBackfill.sql
21
21
  - toga25-supply/src/layout/ItemRecordModalLayout/hooks/useItemEditForm.tsx
22
+ - toga25-supply/src/layout/ItemRecordModalLayout/hooks/useItemCreateForm.tsx
23
+ - _underscore/Model/Compass/Canada/SalesOrder.php
22
24
  related:
23
25
  - ../profile.md
24
26
  - ../../compass-canada/profile.md
@@ -32,7 +34,7 @@ Why Compass storefront Qty-Fulfilled cells stay dead — `Items.isFulfillable` N
32
34
  ## Summary
33
35
  A prod-data investigation (2026-08-12, read-only) into why so many Compass storefront lines still have a dead **Qty Fulfilled** cell. Conclusion: the remaining `Items.isFulfillable` NULLs are **not a backfill backlog — they are structurally unreachable**, and the values that *are* set are partly wrong. Records the measured state of `Client_Compass` / `Client_CompassCanada`, the evidence for/against deriving the flag from item type, the **decision** to override NetSuite with a type-derived rule for Compass, and the **write-time guard** that decision requires to survive the daily NetSuite sync.
34
36
 
35
- **⚠ Since TRUE-79669 (2026-10-15) the flag also drives Compass order status** — the base `_fulfillmentStatus()` skips `isFulfillable = 0` lines, NULL counts as fulfillable ([order fulfillment status](order-fulfillment-status-per-line.md)). A wrong 1/NULL on a non-shipping item now holds orders at Partially Fulfilled. Figures dated 2026-08-12 were read from production that date; later sections (retraction, column choice, data-fix status) were measured 2026-08-27 against prod and client-sandbox as marked.
37
+ **⚠ The flag also drives order status.** Deployed today (verified 2026-10-29): **Canada only** — its override skips `isFulfillable = 0` lines ([Canada status](../../compass-canada/features/order-fulfillment-status-per-line.md)); USA still uses order totals and ignores the flag. Once TRUE-79669 merges, both use the base rule, which skips 0 and counts NULL as fulfillable ([order fulfillment status](order-fulfillment-status-per-line.md)). A wrong 1/NULL on a non-shipping item then holds orders at Partially Fulfilled. Figures dated 2026-08-12 were read from production that date; later sections (retraction, column choice, data-fix status) were measured 2026-08-27 against prod and client-sandbox as marked.
36
38
 
37
39
  ## How it works
38
40
 
@@ -104,6 +106,7 @@ String(item?.Items?.isFulfillable) === "1" && Number(item?._qtyFulfilled) !== 0
104
106
  plus an extra **hardcoded** `hostName === COMPASS && AssetTypes.name === 'FEE' → false`.
105
107
  - NULL and 0 behave identically at this gate.
106
108
  - That FEE hardcode duplicates the proposed data rule — once the rule lands in the DB it should be deleted so the logic lives in one place.
109
+ - toga25-supply mirrors this gate (true or 1, FEE hardcode included) in the sales order Items table — see [Sales order Items table](../../../2.0/apps/toga25-supply/features/sales-order-items-table.md).
107
110
 
108
111
  ### Compass Canada uses a DIFFERENT type vocabulary
109
112
  Canada's `assetTypes` are **title case** (`Equipment`, `Hardware`, `Services`, `Fee`, `Consulting`) and its `itemTypes` are a separate vocabulary (`LAPTOP`, `MACBOOK`, `MONITORS`, `CABLES`, `SERVICE`, `Services`, `CONSULTING`, `SOFTWARE LICENSING`) — it has **no `FEE` or `SERVICEFEES` itemType at all**.
@@ -125,6 +128,8 @@ The two columns disagree, and the gap matters:
125
128
  **Compass Canada reads both columns.** `2026-10-15a` set only Canada's non-shipping `ItemTypes` (`SERVICE`, `SERVICES`, `CONSULTING`, `WARRANTY`) to 0; the 2026-10-28 backfill (below) also uses `AssetTypes` `Fee` / `Services`, which do exist in Canada. See [Compass Canada — order fulfillment status](../../compass-canada/features/order-fulfillment-status-per-line.md).
126
129
 
127
130
  ### Editing the flag in TOGa Supply — the item modal toggle (TRUE-82989, 2026-10-28)
131
+ - **Why it exists:** the Dev Team sets `Items.isFulfillable` from TOGa Supply instead of a DB update, because the flag drives order fulfillment status (Canada today, USA after TRUE-79669) and the Qty Fulfilled drill-down.
132
+ - **Create modal (2026-10-29):** same toggle on item create, **default ON**, so a new Compass / Canada item is saved with `isFulfillable = 1`, not NULL. Turn it OFF on create for a fee / service / warranty item. FE detail: [Surface Frontend](../../../2.0/apps/toga25-supply/features/surface-frontend.md).
128
133
  - **Fulfillable toggle** in the toga25-supply item record modal, Attributes section after Store Visible, **Compass USA + Compass Canada only**. FE wiring (view = Surface, edit = JSON, `apiFields` scoping): [Surface Frontend](../../../2.0/apps/toga25-supply/features/surface-frontend.md). Seed (Core element 236, client opt-in): [Surface Layer Schema](../../../2.0/apps/dbchanges2/features/surface-layer-schema.md).
129
134
  - **Save sends `isFulfillable` only when the toggle changed**, because the key alone triggers a chain walk — see [propagation](../../../2.0/apps/_underscore/features/fulfillable-item-propagation.md).
130
135
  - **View shows `—` for NULL** ("not set yet"; propagation skips NULL), `No` for 0, `Yes` for 1.
@@ -133,7 +138,7 @@ The two columns disagree, and the gap matters:
133
138
  - **USA** values still come from the NetSuite item sync (gotchas below); **67** USA items still NULL in prod (2026-10-28).
134
139
 
135
140
  ### Data fixes written for TRUE-79669 (2026-10-15, `dbchanges2` branch `TRUE-79669`, not run in prod)
136
- - `Client_Compass/2026-10-15a - FeeItemsNotFulfillable.sql` — every `ItemTypes.name = 'FEE'` item → 0 (13 of 16 already 0; `LT-FREIGHT`, `LT-PROPERTY TAX`, `TabAdminSupp` were 1). ItemTypes only — the wider assetType/catalog rule below is still pending.
141
+ - `Client_Compass/2026-10-15a - FeeItemsNotFulfillable.sql` — every `ItemTypes.name = 'FEE'` item → 0 (13 of 16 already 0; `LT-FREIGHT`, `LT-PROPERTY TAX`, `TabAdminSupp` were 1; `LT-FREIGHT` and `LT-PROPERTY TAX` still 1 on client-sandbox 2026-10-29, so they will hold USA orders at Partially Fulfilled once the per-line rule ships unless this file runs first). ItemTypes only — the wider assetType/catalog rule below is still pending.
137
142
  - `Client_CompassCanada/2026-10-15a - NonShippableItemTypesNotFulfillable.sql` — CONSULTING / SERVICE / SERVICES / WARRANTY → 0.
138
143
 
139
144
  ### Status of the type-derived data fix (verified 2026-08-27)
@@ -146,7 +151,7 @@ The developer's two-statement `UPDATE`, scoped to `catalogId = 1`, sets `isFulfi
146
151
  | prod | not applied |
147
152
 
148
153
  ## Gotchas
149
- - **⚠ New Compass Canada service-type items start with `isFulfillable` NULL** (= fulfillable to the status rule). The 2026-10-15 and 2026-10-28 fixes cover existing items only; a new item must be set with the item modal toggle (no write-time guard yet).
154
+ - **⚠ New Compass / Canada items created in TOGa Supply start with `isFulfillable = 1`** (create toggle defaults ON, 2026-10-29). A new fee / service / warranty item must be switched OFF on create or later, or it counts as a shipping line. Items created any other way (api2 without the key) still start NULL. No write-time guard yet.
150
155
  - **⚠ `Client_Compass/2026-08-11 - ItemsIsFulfillableFieldPermissions.sql` grants the WRONG field.** It uses `recordFieldId 2615`, which is `inventoryAdjustmentItemId`, not `isFulfillable` (**2434**). Check the RecordFields id against prod before trusting a grant file's name.
151
156
  - **⚠ CRITICAL — a backfill alone will be undone by the daily NetSuite sync.** Phase 1's `getCreateItem` **refreshes `isFulfillable` on EXISTING items** whenever NetSuite's value differs from the stored value. NetSuite returns **`T` for service (`NonInvtPart`) items**, so any type-derived **0** is flipped back to 1 on the next sync run. Proven from the audit log: Compass items **2382 / 2384 / 2385** (`US-IOS-SSC-SHR`, `US-IOS-VENUENEXT`, `US-IOS-VIRTUALMGR` — itemType `SERVICES`, assetType `CONSULTING`) were stamped NULL → 1 on 2026-08-04 13:56:00-01 by `apiId 1`, `userId NULL`, inside a 1–2 items/second cron sweep that began 13:45 and set every item to 1. **Therefore the rule MUST be enforced at write time** — a `prePut`/`prePost` guard on Items in `_Model_Client_Item` — in addition to the backfill migration. A migration on its own silently reverts within a day.
152
157
  - **⚠ Recorded caveat — the SERVICES/CONSULTING half of the rule is NOT supported by the evidence.** 413 `SERVICES` fulfillment rows carry their own tracking, and the rule flips **131 currently-clickable items off**. That half needs product sign-off. `SOFTWARE` (100% ride-along) is the *stronger* candidate on the evidence but was deliberately left out of scope.
@@ -158,8 +163,9 @@ The developer's two-statement `UPDATE`, scoped to `catalogId = 1`, sets `isFulfi
158
163
  - **⚠ The `catalogId = 1` scope misses 49 items carrying 52,367 priced order lines** — `PC-KIT-OPT2` (33,259 lines), `CG-MEDKITTING` (6,317), `CG-MEDKITTING-NWN-APPLE`, `ASI-WAREHAND`, `ODP-COMPASS_KIOSKBUILD` — all on catalog 2 or NULL, all flagged `isFulfillable = 1`, and **none has ever shipped**. They cause no problem *today* only because none has a PO link, so a purchased-line test excludes them anyway. Widen the scope or accept the flag is wrong on the largest-volume non-shipping SKU in the catalog.
159
164
 
160
165
  ## Change history
166
+ - 2026-10-29 — Fulfillable toggle on the item create modal, default ON (new items no longer start NULL); deployed status rule verified: Canada reads the flag, USA does not (TRUE-82989). (bala)
161
167
  - 2026-10-28 — Fulfillable toggle in the toga25-supply item modal for USA + Canada; USA write grant on 2434 for roles 4/7; Canada NULL backfill 14 → 0 / 214 → 1 (TRUE-82989). (bala)
162
- - 2026-10-15 — The flag now drives Compass order status (TRUE-79669); FEE (USA) and service-type (Canada) data fixes written; new Canada service items still start NULL (follow-up). (jcardinal)
168
+ - 2026-10-15 — The flag now drives Compass order status (TRUE-79669); FEE (USA) and service-type (Canada) data fixes written; new Canada service items started NULL (follow-up, closed 2026-10-29 by the create toggle). (jcardinal)
163
169
 
164
170
  ## Related
165
171
  - [Phase 1 — isFulfillable from NetSuite during item sync (1.0)](../../../1.0/apps/library/features/netsuite-item-isfulfillable-sync.md) — where the value is born and the refresh that reverts overrides.
@@ -6,7 +6,7 @@ project: _Underscore
6
6
  client: compass-usa
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-10-15
9
+ updated: 2026-10-29
10
10
  owners: ["bala", "jcardinal"]
11
11
  files:
12
12
  - _underscore/Model/Client/SalesOrder.php
@@ -35,6 +35,10 @@ Why now: the old reason to keep USA on order totals (ODP duplicate ASNs, broken
35
35
 
36
36
  **State:** `_underscore` branch `TRUE-79669` (not yet merged), data SQL committed on `dbchanges2` `TRUE-79669`. **Not deployed** — non-prod test planned first.
37
37
 
38
+ **Deployed today (verified 2026-10-29 on `origin/_sandbox-client` and `_production`):**
39
+ - **USA:** `_Model_Compass_SalesOrder::_fulfillmentStatus()` is still the **order-total** rule (TOGA Tech IFs + Strategic Systems ASNs). It does **not** read `isFulfillable`, so the TOGa Supply Fulfillable toggle does not change USA order status yet. Test orders on client-sandbox: `SA118967`, `SA132389`.
40
+ - **Canada:** still its own per-line ASN override, which **does** skip `isFulfillable = 0` lines — see [Canada status](../../compass-canada/features/order-fulfillment-status-per-line.md).
41
+
38
42
  ## How it works
39
43
 
40
44
  ### Key files / entry points
@@ -80,6 +84,7 @@ fulfilled otherwise
80
84
  - **Superseded — do not resurrect:** the USA "order-total kept on purpose (2026-08-27)" decision and Canada's ASN-bridge per-line rule (90 orders corrected, `ITEM_TYPES__NOT_SHIPPABLE`). Order totals let one line's surplus hide another's gap; the base per-line rule replaces both.
81
85
 
82
86
  ## Change history
87
+ - 2026-10-29 — Verified deployed state: USA still order-total (ignores `isFulfillable`), Canada override still live (TRUE-82989). (bala)
83
88
  - 2026-10-15 — TRUE-79669: Compass USA + Canada switched to the standard base `_fulfillmentStatus()` (per line, skips `isFulfillable = 0`, adds Picked/Packed); deleted USA order-total rule and Canada ASN override; FEE / service item data fixes. Supersedes the 2026-08-27 "USA keeps order-total" decision. Not deployed. (jcardinal)
84
89
  - 2026-10-13 — Noted that Fulfilled has no stage and is reached only through shipped quantity. (bala)
85
90
  - 2026-08-27 — Per-line USA rule built, measured (5,223 orders) and reverted; Canada got its own ASN per-line rule. (bala)
@@ -288,7 +288,8 @@ separate, related client (see its own profile).
288
288
  Compass USA + Canada keep only their approval gates in `_Model_Compass_SalesOrder::_status`; the
289
289
  fulfillment half is the base `_Model_Client_SalesOrder::_fulfillmentStatus()` (per line, shipped
290
290
  IF qty, skips `isFulfillable = 0`, shows Picked/Packed). Replaces the order-total rule kept on
291
- purpose since 2026-08-27. Needs fee items at `isFulfillable = 0`.
291
+ purpose since 2026-08-27. Needs fee items at `isFulfillable = 0`. **Deployed code (verified
292
+ 2026-10-29) still runs the order-total rule and ignores `isFulfillable`.**
292
293
  [Order fulfillment status](features/order-fulfillment-status-per-line.md).
293
294
  - **⚠ The approval gate strands far more orders than the fulfillment rule mislabels (2026-08-27).**
294
295
  **11,341** Compass USA orders sit at a pending-approval status and never reach the fulfillment
@@ -313,7 +314,7 @@ separate, related client (see its own profile).
313
314
  [isFulfillable — Data Quality & the Type-Derived Rule](features/isfulfillable-data-quality-and-type-rule.md).
314
315
  **Editable in TOGa Supply since TRUE-82989 (2026-10-28):** Fulfillable toggle in the toga25-supply
315
316
  item modal; `Client_Compass/2026-10-28a` grants write on 2434 to roles 4 (Admin) + 7 (Compass
316
- Base). Same doc.
317
+ Base). Create modal too since 2026-10-29 (default ON). Same doc.
317
318
 
318
319
 
319
320
  ## Talos tenant reference
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.1205",
3
+ "version": "1.0.1206",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",