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

This commit is contained in:
ldestailleur 2025-04-15 12:27:32 +02:00
commit 708a6fa527
23 changed files with 264 additions and 83 deletions

View file

@ -676,7 +676,6 @@ RECUR
RSS
RateIndirect
Re-Open
Rechercher
ReconciledBy
RecordNotFound
RecruitmentJobPosition
@ -808,8 +807,6 @@ SupplierMissingRef
Synchronize
Syslog
TLS/STARTTLS
TLSv1
TLSv1.2
TYPE_
Tabs
Tag

View file

@ -158,8 +158,8 @@ if ($action == 'updatemode') {
$error = 0;
$accounting_modes = array(
'RECETTES-DEPENSES',
'CREANCES-DETTES'
'CREANCES-DETTES',
'RECETTES-DEPENSES'
);
$accounting_mode = GETPOST('accounting_mode', 'alpha');

View file

@ -2,7 +2,7 @@
/* Copyright (C) 2014-2017 Olivier Geffroy <jeff@jeffinfo.com>
* Copyright (C) 2015-2022 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2015-2020 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Jose MARTINEZ <jose.martinez@pichinov.com>
*
@ -2617,7 +2617,7 @@ class BookKeeping extends CommonObject
* @param string $thirdpartyname Thirdparty name
* @param string $reference Reference of the element
* @param string $labelaccount Label of the accounting account
* @param string $full 0=Default, 1=Keep label intact (no trunc so HTML content is not corrupted)
* @param int<0,1> $full 0=Default, 1=Keep label intact (no trunc so HTML content is not corrupted)
* @return string Label of the operation
*/
public function accountingLabelForOperation($thirdpartyname, $reference, $labelaccount, $full = 0)

View file

@ -7,7 +7,7 @@
* Copyright (C) 2013-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2013-2014 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2013-2014 Olivier Geffroy <jeff@jeffinfo.com>
* Copyright (C) 2017-2018 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2017-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018 Ferran Marcet <fmarcet@2byte.es>
*
* This program is free software; you can redistribute it and/or modify
@ -42,6 +42,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
@ -1129,7 +1130,7 @@ $journal_label = $langs->transnoentitiesnoconv($accountingjournalstatic->label);
$MAXNBERRORS = 5;
// Write bookkeeping
if ($action == 'writebookkeeping' && $user->hasRight('accounting', 'bind', 'write')) {
if ($action == 'writebookkeeping' /* && $user->hasRight('accounting', 'bind', 'write') */) { // Test on permission already done
foreach ($tabpay as $payment_id => $payment) {
$accountInfos = $tabaccount[$payment["fk_bank_account"]];
@ -1346,7 +1347,7 @@ if ($action == 'writebookkeeping' && $user->hasRight('accounting', 'bind', 'writ
$param .= '&date_endmonth='.$date_endmonth;
$param .= '&date_endyear='.$date_endyear;
$param .= '&in_bookkeeping='.$in_bookkeeping;
header("Location: ".$_SERVER['PHP_SELF'].($param ? '?'.$param : ''));
header("Location: " . $_SERVER['PHP_SELF'] . '?' . $param);
exit;
}
}

View file

@ -8,8 +8,8 @@
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2021-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2021-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.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
@ -1347,7 +1347,7 @@ if ($mode == 'marketplace') {
<input name="search_keyword" placeholder="<?php echo $langs->trans('Keyword') ?>" id="search_keyword" type="text" class="minwidth200" value="<?php echo dol_escape_htmltag($options['search']) ?>">
</div>
<div class="divsearchfield">
<input class="button buttongen reposition" value="<?php echo $langs->trans('Rechercher') ?>" type="submit">
<input class="button buttongen reposition" value="<?php echo $langs->trans('Search') ?>" type="submit">
<a class="buttonreset reposition" href="<?php echo $_SERVER["PHP_SELF"].'?mode=marketplace'; ?>"><?php echo $langs->trans('Reset') ?></a>
&nbsp;

View file

@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2022 Alice Adminson <aadminson@example.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Coryright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
*
* This program is free software: you can redistribute it and/or modify
@ -54,6 +54,7 @@ $pre_prompt = GETPOST('prePrompt');
$post_prompt = GETPOST('postPrompt');
$blacklists = GETPOST('blacklists');
$test = GETPOST('test');
$key = (string) GETPOST('key', 'alpha');
if (empty($action)) {
$action = 'edit';
@ -80,16 +81,16 @@ $aiservice = getDolGlobalString('AI_API_SERVICE', 'chatgpt');
// Setup conf for AI model
$formSetup->formHiddenInputs['action'] = "updatefeaturemodel";
foreach ($arrayofaifeatures as $key => $val) {
$newkey = $key;
if (preg_match('/^text/', $key)) {
foreach ($arrayofaifeatures as $featurekey => $feature) {
$newkey = $featurekey;
if (preg_match('/^text/', $featurekey)) {
$newkey = 'textgeneration';
}
$item = $formSetup->newItem('AI_API_'.strtoupper($aiservice).'_MODEL_'.$val["function"]); // Name of constant must end with _KEY so it is encrypted when saved into database.
$item = $formSetup->newItem('AI_API_'.strtoupper($aiservice).'_MODEL_'.$feature["function"]); // Name of constant must end with _KEY so it is encrypted when saved into database.
if ($arrayofai[$aiservice][$newkey] != 'na') {
$item->nameText = $langs->trans("AI_API_MODEL_".$val["function"]).' <span class="opacitymedium">('.$langs->trans("Default").' = '.$arrayofai[$aiservice][$newkey].')</span>';
$item->nameText = $langs->trans("AI_API_MODEL_".$feature["function"]).' <span class="opacitymedium">('.$langs->trans("Default").' = '.$arrayofai[$aiservice][$newkey].')</span>';
} else {
$item->nameText = $langs->trans("AI_API_MODEL_".$val["function"]).' <span class="opacitymedium">('.$langs->trans("None").')</span>';
$item->nameText = $langs->trans("AI_API_MODEL_".$feature["function"]).' <span class="opacitymedium">('.$langs->trans("None").')</span>';
}
$item->cssClass = 'minwidth500 input';
}
@ -158,8 +159,6 @@ if ($action == 'update' && !$cancel && !$test) {
// Update entry
if ($action == 'updatePrompts' && !$test) {
$key = GETPOST('key', 'alpha');
$blacklistArray = array_filter(array_map('trim', explode(',', $blacklists)));
$currentConfigurations[$key] = [
@ -187,8 +186,6 @@ if ($action == 'updatePrompts' && $test) {
// Delete entry
if ($action == 'confirm_deleteproperty' && GETPOST('confirm') == 'yes') {
$key = GETPOST('key', 'alpha');
if (isset($currentConfigurations[$key])) {
unset($currentConfigurations[$key]);
@ -276,10 +273,10 @@ if ($action == 'create') {
// Combo list of AI features
$out .= '<select name="functioncode" id="functioncode" class="flat minwidth500">';
$out .= '<option>&nbsp;</option>';
foreach ($arrayofaifeatures as $key => $val) {
$labelhtml = $langs->trans($arrayofaifeatures[$key]['label']).($arrayofaifeatures[$key]['status'] == 'notused' ? ' <span class="opacitymedium">('.$langs->trans("NotYetAvailable").')</span>' : "");
$labeltext = $langs->trans($arrayofaifeatures[$key]['label']);
$out .= '<option value="'.dol_escape_js($key).'" data-html="'.dol_escape_htmltag($labelhtml).'">'.dol_escape_htmltag($labeltext).'</option>';
foreach ($arrayofaifeatures as $featurekey => $feature) {
$labelhtml = $langs->trans($arrayofaifeatures[$featurekey]['label']).($arrayofaifeatures[$featurekey]['status'] == 'notused' ? ' <span class="opacitymedium">('.$langs->trans("NotYetAvailable").')</span>' : "");
$labeltext = $langs->trans($arrayofaifeatures[$featurekey]['label']);
$out .= '<option value="'.dol_escape_js($featurekey).'" data-html="'.dol_escape_htmltag($labelhtml).'">'.dol_escape_htmltag($labeltext).'</option>';
}
$out .= '</select>';
$out .= ajax_combobox("functioncode");
@ -290,8 +287,8 @@ if ($action == 'create') {
var changedValue = $(this).val();
console.log(changedValue);
var arrayplaceholder = {';
foreach ($arrayofaifeatures as $key => $val) {
$out .= dol_escape_js($key).': \''.dol_escape_js(empty($val['placeholder']) ? '' : $val['placeholder']).'\',';
foreach ($arrayofaifeatures as $featurekey => $feature) {
$out .= dol_escape_js($featurekey).': \''.dol_escape_js(empty($feature['placeholder']) ? '' : $feature['placeholder']).'\',';
}
$out .= '}
jQuery("#prePromptInput'.dol_escape_js($key).'").val(arrayplaceholder[changedValue]);
@ -350,22 +347,22 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
$out = '';
if (!empty($currentConfigurations)) {
foreach ($currentConfigurations as $key => $config) {
if (!empty($key) && !preg_match('/^[a-z]+$/i', $key)) { // Ignore empty saved setup
foreach ($currentConfigurations as $confkey => $config) {
if (!empty($confkey) && !preg_match('/^[a-z]+$/i', $confkey)) { // Ignore empty saved setup
continue;
}
$out .= '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
$out .= '<input type="hidden" name="token" value="'.newToken().'">';
$out .= '<input type="hidden" name="key" value="'.$key.'" />';
$out .= '<input type="hidden" name="key" value="'.$confkey.'" />';
$out .= '<input type="hidden" name="action" value="updatePrompts">';
$out .= '<input type="hidden" name="page_y" value="">';
$out .= '<table class="noborder centpercent">';
$out .= '<thead>';
$out .= '<tr class="liste_titre">';
$out .= '<td class="titlefield">'.$arrayofaifeatures[$key]['picto'].' '.$langs->trans($arrayofaifeatures[$key]['label']);
$out .= '<a class="deletefielda reposition marginleftonly right" href="'.$_SERVER["PHP_SELF"].'?action=deleteproperty&token='.newToken().'&key='.urlencode($key).'">'.img_delete().'</a>';
$out .= '<td class="titlefield">'.$arrayofaifeatures[$confkey]['picto'].' '.$langs->trans($arrayofaifeatures[$confkey]['label']);
$out .= '<a class="deletefielda reposition marginleftonly right" href="'.$_SERVER["PHP_SELF"].'?action=deleteproperty&token='.newToken().'&key='.urlencode($confkey).'">'.img_delete().'</a>';
$out .= '</td>';
$out .= '<td></td>';
$out .= '</tr>';
@ -377,7 +374,7 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
$out .= '<span id="prePrompt" class="spanforparamtooltip">'.$langs->trans("Pre-Prompt").'</span>';
$out .= '</td>';
$out .= '<td>';
$out .= '<textarea class="flat minwidth500 quatrevingtpercent" id="prePromptInput_'.$key.'" name="prePrompt" rows="2">'.$config['prePrompt'].'</textarea>';
$out .= '<textarea class="flat minwidth500 quatrevingtpercent" id="prePromptInput_'.$confkey.'" name="prePrompt" rows="2">'.$config['prePrompt'].'</textarea>';
$out .= '</td>';
$out .= '</tr>';
@ -386,14 +383,14 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
$out .= '<span id="postPrompt" class="spanforparamtooltip">'.$langs->trans("Post-Prompt").'</span>';
$out .= '</td>';
$out .= '<td>';
$out .= '<textarea class="flat minwidth500 quatrevingtpercent" id="postPromptInput_'.$key.'" name="postPrompt" rows="2">'.$config['postPrompt'].'</textarea>';
$out .= '<textarea class="flat minwidth500 quatrevingtpercent" id="postPromptInput_'.$confkey.'" name="postPrompt" rows="2">'.$config['postPrompt'].'</textarea>';
$out .= '</td>';
$out .= '</tr>';
$out .= '<tr id="fichetwothirdright-'.$key.'" class="oddeven">';
$out .= '<tr id="fichetwothirdright-'.$confkey.'" class="oddeven">';
$out .= '<td>'.$form->textwithpicto($langs->trans("BlackListWords"), $langs->trans("BlackListWordsHelp")).'</td>';
$out .= '<td>';
$out .= '<input type="text" class="flat minwidth500 quatrevingtpercent" id="blacklist_'.$key.'" name="blacklists" value="'.(isset($config['blacklists']) ? implode(', ', (array) $config['blacklists']) : '').'">';
$out .= '<input type="text" class="flat minwidth500 quatrevingtpercent" id="blacklist_'.$confkey.'" name="blacklists" value="'.(isset($config['blacklists']) ? implode(', ', (array) $config['blacklists']) : '').'">';
$out .= '</td>';
$out .= '</tr>';
@ -405,10 +402,10 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
$formmail = new FormMail($db);
$formmail->withaiprompt = 'html'; // set format
$showlinktoai = $key; // 'textgenerationemail', 'textgenerationwebpage', 'imagegeneration', ...
$showlinktoai = $confkey; // 'textgenerationemail', 'textgenerationwebpage', 'imagegeneration', ...
$showlinktoailabel = $langs->trans("ToTest");
$htmlname = $key;
$onlyenhancements = $key;
$htmlname = $confkey;
$onlyenhancements = $confkey;
// Fill $out
include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php';
@ -421,7 +418,7 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
$out .= '</tbody>';
$out .= '</table>';
$out .= '<center><input type="submit" class="button small submitBtn reposition" name="modify" data-index="'.$key.'" value="'.dol_escape_htmltag($langs->trans("Save")).'"/></center>';
$out .= '<center><input type="submit" class="button small submitBtn reposition" name="modify" data-index="'.$confkey.'" value="'.dol_escape_htmltag($langs->trans("Save")).'"/></center>';
$out .= '</form>';

View file

@ -1,6 +1,6 @@
<?php
/* Copyright (C) 2022 Alice Adminson <aadminson@example.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software: you can redistribute it and/or modify
@ -36,13 +36,13 @@ function getListOfAIFeatures()
global $langs;
$arrayofaifeatures = array(
'textgenerationemail' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("EmailContent").')', 'picto' => '', 'status' => 'dolibarr', 'function' => 'TEXT', 'placeholder' => AI::AI_DEFAULT_PROMPT_FOR_EMAIL),
'textgenerationwebpage' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("WebsitePage").')', 'picto' => '', 'status' => 'dolibarr', 'function' => 'TEXT', 'placeholder' => AI::AI_DEFAULT_PROMPT_FOR_WEBPAGE),
'textgenerationemail' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("EmailContent").')', 'picto' => '', 'status' => 'dolibarr', 'function' => 'TEXT', 'placeholder' => Ai::AI_DEFAULT_PROMPT_FOR_EMAIL),
'textgenerationwebpage' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("WebsitePage").')', 'picto' => '', 'status' => 'dolibarr', 'function' => 'TEXT', 'placeholder' => Ai::AI_DEFAULT_PROMPT_FOR_WEBPAGE),
'textgeneration' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("Other").')', 'picto' => '', 'status' => 'notused', 'function' => 'TEXT'),
'texttranslation' => array('label' => $langs->trans('TextTranslation'), 'picto' => '', 'status'=>'dolibarr', 'function' => 'TEXT', 'placeholder' => AI::AI_DEFAULT_PROMPT_FOR_TEXT_TRANSLATION),
'textsummarize' => array('label' => $langs->trans('TextSummarize'), 'picto' => '', 'status'=>'dolibarr', 'function' => 'TEXT', 'placeholder' => AI::AI_DEFAULT_PROMPT_FOR_TEXT_SUMMARIZE),
'textrephrase' => array('label' => $langs->trans('TextRephraser'), 'picto' => '', 'status'=>'dolibarr', 'function' => 'TEXT', 'placeholder' => AI::AI_DEFAULT_PROMPT_FOR_TEXT_REPHRASER),
'texttranslation' => array('label' => $langs->trans('TextTranslation'), 'picto' => '', 'status'=>'dolibarr', 'function' => 'TEXT', 'placeholder' => Ai::AI_DEFAULT_PROMPT_FOR_TEXT_TRANSLATION),
'textsummarize' => array('label' => $langs->trans('TextSummarize'), 'picto' => '', 'status'=>'dolibarr', 'function' => 'TEXT', 'placeholder' => Ai::AI_DEFAULT_PROMPT_FOR_TEXT_SUMMARIZE),
'textrephrase' => array('label' => $langs->trans('TextRephraser'), 'picto' => '', 'status'=>'dolibarr', 'function' => 'TEXT', 'placeholder' => Ai::AI_DEFAULT_PROMPT_FOR_TEXT_REPHRASER),
'imagegeneration' => array('label' => 'ImageGeneration', 'picto' => '', 'status' => 'notused', 'function' => 'IMAGE'),
'videogeneration' => array('label' => 'VideoGeneration', 'picto' => '', 'status' => 'notused', 'function' => 'VIDEO'),

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-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2025 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>
@ -499,9 +499,16 @@ if (!empty($actioncode)) {
} else {
if (is_array($actioncode)) {
// Remove all -1 values
$actioncode = array_filter($actioncode, function ($value) {
return ((string) $value !== '-1');
});
$actioncode = array_filter(
$actioncode,
/**
* @param string $value
* @return bool
*/
function ($value) {
return ((string) $value !== '-1');
}
);
if (count($actioncode)) {
$sql .= " AND c.code IN (".$db->sanitize("'".implode("','", $actioncode)."'", 1).")";
}

View file

@ -375,7 +375,7 @@ if ($object->id > 0) {
print '<table class="border centpercent tableforfield">';
// Nature Prospect/Customer/Supplier
print '<tr><td class="titlefield">'.$langs->trans('NatureOfThirdParty').'</td><td>';
print '<tr><td class="titlefieldmiddle">'.$langs->trans('NatureOfThirdParty').'</td><td>';
print $object->getTypeUrl(1);
print '</td></tr>';

View file

@ -1450,7 +1450,7 @@ if (empty($reshook)) {
}
// Standard or deposit invoice, not from a Predefined template invoice
if ((GETPOST('type') == Facture::TYPE_STANDARD || GETPOST('type') == Facture::TYPE_DEPOSIT || GETPOST('type') == Facture::TYPE_PROFORMA || (GETPOST('type') == Facture::TYPE_SITUATION && !GETPOST('situations'))) && GETPOST('fac_rec') <= 0) {
if ((GETPOST('type') == Facture::TYPE_STANDARD || GETPOST('type') == Facture::TYPE_DEPOSIT || GETPOST('type') == Facture::TYPE_PROFORMA || (GETPOST('type') == Facture::TYPE_SITUATION && GETPOSTINT('situations') < 0)) && GETPOST('fac_rec') <= 0) {
$typeamount = GETPOST('typedeposit', 'aZ09');
$valuestandardinvoice = price2num(str_replace('%', '', GETPOST('valuestandardinvoice', 'alpha')), 'MU');
$valuedeposit = price2num(str_replace('%', '', GETPOST('valuedeposit', 'alpha')), 'MU');
@ -1991,7 +1991,7 @@ if (empty($reshook)) {
}
// Situation invoices
if (GETPOST('type') == Facture::TYPE_SITUATION && GETPOST('situations')) {
if (GETPOST('type') == Facture::TYPE_SITUATION && GETPOSTINT('situations') > 0) {
if (empty($dateinvoice)) {
$error++;
$mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date"));

View file

@ -5244,6 +5244,11 @@ class Facture extends CommonInvoice
}
$sql .= " ORDER BY f.ref";
if (getDolGlobalInt('LIST_OF_QUALIFIED_INVOICES_LIMIT_DEFINED') > 0) {
$sql .= " DESC"; //order by
$sql .= " LIMIT " . getDolGlobalInt('LIST_OF_QUALIFIED_INVOICES_LIMIT_DEFINED');
}
dol_syslog(get_class($this)."::list_qualified_avoir_invoices", LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {

View file

@ -614,13 +614,63 @@ if ($action == 'makepayment_confirm' && $user->hasRight('facture', 'paiement'))
if (!empty($listofbills)) {
$nbwithdrawrequestok = 0;
foreach ($listofbills as $aBill) {
$db->begin();
$result = $aBill->demande_prelevement($user, $aBill->resteapayer, 'direct-debit', 'facture');
if ($result > 0) {
$db->commit();
$nbwithdrawrequestok++;
// Note: The 2 following SQL requests are wrong but it works because we have one record into pfd for one record into pl and for into p for the same fk_facture_fourn.
// The table prelevement and prelevement_lignes and must be removed in future and merged into prelevement_demande
// Step 1: Move field fk_... of llx_prelevement into llx_prelevement_lignes
// Step 2: Move field fk_... + status into prelevement_demande.
$pending = 0;
// Get pending requests open with no transfer receipt yet
$sql = "SELECT SUM(pfd.amount) as amount";
$sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pfd";
//if ($type == 'bank-transfer') {
//$sql .= " WHERE pfd.fk_facture_fourn = ".((int) $aBill->id);
//} else {
$sql .= " WHERE pfd.fk_facture = ".((int) $aBill->id);
//}
$sql .= " AND pfd.traite = 0";
//$sql .= " AND pfd.type = 'ban'";
$resql = $db->query($sql);
if ($resql) {
$obj = $db->fetch_object($resql);
if ($obj) {
$pending += (float) $obj->amount;
}
} else {
$db->rollback();
dol_print_error($db);
}
// Get pending request with a transfer receipt generated but not yet processed
$sqlPending = "SELECT SUM(pl.amount) as amount";
$sqlPending .= " FROM ".$db->prefix()."prelevement_lignes as pl";
$sqlPending .= " INNER JOIN ".$db->prefix()."prelevement as p ON p.fk_prelevement_lignes = pl.rowid";
//if ($type == 'bank-transfer') {
//$sqlPending .= " WHERE p.fk_facture_fourn = ".((int) $aBill->id);
//} else {
$sqlPending .= " WHERE p.fk_facture = ".((int) $aBill->id);
//}
$sqlPending .= " AND (pl.statut IS NULL OR pl.statut = 0)";
$resPending = $db->query($sqlPending);
if ($resPending) {
if ($objPending = $db->fetch_object($resPending)) {
$pending += (float) $objPending->amount;
}
}
$db->free($resPending);
$requestAmount = $aBill->resteapayer - $pending;
$db->begin();
$result = $aBill->demande_prelevement($user, $requestAmount, 'direct-debit', 'facture');
if ($requestAmount > 0) {
if ($result > 0) {
$db->commit();
$nbwithdrawrequestok++;
} else {
$db->rollback();
setEventMessages($aBill->error, $aBill->errors, 'errors');
}
} else {
$aBill->error = 'WithdrawRequestErrorNilAmount';
$aBill->errors[] = $aBill->error;
setEventMessages($aBill->error, $aBill->errors, 'errors');
}
}

View file

@ -1,5 +1,4 @@
<?php
/* Copyright (C) 2002-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
* Copyright (C) 2004-2016 Laurent Destailleur <eldy@users.sourceforge.net>
@ -722,7 +721,49 @@ if ($object->id > 0) {
// For which amount ?
// Note: The 2 following SQL requests are wrong but it works because we have one record into pfd for one record into pl and for into p for the same fk_facture_fourn.
// The table prelevement and prelevement_lignes and must be removed in future and merged into prelevement_demande
// Step 1: Move field fk_... of llx_prelevement into llx_prelevement_lignes
// Step 2: Move field fk_... + status into prelevement_demande.
$pending = 0;
// Get pending requests open with no transfer receipt yet
$sql = "SELECT SUM(pfd.amount) as amount";
$sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pfd";
if ($type == 'bank-transfer') {
$sql .= " WHERE pfd.fk_facture_fourn = ".((int) $object->id);
} else {
$sql .= " WHERE pfd.fk_facture = ".((int) $object->id);
}
$sql .= " AND pfd.traite = 0";
//$sql .= " AND pfd.type = 'ban'";
$resql = $db->query($sql);
if ($resql) {
$obj = $db->fetch_object($resql);
if ($obj) {
$pending += (float) $obj->amount;
}
} else {
dol_print_error($db);
}
// Get pending request with a transfer receipt generated but not yet processed
$sqlPending = "SELECT SUM(pl.amount) as amount";
$sqlPending .= " FROM ".$db->prefix()."prelevement_lignes as pl";
$sqlPending .= " INNER JOIN ".$db->prefix()."prelevement as p ON p.fk_prelevement_lignes = pl.rowid";
if ($type == 'bank-transfer') {
$sqlPending .= " WHERE p.fk_facture_fourn = ".((int) $object->id);
} else {
$sqlPending .= " WHERE p.fk_facture = ".((int) $object->id);
}
$sqlPending .= " AND (pl.statut IS NULL OR pl.statut = 0)";
$resPending = $db->query($sqlPending);
if ($resPending) {
if ($objPending = $db->fetch_object($resPending)) {
$pending += (float) $objPending->amount;
}
}
$db->free($resPending);
/*
$sql = "SELECT SUM(pfd.amount) as amount";
$sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pfd";
if ($type == 'bank-transfer') {
@ -737,12 +778,12 @@ if ($object->id > 0) {
if ($resql) {
$obj = $db->fetch_object($resql);
if ($obj) {
$pending = $obj->amount;
$pendingAmount = $obj->amount;
}
} else {
dol_print_error($db);
}
*/
/*
* Buttons

View file

@ -817,6 +817,8 @@ class FormSetupItem
$val_const = GETPOST($this->confKey, 'restricthtml');
} elseif ($this->type == 'email') {
$val_const = GETPOST($this->confKey, 'alphawithlgt');
} elseif ($this->type == 'number') {
$val_const = GETPOSTINT($this->confKey);
} else {
$val_const = GETPOST($this->confKey, 'alphanohtml');
}
@ -1411,6 +1413,31 @@ class FormSetupItem
return $this;
}
/**
* Set type of input as number
* @param int $min minimum value for input number
* @param int $max maximum value for input number
* @param int $step legal number intervals
*
* @return self
*/
public function setAsNumber($min = null, $max = null, $step = null)
{
$this->type = 'number'; //for GETPOSTINT
$this->fieldAttr['type'] = 'number'; //generic thanks to generateAttributesStringFromArray
if (!is_null($min)) {
$this->fieldAttr['min'] = $min;
}
if (!is_null($max)) {
$this->fieldAttr['max'] = $max;
}
if (!is_null($step)) {
$this->fieldAttr['step'] = $step;
}
return $this;
}
/**
* Set type of input as string
*

View file

@ -1602,7 +1602,7 @@ class pdf_einstein extends ModelePDFCommandes
$companystatic = new Societe($this->db);
$companystatic->fetch($object->contact->fk_soc);
$carac_client_name_shipping = pdfBuildThirdpartyName($object->contact, $outputlangs);
$carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $companystatic, $object->contact, ($usecontact ? 1 : 0), 'target', $object);
$carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $companystatic, $object->contact, 1, 'target', $object);
} else {
$carac_client_name_shipping = pdfBuildThirdpartyName($object->thirdparty, $outputlangs);
$carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'target', $object);

View file

@ -1798,7 +1798,7 @@ class pdf_eratosthene extends ModelePDFCommandes
$companystatic = new Societe($this->db);
$companystatic->fetch($object->contact->fk_soc);
$carac_client_name_shipping = pdfBuildThirdpartyName($object->contact, $outputlangs);
$carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $companystatic, $object->contact, ($usecontact ? 1 : 0), 'target', $object);
$carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $companystatic, $object->contact, 1, 'target', $object);
} else {
$carac_client_name_shipping = pdfBuildThirdpartyName($object->thirdparty, $outputlangs);
$carac_client_shipping = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'target', $object);

View file

@ -318,8 +318,10 @@ if ($disablenofollow) {
if ($passwordInput.is('[type=password]')) {
$passwordInput.attr('type', 'text');
jQuery('#togglepassword .fa-eye').attr('class', 'fa fa-eye-slash');
} else {
$passwordInput.attr('type', 'password');
jQuery('#togglepassword .fa-eye-slash').attr('class', 'fa fa-eye');
}
return false; // This prevents the click from reloading the page

View file

@ -406,13 +406,62 @@ if (empty($reshook)) {
if (!empty($listofbills)) {
$nbwithdrawrequestok = 0;
foreach ($listofbills as $aBill) {
$db->begin();
$result = $aBill->demande_prelevement($user, $aBill->resteapayer, 'bank-transfer', 'supplier_invoice');
if ($result > 0) {
$db->commit();
$nbwithdrawrequestok++;
// Note: The 2 following SQL requests are wrong but it works because we have one record into pfd for one record into pl and for into p for the same fk_facture_fourn.
// The table prelevement and prelevement_lignes and must be removed in future and merged into prelevement_demande
// Step 1: Move field fk_... of llx_prelevement into llx_prelevement_lignes
// Step 2: Move field fk_... + status into prelevement_demande.
$pending = 0;
// Get pending requests open with no transfer receipt yet
$sql = "SELECT SUM(pfd.amount) as amount";
$sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pfd";
//if ($type == 'bank-transfer') {
$sql .= " WHERE pfd.fk_facture_fourn = ".((int) $aBill->id);
//} else {
// $sql .= " WHERE pfd.fk_facture = ".((int) $aBill->id);
//}
$sql .= " AND pfd.traite = 0";
//$sql .= " AND pfd.type = 'ban'";
$resql = $db->query($sql);
if ($resql) {
$obj = $db->fetch_object($resql);
if ($obj) {
$pending += (float) $obj->amount;
}
} else {
$db->rollback();
dol_print_error($db);
}
// Get pending request with a transfer receipt generated but not yet processed
$sqlPending = "SELECT SUM(pl.amount) as amount";
$sqlPending .= " FROM ".$db->prefix()."prelevement_lignes as pl";
$sqlPending .= " INNER JOIN ".$db->prefix()."prelevement as p ON p.fk_prelevement_lignes = pl.rowid";
//if ($type == 'bank-transfer') {
$sqlPending .= " WHERE p.fk_facture_fourn = ".((int) $aBill->id);
//} else {
// $sqlPending .= " WHERE p.fk_facture = ".((int) $aBill->id);
//}
$sqlPending .= " AND (pl.statut IS NULL OR pl.statut = 0)";
$resPending = $db->query($sqlPending);
if ($resPending) {
if ($objPending = $db->fetch_object($resPending)) {
$pending += (float) $objPending->amount;
}
}
$db->free($resPending);
$requestAmount = $aBill->resteapayer - $pending;
if ($requestAmount > 0) {
$db->begin();
$result = $aBill->demande_prelevement($user, $requestAmount, 'bank-transfer', 'supplier_invoice');
if ($result > 0) {
$db->commit();
$nbwithdrawrequestok++;
} else {
$db->rollback();
setEventMessages($aBill->error, $aBill->errors, 'errors');
}
} else {
$aBill->error = 'WithdrawRequestErrorNilAmount';
$aBill->errors[] = $aBill->error;
setEventMessages($aBill->error, $aBill->errors, 'errors');
}
}

View file

@ -16,13 +16,15 @@
--
-- ===================================================================
-- TODO This tables is a duplicate with table llx_prelevement_demande.
-- TODO Move the field status into table llx_prelevement_demande and remove this table.
create table llx_prelevement_lignes
(
rowid integer AUTO_INCREMENT PRIMARY KEY,
fk_prelevement_bons integer,
fk_soc integer NOT NULL,
fk_user integer NULL,
statut smallint DEFAULT 0,
client_nom varchar(255),

View file

@ -2667,6 +2667,6 @@ MULTICURRENCY_APP_ENDPOINT=API endpoint URL to get live multicurrencies from a s
NoWebsite=No website
UploadName=Upload
MAIN_SECURITY_MAXFILESIZE_DOWNLOADED=Max size of downloaded files
ShowTopMenuURLIntoAFrame=Show the content into a frame
ShowTopMenuURLIntoAFrame=Include content of URL into a frame under the top menu
SameWindow=Same window
NewWindow=New window / New tab

View file

@ -374,3 +374,5 @@ RephraseStyleProfessional=Professional style
RephraseStyleHumouristic=Humouristic style
TextRephraser=Text style rephraser
AIEnhancements=AI Enhancements
TLSv1=TLSv1
TLSv1.2=TLSv1.2

View file

@ -1,5 +1,6 @@
<?php
/* Copyright (C) 2004-2025 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2025 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
@ -244,7 +245,7 @@ function analyseVarsForSqlAndScriptsInjection(&$var, $type, $stopcode = 1)
if (is_array($var)) {
foreach ($var as $key => $value) { // Warning, $key may also be used for attacks
// Exclude check for some variable keys
if ($type === 0 && defined('NOSCANPOSTFORINJECTION') && is_array(constant('NOSCANPOSTFORINJECTION')) && in_array($key, constant('NOSCANPOSTFORINJECTION'))) {
if ($type === 0 && defined('NOSCANPOSTFORINJECTION') && is_array(constant('NOSCANPOSTFORINJECTION')) && in_array($key, (array) constant('NOSCANPOSTFORINJECTION'))) {
continue;
}

View file

@ -331,7 +331,7 @@ $manifestjsoncontentdefault .= '{
$listofpages = array();
$algo = '';
if (GETPOST('optiopagecontent')) {
if (GETPOST('optionpagecontent')) {
$algo .= 'content';
}
if (GETPOST('optionmeta')) {
@ -631,7 +631,7 @@ if ($massaction == 'replace' && GETPOST('confirmmassaction', 'alpha') && $userca
if ($objectpage->pageurl) {
dol_syslog("Replace string into page ".$objectpage->pageurl);
if (GETPOST('optiopagecontent', 'aZ09')) {
if (GETPOST('optionpagecontent', 'aZ09')) {
$objectpage->content = str_replace($searchkey, $replacestring, $objectpage->content);
}
if (GETPOST('optionmeta', 'aZ09')) {
@ -5425,7 +5425,7 @@ if ($mode == 'replacesite' || $massaction == 'replace') {
print $langs->trans("SearchReplaceInto");
print '</div>';
print '<div class="tagtd">';
print '<input type="checkbox" class="marginleftonly" id="checkboxoptiopagecontent" name="optiopagecontent" value="content"'.((!GETPOSTISSET('buttonreplacesitesearch') || GETPOST('optiopagecontent', 'aZ09')) ? ' checked' : '').'> <label for="checkboxoptiopagecontent" class="tdoverflowmax150onsmartphone inline-block valignmiddle">'.$langs->trans("Content").'</label><br>';
print '<input type="checkbox" class="marginleftonly" id="checkboxoptionpagecontent" name="optionpagecontent" value="content"'.((!GETPOSTISSET('buttonreplacesitesearch') || GETPOST('optionpagecontent', 'aZ09')) ? ' checked' : '').'> <label for="checkboxoptionpagecontent" class="tdoverflowmax150onsmartphone inline-block valignmiddle">'.$langs->trans("Content").'</label><br>';
print '<input type="checkbox" class="marginleftonly" id="checkboxoptionmeta" name="optionmeta" value="meta"'.(GETPOST('optionmeta', 'aZ09') ? ' checked' : '').'> <label for="checkboxoptionmeta" class="tdoverflowmax150onsmartphone inline-block valignmiddle">'.$langs->trans("Title").' | '.$langs->trans("Description").' | '.$langs->trans("Keywords").'</label><br>';
print '<input type="checkbox" class="marginleftonly" id="checkboxoptionsitefiles" name="optionsitefiles" value="sitefiles"'.(GETPOST('optionsitefiles', 'aZ09') ? ' checked' : '').'> <label for="checkboxoptionsitefiles" class="tdoverflowmax150onsmartphone inline-block valignmiddle">'.$langs->trans("GlobalCSSorJS").'</label><br>';
print '</div>';
@ -5545,8 +5545,8 @@ if ($mode == 'replacesite' || $massaction == 'replace') {
$param = 'mode=replacesite&website='.urlencode($website->ref);
$param .= '&searchstring='.urlencode($searchkey);
if (GETPOST('optiopagecontent')) {
$param .= '&optiopagecontent=content';
if (GETPOST('optionpagecontent')) {
$param .= '&optionpagecontent=content';
}
if (GETPOST('optionmeta')) {
$param .= '&optionmeta=meta';
@ -5595,7 +5595,7 @@ if ($mode == 'replacesite' || $massaction == 'replace') {
if (is_object($answerrecord) && get_class($answerrecord) == 'WebsitePage') {
$param = '?mode=replacesite';
$param .= '&websiteid='.$website->id;
$param .= '&optiopagecontent='.GETPOST('optiopagecontent', 'aZ09');
$param .= '&optionpagecontent='.GETPOST('optionpagecontent', 'aZ09');
$param .= '&optionmeta='.GETPOST('optionmeta', 'aZ09');
$param .= '&optionsitefiles='.GETPOST('optionsitefiles', 'aZ09');
$param .= '&optioncontainertype='.GETPOST('optioncontainertype', 'aZ09');
@ -5740,7 +5740,7 @@ if ($mode == 'replacesite' || $massaction == 'replace') {
'@phan-var-force array{type:string} $answerrecord';
$param = '?mode=replacesite';
$param .= '&websiteid='.$website->id;
$param .= '&optiopagecontent='.GETPOST('optiopagecontent', 'aZ09');
$param .= '&optionpagecontent='.GETPOST('optionpagecontent', 'aZ09');
$param .= '&optionmeta='.GETPOST('optionmeta', 'aZ09');
$param .= '&optionsitefiles='.GETPOST('optionsitefiles', 'aZ09');
$param .= '&optioncontainertype='.GETPOST('optioncontainertype', 'aZ09');