ultimatepos/Modules/Maintenance/Utils/MaintenanceUtil.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 ?: '—');
}
}