fix(invoice): fetch invoice lines from separate API endpoint

Problem:
get_invoice_by_id() returned invoices WITHOUT lines, making it
impossible to analyze invoice details (e.g., filtering by service
types for statistics).

Root Cause:
Dolibarr REST API separates invoice data into two endpoints:
- GET /invoices/{id}       → header only (no lines)
- GET /invoices/{id}/lines → invoice line items

The MCP client only called the header endpoint.

Solution:
- Fetch lines separately via /invoices/{id}/lines endpoint
- Merge lines into invoice response
- Add error handling for backward compatibility with older
  Dolibarr versions that might not support the lines endpoint

Fixes issue reported in GitHub PR #3 comment
https://github.com/latinogino/dolibarr-mcp/pull/3#issuecomment-3906147396

Impact:
Enables detailed invoice analysis, e.g.:
- Filter invoices by line item descriptions
- Calculate statistics per service type
- Analyze quantities and prices per position
This commit is contained in:
latinogino
2026-02-22 16:11:29 +01:00
parent 38d411721f
commit e262d860a3

View File

@@ -627,8 +627,28 @@ class DolibarrClient:
return result if isinstance(result, list) else []
async def get_invoice_by_id(self, invoice_id: int) -> Dict[str, Any]:
"""Get specific invoice by ID."""
return await self.request("GET", f"invoices/{invoice_id}")
"""Get specific invoice by ID including invoice lines.
Note: Dolibarr API separates invoice header and lines into different endpoints.
This method combines both to provide complete invoice data including lines.
"""
invoice = await self.request("GET", f"invoices/{invoice_id}")
# Fetch invoice lines separately (Dolibarr API design)
try:
lines = await self.request("GET", f"invoices/{invoice_id}/lines")
invoice['lines'] = lines if isinstance(lines, list) else []
except DolibarrAPIError as e:
# If lines endpoint fails, return invoice without lines
# This ensures backward compatibility with older Dolibarr versions
self.logger.warning(
"Failed to fetch lines for invoice %s: %s",
invoice_id,
str(e)
)
invoice['lines'] = []
return invoice
async def create_invoice(
self,