softDelete features
This commit is contained in:
@@ -1158,6 +1158,112 @@ class InvoiceManager {
|
||||
* @param array $logging Audit entry.
|
||||
* @throws Exception
|
||||
*/
|
||||
/**
|
||||
* Soft-delete an invoice/credit_note/purchase_invoice/supplier_credit_note by negating
|
||||
* company_id. Blocked if any active (non-soft-deleted) downstream documents exist.
|
||||
* Deletes the GL entry if one was posted (subject to posting window).
|
||||
*/
|
||||
public function softDelete(int $id): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT id, doc_type, issued_date FROM td_invoice
|
||||
WHERE company_id = :cid AND id = :id LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->company_id, ':id' => $id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$row) throw new Exception('Invoice not found.');
|
||||
$doc_type = (string)$row['doc_type'];
|
||||
|
||||
$this->assertPostingWindow($row['issued_date'] ?: date('Y-m-d'), ucfirst(str_replace('_', ' ', $doc_type)) . ' deletion');
|
||||
|
||||
if (in_array($doc_type, ['invoice', 'credit_note'], true)) {
|
||||
$sth2 = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) FROM td_receipt_item
|
||||
WHERE company_id = :cid AND invoice_id = :id"
|
||||
);
|
||||
$sth2->execute([':cid' => $this->company_id, ':id' => $id]);
|
||||
if ((int)$sth2->fetchColumn() > 0) {
|
||||
throw new Exception('Cannot delete — receipts are allocated to this document. Delete the receipts first.');
|
||||
}
|
||||
|
||||
$sth3 = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) FROM td_payment_item
|
||||
WHERE company_id = :cid AND invoice_id = :id"
|
||||
);
|
||||
$sth3->execute([':cid' => $this->company_id, ':id' => $id]);
|
||||
if ((int)$sth3->fetchColumn() > 0) {
|
||||
throw new Exception('Cannot delete — payments are allocated to this document. Delete the payments first.');
|
||||
}
|
||||
|
||||
$sth4 = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) FROM td_receipt_billing_item
|
||||
WHERE company_id = :cid AND invoice_id = :id"
|
||||
);
|
||||
$sth4->execute([':cid' => $this->company_id, ':id' => $id]);
|
||||
if ((int)$sth4->fetchColumn() > 0) {
|
||||
throw new Exception('Cannot delete — receipt billings reference this document. Delete the receipt billings first.');
|
||||
}
|
||||
|
||||
$sth5 = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) FROM td_payment_billing_item
|
||||
WHERE company_id = :cid AND invoice_id = :id"
|
||||
);
|
||||
$sth5->execute([':cid' => $this->company_id, ':id' => $id]);
|
||||
if ((int)$sth5->fetchColumn() > 0) {
|
||||
throw new Exception('Cannot delete — payment billings reference this document. Delete the payment billings first.');
|
||||
}
|
||||
|
||||
if ($doc_type === 'invoice') {
|
||||
$sth6 = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) FROM td_invoice
|
||||
WHERE company_id = :cid AND ref_invoice_id = :id AND doc_type = 'credit_note'"
|
||||
);
|
||||
$sth6->execute([':cid' => $this->company_id, ':id' => $id]);
|
||||
if ((int)$sth6->fetchColumn() > 0) {
|
||||
throw new Exception('Cannot delete — credit notes reference this invoice. Delete the credit notes first.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($doc_type, ['purchase_invoice', 'supplier_credit_note'], true)) {
|
||||
$sth7 = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) FROM td_payment_item
|
||||
WHERE company_id = :cid AND invoice_id = :id"
|
||||
);
|
||||
$sth7->execute([':cid' => $this->company_id, ':id' => $id]);
|
||||
if ((int)$sth7->fetchColumn() > 0) {
|
||||
throw new Exception('Cannot delete — payments are allocated to this document. Delete the payments first.');
|
||||
}
|
||||
|
||||
$sth8 = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) FROM td_payment_billing_item
|
||||
WHERE company_id = :cid AND invoice_id = :id"
|
||||
);
|
||||
$sth8->execute([':cid' => $this->company_id, ':id' => $id]);
|
||||
if ((int)$sth8->fetchColumn() > 0) {
|
||||
throw new Exception('Cannot delete — payment billings reference this document. Delete the payment billings first.');
|
||||
}
|
||||
}
|
||||
|
||||
// Delete GL entry if posted (no-op if none exists; throws if period is closed)
|
||||
global $pdo1;
|
||||
if (isset($pdo1) && $pdo1 instanceof PDO) {
|
||||
$guard = new PostingWindowGuard($pdo1, $this->company_id);
|
||||
$gl = new GlManager($this->pdo, $this->company_id, $guard);
|
||||
$gl->delete($doc_type, $id);
|
||||
}
|
||||
|
||||
$this->pdo->prepare(
|
||||
"UPDATE td_invoice_item SET company_id = company_id * -1
|
||||
WHERE invoice_id = :id AND company_id = :cid"
|
||||
)->execute([':id' => $id, ':cid' => $this->company_id]);
|
||||
|
||||
$this->pdo->prepare(
|
||||
"UPDATE td_invoice SET company_id = company_id * -1
|
||||
WHERE id = :id AND company_id = :cid"
|
||||
)->execute([':id' => $id, ':cid' => $this->company_id]);
|
||||
}
|
||||
|
||||
public function voidInvoice(int $id, array $logging): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
|
||||
Reference in New Issue
Block a user