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

6.1 KiB
Raw Blame History

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.
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.

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.