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:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user