toga-ai 1.0.397 → 1.0.399
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.
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
| [Column Visibility (URL-driven show/hide columns)](features/column-visibility.md) | A "Columns" header button that opens a modal listing every column from the table meta, lets the user show/hide columns, adjusts the table live, and persists the | toga25-supply/src/components/ColumnVisibilityModal/, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx |
|
|
10
10
|
| [Meta-Driven Page & Table Setup](features/meta-driven-table-data.md) | A page in this app is **meta-driven end to end**: the page view model fetches *page meta* (labels, sections, ACL) and *table meta* (the columns/fields + table s | toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableState.ts, toga25-supply/src/hooks/useTablePageMeta.ts, toga-blox-npm/dist/hooks/useFetchPageMeta.d.ts, toga-blox-npm/dist/hooks/useFetchTablePageMeta.d.ts, toga-blox-npm/dist/hooks/useAssignTableFieldLabels.d.ts, toga-blox-npm/dist/components/Table/hooks/useTableData.d.ts |
|
|
11
11
|
| [Record Modals & Nested Tables](features/record-modals-and-nested-tables.md) | The repo's family of modal + nested-table patterns layered over toga-blox `TableRecordModal` and `PrimaryTable*Layout`. | toga25-supply/src/layout/ItemRecordModalLayout/, toga25-supply/src/layout/SalesOrderRecordModalLayout/, toga25-supply/src/layout/SalesOrderItemsTableLayout/, toga25-supply/src/layout/ItemFulfillmentModal/, toga25-supply/src/layout/GenericNestedTables/GenericNestedTables.tsx, toga25-supply/src/layout/GenericNestedTables/GenericTableLayout.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/DEFAULT/inventoryGroupings.json, toga25-supply/src/hooks/useTableCellInteractions.ts, toga25-supply/src/hooks/useServerTableUrlState.ts |
|
|
12
|
-
| [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SurfaceRowActions.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx |
|
|
12
|
+
| [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SurfaceRowActions.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx |
|
|
13
13
|
| [AWS Amplify Multi-Environment Deployment](workflows/amplify-deployment.md) | How `toga25-supply` deploys to **all** of its environments on AWS Amplify from a **single shared `amplify.yml`**. | toga25-supply/amplify.yml, toga25-supply/src/api/api.ts, toga25-supply/src/hooks/useAuthenticationFlow.ts, toga25-supply/vite.config.ts, toga25-supply/package.json |
|
|
14
14
|
| [Cypress Testing Harness (component + e2e)](workflows/cypress-testing.md) | The Cypress test harness for the `toga25-supply` frontend, bootstrapped from scratch (`cypress` was already a dependency but there was no config, no `cypress/` | toga25-supply/cypress.config.ts, toga25-supply/cypress/tsconfig.json, toga25-supply/cypress/support/component.tsx, toga25-supply/cypress/support/component-index.html, toga25-supply/cypress/support/e2e.ts, toga25-supply/cypress/support/commands.ts, toga25-supply/cypress/support/fixtures.ts, toga25-supply/cypress/support/mocks/useApprovalModalViewModel.ts, toga25-supply/cypress/component/SalesOrderApprovalModalsLayout.cy.tsx, toga25-supply/cypress/component/RecordApprovalModalLayout.cy.tsx, toga25-supply/cypress/component/EnterPoNumberModal.cy.tsx, toga25-supply/cypress/e2e/salesOrderApproval.cy.ts |
|
|
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-21
|
|
10
10
|
owners: [jcardinal, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/surface/useFetchSurfaceMeta.ts
|
|
@@ -47,6 +47,8 @@ files:
|
|
|
47
47
|
- toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx
|
|
48
48
|
- toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx
|
|
49
49
|
- toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx
|
|
50
|
+
- toga25-supply/src/surface/useStatusColors.ts
|
|
51
|
+
- toga25-supply/src/surface/SurfaceHeader.tsx
|
|
50
52
|
related:
|
|
51
53
|
- ../../_underscore/features/surface-resolver.md
|
|
52
54
|
- meta-driven-table-data.md
|
|
@@ -344,6 +346,25 @@ shaped `{ order }`, so the predicate reads **`order.currentStage` / `order.stage
|
|
|
344
346
|
`clipboardListCheck`, `penToSquare`) and must be used as-is (`element.tokens?.icon ?? "check"`).
|
|
345
347
|
Running it through `resolveThemeToken` misses on every icon and silently falls back to `"check"` for
|
|
346
348
|
every button.
|
|
349
|
+
- **Vocabulary color tokens are unresolvable because the resolved bundle's `theme` map is empty —
|
|
350
|
+
resolve status-badge dot colors from the status endpoint's `colorHex` instead.** A resolved bundle
|
|
351
|
+
ships theme-token SLUGS on each vocabulary term (`tokens.color = "color.status.<slug>"`,
|
|
352
|
+
`tokens.bg = "bg.status.<slug>"`) but the bundle's `theme` map arrives as `{}`, so
|
|
353
|
+
`resolveThemeToken(theme, term.tokens?.color)` returns `undefined` and the status dot renders
|
|
354
|
+
colorless in the sales-order record-modal header. Fix: resolve the dot color from the
|
|
355
|
+
`sales-order-statuses` API's `colorHex` field keyed by slug — a `SurfaceVocabularyTerm.value` **IS**
|
|
356
|
+
the status slug, so it maps 1:1 to `sales-order-statuses[].slug`. The **`useStatusColors(vocabularySlug)`**
|
|
357
|
+
hook (`src/surface/useStatusColors.ts`) fetches `GET /v2/sales-order-statuses?fields=slug,colorHex&sort=sortOrder`
|
|
358
|
+
(session-cached via react-query, `staleTime`/`gcTime` Infinity, same pattern as `useFetchSurfaceMeta`)
|
|
359
|
+
and returns a `(slug) => "#RRGGBB" | undefined` resolver, gated by a
|
|
360
|
+
`VOCABULARY_COLOR_ROUTES = { "sales-order-status": "sales-order-statuses" }` map so it is reusable
|
|
361
|
+
for other STATUS vocabularies (walks the V2 envelope to find the row array, no hard-coded
|
|
362
|
+
`data.salesOrderStatuses`). `SurfaceHeader.tsx`'s BADGE branch was extracted into a
|
|
363
|
+
`SurfaceStatusBadge` sub-component (needed because `useStatusColors` is a hook) whose dot color is
|
|
364
|
+
`resolveStatusColor(term.value) ?? resolveThemeToken(theme, term.tokens?.color)` — `colorHex` wins,
|
|
365
|
+
the theme token remains a fallback if the `theme` map is ever populated. Label resolution and the
|
|
366
|
+
"no matching term → render nothing" behavior are unchanged. The legacy table path
|
|
367
|
+
(`PrimaryTable*Layout` via `statusTypes.json`) already used `colorHex` and was untouched.
|
|
347
368
|
- **Scripted-API GET return values land under `data.<scriptRoute>`, not `data` directly.** V2 nests a
|
|
348
369
|
scripted-API return value inside the envelope under a route-keyed slot of `data` (e.g. `data.meta`,
|
|
349
370
|
`data.surfaces`), one level deeper than a normal data fetch. The bundle hook unwraps this with
|
|
@@ -373,6 +394,17 @@ shaped `{ order }`, so the predicate reads **`order.currentStage` / `order.stage
|
|
|
373
394
|
treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
|
|
374
395
|
|
|
375
396
|
## Change history
|
|
397
|
+
- 2026-07-21 — Fixed the surface-driven **status badge dot rendering colorless** in the sales-order
|
|
398
|
+
record-modal header: the resolved bundle ships vocabulary color TOKEN SLUGS
|
|
399
|
+
(`color.status.*`/`bg.status.*`) but its `theme` map is empty (`{}`), so `resolveThemeToken` returns
|
|
400
|
+
`undefined`. New **`useStatusColors(vocabularySlug)`** hook (`src/surface/useStatusColors.ts`)
|
|
401
|
+
resolves the dot color from the `sales-order-statuses` API's `colorHex` keyed by slug (a
|
|
402
|
+
`SurfaceVocabularyTerm.value` IS the status slug → 1:1 to `sales-order-statuses[].slug`);
|
|
403
|
+
session-cached via react-query, gated by a `VOCABULARY_COLOR_ROUTES` map for reuse across STATUS
|
|
404
|
+
vocabularies. `SurfaceHeader.tsx`'s BADGE branch extracted into a `SurfaceStatusBadge` sub-component
|
|
405
|
+
(hook use), dot color = `resolveStatusColor(term.value) ?? resolveThemeToken(...)` (colorHex wins,
|
|
406
|
+
theme token remains a fallback). Legacy table path (via `statusTypes.json`) already used `colorHex`,
|
|
407
|
+
untouched. `tsc --noEmit` clean. (apeterson)
|
|
376
408
|
- 2026-07-20 — Documented the **two FE rule engines** (frozen surface Tier-1 `evaluateSurfaceRule`,
|
|
377
409
|
which THROWS on `{type}` nodes, vs legacy `evaluateEnableRule` with `{type}` NAMED_RULES incl
|
|
378
410
|
`stepTwoAssigned` reading `ctx.currentStage`/`ctx.stages`) and why the corrected Approve rule is
|
|
@@ -6,8 +6,8 @@ project: Worker
|
|
|
6
6
|
client: nycdoe
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
10
|
-
owners: [mhammontree]
|
|
9
|
+
updated: 2026-07-20
|
|
10
|
+
owners: [mhammontree, sking]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/sync/nycdoe/send_ticket_updates.php
|
|
13
13
|
- worker/crons/sync/nycdoe/process_tickets.php
|
|
@@ -76,29 +76,55 @@ revert or never reach ServiceNow. It is the status-sync companion to the broader
|
|
|
76
76
|
- `Hold` has **statusRank 6**, beating `Work in Progress` (rank 5) in the downgrade-guard
|
|
77
77
|
added by TRUE-76812 — so a downstream sync cannot silently demote a held order.
|
|
78
78
|
|
|
79
|
-
### Status
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
79
|
+
### Status fields (ServiceNow side) — CORRECTED 2026-07-20
|
|
80
|
+
> **The earlier "native `state` is authoritative; never use `u_status_task`" model was WRONG**
|
|
81
|
+
> (see Change history 2026-07-20). Both fields exist and both matter, with distinct roles:
|
|
82
|
+
- **`u_status_task` (custom) is the CLIENT-FACING, authoritative incident status** — the field
|
|
83
|
+
DOE's own ServiceNow views/reporting read (values `Open`, `In Progress`, `On Hold`, …). **A hold
|
|
84
|
+
MUST be written here or the client never sees it.** It is **writable from any active state**; a
|
|
85
|
+
controlled test PATCH of `{u_status_task:'On Hold'}` returned success and PERSISTED for days
|
|
86
|
+
(INC2192842 held 3+ days). **The TOGaDesk inbound sync keys off `u_status_task`**
|
|
87
|
+
(`process_tickets.php:206` reads `$data->u_status_task`), and the RITM cron checks
|
|
88
|
+
`u_status_task == 'On Hold'` (`send_request_item_updates.php:224`) — so `u_status_task` **is**
|
|
89
|
+
the correct field for hold detection.
|
|
90
|
+
- The native **`state`** field is ServiceNow's INTERNAL, SLA-bearing state machine. Setting
|
|
91
|
+
`state=3` (On Hold) pauses the SNOW SLA clock but is **invisible to the DOE client**. ServiceNow
|
|
92
|
+
only permits the native On Hold transition **FROM In Progress (`state=2`)** — pushing `state=3`
|
|
93
|
+
from any other state (Assigned, or already Resolved/Closed/Canceled) is silently rejected (HTTP
|
|
94
|
+
200, empty body — see the empty-body gotcha). `hold_reason=10` is not a valid choice on this
|
|
95
|
+
instance and is stored empty/dropped, but native On Hold still sticks without it.
|
|
96
|
+
- Numeric `state` codes seen on the wire: INC `On Hold`=**3**, `In Progress`=**2**;
|
|
97
|
+
RITM `On Hold`=**8**, the scheduled-update state=**"-14"**.
|
|
98
|
+
|
|
99
|
+
### Outbound (TOGaDesk → ServiceNow) — INC hold push (worker #1676 + #1677)
|
|
100
|
+
When a DOE repair order is on hold, `send_ticket_updates.php` pushes:
|
|
101
|
+
- **Always set `u_status_task='On Hold'`** — the client-facing field, writable from any active
|
|
102
|
+
state; on release set it back to `'In Progress'`. This is what actually surfaces the hold to the
|
|
103
|
+
client. Until the local status stops reverting (the library `qqStatus()` fix below) the outbound
|
|
104
|
+
never sees `orderIsOnHold=true` and never pushes `u_status_task` at all — hence the deploy
|
|
105
|
+
dependency.
|
|
106
|
+
- **Set native `state=3` (On Hold) only when the incident is currently `In Progress`** — an SLA
|
|
107
|
+
pause. Because ServiceNow rejects the transition from any other state (empty-body 200), the cron
|
|
108
|
+
**defers without advancing `dtSynced`** when the incident is neither On Hold nor In Progress, and
|
|
109
|
+
re-pushes only once it reaches In Progress.
|
|
110
|
+
- **Closed-incident reconcile:** if the incident is already `Resolved`/`Closed`/`Canceled` on the
|
|
111
|
+
SNOW side it can no longer be held — the cron **advances `dtSynced`** to accept the remote
|
|
112
|
+
closure instead of retrying forever. (A canceled incident, INC2190846, had generated **2,392**
|
|
113
|
+
failed empty-body PATCHes before this.)
|
|
114
|
+
- The "Assigned → In Progress" auto-start block is still guarded so it never pushes `state:2` over
|
|
115
|
+
a held order (TRUE-79922). Named constants: `SN_STATE_IN_PROGRESS` / `SN_STATE_ON_HOLD` /
|
|
116
|
+
`SN_HOLD_REASON_DEFAULT` in `send_ticket_updates.php`, plus `SN_TASK_STATUS_ON_HOLD` /
|
|
117
|
+
`SN_TASK_STATUS_IN_PROGRESS` for the `u_status_task` writes (#1677).
|
|
118
|
+
- `send_request_item_updates.php` (RITM) pushes `state:"8"` (On Hold) and carries a defensive
|
|
119
|
+
`HOLD_*` guard on the scheduled-update path so it can't push `state:"-14"` while held; its
|
|
120
|
+
reverse-hold logic keys off `u_status_task == 'On Hold'`.
|
|
95
121
|
|
|
96
122
|
### Inbound (ServiceNow → TOGaDesk)
|
|
97
|
-
- `process_tickets.php`
|
|
98
|
-
(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
123
|
+
- `process_tickets.php` reads the client-facing **`u_status_task`** field
|
|
124
|
+
(`$status = $data->u_status_task`, L206) — **NOT** native `state` — and maps `On Hold` to the
|
|
125
|
+
bare **`STATUS_HOLD`** on the local repair order (this is exactly why the library `qqStatus()`
|
|
126
|
+
fix below had to recognize bare `HOLD`). Both inbound "Assigned-force" `patchINC(state:2)` blocks
|
|
127
|
+
are guarded against a local hold.
|
|
102
128
|
|
|
103
129
|
### RITM "ETA-in-hold"
|
|
104
130
|
- `send_request_item_updates.php` syncs `scheduled_date` (ETA) to ServiceNow **inside the
|
|
@@ -182,9 +208,19 @@ The SNOW round-trip note reconciliation (delete-and-reinsert in `process_tickets
|
|
|
182
208
|
auto-start fought the hold push — `state:3` then `state:2` pushed seconds apart, every run,
|
|
183
209
|
so SNOW never stayed On Hold. Any future auto-start/auto-status block on these crons MUST
|
|
184
210
|
short-circuit when the local RO is in a `HOLD_*` status.
|
|
185
|
-
-
|
|
186
|
-
native `state`
|
|
187
|
-
|
|
211
|
+
- **⚠ SUPERSEDED (see Change history 2026-07-20):** TRUE-79922 originally concluded that reading
|
|
212
|
+
`u_status_task` (instead of native `state`) was a bug and that hold detection should be on native
|
|
213
|
+
`state` only. That was **backwards.** `u_status_task` is the client-facing authoritative field
|
|
214
|
+
and **is** the correct hold-detection field — the live inbound cron reads it at
|
|
215
|
+
`process_tickets.php:206`. Native `state` is the internal SLA state, pushed only as a secondary
|
|
216
|
+
pause when the incident is In Progress.
|
|
217
|
+
- **The empty-body 200 (nycd3 `data/tasks` PATCH):** `App_Api_NYCDOEV2::patchINC`/`patchRITM`
|
|
218
|
+
(both hit `/api/nycd3/data/tasks/{number}`) return **HTTP 200 with an EMPTY body (no `result`
|
|
219
|
+
object)** when a requested native `state` transition is illegal — e.g. pushing `state=3` from a
|
|
220
|
+
non-In-Progress state. This is silent: no exception, no error field. A canceled incident
|
|
221
|
+
(INC2190846) generated **2,392** such failed PATCHes before the closed-incident reconcile was
|
|
222
|
+
added. Gate native `state=3` on `snState == 'In Progress'`, and reconcile
|
|
223
|
+
Resolved/Closed/Canceled by advancing `dtSynced`.
|
|
188
224
|
- Holds are **never auto-released** — if a SNOW user manually changes status, the outbound
|
|
189
225
|
cron correctly re-asserts On Hold; that is intended, not a bug. Clear the hold in TOGaDesk.
|
|
190
226
|
- **Diagnosing which writer dropped a hold:** `repair_order_history`
|
|
@@ -217,6 +253,25 @@ The SNOW round-trip note reconciliation (delete-and-reinsert in `process_tickets
|
|
|
217
253
|
`repair_orders.status` directly cannot distinguish the six hold variants after a recompute.
|
|
218
254
|
|
|
219
255
|
## Change history
|
|
256
|
+
- 2026-07-20 — **Corrected the authoritative-field model and shipped the INC hold push.**
|
|
257
|
+
Controlled test PATCHes proved the earlier "native `state` is authoritative; never use
|
|
258
|
+
`u_status_task`" conclusion (TRUE-79922) **backwards**: the custom **`u_status_task`** is the
|
|
259
|
+
client-facing authoritative incident status (a `{u_status_task:'On Hold'}` PATCH persisted 3+
|
|
260
|
+
days, INC2192842), the inbound cron reads it (`process_tickets.php:206`), and the RITM cron keys
|
|
261
|
+
off it (L224). Native `state` is the internal SLA state and only accepts On Hold **from In
|
|
262
|
+
Progress** — any other transition returns a silent HTTP-200 empty body (a canceled incident,
|
|
263
|
+
INC2190846, had racked up 2,392 failed PATCHes). Fix: outbound `send_ticket_updates.php` now
|
|
264
|
+
always writes `u_status_task='On Hold'` (client-facing; `'In Progress'` on release), sets native
|
|
265
|
+
`state=3` only when In Progress (SLA pause), and reconciles already-closed incidents by advancing
|
|
266
|
+
`dtSynced` instead of retrying forever; added `SN_STATE_*` / `SN_HOLD_REASON_DEFAULT` /
|
|
267
|
+
`SN_TASK_STATUS_*` constants. worker PRs **#1676** (merged — retry-storm/closed reconcile + native
|
|
268
|
+
state gate) and **#1677** (open — `u_status_task` write). (sking)
|
|
269
|
+
- 2026-07-20 — library **#845**: `App_Model_TogaDesk_RepairOrder::qqStatus()` now recognizes all
|
|
270
|
+
six `HOLD_*` constants in both "on hold" IN-clauses. The inbound sync writes SNOW On Hold as the
|
|
271
|
+
bare `STATUS_HOLD` ('HOLD'), which the prior 2-of-6 match dropped, silently recomputing the order
|
|
272
|
+
to `ORDER_ASSIGNED_AWAITING_SCHEDULING` (the local "hold revert"). Additive only. Must ship with/
|
|
273
|
+
before worker #1677 — until the local status stops reverting, the outbound never sees
|
|
274
|
+
`orderIsOnHold=true`. (sking)
|
|
220
275
|
- 2026-07-07 — TRUE-80060 (togadesk portion): closed the **third** DOE hold-failure path — a
|
|
221
276
|
hold set via a note/comment (`Repair::addNotes`) never reaching SNOW. `addNotes` recorded
|
|
222
277
|
status only on `repair_order_notes.newstatus` (never the `repair_orders.status` cache the
|
package/package.json
CHANGED