Commit graph

154001 commits

Author SHA1 Message Date
Pichinov-Jose
cdaff51147
Fix: FactureFournisseur::addline parameter order in ToolCrudObjects (#38295)
Unlike Facture::addline() which takes ($desc, $pu, $qty, $txtva, ...),
FactureFournisseur::addline() uses a different parameter order:

  ($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty,
   $fk_product, $remise_percent, ...)

i.e. $qty is in position 6, not 3.

ToolCrudObjects::processAddLine() was calling
FactureFournisseur::addline() with the Facture order, which produced:

- our $qty (1) passed as $txtva  -> effective VAT rate 1%
- our $vat (20) passed as $txlocaltax1 -> wrong local tax
- a hardcoded 0 passed as $qty -> the line was inserted with
  qty=0 and silently dropped from the visible totals

The result: every supplier invoice created via create_other_document
ended up with the header correctly created but a 0.00 EUR total,
with no error returned (lines_added was reported as success).

This patch reorders the parameters to match the actual
FactureFournisseur::addline() signature and adds an inline comment
to warn future maintainers about this Dolibarr API quirk.

Other addline calls in the same switch (invoice/order/proposal,
supplier_order/supplier_proposal) use their respective correct
signatures already and are unaffected.

Tested with: create_other_document object_type=supplier_invoice
with one line (qty=1, unit_price=10, vat_rate=20) -> line now
appears with Total HT 10.00 EUR.
2026-05-18 01:16:32 +02:00
Pichinov-Jose
0e591afe34
Fix: allow free-text invoice/order/proposal lines when no product matches (#38298)
ToolCrudObjects::processAddLine() uses the line's 'description' as a
fallback product identifier when 'product' is not explicitly provided.
It then calls findProduct() to look up an existing Dolibarr product
by ref/barcode/label.

When no product matches (the common case for AI-generated lines that
describe a one-off service or a custom item), findProduct() returns
an error array and the function aborts WITHOUT ever calling addline().
The resulting document gets its header created but ends up with zero
lines and a 0.00 total.

This is contrary to standard Dolibarr behavior: free-text line items
(no product link, just a description and a price) are a first-class
feature of invoices, orders, and proposals.

This patch distinguishes between the two cases:

- Caller explicitly passed 'product': they wanted a specific product
  -> still abort with the original error if not found (unchanged).

- Caller only passed 'description': it's a free-text line ->
  silently fall through with $prod = null. Dolibarr then creates a
  free-text line with the user-provided description, qty, unit_price
  and vat_rate.

Tested with: create_other_document object_type=proposal containing
11 line items, none of which match existing product references ->
all 11 lines are now created with their correct totals (previously
the document was created with 0 lines and a 0.00 total).
2026-05-18 01:16:01 +02:00
Pichinov-Jose
6ee5130f24
Fix: parse_intent.php model resolution (array default and wrong constant) (#38300)
Two bugs combined to make UniversalLLMAdapter::__construct() crash with:

  TypeError: Argument #4 ($model) must be of type string, array given

at parse_intent.php line 301 when the user-configured model wasn't
properly read.

(1) Wrong shape assumption -- getListOfAIServices() declares model
defaults as a NESTED array of the form:

    $servicesList[$key]['textgeneration'] = ['default' => 'model-name']

The previous code read $servicesList[$key]['textgeneration'] (without
['default']) and got the inner array back. That array was then passed
as the $defModel fallback to a (string)-typed constructor argument.

(2) Wrong constant -- the admin UI ("Prompt and custom AI models" tab
in setup.php) stores the per-function model under
AI_API_<SERVICE>_MODEL_TEXT, matching the convention already used by
Ai::generateContent() for the same data. The previous code read
AI_API_<SERVICE>_MODEL (no _TEXT suffix), which is never written by
that form. So the user-configured model was silently ignored and we
fell back to the (array) default from bug #1.

Together these two bugs reliably reproduced the TypeError on any
default install of the AI module where the user had set a custom
text model via the admin UI.

This patch:

- Walks $servicesList[$key]['textgeneration'] correctly (extracting
  ['default'] when it's an array, accepting strings for backward
  compat).
- Tries AI_API_<SERVICE>_MODEL_TEXT first (the constant the admin
  UI writes), and falls back to the legacy AI_API_<SERVICE>_MODEL
  for compatibility with anyone who might have set it manually.
- Defensively coerces the final value to a string and falls back to
  the default if for any reason it's still not a string.

Tested with google (gemini-2.5-flash) and chatgpt configured via
the admin UI -> UniversalLLMAdapter receives a string and the
AI request goes through.
2026-05-18 01:15:23 +02:00
Pichinov-Jose
dbae20fb5e
Fix: ToolCrudObjects header inputSchema JSON Schema compliance (#38299)
create_customer_invoice and create_other_document declare a 'header'
object whose schema has 'required: [socid]' but no 'properties'
entry. This is technically allowed by JSON Schema but stricter
validators flag it as suspicious: the 'required' array references a
property that is not declared in 'properties'.

Beyond compliance, the omission means LLMs (and human reviewers) have
no machine-readable description of which fields are actually accepted
in the 'header' object. The Dolibarr backend already accepts socid,
date, duree_validite, note_public, note_private and others -- this
patch documents the most useful ones explicitly so consumer tools
can produce richer payloads.

Tested with: create_other_document object_type=proposal accepting
a 'header' that contains all of socid + date + duree_validite +
note_public -> all four fields are correctly mapped to the Propal
object (Dolibarr already accepts unknown properties via dynamic
property assignment, so this is purely a schema-documentation change).
2026-05-18 01:14:03 +02:00
Laurent Destailleur
e735bed124 Add AI assistant in top menu #38307 2026-05-18 01:09:26 +02:00
Laurent Destailleur
034647c653 NEW Add picto "AI assistant" 2026-05-18 01:00:27 +02:00
Laurent Destailleur
2cf81fe636 Debug v24 2026-05-18 00:37:43 +02:00
Laurent Destailleur
586b26a153 Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop 2026-05-18 00:18:20 +02:00
Laurent Destailleur
5ae36d72bd Trans 2026-05-18 00:14:00 +02:00
Pichinov-Jose
d0df12ab81
Qual: preserve full tool descriptions for modern LLMs (#38301)
cleanToolSchemaForLLM() truncates each tool description to 3 words
whenever the schema has more than 20 tools. This heuristic likely
made sense for GPT-3.5 (4K context window) but is harmful with the
current crop of LLMs:

- Gemini 2.5 Flash: 1M token context
- GPT-4o: 128K
- Claude Sonnet: 1M

Three words per description is far too aggressive and actively
breaks tool selection. For example:

- create_other_document  -> "Create documents other"
- create_sales_order     -> "Create a CUSTOMER"
- add_line_item          -> "Add a single"

The LLM then has no way to know that create_other_document is what
should be used for supplier_invoice, or that add_line_item modifies
an EXISTING draft. We've observed Gemini concluding that
"supplier_invoice creation is not available" because the truncated
description didn't mention it.

Two complementary changes:

1. Raise the threshold from 20 to 100 -- this effectively disables
   compression for normal installs (~30 tools) while leaving a
   safety net for very large custom installs that register dozens
   of additional tools via the addMcpTools hook.

2. Even when compression IS active, never touch the tool-level
   description. Parameter-level compression (stripping defaults,
   descriptions of optional fields, collapsing complex objects)
   still applies and continues to save tokens. The right answer
   for truly oversized schemas is to filter the toolset upstream
   (which filterToolsProfessional() already does), not to mutilate
   each tool's description.

Tested with: a 30-tool schema sent in full now allows the LLM to
correctly select create_other_document with object_type
'supplier_invoice', 'proposal' and 'supplier_proposal' --
previously it returned a "feature not available" message.
2026-05-18 00:08:47 +02:00
Pichinov-Jose
fd8d0e3646
New: log MCP tools/call invocations to llx_ai_request_log (#38302)
The AI Assistant web UI (parse_intent.php) persists every request to
llx_ai_request_log via ai_log_request() in ai.lib.php. Administrators
can browse the resulting log via the existing Log Viewer
(htdocs/ai/admin/log_viewer.php).

The MCP HTTP server (mcp_server.php) does NOT call this helper. As a
result, external MCP client activity (Claude Desktop, Claude Code CLI,
MCP Inspector, custom scripts, etc.) is invisible to administrators
-- only dol_syslog() entries land in documents/dolibarr.log, which
is much harder to filter and audit.

This patch adds an mcp_log_request() helper alongside the existing
auth/dispatch logic in mcp_server.php. The helper:

- Persists every tools/call invocation to llx_ai_request_log
- Filters out lifecycle methods (initialize, ping,
  notifications/initialized, tools/list, ...) to avoid log spam
- Logs with provider='mcp' so administrators can filter MCP traffic
  in the Log Viewer
- Captures the tool name + arguments, success/error status, raw
  request payload and raw response payload
- Charges the elapsed time per-call (also per-call inside batches)

The helper is gated by the same AI_LOG_REQUESTS toggle that already
controls the web UI logging -- no new admin setting introduced.

Tested with: search_products, create_other_document, search_invoice,
get_sales_report from Claude Desktop and Claude Code CLI -- each
call appears as a new row in the Log Viewer with the right tool name,
status, payloads, and elapsed time.
2026-05-18 00:06:22 +02:00
Pichinov-Jose
325000c398
Fix: get_sales_report respects the group_by parameter (#38303)
The get_sales_report tool declares a group_by parameter with enum
values [thirdparty, product, month] in its inputSchema, but the
implementation ignored $args['group_by'] entirely and always
returned a flat list of invoices.

Meanwhile getPurchaseReport() in the same class already implemented
the correct grouped pattern (s.nom or DATE_FORMAT date) -- this
patch ports that pattern to getSalesReport(), with three modes:

- With a socid: detailed invoice list for that customer (unchanged
  behavior, kept for backward compatibility).

- Without socid, group_by=thirdparty (default): aggregate by
  customer with COUNT and SUM(total_ttc) -> Top-customers report.

- Without socid, group_by=month: aggregate by DATE_FORMAT(datef,
  '%Y-%m') -> monthly revenue report.

- Without socid, group_by=product: aggregate by product reference
  via INNER JOIN on facturedet, with COALESCE(p.ref, fd.description)
  so free-text lines without a product link still appear under
  their description. SUM(fd.total_ttc) gives per-product revenue,
  COUNT(DISTINCT f.rowid) gives the number of invoices that
  contained the product.

Tested:
- group_by=thirdparty: top customers correctly ranked by total.
- group_by=month: 2026-04 and 2026-05 buckets correctly populated.
- group_by=product: top product (SAM-BIK3-CAS-1430-24-N) correctly
  ranked at 1 276 EUR over 5 invoices.
- With socid: unchanged detailed listing.

Note: the 'product' grouping is also missing from getPurchaseReport()
in the current code. That can be addressed in a follow-up patch on
the same pattern.
2026-05-18 00:04:14 +02:00
Jon Bendtsen
f9e3058145
NEW add text field to search for label (#38306)
* NEW add text field to search for label

* PHAN fix #16134 - remove unused variable

---------

Co-authored-by: Jon Bendtsen <jonbendtsen@jonb.dk>
2026-05-18 00:02:31 +02:00
Jon Bendtsen
8edded0e14
New show linked member status on Thirdparty card (#38308)
Co-authored-by: Jon Bendtsen <jonbendtsen@jonb.dk>
2026-05-17 23:59:35 +02:00
Laurent Destailleur
4108a164ee Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop 2026-05-17 23:58:14 +02:00
Laurent Destailleur
f8fba79063 Doc 2026-05-17 23:57:26 +02:00
Laurent Destailleur
95b57550d3 Fix curl direct use is forbidden. Must use getUrlContent(). 2026-05-17 23:55:13 +02:00
Alexandre SPANGARO
23e9dec86e
NEW UIUX Intervention - Merge two-step creation form into a single unified form (#38284)
* UIUX Intervention - Fix: Merge two-step creation form into a single unified form

* CI

* CI
2026-05-17 23:42:31 +02:00
Pichinov-Jose
34aa548ba7
Fix: PDF.js text extraction for PDFs using custom CID fonts (TCPDF etc.) (#38297)
The local document parser in ai_assistant.js calls
getDocument({ data: arrayBuffer }) without cMapUrl and
standardFontDataUrl. As a consequence, PDFs that ship with custom
CID fonts referencing ToUnicode CMaps (which is the norm for files
produced by TCPDF, wkhtmltopdf, iText and most server-side
generators) cannot be decoded properly: PDF.js getTextContent()
returns raw glyph codes and the resulting "text" looks like garbled
base64.

This patch passes the two URLs to getDocument(), pointing them at
the jsdelivr CDN package (cdnjs, which is used elsewhere for the
pdf.js bundle itself, does not distribute the cmaps/ and
standard_fonts/ resource directories).

Tested with a TCPDF-generated invoice that previously returned
unreadable base64-like output. Text now extracts cleanly, including
French accented characters.
2026-05-17 21:50:32 +02:00
Pichinov-Jose
5e3d1a86a0
New: accept api_key in query string for header-less MCP clients (#38292)
The MCP server currently accepts the API key via X-API-Key or
Authorization: Bearer headers only. This works for most MCP clients
(Claude Code CLI, MCP Inspector, mcp-proxy, custom scripts) which let
users configure arbitrary headers.

However, Claude Desktop's "Custom Connectors" feature (currently in
public beta) only exposes OAuth credential fields in its UI. There is
no way for end users to pass a custom X-API-Key or Authorization
header from that client today.

Without a fallback, Claude Desktop users cannot connect to a Dolibarr
MCP server at all (HTTP 401 on every request), even though all other
prerequisites are met.

This patch adds a query-string fallback: ?api_key=XXX or ?key=XXX.
The header-based methods remain preferred and are tried first; the
query string is checked only when no valid header was provided.

Security considerations:
- Query-string credentials appear in webserver access logs and
  potentially in Referer headers. This is documented in an inline
  comment alongside the code.
- Administrators relying on the fallback should restrict access at
  the webserver level (IP allowlist, mTLS, etc.) and/or rotate
  AI_MCP_API_KEY regularly.
- Header-based auth remains the recommended path and is unchanged.

Tested with:
- Claude Desktop Custom Connectors (beta): connection now succeeds
- Claude Code CLI with --header: existing header path unchanged
- curl with both header and query-string: both work
2026-05-17 21:28:05 +02:00
Pichinov-Jose
8fa5e781da
Fix: ToolThirdParty inputSchema JSON Schema 2020-12 compliance (#38290)
Several tool schemas in ToolThirdParty::getDefinitions() declared
'required' => true inside individual properties. This is not valid
JSON Schema draft 2020-12, which requires 'required' to be an array
of property names at the parent object level.

Strict validators (such as Anthropic's Claude API used by Claude
Desktop Custom Connectors and Claude Code) reject these schemas with
HTTP 400 'input_schema is invalid', causing the entire MCP connection
to fail before any tool can be called.

This patch:
- Moves 'required: true' flags from individual properties to a proper
  'required: [...]' array at the parent level
- Adds the missing parent-level required array to get_thirdparty_details
  (which previously had no way to mark id as required in a compliant manner)
- Fixes 5 tools in total: get_thirdparty_details, create_thirdparty,
  update_thirdparty, list_thirdparty_contacts, add_thirdparty_contact

Tested with:
- Claude Desktop Custom Connectors (BETA): tools/list now succeeds
- Claude Code CLI: 32 MCP tools load without 400 errors
2026-05-17 21:24:23 +02:00
Laurent Destailleur
d84479e30f Merge branch '23.0' of git@github.com:Dolibarr/dolibarr.git into develop 2026-05-17 18:20:43 +02:00
Laurent Destailleur
57d0be7dc9 Fix packaging 23.0.3 2026-05-17 18:19:40 +02:00
Laurent Destailleur
a7af2b7a69 Fix packaging 2026-05-17 18:10:33 +02:00
Laurent Destailleur
8172b31c56 Debug makepack scripts 2026-05-17 16:31:00 +02:00
Laurent Destailleur
8ecf2f9700 Merge branch '23.0' of git@github.com:Dolibarr/dolibarr.git into develop 2026-05-17 15:38:08 +02:00
Laurent Destailleur
f5776c68e1 Prepare 23.0.3 2026-05-17 15:34:44 +02:00
Laurent Destailleur
0032e0becc Fix version 2026-05-17 15:29:17 +02:00
Laurent Destailleur
34ab5bcc33 Prepare v23 2026-05-17 15:28:20 +02:00
Laurent Destailleur
468645cee8 Doc 2026-05-17 15:25:53 +02:00
Laurent Destailleur
64c34d47c5 Merge 2026-05-17 15:01:32 +02:00
Laurent Destailleur
194c81ff8f Doc 2026-05-17 14:56:38 +02:00
Laurent Destailleur
5348deb56b Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop 2026-05-17 01:27:57 +02:00
Laurent Destailleur
64ef9998aa CSs to avoid text of multiselect on another line than the x. 2026-05-17 01:09:11 +02:00
brice.l
41bfa24f54
Change holiday status update to validation (#38181)
Replaced the update method with validate for holiday status in order to update the ref of the created objects.
2026-05-13 14:48:15 +02:00
Laurent Destailleur
9f6688f369 Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop 2026-05-13 14:40:44 +02:00
Laurent Destailleur
45a04e928b Clean code 2026-05-13 14:39:46 +02:00
daraelmin
88f7cb5672
NEW [RGPD] Add personal_data column on llx_extrafields table - Third step of anonymise extrafields #8612 (#38194)
* anonymize extra fields flagged as personal data during GDPR cron

* Fix travis

* Copyright
2026-05-13 14:37:14 +02:00
Alexandre SPANGARO
72ac1a4877
NEW UIUX Category - Add category type in create form (#38206)
* UIIX - Category - Add category type in create form

* Better UX

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-13 14:30:17 +02:00
Alexandre SPANGARO
4babfaf069
NEW Accountancy - Export Quadra - Add option to export thirdparty account only if not existing (#38208)
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-13 14:29:02 +02:00
Lenin Rivas
9601ff2fc3
UX for upload files in create ticket (#38202)
Improves user experience and prevents human error when creating tickets; files do not load.

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-13 14:28:26 +02:00
Alexandre SPANGARO
0ddc99bc32
NEW Category - API - Add type list (#38207)
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-13 14:26:29 +02:00
Laurent Dinclaux
29e01f3aee
FIX #38203 Realign New Caledonia legal forms on ISEE-NC nomenclature (#38204)
PR #37840 added NC legal forms by adapting the French INSEE Niveau II
codes (prefixed with '165' to fit the country id) and inserting GDPL
entries on arbitrary 165XX slots. That approach worked but bypassed the
actual NC reference: the ISEE-NC (Institut de la Statistique et des
Études Économiques de Nouvelle-Calédonie) maintains its own published
"Nomenclature des formes juridiques" (July 2020 edition), which is what
the RIDET business registry actually uses to classify NC entities.

This commit replaces the 46 ad-hoc 165XX codes with the 34 INSEE Niveau
II codes from the ISEE-NC nomenclature (same convention used for France
in the same file), prefixed with '165' to satisfy the table's UNIQUE
(code) constraint, plus one Niveau IV entry for the GDPL (code '6901'
becomes '1656901'), which has no N2 equivalent.

Stripping the '165' prefix from any NC code yields the official
ISEE-NC code.

Source: https://www.isee.nc/definitions-et-methodes/nomenclatures
        ("Nomenclature des formes juridiques", July 2020)

The GDPL (Groupement de Droit Particulier Local) is a NC-specific legal
form established by French law 88-1028 of 9 November 1988, decree
89-570 of 16 August 1989, and reinforced by organic law 99-209 of
19 March 1999, art. 18.

This realignment lands before any Dolibarr release ships the 165XX
codes from PR #37840 (which sits only in develop, no tag contains it
yet), so no upgrade migration is needed on the upstream side.

Fixes #38203

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-13 12:35:08 +02:00
Laurent Destailleur
dcf8812949 ci 2026-05-13 11:45:28 +02:00
Laurent Destailleur
774cd0d194 ci 2026-05-13 11:45:27 +02:00
Laurent Destailleur
b919733173 Doc 2026-05-12 11:29:05 +02:00
Laurent Destailleur
0d24c86dd5 Fix warning 2026-05-12 10:19:39 +02:00
Laurent Destailleur
f78c069201 Doc 2026-05-12 10:17:46 +02:00
Laurent Destailleur
3973b28d34 Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop 2026-05-12 03:13:50 +02:00
Laurent Destailleur
a0ae86076b Look and feel v24 2026-05-12 02:53:35 +02:00