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

This commit is contained in:
Laurent Destailleur
2026-01-27 23:29:29 +01:00
7 changed files with 66 additions and 44 deletions

View File

@@ -541,7 +541,7 @@ if (isModEnabled('invoice')) {
print ajax_constantonoff('PDF_INVOICE_SHOW_VAT_ANALYSIS');
} else {
$arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes"));
print $form->selectarray("PDF_INVOICE_SHOW_VAT_ANALYSIS", $arrval, $conf->global->PDF_INVOICE_SHOW_VAT_ANALYSIS);
print $form->selectarray("PDF_INVOICE_SHOW_VAT_ANALYSIS", $arrval, getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS'));
}
print '</td></tr>';
@@ -553,7 +553,7 @@ if (isModEnabled('invoice')) {
print ajax_constantonoff('PDF_INVOICE_SHOW_BALANCE_SUMMARY');
} else {
$arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes"));
print $form->selectarray("PDF_INVOICE_SHOW_BALANCE_SUMMARY", $arrval, $conf->global->PDF_INVOICE_SHOW_BALANCE_SUMMARY);
print $form->selectarray("PDF_INVOICE_SHOW_BALANCE_SUMMARY", $arrval, getDolGlobalInt('PDF_INVOICE_SHOW_BALANCE_SUMMARY'));
}
print '</td></tr>';
@@ -565,7 +565,7 @@ if (isModEnabled('invoice')) {
print ajax_constantonoff('SUPPLIER_PROPOSAL_ADD_BILLING_CONTACT');
} else {
$arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes"));
print $form->selectarray("SUPPLIER_PROPOSAL_ADD_BILLING_CONTACT", $arrval, $conf->global->SUPPLIER_PROPOSAL_ADD_BILLING_CONTACT);
print $form->selectarray("SUPPLIER_PROPOSAL_ADD_BILLING_CONTACT", $arrval, getDolGlobalInt('SUPPLIER_PROPOSAL_ADD_BILLING_CONTACT'));
}
print '</td></tr>';
*/

View File

@@ -857,6 +857,7 @@ class Facture extends CommonInvoice
$newinvoiceline->fk_facture = $this->id;
$newinvoiceline->origin = $this->lines[$i]->element;
$newinvoiceline->origin_type = $this->lines[$i]->element;
$newinvoiceline->origin_id = $this->lines[$i]->id;
// Auto set date of service ?
@@ -4319,25 +4320,25 @@ class Facture extends CommonInvoice
return -3;
}
}
}
$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
$tmpproduct = new Product($this->db);
$result = $tmpproduct->fetch($fk_product);
if (abs($qty) < $tmpproduct->packaging) {
$qty = (float) $tmpproduct->packaging;
setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
} else {
if (!empty($tmpproduct->packaging) && $qty > $tmpproduct->packaging) {
$coeff = intval(abs($qty) / $tmpproduct->packaging) + 1;
$qty = price2num((float) $tmpproduct->packaging * $coeff, 'MS');
if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
$tmpproduct = new Product($this->db);
$result = $tmpproduct->fetch($fk_product);
if (abs($qty) < $tmpproduct->packaging) {
$qty = (float) $tmpproduct->packaging;
setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
} else {
if (!empty($tmpproduct->packaging) && $qty > $tmpproduct->packaging) {
$coeff = intval(abs($qty) / $tmpproduct->packaging) + 1;
$qty = price2num((float) $tmpproduct->packaging * $coeff, 'MS');
setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
}
}
}
}
$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
// Clean vat code
$reg = array();
$vat_src_code = '';

View File

@@ -705,9 +705,9 @@ class Conf extends stdClass
if (!isset($this->global->FACTURE_TVAOPTION)) {
$this->global->FACTURE_TVAOPTION = 1;
}
if (!isset($this->global->FAC_FORCE_DATE_VALIDATION)) {
$this->global->FAC_FORCE_DATE_VALIDATION = 1;
}
/*if (!isset($this->global->INVOICE_CHECK_POSTERIOR_DATE)) {
$this->global->INVOICE_CHECK_POSTERIOR_DATE = 1;
}*/
// Variable globales LDAP
if (empty($this->global->LDAP_FIELD_FULLNAME)) {

View File

@@ -782,10 +782,11 @@ function ajax_constantonoff($code, $input = array(), $entity = null, $revertonof
* @param string $htmlname Name of HTML component. Keep '' or use a different value if you need to use this component several time on the same page for the same field.
* @param int $forcenojs Force the component to work as link post (without javascript) instead of ajax call
* @param string $moreparam When $forcenojs=1 then we can add more parameters to the backtopage URL. String must url encoded. Example: 'abc=def&fgh=ijk'
* @param int $readonly Use 1 if button not allowed.
* @return string html for button on/off
* @see ajax_constantonoff() to update that value of a constant
*/
function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input = array(), $morecss = '', $htmlname = '', $forcenojs = 0, $moreparam = '')
function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input = array(), $morecss = '', $htmlname = '', $forcenojs = 0, $moreparam = '', $readonly = 0)
{
global $conf, $langs;
@@ -795,7 +796,7 @@ function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input =
$out = '';
if (!empty($conf->use_javascript_ajax) && empty($forcenojs)) {
if (!empty($conf->use_javascript_ajax) && empty($forcenojs) && empty($readonly)) {
$out .= '<script>
$(function() {
var input = '.json_encode($input).';
@@ -889,8 +890,16 @@ function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input =
}
if (empty($conf->use_javascript_ajax) || $forcenojs) {
$out .= '<a id="set_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? 'hideobject' : '').($morecss ? ' '.$morecss : '').'" href="'.DOL_URL_ROOT.'/core/ajax/objectonoff.php?action=set&token='.newToken().'&id='.((int) $object->id).'&element='.urlencode($object->element).'&field='.urlencode($field).'&value=1&backtopage='.urlencode($_SERVER["PHP_SELF"].'?id='.$object->id.($moreparam ? '&'.$moreparam : '')).'">'.img_picto($langs->trans($text_off), $switchoff, '', 0, 0, 0, '', $cssswitchoff).'</a>';
$out .= '<a id="del_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? '' : 'hideobject').($morecss ? ' '.$morecss : '').'" href="'.DOL_URL_ROOT.'/core/ajax/objectonoff.php?action=set&token='.newToken().'&id='.((int) $object->id).'&element='.urlencode($object->element).'&field='.urlencode($field).'&value=0&backtopage='.urlencode($_SERVER["PHP_SELF"].'?id='.$object->id.($moreparam ? '&'.$moreparam : '')).'">'.img_picto($langs->trans($text_on), $switchon, '', 0, 0, 0, '', $cssswitchon).'</a>';
$url = DOL_URL_ROOT.'/core/ajax/objectonoff.php?action=set&token='.newToken().'&id='.((int) $object->id).'&element='.urlencode($object->element).'&field='.urlencode($field).'&value=1&backtopage='.urlencode($_SERVER["PHP_SELF"].'?id='.$object->id.($moreparam ? '&'.$moreparam : ''));
if ($readonly) {
$url ='#';
}
$out .= '<a id="set_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? 'hideobject' : '').($morecss ? ' '.$morecss : '').'" href="'.$url.'">'.img_picto($langs->trans($text_off), $switchoff, '', 0, 0, 0, '', $cssswitchoff).'</a>';
$url = DOL_URL_ROOT.'/core/ajax/objectonoff.php?action=set&token='.newToken().'&id='.((int) $object->id).'&element='.urlencode($object->element).'&field='.urlencode($field).'&value=0&backtopage='.urlencode($_SERVER["PHP_SELF"].'?id='.$object->id.($moreparam ? '&'.$moreparam : ''));
if ($readonly) {
$url ='#';
}
$out .= '<a id="del_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? '' : 'hideobject').($morecss ? ' '.$morecss : '').'" href="'.$url.'">'.img_picto($langs->trans($text_on), $switchon, '', 0, 0, 0, '', $cssswitchon).'</a>';
} else {
$out .= '<span id="set_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? 'hideobject' : '').($morecss ? ' '.$morecss : '').'">'.img_picto($langs->trans($text_off), $switchoff, '', 0, 0, 0, '', $cssswitchoff).'</span>';
$out .= '<span id="del_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? '' : 'hideobject').($morecss ? ' '.$morecss : '').'">'.img_picto($langs->trans($text_on), $switchon, '', 0, 0, 0, '', $cssswitchon).'</span>';

View File

@@ -2479,7 +2479,7 @@ INVOICE_SHOW_SHIPPING_ADDRESS=Show shipping address
INVOICE_SHOW_SHIPPING_ADDRESSMore=Compulsory indication in some countries (France, ...)
SUPPLIER_PROPOSAL_ADD_BILLING_CONTACT=Show billing contact on proposal
SUPPLIER_PROPOSAL_ADD_BILLING_CONTACTMore=By default the contact only appears for billing
INVOICE_HIDE_LINKED_OBJECT=Hide linked object
INVOICE_HIDE_LINKED_OBJECT=Hide linked objects
INVOICE_HIDE_LINKED_OBJECTMore=This feature prevents linked objects from being displayed on the generated PDF invoice document.
UrlSocialNetworksDesc=Url link of social network. Use {socialid} for the variable part that contains the social network ID.
IfThisCategoryIsChildOfAnother=If this category is a child of another one
@@ -2668,7 +2668,7 @@ CaptchaDesc=If you want to protect your login page with a Captcha, you can choos
DolibarrStandardCaptcha=A native captcha generated by Dolibarr
SALES_ORDER_SHOW_SHIPPING_ADDRESS=Show shipping address
SALES_ORDER_SHOW_SHIPPING_ADDRESSMore=Compulsory indication in some countries (France, ...)
PDF_INVOICE_SHOW_VAT_ANALYSIS=Show vat analysis per rate
PDF_INVOICE_SHOW_VAT_ANALYSIS=Show the vat per rate analysis in columns (instead of in lines)
MaxNbOfRecordOnListIsOk=You have a max size for lists set to <b>%s</b> lines. This is a good value.
YouHaveALargeAmountOfRecordOnLists=You have a default max size for lists set to <b>%s</b> lines. This is a large value that need scrolling to see all answers. It is better to have a value lower than <b>%s</b> and use pagination to see record over this number. Change this in menu Home - Setup - Display.
RoundBorders=Round borders

View File

@@ -3168,6 +3168,13 @@ if (!GETPOST('hide_websitemenu')) {
}
$disabled = '';
$morecss = '';
if (!$user->hasRight('website', 'write')) {
$disabled = ' disabled="disabled"';
$morecss = 'opacitymedium cursordefault';
}
//var_dump($objectpage);exit;
print '<div class="centpercent websitebar'.(GETPOST('dol_openinpopup', 'aZ09') ? ' hiddenforpopup' : '').'">'."\n";
@@ -3183,9 +3190,11 @@ if (!GETPOST('hide_websitemenu')) {
// Button Add new website
$urltocreatenewwebsite = $_SERVER["PHP_SELF"].'?action=createsite';
print '<span class="websiteselection paddingrightonly">';
print '<a href="'.$urltocreatenewwebsite.'" class=""'.$disabled.' title="'.dol_escape_htmltag($langs->trans("AddWebsite")).'"><span class="fa fa-plus-circle valignmiddle btnTitle-icon"><span></a>';
print '</span>';
if ($user->hasRight('website', 'write')) {
print '<span class="websiteselection paddingrightonly">';
print '<a href="'.$urltocreatenewwebsite.'" class=""'.$disabled.' title="'.dol_escape_htmltag($langs->trans("AddWebsite")).'"><span class="fa fa-plus-circle valignmiddle btnTitle-icon"><span></a>';
print '</span>';
}
// List of website
print '<span class="websiteselection nopaddingrightimp">';
@@ -3247,10 +3256,10 @@ if (!GETPOST('hide_websitemenu')) {
//print '</div>';
if ($website->status == $website::STATUS_DRAFT) {
$text_off = 'Offline';
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setwebsiteonline&token='.newToken().'&website='.urlencode($website->ref).'&websitepage='.((int) $websitepage->id).'">'.img_picto($langs->trans($text_off), 'switch_off').'</a>';
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setwebsiteonline&token='.newToken().'&website='.urlencode($website->ref).'&websitepage='.((int) $websitepage->id).'"'.$disabled.'>'.img_picto($langs->trans($text_off), 'switch_off', '', 0, 0, 0, '', $morecss).'</a>';
} else {
$text_off = 'Online';
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setwebsiteoffline&token='.newToken().'&website='.urlencode($website->ref).'&websitepage='.((int) $websitepage->id).'">'.img_picto($langs->trans($text_off), 'switch_on').'</a>';
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setwebsiteoffline&token='.newToken().'&website='.urlencode($website->ref).'&websitepage='.((int) $websitepage->id).'"'.$disabled.'>'.img_picto($langs->trans($text_off), 'switch_on', '', 0, 0, 0, '', $morecss).'</a>';
}
print '</span>';
}
@@ -3258,7 +3267,7 @@ if (!GETPOST('hide_websitemenu')) {
// Refresh / Reload web site (for non javascript browsers)
if (empty($conf->use_javascript_ajax)) {
print '<span class="websiteselection">';
print '<input type="image" class="valignmiddle" src="'.img_picto('', 'refresh', '', 0, 1).'" name="refreshsite" value="'.$langs->trans("Load").'">';
print '<input type="image" class="valignmiddle" src="'.img_picto('', 'refresh', '', 0, 1).'" name="refreshsite" value="'.$langs->trans("Load").'"'.$disabled.'>';
print '</span>';
}
@@ -3468,10 +3477,11 @@ if (!GETPOST('hide_websitemenu')) {
print '</div>';
// Button Add new web page
print '<span class="websiteselection paddingrightonly">';
print '<a href="'.$_SERVER["PHP_SELF"].'?action=createcontainer&token='.newToken().'&website='.urlencode($website->ref).'" class=""'.$disabled.' title="'.dol_escape_htmltag($langs->trans("AddPage")).'"><span class="fa fa-plus-circle valignmiddle btnTitle-icon"></span></a>';
print '</span>';
if ($user->hasRight('website', 'write')) {
print '<span class="websiteselection paddingrightonly">';
print '<a href="'.$_SERVER["PHP_SELF"].'?action=createcontainer&token='.newToken().'&website='.urlencode($website->ref).'" class=""'.$disabled.' title="'.dol_escape_htmltag($langs->trans("AddPage")).'"><span class="fa fa-plus-circle valignmiddle btnTitle-icon"></span></a>';
print '</span>';
}
$out = '';
@@ -3507,13 +3517,13 @@ if (!GETPOST('hide_websitemenu')) {
if ($object->status == $object::STATUS_DRAFT) { // website is off, we do not allow to change status of page
$text_off = 'SetWebsiteOnlineBefore';
if ($websitepage->status == $websitepage::STATUS_DRAFT) { // page is off
print '<span class="valignmiddle disabled opacitymedium">'.img_picto($langs->trans($text_off), 'switch_off').'</span>';
print '<span class="valignmiddle disabled opacitymedium">'.img_picto($langs->trans($text_off), 'switch_off', '', 0, 0, 0, '', $morecss).'</span>';
} else {
print '<span class="valignmiddle disabled opacitymedium">'.img_picto($langs->trans($text_off), 'switch_on').'</span>';
print '<span class="valignmiddle disabled opacitymedium">'.img_picto($langs->trans($text_off), 'switch_on', '', 0, 0, 0, '', $morecss).'</span>';
}
} else {
if ($objectpage->type_container != 'setup') { // we do not allow to change status of setup pages
print ajax_object_onoff($websitepage, 'status', 'status', 'Online', 'Offline', array(), 'valignmiddle inline-block'.(empty($websitepage->id) ? ' opacitymedium disabled' : ''), 'statuswebsitepage', 1, 'website='.urlencode($website->ref).'&pageid='.((int) $websitepage->id));
print ajax_object_onoff($websitepage, 'status', 'status', 'Online', 'Offline', array(), 'valignmiddle inline-block'.((empty($websitepage->id) || !$user->hasRight('website', 'write')) ? ' opacitymedium disabled' : ''), 'statuswebsitepage', 1, 'website='.urlencode($website->ref).'&pageid='.((int) $websitepage->id), $user->hasRight('website', 'write') ? 0 : 1);
}
}
//print '</div>';

View File

@@ -73,6 +73,8 @@ class NumberingModulesTest extends CommonClassTest
$conf->global->FACTURE_MERCURE_MASK_DEPOSIT = '{yyyy}-{0000}';
$conf->global->FACTURE_MERCURE_MASK_REPLACEMENT = '{yyyy}-{0000}';
$conf->global->FAC_FORCE_DATE_VALIDATION = 0; // We disable option "Force date on validation", so we can make test on old dates
$conf->global->INVOICE_CHECK_POSTERIOR_DATE = 0; // We disable option "Check date on validation", so we can make test on old dates
$conf->global->MAIN_DISABLE_ALL_MAILS = 1;
$localobject = new Facture($db);
$localobject->initAsSpecimen();
@@ -80,14 +82,14 @@ class NumberingModulesTest extends CommonClassTest
$localobject->date = dol_mktime(12, 0, 0, 1, 1, 1915); // we use year 1915 to be sure to not have existing invoice for this year (useful only if numbering is {0000@1}
$numbering = new mod_facture_mercure();
$result = $numbering->getNextValue($mysoc, $localobject);
print __METHOD__." result=".$result."\n";
$this->assertEquals('1915-0001', $result, 'Test for {yyyy}-{0000}, 1st invoice'); // counter must start to 1
$result2 = $localobject->create($user, 1);
$newref = $numbering->getNextValue($mysoc, $localobject);
print __METHOD__." newref=".$newref."\n";
$this->assertEquals('1915-0001', $newref, 'Test for {yyyy}-{0000}, 1st invoice'); // counter must start to 1
$result = $localobject->create($user, 1);
print __METHOD__." result2=".$result."\n";
$result3 = $localobject->validate($user, $result); // create invoice by forcing ref
$result3 = $localobject->validate($user, $newref); // create invoice by forcing ref
print __METHOD__." result3=".$result."\n";
$this->assertEquals(1, $result3, 'Test validation of invoice with forced ref is ok'); // counter must start to 1
$this->assertEquals(1, $result3, 'Test that the validation of an invoice with the forced ref '.$newref.' is ok: '.$localobject->error); // counter must start to 1
// Force enable of BlockedLog (not possible from application, but required to allow the test with sample data))
activateModule('modBlockedLog', 1, 1);