toga-ai 1.0.267 → 1.0.268
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.
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
| [Assortment Name Translation (AssortmentTranslations sidecar)](features/assortment-name-translation.md) | Serves Assortment (product-grouping) **names** in multiple languages by adding a per-language **sidecar** table `AssortmentTranslations`, reusing the platform's | _underscore/Model/Client/AssortmentTranslation.php, dbchanges2/Client/2026-06-26a - AssortmentTranslations.sql, dbchanges2/Core/2026-06-26a - AssortmentTranslationsRecord.sql, dbchanges2/Client/2026-06-26b - AssortmentTranslationsAcl.sql |
|
|
8
8
|
| [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/LabelPdf/LabelPdf.php, _underscore/Component/Library/Carriers/ShipmentRequest/ShipmentRequest.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Component/Library/Carriers/Fedex/Fedex.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
|
|
9
9
|
| [Client Email Template Sending](features/email-template-sending.md) | `_Model_Client_EmailTemplate` sends a stored, client-defined email template by UUID. | _underscore/Model/Client/EmailTemplate.php, _underscore/Model/Client/EmailTemplateOutgoingEmailAddress.php, _underscore/Email.php |
|
|
10
|
+
| [Error Reporting — Issue/Event Aggregation (exceptionHandler)](features/error-reporting-issue-event.md) | `_underscore`'s global exception handler persists every uncaught exception into a two-table **Issue / Event** model in the **shared Core Logs DB** (`_underscore | _underscore/Error.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/Event.php, dbchanges2/Logs/2026-07-06 - Issue and Event tables.sql |
|
|
10
11
|
| [Record-Changed Event Publishing (_Event::publish to SQS)](features/event-publish-sqs.md) | `_Event::publish()` (in `_underscore/Event.php`) is the PHP side of the real-time event pipeline. | _underscore/Event.php |
|
|
11
12
|
| [Forecast.Sales NetSuite import engine (real-time webhook)](features/forecast-sale-import.md) | Real-time importer that takes a NetSuite **sale** record and writes its lines into `Forecast.Sales` (the Forecast2 revenue table). | _underscore/Component/Forecast/SaleImport/SaleImport.php, _underscore/Component/Forecast/Db/Db.php, _underscore/Component/Api/Netsuite/Netsuite.php, worker2/Worker/Netsuite/Invoice.php, worker2/Worker/Netsuite/CashSale.php, worker2/Worker/Netsuite/CreditMemo.php, worker2/Worker/Netsuite/CashRefund.php, worker2/Worker/Netsuite/JournalEntry.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Worker/Netsuite/SalesOrder.php, dbchanges2/Forecast/2026-06-26a - Add journalEntry to Sales transaction type enum.sql, test/@dave/test_invoice_lifecycle.php, test/@dave/test_je_lifecycle.php, test/@dave/test_creditmemo_lifecycle.php, test/@dave/test_cashsale_lifecycle.php, test/@dave/test_cashrefund_lifecycle.php, test/@dave/test_fetchrecord_routes.php, test/@dave/verify_je_classification.php, test/@dave/probe_je_accounts.php, test/@dave/probe_je_shape.php, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
|
|
12
13
|
| [Item-Fulfillment Stage Lifecycle (picked/packed/shipped) & Order Status](features/item-fulfillment-stage-lifecycle-and-order-status.md) | Every ItemFulfillment (IF) now carries an explicit **stage** — picked → packed → shipped — resolved through `ItemFulfillmentStages → ItemFulfillmentStatuses` (m | _underscore/Model/Client/SalesOrder.php, _underscore/Model/Quad/SalesOrder.php, _underscore/Model/Compass/SalesOrder.php, _underscore/Model/Compass/SalesOrderStatus.php, _underscore/Model/Client/SalesOrderItem.php, _underscore/Model/Client/Item.php, _underscore/Model/Client/PurchaseOrderItem.php, library/app/api/toga2.php, dbchanges2/Client/2026-06-30a - BackfillNullStageItemFulfillmentsToShipped.sql, dbchanges2/Client/2026-06-30b - SalesOrderStatusesPickedPacked.sql, dbchanges2/Client/2026-06-30c - ItemFulfillmentStageIdNotNull.sql, dbchanges2/Client_CompassCanada/2026-06-30a - ItemFulfillmentLifecycleAndShippedBackfill.sql |
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Error Reporting — Issue/Event Aggregation (exceptionHandler)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: _underscore
|
|
5
|
+
project: _Underscore
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-06
|
|
10
|
+
owners: ["dfranks"]
|
|
11
|
+
files:
|
|
12
|
+
- _underscore/Error.php
|
|
13
|
+
- _underscore/Model/Core/Logs/Issue.php
|
|
14
|
+
- _underscore/Model/Core/Logs/Event.php
|
|
15
|
+
- dbchanges2/Logs/2026-07-06 - Issue and Event tables.sql
|
|
16
|
+
related:
|
|
17
|
+
- ../architecture.md
|
|
18
|
+
- ./per-client-database-connections.md
|
|
19
|
+
- ../../dbchanges2/architecture.md
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Summary
|
|
23
|
+
|
|
24
|
+
`_underscore`'s global exception handler persists every uncaught exception into a
|
|
25
|
+
two-table **Issue / Event** model in the **shared Core Logs DB** (`_underscore::DB_LOGS`,
|
|
26
|
+
models under `Model/Core/Logs/`) — **not** the per-client Logs DB. An **Issue** is the
|
|
27
|
+
deduplicated record for a distinct error (keyed by a hash of the backtrace) carrying an
|
|
28
|
+
aggregate `occurrences` counter; an **Event** is one row per individual occurrence with
|
|
29
|
+
its own stack trace and context. This is platform-wide error-reporting infrastructure,
|
|
30
|
+
not client-specific.
|
|
31
|
+
|
|
32
|
+
## Key files / entry points
|
|
33
|
+
|
|
34
|
+
- **`_underscore/Error.php`** — `exceptionHandler()` (registered via
|
|
35
|
+
`set_exception_handler`). Computes `$issueHash = md5(serialize($backtrace))`, then, only
|
|
36
|
+
when the Core-Logs register is present (`isset(\_Database::$_registers[_underscore::DB_LOGS])`),
|
|
37
|
+
upserts an `_Model_Core_Logs_Issue` and inserts an `_Model_Core_Logs_Event`, committing
|
|
38
|
+
the `DB_LOGS` transaction.
|
|
39
|
+
- **`_underscore/Model/Core/Logs/Issue.php`** — `const DATABASE = _underscore::DB_LOGS;
|
|
40
|
+
const TABLE = 'Issues';`. Fields: `id, uuid, dtCreated, dtLastOccurred, urgency, subject,
|
|
41
|
+
description, clickupIdentifier, hash, errorMessage, occurrences, trace`.
|
|
42
|
+
- **`_underscore/Model/Core/Logs/Event.php`** — `const DATABASE = _underscore::DB_LOGS;
|
|
43
|
+
const TABLE = 'Events';`. Fields: `id, uuid, issueId (FK → Issue), errorMessage, trace,
|
|
44
|
+
context, dtCreated`.
|
|
45
|
+
|
|
46
|
+
## How it works
|
|
47
|
+
|
|
48
|
+
1. On an uncaught exception, hash the backtrace to identify the distinct error.
|
|
49
|
+
2. Load the existing `Issue` by `hash`. On a miss, create it with `errorMessage`, `trace`,
|
|
50
|
+
`urgency = 'LOW'`, and `occurrences = 0`.
|
|
51
|
+
3. **Increment `occurrences`** (`($issue->occurrences ?? 0) + 1`) and set
|
|
52
|
+
`dtLastOccurred`, then save — so the counter advances on every occurrence, hit or new.
|
|
53
|
+
4. Insert one `Event` for this occurrence: `issueId`, `errorMessage`, per-occurrence
|
|
54
|
+
`trace`, and `context` (`print_r($GLOBALS, true)`).
|
|
55
|
+
5. Commit the `DB_LOGS` transaction; surface `Error {issueId}, Event {eventId}` in the
|
|
56
|
+
debug body.
|
|
57
|
+
|
|
58
|
+
The `Issues`/`Events` tables live in the shared **Core Logs** DB, provisioned from
|
|
59
|
+
`dbchanges2/Logs/` (the `Logs/` folder targets the framework-level, non-tenant `Logs` DB
|
|
60
|
+
with unqualified table names — see the dbchanges2 architecture folder→DB mapping). Contrast
|
|
61
|
+
with per-client API/error logging in `Logs_<Tenant>` via `Model/Client/Logs/*`
|
|
62
|
+
(`DB_CLIENT_LOGS`, `dbchanges2/Logs_Client/`).
|
|
63
|
+
|
|
64
|
+
## Data model
|
|
65
|
+
|
|
66
|
+
- **Issue** = one row per distinct error (dedup key = `hash`); `occurrences` is the rolling
|
|
67
|
+
count, `dtLastOccurred` the most-recent hit, `urgency` a list field, `clickupIdentifier`
|
|
68
|
+
links to an escalation ticket. There is **no `type` column** — it was dropped.
|
|
69
|
+
- **Event** = one row per occurrence, FK `issueId` → Issue, carrying the occurrence's own
|
|
70
|
+
`trace` and `context`.
|
|
71
|
+
|
|
72
|
+
## Gotchas / known issues
|
|
73
|
+
|
|
74
|
+
- **`occurrences` must be incremented, not just seeded.** It was previously set to `0` only
|
|
75
|
+
at creation and never advanced, so the aggregate counter never moved. Fixed 2026-07-06 to
|
|
76
|
+
increment on every occurrence.
|
|
77
|
+
- **`Event.trace` must be set per occurrence.** The schema has a per-Event `trace` column
|
|
78
|
+
that was never populated; now recorded on every Event.
|
|
79
|
+
- **Persistence is conditional on the Core-Logs register.** If
|
|
80
|
+
`\_Database::$_registers[_underscore::DB_LOGS]` isn't set, the whole Issue/Event block is
|
|
81
|
+
skipped silently — no error row is written.
|
|
82
|
+
- **Shared-schema migration collision.** Because `Issues`/`Events` live in the *shared* Core
|
|
83
|
+
Logs DB, any two dbchanges2 `Logs/` migrations that both `CREATE TABLE Issues`/`Events`
|
|
84
|
+
will collide — the second fails with "table already exists". Two separate error-reporting
|
|
85
|
+
workstreams introduced these same tables (one adding the escalation cron + an
|
|
86
|
+
`IssueEmailAddresses` table, one adding this error-class persistence). Only **one**
|
|
87
|
+
migration may create the shared tables; reconcile overlapping `Logs/` create-scripts at
|
|
88
|
+
merge so the tables are created exactly once and later migrations only `ALTER`.
|
|
89
|
+
|
|
90
|
+
## Change history
|
|
91
|
+
|
|
92
|
+
- 2026-07-06 — Moved Issue/Event tables and exceptionHandler persistence from the per-client
|
|
93
|
+
Logs DB to the shared Core Logs DB and dropped the `type` column; fixed `occurrences` to
|
|
94
|
+
increment each occurrence and set `Event.trace` per occurrence; removed dead commented-out
|
|
95
|
+
per-client-logs persistence block. (dfranks)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -16,7 +16,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
16
16
|
|
|
17
17
|
## 2.0 framework
|
|
18
18
|
|
|
19
|
-
- **_underscore** (_Underscore) _(framework core)_ —
|
|
19
|
+
- **_underscore** (_Underscore) _(framework core)_ — 22 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
20
20
|
- **worker2** (Worker) — 25 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
21
21
|
- **api2** (API) — 7 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
22
22
|
- **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
package/package.json
CHANGED