ultimatepos/Modules/Maintenance/Http/Controllers/FieldMissionController.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);
}
}