From e262d860a37cd7dca0ff53892c5e1877c4ccfa8c Mon Sep 17 00:00:00 2001 From: latinogino <154024559+latinogino@users.noreply.github.com> Date: Sun, 22 Feb 2026 16:11:29 +0100 Subject: [PATCH] fix(invoice): fetch invoice lines from separate API endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/dolibarr_mcp/dolibarr_client.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/dolibarr_mcp/dolibarr_client.py b/src/dolibarr_mcp/dolibarr_client.py index 6bc6c1f..7f501cf 100644 --- a/src/dolibarr_mcp/dolibarr_client.py +++ b/src/dolibarr_mcp/dolibarr_client.py @@ -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,