604 lines
19 KiB
PHP
604 lines
19 KiB
PHP
<?php
|
|
|
|
namespace Modules\Maintenance\Utils;
|
|
|
|
use App\Business;
|
|
use App\Contact;
|
|
use App\User;
|
|
use App\Utils\ModuleUtil;
|
|
use App\Utils\Util;
|
|
use Modules\Maintenance\Models\Equipment;
|
|
use Modules\Maintenance\Models\EquipmentCategory;
|
|
use Modules\Maintenance\Models\ProductionLine;
|
|
|
|
class MaintenanceUtil
|
|
{
|
|
protected $commonUtil;
|
|
|
|
protected $moduleUtil;
|
|
|
|
public function __construct(Util $commonUtil, ModuleUtil $moduleUtil)
|
|
{
|
|
$this->commonUtil = $commonUtil;
|
|
$this->moduleUtil = $moduleUtil;
|
|
}
|
|
|
|
public function getBusinessId(): int
|
|
{
|
|
return (int) request()->session()->get('user.business_id');
|
|
}
|
|
|
|
public function getBusiness(): ?Business
|
|
{
|
|
$business_id = $this->getBusinessId();
|
|
|
|
return $business_id ? Business::find($business_id) : null;
|
|
}
|
|
|
|
public function getCategoriesDropdown($business_id, $exclude_id = null): array
|
|
{
|
|
$categories = EquipmentCategory::where('business_id', $business_id)
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->get(['id', 'name', 'parent_id']);
|
|
|
|
$dropdown = [];
|
|
$this->buildCategoryDropdown($categories, null, $dropdown, 0, $exclude_id);
|
|
|
|
return $dropdown;
|
|
}
|
|
|
|
protected function buildCategoryDropdown($categories, $parent_id, &$dropdown, $depth, $exclude_id): void
|
|
{
|
|
foreach ($categories->where('parent_id', $parent_id) as $category) {
|
|
if ($exclude_id && (int) $category->id === (int) $exclude_id) {
|
|
continue;
|
|
}
|
|
$prefix = str_repeat('— ', $depth);
|
|
$dropdown[$category->id] = $prefix.$category->name;
|
|
$this->buildCategoryDropdown($categories, $category->id, $dropdown, $depth + 1, $exclude_id);
|
|
}
|
|
}
|
|
|
|
public function getProductionLinesDropdown($business_id, $project_id = null): array
|
|
{
|
|
$query = ProductionLine::where('business_id', $business_id)
|
|
->where('status', 'active')
|
|
->orderBy('name');
|
|
|
|
if ($project_id) {
|
|
$query->where('project_id', $project_id);
|
|
}
|
|
|
|
return $query->get()->mapWithKeys(function ($line) {
|
|
$label = $line->code ? "{$line->code} — {$line->name}" : $line->name;
|
|
|
|
return [$line->id => $label];
|
|
})->toArray();
|
|
}
|
|
|
|
public function getProjectsDropdown($business_id): array
|
|
{
|
|
if (! class_exists(\Modules\Project\Entities\Project::class)) {
|
|
return [];
|
|
}
|
|
|
|
if (! $this->moduleUtil->isModuleInstalled('Project')) {
|
|
return [];
|
|
}
|
|
|
|
return \Modules\Project\Entities\Project::where('business_id', $business_id)
|
|
->orderBy('name')
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
}
|
|
|
|
public function getCustomersDropdown($business_id): array
|
|
{
|
|
return Contact::customersDropdown($business_id, false)->toArray();
|
|
}
|
|
|
|
public function getEquipmentDropdown($business_id, $project_id = null, $production_line_id = null): array
|
|
{
|
|
$query = Equipment::where('business_id', $business_id)
|
|
->orderBy('name');
|
|
|
|
if ($production_line_id) {
|
|
$query->where('production_line_id', $production_line_id);
|
|
} elseif ($project_id) {
|
|
$query->where('project_id', $project_id);
|
|
}
|
|
|
|
return $query->get()->mapWithKeys(function ($eq) {
|
|
$label = $eq->code ? "{$eq->code} — {$eq->name}" : $eq->name;
|
|
|
|
return [$eq->id => $label];
|
|
})->toArray();
|
|
}
|
|
|
|
public function getUsersDropdown($business_id): array
|
|
{
|
|
return User::forDropdown($business_id, true);
|
|
}
|
|
|
|
public function statusLabels(): array
|
|
{
|
|
return [
|
|
'active' => 'فعال',
|
|
'inactive' => 'غیرفعال',
|
|
'under_maintenance' => 'در حال تعمیر',
|
|
'under_overhaul' => 'در حال اورهال',
|
|
'under_rebuild' => 'در حال بازسازی',
|
|
'retired' => 'مستهلک',
|
|
'draft' => 'پیشنویس',
|
|
'open' => 'باز',
|
|
'in_progress' => 'در حال انجام',
|
|
'on_hold' => 'معلق',
|
|
'completed' => 'تکمیلشده',
|
|
'cancelled' => 'لغوشده',
|
|
'planned' => 'برنامهریزیشده',
|
|
'approved' => 'تأییدشده',
|
|
'waiting' => 'در انتظار',
|
|
'suspended' => 'متوقف',
|
|
'scheduled' => 'زمانبندیشده',
|
|
'pending' => 'در انتظار بررسی',
|
|
'passed' => 'قبول',
|
|
'failed' => 'رد',
|
|
];
|
|
}
|
|
|
|
public function priorityLabels(): array
|
|
{
|
|
return [
|
|
'low' => 'کم',
|
|
'medium' => 'متوسط',
|
|
'high' => 'بالا',
|
|
'critical' => 'بحرانی',
|
|
'urgent' => 'فوری',
|
|
];
|
|
}
|
|
|
|
public function formatMoney($amount): string
|
|
{
|
|
return $this->commonUtil->num_f($amount, true);
|
|
}
|
|
|
|
public function parseInputDate(?string $date): ?string
|
|
{
|
|
if (empty($date)) {
|
|
return null;
|
|
}
|
|
|
|
if (function_exists('jalali_to_gregorian')) {
|
|
$converted = jalali_to_gregorian($date, session('business.date_format', 'Y/m/d'), false);
|
|
if (! empty($converted) && preg_match('/^\d{4}-\d{2}-\d{2}/', $converted)) {
|
|
return $converted;
|
|
}
|
|
}
|
|
|
|
return $this->commonUtil->uf_date($date);
|
|
}
|
|
|
|
public function parseInputDateTime(?string $date): ?string
|
|
{
|
|
if (empty($date)) {
|
|
return null;
|
|
}
|
|
|
|
if (function_exists('jalali_to_gregorian')) {
|
|
$converted = jalali_to_gregorian($date, session('business.date_format', 'Y/m/d'), true);
|
|
if (! empty($converted) && preg_match('/^\d{4}-\d{2}-\d{2}/', $converted)) {
|
|
return $converted;
|
|
}
|
|
}
|
|
|
|
return $this->commonUtil->uf_date($date, true);
|
|
}
|
|
|
|
public function formatDate($date): ?string
|
|
{
|
|
if (empty($date)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$carbon = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date);
|
|
$format = session('business.date_format', 'Y/m/d');
|
|
$formatted = format_jalali_date($carbon, $format);
|
|
|
|
return function_exists('persian_number') ? persian_number($formatted) : $formatted;
|
|
} catch (\Exception $e) {
|
|
return format_date($date);
|
|
}
|
|
}
|
|
|
|
public function formatDateTime($date): ?string
|
|
{
|
|
if (empty($date)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$carbon = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date);
|
|
$format = session('business.date_format', 'Y/m/d');
|
|
$time_format = session('business.time_format', 24) == 12 ? ' h:i A' : ' H:i';
|
|
$formatted = format_jalali_date($carbon, $format.$time_format);
|
|
|
|
return function_exists('persian_number') ? persian_number($formatted) : $formatted;
|
|
} catch (\Exception $e) {
|
|
return format_datetime($date);
|
|
}
|
|
}
|
|
|
|
public function formatMonthLabel(\Carbon\Carbon $date): string
|
|
{
|
|
try {
|
|
$formatted = format_jalali_date($date, 'Y/m');
|
|
|
|
return function_exists('persian_number') ? persian_number($formatted) : $formatted;
|
|
} catch (\Exception $e) {
|
|
return $date->format('Y-m');
|
|
}
|
|
}
|
|
|
|
protected function apiDateFields(): array
|
|
{
|
|
return [
|
|
'installation_date' => false,
|
|
'warranty_expires_at' => false,
|
|
'last_service_date' => false,
|
|
'next_service_date' => false,
|
|
'last_overhaul_date' => false,
|
|
'next_overhaul_date' => false,
|
|
'start_date' => false,
|
|
'end_date' => false,
|
|
'replaced_at' => false,
|
|
'expected_replacement_date' => false,
|
|
'last_replaced_at' => false,
|
|
'effective_date' => false,
|
|
'scheduled_at' => true,
|
|
'completed_at' => true,
|
|
'started_at' => true,
|
|
'reported_at' => true,
|
|
'visit_date' => true,
|
|
'performed_at' => true,
|
|
'next_due_at' => true,
|
|
'last_performed_at' => true,
|
|
'created_at' => true,
|
|
'updated_at' => true,
|
|
'read_at' => true,
|
|
'sent_at' => true,
|
|
'approved_at' => true,
|
|
];
|
|
}
|
|
|
|
public function appendJalaliDatesToApiPayload(mixed $data): mixed
|
|
{
|
|
if ($data instanceof \Illuminate\Database\Eloquent\Model) {
|
|
$data = $data->toArray();
|
|
} elseif ($data instanceof \Illuminate\Support\Collection) {
|
|
$data = $data->toArray();
|
|
}
|
|
|
|
if (! is_array($data)) {
|
|
return $data;
|
|
}
|
|
|
|
if (array_is_list($data)) {
|
|
return array_map(fn ($item) => $this->appendJalaliDatesToApiPayload($item), $data);
|
|
}
|
|
|
|
foreach ($this->apiDateFields() as $field => $isDatetime) {
|
|
if (! array_key_exists($field, $data) || $data[$field] === null || $data[$field] === '') {
|
|
continue;
|
|
}
|
|
|
|
$data[$field.'_jalali'] = $isDatetime
|
|
? $this->formatDateTime($data[$field])
|
|
: $this->formatDate($data[$field]);
|
|
}
|
|
|
|
foreach ($data as $key => $value) {
|
|
if (is_array($value)) {
|
|
$data[$key] = $this->appendJalaliDatesToApiPayload($value);
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function statusLabel(?string $status): string
|
|
{
|
|
return $this->statusLabels()[$status] ?? ($status ?: '—');
|
|
}
|
|
|
|
public function priorityLabel(?string $priority): string
|
|
{
|
|
return $this->priorityLabels()[$priority] ?? ($priority ?: '—');
|
|
}
|
|
|
|
public function jsonOrRedirect(\Illuminate\Http\Request $request, bool $success, string $msg, ?string $redirect = null)
|
|
{
|
|
$output = ['success' => $success, 'msg' => $msg];
|
|
|
|
if ($request->ajax() || $request->wantsJson()) {
|
|
return response()->json($output);
|
|
}
|
|
|
|
if ($success) {
|
|
return redirect()->to($redirect ?? url()->previous())->with('status', $msg);
|
|
}
|
|
|
|
return redirect()->back()->with('status', $msg);
|
|
}
|
|
|
|
public function getAssetsDropdown($business_id, $project_id = null): array
|
|
{
|
|
if (! class_exists(\Modules\AssetManagement\Entities\Asset::class)
|
|
|| ! $this->moduleUtil->isModuleInstalled('AssetManagement')) {
|
|
return [];
|
|
}
|
|
|
|
$query = \Modules\AssetManagement\Entities\Asset::where('business_id', $business_id)
|
|
->orderBy('name');
|
|
|
|
if ($project_id) {
|
|
$query->where(function ($q) use ($project_id) {
|
|
$q->where('project_id', $project_id)->orWhereNull('project_id');
|
|
});
|
|
}
|
|
|
|
return $query->get(['id', 'asset_code', 'name'])
|
|
->mapWithKeys(function ($asset) {
|
|
$label = $asset->asset_code ? "{$asset->asset_code} — {$asset->name}" : $asset->name;
|
|
|
|
return [$asset->id => $label];
|
|
})
|
|
->toArray();
|
|
}
|
|
|
|
public function getPartCatalogsDropdown($business_id): array
|
|
{
|
|
if (! class_exists(\Modules\Maintenance\Models\PartCatalog::class)) {
|
|
return [];
|
|
}
|
|
|
|
return \Modules\Maintenance\Models\PartCatalog::where('business_id', $business_id)
|
|
->where('status', 'active')
|
|
->orderBy('name')
|
|
->get()
|
|
->mapWithKeys(fn ($c) => [$c->id => ($c->catalog_code ? $c->catalog_code.' — ' : '').$c->name])
|
|
->toArray();
|
|
}
|
|
|
|
public function getSparePartsDropdown($business_id): array
|
|
{
|
|
return \Modules\Maintenance\Models\SparePart::where('business_id', $business_id)
|
|
->orderBy('name')
|
|
->get()
|
|
->mapWithKeys(fn ($p) => [$p->id => ($p->part_number ? $p->part_number.' — ' : '').$p->name])
|
|
->toArray();
|
|
}
|
|
|
|
public function getServiceProvidersDropdown($business_id): array
|
|
{
|
|
if (! class_exists(\Modules\Maintenance\Models\ServiceProvider::class)) {
|
|
return [];
|
|
}
|
|
|
|
return \Modules\Maintenance\Models\ServiceProvider::where('business_id', $business_id)
|
|
->orderBy('name')
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
}
|
|
|
|
public function getExternalServiceProvidersDropdown($business_id): array
|
|
{
|
|
if (! class_exists(\Modules\Maintenance\Models\ServiceProvider::class)) {
|
|
return [];
|
|
}
|
|
|
|
return \Modules\Maintenance\Models\ServiceProvider::where('business_id', $business_id)
|
|
->where('provider_type', 'external')
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
}
|
|
|
|
public function getHoldingEntitiesDropdown($business_id): array
|
|
{
|
|
if (! class_exists(\App\HoldingEntity::class)) {
|
|
return [];
|
|
}
|
|
|
|
return \App\HoldingEntity::where('business_id', $business_id)
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
}
|
|
|
|
public function getOrgUnitsDropdown($business_id, $entity_id = null): array
|
|
{
|
|
if (! class_exists(\App\OrgUnit::class)) {
|
|
return [];
|
|
}
|
|
|
|
$query = \App\OrgUnit::query()
|
|
->whereHas('entity', fn ($q) => $q->where('business_id', $business_id))
|
|
->where('is_active', true)
|
|
->orderBy('name');
|
|
|
|
if ($entity_id) {
|
|
$query->where('entity_id', $entity_id);
|
|
}
|
|
|
|
return $query->pluck('name', 'id')->toArray();
|
|
}
|
|
|
|
public function getEquipmentPartsDropdown($equipment_id, $exclude_id = null): array
|
|
{
|
|
$parts = \Modules\Maintenance\Models\EquipmentPart::where('equipment_id', $equipment_id)
|
|
->orderBy('part_name')
|
|
->get(['id', 'part_name', 'parent_id']);
|
|
|
|
$dropdown = [];
|
|
$this->buildPartDropdown($parts, null, $dropdown, 0, $exclude_id);
|
|
|
|
return $dropdown;
|
|
}
|
|
|
|
protected function buildPartDropdown($parts, $parent_id, &$dropdown, $depth, $exclude_id): void
|
|
{
|
|
foreach ($parts->where('parent_id', $parent_id) as $part) {
|
|
if ($exclude_id && (int) $part->id === (int) $exclude_id) {
|
|
continue;
|
|
}
|
|
$prefix = str_repeat('— ', $depth);
|
|
$dropdown[$part->id] = $prefix.$part->part_name;
|
|
$this->buildPartDropdown($parts, $part->id, $dropdown, $depth + 1, $exclude_id);
|
|
}
|
|
}
|
|
|
|
public function partTypeLabels(): array
|
|
{
|
|
return [
|
|
'mechanical' => __('maintenance::lang.part_type_mechanical'),
|
|
'electronic' => __('maintenance::lang.part_type_electronic'),
|
|
'consumable' => __('maintenance::lang.part_type_consumable'),
|
|
'hydraulic' => __('maintenance::lang.part_type_hydraulic'),
|
|
'pneumatic' => __('maintenance::lang.part_type_pneumatic'),
|
|
];
|
|
}
|
|
|
|
public function warrantyStatusLabels(): array
|
|
{
|
|
return [
|
|
'none' => __('maintenance::lang.warranty_none'),
|
|
'active' => __('maintenance::lang.warranty_active'),
|
|
'expired' => __('maintenance::lang.warranty_expired'),
|
|
];
|
|
}
|
|
|
|
public function getMaintenanceSettings($business_id): array
|
|
{
|
|
if (! \Illuminate\Support\Facades\Schema::hasColumn('business', 'maintenance_settings')) {
|
|
return [];
|
|
}
|
|
|
|
$raw = Business::where('id', $business_id)->value('maintenance_settings');
|
|
|
|
return ! empty($raw) ? (json_decode($raw, true) ?: []) : [];
|
|
}
|
|
|
|
public function pmRemindersEnabled($business_id): bool
|
|
{
|
|
$settings = $this->getMaintenanceSettings($business_id);
|
|
|
|
return ! array_key_exists('auto_pm_reminders', $settings) || ! empty($settings['auto_pm_reminders']);
|
|
}
|
|
|
|
public function pmAutoWorkOrderEnabled($business_id): bool
|
|
{
|
|
$settings = $this->getMaintenanceSettings($business_id);
|
|
|
|
return ! empty($settings['auto_create_pm_work_order']);
|
|
}
|
|
|
|
public function isAccountingEnabled($business_id): bool
|
|
{
|
|
if (! class_exists(\Modules\Accounting\Entities\AccountingAccount::class)) {
|
|
return false;
|
|
}
|
|
|
|
return $this->moduleUtil->isModuleInstalled('Accounting')
|
|
&& $this->moduleUtil->hasThePermissionInSubscription($business_id, 'accounting_module');
|
|
}
|
|
|
|
public function isAssetManagementEnabled($business_id): bool
|
|
{
|
|
return class_exists(\Modules\AssetManagement\Entities\Asset::class)
|
|
&& $this->moduleUtil->isModuleInstalled('AssetManagement')
|
|
&& $this->moduleUtil->hasThePermissionInSubscription($business_id, 'assetmanagement_module');
|
|
}
|
|
|
|
public function isCrmEnabled($business_id): bool
|
|
{
|
|
return class_exists(\Modules\Crm\Entities\Schedule::class)
|
|
&& $this->moduleUtil->isModuleInstalled('Crm')
|
|
&& $this->moduleUtil->hasThePermissionInSubscription($business_id, 'crm_module');
|
|
}
|
|
|
|
public function getAssetCategoriesDropdown($business_id): array
|
|
{
|
|
if (! class_exists(\App\Category::class)) {
|
|
return [];
|
|
}
|
|
|
|
return \App\Category::forDropdown($business_id, 'asset')->toArray();
|
|
}
|
|
|
|
public function getAccountingAccountsDropdown($business_id): array
|
|
{
|
|
if (! $this->isAccountingEnabled($business_id)) {
|
|
return [];
|
|
}
|
|
|
|
return \Modules\Accounting\Entities\AccountingAccount::forDropdown($business_id)->toArray();
|
|
}
|
|
|
|
public function buildEquipmentPartsTree($parts, ?int $parentId = null): array
|
|
{
|
|
$branch = [];
|
|
foreach ($parts as $part) {
|
|
if ((int) $part->parent_id === (int) $parentId) {
|
|
$branch[] = [
|
|
'part' => $part,
|
|
'children' => $this->buildEquipmentPartsTree($parts, $part->id),
|
|
];
|
|
}
|
|
}
|
|
|
|
return $branch;
|
|
}
|
|
|
|
public function getLocationsDropdown($business_id): array
|
|
{
|
|
if (! class_exists(\App\BusinessLocation::class)) {
|
|
return [];
|
|
}
|
|
|
|
$locations = \App\BusinessLocation::forDropdown($business_id, false, false, true, false);
|
|
|
|
return $locations instanceof \Illuminate\Support\Collection
|
|
? $locations->toArray()
|
|
: (array) $locations;
|
|
}
|
|
|
|
public function getProductsDropdown($business_id, int $limit = 500): array
|
|
{
|
|
return \App\Product::where('business_id', $business_id)
|
|
->where('type', '!=', 'modifier')
|
|
->orderBy('name')
|
|
->limit($limit)
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
}
|
|
|
|
public function toolStatusLabels(): array
|
|
{
|
|
return [
|
|
'available' => __('maintenance::lang.tool_status_available'),
|
|
'checked_out' => __('maintenance::lang.tool_status_checked_out'),
|
|
'maintenance' => __('maintenance::lang.tool_status_maintenance'),
|
|
'retired' => __('maintenance::lang.tool_status_retired'),
|
|
];
|
|
}
|
|
|
|
public function toolStatusLabel(?string $status): string
|
|
{
|
|
return $this->toolStatusLabels()[$status] ?? ($status ?: '—');
|
|
}
|
|
}
|