Files
claudetools/.claude/memory/feedback_syncro_billing.md
Mike Swanson 0c000109dc chore(memory): consolidate scattered feedback/project/reference files
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.
2026-06-01 16:25:45 -07:00

105 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: Syncro billing rules — products, rates, taxes, attribution, emergency, warranty
description: 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.
metadata:
type: feedback
---
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.
```bash
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_retail` by 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) → override `price_retail` to `225`.
**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_item` in place** — it preserves `user_id`.
- **If you must remove + re-add:** the new line defaults to the **API-key owner's** `user_id`. Explicitly set `user_id` to the original tech on `add_line_item`, or PUT-fix it afterward.
- Determine the original tech from `.ticket.user_id` and the line's `.user_id` BEFORE 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.