353 lines
14 KiB
PHP
353 lines
14 KiB
PHP
<?php
|
|
|
|
namespace Modules\Maintenance\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Controller;
|
|
use Modules\Maintenance\Http\Controllers\Concerns\AuthorizesMaintenance;
|
|
use Modules\Maintenance\Models\FieldMission;
|
|
use Modules\Maintenance\Models\MaintenanceTool;
|
|
use Modules\Maintenance\Models\MissionCrewMember;
|
|
use Modules\Maintenance\Models\MissionExpense;
|
|
use Modules\Maintenance\Models\MissionItem;
|
|
use Modules\Maintenance\Services\FieldMissionService;
|
|
use Modules\Maintenance\Services\MaterialStockService;
|
|
use Modules\Maintenance\Utils\MaintenanceUtil;
|
|
use Yajra\DataTables\Facades\DataTables;
|
|
|
|
class FieldMissionController extends Controller
|
|
{
|
|
use AuthorizesMaintenance;
|
|
|
|
public function __construct(
|
|
protected MaintenanceUtil $maintenanceUtil,
|
|
protected FieldMissionService $missionService,
|
|
protected MaterialStockService $stockService
|
|
) {}
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.view');
|
|
|
|
$business_id = $this->maintenanceUtil->getBusinessId();
|
|
|
|
if ($request->ajax()) {
|
|
$query = FieldMission::where('business_id', $business_id)
|
|
->with(['leadUser:id,first_name,last_name', 'equipment:id,name'])
|
|
->orderByDesc('id');
|
|
|
|
if ($status = $request->get('status')) {
|
|
$query->where('status', $status);
|
|
}
|
|
|
|
$statusLabels = $this->missionService->statusLabels();
|
|
$typeLabels = $this->missionService->missionTypeLabels();
|
|
|
|
return DataTables::of($query)
|
|
->addColumn('lead_name', fn ($row) => $row->leadUser
|
|
? trim($row->leadUser->first_name.' '.($row->leadUser->last_name ?? ''))
|
|
: '—')
|
|
->addColumn('equipment_name', fn ($row) => $row->equipment?->name ?? '—')
|
|
->addColumn('status_label', fn ($row) => $statusLabels[$row->status] ?? $row->status)
|
|
->addColumn('type_label', fn ($row) => $typeLabels[$row->mission_type] ?? $row->mission_type)
|
|
->addColumn('action', fn ($row) => '<a href="'.action([self::class, 'show'], [$row->id]).'" class="btn btn-xs btn-info"><i class="fa fa-eye"></i></a>')
|
|
->editColumn('planned_departure_at', fn ($row) => $this->maintenanceUtil->formatDateTime($row->planned_departure_at))
|
|
->editColumn('total_cost', fn ($row) => $this->maintenanceUtil->formatMoney($row->total_cost))
|
|
->rawColumns(['action'])
|
|
->make(true);
|
|
}
|
|
|
|
return view('maintenance::field_missions.index', [
|
|
'statuses' => $this->missionService->statusLabels(),
|
|
]);
|
|
}
|
|
|
|
public function show($id)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.view');
|
|
|
|
$business_id = $this->maintenanceUtil->getBusinessId();
|
|
$mission = FieldMission::where('business_id', $business_id)
|
|
->with(['crewMembers.user', 'items.sparePart', 'items.tool', 'expenses', 'equipment', 'destinationLocation', 'leadUser'])
|
|
->findOrFail($id);
|
|
|
|
return view('maintenance::field_missions.show', $this->showViewData($mission));
|
|
}
|
|
|
|
public function createForSource(Request $request, string $sourceType, int $sourceId)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.manage');
|
|
|
|
$business = $this->maintenanceUtil->getBusiness();
|
|
|
|
try {
|
|
$mission = $this->missionService->getOrCreateForSource($business, $sourceType, $sourceId, auth()->user());
|
|
} catch (\InvalidArgumentException $e) {
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage());
|
|
}
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect(
|
|
$request,
|
|
true,
|
|
__('maintenance::lang.fm_created', ['ref' => $mission->mission_number]),
|
|
action([self::class, 'show'], [$mission->id])
|
|
);
|
|
}
|
|
|
|
public function storeCrew(Request $request, $id)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.manage');
|
|
|
|
$mission = $this->findMission($id);
|
|
$data = $request->validate([
|
|
'user_id' => 'nullable|exists:users,id',
|
|
'person_name' => 'nullable|string|max:255',
|
|
'role' => 'nullable|string|max:50',
|
|
'is_lead' => 'nullable|boolean',
|
|
'labor_hours' => 'nullable|numeric|min:0',
|
|
'hourly_rate' => 'nullable|integer|min:0',
|
|
'per_diem' => 'nullable|integer|min:0',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
if (empty($data['user_id']) && empty($data['person_name'])) {
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, false, __('maintenance::lang.fm_crew_required'));
|
|
}
|
|
|
|
$this->missionService->addCrewMember($mission, $data);
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_crew_added'));
|
|
}
|
|
|
|
public function updateCrew(Request $request, int $crewId)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.manage');
|
|
|
|
$member = MissionCrewMember::whereHas('mission', fn ($q) => $q->where('business_id', $this->maintenanceUtil->getBusinessId()))
|
|
->findOrFail($crewId);
|
|
|
|
$data = $request->validate([
|
|
'user_id' => 'nullable|exists:users,id',
|
|
'person_name' => 'nullable|string|max:255',
|
|
'role' => 'nullable|string|max:50',
|
|
'is_lead' => 'nullable|boolean',
|
|
'labor_hours' => 'nullable|numeric|min:0',
|
|
'hourly_rate' => 'nullable|integer|min:0',
|
|
'per_diem' => 'nullable|integer|min:0',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
$this->missionService->updateCrewMember($member, $data);
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_crew_updated'));
|
|
}
|
|
|
|
public function destroyCrew(Request $request, int $crewId)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.manage');
|
|
|
|
$member = MissionCrewMember::whereHas('mission', fn ($q) => $q->where('business_id', $this->maintenanceUtil->getBusinessId()))
|
|
->findOrFail($crewId);
|
|
|
|
$this->missionService->removeCrewMember($member);
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_crew_removed'));
|
|
}
|
|
|
|
public function storeItem(Request $request, $id)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.issue');
|
|
|
|
$mission = $this->findMission($id);
|
|
$data = $request->validate([
|
|
'item_type' => 'required|in:spare_part,product,tool,other',
|
|
'spare_part_id' => 'nullable|exists:maintenance_spare_parts,id',
|
|
'tool_id' => 'nullable|exists:maintenance_tools,id',
|
|
'part_name' => 'nullable|string|max:255',
|
|
'quantity_issued' => 'required|numeric|min:0.001',
|
|
'location_id' => 'nullable|integer|exists:business_locations,id',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
if (in_array($mission->status, ['closed', 'cancelled'], true)) {
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, false, __('maintenance::lang.fm_mission_closed'));
|
|
}
|
|
|
|
$this->missionService->addItem($mission, $data);
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_item_added'));
|
|
}
|
|
|
|
public function destroyItem(Request $request, int $itemId)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.issue');
|
|
|
|
$item = MissionItem::whereHas('mission', fn ($q) => $q->where('business_id', $this->maintenanceUtil->getBusinessId()))
|
|
->findOrFail($itemId);
|
|
|
|
try {
|
|
$this->missionService->removeItem($item);
|
|
} catch (\InvalidArgumentException $e) {
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage());
|
|
}
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_item_removed'));
|
|
}
|
|
|
|
public function dispatchMission(Request $request, $id)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.issue');
|
|
|
|
$mission = $this->findMission($id);
|
|
|
|
try {
|
|
$this->missionService->dispatch($mission, auth()->user());
|
|
} catch (\InvalidArgumentException $e) {
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage());
|
|
}
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_dispatched'));
|
|
}
|
|
|
|
public function recordReturns(Request $request, $id)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.issue');
|
|
|
|
$mission = $this->findMission($id);
|
|
$data = $request->validate([
|
|
'lines' => 'required|array|min:1',
|
|
'lines.*.item_id' => 'required|integer',
|
|
'lines.*.quantity_returned' => 'required|numeric|min:0',
|
|
'lines.*.quantity_lost' => 'nullable|numeric|min:0',
|
|
]);
|
|
|
|
try {
|
|
$this->missionService->recordReturns($mission, $data['lines']);
|
|
} catch (\InvalidArgumentException $e) {
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage());
|
|
}
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_returns_saved'));
|
|
}
|
|
|
|
public function closeMission(Request $request, $id)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.manage');
|
|
|
|
$mission = $this->findMission($id);
|
|
|
|
try {
|
|
$this->missionService->closeMission($mission, auth()->user());
|
|
} catch (\InvalidArgumentException $e) {
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage());
|
|
}
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_closed'));
|
|
}
|
|
|
|
public function storeExpense(Request $request, $id)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.expense');
|
|
|
|
$mission = $this->findMission($id);
|
|
$data = $request->validate([
|
|
'expense_type' => 'required|string|max:50',
|
|
'description' => 'nullable|string|max:255',
|
|
'amount' => 'required|integer|min:0',
|
|
'expense_date' => 'nullable|string',
|
|
'receipt_ref' => 'nullable|string|max:100',
|
|
'vendor_name' => 'nullable|string|max:255',
|
|
'paid_by' => 'nullable|in:company,employee',
|
|
'crew_member_id' => 'nullable|integer',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
if (! empty($data['expense_date'])) {
|
|
$data['expense_date'] = $this->maintenanceUtil->parseInputDate($data['expense_date']);
|
|
}
|
|
|
|
$this->missionService->addExpense($mission, $data);
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_expense_added'));
|
|
}
|
|
|
|
public function destroyExpense(Request $request, int $expenseId)
|
|
{
|
|
$this->authorizeMaintenanceAccess('maintenance.mission.expense');
|
|
|
|
$expense = MissionExpense::whereHas('mission', fn ($q) => $q->where('business_id', $this->maintenanceUtil->getBusinessId()))
|
|
->findOrFail($expenseId);
|
|
|
|
$this->missionService->removeExpense($expense);
|
|
|
|
return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_expense_removed'));
|
|
}
|
|
|
|
protected function findMission($id): FieldMission
|
|
{
|
|
return FieldMission::where('business_id', $this->maintenanceUtil->getBusinessId())->findOrFail($id);
|
|
}
|
|
|
|
protected function showViewData(FieldMission $mission): array
|
|
{
|
|
$business_id = $mission->business_id;
|
|
$mission->loadMissing('business');
|
|
$locations = $mission->source_id
|
|
? ($this->missionService->getPanelData($mission->business, $mission->source_type, (int) $mission->source_id)['locations'] ?? collect())
|
|
: $this->stockService->locationsForBusiness($business_id);
|
|
|
|
return [
|
|
'mission' => $mission,
|
|
'statuses' => $this->missionService->statusLabels(),
|
|
'mission_types' => $this->missionService->missionTypeLabels(),
|
|
'role_labels' => $this->missionService->roleLabels(),
|
|
'expense_types' => $this->missionService->expenseTypeLabels(),
|
|
'users' => $this->maintenanceUtil->getUsersDropdown($business_id),
|
|
'spare_parts' => $this->maintenanceUtil->getSparePartsDropdown($business_id),
|
|
'tools' => MaintenanceTool::where('business_id', $business_id)->where('status', 'available')->orderBy('name')->pluck('name', 'id'),
|
|
'locations' => $locations,
|
|
'can_manage' => auth()->user()->can('maintenance.mission.manage'),
|
|
'can_issue' => auth()->user()->can('maintenance.mission.issue'),
|
|
'can_expense' => auth()->user()->can('maintenance.mission.expense'),
|
|
'tms_enabled' => $this->isTmsEnabledForMission(),
|
|
'mission_shipment' => $this->findMissionShipment($mission),
|
|
'tms_statuses' => class_exists(\Modules\Tms\Utils\TmsUtil::class)
|
|
? app(\Modules\Tms\Utils\TmsUtil::class)->shipmentStatuses()
|
|
: [],
|
|
];
|
|
}
|
|
|
|
protected function isTmsEnabledForMission(): bool
|
|
{
|
|
if (! \Module::has('Tms') || ! \Module::isEnabled('Tms')) {
|
|
return false;
|
|
}
|
|
|
|
$module_util = new \App\Utils\ModuleUtil();
|
|
$business_id = $this->maintenanceUtil->getBusinessId();
|
|
|
|
if (! $module_util->isModuleInstalled('Tms')) {
|
|
return false;
|
|
}
|
|
|
|
if (! auth()->user()->can('tms.shipments.view') && ! auth()->user()->can('tms.shipments.create')) {
|
|
return false;
|
|
}
|
|
|
|
if ($module_util->isSuperadminInstalled()) {
|
|
return (bool) $module_util->hasThePermissionInSubscription($business_id, 'tms_module');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function findMissionShipment(FieldMission $mission)
|
|
{
|
|
if (! class_exists(\Modules\Tms\Services\TmsMissionSyncService::class)) {
|
|
return null;
|
|
}
|
|
|
|
return app(\Modules\Tms\Services\TmsMissionSyncService::class)->findShipmentForMission($mission);
|
|
}
|
|
}
|