toga-ai 1.0.601 → 1.0.602
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.
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
| [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/Model.php, _underscore/Query.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php, api2/Controller/Index.php |
|
|
36
36
|
| [Persona Name Translation (PersonaTranslations sidecar)](features/persona-name-translation.md) | Serves Persona **names** in multiple languages by adding a per-language **sidecar** table `PersonaTranslations`, reusing the platform's existing metadata-driven | _underscore/Model/Client/PersonaTranslation.php, dbchanges2/Client/2026-07-22b - PersonaTranslations.sql, dbchanges2/Core/2026-07-22a - PersonaTranslationsRecord.sql, dbchanges2/Client/2026-07-22c - PersonaTranslationsAcl.sql, dbchanges2/Client_CompassCanada/2026-07-22 - PersonaTranslationsFrench.sql, toga2-commerce/src/pages/Account/view/MySettingsView.tsx |
|
|
37
37
|
| [Record Change Audit Log (Logs_<Client>.Record / RecordField) — reading a field's history](features/record-change-audit-log.md) | Every 2.0 client schema has a sibling **logs** schema `Logs_<Tenant>` (e.g. | _underscore/Model/Client/Logs/Record.php, _underscore/Model/Client/Logs/RecordField.php, _underscore/Model/Client/Logs/CustomRecordField.php |
|
|
38
|
-
| [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql, dbchanges2/Client_Compass/2026-07-02a - FixSA133377TrackingSerialAndDuplicateIF.sql |
|
|
38
|
+
| [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql, dbchanges2/Client_Compass/2026-07-02a - FixSA133377TrackingSerialAndDuplicateIF.sql, dbchanges2/Client_Compass/2026-08-18 - FixSA135471HeroItemHalfQuantityFulfillment.sql |
|
|
39
39
|
| [_String helpers — ASCII-safe HTML entity encoding (and the parseBetween trap)](features/string-html-entity-helpers.md) | `_String` is the 2.0 framework's static string utility class. | _underscore/String.php |
|
|
40
40
|
| [Surface Resolver (_Model_Core_Surface::resolve — replaces Page::meta)](features/surface-resolver.md) | The runtime for the platform-wide **Surface** UI presentation layer: 9 `_underscore` models plus a cached resolver, `_Model_Core_Surface::resolve(&$api, string | _underscore/Model/Core/Surface.php, _underscore/Model/Client/AclRecordScript.php, _underscore/Model/Core/RecordScript.php, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Quad/2026-07-01a - GrantSurfacesMetaGroupScriptAcl.sql, dbchanges2/Client_CompassCanada/2026-07-01a - GrantSurfacesMetaGroupScriptAcl.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Client_Compass/2026-07-15f - SalesOrderRecordActionsRemoveDeadConfigRuleOverrides.sql, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, _underscore/Model/Core/SurfaceElement.php, _underscore/Model/Core/Action.php, _underscore/Model/Core/Vocabulary.php, _underscore/Model/Core/VocabularyTerm.php, _underscore/Model/Core/Message.php, _underscore/Model/Client/SurfaceOverride.php, _underscore/Model/Client/MessageTranslation.php, _underscore/Model/Client/ThemeToken.php, _underscore/Model/Core/Page.php, api2/Component/Api/V2/V2.php |
|
|
41
41
|
| [Table-View Hyperlink Columns (meta → ACL → computed URL → render)](features/tableview-hyperlink-columns.md) | Any 2.0 table-view column can render its value as a clickable link instead of plain text. | _underscore/Model/Client/TableView.php, _underscore/Model/Client/TrackingNumber.php, api2/Component/Api/V2/V2.php, toga2-supply/src/api/toga.ts, toga2-supply/src/components/ui/Tables/PrimaryTable/helperFunctions/formatTableData.tsx, toga2-supply/src/components/ui/Tables/PrimaryTable/helperFunctions/convertData.tsx, toga2-supply/src/components/ui/Tables/hooks/useDataTableState.tsx, dbchanges2/Client/2026-07-20 - TrackingNumberHyperlinkAndFieldPermission.sql |
|
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-08-18
|
|
10
10
|
owners: [jcardinal, rgirish]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Client/ItemFulfillment.php
|
|
@@ -17,6 +17,7 @@ files:
|
|
|
17
17
|
- dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql
|
|
18
18
|
- dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql
|
|
19
19
|
- dbchanges2/Client_Compass/2026-07-02a - FixSA133377TrackingSerialAndDuplicateIF.sql
|
|
20
|
+
- dbchanges2/Client_Compass/2026-08-18 - FixSA135471HeroItemHalfQuantityFulfillment.sql
|
|
20
21
|
related:
|
|
21
22
|
- ../architecture.md
|
|
22
23
|
- ../../api2/architecture.md
|
|
@@ -55,8 +56,10 @@ fire those records' interceptors) no-op. A `while` loop walks UP one level at a
|
|
|
55
56
|
1. Load downstream IF; no `salesOrderId` (transfer order) → return null (out of scope).
|
|
56
57
|
2. Resolve upstream SO via the **header walk** (bridge tables); none → null (top of chain).
|
|
57
58
|
3. Build desired upstream items: map each downstream IFI's SOI up via the **item walk**,
|
|
58
|
-
GROUP BY upstream SOI, sum fulfilled qty, then **scale into upstream order units**
|
|
59
|
-
|
|
59
|
+
GROUP BY upstream SOI, sum fulfilled qty, then **scale into upstream order units** — but
|
|
60
|
+
only for genuine multi-component bundles, keyed on **distinct downstream items** (see
|
|
61
|
+
Gotchas → Bundle scaling). A single distinct downstream item (incl. accepted over-ship)
|
|
62
|
+
mirrors unscaled.
|
|
60
63
|
4. Resolve-or-create the upstream IF (**eager** — header-only POST still builds the chain):
|
|
61
64
|
load existing via `upstreamItemFulfillmentId` and PUT changed header fields, or POST a
|
|
62
65
|
new IF with mirrored header + upstream SO then PUT the downstream IF's link.
|
|
@@ -95,9 +98,36 @@ inheritance. Verified against prod `Client_Compass` chains (≥3 levels deep).
|
|
|
95
98
|
`…Put.sql` for POST/PUT) against that env's Core DB — the PHP won't fire on PUT otherwise.
|
|
96
99
|
- **`depth: -1` always** on orchestration calls. `depth => 0` means *unlimited* FK
|
|
97
100
|
traversal → OOM. Never use 0.
|
|
98
|
-
- **Bundle scaling
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
- **Bundle scaling is gated on DISTINCT DOWNSTREAM ITEMS, not downstream SOI lines
|
|
102
|
+
(corrected 2026-08-18).** A "Recipe" query, per upstream SOI, takes **one ordered-qty slot
|
|
103
|
+
per distinct downstream `itemId`** (MAX ordered qty across duplicate same-item sourcing
|
|
104
|
+
lines) and counts the downstream lines. Scaling
|
|
105
|
+
(`upstreamQty = totalDownstreamFulfilled × upstreamOrderedQty / Σ distinctDownstreamOrderedQty`,
|
|
106
|
+
**capped at the raw sum** so it can only reduce, never inflate) applies **only when
|
|
107
|
+
`distinctDownstreamItemCount > 1`** — a genuine multi-component bundle. A qty-1 bundle of 6
|
|
108
|
+
fully-shipped distinct components still → upstream qty 1 (6×1/6); 3×2/6=1 preserved. When a
|
|
109
|
+
single distinct downstream item is sourced (incl. an accepted over-shipment split across
|
|
110
|
+
several downstream SOs/SOIs), the raw fulfilled qty mirrors up **unscaled** (round() applied
|
|
111
|
+
symmetrically on this branch too).
|
|
112
|
+
- **Same-item over-shipment used to mis-scale into fractions (root cause, fixed 2026-08-18).**
|
|
113
|
+
Pre-fix the denominator summed ordered qty over *distinct downstream SOI lines*. A vendor
|
|
114
|
+
(e.g. Office Depot / ODP) can create **multiple downstream SalesOrders for the SAME item off
|
|
115
|
+
ONE upstream PO line** (accepted over-ship / re-send); each duplicate is another distinct
|
|
116
|
+
downstream SOI, inflating the denominator and shrinking the factor. Compass SA135471's hero
|
|
117
|
+
SOI (item 2682, qty 1) mapped to two ODP SOIs (same item, each qty 1) via one POI, so factor
|
|
118
|
+
= 1/2 and each of two customer IFs showed **qty 0.50** ("Quantity Fulfilled" 0.5 + 0.5). Two
|
|
119
|
+
physically distinct serials had shipped — a real over-ship, not a phantom duplicate. Fix:
|
|
120
|
+
distinct-item denominator (above) mirrors the raw qty (→ 2). Data repaired by
|
|
121
|
+
`dbchanges2/Client_Compass/2026-08-18 - FixSA135471HeroItemHalfQuantityFulfillment.sql`.
|
|
122
|
+
- **Mixed topology (a real bundle whose component is ALSO sourced across multiple downstream
|
|
123
|
+
lines) is logged, not silently averaged.** When the Recipe's `lineCount > distinctItemCount`,
|
|
124
|
+
the engine `error_log()`s a diagnostic instead of averaging — same log-don't-swallow rule as
|
|
125
|
+
the broken-bridge guard.
|
|
126
|
+
- **Diagnosing over-ship vs. phantom duplicate:** traverse the bridge — upstream SOI →
|
|
127
|
+
`SalesOrderItems_PurchaseOrderItems` → POI → `PurchaseOrders_SalesOrders` → downstream ODP
|
|
128
|
+
SOs (`customerId=1`); compare downstream vs. upstream `itemId` (**same itemId ⇒ over-ship /
|
|
129
|
+
duplicate sourcing, NOT a bundle**); then check `ItemFulfillmentItemUnits` serials — distinct
|
|
130
|
+
serials ⇒ genuine over-shipment, identical ⇒ phantom duplicate.
|
|
101
131
|
- **DELETE propagation is NOT implemented.** The engine sets `$outData = null` on DELETE so
|
|
102
132
|
post-delete hooks never fire; a standalone downstream DELETE doesn't immediately remove its
|
|
103
133
|
upstream mirror. A later reconciling PUT cleans up stale records via set comparison. Full
|
|
@@ -131,6 +161,19 @@ inheritance. Verified against prod `Client_Compass` chains (≥3 levels deep).
|
|
|
131
161
|
upstream record is written at most once — idempotent). Fine for normal fulfillment sizes.
|
|
132
162
|
|
|
133
163
|
## Change history
|
|
164
|
+
- 2026-08-18 — **Bundle scaling now discriminates on distinct downstream items, not SOI lines
|
|
165
|
+
(CTO-approved "Option B").** `reconcileUpstreamLevel`'s Recipe query takes one ordered-qty
|
|
166
|
+
slot per distinct downstream `itemId` and scales only when `distinctDownstreamItemCount > 1`;
|
|
167
|
+
a single distinct item (incl. accepted over-ship split across multiple downstream SOs)
|
|
168
|
+
mirrors the raw fulfilled qty unscaled. Mixed topology (recipe lineCount > distinctItemCount)
|
|
169
|
+
is `error_log`'d, not averaged. Added `(int)` casts on the method's trusted-PK SQL
|
|
170
|
+
interpolations and round() symmetry on the unscaled branch. Root-caused on Compass prod
|
|
171
|
+
SA135471: the hero item (2682, qty 1) sourced via two ODP SalesOrders off one PO line showed
|
|
172
|
+
"Quantity Fulfilled" 0.5 + 0.5 across two customer IFs (old denominator summed distinct SOI
|
|
173
|
+
lines → factor 1/2); two distinct serials confirmed a genuine over-ship. Data repaired by the
|
|
174
|
+
idempotent retro migration `dbchanges2/Client_Compass/2026-08-18 - FixSA135471HeroItemHalfQuantityFulfillment.sql`
|
|
175
|
+
(IFI 302121 / 302126 quantity 0.50 → 1.00). Deploy order: `_underscore` code first, then the
|
|
176
|
+
SQL against the Compass DB. Neither deployed at capture time. (jcardinal)
|
|
134
177
|
- 2026-07-02 — Added a **broken-bridge guard** to `reconcileUpstreamLevel`: when `$desiredItems`
|
|
135
178
|
is empty but the downstream IF has items (broken SOI↔POI bridge), it logs and returns null
|
|
136
179
|
instead of creating an empty header-only duplicate upstream IF. Root-caused on Compass SA133377
|
package/package.json
CHANGED