Skip to content

Commit a880eae

Browse files
committed
fix(phase-19/22): fix search closure capture bug and report column names
SearchController: arrow functions (fn()) capture variables by value in PHP, so fn(\$p) => \$results[] = [...] never modified the outer \$results array. Replaced all ->each(fn()) with ->each(function() use (&\$results)) closures. ReportsController: invoices/bills have no total column (computed from items). Use JOIN with invoice_items/bill_items for financial totals. Use stock_quantity instead of quantity_on_hand, and status instead of employment_status on Employee. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 560c544 commit a880eae

2 files changed

Lines changed: 111 additions & 82 deletions

File tree

erp/app/Http/Controllers/Api/V1/ReportsController.php

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Http\Request;
66
use Illuminate\Http\JsonResponse;
7+
use Illuminate\Support\Facades\DB;
78
use App\Modules\Finance\Models\Invoice;
89
use App\Modules\Finance\Models\Bill;
910
use App\Modules\Inventory\Models\Product;
@@ -18,43 +19,47 @@ public function financial(Request $request): JsonResponse
1819
$tenantId = app()->has('tenant') ? app('tenant')->id : $request->user()->tenant_id;
1920
$year = $request->integer('year', now()->year);
2021

21-
$invoiceTotals = Invoice::where('tenant_id', $tenantId)
22+
$invoiceSummary = Invoice::where('tenant_id', $tenantId)
2223
->whereYear('created_at', $year)
23-
->selectRaw('status, COUNT(*) as count, SUM(total) as total')
24+
->selectRaw('status, COUNT(*) as count')
2425
->groupBy('status')
2526
->get();
2627

27-
$monthlyRevenue = Invoice::where('tenant_id', $tenantId)
28-
->where('status', 'paid')
29-
->whereYear('created_at', $year)
30-
->selectRaw("strftime('%m', created_at) as month, SUM(total) as revenue")
28+
$monthlyRevenue = DB::table('invoices')
29+
->join('invoice_items', 'invoices.id', '=', 'invoice_items.invoice_id')
30+
->where('invoices.tenant_id', $tenantId)
31+
->where('invoices.status', 'paid')
32+
->whereYear('invoices.created_at', $year)
33+
->selectRaw("strftime('%m', invoices.created_at) as month, SUM(invoice_items.quantity * invoice_items.unit_price) as revenue")
3134
->groupBy('month')
3235
->orderBy('month')
3336
->get();
3437

35-
$billTotals = Bill::where('tenant_id', $tenantId)
36-
->whereYear('created_at', $year)
37-
->selectRaw('SUM(total) as total_expenses')
38-
->first();
38+
$totalExpenses = DB::table('bills')
39+
->join('bill_items', 'bills.id', '=', 'bill_items.bill_id')
40+
->where('bills.tenant_id', $tenantId)
41+
->whereYear('bills.created_at', $year)
42+
->whereNull('bills.deleted_at')
43+
->sum(DB::raw('bill_items.quantity * bill_items.unit_price'));
3944

4045
return $this->success([
4146
'year' => $year,
42-
'invoice_summary' => $invoiceTotals,
47+
'invoice_summary' => $invoiceSummary,
4348
'monthly_revenue' => $monthlyRevenue,
44-
'total_expenses' => $billTotals?->total_expenses ?? 0,
49+
'total_expenses' => $totalExpenses ?? 0,
4550
]);
4651
}
4752

4853
public function inventory(Request $request): JsonResponse
4954
{
5055
$tenantId = app()->has('tenant') ? app('tenant')->id : $request->user()->tenant_id;
5156

52-
$stockValue = Product::where('tenant_id', $tenantId)
53-
->selectRaw('COUNT(*) as total_products, SUM(quantity_on_hand * cost_price) as stock_value')
57+
$stockStats = Product::where('tenant_id', $tenantId)
58+
->selectRaw('COUNT(*) as total_products, SUM(stock_quantity * cost_price) as stock_value')
5459
->first();
5560

5661
$lowStock = Product::where('tenant_id', $tenantId)
57-
->whereColumn('quantity_on_hand', '<=', 'reorder_point')
62+
->whereColumn('stock_quantity', '<=', 'reorder_point')
5863
->where('reorder_point', '>', 0)
5964
->count();
6065

@@ -65,8 +70,8 @@ public function inventory(Request $request): JsonResponse
6570
->get(['id', 'product_id', 'type', 'quantity', 'created_at']);
6671

6772
return $this->success([
68-
'total_products' => $stockValue?->total_products ?? 0,
69-
'stock_value' => $stockValue?->stock_value ?? 0,
73+
'total_products' => $stockStats?->total_products ?? 0,
74+
'stock_value' => $stockStats?->stock_value ?? 0,
7075
'low_stock_count' => $lowStock,
7176
'recent_movements' => $recentMovements,
7277
]);
@@ -77,8 +82,8 @@ public function hr(Request $request): JsonResponse
7782
$tenantId = app()->has('tenant') ? app('tenant')->id : $request->user()->tenant_id;
7883

7984
$headcount = Employee::where('tenant_id', $tenantId)
80-
->selectRaw('employment_status, COUNT(*) as count')
81-
->groupBy('employment_status')
85+
->selectRaw('status, COUNT(*) as count')
86+
->groupBy('status')
8287
->get();
8388

8489
$payrollSummary = PayrollRun::where('tenant_id', $tenantId)

erp/app/Http/Controllers/Api/V1/SearchController.php

Lines changed: 87 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -23,95 +23,119 @@ public function search(Request $request): JsonResponse
2323

2424
// Invoices
2525
Invoice::where('tenant_id', $tenantId)
26-
->where(fn($query) => $query->where('number', 'like', "%{$q}%")
27-
->orWhereHas('contact', fn($q2) => $q2->where('name', 'like', "%{$q}%")))
26+
->where(function ($query) use ($q) {
27+
$query->where('number', 'like', "%{$q}%")
28+
->orWhereHas('contact', fn ($q2) => $q2->where('name', 'like', "%{$q}%"));
29+
})
2830
->limit(5)->get()
29-
->each(fn($inv) => $results[] = [
30-
'module' => 'invoice',
31-
'id' => $inv->id,
32-
'title' => "Invoice #{$inv->number}",
33-
'subtitle' => $inv->status,
34-
'url' => "/finance/invoices/{$inv->id}",
35-
]);
31+
->each(function ($inv) use (&$results) {
32+
$results[] = [
33+
'module' => 'invoice',
34+
'id' => $inv->id,
35+
'title' => "Invoice #{$inv->number}",
36+
'subtitle' => $inv->status,
37+
'url' => "/finance/invoices/{$inv->id}",
38+
];
39+
});
3640

3741
// Contacts
3842
Contact::where('tenant_id', $tenantId)
39-
->where(fn($query) => $query->where('name', 'like', "%{$q}%")
40-
->orWhere('email', 'like', "%{$q}%"))
43+
->where(function ($query) use ($q) {
44+
$query->where('name', 'like', "%{$q}%")
45+
->orWhere('email', 'like', "%{$q}%");
46+
})
4147
->limit(5)->get()
42-
->each(fn($c) => $results[] = [
43-
'module' => 'contact',
44-
'id' => $c->id,
45-
'title' => $c->name,
46-
'subtitle' => $c->type,
47-
'url' => "/finance/contacts/{$c->id}",
48-
]);
48+
->each(function ($c) use (&$results) {
49+
$results[] = [
50+
'module' => 'contact',
51+
'id' => $c->id,
52+
'title' => $c->name,
53+
'subtitle' => $c->type,
54+
'url' => "/finance/contacts/{$c->id}",
55+
];
56+
});
4957

5058
// Products
5159
Product::where('tenant_id', $tenantId)
52-
->where(fn($query) => $query->where('name', 'like', "%{$q}%")
53-
->orWhere('sku', 'like', "%{$q}%"))
60+
->where(function ($query) use ($q) {
61+
$query->where('name', 'like', "%{$q}%")
62+
->orWhere('sku', 'like', "%{$q}%");
63+
})
5464
->limit(5)->get()
55-
->each(fn($p) => $results[] = [
56-
'module' => 'product',
57-
'id' => $p->id,
58-
'title' => $p->name,
59-
'subtitle' => $p->sku,
60-
'url' => "/inventory/products/{$p->id}",
61-
]);
65+
->each(function ($p) use (&$results) {
66+
$results[] = [
67+
'module' => 'product',
68+
'id' => $p->id,
69+
'title' => $p->name,
70+
'subtitle' => $p->sku,
71+
'url' => "/inventory/products/{$p->id}",
72+
];
73+
});
6274

6375
// Employees
6476
Employee::where('tenant_id', $tenantId)
65-
->where(fn($query) => $query->where('first_name', 'like', "%{$q}%")
66-
->orWhere('last_name', 'like', "%{$q}%")
67-
->orWhere('email', 'like', "%{$q}%"))
77+
->where(function ($query) use ($q) {
78+
$query->where('first_name', 'like', "%{$q}%")
79+
->orWhere('last_name', 'like', "%{$q}%")
80+
->orWhere('email', 'like', "%{$q}%");
81+
})
6882
->limit(5)->get()
69-
->each(fn($e) => $results[] = [
70-
'module' => 'employee',
71-
'id' => $e->id,
72-
'title' => "{$e->first_name} {$e->last_name}",
73-
'subtitle' => $e->email,
74-
'url' => "/hr/employees/{$e->id}",
75-
]);
83+
->each(function ($e) use (&$results) {
84+
$results[] = [
85+
'module' => 'employee',
86+
'id' => $e->id,
87+
'title' => "{$e->first_name} {$e->last_name}",
88+
'subtitle' => $e->email,
89+
'url' => "/hr/employees/{$e->id}",
90+
];
91+
});
7692

7793
// CRM Leads
7894
CrmLead::where('tenant_id', $tenantId)
79-
->where(fn($query) => $query->where('contact_name', 'like', "%{$q}%")
80-
->orWhere('company_name', 'like', "%{$q}%")
81-
->orWhere('email', 'like', "%{$q}%")
82-
->orWhere('reference', 'like', "%{$q}%"))
95+
->where(function ($query) use ($q) {
96+
$query->where('contact_name', 'like', "%{$q}%")
97+
->orWhere('company_name', 'like', "%{$q}%")
98+
->orWhere('email', 'like', "%{$q}%")
99+
->orWhere('reference', 'like', "%{$q}%");
100+
})
83101
->limit(5)->get()
84-
->each(fn($l) => $results[] = [
85-
'module' => 'lead',
86-
'id' => $l->id,
87-
'title' => $l->contact_name ?? $l->company_name ?? $l->reference,
88-
'subtitle' => $l->status ?? '',
89-
'url' => "/crm/leads/{$l->id}",
90-
]);
102+
->each(function ($l) use (&$results) {
103+
$results[] = [
104+
'module' => 'lead',
105+
'id' => $l->id,
106+
'title' => $l->contact_name ?? $l->company_name ?? $l->reference,
107+
'subtitle' => $l->status ?? '',
108+
'url' => "/crm/leads/{$l->id}",
109+
];
110+
});
91111

92112
// Projects
93113
Project::where('tenant_id', $tenantId)
94114
->where('name', 'like', "%{$q}%")
95115
->limit(5)->get()
96-
->each(fn($p) => $results[] = [
97-
'module' => 'project',
98-
'id' => $p->id,
99-
'title' => $p->name,
100-
'subtitle' => $p->status,
101-
'url' => "/pm/projects/{$p->id}",
102-
]);
116+
->each(function ($p) use (&$results) {
117+
$results[] = [
118+
'module' => 'project',
119+
'id' => $p->id,
120+
'title' => $p->name,
121+
'subtitle' => $p->status,
122+
'url' => "/pm/projects/{$p->id}",
123+
];
124+
});
103125

104126
// Purchase Orders
105127
PurchaseOrder::where('tenant_id', $tenantId)
106-
->where(fn($query) => $query->where('po_number', 'like', "%{$q}%"))
128+
->where('po_number', 'like', "%{$q}%")
107129
->limit(5)->get()
108-
->each(fn($po) => $results[] = [
109-
'module' => 'purchase_order',
110-
'id' => $po->id,
111-
'title' => "PO #{$po->po_number}",
112-
'subtitle' => $po->status,
113-
'url' => "/purchase/orders/{$po->id}",
114-
]);
130+
->each(function ($po) use (&$results) {
131+
$results[] = [
132+
'module' => 'purchase_order',
133+
'id' => $po->id,
134+
'title' => "PO #{$po->po_number}",
135+
'subtitle' => $po->status,
136+
'url' => "/purchase/orders/{$po->id}",
137+
];
138+
});
115139

116140
return $this->success(['query' => $q, 'results' => $results, 'total' => count($results)]);
117141
}

0 commit comments

Comments
 (0)