diff --git a/.travis.yml b/.travis.yml index 5380ef54d3d..eef14f01386 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,10 +72,6 @@ jobs: if: type = pull_request OR type = push php: '8.1' env: DB=mysql - - stage: PHP Dev - if: type = push AND branch = develop - php: nightly - env: DB=mysql - stage: PHP Dev if: type = push AND branch = 17.0 php: nightly diff --git a/htdocs/admin/system/security.php b/htdocs/admin/system/security.php index 90bb35320c0..15db7d2ba0d 100644 --- a/htdocs/admin/system/security.php +++ b/htdocs/admin/system/security.php @@ -241,14 +241,41 @@ print '
'; print '
'; $installlock = DOL_DATA_ROOT.'/install.lock'; +$upgradeunlock = DOL_DATA_ROOT.'/upgrade.unlock'; +$installmoduleslock = DOL_DATA_ROOT.'/installmodules.lock'; + +// Is install (upgrade) locked print ''.$langs->trans("DolibarrSetup").': '; if (file_exists($installlock)) { - print img_picto('', 'tick').' '.$langs->trans("InstallAndUpgradeLockedBy", $installlock); + if (file_exists($upgradeunlock)) { + print img_picto('', 'tick').' '.$langs->trans("InstallLockedBy", $installlock); + } else { + print img_picto('', 'tick').' '.$langs->trans("InstallAndUpgradeLockedBy", $installlock); + } } else { print img_warning().' '.$langs->trans("WarningLockFileDoesNotExists", DOL_DATA_ROOT); } print '
'; +// Is upgrade unlocked +if (file_exists($installlock)) { // If install not locked, no need to show this. + if (file_exists($upgradeunlock)) { + print ''.$langs->trans("DolibarrUpgrade").': '; + print img_warning().' '.$langs->trans("UpgradeHasBeenUnlocked", $upgradeunlock); + print '
'; + } +} + +// Is addon install locked ? +print ''.$langs->trans("DolibarrAddonInstall").': '; +if (file_exists($installmoduleslock)) { + print img_picto('', 'tick').' '.$langs->trans("InstallAndUpgradeLockedBy", $installmoduleslock); +} else { + print $langs->trans("InstallOfAddonIsNotBlocked", DOL_DATA_ROOT); +} +print '
'; + + // File conf.php @@ -286,7 +313,7 @@ if (empty($dolibarr_main_restrict_os_commands)) { } else { print $dolibarr_main_restrict_os_commands; } -print ' ('.$langs->trans("RecommendedValueIs", 'mysqldump, mysql, pg_dump, pgrestore').')'; +print ' ('.$langs->trans("RecommendedValueIs", 'mysqldump, mysql, pg_dump, pgrestore, clamdscan').')'; print '
'; if (empty($conf->global->SECURITY_DISABLE_TEST_ON_OBFUSCATED_CONF)) { diff --git a/htdocs/categories/class/categorie.class.php b/htdocs/categories/class/categorie.class.php index 5f1b081c46d..6762770d564 100644 --- a/htdocs/categories/class/categorie.class.php +++ b/htdocs/categories/class/categorie.class.php @@ -845,10 +845,12 @@ class Categorie extends CommonObject * @param int $offset Offset * @param string $sortfield Sort fields * @param string $sortorder Sort order ('ASC' or 'DESC'); + * @param array $filter Filter array. Example array('field'=>'valueforlike', 'customsql'=>...) + * @param string $filtermode Filter mode (AND or OR) * @return array|int -1 if KO, array of instance of object if OK * @see containsObject() */ - public function getObjectsInCateg($type, $onlyids = 0, $limit = 0, $offset = 0, $sortfield = '', $sortorder = 'ASC') + public function getObjectsInCateg($type, $onlyids = 0, $limit = 0, $offset = 0, $sortfield = '', $sortorder = 'ASC', $filter = array(), $filtermode = 'AND') { global $user; @@ -867,10 +869,24 @@ class Categorie extends CommonObject if (($type == 'customer' || $type == 'supplier') && $user->socid > 0) { $sql .= " AND o.rowid = ".((int) $user->socid); } + // Manage filter + $sqlwhere = array(); + if (count($filter) > 0) { + foreach ($filter as $key => $value) { + if ($key == 'o.rowid') { + $sqlwhere[] = $key." = ".((int) $value); + } elseif ($key == 'customsql') { + $sqlwhere[] = $value; + } + } + } + if (count($sqlwhere) > 0) { + $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")"; + } + $sql .= $this->db->order($sortfield, $sortorder); if ($limit > 0 || $offset > 0) { $sql .= $this->db->plimit($limit + 1, $offset); } - $sql .= $this->db->order($sortfield, $sortorder); dol_syslog(get_class($this)."::getObjectsInCateg", LOG_DEBUG); $resql = $this->db->query($sql); diff --git a/htdocs/compta/bank/class/paymentvarious.class.php b/htdocs/compta/bank/class/paymentvarious.class.php index 705ff2d20ca..912f46e074a 100644 --- a/htdocs/compta/bank/class/paymentvarious.class.php +++ b/htdocs/compta/bank/class/paymentvarious.class.php @@ -828,4 +828,36 @@ class PaymentVarious extends CommonObject $return .= ''; return $return; } + + /** + * Return General accounting account with defined length (used for product and miscellaneous) + * + * @param string $account General accounting account + * @return string String with defined length + */ + public function lengthAccountg($account) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; + + /* + if (isModEnabled('accounting')) { + $accountingaccount = new AccountingAccount($db); + $accountingaccount->fetch('', $valuetoshow, 1); + }*/ + + return length_accountg($account); + } + + /** + * Return Auxiliary accounting account of thirdparties with defined length + * + * @param string $account Auxiliary accounting account + * @return string String with defined length + */ + public function lengthAccounta($account) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; + + return length_accounta($account); + } } diff --git a/htdocs/compta/bank/various_payment/card.php b/htdocs/compta/bank/various_payment/card.php index 1eb552fd711..7fbf2774e85 100644 --- a/htdocs/compta/bank/various_payment/card.php +++ b/htdocs/compta/bank/various_payment/card.php @@ -1,6 +1,7 @@ * Copyright (C) 2018-2020 Frédéric France + * Copyright (C) 2023 Laurent Destailleur * * 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 @@ -71,6 +72,8 @@ $object = new PaymentVarious($db); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('variouscard', 'globalcard')); +$permissiontoadd = $user->hasRight('banque', 'modifier'); + /** * Actions @@ -83,14 +86,8 @@ if ($reshook < 0) { } if (empty($reshook)) { - // Link to a project - if ($action == 'classin' && $user->rights->banque->modifier) { - $object->fetch($id); - $object->setProject(GETPOST('projectid')); - } - if ($cancel) { - if ($action != 'addlink') { + if ($action != 'addlink' && $action != 'setaccountancy_code' && $action != 'setsubledger_account') { $urltogo = $backtopage ? $backtopage : dol_buildpath('/compta/bank/various_payment/list.php', 1); header("Location: ".$urltogo); exit; @@ -101,6 +98,12 @@ if (empty($reshook)) { $action = ''; } + // Link to a project + if ($action == 'classin' && $permissiontoadd) { + $object->fetch($id); + $object->setProject(GETPOST('projectid', 'int')); + } + if ($action == 'add') { $error = 0; @@ -214,6 +217,22 @@ if (empty($reshook)) { } } + if ($action == 'setaccountancy_code') { + $db->begin(); + + $result = $object->fetch($id); + + $object->accountancy_code = GETPOST('accountancy_code', 'alpha'); + + $res = $object->update($user); + if ($res > 0) { + $db->commit(); + } else { + $db->rollback(); + setEventMessages($object->error, $object->errors, 'errors'); + } + } + if ($action == 'setsubledger_account') { $db->begin(); @@ -236,7 +255,7 @@ if ($action == 'confirm_clone' && $confirm != 'yes') { $action = ''; } -if ($action == 'confirm_clone' && $confirm == 'yes' && ($user->rights->banque->modifier)) { +if ($action == 'confirm_clone' && $confirm == 'yes' && $permissiontoadd) { $db->begin(); $originalId = $id; @@ -560,32 +579,25 @@ if ($id) { // Project if (isModEnabled('project')) { $langs->load("projects"); - $morehtmlref .= $langs->trans('Project').' '; - if ($user->rights->banque->modifier) { + //$morehtmlref .= '
'; + if ($permissiontoadd) { + $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"'); if ($action != 'classify') { - $morehtmlref .= ''.img_edit($langs->transnoentitiesnoconv('SetProject')).' : '; - } - if ($action == 'classify') { - //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); - $morehtmlref .= '
'; - $morehtmlref .= ''; - $morehtmlref .= ''; - $morehtmlref .= $formproject->select_projects(0, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1); - $morehtmlref .= ''; - $morehtmlref .= '
'; - } else { - $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1, '', 'maxwidth300'); + $morehtmlref .= ''.img_edit($langs->transnoentitiesnoconv('SetProject')).' '; } + $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->socid, $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300'); } else { if (!empty($object->fk_project)) { $proj = new Project($db); $proj->fetch($object->fk_project); $morehtmlref .= $proj->getNomUrl(1); - } else { - $morehtmlref .= ''; + if ($proj->title) { + $morehtmlref .= ' - '.dol_escape_htmltag($proj->title).''; + } } } } + $morehtmlref .= ''; $linkback = ''.$langs->trans("BackToList").''; @@ -622,25 +634,24 @@ if ($id) { print ''.$langs->trans("Amount").''.price($object->amount, 0, $langs, 1, -1, -1, $conf->currency).''; - // Accountancy code - print ''; - print $langs->trans("AccountAccounting"); - print ''; + // Account of Chart of account + $editvalue = ''; if (isModEnabled('accounting')) { - $accountingaccount = new AccountingAccount($db); - $accountingaccount->fetch('', $object->accountancy_code, 1); - - print $accountingaccount->getNomUrl(0, 1, 1, '', 1); - } else { - print $object->accountancy_code; + $editvalue = $formaccounting->select_account($object->accountancy_code, 'accountancy_code', 1, null, 1, 1); } + + print ''; + print ''; + print $form->editfieldkey('AccountAccounting', 'accountancy_code', $object->accountancy_code, $object, (!$alreadyaccounted && $permissiontoadd), 'string', '', 0); + print ''; + print $form->editfieldval('AccountAccounting', 'accountancy_code', $object->accountancy_code, $object, (!$alreadyaccounted && $permissiontoadd), 'asis', $editvalue, 0, null, '', 1, 'lengthAccountg'); print ''; // Subledger account print ''; - print $form->editfieldkey('SubledgerAccount', 'subledger_account', $object->subledger_account, $object, (!$alreadyaccounted && $user->rights->banque->modifier), 'string', '', 0); + print $form->editfieldkey('SubledgerAccount', 'subledger_account', $object->subledger_account, $object, (!$alreadyaccounted && $permissiontoadd), 'string', '', 0); print ''; - print $form->editfieldval('SubledgerAccount', 'subledger_account', $object->subledger_account, $object, (!$alreadyaccounted && $user->rights->banque->modifier), 'string', '', 0); + print $form->editfieldval('SubledgerAccount', 'subledger_account', $object->subledger_account, $object, (!$alreadyaccounted && $permissiontoadd), 'string', '', 0, null, '', 1, 'lengthAccounta'); print ''; $bankaccountnotfound = 0; @@ -689,13 +700,13 @@ if ($id) { // Add button modify // Clone - if ($user->rights->banque->modifier) { + if ($permissiontoadd) { print '"; } // Delete if (empty($object->rappro) || $bankaccountnotfound) { - if (!empty($user->rights->banque->modifier)) { + if ($permissiontoadd) { if ($alreadyaccounted) { print ''; } else { diff --git a/htdocs/compta/bank/various_payment/document.php b/htdocs/compta/bank/various_payment/document.php index 43259727917..58e4d3680b9 100644 --- a/htdocs/compta/bank/various_payment/document.php +++ b/htdocs/compta/bank/various_payment/document.php @@ -100,7 +100,6 @@ if ($object->id) { // Project if (isModEnabled('project')) { $langs->load("projects"); - $morehtmlref .= $langs->trans('Project').' : '; if ($user->rights->banque->modifier && 0) { if ($action != 'classify') { $morehtmlref .= ''.img_edit($langs->transnoentitiesnoconv('SetProject')).' : '; diff --git a/htdocs/compta/bank/various_payment/info.php b/htdocs/compta/bank/various_payment/info.php index ca31915d2f4..06f243a9847 100644 --- a/htdocs/compta/bank/various_payment/info.php +++ b/htdocs/compta/bank/various_payment/info.php @@ -60,7 +60,6 @@ $morehtmlref = '
'; // Project if (isModEnabled('project')) { $langs->load("projects"); - $morehtmlref .= $langs->trans('Project').' : '; if ($user->rights->banque->modifier && 0) { if ($action != 'classify') { $morehtmlref .= ''.img_edit($langs->transnoentitiesnoconv('SetProject')).' : '; diff --git a/htdocs/compta/facture/card-rec.php b/htdocs/compta/facture/card-rec.php index ce7bf072073..a2a87d6d64a 100644 --- a/htdocs/compta/facture/card-rec.php +++ b/htdocs/compta/facture/card-rec.php @@ -3,7 +3,7 @@ * Copyright (C) 2004-2016 Laurent Destailleur * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2013 Florian Henry - * Copyright (C) 2013 Juanjo Menent + * Copyright (C) 2013-2023 Juanjo Menent * Copyright (C) 2015 Jean-François Ferry * Copyright (C) 2012 Cedric Salvador * Copyright (C) 2015 Alexandre Spangaro @@ -437,6 +437,9 @@ if (empty($reshook)) { $qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS', 2); $remise_percent = price2num(GETPOST('remise_percent'.$predef), '', 2); + if (empty($remise_percent)) { + $remise_percent = 0; + } // Extrafields $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line); @@ -784,6 +787,9 @@ if (empty($reshook)) { }*/ $remise_percent = price2num(GETPOST('remise_percent'), '', 2); + if (empty($remise_percent)) { + $remise_percent = 0; + } // Check minimum price $productid = GETPOST('productid', 'int'); diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index 591601dbf52..a4e7bd52430 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -3578,6 +3578,7 @@ class Facture extends CommonInvoice /** * Add an invoice line into database (linked to product/service or not). + * Note: ->thirdparty must be defined. * Les parametres sont deja cense etre juste et avec valeurs finales a l'appel * de cette methode. Aussi, pour le taux tva, il doit deja avoir ete defini * par l'appelant par la methode get_default_tva(societe_vendeuse,societe_acheteuse,produit) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index ee8806f267c..c85f66ca2c6 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -12,7 +12,7 @@ * Copyright (C) 2017 ATM Consulting * Copyright (C) 2017-2019 Nicolas ZABOURI * Copyright (C) 2017 Rui Strecht - * Copyright (C) 2018-2021 Frédéric France + * Copyright (C) 2018-2023 Frédéric France * Copyright (C) 2018 Josep Lluís Amador * Copyright (C) 2023 Gauthier VERDOL * Copyright (C) 2021 Grégory Blémand @@ -183,6 +183,7 @@ abstract class CommonObject public $fk_project; /** + * @var Project The related project object * @deprecated * @see project */ diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 022b2f40914..b31d444eff8 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -191,23 +191,23 @@ class Form /** * Output value of a field for an editable field * - * @param string $text Text of label (not used in this function) - * @param string $htmlname Name of select field - * @param string $value Value to show/edit - * @param object $object Object (that we want to show) - * @param boolean $perm Permission to allow button to edit parameter - * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols%', 'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datehourpicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select;xkey:xval,ykey:yval,...') - * @param string $editvalue When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of numeric value). Use '' to use same than $value - * @param object $extObject External object ??? - * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage') - * @param string $moreparam More param to add on the form on action href URL parameter - * @param int $notabletag Do no output table tags - * @param string $formatfunc Call a specific function to output field in view mode (For example: 'dol_print_email') - * @param string $paramid Key of parameter for id ('id', 'socid') - * @param string $gm 'auto' or 'tzuser' or 'tzuserrel' or 'tzserver' (when $typeofdata is a date) - * @param array $moreoptions Array with more options. For example array('addnowlink'=>1), array('valuealreadyhtmlescaped'=>1) - * @param string $editaction [=''] use GETPOST default action or set action to edit mode - * @return string HTML edit field + * @param string $text Text of label (not used in this function) + * @param string $htmlname Name of select field + * @param string $value Value to show/edit + * @param object $object Object (that we want to show) + * @param boolean $perm Permission to allow button to edit parameter + * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols%', 'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datehourpicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select;xkey:xval,ykey:yval,...') + * @param string $editvalue When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of numeric value, or a select combo). Use '' to use same than $value + * @param object $extObject External object ??? + * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage') + * @param string $moreparam More param to add on the form on action href URL parameter + * @param int $notabletag Do no output table tags + * @param string $formatfunc Call a specific method of $object->$formatfunc to output field in view mode (For example: 'dol_print_email') + * @param string $paramid Key of parameter for id ('id', 'socid') + * @param string $gm 'auto' or 'tzuser' or 'tzuserrel' or 'tzserver' (when $typeofdata is a date) + * @param array $moreoptions Array with more options. For example array('addnowlink'=>1), array('valuealreadyhtmlescaped'=>1) + * @param string $editaction [=''] use GETPOST default action or set action to edit mode + * @return string HTML edit field */ public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 1, $formatfunc = '', $paramid = 'id', $gm = 'auto', $moreoptions = array(), $editaction = '') { @@ -310,6 +310,8 @@ class Form require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; $doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), (empty($tmp[2]) ? '' : $tmp[2]), (empty($tmp[3]) ? '100' : $tmp[3]), (empty($tmp[1]) ? 'dolibarr_notes' : $tmp[1]), 'In', (empty($tmp[5]) ? 0 : $tmp[5]), (isset($tmp[8]) ? ($tmp[8] ? true : false) : true), true, (empty($tmp[6]) ? '20' : $tmp[6]), (empty($tmp[7]) ? '100' : $tmp[7])); $ret .= $doleditor->Create(1); + } elseif ($typeofdata == 'asis') { + $ret .= ($editvalue ? $editvalue : $value); } if (empty($notabletag)) { $ret .= ''; @@ -7970,7 +7972,13 @@ class Form } // Add where from hooks - $parameters = array(); + $parameters = array( + 'object' => $objecttmp, + 'htmlname' => $htmlname, + 'filter' => $filter, + 'searchkey' => $searchkey + ); + $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook if (!empty($hookmanager->resPrint)) { $sql .= $hookmanager->resPrint; diff --git a/htdocs/core/class/html.formcompany.class.php b/htdocs/core/class/html.formcompany.class.php index e2a1cafcb3d..d2fda62ae97 100644 --- a/htdocs/core/class/html.formcompany.class.php +++ b/htdocs/core/class/html.formcompany.class.php @@ -989,15 +989,15 @@ class FormCompany extends Form // phpcs:enable $tax = get_localtax_by_third($local); - $num = $this->db->num_rows($tax); - $i = 0; - if ($num) { + if ($tax) { $valors = explode(":", $tax); + $nbvalues = count($valors); - if (count($valors) > 1) { + if ($nbvalues > 1) { //montar select print ''; + print ''; } } } diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index c5fca0eff5c..24dafa06f69 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -6190,30 +6190,30 @@ function isOnlyOneLocalTax($local) /** * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value * - * @param int $local LocalTax to get - * @return number Values of localtax + * @param int $local LocalTax to get + * @return string Values of localtax (Can be '20', '-19:-15:-9') */ function get_localtax_by_third($local) { global $db, $mysoc; - $sql = "SELECT t.localtax1, t.localtax2 "; - $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays"; - $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux=("; - $sql .= " SELECT max(tt.taux) FROM ".MAIN_DB_PREFIX."c_tva as tt inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=tt.fk_pays"; - $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1"; - $sql .= " )"; + + $sql = " SELECT t.localtax".$local." as localtax"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays"; + $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux = ("; + $sql .= "SELECT MAX(tt.taux) FROM ".MAIN_DB_PREFIX."c_tva as tt INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = tt.fk_pays"; + $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1)"; + $sql .= " AND t.localtax".$local."_type <> '0'"; + $sql .= " ORDER BY t.rowid DESC"; $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); - if ($local == 1) { - return $obj->localtax1; - } elseif ($local == 2) { - return $obj->localtax2; - } + return $obj->localtax; + } else { + return 'Error'; } - return 0; + return '0'; } @@ -6322,10 +6322,12 @@ function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisi } $sql .= ", ".MAIN_DB_PREFIX."c_country as c"; - if ($mysoc->country_code == 'ES') { - $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // local tax in spain use the buyer country ?? + if (!empty($mysoc) && $mysoc->country_code == 'ES') { + $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code); + $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ?? } else { - $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape(empty($seller->country_code) ? $mysoc->country_code : $seller->country_code)."'"; + $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code); + $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; } $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; if ($vatratecode) { diff --git a/htdocs/install/mysql/migration/14.0.0-15.0.0.sql b/htdocs/install/mysql/migration/14.0.0-15.0.0.sql index 9bfdc55a7b0..30af80d1f8e 100644 --- a/htdocs/install/mysql/migration/14.0.0-15.0.0.sql +++ b/htdocs/install/mysql/migration/14.0.0-15.0.0.sql @@ -561,3 +561,6 @@ ALTER TABLE llx_bank ADD COLUMN amount_main_currency double(24,8) NULL; ALTER TABLE llx_commande_fournisseurdet MODIFY COLUMN ref varchar(128); ALTER TABLE llx_facture_fourn_det MODIFY COLUMN ref varchar(128); + +UPDATE llx_c_tva SET localtax2 = '-19:-15:-9' WHERE localtax2 = '-19' AND localtax2_type = '5' AND fk_pays = 4 AND taux = 21; + diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 0214b73292d..484e207e347 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -51,6 +51,8 @@ ClientSortingCharset=Client collation WarningModuleNotActive=Module %s must be enabled WarningOnlyPermissionOfActivatedModules=Only permissions related to activated modules are shown here. You can activate other modules in the Home->Setup->Modules page. DolibarrSetup=Dolibarr install or upgrade +DolibarrUpgrade=Dolibarr upgrade +DolibarrAddonInstall=Installation of Addon/External modules (uploaded or generated) InternalUsers=Internal users ExternalUsers=External users UserInterface=User interface @@ -2264,6 +2266,8 @@ DatabasePasswordNotObfuscated=Database password is NOT obfuscated in conf file APIsAreNotEnabled=APIs modules are not enabled YouShouldSetThisToOff=You should set this to 0 or off InstallAndUpgradeLockedBy=Install and upgrades are locked by the file %s +InstallLockedBy=Install/Reinstall is locked by the file %s +InstallOfAddonIsNotBlocked=Installations of addons are not locked. Create a file installmodules.lock into directory %s to block installations of external addons/modules. OldImplementation=Old implementation PDF_SHOW_LINK_TO_ONLINE_PAYMENT=If some online payment modules are enabled (Paypal, Stripe, ...), add a link on the PDF to make the online payment DashboardDisableGlobal=Disable globally all the thumbs of open objects diff --git a/htdocs/product/dynamic_price/class/price_parser.class.php b/htdocs/product/dynamic_price/class/price_parser.class.php index 412d015f8b9..47e290677c6 100644 --- a/htdocs/product/dynamic_price/class/price_parser.class.php +++ b/htdocs/product/dynamic_price/class/price_parser.class.php @@ -124,15 +124,15 @@ class PriceParser */ public function parseExpression($product, $expression, $values) { - global $user; - global $hookmanager; + global $user, $hookmanager, $extrafields; + $action = 'PARSEEXPRESSION'; - if ($result = $hookmanager->executeHooks('doDynamiPrice', array( - 'expression' =>$expression, - 'product' => $product, - 'values' => $values + if ($reshook = $hookmanager->executeHooks('doDynamiPrice', array( + 'expression' => &$expression, + 'product' => &$product, + 'values' => &$values ), $this, $action)) { - return $result; + return $hookmanager->resArray['return']; } //Check if empty $expression = trim($expression); @@ -154,8 +154,12 @@ class PriceParser "pmp" => $product->pmp, )); - //Retrieve all extrafield for product and add it to values - $extrafields = new ExtraFields($this->db); + // Retrieve all extrafields if not already not know (should not happen) + if (! is_object($extrafields)) { + $extrafields = new ExtraFields($this->db); + $extrafields->fetch_name_optionals_label(); + } + $product->fetch_optionals(); if (is_array($extrafields->attributes[$product->table_element]['label'])) { foreach ($extrafields->attributes[$product->table_element]['label'] as $key => $label) { diff --git a/htdocs/product/list.php b/htdocs/product/list.php index ed0e0d22c91..670e77ad4d7 100644 --- a/htdocs/product/list.php +++ b/htdocs/product/list.php @@ -3,7 +3,7 @@ * Copyright (C) 2004-2019 Laurent Destailleur * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2012-2016 Marcos García - * Copyright (C) 2013-2019 Juanjo Menent + * Copyright (C) 2013-2023 Juanjo Menent * Copyright (C) 2013-2015 Raphaël Doursenaud * Copyright (C) 2013 Jean Heimburger * Copyright (C) 2013 Cédric Salvador @@ -1374,6 +1374,7 @@ while ($i < $imaxinloop) { $product_static->ref_fourn = empty($obj->ref_supplier) ? '' : $obj->ref_supplier; // deprecated $product_static->ref_supplier = empty($obj->ref_supplier) ? '' : $obj->ref_supplier; $product_static->label = $obj->label; + $product_static->barcode = $obj->barcode; $product_static->finished = $obj->finished; $product_static->type = $obj->fk_product_type; $product_static->status_buy = $obj->tobuy; diff --git a/htdocs/product/reassort.php b/htdocs/product/reassort.php index bfe12d7efe2..02d8e6e2002 100644 --- a/htdocs/product/reassort.php +++ b/htdocs/product/reassort.php @@ -151,6 +151,10 @@ $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as s ON p.rowid = s.fk_produ if (!empty($conf->global->PRODUCT_USE_UNITS)) { $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_units as u on p.fk_unit = u.rowid'; } +// Add table from hooks +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql .= $hookmanager->resPrint; $sql .= " WHERE p.entity IN (".getEntity('product').")"; if (!empty($search_categ) && $search_categ != '-1') { $sql .= " AND "; @@ -207,10 +211,12 @@ $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $objec $sql .= $hookmanager->resPrint; $sql .= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,"; $sql .= " p.fk_product_type, p.tms, p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock"; -// Add fields from hooks + +// Add GROUP BY from hooks $parameters = array(); -$reshook = $hookmanager->executeHooks('printFieldSelect', $parameters, $object); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldListGroupBy', $parameters, $object); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; + $sql_having = ''; if ($search_toolowstock) { $sql_having .= " HAVING SUM(".$db->ifsql('s.reel IS NULL', '0', 's.reel').") < p.seuil_stock_alerte"; @@ -226,6 +232,19 @@ if ($search_stock_physique != '') { } $sql_having .= $natural_search_physique; } + +// Add HAVING from hooks +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters, $object); // Note that $action and $object may have been modified by hook +if (!empty($hookmanager->resPrint)) { + if (!empty($sql_having)) { + $sql_having .= " AND"; + } else { + $sql_having .= " HAVING"; + } + $sql_having .= $hookmanager->resPrint; +} + if (!empty($sql_having)) { $sql .= $sql_having; } diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php index c823379ce31..d24b5282d11 100644 --- a/htdocs/product/reassortlot.php +++ b/htdocs/product/reassortlot.php @@ -126,6 +126,9 @@ if (!empty($canvas)) { $objcanvas->getCanvas('product', 'list', $canvas); } +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('productreassortlotlist')); + // Security check if ($user->socid) { $socid = $user->socid; @@ -222,11 +225,19 @@ $sql .= ' e.ref as warehouse_ref, e.lieu as warehouse_lieu, e.fk_parent as wareh $sql .= ' pb.batch, pb.eatby as oldeatby, pb.sellby as oldsellby,'; $sql .= ' pl.rowid as lotid, pl.eatby, pl.sellby,'; $sql .= ' SUM(pb.qty) as stock_physique, COUNT(pb.rowid) as nbinbatchtable'; +// Add fields from hooks +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql .= $hookmanager->resPrint; $sql .= ' FROM '.MAIN_DB_PREFIX.'product as p'; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as ps on p.rowid = ps.fk_product'; // Detail for each warehouse $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot as e on ps.fk_entrepot = e.rowid'; // Link on unique key $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb on pb.fk_product_stock = ps.rowid'; // Detail for each lot on each warehouse $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_lot as pl on pl.fk_product = p.rowid AND pl.batch = pb.batch'; // Link on unique key +// Add table from hooks +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql .= $hookmanager->resPrint; $sql .= " WHERE p.entity IN (".getEntity('product').") AND e.entity IN (".getEntity('stock').")"; if (!empty($search_categ) && $search_categ != '-1') { $sql .= " AND "; @@ -315,6 +326,10 @@ foreach ($search as $key => $val) { } } } +// Add where from hooks +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql .= $hookmanager->resPrint; $sql .= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,"; $sql .= " p.fk_product_type, p.tms,"; @@ -323,6 +338,10 @@ $sql .= " ps.fk_entrepot, ps.reel,"; $sql .= " e.ref, e.lieu, e.fk_parent,"; $sql .= " pb.batch, pb.eatby, pb.sellby,"; $sql .= " pl.rowid, pl.eatby, pl.sellby"; +// Add GROUP BY from hooks +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListGroupBy', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql .= $hookmanager->resPrint; $sql_having = ''; if ($search_toolowstock) { $sql_having .= " HAVING SUM(".$db->ifsql('ps.reel IS NULL', '0', 'ps.reel').") < p.seuil_stock_alerte"; // Not used yet @@ -337,6 +356,17 @@ if ($search_stock_physique != '') { } $sql_having .= $natural_search_physique; } +// Add HAVING from hooks +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters, $object); // Note that $action and $object may have been modified by hook +if (!empty($hookmanager->resPrint)) { + if (!empty($sql_having)) { + $sql_having .= " AND"; + } else { + $sql_having .= " HAVING"; + } + $sql_having .= $hookmanager->resPrint; +} if (!empty($sql_having)) { $sql .= $sql_having; } @@ -563,6 +593,9 @@ print ''; print ' '; print ' '; print ' '; +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; // Action column if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { print ''; @@ -602,6 +635,10 @@ print_liste_field_titre("PhysicalStock", $_SERVER["PHP_SELF"], "stock_physique", print_liste_field_titre(''); print_liste_field_titre("ProductStatusOnSell", $_SERVER["PHP_SELF"], "p.tosell", "", $param, '', $sortfield, $sortorder, 'right '); print_liste_field_titre("ProductStatusOnBuy", $_SERVER["PHP_SELF"], "p.tobuy", "", $param, '', $sortfield, $sortorder, 'right '); +// Hook fields +$parameters = array('param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder); +$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { print_liste_field_titre(''); } @@ -741,6 +778,11 @@ while ($i < $imaxinloop) { print ''.$product_static->LibStatut($objp->tobuy, 5, 1).''; + // Fields values from hook + $parameters = array('obj'=>$objp); + $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $product); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // Action column if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { print ''; diff --git a/htdocs/takepos/ajax/ajax.php b/htdocs/takepos/ajax/ajax.php index 87357d10091..5b75ef04275 100644 --- a/htdocs/takepos/ajax/ajax.php +++ b/htdocs/takepos/ajax/ajax.php @@ -62,6 +62,10 @@ $hookmanager->initHooks(array('takeposproductsearch')); // new context for produ */ if ($action == 'getProducts') { + $tosell = GETPOSTISSET('tosell') ? GETPOST('tosell', 'int') : ''; + $limit = GETPOSTISSET('limit') ? GETPOST('limit', 'int') : 0; + $offset = GETPOSTISSET('offset') ? GETPOST('offset', 'int') : 0; + top_httphead('application/json'); $object = new Categorie($db); @@ -70,7 +74,11 @@ if ($action == 'getProducts') { } $result = $object->fetch($category); if ($result > 0) { - $prods = $object->getObjectsInCateg("product", 0, 0, 0, getDolGlobalString('TAKEPOS_SORTPRODUCTFIELD'), 'ASC'); + $filter = array(); + if ($tosell != '') { + $filter = array('customsql' => 'o.tosell = '.((int) $tosell)); + } + $prods = $object->getObjectsInCateg("product", 0, $limit, $offset, getDolGlobalString('TAKEPOS_SORTPRODUCTFIELD'), 'ASC', $filter); // Removed properties we don't need $res = array(); if (is_array($prods) && count($prods) > 0) { diff --git a/htdocs/takepos/index.php b/htdocs/takepos/index.php index bbbf9e0cd33..84ec7439ca3 100644 --- a/htdocs/takepos/index.php +++ b/htdocs/takepos/index.php @@ -322,7 +322,12 @@ function LoadProducts(position, issubcat) { }); idata=0; //product data counter - $.getJSON('/takepos/ajax/ajax.php?action=getProducts&token=&category='+currentcat, function(data) { + var limit = 0; + if (maxproduct >= 1) { + limit = maxproduct-1; + } + // Only show products for sale (tosell=1) + $.getJSON('/takepos/ajax/ajax.php?action=getProducts&token=&category='+currentcat+'&tosell=1&limit='+limit+'&offset=0', function(data) { console.log("Call ajax.php (in LoadProducts) to get Products of category "+currentcat+" then loop on result to fill image thumbs"); console.log(data); while (ishow < maxproduct) { @@ -342,10 +347,7 @@ function LoadProducts(position, issubcat) { $("#proprice"+ishow).html(""); $("#prodiv"+ishow).data("rowid",""); $("#prodiv"+ishow).attr("class","wrapper2 divempty"); - $("#prowatermark"+ishow).hide(); - ishow++; //Next product to show after print data product - } - else if ((data[idata]['status']) == "1") { // Only show products with status=1 (for sell) + } else { transnoentities('Ref').': ')."' + data[idata]['ref']"; $titlestring .= " + ' - ".dol_escape_js($langs->trans("Barcode").': ')."' + data[idata]['barcode']"; @@ -376,8 +378,7 @@ function LoadProducts(position, issubcat) { console.log($('#prodiv4').data('rowid')); $("#prodiv"+ishow).data("iscat", 0); $("#prodiv"+ishow).attr("class","wrapper2"); - $("#prowatermark"+ishow).hide(); - ishow++; //Next product to show after print data product + resPrint; ?> } - //console.log("Hide the prowatermark for ishow="+ishow); + $("#prowatermark"+ishow).hide(); + ishow++; //Next product to show after print data product idata++; //Next data everytime } }); @@ -414,15 +416,22 @@ function MoreProducts(moreorless) { if (pageproducts==0) return; //Return if no less pages pageproducts=pageproducts-1; } - $.getJSON('/takepos/ajax/ajax.php?action=getProducts&token=&category='+currentcat, function(data) { + + ishow=0; //product to show counter + idata=0; //product data counter + var limit = 0; + if (maxproduct >= 1) { + limit = maxproduct-1; + } + var offset = * pageproducts; + // Only show products for sale (tosell=1) + $.getJSON('/takepos/ajax/ajax.php?action=getProducts&token=&category='+currentcat+'&tosell=1&limit='+limit+'&offset='+offset, function(data) { console.log("Call ajax.php (in MoreProducts) to get Products of category "+currentcat); - if (typeof (data[(maxproduct * pageproducts)]) == "undefined" && moreorless=="more"){ // Return if no more pages + if (typeof (data[0]) == "undefined" && moreorless=="more"){ // Return if no more pages pageproducts=pageproducts-1; return; } - idata= * pageproducts; //product data counter - ishow=0; //product to show counter while (ishow < maxproduct) { if (typeof (data[idata]) == "undefined") { @@ -434,10 +443,7 @@ function MoreProducts(moreorless) { $("#proprice"+ishow).html(""); $("#proimg"+ishow).attr("src","genimg/empty.png"); $("#prodiv"+ishow).data("rowid",""); - ishow++; //Next product to show after print data product - } - else if ((data[idata]['status']) == "1") { - //Only show products with status=1 (for sell) + } else { $("#prodivdesc"+ishow).show(); @@ -454,9 +460,9 @@ function MoreProducts(moreorless) { $("#proimg"+ishow).attr("src","genimg/index.php?query=pro&id="+data[idata]['id']); $("#prodiv"+ishow).data("rowid",data[idata]['id']); $("#prodiv"+ishow).data("iscat",0); - ishow++; //Next product to show after print data product } $("#prowatermark"+ishow).hide(); + ishow++; //Next product to show after print data product idata++; //Next data everytime } }); diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index 59357fec734..6c504e43729 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -1383,7 +1383,7 @@ class Ticket extends CommonObject public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0) { // phpcs:enable - global $langs; + global $langs, $hookmanager; $labelStatus = $this->statuts[$status]; $labelStatusShort = $this->statuts_short[$status]; @@ -1411,6 +1411,18 @@ class Ticket extends CommonObject $mode = 0; } + $parameters = array( + 'status' => $status, + 'mode' => $mode, + ); + + // Note that $action and $object may have been modified by hook + $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); + + if ($reshook > 0) { + return $hookmanager->resPrint; + } + $params = array(); if ($notooltip) { $params = array('tooltip' => 'no'); diff --git a/test/phpunit/FunctionsLibTest.php b/test/phpunit/FunctionsLibTest.php index f398aef1050..57b640cd606 100644 --- a/test/phpunit/FunctionsLibTest.php +++ b/test/phpunit/FunctionsLibTest.php @@ -80,6 +80,7 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase protected $savuser; protected $savlangs; protected $savdb; + protected $savmysoc; /** * Constructor @@ -92,11 +93,12 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase parent::__construct(); //$this->sharedFixture - global $conf,$user,$langs,$db; + global $conf,$user,$langs,$db,$mysoc; $this->savconf=$conf; $this->savuser=$user; $this->savlangs=$langs; $this->savdb=$db; + $this->savmysoc=$mysoc; print __METHOD__." db->type=".$db->type." user->id=".$user->id; //print " - db ".$db->db; @@ -142,17 +144,18 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase } /** - * Init phpunit tests + * Init phpunit tests. Restore variables before each test. * * @return void */ protected function setUp(): void { - global $conf,$user,$langs,$db; + global $conf,$user,$langs,$db,$mysoc; $conf=$this->savconf; $user=$this->savuser; $langs=$this->savlangs; $db=$this->savdb; + $mysoc=$this->savmysoc; print __METHOD__."\n"; } @@ -1233,7 +1236,6 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase // We do same tests but with option SERVICE_ARE_ECOMMERCE_200238EC on. $conf->global->SERVICE_ARE_ECOMMERCE_200238EC = 1; - // Test RULE 1 (FR-US) $vat=get_default_tva($companyfr, $companyus, 0); $this->assertEquals(0, $vat, 'RULE 1 ECOMMERCE_200238EC'); @@ -1256,7 +1258,7 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase } /** - * testGetDefaultTva + * testGetDefaultLocalTax * * @return void */ @@ -1343,6 +1345,27 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase } + /** + * testGetLocalTaxByThird + * + * @return void + */ + public function testGetLocalTaxByThird() + { + global $mysoc; + + $mysoc->country_code = 'ES'; + + $result = get_localtax_by_third(1); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('5.2', $result); + + $result = get_localtax_by_third(2); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('-19:-15:-9', $result); + } + + /** * testDolExplodeIntoArray * diff --git a/test/phpunit/NumberingModulesTest.php b/test/phpunit/NumberingModulesTest.php index b2b6285f9e3..e5d31df1cf7 100644 --- a/test/phpunit/NumberingModulesTest.php +++ b/test/phpunit/NumberingModulesTest.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2012-2023 Laurent Destailleur * * 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 @@ -152,6 +152,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $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 (usefull only if numbering is {0000@1} $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -165,8 +167,11 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $result=$localobject->is_erasable(); print __METHOD__." is_erasable=".$result."\n"; $this->assertGreaterThanOrEqual(1, $result, 'Test for is_erasable, 1st invoice'); // Can be deleted + $localobject2=new Facture($this->savdb); $localobject2->initAsSpecimen(); + $localobject2->fetch_thirdparty(); + $localobject2->date=dol_mktime(12, 0, 0, 1, 1, 1916); // we use following year for second invoice (there is no reset into mask) $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject2, 'last'); @@ -193,6 +198,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1910); // we use year 1910 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -200,15 +207,21 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $result3=$localobject->validate($user, $result); print __METHOD__." result=".$result."\n"; $this->assertEquals('1910-0001', $result, 'Test for {yyyy}-{0000@1} 1st invoice'); // counter must start to 1 + $localobject2=new Facture($this->savdb); $localobject2->initAsSpecimen(); + $localobject2->fetch_thirdparty(); + $localobject2->date=dol_mktime(12, 0, 0, 1, 1, 1910); // we use same year for second invoice (and there is a reset required) $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject2); print __METHOD__." result=".$result."\n"; $this->assertEquals('1910-0002', $result, 'Test for {yyyy}-{0000@1} 2nd invoice, same day'); // counter must be now 2 + $localobject3=new Facture($this->savdb); $localobject3->initAsSpecimen(); + $localobject3->fetch_thirdparty(); + $localobject3->date=dol_mktime(12, 0, 0, 1, 1, 1911); // we use next year for third invoice (and there is a reset required) $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject3); @@ -218,8 +231,11 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase // Same but we add month after year $conf->global->FACTURE_MERCURE_MASK_CREDIT='{yyyy}{mm}-{0000@1}'; $conf->global->FACTURE_MERCURE_MASK_INVOICE='{yyyy}{mm}-{0000@1}'; + $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1920); // we use year 1920 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -230,8 +246,11 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $result=$localobject->is_erasable(); print __METHOD__." is_erasable=".$result."\n"; $this->assertGreaterThanOrEqual(1, $result); // Can be deleted + $localobject2=new Facture($this->savdb); $localobject2->initAsSpecimen(); + $localobject2->fetch_thirdparty(); + $localobject2->date=dol_mktime(12, 0, 0, 1, 1, 1921); // we use following year for second invoice (and there is a reset required) $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject2); @@ -251,6 +270,7 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $conf->global->FACTURE_MERCURE_MASK_INVOICE='{mm}{yy}-{0000@1}'; $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1925); // we use year 1925 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -261,8 +281,10 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $result=$localobject->is_erasable(); // This call get getNextNumRef with param 'last' print __METHOD__." is_erasable=".$result."\n"; $this->assertGreaterThanOrEqual(1, $result); // Can be deleted + $localobject2=new Facture($this->savdb); $localobject2->initAsSpecimen(); + $localobject2->fetch_thirdparty(); $localobject2->date=dol_mktime(12, 0, 0, 1, 1, 1925); // we use same year 1925 for second invoice (and there is a reset required) $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject2); @@ -276,8 +298,10 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $result=$localobject->is_erasable(); print __METHOD__." is_erasable=".$result."\n"; $this->assertLessThanOrEqual(0, $result); // Case 1 can not be deleted (because there is an invoice 2) + $localobject3=new Facture($this->savdb); $localobject3->initAsSpecimen(); + $localobject3->fetch_thirdparty(); $localobject3->date=dol_mktime(12, 0, 0, 1, 1, 1926); // we use following year for third invoice (and there is a reset required) $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject3); @@ -295,6 +319,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1930); // we use year 1930 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject, 'last'); @@ -311,6 +337,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1930); // we use same year but fiscal month after $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject, 'last'); @@ -324,6 +352,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1931); // we use same fiscal year but different year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -334,6 +364,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1931); // we use different fiscal year but same year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -348,6 +380,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1940); // we use year 1940 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -358,6 +392,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1940); // we use same year but fiscal month after $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -368,6 +404,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1941); // we use same fiscal year but different year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -378,6 +416,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1941); // we use different discal year but same year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -392,6 +432,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1950); // we use year 1950 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -402,6 +444,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1950); // we use same year but fiscal month after $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -412,6 +456,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1951); // we use same fiscal year but different year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -422,6 +468,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1951); // we use different discal year but same year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -436,6 +484,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1960); // we use year 1960 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -446,6 +496,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1960); // we use same year but fiscal month after $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -456,6 +508,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1961); // we use same fiscal year but different year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -466,6 +520,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1961); // we use different discal year but same year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -480,6 +536,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1970); // we use year 1970 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -490,6 +548,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1970); // we use same year but fiscal month after $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -500,6 +560,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1971); // we use same fiscal year but different year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -510,6 +572,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 12, 1, 1971); // we use different fiscal year but same year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -523,6 +587,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1980); // we use year 1980 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -533,6 +599,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1980); // we use year 1980 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -543,6 +611,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 2, 1, 1980); // we use year 1980 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -553,6 +623,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1981); // we use year 1981 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); @@ -572,6 +644,8 @@ class NumberingModulesTest extends PHPUnit\Framework\TestCase $localobject=new Facture($this->savdb); $localobject->initAsSpecimen(); + $localobject->fetch_thirdparty(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1982); // we use year 1982 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($tmpthirdparty, $localobject);