chore: update document approval workflow and bug fixes

This commit is contained in:
2026-05-25 13:05:20 +08:00
parent d573c02893
commit 39a8e1d4cd
910 changed files with 49994 additions and 1010 deletions

View File

@@ -0,0 +1,167 @@
<?php
namespace Modules\MasterData\Http\Controllers;
use App\Http\Controllers\Controller;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use Modules\MasterData\Exports\BoqTemplateExport;
use Modules\MasterData\Exports\MaterialTemplateExport;
use Modules\MasterData\Imports\BoqImport;
use Modules\MasterData\Imports\MaterialImport;
use Modules\MasterData\Models\Material;
use Modules\ProjectManagement\Models\Project;
use Modules\ProjectManagement\Models\Task;
class MaterialImportController extends Controller
{
public function template()
{
return Excel::download(new MaterialTemplateExport(), 'material_import_template.xlsx');
}
public function import(Request $request)
{
$request->validate([
'file' => 'required|file|mimes:xlsx,csv,xls|max:10240',
]);
$import = new MaterialImport();
try {
Excel::import($import, $request->file('file'));
} catch (\Exception $e) {
return back()->with('error', 'Import failed: ' . $e->getMessage());
}
$failures = $import->failures();
$failureCount = $failures->count();
$message = "{$import->getImportedCount()} materials imported.";
if ($import->getSkippedCount() > 0) {
$message .= " {$import->getSkippedCount()} skipped (duplicate SKU).";
}
if (count($import->getGroupsCreated()) > 0) {
$message .= " Groups: " . implode(', ', $import->getGroupsCreated()) . ".";
}
if ($failureCount > 0) {
$message .= " {$failureCount} rows had validation errors.";
}
return back()->with('success', $message);
}
// --- BOQ Import ---
public function boqTemplate()
{
return Excel::download(new BoqTemplateExport(), 'boq_template.xlsx');
}
public function boqPreview(Request $request)
{
$request->validate([
'file' => 'required|file|mimes:xlsx,csv,xls|max:10240',
]);
$import = new BoqImport();
try {
Excel::import($import, $request->file('file'));
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to parse file: ' . $e->getMessage(),
], 422);
}
if (!empty($import->getErrors())) {
return response()->json([
'success' => false,
'message' => implode('; ', $import->getErrors()),
], 422);
}
return response()->json([
'success' => true,
'header' => $import->getHeader(),
'items' => $import->getItems(),
]);
}
public function boqConfirm(Request $request)
{
$validated = $request->validate([
'project_ulid' => 'required|string',
'task_ulid' => 'required|string',
'header' => 'required|array',
'header.project_name' => 'nullable|string',
'header.owner_name' => 'nullable|string',
'header.location' => 'nullable|string',
'header.date' => 'nullable|string',
'items' => 'required|array|min:1',
'items.*.material' => 'required|string|max:255',
'items.*.unit' => 'required|string|max:20',
'items.*.approved_qty' => 'nullable|numeric|min:0',
'items.*.request_qty' => 'nullable|numeric|min:0',
'items.*.work_area' => 'nullable|string|max:255',
]);
$project = Project::where('ulid', $validated['project_ulid'])->firstOrFail();
$task = Task::where('ulid', $validated['task_ulid'])
->where('project_id', $project->id)
->firstOrFail();
$created = 0;
DB::transaction(function () use ($validated, $project, $task, &$created) {
// Update project fields from header
$updates = [];
if (!empty($validated['header']['location'])) {
$updates['location'] = $validated['header']['location'];
}
if (!empty($validated['header']['date'])) {
try {
$updates['start_date'] = Carbon::parse($validated['header']['date'])->format('Y-m-d');
} catch (\Exception) {
// Skip invalid date
}
}
if (!empty($updates)) {
$project->update($updates);
}
// Create materials and assign to task
foreach ($validated['items'] as $item) {
$material = Material::create([
'name' => $item['material'],
'unit' => $item['unit'],
'unit_cost' => 0,
'category' => null,
'status' => 'active',
]);
$task->taskMaterials()->create([
'material_id' => $material->id,
'planned_qty' => $item['request_qty'] ?? 0,
'actual_qty' => $item['approved_qty'] ?? 0,
'unit_cost' => 0,
'notes' => $item['work_area'] ?? null,
]);
$created++;
}
// Recalculate project capitalization
$project->load('tasks.taskMaterials');
$project->recalculateCapitalization();
});
return back()->with('success', "{$created} materials imported and assigned to task \"{$task->name}\".");
}
}