Data model
Schema, relationships, D1 tables, KV/R2 usage
Storage layers
- DB — D1 SQLite gfs-netsuite (3818ecd5-995e-4694-a08b-a273c94291da). Mirror of NetSuite entities + transactions.
- CACHE — KV namespace e880e40bc3674963bc37cf90e02f6369. Short-TTL caching.
- STORAGE — R2 bucket gfs-files. Binary artifacts.
Entity relationship diagram
D1 tables and the FK relationships they encode. Tables shown with up to 10 columns each — open per-table detail below for the full column list.
Table inventory 18 tables
| Table | Cols | Rows | Indexes |
|---|---|---|---|
| customers | 13 | 283 | idx_customers_name(companyname), idx_customers_salesrep(salesrep) |
| vendors | 12 | 484 | idx_vendors_name(companyname) |
| items | 28 | 1,265 | idx_items_itemid(itemid), idx_items_type(itemtype), idx_items_dept(department) |
| contacts | 9 | 490 | idx_contacts_company(company) |
| employees | 17 | 116 | — |
| transactions | 12 | 102,367 | idx_txn_type(type), idx_txn_date(trandate), idx_txn_entity(entity), idx_txn_type_date(type, trandate), idx_txn_year(year) |
| invoice_lines | 11 | 28,528 | idx_invlines_txn(txn_id), idx_invlines_item(item), idx_invlines_year(year) |
| so_lines | 11 | 29,098 | idx_solines_txn(txn_id), idx_solines_item(item) |
| vb_lines | 11 | 21,315 | idx_vblines_txn(txn_id), idx_vblines_item(item) |
| gl_accounts | 6 | 152 | — |
| customer_pricing | 8 | 1,264 | idx_pricing_cust(customer_id), idx_pricing_item(item_id) |
| sync_log | 7 | — | — |
| ref_terms | 1 | — | — |
| ref_departments | 1 | — | — |
| ref_classes | 1 | — | — |
| ref_locations | 1 | — | — |
| ref_categories | 1 | — | — |
| ref_shipping | 1 | — | — |
Example D1 queries
Run via wrangler d1 execute gfs-netsuite --remote --command="…".
Revenue YTD
Open AR
Top 10 customers (2026)
Customer pricing — stale
Item velocity (units shipped 2026)
Vendor bill activity
Sync log tail
Date format reference
Different date representations live in D1 and at the NS edge — common source of join/comparison bugs.
| Column / Source | Format | Example |
|---|---|---|
| transactions.trandate | YYYY-MM-DD | 2026-05-19 |
| transactions.lastmodifieddate | ISO 8601 UTC | 2026-05-19T13:33:03.674Z |
| transactions.year | YYYY (int) | 2026 |
| NetSuite NSDate via Chartstone | MM/DD/YYYY hh:mm AM/PM | 05/19/2026 1:33 PM |
| D1 native | TEXT (no DATE type) | All dates stored as TEXT |
SuiteQL admin query library 30 queries
Admin SuiteQL queries — copy/paste into Chartstone Pro or any SuiteQL runner. All read-only.
Custom records 1 queries
Custom record types with row counts
Lists each customrecordtype with how many records currently exist. Empty types are removal candidates.
Dunning 1 queries
Customers by current dunning level
Counts per dunning level — operational signal for the collections team.
Field audit 4 queries
Custom fields never populated
Custom body/entity/item fields that are NULL across every record — candidates for removal.
Customer fields coverage
Per-field NULL counts on the customer record — identifies onboarding-data gaps.
Vendor fields coverage
Per-field NULL counts on the vendor record — flags the documented 93% missing terms and 70% missing emails.
Item attributes coverage
GTIN, weight, storage temp coverage on inventory items — flags warehouse/BOL blockers.
Files 2 queries
Largest files in the File Cabinet
Top 50 files by size. Watch for runaway logs, screenshot bloat, or accidental uploads.
Files not accessed in 1 year
Stale files in the File Cabinet — candidates for archival to R2.
Integration audit 2 queries
Active OAuth/TBA tokens
Inventory of active integration tokens with role and last-used (where available).
RESTlet call volume (last 7 days)
Per-RESTlet execution counts — identifies hot integrations and ones that have gone silent.
Inventory 2 queries
Items with zero stock across all locations
Items showing as stocked but with zero on-hand everywhere — pruning or restock signal.
Inventory aging — items not received in 90 days
Items that have not had an item-receipt in the last 90 days. Slow-mover signal.
Period close 2 queries
Open accounting periods
Every period currently open to posting — a Tier 1 gap until closed through FY2024.
Period close blockers — unposted transactions in old periods
Transactions sitting unposted in periods you would otherwise close.
Role audit 3 queries
Roles with permission counts
Inventory of every role with the number of explicit record-type permissions assigned. Surfaces over-permissive roles.
Roles with full access on sensitive record types
Lists every role that has level=FULL on customer, vendor, employee, transaction, file, or company-information.
Users assigned to each role
Count of active employees per role. Highlights single-user roles (bus factor).
Saved search audit 1 queries
Saved searches not opened in 12 months
Stale saved searches — candidates to archive. Requires the LoginAudit / SavedSearchExecution table.
Script audit 3 queries
Deployed but never executed
Scripts with deployment records but no entries in the scriptexecutionlog within the lookback window — candidates for deactivation.
Top 20 scripts by execution time
Performance hot-spots — scripts consuming the most governance / wall-clock per execution.
Scripts in TESTING status
TESTING-status scripts run only for the deployer — finds dormant/half-deployed automation.
Suitelet audit 1 queries
Publicly accessible Suitelets (isonline=T)
Lists every Suitelet deployed with Available Without Login — the #1 security exposure in this account.
System health 3 queries
Sync log entries (last 50)
Most-recent entries in the local sync log — paste into the D1 console after replacing transaction → sync_log.
D1 row counts (per table)
Counts every table in the D1 mirror. Run against D1 via wrangler d1 execute.
Bundles installed (with version)
Inventory of every installed SuiteApp / bundle. Use to plan retirement of unused bundles (SII, Intrastat, etc.).
Transaction analysis 3 queries
Transactions posted to closed periods (should be zero)
Catches transactions that landed in periods that should have been closed — accounting integrity check.
Open invoices by aging bucket
AR aging by bucket. Use as the source for an admin AR aging dashboard.
Transactions modified by user (audit trail)
Who touched what, when. Useful for security investigations.
Workflow audit 2 queries
Workflows in TESTING owned by individuals
Personal-owner workflows in TESTING affect production for the owner only — silent inconsistency.
Active workflows by record type
Distribution of active workflows across record types — surfaces over-engineered processes.
Per-table detail
Columns, types, and FK annotations per table. Indexes listed where they exist.
customers 13 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY | |
| companyname | TEXT NOT NULL | |
| entityid | TEXT | |
| TEXT | ||
| phone | TEXT | |
| fax | TEXT | |
| terms | INTEGER | FK → ref_terms |
| salesrep | INTEGER | FK → employees |
| category | INTEGER | FK → ref_categories |
| creditlimit | REAL | |
| isperson | TEXT DEFAULT 'F' | |
| datecreated | TEXT | |
| lastmodifieddate | TEXT |
Indexes
- idx_customers_name on (companyname)
- idx_customers_salesrep on (salesrep)
vendors 12 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY | |
| companyname | TEXT NOT NULL | |
| entityid | TEXT | |
| TEXT | ||
| phone | TEXT | |
| terms | INTEGER | FK → ref_terms |
| creditlimit | REAL | |
| is1099eligible | TEXT DEFAULT 'F' | |
| taxidnum | TEXT | |
| datecreated | TEXT | |
| lastmodifieddate | TEXT | |
| isinactive | TEXT DEFAULT 'F' |
Indexes
- idx_vendors_name on (companyname)
items 28 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY | |
| itemid | TEXT NOT NULL | |
| displayname | TEXT | |
| itemtype | TEXT | |
| class | INTEGER | FK → ref_classes |
| department | INTEGER | FK → ref_departments |
| upccode | TEXT | |
| averagecost | REAL | |
| cost | REAL | |
| isinactive | TEXT DEFAULT 'F' | |
| custitem1 | INTEGER | |
| custitem3 | TEXT | |
| custitem4 | TEXT | |
| custitem6 | TEXT | |
| custitem_as_milk | TEXT DEFAULT 'F' | |
| custitem_as_eggs | TEXT DEFAULT 'F' | |
| custitem_as_peanuts | TEXT DEFAULT 'F' | |
| custitem_as_soybeans | TEXT DEFAULT 'F' | |
| custitem_as_wheat | TEXT DEFAULT 'F' | |
| custitem_as_fish | TEXT DEFAULT 'F' | |
| custitem_as_tree_nuts | TEXT DEFAULT 'F' | |
| custitem_as_crustacean | TEXT DEFAULT 'F' | |
| custitem_as_celery | TEXT DEFAULT 'F' | |
| custitem_as_lupin | TEXT DEFAULT 'F' | |
| custitem_as_molluscs | TEXT DEFAULT 'F' | |
| custitem_as_mustard | TEXT DEFAULT 'F' | |
| custitem_as_sesame | TEXT DEFAULT 'F' | |
| custitem_as_sulphur_dioxide | TEXT DEFAULT 'F' |
Indexes
- idx_items_itemid on (itemid)
- idx_items_type on (itemtype)
- idx_items_dept on (department)
contacts 9 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY | |
| firstname | TEXT | |
| lastname | TEXT | |
| TEXT | ||
| phone | TEXT | |
| title | TEXT | |
| company | INTEGER | FK → vendors |
| company_name | TEXT | |
| isinactive | TEXT DEFAULT 'F' |
Indexes
- idx_contacts_company on (company)
employees 17 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY | |
| entityid | TEXT | |
| firstname | TEXT | |
| lastname | TEXT | |
| TEXT | ||
| title | TEXT | |
| phone | TEXT | |
| department | INTEGER | FK → ref_departments |
| class | INTEGER | FK → ref_classes |
| location | INTEGER | FK → ref_locations |
| supervisor | INTEGER | FK → employees |
| isinactive | TEXT DEFAULT 'F' | |
| issalesrep | TEXT DEFAULT 'F' | |
| issupportrep | TEXT DEFAULT 'F' | |
| hiredate | TEXT | |
| releasedate | TEXT | |
| giveaccess | TEXT DEFAULT 'F' |
transactions 12 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY | |
| tranid | TEXT | |
| trandate | TEXT NOT NULL | |
| type | TEXT NOT NULL | |
| entity | INTEGER | FK → vendors |
| entity_name | TEXT | |
| foreigntotal | REAL | |
| status | TEXT | |
| duedate | TEXT | |
| shipdate | TEXT | |
| memo | TEXT | |
| year | INTEGER GENERATED ALWAYS AS (CAST(SUBSTR(trandate, -4) AS INTEGER)) STORED |
Indexes
- idx_txn_type on (type)
- idx_txn_date on (trandate)
- idx_txn_entity on (entity)
- idx_txn_type_date on (type, trandate)
- idx_txn_year on (year)
invoice_lines 11 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY AUTOINCREMENT | |
| txn_id | INTEGER | FK → transactions |
| tranid | TEXT | |
| trandate | TEXT | |
| customer | TEXT | |
| item | INTEGER | FK → items |
| item_name | TEXT | |
| quantity | REAL | |
| rate | REAL | |
| netamount | REAL | |
| year | INTEGER |
Indexes
- idx_invlines_txn on (txn_id)
- idx_invlines_item on (item)
- idx_invlines_year on (year)
so_lines 11 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY AUTOINCREMENT | |
| txn_id | INTEGER | FK → transactions |
| tranid | TEXT | |
| trandate | TEXT | |
| customer | TEXT | |
| item | INTEGER | FK → items |
| item_name | TEXT | |
| quantity | REAL | |
| rate | REAL | |
| netamount | REAL | |
| year | INTEGER |
Indexes
- idx_solines_txn on (txn_id)
- idx_solines_item on (item)
vb_lines 11 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY AUTOINCREMENT | |
| txn_id | INTEGER | FK → transactions |
| tranid | TEXT | |
| trandate | TEXT | |
| vendor | TEXT | |
| item | INTEGER | FK → items |
| item_name | TEXT | |
| quantity | REAL | |
| rate | REAL | |
| netamount | REAL | |
| year | INTEGER |
Indexes
- idx_vblines_txn on (txn_id)
- idx_vblines_item on (item)
gl_accounts 6 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY | |
| acctnumber | TEXT | |
| fullname | TEXT | |
| accttype | TEXT | |
| balance | REAL | |
| isinactive | TEXT DEFAULT 'F' |
customer_pricing 8 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY AUTOINCREMENT | |
| customer_id | INTEGER | |
| customer | TEXT | FK → customers |
| item_id | INTEGER | |
| item_name | TEXT | |
| price | REAL | |
| level | INTEGER | |
| currency | INTEGER |
Indexes
- idx_pricing_cust on (customer_id)
- idx_pricing_item on (item_id)
sync_log 7 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY AUTOINCREMENT | |
| sync_type | TEXT | |
| started_at | TEXT | |
| completed_at | TEXT | |
| records_synced | INTEGER DEFAULT 0 | |
| status | TEXT DEFAULT 'running' | |
| error | TEXT |
ref_terms 1 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY, name TEXT |
ref_departments 1 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY, name TEXT |
ref_classes 1 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY, name TEXT |
ref_locations 1 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY, name TEXT |
ref_categories 1 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY, name TEXT |
ref_shipping 1 columns
| Column | Type | FK |
|---|---|---|
| id | INTEGER PRIMARY KEY, name TEXT |
Source
- schema.sql — canonical schema
- sql/ — 45 one-time D1 loaders
- wrangler.jsonc — binding declarations
- data/suiteql-library.json — admin query library source