Commit 63b67cb9 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Track invoice/receipt print count + rename dashboard income to تحصيلات

- Add print_count and last_printed_at to invoices and payments tables
- InvoicePrintController increments count on every print view
- ReceiptController increments count on every payment receipt print
- Print count badge shown next to print button on invoice show page
- Print receipt button component accepts optional printCount prop
- Rename dashboard revenue widget label from الإيرادات to التحصيلات
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 1cae2631
......@@ -42,6 +42,8 @@ class Invoice extends Model
'cancelled_at',
'notes',
'metadata',
'print_count',
'last_printed_at',
'created_by',
'approved_by',
];
......@@ -62,6 +64,7 @@ protected function casts(): array
'paid_at' => 'datetime',
'cancelled_at' => 'datetime',
'metadata' => 'array',
'last_printed_at' => 'datetime',
];
}
......
......@@ -37,6 +37,8 @@ class Payment extends Model
'notes',
'gateway_data',
'metadata',
'print_count',
'last_printed_at',
'received_by',
'created_by',
];
......@@ -52,6 +54,7 @@ protected function casts(): array
'failed_at' => 'datetime',
'gateway_data' => 'array',
'metadata' => 'array',
'last_printed_at' => 'datetime',
];
}
......
......@@ -13,6 +13,9 @@ public function __invoke(Invoice $invoice)
$invoice->load(['items', 'payments', 'billable']);
$invoice->increment('print_count');
$invoice->update(['last_printed_at' => now()]);
return view('print.invoice', compact('invoice'));
}
}
......@@ -18,6 +18,9 @@ public function paymentPrint(Payment $payment)
{
$payment->load(['invoice.billable.person', 'invoice.items', 'creator']);
$payment->increment('print_count');
$payment->update(['last_printed_at' => now()]);
return view('print.payment-receipt', compact('payment'));
}
}
<?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('invoices', function (Blueprint $table) {
$table->unsignedInteger('print_count')->default(0)->after('metadata');
$table->timestamp('last_printed_at')->nullable()->after('print_count');
});
Schema::table('payments', function (Blueprint $table) {
$table->unsignedInteger('print_count')->default(0)->after('metadata');
$table->timestamp('last_printed_at')->nullable()->after('print_count');
});
}
public function down(): void
{
Schema::table('invoices', function (Blueprint $table) {
$table->dropColumn(['print_count', 'last_printed_at']);
});
Schema::table('payments', function (Blueprint $table) {
$table->dropColumn(['print_count', 'last_printed_at']);
});
}
};
@props(['route', 'size' => 'md', 'label' => null])
@props(['route', 'size' => 'md', 'label' => null, 'printCount' => 0])
@php
$sizeClasses = match($size) {
......@@ -17,4 +17,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/>
</svg>
{{ $label ?? __('طباعة الإيصال') }}
@if($printCount > 0)
<span class="px-1.5 py-0.5 bg-white/20 rounded text-xs">{{ $printCount }}</span>
@endif
</a>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4">
<div class="flex items-center justify-between mb-3">
<h3 class="text-sm font-semibold text-gray-700">{{ __('الإيرادات') }}</h3>
<h3 class="text-sm font-semibold text-gray-700">{{ __('التحصيلات') }}</h3>
<select wire:model.live="period" class="text-xs rounded border-gray-200 py-1 px-2">
<option value="today">{{ __('اليوم') }}</option>
<option value="week">{{ __('الأسبوع') }}</option>
......
......@@ -6,6 +6,9 @@
class="inline-flex items-center gap-1.5 text-sm font-medium text-green-600 hover:text-green-800 active:text-green-900 transition-colors">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/></svg>
{{ __('طباعة') }}
@if($invoice->print_count > 0)
<span class="px-1.5 py-0.5 bg-gray-100 text-gray-600 rounded text-xs font-normal">{{ $invoice->print_count }}</span>
@endif
</a>
<a href="{{ route('invoices.list') }}" wire:navigate
class="text-gray-600 hover:text-gray-800 text-sm font-medium">
......@@ -229,7 +232,7 @@ class="text-xs border border-amber-300 rounded-md px-2 py-1 focus:ring-2 focus:r
</td>
<td class="px-4 py-3 text-center">
<div class="flex items-center justify-center gap-2">
<x-ui.print-receipt-button :route="route('receipts.payment.print', $payment->uuid)" size="sm" />
<x-ui.print-receipt-button :route="route('receipts.payment.print', $payment->uuid)" size="sm" :printCount="$payment->print_count ?? 0" />
@if($pStatusValue === 'confirmed' && $payment->direction === 'inbound')
@can('refunds.initiate')
<button type="button"
......
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