Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop

This commit is contained in:
Laurent Destailleur 2026-05-18 13:05:54 +02:00
commit 1f021efaed
92 changed files with 1748 additions and 1337 deletions

View file

@ -41,6 +41,9 @@ repos:
exclude_types: [markdown]
# Fix the end of file
- id: end-of-file-fixer
exclude: |
(?x)^(htdocs/public/includes/tinymce/.*
)$
# Check that there are no completely merged file conflicts
- id: check-merge-conflict
stages: [pre-rebase, pre-commit, pre-merge-commit]
@ -66,7 +69,7 @@ repos:
# Check github actions
- repo: https://github.com/rhysd/actionlint
rev: v1.7.11
rev: v1.7.12
hooks:
- id: actionlint
stages: [manual] # To run: pre-commit run -a --hook-stage=manual actionlint
@ -218,7 +221,7 @@ repos:
# Execute codespell to fix typo errors (setup of codespell into dev/tools/codespell/)
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
rev: v2.4.2
hooks:
- id: codespell
# Due to a current limitation of configuration files,
@ -269,7 +272,7 @@ repos:
# Check sql file syntax
- repo: https://github.com/sqlfluff/sqlfluff
rev: 4.0.4
rev: 4.2.0
hooks:
- id: sqlfluff-lint
stages: [pre-commit, manual] # manual needed for ci

View file

@ -13,6 +13,7 @@ This project is full sources of Dolibarr ERP and CRM application. Every modifica
- ❌ Do not break compatibility of PHP functions and methods
- ❌ Do not introduce external dependencies without validation
- ❌ Separate page actions in the `/* Actions */` section of the PHP code and the rendering part in the `/* Views */` section
- ❌ Never use PHP native curl functions to call a GET or POST URL, but use instead the Dolibarr function getURLContent()
- ✅ Use Dolibarr hooks whenever possible
- ✅ Respect existing naming conventions

View file

@ -1851,7 +1851,7 @@ FIX: PHP8 warning if $conf->reception is checked the old fashion way (#29697)
FIX: PHP 8 warning on output of successful cronjob (#29922)
FIX: PHP exception on getSpecialCode (#29646)
FIX: php warning if cookie doesn’t exist (#29723)
FIX: pos: invoice date incorrectly set because of timezome mismatches (reverts #36e91da) (#30184)
FIX: pos: invoice date incorrectly set because of timezone mismatches (reverts #36e91da) (#30184)
FIX: public project form return an error if SOCIETE_EMAIL_UNIQUE (#29942)
FIX: REPLENISH MANY FOURN WHEN ORDER ALREADY CREATE (#29710)
FIX: Supplier Order search on date valid (#30448)
@ -1967,7 +1967,7 @@ FIX: navigation after filter in subaccount
FIX: notification: error 500 in fixed emails due to a bad copy/paste (#29580)
FIX: notification module: for supplier orders (any of the 3 triggers), user can choose an e-mail template in conf, but the conf is not used when sending the notification (#28216)
FIX: not redirtect when error occurs on updating card (#29388)
FIX: Not trancate the multicurrency rate shown on cards (even if the global MAIN_MAX_DECIMALS_SHOWN is set to 0) (#28211)
FIX: Not truncate the multicurrency rate shown on cards (even if the global MAIN_MAX_DECIMALS_SHOWN is set to 0) (#28211)
FIX: Oauth login
FIX: Option MAIN_DOC_USE_TIMING can be a string with date format
FIX: Payment on customer invoice - Remove accountid in url if empty for apply default value (#28156)
@ -2070,7 +2070,7 @@ FIX: Fix create shipping with product who have negative stocks on warehouse but
FIX: migration missing 2 columns in llx_resource and 1 in llx_user
FIX: missing trans
FIX: notification module: for supplier orders (any of the 3 triggers), user can choose an e-mail template in conf, but the conf is not used when sending the notification (#28216)
FIX: Not trancate the multicurrency rate shown on cards (even if the global MAIN_MAX_DECIMALS_SHOWN is set to 0) (#28211)
FIX: Not truncate the multicurrency rate shown on cards (even if the global MAIN_MAX_DECIMALS_SHOWN is set to 0) (#28211)
FIX: Option MAIN_DOC_USE_TIMING can be a string with date format
FIX: Payment on customer invoice - Remove accountid in url if empty for apply default value (#28156)
FIX: Pb in redirect of a website page in USEDOLIBARRSERVER mode
@ -2630,7 +2630,7 @@ FIX: notification: error 500 in fixed emails due to a bad copy/paste (#29580)
FIX: notification module: for supplier orders (any of the 3 triggers), user can choose an e-mail template in conf, but the conf is not used when sending the notification (#28216)
FIX: Not qualified lines for reception (#29473)
FIX: not redirect when error occurs on updating card (#29388)
FIX: Not trancate the multicurrency rate shown on cards (even if the global MAIN_MAX_DECIMALS_SHOWN is set to 0) (#28211)
FIX: Not truncate the multicurrency rate shown on cards (even if the global MAIN_MAX_DECIMALS_SHOWN is set to 0) (#28211)
FIX: on change ref for bank account attachment are lost (#30529)
FIX: Option MAIN_DOC_USE_TIMING can be a string with date format
FIX: orders to bill menu (#30179)
@ -2642,7 +2642,7 @@ FIX: php8: Fatal when empty $tmpvat is an empty string (no silent conversion to
FIX: PHP 8 warning on output of successful cronjob (#29922)
FIX: PHP exception on getSpecialCode (#29646)
FIX: php warning global conf (#29478)
FIX: pos: invoice date incorrectly set because of timezome mismatches (reverts #36e91da) (#30184)
FIX: pos: invoice date incorrectly set because of timezone mismatches (reverts #36e91da) (#30184)
FIX: public project form return an error if SOCIETE_EMAIL_UNIQUE (#29942)
FIX: purge files cron: php warnings when rest module enabled (#30919)
FIX: PUT /thirdparties/{id} and PUT /contacts/{id} should throw exception if update returns < 0 (#29596)

View file

@ -2664,12 +2664,6 @@ parameters:
count: 1
path: ../../../htdocs/core/ajax/ajaxdirpreview.php
-
message: '#^Variable \$action might not be defined\.$#'
identifier: variable.undefined
count: 1
path: ../../../htdocs/core/ajax/ajaxdirtree.php
-
message: '#^Variable \$original_file might not be defined\.$#'
identifier: variable.undefined
@ -3306,144 +3300,6 @@ parameters:
count: 1
path: ../../../htdocs/core/class/interfaces.class.php
-
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Negated boolean expression is always true\.$#'
identifier: booleanNot.alwaysTrue
count: 2
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_add expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_bind expects LDAP\\Connection, bool\|resource given\.$#'
identifier: argument.type
count: 2
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_delete expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_errno expects LDAP\\Connection, bool\|resource given\.$#'
identifier: argument.type
count: 9
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_errno expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 3
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_error expects LDAP\\Connection, bool\|resource given\.$#'
identifier: argument.type
count: 9
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_error expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 8
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_first_entry expects LDAP\\Connection, bool\|resource given\.$#'
identifier: argument.type
count: 2
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_first_entry expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_get_attributes expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_get_entries expects LDAP\\Connection, bool\|resource given\.$#'
identifier: argument.type
count: 3
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_get_option expects LDAP\\Connection, bool\|resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_get_values_len expects LDAP\\Connection, bool\|resource given\.$#'
identifier: argument.type
count: 2
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_mod_add expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_mod_del expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_mod_replace expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 2
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_rename expects LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_search expects array\|LDAP\\Connection, bool\|resource given\.$#'
identifier: argument.type
count: 6
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_search expects array\|LDAP\\Connection, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_set_option expects LDAP\\Connection\|null, bool\|resource given\.$#'
identifier: argument.type
count: 3
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Parameter \#1 \$ldap of function ldap_unbind expects LDAP\\Connection, resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/class/ldap.class.php
-
message: '#^Negated boolean expression is always true\.$#'
identifier: booleanNot.alwaysTrue
@ -3600,48 +3456,6 @@ parameters:
count: 1
path: ../../../htdocs/core/db/mysqli.class.php
-
message: '#^Parameter \#1 \$result of function pg_affected_rows expects PgSql\\Result, bool\|resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/db/pgsql.class.php
-
message: '#^Parameter \#1 \$result of function pg_fetch_array expects PgSql\\Result, bool\|resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/db/pgsql.class.php
-
message: '#^Parameter \#1 \$result of function pg_fetch_object expects PgSql\\Result, bool\|resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/db/pgsql.class.php
-
message: '#^Parameter \#1 \$result of function pg_fetch_row expects PgSql\\Result, bool\|resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/db/pgsql.class.php
-
message: '#^Parameter \#1 \$result of function pg_free_result expects PgSql\\Result, resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/db/pgsql.class.php
-
message: '#^Parameter \#1 \$result of function pg_num_rows expects PgSql\\Result, resource\|true given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/db/pgsql.class.php
-
message: '#^Parameter \#1 \$resultset of method DoliDBPgsql\:\:fetch_row\(\) expects resource, PgSql\\Result given\.$#'
identifier: argument.type
count: 2
path: ../../../htdocs/core/db/pgsql.class.php
-
message: '#^Variable \$conf might not be defined\.$#'
identifier: variable.undefined
@ -4026,18 +3840,6 @@ parameters:
count: 1
path: ../../../htdocs/core/login/functions_ldap.php
-
message: '#^Parameter \#1 \$ldap of function ldap_errno expects LDAP\\Connection, resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/login/functions_ldap.php
-
message: '#^Parameter \#1 \$ldap of function ldap_error expects LDAP\\Connection, resource given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/core/login/functions_ldap.php
-
message: '#^If condition is always true\.$#'
identifier: if.alwaysTrue
@ -5844,180 +5646,6 @@ parameters:
count: 1
path: ../../../htdocs/imports/import.php
-
message: '#^Class Segment implements generic interface IteratorAggregate but does not specify its types\: TKey, TValue$#'
identifier: missingType.generics
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Default value of the parameter \#3 \$encode \(true\) of method Segment\:\:setVars\(\) is incompatible with type string\.$#'
identifier: parameter.defaultValue
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Method Segment\:\:__call\(\) has parameter \$args with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Method Segment\:\:macroReplace\(\) has no return type specified\.$#'
identifier: missingType.return
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Parameter \#3 \$encode of method Segment\:\:setVars\(\) expects string, false given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Property Segment\:\:\$children has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Property Segment\:\:\$file has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Property Segment\:\:\$images has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Property Segment\:\:\$odf has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Property Segment\:\:\$vars has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/Segment.php
-
message: '#^Class SegmentIterator implements generic interface RecursiveIterator but does not specify its types\: TKey, TValue$#'
identifier: missingType.generics
count: 1
path: ../../../htdocs/includes/odtphp/SegmentIterator.php
-
message: '#^Method SegmentIterator\:\:__construct\(\) has parameter \$ref with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: ../../../htdocs/includes/odtphp/SegmentIterator.php
-
message: '#^Method SegmentIterator\:\:getChildren\(\) return type with generic interface RecursiveIterator does not specify its types\: TKey, TValue$#'
identifier: missingType.generics
count: 1
path: ../../../htdocs/includes/odtphp/SegmentIterator.php
-
message: '#^Property SegmentIterator\:\:\$key has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/SegmentIterator.php
-
message: '#^Property SegmentIterator\:\:\$keys has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/SegmentIterator.php
-
message: '#^Property SegmentIterator\:\:\$ref has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/SegmentIterator.php
-
message: '#^Method Odf\:\:__construct\(\) has parameter \$config with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Method Odf\:\:_getDataFromHtml\(\) return type has no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Method Odf\:\:_isHtmlTag\(\) is unused\.$#'
identifier: method.unused
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Method Odf\:\:_replaceHtmlWithOdtTag\(\) has parameter \$customStyles with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Method Odf\:\:_replaceHtmlWithOdtTag\(\) has parameter \$fontDeclarations with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Method Odf\:\:_replaceHtmlWithOdtTag\(\) has parameter \$tags with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Method Odf\:\:getConfig\(\) should return string but returns false\.$#'
identifier: return.type
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Parameter \#1 \$prefix of function uniqid expects string, int\<0, max\> given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Property Odf\:\:\$config has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Property Odf\:\:\$images has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Property Odf\:\:\$segments has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Property Odf\:\:\$userdefined has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Property Odf\:\:\$vars has no type specified\.$#'
identifier: missingType.property
count: 1
path: ../../../htdocs/includes/odtphp/odf.php
-
message: '#^Ternary operator condition is always true\.$#'
identifier: ternary.alwaysTrue

View file

@ -16,6 +16,7 @@
jQuery("#mouvement option[value=1]").attr("selected","selected").trigger("change");
jQuery("#mouvement").trigger("change");
$action = 'transfert';
$currentpos = ftell($infp);
$date_liv = dol_mktime(GETPOSTINT('rehour'), GETPOSTINT('remin'), GETPOSTINT('resec'), GETPOSTINT("remonth"), GETPOSTINT("reday"), GETPOSTINT("reyear"));
$newfiletmp = preg_replace('/\.od(s|t)/i', '', $newfile);
$newfiletmp = preg_replace('/\.od[ts]/i', '', $newfile);
@ -29,6 +30,7 @@
if (jQuery("#mouvement").val() == \'0\') jQuery("#unitprice").removeAttr("disabled");
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=transfert&token='.newToken().'">'.$langs->trans("TransferStock").'</a>';
$action = 'transfert';
$currentpos = ftell($infp);
$date_com = dol_mktime(GETPOSTINT('rehour'), GETPOSTINT('remin'), GETPOSTINT('resec'), GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
$date_next_execution = dol_mktime($rehour, $remin, 0, $remonth, $reday, $reyear);
$datesubscription = dol_mktime(12, 0, 0, GETPOSTINT("remonth"), GETPOSTINT("reday"), GETPOSTINT("reyear"));
@ -87,6 +89,7 @@
$datepaye = dol_mktime(GETPOSTINT("rehour"), GETPOSTINT("remin"), GETPOSTINT("resec"), GETPOSTINT("remonth"), GETPOSTINT("reday"), GETPOSTINT("reyear"), 'tzuserrel');
$ensemblereponses = $obj->reponses;
$head[$h][1] = $langs->trans('Referers');
$infp = fopen($infilename, "rt");
$inj += preg_match('/on(dblclick|drag|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', $val);
$opensurveysondage->mail_admin = $_SESSION['adresse'];
$pdf->SetXY($savx, $savy);
@ -108,9 +111,12 @@
// Ligne de la periode d'analyse du rapport
// ceci afin d'etre compatible avec les cas ou la periode n'etait pas obligatoire
<strong>TaskItem(<em>pID, pName, pStart, pEnd, pColor, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGantt</em>)</strong></p>
Issue summary: If a victim had access to a spoofed version of ckeditor.com via HTTP (e.g. due to DNS spoofing, using a hacked public network or mailicious hotspot), then when using a link to the ckeditor.com website it was possible for the attacker to change the current URL of the opening page, even if the opening page was protected with SSL.
fclose($infp);
if ($action == "transfert") {
if (GETPOST('reday')) {
if (GETPOSTINT("reyear") && GETPOSTINT("remonth") && GETPOSTINT("reday")) {
if (empty($infp)) {
print '<option value="1"'.(GETPOST('mouvement') ? ' selected="selected"' : '').'>'.$langs->trans("Delete").'</option>';
print '<select name="mouvement" id="mouvement" class="minwidth100 valignmiddle">';
print '<td class="left" width="25%">'.$langs->trans("Referers").'</td>';
@ -127,10 +133,16 @@
public $childs = array();
public $periode;
unset($_SESSION["adresse"]);
while ($instr = fgets($infp)) {
- creat,unitl,alltime,datas,referers
- informations,medias,uptodate,reenable,crypted,developpers
- ned
claim (excluding declatory judgment actions) against Initial Developer
waitFor: (name: string, state?: WaitState) => Promise<void>;
Corrected closure based memeory leak - there may be others?
Corrected table layout and tweaked display for more consisent presentation. Nice catch from Jonathan Pasquier.
* Copyright (C) 2019 Cedric Ancelin <icedo.anc@gmail.com>
* This solution does not work in IE, Edge and Safari due to upstream isues:
$permissiontoadd = $user->hasRight('stock', 'mouvement', 'creer');
$permissiontodelete = $user->hasRight('stock', 'mouvement', 'creer'); // There is no deletion permission for stock movement as we should never delete
$permissiontoread = $user->hasRight('stock', 'mouvement', 'lire');
@ -148,10 +160,15 @@ $usercandelete = (($user->hasRight('stock', 'mouvement', 'supprimer')));
$usercanread = $user->hasRight('stock', 'mouvement', 'lire');
$usercanread = (($user->hasRight('stock', 'mouvement', 'lire')));
$usercanupdatestock = $user->hasRight('stock', 'mouvement', 'creer');
* Completely reworked themeing via CSS sprites, resulting in only two files per theme
* Fixed XSS vulnerability in the HTML parser reported by [Maco Cortes](https://www.facebook.com/Maaacoooo).
* The private array ->status_short, ->statuts and ->status_long are now array ->labelStatusShort and ->labelStatus everywhere.
- Fix: [ bug #485 ] Configurated amount for public auto-subscription form is not taken into account
- New: Add proposals into referer page of thirdparty.
.treeview li.collapsable, .treeview li.expandable { background-position: 0 -176px; }
Lightweight and flexible transformation of an unordered list into an expandable and collapsable tree, great for unobtrusive navigation enhancements. Supports both location and cookie based persistence.
NEW: ModuleBuilder: Checkin comments begin and end before each actions
Takes an unordered list and makes all branches collapsable. The "treeview" class is added if not already present. To hide branches on first display, mark their li elements with the class "closed". If the "collapsed" option is used, mark initially open branches with class "open".
Vous pouvez voir la progression du ticket en cliquant sur le lien
for /F "tokens=2 delims=," %%i in ('tasklist /FI "IMAGENAME eq php.exe" /FO CSV /NH') do (
function checkES($IentOfi, $InumCta)

View file

@ -32,7 +32,7 @@ parameters:
- htdocs/includes/mobiledetect/*
- htdocs/includes/nusoap/*
- htdocs/includes/OAuth/*
- htdocs/includes/odtphp/zip/*
- htdocs/includes/odtphp/*
- htdocs/includes/parsedown/*
- htdocs/includes/php-iban/*
- htdocs/includes/phpoffice/*

View file

@ -164,7 +164,7 @@ if [ -s "${MISSING_FILE}.grep" ] ; then
git grep -n --column -r -F -f "${MISSING_FILE}.grep" -- ':*.php' ':*.html' ':*.js' \
| sort -t: -k 4 \
| sed 's@^\([^:]*:[^:]*:[^:]*:\)\s*@\1 Missing translation; @' > "${MISSING_FILE}.result"
| sed 's@^\([^:]*:[^:]*:[^:]*:\)\s*@\1 error - Missing translation; @' > "${MISSING_FILE}.result"
if [ -s "${MISSING_FILE}.result" ] ; then
exit_code=1

View file

@ -6,7 +6,7 @@
* Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2012-2020 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2015-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2021 Waël Almoman <info@almoman.com>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
*
@ -52,7 +52,6 @@ require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php';
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';

View file

@ -2181,9 +2181,10 @@ class Adherent extends CommonObject
* Function that validate a member
*
* @param User $user user adherent qui valide
* @param int $notrigger 1=disable trigger UPDATE (when called by create)
* @return int Return integer <0 if KO, 0 if nothing done, >0 if OK
*/
public function validate($user)
public function validate($user, $notrigger = 0)
{
global $langs, $conf;
@ -2211,11 +2212,13 @@ class Adherent extends CommonObject
$this->status = self::STATUS_VALIDATED;
// Call trigger
$result = $this->call_trigger('MEMBER_VALIDATE', $user);
if ($result < 0) {
$error++;
$this->db->rollback();
return -1;
if (!$notrigger) {
$result = $this->call_trigger('MEMBER_VALIDATE', $user);
if ($result < 0) {
$error++;
$this->db->rollback();
return -1;
}
}
// End call triggers

View file

@ -666,6 +666,7 @@ class Members extends DolibarrApi
* @throws RestException 403 Access denied
* @throws RestException 404 Member not found
* @throws RestException 422 Malformed data
* @throws RestException 500 server error
*/
public function createSubscription($id, $start_date, $end_date, $amount, $label = '')
{
@ -675,6 +676,9 @@ class Members extends DolibarrApi
if (!is_numeric($start_date) || !is_numeric($end_date) || !is_numeric($amount)) {
throw new RestException(422, 'Malformed data: subscription start or end date, or subscription amount, is not numeric');
}
if ($start_date > $end_date) {
throw new RestException(422, 'Malformed data: subscription start is not larger than end date');
}
$member = new Adherent($this->db);
$result = $member->fetch($id);
@ -682,7 +686,12 @@ class Members extends DolibarrApi
throw new RestException(404, 'member not found');
}
return $member->subscription((int) $start_date, (float) $amount, 0, '', $label, '', '', '', (int) $end_date);
$result = $member->subscription((int) $start_date, (float) $amount, 0, '', $label, '', '', '', (int) $end_date);
if ($result < 1) {
throw new RestException(500, $member->error);
} else {
return $result;
}
}
/**

View file

@ -440,12 +440,14 @@ class Subscription extends CommonObject
$result = '';
$langs->load("members");
$langs->load("main");
$label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Subscription").'</u>';
/*if (isset($this->statut)) {
$label .= ' '.$this->getLibStatut(5);
}*/
$label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
$label .= '<br><b>'.$langs->trans('Label').':</b> '.$this->note_public;
if (!empty($this->dateh)) {
$label .= '<br><b>'.$langs->trans('DateStart').':</b> '.dol_print_date($this->dateh, 'day');
}

View file

@ -356,6 +356,7 @@ if (empty($reshook)) {
if ($action == 'createsubscription_confirm' && $confirm == "yes" && $user->hasRight('adherent', 'creer')) {
$tmpmember = new Adherent($db);
$adht = new AdherentType($db);
$label = GETPOST("label");
$nbcreated = 0;
$now = dol_now();
$amount = price2num(GETPOST('amount', 'alpha'));
@ -363,7 +364,7 @@ if (empty($reshook)) {
foreach ($toselect as $id) {
$res = $tmpmember->fetch($id);
if ($res > 0) {
$result = $tmpmember->subscription($now, (float) $amount);
$result = $tmpmember->subscription($now, (float) $amount, 0, '', $label);
if ($result < 0) {
$error++;
} else {
@ -830,6 +831,7 @@ if ($massaction == 'createsubscription') {
$formquestion = array(
array('label' => $langs->trans("DateSubscription"), 'type' => 'other', 'value' => $date),
array('label' => $langs->trans("Amount"), 'type' => 'text', 'value' => price($amount, 0, '', 0), 'name' => 'amount'),
array('label' => $langs->trans("Label"), 'type' => 'text', 'value' => '', 'name' => 'label'),
array('type' => 'separator'),
array('label' => $langs->trans("MoreActions"), 'type' => 'other', 'value' => $langs->trans("None").' '.img_warning($langs->trans("WarningNoComplementaryActionDone"))),
);

View file

@ -2,7 +2,7 @@
/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2021 NextGestion <contact@nextgestion.com>
* Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,6 +26,14 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
@ -34,13 +42,6 @@ require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php';
require_once DOL_DOCUMENT_ROOT.'/partnership/class/partnership.class.php';
require_once DOL_DOCUMENT_ROOT.'/partnership/lib/partnership.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
// Load translation files required by the page
$langs->loadLangs(array("companies","members","partnership", "other"));
@ -62,9 +63,8 @@ if ($id > 0) {
}
// Initialize a technical objects
$object = new Partnership($db);
$extrafields = new ExtraFields($db);
$adht = new AdherentType($db);
$object = new Partnership($db);
$adht = new AdherentType($db);
$diroutputmassaction = $conf->partnership->dir_output.'/temp/massgeneration/'.$user->id;
$hookmanager->initHooks(array('partnershipthirdparty', 'globalcard')); // Note that conf->hooks_modules contains array

View file

@ -2,7 +2,7 @@
/* Copyright (c) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -98,139 +98,126 @@ $data = array();
$tab = null;
$label = '';
if ($mode) {
// Define sql
$sql = null;
if ($mode == 'memberbycountry') {
$label = $langs->trans("Country");
$tab = 'statscountry';
// Define sql
$sql = null;
if ($mode == 'memberbycountry') {
$label = $langs->trans("Country");
$tab = 'statscountry';
$sql = "SELECT COUNT(DISTINCT d.rowid) as nb, COUNT(s.rowid) as nbsubscriptions, MAX(d.datevalid) as lastdate, MAX(s.dateadh) as lastsubscriptiondate, c.code, c.label";
$sql .= " FROM ".MAIN_DB_PREFIX."adherent as d";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as c on d.country = c.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."subscription as s ON s.fk_adherent = d.rowid";
$sql .= " WHERE d.entity IN (".getEntity('adherent').")";
$sql .= " AND d.statut <> ".Adherent::STATUS_DRAFT;
$sql .= " GROUP BY c.label, c.code";
//print $sql;
}
$sql = "SELECT COUNT(DISTINCT d.rowid) as nb, COUNT(s.rowid) as nbsubscriptions, MAX(d.datevalid) as lastdate, MAX(s.dateadh) as lastsubscriptiondate, c.code, c.label";
$sql .= " FROM ".MAIN_DB_PREFIX."adherent as d";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as c on d.country = c.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."subscription as s ON s.fk_adherent = d.rowid";
$sql .= " WHERE d.entity IN (".getEntity('adherent').")";
$sql .= " AND d.statut <> ".Adherent::STATUS_DRAFT;
$sql .= " GROUP BY c.label, c.code";
} elseif ($mode == 'memberbystate') {
$label = $langs->trans("Country");
$label2 = $langs->trans("State");
$tab = 'statsstate';
if ($mode == 'memberbystate') {
$label = $langs->trans("Country");
$label2 = $langs->trans("State");
$tab = 'statsstate';
$data = array();
$sql = "SELECT COUNT(DISTINCT d.rowid) as nb, COUNT(s.rowid) as nbsubscriptions, MAX(d.datevalid) as lastdate, MAX(s.dateadh) as lastsubscriptiondate, co.code, co.label, c.nom as label2"; //
$sql .= " FROM ".MAIN_DB_PREFIX."adherent as d";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as c on d.state_id = c.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_regions as r on c.fk_region = r.code_region";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as co on d.country = co.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."subscription as s ON s.fk_adherent = d.rowid";
$sql .= " WHERE d.entity IN (".getEntity('adherent').")";
$sql .= " AND d.statut <> ".Adherent::STATUS_DRAFT;
$sql .= " GROUP BY co.label, co.code, c.nom";
//print $sql;
} elseif ($mode == 'memberbyregion') {
$label = $langs->trans("Country");
$label2 = $langs->trans("Region"); //département
$tab = 'statsregion'; //onglet
$data = array();
$sql = "SELECT COUNT(DISTINCT d.rowid) as nb, COUNT(s.rowid) as nbsubscriptions, MAX(d.datevalid) as lastdate, MAX(s.dateadh) as lastsubscriptiondate, co.code, co.label, c.nom as label2"; //
$sql .= " FROM ".MAIN_DB_PREFIX."adherent as d";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as c on d.state_id = c.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_regions as r on c.fk_region = r.code_region";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as co on d.country = co.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."subscription as s ON s.fk_adherent = d.rowid";
$sql .= " WHERE d.entity IN (".getEntity('adherent').")";
$sql .= " AND d.statut <> ".Adherent::STATUS_DRAFT;
$sql .= " GROUP BY co.label, co.code, c.nom";
//print $sql;
}
if ($mode == 'memberbyregion') { //
$label = $langs->trans("Country");
$label2 = $langs->trans("Region"); //département
$tab = 'statsregion'; //onglet
$data = array(); //tableau de donnée
$sql = "SELECT COUNT(DISTINCT d.rowid) as nb, COUNT(s.rowid) as nbsubscriptions, MAX(d.datevalid) as lastdate, MAX(s.dateadh) as lastsubscriptiondate, co.code, co.label, r.nom as label2";
$sql .= " FROM ".MAIN_DB_PREFIX."adherent as d";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as c on d.state_id = c.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_regions as r on c.fk_region = r.code_region";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as co on d.country = co.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."subscription as s ON s.fk_adherent = d.rowid";
$sql .= " WHERE d.entity IN (".getEntity('adherent').")";
$sql .= " AND d.statut <> ".Adherent::STATUS_DRAFT;
$sql .= " GROUP BY co.label, co.code, r.nom"; //+
} elseif ($mode == 'memberbytown') {
$label = $langs->trans("Country");
$label2 = $langs->trans("Town");
$tab = 'statstown';
$data = array(); //tableau de donnée
$sql = "SELECT COUNT(DISTINCT d.rowid) as nb, COUNT(s.rowid) as nbsubscriptions, MAX(d.datevalid) as lastdate, MAX(s.dateadh) as lastsubscriptiondate, co.code, co.label, r.nom as label2";
$sql .= " FROM ".MAIN_DB_PREFIX."adherent as d";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as c on d.state_id = c.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_regions as r on c.fk_region = r.code_region";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as co on d.country = co.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."subscription as s ON s.fk_adherent = d.rowid";
$sql .= " WHERE d.entity IN (".getEntity('adherent').")";
$sql .= " AND d.statut <> ".Adherent::STATUS_DRAFT;
$sql .= " GROUP BY co.label, co.code, r.nom"; //+
//print $sql;
}
if ($mode == 'memberbytown') {
$label = $langs->trans("Country");
$label2 = $langs->trans("Town");
$tab = 'statstown';
$data = array();
$sql = "SELECT COUNT(DISTINCT d.rowid) as nb, COUNT(s.rowid) as nbsubscriptions, MAX(d.datevalid) as lastdate, MAX(s.dateadh) as lastsubscriptiondate, c.code, c.label, d.town as label2";
$sql .= " FROM ".MAIN_DB_PREFIX."adherent as d";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as c on d.country = c.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."subscription as s ON s.fk_adherent = d.rowid";
$sql .= " WHERE d.entity IN (".getEntity('adherent').")";
$sql .= " AND d.statut <> ".Adherent::STATUS_DRAFT;
$sql .= " GROUP BY c.label, c.code, d.town";
}
$data = array();
$sql = "SELECT COUNT(DISTINCT d.rowid) as nb, COUNT(s.rowid) as nbsubscriptions, MAX(d.datevalid) as lastdate, MAX(s.dateadh) as lastsubscriptiondate, c.code, c.label, d.town as label2";
$sql .= " FROM ".MAIN_DB_PREFIX."adherent as d";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as c on d.country = c.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."subscription as s ON s.fk_adherent = d.rowid";
$sql .= " WHERE d.entity IN (".getEntity('adherent').")";
$sql .= " AND d.statut <> ".Adherent::STATUS_DRAFT;
$sql .= " GROUP BY c.label, c.code, d.town";
//print $sql;
}
$langsen = new Translate('', $conf);
$langsen->setDefaultLang('en_US');
$langsen->load("dict");
//print $langsen->trans("Country"."FI");exit;
$langsen = new Translate('', $conf);
$langsen->setDefaultLang('en_US');
$langsen->load("dict");
//print $langsen->trans("Country"."FI");exit;
// Define $data array
dol_syslog("Count member", LOG_DEBUG);
if ($sql != null) {
$resql = $db->query($sql);
} else {
$resql = false;
dol_syslog(__FILE__.":No SQL, invalid mode '$mode'", LOG_ERR);
}
if ($resql) {
$num = $db->num_rows($resql);
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
if ($mode == 'memberbycountry') {
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'code' => (string) $obj->code,
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
}
if ($mode == 'memberbyregion') { //+
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
}
if ($mode == 'memberbystate') {
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
}
if ($mode == 'memberbytown') {
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => (string) ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
}
$i++;
// Define $data array
dol_syslog("Count member", LOG_DEBUG);
if ($sql != null) {
$resql = $db->query($sql);
} else {
$resql = false;
dol_syslog(__FILE__.":No SQL, invalid mode '$mode'", LOG_ERR);
}
if ($resql) {
$num = $db->num_rows($resql);
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
if ($mode == 'memberbycountry') {
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'code' => (string) $obj->code,
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
} elseif ($mode == 'memberbyregion') { //+
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
} elseif ($mode == 'memberbystate') {
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
} elseif ($mode == 'memberbytown') {
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => (string) ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
}
$db->free($resql);
} else {
dol_print_error($db);
$i++;
}
$db->free($resql);
} else {
dol_print_error($db);
}
@ -240,7 +227,7 @@ print dol_get_fiche_head($head, (string) $tab, '', -1, '');
// Print title
if ($mode && !count($data)) {
if (!count($data)) {
print '<span class="opacitymedium">'.$langs->trans("NoValidatedMemberYet").'</span><br>';
print '<br>';
} else {
@ -316,35 +303,33 @@ if (getDolGlobalString("GOOGLE_SHOW_COUNTRY_GRAPH") && $mode == 'memberbycountry
print '<div class="center" id="'.$mode.'"></div>'."\n";
}
if ($mode) {
// Print array
print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
print '<table class="liste centpercent noborder">';
print '<tr class="liste_titre">';
print '<th>'.$label.'</th>';
if (isset($label2)) {
print '<th class="center">'.$label2.'</th>';
}
print '<th class="right">'.$langs->trans("NbOfMembers").' <span class="opacitymedium">('.$langs->trans("AllTime").')</span></th>';
print '<th class="center">'.$langs->trans("LastMemberDate").'</th>';
print '<th class="center">'.$langs->trans("LatestSubscriptionDate").'</th>';
print '</tr>';
foreach ($data as $val) {
print '<tr class="oddeven">';
print '<td>'.$val['label'].'</td>';
if (isset($label2)) {
print '<td class="center">'.$val['label2'].'</td>';
}
print '<td class="right">'.$val['nb'].'</td>';
print '<td class="center">'.dol_print_date($val['lastdate'], 'dayhour', 'auto', null, false, 1).'</td>';
print '<td class="center">'.dol_print_date($val['lastsubscriptiondate'], 'dayhour', 'auto', null, false, 1).'</td>';
print '</tr>';
}
print '</table>';
print '</div>';
// Print array
print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
print '<table class="liste centpercent noborder">';
print '<tr class="liste_titre">';
print '<th>'.$label.'</th>';
if (isset($label2)) {
print '<th class="center">'.$label2.'</th>';
}
print '<th class="right">'.$langs->trans("NbOfMembers").' <span class="opacitymedium">('.$langs->trans("AllTime").')</span></th>';
print '<th class="center">'.$langs->trans("LastMemberDate").'</th>';
print '<th class="center">'.$langs->trans("LatestSubscriptionDate").'</th>';
print '</tr>';
foreach ($data as $val) {
print '<tr class="oddeven">';
print '<td>'.$val['label'].'</td>';
if (isset($label2)) {
print '<td class="center">'.$val['label2'].'</td>';
}
print '<td class="right">'.$val['nb'].'</td>';
print '<td class="center">'.dol_print_date($val['lastdate'], 'dayhour', 'auto', null, false, 1).'</td>';
print '<td class="center">'.dol_print_date($val['lastsubscriptiondate'], 'dayhour', 'auto', null, false, 1).'</td>';
print '</tr>';
}
print '</table>';
print '</div>';
print dol_get_fiche_end();

View file

@ -717,7 +717,7 @@ if ($action != 'createsubscription' && $action != 'create_thirdparty') {
$sql = "SELECT d.rowid, d.firstname, d.lastname, d.societe, d.fk_adherent_type as type,";
$sql .= " c.rowid as crowid, c.subscription,";
$sql .= " c.datec, c.fk_type as cfk_type,";
$sql .= " c.dateadh as dateh,";
$sql .= " c.dateadh as dateh, c.note as clabel,";
$sql .= " c.datef,";
$sql .= " c.fk_bank,";
$sql .= " b.rowid as bid,";
@ -744,6 +744,7 @@ if ($action != 'createsubscription' && $action != 'create_thirdparty') {
print_liste_field_titre('DateStart', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'center ');
print_liste_field_titre('DateEnd', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'center ');
print_liste_field_titre('Amount', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
print_liste_field_titre('Label', $_SERVER["PHP_SELF"], 'clabel', '', $param, '', $sortfield, $sortorder);
if (isModEnabled('bank')) {
print_liste_field_titre('Account', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
}
@ -762,6 +763,7 @@ if ($action != 'createsubscription' && $action != 'create_thirdparty') {
$subscriptionstatic->ref = $objp->crowid;
$subscriptionstatic->id = $objp->crowid;
$subscriptionstatic->note_public = $objp->clabel;
$typeid = $objp->cfk_type;
if ($typeid > 0) {
@ -779,6 +781,7 @@ if ($action != 'createsubscription' && $action != 'create_thirdparty') {
print '<td class="center">'.dol_print_date($db->jdate($objp->dateh), 'day')."</td>\n";
print '<td class="center">'.dol_print_date($db->jdate($objp->datef), 'day')."</td>\n";
print '<td class="right amount">'.price($objp->subscription).'</td>';
print '<td class="left">'.$objp->clabel.'</td>';
if (isModEnabled('bank')) {
print '<td class="tdoverflowmax100 right">';
if ($objp->bid) {

View file

@ -138,7 +138,7 @@ if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massa
$massaction = '';
}
$parameters = array('socid' => isset($socid) ? $socid : null);
$parameters = array('socid' => null);
$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
if ($reshook < 0) {
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
@ -480,7 +480,7 @@ if (!empty($arrayfields['d.login']['checked'])) {
if (!empty($arrayfields['c.note']['checked'])) {
print '<td class="liste_titre">';
print '';
print '<input class="flat maxwidth100" type="text" name="search_note" value="'.dol_escape_htmltag($search_note).'"></td>';
print '</td>';
}

View file

@ -29,7 +29,6 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
@ -38,7 +37,6 @@ require '../main.inc.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php';
// Load translation files required by the page

View file

@ -0,0 +1,109 @@
<?php
/* Copyright (C) 2001-2002 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
* Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/admin/bank_various_payment_extrafields.php
* \ingroup bank
* \brief Page to setup extra fields of various payments
*/
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
// Load translation files required by the page
$langs->loadLangs(array("admin", "banks", "other"));
$extrafields = new ExtraFields($db);
$form = new Form($db);
// List of supported format
$type2label = ExtraFields::getListOfTypesLabels();
$action = GETPOST('action', 'aZ09');
$attrname = GETPOST('attrname', 'alpha');
$elementtype = 'payment_various'; // Must match the table_element of PaymentVarious
if (!$user->admin) {
accessforbidden();
}
/*
* Actions
*/
require DOL_DOCUMENT_ROOT.'/core/actions_extrafields.inc.php';
/*
* View
*/
$textobject = $langs->transnoentitiesnoconv("VariousPayments");
llxHeader('', $langs->trans("BankSetupModule"));
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
print load_fiche_titre($langs->trans("BankSetupModule"), $linkback, 'title_setup');
$head = bank_admin_prepare_head(null);
print dol_get_fiche_head($head, 'bank_various_payment_extrafields', $langs->trans("BankSetupModule"), -1, '');
require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php';
print dol_get_fiche_end();
// Buttons
if ($action != 'create' && $action != 'edit') {
print '<div class="tabsAction">';
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=create">'.$langs->trans("NewAttribute").'</a>';
print "</div>";
}
// Creation of an optional field
if ($action == 'create') {
print '<br><div id="newattrib"></div>';
require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_add.tpl.php';
}
// Edition of an optional field
if ($action == 'edit' && !empty($attrname)) {
print "<br>";
require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_edit.tpl.php';
}
// End of page
llxFooter();
$db->close();

View file

@ -223,6 +223,7 @@ print ajax_constantonoff('AI_MCP_ENABLED', array(), null, 0, 0, 1);
//print '</table>';
print '</div>';
print '<br>';
print '<br>';
if (getDolGlobalString('AI_MCP_ENABLED')) {
@ -237,21 +238,22 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
// Input Mode
print '<tr class="oddeven">';
print '<td>Default Interface Mode</td>';
print '<td>';
print $form->textwithpicto('Default Interface', $langs->trans("InputMethodHelp"));
print '</td>';
print '<td>';
$input_modes = [
'text' => '💬 ' . $langs->trans('OptionTextOnly'),
'native' => '☁️ ' . $langs->trans('OptionCloudFast') . ' - ' . $langs->trans('OptionCloudFasthelp'),
'whisper' => '🔒 ' . $langs->trans('OptionWhisperLocal') . ' - ' . $langs->trans('OptionWhisperLocalhelp')
'text' => $langs->trans('OptionTextOnly'),
'native' => $langs->trans('OptionCloudFast') . ' - ' . $langs->trans('OptionCloudFasthelp'),
'whisper' => $langs->trans('OptionWhisperLocal') . ' - ' . $langs->trans('OptionWhisperLocalhelp')
];
print $form->selectarray('AI_DEFAULT_INPUT_MODE', $input_modes, getDolGlobalString('AI_DEFAULT_INPUT_MODE'), 0, 0, 0);
print '<br><span class="opacitymedium small">' . $langs->trans("InputMethodHelp") . '</span>';
print '</td>';
print '</tr>';
// Enhanced Privacy control
print '<tr class="oddeven">';
print '<td class="titlefield" width="30%">' . $langs->trans('ObfuscatePIIData') . '</td>';
print '<td>' . $langs->trans('ObfuscatePIIData') . '</td>';
print '<td>';
print ajax_constantonoff('AI_PRIVACY_REDACTION');
print ' <span class="opacitymedium">' . $langs->trans("RedactionHelp") . '</span>';
@ -260,8 +262,7 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
// Confirmation Level
print '<tr class="oddeven">';
print '<td class="titlefield" width="30%">' . $langs->trans("AskConfirmation");
print ' <span class="fa fa-info-circle" title="' . $langs->trans("AskConfirmationHelp") . '"></span></td>';
print '<td>' . $form->textwithpicto($langs->trans("AskConfirmation"), $langs->trans("AskConfirmationHelp")).'</td>';
print '<td>';
$confirmation_options = [
'0' => $langs->trans("ConfirmNever"),
@ -274,7 +275,7 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
// Logging
print '<tr class="oddeven">';
print '<td class="titlefield" width="30%">' . $langs->trans('EnableLogging') . '</td>';
print '<td>' . $langs->trans('EnableLogging') . '</td>';
print '<td>';
print ajax_constantonoff('AI_LOG_REQUESTS');
print ' <a href="' . DOL_URL_ROOT . '/ai/admin/log_viewer.php" target="_blank" class="button" style="padding-top: 4px; padding-bottom: 4px;">View Logs</a>';
@ -283,7 +284,7 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
print '<tr class="oddeven">';
print '<td>' . $langs->trans("LogRetention") . '</td>';
print '<td><input type="number" name="AI_LOG_RETENTION" value="' . getDolGlobalInt('AI_LOG_RETENTION', 30) . '" size="5"> (0 = Forever)</td>';
print '<td><input class="width50" type="number" name="AI_LOG_RETENTION" value="' . getDolGlobalInt('AI_LOG_RETENTION', 30) . '"> (0 = Forever)</td>';
print '</tr>';
// System Prompt
@ -302,6 +303,8 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
print '<div class="center"><input type="submit" class="button" value="'.$langs->trans("Save").'"></div>';
print '</form>';
print '<br>';
print '<br>';
// AI Provider Config and Connection testing
@ -332,11 +335,11 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
print '</div>';
}
print '</div>';
print '</form>';
print '<br><br>';
// External Access Configuration
print load_fiche_titre($langs->trans("AiMcpExternalAccess"), '', 'fas fa-lock-open text-danger');
print load_fiche_titre($langs->trans("AiMcpExternalAccess"), '', 'fas fa-lock-open');
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
print '<input type="hidden" name="token" value="'.newToken().'">';
@ -362,11 +365,10 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
print '<td>';
if ($apiKey) {
print '<input type="text" id="apikey" value="'.$apiKey.'" readonly style="width:400px; padding:6px; background:#f4f4f4; border:1px solid #ccc; color:#555;">';
print ' <button type="button" class="button small" onclick="navigator.clipboard.writeText(document.getElementById(\'apikey\').value)">' . $langs->trans("Copy") . '</button>';
print ' <a class="button" href="'.$_SERVER["PHP_SELF"].'?action=generate_key&token='.newToken().'">Generate New Key</a>';
print ' <a class="button smallpaddingimp" href="'.$_SERVER["PHP_SELF"].'?action=generate_key&token='.newToken().'">Generate New Key</a>';
} else {
print '<span class="opacitymedium">' . $langs->trans("NoKeyWarning") . '</span>';
print ' <a class="button" href="' . $_SERVER["PHP_SELF"] . '?action=generate_key&token=' . newToken() . '">' . $langs->trans("GenerateKey") . '</a>';
print ' <a class="button smallpaddingimp" href="' . $_SERVER["PHP_SELF"] . '?action=generate_key&token=' . newToken() . '">' . $langs->trans("GenerateKey") . '</a>';
}
print '</td>';
print '</tr>';
@ -390,7 +392,8 @@ if (getDolGlobalString('AI_MCP_ENABLED')) {
print '<div style="background:#fcfcfc; border:1px solid #eee; padding:15px; border-radius:5px;">';
print '<strong>' . $langs->trans("ClaudeDesktopConfig") . '</strong><br>';
print '<pre style="background:#333; color:#fff; padding:10px; border-radius:4px; overflow:auto; margin-top:10px;">';
echo htmlspecialchars('{
echo htmlspecialchars('
{
"mcpServers": {
"dolibarr": {
"command": "node",

View file

@ -270,7 +270,16 @@ try {
}
// If we are sending a lot of tools (Non-Latin or Fallback), we strip descriptions.
$isLargeSchema = count($toolsSchema) > 20;
// The 20-tool threshold was likely chosen for GPT-3.5 (4K context window). Modern
// LLMs handle the full schema trivially: Gemini 2.5 Flash has a 1M token context,
// GPT-4o has 128K, Claude Sonnet has 1M. Compression hurts more than it helps
// today because it also truncates tool *descriptions* (down to 3 words), which
// breaks tool selection (e.g. "create_other_document" becomes "Create documents
// other" -- the LLM then thinks supplier_invoice creation is not available).
// We raise the threshold to 100 to effectively disable compression for the
// default install (~30 tools), while still leaving a safety net for very large
// custom installs that register dozens of additional addMcpTools hooks.
$isLargeSchema = count($toolsSchema) > 100;
$toolsForLLM = cleanToolSchemaForLLM($toolsSchema, $isLargeSchema);
// Build System Prompt
@ -293,8 +302,34 @@ try {
$defUrl = $servicesList[$serviceKey]['url'] ?? '';
$url = getDolGlobalString('AI_API_' . strtoupper($serviceKey) . '_URL') ?: $defUrl;
$defModel = $servicesList[$serviceKey]['textgeneration'] ?? 'gpt-4o-mini';
$model = getDolGlobalString('AI_API_' . strtoupper($serviceKey) . '_MODEL') ?: $defModel;
// The model defaults declared in getListOfAIServices() are nested:
// $servicesList[$key]['textgeneration'] = ['default' => 'model-name']
// Reading 'textgeneration' without ['default'] returns the inner array, which
// then fails the (string) type-hint of UniversalLLMAdapter's 4th argument with:
// "Argument #4 ($model) must be of type string, array given"
//
// The admin UI (htdocs/ai/admin/setup.php "Prompt and custom AI models" tab) also
// 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 lookup used AI_API_<SERVICE>_MODEL which is never written by that
// form, so the user-configured model was silently ignored.
$rawDefault = $servicesList[$serviceKey]['textgeneration'] ?? null;
if (is_array($rawDefault)) {
$defModel = $rawDefault['default'] ?? 'gpt-4o-mini';
} else {
$defModel = $rawDefault ?: 'gpt-4o-mini';
}
$prefix = 'AI_API_' . strtoupper($serviceKey);
$model = getDolGlobalString($prefix . '_MODEL_TEXT')
?: getDolGlobalString($prefix . '_MODEL')
?: $defModel;
// Defensive: coerce to string if anyone stored an array in this constant
if (is_array($model)) {
$model = $model['default'] ?? $defModel;
}
if (!is_string($model) || $model === '') {
$model = (string) $defModel;
}
$adapterType = $servicesList[$serviceKey]['adapter_type'] ?? 'openai';
if (!empty($apiKey)) {
@ -727,12 +762,15 @@ function cleanToolSchemaForLLM(array $tools, bool $isLargeSchema = false)
$cleaned = [];
foreach ($tools as $tool) {
// Tool Description: Max 3 words
// Tool descriptions are how the LLM selects the right tool -- never truncate
// them, even when the schema is large. Truncating to 3 words ("Create documents
// other", "Add a single") breaks tool selection. If the schema really is too
// big for the chosen model, the right answer is to filter the toolset before
// it reaches the LLM (which is what filterToolsProfessional() already does
// upstream of this function), not to mutilate each tool's description.
// Parameter-level compression (stripping defaults, descriptions of optional
// fields, etc.) remains gated on $isLargeSchema below.
$desc = $tool['description'];
if ($isLargeSchema) {
$words = explode(' ', $desc);
$desc = implode(' ', array_slice($words, 0, 3));
}
// Get parameters
$toolParams = $tool['parameters'] ?? $tool['inputSchema'] ?? [];

View file

@ -140,7 +140,14 @@ class Ai
$this->apiEndpoint .= (preg_match('/\/$/', $this->apiEndpoint) ? '' : '/').'threads';
} else { // if $function == 'docparsing', 'text...', ...
$this->apiEndpoint = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_URL', $arrayofai[$this->apiService]['url']);
$this->apiEndpoint .= (preg_match('/\/$/', $this->apiEndpoint) ? '' : '/').'chat/completions';
if ($this->apiService == 'google') {
// Google Gemini native API: the /models/<model>:generateContent suffix is
// appended later (once $model has been resolved). The OpenAI-style
// /chat/completions does not exist on the native Gemini endpoint.
$this->apiEndpoint = rtrim($this->apiEndpoint, '/');
} else {
$this->apiEndpoint .= (preg_match('/\/$/', $this->apiEndpoint) ? '' : '/').'chat/completions';
}
}
}
if ($moreendpoint) {
@ -170,6 +177,12 @@ class Ai
}
}
// Google Gemini: append /models/<model>:generateContent now that $model is resolved.
if ($this->apiService == 'google' && !in_array($function, array('file', 'assistant', 'thread'))
&& strpos($this->apiEndpoint, ':generateContent') === false) {
$this->apiEndpoint .= '/models/'.rawurlencode($model).':generateContent';
}
dol_syslog("Call API for apiKey=".substr($this->apiKey, 0, 5).'***********, apiEndpoint='.$this->apiEndpoint.", model=".$model.", format=".$format);
if (getDolGlobalString("AI_DEBUG")) {
if (@is_writable($dolibarr_main_data_root)) { // Avoid fatal error on fopen with open_basedir
@ -269,18 +282,33 @@ class Ai
"top_p": 0.95
}*/
$arrayforpayload = array(
'messages' => array(array('role' => 'user', 'content' => $fullInstructions)),
'model' => $model,
);
// Add a system message
$addDateTimeContext = false;
if ($addDateTimeContext) { // @phpstan-ignore-line
$prePrompt = ($prePrompt ? $prePrompt.(preg_match('/[\.\!\?]$/', $prePrompt) ? '' : '.').' ' : '').'Today we are '.dol_print_date(dol_now(), 'dayhourtext');
}
if ($prePrompt) {
$arrayforpayload['messages'][] = array('role' => 'system', 'content' => $prePrompt);
if ($this->apiService == 'google') {
// Google Gemini native payload format (different from OpenAI's "messages").
$arrayforpayload = array(
'contents' => array(
array('role' => 'user', 'parts' => array(array('text' => $fullInstructions)))
)
);
if ($prePrompt) {
$arrayforpayload['system_instruction'] = array(
'parts' => array(array('text' => $prePrompt))
);
}
} else {
// OpenAI-compatible payload format (chatgpt, mistral, groq, anthropic-compat, custom, ...)
$arrayforpayload = array(
'messages' => array(array('role' => 'user', 'content' => $fullInstructions)),
'model' => $model,
);
if ($prePrompt) {
$arrayforpayload['messages'][] = array('role' => 'system', 'content' => $prePrompt);
}
}
}
@ -296,9 +324,16 @@ class Ai
$payload = json_encode($arrayforpayload);
}
$headers = array(
'Authorization: Bearer ' . $this->apiKey,
);
if ($this->apiService == 'google') {
// Google Gemini uses the x-goog-api-key header (Bearer is not accepted by the native API).
$headers = array(
'x-goog-api-key: ' . $this->apiKey,
);
} else {
$headers = array(
'Authorization: Bearer ' . $this->apiKey,
);
}
if ($function != 'file') {
$headers[] = 'Content-Type: application/json';
}
@ -388,6 +423,18 @@ class Ai
} else {
$generatedContent = var_export($decodedResponse['error'], true);
}
} elseif ($this->apiService == 'google') {
// Google Gemini response shape: candidates[0].content.parts[*].text
// (parts is an array because Gemini can return mixed-modality output;
// we concatenate the textual parts.)
$generatedContent = '';
if (!empty($decodedResponse['candidates'][0]['content']['parts'])) {
foreach ($decodedResponse['candidates'][0]['content']['parts'] as $part) {
if (isset($part['text'])) {
$generatedContent .= $part['text'];
}
}
}
} else {
$generatedContent = $decodedResponse['choices'][0]['message']['content'];
}

View file

@ -195,32 +195,42 @@ class UniversalLLMAdapter
*/
private function curl(string $url, array $data, array $headers, bool $isClaude = false, bool $isGemini = false): ?string
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
$payload = json_encode($data);
if ($payload === false) {
return "Error: Failed to encode JSON payload.";
// By default, we accept only external endpoints ($dolibarr_ai_allow_local_endpoints is not set).
// To allow local endpoints, we must set $dolibarr_ai_allow_local_endpoints to 1 or 2 in conf.php.
global $dolibarr_ai_allow_local_endpoints;
$localurl = $dolibarr_ai_allow_local_endpoints ?? 0;
// Pass $this->timeout as the response timeout so the LLM-specific value configured
// at construction time is honored (getURLContent's $timeoutresponse is the 10th arg;
// preceding args $ssl_verifypeer=-1 and $timeoutconnect=0 keep their defaults).
$result = getURLContent($url, 'POST', json_encode($data), 1, $headers, array('http', 'https'), $localurl, -1, 0, $this->timeout);
$body = (string) ($result['content'] ?? '');
$httpCode = (int) ($result['http_code'] ?? 0);
$effectiveUrl = (string) ($result['url'] ?? $url);
// Store an enriched payload so the admin Log Viewer ("VIEW LOGS" in the AI Server
// MCP setup page) shows something actionable when something goes wrong, not just
// a bare "Invalid JSON response from API." with an empty body.
$this->lastResponse = "HTTP " . $httpCode . " from " . $effectiveUrl
. "\n--- body (" . strlen($body) . " bytes) ---\n"
. $body;
if (!empty($result['curl_error_no'])) {
return "Error: cURL #" . $result['curl_error_no'] . " " . $result['curl_error_msg'] . " (url=" . $effectiveUrl . ")";
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
$res = curl_exec($ch);
$this->lastResponse = (string) $res;
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
return "Error: " . $error;
}
curl_close($ch);
$json = json_decode((string) $res, true);
$json = json_decode($body, true);
if ($json === null && json_last_error() !== JSON_ERROR_NONE) {
return "Error: Invalid JSON response from API.";
// Common real-world causes: HTTP 4xx/5xx with empty body, HTML error page
// from a proxy, gateway timeout, etc. Surface the HTTP code and a short
// body snippet so the admin can diagnose without re-running with curl.
$snippet = substr($body, 0, 500);
return "Error: Invalid JSON response from API (HTTP " . $httpCode . ", "
. strlen($body) . " bytes). Body snippet: "
. ($snippet !== '' ? $snippet : '<empty>');
}
if (isset($json['error'])) {

View file

@ -589,7 +589,20 @@ document.addEventListener('DOMContentLoaded', () => {
try {
const { getDocument } = await getPdfLib();
const arrayBuffer = await file.arrayBuffer();
const pdf = await getDocument({ data: arrayBuffer }).promise;
// cMapUrl + standardFontDataUrl are REQUIRED for PDFs that ship with
// custom CID fonts referencing ToUnicode CMaps (extremely common with
// generators like TCPDF, wkhtmltopdf, iText, etc.). Without these,
// getTextContent() returns raw glyph codes that look like garbled
// base64. jsdelivr is used here because cdnjs (used elsewhere for the
// pdf.js bundle) does not distribute the cmaps/ and standard_fonts/
// resource directories.
const PDFJS_RES = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@5.4.149';
const pdf = await getDocument({
data: arrayBuffer,
cMapUrl: PDFJS_RES + '/cmaps/',
cMapPacked: true,
standardFontDataUrl: PDFJS_RES + '/standard_fonts/',
}).promise;
let fullText = "";
const totalPages = pdf.numPages; // Get total pages
@ -688,7 +701,7 @@ document.addEventListener('DOMContentLoaded', () => {
class SpeechPipeline {
static task = 'automatic-speech-recognition';
static model = 'Xenova/whisper-small.en';
static model = 'Xenova/whisper-small.en';
static instance = null;
static async getInstance(progress_callback = null) {

View file

@ -76,12 +76,20 @@ $headers = array_change_key_case($headers, CASE_LOWER);
$authHeader = $headers['authorization'] ?? '';
$apiKeyHeader = $headers['x-api-key'] ?? '';
// Fallback: also accept the key in a query string parameter (?api_key=XXX or ?key=XXX).
// Required for MCP clients that don't support custom auth headers in their connector UI
// (e.g. Claude Desktop "Custom Connectors" in beta only exposes OAuth fields).
// SECURITY NOTE: query-string keys appear in webserver access logs and possibly in Referer
// headers. Header-based auth (X-API-Key / Authorization) remains preferred and is tried first.
// Administrators relying on the fallback should restrict access at the webserver level
// and/or rotate AI_MCP_API_KEY regularly.
$apiKeyQuery = $_GET['api_key'] ?? $_GET['key'] ?? '';
$storedKey = getDolGlobalString('AI_MCP_API_KEY');
$valid = false;
if (!empty($storedKey)) {
// X-API-Key
// X-API-Key header (preferred)
if (!empty($apiKeyHeader)) {
$valid = hash_equals($storedKey, $apiKeyHeader);
}
@ -94,6 +102,11 @@ if (!empty($storedKey)) {
$valid = hash_equals($storedKey, $token);
}
}
// Query-string fallback (last resort for header-less clients)
if (!$valid && !empty($apiKeyQuery)) {
$valid = hash_equals($storedKey, $apiKeyQuery);
}
}
if (!$valid) {
@ -133,8 +146,74 @@ if ($userId > 0) {
exit;
}
// Load the AI request log helper so we can persist tools/call invocations to
// llx_ai_request_log (same table the AI Assistant web UI logs to). This gives
// administrators a single place to audit external MCP client activity.
require_once DOL_DOCUMENT_ROOT . '/ai/lib/ai.lib.php';
/**
* Persist an MCP tools/call invocation to the llx_ai_request_log table.
* Mirrors what parse_intent.php does for the web AI Assistant.
* Only tools/call is logged -- lifecycle methods (initialize, ping,
* notifications/initialized, tools/list, ...) are skipped to avoid noise.
*
* @param array<string,mixed> $req The JSON-RPC request
* @param mixed $resp The JSON-RPC response (null for notifications)
* @param float $tStart microtime(true) captured before processing
* @param string $rawInput Raw request body
* @return void
*/
function mcp_log_request(array $req, $resp, float $tStart, string $rawInput): void
{
global $db, $serviceUser;
$method = isset($req['method']) ? (string) $req['method'] : '';
if ($method !== 'tools/call' || !function_exists('ai_log_request')) {
return;
}
$params = isset($req['params']) && is_array($req['params']) ? $req['params'] : [];
$toolName = isset($params['name']) ? (string) $params['name'] : '';
$toolArgs = isset($params['arguments']) ? $params['arguments'] : [];
$argsJson = is_string($toolArgs) ? $toolArgs : (string) json_encode($toolArgs);
$query = '[MCP] ' . $toolName . ' ' . dol_substr($argsJson, 0, 1000);
$responseShape = ['tool' => $toolName, 'arguments' => $toolArgs];
$status = 'Success';
$errorMsg = '';
if (is_array($resp) && isset($resp['error'])) {
$status = 'Error';
$errorMsg = is_array($resp['error']) && isset($resp['error']['message'])
? (string) $resp['error']['message']
: (string) json_encode($resp['error']);
} elseif (is_array($resp) && isset($resp['result']['isError']) && $resp['result']['isError']) {
$status = 'Error';
$errorMsg = is_array($resp['result']['content'] ?? null) ? (string) json_encode($resp['result']['content']) : '';
}
$rawResStr = is_string($resp) ? $resp : (string) json_encode($resp);
ai_log_request(
$db,
$serviceUser,
$query,
$responseShape,
'mcp',
microtime(true) - $tStart,
1.0,
$status,
$errorMsg,
$rawInput,
$rawResStr
);
}
// Request handling
try {
$tStart = microtime(true);
// Basic payload size limit
$rawInput = file_get_contents('php://input');
if ($rawInput === false || strlen($rawInput) > 1024 * 1024) {
@ -168,11 +247,15 @@ try {
continue;
}
$reqStart = microtime(true);
$res = $server->handleRequest($req);
if ($res !== null) {
$responses[] = $res;
}
// Log each tools/call separately so the admin log viewer shows them individually.
mcp_log_request($req, $res, $reqStart, (string) json_encode($req));
}
echo json_encode($responses);
@ -187,6 +270,10 @@ try {
if ($response !== null) {
echo json_encode($response);
}
// Log this tools/call to llx_ai_request_log (no-op unless AI_LOG_REQUESTS is enabled
// and the method is tools/call).
mcp_log_request($request, $response, $tStart, $rawInput);
}
} catch (Exception $e) {
dol_syslog(

View file

@ -196,6 +196,12 @@ If user says 'order' without any qualifier, they mean a SALES ORDER - use this t
"header" => [
"type" => "object",
"description" => "Invoice header data. Must include 'socid' (Customer ID).",
"properties" => [
"socid" => ["type" => "integer", "description" => "Customer ID (Thirdparty ID)"],
"date" => ["type" => "string", "description" => "Invoice date (YYYY-MM-DD)"],
"note_public" => ["type" => "string", "description" => "Public note"],
"note_private" => ["type" => "string", "description" => "Private note"]
],
"required" => ["socid"]
],
"lines" => [
@ -233,6 +239,13 @@ If user says 'order' without any qualifier, they mean a SALES ORDER - use this t
"header" => [
"type" => "object",
"description" => "Header data. Must include 'socid'.",
"properties" => [
"socid" => ["type" => "integer", "description" => "Thirdparty ID (Customer for proposal, Supplier for supplier_*)"],
"date" => ["type" => "string", "description" => "Document date (YYYY-MM-DD)"],
"duree_validite" => ["type" => "integer", "description" => "Validity in days (proposal only)"],
"note_public" => ["type" => "string", "description" => "Public note"],
"note_private" => ["type" => "string", "description" => "Private note"]
],
"required" => ["socid"]
],
"lines" => [
@ -539,8 +552,17 @@ If user says 'order' without any qualifier, they mean a SALES ORDER - use this t
if ($productIdentifier !== '') {
$findResult = $this->findProduct($productIdentifier);
if (is_array($findResult) && isset($findResult['error'])) {
// Ensure the return array matches the expected shape by adding 'success' => false
return array_merge(['success' => false], $findResult);
// Only abort if the caller EXPLICITLY asked for a product (via the 'product'
// argument). If they only provided a free-text 'description', we silently
// fall through with $prod = null so Dolibarr creates a free-text line item,
// which is a perfectly valid Dolibarr feature.
// Previous behaviour aborted ALL line creations whose description didn't
// match an existing product reference, which broke AI-driven creation of
// invoices/orders/proposals from one-off line descriptions.
if (isset($args['product'])) {
return array_merge(['success' => false], $findResult);
}
// fall through: $prod stays null
}
if (is_object($findResult)) {
$prod = $findResult;
@ -599,7 +621,14 @@ If user says 'order' without any qualifier, they mean a SALES ORDER - use this t
$res = $object->addline($desc, $price, $qty, $vat, 0, 0, $fkProduct, $discount, 'HT', 0, 0, $prodType);
} elseif ($docType === 'supplier_invoice') {
/** @var FactureFournisseur $object */
$res = $object->addline($desc, $price, $qty, $vat, 0, 0, $fkProduct, $discount, 0, 0, '', 0, 'HT', 0, $prodType);
// IMPORTANT: FactureFournisseur::addline() does NOT share the same signature
// as Facture::addline(). Its parameter order is:
// ($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product, $remise_percent, ...)
// i.e. $qty is in position 6, not 3 (unlike customer Facture / Commande / Propal).
// The previous call passed our $qty as $txtva (-> a 1% VAT rate) and our $vat
// as $txlocaltax1, and position 6 ended up being a hardcoded 0 -> a line was
// inserted with qty=0, which Dolibarr silently dropped from the visible totals.
$res = $object->addline($desc, $price, $vat, 0, 0, $qty, $fkProduct, $discount, '', '', 0, 0, 'HT', $prodType);
} elseif ($docType === 'supplier_order') {
/** @var CommandeFournisseur $object */
$res = $object->addline($desc, $price, $qty, $vat, 0, 0, $fkProduct, $discount, 0, 0, 'HT', 0, '', '', $prodType);

View file

@ -230,77 +230,129 @@ class ToolReports extends McpTool
{
global $langs;
$langs->loadLangs(array("main", "bills", "companies"));
$langs->loadLangs(array("main", "bills", "companies", "products"));
$limit = isset($args['limit']) ? (int) $args['limit'] : 50;
$limit = isset($args['limit']) ? (int) $args['limit'] : 50;
$dateStart = dol_stringtotime($args['date_start']);
$dateEnd = dol_stringtotime($args['date_end']);
$socid = $this->resolveThirdparty($args);
$sql = "SELECT f.rowid, f.ref, f.total_ttc, f.fk_statut, f.paye, f.datef, s.nom
FROM " . MAIN_DB_PREFIX . "facture as f
LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON f.fk_soc = s.rowid
WHERE f.entity IN (" . getEntity('facture') . ")";
$sql .= " AND f.datef >= '" . $this->db->idate($dateStart) . "'";
$sql .= " AND f.datef <= '" . $this->db->idate($dateEnd) . "'";
$dateEnd = dol_stringtotime($args['date_end']);
$socid = $this->resolveThirdparty($args);
$groupBy = isset($args['group_by']) ? (string) $args['group_by'] : 'thirdparty';
$list = [];
$totalSum = 0.0;
// Status Filter: Valid (1) and Paid (2). Exclude Draft (0) and Abandoned (3).
$sql .= " AND f.fk_statut IN (1, 2)";
$dateRange = " AND f.datef >= '" . $this->db->idate($dateStart)
. "' AND f.datef <= '" . $this->db->idate($dateEnd)
. "' AND f.fk_statut IN (1, 2)";
// CASE 1 -- Detailed list for a specific thirdparty.
if ($socid) {
$sql .= " AND f.fk_soc = " . (int) $socid;
}
$sql = "SELECT f.rowid, f.ref, f.total_ttc, f.fk_statut, f.paye, f.datef, s.nom FROM "
. MAIN_DB_PREFIX . "facture as f LEFT JOIN "
. MAIN_DB_PREFIX . "societe as s ON f.fk_soc = s.rowid WHERE f.entity IN ("
. getEntity('facture') . ")"
. $dateRange
. " AND f.fk_soc = " . (int) $socid
. " ORDER BY f.datef DESC LIMIT " . ((int) $limit);
$sql .= " ORDER BY f.datef DESC LIMIT " . ((int) $limit);
$resql = $this->db->query($sql);
if ($resql) {
while ($r = $this->db->fetch_object($resql)) {
$totalSum += (float) $r->total_ttc;
$resql = $this->db->query($sql);
$list = [];
$totalSum = 0.0;
$statusLabel = $langs->transnoentitiesnoconv("Unknown");
if ($r->fk_statut == 1 && $r->paye == 0) {
$statusLabel = $langs->transnoentitiesnoconv("BillStatusNotPaid");
} elseif ($r->fk_statut == 1 && $r->paye == 1) {
$statusLabel = $langs->transnoentitiesnoconv("BillStatusStarted");
} elseif ($r->fk_statut == 2) {
$statusLabel = $langs->transnoentitiesnoconv("BillStatusPaid");
}
if ($resql) {
while ($r = $this->db->fetch_object($resql)) {
$totalSum += (float) $r->total_ttc;
$url = DOL_URL_ROOT . "/compta/facture/card.php?id=" . $r->rowid;
$refHtml = '<a href="' . $url . '">' . $r->ref . '</a>';
// Determine Localized Status
$statusLabel = $langs->transnoentitiesnoconv("Unknown");
if ($r->fk_statut == 1 && $r->paye == 0) {
$statusLabel = $langs->transnoentitiesnoconv("BillStatusNotPaid");
} elseif ($r->fk_statut == 1 && $r->paye == 1) {
$statusLabel = $langs->transnoentitiesnoconv("BillStatusStarted");
} elseif ($r->fk_statut == 2) {
$statusLabel = $langs->transnoentitiesnoconv("BillStatusPaid");
$list[] = [
$langs->transnoentitiesnoconv("Ref") => $refHtml,
$langs->transnoentitiesnoconv("Date") => dol_print_date($this->db->jdate($r->datef), 'day'),
$langs->transnoentitiesnoconv("Customer") => $r->nom,
$langs->transnoentitiesnoconv("Amount") => price($r->total_ttc),
$langs->transnoentitiesnoconv("Status") => $statusLabel
];
}
// Build relative URL
$url = DOL_URL_ROOT . "/compta/facture/card.php?id=" . $r->rowid;
// Make Ref clickable
$refHtml = '<a href="' . $url . '">' . $r->ref . '</a>';
$list[] = [
$langs->transnoentitiesnoconv("Ref") => $refHtml,
$langs->transnoentitiesnoconv("Date") => dol_print_date($this->db->jdate($r->datef), 'day'),
$langs->transnoentitiesnoconv("Customer") => $r->nom,
$langs->transnoentitiesnoconv("Amount") => price($r->total_ttc),
$langs->transnoentitiesnoconv("Status") => $statusLabel
];
$this->db->free($resql);
}
} else {
// CASE 2 -- Global grouped report.
// Mirrors the pattern already used by getPurchaseReport(); previous implementation
// of getSalesReport() ignored $groupBy entirely and always returned a flat list.
$sanitizedSqlGroup = '';
$colName = '';
$sqlJoin = " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON f.fk_soc = s.rowid";
if ($groupBy === 'month') {
$sanitizedSqlGroup = "DATE_FORMAT(f.datef, '%Y-%m')";
$colName = $langs->transnoentitiesnoconv("Month");
} elseif ($groupBy === 'product') {
// Aggregate on product line items. Lines without product_id fall back to their description.
$sanitizedSqlGroup = "COALESCE(p.ref, fd.description, '?')";
$colName = $langs->transnoentitiesnoconv("Product");
$sqlJoin .= " INNER JOIN " . MAIN_DB_PREFIX . "facturedet as fd ON fd.fk_facture = f.rowid LEFT JOIN "
. MAIN_DB_PREFIX . "product as p ON fd.fk_product = p.rowid";
} else {
// Default: group by customer
$sanitizedSqlGroup = "s.nom";
$colName = $langs->transnoentitiesnoconv("Customer");
}
// For product grouping we sum line totals (more accurate per-product);
// otherwise we sum the invoice total_ttc.
$amountExpr = ($groupBy === 'product') ? "SUM(fd.total_ttc)" : "SUM(f.total_ttc)";
$countExpr = ($groupBy === 'product') ? "COUNT(DISTINCT f.rowid)" : "COUNT(f.rowid)";
$sql = "SELECT " . $sanitizedSqlGroup . " as group_key, "
. $amountExpr . " as total_amount, "
. $countExpr . " as count_inv FROM "
. MAIN_DB_PREFIX . "facture as f"
. $sqlJoin
. " WHERE f.entity IN (" . getEntity('facture') . ")"
. $dateRange
. " GROUP BY group_key ORDER BY total_amount DESC LIMIT "
. ((int) max(1, $limit));
$resql = $this->db->query($sql);
if ($resql) {
while ($r = $this->db->fetch_object($resql)) {
$totalSum += (float) $r->total_amount;
$list[] = [
$colName => $r->group_key ? $r->group_key : $langs->transnoentitiesnoconv('Unknown'),
$langs->transnoentitiesnoconv("Number") => (int) $r->count_inv,
$langs->transnoentitiesnoconv("Amount") => price($r->total_amount)
];
}
$this->db->free($resql);
}
$this->db->free($resql);
}
if (empty($list)) {
return [[$langs->transnoentitiesnoconv("Info") => $langs->transnoentitiesnoconv("NoRecordFound")]];
}
// Append Total Row
$list[] = [
$langs->transnoentitiesnoconv("Ref") => $langs->transnoentitiesnoconv("Total"),
$langs->transnoentitiesnoconv("Date") => "",
$langs->transnoentitiesnoconv("Customer") => "",
$langs->transnoentitiesnoconv("Amount") => price($totalSum),
$langs->transnoentitiesnoconv("Status") => ""
];
// Append Total Row (shape depends on detailed-vs-grouped path)
if ($socid) {
$list[] = [
$langs->transnoentitiesnoconv("Ref") => $langs->transnoentitiesnoconv("Total"),
$langs->transnoentitiesnoconv("Date") => "",
$langs->transnoentitiesnoconv("Customer") => "",
$langs->transnoentitiesnoconv("Amount") => price($totalSum),
$langs->transnoentitiesnoconv("Status") => ""
];
} else {
$list[] = [
$langs->transnoentitiesnoconv("Total") => $langs->transnoentitiesnoconv("Total"),
$langs->transnoentitiesnoconv("Amount") => price($totalSum)
];
}
return $list;
}

View file

@ -72,8 +72,9 @@ class ToolThirdParty extends McpTool
"inputSchema" => [
"type" => "object",
"properties" => [
"id" => ["type" => "integer", "required" => true]
]
"id" => ["type" => "integer", "description" => "The unique numerical ID of the thirdparty."]
],
"required" => ["id"]
]
],
[
@ -82,7 +83,7 @@ class ToolThirdParty extends McpTool
"inputSchema" => [
"type" => "object",
"properties" => [
"name" => ["type" => "string", "description" => "Name of the thirdparty", "required" => true],
"name" => ["type" => "string", "description" => "Name of the thirdparty"],
"type" => ["type" => "string", "enum" => ["customer", "prospect", "supplier", "both", "none"], "default" => "customer", "description" => "Type of thirdparty. 'none' means not a customer or prospect."],
"email" => ["type" => "string", "description" => "Email address"],
"phone" => ["type" => "string", "description" => "Phone number"],
@ -105,7 +106,7 @@ class ToolThirdParty extends McpTool
"inputSchema" => [
"type" => "object",
"properties" => [
"id" => ["type" => "integer", "description" => "The ID of the thirdparty to update.", "required" => true],
"id" => ["type" => "integer", "description" => "The ID of the thirdparty to update."],
"name" => ["type" => "string", "description" => "The new name for the thirdparty."],
"email" => ["type" => "string", "description" => "The new email address."],
"phone" => ["type" => "string", "description" => "The new phone number."],
@ -123,7 +124,7 @@ class ToolThirdParty extends McpTool
"inputSchema" => [
"type" => "object",
"properties" => [
"id" => ["type" => "integer", "description" => "The ID of the thirdparty.", "required" => true]
"id" => ["type" => "integer", "description" => "The ID of the thirdparty."]
],
"required" => ["id"]
]
@ -136,11 +137,10 @@ class ToolThirdParty extends McpTool
"properties" => [
"thirdparty_identifier" => [
"type" => ["string", "integer"],
"description" => "The ID or name of the thirdparty to add the contact to.",
"required" => true
"description" => "The ID or name of the thirdparty to add the contact to."
],
"firstname" => ["type" => "string", "description" => "Contact's first name.", "required" => true],
"lastname" => ["type" => "string", "description" => "Contact's last name.", "required" => true],
"firstname" => ["type" => "string", "description" => "Contact's first name."],
"lastname" => ["type" => "string", "description" => "Contact's last name."],
"email" => ["type" => "string", "description" => "Contact's email address."],
"phone" => ["type" => "string", "description" => "Contact's phone number."],
"role" => ["type" => "string", "description" => "Contact's role or position within the company."]

View file

@ -40,7 +40,6 @@ require '../../main.inc.php';
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/asset.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
// Load translation files required by the page
$langs->loadLangs(array("assets", "admin", "companies"));

View file

@ -40,7 +40,6 @@ require '../../main.inc.php';
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/asset.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
// Load translation files required by the page
$langs->loadLangs(array("assets", "admin", "companies"));

View file

@ -37,7 +37,6 @@ require '../../main.inc.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once '../lib/bookcal.lib.php';
// Load translation files required by the page

View file

@ -38,7 +38,6 @@ require '../../main.inc.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once '../lib/bookcal.lib.php';
// Load translation files required by the page

View file

@ -36,11 +36,11 @@ require '../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
@ -131,7 +131,6 @@ $donotclearsession = GETPOST('donotclearsession') ? GETPOST('donotclearsession')
$object = new ActionComm($db);
$cactioncomm = new CActionComm($db);
$contact = new Contact($db);
$extrafields = new ExtraFields($db);
$formfile = new FormFile($db);
$form = new Form($db);
@ -420,11 +419,22 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
$elProp = getElementProperties(GETPOST("elementtype", 'alpha'));
$modulecodetouseforpermissioncheck = $elProp['module'];
// Keep permission check aligned with rights class aliases (see restrictedArea()).
if ($modulecodetouseforpermissioncheck == 'productbatch') {
$modulecodetouseforpermissioncheck = 'produit';
}
$submodulecodetouseforpermissioncheck = $elProp['subelement'];
switch ($modulecodetouseforpermissioncheck) {
case 'productbatch':
$modulecodetouseforpermissioncheck = 'produit';
break;
case 'eventorganization':
// Event organization relies on Project permissions
$modulecodetouseforpermissioncheck = 'projet';
$submodulecodetouseforpermissioncheck = ''; // Project doesn't use submodules for read
break;
default:
// No mapping needed, keep original values
break;
}
$hasPermissionOnLinkedObject = 0;
if ($user->hasRight($modulecodetouseforpermissioncheck, 'read')) {
$hasPermissionOnLinkedObject = 1;
@ -1001,8 +1011,20 @@ if (empty($reshook) && $action == 'update' && $usercancreate) {
$elProp = getElementProperties(GETPOST("elementtype", 'alpha'));
$modulecodetouseforpermissioncheck = $elProp['module'];
// Keep permission check aligned with rights class aliases (see restrictedArea()).
if ($modulecodetouseforpermissioncheck == 'productbatch') {
$modulecodetouseforpermissioncheck = 'produit';
$submodulecodetouseforpermissioncheck = $elProp['subelement'];
switch ($modulecodetouseforpermissioncheck) {
case 'productbatch':
$modulecodetouseforpermissioncheck = 'produit';
break;
case 'eventorganization':
// Event organization relies on Project permissions
$modulecodetouseforpermissioncheck = 'projet';
$submodulecodetouseforpermissioncheck = ''; // Project doesn't use submodules for read
break;
default:
// No mapping needed, keep original values
break;
}
$hasPermissionOnLinkedObject = 0;
@ -1869,9 +1891,22 @@ if ($action == 'create') {
$elProp = getElementProperties($origin);
$modulecodetouseforpermissioncheck = $elProp['module'];
// Keep permission check aligned with rights class aliases (see restrictedArea()).
if ($modulecodetouseforpermissioncheck == 'productbatch') {
$modulecodetouseforpermissioncheck = 'produit';
$submodulecodetouseforpermissioncheck = $elProp['subelement'];
switch ($modulecodetouseforpermissioncheck) {
case 'productbatch':
$modulecodetouseforpermissioncheck = 'produit';
break;
case 'eventorganization':
// Event organization relies on Project permissions
$modulecodetouseforpermissioncheck = 'projet';
$submodulecodetouseforpermissioncheck = ''; // Project doesn't use submodules for read
break;
default:
// No mapping needed, keep original values
break;
}
if ($user->hasRight($modulecodetouseforpermissioncheck, 'read') || $user->hasRight($modulecodetouseforpermissioncheck, $elProp['element'], 'read')) {
$hasPermissionOnLinkedObject = 1;
}

View file

@ -4,7 +4,7 @@
* Copyright (C) 2004-2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2017 Open-DSI <support@open-dsi.fr>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
@ -34,6 +34,7 @@ require '../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
@ -42,9 +43,8 @@ require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
// Load translation files required by the page
$langs->loadLangs(array("users", "companies", "agenda", "commercial", "other", "orders", "bills"));
@ -123,8 +123,6 @@ $search_categ_cus = GETPOST("search_categ_cus", "intcomma", 3) ? GETPOST("search
$object = new ActionComm($db);
$hookmanager->initHooks(array('agendalist'));
$extrafields = new ExtraFields($db);
// fetch optionals attributes and labels
$extrafields->fetch_name_optionals_label($object->table_element);

View file

@ -3,7 +3,7 @@
* Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
* Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,21 +27,19 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/propal.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/propal.lib.php';
// Load translation files required by the page
$langs->loadLangs(array('companies', 'admin', 'propal'));
$extrafields = new ExtraFields($db);
$form = new Form($db);
// List of supported format
@ -63,7 +61,6 @@ if (!$user->admin) {
require DOL_DOCUMENT_ROOT.'/core/actions_extrafields.inc.php';
/*
* View
*/

View file

@ -6,7 +6,7 @@
* Copyright (C) 2012 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2013 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -30,21 +30,19 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/propal.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/propal.lib.php';
// Load translation files required by the page
$langs->loadlangs(array('admin', 'other', 'propal'));
$extrafields = new ExtraFields($db);
$form = new Form($db);
// List of supported format

View file

@ -7,7 +7,7 @@
* Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
* Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2021-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2021-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
* Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
@ -38,6 +38,7 @@ require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
@ -128,7 +129,6 @@ if (!$sortfield) {
$cancel = GETPOST('cancel', 'alpha');
$object = new Client($db);
$extrafields = new ExtraFields($db);
$formfile = new FormFile($db);
// fetch optionals attributes and labels

View file

@ -2,7 +2,7 @@
/* Copyright (C) 2014 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,6 +27,14 @@
// Load Dolibarr environment
require '../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmailing.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
@ -39,14 +47,6 @@ require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
// Load translation files required by the page
$langs->loadLangs(array('mails', 'admin', 'companies', 'categories'));
@ -895,8 +895,6 @@ if ($object->fetch($id) >= 0) {
$socstatic = new Societe($db);
$elementtype = $socstatic->table_element;
// fetch optionals attributes and labels
require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
$extrafields = new ExtraFields($db);
$extrafields->fetch_name_optionals_label($elementtype);
foreach ($extrafields->attributes[$elementtype]['label'] as $key => $val) {
if ($key != 'ts_nameextra' && $key != 'ts_payeur') {
@ -1086,8 +1084,6 @@ if ($object->fetch($id) >= 0) {
$contactstatic = new Contact($db);
$elementype = $contactstatic->table_element;
// fetch optionals attributes and labels
dol_include_once('/core/class/extrafields.class.php');
$extrafields = new ExtraFields($db);
$extrafields->fetch_name_optionals_label($elementype);
if (!empty($extrafields->attributes[$elementtype]['type'])) {
foreach ($extrafields->attributes[$elementtype]['type'] as $key => &$value) {

View file

@ -5,7 +5,7 @@
* Copyright (C) 2021 Waël Almoman <info@almoman.com>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -36,6 +36,7 @@ require '../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
@ -49,7 +50,6 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
require_once DOL_DOCUMENT_ROOT.'/comm/mailing/class/mailing.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
if (isModEnabled('project')) {
require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
@ -70,7 +70,6 @@ $backtopageforcancel = GETPOST('backtopageforcancel');
// Initialize a technical objects
$object = new Mailing($db);
$extrafields = new ExtraFields($db);
$hookmanager->initHooks(array('mailingcard', 'globalcard'));
// Fetch optionals attributes and labels

View file

@ -3,8 +3,8 @@
* Copyright (C) 2005-2010 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2025 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2025 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
* Copyright (C) 2025 Josep Lluís Amador Teruel <joseplluis@lliuretic.cat>
*
* This program is free software; you can redistribute it and/or modify
@ -32,6 +32,7 @@ require '../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
@ -82,7 +83,6 @@ $search_project = GETPOST('search_project', 'alpha');
// Initialize a technical objects
$object = new Mailing($db);
$extrafields = new ExtraFields($db);
if ($projectid > 0) {
$hookmanager->initHooks(array('projectmailing', 'globalcard'));
} else {

View file

@ -1,6 +1,6 @@
<?php
/* Copyright (C) 2007-2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,17 +24,16 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/comm/mailing/class/mailing.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/emailing.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/comm/mailing/class/mailing.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/emailing.lib.php';
// Load translation files required by the page
$langs->loadLangs(array("mails", "mailing", "companies"));
@ -48,7 +47,6 @@ $backtopage = GETPOST('backtopage', 'alpha');
// Initialize a technical objects
$object = new Mailing($db);
$extrafields = new ExtraFields($db);
$diroutputmassaction = $conf->mailing->dir_output.'/temp/massgeneration/'.$user->id;
$hookmanager->initHooks(array('mailingnote', 'globalcard')); // Note that conf->hooks_modules contains array
// Fetch optionals attributes and labels

View file

@ -45,6 +45,15 @@
// Load Dolibarr environment
require '../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formother.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formfile.class.php';
@ -66,14 +75,6 @@ if (isModEnabled('variants')) {
require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination.class.php';
}
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
// Load translation files required by the page
$langs->loadLangs(array('companies', 'propal', 'compta', 'bills', 'orders', 'products', 'sendings', 'other'));
@ -109,7 +110,6 @@ $hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalStri
$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
$object = new Propal($db);
$extrafields = new ExtraFields($db);
// fetch optionals attributes and labels
$extrafields->fetch_name_optionals_label($object->table_element);

View file

@ -3,7 +3,7 @@
* Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2019 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2019-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2019-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -28,12 +28,6 @@
// Load Dolibarr environment
require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/client.class.php';
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/order.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
@ -41,6 +35,12 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/order.lib.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/client.class.php';
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/order.lib.php';
// Load translation files required by the page
$langs->loadLangs(array('orders', 'bills'));
@ -50,8 +50,6 @@ if (!$user->hasRight('commande', 'lire')) {
accessforbidden();
}
$hookmanager = new HookManager($db);
// Initialize a technical object to manage hooks. Note that conf->hooks_modules contains array
$hookmanager->initHooks(array('ordersindex'));

View file

@ -1,7 +1,8 @@
<?php
/* Copyright (C) 2017-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
/* Copyright (C) 2017-2021 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -272,7 +273,13 @@ class PaymentVarious extends CommonObject
return -1;
}
if (!$notrigger) {
// Actions on extra fields
$result = $this->insertExtraFields();
if ($result < 0) {
$error++;
}
if (!$error && !$notrigger) {
// Call trigger
$result = $this->call_trigger('PAYMENT_VARIOUS_MODIFY', $user);
if ($result < 0) {
@ -354,6 +361,10 @@ class PaymentVarious extends CommonObject
$this->rappro = $obj->rappro;
$this->bank_num_releve = $obj->bank_num_releve;
}
// Fetch extrafields
$this->fetch_optionals();
$this->db->free($resql);
return 1;
@ -533,6 +544,12 @@ class PaymentVarious extends CommonObject
$this->ref = (string) $this->id;
if ($this->id > 0) {
// Insert extrafields
$result = $this->insertExtraFields();
if ($result < 0) {
$error++;
}
if (isModEnabled("bank") && !empty($this->amount)) {
// Insert into llx_bank
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';

View file

@ -7,7 +7,7 @@
* Copyright (C) 2015-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
*
@ -36,6 +36,7 @@ require '../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
@ -90,7 +91,6 @@ if (!$user->hasRight('banque', 'lire') && !$user->hasRight('banque', 'consolidat
}
$object = new AccountLine($db);
$extrafields = new ExtraFields($db);
$extrafields->fetch_name_optionals_label($object->element);
if ($id > 0) {

View file

@ -1,9 +1,10 @@
<?php
/* Copyright (C) 2017-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
/* Copyright (C) 2017-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,18 +28,6 @@
// Load Dolibarr environment
require '../../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php';
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
if (isModEnabled('project')) {
require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
}
/**
* @var Conf $conf
@ -48,6 +37,20 @@ if (isModEnabled('project')) {
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php';
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
if (isModEnabled('project')) {
require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
}
// Load translation files required by the page
$langs->loadLangs(array("accountancy", "banks", "bills", "categories", "compta", "users"));
@ -84,6 +87,9 @@ $result = restrictedArea($user, 'banque', '', '', '');
$object = new PaymentVarious($db);
$extrafields = new ExtraFields($db);
$extrafields->fetch_name_optionals_label($object->table_element);
$permissiontoadd = $user->hasRight('banque', 'modifier');
$permissiontodelete = $user->hasRight('banque', 'modifier');
@ -148,6 +154,12 @@ if (empty($reshook)) {
$object->sens = GETPOSTINT('sens');
$object->fk_project = GETPOSTINT('fk_project');
// Fill array 'array_options' with data from add form
$ret = $extrafields->setOptionalsFromPost(null, $object);
if ($ret < 0) {
$error++;
}
if (!checkGeneralAccountAllowsAuxiliary($db, $object->accountancy_code, $object->subledger_account)) {
setEventMessages($langs->trans("ErrorAccountNotCentralized"). ". " . $langs->trans("RemoveSubsidiaryAccountOrAdjustTheGeneralAccount"), null, 'errors');
$error++;
@ -538,6 +550,8 @@ if ($action == 'create') {
}
// Other attributes
print $object->showOptionals($extrafields, 'create');
$parameters = array();
$reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;

View file

@ -1,9 +1,10 @@
<?php
/* Copyright (C) 2017-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
/* Copyright (C) 2017-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,6 +28,15 @@
// Load Dolibarr environment
require '../../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php';
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
@ -37,14 +47,6 @@ if (isModEnabled('project')) {
require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
}
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
// Load translation files required by the page
$langs->loadLangs(array("compta", "banks", "bills", "accountancy"));
@ -183,6 +185,9 @@ $arrayfields = array(
'credit' => array('label' => "Credit", 'checked' => '1', 'position' => 510),
);
// Extra fields
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_array_fields.tpl.php';
$arrayfields = dol_sort_array($arrayfields, 'position');
// Security check
@ -266,9 +271,19 @@ $sql = "SELECT v.rowid, v.sens, v.amount, v.label, v.datep as datep, v.datev as
$sql .= " ba.rowid as bid, ba.ref as bref, ba.number as bnumber, ba.account_number as bank_account_number, ba.fk_accountancy_journal as accountancy_journal, ba.label as blabel,";
$sql .= " pst.code as payment_code";
// Add fields from extrafields
if (!empty($extrafields->attributes[$object->table_element]['label'])) {
foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
$sql .= ($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? ", ef.".$key." as options_".$key : '');
}
}
$sqlfields = $sql; // $sql fields to remove for count total
$sql .= " FROM ".MAIN_DB_PREFIX."payment_various as v";
if (!empty($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) {
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (v.rowid = ef.fk_object)";
}
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pst ON v.fk_typepayment = pst.id";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON v.fk_bank = b.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON b.fk_account = ba.rowid";
@ -600,6 +615,9 @@ if ($arrayfields['credit']['checked']) {
print '</td>';
}
// Extra fields
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
if (!$conf->main_checkbox_left_column) {
print '<td class="liste_titre center maxwidthsearch">';
$searchpicto = $form->showFilterButtons();
@ -893,6 +911,9 @@ while ($i < $imaxinloop) {
print '</td>';
}
// Extra fields
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php';
if (!$conf->main_checkbox_left_column) {
print '<td></td>';
if (!$i) {

View file

@ -1,6 +1,6 @@
<?php
/* Copyright (C) 2007-2024 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2025-2026 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
@ -28,6 +28,7 @@ require '../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
@ -73,7 +74,6 @@ $pagenext = $page + 1;
// Initialize a technical objects
$object = new CashControl($db);
$extrafields = new ExtraFields($db);
//$diroutputmassaction = $conf->mymodule->dir_output.'/temp/massgeneration/'.$user->id;
$hookmanager->initHooks(array($contextpage)); // Note that conf->hooks_modules contains array

View file

@ -52,7 +52,6 @@ if (isModEnabled('project')) {
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/invoice.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
// Load translation files required by the page
$langs->loadLangs(array('bills', 'companies', 'compta', 'admin', 'other', 'products', 'banks'));

View file

@ -8,7 +8,7 @@
* Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2014 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,6 +35,7 @@ require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
@ -46,7 +47,6 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
@ -61,8 +61,8 @@ $error = 0;
$errors = array();
// Get parameters
$action = (GETPOST('action', 'alpha') ? GETPOST('action', 'alpha') : 'view');
$confirm = GETPOST('confirm', 'alpha');
$action = (GETPOST('action', 'alpha') ? GETPOST('action', 'alpha') : 'view');
$confirm = GETPOST('confirm', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
$contextpage = GETPOST('contextpage', 'aZ09');
@ -71,7 +71,6 @@ $socid = GETPOSTINT('socid');
// Initialize objects
$object = new Contact($db);
$extrafields = new ExtraFields($db);
// fetch optionals attributes and labels
$extrafields->fetch_name_optionals_label($object->table_element);

View file

@ -8,7 +8,7 @@
* Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2014 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2019 Josep Lluís Amador <joseplluis@lliuretic.cat>
* Copyright (C) 2020-2026 Open-Dsi <support@open-dsi.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
@ -36,6 +36,15 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/contact.lib.php';
@ -44,21 +53,11 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
// Load translation files required by the page
$langs->loadLangs(array('companies', 'users', 'other', 'commercial'));
@ -75,7 +74,6 @@ $socid = GETPOSTINT('socid');
// Initialize a technical object
$object = new Contact($db);
$extrafields = new ExtraFields($db);
// fetch optionals attributes and labels
$extrafields->fetch_name_optionals_label($object->table_element);

View file

@ -9,11 +9,11 @@
* Copyright (C) 2014-2020 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023 Charlene Benke <charlene@patas-monkey.com>
* Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023-2026 Charlene Benke <charlene@patas-monkey.com>
* Copyright (C) 2023 Nick Fragoulis
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2024-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2025 William Mead <william@m34d.com>
*
* This program is free software; you can redistribute it and/or modify
@ -1138,6 +1138,7 @@ llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-contrat page-card');
$form = new Form($db);
$formfile = new FormFile($db);
$staticcontractline = new ContratLigne($db);
if (isModEnabled('project')) {
$formproject = new FormProjets($db);
}
@ -1525,10 +1526,10 @@ if ($action == 'create') {
print '<div class="fichecenter">';
print '<div class="fichehalfleft">';
print '<div class="underbanner clearboth"></div>';
print '<table class="border tableforfield" width="100%">';
print '<table class="border centpercent tableforfield">';
// Line info of thirdparty discounts
print '<tr><td class="titlefield">'.$langs->trans('Discount').'</td><td colspan="3">';
@ -1560,6 +1561,86 @@ if ($action == 'create') {
print "</table>";
print '</div>';
print '<div class="fichehalfright">';
print '<!-- amounts -->'."\n";
print '<div class="underbanner clearboth"></div>';
print '<table class="border tableforfield centpercent">';
// Qty by service status
print '<tr><td class="titlefield">'."".'</td>';
print '<td class=right>'.$langs->trans('Total').'</td>';
print '<td class=right>'.$staticcontractline->LibStatut(0, 5, 0).'</td>';
print '<td class=right>'.$staticcontractline->LibStatut(4, 5, 0).'</td>';
print '<td class=right>'.$staticcontractline->LibStatut(4, 5, 1).'</td>';
print '<td class=right>'.$staticcontractline->LibStatut(5, 5, 0).'</td>';
print '</tr>';
$all= $object->getTotalizedLines(-1, 0);
$draft= $object->getTotalizedLines(0, 0);
$enabled= $object->getTotalizedLines(4, 0);
$expired= $object->getTotalizedLines(4, 1);
$close= $object->getTotalizedLines(5, 0);
print '<tr><td class="titlefield">'.$langs->trans("Qty").'</td>';
print '<td class="right nowrap">'.price2num($all['total_qty']).'</td>';
print '<td class="right">'.price2num($draft['total_qty']).'</td>';
print '<td class="right">'.price2num($enabled['total_qty']).'</td>';
print '<td class="right">'.price2num($expired['total_qty']).'</td>';
print '<td class="right">'.price2num($close['total_qty']).'</td>';
print '</tr>';
print '<tr><td class="titlefield">'.$langs->trans("TotalHT").'</td>';
print '<td class="nowraponall amountcard right">'.price($all['total_ht']).'</td>';
print '<td class="nowraponall amountcard right">'.price($draft['total_ht']).'</td>';
print '<td class="nowraponall amountcard right">'.price($enabled['total_ht']).'</td>';
print '<td class="nowraponall amountcard right">'.price($expired['total_ht']).'</td>';
print '<td class="nowraponall amountcard right">'.price($close['total_ht']).'</td>';
print '</tr>';
print '<tr><td class="titlefield">'.$langs->trans("TotalVAT").'</td>';
print '<td class="nowraponall amountcard right nowrap">'.price($all['total_tva']).'</td>';
print '<td class="nowraponall amountcard right">'.price($draft['total_tva']).'</td>';
print '<td class="nowraponall amountcard right">'.price($enabled['total_tva']).'</td>';
print '<td class="nowraponall amountcard right">'.price($expired['total_tva']).'</td>';
print '<td class="nowraponall amountcard right">'.price($close['total_tva']).'</td>';
print '</tr>';
if ($mysoc->localtax1_assuj == "1" || $all['total_localtax1'] != 0) {
print '<tr><td class="titlefield">' . $langs->trans("TotalLT1") . '</td>';
print '<td class="nowraponall amountcard right nowrap">' . price($all['total_localtax1']) . '</td>';
print '<td class="nowraponall amountcard right">' . price($draft['total_localtax1']) . '</td>';
print '<td class="nowraponall amountcard right">' . price($enabled['total_localtax1']) . '</td>';
print '<td class="nowraponall amountcard right">' . price($expired['total_localtax1']) . '</td>';
print '<td class="nowraponall amountcard right">' . price($close['total_localtax1']) . '</td>';
print '</tr>';
}
if ($mysoc->localtax2_assuj == "1" || $all['total_localtax2'] != 0) {
print '<tr><td class="titlefield">' . $langs->trans("TotalLT2") . '</td>';
print '<td class="nowraponall amountcard right nowrap">' . price($all['total_localtax2']) . '</td>';
print '<td class="nowraponall amountcard right">' . price($draft['total_localtax2']) . '</td>';
print '<td class="nowraponall amountcard right">' . price($enabled['total_localtax2']) . '</td>';
print '<td class="nowraponall amountcard right">' . price($expired['total_localtax2']) . '</td>';
print '<td class="nowraponall amountcard right">' . price($close['total_localtax2']) . '</td>';
print '</tr>';
}
print '<tr><td class="titlefield">'.$langs->trans("TotalTTC").'</td>';
print '<td class="nowraponall amountcard right nowrap">'.price($all['total_ttc']).'</td>';
print '<td class="nowraponall amountcard right">'.price($draft['total_ttc']).'</td>';
print '<td class="nowraponall amountcard right">'.price($enabled['total_ttc']).'</td>';
print '<td class="nowraponall amountcard right">'.price($expired['total_ttc']).'</td>';
print '<td class="nowraponall amountcard right">'.price($close['total_ttc']).'</td>';
print '</tr>';
print "</table>";
print '</div>';
print '</div>';
print '<div class="clearboth"></div><br>';
if ($object->status == $object::STATUS_DRAFT && $user->hasRight('contrat', 'creer')) {
print '</form>';

View file

@ -1,18 +1,20 @@
<?php
/* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2012 Destailleur Laurent <eldy@users.sourceforge.net>
* Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
* Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
* Copyright (C) 2010-2016 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2013 Christophe Battarel <christophe.battarel@altairis.fr>
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
/* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2012 Destailleur Laurent <eldy@users.sourceforge.net>
* Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
* Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
* Copyright (C) 2010-2016 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2013 Christophe Battarel <christophe.battarel@altairis.fr>
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2015-2018 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2015-2018 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2026 Charlene Benke <charlene@patas-monkey.com>
* Copyright (C) 2026 Alexandre Spangaro <alexandre@inovea-conseil.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -2951,6 +2953,45 @@ class Contrat extends CommonObject
return $return;
}
/**
* Return totalized lines
*
* @param int $statut Status of lines
* @param int $expired 1=expired, 0=not expired
* @return array<string,mixed>|int Array of totalized lines or int if error
*/
public function getTotalizedLines($statut, int $expired)
{
$sql = "SELECT SUM(cd.qty) as total_qty, SUM(cd.total_ht) as total_ht, SUM(cd.total_tva) as total_tva, SUM(cd.total_ttc) as total_ttc,";
$sql .= " SUM(cd.total_localtax1) as total_localtax1, SUM(cd.total_localtax2) as total_localtax2";
$sql .= " FROM ".MAIN_DB_PREFIX."contratdet as cd";
$sql .= " WHERE cd.fk_contrat =".((int) $this->id);
if ($statut >= 0) {
$sql .= " AND cd.statut = ".((int) $statut);
if ($expired > 0) {
$sql .= " AND cd.date_fin_validite < '".$this->db->idate(dol_now())."'";
} else {
$sql .= " AND cd.date_fin_validite >= '".$this->db->idate(dol_now())."'";
}
}
$ret = $this->db->query($sql);
$response = array();
if ($ret) {
$obj = $this->db->fetch_object($ret);
$response['total_qty'] = $obj->total_qty;
$response['total_ht'] = $obj->total_ht;
$response['total_tva'] = $obj->total_tva;
$response['total_localtax1'] = $obj->total_localtax1;
$response['total_localtax2'] = $obj->total_localtax2;
$response['total_ttc'] = $obj->total_ttc;
} else {
dol_print_error($this->db);
$this->error = $this->db->lasterror();
return -1;
}
return $response;
}
// @Todo getLibSignedStatus, LibSignedStatus
/**

View file

@ -1123,7 +1123,7 @@ class Form
/**
* Return a select list of country phone calling codes
*
* @param string $selected Pre-selected phone code value (e.g. "+33")
* @param string $selected Preselected phone code value (e.g. "+33")
* @param string $htmlname Name of HTML select element
* @param string $morecss More CSS classes
* @param int $showempty Show empty option (1) or not (0)

View file

@ -30,7 +30,7 @@
* Note:
* LDAP_ESCAPE_FILTER is to escape char array('\\', '*', '(', ')', "\x00")
* LDAP_ESCAPE_DN is to escape char array('\\', ',', '=', '+', '<', '>', ';', '"', '#')
* @phan-file-suppress PhanTypeMismatchArgumentInternal (notifications concern 'resource)
* @phan-file-suppress PhanTypeMismatchArgumentInternal (notifications concern 'resource')
*/
/**
@ -242,7 +242,8 @@ class Ldap
public $ldapcharset = 'UTF-8';
/**
* @var bool|resource The internal LDAP connection handle. Was a resource before PHP 8.1 and is an object of class LDAP\Connection since PHP 8.1
* @var false|resource The internal LDAP connection handle. Was a resource before PHP 8.1 and is an object of class LDAP\Connection since PHP 8.1
* @phpstan-var LDAP\Connection
*/
public $connection;
@ -346,7 +347,7 @@ class Ldap
if (empty($this->error)) {
// Loop on each ldap server
foreach ($this->server as $host) {
if ($connected) {
if ($connected) { // @phpstan-ignore if.alwaysFalse
break;
}
if (empty($host)) {
@ -384,7 +385,7 @@ class Ldap
}
}
if (is_resource($this->connection) || is_object($this->connection)) {
if ($this->connection !== false) {
if ($ldapdebug) {
dol_syslog(get_class($this)."::connectBind this->connection is ok", LOG_DEBUG);
}
@ -436,7 +437,7 @@ class Ldap
}
}
// Try in anonymous
if (!$this->bind) {
if (!$this->bind) { // @phpstan-ignore booleanNot.alwaysTrue
dol_syslog(get_class($this)."::connectBind try bind anonymously on ".$host, LOG_DEBUG);
$result = $this->bind();
if ($result) {
@ -451,7 +452,7 @@ class Ldap
}
}
if (!$connected) {
if (!$connected) { // @phpstan-ignore booleanNot.alwaysTrue
$this->unbind();
}
} // End loop on each server
@ -540,7 +541,7 @@ class Ldap
}
}
} else {
if (is_resource($this->connection)) {
if ($this->connection !== false) {
// @phan-suppress-next-line PhanTypeMismatchArgumentInternalReal PhanTypeSuspiciousIndirectVariable
$this->result = @ldap_unbind($this->connection);
}

View file

@ -62,7 +62,9 @@ class DoliDBPgsql extends DoliDB
public $standard_conforming_strings = false;
/** @var resource|boolean Resultset of last query */
/**
* @var false|resource|PgSql\Result Resultset of last query
*/
private $_results;
@ -624,7 +626,8 @@ class DoliDBPgsql extends DoliDB
/**
* Return datas as an array
*
* @param resource $resultset Resultset of request
* @param bool|resource $resultset Resultset of request
* @phpstan-param bool|resource|PgSql\Result $resultset
* @return array<int,mixed>|null|int<0,0> Array or null if KO or end of cursor or 0 if resultset is bool
*/
public function fetch_row($resultset)
@ -634,6 +637,9 @@ class DoliDBPgsql extends DoliDB
if (!is_resource($resultset) && !is_object($resultset)) {
$resultset = $this->_results;
}
if (is_bool($resultset)) {
return 0;
}
return pg_fetch_row($resultset); // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal
}

View file

@ -1,12 +1,13 @@
<?php
/* Copyright (C) 2006-2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2015 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2016 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2019 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2021 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
/* Copyright (C) 2006-2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2015 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2016 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2019 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2021 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -207,6 +208,7 @@ function bank_admin_prepare_head($object)
$extrafields->fetch_name_optionals_label('bank_account');
$extrafields->fetch_name_optionals_label('bank');
$extrafields->fetch_name_optionals_label('paiement');
$extrafields->fetch_name_optionals_label('payment_various');
$h = 0;
$head = array();
@ -255,6 +257,15 @@ function bank_admin_prepare_head($object)
$head[$h][2] = 'bank_payments_extrafields';
$h++;
$head[$h][0] = dolBuildUrl(DOL_URL_ROOT.'/admin/bank_various_payment_extrafields.php');
$head[$h][1] = $langs->trans("ExtraFields").' ('.$langs->trans("VariousPayments").')';
$nbExtrafields = $extrafields->attributes['payment_various']['count'];
if ($nbExtrafields > 0) {
$head[$h][1] .= '<span class="badge marginleftonlyshort">'.$nbExtrafields.'</span>';
}
$head[$h][2] = 'bank_various_payment_extrafields';
$h++;
complete_head_from_modules($conf, $langs, $object, $head, $h, 'bank_admin', 'remove');
return $head;

View file

@ -546,13 +546,20 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
$feature2 = 'evaluation';
}
// @todo check : project_task
// @todo possible ?
// elseif (substr($features, -3, 3) == 'det') {
// $features = substr($features, 0, -3);
// } elseif (substr($features, -4, 4) == '_det' || substr($features, -4, 4) == 'line') {
// $features = substr($features, 0, -4);
// }
// When the object is a task (element='project_task') and $feature2 is empty,
// $checkUserAccessToObject() falls into the $checkproject path and uses the task ID
// as project ID, which always fails. Setting $feature2='project_task' triggers the
// normalization at line 974 that redirects to the $checktask path, which correctly
// resolves $task->fk_project before calling getProjectsAuthorizedForUser().
if (is_object($object) && in_array($object->element, array('project_task', 'task'))
&& (empty($features) || in_array($features, array('projet', 'project')))
&& empty($feature2)) {
$features = 'projet';
$feature2 = 'project_task';
if (empty($tableandshare)) {
$tableandshare = 'projet_task';
}
}
// print $features.' - '.$tableandshare.' - '.$feature2.' - '.$dbt_select."\n";

View file

@ -1,9 +1,10 @@
<?php
/* Copyright (C) 2003-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2008 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
* Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
* Copyright (C) 2008-2011 Regis Houssin <regis.houssin@inodbox.com>
/* Copyright (C) 2003-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2008 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
* Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
* Copyright (C) 2008-2011 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -230,10 +231,16 @@ class modBanque extends DolibarrModules
'v.note'=>"payment", 'v.datec'=>"payment",
"p.ref"=>"project", "p.title"=>"project"
);
// Add extra fields
$keyforselect = 'payment_various';
$keyforelement = 'payment';
$keyforaliasextra = 'extra';
include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php';
$this->export_sql_start[$r] = 'SELECT ';
$this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'payment_various as v';
$this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX."projet as p ON v.fk_projet = p.rowid";
$this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX."c_paiement as cp ON v.fk_typepayment = cp.id";
$this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'payment_various_extrafields as extra ON v.rowid = extra.fk_object';
$this->export_sql_end[$r] .= ' WHERE v.entity IN ('.getEntity('payment_various').')';
$this->export_sql_order[$r] = ' ORDER BY v.datep';
}

View file

@ -33,6 +33,10 @@
include_once DOL_DOCUMENT_ROOT."/core/modules/DolibarrModules.class.php";
require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php';
if (!defined('SUBTOTALS_SPECIAL_CODE')) {
define('SUBTOTALS_SPECIAL_CODE', 81);
}
/**
* Description and activation class for module subtotals
*/

View file

@ -1304,7 +1304,8 @@ if (!empty($object->thirdparty)) {
}
// Deal with supplier ref price (idprodfournprice = int)
if (jQuery('#idprodfournprice').val() > 0)
var supplierVal = jQuery('#idprodfournprice').val();
if (supplierVal && supplierVal !== '-1' && (supplierVal > 0 || supplierVal.indexOf('idprod_') === 0))
{
console.log("objectline_create.tpl #idprodfournprice is an ID > 0, so we set some properties into page");

View file

@ -8,7 +8,7 @@
* Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2023-2024 William Mead <william.mead@manchenumerique.fr>
* Copyright (C) 2023 Christian Foellmann <christian@foellmann.de>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
@ -209,13 +209,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ContractValidatedInDolibarr", (string) ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("ContractValidatedInDolibarr", (string) ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ContractValidatedInDolibarr", (string) ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("ContractValidatedInDolibarr", (string) ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -244,13 +244,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("PropalValidatedInDolibarr", (string) ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("PropalValidatedInDolibarr", (string) ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("PropalValidatedInDolibarr", (string) ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("PropalValidatedInDolibarr", (string) ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -261,13 +261,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("PropalBackToDraftInDolibarr", (string) ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("PropalBackToDraftInDolibarr", (string) ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("PropalBackToDraftInDolibarr", (string) ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("PropalBackToDraftInDolibarr", (string) ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -347,13 +347,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("OrderValidatedInDolibarr", (string) ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("OrderValidatedInDolibarr", (string) ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("OrderValidatedInDolibarr", (string) ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("OrderValidatedInDolibarr", (string) ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -433,13 +433,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) { // Label
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoiceValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("InvoiceValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) { // Description
$object->actionmsg = $langs->transnoentities("InvoiceValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("InvoiceValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -450,13 +450,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoiceBackToDraftInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InvoiceBackToDraftInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoiceBackToDraftInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InvoiceBackToDraftInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -467,13 +467,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoiceSentByEMail", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InvoiceSentByEMail", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoiceSentByEMail", $object->ref);
$object->actionmsg = $langs->transnoentities("InvoiceSentByEMail", (string) $object->ref);
}
// Parameters $object->sendtoid defined by caller
@ -486,13 +486,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
// Values for this action can't be defined by caller.
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoicePaidInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InvoicePaidInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoicePaidInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InvoicePaidInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -503,13 +503,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoiceCanceledInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InvoiceCanceledInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoiceCanceledInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InvoiceCanceledInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -520,13 +520,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InterventionCreatedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InterventionCreatedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InterventionCreatedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InterventionCreatedInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -539,13 +539,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InterventionValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("InterventionValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InterventionValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("InterventionValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -558,13 +558,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InterventionModifiedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InterventionModifiedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InterventionModifiedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InterventionModifiedInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -577,13 +577,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InterventionSentByEMail", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InterventionSentByEMail", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InterventionSentByEMail", $object->ref);
$object->actionmsg = $langs->transnoentities("InterventionSentByEMail", (string) $object->ref);
}
// Parameters $object->sendtoid defined by caller
@ -595,13 +595,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InterventionClassifiedBilledInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InterventionClassifiedBilledInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InterventionClassifiedBilledInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InterventionClassifiedBilledInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -612,13 +612,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InterventionClassifiedUnbilledInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InterventionClassifiedUnbilledInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InterventionClassifiedUnbilledInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InterventionClassifiedUnbilledInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -629,13 +629,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InterventionClosedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InterventionClosedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InterventionClosedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InterventionClosedInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -648,13 +648,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InterventionDeletedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InterventionDeletedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InterventionDeletedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InterventionDeletedInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -667,13 +667,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ShippingValidated", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("ShippingValidated", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ShippingValidated", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("ShippingValidated", ($object->newref ? $object->newref : (string) $object->ref));
}
// Parameters $object->sendtoid defined by caller
@ -685,13 +685,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ShippingSentByEMail", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ShippingSentByEMail", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ShippingSentByEMail", $object->ref);
$object->actionmsg = $langs->transnoentities("ShippingSentByEMail", (string) $object->ref);
}
// Parameters $object->sendtoid defined by caller
@ -704,13 +704,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ReceptionValidated", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("ReceptionValidated", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ReceptionValidated", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("ReceptionValidated", ($object->newref ? $object->newref : (string) $object->ref));
}
// Parameters $object->sendtoid defined by caller
@ -723,13 +723,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ReceptionSentByEMail", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ReceptionSentByEMail", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ReceptionSentByEMail", $object->ref);
$object->actionmsg = $langs->transnoentities("ReceptionSentByEMail", (string) $object->ref);
}
// Parameters $object->sendtoid defined by caller
@ -741,13 +741,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("PropalValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("PropalValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("PropalValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("PropalValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -758,13 +758,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ProposalSentByEMail", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ProposalSentByEMail", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ProposalSentByEMail", $object->ref);
$object->actionmsg = $langs->transnoentities("ProposalSentByEMail", (string) $object->ref);
}
// Parameters $object->sendtoid defined by caller
@ -776,13 +776,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("PropalClosedSignedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("PropalClosedSignedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("PropalClosedSignedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("PropalClosedSignedInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -793,13 +793,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("PropalClosedRefusedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("PropalClosedRefusedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("PropalClosedRefusedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("PropalClosedRefusedInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -810,13 +810,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("OrderCreatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("OrderCreatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("OrderCreatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("OrderCreatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -827,13 +827,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("OrderValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("OrderValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("OrderValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("OrderValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -844,13 +844,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("OrderApprovedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("OrderApprovedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("OrderApprovedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("OrderApprovedInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -861,13 +861,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("OrderRefusedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("OrderRefusedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("OrderRefusedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("OrderRefusedInDolibarr", (string) $object->ref);
}
if (!empty($object->refuse_note)) {
@ -883,13 +883,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("OrderCanceledInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("OrderCanceledInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("OrderCanceledInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("OrderCanceledInDolibarr", (string) $object->ref);
}
if (!empty($object->cancel_note)) {
@ -905,13 +905,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("SupplierOrderSubmitedInDolibarr", ($object->newref ?: $object->ref), $object->getInputMethod());
$object->actionmsg2 = $langs->transnoentities("SupplierOrderSubmitedInDolibarr", ($object->newref ?: (string) $object->ref), $object->getInputMethod());
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("SupplierOrderSubmitedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("SupplierOrderSubmitedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
if (!empty($object->context['comments'])) {
@ -927,13 +927,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("SupplierOrderReceivedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("SupplierOrderReceivedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("SupplierOrderReceivedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("SupplierOrderReceivedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -944,13 +944,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("SupplierOrderSentByEMail", $object->ref);
$object->actionmsg2 = $langs->transnoentities("SupplierOrderSentByEMail", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("SupplierOrderSentByEMail", $object->ref);
$object->actionmsg = $langs->transnoentities("SupplierOrderSentByEMail", (string) $object->ref);
}
// Parameters $object->sendtoid defined by caller
@ -962,13 +962,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("SupplierOrderClassifiedBilled", $object->ref);
$object->actionmsg2 = $langs->transnoentities("SupplierOrderClassifiedBilled", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("SupplierOrderClassifiedBilled", $object->ref);
$object->actionmsg = $langs->transnoentities("SupplierOrderClassifiedBilled", (string) $object->ref);
}
$object->sendtoid = array();
@ -979,13 +979,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoiceCreatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("InvoiceCreatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoiceCreatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("InvoiceCreatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -996,13 +996,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoiceValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg2 = $langs->transnoentities("InvoiceValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoiceValidatedInDolibarr", ($object->newref ? $object->newref : $object->ref));
$object->actionmsg = $langs->transnoentities("InvoiceValidatedInDolibarr", ($object->newref ? $object->newref : (string) $object->ref));
}
$object->sendtoid = array();
@ -1013,13 +1013,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoiceBackToDraftInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InvoiceBackToDraftInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoiceBackToDraftInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InvoiceBackToDraftInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -1030,13 +1030,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("SupplierInvoiceSentByEMail", $object->ref);
$object->actionmsg2 = $langs->transnoentities("SupplierInvoiceSentByEMail", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("SupplierInvoiceSentByEMail", $object->ref);
$object->actionmsg = $langs->transnoentities("SupplierInvoiceSentByEMail", (string) $object->ref);
}
// Parameters $object->sendtoid defined by caller
@ -1048,13 +1048,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoicePaidInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InvoicePaidInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoicePaidInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InvoicePaidInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -1064,13 +1064,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("InvoiceCanceledInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("InvoiceCanceledInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("InvoiceCanceledInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("InvoiceCanceledInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -1191,13 +1191,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("MemberSubscriptionDeletedInDolibarr", $object->ref, $member->getFullName($langs));
$object->actionmsg2 = $langs->transnoentities("MemberSubscriptionDeletedInDolibarr", (string) $object->ref, $member->getFullName($langs));
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("MemberSubscriptionDeletedInDolibarr", $object->ref, $member->getFullName($langs));
$object->actionmsg = $langs->transnoentities("MemberSubscriptionDeletedInDolibarr", (string) $object->ref, $member->getFullName($langs));
$object->actionmsg .= "\n".$langs->transnoentities("Member").': '.$member->getFullName($langs);
$object->actionmsg .= "\n".$langs->transnoentities("Type").': '.$object->fk_type;
$object->actionmsg .= "\n".$langs->transnoentities("Amount").': '.$object->amount;
@ -1273,14 +1273,14 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ProjectCreatedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ProjectCreatedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ProjectCreatedInDolibarr", $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Project").': '.$object->ref;
$object->actionmsg = $langs->transnoentities("ProjectCreatedInDolibarr", (string) $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Project").': '.(string) $object->ref;
}
$object->sendtoid = array();
@ -1291,14 +1291,14 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ProjectValidatedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ProjectValidatedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ProjectValidatedInDolibarr", $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Project").': '.$object->ref;
$object->actionmsg = $langs->transnoentities("ProjectValidatedInDolibarr", (string) $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Project").': '.(string) $object->ref;
}
$object->sendtoid = array();
@ -1309,13 +1309,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ProjectModifiedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ProjectModifiedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ProjectModifiedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("ProjectModifiedInDolibarr", (string) $object->ref);
}
//$object->actionmsg .= "\n".$langs->transnoentities("Task").': ???';
@ -1331,13 +1331,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ProjectSentByEMail", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ProjectSentByEMail", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ProjectSentByEMail", $object->ref);
$object->actionmsg = $langs->transnoentities("ProjectSentByEMail", (string) $object->ref);
}
// Parameters $object->sendtoid defined by caller
@ -1348,9 +1348,9 @@ class InterfaceActionsAuto extends DolibarrTriggers
$langs->loadLangs(array("agenda", "other", "projects"));
if (empty($object->actionmsg2)) {
$object->actionmsg2 = $langs->transnoentities("ProjectDeletedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ProjectDeletedInDolibarr", (string) $object->ref);
}
$object->actionmsg = $langs->transnoentities("ProjectDeletedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("ProjectDeletedInDolibarr", (string) $object->ref);
$object->sendtoid = array();
} elseif ($action == 'PROJECT_CLOSE' && $object instanceof Project) {
@ -1359,13 +1359,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("ProjectClosedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("ProjectClosedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("ProjectClosedInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("ProjectClosedInDolibarr", (string) $object->ref);
}
$object->sendtoid = array();
@ -1377,14 +1377,14 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("TaskCreatedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("TaskCreatedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("TaskCreatedInDolibarr", $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Task").': '.$object->ref;
$object->actionmsg = $langs->transnoentities("TaskCreatedInDolibarr", (string) $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Task").': '.(string) $object->ref;
}
$object->sendtoid = array();
@ -1395,14 +1395,14 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("TaskModifiedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("TaskModifiedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("TaskModifiedInDolibarr", $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Task").': '.$object->ref;
$object->actionmsg = $langs->transnoentities("TaskModifiedInDolibarr", (string) $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Task").': '.(string) $object->ref;
}
$object->sendtoid = array();
@ -1413,14 +1413,14 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("TaskDeletedInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("TaskDeletedInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("TaskDeletedInDolibarr", $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Task").': '.$object->ref;
$object->actionmsg = $langs->transnoentities("TaskDeletedInDolibarr", (string) $object->ref);
$object->actionmsg .= "\n".$langs->transnoentities("Task").': '.(string) $object->ref;
}
$object->sendtoid = array();
@ -1431,13 +1431,13 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
if (empty($object->context['actionmsg2'])) {
$object->actionmsg2 = $langs->transnoentities("TICKET_ASSIGNEDInDolibarr", $object->ref);
$object->actionmsg2 = $langs->transnoentities("TICKET_ASSIGNEDInDolibarr", (string) $object->ref);
} else {
$object->actionmsg2 = $object->context['actionmsg2'];
}
}
if (empty($object->actionmsg)) {
$object->actionmsg = $langs->transnoentities("TICKET_ASSIGNEDInDolibarr", $object->ref);
$object->actionmsg = $langs->transnoentities("TICKET_ASSIGNEDInDolibarr", (string) $object->ref);
}
if ($object->oldcopy->fk_user_assign > 0) {
@ -1475,21 +1475,21 @@ class InterfaceActionsAuto extends DolibarrTriggers
if (empty($object->actionmsg2)) {
// Load translation files required by the page
$langs->loadLangs(array("agenda", "other"));
if ($langs->transnoentities($action."InDolibarr", (empty($object->newref) ? $object->ref : $object->newref)) != $action."InDolibarr") { // specific translation key
$object->actionmsg2 = $langs->transnoentities($action."InDolibarr", (empty($object->newref) ? $object->ref : $object->newref));
if ($langs->transnoentities($action."InDolibarr", (empty($object->newref) ? (string) $object->ref : $object->newref)) != $action."InDolibarr") { // specific translation key
$object->actionmsg2 = $langs->transnoentities($action."InDolibarr", (empty($object->newref) ? (string) $object->ref : $object->newref));
} else { // generic translation key
$tmp = explode('_', $action);
$object->actionmsg2 = $langs->transnoentities($tmp[count($tmp) - 1]."InDolibarr", (empty($object->newref) ? $object->ref : $object->newref));
$object->actionmsg2 = $langs->transnoentities($tmp[count($tmp) - 1]."InDolibarr", (empty($object->newref) ? (string) $object->ref : $object->newref));
}
}
if (empty($object->actionmsg)) {
// Load translation files required by the page
$langs->loadLangs(array("agenda", "other"));
if ($langs->transnoentities($action."InDolibarr", (empty($object->newref) ? $object->ref : $object->newref)) != $action."InDolibarr") { // specific translation key
$object->actionmsg = $langs->transnoentities($action."InDolibarr", (empty($object->newref) ? $object->ref : $object->newref));
if ($langs->transnoentities($action."InDolibarr", (empty($object->newref) ? (string) $object->ref : $object->newref)) != $action."InDolibarr") { // specific translation key
$object->actionmsg = $langs->transnoentities($action."InDolibarr", (empty($object->newref) ? (string) $object->ref : $object->newref));
} else { // generic translation key
$tmp = explode('_', $action);
$object->actionmsg = $langs->transnoentities($tmp[count($tmp) - 1]."InDolibarr", (empty($object->newref) ? $object->ref : $object->newref));
$object->actionmsg = $langs->transnoentities($tmp[count($tmp) - 1]."InDolibarr", (empty($object->newref) ? (string) $object->ref : $object->newref));
}
if (isModEnabled('multicompany') && property_exists($object, 'entity') && $object->entity > 1) {
$object->actionmsg .= ' ('.$langs->trans("Entity").' '.$object->entity.')';

View file

@ -2,7 +2,7 @@
/* Copyright (C) 2005-2012 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2021 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
@ -551,79 +551,83 @@ class InterfaceLdapsynchro extends DolibarrTriggers
$object->oldcopy = clone $object; // @phan-suppress-current-line PhanTypeMismatchProperty
}
if (!method_exists($object->oldcopy, '_load_ldap_info')) {
dol_syslog("Trigger ".$action." was called by a function that did not set previously the method ->_load_ldap_info onto object", LOG_WARNING);
$object->oldcopy = clone $object; // @phan-suppress-current-line PhanTypeMismatchProperty
}
$oldcopy = $object->oldcopy; // Copy for static analysis
$oldinfo = $object->oldcopy->_load_ldap_info();
$olddn = $object->oldcopy->_load_ldap_dn($oldinfo);
if (is_object($oldcopy)) {
if (!method_exists($oldcopy, '_load_ldap_info')) {
dol_syslog("Trigger ".$action." was called by a function that did not set previously the method ->_load_ldap_info onto object", LOG_WARNING);
$oldcopy = clone $object; // @phan-suppress-current-line PhanTypeMismatchProperty
}
// Verify if entry exist
$container = $object->oldcopy->_load_ldap_dn($oldinfo, 1);
$search = "(".$object->oldcopy->_load_ldap_dn($oldinfo, 2).")";
$records = $ldap->search($container, $search);
if (count($records) && $records['count'] == 0) {
$olddn = '';
}
$info = $object->_load_ldap_info();
$dn = $object->_load_ldap_dn($info);
$newrdn = $object->_load_ldap_dn($info, 2);
$newparent = $object->_load_ldap_dn($info, 1);
$result = $ldap->update($dn, $info, $user, $olddn, $newrdn, $newparent);
// For member type
if (getDolGlobalString('LDAP_MEMBER_TYPE_ACTIVE') && getDolGlobalInt('LDAP_MEMBER_TYPE_ACTIVE') === Ldap::SYNCHRO_DOLIBARR_TO_LDAP) {
require_once DOL_DOCUMENT_ROOT."/adherents/class/adherent_type.class.php";
/*
* Change member info
*/
$newmembertype = new AdherentType($this->db);
$newmembertype->fetch($object->typeid);
$newmembertype->listMembersForMemberType('', 1);
$oldinfo = $newmembertype->_load_ldap_info();
$olddn = $newmembertype->_load_ldap_dn($oldinfo);
$oldinfo = $oldcopy->_load_ldap_info();
$olddn = $oldcopy->_load_ldap_dn($oldinfo);
// Verify if entry exist
$container = $newmembertype->_load_ldap_dn($oldinfo, 1);
$search = "(".$newmembertype->_load_ldap_dn($oldinfo, 2).")";
$container = $oldcopy->_load_ldap_dn($oldinfo, 1);
$search = "(".$oldcopy->_load_ldap_dn($oldinfo, 2).")";
$records = $ldap->search($container, $search);
if (count($records) && $records['count'] == 0) {
$olddn = '';
}
$info = $newmembertype->_load_ldap_info(); // Contains all members, included the new one (insert already done before trigger call)
$dn = $newmembertype->_load_ldap_dn($info);
$info = $object->_load_ldap_info();
$dn = $object->_load_ldap_dn($info);
$newrdn = $object->_load_ldap_dn($info, 2);
$newparent = $object->_load_ldap_dn($info, 1);
$result = $ldap->update($dn, $info, $user, $olddn);
$result = $ldap->update($dn, $info, $user, $olddn, $newrdn, $newparent);
// For member type
if (getDolGlobalString('LDAP_MEMBER_TYPE_ACTIVE') && getDolGlobalInt('LDAP_MEMBER_TYPE_ACTIVE') === Ldap::SYNCHRO_DOLIBARR_TO_LDAP) {
require_once DOL_DOCUMENT_ROOT."/adherents/class/adherent_type.class.php";
if ($object->oldcopy->typeid != $object->typeid) {
/*
* Remove member in old member type
* Change member info
*/
$oldmembertype = new AdherentType($this->db);
$oldmembertype->fetch($object->oldcopy->typeid);
$oldmembertype->listMembersForMemberType('', 1);
$newmembertype = new AdherentType($this->db);
$newmembertype->fetch($object->typeid);
$newmembertype->listMembersForMemberType('', 1);
$oldinfo = $oldmembertype->_load_ldap_info();
$olddn = $oldmembertype->_load_ldap_dn($oldinfo);
$oldinfo = $newmembertype->_load_ldap_info();
$olddn = $newmembertype->_load_ldap_dn($oldinfo);
// Verify if entry exist
$container = $oldmembertype->_load_ldap_dn($oldinfo, 1);
$search = "(".$oldmembertype->_load_ldap_dn($oldinfo, 2).")";
$container = $newmembertype->_load_ldap_dn($oldinfo, 1);
$search = "(".$newmembertype->_load_ldap_dn($oldinfo, 2).")";
$records = $ldap->search($container, $search);
if (count($records) && $records['count'] == 0) {
$olddn = '';
}
$info = $oldmembertype->_load_ldap_info(); // Contains all members, included the new one (insert already done before trigger call)
$dn = $oldmembertype->_load_ldap_dn($info);
$info = $newmembertype->_load_ldap_info(); // Contains all members, included the new one (insert already done before trigger call)
$dn = $newmembertype->_load_ldap_dn($info);
$result = $ldap->update($dn, $info, $user, $olddn);
if ($oldcopy->typeid != $object->typeid) {
/*
* Remove member in old member type
*/
$oldmembertype = new AdherentType($this->db);
$oldmembertype->fetch($oldcopy->typeid);
$oldmembertype->listMembersForMemberType('', 1);
$oldinfo = $oldmembertype->_load_ldap_info();
$olddn = $oldmembertype->_load_ldap_dn($oldinfo);
// Verify if entry exist
$container = $oldmembertype->_load_ldap_dn($oldinfo, 1);
$search = "(".$oldmembertype->_load_ldap_dn($oldinfo, 2).")";
$records = $ldap->search($container, $search);
if (count($records) && $records['count'] == 0) {
$olddn = '';
}
$info = $oldmembertype->_load_ldap_info(); // Contains all members, included the new one (insert already done before trigger call)
$dn = $oldmembertype->_load_ldap_dn($info);
$result = $ldap->update($dn, $info, $user, $olddn);
}
}
}
}
@ -766,25 +770,29 @@ class InterfaceLdapsynchro extends DolibarrTriggers
$object->oldcopy = clone $object; // @phan-suppress-current-line PhanTypeMismatchProperty
}
$object->oldcopy->listMembersForMemberType('', 1);
$oldcopy = $object->oldcopy; // Local copy to help static analysis.
$oldinfo = $object->oldcopy->_load_ldap_info();
$olddn = $object->oldcopy->_load_ldap_dn($oldinfo);
if (is_object($oldcopy)) {
$oldcopy->listMembersForMemberType('', 1);
// Verify if entry exist
$container = $object->oldcopy->_load_ldap_dn($oldinfo, 1);
$search = "(".$object->oldcopy->_load_ldap_dn($oldinfo, 2).")";
$records = $ldap->search($container, $search);
if (count($records) && $records['count'] == 0) {
$olddn = '';
$oldinfo = $oldcopy->_load_ldap_info();
$olddn = $oldcopy->_load_ldap_dn($oldinfo);
// Verify if entry exist
$container = $oldcopy->_load_ldap_dn($oldinfo, 1);
$search = "(".$oldcopy->_load_ldap_dn($oldinfo, 2).")";
$records = $ldap->search($container, $search);
if (count($records) && $records['count'] == 0) {
$olddn = '';
}
$object->listMembersForMemberType('', 1);
$info = $object->_load_ldap_info();
$dn = $object->_load_ldap_dn($info);
$result = $ldap->update($dn, $info, $user, $olddn);
}
$object->listMembersForMemberType('', 1);
$info = $object->_load_ldap_info();
$dn = $object->_load_ldap_dn($info);
$result = $ldap->update($dn, $info, $user, $olddn);
}
if ($result <= 0) {

View file

@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2014-2016 Jean-François Ferry <hello@librethic.io>
* Copyright (C) 2016 Christophe Battarel <christophe@altairis.fr>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023-2025 Benjamin Falière <benjamin@faliere.com>
*
@ -378,8 +378,8 @@ class InterfaceTicketEmail extends DolibarrTriggers
$appli = $mysoc->name;
/* Send email to admin */
$subject = '['.$appli.'] '.$langs->transnoentities($base_subject, $object->ref, $object->track_id);
$message_admin = $langs->transnoentities($body, $object->track_id).'<br>';
$subject = '['.$appli.'] '.$langs->transnoentities($base_subject, (string) $object->ref, (string) $object->track_id);
$message_admin = $langs->transnoentities($body, (string) $object->track_id).'<br>';
$message_admin .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
$message_admin .= '<li>'.$langs->trans('Type').' : '.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code).'</li>';
$message_admin .= '<li>'.$langs->trans('TicketCategory').' : '.$langs->getLabelFromKey($this->db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code).'</li>';
@ -449,7 +449,7 @@ class InterfaceTicketEmail extends DolibarrTriggers
$appli = $mysoc->name;
$subject = '['.$appli.'] '.$langs->transnoentities($base_subject);
$message_customer = $langs->transnoentities($body, $object->track_id).'<br>';
$message_customer = $langs->transnoentities($body, (string) $object->track_id).'<br>';
$message_customer .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
$message_customer .= '<li>'.$langs->trans('Type').' : '.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code).'</li>';
$message_customer .= '<li>'.$langs->trans('TicketCategory').' : '.$langs->getLabelFromKey($this->db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code).'</li>';
@ -490,7 +490,7 @@ class InterfaceTicketEmail extends DolibarrTriggers
$message_customer .= '<p>'.$langs->trans('Message').' : <br><br>'.$message.'</p><br>';
if (getDolGlobalInt('TICKET_ENABLE_PUBLIC_INTERFACE')) {
$url_public_ticket = getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE', dol_buildpath('/public/ticket/', 2)).'view.php?track_id='.urlencode($object->track_id);
$url_public_ticket = getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE', dol_buildpath('/public/ticket/', 2)).'view.php?track_id='.urlencode((string) $object->track_id);
$message_customer .= '<p>'.$langs->trans($see_ticket).' : <a href="'.$url_public_ticket.'">'.$url_public_ticket.'</a></p>';
$message_customer .= '<p>'.$langs->trans('TicketEmailPleaseDoNotReplyToThisEmail').'</p>';
} else {
@ -547,7 +547,7 @@ class InterfaceTicketEmail extends DolibarrTriggers
$appli = $mysoc->name;
$subject = '['.$appli.'] '.$langs->transnoentities($base_subject);
$message = '<p>'.$langs->transnoentities($body, $object->track_id, dolGetFirstLastname($user->firstname, $user->lastname))."</p>";
$message = '<p>'.$langs->transnoentities($body, (string) $object->track_id, dolGetFirstLastname($user->firstname, $user->lastname))."</p>";
$message .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
$message .= '<li>'.$langs->trans('Type').' : '.$object->type_label.'</li>';
$message .= '<li>'.$langs->trans('Category').' : '.$object->category_label.'</li>';

View file

@ -61,12 +61,12 @@ class CdavLib
/**
* Base sql request for calendar events
*
* @param int $calid Calendar id
* @param int $calendarId Calendar id
* @param int|bool $oid Oid
* @param int|bool $ouri Ouri
* @return string
*/
public function getSqlCalEvents($calid, $oid = false, $ouri = false)
public function getSqlCalEvents($calendarId, $oid = false, $ouri = false)
{
// TODO : replace GROUP_CONCAT by
$sql = 'SELECT
@ -94,7 +94,7 @@ class CdavLib
FROM '.MAIN_DB_PREFIX.'actioncomm AS a';
$sql .= " LEFT JOIN '.MAIN_DB_PREFIX.'c_country as co ON co.rowid = sp.fk_pays
LEFT JOIN '.MAIN_DB_PREFIX.'c_country as cos ON cos.rowid = s.fk_pays
WHERE a.id IN (SELECT ar.fk_actioncomm FROM '.MAIN_DB_PREFIX.'actioncomm_resources ar WHERE ar.element_type='user' AND ar.fk_element=".((int) $calid).")
WHERE a.id IN (SELECT ar.fk_actioncomm FROM '.MAIN_DB_PREFIX.'actioncomm_resources ar WHERE ar.element_type='user' AND ar.fk_element=".((int) $calendarId).")
AND a.code IN (SELECT cac.code FROM '.MAIN_DB_PREFIX.'c_actioncomm cac WHERE cac.type <> 'systemauto')
AND a.entity IN (".getEntity('societe', 1).")";
// TODO Restrict on external users
@ -112,11 +112,11 @@ class CdavLib
/**
* Convert calendar row to VCalendar string
*
* @param int $calid Calendar id
* @param int $calendarId Calendar id
* @param Object $obj Object id
* @return string
*/
public function toVCalendar($calid, $obj)
public function toVCalendar($calendarId, $obj)
{
/*$categ = array();
if($obj->soc_client)
@ -169,7 +169,7 @@ class CdavLib
$caldata .= "LAST-MODIFIED:".gmdate('Ymd\THis', strtotime($obj->lastupd))."Z\n";
$caldata .= "DTSTAMP:".gmdate('Ymd\THis', strtotime($obj->lastupd))."Z\n";
if ($obj->sourceuid == '') {
$caldata .= "UID:".$obj->id.'-ev-'.$calid.'-cal-'.constant('CDAV_URI_KEY')."\n";
$caldata .= "UID:".$obj->id.'-ev-'.$calendarId.'-cal-'.constant('CDAV_URI_KEY')."\n";
} else {
$caldata .= "UID:".$obj->sourceuid."\n";
}
@ -251,24 +251,24 @@ class CdavLib
*/
public function getFullCalendarObjects($calendarId, $bCalendarData)
{
$calid = (int) $calendarId;
$calendarId = (int) $calendarId;
$calevents = array();
if (!$this->user->rights->agenda->myactions->read) {
return $calevents;
}
if ($calid != $this->user->id && (!isset($this->user->rights->agenda->allactions->read) || !$this->user->rights->agenda->allactions->read)) {
if ($calendarId != $this->user->id && (!isset($this->user->rights->agenda->allactions->read) || !$this->user->rights->agenda->allactions->read)) {
return $calevents;
}
$sql = $this->getSqlCalEvents($calid);
$sql = $this->getSqlCalEvents($calendarId);
$result = $this->db->query($sql);
if ($result) {
while ($obj = $this->db->fetch_object($result)) {
$calendardata = $this->toVCalendar($calid, $obj);
$calendardata = $this->toVCalendar($calendarId, $obj);
if ($bCalendarData) {
$calevents[] = array(

View file

@ -301,7 +301,7 @@ class ConferenceOrBoothAttendee extends CommonObject
$result = $this->createCommon($user, $notrigger);
if ($result > 0) {
$result = $this->fetch($result);
if ($result > 0) {
if ($result > 0 && empty($this->ref)) {
$this->ref = (string) $this->id;
$result = $this->update($user);
}

View file

@ -967,12 +967,14 @@ $help_url = 'EN:Module_Interventions|FR:Module_Fiches_d\'interventions';
llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-fichinter page-card');
// Create new intervention
if ($action == 'create') {
// Create new intervention
$soc = new Societe($db);
if ($socid > 0) {
$soc->fetch($socid);
}
print load_fiche_titre($langs->trans("AddIntervention"), '', 'intervention');
print load_fiche_titre($langs->trans("NewIntervention"), '', 'intervention');
if ($error > 0) {
dol_htmloutput_errors($mesg);
@ -980,10 +982,6 @@ if ($action == 'create') {
dol_htmloutput_mesg($mesg);
}
if ($socid) {
$res = $soc->fetch($socid);
}
if (GETPOST('origin', 'alphanohtml') && GETPOSTINT('originid')) {
// Parse element/subelement (ex: project_task)
$regs = array();
@ -1036,6 +1034,8 @@ if ($action == 'create') {
}
} else {
$projectid = GETPOSTINT('projectid');
$note_private = GETPOST("note_private", "alpha");
$note_public = GETPOST("note_public", "alpha");
}
if (!$conf->global->FICHEINTER_ADDON) {
@ -1051,206 +1051,192 @@ if ($action == 'create') {
//$modFicheinter = new $obj;
//$numpr = $modFicheinter->getNextValue($soc, $object);
print '<form name="fichinter" action="'.$_SERVER['PHP_SELF'].'" method="POST">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="socid" value='.$soc->id.'>';
print '<input type="hidden" name="action" value="add">';
print '<input type="hidden" name="socid" value="'.$soc->id.'">'."\n";
print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
print dol_get_fiche_head([]);
print '<table class="border centpercent">';
// Ref
print '<tr><td class="fieldrequired">'.$langs->trans('Ref').'</td><td>'.$langs->trans("Draft").'</td></tr>';
// Ref customer
print '<tr class="field_ref_client"><td class="titlefieldcreate">'.$langs->trans('RefCustomer').'</td><td class="valuefieldcreate">';
print '<input type="text" name="ref_client" value="'.GETPOST('ref_client').'"></td>';
print '</tr>';
// Thirdparty
print '<tr>';
print '<td class="fieldrequired">'.$langs->trans('ThirdParty').'</td>';
if ($socid > 0) {
$soc = new Societe($db);
$soc->fetch($socid);
print '<form name="fichinter" action="'.$_SERVER['PHP_SELF'].'" method="POST">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="socid" value='.$soc->id.'>';
print '<input type="hidden" name="action" value="add">';
print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
print dol_get_fiche_head([]);
print '<table class="border centpercent">';
print '<tr><td class="fieldrequired titlefieldcreate">'.$langs->trans("ThirdParty").'</td><td>'.$soc->getNomUrl(1).'</td></tr>';
// Ref
print '<tr><td class="fieldrequired">'.$langs->trans('Ref').'</td><td>'.$langs->trans("Draft").'</td></tr>';
// Ref customer
print '<tr class="field_ref_client"><td class="titlefieldcreate">'.$langs->trans('RefCustomer').'</td><td class="valuefieldcreate">';
print '<input type="text" name="ref_client" value="'.GETPOST('ref_client').'"></td>';
print '</tr>';
// Description (must be a textarea and not html must be allowed (used in list view)
print '<tr><td class="tdtop">'.$langs->trans("Description").'</td>';
print '<td>';
print '<textarea name="description" class="quatrevingtpercent" rows="'.ROWS_3.'">'.GETPOST('description').'</textarea>';
print '</td></tr>';
// Project
if (isModEnabled('project')) {
$formproject = new FormProjets($db);
$langs->load("project");
print '<tr><td>'.$langs->trans("Project").'</td><td>';
/* Fix: If a project must be linked to any companies (suppliers or not), project must be not be set as limited to customer but must be not linked to any particular thirdparty
if ($societe->fournisseur==1)
$numprojet=select_projects(-1, GETPOST("projectid", 'int'), 'projectid');
else
$numprojet=select_projects($societe->id, GETPOST("projectid", 'int'), 'projectid');
*/
$numprojet = $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 0, 0, 'maxwidth500 widthcentpercentminusxx');
if ($numprojet == 0) {
print ' &nbsp; <a href="'.DOL_URL_ROOT.'/projet/card.php?socid='.$soc->id.'&action=create"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddProject").'"></span></a>';
}
print '</td></tr>';
}
// Category
if (isModEnabled('category')) {
// Categories
print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
print $form->selectCategories(Categorie::TYPE_FICHINTER, 'categories', $object);
print "</td></tr>";
}
// Contract
if (isModEnabled('contract') && is_object($formcontract)) {
// This feature hang the application on large list of contracts, because the select component is not complete: it does not work like select of thirdparty or product to support large lists
// So we add a hidden option to avoid to have it used and the application locked, until the select_contract is fixed.
if (getDolGlobalString("CONTRACT_CAN_USE_THE_BUGGED_SELECT_COMPONENT")) {
$langs->load("contracts");
print '<tr><td>'.$langs->trans("Contract").'</td><td>';
$numcontrat = $formcontract->select_contract($soc->id, GETPOSTINT('contratid'), 'contratid', 0, 1, 1);
if ($numcontrat == 0) {
print ' &nbsp; <a href="'.DOL_URL_ROOT.'/contrat/card.php?socid='.$soc->id.'&action=create"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddContract").'"></span></a>';
}
print '</td></tr>';
}
}
// Model
$liste = ModelePDFFicheinter::liste_modeles($db);
if (count($liste) > 0) {
print '<tr>';
print '<td>'.$langs->trans("DefaultModel").'</td>';
print '<td>';
print $form->selectarray('model', $liste, getDolGlobalString('FICHEINTER_ADDON_PDF'));
print "</td></tr>";
}
// Public note
print '<tr>';
print '<td class="tdtop">'.$langs->trans('NotePublic').'</td>';
print '<td>';
$doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
print $doleditor->Create(1);
//print '<textarea name="note_public" cols="80" rows="'.ROWS_3.'">'.$note_public.'</textarea>';
print '</td></tr>';
// Private note
if (empty($user->socid)) {
print '<tr>';
print '<td class="tdtop">'.$langs->trans('NotePrivate').'</td>';
print '<td>';
$doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
print $doleditor->Create(1);
//print '<textarea name="note_private" cols="80" rows="'.ROWS_3.'">'.$note_private.'</textarea>';
print '</td></tr>';
}
// Other attributes
$parameters = array();
$reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;
if (empty($reshook)) {
print $object->showOptionals($extrafields, 'create');
}
// Show link to origin object
if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
$newclassname = $classname;
if ($newclassname == 'Propal') {
$langs->load('propal');
$newclassname = 'CommercialProposal';
}
print '<tr><td>'.$langs->trans($newclassname).'</td><td colspan="2">'.$objectsrc->getNomUrl(1).'</td></tr>';
// Amount
/* Hide amount because we only copy services so amount may differ than source
print '<tr><td>' . $langs->trans('AmountHT') . '</td><td>' . price($objectsrc->total_ht) . '</td></tr>';
print '<tr><td>' . $langs->trans('AmountVAT') . '</td><td>' . price($objectsrc->total_tva) . "</td></tr>";
if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) // Localtax1 RE
{
print '<tr><td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax1) . "</td></tr>";
}
if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) // Localtax2 IRPF
{
print '<tr><td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax2) . "</td></tr>";
}
print '<tr><td>' . $langs->trans('AmountTTC') . '</td><td>' . price($objectsrc->total_ttc) . "</td></tr>";
if (isModEnabled("multicurrency"))
{
print '<tr><td>' . $langs->trans('MulticurrencyAmountHT') . '</td><td>' . price($objectsrc->multicurrency_total_ht) . '</td></tr>';
print '<tr><td>' . $langs->trans('MulticurrencyAmountVAT') . '</td><td>' . price($objectsrc->multicurrency_total_tva) . "</td></tr>";
print '<tr><td>' . $langs->trans('MulticurrencyAmountTTC') . '</td><td>' . price($objectsrc->multicurrency_total_ttc) . "</td></tr>";
}
*/
}
print '</table>';
if (is_object($objectsrc)) {
print '<input type="hidden" name="origin" value="'.$objectsrc->element.'">';
print '<input type="hidden" name="originid" value="'.$objectsrc->id.'">';
} elseif ($origin == 'project' && !empty($projectid)) {
print '<input type="hidden" name="projectid" value="'.$projectid.'">';
}
print dol_get_fiche_end();
print $form->buttonsSaveCancel("CreateDraftIntervention");
// Show origin lines
if (!empty($origin) && !empty($originid) && is_object($objectsrc) && !getDolGlobalInt('FICHINTER_DISABLE_DETAILS')) {
$title = $langs->trans('Services');
print load_fiche_titre($title);
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';
$objectsrc->printOriginLinesList(!getDolGlobalString('FICHINTER_PRINT_PRODUCTS') ? 'services' : ''); // Show only service, except if option FICHINTER_PRINT_PRODUCTS is on
print '</table>';
print '</div>';
}
print '</form>';
print $soc->getNomUrl(1);
print '<input type="hidden" name="socid" value="'.$soc->id.'">';
print '</td>';
} else {
print '<form name="fichinter" action="'.$_SERVER['PHP_SELF'].'" method="POST">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="create">'; // We go back to create action
print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
print dol_get_fiche_head([]);
if (is_object($objectsrc)) {
print '<input type="hidden" name="origin" value="'.$objectsrc->element.'">';
print '<input type="hidden" name="originid" value="'.$objectsrc->id.'">';
} elseif ($origin == 'project' && !empty($projectid)) {
print '<input type="hidden" name="projectid" value="'.$projectid.'">';
}
print '<table class="border centpercent">';
print '<tr><td class="fieldrequired">'.$langs->trans("ThirdParty").'</td><td>';
print $form->select_company('', 'socid', '', 'SelectThirdParty', 1, 0, array(), 0, 'minwidth300');
print ' <a href="'.DOL_URL_ROOT.'/societe/card.php?action=create&customer=3&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=create').'"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddThirdParty").'"></span></a>';
print '</td></tr>';
print '</table>';
print dol_get_fiche_end();
print $form->buttonsSaveCancel("CreateDraftIntervention");
print '</form>';
print '<td>';
print img_picto('', 'company', 'class="pictofixedwidth"');
print $form->select_company('', 'socid', '', 'SelectThirdParty', 1, 0, array(), 0, 'minwidth150 widthcentpercentminusxx maxwidth500');
print ' <a href="'.dolBuildUrl(DOL_URL_ROOT . '/societe/card.php', ['action' => 'create', 'customer' => 3, 'backtopage' => dolBuildUrl($_SERVER["PHP_SELF"], ['action' => 'create'])]).'"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddThirdParty").'"></span></a>';
print '</td>';
}
print '</tr>'."\n";
// Description (must be a textarea and not html must be allowed (used in list view)
print '<tr><td class="tdtop">'.$langs->trans("Description").'</td>';
print '<td>';
print '<textarea name="description" class="quatrevingtpercent" rows="'.ROWS_3.'">'.GETPOST('description').'</textarea>';
print '</td></tr>';
// Project
if (isModEnabled('project')) {
$formproject = new FormProjets($db);
$langs->load("project");
print '<tr><td>'.$langs->trans("Project").'</td><td>';
print img_picto('', 'project', 'class="pictofixedwidth"');
/* Fix: If a project must be linked to any companies (suppliers or not), project must be not be set as limited to customer but must be not linked to any particular thirdparty
if ($societe->fournisseur==1)
$numprojet=select_projects(-1, GETPOST("projectid", 'int'), 'projectid');
else
$numprojet=select_projects($societe->id, GETPOST("projectid", 'int'), 'projectid');
*/
$numprojet = $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 0, 0, 'maxwidth500 widthcentpercentminusxx');
if ($numprojet == 0) {
print ' &nbsp; <a href="' . dolBuildUrl(DOL_URL_ROOT . '/projet/card.php', ['socid' => $soc->id, 'action' => 'create', 'status' => 1, 'backtopage' => dolBuildUrl($_SERVER["PHP_SELF"], ['action' => 'create', 'socid' => $soc->id])]) . '"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddProject").'"></span></a>';
}
print '</td></tr>';
}
// Category
if (isModEnabled('category')) {
// Categories
print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
print $form->selectCategories(Categorie::TYPE_FICHINTER, 'categories', $object);
print "</td></tr>";
}
// Contract
if (isModEnabled('contract') && is_object($formcontract)) {
// This feature hang the application on large list of contracts, because the select component is not complete: it does not work like select of thirdparty or product to support large lists
// So we add a hidden option to avoid to have it used and the application locked, until the select_contract is fixed.
if (getDolGlobalString("CONTRACT_CAN_USE_THE_BUGGED_SELECT_COMPONENT")) {
$langs->load("contracts");
print '<tr><td>'.$langs->trans("Contract").'</td><td>';
$numcontrat = $formcontract->select_contract($soc->id, GETPOSTINT('contratid'), 'contratid', 0, 1, 1);
if ($numcontrat == 0) {
print ' &nbsp; <a href="'.DOL_URL_ROOT.'/contrat/card.php?socid='.$soc->id.'&action=create"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddContract").'"></span></a>';
}
print '</td></tr>';
}
}
// Model
$liste = ModelePDFFicheinter::liste_modeles($db);
if (count($liste) > 0) {
print '<tr>';
print '<td>'.$langs->trans("DefaultModel").'</td>';
print '<td>';
print $form->selectarray('model', $liste, getDolGlobalString('FICHEINTER_ADDON_PDF'));
print "</td></tr>";
}
// Public note
print '<tr>';
print '<td class="tdtop">'.$langs->trans('NotePublic').'</td>';
print '<td>';
$doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
print $doleditor->Create(1);
//print '<textarea name="note_public" cols="80" rows="'.ROWS_3.'">'.$note_public.'</textarea>';
print '</td></tr>';
// Private note
if (empty($user->socid)) {
print '<tr>';
print '<td class="tdtop">'.$langs->trans('NotePrivate').'</td>';
print '<td>';
$doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
print $doleditor->Create(1);
//print '<textarea name="note_private" cols="80" rows="'.ROWS_3.'">'.$note_private.'</textarea>';
print '</td></tr>';
}
// Other attributes
$parameters = array();
$reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;
if (empty($reshook)) {
print $object->showOptionals($extrafields, 'create');
}
// Show link to origin object
if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
$newclassname = $classname;
if ($newclassname == 'Propal') {
$langs->load('propal');
$newclassname = 'CommercialProposal';
}
print '<tr><td>'.$langs->trans($newclassname).'</td><td colspan="2">'.$objectsrc->getNomUrl(1).'</td></tr>';
// Amount
/* Hide amount because we only copy services so amount may differ than source
print '<tr><td>' . $langs->trans('AmountHT') . '</td><td>' . price($objectsrc->total_ht) . '</td></tr>';
print '<tr><td>' . $langs->trans('AmountVAT') . '</td><td>' . price($objectsrc->total_tva) . "</td></tr>";
if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) // Localtax1 RE
{
print '<tr><td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax1) . "</td></tr>";
}
if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) // Localtax2 IRPF
{
print '<tr><td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax2) . "</td></tr>";
}
print '<tr><td>' . $langs->trans('AmountTTC') . '</td><td>' . price($objectsrc->total_ttc) . "</td></tr>";
if (isModEnabled("multicurrency"))
{
print '<tr><td>' . $langs->trans('MulticurrencyAmountHT') . '</td><td>' . price($objectsrc->multicurrency_total_ht) . '</td></tr>';
print '<tr><td>' . $langs->trans('MulticurrencyAmountVAT') . '</td><td>' . price($objectsrc->multicurrency_total_tva) . "</td></tr>";
print '<tr><td>' . $langs->trans('MulticurrencyAmountTTC') . '</td><td>' . price($objectsrc->multicurrency_total_ttc) . "</td></tr>";
}
*/
}
print '</table>';
if (is_object($objectsrc)) {
print '<input type="hidden" name="origin" value="'.$objectsrc->element.'">';
print '<input type="hidden" name="originid" value="'.$objectsrc->id.'">';
} elseif ($origin == 'project' && !empty($projectid)) {
print '<input type="hidden" name="projectid" value="'.$projectid.'">';
}
print dol_get_fiche_end();
print $form->buttonsSaveCancel("CreateDraftIntervention");
// Show origin lines
if (!empty($origin) && !empty($originid) && is_object($objectsrc) && !getDolGlobalInt('FICHINTER_DISABLE_DETAILS')) {
$title = $langs->trans('Services');
print load_fiche_titre($title);
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';
$objectsrc->printOriginLinesList(!getDolGlobalString('FICHINTER_PRINT_PRODUCTS') ? 'services' : ''); // Show only service, except if option FICHINTER_PRINT_PRODUCTS is on
print '</table>';
print '</div>';
}
print '</form>';
} elseif ($id > 0 || !empty($ref)) {
// View mode

View file

@ -7,7 +7,7 @@
* Copyright (C) 2014 Jean Heimburger <jean@tiaris.info>
* Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2021-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2021-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
*
@ -33,6 +33,15 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php';
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture-rec.class.php';
@ -40,7 +49,6 @@ require_once DOL_DOCUMENT_ROOT.'/supplier_proposal/class/supplier_proposal.class
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
if (isModEnabled('member')) {
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
}
@ -49,15 +57,6 @@ if (isModEnabled('accounting')) {
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
}
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
// Load translation files required by page
$langs->loadLangs(array('accountancy', 'companies', 'suppliers', 'products', 'bills', 'orders', 'commercial'));
@ -75,7 +74,6 @@ $hookmanager->initHooks(array('thirdpartysupplier', 'globalcard'));
$result = restrictedArea($user, 'societe&fournisseur', $id, '&societe', '', 'rowid');
$object = new Fournisseur($db);
$extrafields = new ExtraFields($db);
// fetch optionals attributes and labels
$extrafields->fetch_name_optionals_label($object->table_element);

View file

@ -171,12 +171,12 @@ class CommandeFournisseurLigne extends CommonOrderLine
{
$sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
$sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
$sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
$sql .= ' cd.remise, cd.remise_percent, cd.subprice, cd.subprice_ttc,';
$sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
$sql .= ' cd.total_localtax1, cd.total_localtax2,';
$sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
$sql .= ' cd.date_start, cd.date_end, cd.fk_unit, cd.extraparams,';
$sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
$sql .= ' cd.multicurrency_subprice, cd.multicurrency_subprice_ttc, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
$sql .= ' c.fk_soc as socid';
$sql .= ' FROM '.$this->db->prefix().'commande_fournisseur as c, '.$this->db->prefix().'commande_fournisseurdet as cd';
$sql .= ' LEFT JOIN '.$this->db->prefix().'product as p ON cd.fk_product = p.rowid';

View file

@ -538,14 +538,18 @@ if (empty($reshook)) {
}
// option to notify the validator if the balance is less than the request
// (only for leave types that actually use the balance counter)
if (!getDolGlobalString('HOLIDAY_HIDE_APPROVER_ABOUT_NEGATIVE_BALANCE')) {
$tmpUser = new User($db);
$tmpUser->fetch($object->fk_user);
$affectingtypes = $object->getTypes(1, 1);
if (!empty($affectingtypes[$object->fk_type])) {
$tmpUser = new User($db);
$tmpUser->fetch($object->fk_user);
$nbopenedday = num_open_day($object->date_debut_gmt, $object->date_fin_gmt, 0, 1, $object->halfday, $tmpUser->country_id, $object->fk_user);
$nbopenedday = num_open_day($object->date_debut_gmt, $object->date_fin_gmt, 0, 1, $object->halfday, $tmpUser->country_id, $object->fk_user);
if ($nbopenedday > $object->getCPforUser($object->fk_user, $object->fk_type)) {
$message .= "<p>".$langs->transnoentities("HolidaysToValidateAlertSolde")."</p>\n";
if ($nbopenedday > $object->getCPforUser($object->fk_user, $object->fk_type)) {
$message .= "<p>".$langs->transnoentities("HolidaysToValidateAlertSolde")."</p>\n";
}
}
}

View file

@ -760,11 +760,15 @@ function sendMail($id, $cancreate, $now, $autoValidation)
}
// option to notify the validator if the balance is less than the request
// (only for leave types that actually use the balance counter)
if (!getDolGlobalString('HOLIDAY_HIDE_APPROVER_ABOUT_NEGATIVE_BALANCE')) {
$nbopenedday = num_open_day($object->date_debut_gmt, $object->date_fin_gmt, 0, 1, $object->halfday, $expediteur->country_id, $object->fk_user);
$affectingtypes = $object->getTypes(1, 1);
if (!empty($affectingtypes[$object->fk_type])) {
$nbopenedday = num_open_day($object->date_debut_gmt, $object->date_fin_gmt, 0, 1, $object->halfday, $expediteur->country_id, $object->fk_user);
if ($nbopenedday > $object->getCPforUser($object->fk_user, $object->fk_type)) {
$message .= "<p>".$langs->transnoentities("HolidaysToValidateAlertSolde")."</p>\n";
if ($nbopenedday > $object->getCPforUser($object->fk_user, $object->fk_type)) {
$message .= "<p>".$langs->transnoentities("HolidaysToValidateAlertSolde")."</p>\n";
}
}
}

View file

@ -59,6 +59,22 @@ insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (2, 220, 'Prime existence', 1,1,'TAXBEPRIEXI');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (2, 230, 'Precompte immobilier', 1,1,'TAXBEPREIMMO');
--
-- Switzerland
--
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 600, 'AVS / AI / APG (Basic social contributions grouped)', 1, 1, 'TAXCHBASE');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 601, 'Old-age insurance (AVS)', 1, 1, 'TAXCHAVS');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 602, 'Disability insurance (AI)', 1, 1, 'TAXCHAI');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 603, 'Income compensation allowance (APG)', 1, 1, 'TAXCHAPG');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 604, 'Family allowances (CAF)', 1, 1, 'TAXCHAC');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 605, 'Unemployment insurance (AC)', 1, 1, 'TAXCHACHO');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 606, 'Occupational pension plan (LPP / 2nd pillar)', 1, 1, 'TAXCHLPP');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 607, 'Occupational accident insurance (LAA - employer share)', 1, 1, 'TAXCHAA');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 608, 'Non-occupational accident insurance (LAA - employer share)', 1, 1, 'TAXCHAA-NP');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 609, 'Health insurance (LAMal - optional employer contribution)', 1, 1, 'TAXCHLAMAL');
insert into llx_c_chargesociales (fk_pays, id, libelle, deductible, active, code) values (6, 610, 'Withholding tax (employees taxed at source)', 1, 1, 'TAXCHIS');
--
-- Austria
--

View file

@ -530,4 +530,17 @@ ALTER TABLE llx_product_warehouse_properties ADD INDEX idx_product_warehouse_pro
ALTER TABLE llx_product_warehouse_properties ADD CONSTRAINT fk_product_warehouse_properties_fk_product FOREIGN KEY (fk_product) REFERENCES llx_product (rowid);
ALTER TABLE llx_product_warehouse_properties ADD CONSTRAINT fk_product_warehouse_properties_fk_entrepot FOREIGN KEY (fk_entrepot) REFERENCES llx_entrepot (rowid);
-- Add extrafields on various payment
CREATE TABLE llx_payment_various_extrafields
(
rowid integer AUTO_INCREMENT PRIMARY KEY,
tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
fk_object integer NOT NULL,
import_key varchar(14) -- import key
) ENGINE=innodb;
ALTER TABLE llx_payment_various_extrafields ADD UNIQUE INDEX uk_payment_various_extrafields (fk_object);
ALTER TABLE llx_actioncomm ADD COLUMN max_participants integer DEFAULT NULL AFTER status;
ALTER TABLE llx_actioncomm ADD INDEX idx_actioncomm_max_participants (max_participants);
-- end of migration

View file

@ -30,5 +30,6 @@ ALTER TABLE llx_actioncomm ADD INDEX idx_actioncomm_ref_ext (ref_ext);
ALTER TABLE llx_actioncomm ADD INDEX idx_actioncomm_percent (percent);
ALTER TABLE llx_actioncomm ADD INDEX idx_actioncomm_ref (ref, entity);
ALTER TABLE llx_actioncomm ADD INDEX idx_actioncomm_max_participants (max_participants);
-- To test performance to get fast next num on actioncomm
-- ALTER TABLE llx_actioncomm ADD INDEX idx_actioncomm_entity_id (entity, id);

View file

@ -76,6 +76,7 @@ create table llx_actioncomm
num_vote integer DEFAULT NULL, -- use for Event Organization module
event_paid smallint NOT NULL DEFAULT 0, -- use for Event Organization module
status smallint NOT NULL DEFAULT 0, -- use for Event Organization module for now, but could be use after for event global status
max_participants integer DEFAULT NULL, -- Maximum number of participants allowed for this action (event, meeting, shift)
fk_element integer DEFAULT NULL, -- For link to an element (proposal, invoice, order, ...)
elementtype varchar(255) DEFAULT NULL, -- For link to an element (proposal, invoice, order, ...)

View file

@ -0,0 +1,21 @@
-- ===================================================================
-- Copyright (C) 2013 Jean-Francois FERRY <jfefe@aternatik.fr>
-- Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
--
-- ===================================================================
ALTER TABLE llx_payment_various_extrafields ADD UNIQUE INDEX uk_payment_various_extrafields (fk_object);

View file

@ -0,0 +1,26 @@
-- ===================================================================
-- Copyright (C) 2013 Jean-Francois FERRY <jfefe@aternatik.fr>
-- Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
--
-- ===================================================================
CREATE TABLE llx_payment_various_extrafields
(
rowid integer AUTO_INCREMENT PRIMARY KEY,
tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
fk_object integer NOT NULL,
import_key varchar(14) -- import key
) ENGINE=innodb;

View file

@ -20,4 +20,4 @@ ALTER TABLE llx_product_warehouse_properties ADD INDEX idx_product_warehouse_pro
ALTER TABLE llx_product_warehouse_properties ADD INDEX idx_product_warehouse_properties_fk_entrepot (fk_entrepot);
ALTER TABLE llx_product_warehouse_properties ADD CONSTRAINT fk_product_warehouse_properties_fk_product FOREIGN KEY (fk_product) REFERENCES llx_product (rowid);
ALTER TABLE llx_product_warehouse_properties ADD CONSTRAINT fk_product_warehouse_properties_fk_entrepot FOREIGN KEY (fk_entrepot) REFERENCES llx_entrepot (rowid);
ALTER TABLE llx_product_warehouse_properties ADD CONSTRAINT fk_product_warehouse_properties_fk_entrepot FOREIGN KEY (fk_entrepot) REFERENCES llx_entrepot (rowid);

View file

@ -1,13 +1,14 @@
<?php
/* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2021-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Vincent de Grandpré <vincent@de-grandpre.quebec>
* Copyright (C) 2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
/* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2021-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Vincent de Grandpré <vincent@de-grandpre.quebec>
* Copyright (C) 2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2026 Solution Libre SAS <contact@solution-libre.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -414,7 +415,7 @@ if ($ok && GETPOST('standard', 'alpha')) {
'fichinter' => 'fichinter', 'fichinterdet' => 'fichinterdet',
'inventory' => 'inventory',
'actioncomm' => 'actioncomm', 'bom_bom' => 'bom_bom', 'mrp_mo' => 'mrp_mo',
'adherent_type' => 'adherent_type', 'user' => 'user', 'partnership' => 'partnership', 'projet' => 'projet', 'projet_task' => 'projet_task', 'ticket' => 'ticket');
'adherent_type' => 'adherent_type', 'user' => 'user', 'partnership' => 'partnership', 'projet' => 'projet', 'projet_task' => 'projet_task', 'ticket' => 'ticket', 'payment_various' => 'payment_various');
//$listofmodulesextra = array('fichinter'=>'fichinter');
print '<tr><td colspan="2"><br>*** Check that fields into the extra table structure match the table of definition. If not, add column into table</td></tr>';

View file

@ -2825,7 +2825,7 @@ ViewLogs=View Logs
DisableMCPAI=Turn off to completely disable AI access to MCP server
EnableMCPServer=Enable MCP Server
EnableLogging=Enable Request Logging
ObfuscatePIIData=Obfuscate PII Data
ObfuscatePIIData=Obfuscate Prompt Data
AIRequestLogs=AI Request Logs
ErrorTableNotExists=Table %s does not exist
PleaseRunSQLToEnableLogging=Please run the following SQL to enable logging

View file

@ -1436,3 +1436,4 @@ Delay=Delay
RefOfOnCustomerSide=Reference of %s on customer side
RefOfOnVendorSide=Reference of %s on vendor side
SeeDocumentation=See documentation
AIAssistant=AI Assistant

View file

@ -419,7 +419,6 @@ AIWelcomeMessage=Hi! I'm your Dolibarr assistant. How can I help you today?
ErrorThirdPartyNotFound=Error ThirdParty Not Found
TotalValue=Total Value
Expenses=Expenses
AIAssistant=AI Assistant
ClearChatHistoryTitle=Clear Chat History
Clear=Clear
OptionTextOnly=Text Only

View file

@ -2261,14 +2261,17 @@ function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead
$toprightmenu .= top_menu_search();
}
// Add AI picto
$toprightmenu .= top_menu_ai();
// Add bookmark dropdown
$toprightmenu .= top_menu_bookmark();
if (getDolGlobalString('MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN')) {
// Add the quick add object dropdown
$toprightmenu .= top_menu_quickadd();
}
// Add bookmark dropdown
$toprightmenu .= top_menu_bookmark();
if (getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE')) {
// Add the import file link
$toprightmenu .= top_menu_importfile();
@ -2710,6 +2713,33 @@ function top_menu_user($hideloginname = 0, $urllogout = '')
return $btnUser;
}
/**
* Build the tooltip on top menu quick add.
* Called when option MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN is set
*
* @return string HTML content
*/
function top_menu_ai()
{
global $conf, $langs;
// Button disabled on text browser
/*if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
return '';
}*/
$html = '';
if (isModEnabled('ai')) {
$html .= '<!-- div for quick ai link -->
<div id="topmenu-tool" class="atoplogin dropdown inline-block">
<a accesskey="a" class="nofocusvisible" href="'.DOL_URL_ROOT.'/ai/assistant/index.php" title="'.$langs->trans('AIAssistant').' ('.$conf->browser->stringforfirstkey.' a)"><i class="fa fa-magic"></i></a>
</div>';
}
return $html;
}
/**
* Build the tooltip on top menu quick add.
* Called when option MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN is set
@ -2730,7 +2760,7 @@ function top_menu_quickadd()
if (!empty($conf->use_javascript_ajax)) {
$html .= '<!-- div for quick add link -->
<div id="topmenu-quickadd-dropdown" class="atoplogin dropdown inline-block">
<a accesskey="a" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('QuickAdd').' ('.$conf->browser->stringforfirstkey.' a)"><i class="fa fa-plus-circle"></i></a>
<a accesskey="c" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('QuickAdd').' ('.$conf->browser->stringforfirstkey.' c)"><i class="fa fa-plus-circle"></i></a>
<div class="dropdown-menu">'.printDropdownQuickadd().'</div>
</div>';
if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) { // This may be set by some pages that use different jquery version to avoid errors

View file

@ -174,8 +174,10 @@ $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=orderstats&file=ordersnbiny
$px1 = new DolGraph();
$displaypx1 = false;
$mesg = $px1->isGraphKo();
if (!$mesg) {
$displaypx1 = true;
$px1->SetData($data);
$i = $startyear;
$legend = array();
@ -225,8 +227,10 @@ if (!$user->hasRight('societe', 'client', 'voir')) {
}
$px2 = new DolGraph();
$displaypx2 = false;
$mesg = $px2->isGraphKo();
if (!$mesg) {
$displaypx2 = true;
$px2->SetData($data);
$i = $startyear;
$legend = array();
@ -273,8 +277,10 @@ if (!$user->hasRight('societe', 'client', 'voir')) {
}
$px3 = new DolGraph();
$displaypx3 = false;
$mesg = $px3->isGraphKo();
if (!$mesg) {
$displaypx3 = true;
$px3->SetData($data);
$i = $startyear;
$legend = array();
@ -435,16 +441,16 @@ print '<table class="border centpercent"><tr class="pair nohover"><td align="cen
if ($mesg) {
print $mesg;
} else {
if (isset($px1)) {
if ($displaypx1) {
print $px1->show();
print "<br>\n";
}
/*
if (isset($px2)) {
if ($displaypx2) {
print $px2->show();
print "<br>\n";
}
if (isset($px2)) {
if ($displaypx3) {
print $px3->show();
print "<br>\n";
}

View file

@ -503,6 +503,8 @@ if (empty($reshook)) {
if ($result <= 0) {
setEventMessages($object->error, $object->errors, 'errors');
$action = '';
} else {
setEventMessages($langs->trans("FileGenerated"), null, 'mesgs');
}
}

View file

@ -1612,7 +1612,27 @@ if ($action == 'create' && $permissiontoadd) {
if ($reshook < 0) {
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
} elseif (empty($reshook)) {
$dispatchLines = array_merge($dispatchLines, $hookmanager->resArray);
// Only merge if resArray is a valid, non-empty array
if (is_array($hookmanager->resArray) && !empty($hookmanager->resArray)) {
// Check if the merge would overwrite valid data with NULL
$tempMerged = array_merge($dispatchLines, $hookmanager->resArray);
// Sanity check: ensure our existing keys are still valid arrays
$corrupted = false;
foreach ($dispatchLines as $idx => $val) {
if (!is_array($tempMerged[$idx]) || !isset($tempMerged[$idx]['fk_commandefourndet'])) {
$corrupted = true;
break;
}
}
if (!$corrupted) {
$dispatchLines = $tempMerged;
} else {
// If merge corrupted data, ignore the hook result and keep original
error_log("WARNING: Hook dataProcessing returned invalid data, ignoring merge.");
}
}
} elseif ($reshook > 0) {
// $resArray starts from [0], we need $dispatchLines to start from [1], so we shift it
$dispatchLines = $hookmanager->resArray;
@ -1622,7 +1642,7 @@ if ($action == 'create' && $permissiontoadd) {
}
print '<script type="text/javascript">
jQuery(document).ready(function() {
jQuery(document).ready(function() {
jQuery("#autofill").click(function(event) {
event.preventDefault();';
$i = 1;
@ -1639,8 +1659,8 @@ if ($action == 'create' && $permissiontoadd) {
$i++;
}
print '});
});
</script>';
});
</script>';
print '<br>';
@ -2526,7 +2546,7 @@ if ($action == 'create' && $permissiontoadd) {
// Get list of products already sent for same source object into $alreadysent
$alreadysent = array();
if (empty($origin)) {
if (empty($origin) || $origin == 'order_supplier') {
$origin = 'supplier_order';
}

View file

@ -3482,6 +3482,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($canvasdisplayactio
if ($result > 0) {
$adh->ref = $adh->getFullName($langs);
print $adh->getNomUrl(-1);
print ' &mdash; '.$adh->getLibStatut(0);
} else {
print '<span class="opacitymedium">'.$langs->trans("ThirdpartyNotLinkedToMember").'</span>';
}

View file

@ -1024,8 +1024,14 @@ class Societe extends CommonObject
$this->code_compta_client = trim($this->code_compta_client ?? '');
$this->accountancy_code_customer_general = trim($this->accountancy_code_customer_general ?? '');
if ($this->accountancy_code_customer_general === '-1') {
$this->accountancy_code_customer_general = '';
}
$this->accountancy_code_customer = trim((string) $this->code_compta_client);
$this->accountancy_code_supplier_general = trim($this->accountancy_code_supplier_general ?? '');
if ($this->accountancy_code_supplier_general === '-1') {
$this->accountancy_code_supplier_general = '';
}
$this->accountancy_code_supplier = trim((string) $this->code_compta_fournisseur);
$this->accountancy_code_buy = trim((string) $this->accountancy_code_buy);
$this->accountancy_code_sell = trim((string) $this->accountancy_code_sell);
@ -1537,8 +1543,14 @@ class Societe extends CommonObject
}
$this->accountancy_code_customer_general = trim((string) $this->accountancy_code_customer_general);
if ($this->accountancy_code_customer_general === '-1') {
$this->accountancy_code_customer_general = '';
}
$this->code_compta_client = trim((string) $this->code_compta_client);
$this->accountancy_code_supplier_general = trim((string) $this->accountancy_code_supplier_general);
if ($this->accountancy_code_supplier_general === '-1') {
$this->accountancy_code_supplier_general = '';
}
$this->code_compta_fournisseur = trim((string) $this->code_compta_fournisseur);
// Check parameters. More tests are done later in the ->verify()

View file

@ -351,7 +351,8 @@ if (empty($reshook)) {
// Add the payment
if (!$error && $res >= 0) {
$remaintopay = $invoice->getRemainToPay();
if ($remaintopay > 0) {
// Credit notes have negative remaintopay; regular invoices have positive
if (($remaintopay > 0 && $invoice->type != Facture::TYPE_CREDIT_NOTE) || ($remaintopay < 0 && $invoice->type == Facture::TYPE_CREDIT_NOTE)) {
$payment = new Paiement($db);
$payment->datepaye = $now;

View file

@ -330,7 +330,7 @@ a.top-menu-dropdown-link {
border-radius: 0;
}
#topmenu-tool,
#topmenu-global-search-dropdown,
#topmenu-quickadd-dropdown,
#topmenu-bookmark-dropdown,

View file

@ -51,7 +51,7 @@ button.dropdown-item.global-search-item {
display: block;
}
#topmenu-tool a,
#topmenu-global-search-dropdown a.login-dropdown-a,
#topmenu-quickadd-dropdown a.login-dropdown-a,
#topmenu-bookmark-dropdown a.login-dropdown-a,
@ -347,7 +347,7 @@ a.top-menu-dropdown-link {
border-radius: 0;
}
#topmenu-tool,
#topmenu-login-dropdown,
#topmenu-quickadd-dropdown,
#topmenu-bookmark-dropdown,

View file

@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2019 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
/* Copyright (C) 2019 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -25,10 +25,6 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticketstats.class.php';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
@ -36,6 +32,10 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticketstats.class.php';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php';
$WIDTH = DolGraph::getDefaultGraphSizeForStats('width');
$HEIGHT = DolGraph::getDefaultGraphSizeForStats('height');
@ -85,16 +85,18 @@ $help_url = '';
llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-ticket page-stats');
$param = '';
$mode = 'statistics';
$url = DOL_URL_ROOT.'/ticket/card.php?action=create&mode=init'.($socid ? '&socid='.$socid : '');
$params = [
'action' => 'create',
'mode' => 'init',
];
if (!empty($socid)) {
$url .= '&socid='.$socid;
$params += ['socid' => $socid];
}
$url = dolBuildUrl(DOL_URL_ROOT.'/ticket/card.php', $params);
$newcardbutton = '';
$newcardbutton .= dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/ticket/list.php?mode=common'.preg_replace('/(&|\?)*mode=[^&]+/', '', $param), '', ((empty($mode) || $mode == 'common') ? 2 : 1), array('morecss' => 'reposition'));
$newcardbutton .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', DOL_URL_ROOT.'/ticket/list.php?mode=kanban'.preg_replace('/(&|\?)*mode=[^&]+/', '', $param), '', ($mode == 'kanban' ? 2 : 1), array('morecss' => 'reposition'));
$newcardbutton .= dolGetButtonTitle($langs->trans('Statistics'), '', 'fa fa-chart-bar imgforviewmode', DOL_URL_ROOT.'/ticket/stats/index.php?mode=statistics&objecttype=ticket@ticket'.preg_replace('/(&|\?)*(mode|groupby)=[^&]+/', '', $param), '', ($mode == 'statistics' ? 2 : 1), array('morecss' => 'reposition'));
$newcardbutton .= dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/ticket/list.php?mode=common', '', 1, array('morecss' => 'reposition'));
$newcardbutton .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', DOL_URL_ROOT.'/ticket/list.php?mode=kanban', '', 1, array('morecss' => 'reposition'));
$newcardbutton .= dolGetButtonTitle($langs->trans('Statistics'), '', 'fa fa-chart-bar imgforviewmode', DOL_URL_ROOT.'/ticket/stats/index.php?mode=statistics&objecttype=ticket@ticket', '', 2, array('morecss' => 'reposition'));
$newcardbutton .= dolGetButtonTitleSeparator();
$newcardbutton .= dolGetButtonTitle($langs->trans('NewTicket'), '', 'fa fa-plus-circle', $url, '', $user->hasRight('ticket', 'write'));
@ -111,7 +113,6 @@ if ($object_status != '' && $object_status >= -1) {
// Build graphic number of object
$data = $stats->getNbByMonthWithPrevYear($endyear, $startyear);
//var_dump($data);
// $data = array(array('Lib',val1,val2,val3),...)
@ -149,9 +150,8 @@ if (!$mesg) {
// Build graphic amount of object
$data = $stats->getAmountByMonthWithPrevYear($endyear, $startyear);
//var_dump($data);
// $data = array(array('Lib',val1,val2,val3),...)
// $data = array(array('Lib',val1,val2,val3),...)
// Show array
$data = $stats->getAllByYear();

View file

@ -34,7 +34,7 @@ parameters:
- htdocs/includes/mobiledetect/*
- htdocs/includes/nusoap/*
- htdocs/includes/OAuth/*
- htdocs/includes/odtphp/zip/*
- htdocs/includes/odtphp/*
- htdocs/includes/parsedown/*
- htdocs/includes/php-iban/*
- htdocs/includes/phpoffice/*