367 lines
12 KiB
PHP
367 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Modules\Tms\Utils;
|
|
|
|
use App\Business;
|
|
use App\BusinessLocation;
|
|
use App\Contact;
|
|
use App\Transaction;
|
|
use App\User;
|
|
use App\Utils\ModuleUtil;
|
|
use App\Utils\Util;
|
|
use Modules\Tms\Models\Carrier;
|
|
use Modules\Tms\Models\Driver;
|
|
use Modules\Tms\Models\Shipment;
|
|
use Modules\Tms\Models\Vehicle;
|
|
|
|
class TmsUtil
|
|
{
|
|
public function __construct(
|
|
protected Util $commonUtil,
|
|
protected 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 isModuleActive(): bool
|
|
{
|
|
if (! \Module::has('Tms') || ! \Module::isEnabled('Tms')) {
|
|
return false;
|
|
}
|
|
|
|
return (bool) $this->moduleUtil->isModuleInstalled('Tms');
|
|
}
|
|
|
|
public function getTmsSettings(?int $business_id = null): array
|
|
{
|
|
$business_id = $business_id ?? $this->getBusinessId();
|
|
$business = Business::find($business_id);
|
|
$defaults = [
|
|
'ref_prefix' => 'TMS',
|
|
'auto_create_on_sale' => false,
|
|
'sync_transaction_shipping' => true,
|
|
'default_carrier_id' => null,
|
|
'auto_post_freight_journal' => false,
|
|
'freight_expense_account_id' => null,
|
|
'freight_payment_account_id' => null,
|
|
'customs_ref_prefix' => 'CUS',
|
|
'auto_post_customs_journal' => false,
|
|
'customs_duty_expense_account_id' => null,
|
|
'customs_payment_account_id' => null,
|
|
'sync_customs_shipment_status' => true,
|
|
'auto_link_customs_shipment' => true,
|
|
'auto_create_shipment_on_customs_import' => true,
|
|
'require_customs_cleared_before_transit' => true,
|
|
'exclude_uncleared_from_routes' => true,
|
|
'sync_mission_status' => true,
|
|
];
|
|
|
|
if (! $business || empty($business->tms_settings)) {
|
|
return $defaults;
|
|
}
|
|
|
|
$stored = json_decode($business->tms_settings, true);
|
|
|
|
return is_array($stored) ? array_merge($defaults, $stored) : $defaults;
|
|
}
|
|
|
|
public function shipmentStatuses(): array
|
|
{
|
|
return [
|
|
'draft' => __('tms::lang.status_draft'),
|
|
'scheduled' => __('tms::lang.status_scheduled'),
|
|
'at_customs' => __('tms::lang.status_at_customs'),
|
|
'in_transit' => __('tms::lang.status_in_transit'),
|
|
'delivered' => __('tms::lang.status_delivered'),
|
|
'cancelled' => __('tms::lang.status_cancelled'),
|
|
];
|
|
}
|
|
|
|
public function mapCustomsStatusToShipment(string $customsStatus, ?string $currentShipmentStatus = null): ?string
|
|
{
|
|
$locked = in_array($currentShipmentStatus, ['in_transit', 'delivered'], true);
|
|
|
|
return match ($customsStatus) {
|
|
'draft', 'declared', 'assessment', 'payment_pending' => $locked ? null : 'at_customs',
|
|
'cleared' => $locked ? null : 'scheduled',
|
|
'released' => 'in_transit',
|
|
'cancelled' => 'cancelled',
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
public function vehicleTypes(): array
|
|
{
|
|
return [
|
|
'truck' => __('tms::lang.vehicle_truck'),
|
|
'van' => __('tms::lang.vehicle_van'),
|
|
'pickup' => __('tms::lang.vehicle_pickup'),
|
|
'trailer' => __('tms::lang.vehicle_trailer'),
|
|
'other' => __('tms::lang.vehicle_other'),
|
|
];
|
|
}
|
|
|
|
public function vehicleStatuses(): array
|
|
{
|
|
return [
|
|
'available' => __('tms::lang.vehicle_available'),
|
|
'in_use' => __('tms::lang.vehicle_in_use'),
|
|
'maintenance' => __('tms::lang.vehicle_maintenance'),
|
|
'inactive' => __('tms::lang.vehicle_inactive'),
|
|
];
|
|
}
|
|
|
|
public function shipmentTypes(): array
|
|
{
|
|
return [
|
|
'manual' => __('tms::lang.type_manual'),
|
|
'sell' => __('tms::lang.type_sell'),
|
|
'stock_transfer' => __('tms::lang.type_stock_transfer'),
|
|
'import' => __('tms::lang.type_import'),
|
|
'export' => __('tms::lang.type_export'),
|
|
'field_service' => __('tms::lang.type_field_service'),
|
|
'project_delivery' => __('tms::lang.type_project_delivery'),
|
|
];
|
|
}
|
|
|
|
public function carrierTypes(): array
|
|
{
|
|
return [
|
|
'internal' => __('tms::lang.carrier_internal'),
|
|
'external' => __('tms::lang.carrier_external'),
|
|
];
|
|
}
|
|
|
|
public function generateRefNo(int $business_id): string
|
|
{
|
|
$settings = $this->getTmsSettings($business_id);
|
|
$prefix = $settings['ref_prefix'] ?? 'TMS';
|
|
$count = Shipment::where('business_id', $business_id)->count() + 1;
|
|
|
|
return $prefix.'-'.str_pad((string) $count, 5, '0', STR_PAD_LEFT);
|
|
}
|
|
|
|
public function generateCustomsRefNo(int $business_id): string
|
|
{
|
|
$settings = $this->getTmsSettings($business_id);
|
|
$prefix = $settings['customs_ref_prefix'] ?? 'CUS';
|
|
$count = \Modules\Tms\Models\CustomsClearance::where('business_id', $business_id)->count() + 1;
|
|
|
|
return $prefix.'-'.str_pad((string) $count, 5, '0', STR_PAD_LEFT);
|
|
}
|
|
|
|
public function customsStatuses(): array
|
|
{
|
|
return [
|
|
'draft' => __('tms::lang.customs_status_draft'),
|
|
'declared' => __('tms::lang.customs_status_declared'),
|
|
'assessment' => __('tms::lang.customs_status_assessment'),
|
|
'payment_pending' => __('tms::lang.customs_status_payment_pending'),
|
|
'cleared' => __('tms::lang.customs_status_cleared'),
|
|
'released' => __('tms::lang.customs_status_released'),
|
|
'cancelled' => __('tms::lang.customs_status_cancelled'),
|
|
];
|
|
}
|
|
|
|
public function customsTypes(): array
|
|
{
|
|
return [
|
|
'import' => __('tms::lang.customs_type_import'),
|
|
'export' => __('tms::lang.customs_type_export'),
|
|
];
|
|
}
|
|
|
|
public function customsDocumentTypes(): array
|
|
{
|
|
return [
|
|
'commercial_invoice' => __('tms::lang.doc_commercial_invoice'),
|
|
'packing_list' => __('tms::lang.doc_packing_list'),
|
|
'bill_of_lading' => __('tms::lang.doc_bill_of_lading'),
|
|
'certificate_of_origin' => __('tms::lang.doc_certificate_of_origin'),
|
|
'customs_declaration' => __('tms::lang.doc_customs_declaration'),
|
|
'other' => __('tms::lang.doc_other'),
|
|
];
|
|
}
|
|
|
|
public function getBrokersDropdown(int $business_id): array
|
|
{
|
|
return \Modules\Tms\Models\CustomsBroker::where('business_id', $business_id)
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
}
|
|
|
|
public function getShipmentsDropdown(int $business_id): array
|
|
{
|
|
return Shipment::where('business_id', $business_id)
|
|
->orderByDesc('id')
|
|
->limit(200)
|
|
->get()
|
|
->mapWithKeys(fn ($s) => [$s->id => $s->ref_no])
|
|
->toArray();
|
|
}
|
|
|
|
public function getLinkableTransactionsDropdown(int $business_id, ?int $include_id = null): array
|
|
{
|
|
$typeLabels = [
|
|
'sell' => __('tms::lang.type_sell'),
|
|
'sell_transfer' => __('tms::lang.type_stock_transfer'),
|
|
'purchase' => __('tms::lang.type_import'),
|
|
];
|
|
|
|
$transactions = Transaction::where('business_id', $business_id)
|
|
->whereIn('type', ['sell', 'sell_transfer', 'purchase'])
|
|
->where('status', '!=', 'draft')
|
|
->with('contact:id,name,supplier_business_name')
|
|
->orderByDesc('transaction_date')
|
|
->limit(500)
|
|
->get(['id', 'ref_no', 'invoice_no', 'type', 'contact_id', 'transaction_date']);
|
|
|
|
$items = [];
|
|
foreach ($transactions as $txn) {
|
|
$items[$txn->id] = $this->formatTransactionDropdownLabel($txn, $typeLabels);
|
|
}
|
|
|
|
if ($include_id && ! isset($items[$include_id])) {
|
|
$txn = Transaction::where('business_id', $business_id)
|
|
->whereIn('type', ['sell', 'sell_transfer', 'purchase'])
|
|
->with('contact:id,name,supplier_business_name')
|
|
->find($include_id);
|
|
|
|
if ($txn) {
|
|
$items[$txn->id] = $this->formatTransactionDropdownLabel($txn, $typeLabels);
|
|
}
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
protected function formatTransactionDropdownLabel(Transaction $txn, array $typeLabels): string
|
|
{
|
|
$number = $txn->invoice_no ?: $txn->ref_no;
|
|
$label = $number.' — '.($typeLabels[$txn->type] ?? $txn->type);
|
|
|
|
$contactName = $txn->contact?->supplier_business_name ?: $txn->contact?->name;
|
|
if ($contactName) {
|
|
$label .= ' ('.$contactName.')';
|
|
}
|
|
|
|
if ($txn->transaction_date) {
|
|
$label .= ' ['.\Carbon::parse($txn->transaction_date)->format('Y-m-d').']';
|
|
}
|
|
|
|
return $label;
|
|
}
|
|
|
|
public function getUsersDropdown(int $business_id): array
|
|
{
|
|
$users = User::forDropdown($business_id, false, false, true);
|
|
|
|
return is_array($users) ? $users : $users->toArray();
|
|
}
|
|
|
|
public function getLocationsDropdown(int $business_id): array
|
|
{
|
|
$locations = BusinessLocation::forDropdown($business_id, true);
|
|
|
|
return is_array($locations) ? $locations : $locations->toArray();
|
|
}
|
|
|
|
public function getCustomersDropdown(int $business_id): array
|
|
{
|
|
return Contact::customersDropdown($business_id, false)->toArray();
|
|
}
|
|
|
|
public function getSuppliersDropdown(int $business_id): array
|
|
{
|
|
return Contact::suppliersDropdown($business_id, false)->toArray();
|
|
}
|
|
|
|
public function getVehiclesDropdown(int $business_id): array
|
|
{
|
|
return Vehicle::where('business_id', $business_id)
|
|
->where('is_active', true)
|
|
->orderBy('plate_number')
|
|
->get()
|
|
->mapWithKeys(fn ($v) => [$v->id => $v->plate_number.' — '.($this->vehicleTypes()[$v->vehicle_type] ?? $v->vehicle_type)])
|
|
->toArray();
|
|
}
|
|
|
|
public function getDriversDropdown(int $business_id): array
|
|
{
|
|
return Driver::where('business_id', $business_id)
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
}
|
|
|
|
public function getCarriersDropdown(int $business_id): array
|
|
{
|
|
return Carrier::where('business_id', $business_id)
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
}
|
|
|
|
public function mapStatusToTransactionShipping(string $tms_status): ?string
|
|
{
|
|
return match ($tms_status) {
|
|
'scheduled' => 'ordered',
|
|
'at_customs' => 'ordered',
|
|
'in_transit' => 'shipped',
|
|
'delivered' => 'delivered',
|
|
'cancelled' => 'cancelled',
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
$flash = ['success' => $success ? 1 : 0, 'msg' => $msg];
|
|
|
|
if ($success) {
|
|
return redirect()->to($redirect ?? url()->previous())->with('status', $flash);
|
|
}
|
|
|
|
return redirect()->back()->with('status', $flash);
|
|
}
|
|
|
|
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 getAccountingAccountsDropdown($business_id): array
|
|
{
|
|
if (! $this->isAccountingEnabled($business_id)) {
|
|
return [];
|
|
}
|
|
|
|
return \Modules\Accounting\Entities\AccountingAccount::forDropdown($business_id)->toArray();
|
|
}
|
|
}
|