117 lines
3.6 KiB
PHP
117 lines
3.6 KiB
PHP
<?php
|
|
/**
|
|
* Update Invoice (Before Approval Only)
|
|
* POST /v1/invoices/update
|
|
* Allows editing extracted data before final approval.
|
|
*/
|
|
|
|
use App\Core\Database;
|
|
use App\Core\Encryption;
|
|
use App\Core\AuditLogger;
|
|
use App\Middleware\AuthMiddleware;
|
|
|
|
$decoded = AuthMiddleware::check();
|
|
$data = input();
|
|
|
|
$id = $data['id'] ?? null;
|
|
if (!$id) json_error('معرّف الفاتورة مطلوب', 422);
|
|
|
|
$db = Database::getInstance();
|
|
$tenantId = $decoded['tenant_id'];
|
|
$role = $decoded['role'];
|
|
|
|
// 1. Fetch & verify access
|
|
$query = $role === 'super_admin'
|
|
? "SELECT * FROM invoices WHERE id = ?"
|
|
: "SELECT * FROM invoices WHERE id = ? AND tenant_id = ?";
|
|
$params = $role === 'super_admin' ? [$id] : [$id, $tenantId];
|
|
$stmt = $db->prepare($query);
|
|
$stmt->execute($params);
|
|
$invoice = $stmt->fetch();
|
|
|
|
if (!$invoice) json_error('الفاتورة غير موجودة', 404);
|
|
|
|
// 2. Only allow editing extracted (not yet approved) invoices
|
|
if (!in_array($invoice['status'], ['extracted', 'pending'])) {
|
|
json_error('لا يمكن تعديل الفاتورة بعد اعتمادها', 403);
|
|
}
|
|
|
|
$db->beginTransaction();
|
|
try {
|
|
// 3. Update main invoice fields
|
|
$fields = [];
|
|
$values = [];
|
|
|
|
$plainFields = ['invoice_number', 'invoice_date', 'invoice_type', 'invoice_category',
|
|
'subtotal', 'tax_amount', 'discount_total', 'grand_total', 'currency_code'];
|
|
|
|
foreach ($plainFields as $f) {
|
|
if (isset($data[$f])) {
|
|
$fields[] = "$f = ?";
|
|
$values[] = $data[$f];
|
|
}
|
|
}
|
|
|
|
// Encrypted fields
|
|
$encryptedFields = [
|
|
'supplier_name' => 'supplier_name',
|
|
'supplier_tin' => 'supplier_tin',
|
|
'supplier_address' => 'supplier_address',
|
|
'buyer_name' => 'buyer_name',
|
|
'buyer_tin' => 'buyer_tin',
|
|
'buyer_national_id' => 'buyer_national_id',
|
|
];
|
|
|
|
foreach ($encryptedFields as $key => $column) {
|
|
if (isset($data[$key])) {
|
|
$fields[] = "$column = ?";
|
|
$values[] = !empty($data[$key]) ? Encryption::encrypt($data[$key]) : '';
|
|
}
|
|
}
|
|
|
|
if (!empty($fields)) {
|
|
$fields[] = 'updated_at = NOW()';
|
|
$values[] = $id;
|
|
$sql = "UPDATE invoices SET " . implode(', ', $fields) . " WHERE id = ?";
|
|
$db->prepare($sql)->execute($values);
|
|
}
|
|
|
|
// 4. Update line items (if provided)
|
|
if (isset($data['items']) && is_array($data['items'])) {
|
|
// Delete old lines
|
|
$db->prepare("DELETE FROM invoice_lines WHERE invoice_id = ?")->execute([$id]);
|
|
|
|
// Insert new lines
|
|
$lineStmt = $db->prepare(
|
|
"INSERT INTO invoice_lines (id, invoice_id, line_number, description, quantity, unit_price, tax_rate, line_total)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
);
|
|
|
|
foreach ($data['items'] as $idx => $item) {
|
|
$lineStmt->execute([
|
|
Database::generateUuid(),
|
|
$id,
|
|
$item['line_number'] ?? ($idx + 1),
|
|
$item['description'] ?? '',
|
|
$item['quantity'] ?? 1,
|
|
$item['unit_price'] ?? 0,
|
|
$item['tax_rate'] ?? 0,
|
|
$item['line_total'] ?? 0,
|
|
]);
|
|
}
|
|
}
|
|
|
|
$db->commit();
|
|
|
|
AuditLogger::log('invoice.updated', 'invoice', $id, null, [
|
|
'fields_updated' => array_keys($data),
|
|
], $decoded);
|
|
|
|
json_success(null, 'تم تحديث بيانات الفاتورة بنجاح');
|
|
|
|
} catch (\Exception $e) {
|
|
$db->rollBack();
|
|
error_log("Invoice Update Error: " . $e->getMessage());
|
|
safe_error($e, 'invoices/update', 'فشل تحديث الفاتورة.');
|
|
}
|