toga-ai 1.0.71 → 1.0.73

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.
@@ -1,8 +1,11 @@
1
- # togadesk (TogaDesk) — 1.0 knowledge
1
+ # togadesk (TOGa Desk) — 1.0 knowledge
2
2
 
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
- | [TogaDesk Architecture](architecture.md) | TogaDesk is the staff-facing support desk (analysts work at `/desk/`). | desk/includes/classes/class.ticket.php, desk/includes/controllers/actions.php, desk/api/resources/tickets.php, crons/tickets.php |
5
+ | [TOGa Desk Architecture](architecture.md) | TOGa Desk is the staff-facing support desk **and field-service platform** (analysts work at `/desk/`). | desk/index.php, desk/includes/loader.php, desk/config.php, desk/includes/controllers/actions.php, desk/includes/controllers/data.php, desk/includes/controllers/modals.php, desk/includes/classes/class.app.php, desk/includes/classes/class.ticket.php, desk/api/index.php, desk/api/resources/tickets.php, crons/tickets.php |
6
+ | [Email-to-Ticket Intake (crons/tickets.php)](features/email-to-ticket-intake.md) | TOGa Desk ingests support email into tickets through a cron-driven IMAP poller (`crons/tickets.php`) plus a postfix pipe variant (`crons/pipe.php`). | crons/tickets.php, crons/tickets_prod.php, crons/pipe.php, desk/includes/classes/class.ticket.php |
7
+ | [Field-Service Dispatch (central / repair orders)](features/field-service-dispatch.md) | The **central** subsystem is TOGa Desk's field-service dispatch domain: repair-order lifecycle, technician scheduling, onsite vs depot service, parts, and shipm | desk/includes/controllers/actions/central/, desk/includes/classes/class.repair.php, desk/includes/classes/class.repairhistory.php, desk/_/browser/datatable/central.php, desk/template/pages/central.php, desk/template/pages/central/view.php |
8
+ | [REST API (RPC-over-POST) & API-Key Auth](features/rest-api.md) | TOGa Desk exposes a programmatic API at `desk/api/`. | desk/api/index.php, desk/api/resources/tickets.php, desk/api/resources/assets.php, desk/api/resources/authenticate.php, desk/includes/classes/class.apikey.php, desk/includes/functions.php |
6
9
  | [SMB Contract Editing & the clientMspId Corruption Trap](features/smb-contract-editing.md) | The SMB contracts page (`/desk/?route=toga/smbcontracts&togaClientId=<id>`) edits `TOGA_*.SMBContracts` rows via a modal. | desk/template/modals/toga/smbcontracts/smbContract.php, desk/includes/controllers/modals/toga/smbcontracts/smbContract.php, desk/includes/controllers/actions/toga/smbcontracts/smbContract.php, desk/includes/controllers/actions/toga/smbcontracts/edit.php |
7
- | [Ticket Lifecycle (class.ticket.php)](features/ticket-lifecycle.md) | All TogaDesk ticket creation and reply handling funnels through `Ticket` in `desk/includes/classes/class.ticket.php`. | desk/includes/classes/class.ticket.php, desk/includes/controllers/actions.php, desk/api/resources/tickets.php, crons/tickets.php, desk/includes/controllers/actions/tickets/merge.php |
8
- | [Standalone PHP Test Script Bootstrap (TogaDesk)](workflows/standalone-test-scripts.md) | How to write a standalone CLI PHP script that bootstraps the TogaDesk framework for read-only testing of desk classes (e.g. | |
10
+ | [Ticket Lifecycle (class.ticket.php)](features/ticket-lifecycle.md) | All TOGa Desk ticket creation and reply handling funnels through `Ticket` in `desk/includes/classes/class.ticket.php`. | desk/includes/classes/class.ticket.php, desk/includes/controllers/actions.php, desk/api/resources/tickets.php, crons/tickets.php, desk/includes/controllers/actions/tickets/merge.php |
11
+ | [Standalone PHP Test Script Bootstrap (TOGa Desk)](workflows/standalone-test-scripts.md) | How to write a standalone CLI PHP script that bootstraps the TOGa Desk framework for read-only testing of desk classes (e.g. | |
@@ -1,38 +1,137 @@
1
1
  ---
2
- title: TogaDesk Architecture
2
+ title: TOGa Desk Architecture
3
3
  framework: "1.0"
4
4
  repo: togadesk
5
- project: TogaDesk
5
+ project: TOGa Desk
6
6
  client: shared
7
7
  type: architecture
8
8
  status: active
9
- updated: 2026-06-12
10
- owners: ["mhammontree"]
9
+ updated: 2026-06-15
10
+ owners: ["mhammontree", "jcardinal"]
11
11
  files:
12
- - desk/includes/classes/class.ticket.php
12
+ - desk/index.php
13
+ - desk/includes/loader.php
14
+ - desk/config.php
13
15
  - desk/includes/controllers/actions.php
16
+ - desk/includes/controllers/data.php
17
+ - desk/includes/controllers/modals.php
18
+ - desk/includes/classes/class.app.php
19
+ - desk/includes/classes/class.ticket.php
20
+ - desk/api/index.php
14
21
  - desk/api/resources/tickets.php
15
22
  - crons/tickets.php
16
23
  related:
17
24
  - 1.0/apps/togaview/architecture.md
18
25
  - 1.0/apps/togadesk/features/ticket-lifecycle.md
19
26
  - 1.0/apps/togadesk/features/smb-contract-editing.md
27
+ - 1.0/apps/togadesk/features/rest-api.md
28
+ - 1.0/apps/togadesk/features/field-service-dispatch.md
29
+ - 1.0/apps/togadesk/features/email-to-ticket-intake.md
30
+ - 1.0/apps/togadesk/workflows/standalone-test-scripts.md
20
31
  ---
21
32
 
22
33
  ## Summary
23
- TogaDesk is the staff-facing support desk (analysts work at `/desk/`). It is a framework 1.0
24
- (`App_`) app bootstrapped from the `library` core (`_.php`). The on-disk repo root contains
25
- legacy top-level code; **the live application root is `desk/`** — e.g. the live
26
- `class.ticket.php` is `desk/includes/classes/class.ticket.php`, while the root
27
- `includes/classes/class.ticket.php` is an ancient variant that must never be extended.
34
+ TOGa Desk is the staff-facing support desk **and field-service platform** (analysts work at
35
+ `/desk/`). It is a framework 1.0 (`App_`) app bootstrapped from the `library` core (`_.php`).
36
+ Beyond ticketing it covers asset/license/inventory management, repair-order dispatch (depot +
37
+ onsite technicians), a knowledge base, host/service monitoring, RBAC, a REST-ish API, and
38
+ IMAP email intake. The on-disk repo root contains legacy top-level code; **the live
39
+ application root is `desk/`** — e.g. the live `class.ticket.php` is
40
+ `desk/includes/classes/class.ticket.php`, while the root `includes/classes/class.ticket.php`
41
+ is an ancient variant that must never be extended.
42
+
43
+ Primary database `db_togadesk` (schema `TOGaDeskSupport`); also `db_common` and per-client
44
+ databases. TOGa Desk shares `TOGaDeskSupport` with **TOGa View** (the client-facing portal,
45
+ sibling repo `togaview`).
46
+
47
+ ## Entry points & request pipeline
48
+ - **Web:** `desk/index.php` → `desk/includes/loader.php`. The repo-root `index.php` / `ajax.php`
49
+ are thin wrappers around the `desk/` versions.
50
+ - **Bootstrap order** (`loader.php`): local `functions.php` → `config.php` → register the two
51
+ custom autoloaders `vendorClassAutoload` (`desk/vendor/classes/`) and `appClassAutoload`
52
+ (`desk/includes/classes/`) → Composer autoload → `require _.php` + `App_Framework_TOGaDesk::initialize()`
53
+ (starts session with `HttpOnly`/`Secure`, inits Sentry, XSS-protects `$_REQUEST`/`$_GET`/`$_POST`)
54
+ → `App_Page::validateCrossSiteRequestForgery()` → Medoo `$database` instantiated from config →
55
+ resolve logged-in user `$liu` via `people.sessionid` → unserialize the user's role `perms`.
56
+ - **`$route`** comes from `$_GET['route']` (defaults to `dashboard`) and is sanitized against
57
+ path traversal.
58
+ - **Sequential controller pipeline**, each stage gated on a query param:
59
+ | Stage | Trigger | File | Role |
60
+ |---|---|---|---|
61
+ | general | always | `controllers/general.php` | signin / signout / password reset |
62
+ | modals | `?modal=` | `controllers/modals.php` | assemble modal form data → `template/modals/<modal>.php` |
63
+ | quickactions | `?qa=` | `controllers/quickactions.php` | small AJAX/JSON/file-stream ops, then `exit` |
64
+ | actions | `?action=` | `controllers/actions.php` | CRUD; `isAuthorized()` → domain class → status code → `reroute()` |
65
+ | data | always | `controllers/data.php` | populate page context; route-specific `controllers/data/<route>.php` |
66
+ - **Render** (when no modal/qa): `template/header.php` + `template/pages/<route>.php` + `template/footer.php`.
67
+ - Unmatched `?action=` values fall back to `controllers/actions/<action>.php` — so actions live
68
+ either inline in `actions.php` or as files under `controllers/actions/<domain>/`.
69
+
70
+ ## Server-rendered UI model
71
+ Classic **page ← data + modal + action** loop:
72
+ 1. A page (`template/pages/<route>.php`) instantiates a **DataTable**
73
+ (`desk/_/browser/datatable/<domain>.php`, extends `Browser_Datatable_TogaDesk`) that builds
74
+ the grid SQL/joins and renders row action buttons.
75
+ 2. A button calls `showM("?modal=<domain>/<action>&id=…")`, which XHR-loads the modal; its modal
76
+ controller (`controllers/modals/<domain>/<action>.php`) assembles dropdowns/dependent data and
77
+ the modal template renders a form with hidden `routeid` / `id` / `reroute` fields.
78
+ 3. The form POSTs `?action=…` → a domain class static method → a numeric **status code** →
79
+ `reroute()` redirects back to the originating page. Status codes map to user-facing messages
80
+ via the `statuscodes` table (`10`=add, `20`=edit, `30`=delete, etc.).
81
+
82
+ ## Domain class layer
83
+ - ~41 thin classes in `desk/includes/classes/class.<name>.php` with **plain names** (`Ticket`,
84
+ `Asset`, `Repair`, `Client`…), all extending an **empty `App` base** (`class.app.php`) —
85
+ behavior lives in **static facade methods** (`Ticket::add($data)`), not inheritance or DI.
86
+ - Persistence pairs these with **`App_Model_TogaDesk_*` ORM models** (defined in `library`),
87
+ instantiated by id (`new App_Model_TogaDesk_Ticket($id)`).
88
+ - **DB access is mixed:** the Medoo global `$database` (`->select/insert/get`) for simple work,
89
+ and `App_Database::query()` / `App_Database::fetchRow()` for complex SQL. Link names:
90
+ `db_togadesk` (main), `db_common` (shared: States, carriers, ASN), and per-client databases via
91
+ `App_Model_Client::changeDbLinkToClientDatabase()`.
92
+ - **Functional groups:**
93
+ - **Ticketing/support** — `Ticket` (the ~2,200-line core: creation, the `addReply()` status
94
+ switch, `deriveCustomerId()`, Talos AI, policy tasks), `TicketHistory`, `Comment`,
95
+ `Resolution`, `Preply`.
96
+ - **Asset & inventory** — `Asset`, `License`, `Category`, `Attribute`, `CustomField`,
97
+ `Certificate`, `Credential` (encrypted).
98
+ - **Repair & dispatch** — `Repair` (depot + onsite work orders), `RepairHistory`.
99
+ - **Client/contact** — `Client`, `ClientDepartment`, `ClientPortal`, `Contact`.
100
+ - **Users & RBAC** — `User`/`Staff` (both in `people`, by `type`), `Profile`, `Role`,
101
+ `ApiKey`, `Policy`. Permissions are stored **serialized** in `roles.perms`.
102
+ - **Knowledge base** — `Kb` (articles/docs/FAQs; uploads to S3 `asifiles`).
103
+ - **Projects** — `Project`, `Issue`, `Milestone`. **Monitoring** — `Monitoring`, `Diagnostic`.
104
+ - **Comms** — `Notification`, `ChatWidget`, `Timelog`. **Data** — `Import`, `File`.
105
+
106
+ ## REST-ish API
107
+ `desk/api/index.php` is an **RPC-over-POST** dispatcher (NOT verb/URL REST): params `key`,
108
+ `method` (get/add/edit/delete/attach/detach), `resource`, `filters`, `data`, `id` route to one
109
+ of ~37 handlers in `desk/api/resources/<resource>.php`. API-key auth resolves a role → perms;
110
+ each op calls `isAuthorizedApi(...)`. Responses use the `{status, status_message, result}`
111
+ envelope. See the **REST API** feature doc.
112
+
113
+ ## Background jobs & integrations
114
+ Crons live at the **repo root** (`crons/`), bootstrapped via the library framework +
115
+ `App_Framework::cronInitialization()`, with per-environment wrappers (`*_prod.php` etc.):
116
+ - `tickets.php` — IMAP **email→ticket** intake from O365 (OAuth2 in prod). See the
117
+ **email-to-ticket intake** feature doc.
118
+ - `monitoring.php` — `Monitoring::runAll()` host/port checks + up/down alerts.
119
+ - `pipe.php` — postfix stdin push variant; `general.php` — stub.
120
+
121
+ External integrations: **Talos/TogaIQ** AI (ticket summaries / improved replies), **Syncro**
122
+ PSA status push (when `tickets.referenceId='SYNCRO'`), **S3** (`asifiles`,
123
+ `App_Cloud::copyFileToS3()`), **Sentry** (client-side JS), **PHPMailer** SMTP, and **NetSuite**
124
+ customer id captured on SMB contracts. The `controllers/actions/toga/` and
125
+ `controllers/data/toga/` subsystems bridge to the **TOGA** database (inventory items/levels/orders,
126
+ SMB contracts, customer renewals).
28
127
 
29
128
  ## System topology
30
129
  | System | Serves | DB connections |
31
130
  |---|---|---|
32
- | TogaDesk (`desk/`) | Staff/analysts | `db_togadesk` (TOGaDeskSupport), `db_toga` (TOGA), `db_client_aig`, per-client by name |
33
- | TogaView (sibling repo) | Clients at `<client>.togaview.com` | same cluster, same link names |
131
+ | TOGa Desk (`desk/`) | Staff/analysts | `db_togadesk` (TOGaDeskSupport), `db_toga` (TOGA), `db_client_aig`, per-client by name |
132
+ | TOGa View (sibling repo) | Clients at `<client>.togaview.com` | same cluster, same link names |
34
133
 
35
- - **TogaDesk and TogaView share the `TOGaDeskSupport` database** — tickets, clients,
134
+ - **TOGa Desk and TOGa View share the `TOGaDeskSupport` database** — tickets, clients,
36
135
  contacts, departments, ClientMsp all live there. A change to ticket data semantics affects
37
136
  both apps.
38
137
  - All legacy databases (TOGaDeskSupport, TOGA, TOGA_*, RetailServices) are on the same MySQL
@@ -78,10 +177,30 @@ Invariants:
78
177
  (`crons/tickets.php` IMAP poll → `emailToTicket()`), merge. See ticket-lifecycle feature doc.
79
178
  - SMB contract administration edits TOGA_* `SMBContracts` from `/desk/?route=toga/smbcontracts`.
80
179
  See smb-contract-editing feature doc.
180
+ - Field-service dispatch (repair orders, technician scheduling, onsite/depot, tracking) runs
181
+ through `controllers/actions/central/` + `class.repair.php`. See field-service-dispatch doc.
81
182
 
82
183
  ## Key decisions
83
184
  - `Ticket::deriveCustomerId()` (June 2026) resolves `tickets.customerid` at creation so
84
- staff/API/email tickets are visible in the TogaView MSP portal; it returns null rather than
185
+ staff/API/email tickets are visible in the TOGa View MSP portal; it returns null rather than
85
186
  guess when ambiguous, and swallows (logs) exceptions so email intake is never blocked.
86
187
  - DB access for tooling: legacy cluster via the TOGa Database Integration MCP, environment
87
188
  `legacy`; schemas `TOGaDeskSupport`, `TOGA`, `TOGA_True`, etc.
189
+
190
+ ## Gotchas / known issues
191
+ - `ENVIRONMENT` defaults to `worker` when unset → CLI scripts read the wrong config file.
192
+ - Heavy **hardcoded client-ID branching** in `central` dispatch and `tickets/add` (Walmart,
193
+ WJE, Compass/ODP, AIG, RUMCSI) — verify the client arm before changing shared logic.
194
+ - **Two `class.ticket.php` files** — only `desk/includes/classes/` is live; the root variant is
195
+ dead (random ticket numbers, stale schema).
196
+ - The API has **no pagination** — `get` returns full result sets; Medoo `filters` pass straight
197
+ to the WHERE clause.
198
+ - The prod O365 OAuth secret used by `crons/tickets_prod.php` has a **hard expiry (~2026-12-20)**.
199
+
200
+ ## Related docs
201
+ - [TOGa View architecture](../togaview/architecture.md)
202
+ - [Ticket lifecycle](features/ticket-lifecycle.md)
203
+ - [REST API](features/rest-api.md)
204
+ - [Field-service dispatch (central)](features/field-service-dispatch.md)
205
+ - [Email-to-ticket intake](features/email-to-ticket-intake.md)
206
+ - [SMB contract editing](features/smb-contract-editing.md)
@@ -0,0 +1,66 @@
1
+ ---
2
+ title: Email-to-Ticket Intake (crons/tickets.php)
3
+ framework: "1.0"
4
+ repo: togadesk
5
+ project: TOGa Desk
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-15
10
+ owners: ["jcardinal"]
11
+ files:
12
+ - crons/tickets.php
13
+ - crons/tickets_prod.php
14
+ - crons/pipe.php
15
+ - desk/includes/classes/class.ticket.php
16
+ related:
17
+ - 1.0/apps/togadesk/architecture.md
18
+ - 1.0/apps/togadesk/features/ticket-lifecycle.md
19
+ ---
20
+
21
+ ## Summary
22
+ TOGa Desk ingests support email into tickets through a cron-driven IMAP poller
23
+ (`crons/tickets.php`) plus a postfix pipe variant (`crons/pipe.php`). Both funnel into
24
+ `Ticket::emailToTicket()`, which threads replies onto existing tickets or creates new ones.
25
+
26
+ ## Key files / entry points
27
+ - `crons/tickets.php` — the IMAP poll loop. Per-environment wrappers (`tickets_prod.php`,
28
+ `tickets_alpha.php`, `tickets_beta.php`) set `$env`/`$processName` and `require` it; the base
29
+ script refuses to run unwrapped.
30
+ - `crons/pipe.php` — reads a single message from stdin (postfix pipe delivery) and calls the
31
+ same intake; minimal autoload bootstrap (no full framework include path).
32
+ - `Ticket::emailToTicket()` in `desk/includes/classes/class.ticket.php` — the parser/router.
33
+
34
+ ## How it works
35
+ 1. **Bootstrap:** cron wrappers load the `library` framework + `App_Framework::cronInitialization()`,
36
+ set `$env`, and register the IMAP (`Imap2`) helper. `tickets_prod.php` additionally performs
37
+ **OAuth2/MSAL** token acquisition for the O365 mailbox.
38
+ 2. **Poll:** connect to `outlook.office365.com:993` INBOX and loop for ~22 minutes (header
39
+ comment suggests running every ~30 min), processing each message then deleting it.
40
+ 3. **Parse & route (`emailToTicket()`):** extract from/to/cc/subject/body (with encoding fixes);
41
+ match an existing ticket via `In-Reply-To` + `Message-ID` headers and **plus-addressing**
42
+ (`support+<ticketnum>@…`). Matched ⇒ `addReply()`; otherwise ⇒ `add()`.
43
+ 4. **Side effects:** auto-create a user when the sender domain maps to an existing client; assign
44
+ to a department via the department email lookup; save attachments locally then push to **S3**
45
+ (`App_Cloud::copyFileToS3()`, bucket `asifiles`); optionally generate Talos/TogaIQ summaries.
46
+ 5. **Post-pass:** `Ticket::processRules()` (escalation) and `Ticket::autoClose()` (SLA auto-close)
47
+ run after the mailbox sweep.
48
+
49
+ ## Data model
50
+ Writes `tickets`, `tickets_replies`, attachment `files`; reads config from the `config` table
51
+ (`tickets_defaultdepartment`, `tickets_defaultclient`, `auto_close_tickets`,
52
+ `email_from_address`, `timezone`). Department routing keys on `tickets_departments.email`.
53
+
54
+ ## Gotchas / known issues
55
+ - **`$processName` guard:** `tickets.php` throws if executed directly instead of via an env
56
+ wrapper — and `ENVIRONMENT`/`$env` must be set or it loads the wrong config.
57
+ - **OAuth2 expiry:** the prod O365 app secret in `tickets_prod.php` has a hard expiry
58
+ (~2026-12-20); rotate before then or intake silently stops.
59
+ - **Loop prevention** only excludes FROM==department-TO addresses; CC/reply-all across multiple
60
+ department addresses can still loop.
61
+ - **Auto-reply filters** are partly hardcoded (e.g. ignore `TechHub@compass-usa.com`
62
+ "Automatic reply:" messages).
63
+ - Attachments are deleted locally immediately after the S3 upload with **no retry** if S3 fails.
64
+
65
+ ## Change history
66
+ - 2026-06-15 — documented from a source read of `crons/tickets*.php` and `emailToTicket()` (jcardinal)
@@ -0,0 +1,76 @@
1
+ ---
2
+ title: Field-Service Dispatch (central / repair orders)
3
+ framework: "1.0"
4
+ repo: togadesk
5
+ project: TOGa Desk
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-15
10
+ owners: ["jcardinal"]
11
+ files:
12
+ - desk/includes/controllers/actions/central/
13
+ - desk/includes/classes/class.repair.php
14
+ - desk/includes/classes/class.repairhistory.php
15
+ - desk/_/browser/datatable/central.php
16
+ - desk/template/pages/central.php
17
+ - desk/template/pages/central/view.php
18
+ related:
19
+ - 1.0/apps/togadesk/architecture.md
20
+ - 1.0/apps/togadesk/features/ticket-lifecycle.md
21
+ ---
22
+
23
+ ## Summary
24
+ The **central** subsystem is TOGa Desk's field-service dispatch domain: repair-order lifecycle,
25
+ technician scheduling, onsite vs depot service, parts, and shipment tracking. It is the largest
26
+ action domain (~23 files under `controllers/actions/central/`) and is heavily client-aware.
27
+
28
+ ## Key files / entry points
29
+ - Action controllers: `desk/includes/controllers/actions/central/*.php` — repair-order create
30
+ /clone, status transitions (`awaitingDispatch.php`, `awaitingDiagnostic.php`,
31
+ `installStarted.php`, `reviewInstallation.php`, `reworkAwaitingScheduling.php`), technician
32
+ assignment & scheduling (`assignTechnicians.php`, `scheduleDispatch.php`, `setSchedule.php`),
33
+ onsite state (`technicianEnRoute.php`, `technicianOnsite.php`), parts/tracking
34
+ (`part.php`, `addTracking.php`, `voidshipment.php`), and bulk ops
35
+ (`bulkCloseInstallTickets.php`, `bulkEditOnsiteServiceTickets.php`).
36
+ - Domain class: `desk/includes/classes/class.repair.php` (`Repair`) with the
37
+ `App_Model_TogaDesk_RepairOrder` / `RepairOrderCustomer` / `RepairOrderAddress` models.
38
+ - Grid: `desk/_/browser/datatable/central.php` — joins `repair_orders`
39
+ + `repair_order_customers` + `ServiceRequests` + departments, renders status badges and the
40
+ per-row action buttons that open the central modals.
41
+ - Pages: `desk/template/pages/central.php` (list) and `central/view.php` (detail).
42
+
43
+ ## How it works
44
+ A page renders the central DataTable; row buttons `showM("?modal=central/<action>&id=…")` open a
45
+ modal whose controller (`controllers/modals/central/<action>.php`) assembles technicians,
46
+ contracts, vendor POs, schedule data, etc. The modal form POSTs `?action=<central action>`,
47
+ which resolves to the inline `actions.php` switch or to
48
+ `controllers/actions/central/<action>.php`, mutates `repair_orders` / `repair_order_technicians`
49
+ and calls `Repair`/`RepairOrder::updateStatus()` to recompute state, then `reroute()`s back to
50
+ the originating page. Status changes are audited through `RepairHistory` (`repair_history`).
51
+
52
+ **State model (typical):** awaiting diagnostic → awaiting dispatch → scheduled → en-route →
53
+ onsite/install started → review → closed; depot repairs add parts/tracking steps. Bulk actions
54
+ exist for closing installs and editing onsite tickets.
55
+
56
+ ## Data model
57
+ `repair_orders`, `repair_order_customers`, `repair_order_addresses`, `repair_order_technicians`,
58
+ `repair_history` (in `db_togadesk`), joined to TOGA `ServiceRequests` for managed-service-order
59
+ context. One repair order generally maps to one serviced unit.
60
+
61
+ ## Client variations
62
+ Dispatch flows branch on hardcoded client ids (e.g. Walmart / Walmart Plus, WJE, Office
63
+ Depot / Compass, AIG, RUMCSI) for department mapping, scheduling rules, and notification copy —
64
+ always confirm the client arm before changing shared central logic.
65
+
66
+ ## Gotchas / known issues
67
+ - Actions are split across two locations: the inline `actions.php` switch **and**
68
+ `controllers/actions/central/*.php` (the default fallback). Check both when tracing an action.
69
+ - Status recomputation is centralized in `updateStatus()` — write paths that bypass it can leave
70
+ an order in an inconsistent state.
71
+ - Cross-database context: some central data controllers switch the DB link to a client database
72
+ (`App_Model_Client::changeDbLinkToClientDatabase()`).
73
+
74
+ ## Change history
75
+ - 2026-06-15 — documented from a source read of `controllers/actions/central/` and
76
+ `class.repair.php` (jcardinal)
@@ -0,0 +1,87 @@
1
+ ---
2
+ title: REST API (RPC-over-POST) & API-Key Auth
3
+ framework: "1.0"
4
+ repo: togadesk
5
+ project: TOGa Desk
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-15
10
+ owners: ["jcardinal"]
11
+ files:
12
+ - desk/api/index.php
13
+ - desk/api/resources/tickets.php
14
+ - desk/api/resources/assets.php
15
+ - desk/api/resources/authenticate.php
16
+ - desk/includes/classes/class.apikey.php
17
+ - desk/includes/functions.php
18
+ related:
19
+ - 1.0/apps/togadesk/architecture.md
20
+ - 1.0/apps/togadesk/features/ticket-lifecycle.md
21
+ ---
22
+
23
+ ## Summary
24
+ TOGa Desk exposes a programmatic API at `desk/api/`. Despite the name it is **not** verb/URL
25
+ REST — it is an **RPC-over-POST** dispatcher: every request is a POST to `desk/api/index.php`
26
+ carrying a `resource` and a `method` in the body. ~37 resource handlers under
27
+ `desk/api/resources/` delegate to the same domain classes the web UI uses.
28
+
29
+ ## Key files / entry points
30
+ - `desk/api/index.php` — the single entry point: authenticates, validates the request shape,
31
+ and `include`s the matching `resources/<resource>.php` (handlers run in index.php scope).
32
+ - `desk/api/resources/*.php` — one file per resource (`tickets`, `ticket_replies`,
33
+ `ticket_departments`, `assets`, `asset_categories`, `clients`, `contacts`, `credentials`,
34
+ `licenses`, `users`, `staff`, `roles`, `kb_articles`, `monitoring_hosts`,
35
+ `monitoring_checks`, `time_log`, `files`, `qrcodes`, lookup/reference resources, etc.).
36
+ - `desk/includes/classes/class.apikey.php` — API key generation/storage.
37
+ - `isAuthorizedApi()` and `signInApi()` in `desk/includes/functions.php`.
38
+
39
+ ## How it works
40
+ **Request (POST to `desk/api/index.php`):**
41
+ - `key` — the API secret (required).
42
+ - `method` — one of `get | add | edit | delete | attach | detach`.
43
+ - `resource` — e.g. `tickets`, `assets`, `users`.
44
+ - `filters` — array, for `get` (passed to Medoo as the WHERE clause).
45
+ - `data` — array, for `add`/`edit`/`attach`/`detach`.
46
+ - `id` — string, for `delete`/`detach`.
47
+
48
+ **Auth & authorization:**
49
+ 1. `index.php` looks up `api_keys` by `secretkey`; invalid ⇒ status `903`.
50
+ 2. The key's `roleid` loads `roles.perms` (unserialized PHP array).
51
+ 3. Each handler calls `isAuthorizedApi("<permission>")` (e.g. `viewTickets`, `addAsset`,
52
+ `manageSettings`); missing permission ⇒ status `905` + `exit`.
53
+ 4. `resources/authenticate.php` is a **separate** path: user/password login via `signInApi()`
54
+ (not API-key auth).
55
+
56
+ **Response envelope (always JSON):**
57
+ ```json
58
+ { "status": 1, "status_message": "Success!...", "result": [ ... ] }
59
+ ```
60
+ Handlers post-process rows: serialized fields (`tickets.ccs`, `roles.perms`) are unserialized
61
+ and encrypted fields (license `serial`) are decrypted before returning.
62
+
63
+ **Status codes:** `1` success, `2` generic error; `901` unknown, `902` key missing, `903` auth
64
+ failed, `904` resource not found, `905` not authorized, `906` method not found, `907` method not
65
+ allowed for resource, `908–912` malformed `filters`/`data`/`id`. Domain-class return codes
66
+ (`10` add / `20` edit / `30` delete) are mapped to `1`/`2` in the handler.
67
+
68
+ **Delegation:** handlers call the same static facades as the UI — e.g. `tickets.php` →
69
+ `Ticket::add()/edit()/delete()`, `ticket_replies.php` → `Ticket::addReply()` (add-only),
70
+ `files.php` → `File::upload($data, $_FILES)`, `qrcodes.php` uses `attach`/`detach`. For `get`,
71
+ many resources query Medoo directly (`$database->select(...)`).
72
+
73
+ ## Data model
74
+ Backed by the same `db_togadesk` (`TOGaDeskSupport`) tables as the UI, plus `api_keys`
75
+ (`name`, `secretkey`, `roleid`) and `roles` (`perms` serialized).
76
+
77
+ ## Gotchas / known issues
78
+ - **No pagination.** `get` returns the full result set; large tables (tickets, assets) can be
79
+ expensive. `filters` is passed straight into the Medoo WHERE clause.
80
+ - **RPC, not REST.** Clients send `method`/`resource` in the POST body — there are no per-verb
81
+ HTTP semantics and no `/api/tickets/123` URLs.
82
+ - **Per-resource permission strings** must exist in the API key's role `perms`, or every call
83
+ returns `905`.
84
+ - Minimal request validation in `index.php`; real validation lives in the domain classes.
85
+
86
+ ## Change history
87
+ - 2026-06-15 — documented from a full source read of `desk/api/` (jcardinal)
@@ -2,7 +2,7 @@
2
2
  title: SMB Contract Editing & the clientMspId Corruption Trap
3
3
  framework: "1.0"
4
4
  repo: togadesk
5
- project: TogaDesk
5
+ project: TOGa Desk
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
@@ -22,7 +22,7 @@ related:
22
22
  The SMB contracts page (`/desk/?route=toga/smbcontracts&togaClientId=<id>`) edits
23
23
  `TOGA_*.SMBContracts` rows via a modal. Because multiple TOGA clients can share one TOGA_*
24
24
  database, this page historically corrupted `clientMspId` on save — the root cause of MSP
25
- portal users seeing "You have no assigned clients" in TogaView. Fixed June 2026.
25
+ portal users seeing "You have no assigned clients" in TOGa View. Fixed June 2026.
26
26
 
27
27
  ## Key files / entry points
28
28
  - Modal template: `desk/template/modals/toga/smbcontracts/smbContract.php`
@@ -40,7 +40,7 @@ The bug pattern (now guarded against):
40
40
  2. The modal's MSP dropdown was scoped to client A's `ClientMsp` rows; the contract's real MSP
41
41
  wasn't an option; no option got `selected`; **the browser silently auto-selects the first
42
42
  option**; saving wrote the wrong `clientMspId`.
43
- 3. TogaView msp_dashboard's `SMBContracts WHERE clientMspId = <session clientMspId>` then
43
+ 3. TOGa View msp_dashboard's `SMBContracts WHERE clientMspId = <session clientMspId>` then
44
44
  matched nothing for the real MSP's users.
45
45
 
46
46
  Fixes in place (June 2026):
@@ -80,11 +80,11 @@ client 20 (`TOGA_True`), SMBContract 230 (Customer 932297).
80
80
  ## Gotchas / known issues
81
81
  - `dateContractEnd` in the past is display-only — it does not hide tickets in the MSP portal.
82
82
  - `ClientMsp.ticketDepartmentId` holds only ONE department even when the client has several —
83
- never build department logic on it (see TogaView msp-dashboard doc).
83
+ never build department logic on it (see TOGa View msp-dashboard doc).
84
84
 
85
85
  ## Change history
86
86
  - 2026-06-12 — documented; modal + action guards shipped to togadesk dev branch (mhammontree)
87
87
 
88
88
  ## Related docs
89
89
  - [Ticket lifecycle](ticket-lifecycle.md)
90
- - [TogaView MSP dashboard](../../togaview/features/msp-dashboard.md)
90
+ - [TOGa View MSP dashboard](../../togaview/features/msp-dashboard.md)
@@ -2,7 +2,7 @@
2
2
  title: Ticket Lifecycle (class.ticket.php)
3
3
  framework: "1.0"
4
4
  repo: togadesk
5
- project: TogaDesk
5
+ project: TOGa Desk
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
@@ -20,7 +20,7 @@ related:
20
20
  ---
21
21
 
22
22
  ## Summary
23
- All TogaDesk ticket creation and reply handling funnels through `Ticket` in
23
+ All TOGa Desk ticket creation and reply handling funnels through `Ticket` in
24
24
  `desk/includes/classes/class.ticket.php`. This doc covers the creation paths, the
25
25
  `deriveCustomerId()` resolution added June 2026, and the `addReply()` status-transition
26
26
  switch — including its known gaps.
@@ -99,4 +99,4 @@ backfill (e.g. craftex:
99
99
 
100
100
  ## Related docs
101
101
  - [SMB contract editing](smb-contract-editing.md)
102
- - [TogaView MSP dashboard](../../togaview/features/msp-dashboard.md)
102
+ - [TOGa View MSP dashboard](../../togaview/features/msp-dashboard.md)
@@ -1,8 +1,8 @@
1
1
  ---
2
- title: Standalone PHP Test Script Bootstrap (TogaDesk)
2
+ title: Standalone PHP Test Script Bootstrap (TOGa Desk)
3
3
  framework: "1.0"
4
4
  repo: togadesk
5
- project: TogaDesk
5
+ project: TOGa Desk
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
@@ -14,7 +14,7 @@ related:
14
14
  ---
15
15
 
16
16
  ## Summary
17
- How to write a standalone CLI PHP script that bootstraps the TogaDesk framework for
17
+ How to write a standalone CLI PHP script that bootstraps the TOGa Desk framework for
18
18
  read-only testing of desk classes (e.g. `Ticket::deriveCustomerId()`), without running the
19
19
  web stack. Example harness: `C:\WWW\test\@Mark\TOGaDeskSupport\test_derive_customer_id.php`
20
20
  (local path — adapt per machine).
@@ -1,8 +1,9 @@
1
- # togaview (TogaView) — 1.0 knowledge
1
+ # togaview (TOGa View) — 1.0 knowledge
2
2
 
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
- | [TogaView Architecture](architecture.md) | TogaView is the client-facing support portal, served per client at `<client>.togaview.com`. | index.php, _/app/framework.php, mvc/login/post.php |
6
- | [TogaView Login Flows & Session Variables](features/login-flows.md) | `mvc/login/post.php` tries login flows in order; the first match wins. | mvc/login/post.php, _/app/framework.php |
7
- | [MSP Dashboard & Ticket Visibility Rules](features/msp-dashboard.md) | Why tickets "disappear" in the TogaView client portal: different pages scope tickets **differently**, and the MSP pages depend on `tickets.customerid` and `SMBC | common/togaview/msp_dashboard.php, mvc/msp_client_dashboard, mvc/enterprise_dashboard, mvc/support/support.php |
5
+ | [TOGa View Architecture](architecture.md) | TOGa View is the client-facing support portal, served per client at `<client>.togaview.com`. | index.php, reset.php, _/app/framework.php, _/app/frameworkindex.php, _/browser/pagination.php, mvc/login/post.php, mvc/ticket/get.php |
6
+ | [TOGa View Login Flows & Session Variables](features/login-flows.md) | `mvc/login/post.php` tries login flows in order; the first match wins. | mvc/login/post.php, _/app/framework.php |
7
+ | [MSP Dashboard & Ticket Visibility Rules](features/msp-dashboard.md) | Why tickets "disappear" in the TOGa View client portal: different pages scope tickets **differently**, and the MSP pages depend on `tickets.customerid` and `SMB | common/togaview/msp_dashboard.php, mvc/msp_client_dashboard, mvc/enterprise_dashboard, mvc/support/support.php |
8
+ | [Retail Commerce Lifecycle (winback, purchase, claims, activation)](features/retail-commerce-lifecycle.md) | Beyond support, TOGa View hosts the **retail tech-support commerce flows**: subscription purchase/renewal (with payment), service activation, and insurance-styl | mvc/winback_landing/get.php, mvc/winback_payment/get.php, mvc/winback_payment/post.php, mvc/purchase/get.php, mvc/service_activation/get.php, mvc/claims/get.php, mvc/techsupport_app/get.php |
8
9
  | [Ticket Detail Page Security (common/togaview/ticket.php)](features/ticket-detail-page.md) | `common/togaview/ticket.php` is the ticket detail page for nearly ALL hosts — only towfoundation/newcenturyholdingsllc have their own variants; every other clie | common/togaview/ticket.php |
@@ -1,37 +1,60 @@
1
1
  ---
2
- title: TogaView Architecture
2
+ title: TOGa View Architecture
3
3
  framework: "1.0"
4
4
  repo: togaview
5
- project: TogaView
5
+ project: TOGa View
6
6
  client: shared
7
7
  type: architecture
8
8
  status: active
9
- updated: 2026-06-12
10
- owners: ["mhammontree"]
9
+ updated: 2026-06-15
10
+ owners: ["mhammontree", "jcardinal"]
11
11
  files:
12
12
  - index.php
13
+ - reset.php
13
14
  - _/app/framework.php
15
+ - _/app/frameworkindex.php
16
+ - _/browser/pagination.php
14
17
  - mvc/login/post.php
18
+ - mvc/ticket/get.php
15
19
  related:
16
20
  - 1.0/apps/togadesk/architecture.md
17
21
  - 1.0/apps/togaview/features/login-flows.md
18
22
  - 1.0/apps/togaview/features/msp-dashboard.md
19
23
  - 1.0/apps/togaview/features/ticket-detail-page.md
24
+ - 1.0/apps/togaview/features/retail-commerce-lifecycle.md
20
25
  ---
21
26
 
22
27
  ## Summary
23
- TogaView is the client-facing support portal, served per client at `<client>.togaview.com`.
28
+ TOGa View is the client-facing support portal, served per client at `<client>.togaview.com`.
24
29
  Framework 1.0 (`App_`) app bootstrapped from the `library` core (`_.php`). It shares the
25
- `TOGaDeskSupport` database with TogaDesk (tickets, clients, contacts, departments, ClientMsp)
26
- — see the TogaDesk architecture doc for the cluster topology and the full MSP data model;
30
+ `TOGaDeskSupport` database with TOGa Desk (tickets, clients, contacts, departments, ClientMsp)
31
+ — see the TOGa Desk architecture doc for the cluster topology and the full MSP data model;
27
32
  those invariants apply here unchanged.
28
33
 
34
+ Architecturally it is a **multi-tenant, single-codebase MVC portal**: one deploy serves every
35
+ client, and `$_SERVER['HTTP_HOST']` selects a per-client *stylePath* (templates/branding) at
36
+ bootstrap. Pages are procedural `mvc/<page>/{get,post}.php` scripts; most clients render the
37
+ generic `common/togaview/*.php` templates, while a handful have full per-client overrides.
38
+
39
+ ## Entry points & bootstrap
40
+ - **Entry:** `index.php` → `require _.php` (autoloader from `library`) →
41
+ `App_Framework_TogaView::initialize()` → `renderIndex()` (in `_/app/framework.php`).
42
+ `reset.php` is a small session-clear utility.
43
+ - **`initialize()`** (extends `App_Framework`): resolves `ENVIRONMENT` (defaults `worker`),
44
+ loads `config.<env>.ini` via `App_Config`, registers DB links (`db_togadesk` →
45
+ `[database_togadesk]`, plus `db_toga`, `db_retail`, `db_log`, per-client), XSS-protects
46
+ superglobals, starts the session (`HttpOnly`/`Secure`), inits Sentry, and registers the
47
+ NetSuite toolkit + SAML.
48
+ - **`frameworkindex.php`** is the page-shell renderer (CSS/JS registration, page title).
49
+ **`framework_DOA.php`** is a near-duplicate variant of `framework.php`.
50
+
29
51
  ## Page routing (stylePath model)
30
52
  `index.php → App_Framework_TogaView::renderIndex()` (in `_/app/framework.php`):
31
53
 
32
54
  1. A giant `switch ($_SERVER['HTTP_HOST'])` sets `$_SESSION['stylePath']` (and sometimes
33
55
  `$_SESSION['host']`). Client subdomains are **hardcoded** — a new client subdomain must be
34
56
  added to this switch (e.g. `craftex.togaview.com` → stylePath `togatechnology`, no `host`).
57
+ The default stylePath is `true`.
35
58
  2. Page files: `mvc/<page>/get.php` typically resolves the template as:
36
59
  - host in `HOSTS_USING_TOGA_TECH_STYLE` (= `['towfoundation','newcenturyholdingsllc']`)
37
60
  → `common/<host>/<page>.php`;
@@ -44,6 +67,24 @@ those invariants apply here unchanged.
44
67
  **Implication:** a change to `common/togaview/<page>.php` affects nearly every client at
45
68
  once; a per-client behavior change belongs in a host/stylePath variant or an inline branch.
46
69
 
70
+ ## MVC request lifecycle
71
+ - `App_MVC::parseRoute()` (in `library`) turns the request URI into route parts and resolves
72
+ `mvc/<route…>/<method>.php`, where `<method>` is `get` or `post` (from the HTTP verb,
73
+ overridable via a `_method` POST param). It matches longest-to-shortest, so overlapping
74
+ paths can surprise — keep route folders distinct.
75
+ - **GET** (`get.php`) selects a template via the fallback chain above and passes data to it
76
+ (no central master-layout system — each `common/<…>/<page>.php` is self-contained).
77
+ - **POST** (`post.php`) reads input with `App_Page::getVar()/getVarIfSet()`, does the work,
78
+ then `header("Location: …"); exit;`. Login/auth, ticket creation, user management live here.
79
+ - AJAX requests dispatch through `App_Framework::ajaxHandler()`; `?fileId=` downloads route
80
+ through `App_MVC::loadFile()`. `mvc/_TEMPLATE/` is the scaffold for new pages.
81
+
82
+ ## Browser subsystem (grids & modals)
83
+ `_/browser/`: `Browser_Datatable_*` classes (`initialize()` registers fields/joins/DB link via
84
+ `setDatabaseLinkString()` / `setTableJoins()`, `Browser_DataTableField` for cell rendering),
85
+ `Browser_Modal_*` classes (capture HTML via `App_Page::startCapture()/stopCapture()`, e.g.
86
+ `Browser_Modal_CreateNewTicket`, `CreatenewUser`), and `Browser_Pagination_TogaView`.
87
+
47
88
  ## Session / identity model
48
89
  Login (`mvc/login/post.php`) tries flows in order — Retail, MSP contact, SMB admin, end user,
49
90
  plus inline client branches (staplesprotection) and SAML clients handled in `framework.php`
@@ -52,12 +93,49 @@ staplesprotection sets `$_SESSION['email']` (not `emailAddress`) and no `togades
52
93
  Full matrix: login-flows feature doc. Any page reading session identity must tolerate the
53
94
  divergent shapes (read `emailAddress` with `email` fallback).
54
95
 
55
- ## Ticket visibility
56
- Each page scopes tickets differently (msp_dashboard, msp_client_dashboard,
57
- enterprise_dashboard, support.php) see the msp-dashboard feature doc. The MSP portal
58
- depends on `tickets.customerid` being populated (TOGA `Customers.id`), which TogaDesk's
96
+ **SAML** (rumcsi, towfoundation, newcenturyholdingsllc) is handled in `framework.php`
97
+ (`meta`/`acs`/`sls`); newcenturyholdingsllc and towfoundation **auto-create `people` rows** for
98
+ unknown SAML users. rumcsi additionally accepts a legacy `?hash=<base64 email>` auto-login.
99
+
100
+ ## Dashboards
101
+ Five dashboards, one per audience/business model, each delegating to a client override or the
102
+ generic template:
103
+ - **retail_dashboard** — retail warranty/subscription end users (Walmart+, ODP): profile,
104
+ software locker, support app download, service activation status.
105
+ - **msp_dashboard** — MSP admins managing many SMB customers: per-customer ticket metrics,
106
+ seat/contract capacity, Excel/Charts export (see msp-dashboard feature doc).
107
+ - **msp_client_dashboard** — end users under an MSP-managed SMB contract: seat capacity, add-user
108
+ (disabled when oversubscribed), resend welcome.
109
+ - **enterprise_dashboard** — enterprise/insurance clients (rumcsi, staplesprotection,
110
+ towfoundation): client-specific logic incl. claims and asset tracking.
111
+ - **wb2b_dashboard** — Walmart B2B (hardcoded `TOGA_WalmartB2B`, clientId 13): managed services
112
+ + activation status.
113
+
114
+ ## Self-service identity lifecycle
115
+ `signup` / `register` / `activate` (retail, token + encrypted `clientAccountId` link, password
116
+ policy), `forgot_password` / `reset_password`, and the MSP/enterprise side: `create_account`,
117
+ `manage_users`, `msp_user`, `user_account`, `activation_admin` (WalmartBusiness bulk admin
118
+ creation). Models: `App_Model_RetailServices_ClientAccount`, `App_Model_Toga_Contact`,
119
+ `App_Model_Customer`.
120
+
121
+ ## Knowledge / content surface
122
+ Read-only client-facing KB sourced from TOGa Desk's `db_togadesk` KB tables, gated behind
123
+ `$_SESSION['togadeskClientId']`: `article` / `article_list` (`kb_articles`), `faq` / `faq_list`
124
+ (`kb_faqs`), `doc` / `doc_list` (`kb_documents`, file download via `?fileId=`).
125
+
126
+ ## Support / ticketing
127
+ `support` (ticket hub + `Browser_Modal_CreateNewTicket`), `ticket` (detail; ownership-checked —
128
+ see ticket-detail-page feature doc), `task_list` (`issues`), and `service_desk` (Walmart B2B
129
+ service activation against `db_toga` `ServiceRequests`). The MSP portal depends on
130
+ `tickets.customerid` being populated (TOGA `Customers.id`), which TOGa Desk's
59
131
  `Ticket::deriveCustomerId()` now sets at creation.
60
132
 
133
+ ## Commerce / retail lifecycle
134
+ `winback_landing` → `winback_payment` → `winback_activating` (ODP renewal), `purchase` /
135
+ `purchase_service`, `service_activation`, `claims` (Staples Protection / AIG), and
136
+ `techsupport_app` (Optimum Desk app download). Payment via **Braintree**. See the
137
+ retail-commerce-lifecycle feature doc.
138
+
61
139
  ## Key decisions
62
140
  - The ticket detail page `common/togaview/ticket.php` enforces an ownership check (clientid
63
141
  match OR requester-email match) on both ticket view and attachment download; the email arm
@@ -65,3 +143,22 @@ depends on `tickets.customerid` being populated (TOGA `Customers.id`), which Tog
65
143
  semantics require it). See ticket-detail-page feature doc.
66
144
  - New client onboarding touches at minimum: the HTTP_HOST switch in `framework.php`, a login
67
145
  flow (or SAML branch), and possibly the `smbContractSkus` whitelist in `login/post.php`.
146
+
147
+ ## Gotchas / known issues
148
+ - **Template fallback is per-page, not central.** Each `get.php` re-implements the
149
+ host→stylePath→togaview chain; it's easy to miss a host-specific branch.
150
+ - **`common/togaview/*` is the blast radius** — editing a generic page changes nearly every
151
+ client at once.
152
+ - **Divergent session shapes** across login flows (see Session model) — always use the
153
+ `emailAddress`/`email` fallback and tolerate a missing `togadeskClientId`.
154
+ - **Hardcoded Braintree credentials (including a private key) are committed under
155
+ `mvc/winback_payment/`** — these should be rotated and moved to `config.<env>.ini`
156
+ secret management. Flagged 2026-06-15; not yet remediated.
157
+ - `ENVIRONMENT` defaults to `worker` when unset → wrong config file.
158
+
159
+ ## Related docs
160
+ - [TOGa Desk architecture](../togadesk/architecture.md)
161
+ - [Login flows & session variables](features/login-flows.md)
162
+ - [MSP dashboard & ticket visibility](features/msp-dashboard.md)
163
+ - [Ticket detail page security](features/ticket-detail-page.md)
164
+ - [Retail commerce lifecycle](features/retail-commerce-lifecycle.md)
@@ -1,8 +1,8 @@
1
1
  ---
2
- title: TogaView Login Flows & Session Variables
2
+ title: TOGa View Login Flows & Session Variables
3
3
  framework: "1.0"
4
4
  repo: togaview
5
- project: TogaView
5
+ project: TOGa View
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
@@ -2,7 +2,7 @@
2
2
  title: MSP Dashboard & Ticket Visibility Rules
3
3
  framework: "1.0"
4
4
  repo: togaview
5
- project: TogaView
5
+ project: TOGa View
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
@@ -20,7 +20,7 @@ related:
20
20
  ---
21
21
 
22
22
  ## Summary
23
- Why tickets "disappear" in the TogaView client portal: different pages scope tickets
23
+ Why tickets "disappear" in the TOGa View client portal: different pages scope tickets
24
24
  **differently**, and the MSP pages depend on `tickets.customerid` and `SMBContracts`
25
25
  integrity. Covers the per-page filters and the June 2026 multi-department stats fix.
26
26
 
@@ -80,5 +80,5 @@ category resolution).
80
80
 
81
81
  ## Related docs
82
82
  - [Login flows](login-flows.md)
83
- - [TogaDesk ticket lifecycle](../../togadesk/features/ticket-lifecycle.md)
83
+ - [TOGa Desk ticket lifecycle](../../togadesk/features/ticket-lifecycle.md)
84
84
  - [SMB contract editing](../../togadesk/features/smb-contract-editing.md)
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: Retail Commerce Lifecycle (winback, purchase, claims, activation)
3
+ framework: "1.0"
4
+ repo: togaview
5
+ project: TOGa View
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-15
10
+ owners: ["jcardinal"]
11
+ files:
12
+ - mvc/winback_landing/get.php
13
+ - mvc/winback_payment/get.php
14
+ - mvc/winback_payment/post.php
15
+ - mvc/purchase/get.php
16
+ - mvc/service_activation/get.php
17
+ - mvc/claims/get.php
18
+ - mvc/techsupport_app/get.php
19
+ related:
20
+ - 1.0/apps/togaview/architecture.md
21
+ - 1.0/apps/togaview/features/login-flows.md
22
+ ---
23
+
24
+ ## Summary
25
+ Beyond support, TOGa View hosts the **retail tech-support commerce flows**: subscription
26
+ purchase/renewal (with payment), service activation, and insurance-style claims. These are
27
+ client-specific surfaces (chiefly ODP/Office Depot, Walmart B2B, and Staples Protection/AIG)
28
+ that mostly bypass the generic dashboards.
29
+
30
+ ## Key files / entry points
31
+ - **Winback (renewal):** `mvc/winback_landing/get.php` → `mvc/winback_payment/get.php` +
32
+ `post.php` → `mvc/winback_activating/`. ODP tech-support renewal (1-year subscription).
33
+ Returning customers arrive via an encrypted email link.
34
+ - **Purchase:** `mvc/purchase/get.php` (routes to `mvc/purchase/pts/`) and
35
+ `mvc/purchase_service/` — subscription purchase.
36
+ - **Service activation:** `mvc/service_activation/get.php` — finalizes remote/on-site service
37
+ against `TOGA_WalmartB2B`; surfaces the desktop support app download and per-`SMBContract`
38
+ support phone.
39
+ - **Claims:** `mvc/claims/get.php` — Staples Protection Plan (AIG) claim submission; looks up
40
+ asset serial / contract, auto-creates a ticket + asset when not found, routes printer vs.
41
+ general equipment.
42
+ - **techsupport_app:** `mvc/techsupport_app/get.php` — Optimum Desk (`dws.optimumdesk.com`)
43
+ download link, service type via query params.
44
+
45
+ ## How it works
46
+ The winback flow validates the customer (new vs. returning via encrypted `clientAccountId`),
47
+ collects a Braintree payment nonce, runs the transaction, and on success writes the TOGA
48
+ `Customer` / `Contact` / `ClientAccountService` records that grant the subscription. Claims and
49
+ service-activation flows read/write the relevant per-client TOGA database and create TOGa Desk
50
+ tickets/assets as needed. All are reached as standalone `mvc/` pages, not via a dashboard tab.
51
+
52
+ ## Data model
53
+ - Retail: `App_Model_RetailServices_ClientAccount`, `ClientAccountService` (`db_retail`).
54
+ - TOGA-side: `Customers`, `Contacts`, `SMBContracts`/`SMBContractItems`, `ServiceRequests`
55
+ (per-client `TOGA_*` / `TOGA_WalmartB2B`).
56
+ - Claims also touch `db_togadesk` `assets` / `tickets`.
57
+
58
+ ## External integrations
59
+ - **Braintree** payment gateway (`assets/braintree/` and `assets/braintree_latest_version/`),
60
+ used by `mvc/winback_payment/`. Two SDK copies are vendored.
61
+ - **Optimum Desk** remote-support app download links.
62
+
63
+ ## Gotchas / known issues
64
+ - **SECURITY — hardcoded payment credentials:** Braintree merchant/public/**private** keys are
65
+ committed directly in `mvc/winback_payment/get.php` rather than read from config. Treat the
66
+ committed keys as compromised: rotate them and move all three into `config.<env>.ini`
67
+ (`[external]`/secret section). Flagged 2026-06-15; not yet remediated.
68
+ - **Two vendored Braintree SDKs** (`assets/braintree/` vs `assets/braintree_latest_version/`) —
69
+ confirm which the live flow includes before upgrading.
70
+ - Encrypted email-link parameters (`clientAccountId`) gate returning-customer flows — a bad/old
71
+ link silently falls back to the new-customer path.
72
+
73
+ ## Change history
74
+ - 2026-06-15 — documented from a source read of the `mvc/` commerce pages; flagged the
75
+ committed Braintree credentials for rotation (jcardinal)
@@ -2,7 +2,7 @@
2
2
  title: Ticket Detail Page Security (common/togaview/ticket.php)
3
3
  framework: "1.0"
4
4
  repo: togaview
5
- project: TogaView
5
+ project: TOGa View
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
@@ -6,8 +6,8 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
6
6
 
7
7
  - **library** (Library) _(framework core)_ — 4 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
8
8
  - **worker** (Worker) — 4 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
9
- - **togadesk** (TogaDesk) — 4 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
10
- - **togaview** (TogaView) — 4 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
9
+ - **togadesk** (TOGa Desk) — 7 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
10
+ - **togaview** (TOGa View) — 5 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
11
11
 
12
12
  ## 2.0 framework
13
13
 
@@ -3,7 +3,7 @@
3
3
  | Doc | Framework | Summary | Files |
4
4
  |-----|-----------|---------|-------|
5
5
  | [Compass ASN → ItemFulfillment Auto-Creation](features/asn-to-item-fulfillment.md) | 2.0 | For Compass USA, posting an AdvanceShippingNotice (ASN) auto-creates the ItemFulfillment (IF) on the upstream SalesOrder. | _underscore/Model/Compass/AdvanceShippingNotice.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client_Compass/2026-06-11 - AsnItemTrackingNumberAcl.sql |
6
- | [Compass: Item-Fulfillment TableViews (for-sales-order-items & for-sales-orders, tracking via bridge)](features/item-fulfillment-tracking-tableview.md) | 2.0 | Two sibling Compass TableViews in `Client_Compass` display fulfilled items in toga2-supply, both driven by `TableViews` / `TableViewJoins` / `TableViewFields` c | dbchanges2/Client_Compass/2026-06-10 - ItemFulfillmentsForSalesOrderItemsTableView.sql, dbchanges2/Client_Compass/2026-06-11 - ItemFulfillmentsForSalesOrdersTableView.sql |
6
+ | [Compass: Item-Fulfillment TableViews (for-sales-order-items & for-sales-orders, tracking via bridge)](features/item-fulfillment-tracking-tableview.md) | 2.0 | Two sibling Compass TableViews in `Client_Compass` display fulfilled items in toga2-supply, both driven by `TableViews` / `TableViewJoins` / `TableViewFields` c | dbchanges2/Client_Compass/2026-06-10 - ItemFulfillmentsForSalesOrderItemsTableView.sql, dbchanges2/Client_Compass/2026-06-11 - ItemFulfillmentsForSalesOrdersTableView.sql, dbchanges2/Client_Compass/2026-06-15a - FixItemFulfillmentTrackingNumberJoins.sql |
7
7
  | [Compass MITS PO → SO Item Linking](features/mits-po-to-so-item-linking.md) | 2.0 | MITS sends Compass inbound Purchase Orders (`POST /v2/purchase-orders`) against a Sales Order (`mitsSalesOrder`). | _underscore/Model/Compass/PurchaseOrder.php |
8
8
  | [Compass USA](profile.md) | 2.0 | Compass USA is a TOGA client running a multi-tier supply-chain commerce operation. | |
9
9
  | [Compass Order Lifecycle & Data-Integrity Invariants](workflows/order-lifecycle-and-data-integrity.md) | 2.0 | End-to-end map of how a Compass order flows through the `Client_Compass` (2.0) database and the **expected raw-data shape** at each link/ASN/IF level. | |
@@ -5,11 +5,12 @@ project: _Underscore
5
5
  client: compass-usa
6
6
  type: client-feature
7
7
  status: active
8
- updated: 2026-06-11
8
+ updated: 2026-06-15
9
9
  owners: ["jcardinal"]
10
10
  files:
11
11
  - dbchanges2/Client_Compass/2026-06-10 - ItemFulfillmentsForSalesOrderItemsTableView.sql
12
12
  - dbchanges2/Client_Compass/2026-06-11 - ItemFulfillmentsForSalesOrdersTableView.sql
13
+ - dbchanges2/Client_Compass/2026-06-15a - FixItemFulfillmentTrackingNumberJoins.sql
13
14
  related:
14
15
  - ../../../2.0/apps/_underscore/features/tracking-number-bridges.md
15
16
  ---
@@ -35,8 +36,9 @@ Both views are re-rooted at **`ItemFulfillmentItems` (record 29)** so every fulf
35
36
  with the unit/tracking chain made **OUTER** so missing units no longer drop rows.
36
37
 
37
38
  **View 13 — `item-fulfillments-for-sales-order-items`:**
38
- - INNER `SalesOrderItems`; OUTER `ItemFulfillmentItemUnits` → OUTER `Units` OUTER
39
- `ItemFulfillmentItemUnits_TrackingNumbers` (record 319) OUTER `TrackingNumbers` OUTER `ShippingCarriers`.
39
+ - INNER `SalesOrderItems`; OUTER `ItemFulfillmentItemUnits` → OUTER `Units` (kept for Serial #
40
+ / Asset Tag columns); OUTER `ItemFulfillmentItems_TrackingNumbers` (record **318**) joined
41
+ directly onto `ItemFulfillmentItems` → OUTER `TrackingNumbers` → OUTER `ShippingCarriers`.
40
42
  - Columns: **Sales Order Line Number, Quantity Fulfilled (`ItemFulfillmentItems.quantity`),
41
43
  Outbound Tracking #, Carrier, Serial #, Asset Tag.**
42
44
 
@@ -48,14 +50,19 @@ with the unit/tracking chain made **OUTER** so missing units no longer drop rows
48
50
  - Default sort re-pointed to the line-number field.
49
51
 
50
52
  Stable Core ids used: records 29 IFI, 30 IFIU, 31 Units, 15 SalesOrderItems, 14 SalesOrders,
51
- 28 ItemFulfillments, 21 Items, 319 IFIU-tracking bridge, 62 TrackingNumbers, 9 ShippingCarriers.
53
+ 28 ItemFulfillments, 21 Items, **318 IFI-tracking bridge** (item-level), 62 TrackingNumbers,
54
+ 9 ShippingCarriers. (Record 319, the IFIU/unit-level bridge, is no longer used for tracking —
55
+ see Change history 2026-06-15.)
52
56
 
53
57
  ## Data model
54
58
 
55
59
  Join keys (RecordField ids): 60 SOI.id / 368 IFI.salesOrderItemId; 360 IF.id / 367 IFI.itemFulfillmentId;
56
60
  59 SO.id / 254 SOI.salesOrderId; 105 Item.id / 66 SOI.itemId; 374 IFIU.itemFulfillmentItemId / 365 IFI.id;
57
- 236 Unit.id / 375 IFIU.unitId; 2183 bridge.itemFulfillmentItemUnitId / 372 IFIU.id;
58
- 336 TN.id / 2184 bridge.trackingNumberId; 21 SC.id / 613 TN.shippingCarrierId.
61
+ 236 Unit.id / 375 IFIU.unitId; **2178 IFI-bridge.itemFulfillmentItemId / 365 IFI.id**;
62
+ 336 TN.id / **2179 IFI-bridge.trackingNumberId**; 21 SC.id / 613 TN.shippingCarrierId.
63
+ (Record 318 fields: 2176 id, 2178 itemFulfillmentItemId, 2179 trackingNumberId, 2180
64
+ returnTrackingNumberId. Superseded unit-level keys: 2183 IFIU-bridge.itemFulfillmentItemUnitId
65
+ / 372 IFIU.id; 2184 IFIU-bridge.trackingNumberId.)
59
66
 
60
67
  ## Gotchas / known issues
61
68
 
@@ -64,9 +71,22 @@ Join keys (RecordField ids): 60 SOI.id / 368 IFI.salesOrderItemId; 360 IF.id / 3
64
71
  ItemFulfillmentItemUnits / 0 Units** → view 12 showed "0 records" while the re-rooted sibling
65
72
  view 13 showed the items fine. Fix is structural (re-root + OUTER chain), independent of the
66
73
  missing-unit data issue.
67
- - Tracking is sourced through the bridge join (`ItemFulfillmentItemUnits_TrackingNumbers.trackingNumberId`),
68
- NOT the dropped `ItemFulfillmentItemUnits.trackingNumberId` column. The bridge record/fields
69
- (319 / 2183 / 2184) come from the Core migration and must exist before these files run.
74
+ - **Tracking must be sourced at the ITEM level** via `ItemFulfillmentItems_TrackingNumbers`
75
+ (record 318), joined directly onto `ItemFulfillmentItems` (rec 29). The original re-root
76
+ (2026-06-10/11) left tracking on the **unit-level** bridge `ItemFulfillmentItemUnits_TrackingNumbers`
77
+ (record 319), which is empty for **non-serialized** lines (no `ItemFulfillmentItemUnits` rows) →
78
+ the OUTER join yielded NULL and the tracking column rendered blank even though the data was
79
+ correct. Symptom: SO **MR243437** (id 105730) showed tracking at the order level but not the
80
+ item level; item 266006 had 0 unit rows but a valid `ItemFulfillmentItems_TrackingNumbers` row
81
+ (→ TN 67248). Fixed 2026-06-15.
82
+ - **Compass imports exactly one tracking number per item fulfillment** (NetSuite does not support
83
+ multiple tracking numbers at the unit level; a different tracking number arrives as a separate
84
+ item fulfillment). So the item-level bridge (318) is always the complete source and re-pointing
85
+ away from the unit-level bridge (319) loses nothing.
86
+ - Re-pointing the bridge is **not** a one-column swap: records 318 and 319 have different
87
+ `RecordFields` ids, so `joinRecordId` (319→318), `joinOnRecordFieldId` (2183→2178),
88
+ `parentRecordFieldId` (372→365), and the downstream `TrackingNumbers` join's parent field
89
+ (2184→2179) all change in lockstep.
70
90
  - `TableViews.sortPrimaryTableViewFieldId` is a FK to `TableViewFields`; set it NULL before deleting
71
91
  the old fields, then re-point via a multi-table UPDATE (not a self-subquery) to avoid MySQL 1093.
72
92
  - `TableViewFields` must be deleted before `TableViewJoins` (`tableViewJoinId` FK).
@@ -78,6 +98,7 @@ Join keys (RecordField ids): 60 SOI.id / 368 IFI.salesOrderItemId; 360 IF.id / 3
78
98
  Compass-only. The underlying bridge model is shared (see the _underscore feature doc).
79
99
 
80
100
  ## Change history
101
+ - 2026-06-15 — Re-pointed tracking from the unit-level bridge (319) to the item-level bridge `ItemFulfillmentItems_TrackingNumbers` (318) in views 12 & 13; fixes blank item-level tracking on non-serialized lines (e.g. SO MR243437). Migration `2026-06-15a`. (jcardinal)
81
102
  - 2026-06-11 — Re-rooted `item-fulfillments-for-sales-orders` (id 12) from Units to ItemFulfillmentItems with OUTER unit/tracking chain; fixes 0-records on orders lacking unit breakdown (e.g. SA132740). (jcardinal)
82
103
  - 2026-06-10 — Re-rooted `item-fulfillments-for-sales-order-items` (id 13) and moved tracking to the bridge join. (jcardinal)
83
104
 
@@ -38,7 +38,7 @@ related:
38
38
  ## Summary
39
39
 
40
40
  The NYCDOE/ServiceNow integration mirrors DOE's ServiceNow tickets (Incidents + RITMs) into
41
- local tables, turns vendor shipment notices into NetSuite Sales Orders and TogaDesk
41
+ local tables, turns vendor shipment notices into NetSuite Sales Orders and TOGa Desk
42
42
  installation repair orders (the **ASN pipeline** — the heart of the integration), and pushes
43
43
  ticket state / ETA / proof-of-delivery back to ServiceNow. A parallel EDI surface exchanges
44
44
  850 POs and invoices over DOE's SFTP.
@@ -57,7 +57,7 @@ ticket state / ETA / proof-of-delivery back to ServiceNow. A parallel EDI surfac
57
57
  | `NYCDOELocations` | Site-id → address mirror (backs ASN ship-to resolution) |
58
58
  | `AdvanceShippingNoticeQueue` | Stage-1 landing zone; **UNIQUE index `idx_dedupeKey`** |
59
59
  | `AdvanceShippingNotices` / `…Items` / `…Units` | ASN header (vendor+PO) → per-part items (`qtyOrder`) → individual units |
60
- | `managed_service_orders`, `repair_orders` (`db_togadesk`) | TogaDesk-side fulfillment (one MSO per ASN; `nycDoeTicketId` links back to the RITM) |
60
+ | `managed_service_orders`, `repair_orders` (`db_togadesk`) | TOGa Desk-side fulfillment (one MSO per ASN; `nycDoeTicketId` links back to the RITM) |
61
61
 
62
62
  ## Integration surfaces (live schedule)
63
63
 
@@ -66,11 +66,11 @@ ticket state / ETA / proof-of-delivery back to ServiceNow. A parallel EDI surfac
66
66
  | Incidents in | `import_inc.php` (500/batch into `NYCDOETickets`) | every 15 min |
67
67
  | RITMs in (ASN Stage 1a) | `import_asn.php` via `App_Api_NYCDOEV2::listRequestItems(500)` | every 15 min |
68
68
  | Vendor SFTP in (ASN Stage 1b) | `legacy_import_asn.php` — "Will NOT be turned off" | every 5 min |
69
- | Incident → TogaDesk ticket | `process_tickets.php` (client 16; throws if >200 unprocessed — flood valve) | every 15 min |
69
+ | Incident → TOGa Desk ticket | `process_tickets.php` (client 16; throws if >200 unprocessed — flood valve) | every 15 min |
70
70
  | Queue → ASN records (Stage 2) | `legacy_process_asn_queue.php` | every 15 min |
71
71
  | NetSuite SO create (Stage 3, **freeze**) | `1_send_asn_to_netsuite.php` | every 2 h at :07 |
72
72
  | NetSuite PO/serials (Stage 4) | `2_send_serials_to_netsuite.php` | every 2 h at :27 |
73
- | TogaDesk repair orders (Stage 5) | `3_create_installation_ticket.php` | every 5 min |
73
+ | TOGa Desk repair orders (Stage 5) | `3_create_installation_ticket.php` | every 5 min |
74
74
  | NetSuite Item Fulfillment (Stage 6) | `4_create_ns_item_fulfillment.php` (**paused**, `active: 0`); `5_DOA_…` unscheduled | not running |
75
75
  | ETA → ServiceNow | `send_ticket_updates.php` | every 6 min |
76
76
  | RITM state → ServiceNow | `send_request_item_updates.php` | every 5 min |
@@ -98,11 +98,11 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
98
98
  │ │ │
99
99
  │ 1_send_asn_to_netsuite.php 3_create_installation_ticket.php
100
100
  │ ▼ ▼
101
- │ NetSuite Sales Order TogaDesk repair orders (1 per Unit)
101
+ │ NetSuite Sales Order TOGa Desk repair orders (1 per Unit)
102
102
  │ ★ FREEZE qtyOrder ★
103
103
  │ │
104
104
  ▼ 2_send_serials_to_netsuite.php
105
- TogaDesk "Receiving Summary" → "#received / #ordered" (denominator = SUM(qtyOrder))
105
+ TOGa Desk "Receiving Summary" → "#received / #ordered" (denominator = SUM(qtyOrder))
106
106
  ```
107
107
 
108
108
  - **Stage 1a (API):** vendor detected by string match on `u_asset_mfg` (apple=1, acer=2,
@@ -128,8 +128,8 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
128
128
  - **Stage 4:** reconciles the NetSuite **PO** (`netSuiteInternalPurchaseOrderId` — not the
129
129
  SO id) and sends serial inventory assignments.
130
130
  - **Stage 5:** for units `WHERE togadeskRepairOrderId IS NULL`, reads serials from NetSuite
131
- item receipts (so TogaDesk RO serials are **driven by NetSuite**, not the ASN unit),
132
- creates/reuses TogaDesk manufacturers/models/assets/serial-numbers/MSO and creates **a
131
+ item receipts (so TOGa Desk RO serials are **driven by NetSuite**, not the ASN unit),
132
+ creates/reuses TOGa Desk manufacturers/models/assets/serial-numbers/MSO and creates **a
133
133
  new repair_orders row unconditionally per loop iteration** — one RO per Unit (a 50-cable
134
134
  line = 1 unit = 1 RO). Existing-serial lookup is scoped **per item row** (`$asnItemId`).
135
135
  - **"#/N received" UI** (`togadesk/desk/template/pages/managedserviceorders/view.php`):
@@ -17,14 +17,14 @@ NYC DOE (New York City Department of Education) is a TOGA client whose entire in
17
17
  runs in the **1.0 worker tier** (~30 cron scripts under `worker/crons/sync/nycdoe/` and
18
18
  `worker/crons/notifications/nycdoe/`, on the `sync` and `notification` worker roles).
19
19
  ServiceNow is DOE's system of record for tickets (Incidents and RITMs); TOGA mirrors those
20
- locally, drives fulfillment through TogaDesk and NetSuite, and pushes status / ETA /
20
+ locally, drives fulfillment through TOGa Desk and NetSuite, and pushes status / ETA /
21
21
  proof-of-delivery back to ServiceNow. A parallel EDI relationship (850 POs in, invoices out)
22
22
  runs over DOE's SFTP.
23
23
 
24
24
  ## Platforms & data
25
25
  - **1.0 worker tier only** — no 2.0 footprint. Local mirror tables live in `db_common`
26
26
  (`NYCDOETickets`, `NYCDOELocations`, `AdvanceShippingNotice*`).
27
- - **TogaDesk:** DOE is client id **16** (`db_togadesk`) — repair orders, managed service
27
+ - **TOGa Desk:** DOE is client id **16** (`db_togadesk`) — repair orders, managed service
28
28
  orders, and the "#received / #ordered" Receiving Summary UI.
29
29
  - **NetSuite:** Sales Orders, PO reconciliation, item receipts, Item Fulfillments, invoices
30
30
  (SuiteTalk toolkit in `library`).
@@ -8,6 +8,6 @@
8
8
  { "repo": "toga2-supply", "project": "TOGa Supply", "framework": "2.0", "role": "app", "dependsOn": ["api2"] },
9
9
  { "repo": "saml", "project": "SAML SSO Gateway", "framework": "2.0", "role": "app", "dependsOn": [] },
10
10
  { "repo": "toga2-view", "project": "TOGa View Frontend", "framework": "2.0", "role": "app", "dependsOn": ["api2"] },
11
- { "repo": "togadesk", "project": "TogaDesk", "framework": "1.0", "role": "app", "dependsOn": [] },
12
- { "repo": "togaview", "project": "TogaView", "framework": "1.0", "role": "app", "dependsOn": [] }
11
+ { "repo": "togadesk", "project": "TOGa Desk", "framework": "1.0", "role": "app", "dependsOn": [] },
12
+ { "repo": "togaview", "project": "TOGa View", "framework": "1.0", "role": "app", "dependsOn": [] }
13
13
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.71",
3
+ "version": "1.0.73",
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",