Update: 2026-05-04 02:53:16
This commit is contained in:
91
app/modules_app/invoices/approve.php
Normal file
91
app/modules_app/invoices/approve.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* Approve Invoice & Submit to JoFotara
|
||||
*/
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Core\JoFotara;
|
||||
use App\Middleware\AuthMiddleware;
|
||||
|
||||
$decoded = AuthMiddleware::check();
|
||||
$db = Database::getInstance();
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$id = $data['id'] ?? null;
|
||||
|
||||
if (!$id) {
|
||||
json_error('Invoice ID is required', 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
// 1. Fetch Invoice
|
||||
$stmt = $db->prepare("SELECT * FROM invoices WHERE id = ? FOR UPDATE");
|
||||
$stmt->execute([$id]);
|
||||
$invoice = $stmt->fetch();
|
||||
|
||||
if (!$invoice) {
|
||||
json_error('Invoice not found', 404);
|
||||
}
|
||||
|
||||
if ($invoice['status'] === 'approved') {
|
||||
json_error('Invoice is already approved', 400);
|
||||
}
|
||||
|
||||
// Authorization
|
||||
if ($decoded['role'] !== 'super_admin' && $invoice['tenant_id'] !== $decoded['tenant_id']) {
|
||||
json_error('Unauthorized', 403);
|
||||
}
|
||||
|
||||
// 2. Fetch Line Items
|
||||
$stmtLines = $db->prepare("SELECT * FROM invoice_lines WHERE invoice_id = ?");
|
||||
$stmtLines->execute([$id]);
|
||||
$invoice['items'] = $stmtLines->fetchAll();
|
||||
|
||||
// 3. Decrypt Sensitive Data for XML Generation
|
||||
$invoice['supplier_name'] = \App\Core\Encryption::decrypt($invoice['supplier_name']) ?: '';
|
||||
$invoice['supplier_tin'] = \App\Core\Encryption::decrypt($invoice['supplier_tin']) ?: '';
|
||||
$invoice['buyer_name'] = \App\Core\Encryption::decrypt($invoice['buyer_name']) ?: '';
|
||||
$invoice['buyer_tin'] = \App\Core\Encryption::decrypt($invoice['buyer_tin']) ?: '';
|
||||
|
||||
// 4. Initialize JoFotara Core
|
||||
$jofotara = new JoFotara();
|
||||
|
||||
// 5. Generate TLV QR Code Base64
|
||||
$qrBase64 = $jofotara->generateQRCode($invoice);
|
||||
|
||||
// 6. Generate UBL 2.1 XML
|
||||
$xmlContent = $jofotara->generateXML($invoice);
|
||||
|
||||
// 7. Submit to JoFotara API (Simulation for now)
|
||||
$apiResponse = $jofotara->submitInvoice($xmlContent);
|
||||
|
||||
if (!$apiResponse['success']) {
|
||||
throw new \Exception("JoFotara Rejection: " . ($apiResponse['error'] ?? 'Unknown Error'));
|
||||
}
|
||||
|
||||
// 8. Update Invoice Status & Save JoFotara UUID/QR
|
||||
$updateStmt = $db->prepare("
|
||||
UPDATE invoices
|
||||
SET status = 'approved',
|
||||
jofotara_uuid = ?,
|
||||
qr_code = ?,
|
||||
updated_at = NOW()
|
||||
WHERE id = ?
|
||||
");
|
||||
$updateStmt->execute([$apiResponse['uuid'] ?? 'mock-uuid', $qrBase64, $id]);
|
||||
|
||||
$db->commit();
|
||||
|
||||
json_success([
|
||||
'message' => 'Invoice approved and submitted to JoFotara successfully.',
|
||||
'jofotara_uuid' => $apiResponse['uuid'] ?? 'mock-uuid',
|
||||
'qr_code' => $qrBase64
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$db->rollBack();
|
||||
error_log("JoFotara Approve Error: " . $e->getMessage());
|
||||
json_error('Failed to approve invoice: ' . $e->getMessage(), 500);
|
||||
}
|
||||
@@ -6,6 +6,19 @@
|
||||
use App\Core\Database;
|
||||
use App\Middleware\AuthMiddleware;
|
||||
|
||||
// Helper to output error as an image for debugging
|
||||
function outputErrorImage($message) {
|
||||
header('Content-Type: image/png');
|
||||
$im = imagecreatetruecolor(400, 100);
|
||||
$bg = imagecolorallocate($im, 20, 20, 20);
|
||||
$tc = imagecolorallocate($im, 255, 50, 50);
|
||||
imagefilledrectangle($im, 0, 0, 400, 100, $bg);
|
||||
imagestring($im, 5, 10, 40, $message, $tc);
|
||||
imagepng($im);
|
||||
imagedestroy($im);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Extract token from header OR query string
|
||||
$headers = getallheaders();
|
||||
$authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
|
||||
@@ -17,44 +30,41 @@ if (preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
|
||||
$token = $_GET['token'];
|
||||
}
|
||||
|
||||
if (!$token) die('Forbidden: No token provided');
|
||||
if (!$token) outputErrorImage('Forbidden: No token');
|
||||
|
||||
$decoded = \App\Core\JWT::decode($token);
|
||||
if (!$decoded) die('Forbidden: Invalid token');
|
||||
if (!$decoded) outputErrorImage('Forbidden: Invalid token');
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
$id = input('id');
|
||||
if (!$id) die('Forbidden');
|
||||
if (!$id) outputErrorImage('Forbidden: No ID');
|
||||
|
||||
$stmt = $db->prepare("SELECT tenant_id, original_file_path FROM invoices WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$invoice = $stmt->fetch();
|
||||
|
||||
if (!$invoice) die('Not found');
|
||||
if (!$invoice) outputErrorImage('Error: Invoice not found');
|
||||
|
||||
// Authorization
|
||||
if ($decoded['role'] !== 'super_admin' && $invoice['tenant_id'] !== $decoded['tenant_id']) {
|
||||
die('Unauthorized');
|
||||
outputErrorImage('Error: Unauthorized');
|
||||
}
|
||||
|
||||
$filePath = $invoice['original_file_path'];
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
error_log("FILE PROXY ERROR: File not found at " . $filePath);
|
||||
header("HTTP/1.0 404 Not Found");
|
||||
exit('File missing');
|
||||
outputErrorImage('Error: File missing on disk');
|
||||
}
|
||||
|
||||
if (!is_readable($filePath)) {
|
||||
error_log("FILE PROXY ERROR: File not readable at " . $filePath);
|
||||
header("HTTP/1.0 403 Forbidden");
|
||||
exit('Permission denied');
|
||||
outputErrorImage('Error: Permission denied');
|
||||
}
|
||||
|
||||
$mime = mime_content_type($filePath);
|
||||
if (!$mime) $mime = 'application/octet-stream';
|
||||
|
||||
header("Content-Type: $mime");
|
||||
header("Content-Length: " . filesize($filePath));
|
||||
header("Cache-Control: public, max-age=3600"); // Add caching for speed
|
||||
header("Cache-Control: public, max-age=3600");
|
||||
readfile($filePath);
|
||||
exit;
|
||||
|
||||
Reference in New Issue
Block a user