Update: 2026-05-15 04:35:25
This commit is contained in:
@@ -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
30
public/migrate_008.php
Normal 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>";
|
||||
}
|
||||
@@ -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 '';
|
||||
|
||||
Reference in New Issue
Block a user