Automated merge from 21.0 to 22.0 by tool pullmerge.sh

This commit is contained in:
Laurent Destailleur 2026-06-10 12:28:29 +02:00
commit a623693bd0
15 changed files with 90 additions and 22 deletions

View file

@ -62,9 +62,8 @@ exit_code=0
grep --no-filename -r -oP -- '^([^#=]+?)(?=\s*=.*)' "${LANG_DIR}" \
| grep -x -v -F -f "${EXCLUDE_KEYS_SRC_FILE}" \
| sort > "${AVAILABLE_FILE_NODEDUP}"
sort -u \
< "${AVAILABLE_FILE_NODEDUP}" \
> "${AVAILABLE_FILE}"
# sort -u is buggued in ubutnu 26.04. The line StatusProspect-1 and StatusProspect1 are treated as similar lines Replaced with "uniq".
uniq < "${AVAILABLE_FILE_NODEDUP}" > "${AVAILABLE_FILE}"
# Combine strings found in sources with pre-determined dynamic string values.

View file

@ -378,7 +378,19 @@ if (empty($reshook)) {
foreach ($toselect as $id) {
$res = $tmpmember->fetch($id);
if ($res > 0) {
$result = $tmpmember->subscription($now, (float) $amount, 0, '', '', '', '', '', $datesubend);
// Mirror the single subscription form (adherents/subscription.php): if no
// global EOM/EOY override was set, compute the end date from the member's
// AdherentType duration_value/duration_unit so a 6-month type does not
// silently fall back to the 1-year default in Adherent::subscription()
$datesubendthismember = $datesubend;
if (!$datesubendthismember && $tmpmember->typeid > 0) {
if ($adht->fetch($tmpmember->typeid) > 0) {
$delay = !empty($adht->duration_value) ? $adht->duration_value : 1;
$delayunit = !empty($adht->duration_unit) ? $adht->duration_unit : 'y';
$datesubendthismember = dol_time_plus_duree(dol_time_plus_duree($now, $delay, $delayunit), -1, 'd');
}
}
$result = $tmpmember->subscription($now, (float) $amount, 0, '', '', '', '', '', $datesubendthismember);
if ($result < 0) {
$error++;
} else {

View file

@ -1270,6 +1270,12 @@ class Facture extends CommonInvoice
$this->db->begin();
$object->fetch($fromid);
// fetch() clears $object->thirdparty, but the subsequent $object->create() path
// reaches addLine() -> getLocalTaxesFromRate() -> get_localtax() and uses the
// buyer thirdparty to decide whether IRPF/localtax2 applies. Without this fetch,
// get_localtax() sees $thirdparty_buyer = null, returns 0 and the cloned lines
// lose their localtax2_tx (see issue #29052).
$object->fetch_thirdparty();
// Load source object
$objFrom = clone $object;

View file

@ -2187,8 +2187,8 @@ class BonPrelevement extends CommonObject
$pre = substr(dol_string_nospecial(dol_string_unaccent($langs->transnoentitiesnoconv('RUM'))), 0, 3); // Must always be on 3 char ('RUM' or 'UMR'. This is a protection against bad translation)
// 3 char + '-' + 12 + '-' + id + '-' + code Must be lower than 32.
return $pre . '-' . dol_print_date($row_datec, 'dayhourlogsmall') . '-' . dol_trunc($row_drum . ($row_code_client ? '-' . $row_code_client : ''), 13, 'right', 'UTF-8', 1);
// 3 char + '-' + 10 (yymmddHHMM) + '-' + id + '-' + code. Must be under 32 (SEPA char limit for MndtId is however 35).
return $pre . '-' . dol_print_date($row_datec, 'dayhourlogsmall') . '-' . dol_trunc($row_drum . ($row_code_client ? '-' . $row_code_client : ''), 17, 'right', 'UTF-8', 1);
}

View file

@ -324,19 +324,23 @@ if (!$error && $massaction == 'confirm_presend') {
foreach ($listofobjectref[$thirdpartyid] as $objectid => $objectobj) {
//var_dump($thirdpartyid.' - '.$objectid.' - '.$objectobj->statut);
if ($objectclass == 'Propal' && $objectobj->status == Propal::STATUS_DRAFT) {
// Honour the same per-object hidden constants used by the "Send by email"
// button on the card pages (comm/propal/card.php, commande/card.php,
// compta/facture/card.php) so the behaviour is consistent between the
// single-object action and the mass action.
if ($objectclass == 'Propal' && $objectobj->status == Propal::STATUS_DRAFT && !getDolGlobalString('PROPOSAL_SENDBYEMAIL_FOR_ALL_STATUS')) {
$langs->load("errors");
$nbignored++;
$resaction .= '<div class="error">'.$langs->trans('ErrorOnlyProposalNotDraftCanBeSentInMassAction', $objectobj->ref).'</div><br>';
continue; // Payment done or started or canceled
}
if ($objectclass == 'Commande' && $objectobj->status == Commande::STATUS_DRAFT) {
if ($objectclass == 'Commande' && $objectobj->status == Commande::STATUS_DRAFT && !getDolGlobalString('COMMANDE_SENDBYEMAIL_FOR_ALL_STATUS')) {
$langs->load("errors");
$nbignored++;
$resaction .= '<div class="error">'.$langs->trans('ErrorOnlyOrderNotDraftCanBeSentInMassAction', $objectobj->ref).'</div><br>';
continue;
}
if ($objectclass == 'Facture' && $objectobj->status == Facture::STATUS_DRAFT) {
if ($objectclass == 'Facture' && $objectobj->status == Facture::STATUS_DRAFT && !getDolGlobalString('FACTURE_SENDBYEMAIL_FOR_ALL_STATUS')) {
$langs->load("errors");
$nbignored++;
$resaction .= '<div class="error">'.$langs->trans('ErrorOnlyInvoiceValidatedCanBeSentInMassAction', $objectobj->ref).'</div><br>';

View file

@ -1745,9 +1745,9 @@ class ExtraFields
$InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
}
// Fix better compatibility with some old extrafield syntax filter "(field=123)"
// Fix better compatibility with some old extrafield syntax filter "(field_name=123)"
$reg = array();
if (preg_match('/^\(?([a-z0-9]+)([=<>]+)(\d+)\)?$/i', $InfoFieldList[4], $reg)) {
if (preg_match('/^\(?([a-z0-9_]+)([=<>]+)(\d+)\)?$/i', $InfoFieldList[4], $reg)) {
$InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
}

View file

@ -44,7 +44,7 @@ function holiday_prepare_head($object)
// Attachments
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php';
$upload_dir = $conf->holiday->multidir_output[$object->entity].'/'.dol_sanitizeFileName($object->ref);
$upload_dir = $conf->holiday->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, 'holiday');
$nbFiles = count(dol_dir_list($upload_dir, 'files', 0, '', '(\.meta|_preview.*\.png)$'));
$nbLinks = Link::count($db, $object->element, $object->id);
$head[$h][0] = DOL_URL_ROOT.'/holiday/document.php?id='.$object->id;

View file

@ -195,14 +195,14 @@ class pdf_standard_evaluation extends ModelePDFEvaluation
if ($conf->hrm->dir_output) {
// Definition of $dir and $file
$entity = isset($object->entity) ? $object->entity : 1;
$basedir = empty($conf->hrm->multidir_output[$entity]) ? $conf->hrm->dir_output : $conf->hrm->multidir_output[$entity];
if ($object->specimen) {
//$dir = $conf->hrm->dir_output;
$dir = $conf->hrm->multidir_output[isset($object->entity) ? $object->entity : 1].'/evaluation';
$dir = $basedir.'/evaluation';
$file = $dir."/SPECIMEN.pdf";
} else {
$objectref = dol_sanitizeFileName($object->ref);
//$dir = $conf->hrm->dir_output."/".$objectref;
$dir = $conf->hrm->multidir_output[isset($object->entity) ? $object->entity : 1].'/evaluation'."/".$objectref;
$dir = $basedir.'/evaluation'."/".$objectref;
$file = $dir."/".$objectref.".pdf";
}

View file

@ -529,6 +529,7 @@ 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')) {

View file

@ -446,3 +446,4 @@ ThisIdNotDefined=Id not defined
OperNotDefined=Payment method not defined
EmptyMessageNotAllowedError=Empty message is not allowed
SomeShipmentExists=Error, there is some shipment linked to the order. Deletion refused.
ErrorTooManyLinksIntoHTMLString=The HTML content contains too many links or images. Reduce the number or raise MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT.

View file

@ -37,6 +37,7 @@ require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php';
require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formother.class.php';
@ -568,8 +569,11 @@ if ($action == 'confirm_generateinvoice' && $user->hasRight('facture', 'creer'))
$idprodline = $fk_product;
}
// Add lines
$lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice", $username) . ' : ' . $qtyhourtext, $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject);
// Add lines. Pass type=1 (service) explicitly so the invoice line is tagged
// as a service even when no product is bound to the time entry. Otherwise
// the default $type=0 leaks through and the PDF labels the operation as
// "Delivery of goods" instead of "Provision of services" (issue #34571).
$lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice", $username) . ' : ' . $qtyhourtext, $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject, '', '', 0, 0, '', 'HT', 0, Product::TYPE_SERVICE);
if ($lineid < 0) {
$error++;
setEventMessages(null, $tmpinvoice->errors, 'errors');
@ -668,7 +672,11 @@ if ($action == 'confirm_generateinvoice' && $user->hasRight('facture', 'creer'))
}
$idprodline = $value['fk_product'];
}
$lineid = $tmpinvoice->addline($value['note'], $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject);
// Pass type=1 (service) explicitly so the invoice line is tagged as a service
// even when no product is bound to the time entry. Otherwise the default
// $type=0 leaks through and the PDF labels the operation as
// "Delivery of goods" instead of "Provision of services" (issue #34571).
$lineid = $tmpinvoice->addline($value['note'], $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject, '', '', 0, 0, '', 'HT', 0, Product::TYPE_SERVICE);
if ($lineid < 0) {
$error++;
setEventMessages(null, $tmpinvoice->errors, 'errors');

View file

@ -416,7 +416,9 @@ class Ticket extends CommonObject
if (isset($this->message)) {
$this->message = trim($this->message);
if (dol_strlen($this->message) > 65000) {
$this->errors[] = 'ErrorFieldTooLong';
global $langs;
$langs->loadLangs(array('errors', 'ticket'));
$this->errors[] = $langs->trans('ErrorFieldTooLong', $langs->transnoentitiesnoconv('InitialMessage'));
dol_syslog(get_class($this).'::create error -1 message too long', LOG_ERR);
$result = -1;
}
@ -1039,7 +1041,9 @@ class Ticket extends CommonObject
if (isset($this->message)) {
$this->message = trim($this->message);
if (dol_strlen($this->message) > 65000) {
$this->errors[] = 'ErrorFieldTooLong';
global $langs;
$langs->loadLangs(array('errors', 'ticket'));
$this->errors[] = $langs->trans('ErrorFieldTooLong', $langs->transnoentitiesnoconv('InitialMessage'));
dol_syslog(get_class($this).'::update error -1 message too long', LOG_ERR);
return -1;
}

View file

@ -317,6 +317,16 @@ if (empty($reshook)) {
$object->email = preg_replace('/\s+/', '', GETPOST("email", 'alphanohtml'));
$object->job = GETPOST("job", 'alphanohtml');
$object->signature = GETPOST("signature", 'restricthtml');
// restricthtml may swap the value with the literal 'ErrorTooManyLinksIntoHTMLString'
// when the html exceeds MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT (see issue #27987).
// Refuse the save so the literal does not end up persisted and later sent as an
// email body to customers.
if ($object->signature === 'ErrorTooManyLinksIntoHTMLString') {
$error++;
$langs->load("errors");
setEventMessages($langs->trans('ErrorTooManyLinksIntoHTMLString'), null, 'errors');
$action = 'create';
}
$object->accountancy_code = GETPOST("accountancy_code", 'alphanohtml');
$object->note_public = GETPOST("note_public", 'restricthtml');
$object->note_private = GETPOST("note_private", 'restricthtml');
@ -500,6 +510,16 @@ if (empty($reshook)) {
$object->email = preg_replace('/\s+/', '', GETPOST("email", 'alphanohtml'));
$object->job = GETPOST("job", 'alphanohtml');
$object->signature = GETPOST("signature", 'restricthtml');
// restricthtml may swap the value with the literal 'ErrorTooManyLinksIntoHTMLString'
// when the html exceeds MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT (see issue #27987).
// Refuse the save so the literal does not end up persisted and later sent as an
// email body to customers.
if ($object->signature === 'ErrorTooManyLinksIntoHTMLString') {
$error++;
$langs->load("errors");
setEventMessages($langs->trans('ErrorTooManyLinksIntoHTMLString'), null, 'errors');
$action = 'edit';
}
$object->accountancy_code = GETPOST("accountancy_code", 'alphanohtml');
$object->openid = GETPOST("openid", 'alphanohtml');
$object->fk_user = GETPOSTINT("fk_user") > 0 ? GETPOSTINT("fk_user") : 0;

View file

@ -723,6 +723,12 @@ class Users extends DolibarrApi
throw new RestException(404, 'Group not found');
}
if ($load_members > 0 && is_array($group_static->members) && count($group_static->members) > 0) {
foreach ($group_static->members as &$member) {
$member = $this->_cleanObjectDatas($member);
}
}
return $this->_cleanObjectDatas($group_static);
}

View file

@ -430,7 +430,14 @@ class ProductCombination
$parent = new Product($this->db);
$parent->fetch($this->fk_product_parent);
$this->updateProperties($parent, $user);
// Propagate failure of updateProperties (otherwise an update where the variant
// percentage drives the new price below the parent product's price_min returns
// success but leaves the variant product at the price=0 it had right after
// createProductCombination, see issue #32372).
$result = $this->updateProperties($parent, $user);
if ($result < 0) {
return $result;
}
return 1;
}