diff --git a/doc/images/dolibarr_512x512.png b/doc/images/dolibarr_512x512.png new file mode 100644 index 00000000000..bd8d7ac3bfd Binary files /dev/null and b/doc/images/dolibarr_512x512.png differ diff --git a/htdocs/accountancy/class/accountancyexport.class.php b/htdocs/accountancy/class/accountancyexport.class.php index b4f420b262c..9845502321a 100644 --- a/htdocs/accountancy/class/accountancyexport.class.php +++ b/htdocs/accountancy/class/accountancyexport.class.php @@ -1016,14 +1016,13 @@ class AccountancyExport * Export format : Charlemagne * * @param array $objectLines data - * * @return void */ - public function exportCharlemagne($objectLines) + public function exportCharlemagne($objectLines) { global $langs; $langs->load('compta'); - + $separator = "\t"; $end_line = "\n"; @@ -1045,9 +1044,9 @@ class AccountancyExport print self::trunc($langs->transnoentitiesnoconv('Analytic') . ' 3', 15) . $separator; print self::trunc($langs->transnoentitiesnoconv('AnalyticLabel') . ' 3', 60) . $separator; print $end_line; - + foreach($objectLines as $line) { - + $date = dol_print_date($line->doc_date, '%Y%m%d'); print $date . $separator; //Date @@ -1069,7 +1068,6 @@ class AccountancyExport print $separator;//Analytic print $separator;//Analytic print $end_line; - } } diff --git a/htdocs/core/ajax/extraparams.php b/htdocs/core/ajax/extraparams.php index 7ef25e62b8b..e039529eef7 100644 --- a/htdocs/core/ajax/extraparams.php +++ b/htdocs/core/ajax/extraparams.php @@ -17,7 +17,7 @@ /** * \file /htdocs/core/ajax/extraparams.php - * \brief File to return Ajax response on set extra parameters of elements + * \brief File to make Ajax action on setting extra parameters of elements */ if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Disables token renewal diff --git a/htdocs/core/ajax/objectonoff.php b/htdocs/core/ajax/objectonoff.php index 6b06cccd50c..987a59ec3fb 100644 --- a/htdocs/core/ajax/objectonoff.php +++ b/htdocs/core/ajax/objectonoff.php @@ -15,8 +15,9 @@ */ /** - * \file htdocs/core/ajax/productonoff.php - * \brief File to set tosell and tobuy for product + * \file htdocs/core/ajax/objectonoff.php + * \brief File to set status for an object + * This Ajax service is called when option MAIN_DIRECT_STATUS_UPDATE is set. */ if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Disables token renewal @@ -36,6 +37,13 @@ $field=GETPOST('field', 'alpha'); $element=GETPOST('element', 'alpha'); $object = new GenericObject($db); + +// Security check +if (! empty($user->societe_id)) + $socid = $user->societe_id; + + + /* * View */ @@ -44,6 +52,20 @@ top_httphead(); print ''."\n"; +if ($element == 'societe' && in_array($field, array('status'))) +{ + $result = restrictedArea($user, 'societe', $id); +} +elseif ($element == 'product' && in_array($field, array('tosell', 'tobuy', 'tobatch'))) +{ + $result = restrictedArea($user, 'produit|service', $id, 'product&product', '', '', 'rowid'); +} +else +{ + accessforbidden("Bad value for combination of parameters element/field.", 0, 0, 1); + exit; +} + // Registering new values if (($action == 'set') && ! empty($id)) $object->setValueFrom($field, $value, $element, $id); diff --git a/htdocs/core/ajax/pingresult.php b/htdocs/core/ajax/pingresult.php new file mode 100644 index 00000000000..9b46546f5c3 --- /dev/null +++ b/htdocs/core/ajax/pingresult.php @@ -0,0 +1,72 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/core/ajax/pingresult.php + * \brief File to save result of anonymous ping + * Example: captureserver/public/index.php?action=dolibarrping + */ + +if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Disables token renewal +if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); +if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML', '1'); +if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1'); +if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC', '1'); +if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN', '1'); + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; + +$action=GETPOST('action', 'alpha'); +$hash_unique_id=GETPOST('hash_unique_id', 'alpha'); +$hash_algo=GETPOST('hash', 'alpha'); + + +// Security check +if (! empty($user->societe_id)) + $socid = $user->societe_id; + +$now = dol_now(); + + +/* + * View + */ + +top_httphead(); + +print ''."\n"; + +// If ok +if ($action == 'firstpingok') +{ + // Note: pings are by entities + dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_DATE', dol_print_date($now, 'dayhourlog', 'gmt')); + dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_ID', $hash_unique_id); + + print 'First ping OK saved for entity '.$conf->entity; +} +// If ko +elseif ($action == 'firstpingko') +{ + // Note: pings are by entities + dolibarr_set_const($db, 'MAIN_LAST_PING_KO_DATE', dol_print_date($now, 'dayhourlog'), 'gmt'); + print 'First ping KO saved for entity '.$conf->entity; +} +else { + print 'Error action='.$action.' not supported'; +} diff --git a/htdocs/core/ajax/security.php b/htdocs/core/ajax/security.php index 9e7dea2ef95..faaddd31b23 100644 --- a/htdocs/core/ajax/security.php +++ b/htdocs/core/ajax/security.php @@ -17,7 +17,7 @@ /** * \file htdocs/core/ajax/security.php - * \brief This ajax component is used to generated has keys for security purposes + * \brief This ajax component is used to generated hash keys for security purposes * like key to use into URL to protect them. */ diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index f69b4db68c1..e6d47c53d5f 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -2053,7 +2053,8 @@ abstract class CommonObject return -2; } } - + + /** * Change the retained warranty payments terms * @@ -2066,11 +2067,11 @@ abstract class CommonObject if ($this->statut >= 0 || $this->element == 'societe') { $fieldname = 'retained_warranty_fk_cond_reglement'; - + $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; $sql .= ' SET '.$fieldname.' = '.$id; $sql .= ' WHERE rowid='.$this->id; - + if ($this->db->query($sql)) { $this->retained_warranty_fk_cond_reglement = $id; @@ -5064,7 +5065,7 @@ abstract class CommonObject //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG); //dol_syslog("attributeType=".$attributeType, LOG_DEBUG); - + if (!empty($attrfieldcomputed)) { if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) @@ -6111,7 +6112,7 @@ abstract class CommonObject { $morecss = 'minwidth100imp'; } - elseif ($type == 'datetime') + elseif ($type == 'datetime' || $type == 'timestamp') { $morecss = 'minwidth200imp'; } @@ -6155,7 +6156,7 @@ abstract class CommonObject $value=''; } } - elseif ($type == 'datetime') + elseif ($type == 'datetime' || $type == 'timestamp') { if(! empty($value)) { $value=dol_print_date($value, 'dayhour'); @@ -7390,6 +7391,7 @@ abstract class CommonObject if (!empty($id)) $sql.= ' WHERE rowid = '.$id; elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']); else $sql.=' WHERE 1 = 1'; // usage with empty id and empty ref is very rare + if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql.=' AND entity IN ('.getEntity($this->table_element).')'; if ($morewhere) $sql.= $morewhere; $sql.=' LIMIT 1'; // This is a fetch, to be sure to get only one record diff --git a/htdocs/core/class/fileupload.class.php b/htdocs/core/class/fileupload.class.php index ee090fed65d..2f0ae34b529 100644 --- a/htdocs/core/class/fileupload.class.php +++ b/htdocs/core/class/fileupload.class.php @@ -17,7 +17,7 @@ */ /** - * \file htdocs/core/ajax/fileupload.php + * \file htdocs/core/ajax/fileupload.class.php * \brief File to return Ajax response on file upload */ diff --git a/htdocs/core/lib/ajax.lib.php b/htdocs/core/lib/ajax.lib.php index a96e63a4f43..43f4723d695 100644 --- a/htdocs/core/lib/ajax.lib.php +++ b/htdocs/core/lib/ajax.lib.php @@ -538,11 +538,12 @@ function ajax_constantonoff($code, $input = array(), $entity = null, $revertonof } /** - * On/off button for object + * On/off button to change status of an object + * This is called when MAIN_DIRECT_STATUS_UPDATE is set and it use tha ajax service objectonoff.php * * @param Object $object Object to set * @param string $code Name of constant : status or status_buy for product by example - * @param string $field Name of database field : tosell or tobuy for product by example + * @param string $field Name of database field : 'tosell' or 'tobuy' for product by example * @param string $text_on Text if on * @param string $text_off Text if off * @param array $input Array of type->list of CSS element to switch. Example: array('disabled'=>array(0=>'cssid')) diff --git a/htdocs/core/lib/security.lib.php b/htdocs/core/lib/security.lib.php index 051f3a5392a..2ee5a45c010 100644 --- a/htdocs/core/lib/security.lib.php +++ b/htdocs/core/lib/security.lib.php @@ -279,7 +279,7 @@ function restrictedArea($user, $features, $objectid = 0, $tableandshare = '', $f // Check write permission from module (we need to know write permission to create but also to delete drafts record) $createok=1; $nbko=0; - if (GETPOST('action', 'aZ09') == 'create' || ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete')) + if (GETPOST('action', 'aZ09') == 'create' || GETPOST('action', 'aZ09') == 'update' || ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete')) { foreach ($featuresarray as $feature) { @@ -329,7 +329,7 @@ function restrictedArea($user, $features, $objectid = 0, $tableandshare = '', $f // If a or and at least one ok if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) $createok=1; - if (GETPOST('action', 'aZ09') == 'create' && ! $createok) accessforbidden(); + if ((GETPOST('action', 'aZ09') == 'create' || GETPOST('action', 'aZ09') == 'update') && ! $createok) accessforbidden(); //print "Write access is ok"; } diff --git a/htdocs/expensereport/card.php b/htdocs/expensereport/card.php index a764e2df47c..c7cb9776aeb 100644 --- a/htdocs/expensereport/card.php +++ b/htdocs/expensereport/card.php @@ -996,6 +996,34 @@ if (empty($reshook)) } } + if ($action == 'set_unpaid' && $id > 0 && $user->rights->expensereport->to_paid) + { + $object = new ExpenseReport($db); + $object->fetch($id); + + $result = $object->set_unpaid($user); + + if ($result > 0) + { + // Define output language + if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) + { + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09'); + if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $object->thirdparty->default_lang; + if (! empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + $model=$object->modelpdf; + $ret = $object->fetch($id); // Reload to get new records + + $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } + } + if ($action == 'set_paid' && $id > 0 && $user->rights->expensereport->to_paid) { $object = new ExpenseReport($db); @@ -1679,7 +1707,7 @@ else if ($action == 'cancel') { - $array_input = array('text'=>$langs->trans("ConfirmCancelTrip"), array('type'=>"text",'label'=>''.$langs->trans("Comment").'','name'=>"detail_cancel",'size'=>"50",'value'=>"")); + $array_input = array('text'=>$langs->trans("ConfirmCancelTrip"), array('type'=>"text",'label'=>''.$langs->trans("Comment").'','name'=>"detail_cancel",'value'=>"")); $formconfirm=$form->formconfirm($_SEVER["PHP_SELF"]."?id=".$id, $langs->trans("Cancel"), "", "confirm_cancel", $array_input, "", 1); } @@ -1690,7 +1718,7 @@ else if ($action == 'refuse') // Deny { - $array_input = array('text'=>$langs->trans("ConfirmRefuseTrip"), array('type'=>"text",'label'=>$langs->trans("Comment"),'name'=>"detail_refuse",'size'=>"50",'value'=>"")); + $array_input = array('text'=>$langs->trans("ConfirmRefuseTrip"), array('type'=>"text",'label'=>$langs->trans("Comment"),'name'=>"detail_refuse",'value'=>"")); $formconfirm=$form->formconfirm($_SERVER["PHP_SELF"]."?id=".$id, $langs->trans("Deny"), '', "confirm_refuse", $array_input, "yes", 1); } @@ -2662,8 +2690,8 @@ if ($action != 'create' && $action != 'edit') } - // If status is Appoved - // -------------------- + // If status is Approved + // --------------------- if ($user->rights->expensereport->approve && $object->fk_statut == ExpenseReport::STATUS_APPROVED) { @@ -2707,9 +2735,15 @@ if ($action != 'create' && $action != 'edit') print '
id.'">'.$langs->trans('Cancel').'
'; } + if ($user->rights->expensereport->to_paid && $object->paid && $object->fk_statut == ExpenseReport::STATUS_CLOSED) + { + // Set unpaid + print '
id.'">'.$langs->trans('ClassifyUnPaid').'
'; + } + // Clone if ($user->rights->expensereport->creer) { - print '
' . $langs->trans("ToClone") . '
'; + print '
' . $langs->trans("ToClone") . '
'; } /* If draft, validated, cancel, and user can create, he can always delete its card before it is approved */ diff --git a/htdocs/expensereport/class/expensereport.class.php b/htdocs/expensereport/class/expensereport.class.php index 272125530e3..9b1a62ed402 100644 --- a/htdocs/expensereport/class/expensereport.class.php +++ b/htdocs/expensereport/class/expensereport.class.php @@ -1397,12 +1397,12 @@ class ExpenseReport extends CommonObject // phpcs:enable $error = 0; - if ($this->fk_c_deplacement_statuts != 5) + if ($this->paid) { $this->db->begin(); $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; - $sql.= " SET fk_statut = 5"; + $sql.= " SET paid = 0"; $sql.= ' WHERE rowid = '.$this->id; dol_syslog(get_class($this)."::set_unpaid sql=".$sql, LOG_DEBUG); diff --git a/htdocs/expensereport/payment/card.php b/htdocs/expensereport/payment/card.php index 73da11f4619..890884b2e37 100644 --- a/htdocs/expensereport/payment/card.php +++ b/htdocs/expensereport/payment/card.php @@ -286,7 +286,6 @@ else dol_print_error($db); } -print ''; /* diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index 5f4a9ff0bba..53535e58b46 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -95,6 +95,7 @@ PaymentHigherThanReminderToPay=Payment higher than reminder to pay HelpPaymentHigherThanReminderToPay=Attention, the payment amount of one or more bills is higher than the outstanding amount to pay.
Edit your entry, otherwise confirm and consider creating a credit note for the excess received for each overpaid invoice. HelpPaymentHigherThanReminderToPaySupplier=Attention, the payment amount of one or more bills is higher than the outstanding amount to pay.
Edit your entry, otherwise confirm and consider creating a credit note for the excess paid for each overpaid invoice. ClassifyPaid=Classify 'Paid' +ClassifyUnPaid=Classify 'Unpaid' ClassifyPaidPartially=Classify 'Paid partially' ClassifyCanceled=Classify 'Abandoned' ClassifyClosed=Classify 'Closed' diff --git a/htdocs/langs/fr_FR/bills.lang b/htdocs/langs/fr_FR/bills.lang index 41f5aca717c..ca251ed0d93 100644 --- a/htdocs/langs/fr_FR/bills.lang +++ b/htdocs/langs/fr_FR/bills.lang @@ -96,6 +96,7 @@ HelpPaymentHigherThanReminderToPay=Attention, le montant de paiement pour une ou HelpPaymentHigherThanReminderToPaySupplier=Attention, le montant de paiement pour une ou plusieurs factures est supérieur au reste à payer.
Corrigez votre saisie, sinon, confirmez et pensez à créer un avoir pour l'excédent pour chaque facture surpayée. ClassifyPaid=Classer 'Payée' ClassifyPaidPartially=Classer 'Payée partiellement' +ClassifyUnPaid=Classer 'Non payée' ClassifyCanceled=Classer 'Abandonnée' ClassifyClosed=Classer 'Fermée' ClassifyUnBilled=Classer 'Non facturée' diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index bf4c40a4176..7b983ca05eb 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -1124,6 +1124,7 @@ function top_httphead($contenttype = 'text/html', $forcenocache = 0) if ($contenttype == 'text/html' ) header("Content-Type: text/html; charset=".$conf->file->character_set_client); else header("Content-Type: ".$contenttype); + // Security options header("X-Content-Type-Options: nosniff"); // With the nosniff option, if the server says the content is text/html, the browser will render it as text/html (note that most browsers now force this option to on) if (! defined('XFRAMEOPTIONS_ALLOWALL')) header("X-Frame-Options: SAMEORIGIN"); // Frames allowed only if on same domain (stop some XSS attacks) @@ -2319,6 +2320,51 @@ if (! function_exists("llxFooter")) print "\n\n"; print ''."\n"; + // Add code for the asynchronous anonymous first ping (for telemetry) + if (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || GETPOST('forceping', 'alpha')) + { + if (empty($conf->global->MAIN_FIRST_PING_OK_DATE) + || (! empty($conf->file->instance_unique_id) && (md5($conf->file->instance_unique_id) != $conf->global->MAIN_FIRST_PING_OK_ID)) + || GETPOST('forceping', 'alpha')) + { + print "\n".''."\n"; + print "\n\n"; + ?> + + \n"; print "\n"; diff --git a/htdocs/modulebuilder/template/class/myobject.class.php b/htdocs/modulebuilder/template/class/myobject.class.php index d2bd1c6ac03..35f96b1588f 100644 --- a/htdocs/modulebuilder/template/class/myobject.class.php +++ b/htdocs/modulebuilder/template/class/myobject.class.php @@ -60,7 +60,7 @@ class MyObject extends CommonObject const STATUS_DRAFT = 0; const STATUS_VALIDATED = 1; - const STATUS_DISABLED = 9; + const STATUS_CANCELED = 9; /** @@ -388,7 +388,7 @@ class MyObject extends CommonObject $sql = 'SELECT '; $sql .= $this->getFieldList(); $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element. ' as t'; - if ($this->ismultientitymanaged) $sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')'; + if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')'; else $sql .= ' WHERE 1 = 1'; // Manage filter $sqlwhere = array(); diff --git a/htdocs/modulebuilder/template/myobject_list.php b/htdocs/modulebuilder/template/myobject_list.php index 5e6388d55f9..11fafd59ab5 100644 --- a/htdocs/modulebuilder/template/myobject_list.php +++ b/htdocs/modulebuilder/template/myobject_list.php @@ -135,7 +135,7 @@ $arrayfields=array(); foreach($object->fields as $key => $val) { // If $val['visible']==0, then we never show the field - if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>$val['enabled'], 'position'=>$val['position']); + if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>($val['enabled'] && ($val['visible'] != 3)), 'position'=>$val['position']); } // Extra fields if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) @@ -493,7 +493,7 @@ while ($i < min($num, $limit)) if (in_array($val['type'], array('timestamp'))) $cssforfield.=($cssforfield?' ':'').'nowrap'; elseif ($key == 'ref') $cssforfield.=($cssforfield?' ':'').'nowrap'; - if (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price'))) $cssforfield.=($cssforfield?' ':'').'right'; + if (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price')) && $key != 'status') $cssforfield.=($cssforfield?' ':'').'right'; if (! empty($arrayfields['t.'.$key]['checked'])) {