Commit eb96c326 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Add sales receipts tab to product detail page

Shows all POS receipts (RCP-*) containing this product with: receipt
number, customer, total, payment status, method, cashier, date.
Each receipt has a print link and invoice link for further actions.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent e3750618
......@@ -6,6 +6,8 @@
use App\Domain\Inventory\Enums\MovementDirection;
use App\Domain\Inventory\Models\InventoryMovement;
use App\Domain\Inventory\Models\Product;
use App\Domain\POS\Models\POSTransaction;
use App\Domain\POS\Models\POSTransactionItem;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
......@@ -85,6 +87,18 @@ public function render()
$margin = round((($this->product->selling_price - $this->product->cost_price) / $this->product->selling_price) * 100, 1);
}
// POS Receipts containing this product
$transactionIds = POSTransactionItem::where('item_type', 'product')
->where('item_id', $this->product->id)
->pluck('pos_transaction_id')
->unique();
$salesReceipts = POSTransaction::whereIn('id', $transactionIds)
->with(['participant.person', 'processedBy', 'items', 'invoice'])
->orderByDesc('created_at')
->limit(50)
->get();
return view('livewire.inventory.product-show', [
'levels' => $levels,
'totalOnHand' => $totalOnHand,
......@@ -98,6 +112,7 @@ public function render()
'collectedPercent' => $collectedPercent,
'avgMonthlyMovement' => $avgMonthlyMovement,
'margin' => $margin,
'salesReceipts' => $salesReceipts,
]);
}
}
......@@ -108,6 +108,14 @@ class="px-6 py-3 text-sm font-medium border-b-2 transition-colors">
class="px-6 py-3 text-sm font-medium border-b-2 transition-colors">
{{ __('الحركات') }}
</button>
<button @click="activeTab = 'sales'"
:class="activeTab === 'sales' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
class="px-6 py-3 text-sm font-medium border-b-2 transition-colors">
{{ __('المبيعات') }}
@if($salesReceipts->count() > 0)
<span class="ms-1 px-1.5 py-0.5 text-[10px] rounded-full bg-blue-100 text-blue-700">{{ $salesReceipts->count() }}</span>
@endif
</button>
<button @click="activeTab = 'analytics'"
:class="activeTab === 'analytics' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
class="px-6 py-3 text-sm font-medium border-b-2 transition-colors">
......@@ -345,6 +353,96 @@ class="px-6 py-3 text-sm font-medium border-b-2 transition-colors">
@endif
</div>
{{-- Sales/Receipts Tab --}}
<div x-show="activeTab === 'sales'" x-cloak>
@if($salesReceipts->count() > 0)
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th class="px-4 py-3 text-start font-medium text-gray-600">{{ __('رقم الإيصال') }}</th>
<th class="px-4 py-3 text-start font-medium text-gray-600">{{ __('العميل') }}</th>
<th class="px-4 py-3 text-center font-medium text-gray-600">{{ __('الإجمالي') }}</th>
<th class="px-4 py-3 text-center font-medium text-gray-600">{{ __('حالة الدفع') }}</th>
<th class="px-4 py-3 text-center font-medium text-gray-600">{{ __('طريقة الدفع') }}</th>
<th class="px-4 py-3 text-start font-medium text-gray-600">{{ __('الكاشير') }}</th>
<th class="px-4 py-3 text-center font-medium text-gray-600">{{ __('التاريخ') }}</th>
<th class="px-4 py-3 text-center font-medium text-gray-600">{{ __('إجراءات') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@foreach($salesReceipts as $txn)
@php
$statusColors = [
'paid' => 'green',
'partially_paid' => 'amber',
'pending' => 'gray',
'refunded' => 'red',
];
$statusLabels = [
'paid' => 'مدفوع',
'partially_paid' => 'مدفوع جزئياً',
'pending' => 'معلّق',
'refunded' => 'مسترد',
];
$paymentStatus = $txn->payment_status->value ?? $txn->payment_status ?? 'pending';
$color = $statusColors[$paymentStatus] ?? 'gray';
$label = $statusLabels[$paymentStatus] ?? $paymentStatus;
$methodLabels = [
'cash' => 'نقدي',
'card' => 'بطاقة',
'wallet' => 'محفظة',
'split' => 'مقسم',
'bank_transfer' => 'تحويل',
];
$methodValue = $txn->payment_method->value ?? $txn->payment_method ?? '';
@endphp
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 font-mono text-gray-700" dir="ltr">{{ $txn->receipt_number }}</td>
<td class="px-4 py-3 text-gray-800">
{{ $txn->participant?->person?->name_ar ?? __('عميل عابر') }}
</td>
<td class="px-4 py-3 text-center font-medium" dir="ltr">
{{ number_format($txn->total_amount / 100, 2) }} {{ __('ج.م') }}
</td>
<td class="px-4 py-3 text-center">
<span class="px-2 py-0.5 text-xs rounded-full bg-{{ $color }}-100 text-{{ $color }}-700">{{ __($label) }}</span>
</td>
<td class="px-4 py-3 text-center text-gray-600">
{{ $methodLabels[$methodValue] ?? $methodValue }}
</td>
<td class="px-4 py-3 text-gray-600 text-xs">{{ $txn->processedBy?->name ?? '-' }}</td>
<td class="px-4 py-3 text-center text-gray-500 text-xs" dir="ltr">
{{ $txn->created_at->format('Y-m-d H:i') }}
</td>
<td class="px-4 py-3 text-center">
<a href="{{ route('receipts.pos.print', $txn) }}" target="_blank"
class="text-blue-600 hover:text-blue-800 text-xs font-medium">
{{ __('طباعة') }}
</a>
@if($txn->invoice)
<a href="{{ route('invoices.show', $txn->invoice) }}" wire:navigate
class="text-emerald-600 hover:text-emerald-800 text-xs font-medium ms-2">
{{ __('الفاتورة') }}
</a>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="text-center py-12">
<svg class="mx-auto h-12 w-12 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
<p class="mt-3 text-sm text-gray-500">{{ __('لم يتم بيع هذا المنتج بعد') }}</p>
</div>
@endif
</div>
{{-- Analytics Tab --}}
<div x-show="activeTab === 'analytics'" x-cloak>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
......
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