toga-ai 1.0.159 → 1.0.161
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.
- package/knowledge/1.0/apps/library/INDEX.md +1 -0
- package/knowledge/1.0/apps/library/features/startech-pcmaticb2b-sync.md +68 -0
- package/knowledge/2.0/apps/worker2/INDEX.md +1 -0
- package/knowledge/2.0/apps/worker2/features/startech-webhook-handler.md +58 -0
- package/knowledge/INDEX.md +3 -2
- package/knowledge/clients/pcmaticb2b/INDEX.md +6 -0
- package/knowledge/clients/pcmaticb2b/features/startech-ticket-sync.md +159 -0
- package/knowledge/clients/pcmaticb2b/profile.md +58 -0
- package/knowledge/clients/tow-foundation/INDEX.md +1 -1
- package/knowledge/clients/tow-foundation/features/receipt-processing.md +36 -7
- package/package.json +1 -1
|
@@ -7,3 +7,4 @@
|
|
|
7
7
|
| [Branded HTML Email Templates (App_Email_Template)](features/email-templates.md) | `App_Email_Template` (`app/email/template.php`) is the base class for branded HTML emails in the 1.0 (`App_`) framework. | library/app/email/template.php, library/app/email/agilant.php |
|
|
8
8
|
| [NetSuite SuiteQL/REST API Reference](features/netsuite-suiteql-api-reference.md) | General working reference for the Agilant NetSuite integration: how to authenticate, how SuiteQL behaves, and the confirmed schema of the tables/columns/codes w | library/app/api/netsuite/rest.php, library/ssl/netsuite_ec_key.pem |
|
|
9
9
|
| [NetSuite SuiteQL/REST Shim — Field Semantics](features/netsuite-suiteql-rest-shim.md) | `App_Api_Netsuite_Rest` is the REST/SuiteQL replacement for the deprecated NetSuite SOAP toolkit. | library/app/api/netsuite/rest.php |
|
|
10
|
+
| [Startech PC Matic B2B Sync (library)](features/startech-pcmaticb2b-sync.md) | `library/app/api/toga2.php` handles bidirectional ticket sync for PC Matic B2B between TOGaDesk 1.0 and TOGA 2.0. | library/app/api/toga2.php |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Startech PC Matic B2B Sync (library)
|
|
3
|
+
framework: "1.0"
|
|
4
|
+
repo: library
|
|
5
|
+
project: Library
|
|
6
|
+
client: pcmaticb2b
|
|
7
|
+
type: client-feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-23
|
|
10
|
+
owners: [snaredla]
|
|
11
|
+
files:
|
|
12
|
+
- library/app/api/toga2.php
|
|
13
|
+
related:
|
|
14
|
+
- clients/pcmaticb2b/features/startech-ticket-sync.md
|
|
15
|
+
- clients/pcmaticb2b/profile.md
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Summary
|
|
19
|
+
|
|
20
|
+
`library/app/api/toga2.php` handles bidirectional ticket sync for PC Matic B2B between TOGaDesk 1.0 and TOGA 2.0. All tickets sync in both directions regardless of `c_escalateToStartech` value — the Startech exclusion is enforced by `_Trait_Startech_Ticket`, not here.
|
|
21
|
+
|
|
22
|
+
## Key Function
|
|
23
|
+
|
|
24
|
+
`App_Api_Toga2::syncToga2TicketIntoTogadesk1Ticket()` (~line 5095)
|
|
25
|
+
|
|
26
|
+
PC Matic B2B path detected via:
|
|
27
|
+
```php
|
|
28
|
+
$isPcmaticb2bUuid = (self::$togaClientUuid == App_Api_Toga2::CLIENT_UUID_PCMATICB2B);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Ticket Type Map: TOGA 2.0 to TOGaDesk
|
|
32
|
+
|
|
33
|
+
```php
|
|
34
|
+
$ticketTypeMap = [
|
|
35
|
+
TOGA2_TICKET_TYPE_CODE__SUPPORT => TOGADESK_TICKET_TYPE__INCIDENT,
|
|
36
|
+
TOGA2_TICKET_TYPE_CODE__API => TOGADESK_TICKET_TYPE__API,
|
|
37
|
+
TOGA2_TICKET_TYPE_CODE__PCS => TOGADESK_TICKET_TYPE__PCS,
|
|
38
|
+
TOGA2_TICKET_TYPE_CODE__TOGADESK_PCS => TOGADESK_TICKET_TYPE__TOGADESK_PCS,
|
|
39
|
+
];
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Ticket Type Map: TOGaDesk to TOGA 2.0
|
|
43
|
+
|
|
44
|
+
Applied at two callsites (~lines 6845 and 7417):
|
|
45
|
+
|
|
46
|
+
```php
|
|
47
|
+
if ($togadeskTicketTypeCustomFieldValue === TOGADESK_TICKET_TYPE__API) {
|
|
48
|
+
$toga2TicketTypeCode = TOGA2_TICKET_TYPE_CODE__API;
|
|
49
|
+
} elseif ($togadeskTicketTypeCustomFieldValue === TOGADESK_TICKET_TYPE__PCS
|
|
50
|
+
|| $togadeskTicketTypeCustomFieldValue === TOGADESK_TICKET_TYPE__TOGADESK_PCS) {
|
|
51
|
+
$toga2TicketTypeCode = TOGA2_TICKET_TYPE_CODE__PCS;
|
|
52
|
+
} else {
|
|
53
|
+
$toga2TicketTypeCode = TOGA2_TICKET_TYPE_CODE__SUPPORT;
|
|
54
|
+
}
|
|
55
|
+
$payload['ticketType'] = ['code' => $toga2TicketTypeCode];
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Both `PCS Ticket` and `TOGaDesk-PCS` map to `PCS` in TOGA 2.0.
|
|
59
|
+
|
|
60
|
+
## Gotchas
|
|
61
|
+
|
|
62
|
+
- **Do not add a NULL early-return guard in this function.** NULL `c_escalateToStartech` means the ticket was created in TOGaDesk — it must still sync to TOGA 2.0. The Startech gate lives in `_Trait_Startech_Ticket::postPost`, not here.
|
|
63
|
+
- **`SUPPORT REQUEST` with a space** — `TOGA2_TICKET_TYPE_CODE__SUPPORT = 'SUPPORT REQUEST'`. Using `'SUPPORT'` silently breaks type matching.
|
|
64
|
+
- **`$isPcmaticb2bUuid` flag** is used for PC Matic B2B-specific branching throughout `syncToga2TicketIntoTogadesk1Ticket()`.
|
|
65
|
+
|
|
66
|
+
## Change history
|
|
67
|
+
|
|
68
|
+
- 2026-06-23: Initial doc — ticket type maps (both directions), NULL sync behavior, TOGADESK_PCS type added
|
|
@@ -9,4 +9,5 @@
|
|
|
9
9
|
| [Monitoring Framework (Orchestrator + Child Monitors)](features/monitoring-framework.md) | A unified, DB-driven monitoring framework for business-critical data flows (Compass POs, Prudential asset imports, AIG closed claims, …). | worker2/Worker/Monitor.php, worker2/Worker/Monitors/, worker2/Worker/Notification/Email.php, dbchanges2/Core/2026-05-21 - Monitors.sql |
|
|
10
10
|
| [NetSuite → TOGA Opportunity Sync (API Message Queue + worker2 webhook)](features/netsuite-opportunity-sync.md) | Outbound sync from NetSuite to TOGA for the record types the Forecast2 importer pulls (opportunities first; sales/items/etc. | worker2/Worker/Netsuite.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Controller/Index.php, _underscore/Worker.php, test/@dave/NetSuite/api-message-queue/lib_amq_queue.js, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/ue_amq_drain.js, test/@dave/NetSuite/api-message-queue/ss_amq_drain.js, test/@dave/NetSuite/api-message-queue/DEPLOY_RUNBOOK.md, test/@dave/clickup/backfill_opportunity_numbers.php, test/@dave/clickup/probe_opportunity_fields.php, test/@dave/probe_clickup_desc_match.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
|
|
11
11
|
| [NetSuite → Forecast Open-Orders Sync (salesOrder webhook → OpenOrderItems)](features/netsuite-salesorder-open-orders-sync.md) | Webhook-driven, single-record port of the legacy open-orders importer (TRUE-79142). | worker2/Worker/Netsuite/SalesOrder.php, worker2/Worker/Netsuite.php, test/@dave/probe_salesorder_rest_shape.php, test/@dave/probe_open_order_lines.php, test/@dave/check_so_status.php, test/@dave/check_so_history.php, test/@dave/probe_so_rest_lines.php, test/@dave/probe_missing_oo_timing.php, test/@dave/probe_missing_oo_createdby.php, worker/crons/toga2/forecast2/import_open_orders.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
|
|
12
|
+
| [Startech Webhook Handler (worker2)](features/startech-webhook-handler.md) | Receives inbound webhook events from Startech (Easeedesk) and creates or updates the corresponding ticket in TOGA 2.0. | worker2/Worker/Startech.php |
|
|
12
13
|
| [Teams Meeting Transcript Export](features/teams-transcript-export.md) | `_Worker_Team_Transcripts` (action `Team/Transcripts/Export`) polls Microsoft Graph for Teams meeting transcripts produced by a set of organizers, classifies ea | worker2/Worker/Team/Transcripts.php, worker2/Config/production.ini, worker2/Database/TeamsTranscriptExports.sql, dbchanges2/Core/2026-06-18a - Teams Transcript Export schedule.sql |
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Startech Webhook Handler (worker2)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: worker2
|
|
5
|
+
project: Worker
|
|
6
|
+
client: pcmaticb2b
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-23
|
|
10
|
+
owners: [snaredla]
|
|
11
|
+
files:
|
|
12
|
+
- worker2/Worker/Startech.php
|
|
13
|
+
related:
|
|
14
|
+
- clients/pcmaticb2b/features/startech-ticket-sync.md
|
|
15
|
+
- clients/pcmaticb2b/profile.md
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Summary
|
|
19
|
+
|
|
20
|
+
Receives inbound webhook events from Startech (Easeedesk) and creates or updates the corresponding ticket in TOGA 2.0. Sets `c_escalateToStartech = 0` on all webhook-originated tickets so the sync system recognizes them as Startech-sourced without re-posting them back to Startech.
|
|
21
|
+
|
|
22
|
+
## Key File
|
|
23
|
+
|
|
24
|
+
`worker2/Worker/Startech.php` — `_Worker_Startech::Webhook()`
|
|
25
|
+
|
|
26
|
+
## How It Works
|
|
27
|
+
|
|
28
|
+
1. Startech POSTs a webhook event to the TOGA 2.0 worker endpoint
|
|
29
|
+
2. `Webhook()` extracts `ticket_id` from the payload as `$startechTicketId`
|
|
30
|
+
3. Searches TOGA 2.0 for an existing ticket with `c_startechTicketId = $startechTicketId`
|
|
31
|
+
4. If not found: `POST /tickets` (create); if found: `PUT /tickets/{uuid}` (update)
|
|
32
|
+
5. Builds payload with ticket fields + `c_escalateToStartech: 0`
|
|
33
|
+
6. `_Trait_Startech_Ticket::postPost` gate: `!($payload->c_escalateToStartech ?? null)` evaluates `!0 = true` — skips re-posting to Startech since ticket already exists there
|
|
34
|
+
|
|
35
|
+
## Payload Fields
|
|
36
|
+
|
|
37
|
+
```php
|
|
38
|
+
$payload = [
|
|
39
|
+
'c_startechTicketId' => $startechTicketId,
|
|
40
|
+
'c_escalateToStartech' => 0,
|
|
41
|
+
'number' => (string) $startechTicketId,
|
|
42
|
+
'ticketType' => ['c_startechTicketTypeId' => $startechPayload->ticket_type],
|
|
43
|
+
'urgency' => ['c_startechPriorityId' => $startechPayload->ticket_priority],
|
|
44
|
+
'shortDescription' => $startechPayload->subject,
|
|
45
|
+
'longDescription' => $startechPayload->description,
|
|
46
|
+
];
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Conditional additions: `ticketStage` (from `ticket_status`), `contact` (from `creator`).
|
|
50
|
+
|
|
51
|
+
## Gotchas
|
|
52
|
+
|
|
53
|
+
- `c_escalateToStartech: 0` must be in the payload. If omitted, the interceptor still skips Startech (null is also falsy), but the field stays NULL in TOGA 2.0 — making webhook tickets indistinguishable from TOGaDesk-created ones.
|
|
54
|
+
- `ticketType` is resolved via `c_startechTicketTypeId` on `TicketTypes` — not a hardcoded string. Startech type IDs: SR=6, PCS=54, API=201.
|
|
55
|
+
|
|
56
|
+
## Change history
|
|
57
|
+
|
|
58
|
+
- 2026-06-23: Initial doc — webhook handler, c_escalateToStartech=0 payload field, POST vs PUT logic
|
package/knowledge/INDEX.md
CHANGED
|
@@ -4,7 +4,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
4
4
|
|
|
5
5
|
## 1.0 framework
|
|
6
6
|
|
|
7
|
-
- **library** (Library) _(framework core)_ —
|
|
7
|
+
- **library** (Library) _(framework core)_ — 6 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
|
|
8
8
|
- **worker** (Worker) — 10 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
|
|
9
9
|
- **togadesk** (TOGa Desk) — 7 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
|
|
10
10
|
- **togaview** (TOGa View) — 6 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
|
|
@@ -15,7 +15,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
15
15
|
## 2.0 framework
|
|
16
16
|
|
|
17
17
|
- **_underscore** (_Underscore) _(framework core)_ — 10 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
18
|
-
- **worker2** (Worker) —
|
|
18
|
+
- **worker2** (Worker) — 9 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
19
19
|
- **api2** (API) — 4 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
20
20
|
- **dbchanges2** (Database Changes) _(framework core)_ — 1 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
21
21
|
- **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
|
|
@@ -42,6 +42,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
42
42
|
- **New York City Department of Education** (`nycdoe`) → [clients/nycdoe/INDEX.md](clients/nycdoe/INDEX.md)
|
|
43
43
|
- **NYC Health & Hospitals** (`nychh`) → [clients/nychh/INDEX.md](clients/nychh/INDEX.md)
|
|
44
44
|
- **Office Depot** (`office-depot`) → [clients/office-depot/INDEX.md](clients/office-depot/INDEX.md)
|
|
45
|
+
- **PC Matic B2B Client Profile** (`pcmaticb2b`) → [clients/pcmaticb2b/INDEX.md](clients/pcmaticb2b/INDEX.md)
|
|
45
46
|
- **Prudential Financial** (`prudential`) → [clients/prudential/INDEX.md](clients/prudential/INDEX.md)
|
|
46
47
|
- **Quad Graphics** (`quad`) → [clients/quad/INDEX.md](clients/quad/INDEX.md)
|
|
47
48
|
- **Rate** (`rate`) → [clients/rate/INDEX.md](clients/rate/INDEX.md)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Client: PC Matic B2B Client Profile `pcmaticb2b`
|
|
2
|
+
|
|
3
|
+
| Doc | Framework | Summary | Files |
|
|
4
|
+
|-----|-----------|---------|-------|
|
|
5
|
+
| [PC Matic B2B — Startech Ticket Sync](features/startech-ticket-sync.md) | 1.0 | Bidirectional ticket sync between TOGaDesk 1.0 (client 177), TOGA 2.0 (client 21), and Startech (Easeedesk). | worker/crons/toga2/startech/sync_togadesk_startech_pcmaticb2b.php, library/app/api/toga2.php, togadesk/desk/includes/controllers/quickactions.php, togadesk/desk/template/pages/tickets/manage.php, togadesk/desk/includes/functions.php, worker2/Worker/Startech.php, _underscore/Trait/Startech/Ticket.php, dbchanges2/Client_Pcmaticb2b/2026-06-22-pcmaticb2b-enhancements.sql |
|
|
6
|
+
| [PC Matic B2B Client Profile](profile.md) | 1.0 | PC Matic B2B is a client using TOGaDesk 1.0 for ticket management, with TOGA 2.0 as the data layer and Startech (Easeedesk) as an external ticketing system for | worker/crons/toga2/startech/sync_togadesk_startech_pcmaticb2b.php, togadesk/desk/includes/functions.php |
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: PC Matic B2B — Startech Ticket Sync
|
|
3
|
+
framework: "1.0"
|
|
4
|
+
project: TOGa
|
|
5
|
+
client: pcmaticb2b
|
|
6
|
+
type: client-feature
|
|
7
|
+
status: active
|
|
8
|
+
updated: 2026-06-23
|
|
9
|
+
owners: [snaredla]
|
|
10
|
+
files:
|
|
11
|
+
- worker/crons/toga2/startech/sync_togadesk_startech_pcmaticb2b.php
|
|
12
|
+
- library/app/api/toga2.php
|
|
13
|
+
- togadesk/desk/includes/controllers/quickactions.php
|
|
14
|
+
- togadesk/desk/template/pages/tickets/manage.php
|
|
15
|
+
- togadesk/desk/includes/functions.php
|
|
16
|
+
- worker2/Worker/Startech.php
|
|
17
|
+
- _underscore/Trait/Startech/Ticket.php
|
|
18
|
+
- dbchanges2/Client_Pcmaticb2b/2026-06-22-pcmaticb2b-enhancements.sql
|
|
19
|
+
related:
|
|
20
|
+
- clients/pcmaticb2b/profile.md
|
|
21
|
+
- 2.0/apps/worker2/features/startech-webhook-handler.md
|
|
22
|
+
- 1.0/apps/library/features/startech-pcmaticb2b-sync.md
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Summary
|
|
26
|
+
|
|
27
|
+
Bidirectional ticket sync between TOGaDesk 1.0 (client 177), TOGA 2.0 (client 21), and Startech (Easeedesk). The goal is full data parity between 1.0 and 2.0 (long-term 1.0 retirement path), while Startech only receives tickets that originated there or were explicitly escalated by an analyst.
|
|
28
|
+
|
|
29
|
+
## c_escalateToStartech Field
|
|
30
|
+
|
|
31
|
+
The `Client_Pcmaticb2b.Tickets.c_escalateToStartech` TINYINT(1) field controls sync behavior:
|
|
32
|
+
|
|
33
|
+
| Value | Meaning | Syncs 1.0↔2.0 | Posts to Startech |
|
|
34
|
+
|-------|---------|----------------|-------------------|
|
|
35
|
+
| `NULL` | Created in TOGaDesk manually | Yes | No |
|
|
36
|
+
| `0` | Originated from Startech (webhook-created) | Yes | No (already there) |
|
|
37
|
+
| `1` | Escalated via "Escalate to Startech" button | Yes | Yes |
|
|
38
|
+
|
|
39
|
+
Column: `TINYINT(1) UNSIGNED NULL DEFAULT NULL`
|
|
40
|
+
|
|
41
|
+
## Ticket Type Map
|
|
42
|
+
|
|
43
|
+
### TOGA 2.0 to TOGaDesk 1.0
|
|
44
|
+
|
|
45
|
+
| TOGA 2.0 Code | TOGaDesk Name | TOGaDesk Dept |
|
|
46
|
+
|---------------|---------------|---------------|
|
|
47
|
+
| `SUPPORT REQUEST` | Startech Incident | 336 |
|
|
48
|
+
| `PCS` | PCS Ticket | 337 |
|
|
49
|
+
| `API` | API Ticket | 338 |
|
|
50
|
+
| `TOGADESK_PCS` | TOGaDesk-PCS | 337 |
|
|
51
|
+
|
|
52
|
+
### TOGaDesk 1.0 to TOGA 2.0
|
|
53
|
+
|
|
54
|
+
| TOGaDesk Name | TOGA 2.0 Code |
|
|
55
|
+
|---------------|---------------|
|
|
56
|
+
| `API Ticket` | `API` |
|
|
57
|
+
| `PCS Ticket` | `PCS` |
|
|
58
|
+
| `TOGaDesk-PCS` | `PCS` |
|
|
59
|
+
| anything else | `SUPPORT REQUEST` |
|
|
60
|
+
|
|
61
|
+
`TOGADESK_PCS` maps to `PCS` in TOGA 2.0 — it is the pre-escalation placeholder type for manually created tickets destined for PCS.
|
|
62
|
+
|
|
63
|
+
## Startech Ticket Type IDs (c_startechTicketTypeId)
|
|
64
|
+
|
|
65
|
+
| Code | Startech ID |
|
|
66
|
+
|------|-------------|
|
|
67
|
+
| `SUPPORT REQUEST` | 6 |
|
|
68
|
+
| `PCS` | 54 |
|
|
69
|
+
| `API` | 201 |
|
|
70
|
+
|
|
71
|
+
The `ticketType` on Startech-originated tickets is resolved dynamically via `c_startechTicketTypeId` on `TicketTypes` — not hardcoded.
|
|
72
|
+
|
|
73
|
+
## Sync Direction 1: Startech to TOGA 2.0 (Webhook)
|
|
74
|
+
|
|
75
|
+
`worker2/Worker/Startech.php` — `_Worker_Startech::Webhook()`
|
|
76
|
+
|
|
77
|
+
1. Startech POSTs a webhook event on ticket create/update
|
|
78
|
+
2. Worker looks up TOGA 2.0 ticket by `c_startechTicketId`
|
|
79
|
+
3. Creates (POST) or updates (PUT) the ticket in TOGA 2.0
|
|
80
|
+
4. Payload includes `c_escalateToStartech: 0` — marks it as Startech-originated
|
|
81
|
+
5. `_Trait_Startech_Ticket::postPost` gate evaluates `!0 = true` — skips re-posting to Startech (already there)
|
|
82
|
+
|
|
83
|
+
## Sync Direction 2: TOGA 2.0 to TOGaDesk 1.0 (CRON)
|
|
84
|
+
|
|
85
|
+
`library/app/api/toga2.php` — `App_Api_Toga2::syncToga2TicketIntoTogadesk1Ticket()`
|
|
86
|
+
|
|
87
|
+
CRON calls `syncWithToga()` every 2 minutes. All PC Matic B2B tickets sync regardless of `c_escalateToStartech` value — NULL, 0, and 1 all sync to TOGaDesk. Ticket type is mapped using the TOGA 2.0 to TOGaDesk map above.
|
|
88
|
+
|
|
89
|
+
## Sync Direction 3: TOGaDesk 1.0 to TOGA 2.0 (CRON)
|
|
90
|
+
|
|
91
|
+
`library/app/api/toga2.php` — called by `syncWithTogadesk()` in the same CRON
|
|
92
|
+
|
|
93
|
+
Pushes TOGaDesk ticket changes back to TOGA 2.0. Ticket type mapped using the TOGaDesk to TOGA 2.0 map above.
|
|
94
|
+
|
|
95
|
+
## Sync Direction 4: TOGA 2.0 to Startech (Interceptors)
|
|
96
|
+
|
|
97
|
+
`_underscore/Trait/Startech/Ticket.php`
|
|
98
|
+
|
|
99
|
+
- `postPost`: fires on new TOGA 2.0 ticket creation. Gate: `if (!($payload->c_escalateToStartech ?? null)) { return; }` — only proceeds when value is `1`. NULL and `0` both evaluate to falsy and skip.
|
|
100
|
+
- `postPut`: fires on update. Creates in Startech when `c_escalateToStartech` flips to `1` and no `c_startechTicketId` yet.
|
|
101
|
+
|
|
102
|
+
## Escalate to Startech Button
|
|
103
|
+
|
|
104
|
+
Shown on the TOGaDesk manage ticket page only for client 177. Handler: `quickactions.php` case `"escalateToStartech"`.
|
|
105
|
+
|
|
106
|
+
Flow:
|
|
107
|
+
1. Confirm dialog: "Escalate this ticket to Startech? The ticket type will be changed to PCS."
|
|
108
|
+
2. Loads ticket, gets TOGA 2.0 UUID from `referenceId`
|
|
109
|
+
3. GETs PCS ticket type UUID from TOGA 2.0 (`code = 'PCS'`)
|
|
110
|
+
4. PUTs `/tickets/{uuid}` with `c_escalateToStartech: 1` + `ticketType: {uuid: $pcsTypeUuid}`
|
|
111
|
+
5. Sets TOGaDesk `departmentid = 337` (PCS dept) and saves
|
|
112
|
+
6. `postPut` interceptor fires and creates the Startech ticket
|
|
113
|
+
7. History comment added, page redirects
|
|
114
|
+
|
|
115
|
+
## DB Migration
|
|
116
|
+
|
|
117
|
+
`dbchanges2/Client_Pcmaticb2b/2026-06-22-pcmaticb2b-enhancements.sql`
|
|
118
|
+
|
|
119
|
+
- `Contacts.c_togaCustomerId` — TOGaDesk 1.0 customer ID cross-reference
|
|
120
|
+
- `Tickets.c_escalateToStartech` — sync state flag (see above)
|
|
121
|
+
- `TicketTypes.c_togadeskTicketDepartmentId` — maps TOGA 2.0 ticket type to TOGaDesk dept
|
|
122
|
+
- Department UPDATE: SR=336, PCS=337, API=338
|
|
123
|
+
- INSERT `TOGADESK_PCS` ticket type (code=`TOGADESK_PCS`, dept=337)
|
|
124
|
+
- All new fields registered in `CustomRecordFields` + `AclCustomFieldPermissions`
|
|
125
|
+
|
|
126
|
+
## Constants
|
|
127
|
+
|
|
128
|
+
### togadesk/desk/includes/functions.php
|
|
129
|
+
```php
|
|
130
|
+
const CLIENT_PCMATICB2B = 177;
|
|
131
|
+
const CLIENT_DEPARTMENT_PCMATICB2B_PCS = 337;
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### sync_togadesk_startech_pcmaticb2b.php
|
|
135
|
+
```php
|
|
136
|
+
const TOGADESK_PCMATICB2B_CLIENT_ID = 177;
|
|
137
|
+
const TOGADESK_PCMATICB2B_STARTECH_DEPARTMENT_ID = 326;
|
|
138
|
+
const TOGA_PCMATICB2B_CLIENT_ID = 21;
|
|
139
|
+
const TOGA2_TICKET_TYPE_CODE__SUPPORT = 'SUPPORT REQUEST';
|
|
140
|
+
const TOGA2_TICKET_TYPE_CODE__PCS = 'PCS';
|
|
141
|
+
const TOGA2_TICKET_TYPE_CODE__API = 'API';
|
|
142
|
+
const TOGA2_TICKET_TYPE_CODE__TOGADESK_PCS = 'TOGADESK_PCS';
|
|
143
|
+
const TOGADESK_TICKET_TYPE__INCIDENT = 'Startech Incident';
|
|
144
|
+
const TOGADESK_TICKET_TYPE__PCS = 'PCS Ticket';
|
|
145
|
+
const TOGADESK_TICKET_TYPE__API = 'API Ticket';
|
|
146
|
+
const TOGADESK_TICKET_TYPE__TOGADESK_PCS = 'TOGaDesk-PCS';
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Gotchas / Known Issues
|
|
150
|
+
|
|
151
|
+
- **`SUPPORT REQUEST` not `SUPPORT`** — the DB value has a space. Using `'SUPPORT'` breaks type matching silently.
|
|
152
|
+
- **TOGADESK_PCS maps to PCS in TOGA 2.0** — both `PCS Ticket` and `TOGaDesk-PCS` TOGaDesk types map to the `PCS` code in TOGA 2.0. Intentional.
|
|
153
|
+
- **NULL tickets sync 1.0 and 2.0** — Startech exclusion for NULL is enforced by `_Trait_Startech_Ticket`, not the CRON. Do not add a NULL early-return guard in `syncToga2TicketIntoTogadesk1Ticket()`.
|
|
154
|
+
- **Webhook must set c_escalateToStartech=0** — without this, the `postPost` gate evaluates `!null = true` and would skip Startech for webhook-originated tickets on any subsequent PUT.
|
|
155
|
+
- **ticketType uses c_startechTicketTypeId dynamically** — stored on `TicketTypes`, not hardcoded in the worker.
|
|
156
|
+
|
|
157
|
+
## Change history
|
|
158
|
+
|
|
159
|
+
- 2026-06-23: Initial doc — full bidirectional sync, c_escalateToStartech semantics, ticket type maps, escalate button, DB migration, constants, gotchas
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: PC Matic B2B Client Profile
|
|
3
|
+
framework: "1.0"
|
|
4
|
+
project: TOGa
|
|
5
|
+
client: pcmaticb2b
|
|
6
|
+
type: client-feature
|
|
7
|
+
status: active
|
|
8
|
+
updated: 2026-06-23
|
|
9
|
+
owners: [snaredla]
|
|
10
|
+
files:
|
|
11
|
+
- worker/crons/toga2/startech/sync_togadesk_startech_pcmaticb2b.php
|
|
12
|
+
- togadesk/desk/includes/functions.php
|
|
13
|
+
related:
|
|
14
|
+
- clients/pcmaticb2b/features/startech-ticket-sync.md
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Summary
|
|
18
|
+
|
|
19
|
+
PC Matic B2B is a client using TOGaDesk 1.0 for ticket management, with TOGA 2.0 as the data layer and Startech (Easeedesk) as an external ticketing system for escalated/inbound tickets. All three systems sync bidirectionally.
|
|
20
|
+
|
|
21
|
+
## Key IDs
|
|
22
|
+
|
|
23
|
+
| Identifier | Value |
|
|
24
|
+
|------------|-------|
|
|
25
|
+
| TOGaDesk 1.0 client ID | 177 |
|
|
26
|
+
| TOGA 2.0 client ID | 21 |
|
|
27
|
+
| TOGA 2.0 client UUID constant | `App_Api_Toga2::CLIENT_UUID_PCMATICB2B` |
|
|
28
|
+
| TOGA 2.0 API UUID constant | `App_Api_Toga2::API_UUID_PCMATICB2B` |
|
|
29
|
+
| TOGA 2.0 API secret constant | `App_Api_Toga2::API_SECRET_PCMATICB2ID` |
|
|
30
|
+
| Startech department in TOGaDesk | 326 |
|
|
31
|
+
|
|
32
|
+
## Departments (TOGaDesk 1.0)
|
|
33
|
+
|
|
34
|
+
| Constant | ID | Ticket Type |
|
|
35
|
+
|----------|----|-------------|
|
|
36
|
+
| `TOGADESK_PCMATICB2B_DEPARTMENT_ID__SUPPORT_REQUEST` | 336 | Support Request |
|
|
37
|
+
| `TOGADESK_PCMATICB2B_DEPARTMENT_ID__PCS` | 337 | PCS / TOGaDesk-PCS |
|
|
38
|
+
| `TOGADESK_PCMATICB2B_DEPARTMENT_ID__API` | 338 | API |
|
|
39
|
+
|
|
40
|
+
## Ticket Type IDs in Startech (Easeedesk)
|
|
41
|
+
|
|
42
|
+
| TOGA 2.0 Code | Startech Type ID |
|
|
43
|
+
|---------------|-----------------|
|
|
44
|
+
| `SUPPORT REQUEST` | 6 |
|
|
45
|
+
| `PCS` | 54 |
|
|
46
|
+
| `API` | 201 |
|
|
47
|
+
|
|
48
|
+
## CRON Entry Point
|
|
49
|
+
|
|
50
|
+
`worker/crons/toga2/startech/sync_togadesk_startech_pcmaticb2b.php` — runs every 2 minutes.
|
|
51
|
+
|
|
52
|
+
Calls:
|
|
53
|
+
1. `App_Api_Toga2::syncWithToga()` — pulls TOGA 2.0 tickets into TOGaDesk
|
|
54
|
+
2. `App_Api_Toga2::syncWithTogadesk()` — pushes TOGaDesk tickets to TOGA 2.0
|
|
55
|
+
|
|
56
|
+
## Change history
|
|
57
|
+
|
|
58
|
+
- 2026-06-23: Initial profile — client IDs, departments, Startech ticket type IDs, CRON entry point
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
|
|
3
3
|
| Doc | Framework | Summary | Files |
|
|
4
4
|
|-----|-----------|---------|-------|
|
|
5
|
-
| [Credit Card Receipt Processing](features/receipt-processing.md) | 2.0 | Automated processing of credit card receipts uploaded to SharePoint. | worker2/Worker/Client/TowFoundation.php, worker2/Worker/Client/TowFoundation/ProcessReceipts.php, worker2/Worker/Client/TowFoundation/TowFoundationVendors.php |
|
|
5
|
+
| [Credit Card Receipt Processing](features/receipt-processing.md) | 2.0 | Automated processing of credit card receipts uploaded to SharePoint. | worker2/Worker/Client/TowFoundation.php, worker2/Worker/Client/TowFoundation/ProcessReceipts.php, worker2/Worker/Client/TowFoundation/TowFoundationVendors.php, worker2/Worker/Client/TowFoundation/TowFoundationCategories.php |
|
|
6
6
|
| [Tow Foundation](profile.md) | 2.0 | Tow Foundation is a nonprofit client. | |
|
|
@@ -5,12 +5,13 @@ project: Worker
|
|
|
5
5
|
client: tow-foundation
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-06-
|
|
8
|
+
updated: 2026-06-23
|
|
9
9
|
owners: ["rgirish"]
|
|
10
10
|
files:
|
|
11
11
|
- worker2/Worker/Client/TowFoundation.php
|
|
12
12
|
- worker2/Worker/Client/TowFoundation/ProcessReceipts.php
|
|
13
13
|
- worker2/Worker/Client/TowFoundation/TowFoundationVendors.php
|
|
14
|
+
- worker2/Worker/Client/TowFoundation/TowFoundationCategories.php
|
|
14
15
|
related:
|
|
15
16
|
- clients/tow-foundation/profile.md
|
|
16
17
|
---
|
|
@@ -86,7 +87,7 @@ Amex billing cycle runs 3rd-to-3rd. `computeRefNo()` returns the cycle-end date
|
|
|
86
87
|
`TowFoundationVendors.php` holds 2,994 vendor names exported verbatim from Tow Foundation's
|
|
87
88
|
QuickBooks vendor list (June 2026). `matchQbVendor()` runs three passes against this list:
|
|
88
89
|
1. Exact match (case-insensitive)
|
|
89
|
-
2. Substring match — QB name contains extracted name or vice versa
|
|
90
|
+
2. Substring match — QB name contains extracted name or vice versa; minimum 3 chars. For needles shorter than 4 chars, a **whole-word boundary check** is used (regex `(?<![a-z])<needle>(?![a-z])`) to prevent short acronyms like `"UPS"` from matching inside `"USPS"`. Picks shortest QB name on multiple hits.
|
|
90
91
|
3. `similar_text()` similarity ≥ 80% — picks highest scorer
|
|
91
92
|
|
|
92
93
|
Returns the exact QB vendor string or `''` if no confident match. The AI only extracts
|
|
@@ -95,11 +96,35 @@ Returns the exact QB vendor string or `''` if no confident match. The AI only ex
|
|
|
95
96
|
To update the vendor list: re-export from QuickBooks → run the extraction script →
|
|
96
97
|
replace `TowFoundationVendors.php`. The matching logic is in `matchQbVendor()` and needs no changes.
|
|
97
98
|
|
|
98
|
-
### AI category values
|
|
99
|
-
The AI is prompted to return **specific expense types
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
### AI category values and QB mapping
|
|
100
|
+
The AI is prompted to return **specific expense types**. These are then mapped to the exact
|
|
101
|
+
QB account string via `mapCategory()`, which loads `TowFoundationCategories.php` and does
|
|
102
|
+
a case-insensitive lookup. Unmapped values are passed through as-is and logged as warnings.
|
|
103
|
+
|
|
104
|
+
| AI category value | QB account string |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `Dining` | `6640 Dining & Entertainment` |
|
|
107
|
+
| `Entertainment` | `6640 Dining & Entertainment` |
|
|
108
|
+
| `Travel` | `6609 Travel, conferences, meetings` |
|
|
109
|
+
| `Transportation` | `6610 Travel expense` |
|
|
110
|
+
| `Accommodation` / `Lodging` | `6620 Lodging` |
|
|
111
|
+
| `Office Supplies` | `6751 Office supplies` |
|
|
112
|
+
| `Professional Services` | `6400 Professional Fees & Consultants` |
|
|
113
|
+
| `Subscriptions` | `67271 Subscriptions` |
|
|
114
|
+
| `Technology` | `6710 Technology Systems Expense` |
|
|
115
|
+
| `Software` | `6714 Software License Expense` |
|
|
116
|
+
| `Printing` | `6722 Printing expense` |
|
|
117
|
+
| `Postage` | `6721 Postage` |
|
|
118
|
+
| `Membership` | `6730 Membership Dues` |
|
|
119
|
+
| `Insurance` | `6740 Insurance` |
|
|
120
|
+
| `Telephone` | `6755 Telephone & internet expense` |
|
|
121
|
+
| `Photography` | `67241 Photography` |
|
|
122
|
+
| `Board` | `6650 Board Expenses and Board Development` |
|
|
123
|
+
| `Professional Development` | `6735 Staff Professional Development` |
|
|
124
|
+
|
|
125
|
+
To update the mapping: edit `TowFoundationCategories.php` — keys are case-insensitive,
|
|
126
|
+
values must be the exact QB account string. The AI prompt in `extractReceiptData()` lists
|
|
127
|
+
the valid category names; keep them in sync with the map keys.
|
|
103
128
|
|
|
104
129
|
### Per-person constants
|
|
105
130
|
`CLASS_MAP` and `PAYMENT_ACCOUNT_MAP` in `ProcessReceipts.php` map each cardholder's name
|
|
@@ -158,9 +183,13 @@ Fatal errors send only to `NOTIFY_EMAIL_DEV` (no CC/BCC).
|
|
|
158
183
|
- **`billingCycle` filter is suffix-match, not exact** — always pass just the date portion (`"06-03-2026"`), not the full folder name. Passing the full name (`"Amex ending in 06-03-2026"`) also works but would miss Mastercard folders.
|
|
159
184
|
- **Ole Mole not in QB vendor list** — as of June 2026 this vendor is not in Tow Foundation's QuickBooks, so `qb_vendor` will be blank for Ole Mole charges until they add it to QB and the vendor list is refreshed.
|
|
160
185
|
- **Duplicate detection is within-run only** — if the same receipt was processed in a prior run and archived, it won't be caught. The dedup only covers receipts present in the current run's `$pendingRenames`.
|
|
186
|
+
- **Unmapped AI categories pass through** — if the AI returns a category string not in `TowFoundationCategories.php`, `mapCategory()` logs a warning and writes the raw AI string to the Excel. Check error logs after a run if the Category column looks odd; add the new value to the map file to fix it.
|
|
187
|
+
- **Short vendor acronyms (< 4 chars) require whole-word match** — the substring pass uses a word-boundary regex for needles under 4 chars to avoid false positives (e.g. `"UPS"` inside `"USPS"`). If a short vendor name is not matching, check that the QB vendor list entry starts or ends with the acronym as a whole word.
|
|
188
|
+
- **Archive restore script** — a one-shot CLI script `/tmp/tow_restore_archives.php` was written 2026-06-23 to move archived files back to their billing-cycle folders for reprocessing. It is not committed; re-create from the session transcript if needed. It uses the production credentials from `Config/production.ini`.
|
|
161
189
|
|
|
162
190
|
## Change history
|
|
163
191
|
|
|
192
|
+
- 2026-06-23 — `TowFoundationCategories.php` added: AI category values now mapped to exact QB account strings via `mapCategory()`; `"Transportation"` → `"6610 Travel expense"` etc. (19 mappings). QB vendor substring match minimum lowered from 4 to 3 chars with whole-word boundary guard for short needles — fixes `"CVS"` → `"CVS Pharmacy"`, `"MTA"` → `"MTA Metro card"`, prevents `"UPS"` → `"USPS"` false positive. (rgirish)
|
|
164
193
|
- 2026-06-18 — QB vendor matching via local fuzzy match against `TowFoundationVendors.php` (2,994 vendors); AI category prompt updated to return specific types (Dining, Travel, etc.); duplicate receipt detection added (person+date+vendor+amount dedup); `billingCycle` filter parameter added (suffix-match — card-type agnostic); person folder ` CC receipts` suffix stripped so CLASS_MAP/PAYMENT_ACCOUNT_MAP resolve correctly (rgirish)
|
|
165
194
|
- 2026-06-12 — QB Excel upload moved to root-level `3. QB Excel/`; Archive/exception folders flattened (no person subfolder); unsupported mime types (`application/octet-stream`) now throw ReceiptProcessingException instead of being silently skipped (rgirish)
|
|
166
195
|
- 2026-06-11 — Added CC (Magdalena), BCC (devteam@togatech.com), per-cardholder Archive subfolders, Excel upload to "3. QB Excel" SharePoint folder, two-pass rename with alphabetical duplicate suffix, rename format changed to `YYYY.MM.DD Full Name_Vendor_Amount` (rgirish)
|
package/package.json
CHANGED