Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
StockValuationReport
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
8 / 8
9
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%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 summary
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Reports\Inventory;
4
5use App\Models\admin\Product;
6use App\Reports\Report;
7use Illuminate\Support\Collection;
8
9/** Same as Current Stock's headline number, but at cost — what's actually tied up in inventory. */
10class StockValuationReport extends Report
11{
12    public function key(): string
13    {
14        return 'stock-valuation';
15    }
16
17    public function label(): string
18    {
19        return 'Stock Valuation (at Cost)';
20    }
21
22    public function group(): string
23    {
24        return 'Inventory';
25    }
26
27    public function description(): ?string
28    {
29        return 'On-hand quantity valued at cost price — what capital is tied up in stock. Falls back to retail price when a product has no cost price set.';
30    }
31
32    public function filters(): array
33    {
34        return [];
35    }
36
37    public function columns(): array
38    {
39        return [
40            ['key' => 'name', 'label' => 'Product'],
41            ['key' => 'stock', 'label' => 'On Hand', 'align' => 'right'],
42            ['key' => 'cost_price', 'label' => 'Cost Price', 'align' => 'right', 'render' => fn ($r) => money($r->cost_price)],
43            ['key' => 'value', 'label' => 'Value at Cost', 'align' => 'right', 'render' => fn ($r) => money($r->value)],
44        ];
45    }
46
47    public function rows(array $filters): Collection
48    {
49        return Product::query()
50            ->where('stock', '>', 0)
51            ->orderByDesc('stock')
52            ->get()
53            ->map(function (Product $p) {
54                $costPrice = (float) ($p->cost_price ?: $p->price);
55
56                return (object) [
57                    'name' => $p->name,
58                    'stock' => (int) $p->stock,
59                    'cost_price' => $costPrice,
60                    'value' => $costPrice * (int) $p->stock,
61                ];
62            })
63            ->sortByDesc('value')
64            ->values();
65    }
66
67    public function summary(Collection $rows, array $filters): array
68    {
69        return [
70            'Products in Stock' => number_format($rows->count()),
71            'Total Value at Cost' => money($rows->sum('value')),
72        ];
73    }
74}