From 8019f73f99975ee9fa41c453bf934cf940bf7192 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 5 Dec 2017 10:01:30 +0100 Subject: [PATCH] Fix: protection to not delete invoice if invoice is in bookkeeping --- htdocs/compta/facture/card.php | 30 ++++-- htdocs/compta/facture/class/facture.class.php | 79 ---------------- htdocs/core/class/commoninvoice.class.php | 94 +++++++++++++++++++ htdocs/fourn/facture/card.php | 23 ++++- htdocs/langs/en_US/bills.lang | 2 + 5 files changed, 139 insertions(+), 89 deletions(-) diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 11770f08b27..51703028142 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -3888,7 +3888,6 @@ else if ($id > 0 || ! empty($ref)) print ''; } print '' . price($sign * $objp->amount) . ''; - // TODO Add link to delete payment print ''; if ($object->statut == Facture::STATUS_VALIDATED && $object->paye == 0 && $user->societe_id == 0) { @@ -4380,13 +4379,30 @@ else if ($id > 0 || ! empty($ref)) // Delete if ($user->rights->facture->supprimer) { - if ($object->is_erasable() <= 0) { - print '
' . $langs->trans('Delete') . '
'; - } else if ($objectidnext) { - print '
' . $langs->trans('Delete') . '
'; - } elseif ($object->getSommePaiement()) { + $isErasable = $object->is_erasable(); + //var_dump($isErasable); + if ($isErasable == -4) { print '
' . $langs->trans('Delete') . '
'; - } else { + } + elseif ($isErasable == -3) { + print '
' . $langs->trans('Delete') . '
'; + } + elseif ($isErasable == -2) { + print '
' . $langs->trans('Delete') . '
'; + } + elseif ($isErasable == -1) { + print '
' . $langs->trans('Delete') . '
'; + } + elseif ($isErasable <= 0) // Any other cases + { + print '
' . $langs->trans('Delete') . '
'; + } + elseif ($objectidnext) + { + print '
' . $langs->trans('Delete') . '
'; + } + else + { print '
' . $langs->trans('Delete') . '
'; } } else { diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index e3d9ab1fde0..234b047464f 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -3367,85 +3367,6 @@ class Facture extends CommonInvoice } } - /** - * Renvoi si les lignes de facture sont ventilees et/ou exportees en compta - * - * @return int <0 if KO, 0=no, 1=yes - */ - function getVentilExportCompta() - { - // On verifie si les lignes de factures ont ete exportees en compta et/ou ventilees - $ventilExportCompta = 0 ; - $num=count($this->lines); - for ($i = 0; $i < $num; $i++) - { - if (! empty($this->lines[$i]->export_compta) && ! empty($this->lines[$i]->code_ventilation)) - { - $ventilExportCompta++; - } - } - - if ($ventilExportCompta <> 0) - { - return 1; - } - else - { - return 0; - } - } - - - /** - * Return if an invoice can be deleted - * Rule is: - * If invoice is draft and ha a temporary ref -> yes - * If hidden option INVOICE_CAN_ALWAYS_BE_REMOVED is on, we can. If hidden option INVOICE_CAN_NEVER_BE_REMOVED is on, we can't. - * If invoice has a definitive ref, is last, without payment and not dipatched into accountancy -> yes end of rule - * - * @return int <0 if KO, 0=no, >0=yes - */ - function is_erasable() - { - global $conf; - - // we check if invoice is a temporary number (PROVxxxx) - $tmppart = substr($this->ref, 1, 4); - - if ($this->statut == self::STATUS_DRAFT && $tmppart === 'PROV') // If draft invoice and ref not yet defined - { - return 1; - } - - if (! empty($conf->global->INVOICE_CAN_ALWAYS_BE_REMOVED)) return 2; - if (! empty($conf->global->INVOICE_CAN_NEVER_BE_REMOVED)) return 0; - - // TODO Test if there is at least one payment. If yes, refuse to delete. - // ... - - // If not a draft invoice and not temporary invoice - if ($tmppart !== 'PROV') - { - // We need to have this->thirdparty defined, in case of numbering rule use tags that depend on thirdparty (like {t} tag). - if (empty($this->thirdparty)) $this->fetch_thirdparty(); - - $maxfacnumber = $this->getNextNumRef($this->thirdparty,'last'); - $ventilExportCompta = $this->getVentilExportCompta(); - - // If there is no invoice into the reset range and not already dispatched, we can delete - if ($maxfacnumber == '' && $ventilExportCompta == 0) return 3; - // If invoice to delete is last one and not already dispatched, we can delete - if ($maxfacnumber == $this->ref && $ventilExportCompta == 0) return 4; - - if ($this->situation_cycle_ref) { - $last = $this->is_last_in_cycle(); - return $last; - } - } - - return 0; - } - /** * Return list of invoices (eventually filtered on a user) into an array diff --git a/htdocs/core/class/commoninvoice.class.php b/htdocs/core/class/commoninvoice.class.php index 16f0d7905a3..4c9eb7fdcaf 100644 --- a/htdocs/core/class/commoninvoice.class.php +++ b/htdocs/core/class/commoninvoice.class.php @@ -274,6 +274,100 @@ abstract class CommonInvoice extends CommonObject } } + /** + * Return if an invoice can be deleted + * Rule is: + * If invoice is draft and has a temporary ref -> yes + * If hidden option INVOICE_CAN_NEVER_BE_REMOVED is on -> no (0) + * If invoice is dispatched in bookkeeping -> no (-1) + * If invoice has a definitive ref, is not last and INVOICE_CAN_ALWAYS_BE_REMOVED off -> no (-2) + * If invoice not last in a cycle -> no (-3) + * If there is payment -> no (-4) + * + * @return int <=0 if no, >0 if yes + */ + function is_erasable() + { + global $conf; + + // We check if invoice is a temporary number (PROVxxxx) + $tmppart = substr($this->ref, 1, 4); + + if ($this->statut == self::STATUS_DRAFT && $tmppart === 'PROV') // If draft invoice and ref not yet defined + { + return 1; + } + + if (! empty($conf->global->INVOICE_CAN_NEVER_BE_REMOVED)) return 0; + + // If not a draft invoice and not temporary invoice + if ($tmppart !== 'PROV') + { + $ventilExportCompta = $this->getVentilExportCompta(); + if ($ventilExportCompta != 0) return -1; + + // Get last number of validated invoice + if ($this->element != 'invoice_supplier') + { + if (empty($this->thirdparty)) $this->fetch_thirdparty(); // We need to have this->thirdparty defined, in case of numbering rule use tags that depend on thirdparty (like {t} tag). + $maxfacnumber = $this->getNextNumRef($this->thirdparty,'last'); + + // If there is no invoice into the reset range and not already dispatched, we can delete + // If invoice to delete is last one and not already dispatched, we can delete + if (empty($conf->global->INVOICE_CAN_ALWAYS_BE_REMOVED) && $maxfacnumber != '' && $maxfacnumber != $this->ref) return -2; + + // TODO If there is payment in bookkeeping, check payment is not dispatched in accounting + // ... + + if ($this->situation_cycle_ref) { + $last = $this->is_last_in_cycle(); + if (! $last) return -3; + } + } + } + + // Test if there is at least one payment. If yes, refuse to delete. + if (empty($conf->global->INVOICE_CAN_ALWAYS_BE_REMOVED) && $this->getSommePaiement() > 0) return -4; + + return 1; + } + + /** + * Return if an invoice was dispatched in bookkeeping + * + * @return int <0 if KO, 0=no, 1=yes + */ + function getVentilExportCompta() + { + $alreadydispatched = 0; + + $type = 'customer_invoice'; + if ($this->element == 'invoice_supplier') $type = 'supplier_invoice'; + + $sql = " SELECT COUNT(ab.rowid) as nb FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as ab WHERE ab.doc_type='".$type."' AND ab.fk_doc = ".$this->id; + $resql = $this->db->query($sql); + if ($resql) + { + $obj = $this->db->fetch_object($resql); + if ($obj) + { + $alreadydispatched = $obj->nb; + } + } + else + { + $this->error = $this->db->lasterror(); + return -1; + } + + if ($alreadydispatched) + { + return 1; + } + return 0; + } + + /** * Return label of type of invoice * diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index a87ae0d13ae..61b73fabb75 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -2836,9 +2836,26 @@ else // Delete if ($action != 'confirm_edit' && $user->rights->fournisseur->facture->supprimer) { - if ($object->getSommePaiement()) { - print '
' . $langs->trans('Delete') . '
'; - } else { + $isErasable=$object->is_erasable(); + //var_dump($isErasable); + if ($isErasable == -4) { + print '
' . $langs->trans('Delete') . '
'; + } + elseif ($isErasable == -3) { // Should never happen with supplier invoice + print '
' . $langs->trans('Delete') . '
'; + } + elseif ($isErasable == -2) { // Should never happen with supplier invoice + print '
' . $langs->trans('Delete') . '
'; + } + elseif ($isErasable == -1) { + print '
' . $langs->trans('Delete') . '
'; + } + elseif ($isErasable <= 0) // Any other cases + { + print '
' . $langs->trans('Delete') . '
'; + } + else + { print '
'.$langs->trans('Delete').'
'; } } diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index 5f832ac37ea..a54ecbc0023 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -11,6 +11,8 @@ BillsSuppliersUnpaidForCompany=Unpaid supplier invoices for %s BillsLate=Late payments BillsStatistics=Customers invoices statistics BillsStatisticsSuppliers=Suppliers invoices statistics +DisabledBecauseDispatchedInBookkeeping=Disabled because invoice was dispatched into bookkeeping +DisabledBecauseNotLastInvoice=Disabled because invoice is not erasable. Some invoices were recorded after this one and it will create holes in the counter. DisabledBecauseNotErasable=Disabled because cannot be erased InvoiceStandard=Standard invoice InvoiceStandardAsk=Standard invoice