toga-ai 1.0.60 → 1.0.62
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/INDEX.md +1 -0
- package/knowledge/clients/compass-usa/features/mits-po-to-so-item-linking.md +21 -0
- package/knowledge/clients/tow-foundation/INDEX.md +6 -0
- package/knowledge/clients/tow-foundation/features/receipt-processing.md +130 -0
- package/knowledge/clients/tow-foundation/profile.md +35 -0
- package/package.json +1 -1
package/knowledge/INDEX.md
CHANGED
|
@@ -25,4 +25,5 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
25
25
|
- **New York City Department of Education** (`nycdoe`) → [clients/nycdoe/INDEX.md](clients/nycdoe/INDEX.md)
|
|
26
26
|
- **Prudential Financial** (`prudential`) → [clients/prudential/INDEX.md](clients/prudential/INDEX.md)
|
|
27
27
|
- **Rate** (`rate`) → [clients/rate/INDEX.md](clients/rate/INDEX.md)
|
|
28
|
+
- **Tow Foundation** (`tow-foundation`) → [clients/tow-foundation/INDEX.md](clients/tow-foundation/INDEX.md)
|
|
28
29
|
|
|
@@ -71,7 +71,28 @@ USA and Canada share the parent handler unchanged.
|
|
|
71
71
|
number onto the line). Example seen on SA132739: UPS `1Z8696XA0193326215` actually shipped
|
|
72
72
|
`C40QYUC-1` (SO line 4) but was attached to SO line 1 (`1000555`).
|
|
73
73
|
|
|
74
|
+
- **Over-fulfillment (fulfilled qty > ordered) is the other visible symptom of the same
|
|
75
|
+
scramble.** When a spuriously-linked PO item ships, its ASN/PO-driven ItemFulfillment walks
|
|
76
|
+
the bad bridge link and creates a **phantom `ItemFulfillmentItem` on the wrong SO line**, so
|
|
77
|
+
that line shows more fulfilled than ordered (the same shipment is also fulfilled correctly on
|
|
78
|
+
its real line). Cleanup per order:
|
|
79
|
+
- **Spurious bridge rows:** delete where the SO item's part ≠ the PO item's part, scoped to
|
|
80
|
+
the one `salesOrderId` (`soi.itemId <> vi.itemId` via `VendorItems`). Idempotent.
|
|
81
|
+
- **Phantom IFI:** the duplicate on the over-fulfilled line is the IFI with **no
|
|
82
|
+
`ItemFulfillmentItemUnits`, no `ItemFulfillmentItems_TrackingNumbers`, and nothing
|
|
83
|
+
mirroring it** (`upstreamItemFulfillmentItemId`). The legitimate line fulfillment is the
|
|
84
|
+
sibling IFI that has a real `Unit` and/or a downstream mirror (often in an auto-numbered
|
|
85
|
+
`F#####` IF rather than the SO-named IF). Delete the phantom guarded by
|
|
86
|
+
`id + salesOrderItemId + itemFulfillmentId + quantity`.
|
|
87
|
+
- These are pre-fix orders only (POs created before the 2026-06-11 guard). Cleanup is
|
|
88
|
+
one-time data, written as a dated `dbchanges2/Client_Compass/` file per order.
|
|
89
|
+
- ⚠ Not every over-fulfilled Compass line is this bug — high multipliers (5×–20×) seen on
|
|
90
|
+
other orders (e.g. SA114091 25→125) do not fit the split-PO 2× pattern and are a separate,
|
|
91
|
+
uninvestigated cause.
|
|
92
|
+
|
|
74
93
|
## Change history
|
|
94
|
+
- 2026-06-11 — Documented the over-fulfillment symptom and the phantom-IFI cleanup predicate;
|
|
95
|
+
remediated SA132657 and SA132641 (one dated dbchanges2 file each). (jcardinal)
|
|
75
96
|
- 2026-06-11 — Guarded `postPost` SO↔PO item linking to MR orders only; SA links come solely
|
|
76
97
|
from `prePost`'s `createdFromSalesOrderItem` remap. Fixes spurious cross-part links. (jcardinal)
|
|
77
98
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Client: Tow Foundation `tow-foundation`
|
|
2
|
+
|
|
3
|
+
| Doc | Framework | Summary | Files |
|
|
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 |
|
|
6
|
+
| [Tow Foundation](profile.md) | 2.0 | Tow Foundation is a nonprofit client. | |
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Credit Card Receipt Processing"
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
project: Worker
|
|
5
|
+
client: tow-foundation
|
|
6
|
+
type: client-feature
|
|
7
|
+
status: active
|
|
8
|
+
updated: 2026-06-11
|
|
9
|
+
owners: ["rgirish"]
|
|
10
|
+
files:
|
|
11
|
+
- worker2/Worker/Client/TowFoundation.php
|
|
12
|
+
- worker2/Worker/Client/TowFoundation/ProcessReceipts.php
|
|
13
|
+
related:
|
|
14
|
+
- clients/tow-foundation/profile.md
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Summary
|
|
18
|
+
|
|
19
|
+
Automated processing of credit card receipts uploaded to SharePoint. Downloads each
|
|
20
|
+
receipt, extracts structured data via Talos AI, cross-verifies against billing-cycle
|
|
21
|
+
statements, renames the file, archives it per cardholder, generates a QuickBooks-ready
|
|
22
|
+
Excel file per person, uploads it to SharePoint, and sends a summary email.
|
|
23
|
+
|
|
24
|
+
Invocation: `{"action": "Client/TowFoundation/ProcessReceipts/Run", "parameters": {}}`
|
|
25
|
+
|
|
26
|
+
Optional filter parameters: `limit` (int), `year` (string e.g. `"2026"`), `person` (string e.g. `"Brent Peterkin"`).
|
|
27
|
+
|
|
28
|
+
## Key files / entry points
|
|
29
|
+
|
|
30
|
+
- `TowFoundation.php` — base class; holds all email constants and `initialize()` (empty — no client DB)
|
|
31
|
+
- `ProcessReceipts.php` — all logic; abstract class extending `_Worker_Client_TowFoundation`
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
### SharePoint folder structure
|
|
36
|
+
```
|
|
37
|
+
Credit Card Receipts/
|
|
38
|
+
{Person}/
|
|
39
|
+
{Year}/
|
|
40
|
+
{BillingCycleFolder}/ ← receipt files live here (e.g. "Amex ending in 06-03-2026")
|
|
41
|
+
{Person} reports/ ← .xlsx billing statements for cross-verification
|
|
42
|
+
Archive/
|
|
43
|
+
{Person}/ ← successfully processed receipts land here (renamed)
|
|
44
|
+
exception/
|
|
45
|
+
{Person}/ ← failed receipts land here (original name preserved)
|
|
46
|
+
3. QB Excel/ ← generated Excel files uploaded here after each run
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Processing flow
|
|
50
|
+
|
|
51
|
+
1. **Auth** — OAuth2 client-credentials token from Microsoft Graph
|
|
52
|
+
2. **Walk** — `walkReceiptsFolder()` enumerates all receipt files and statement Excels
|
|
53
|
+
3. **Filter** — optional `$year` / `$person` / `$limit` applied to the receipt list
|
|
54
|
+
4. **Pre-load statements** — all billing-cycle `.xlsx` files under `reports/` are downloaded
|
|
55
|
+
and parsed upfront via PhpSpreadsheet (auto-detect header row by scanning for
|
|
56
|
+
description/amount keywords)
|
|
57
|
+
5. **Pass 1 — Extract** — for each receipt:
|
|
58
|
+
- Enforce size limit (4 MB images, 10 MB documents)
|
|
59
|
+
- Download file bytes from SharePoint
|
|
60
|
+
- POST to Talos AI `/api/ai/generate` → structured `{vendor_name, invoice_date, total, payment_memo, category, ...}`
|
|
61
|
+
- Cross-verify amount ± $0.01 against parsed statement; if matched, override `payment_memo` with statement description
|
|
62
|
+
- Build base filename (no suffix yet) and store in `$pendingRenames`
|
|
63
|
+
- On extract failure → move to `Archive/exception/{Person}/` immediately
|
|
64
|
+
6. **Collision detection** — count base name occurrences across all pending renames
|
|
65
|
+
7. **Pass 2 — Move** — for each pending rename:
|
|
66
|
+
- If base name appears more than once, assign `_a`, `_b`, `_c`... suffix to **all** colliding files (including the first)
|
|
67
|
+
- PATCH SharePoint to rename + move to `Archive/{Person}/`
|
|
68
|
+
8. **Excel generation** — one `.xlsx` per person (PhpSpreadsheet); columns: Row #, Account Name, QB Vendor, Payment Amount, Date, Payment Method, Payment Memo, QB Description, Class, Category, Payment Account, Ref No.
|
|
69
|
+
9. **SharePoint upload** — each Excel uploaded to `{Person}/{Year}/3. QB Excel/` via Graph API PUT
|
|
70
|
+
10. **Summary email** — sent with Excel files attached; To: Jheanelle, CC: Magdalena, BCC: devteam@togatech.com
|
|
71
|
+
|
|
72
|
+
### Renamed file format
|
|
73
|
+
```
|
|
74
|
+
YYYY.MM.DD Name of Cardholder_VendorName_Amount[_a].ext
|
|
75
|
+
```
|
|
76
|
+
- Vendor name: special chars stripped, spaces → underscores
|
|
77
|
+
- Amount: two decimal places (e.g. `42.50`)
|
|
78
|
+
- Suffix `_a`, `_b`... added when date + person + vendor + amount are all identical (e.g. multiple train tickets same day)
|
|
79
|
+
|
|
80
|
+
### Ref No. (QuickBooks)
|
|
81
|
+
Amex billing cycle runs 3rd-to-3rd. `computeRefNo()` returns the cycle-end date as `MMDDYYYY`.
|
|
82
|
+
- Charge on or before the 3rd → cycle ends on the 3rd of the same month
|
|
83
|
+
- Charge after the 3rd → cycle ends on the 3rd of the next month
|
|
84
|
+
|
|
85
|
+
### Per-person constants
|
|
86
|
+
`CLASS_MAP` and `PAYMENT_ACCOUNT_MAP` in `ProcessReceipts.php` map each cardholder's name
|
|
87
|
+
to their QuickBooks Class and Payment Account strings. Ryan Farrell uses Bank of America
|
|
88
|
+
Mastercard; all others use AMEX Open Credit Card.
|
|
89
|
+
|
|
90
|
+
## Email routing
|
|
91
|
+
|
|
92
|
+
Defined as constants in `TowFoundation.php`:
|
|
93
|
+
|
|
94
|
+
| Constant | Value | Role |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `NOTIFY_EMAIL_CLIENT` | `Jheanelle@towfoundation.org` | To |
|
|
97
|
+
| `NOTIFY_EMAIL_CC` | `['Magdalena@towfoundation.org']` | CC |
|
|
98
|
+
| `NOTIFY_EMAIL_BCC` | `['devteam@togatech.com']` | BCC |
|
|
99
|
+
| `NOTIFY_EMAIL_DEV` | `devteam@goagilant.com` | To (always) |
|
|
100
|
+
| `NOTIFY_EMAIL_EXTRA` | jcardinal, ajean, bmorton @goagilant.com | To |
|
|
101
|
+
|
|
102
|
+
Fatal errors send only to `NOTIFY_EMAIL_DEV` (no CC/BCC).
|
|
103
|
+
|
|
104
|
+
## Gotchas / known issues
|
|
105
|
+
|
|
106
|
+
- **`$year` variable shadowing** — `Run()` uses `$year` as both a filter parameter and a
|
|
107
|
+
loop variable (line ~159: `$year = $receipt['year']`). After the loop `$year` holds the
|
|
108
|
+
last receipt's year, not the original filter value. Harmless now but fragile if the loop
|
|
109
|
+
is restructured.
|
|
110
|
+
- **Two-pass rename is required** — collision detection must see ALL base names before any
|
|
111
|
+
file is moved. A single-pass approach would retroactively need to rename the first file
|
|
112
|
+
after discovering a duplicate, which is not possible once it's already on SharePoint.
|
|
113
|
+
- **`_Loader::setThrowExceptionInAutoloaderIfClassNotFound(false)`** — must wrap all
|
|
114
|
+
PhpSpreadsheet calls. ZipStream is a Composer dependency that triggers the autoloader;
|
|
115
|
+
without this flag, it throws on missing class and aborts Excel generation.
|
|
116
|
+
- **Graph API item-ID move** — `renameAndMoveToArchive()` resolves the target folder to an
|
|
117
|
+
item ID before PATCHing. Using path-based `parentReference` causes HTTP 400 on folder
|
|
118
|
+
names with special characters (spaces, dots). Always resolve to ID first.
|
|
119
|
+
- **`ensureFolderPath()` for "3. QB Excel"** — the folder name contains a dot and space;
|
|
120
|
+
`ensureFolderPath` handles this correctly via `rawurlencode` on each path segment.
|
|
121
|
+
- **Statement matching tolerance** — `matchStatementRow()` uses `abs($row['amount'] - $amount) < 0.01`
|
|
122
|
+
for amount matching, then date tie-break for multiple same-amount rows. Returns first
|
|
123
|
+
candidate if date tie-break also ambiguous.
|
|
124
|
+
- **No client DB** — there is no audit trail in MySQL. All state lives in SharePoint folder
|
|
125
|
+
structure and email. If a run is interrupted mid-way, some receipts may be archived
|
|
126
|
+
without a corresponding Excel row.
|
|
127
|
+
|
|
128
|
+
## Change history
|
|
129
|
+
|
|
130
|
+
- 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)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Tow Foundation"
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
project: Worker
|
|
5
|
+
client: tow-foundation
|
|
6
|
+
type: profile
|
|
7
|
+
status: active
|
|
8
|
+
updated: 2026-06-11
|
|
9
|
+
owners: ["rgirish"]
|
|
10
|
+
files: []
|
|
11
|
+
related:
|
|
12
|
+
- clients/tow-foundation/features/receipt-processing.md
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Summary
|
|
16
|
+
|
|
17
|
+
Tow Foundation is a nonprofit client. Their integration automates credit card receipt
|
|
18
|
+
processing: receipts uploaded to SharePoint by cardholders are downloaded, extracted via
|
|
19
|
+
Talos AI, cross-verified against Amex/Mastercard billing statements, renamed, archived,
|
|
20
|
+
and exported as QuickBooks-ready Excel files.
|
|
21
|
+
|
|
22
|
+
No client database is used (no persistence beyond SharePoint and email).
|
|
23
|
+
|
|
24
|
+
## Contacts
|
|
25
|
+
|
|
26
|
+
- **Jheanelle Gordon** — primary contact; receives the processing completion email
|
|
27
|
+
- **Magdalena Minta** — CC on completion email
|
|
28
|
+
- devteam@togatech.com — BCC on all emails
|
|
29
|
+
|
|
30
|
+
## Key integration points
|
|
31
|
+
|
|
32
|
+
- Microsoft SharePoint (Microsoft Graph API) — receipt file storage
|
|
33
|
+
- Talos AI (`/api/ai/generate`) — structured receipt data extraction
|
|
34
|
+
- PhpSpreadsheet — Excel generation and statement parsing
|
|
35
|
+
- `_Email` — completion summary + fatal error notifications
|
package/package.json
CHANGED