Update: 2026-05-15 04:35:25

This commit is contained in:
Hamza-Ayed
2026-05-15 04:35:25 +03:00
parent 1ca7e01ce0
commit 2f1ecca593
14 changed files with 858 additions and 10 deletions

View File

@@ -39,6 +39,7 @@ $routes = [
'v1/invoices/delete' => ['POST', 'invoices/delete.php'],
'v1/invoices/bulk-approve' => ['POST', 'invoices/bulk_approve.php'],
'v1/invoices/export' => ['GET', 'invoices/export.php'],
'v1/invoices/export-excel' => ['GET', 'invoices/export_excel.php'],
'v1/invoices/check-duplicate' => ['POST', 'invoices/check_duplicate.php'],
'v1/reports/tax-summary' => ['GET', 'reports/tax_summary.php'],
'v1/audit-log' => ['GET', 'audit/index.php'],

30
public/migrate_008.php Normal file
View File

@@ -0,0 +1,30 @@
<?php
/**
* Manual Migration Runner for 008_invoice_lines_enhance
*/
require_once __DIR__ . '/../app/bootstrap/init.php';
use App\Core\Database;
try {
$db = Database::getInstance();
$sql = file_get_contents(__DIR__ . '/../database/migrations/008_invoice_lines_enhance.sql');
// Split by semicolon and execute
$queries = array_filter(array_map('trim', explode(';', $sql)));
echo "<h1>Running Migration 008...</h1>";
echo "<ul>";
foreach ($queries as $query) {
if (empty($query)) continue;
$db->exec($query);
echo "<li>✅ Executed: <pre>" . htmlspecialchars(substr($query, 0, 50)) . "...</pre></li>";
}
echo "</ul>";
echo "<h2>Migration completed successfully!</h2>";
echo "<p>Please delete this file (public/migrate_008.php) for security.</p>";
} catch (\Exception $e) {
echo "<h2 style='color:red;'>Migration failed:</h2>";
echo "<pre>" . htmlspecialchars($e->getMessage()) . "</pre>";
}

View File

@@ -1391,6 +1391,9 @@
<button x-show="page==='invoices'" @click="showBatchUploadModal = true" class="btn btn-navy">
<span>📁</span> استيراد مجمع (Batch)
</button>
<button x-show="page==='invoices'" @click="exportExcel()" class="btn btn-ghost btn-sm" style="border-color: var(--green-mid); color: var(--green);">
<span>📥</span> تصدير Excel
</button>
</div>
</div>
@@ -2560,8 +2563,13 @@
</div>
<div x-show="currentInvoice?.status === 'approved'"
style="width:100%; background:var(--green-subtle); color:var(--green-mid); border:1px solid rgba(5,150,105,0.2); padding:10px; border-radius:11px; font-size:14px; font-weight:700; text-align:center;">
مدققة ومعتمدة
style="width:100%; display:flex; flex-direction:column; gap:8px;">
<div style="width:100%; background:var(--green-subtle); color:var(--green-mid); border:1px solid rgba(5,150,105,0.2); padding:10px; border-radius:11px; font-size:14px; font-weight:700; text-align:center;">
مدققة ومعتمدة
</div>
<button @click="exportExcel(currentInvoice.id)" class="btn btn-ghost" style="width:100%; justify-content:center; border-color:var(--green-mid); color:var(--green-mid);">
<span>📥</span> تحميل كـ Excel
</button>
</div>
</div>
</div>
@@ -2588,6 +2596,7 @@
<th style="padding:12px 16px; text-align:center;">الكمية</th>
<th style="padding:12px 16px;">السعر</th>
<th style="padding:12px 16px;">الضريبة</th>
<th style="padding:12px 16px;">التصنيف</th>
<th style="padding:12px 16px;">الإجمالي</th>
</tr>
</thead>
@@ -2598,7 +2607,18 @@
<td style="padding:12px 16px; text-align:center; font-family:'Outfit',sans-serif;" x-text="item.quantity"></td>
<td style="padding:12px 16px; color:var(--green-mid); font-family:'Outfit',sans-serif;" x-text="item.unit_price"></td>
<td style="padding:12px 16px; color:var(--amber-mid); font-family:'Outfit',sans-serif;" x-text="((parseFloat(item.tax_rate) || 0) * 100).toFixed(0) + '%'"></td>
<td style="padding:12px 16px; font-weight:700; color:var(--violet-mid); font-family:'Outfit',sans-serif;" x-text="item.line_total"></td>
<td style="padding:12px 16px;">
<span class="badge"
:class="{
'badge-teal': item.tax_category === 'standard',
'badge-blue': item.tax_category === 'zero_rated',
'badge-amber': item.tax_category === 'exempt',
'badge-red': item.tax_category === 'special'
}"
x-text="item.tax_category === 'standard' ? '16%' : (item.tax_category === 'zero_rated' ? '0%' : (item.tax_category === 'exempt' ? 'معفى' : 'خاص'))">
</span>
</td>
<td style="padding:12px 16px; font-weight:700; color:var(--violet-mid); font-family:'Outfit',sans-serif;" x-text="item.net_total || item.line_total"></td>
</tr>
</template>
</tbody>
@@ -2939,6 +2959,25 @@
if (this.page === 'subscription') this.plans = await this.apiRequest('v1/subscriptions/plans') || [];
if (this.user.role === 'super_admin') this.tenants = await this.apiRequest('v1/tenants') || [];
},
// ── Excel Export ──
exportExcel(invoiceId = null) {
let url = '/index.php?route=v1/invoices/export-excel';
if (invoiceId) {
url += '&invoice_id=' + encodeURIComponent(invoiceId);
} else {
if (this.invoiceCompanyFilter) url += '&company_id=' + encodeURIComponent(this.invoiceCompanyFilter);
if (this.activeInvoiceTab === 'approved') url += '&status=approved';
else if (this.activeInvoiceTab === 'pending') url += '&status=extracted';
else if (this.activeInvoiceTab === 'rejected') url += '&status=rejected';
}
// Download via hidden link with auth token
const link = document.createElement('a');
link.href = url + '&token=' + encodeURIComponent(this.token());
link.download = '';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
//
getQrSrc(inv) {
if (!inv) return '';