Fix /syncro billing: use timer_entry + labor products, not invoice line items

Timer entries use POST /tickets/{id}/timer_entry with labor product IDs
(not invoice products). "Make Invoice" converts timers to invoice.
Documented 7 common labor products with IDs. Fixed line_items path to
/invoices/{id}/line_items.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 11:14:12 -07:00
parent 046175af3a
commit 392c42710c

View File

@@ -84,26 +84,38 @@ API_KEY=$(sops -d D:/vault/msp-tools/syncro.sops.yaml | python -c "import sys,ya
| Get customer | GET | `/customers/<id>` |
| Create customer | POST | `/customers` |
#### Timer Entries (add time to ticket)
| Operation | Method | Endpoint | Body |
|---|---|---|---|
| Add time | POST | `/tickets/<id>/timer_entry` | `{"start_at": "ISO8601", "end_at": "ISO8601", "notes": "...", "billable": true, "product_id": N}` |
| List timers | GET | `/ticket_timers?ticket_id=<id>` |
**IMPORTANT:** `product_id` must be a **labor product**, not an invoice product. Common labor products:
- `1190473` — Labor - Remote Business (standard remote work)
- `26118` — Labor - Onsite Business
- `26184` — Labor - Emergency or After Hours Business
- `9269129` — Labor - Prepaid Project Labor
- `9269124` — Labor - Internal Labor
- `26117` — Fee - Travel Time
- `68055` — Labor - Website Labor
#### Invoices
| Operation | Method | Endpoint | Body |
|---|---|---|---|
| List invoices | GET | `/invoices?per_page=25` |
| Get invoice | GET | `/invoices/<id>` |
| Create from ticket | POST | `/invoices` | `{"ticket_id": N, "customer_id": N}` |
| Create from ticket | POST | `/invoices` | `{"ticket_id": N, "customer_id": N, "category": "Standard"}` |
| Delete invoice | DELETE | `/invoices/<id>` | — |
#### Line Items
**"Make Invoice" flow:** Timer entries on the ticket become invoice line items when you POST `/invoices` with the ticket_id. This is the equivalent of clicking "Make Invoice" in the GUI.
#### Invoice Line Items
| Operation | Method | Endpoint | Body |
|---|---|---|---|
| Add line item | POST | `/line_items` | `{"invoice_id": N, "item": "...", "quantity": 1, "price": 125.00}` |
#### Ticket Timers
| Operation | Method | Endpoint |
|---|---|---|
| List | GET | `/ticket_timers?ticket_id=<id>` |
| Create | POST | `/ticket_timers` |
| Add line item | POST | `/invoices/<id>/line_items` | `{"item": "...", "quantity": 1, "price": 125.00, "product_id": N}` |
### Display formatting
@@ -127,11 +139,16 @@ When showing ticket detail, include:
### Billing workflow
When `/syncro bill <number>` is called:
1. Get ticket details + time entries
2. Show summary: total billable time, line items that would be created
3. Ask for confirmation: "Create invoice for $X? [yes/no]"
4. On yes: POST `/invoices` with ticket_id, then add line items
5. Update ticket status to "Invoiced"
1. Get ticket details + existing time entries
2. If no timer entries exist, ask: "Add time? How many hours + labor type?"
3. Add timer entry via `POST /tickets/{id}/timer_entry` with correct labor product_id
4. Show summary: total billable time, labor product, estimated total
5. Ask for confirmation: "Create invoice? [yes/no]"
6. On yes: `POST /invoices` with `{"ticket_id": N, "customer_id": N, "category": "Standard"}`
7. Update ticket status to "Invoiced"
**The flow is: timer entries → Make Invoice → Syncro auto-creates line items from timers.**
Do NOT create line items manually unless adding non-labor charges (parts, products, etc.).
### Error handling