Commit graph

154604 commits

Author SHA1 Message Date
Laurent Destailleur
6ecf7b7c16 Remove not required hack param 2026-06-10 19:14:07 +02:00
Laurent Destailleur
b9b44cc890 Automated merge from 23.0 to develop 2026-06-10 19:13:04 +02:00
Laurent Destailleur
d00ba1d8ae Automated merge from 22.0 to 23.0 by tool pullmerge.sh 2026-06-10 19:12:32 +02:00
Laurent Destailleur
38065c94b7 Automated merge from 21.0 to 22.0 by tool pullmerge.sh 2026-06-10 19:08:54 +02:00
Laurent Destailleur
2b3909aa85 Automated merge from 20.0 to 21.0 by tool pullmerge.sh 2026-06-10 19:08:51 +02:00
w3houde
782d521978
Fix #36079 keep online signature URL from ending with a bcrypt trailing dot (#38526)
`getOnlineSignatureUrl()` ends each public signature URL with
`&securekey=` followed by the output of `dol_hash(..., '0')`. With the
default `MAIN_SECURITY_HASH_ALGO = password_hash`, that output is a
bcrypt string whose alphabet is `[./A-Za-z0-9]`, so a non-trivial share
of generated URLs legitimately end with `.` or `/`.

Gmail and several mobile mail clients autolink plain-text URLs but
drop trailing punctuation, so the recipient clicks on a URL with the
last character missing and gets a securekey mismatch error
(`Bad value for securitykey`). The user-facing symptom is exactly the
one in #31464.

When multicompany is enabled, the URL already ends with `&entity=N`,
which sidesteps the issue. For installations without multicompany,
append a stable trailing `&_=1` so the URL never ends with the hash.
The receiver in `public/onlinesign/newonlinesign.php` does not read
`_`, only `source`, `ref`, `securekey` and (when multicompany) `entity`,
so adding the parameter has no behavior impact and existing links keep
verifying as before. Only the real URL (mode 0) is affected, not the
mode 1 preview shown in the admin setup.

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
2026-06-10 19:08:26 +02:00
Lucas Marcouiller
d7b1bc7921
Fix some php stan warnings (#38670)
* Fix soem php stan warnings

* fix CI

---------

Co-authored-by: Hystepik <lmarcouiller@dolicloud.com>
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-06-10 16:53:54 +02:00
Laurent Destailleur
ebbece26a0 Fix travis 2026-06-10 16:51:46 +02:00
Jyhere
f29e9e5eb2
FIX Add geoDataType handling for extrafield updates (#38655)
* Add geoDataType handling for extrafield updates

* Geo extrafield update fix, sanitize SQL update statement

* Update commonobject.class.php

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-06-10 16:34:40 +02:00
Laurent Destailleur
a051e91ffd Clean code 2026-06-10 16:28:13 +02:00
w3houde
21cea7627a
Fix #33042 divide by multicurrency_tx when checking minimum price (#38542)
The minimum-price guard on the customer invoice, customer order and
customer proposal forms compares the entered unit price to the
product's price_min. When the document is in a foreign currency the
unit price is provided in the foreign currency ($pu_ht_devise), so
it must first be converted back to the base (company) currency before
the comparison.

card.php was multiplying by $currency_tx:

    $pu_equivalent = (float) $pu_ht_devise * $currency_tx;

but multicurrency_tx is defined as 'units of foreign currency per
1 unit of base', exactly as price.lib.php uses it
(calcul_price_total at line 181):

    $pu = $pu_devise / $multicurrency_tx;

With the reporter's example (1 MXN = 0.055 USD, so tx = 0.055,
product min price = 1000 MXN, entered 55 USD), the wrong direction
gives 55 * 0.055 = 3.025 MXN < 1000 MXN and the form errors out with
'CantBeLessThanMinPrice', even though 55 USD is exactly 1000 MXN.
Dividing yields 55 / 0.055 = 1000 MXN and the check passes.

Mirror calcul_price_total's direction at all four invoice sites
(htdocs/compta/facture/card.php lines 2503/2506 and 2684/2687) plus
the corresponding sites in htdocs/commande/card.php and
htdocs/comm/propal/card.php. Documents in the base currency
($pu_ht_devise empty) are not affected because the conversion
branch is gated on $pu_ht_devise being non-empty.

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
2026-06-10 16:16:35 +02:00
Laurent Destailleur
c49ed304ff Fix label 2026-06-10 15:56:19 +02:00
Laurent Destailleur
0afc6155f4 Label of field 2026-06-10 15:54:44 +02:00
Laurent Destailleur
6c5a86a1c7 Debug v24 2026-06-10 15:50:02 +02:00
Laurent Destailleur
14eaa935a3 Fix phan 2026-06-10 15:28:24 +02:00
Laurent Destailleur
b6a109db31 Prepare code to use an external obfuscation code. 2026-06-10 15:13:57 +02:00
Laurent Destailleur
b55fccb985 Code comment 2026-06-10 12:46:26 +02:00
Laurent Destailleur
836119732d Fix bug of sort -u on ubuntu 26.04. Use uniq instead. 2026-06-10 12:42:04 +02:00
Laurent Destailleur
74292802d3 Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into
develop
2026-06-10 12:38:39 +02:00
Laurent Destailleur
c1291a2e31 Automated merge from 22.0 to 23.0 by tool pullmerge.sh 2026-06-10 12:33:21 +02:00
Laurent Destailleur
a623693bd0 Automated merge from 21.0 to 22.0 by tool pullmerge.sh 2026-06-10 12:28:29 +02:00
Laurent Destailleur
c556d77185 Automated merge from 20.0 to 21.0 by tool pullmerge.sh 2026-06-10 12:13:33 +02:00
Laurent Destailleur
cd2b52b982 Automated merge from 19.0 to 20.0 by tool pullmerge.sh 2026-06-10 12:13:30 +02:00
Laurent Destailleur
6515c4feb1 Automated merge from 18.0 to 19.0 by tool pullmerge.sh 2026-06-10 12:13:28 +02:00
Laurent Destailleur
874422802e Automated merge from 17.0 to 18.0 by tool pullmerge.sh 2026-06-10 12:13:24 +02:00
Laurent Destailleur
05d985f82f Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop 2026-06-10 12:11:45 +02:00
Laurent Destailleur
ed5c0bc2b0 Doc 2026-06-10 12:11:13 +02:00
Vincent Penel
799628a8d2
NEW Add hook getTicketMessageEmailFrom to override sender in Ticket::sendTicketMessageByEmail (#38656)
* Add hook

* Update ticket.class.php

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-06-10 12:10:52 +02:00
w3houde
0f92615313
Fix #30326 scope agenda assignee session by event id (#38665)
htdocs/comm/action/card.php buffered the assignee working set in
$_SESSION['assignedtouser'] and $_SESSION['assignedtoresource'], two
flat keys shared by every browser tab of the same logged-in user. With
two tabs editing two different events, the second tab overwrote the
first one's working set, and a plain save of the first tab then
replaced its assignees with those of the second tab.

Suffix both session keys with the event id (assignedtouser_<id>,
assignedtoresource_<id>), falling back to _new for the still-unsaved
create form. Two tabs editing different events now each have their own
bucket; two tabs creating two new events still share the _new bucket,
which is the same edge the previous design had and is much less common
in practice than the cross-tab edit case the reporter hit.

Compute the keys once near the top of the file and use the variables
everywhere the old flat keys appeared (add, remove, persist, unset
sites). No HTML form change is needed because the event id is already
preserved in the query string across the assignee subform submits.

Per @eldy review on PR #38531, this is the develop-branch version of
the fix; the 21.0 PR is closed in favor of this one.

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
2026-06-10 12:08:21 +02:00
Laurent Destailleur
dcf71ee3c6 More control on data on subcontractor page 2026-06-10 12:06:57 +02:00
Frédéric FRANCE
b16208b6df fix warning in phpsessionindb.lib.php (#38492)
if connexion in webportal
2026-06-10 11:43:15 +02:00
Ferran Marcet
710fb1d71d Fix: Corrected month and day values for 'ES-REYE' public holiday in SQL data file (#38667) 2026-06-10 11:42:51 +02:00
Jyhere
d01185e703 FIX : dolgeophp getXYString and getPointString methods (#38669)
Dolgeophp getXYString and getPointString methods are broken !
2026-06-10 11:42:40 +02:00
Jyhere
b35658a8af
FIX : dolgeophp getXYString and getPointString methods (#38669)
Dolgeophp getXYString and getPointString methods are broken !
2026-06-10 11:42:15 +02:00
Ferran Marcet
ef0ec776cc
Fix: Corrected month and day values for 'ES-REYE' public holiday in SQL data file (#38667) 2026-06-10 11:41:40 +02:00
sonikf
0a35b04674
New AI tool access control (#38668)
* Add new language entries for AI tool management

* Update file reference in assistant.php documentation

* Implement isSystem method in conversation class

* Add Tool Access Control tab

* Initial commit AI general css

* Initial commit AI general js file

* Implement isSystem method in mcptool class

Add isSystem method to determine tool visibility.

* Enhance MCPServer to enforce tool allow-list

* Enhance McpHandler with tool context handling

Added support for tool context in McpHandler class.

* Remove empty line at the start of mcp.class.php

* Refactor tool schema handling for LLM integration

* Modify McpHandler initialization in execute_tool.php

Updated McpHandler initialization to include configuration and context for assistant.

* New AI tool access control

* Fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix phan php-stan
2026-06-10 11:41:10 +02:00
Frédéric FRANCE
d41df96039
fix warning in phpsessionindb.lib.php (#38492)
if connexion in webportal
2026-06-09 19:58:32 +02:00
w3houde
4cf54760b2
Fix #25107 honour FOR_ALL_STATUS in mass send action (#38532)
The per-card Send by email button on Propal, Commande and Facture
already honours the hidden constants
`PROPOSAL_SENDBYEMAIL_FOR_ALL_STATUS`,
`COMMANDE_SENDBYEMAIL_FOR_ALL_STATUS` and
`FACTURE_SENDBYEMAIL_FOR_ALL_STATUS` to allow sending drafts. The
mass Send by email action in the corresponding lists silently dropped
draft objects regardless of those constants, so an administrator who
intentionally enabled the override saw inconsistent behaviour between
the card and the list.

Apply the same constant check on the three draft-skip guards in
`htdocs/core/actions_massactions.inc.php` so the mass action behaves
the same way as the single-object button. Default behaviour is
unchanged: with none of the constants set, drafts are still skipped
with the existing 'OnlyXxxNotDraftCanBeSentInMassAction' error
message.

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
2026-06-09 19:51:36 +02:00
Frédéric FRANCE
3c8eef840a
fix CI (#38490) 2026-06-09 19:42:58 +02:00
w3houde
b3f470f641
Fix #38154 Keep full label in option text of select_all_categories (#38508)
The select2 combobox built around select_all_categories renders the
truncated label via the data-html attribute (see templateResult in
core/lib/ajax.lib.php ajax_combobox), and runs its search matcher on
the bare option text. When the option text itself was the truncated
label, any character outside the visible portion was unreachable from
the search box, so long category names whose distinctive part fell
in the hidden middle stopped matching.

Write the full label in the option text and keep data-html as the
truncated visible rendering. The dropdown still looks compact, but
the matcher can now find a hit anywhere in the full category name.

The full label is run through dol_escape_htmltag to keep the
generated HTML safe (the previous bare dol_trunc output was not
escaped). Same regression on 21.0, 22.0, 23.0 and develop; PR targets
21.0.

Reported by @SteveS1609. Regression introduced by commit 1e76422a50
("NEW Show picto and color into combo for selection of tags") shipped
in v21.

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
2026-06-09 19:10:44 +02:00
w3houde
505c483484
Fix #38491 Name the field in the ticket too-long error message (#38507)
* Fix #38491 Name the field in the ticket too-long error message

When a ticket message exceeds 65000 characters, Ticket::verify() and
Ticket::update() appended the bare string "ErrorFieldTooLong" to
$this->errors. setEventMessages forwards that string and
dol_htmloutput_mesg applies $langs->trans() to it, which yields the
raw template "Field %s is too long." with the %s placeholder still
visible because no argument was passed.

Replace the bare key with a pre-translated message that names the
field ("Initial message"), the same pattern already used in
htdocs/barcode/printsheet.php for the same lang key. Two identical
sites in the file are updated (one in verify, called by create, and
one in update). The "errors" and "ticket" lang files are loaded
defensively before translation because both methods can run before
the page-level loadLangs.

Reported by @ocemag, expected field name confirmed by @aspangaro.

Signed-off-by: Dolicraft <contact@dolicraft.com>

* Fix phan: declare $langs as global in ticket too-long branches

phan level 10 raised PhanUndeclaredVariable and PhanNonClassMethodCall
on the two branches that translate the new error message. The enclosing
Ticket::verify() and Ticket::update() do not declare global $langs;
add it where needed instead of opening the method scope further.

Signed-off-by: Dolicraft <contact@dolicraft.com>

---------

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Alexandre SPANGARO <alexandre.spangaro@gmail.com>
2026-06-09 19:09:12 +02:00
w3houde
8d8debd441
Fix #38171 Allow underscore in extrafields old-syntax filter regex (#38506)
ExtraFields::showInputField normalises the legacy "(field=123)"
filter syntax of "List from a table" extrafields into the modern
USF "(field:=:123)" form. The compatibility regex
"/^\(?([a-z0-9]+)([=<>]+)(\d+)\)?$/i" only accepts alphanumeric
field names, so a filter like "fk_typent=2" does not match, the
filter is left as is, and forgeSQLFromUniversalSearchCriteria later
fails with "Filter error" because the raw expression is not valid
universal search syntax.

Accept the underscore character in the field name class, the rest
of the predicate is untouched. The two identical occurrences in
the file (one per branch of the dispatch) are updated.

Reported and root cause pinpointed by @stephanebourdon-cmyk.

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
2026-06-09 19:07:07 +02:00
w3houde
d17ae69331
Fix #38182 Backport oldcopy empty check to 21.0 (#38505)
Updating a product selling price via /product/price.php triggered a
fatal "Call to undefined method stdClass::isEmpty()" at
Product::update line 1457 of 21.0. In the update_price flow,
$this->oldcopy is a stdClass (not a Product instance), so isEmpty()
does not exist on it. The page returned HTTP 500 and the open
transaction was forcibly closed by DoliDBMysqli::close.

Replace the isEmpty() call with empty($this->oldcopy->id), which is
the form already shipped in 22.0, 23.0 and develop after upstream
commit ef94452f77. The accompanying comment from that commit is
also brought in.

Same root cause as issue 37792 (closed, fixed in 23.0.3 and
forward); the fix was never backported to 21.0 even though the bug
is still triggered on that branch.

Reported by @YOYOKMC.

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
2026-06-09 19:06:04 +02:00
Laurent Destailleur
7f70d9b003 Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop 2026-06-09 19:04:40 +02:00
Laurent Destailleur
9fbab9840b Doc 2026-06-09 19:04:25 +02:00
VIAL-GOUTEYRON Quentin
3216098ab5
NEW: ModuleBuilder - selectable object tabs (+ fixes for multi-object generation) (#38570)
* NEW : add ModuleBuilder object-tabs map and filter helpers

Add getModuleBuilderObjectTabs() (source-of-truth map of optional object
tabs) and filterEnabledTabs() (sanitizes user-requested tab keys against the
map). Covered by ModuleBuilderLibTest.

Next: add BEGIN/END markers around tab flags and blocks in template lib (Task 2)

* NEW : wrap object tab flags and blocks with MODULEBUILDER markers

Surround each optional tab flag declaration (TABFLAG) and each prepareHead tab
block (TAB) with BEGIN/END MODULEBUILDER comment markers, so the generator can
purge a whole tab atomically while keeping $h and $head intact. Card tab is
not wrapped (always generated).

Next: add tab selection checkboxes to the newobject form in modulebuilder/index.php (Task 3)

* NEW : add object tab selection checkboxes to newobject form

Render one checkbox per optional tab (contact, note, document, agenda) in the
ModuleBuilder newobject form, all checked by default and reflecting posted
state on redisplay.

Next: handle enabledtab in initobject — exclude files, purge blocks, hardcode flags (Task 4)

* NEW : apply object tab selection during object generation

Excludes page files of unselected tabs, purges their prepareHead flag and
block via MODULEBUILDER markers, and hardcodes the show flag to 1 for selected
tabs. Emits a best-effort warning when regenerating an existing object.

Next: add language keys EnabledTabsForObject(Help) + WarningTabSelectionOnRegeneration (Task 5)

* NEW : add language keys for object tab selection

Next: add ChangeLog entry (Task 6)

* DOC : changelog entry for ModuleBuilder selectable object tabs

* FIX : purge agenda card widget when agenda tab is excluded

Audit finding: myobject_card.php hardcodes a 'SeeAll' link to myobject_agenda.php
in an event widget. When the agenda tab is excluded the page is not generated,
leaving a latent dead link if the widget is enabled. Wrap the widget with
MODULEBUILDER TAB AGENDA markers and purge it from the generated card when the
agenda tab is not selected.

Next: functional verification (step 5 of dev workflow)

* FIX : check file operation results when applying object tab selection

PR review: align with the nogeneratelines pattern by checking the return of
removePatternFromFile/dolReplaceInFile and raising $error + dol_syslog with the
failing tab/file context, instead of ignoring silent write failures during
object generation. Add PR reference to the changelog entry.

* FIX : do not abort object generation when nogeneratelines targets a missing file

The nogeneratelines handler purges the MODULEBUILDER LINES block from class,
API and card files. When API generation is disabled the api_<module>.class.php
file does not exist, so removePatternFromFile returns false and $error was
incremented, which skipped the whole success path (name substitution,
rebuildObjectClass, $tabobj assignment). Generated classes kept the literal
'class MyObject', so dolGetListOfObjectClasses listed every object as MyObject.
Guard each purge with file_exists so a missing optional file is skipped.

* FIX : keep module descriptor out of the blanket name substitution

The 'substitute all module php files' pass applied the object/module name map
to every .php file including the module descriptor. That rewrote the persistent
/* ... MODULEBUILDER TOPMENU/LEFTMENU MYOBJECT */ marker placeholders to the
first object name, so generating a second object could no longer find them
(checkExistComment returned -1, 'comments not found for section Menus' warning)
and its menu entries were not inserted. Skip the descriptor in that loop: its
module name is already resolved by initmodule and its object entries are added
by the dedicated menu/permission blocks.

* FIX : preserve MODULEBUILDER markers when substituting the module descriptor

The previous commit only skipped the descriptor in the secondary substitution
pass, but the primary pass (over $filetogenerate, which includes the descriptor)
still rewrote its persistent /* ... MODULEBUILDER TOPMENU/LEFTMENU MYOBJECT */
markers to the first object name. Those markers cannot simply be excluded from
substitution because the descriptor also has functional MYOBJECT/MYMODULE tokens
(e.g. the MYMODULE_MYOBJECT_ADDON numbering constant) that must be resolved.

Add dolReplaceInFilePreservingModuleBuilderMarkers(): it hides every MODULEBUILDER
marker behind a sentinel, applies the substitution, then restores the markers
verbatim. Use it for the descriptor in the primary pass; the descriptor stays
excluded from the secondary pass. Generating a second object now finds the
markers (checkExistComment) and inserts its menu entries without warning.

Verified by CLI: markers preserved across two successive objects, while content
(rights, exports, addon constant) is correctly substituted.

* FIX : silence phan on the new ModuleBuilder test and typed closure

CI phan (diff-only) flagged the freshly added test file: PhanUndeclaredExtendedClass
(\CommonClassTest) and PhanUndeclaredMethod (assertSame), like the sibling
NamingContractTest. Add the same @phan-file-suppress block (plus
PhanTypeMismatchArgumentProbablyReal for the non-array guard test case). Also
type the preg_replace_callback parameter (array $matches) to clear
PhanPluginUnknownClosureParamType. phpstan is already green; no phpstan
annotations added.

* FIX : drop closure in marker-preserving substitution to satisfy phan

phan's PhanPluginUnknownArrayClosureParamType wanted key/value types on the
preg_replace_callback closure parameter. Replace the callback with preg_match_all
plus a str_replace loop over the unique markers: same behaviour, no closure,
no plugin warning.

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-06-09 19:04:00 +02:00
Nicolas Domenech
ba85ea8b0f
FIX setCategoriesCommon array_diff/foreach warnings when containing() returns -1 (#38660)
Categorie::containing() returns an integer (-1) when its SQL query fails
(e.g. the categorie_xxx table does not exist). The $remove_existing=false
branch of setCategoriesCommon() passed that int straight to array_diff(),
producing "array_diff(): Expected parameter 2 to be an array, int given"
and "Invalid argument supplied for foreach()" (a fatal TypeError on PHP 8),
which in turn broke the redirect with "headers already sent".

Normalize $existing to an empty array right after the containing() call so
both branches are safe, and drop the now-redundant inner is_array() guard.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 19:03:37 +02:00
MDW
bae2ca251c
Qual: Improve type hinting for $type (in hash related functions) (#38552)
* Qual: Improve type hinting for $type (in hash related functions)

For $type parameter now use specific type hints.

* fix: Update type parameter documentation in dol_hash and dol_verifyHash functions

The $type parameter PHPDoc in the dol_hash and dol_verifyHash is updated to be more specific and include 'hash'

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-06-09 18:58:17 +02:00
w3houde
b87ed03b2b
Fix #27987 refuse to save signature when too-many-links sanitizer fires (#38534)
* Fix #27987 refuse to save signature when too-many-links sanitizer fires

When the user signature contains more inline links/images than
MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT allows, GETPOST('signature',
'restricthtml') returns the literal string 'ErrorTooManyLinksIntoHTMLString'
instead of the sanitized HTML (see dol_string_unicode in
htdocs/core/lib/functions.lib.php:8755-8757). On the user/card.php save
sites, that literal was assigned verbatim to $object->signature and
persisted; the next outgoing email then used the literal as the
signature block, so the customer received an email whose entire
signature was the string 'ErrorTooManyLinksIntoHTMLString'.

Detect the literal at both save sites (action == 'add' around line 316
and action == 'update' around line 499), increment $error, surface a
clear setEventMessages and route the user back to the form so the
signature is not persisted. The MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT
setting is the legitimate guard; this PR only ensures its rejection
output never reaches the database or downstream emails.

Signed-off-by: Dolicraft <contact@dolicraft.com>

* Add ErrorTooManyLinksIntoHTMLString en_US translation

pre-commit CI on PR #38534 (Fix #27987) flagged the new lang key
ErrorTooManyLinksIntoHTMLString used in user/card.php as missing.
Add the en_US entry so the existing helper text from the constant
MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT is reachable through
$langs->trans().

Signed-off-by: Dolicraft <contact@dolicraft.com>

---------

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
2026-06-09 18:52:30 +02:00
w3houde
b7c5d2c76b
Fix #29799 widen RUM truncation so customer code fits (#38537)
* Fix #29799 widen RUM truncation so customer code fits

BonPrelevement::buildRumNumber() builds an auto-generated SEPA UMR
when no UMR was supplied on a new bank-mandate row. The format is

    {RUM/UMR}-{yymmddHHMM}-{drum_id-customer_code}

with the tail truncated to 13 characters. That truncation is too
aggressive: typical Dolibarr customer codes (CU1908-00002,
CU2509-00012, ...) are already 12 characters, so any drum id at all
pushes the tail past 13 and the customer code is cut mid-string.
The generated UMR then no longer matches the actual customer code,
which is the symptom in the report ("customer code CU1908-00002
becomes 1-CU1908-0000 in the UMR").

The SEPA standard allows up to 35 characters for MndtId. The
non-truncatable prefix (RUM/UMR + '-' + 10-char date + '-') uses
15 characters, leaving up to 20 characters for the tail before hitting
the 35-char limit, and 17 before hitting the more conservative 32-char
target the original comment referenced. Bump the truncation to 20:
the typical drum-id + 12-char customer code (14 chars) now fits
cleanly, and longer codes still get truncated but at a much later
point.

Only the generation of newly created mandates is affected. Existing
$companybankaccount->rum values stored in the database are not
touched, so no migration is needed.

Signed-off-by: Dolicraft <contact@dolicraft.com>

* Update bonprelevement.class.php

---------

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-06-09 18:51:03 +02:00