Update: 2026-05-04 20:10:28
This commit is contained in:
179
public/shell.php
179
public/shell.php
@@ -362,6 +362,110 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Upload Invoice Modal -->
|
||||
<div x-show="showUploadModal" x-cloak class="fixed inset-0 bg-black/90 flex items-center justify-center p-6 z-[120]">
|
||||
<div class="bg-surface border border-gray-800 w-full max-w-lg p-10 rounded-[40px] shadow-2xl glass-elevated">
|
||||
<h3 class="text-2xl font-bold mb-8">رفع فاتورة جديدة</h3>
|
||||
<form @submit.prevent="uploadInvoice" class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">اختر الشركة</label>
|
||||
<select x-model="uploadData.company_id" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||||
<option value="">-- يرجى الاختيار --</option>
|
||||
<template x-for="c in companies" :key="c.id">
|
||||
<option :value="c.id" x-text="c.name"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">ملف الفاتورة (صورة أو PDF)</label>
|
||||
<input type="file" @change="selectedFile = $event.target.files[0]" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||||
</div>
|
||||
<div class="flex gap-4 pt-4">
|
||||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold transition-all btn-glow" :disabled="isUploading">
|
||||
<span x-show="!isUploading">📤 رفع وتحليل</span>
|
||||
<span x-show="isUploading">⏳ جاري التحليل...</span>
|
||||
</button>
|
||||
<button type="button" @click="showUploadModal = false" class="px-8 py-4 border border-gray-800 rounded-2xl">إلغاء</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Invoice Modal -->
|
||||
<div x-show="showViewModal" x-cloak class="fixed inset-0 bg-black/90 backdrop-blur-md flex items-center justify-center p-6 z-[110]">
|
||||
<div class="bg-surface border border-gray-800 w-full max-w-6xl h-[90vh] p-8 rounded-[40px] shadow-2xl glass-elevated flex flex-col md:flex-row gap-8" @click.away="showViewModal = false">
|
||||
<!-- Left: Document Preview -->
|
||||
<div class="flex-1 bg-gray-950 rounded-3xl overflow-hidden border border-gray-800 relative">
|
||||
<template x-if="currentInvoice?.file_url">
|
||||
<iframe :src="currentInvoice.file_url" class="w-full h-full border-0"></iframe>
|
||||
</template>
|
||||
<div x-show="!currentInvoice?.file_url" class="absolute inset-0 flex items-center justify-center text-gray-600">لا يوجد ملف مرفق</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Invoice Data -->
|
||||
<div class="w-full md:w-96 flex flex-col h-full overflow-y-auto pr-4 scrollbar-hide">
|
||||
<div class="flex justify-between items-start mb-6">
|
||||
<h3 class="text-2xl font-bold text-emerald-400">تفاصيل الفاتورة</h3>
|
||||
<button @click="showViewModal = false" class="text-gray-500 hover:text-white text-3xl transition">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4 flex-1">
|
||||
<div class="p-4 bg-gray-900/50 rounded-2xl border border-gray-800">
|
||||
<p class="text-[10px] text-gray-500 uppercase tracking-widest mb-1">المورد</p>
|
||||
<p class="font-bold text-lg text-emerald-300" x-text="currentInvoice?.supplier_name || 'غير متوفر'"></p>
|
||||
<p class="text-xs text-gray-400 font-mono mt-1" x-text="'TIN: ' + (currentInvoice?.supplier_tin || '-')"></p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-gray-900/50 rounded-2xl border border-gray-800">
|
||||
<p class="text-[10px] text-gray-500 uppercase tracking-widest mb-1">الرقم والتاريخ</p>
|
||||
<p class="font-bold" x-text="currentInvoice?.invoice_number || '-'"></p>
|
||||
<p class="text-sm text-gray-400 font-mono" x-text="currentInvoice?.invoice_date || '-'"></p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-gray-900/50 rounded-2xl border border-gray-800">
|
||||
<p class="text-[10px] text-gray-500 uppercase tracking-widest mb-1">المجموع الكلي</p>
|
||||
<p class="text-3xl font-bold font-mono text-emerald-400" x-text="parseFloat(currentInvoice?.grand_total || 0).toLocaleString() + ' JOD'"></p>
|
||||
<p class="text-xs text-yellow-500 mt-1 font-mono" x-text="'الضريبة: ' + parseFloat(currentInvoice?.tax_amount || 0).toLocaleString()"></p>
|
||||
</div>
|
||||
|
||||
<!-- Items -->
|
||||
<div x-show="currentInvoice?.items?.length > 0" class="mt-4 border border-gray-800 rounded-2xl overflow-hidden">
|
||||
<table class="w-full text-xs text-right">
|
||||
<thead class="bg-gray-900">
|
||||
<tr><th class="p-2">البند</th><th class="p-2">الكمية</th><th class="p-2">السعر</th></tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-800 bg-gray-900/30">
|
||||
<template x-for="item in currentInvoice?.items" :key="item.id">
|
||||
<tr>
|
||||
<td class="p-2" x-text="item.description"></td>
|
||||
<td class="p-2 font-mono text-gray-400" x-text="item.quantity"></td>
|
||||
<td class="p-2 font-mono text-emerald-400" x-text="item.unit_price"></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- QR Code -->
|
||||
<div x-show="currentInvoice?.jofotara?.qr_image_uri || currentInvoice?.qr_code" class="mt-6 p-4 bg-white rounded-2xl flex justify-center items-center">
|
||||
<img :src="currentInvoice?.jofotara?.qr_image_uri || currentInvoice?.qr_code" class="w-40 h-40 object-contain" alt="QR Code">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="mt-6 pt-6 border-t border-gray-800 space-y-3">
|
||||
<button x-show="currentInvoice?.status === 'extracted'" @click="approveInvoice" class="w-full bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold shadow-lg shadow-emerald-900/50 transition-all active:scale-95 btn-glow" :disabled="isBusy">
|
||||
<span x-show="!isBusy">✔️ اعتماد وإرسال للفوترة</span>
|
||||
<span x-show="isBusy">⏳ جاري الاعتماد...</span>
|
||||
</button>
|
||||
<button x-show="currentInvoice?.status === 'extracted'" class="w-full bg-red-900/30 text-red-400 hover:bg-red-900/50 py-3 rounded-2xl font-bold transition-all">❌ رفض</button>
|
||||
<div x-show="currentInvoice?.status === 'approved'" class="w-full bg-emerald-900/30 text-emerald-400 py-3 rounded-2xl font-bold text-center border border-emerald-900/50">
|
||||
✅ معتمدة مسبقاً
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -451,7 +555,80 @@
|
||||
|
||||
async viewInvoice(id) {
|
||||
this.currentInvoice = await this.apiRequest('v1/invoices/view&id=' + id);
|
||||
if (this.currentInvoice) this.showViewModal = true;
|
||||
if (this.currentInvoice) {
|
||||
this.showViewModal = true;
|
||||
|
||||
// Fallback QR code generation if missing from DB but we have invoice data
|
||||
if (!this.currentInvoice.jofotara?.qr_image_uri && !this.currentInvoice.qr_code && this.currentInvoice.status === 'approved') {
|
||||
try {
|
||||
const qr = new QRious({
|
||||
value: 'Invoice: ' + this.currentInvoice.invoice_number + '\nSeller: ' + this.currentInvoice.supplier_name + '\nTotal: ' + this.currentInvoice.grand_total,
|
||||
size: 250
|
||||
});
|
||||
this.currentInvoice.qr_code = qr.toDataURL();
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
selectedFile: null,
|
||||
isUploading: false,
|
||||
|
||||
async uploadInvoice() {
|
||||
if (!this.selectedFile) return alert('الرجاء اختيار ملف');
|
||||
if (!this.uploadData.company_id) return alert('الرجاء اختيار الشركة');
|
||||
|
||||
this.isUploading = true;
|
||||
const formData = new FormData();
|
||||
formData.append('company_id', this.uploadData.company_id);
|
||||
formData.append('invoice', this.selectedFile);
|
||||
|
||||
try {
|
||||
const res = await fetch('/index.php?route=v1/invoices/upload', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + this.token() },
|
||||
body: formData
|
||||
});
|
||||
const json = await res.json();
|
||||
this.isUploading = false;
|
||||
|
||||
if (json.success) {
|
||||
this.showUploadModal = false;
|
||||
this.selectedFile = null;
|
||||
await this.loadAll();
|
||||
this.viewInvoice(json.data.id);
|
||||
} else {
|
||||
this.showError(json.message);
|
||||
}
|
||||
} catch (e) {
|
||||
this.isUploading = false;
|
||||
this.showError('فشل الاتصال بالخادم أثناء الرفع');
|
||||
}
|
||||
},
|
||||
|
||||
async approveInvoice() {
|
||||
if (!this.currentInvoice || this.isBusy) return;
|
||||
this.isBusy = true;
|
||||
try {
|
||||
const res = await fetch('/index.php?route=v1/invoices/approve', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: this.currentInvoice.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('حدث خطأ أثناء الاعتماد');
|
||||
}
|
||||
},
|
||||
|
||||
logout() { localStorage.clear(); window.location.href = '/login.php'; }
|
||||
|
||||
Reference in New Issue
Block a user