Commit 8bf5451e authored by Mahmoud Aglan's avatar Mahmoud Aglan

Add installment collection breakdown to product analytics

Adds محصّل vs إجمالي الإيرادات with a progress bar and remaining balance.
Collection is computed as a pro-rata share of invoice.paid_amount based on
the product line's fraction of the invoice total — so mixed invoices
(product + program) correctly attribute only the product's share of each
payment. Price overrides are excluded from the product's line because they
always land on the program item, leaving the product total_amount intact.
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 5b81069b
......@@ -49,6 +49,24 @@ public function render()
->orderBy('invoice_items.created_at')
->value('invoice_items.created_at');
// Collected (installment-aware): pro-rata share of invoice.paid_amount
// = SUM( item_total / invoice_total * paid_amount ) per invoice
// Works correctly for mixed invoices (product + program) because the
// price override always lands on the program line, not the product line.
$totalCollected = (int) (clone $soldQuery)
->selectRaw('SUM(
CASE
WHEN invoices.total_amount > 0
THEN (invoice_items.total_amount::float / invoices.total_amount) * invoices.paid_amount
ELSE 0
END
) as collected')
->value('collected');
$collectedPercent = $totalRevenue > 0
? round($totalCollected / $totalRevenue * 100, 1)
: 0;
// Stock-in from movements (genuinely a stock concept)
$totalIn = InventoryMovement::where('product_id', $this->product->id)
->where('direction', MovementDirection::In->value)
......@@ -76,6 +94,8 @@ public function render()
'totalIn' => $totalIn,
'totalSold' => $totalUnitsSold,
'totalRevenue' => $totalRevenue,
'totalCollected' => $totalCollected,
'collectedPercent' => $collectedPercent,
'avgMonthlyMovement' => $avgMonthlyMovement,
'margin' => $margin,
]);
......
......@@ -348,16 +348,39 @@ class="px-6 py-3 text-sm font-medium border-b-2 transition-colors">
{{-- 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">
{{-- Total Revenue --}}
<div class="border border-emerald-200 rounded-lg p-5 text-center bg-emerald-50">
<div class="inline-flex items-center justify-center w-12 h-12 rounded-full bg-emerald-100 mb-3">
<svg class="w-6 h-6 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
{{-- Total Revenue + Collected --}}
<div class="border border-emerald-200 rounded-lg p-5 bg-emerald-50 col-span-1 sm:col-span-2 lg:col-span-1">
<div class="flex items-center gap-3 mb-3">
<div class="inline-flex items-center justify-center w-10 h-10 rounded-full bg-emerald-100 shrink-0">
<svg class="w-5 h-5 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<p class="text-xs font-medium text-emerald-700">{{ __('إجمالي الإيرادات') }}</p>
</div>
{{-- Billed total --}}
<p class="text-2xl font-bold text-emerald-800">{{ number_format($totalRevenue / 100, 2) }} <span class="text-sm font-normal text-emerald-600">ج.م</span></p>
{{-- Collected sub-line --}}
<div class="mt-3">
<div class="flex justify-between items-center mb-1">
<span class="text-xs text-emerald-700">{{ __('محصّل') }}</span>
<span class="text-xs font-semibold text-emerald-800">
{{ number_format($totalCollected / 100, 2) }} ج.م
<span class="text-emerald-600">({{ $collectedPercent }}%)</span>
</span>
</div>
<div class="w-full bg-emerald-200 rounded-full h-2 overflow-hidden">
<div class="h-2 rounded-full transition-all duration-500"
style="width: {{ min(100, $collectedPercent) }}%; background-color: #059669;"></div>
</div>
@if($collectedPercent < 100)
<p class="text-xs text-amber-600 mt-1">
{{ __('متبقي') }}: {{ number_format(($totalRevenue - $totalCollected) / 100, 2) }} ج.م
</p>
@endif
</div>
<p class="text-xs font-medium text-emerald-700 mb-1">{{ __('إجمالي الإيرادات') }}</p>
<p class="text-xl font-bold text-emerald-800">{{ number_format($totalRevenue / 100, 2) }}</p>
<p class="text-xs text-emerald-600 mt-1">{{ __('ج.م') }}</p>
</div>
{{-- Total Sold (units) --}}
......
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