Update: 2026-05-11 01:09:54
This commit is contained in:
@@ -35,6 +35,8 @@ $routes = [
|
||||
'v1/invoices/download_xml' => ['GET', 'invoices/download_xml.php'],
|
||||
'v1/invoices/submit-jofotara' => ['POST', 'invoices/submit_jofotara.php'],
|
||||
'v1/invoices/update' => ['POST', 'invoices/update.php'],
|
||||
'v1/invoices/reject' => ['POST', 'invoices/reject.php'],
|
||||
'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/check-duplicate' => ['POST', 'invoices/check_duplicate.php'],
|
||||
|
||||
144
public/shell.php
144
public/shell.php
@@ -1759,6 +1759,9 @@
|
||||
<button @click="viewInvoice(inv.id)" class="btn-table-action btn-ta-navy">
|
||||
👁️ عرض
|
||||
</button>
|
||||
<button @click="deleteInvoice(inv.id)" class="btn-table-action btn-ta-red" style="margin-right:4px;">
|
||||
🗑️ حذف
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
@@ -2395,11 +2398,11 @@
|
||||
<!-- ── VIEW INVOICE MODAL ──────────────────────────── -->
|
||||
<div x-show="showViewModal" x-cloak class="modal-backdrop" @click.self="showViewModal = false">
|
||||
<div
|
||||
style="background:var(--bg-card); border-radius:22px; box-shadow:var(--shadow-lg); width:100%; max-width:900px; height:88vh; display:flex; overflow:hidden;">
|
||||
style="background:var(--bg-card); border-radius:22px; box-shadow:var(--shadow-lg); width:100%; max-width:95vw; height:95vh; display:flex; overflow:hidden;">
|
||||
|
||||
<!-- Document Preview -->
|
||||
<div
|
||||
style="flex:1; background:#F2F1FA; border-left:1px solid var(--border); position:relative; overflow:hidden;">
|
||||
style="flex:3; background:#F2F1FA; border-left:1px solid var(--border); position:relative; overflow:hidden;">
|
||||
<div style="position:absolute; top:12px; right:12px; z-index:10;">
|
||||
<span class="badge badge-navy">معاينة الملف</span>
|
||||
</div>
|
||||
@@ -2415,15 +2418,20 @@
|
||||
</div>
|
||||
|
||||
<!-- Invoice Data Panel -->
|
||||
<div style="width:345px; flex-shrink:0; display:flex; flex-direction:column; overflow:hidden;">
|
||||
<div style="width:25%; min-width:320px; max-width:450px; flex-shrink:0; display:flex; flex-direction:column; overflow:hidden;">
|
||||
<!-- Header -->
|
||||
<div
|
||||
style="padding:18px 20px 14px; border-bottom:1px solid var(--border); display:flex; align-items:center; justify-content:space-between;">
|
||||
<div>
|
||||
<div style="font-size:16px; font-weight:700; color:var(--text-1);">تفاصيل الفاتورة</div>
|
||||
<div style="font-size:16px; font-weight:700; color:var(--text-1); display:flex; gap:10px; align-items:center;">
|
||||
تفاصيل الفاتورة
|
||||
<button x-show="currentInvoice?.status === 'extracted' && !isEditingInvoice" @click="isEditingInvoice = true" class="btn-sm btn-ghost" style="font-size:11px; padding:2px 8px;">تعديل ✏️</button>
|
||||
<button x-show="isEditingInvoice" @click="updateInvoice" class="btn-sm btn-teal" style="font-size:11px; padding:2px 8px;">حفظ 💾</button>
|
||||
<button x-show="isEditingInvoice" @click="isEditingInvoice = false" class="btn-sm btn-ghost" style="font-size:11px; padding:2px 8px;">إلغاء ✕</button>
|
||||
</div>
|
||||
<span class="badge" style="margin-top:4px;"
|
||||
:class="currentInvoice?.status==='extracted' ? 'badge-blue' : (currentInvoice?.status==='approved' ? 'badge-teal' : 'badge-gray')"
|
||||
x-text="currentInvoice?.status === 'approved' ? '✓ مدققة' : (currentInvoice?.status === 'extracted' ? 'جاهزة للتدقيق' : currentInvoice?.status)">
|
||||
:class="currentInvoice?.status==='extracted' ? 'badge-blue' : (currentInvoice?.status==='approved' ? 'badge-teal' : (currentInvoice?.status==='rejected' ? 'badge-red' : 'badge-gray'))"
|
||||
x-text="currentInvoice?.status === 'approved' ? '✓ مدققة' : (currentInvoice?.status === 'extracted' ? 'جاهزة للتدقيق' : (currentInvoice?.status === 'rejected' ? 'مرفوضة' : currentInvoice?.status))">
|
||||
</span>
|
||||
</div>
|
||||
<button @click="showViewModal = false" class="modal-close-btn">✕</button>
|
||||
@@ -2455,26 +2463,35 @@
|
||||
|
||||
<div class="invoice-field-card">
|
||||
<div class="invoice-field-label">المورد (البائع)</div>
|
||||
<div class="invoice-field-value" x-text="currentInvoice?.supplier_name || 'غير متوفر'"></div>
|
||||
<div style="font-size:12px; color:var(--text-3); font-family:'Outfit',sans-serif; margin-top:4px;"
|
||||
<div x-show="!isEditingInvoice" class="invoice-field-value" x-text="currentInvoice?.supplier_name || 'غير متوفر'"></div>
|
||||
<input x-show="isEditingInvoice" type="text" x-model="currentInvoice.supplier_name" class="form-input" style="padding:4px; font-size:13px; margin-bottom:4px;" placeholder="اسم المورد">
|
||||
|
||||
<div x-show="!isEditingInvoice" style="font-size:12px; color:var(--text-3); font-family:'Outfit',sans-serif; margin-top:4px;"
|
||||
x-text="'TIN: ' + (currentInvoice?.supplier_tin || '—')"></div>
|
||||
<input x-show="isEditingInvoice" type="text" x-model="currentInvoice.supplier_tin" class="form-input" placeholder="الرقم الضريبي" style="padding:4px; font-size:12px;">
|
||||
</div>
|
||||
|
||||
<div class="invoice-field-card">
|
||||
<div class="invoice-field-label">رقم الفاتورة والتاريخ</div>
|
||||
<div class="invoice-field-value" x-text="currentInvoice?.invoice_number || '—'"></div>
|
||||
<div style="font-size:13px; color:var(--text-2); font-family:'Outfit',sans-serif; margin-top:3px;"
|
||||
<div x-show="!isEditingInvoice" class="invoice-field-value" x-text="currentInvoice?.invoice_number || '—'"></div>
|
||||
<input x-show="isEditingInvoice" type="text" x-model="currentInvoice.invoice_number" class="form-input" style="padding:4px; font-size:13px; margin-bottom:4px;" placeholder="رقم الفاتورة">
|
||||
|
||||
<div x-show="!isEditingInvoice" style="font-size:13px; color:var(--text-2); font-family:'Outfit',sans-serif; margin-top:3px;"
|
||||
x-text="currentInvoice?.invoice_date || '—'"></div>
|
||||
<input x-show="isEditingInvoice" type="date" x-model="currentInvoice.invoice_date" class="form-input" style="padding:4px; font-size:13px;">
|
||||
</div>
|
||||
|
||||
<div class="invoice-field-card"
|
||||
style="background:var(--green-subtle); border-color:rgba(5,150,105,0.2);">
|
||||
<div class="invoice-field-label" style="color:var(--green-mid);">المجموع الكلي</div>
|
||||
<div style="font-size:26px; font-weight:700; color:var(--green-mid); font-family:'El Messiri',sans-serif;"
|
||||
<div x-show="!isEditingInvoice" style="font-size:26px; font-weight:700; color:var(--green-mid); font-family:'El Messiri',sans-serif;"
|
||||
x-text="parseFloat(currentInvoice?.grand_total || 0).toLocaleString() + ' JOD'"></div>
|
||||
<div style="font-size:12px; color:var(--amber-mid); margin-top:4px; font-family:'Outfit',sans-serif;"
|
||||
<input x-show="isEditingInvoice" type="number" step="0.01" x-model="currentInvoice.grand_total" class="form-input" style="padding:4px; font-size:16px; font-weight:bold; color:var(--green-mid); margin-bottom:4px;" placeholder="المجموع">
|
||||
|
||||
<div x-show="!isEditingInvoice" style="font-size:12px; color:var(--amber-mid); margin-top:4px; font-family:'Outfit',sans-serif;"
|
||||
x-text="'الضريبة: ' + parseFloat(currentInvoice?.tax_amount || 0).toLocaleString() + ' JOD'">
|
||||
</div>
|
||||
<input x-show="isEditingInvoice" type="number" step="0.01" x-model="currentInvoice.tax_amount" class="form-input" style="padding:4px; font-size:13px; color:var(--amber-mid);" placeholder="الضريبة">
|
||||
</div>
|
||||
|
||||
<!-- Items Table -->
|
||||
@@ -2562,10 +2579,18 @@
|
||||
<span x-show="isBusy">⏳ جاري التدقيق...</span>
|
||||
</button>
|
||||
|
||||
<button x-show="currentInvoice?.status === 'extracted'"
|
||||
style="width:100%; background:var(--red-subtle); color:var(--red-mid); border:none; padding:10px; border-radius:11px; font-family:inherit; font-size:14px; font-weight:600; cursor:pointer; transition:all 0.18s;"
|
||||
onmouseover="this.style.background='#FECACA'"
|
||||
onmouseout="this.style.background='var(--red-subtle)'">❌ رفض الفاتورة</button>
|
||||
<div style="display:flex; gap:8px;">
|
||||
<button x-show="currentInvoice?.status === 'extracted' || currentInvoice?.status === 'rejected'"
|
||||
@click="rejectInvoice(currentInvoice.id)"
|
||||
style="flex:1; background:var(--amber-subtle); color:var(--amber-mid); border:none; padding:10px; border-radius:11px; font-family:inherit; font-size:14px; font-weight:600; cursor:pointer; transition:all 0.18s;"
|
||||
onmouseover="this.style.background='#FDE68A'"
|
||||
onmouseout="this.style.background='var(--amber-subtle)'">❌ رفض</button>
|
||||
|
||||
<button @click="deleteInvoice(currentInvoice.id)"
|
||||
style="flex:1; background:var(--red-subtle); color:var(--red-mid); border:none; padding:10px; border-radius:11px; font-family:inherit; font-size:14px; font-weight:600; cursor:pointer; transition:all 0.18s;"
|
||||
onmouseover="this.style.background='#FECACA'"
|
||||
onmouseout="this.style.background='var(--red-subtle)'">🗑️ حذف</button>
|
||||
</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;">
|
||||
@@ -2829,7 +2854,7 @@
|
||||
showExcelModal: false, showBatchUploadModal: false,
|
||||
isUploadingBatch: false, batchProgress: { total: 0, current: 0 },
|
||||
showAddTenantModal: false, showEditTenantModal: false, showTenantStatsModal: false,
|
||||
acknowledgedWarnings: false,
|
||||
acknowledgedWarnings: false, isEditingInvoice: false,
|
||||
isBusy: false, globalError: '',
|
||||
|
||||
newUser: { name: '', email: '', password: '', role: 'accountant', tenant_id: '' },
|
||||
@@ -2849,14 +2874,8 @@
|
||||
subtitle() { return { dashboard: 'نظرة شاملة على نشاط النظام', users: 'إدارة المستخدمين والصلاحيات', companies: 'إدارة الشركات والربط بالفوترة الحكومية', invoices: 'رفع ومعالجة الفواتير الضريبية', tenants: 'إدارة المكاتب المحاسبية المشتركة', subscription: 'تفاصيل باقتك الحالية واستهلاك الموارد' }[this.page] || ''; },
|
||||
|
||||
get filteredInvoices() {
|
||||
const now = new Date();
|
||||
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
|
||||
return this.invoices.filter(inv => {
|
||||
// 1. Time Filter (Current Month)
|
||||
const invDate = new Date(inv.invoice_date || inv.created_at);
|
||||
if (invDate < startOfMonth) return false;
|
||||
|
||||
// Removed time filter to prevent invoices from disappearing after a month
|
||||
// 2. Company Filter
|
||||
if (this.invoiceCompanyFilter && inv.company_id != this.invoiceCompanyFilter) return false;
|
||||
|
||||
@@ -3155,6 +3174,83 @@
|
||||
}
|
||||
},
|
||||
|
||||
async rejectInvoice(id) {
|
||||
if (this.isBusy) return;
|
||||
if (!confirm('هل أنت متأكد من رفض هذه الفاتورة؟')) return;
|
||||
this.isBusy = true;
|
||||
try {
|
||||
const res = await fetch('/index.php?route=v1/invoices/reject', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: id })
|
||||
});
|
||||
const json = await res.json();
|
||||
this.isBusy = false;
|
||||
|
||||
if (json.success) {
|
||||
alert('تم رفض الفاتورة بنجاح!');
|
||||
this.showViewModal = false;
|
||||
this.loadAll();
|
||||
} else {
|
||||
this.showError(json.message);
|
||||
}
|
||||
} catch (e) {
|
||||
this.isBusy = false;
|
||||
this.showError('حدث خطأ أثناء رفض الفاتورة');
|
||||
}
|
||||
},
|
||||
|
||||
async deleteInvoice(id) {
|
||||
if (this.isBusy) return;
|
||||
if (!confirm('هل أنت متأكد من حذف هذه الفاتورة بشكل نهائي؟ لا يمكن التراجع عن هذا الإجراء.')) return;
|
||||
this.isBusy = true;
|
||||
try {
|
||||
const res = await fetch('/index.php?route=v1/invoices/delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: id })
|
||||
});
|
||||
const json = await res.json();
|
||||
this.isBusy = false;
|
||||
|
||||
if (json.success) {
|
||||
alert('تم حذف الفاتورة بنجاح!');
|
||||
this.showViewModal = false;
|
||||
this.loadAll();
|
||||
} else {
|
||||
this.showError(json.message);
|
||||
}
|
||||
} catch (e) {
|
||||
this.isBusy = false;
|
||||
this.showError('حدث خطأ أثناء حذف الفاتورة');
|
||||
}
|
||||
},
|
||||
|
||||
async updateInvoice() {
|
||||
if (!this.currentInvoice || this.isBusy) return;
|
||||
this.isBusy = true;
|
||||
try {
|
||||
const res = await fetch('/index.php?route=v1/invoices/update', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(this.currentInvoice)
|
||||
});
|
||||
const json = await res.json();
|
||||
this.isBusy = false;
|
||||
|
||||
if (json.success) {
|
||||
alert('تم تحديث بيانات الفاتورة بنجاح!');
|
||||
this.isEditingInvoice = false;
|
||||
this.loadAll();
|
||||
} else {
|
||||
this.showError(json.message);
|
||||
}
|
||||
} catch (e) {
|
||||
this.isBusy = false;
|
||||
this.showError('حدث خطأ أثناء تحديث الفاتورة');
|
||||
}
|
||||
},
|
||||
|
||||
async uploadExcel() {
|
||||
const fileInput = document.getElementById('excelFileInput');
|
||||
if (!fileInput.files[0]) return alert('الرجاء اختيار ملف اكسل');
|
||||
|
||||
Reference in New Issue
Block a user