diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 6a34c482952..52a4e07644d 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -887,30 +887,32 @@ if (empty($reshook)) { // We check if no payment has been made if ($ventilExportCompta == 0) { if (getDolGlobalString('INVOICE_CAN_BE_EDITED_EVEN_IF_PAYMENT_DONE') || ($resteapayer == $object->total_ttc && empty($object->paye))) { + // Set invoice to draft status $result = $object->setDraft($user, $idwarehouse); + if ($result < 0) { setEventMessages($object->error, $object->errors, 'errors'); - } + } else { + // Define output language + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + $outputlangs = $langs; + $newlang = ''; + if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) { + $newlang = GETPOST('lang_id', 'aZ09'); + } + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + $outputlangs->load('products'); + } + $model = $object->model_pdf; + $ret = $object->fetch($id); // Reload to get new records - // Define output language - if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { - $outputlangs = $langs; - $newlang = ''; - if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) { - $newlang = GETPOST('lang_id', 'aZ09'); + $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref); } - if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { - $newlang = $object->thirdparty->default_lang; - } - if (!empty($newlang)) { - $outputlangs = new Translate("", $conf); - $outputlangs->setDefaultLang($newlang); - $outputlangs->load('products'); - } - $model = $object->model_pdf; - $ret = $object->fetch($id); // Reload to get new records - - $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref); } } } @@ -5071,7 +5073,7 @@ if ($action == 'create') { // Confirm back to draft status (action = 'modif') if ($action == 'modif') { $oktomodif = 1; // Assume we can modify by default - + /* $testvalue = $object->isEditable(); if ($testvalue < 0) { switch ($testvalue) { @@ -5111,7 +5113,7 @@ if ($action == 'create') { $oktomodif = 0; $action = ''; } - + */ if ($oktomodif) { $text = $langs->trans('ConfirmUnvalidateBill', $object->ref); $formquestion = array(); diff --git a/htdocs/core/class/commoninvoice.class.php b/htdocs/core/class/commoninvoice.class.php index f979fdf3967..0b697b57d77 100644 --- a/htdocs/core/class/commoninvoice.class.php +++ b/htdocs/core/class/commoninvoice.class.php @@ -783,9 +783,10 @@ abstract class CommonInvoice extends CommonObject } include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php'; - if (isALNERunningVersion()) { - $this->error = 'Action not allowed on the certified version'; + if (isALNERunningVersion() && !empty($this->module_source)) { + $this->error = 'Action to modify an invoice from an external module like the Point Of Sale is not allowed'; return -7; + // Note, edit status to draft is also blocked by trigger. } } diff --git a/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php index dc54f3751fd..3105788ba10 100644 --- a/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php +++ b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php @@ -64,6 +64,21 @@ class InterfaceActionsBlockedLog extends DolibarrTriggers return 0; // Module not active, we do nothing } + // Protect against modification of data that should be immutable on a validated invoice (memory test only, no database access) + // Not required, invoice is already protected by the status Validated + /* + if ($action === 'LINEBILL_INSERT' && in_array($object->element, array('invoiceline', 'facturedet'))) { + $invoice = new Facture($this->db); + $invoice->fetch($object->fk_facture); + + var_dump($invoice->status, $invoice->posmodule);exit; + $this->errors[] = 'Modifying the lines of invoice is not allowed'; + return -2; + } + var_dump($object->element);exit; + */ + + // List of mandatory logged actions $listofqualifiedelement = array('invoice', 'facture', 'don', 'payment', 'payment_donation', 'subscription', 'cashcontrol'); @@ -72,12 +87,13 @@ class InterfaceActionsBlockedLog extends DolibarrTriggers $listofqualifiedelement = array_merge($listofqualifiedelement, explode(',', getDolGlobalString('BLOCKEDLOG_ADD_ACTIONS_SUPPORTED'))); } - // Test if event/record is qualified + // Test if event/record is qualified for immutable logging // If custom actions are not set or if action not into custom actions, we can exclude action if object->element is not valid if (!is_object($object) || !property_exists($object, 'element') || !in_array($object->element, $listofqualifiedelement)) { return 1; } + // Refuse and cancel any trigger event if we are running a certified version without forcing https. // This is a security requirement for certification. We do this check before any other to avoid any risk of logging an event that should be blocked because of non respect of certification rules. global $dolibarr_main_force_https; @@ -88,6 +104,15 @@ class InterfaceActionsBlockedLog extends DolibarrTriggers return -1; } + + if ($action === 'BILL_UNVALIDATE') { + if ($object->isEditable() <= 0) { + $this->errors[] = 'Modifying this invoice is not allowed'; + return -2; + } + } + + /** @var Facture|Don|Paiement|PaymentDonation|Subscription|PaymentVarious|CashControl $object */ dol_syslog("Trigger '".$this->name."' for action '".$action."' launched by ".__FILE__.". id=".$object->id); diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 35b541fab3e..7d46df65c38 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -96,7 +96,7 @@ NotAvailableWhenAjaxDisabled=Not available when Ajax disabled AllowToSelectProjectFromOtherCompany=On document of a third party, can choose a project linked to another third party TimesheetPreventAfterFollowingMonths=Prevent recording time spent after the following number of months PROJECT_DISPLAY_LINKED_BY_CONTACT=Display projects linked to the thirdparty by a common contact -PROJECT_DISPLAY_LINKED_BY_CONTACT_help=This option adds a new list on the Project tab of each Trhid party, with all projects linked to the thirdparty via a contact relationship +PROJECT_DISPLAY_LINKED_BY_CONTACT_help=This option adds a new list on the Project tab of each third party, with all projects linked to the third party via a contact relationship JavascriptDisabled=JavaScript disabled UsePreviewTabs=Use preview tabs ShowPreview=Show preview diff --git a/htdocs/projet/admin/project.php b/htdocs/projet/admin/project.php index 09d4f1cba40..5f449686269 100644 --- a/htdocs/projet/admin/project.php +++ b/htdocs/projet/admin/project.php @@ -871,6 +871,7 @@ print ''; print '