Compressed memory store 104 -> 71 files via four passes: - Syncro: 19 scattered feedback_syncro_* files merged into 3 rule files (api/billing/workflow) + an on-demand feedback_syncro_history.md for incident detail, quotes, and tech/product ID tables. - Four near-duplicate merges: Howard paste-safety, Pluto build server, Howard backend deferral, IX server access (ssh+tailscale). - Per-cluster rule/state/history split applied to GuruConnect (2->1), Dataforth (3->2), Cascades (7->3), GuruRMM (13->3). - New reference_resource_map.md: single auto-loaded cheatsheet for "do I have access to X and how do I connect from this machine?" - MEMORY.md rewritten to match the new layout. Health: broken backlinks 8->7, overlap clusters 12->5, orphans 17->0.
6.1 KiB
name, description, metadata
| name | description | metadata | ||
|---|---|---|---|---|
| Syncro billing rules — products, rates, taxes, attribution, emergency, warranty | How to bill a Syncro ticket correctly — fetch live rates, use real product names, pick the right labor type for the delivery channel, set taxable=false on labor (AZ), warranty product 1049360, emergency ×1.5 (branch by prepay_hours), preserve original tech's user_id on corrections, estimate hardware uses product 32252. |
|
Rules only. Incident detail, verbatim Mike quotes, ticket numbers, dates, the tech user_id table, and the labor-product table all live in feedback_syncro_history — read on-demand when judging an edge case. API mechanics: feedback_syncro_api. Workflow: feedback_syncro_workflow.
.claude/commands/syncro.md is the authoritative live product table.
1. Bill with add_line_item directly — never the timer workflow
POST /tickets/{id}/add_line_item is the billing path for ALL work (labor, warranty, internal, hardware). The timer workflow (timer_entry → charge_timer_entry) is not used.
Payload: product_id, quantity (decimal hours), price_retail (fetched live), name (the product's REAL name — see §3), description (free-text work narrative), taxable: false for labor.
2. ALWAYS fetch the rate live
Fetch price_retail from GET /products/<id> → .product.price_retail before billing. The product-ID table in .claude/commands/syncro.md is valid for IDs but not dollar amounts — rates vary by contract and change.
RATE=$(curl -s "$BASE/products/$PRODUCT_ID?api_key=$API_KEY" | jq -r '.product.price_retail')
Use $RATE for drafts, the user preview, and the price_retail field.
3. NEVER invent or rename labor line items
Every labor line MUST be an existing Syncro product, billed under its REAL name (from GET /products/<id> → .product.name, verbatim). Work-specific narrative goes in description, never the name.
Why: invented names break the Syncro → QuickBooks sync. QB maps each labor line to an existing item; a fabricated name has no QB match and messes up the accounting. If no existing product fits, STOP and ask Mike — never invent one.
Product table lives in feedback_syncro_history (and .claude/commands/syncro.md).
4. Labor type must match delivery channel — never "Prepaid project labor"
Pick the labor product matching how work was delivered: remote (most common), onsite, in-shop, or web. Resolve product_id via GET /products?search=remote+labor etc.
Never default to "Prepaid project labor" — it is exempt and does NOT consume hours from a customer's prepaid block. Block accounting silently drifts.
Verify: after billing a prepay-block customer, confirm the block balance dropped by the expected hours. If it didn't, the labor type was wrong.
5. Labor is NEVER taxable in Arizona
Pass "taxable": false explicitly on every labor line. The product config has taxable: false, but add_line_item does not inherit it — posts as taxable: true regardless. Applies to remote, onsite, in-shop, emergency, warranty, prepaid.
6. Warranty / no-charge → product 1049360, never patch the price
Warranty work uses product_id: 1049360 ("Labor- Warranty work", $0/hr, non-taxable). The line generates at $0 from price_retail × quantity — no need to flag or patch anything.
Do NOT pick a regular labor product and try to neutralize it with billable: false or by patching price_retail to 0. Prices are set by selecting the correct product. If you reach for update_line_item to drop a price, that's the signal to back up and pick a different product_id.
The only legitimate update_line_item price_retail use is the Syncro auto-gen-zero recovery case (auto-line came in at $0 instead of the product's rate).
7. Emergency / after-hours — ×1.5 applied ONCE; branch by prepay_hours
GET /customers/<id> and read prepay_hours BEFORE adding any emergency line. Emergency = time-and-a-half, applied ONCE. Never bill a separate regular line + emergency line for the same hours.
No prepaid block (prepay_hours == 0):
- product
26184, quantity = actual hours (do NOT also ×1.5 the quantity) price_retailby delivery channel (the 1.5× lives in the dollars):- Onsite emergency =
$262.50(175 × 1.5; 26184's default). - Remote / In-Shop emergency =
$225(150 × 1.5) → overrideprice_retailto225.
- Onsite emergency =
Prepaid block (prepay_hours > 0):
- product
26184, quantity = actual hours × 1.5 (premium goes in the QUANTITY) - Delivery channel / dollar rate is irrelevant; prepaid blocks debit by quantity. Invoice nets to $0; block debits hrs×1.5.
- e.g. 1.5 emergency hrs →
26184@2.25.
Always set price_retail explicitly — the rate doesn't auto-populate and the line posts $0 if omitted. Verify after: .invoice.total (non-prepaid) or block decrement (prepaid).
8. Corrections preserve the ORIGINAL tech's attribution; ticket ownership is sticky
Labor lines: when fixing a wrong line, preserve the original tech's user_id so their commission isn't lost.
- Prefer
update_line_itemin place — it preservesuser_id. - If you must remove + re-add: the new line defaults to the API-key owner's
user_id. Explicitly setuser_idto the original tech onadd_line_item, or PUT-fix it afterward. - Determine the original tech from
.ticket.user_idand the line's.user_idBEFORE correcting; verify after.
Ticket ownership: adding notes or labor does NOT change .ticket.user_id. Multiple techs routinely work the same ticket. Only change ticket ownership when explicitly asked. Status PUTs send only status; line edits use update_line_item; neither touches user_id.
Tech user_id table → feedback_syncro_history.
9. Estimate hardware → product 32252
All hardware on estimates uses one generic product: product_id: 32252 ("Hardware", price_retail: 0.0). Differentiate via name ("Dell OptiPlex 7010") and price_retail (actual cost). Hardware is typically taxable: true. Never look up individual hardware product IDs — there's only one.