Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
CustomerDuesReport
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
8 / 8
8
100.00% covered (success)
100.00%
1 / 1
 key
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 label
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 group
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 description
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 filters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 columns
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 rows
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
1
 summary
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Reports\Customers;
4
5use App\Models\admin\Customer;
6use App\Models\Order;
7use App\Reports\Report;
8use Illuminate\Support\Collection;
9
10/** Which customers currently owe money — a snapshot (receivables), no date filter. */
11class CustomerDuesReport extends Report
12{
13    public function key(): string
14    {
15        return 'customer-dues';
16    }
17
18    public function label(): string
19    {
20        return 'Customer Dues (Receivables)';
21    }
22
23    public function group(): string
24    {
25        return 'Customers';
26    }
27
28    public function description(): ?string
29    {
30        return 'Every customer with an outstanding balance across their orders.';
31    }
32
33    public function filters(): array
34    {
35        return [];
36    }
37
38    public function columns(): array
39    {
40        return [
41            ['key' => 'name', 'label' => 'Customer'],
42            ['key' => 'phone', 'label' => 'Phone'],
43            ['key' => 'orders_with_due', 'label' => 'Orders', 'align' => 'right'],
44            ['key' => 'due', 'label' => 'Due', 'align' => 'right', 'render' => fn ($r) => money($r->due)],
45        ];
46    }
47
48    public function rows(array $filters): Collection
49    {
50        $dues = Order::query()
51            ->where('due', '>', 0)
52            ->selectRaw('customer_id, COUNT(*) as orders_with_due, SUM(due) as due')
53            ->whereNotNull('customer_id')
54            ->groupBy('customer_id')
55            ->get()
56            ->keyBy('customer_id');
57
58        return Customer::query()
59            ->whereIn('id', $dues->keys())
60            ->get()
61            ->map(fn (Customer $c) => (object) [
62                'name' => $c->name,
63                'phone' => $c->phone,
64                'orders_with_due' => (int) $dues[$c->id]->orders_with_due,
65                'due' => (float) $dues[$c->id]->due,
66            ])
67            ->sortByDesc('due')
68            ->values();
69    }
70
71    public function summary(Collection $rows, array $filters): array
72    {
73        return [
74            'Customers with Due' => number_format($rows->count()),
75            'Total Receivable' => money($rows->sum('due')),
76        ];
77    }
78}