Commit 0cc5b982 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Fix all column mismatches between code and live database schema

Critical SQL fixes (pages were crashing):
- GlobalSearch: remove non-existent branch_id/invoice_number, fix relationship
- RevenueWidget: payment_method → method
- FinancialReport: line_total → total_amount, balance_due → due_amount
- PaymentPlanCreate: rewrite to match actual payment_plans/installments schema

High-priority display fixes (features showing null):
- invoice_number → number (8 locations)
- balance_due → due_amount (6 locations)
- full_name_ar → full_name (7 locations, accessor doesn't exist)
- payment_method → method in print view
- subtotal → subtotal_amount
- description → notes on invoice
- line_total → total_amount on invoice items
- receipt_number → reference on payment

Medium fixes:
- Dashboard: fix format() call on string time column
- Participant card: access person fields via relationship

Low fixes:
- daily-financial-print: createdBy → creator relationship
- reports-page: payment_method key → method

Infrastructure:
- Add migration for cash_session_id on payments table
- Add cash_session_id to Payment model fillable
- Fix float → decimal:2 casts on Evaluation/EvaluationScore
- Update enum registry docs to match actual DB CHECK constraints
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 8257dec7
...@@ -40,13 +40,13 @@ DB::statement("ALTER TABLE table_name ADD CONSTRAINT table_name_column_check ...@@ -40,13 +40,13 @@ DB::statement("ALTER TABLE table_name ADD CONSTRAINT table_name_column_check
## Complete Enum Registry ## Complete Enum Registry
### InvoiceStatus ### InvoiceStatus
`'draft', 'sent', 'partially_paid', 'paid', 'overdue', 'cancelled', 'refunded'` `'draft', 'pending', 'awaiting_approval', 'sent', 'paid', 'partially_paid', 'overpaid', 'overdue', 'cancelled', 'refunded', 'partially_refunded', 'written_off', 'disputed'`
### PaymentStatus ### PaymentStatus
`'confirmed', 'pending', 'failed', 'refunded', 'partially_refunded'` `'pending', 'confirmed', 'failed', 'cancelled', 'refunded'`
### PaymentMethod ### PaymentMethod
`'cash', 'card', 'bank_transfer', 'wallet', 'cheque', 'other'` `'cash', 'card', 'bank_transfer', 'wallet', 'online', 'cheque', 'other'`
### TransactionType ### TransactionType
`'debit', 'credit'` `'debit', 'credit'`
......
...@@ -21,6 +21,7 @@ class Payment extends Model ...@@ -21,6 +21,7 @@ class Payment extends Model
protected $fillable = [ protected $fillable = [
'academy_id', 'academy_id',
'branch_id', 'branch_id',
'cash_session_id',
'invoice_id', 'invoice_id',
'reference', 'reference',
'direction', 'direction',
......
...@@ -39,7 +39,7 @@ class Evaluation extends Model ...@@ -39,7 +39,7 @@ class Evaluation extends Model
'evaluation_date' => 'date', 'evaluation_date' => 'date',
'period_from' => 'date', 'period_from' => 'date',
'period_to' => 'date', 'period_to' => 'date',
'overall_score' => 'float', 'overall_score' => 'decimal:2',
'approved_at' => 'datetime', 'approved_at' => 'datetime',
'shared_at' => 'datetime', 'shared_at' => 'datetime',
'metadata' => 'array', 'metadata' => 'array',
......
...@@ -17,7 +17,7 @@ class EvaluationScore extends Model ...@@ -17,7 +17,7 @@ class EvaluationScore extends Model
]; ];
protected $casts = [ protected $casts = [
'score' => 'float', 'score' => 'decimal:2',
]; ];
public function evaluation(): BelongsTo public function evaluation(): BelongsTo
......
...@@ -42,9 +42,9 @@ public function render() ...@@ -42,9 +42,9 @@ public function render()
$byMethod = Payment::where('status', 'confirmed') $byMethod = Payment::where('status', 'confirmed')
->where('created_at', '>=', $startDate) ->where('created_at', '>=', $startDate)
->select('payment_method', DB::raw('SUM(amount) as total')) ->select('method', DB::raw('SUM(amount) as total'))
->groupBy('payment_method') ->groupBy('method')
->pluck('total', 'payment_method') ->pluck('total', 'method')
->toArray(); ->toArray();
return view('livewire.dashboard.revenue-widget', [ return view('livewire.dashboard.revenue-widget', [
......
...@@ -57,7 +57,7 @@ private function calculateInstallments(): void ...@@ -57,7 +57,7 @@ private function calculateInstallments(): void
$invoice = Invoice::find($this->invoiceId); $invoice = Invoice::find($this->invoiceId);
if (!$invoice) return; if (!$invoice) return;
$remaining = $invoice->balance_due - ($this->downPayment * 100); $remaining = $invoice->due_amount - ($this->downPayment * 100);
if ($remaining <= 0 || $this->installmentCount < 1) { if ($remaining <= 0 || $this->installmentCount < 1) {
$this->installments = []; $this->installments = [];
return; return;
...@@ -95,22 +95,20 @@ public function save(): void ...@@ -95,22 +95,20 @@ public function save(): void
$plan = PaymentPlan::create([ $plan = PaymentPlan::create([
'academy_id' => $invoice->academy_id, 'academy_id' => $invoice->academy_id,
'invoice_id' => $invoice->id, 'invoice_id' => $invoice->id,
'total_amount' => $invoice->balance_due, 'total_installments' => $this->installmentCount,
'down_payment' => $this->downPayment * 100, 'paid_installments' => 0,
'installment_count' => $this->installmentCount, 'installment_amount' => $this->installments[0]['amount'] ?? 0,
'frequency' => $this->frequency, 'frequency' => $this->frequency,
'start_date' => $this->startDate, 'start_date' => $this->startDate,
'next_due_date' => $this->installments[0]['due_date'] ?? $this->startDate,
'status' => 'active', 'status' => 'active',
'created_by' => auth()->id(),
]); ]);
foreach ($this->installments as $inst) { foreach ($this->installments as $inst) {
$plan->installments()->create([ $plan->installments()->create([
'academy_id' => $invoice->academy_id, 'sequence' => $inst['number'],
'installment_number' => $inst['number'],
'due_date' => $inst['due_date'], 'due_date' => $inst['due_date'],
'amount' => $inst['amount'], 'amount' => $inst['amount'],
'paid_amount' => 0,
'status' => 'pending', 'status' => 'pending',
]); ]);
} }
...@@ -124,7 +122,7 @@ public function render() ...@@ -124,7 +122,7 @@ public function render()
{ {
$invoice = $this->invoiceId ? Invoice::find($this->invoiceId) : null; $invoice = $this->invoiceId ? Invoice::find($this->invoiceId) : null;
$invoices = Invoice::whereIn('status', ['sent', 'partially_paid', 'overdue']) $invoices = Invoice::whereIn('status', ['sent', 'partially_paid', 'overdue'])
->where('balance_due', '>', 0) ->where('due_amount', '>', 0)
->orderByDesc('created_at') ->orderByDesc('created_at')
->limit(50) ->limit(50)
->get(); ->get();
......
...@@ -56,13 +56,10 @@ public function search(): void ...@@ -56,13 +56,10 @@ public function search(): void
]; ];
} }
$invoices = Invoice::with('participant.person') $invoices = Invoice::with('billable')
->when($branchId, fn ($query) => $query->where('branch_id', $branchId))
->where(function ($query) use ($q) { ->where(function ($query) use ($q) {
$query->where('invoice_number', 'ilike', "%{$q}%") $query->where('number', 'ilike', "%{$q}%")
->orWhereHas('participant.person', function ($pq) use ($q) { ->orWhere('contact_name', 'ilike', "%{$q}%");
$pq->where('name_ar', 'ilike', "%{$q}%");
});
}) })
->limit(3) ->limit(3)
->get(); ->get();
...@@ -70,8 +67,8 @@ public function search(): void ...@@ -70,8 +67,8 @@ public function search(): void
foreach ($invoices as $inv) { foreach ($invoices as $inv) {
$results[] = [ $results[] = [
'type' => 'invoice', 'type' => 'invoice',
'label' => $inv->invoice_number, 'label' => $inv->number,
'subtitle' => ($inv->participant?->person?->name_ar ?? '') . ' — ' . number_format($inv->total_amount / 100, 2) . ' ج.م', 'subtitle' => ($inv->contact_name ?? $inv->billable?->person?->name_ar ?? '') . ' — ' . number_format($inv->total_amount / 100, 2) . ' ج.م',
'url' => route('invoices.show', $inv), 'url' => route('invoices.show', $inv),
]; ];
} }
......
...@@ -162,7 +162,7 @@ public function confirm(PaymentService $service): void ...@@ -162,7 +162,7 @@ public function confirm(PaymentService $service): void
], auth()->user()); ], auth()->user());
$this->completed = true; $this->completed = true;
$this->receipt_number = $payment->receipt_number ?? null; $this->receipt_number = $payment->reference ?? null;
$this->last_payment_uuid = $payment->uuid; $this->last_payment_uuid = $payment->uuid;
$this->paid_amount = $amountPiasters; $this->paid_amount = $amountPiasters;
$this->currentStep = 5; $this->currentStep = 5;
......
...@@ -64,12 +64,12 @@ public function render() ...@@ -64,12 +64,12 @@ public function render()
->get(); ->get();
$totalRevenue = $payments->sum('amount'); $totalRevenue = $payments->sum('amount');
$paymentsByMethod = $payments->groupBy('payment_method') $paymentsByMethod = $payments->groupBy('method')
->map(fn ($group) => $group->sum('amount')); ->map(fn ($group) => $group->sum('amount'));
$invoices = Invoice::whereBetween('created_at', [$this->dateFrom, $this->dateTo . ' 23:59:59'])->get(); $invoices = Invoice::whereBetween('created_at', [$this->dateFrom, $this->dateTo . ' 23:59:59'])->get();
$totalInvoiced = $invoices->sum('total_amount'); $totalInvoiced = $invoices->sum('total_amount');
$totalOutstanding = $invoices->whereIn('status', ['sent', 'partially_paid', 'overdue'])->sum('balance_due'); $totalOutstanding = $invoices->whereIn('status', ['sent', 'partially_paid', 'overdue'])->sum('due_amount');
$overdueCount = $invoices->where('status', 'overdue')->count(); $overdueCount = $invoices->where('status', 'overdue')->count();
$dailyRevenue = Payment::where('status', 'confirmed') $dailyRevenue = Payment::where('status', 'confirmed')
...@@ -85,7 +85,7 @@ public function render() ...@@ -85,7 +85,7 @@ public function render()
->where('invoices.status', '!=', 'cancelled') ->where('invoices.status', '!=', 'cancelled')
->whereBetween('invoices.created_at', [$this->dateFrom, $this->dateTo . ' 23:59:59']) ->whereBetween('invoices.created_at', [$this->dateFrom, $this->dateTo . ' 23:59:59'])
->whereNotNull('invoice_items.itemable_type') ->whereNotNull('invoice_items.itemable_type')
->select('invoice_items.description', DB::raw('SUM(invoice_items.line_total) as revenue'), DB::raw('COUNT(*) as count')) ->select('invoice_items.description', DB::raw('SUM(invoice_items.total_amount) as revenue'), DB::raw('COUNT(*) as count'))
->groupBy('invoice_items.description') ->groupBy('invoice_items.description')
->orderByDesc('revenue') ->orderByDesc('revenue')
->limit(10) ->limit(10)
......
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('payments', function (Blueprint $table) {
$table->foreignId('cash_session_id')->nullable()->after('branch_id')->constrained('cash_sessions')->nullOnDelete();
$table->index('cash_session_id');
});
}
public function down(): void
{
Schema::table('payments', function (Blueprint $table) {
$table->dropConstrainedForeignId('cash_session_id');
});
}
};
# Column Mismatch Fix Plan
Audited: 2026-07-01
Source of truth: Live PostgreSQL DB at 18.192.166.221 (elcaptainsportsonly)
Scope: 58 models, 50 migrations, 70+ views/components checked against 1142 live columns
---
## CRITICAL (SQL errors — pages crash)
### C1. GlobalSearch.php — Invoice query uses non-existent columns
**File:** `app/Livewire/GlobalSearch.php`
**Lines:** 59-73
**Bugs:**
- Line 60: `->where('branch_id', $branchId)``invoices` table has NO `branch_id`
- Line 62: `->where('invoice_number', 'like', ...)` — column is `number`, not `invoice_number`
- Line 59: `Invoice::with('participant.person')` — relationship is `billable`, not `participant`
- Line 73: `$invoice->invoice_number` — should be `$invoice->number`
**Fix:**
```php
// Remove branch_id filter (invoices don't have it), fix column name, fix relationship
Invoice::with('billable')
->where('number', 'like', "%{$this->query}%")
->orWhere('contact_name', 'like', "%{$this->query}%")
```
---
### C2. RevenueWidget.php — `payment_method` doesn't exist on `payments`
**File:** `app/Livewire/Dashboard/RevenueWidget.php`
**Lines:** 45-47
**Bug:** `->select('payment_method', ...)->groupBy('payment_method')` — column is `method`
**Fix:** Replace `payment_method` with `method` in select/groupBy
---
### C3. FinancialReport.php — Raw SQL references wrong columns
**File:** `app/Livewire/Reports/FinancialReport.php`
**Bugs:**
- Line 67: `$payments->groupBy('payment_method')` — should be `->groupBy('method')`
- Line 72: `balance_due` — should be `due_amount`
- Line 88: `SUM(invoice_items.line_total)` — should be `SUM(invoice_items.total_amount)`
**Fix:** Replace all three column names
---
### C4. PaymentPlanCreate.php — queries and writes non-existent columns
**File:** `app/Livewire/Financial/PaymentPlanCreate.php`
**Bugs:**
- Line 127: `->where('balance_due', '>', 0)` — should be `->where('due_amount', '>', 0)`
- Line 98: writes `total_amount` to payment_plans — doesn't exist
- Line 99: writes `down_payment` to payment_plans — doesn't exist
- Line 100: writes `installment_count` — should be `total_installments`
- Line 104: writes `created_by` to payment_plans — doesn't exist
- Line 109: writes `academy_id` to installments — doesn't exist
- Line 110: writes `installment_number` — should be `sequence`
- Line 112: writes `paid_amount` to installments — doesn't exist
**Fix:** Rewrite the `createPlan()` method to match actual schema:
```php
// payment_plans columns: invoice_id, academy_id, total_installments, paid_installments,
// installment_amount, frequency, start_date, next_due_date, status, notes, metadata
// installments columns: payment_plan_id, payment_id, sequence, amount, due_date, status, paid_at
```
---
## HIGH (Wrong data displayed — features broken but page loads)
### H1. `$invoice->invoice_number` used in 8 locations — should be `$invoice->number`
| File | Line(s) |
|------|---------|
| `resources/views/print/invoice.blade.php` | 5, 70 |
| `resources/views/livewire/dashboard.blade.php` | 197 |
| `resources/views/livewire/financial/payment-plan-create.blade.php` | 22 |
| `resources/views/livewire/receptionist/collect-payment-wizard.blade.php` | 179, 366 |
| `resources/views/reports/daily-financial-print.blade.php` | 76 |
| `app/Livewire/GlobalSearch.php` | 73 |
**Fix:** Find-replace `->invoice_number` with `->number` in all files
---
### H2. `$invoice->balance_due` used in 6 locations — should be `$invoice->due_amount`
| File | Line(s) |
|------|---------|
| `resources/views/print/invoice.blade.php` | 158 |
| `resources/views/livewire/financial/payment-plan-create.blade.php` | 22, 31 |
| `app/Livewire/Financial/PaymentPlanCreate.php` | 60, 98 |
| `app/Livewire/Reports/FinancialReport.php` | 72 |
**Fix:** Find-replace `->balance_due` with `->due_amount` and `'balance_due'` with `'due_amount'`
---
### H3. `$participant->full_name_ar` in 7 locations — accessor doesn't exist
The `getFullNameAttribute()` already returns Arabic name. There is no `getFullNameArAttribute()`.
| File | Line(s) |
|------|---------|
| `resources/views/print/certificate.blade.php` | 39 |
| `resources/views/print/participant-card.blade.php` | 5, 42, 46 |
| `resources/views/print/group-schedule.blade.php` | 86 |
| `resources/views/print/invoice.blade.php` | 85 |
| `resources/views/livewire/participants/bulk-status-change.blade.php` | 70, 92 |
**Fix:** Replace `->full_name_ar` with `->full_name`
---
### H4. `$payment->payment_method` in print view — column is `method`
**File:** `resources/views/print/invoice.blade.php` line 179
**Fix:** Replace `$payment->payment_method` with `$payment->method`
---
### H5. `$invoice->subtotal` — should be `$invoice->subtotal_amount`
**File:** `resources/views/print/invoice.blade.php` line 127
**Fix:** Replace `->subtotal` with `->subtotal_amount`
---
### H6. `$invoice->description` — should be `$invoice->notes`
**File:** `resources/views/livewire/receptionist/collect-payment-wizard.blade.php` line 180
**Fix:** Replace `->description` with `->notes`
---
### H7. `$item->line_total` in print view — should be `$item->total_amount`
**File:** `resources/views/print/invoice.blade.php` line 116
**Fix:** Replace `$item->line_total` with `$item->total_amount`
---
### H8. Reports page — `$method['payment_method']` key doesn't exist
**File:** `resources/views/livewire/reports/reports-page.blade.php` line 92
**Fix:** Replace `$method['payment_method']` with `$method['method']`
---
### H9. `$payment->receipt_number` — doesn't exist on payments table
**File:** `app/Livewire/Receptionist/CollectPaymentWizard.php` line 165
**Fix:** Remove or use `$payment->reference` (actual column) or derive from POS transaction
---
## MEDIUM (Type errors / wrong table access)
### M1. Dashboard calls `->format('H:i')` on a plain string
**File:** `resources/views/livewire/dashboard.blade.php` line 169
**Bug:** `$session->start_time?->format('H:i')` — start_time is a string, not Carbon
**Fix:** Use `\Carbon\Carbon::parse($session->start_time)->format('H:i')` or add cast to model
---
### M2. Participant card accesses person fields directly on participant
**File:** `resources/views/print/participant-card.blade.php` lines 54, 57, 60, 63
**Bug:** `$participant->date_of_birth`, `$participant->gender` — these are on `people` table
**Fix:** Use `$participant->person->date_of_birth` and `$participant->person->gender`
---
## LOW (Silent relationship failures)
### L1. Dashboard uses non-existent `$invoice->participant` relationship
**File:** `resources/views/livewire/dashboard.blade.php` line 197
**Bug:** `$inv->participant?->person?->name_ar` — relationship is `billable`
**Fix:** Use `$inv->billable?->person?->name_ar` or `$inv->contact_name`
---
### L2. Daily report uses `$payment->createdBy` — relationship is `creator`
**File:** `resources/views/reports/daily-financial-print.blade.php` line 77
**Fix:** Replace `$payment->createdBy` with `$payment->creator`
---
## MIGRATION NEEDED
### MIG1. Add `cash_session_id` to `payments` table
**Why:** Both `Payment::cashSession()` and `CashSession::payments()` reference this FK but it doesn't exist.
```php
Schema::table('payments', function (Blueprint $table) {
$table->foreignId('cash_session_id')->nullable()->after('branch_id')->constrained('cash_sessions')->nullOnDelete();
$table->index('cash_session_id');
});
```
Also add `'cash_session_id'` to Payment model `$fillable`.
---
## MODEL FIXES
### MOD1. Evaluation + EvaluationScore — float cast should be decimal
**Files:**
- `app/Domain/Training/Models/Evaluation.php` — change `'overall_score' => 'float'` to `'decimal:2'`
- `app/Domain/Training/Models/EvaluationScore.php` — change `'score' => 'float'` to `'decimal:2'`
---
### MOD2. Remove `Payment::cashSession()` relationship until migration runs
If migration MIG1 won't run immediately, the relationship should be commented out to prevent runtime errors.
---
## NAMING CONVENTION ISSUE (Non-breaking, future consideration)
### NAM1. `training_groups.program_id` should be `training_program_id`
Per project rules in `01-migration-first.md`. This is a rename that would require:
- Migration to rename column
- Update TrainingGroup model fillable + relationship
- Update all queries/views referencing `program_id`
- Risk: breaking change across many files
**Recommendation:** Low priority. Document as tech debt. The inconsistency works; fixing it mid-development risks introducing bugs.
---
## DOCUMENTATION DRIFT (Update rule files)
### DOC1. Enum registry in `16-enums-and-checks.md` is outdated
| Enum | Documented | Actual (migration) |
|------|-----------|-------------------|
| InvoiceStatus | 7 values | 13 values (add: pending, awaiting_approval, overpaid, partially_refunded, written_off, disputed) |
| PaymentStatus | 5 values | 5 values but different (has `cancelled` instead of `partially_refunded`) |
| PaymentMethod | 6 values | 7 values (add: `online`) |
**Fix:** Update `.claude/rules/16-enums-and-checks.md` to match actual migration values.
---
## EXECUTION ORDER
1. **C1-C4** — Fix critical SQL errors (pages literally crash)
2. **H1-H9** — Fix wrong column names (features show null/empty data)
3. **M1-M2** — Fix type errors and wrong table access
4. **L1-L2** — Fix silent relationship failures
5. **MIG1** — Add missing migration + update model fillable
6. **MOD1-MOD2** — Fix model casts
7. **DOC1** — Update rule file documentation
8. **NAM1** — Consider later (tech debt)
---
## STATS
| Category | Count |
|----------|-------|
| Files to modify | ~18 |
| Critical (crashes) | 4 issues |
| High (broken features) | 9 issues |
| Medium (conditional errors) | 2 issues |
| Low (silent failures) | 2 issues |
| Migrations needed | 1 |
| Model fixes | 2 |
| Doc updates | 1 |
| **Total fixes** | **21** |
...@@ -166,7 +166,7 @@ ...@@ -166,7 +166,7 @@
@foreach($todaySchedule as $session) @foreach($todaySchedule as $session)
<div class="flex items-center gap-3 p-2.5 sm:p-3 bg-gray-50 rounded-lg"> <div class="flex items-center gap-3 p-2.5 sm:p-3 bg-gray-50 rounded-lg">
<div class="text-center min-w-[44px]"> <div class="text-center min-w-[44px]">
<span class="text-xs sm:text-sm font-bold text-blue-600" dir="ltr">{{ $session->start_time?->format('H:i') }}</span> <span class="text-xs sm:text-sm font-bold text-blue-600" dir="ltr">{{ substr($session->start_time, 0, 5) }}</span>
</div> </div>
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<p class="text-xs sm:text-sm font-medium text-gray-800 truncate">{{ $session->group?->name_ar ?? '' }}</p> <p class="text-xs sm:text-sm font-medium text-gray-800 truncate">{{ $session->group?->name_ar ?? '' }}</p>
...@@ -194,7 +194,7 @@ ...@@ -194,7 +194,7 @@
@foreach($overdueInvoices as $inv) @foreach($overdueInvoices as $inv)
<a href="{{ route('invoices.show', $inv) }}" class="flex items-center justify-between p-2.5 sm:p-3 bg-red-50 rounded-lg hover:bg-red-100 transition-colors"> <a href="{{ route('invoices.show', $inv) }}" class="flex items-center justify-between p-2.5 sm:p-3 bg-red-50 rounded-lg hover:bg-red-100 transition-colors">
<div class="min-w-0 flex-1 me-2"> <div class="min-w-0 flex-1 me-2">
<p class="text-xs sm:text-sm font-medium text-gray-800 truncate">{{ $inv->participant?->person?->name_ar ?? $inv->invoice_number }}</p> <p class="text-xs sm:text-sm font-medium text-gray-800 truncate">{{ $inv->contact_name ?? $inv->number }}</p>
<p class="text-xs text-red-600">{{ __('استحقاق:') }} {{ $inv->due_date?->format('Y-m-d') }}</p> <p class="text-xs text-red-600">{{ __('استحقاق:') }} {{ $inv->due_date?->format('Y-m-d') }}</p>
</div> </div>
<span class="text-xs sm:text-sm font-bold text-red-700 whitespace-nowrap" dir="ltr">{{ number_format(($inv->total_amount - $inv->paid_amount) / 100, 2) }} {{ __('ج.م') }}</span> <span class="text-xs sm:text-sm font-bold text-red-700 whitespace-nowrap" dir="ltr">{{ number_format(($inv->total_amount - $inv->paid_amount) / 100, 2) }} {{ __('ج.م') }}</span>
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<option value="">{{ __('اختر فاتورة') }}</option> <option value="">{{ __('اختر فاتورة') }}</option>
@foreach($invoices as $inv) @foreach($invoices as $inv)
<option value="{{ $inv->id }}"> <option value="{{ $inv->id }}">
{{ $inv->invoice_number }} — {{ format_money($inv->balance_due) }} {{ $inv->number }} — {{ format_money($inv->due_amount) }}
</option> </option>
@endforeach @endforeach
</select> </select>
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
@if($invoice) @if($invoice)
<div class="p-3 bg-blue-50 rounded-lg text-sm"> <div class="p-3 bg-blue-50 rounded-lg text-sm">
<p>{{ __('إجمالي الفاتورة') }}: <strong dir="ltr">{{ format_money($invoice->total_amount) }}</strong></p> <p>{{ __('إجمالي الفاتورة') }}: <strong dir="ltr">{{ format_money($invoice->total_amount) }}</strong></p>
<p>{{ __('المتبقي') }}: <strong dir="ltr">{{ format_money($invoice->balance_due) }}</strong></p> <p>{{ __('المتبقي') }}: <strong dir="ltr">{{ format_money($invoice->due_amount) }}</strong></p>
</div> </div>
@endif @endif
......
...@@ -67,7 +67,7 @@ class="w-full sm:w-auto px-4 py-2.5 text-sm bg-red-600 text-white rounded-lg hov ...@@ -67,7 +67,7 @@ class="w-full sm:w-auto px-4 py-2.5 text-sm bg-red-600 text-white rounded-lg hov
<input type="checkbox" wire:model="selectedIds" value="{{ $p->id }}" <input type="checkbox" wire:model="selectedIds" value="{{ $p->id }}"
class="w-4 h-4 text-blue-600 rounded border-gray-300"> class="w-4 h-4 text-blue-600 rounded border-gray-300">
</td> </td>
<td class="px-4 py-3 text-sm text-gray-800">{{ $p->full_name_ar }}</td> <td class="px-4 py-3 text-sm text-gray-800">{{ $p->full_name }}</td>
<td class="px-4 py-3"> <td class="px-4 py-3">
<span class="px-2 py-0.5 text-xs rounded-full bg-gray-100 text-gray-600">{{ __($p->status) }}</span> <span class="px-2 py-0.5 text-xs rounded-full bg-gray-100 text-gray-600">{{ __($p->status) }}</span>
</td> </td>
...@@ -89,7 +89,7 @@ class="w-4 h-4 text-blue-600 rounded border-gray-300"> ...@@ -89,7 +89,7 @@ class="w-4 h-4 text-blue-600 rounded border-gray-300">
<input type="checkbox" wire:model="selectedIds" value="{{ $p->id }}" <input type="checkbox" wire:model="selectedIds" value="{{ $p->id }}"
class="w-4 h-4 text-blue-600 rounded border-gray-300 shrink-0"> class="w-4 h-4 text-blue-600 rounded border-gray-300 shrink-0">
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-800 truncate">{{ $p->full_name_ar }}</p> <p class="text-sm font-medium text-gray-800 truncate">{{ $p->full_name }}</p>
<div class="flex items-center gap-2 mt-1"> <div class="flex items-center gap-2 mt-1">
<span class="px-2 py-0.5 text-xs rounded-full bg-gray-100 text-gray-600">{{ __($p->status) }}</span> <span class="px-2 py-0.5 text-xs rounded-full bg-gray-100 text-gray-600">{{ __($p->status) }}</span>
<span class="text-xs text-gray-500" dir="ltr">{{ $p->created_at?->format('Y-m-d') }}</span> <span class="text-xs text-gray-500" dir="ltr">{{ $p->created_at?->format('Y-m-d') }}</span>
......
...@@ -176,8 +176,8 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-amber-600 text-white ...@@ -176,8 +176,8 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-amber-600 text-white
hover:border-gray-300"> hover:border-gray-300">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<p class="font-bold text-gray-800">{{ $invoice->invoice_number }}</p> <p class="font-bold text-gray-800">{{ $invoice->number }}</p>
<p class="text-sm text-gray-500 mt-1">{{ $invoice->description ?? __('فاتورة اشتراك') }}</p> <p class="text-sm text-gray-500 mt-1">{{ $invoice->notes ?? __('فاتورة اشتراك') }}</p>
@if($invoice->due_date) @if($invoice->due_date)
<p class="text-xs mt-1 {{ $invoice->due_date->isPast() ? 'text-red-600 font-medium' : 'text-gray-400' }}"> <p class="text-xs mt-1 {{ $invoice->due_date->isPast() ? 'text-red-600 font-medium' : 'text-gray-400' }}">
{{ __('تاريخ الاستحقاق') }}: <span dir="ltr">{{ $invoice->due_date->format('Y-m-d') }}</span> {{ __('تاريخ الاستحقاق') }}: <span dir="ltr">{{ $invoice->due_date->format('Y-m-d') }}</span>
...@@ -363,7 +363,7 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-amber-600 text-white ...@@ -363,7 +363,7 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-amber-600 text-white
@if($selectedInvoice) @if($selectedInvoice)
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-gray-600">{{ __('رقم الفاتورة') }}</span> <span class="text-gray-600">{{ __('رقم الفاتورة') }}</span>
<span class="font-medium text-gray-800" dir="ltr">{{ $selectedInvoice->invoice_number }}</span> <span class="font-medium text-gray-800" dir="ltr">{{ $selectedInvoice->number }}</span>
</div> </div>
@endif @endif
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
......
...@@ -89,7 +89,7 @@ class="w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus ...@@ -89,7 +89,7 @@ class="w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus
<tbody class="divide-y divide-gray-200"> <tbody class="divide-y divide-gray-200">
@foreach($reportData['payment_methods'] as $method) @foreach($reportData['payment_methods'] as $method)
<tr> <tr>
<td class="px-4 py-3 text-sm text-gray-800">{{ __($method['payment_method'] ?? '-') }}</td> <td class="px-4 py-3 text-sm text-gray-800">{{ __($method['method'] ?? '-') }}</td>
<td class="px-4 py-3 text-sm text-gray-800" dir="ltr">{{ number_format(($method['total'] ?? 0) / 100, 2) }} {{ __('ج.م') }}</td> <td class="px-4 py-3 text-sm text-gray-800" dir="ltr">{{ number_format(($method['total'] ?? 0) / 100, 2) }} {{ __('ج.م') }}</td>
<td class="px-4 py-3 text-sm text-gray-800" dir="ltr">{{ number_format($method['count'] ?? 0) }}</td> <td class="px-4 py-3 text-sm text-gray-800" dir="ltr">{{ number_format($method['count'] ?? 0) }}</td>
</tr> </tr>
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
<h1>{{ __('شهادة حضور') }}</h1> <h1>{{ __('شهادة حضور') }}</h1>
<p class="subtitle">{{ __('تشهد الأكاديمية بأن') }}</p> <p class="subtitle">{{ __('تشهد الأكاديمية بأن') }}</p>
<p class="recipient">{{ $participant->full_name_ar }}</p> <p class="recipient">{{ $participant->full_name }}</p>
<div class="details"> <div class="details">
<p>{{ __('قد أتم بنجاح حضور برنامج') }}</p> <p>{{ __('قد أتم بنجاح حضور برنامج') }}</p>
......
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
@foreach($group->enrollments as $i => $enrollment) @foreach($group->enrollments as $i => $enrollment)
<tr> <tr>
<td>{{ $i + 1 }}</td> <td>{{ $i + 1 }}</td>
<td>{{ $enrollment->participant?->full_name_ar ?? '-' }}</td> <td>{{ $enrollment->participant?->full_name ?? '-' }}</td>
<td dir="ltr">{{ $enrollment->created_at?->format('Y-m-d') }}</td> <td dir="ltr">{{ $enrollment->created_at?->format('Y-m-d') }}</td>
</tr> </tr>
@endforeach @endforeach
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<html dir="rtl" lang="ar"> <html dir="rtl" lang="ar">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{{ __('فاتورة') }} - {{ $invoice->invoice_number }}</title> <title>{{ __('فاتورة') }} - {{ $invoice->number }}</title>
@php @php
$branding = app(\App\Domain\Shared\Services\SettingsService::class); $branding = app(\App\Domain\Shared\Services\SettingsService::class);
$brandLogo = $branding->get('branding.logo'); $brandLogo = $branding->get('branding.logo');
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
</div> </div>
</div> </div>
<div class="meta"> <div class="meta">
<p><strong>{{ __('رقم الفاتورة') }}:</strong> {{ $invoice->invoice_number }}</p> <p><strong>{{ __('رقم الفاتورة') }}:</strong> {{ $invoice->number }}</p>
<p><strong>{{ __('التاريخ') }}:</strong> {{ $invoice->issue_date ?? $invoice->created_at?->format('Y-m-d') }}</p> <p><strong>{{ __('التاريخ') }}:</strong> {{ $invoice->issue_date ?? $invoice->created_at?->format('Y-m-d') }}</p>
@if($invoice->due_date) @if($invoice->due_date)
<p><strong>{{ __('تاريخ الاستحقاق') }}:</strong> {{ $invoice->due_date }}</p> <p><strong>{{ __('تاريخ الاستحقاق') }}:</strong> {{ $invoice->due_date }}</p>
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
<div class="party"> <div class="party">
<h3>{{ __('العميل') }}</h3> <h3>{{ __('العميل') }}</h3>
@if($invoice->billable) @if($invoice->billable)
<p><strong>{{ $invoice->billable->full_name_ar ?? $invoice->billable->name_ar ?? '-' }}</strong></p> <p><strong>{{ $invoice->billable->full_name ?? $invoice->billable->name_ar ?? '-' }}</strong></p>
@if($invoice->billable->phone ?? null) @if($invoice->billable->phone ?? null)
<p dir="ltr">{{ $invoice->billable->phone }}</p> <p dir="ltr">{{ $invoice->billable->phone }}</p>
@endif @endif
...@@ -113,7 +113,7 @@ ...@@ -113,7 +113,7 @@
<td>{{ $item->description }}</td> <td>{{ $item->description }}</td>
<td>{{ $item->quantity }}</td> <td>{{ $item->quantity }}</td>
<td dir="ltr">{{ format_money($item->unit_price) }}</td> <td dir="ltr">{{ format_money($item->unit_price) }}</td>
<td dir="ltr">{{ format_money($item->line_total) }}</td> <td dir="ltr">{{ format_money($item->total_amount) }}</td>
</tr> </tr>
@empty @empty
<tr><td colspan="5" style="text-align: center; color: #999;">{{ __('لا توجد بنود') }}</td></tr> <tr><td colspan="5" style="text-align: center; color: #999;">{{ __('لا توجد بنود') }}</td></tr>
...@@ -124,7 +124,7 @@ ...@@ -124,7 +124,7 @@
<div class="totals"> <div class="totals">
<div class="row"> <div class="row">
<span>{{ __('المجموع الفرعي') }}</span> <span>{{ __('المجموع الفرعي') }}</span>
<span dir="ltr">{{ format_money($invoice->subtotal ?? $invoice->total_amount) }}</span> <span dir="ltr">{{ format_money($invoice->subtotal_amount ?? $invoice->total_amount) }}</span>
</div> </div>
@if($invoice->discount_amount > 0) @if($invoice->discount_amount > 0)
<div class="row"> <div class="row">
...@@ -155,7 +155,7 @@ ...@@ -155,7 +155,7 @@
</div> </div>
<div class="row" style="font-weight: bold; color: #dc2626;"> <div class="row" style="font-weight: bold; color: #dc2626;">
<span>{{ __('المتبقي') }}</span> <span>{{ __('المتبقي') }}</span>
<span dir="ltr">{{ format_money($invoice->balance_due) }}</span> <span dir="ltr">{{ format_money($invoice->due_amount) }}</span>
</div> </div>
@endif @endif
</div> </div>
...@@ -176,7 +176,7 @@ ...@@ -176,7 +176,7 @@
@foreach($invoice->payments as $payment) @foreach($invoice->payments as $payment)
<tr> <tr>
<td dir="ltr">{{ $payment->created_at?->format('Y-m-d') }}</td> <td dir="ltr">{{ $payment->created_at?->format('Y-m-d') }}</td>
<td>{{ __($payment->payment_method) }}</td> <td>{{ __($payment->method) }}</td>
<td dir="ltr">{{ format_money($payment->amount) }}</td> <td dir="ltr">{{ format_money($payment->amount) }}</td>
<td>{{ __($payment->status) }}</td> <td>{{ __($payment->status) }}</td>
</tr> </tr>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<html dir="rtl" lang="ar"> <html dir="rtl" lang="ar">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{{ __('بطاقة عضوية') }} - {{ $participant->full_name_ar }}</title> <title>{{ __('بطاقة عضوية') }} - {{ $participant->full_name }}</title>
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Cairo', 'Noto Sans Arabic', sans-serif; direction: rtl; padding: 40px; background: #f5f5f5; display: flex; justify-content: center; align-items: center; min-height: 100vh; } body { font-family: 'Cairo', 'Noto Sans Arabic', sans-serif; direction: rtl; padding: 40px; background: #f5f5f5; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
...@@ -39,11 +39,11 @@ ...@@ -39,11 +39,11 @@
</div> </div>
<div class="avatar"> <div class="avatar">
{{ mb_substr($participant->full_name_ar ?? $participant->full_name ?? '?', 0, 1) }} {{ mb_substr($participant->full_name ?? '?', 0, 1) }}
</div> </div>
<div class="card-body"> <div class="card-body">
<p class="name">{{ $participant->full_name_ar }}</p> <p class="name">{{ $participant->full_name }}</p>
<span class="id-badge">{{ $participant->uuid ?? "#{$participant->id}" }}</span> <span class="id-badge">{{ $participant->uuid ?? "#{$participant->id}" }}</span>
<div style="text-align: right; margin-top: 12px;"> <div style="text-align: right; margin-top: 12px;">
...@@ -51,16 +51,16 @@ ...@@ -51,16 +51,16 @@
<label>{{ __('الحالة') }}</label> <label>{{ __('الحالة') }}</label>
<span>{{ __($participant->status) }}</span> <span>{{ __($participant->status) }}</span>
</div> </div>
@if($participant->date_of_birth) @if($participant->person?->date_of_birth)
<div class="info-row"> <div class="info-row">
<label>{{ __('تاريخ الميلاد') }}</label> <label>{{ __('تاريخ الميلاد') }}</label>
<span dir="ltr">{{ $participant->date_of_birth }}</span> <span dir="ltr">{{ $participant->person?->date_of_birth }}</span>
</div> </div>
@endif @endif
@if($participant->gender) @if($participant->person?->gender)
<div class="info-row"> <div class="info-row">
<label>{{ __('النوع') }}</label> <label>{{ __('النوع') }}</label>
<span>{{ $participant->gender === 'male' ? __('ذكر') : __('أنثى') }}</span> <span>{{ $participant->person?->gender === 'male' ? __('ذكر') : __('أنثى') }}</span>
</div> </div>
@endif @endif
</div> </div>
......
...@@ -73,8 +73,8 @@ ...@@ -73,8 +73,8 @@
<td>{{ $payment->reference ?? '-' }}</td> <td>{{ $payment->reference ?? '-' }}</td>
<td class="ltr">{{ number_format($payment->amount / 100, 2) }}</td> <td class="ltr">{{ number_format($payment->amount / 100, 2) }}</td>
<td>{{ $payment->method?->value ?? '' }}</td> <td>{{ $payment->method?->value ?? '' }}</td>
<td>{{ $payment->invoice?->invoice_number ?? '-' }}</td> <td>{{ $payment->invoice?->number ?? '-' }}</td>
<td>{{ $payment->createdBy?->name ?? '' }}</td> <td>{{ $payment->creator?->name ?? '' }}</td>
</tr> </tr>
@endforeach @endforeach
<tr class="total-row"> <tr class="total-row">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment