A manufacturer's next dollar is rarely a new logo — it's the machine you already shipped. The service contract that lapsed, the warranty about to expire, the line running at capacity that wants a second unit. But a vanilla CRM models deals, not deployed assets, so that revenue is invisible: no one can answer "which customers own a press but have no service contract, and whose warranty expires this quarter?" This post makes that question a one-line query.
The problem — and the money
A channel + equipment business sells through dealers and earns for a decade after the sale. The buyer here is the RevOps or service lead who needs the install base and the dealer network inside the CRM, so renewal, upsell, and territory coverage are queries — not spreadsheets stitched to a separate field-service system. The payoff is concrete: surface the $25,000 EU line whose warranty ends 2026-05-01 as an upsell today, and rank the dealer who actually covers that territory above the one who doesn't. That's why this overlay exists — it points the CRM at the install base instead of at net-new firmographics.
This is the CRM suite + the manufacturing overlay. The seven base skills — core, quote/CPQ, account-360, campaign-to-lead and the rest — are unchanged. The overlay only adds: two tables, two columns, a weighted edge kind, and one changed gate. Same bucket, one copy of the rows.
What manufacturing adds
A small, additive diff on top of the suite (skill: crm/manufacturing → the
crm/overlays/manufacturing contract):
- Two tables —
dealers(keydealer_id: the channel — who sells and services in which territory) andinstall_base(keyasset_id: deployed equipment with avalueand awarranty_end— the service/upsell surface). - Two columns on the
crm/coreaccountsmaster —channel_type(direct | dealer | distributor) andterritory— added withALTER TABLE … ADD COLUMNover the pg wire. A column-add, never a second copy. - One weighted edge kind —
serves_territory(dealer account → customer account) inserted into the suite'scrm_edge,weight= 0.9 same-territory, 0.3 cross-territory — a ranked edge read in SQL. - One gate change — account-360's next-best-action prompt targets install-base service + upsell (owns product A but no service contract; warranty near) instead of net-new logos. Same JSON contract.
- One preset — quote/CPQ gains an
orderedstatus and alead_time_dayson quote lines (the quote-to-order leg).
Apply the diff — an ALTER, two new tables, and seed the channel plus two assets:
In the crm bucket, apply the manufacturing overlay: ALTER accounts ADD channel_type + territory; create dealers (key dealer_id) and install_base (key asset_id, incl. value double + warranty_end); add Initech as an NA dealer, tag Acme EU's territory, and register a $40,000 NA press (warranty 2027) and a $25,000 EU line (warranty_end 2026-05-01).
data_pg→data_table_create→data_table_upsertAdded channel_type + territory to accounts (ALTER over pg — the master, not a copy). Created dealers + install_base. Seeded Initech (dealer, NA), tagged acme.eu (EU), and two assets: asset-na-1 $40,000 (warranty 2027-06-01) and asset-eu-1 $25,000 (warranty_end 2026-05-01 — the upsell target).
export BUCKET=crm # the SAME bucket the CRM suite built — the overlay only ADDs
# 1) column-adds on the crm/core accounts master — ALTER over the pg wire, never a second copy
dodil data pg -b "$BUCKET" "ALTER TABLE accounts ADD COLUMN channel_type VARCHAR"
dodil data pg -b "$BUCKET" "ALTER TABLE accounts ADD COLUMN territory VARCHAR"
# 2) the channel + the install base (two new merge-keyed tables)
dodil data table create dealers -b "$BUCKET" --merge-key dealer_id \
--columns-json '[{"name":"dealer_id","type":"string","nullable":false},{"name":"account_domain","type":"string"},{"name":"territory","type":"string"},{"name":"tier","type":"string"},{"name":"authorized_lines","type":"string"}]'
dodil data table create install_base -b "$BUCKET" --merge-key asset_id \
--columns-json '[{"name":"asset_id","type":"string","nullable":false},{"name":"account_domain","type":"string"},{"name":"product_id","type":"string"},{"name":"serial","type":"string"},{"name":"installed_at","type":"string"},{"name":"warranty_end","type":"string"},{"name":"value","type":"double"}]'
# 3) seed the channel + assets — territory on the account drives the rollup
dodil data table upsert dealers -b "$BUCKET" --row '{"dealer_id":"d-initech","account_domain":"initech.com","territory":"NA","tier":"gold","authorized_lines":"press-900,line-200"}'
dodil data table upsert accounts -b "$BUCKET" --merge --row '{"org_domain":"initech.com","name":"Initech","channel_type":"dealer","territory":"NA"}'
dodil data table upsert accounts -b "$BUCKET" --merge --row '{"org_domain":"acme.eu","channel_type":"direct","territory":"EU"}'
dodil data table upsert install_base -b "$BUCKET" --row '{"asset_id":"asset-na-1","account_domain":"initech.com","product_id":"press-900","value":40000,"warranty_end":"2027-06-01"}'
dodil data table upsert install_base -b "$BUCKET" --row '{"asset_id":"asset-eu-1","account_domain":"acme.eu","product_id":"line-200","value":25000,"warranty_end":"2026-05-01"}'Now the payoff read — the territory install-base rollup and the weighted dealer ranking:
In crm, roll up install-base value by territory with a count of warranties expiring soon, then rank the serves_territory dealer edges by weight.
data_sql→data_pgNA = 1 asset $40,000, 0 expiring. EU = 1 asset $25,000, 1 expiring (warranty_end 2026-05-01 — the service/upsell target). The serves_territory edges rank Initech (NA, 0.9) above Acme (EU, 0.3): the dealer who actually covers the territory wins.
# territory install-base rollup — SUM(value) + warranties expiring, JOINed to accounts.territory
dodil data sql -b "$BUCKET" "
SELECT a.territory,
count(*) AS assets,
sum(i.value) AS install_value,
sum(CASE WHEN i.warranty_end < '2026-12-01' THEN 1 ELSE 0 END) AS expiring
FROM install_base i JOIN accounts a ON a.org_domain = i.account_domain
GROUP BY a.territory ORDER BY install_value DESC"
# NA | 1 | 40000 | 0
# EU | 1 | 25000 | 1 <- warranty_end 2026-05-01 = the upsell target
# weighted dealer edges — serves_territory carries a weight (insert the edges, then re-CREATE crm_graph)
dodil data pg -b "$BUCKET" "
SELECT s.name AS dealer, ac.name AS account, e.weight
FROM crm_edge e
JOIN crm_node s ON s.id = e.src
JOIN crm_node a ON a.id = e.dst
JOIN accounts ac ON ac.org_domain = a.biz_key
WHERE e.rel = 'serves_territory' ORDER BY e.weight DESC"
# Initech | ... | 0.9 (same-territory, NA)
# Acme | ... | 0.3 (cross-territory, EU)The next-best-action gate (account-360) then reads this surface directly: whose warranty is near, who
owns a machine but no service contract — the whitespace source is install_base, not a lookalike list.
The gate mechanism is unchanged from the base skill; only the prompt shifts.
Scaffold it — the one-shot
With the DODIL MCP connected, one prompt composes the suite and this overlay:
Scaffold a CRM for my manufacturing company — the CRM suite plus the manufacturing overlay.
Base: the full crm/core suite (accounts, contacts, leads, opportunities, activities; the
account-hierarchy graph; the quote/CPQ + account-360 + campaign-to-lead workflows).
Then apply the manufacturing overlay on the SAME bucket:
1. ALTER accounts ADD channel_type + territory (pg wire — the master, not a copy).
2. Create dealers (key dealer_id) and install_base (key asset_id, value double + warranty_end).
3. Insert serves_territory edges into crm_edge (weight 0.9 same-territory, 0.3 cross), then
(re-)CREATE crm_graph so the new edges are snapshotted.
4. Point account-360's next-best-action prompt at install-base service/upsell (owns the machine,
lacks the service contract; warranty near) — same JSON contract.
5. Add an `ordered` status + quote_lines.lead_time_days for the quote-to-order leg.
Seed the demo above and show the territory rollup + the dealer-edge ranking.Verify
Every number below was live-validated on 2026-09-02 (org IHDIASH, throwaway bucket, torn down after):
# columns land + partial-merge sets them
dodil data pg -b "$BUCKET" "ALTER TABLE accounts ADD COLUMN channel_type VARCHAR" # + territory
# territory rollup: NA = $40,000 (0 expiring), EU = $25,000 (1 expiring, warranty_end 2026-05-01)
dodil data sql -b "$BUCKET" "SELECT a.territory, sum(i.value) FROM install_base i
JOIN accounts a ON a.org_domain = i.account_domain GROUP BY a.territory"
# weighted dealer graph: Bolt MATCH (d)-[:serves_territory]->(a) WHERE id(d)=3 -> 2 account neighbors;
# the SQL edge-rank puts Initech (NA, 0.9) above Acme (EU, 0.3)
dodil data bolt -b "$BUCKET" -g crm_graph "MATCH (d)-[:serves_territory]->(a) WHERE id(d)=3 RETURN a"The ALTER-add mechanism, the two tables, the $40k/$25k territory rollup, and the weighted dealer
ranking are proven live. The quote-to-order ordered status and the install-base NBA prompt reuse
mechanisms already proven in the base quote/CPQ and account-360 skills.
Connect your tools
Everything the overlay wrote lives in the one DataK3 bucket, reachable by your own stack — a
field-service dashboard reads install_base over the Postgres wire; a BI tool charts the territory
rollup; a Bolt/Neo4j driver walks the dealer graph. data connect crm prints the endpoints. Full,
live-validated walkthrough: Connect your tools.
Composes
This page is not a fork of the CRM — it is a composition:
- Base: the seven CRM suite skills (
crm/core,crm/quote-cpq,crm/account-360,crm/campaign-to-lead, …) — the system of record, unchanged. - Overlay:
crm/overlays/manufacturing— the additive diff above (two tables, two columns, the weightedserves_territoryedge, the install-base NBA gate).
Read the base to learn the mechanics; this overlay is the small, industry-specific diff on top.