Skip to content

Commit d408e8e

Browse files
committed
feat(phase-12/13/16): tests, exports, dashboard enhancements (in-progress)
- Phase 12: QueueJobsTest (4 tests) — invoice, low-stock, payroll, bulk-import - Phase 13: MailNotificationsTest (6 tests) — render + dispatch assertions - Phase 14: purchase-order PDF view added - Phase 15: ProductsExport, ContactsExport (partial) - Phase 16: DashboardController module_stats + activity_feed; Dashboard.tsx module overview cards and recent activity feed Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 864d8d3 commit d408e8e

8 files changed

Lines changed: 866 additions & 48 deletions

File tree

erp/app/Exports/ContactsExport.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace App\Exports;
4+
5+
use App\Modules\Finance\Models\Contact;
6+
use Maatwebsite\Excel\Concerns\FromQuery;
7+
use Maatwebsite\Excel\Concerns\WithHeadings;
8+
use Maatwebsite\Excel\Concerns\WithMapping;
9+
10+
class ContactsExport implements FromQuery, WithHeadings, WithMapping
11+
{
12+
public function __construct(private readonly int $tenantId) {}
13+
14+
public function query()
15+
{
16+
return Contact::where('tenant_id', $this->tenantId);
17+
}
18+
19+
public function headings(): array
20+
{
21+
return ['ID', 'Name', 'Email', 'Type'];
22+
}
23+
24+
public function map($contact): array
25+
{
26+
return [
27+
$contact->id,
28+
$contact->name,
29+
$contact->email,
30+
$contact->type,
31+
];
32+
}
33+
}

erp/app/Exports/InvoicesExport.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace App\Exports;
4+
5+
use App\Modules\Finance\Models\Invoice;
6+
use Maatwebsite\Excel\Concerns\FromQuery;
7+
use Maatwebsite\Excel\Concerns\WithHeadings;
8+
use Maatwebsite\Excel\Concerns\WithMapping;
9+
10+
class InvoicesExport implements FromQuery, WithHeadings, WithMapping
11+
{
12+
public function __construct(private readonly int $tenantId) {}
13+
14+
public function query()
15+
{
16+
return Invoice::where('tenant_id', $this->tenantId)->with('items');
17+
}
18+
19+
public function headings(): array
20+
{
21+
return ['ID', 'Number', 'Status', 'Issue Date', 'Total'];
22+
}
23+
24+
public function map($invoice): array
25+
{
26+
return [
27+
$invoice->id,
28+
$invoice->number,
29+
$invoice->status,
30+
$invoice->issue_date?->toDateString(),
31+
$invoice->total,
32+
];
33+
}
34+
}

erp/app/Exports/ProductsExport.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace App\Exports;
4+
5+
use App\Modules\Inventory\Models\Product;
6+
use Maatwebsite\Excel\Concerns\FromQuery;
7+
use Maatwebsite\Excel\Concerns\WithHeadings;
8+
use Maatwebsite\Excel\Concerns\WithMapping;
9+
10+
class ProductsExport implements FromQuery, WithHeadings, WithMapping
11+
{
12+
public function __construct(private readonly int $tenantId) {}
13+
14+
public function query()
15+
{
16+
return Product::where('tenant_id', $this->tenantId);
17+
}
18+
19+
public function headings(): array
20+
{
21+
return ['ID', 'SKU', 'Name', 'Cost Price', 'Sale Price', 'Type'];
22+
}
23+
24+
public function map($product): array
25+
{
26+
return [
27+
$product->id,
28+
$product->sku,
29+
$product->name,
30+
$product->cost_price,
31+
$product->sale_price,
32+
$product->type ?? '',
33+
];
34+
}
35+
}

erp/app/Http/Controllers/DashboardController.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,31 @@ public function index(Request $request)
114114
->where('due_date', '<', now()->startOfDay())
115115
->count();
116116

117+
// Module stats — counts per module scoped to tenant
118+
$moduleStats = [
119+
'open_invoices' => Invoice::where('tenant_id', $tenantId)->whereNotIn('status', ['paid', 'cancelled'])->count(),
120+
'open_bills' => Bill::where('tenant_id', $tenantId)->whereNotIn('status', ['paid', 'cancelled'])->count(),
121+
'pending_pos' => \App\Modules\Purchase\Models\Po::where('tenant_id', $tenantId)->where('status', 'draft')->count(),
122+
'active_projects' => \App\Modules\PM\Models\Project::where('tenant_id', $tenantId)->where('status', 'active')->count(),
123+
'open_tickets' => \App\Modules\Helpdesk\Models\HelpdeskTicket::where('tenant_id', $tenantId)->where('status', '!=', 'closed')->count(),
124+
'pending_approvals' => \App\Modules\Approvals\Models\ApprovalRequest::where('tenant_id', $tenantId)->where('status', 'pending')->count(),
125+
'active_employees' => \App\Modules\HR\Models\Employee::where('tenant_id', $tenantId)->where('status', 'active')->count(),
126+
'total_products' => \App\Modules\Inventory\Models\Product::where('tenant_id', $tenantId)->count(),
127+
];
128+
129+
// Activity feed — last 10 records across key models
130+
$feed = collect();
131+
$feed = $feed->concat(
132+
Invoice::where('tenant_id', $tenantId)->latest()->limit(3)->get()->map(fn ($i) => ['type' => 'invoice', 'label' => 'Invoice ' . $i->number, 'status' => $i->status, 'at' => $i->created_at])
133+
);
134+
$feed = $feed->concat(
135+
\App\Modules\Purchase\Models\Po::where('tenant_id', $tenantId)->latest()->limit(3)->get()->map(fn ($p) => ['type' => 'po', 'label' => 'PO ' . $p->po_number, 'status' => $p->status, 'at' => $p->created_at])
136+
);
137+
$feed = $feed->concat(
138+
\App\Modules\HR\Models\PayrollRun::where('tenant_id', $tenantId)->latest()->limit(2)->get()->map(fn ($r) => ['type' => 'payroll', 'label' => 'Payroll ' . $r->period_label, 'status' => $r->status, 'at' => $r->created_at])
139+
);
140+
$activityFeed = $feed->sortByDesc('at')->take(10)->values();
141+
117142
return Inertia::render('Dashboard', [
118143
'breadcrumbs' => [
119144
['label' => 'Dashboard', 'href' => route('dashboard')],
@@ -125,9 +150,11 @@ public function index(Request $request)
125150
'outstanding_ap' => round($outstandingAp, 2),
126151
'overdue_count' => $overdueCount,
127152
],
128-
'monthly_chart' => $months->values(),
129-
'recent_invoices'=> $recentInvoices->values(),
130-
'low_stock' => $lowStock,
153+
'monthly_chart' => $months->values(),
154+
'recent_invoices' => $recentInvoices->values(),
155+
'low_stock' => $lowStock,
156+
'module_stats' => $moduleStats,
157+
'activity_feed' => $activityFeed,
131158
]);
132159
}
133160
}

0 commit comments

Comments
 (0)