feat: implement core ERP modules, multi-tenant architecture, and comprehensive system workflow documentation.

This commit is contained in:
Ajjj
2026-08-05 01:18:31 +08:00
parent 1fcb468dc1
commit c03de704e2
46 changed files with 1902 additions and 387 deletions

View File

@@ -6,10 +6,10 @@ use Inertia\Inertia;
use Illuminate\Http\Request;
use Modules\ProjectManagement\Models\Project;
use Modules\ProjectManagement\Models\Task;
use Modules\DailyReports\Models\DailyReportLabor;
use Modules\ProjectManagement\Models\TaskActivity;
use Modules\ProjectManagement\Models\TaskDelay;
use Modules\DailyReports\Models\DailyReport;
use Modules\DailyReports\Models\DailyReportLabor;
use Modules\DailyReports\Models\DailyReportEquipment;
use Modules\DailyReports\Models\DailyReportIssue;
use App\Services\WeatherService;
@@ -274,7 +274,7 @@ class DashboardController extends Controller
}
/**
* Compile resource logs (Labor & Equipment) from latest Daily Reports.
* Compile deployed labor and current equipment from active project data.
*/
private function getResourcesData(?Project $project): array
{
@@ -286,31 +286,35 @@ class DashboardController extends Controller
$equipmentIdle = 0;
$equipmentList = [];
// Resolve which projects we care about
$projectIds = $project ? [$project->id] : Project::pluck('id')->toArray();
// Global resource roll-ups should represent active execution only.
// A selected project intentionally narrows the dashboard to that project.
$projectIds = $project
? [$project->id]
: Project::whereIn('status', ['active', 'planning', 'in_progress'])
->pluck('id')
->toArray();
// Fetch latest reports for the selected project or across all active projects
$latestReports = DailyReport::whereIn('project_id', $projectIds)
->whereIn('id', function ($query) use ($projectIds) {
$query->selectRaw('MAX(id)')
->from('daily_reports')
->whereIn('project_id', $projectIds)
->groupBy('project_id');
})
// Labor totals come from every Daily Report labor entry for the active
// project set. This intentionally includes all stored report records.
$laborLogs = DailyReportLabor::whereHas('dailyReport', function ($query) use ($projectIds) {
$query->whereIn('project_id', $projectIds);
})->get();
foreach ($laborLogs as $log) {
$laborActual += (int) $log->workers_count;
$tradeName = $log->trade ?? 'General Labor';
$trades[$tradeName] = ($trades[$tradeName] ?? 0) + (int) $log->workers_count;
}
// Read equipment entries from every daily report in the project set.
// ResourceSummary must represent all stored daily-report records, not
// only the latest report for each project.
$reports = DailyReport::whereIn('project_id', $projectIds)
->with('equipment')
->get();
foreach ($latestReports as $report) {
// Compile Labor
$laborLogs = DailyReportLabor::where('daily_report_id', $report->id)->get();
foreach ($laborLogs as $log) {
$laborActual += (int) $log->workers_count;
$tradeName = $log->trade ?? 'General Labor';
$trades[$tradeName] = ($trades[$tradeName] ?? 0) + (int) $log->workers_count;
}
// Compile Equipment
$equipLogs = DailyReportEquipment::where('daily_report_id', $report->id)->get();
foreach ($equipLogs as $log) {
foreach ($reports as $report) {
foreach ($report->equipment as $log) {
$status = strtolower($log->status ?? 'active');
if (str_contains($status, 'active') || str_contains($status, 'use') || str_contains($status, 'operat')) {
$equipmentActive++;
@@ -330,9 +334,8 @@ class DashboardController extends Controller
}
}
// Expected labor calculation: calculate expected labor from project allocation if present, otherwise dynamically from actual logs
if ($laborActual > 0) {
$laborExpected = max($laborActual, (int)ceil($laborActual * 1.15));
$laborExpected = max($laborActual, (int) ceil($laborActual * 1.15));
}
return [
@@ -345,7 +348,7 @@ class DashboardController extends Controller
'active' => $equipmentActive,
'maintenance' => $equipmentMaintenance,
'idle' => $equipmentIdle,
'list' => array_slice($equipmentList, 0, 5) // Cap list at 5
'list' => $equipmentList
]
];
}
@@ -374,8 +377,14 @@ class DashboardController extends Controller
$totalRetention = (float) $invoicesQuery->sum('retention_amount');
$pendingApprovalsCount = \Modules\ApprovalWorkflow\Models\ApprovalChain::where('status', 'in_review')->count();
$totalProjectsCount = Project::count();
$activeProjectsCount = Project::where('status', 'active')->count();
$projectsAnalyticsQuery = Project::query();
if ($project) {
$projectsAnalyticsQuery->whereKey($project->id);
}
$totalProjectsCount = (clone $projectsAnalyticsQuery)->count();
$activeProjectsCount = (clone $projectsAnalyticsQuery)
->whereIn('status', ['planning', 'in_progress'])
->count();
// 2. PM Milestone & Progress Analytics
$milestonesQuery = \Modules\TimelineScheduling\Models\Milestone::query();
@@ -402,6 +411,7 @@ class DashboardController extends Controller
return [
'role' => $roleName,
'roles' => $user?->getRoleNames()->values()->all() ?? [],
'user_type' => $userType,
'financials' => [
'total_billed' => $totalBilled,