feat: implement comprehensive modules for daily reports, project management, material logistics, and approval workflows

This commit is contained in:
Ajjj
2026-08-26 15:48:41 +08:00
parent d4e2b468ff
commit d6efc46624
52 changed files with 5275 additions and 932 deletions

View File

@@ -166,6 +166,66 @@ class Project extends Model
return $this->belongsToMany(ProjectClassification::class, 'project_classification_pivot');
}
public const CANONICAL_CLASSIFICATIONS = [
'road highway, pavement, railways, airport horizontal structures and bridges' => 'Road, Highway, Pavement, Railways, Airport Horizontal Structures and Bridges',
'road, highway, pavement, railways, airport horizontal structures and bridges' => 'Road, Highway, Pavement, Railways, Airport Horizontal Structures and Bridges',
'irrigation and flood control' => 'Irrigation and Flood Control',
'dam, reservoir, and tunneling' => 'Dam, Reservoir, and Tunneling',
'dam, reservoir and tunneling' => 'Dam, Reservoir, and Tunneling',
'water supply' => 'Water Supply',
'port, harbor and offshore engineering' => 'Port, Harbor and Offshore Engineering',
'building and industrial plant' => 'Building and Industrial Plant',
'sewerage treatment/disposal plant' => 'Sewerage Treatment / Disposal Plant',
'sewerage treatment / disposal plant' => 'Sewerage Treatment / Disposal Plant',
'water treatment plant and system' => 'Water Treatment Plant and System',
'park, playground and recreational work' => 'Park, Playground and Recreational Work',
'electrical work' => 'Electrical Work',
];
/**
* Ensure classifications always return properly formatted Title Case strings.
*/
public function getClassificationsAttribute($value): array
{
if (is_null($value)) {
return [];
}
$decoded = is_string($value) ? json_decode($value, true) : $value;
if (!is_array($decoded)) {
return [];
}
return array_values(array_map(function ($name) {
$trimmed = trim((string) $name);
$lower = strtolower($trimmed);
return self::CANONICAL_CLASSIFICATIONS[$lower] ?? (self::CANONICAL_CLASSIFICATIONS[$trimmed] ?? $trimmed);
}, $decoded));
}
/**
* Mutate classifications on set to canonical Title Case strings.
*/
public function setClassificationsAttribute($value): void
{
if (is_null($value)) {
$this->attributes['classifications'] = json_encode([]);
return;
}
$array = is_string($value) ? json_decode($value, true) : $value;
if (!is_array($array)) {
$array = [];
}
$canonical = array_values(array_map(function ($name) {
$trimmed = trim((string) $name);
$lower = strtolower($trimmed);
return self::CANONICAL_CLASSIFICATIONS[$lower] ?? (self::CANONICAL_CLASSIFICATIONS[$trimmed] ?? $trimmed);
}, $array));
$this->attributes['classifications'] = json_encode($canonical);
}
// --- State Machine Helpers ---
public function transitionTo(ProjectStatus $newStatus): void