Fix: protection to not delete invoice if invoice is in bookkeeping
This commit is contained in:
parent
78c4b7f912
commit
8019f73f99
@ -3888,7 +3888,6 @@ else if ($id > 0 || ! empty($ref))
|
|||||||
print '</td>';
|
print '</td>';
|
||||||
}
|
}
|
||||||
print '<td align="right">' . price($sign * $objp->amount) . '</td>';
|
print '<td align="right">' . price($sign * $objp->amount) . '</td>';
|
||||||
// TODO Add link to delete payment
|
|
||||||
print '<td align="center">';
|
print '<td align="center">';
|
||||||
if ($object->statut == Facture::STATUS_VALIDATED && $object->paye == 0 && $user->societe_id == 0)
|
if ($object->statut == Facture::STATUS_VALIDATED && $object->paye == 0 && $user->societe_id == 0)
|
||||||
{
|
{
|
||||||
@ -4380,13 +4379,30 @@ else if ($id > 0 || ! empty($ref))
|
|||||||
// Delete
|
// Delete
|
||||||
if ($user->rights->facture->supprimer)
|
if ($user->rights->facture->supprimer)
|
||||||
{
|
{
|
||||||
if ($object->is_erasable() <= 0) {
|
$isErasable = $object->is_erasable();
|
||||||
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseNotErasable") . '">' . $langs->trans('Delete') . '</a></div>';
|
//var_dump($isErasable);
|
||||||
} else if ($objectidnext) {
|
if ($isErasable == -4) {
|
||||||
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseReplacedInvoice") . '">' . $langs->trans('Delete') . '</a></div>';
|
|
||||||
} elseif ($object->getSommePaiement()) {
|
|
||||||
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecausePayments") . '">' . $langs->trans('Delete') . '</a></div>';
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecausePayments") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
} else {
|
}
|
||||||
|
elseif ($isErasable == -3) {
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseNotLastSituationInvoice") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
elseif ($isErasable == -2) {
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseNotLastInvoice") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
elseif ($isErasable == -1) {
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseDispatchedInBookkeeping") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
elseif ($isErasable <= 0) // Any other cases
|
||||||
|
{
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseNotErasable") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
elseif ($objectidnext)
|
||||||
|
{
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseReplacedInvoice") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
print '<div class="inline-block divButAction"><a class="butActionDelete" href="' . $_SERVER["PHP_SELF"] . '?facid=' . $object->id . '&action=delete">' . $langs->trans('Delete') . '</a></div>';
|
print '<div class="inline-block divButAction"><a class="butActionDelete" href="' . $_SERVER["PHP_SELF"] . '?facid=' . $object->id . '&action=delete">' . $langs->trans('Delete') . '</a></div>';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -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
|
* Return list of invoices (eventually filtered on a user) into an array
|
||||||
|
|||||||
@ -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
|
* Return label of type of invoice
|
||||||
*
|
*
|
||||||
|
|||||||
@ -2836,9 +2836,26 @@ else
|
|||||||
// Delete
|
// Delete
|
||||||
if ($action != 'confirm_edit' && $user->rights->fournisseur->facture->supprimer)
|
if ($action != 'confirm_edit' && $user->rights->fournisseur->facture->supprimer)
|
||||||
{
|
{
|
||||||
if ($object->getSommePaiement()) {
|
$isErasable=$object->is_erasable();
|
||||||
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecausePayments") . '">' . $langs->trans('Delete') . '</a></div>';
|
//var_dump($isErasable);
|
||||||
} else {
|
if ($isErasable == -4) {
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecausePayments") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
elseif ($isErasable == -3) { // Should never happen with supplier invoice
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseNotLastSituationInvoice") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
elseif ($isErasable == -2) { // Should never happen with supplier invoice
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseNotLastInvoice") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
elseif ($isErasable == -1) {
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseDispatchedInBookkeeping") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
elseif ($isErasable <= 0) // Any other cases
|
||||||
|
{
|
||||||
|
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="' . $langs->trans("DisabledBecauseNotErasable") . '">' . $langs->trans('Delete') . '</a></div>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
print '<div class="inline-block divButAction"><a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete">'.$langs->trans('Delete').'</a></div>';
|
print '<div class="inline-block divButAction"><a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete">'.$langs->trans('Delete').'</a></div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,8 @@ BillsSuppliersUnpaidForCompany=Unpaid supplier invoices for %s
|
|||||||
BillsLate=Late payments
|
BillsLate=Late payments
|
||||||
BillsStatistics=Customers invoices statistics
|
BillsStatistics=Customers invoices statistics
|
||||||
BillsStatisticsSuppliers=Suppliers 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
|
DisabledBecauseNotErasable=Disabled because cannot be erased
|
||||||
InvoiceStandard=Standard invoice
|
InvoiceStandard=Standard invoice
|
||||||
InvoiceStandardAsk=Standard invoice
|
InvoiceStandardAsk=Standard invoice
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user