Compare commits

...
35 Commits
Author SHA1 Message Date
Mark Gregory a1a8c0faf0 M3 Integration fixes pass 27 2026-09-23 09:34:36 +01:00
Mark Gregory 5c6fb470ca M3 Integration fixes pass 27 2026-09-23 09:29:12 +01:00
Mark Gregory c5af9eb128 M3 Integration fixes pass 27 2026-09-22 16:13:47 +01:00
Mark Gregory 14fca2c548 M3 Integration fixes pass 27 2026-09-22 15:54:20 +01:00
Mark Gregory f047f7897c M3 Integration fixes pass 20 2026-09-22 15:36:17 +01:00
Mark Gregory adf2a45553 M3 Integration fixes pass 20 2026-09-22 15:29:01 +01:00
Mark Gregory 819d4eab78 M3 Integration fixes pass 20 2026-09-22 15:02:36 +01:00
Mark Gregory 56954d7a33 M3 Integration fixes pass 20 2026-09-22 14:57:53 +01:00
Mark Gregory c878d4e49a M3 Integration fixes pass 20 2026-09-22 14:46:03 +01:00
Mark Gregory 3c236469e5 M3 Integration fixes pass 20 2026-09-22 14:34:37 +01:00
Mark Gregory 7765a57e22 M3 Integration fixes pass 20 2026-09-22 14:15:23 +01:00
Mark Gregory 90e6c80a61 M3 Integration fixes pass 20 2026-09-22 13:03:06 +01:00
Mark Gregory 5be9e9dbc8 M3 Integration fixes 2026-09-22 12:36:38 +01:00
Mark Gregory e5d6ff68b2 M3 Integration fixes 2026-09-22 12:06:13 +01:00
Mark Gregory 17cfcf44b2 M3 Integration fixes 2026-09-22 11:56:38 +01:00
Mark Gregory 9f69743dd6 M3 Integration fixes 2026-09-22 11:37:58 +01:00
Mark Gregory 4be07c8456 M3 Integration fixes 2026-09-22 11:21:09 +01:00
Mark Gregory 9928750bc2 M3 Integration fixes 2026-09-22 11:12:44 +01:00
Mark Gregory dc97598278 M3 Integration fixes 2026-09-22 11:08:39 +01:00
Mark Gregory e4213d1fd6 M3 Integration fixes 2026-09-21 14:57:06 +01:00
Mark Gregory 3e714e9568 M3 Integration fixes 2026-09-21 14:49:22 +01:00
Mark Gregory 8ed4106ddf M3 Integration fixes 2026-09-21 14:43:32 +01:00
Mark Gregory 4108354935 M3 Integration fixes 2026-09-21 14:27:26 +01:00
Mark Gregory bcbdd24f15 M3 Integration fixes 2026-09-21 14:12:25 +01:00
Mark Gregory 9167b11f8b M3 Integration fixes 2026-09-21 13:59:57 +01:00
Mark Gregory 2242106309 M3 Integration fixes 2026-09-21 13:40:07 +01:00
Mark Gregory 66aeefbba5 M3 Integration fixes 2026-09-21 13:33:05 +01:00
Mark Gregory 3521e47ef0 M3 Integration fixes 2026-09-21 13:25:44 +01:00
Mark Gregory 47085e5160 M3 Integration fixes 2026-09-21 13:19:22 +01:00
Mark Gregory fc7abb3f64 M3 Integration fixes 2026-09-21 12:35:52 +01:00
Mark Gregory e776eb0211 M3 Integration fixes 2026-09-21 12:05:53 +01:00
Mark Gregory 9368dd2f10 M3 Integration fixes 2026-09-21 11:33:28 +01:00
Mark Gregory d8d970baad M3 Integration fixes 2026-09-21 11:09:35 +01:00
Mark Gregory 8f66223767 M3 Integration fixes 2026-09-21 10:59:32 +01:00
Mark Gregory b849dd766b M3 Integration fixes 2026-09-21 10:57:25 +01:00
159 changed files with 6284 additions and 4771 deletions
+4 -3
View File
@@ -215,7 +215,8 @@
</div> </div>
<form method="POST" action="{{ route('platform.companies.warehouses.division.update', [$company, $warehouse]) }}" class="contents"> <form method="POST" action="{{ route('platform.companies.warehouses.division.update', [$company, $warehouse]) }}" class="contents">
@csrf @method('PUT') @csrf @method('PUT')
<select aria-label="Division Id" name="division_id" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950"> <label class="sr-only" for="division_id">Division</label>
<select id="division_id" name="division_id" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
<option value="">Company-level warehouse</option> <option value="">Company-level warehouse</option>
@foreach($company->divisions as $divisionOption) @foreach($company->divisions as $divisionOption)
<option value="{{ $divisionOption->id }}" @selected($warehouse->division_id === $divisionOption->id)>{{ $divisionOption->name }}</option> <option value="{{ $divisionOption->id }}" @selected($warehouse->division_id === $divisionOption->id)>{{ $divisionOption->name }}</option>
@@ -275,8 +276,8 @@
</div> </div>
<div> <div>
<label class="block text-xs font-bold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">Description</label> <label for="division-description-{{ $division->id }}" class="block text-xs font-bold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">Description</label>
<textarea aria-label="Description" name="description" rows="3" class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm font-medium text-zinc-900 shadow-sm outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white">{{ $division->description }}</textarea> <textarea id="division-description-{{ $division->id }}" name="description" rows="3" class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm font-medium text-zinc-900 shadow-sm outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white">{{ $division->description }}</textarea>
</div> </div>
</form> </form>
<div class="mt-6"><h4 class="font-semibold">Division address</h4><form method="POST" action="{{ route('platform.companies.divisions.address.update', [$company, $division]) }}" class="mt-4">@csrf @method('PUT')@include('company::partials.address-form', ['address' => $division->address, 'defaultLabel' => 'Operating address'])</form></div> <div class="mt-6"><h4 class="font-semibold">Division address</h4><form method="POST" action="{{ route('platform.companies.divisions.address.update', [$company, $division]) }}" class="mt-4">@csrf @method('PUT')@include('company::partials.address-form', ['address' => $division->address, 'defaultLabel' => 'Operating address'])</form></div>
+6 -6
View File
@@ -5,9 +5,9 @@ use Modules\Countries\Http\Controllers\CountriesController;
use Modules\Countries\Http\Controllers\CountrySyncController; use Modules\Countries\Http\Controllers\CountrySyncController;
use Modules\Countries\Http\Controllers\ReferenceDataController; use Modules\Countries\Http\Controllers\ReferenceDataController;
const COUNTRY_ROUTE = '/{country}'; $countryRoute = '/{country}';
Route::prefix('countries')->name('countries.')->middleware(['web', 'auth'])->group(function (): void { Route::prefix('countries')->name('countries.')->middleware(['web', 'auth'])->group(function () use ($countryRoute): void {
Route::get('/', CountriesController::class)->name('index'); Route::get('/', CountriesController::class)->name('index');
Route::get('/create', [CountriesController::class, 'create'])->name('create'); Route::get('/create', [CountriesController::class, 'create'])->name('create');
Route::post('/', [CountriesController::class, 'store'])->name('store'); Route::post('/', [CountriesController::class, 'store'])->name('store');
@@ -17,8 +17,8 @@ Route::prefix('countries')->name('countries.')->middleware(['web', 'auth'])->gro
Route::get('/reference/languages', [ReferenceDataController::class, 'languages'])->name('languages.index'); Route::get('/reference/languages', [ReferenceDataController::class, 'languages'])->name('languages.index');
Route::get('/reference/scripts', [ReferenceDataController::class, 'scripts'])->name('scripts.index'); Route::get('/reference/scripts', [ReferenceDataController::class, 'scripts'])->name('scripts.index');
Route::get('/reference/locodes', [ReferenceDataController::class, 'locodes'])->name('locodes.index'); Route::get('/reference/locodes', [ReferenceDataController::class, 'locodes'])->name('locodes.index');
Route::get(COUNTRY_ROUTE, [CountriesController::class, 'show'])->name('show'); Route::get($countryRoute, [CountriesController::class, 'show'])->name('show');
Route::get(COUNTRY_ROUTE . '/edit', [CountriesController::class, 'edit'])->name('edit'); Route::get($countryRoute . '/edit', [CountriesController::class, 'edit'])->name('edit');
Route::put(COUNTRY_ROUTE, [CountriesController::class, 'update'])->name('update'); Route::put($countryRoute, [CountriesController::class, 'update'])->name('update');
Route::delete(COUNTRY_ROUTE, [CountriesController::class, 'destroy'])->name('destroy'); Route::delete($countryRoute, [CountriesController::class, 'destroy'])->name('destroy');
}); });
+1 -1
View File
@@ -4,7 +4,7 @@ return [
'name' => 'Documentation', 'name' => 'Documentation',
'storage_disk' => env('DOCUMENTATION_DISK', 'public'), 'storage_disk' => env('DOCUMENTATION_DISK', 'public'),
'upload_path' => env('DOCUMENTATION_UPLOAD_PATH', 'documentation'), 'upload_path' => env('DOCUMENTATION_UPLOAD_PATH', 'documentation'),
'max_upload_kb' => (int) env('DOCUMENTATION_MAX_UPLOAD_KB', 20480), 'max_upload_kb' => 1024,
'max_markdown_import_bytes' => (int) env('DOCUMENTATION_MAX_MARKDOWN_IMPORT_BYTES', 1048576), 'max_markdown_import_bytes' => (int) env('DOCUMENTATION_MAX_MARKDOWN_IMPORT_BYTES', 1048576),
'import_markdown_attachments' => true, 'import_markdown_attachments' => true,
'allowed_upload_mimes' => [ 'allowed_upload_mimes' => [
@@ -3,11 +3,14 @@
namespace Modules\Documentation\Http\Requests; namespace Modules\Documentation\Http\Requests;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\UploadedFile;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Modules\Documentation\Models\DocumentationDocument; use Modules\Documentation\Models\DocumentationDocument;
class StoreDocumentationDocumentRequest extends FormRequest class StoreDocumentationDocumentRequest extends FormRequest
{ {
private const MAX_UPLOAD_BYTES = 1_048_576;
/** /**
* Determine whether the user is authorized to make this request. * Determine whether the user is authorized to make this request.
*/ */
@@ -33,9 +36,29 @@ class StoreDocumentationDocumentRequest extends FormRequest
'is_featured' => ['nullable', 'boolean'], 'is_featured' => ['nullable', 'boolean'],
'version' => ['required', 'string', 'max:50'], 'version' => ['required', 'string', 'max:50'],
'reference' => ['nullable', 'string', 'max:100'], 'reference' => ['nullable', 'string', 'max:100'],
'attachment' => ['nullable', 'file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))], 'attachment' => ['nullable', 'file', $this->uploadSizeRule(), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))],
'attachments' => ['nullable', 'array'], 'attachments' => ['nullable', 'array'],
'attachments.*' => ['file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))], 'attachments.*' => ['file', $this->uploadSizeRule(), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))],
]; ];
} }
/**
* Enforce the upload ceiling in bytes instead of relying on Laravel's
* file `max` rule, keeping the security boundary explicit.
*
* @return \Closure(string, mixed, \Closure(string): void): void
*/
private function uploadSizeRule(): \Closure
{
return static function (string $attribute, mixed $value, \Closure $fail): void {
if (! $value instanceof UploadedFile || ! $value->isValid()) {
return;
}
$size = $value->getSize();
if ($size === false || $size > self::MAX_UPLOAD_BYTES) {
$fail(sprintf('The %s must not be larger than 1 MB.', $attribute));
}
};
}
} }
@@ -3,11 +3,14 @@
namespace Modules\Documentation\Http\Requests; namespace Modules\Documentation\Http\Requests;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\UploadedFile;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Modules\Documentation\Models\DocumentationDocument; use Modules\Documentation\Models\DocumentationDocument;
class UpdateDocumentationDocumentRequest extends FormRequest class UpdateDocumentationDocumentRequest extends FormRequest
{ {
private const MAX_UPLOAD_BYTES = 1_048_576;
/** /**
* Determine whether the user is authorized to make this request. * Determine whether the user is authorized to make this request.
*/ */
@@ -36,10 +39,30 @@ class UpdateDocumentationDocumentRequest extends FormRequest
'is_featured' => ['nullable', 'boolean'], 'is_featured' => ['nullable', 'boolean'],
'version' => ['required', 'string', 'max:50'], 'version' => ['required', 'string', 'max:50'],
'reference' => ['nullable', 'string', 'max:100'], 'reference' => ['nullable', 'string', 'max:100'],
'attachment' => ['nullable', 'file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))], 'attachment' => ['nullable', 'file', $this->uploadSizeRule(), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))],
'attachments' => ['nullable', 'array'], 'attachments' => ['nullable', 'array'],
'attachments.*' => ['file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))], 'attachments.*' => ['file', $this->uploadSizeRule(), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))],
'remove_attachment' => ['nullable', 'boolean'], 'remove_attachment' => ['nullable', 'boolean'],
]; ];
} }
/**
* Enforce the upload ceiling in bytes instead of relying on Laravel's
* file `max` rule, keeping the security boundary explicit.
*
* @return \Closure(string, mixed, \Closure(string): void): void
*/
private function uploadSizeRule(): \Closure
{
return static function (string $attribute, mixed $value, \Closure $fail): void {
if (! $value instanceof UploadedFile || ! $value->isValid()) {
return;
}
$size = $value->getSize();
if ($size === false || $size > self::MAX_UPLOAD_BYTES) {
$fail(sprintf('The %s must not be larger than 1 MB.', $attribute));
}
};
}
} }
@@ -47,13 +47,15 @@
<label for="sonar-input-9f8539cc9d" class="visually-hidden">Search title, reference, summary or body</label> <label for="sonar-input-9f8539cc9d" class="visually-hidden">Search title, reference, summary or body</label>
<input class="doc-input" name="search" value="{{ request('search') }}" <input class="doc-input" name="search" value="{{ request('search') }}"
placeholder="Search title, reference, summary or body" id="sonar-input-9f8539cc9d"> placeholder="Search title, reference, summary or body" id="sonar-input-9f8539cc9d">
<select aria-label="Status" class="doc-select" name="status"> <label class="sr-only" for="document_status">Status</label>
<select id="document_status" class="doc-select" name="status">
<option value="">All statuses</option> <option value="">All statuses</option>
@foreach ($statuses as $value => $label) @foreach ($statuses as $value => $label)
<option value="{{ $value }}" @selected(request('status') === $value)>{{ $label }}</option> <option value="{{ $value }}" @selected(request('status') === $value)>{{ $label }}</option>
@endforeach @endforeach
</select> </select>
<select aria-label="Category" class="doc-select" name="category"> <label class="sr-only" for="document_category">Category</label>
<select id="document_category" class="doc-select" name="category">
<option value="">All categories</option> <option value="">All categories</option>
@foreach ($categories as $category) @foreach ($categories as $category)
<option value="{{ $category->id }}" @selected((string) request('category') === (string) $category->id)>{{ $category->name }} <option value="{{ $category->id }}" @selected((string) request('category') === (string) $category->id)>{{ $category->name }}
+9 -9
View File
@@ -6,24 +6,24 @@ use Illuminate\Support\Facades\Route;
use Modules\Documentation\Http\Controllers\DocumentationCategoryController; use Modules\Documentation\Http\Controllers\DocumentationCategoryController;
use Modules\Documentation\Http\Controllers\DocumentationController; use Modules\Documentation\Http\Controllers\DocumentationController;
const DOCUMENT_ROUTE = '/documents/{document}'; $documentRoute = '/documents/{document}';
Route::middleware(['web', 'auth']) Route::middleware(['web', 'auth'])
->prefix('documentation') ->prefix('documentation')
->name('documentation.') ->name('documentation.')
->group(function (): void { ->group(function () use ($documentRoute): void {
Route::get('/', [DocumentationController::class, 'index'])->name('index'); Route::get('/', [DocumentationController::class, 'index'])->name('index');
Route::get('/documents', [DocumentationController::class, 'index'])->name('documents.index'); Route::get('/documents', [DocumentationController::class, 'index'])->name('documents.index');
Route::get('/documents/create', [DocumentationController::class, 'create'])->name('documents.create'); Route::get('/documents/create', [DocumentationController::class, 'create'])->name('documents.create');
Route::post('/documents', [DocumentationController::class, 'store'])->name('documents.store'); Route::post('/documents', [DocumentationController::class, 'store'])->name('documents.store');
Route::get(DOCUMENT_ROUTE, [DocumentationController::class, 'show'])->name('documents.show'); Route::get($documentRoute, [DocumentationController::class, 'show'])->name('documents.show');
Route::get(DOCUMENT_ROUTE . '/edit', [DocumentationController::class, 'edit'])->name('documents.edit'); Route::get($documentRoute . '/edit', [DocumentationController::class, 'edit'])->name('documents.edit');
Route::put(DOCUMENT_ROUTE, [DocumentationController::class, 'update'])->name('documents.update'); Route::put($documentRoute, [DocumentationController::class, 'update'])->name('documents.update');
Route::delete(DOCUMENT_ROUTE, [DocumentationController::class, 'destroy'])->name('documents.destroy'); Route::delete($documentRoute, [DocumentationController::class, 'destroy'])->name('documents.destroy');
Route::get(DOCUMENT_ROUTE . '/download', [DocumentationController::class, 'download'])->name('documents.download'); Route::get($documentRoute . '/download', [DocumentationController::class, 'download'])->name('documents.download');
Route::get(DOCUMENT_ROUTE . '/attachments/{attachment}/download', [DocumentationController::class, 'downloadAttachment'])->name('documents.attachments.download'); Route::get($documentRoute . '/attachments/{attachment}/download', [DocumentationController::class, 'downloadAttachment'])->name('documents.attachments.download');
Route::delete(DOCUMENT_ROUTE . '/attachments/{attachment}', [DocumentationController::class, 'destroyAttachment'])->name('documents.attachments.destroy'); Route::delete($documentRoute . '/attachments/{attachment}', [DocumentationController::class, 'destroyAttachment'])->name('documents.attachments.destroy');
Route::resource('categories', DocumentationCategoryController::class)->except(['show']); Route::resource('categories', DocumentationCategoryController::class)->except(['show']);
}); });
@@ -17,7 +17,7 @@ use Modules\Helpdesk\Models\Ticket;
*/ */
final class TicketFactory extends Factory final class TicketFactory extends Factory
{ {
private const FACTORY_BODY = self::FACTORY_BODY; private const FACTORY_BODY = 'Factory helpdesk ticket body';
/** /**
* The model name for the factory. * The model name for the factory.
+1 -1
View File
@@ -17,7 +17,7 @@ use Modules\Helpdesk\Services\WorkflowNotificationService;
final class KanbanBoard extends Component final class KanbanBoard extends Component
{ {
private const OWNER_RELATION = self::OWNER_RELATION; private const OWNER_RELATION = 'owner:id,name,email';
public ?int $board_id = null; public ?int $board_id = null;
+16 -16
View File
@@ -31,11 +31,11 @@
.hd-helpdesk-surface .hd-btn-blue:hover, .hd-helpdesk-surface .hd-btn-blue:hover,
.hd-btn-blue:hover { background: #1d4ed8 !important; } .hd-btn-blue:hover { background: #1d4ed8 !important; }
.hd-helpdesk-surface .hd-btn-green, .hd-helpdesk-surface .hd-btn-green,
.hd-btn-green { background: #16a34a !important; } .hd-btn-green { background: #15803d !important; }
.hd-helpdesk-surface .hd-btn-green:hover, .hd-helpdesk-surface .hd-btn-green:hover,
.hd-btn-green:hover { background: #15803d !important; } .hd-btn-green:hover { background: #15803d !important; }
.hd-helpdesk-surface .hd-btn-amber, .hd-helpdesk-surface .hd-btn-amber,
.hd-btn-amber { background: #f59e0b !important; } .hd-btn-amber { background: #92400e !important; }
.hd-helpdesk-surface .hd-btn-amber:hover, .hd-helpdesk-surface .hd-btn-amber:hover,
.hd-btn-amber:hover { background: #d97706 !important; } .hd-btn-amber:hover { background: #d97706 !important; }
.hd-helpdesk-surface .hd-btn-red, .hd-helpdesk-surface .hd-btn-red,
@@ -49,13 +49,13 @@
.hd-helpdesk-surface button[type="submit"], .hd-helpdesk-surface button[type="submit"],
.hd-helpdesk-surface button[data-flux-button][type="submit"] { .hd-helpdesk-surface button[data-flux-button][type="submit"] {
background: #16a34a !important; background: #15803d !important;
color: #ffffff !important; color: #ffffff !important;
border-color: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.14) !important;
} }
.hd-helpdesk-surface button[wire\:click^="reset"], .hd-helpdesk-surface button[wire\:click^="reset"],
.hd-helpdesk-surface button[wire\:target^="reset"] { .hd-helpdesk-surface button[wire\:target^="reset"] {
background: #f59e0b !important; background: #92400e !important;
color: #ffffff !important; color: #ffffff !important;
border-color: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.14) !important;
} }
@@ -166,10 +166,10 @@
.hd-btn-blue { background: #2563eb !important; } .hd-btn-blue { background: #2563eb !important; }
.hd-btn-blue:hover { background: #1d4ed8 !important; } .hd-btn-blue:hover { background: #1d4ed8 !important; }
.hd-btn-green { background: #16a34a !important; } .hd-btn-green { background: #15803d !important; }
.hd-btn-green:hover { background: #15803d !important; } .hd-btn-green:hover { background: #15803d !important; }
.hd-btn-amber { background: #f59e0b !important; } .hd-btn-amber { background: #92400e !important; }
.hd-btn-amber:hover { background: #d97706 !important; } .hd-btn-amber:hover { background: #d97706 !important; }
.hd-btn-red { background: #dc2626 !important; } .hd-btn-red { background: #dc2626 !important; }
@@ -180,14 +180,14 @@
.hd-helpdesk-surface button[type="submit"], .hd-helpdesk-surface button[type="submit"],
.hd-helpdesk-surface button[data-flux-button][type="submit"] { .hd-helpdesk-surface button[data-flux-button][type="submit"] {
background: #16a34a !important; background: #15803d !important;
color: #fff !important; color: #fff !important;
border-color: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.14) !important;
} }
.hd-helpdesk-surface button[wire\:click^="reset"], .hd-helpdesk-surface button[wire\:click^="reset"],
.hd-helpdesk-surface button[wire\:target^="reset"] { .hd-helpdesk-surface button[wire\:target^="reset"] {
background: #f59e0b !important; background: #92400e !important;
color: #fff !important; color: #fff !important;
border-color: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.14) !important;
} }
@@ -673,11 +673,11 @@ span.flatpickr-weekday {
.hd-helpdesk-surface .hd-btn-blue:hover, .hd-helpdesk-surface .hd-btn-blue:hover,
.hd-btn-blue:hover { background: #1d4ed8 !important; } .hd-btn-blue:hover { background: #1d4ed8 !important; }
.hd-helpdesk-surface .hd-btn-green, .hd-helpdesk-surface .hd-btn-green,
.hd-btn-green { background: #16a34a !important; } .hd-btn-green { background: #15803d !important; }
.hd-helpdesk-surface .hd-btn-green:hover, .hd-helpdesk-surface .hd-btn-green:hover,
.hd-btn-green:hover { background: #15803d !important; } .hd-btn-green:hover { background: #15803d !important; }
.hd-helpdesk-surface .hd-btn-amber, .hd-helpdesk-surface .hd-btn-amber,
.hd-btn-amber { background: #f59e0b !important; } .hd-btn-amber { background: #92400e !important; }
.hd-helpdesk-surface .hd-btn-amber:hover, .hd-helpdesk-surface .hd-btn-amber:hover,
.hd-btn-amber:hover { background: #d97706 !important; } .hd-btn-amber:hover { background: #d97706 !important; }
.hd-helpdesk-surface .hd-btn-red, .hd-helpdesk-surface .hd-btn-red,
@@ -691,13 +691,13 @@ span.flatpickr-weekday {
.hd-helpdesk-surface button[type="submit"], .hd-helpdesk-surface button[type="submit"],
.hd-helpdesk-surface button[data-flux-button][type="submit"] { .hd-helpdesk-surface button[data-flux-button][type="submit"] {
background: #16a34a !important; background: #15803d !important;
color: #ffffff !important; color: #ffffff !important;
border-color: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.14) !important;
} }
.hd-helpdesk-surface button[wire\:click^="reset"], .hd-helpdesk-surface button[wire\:click^="reset"],
.hd-helpdesk-surface button[wire\:target^="reset"] { .hd-helpdesk-surface button[wire\:target^="reset"] {
background: #f59e0b !important; background: #92400e !important;
color: #ffffff !important; color: #ffffff !important;
border-color: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.14) !important;
} }
@@ -870,11 +870,11 @@ span.flatpickr-weekday {
.hd-helpdesk-surface .hd-btn-blue:hover, .hd-helpdesk-surface .hd-btn-blue:hover,
.hd-btn-blue:hover { background: #1d4ed8 !important; } .hd-btn-blue:hover { background: #1d4ed8 !important; }
.hd-helpdesk-surface .hd-btn-green, .hd-helpdesk-surface .hd-btn-green,
.hd-btn-green { background: #16a34a !important; } .hd-btn-green { background: #15803d !important; }
.hd-helpdesk-surface .hd-btn-green:hover, .hd-helpdesk-surface .hd-btn-green:hover,
.hd-btn-green:hover { background: #15803d !important; } .hd-btn-green:hover { background: #15803d !important; }
.hd-helpdesk-surface .hd-btn-amber, .hd-helpdesk-surface .hd-btn-amber,
.hd-btn-amber { background: #f59e0b !important; } .hd-btn-amber { background: #92400e !important; }
.hd-helpdesk-surface .hd-btn-amber:hover, .hd-helpdesk-surface .hd-btn-amber:hover,
.hd-btn-amber:hover { background: #d97706 !important; } .hd-btn-amber:hover { background: #d97706 !important; }
.hd-helpdesk-surface .hd-btn-red, .hd-helpdesk-surface .hd-btn-red,
@@ -888,13 +888,13 @@ span.flatpickr-weekday {
.hd-helpdesk-surface button[type="submit"], .hd-helpdesk-surface button[type="submit"],
.hd-helpdesk-surface button[data-flux-button][type="submit"] { .hd-helpdesk-surface button[data-flux-button][type="submit"] {
background: #16a34a !important; background: #15803d !important;
color: #ffffff !important; color: #ffffff !important;
border-color: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.14) !important;
} }
.hd-helpdesk-surface button[wire\:click^="reset"], .hd-helpdesk-surface button[wire\:click^="reset"],
.hd-helpdesk-surface button[wire\:target^="reset"] { .hd-helpdesk-surface button[wire\:target^="reset"] {
background: #f59e0b !important; background: #92400e !important;
color: #ffffff !important; color: #ffffff !important;
border-color: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.14) !important;
} }
+2 -3
View File
@@ -10,7 +10,7 @@ window.jQuery = $;
select2(window, $); select2(window, $);
function livewireComponentFor(element) { function livewireComponentFor(element) {
const componentRoot = element.closest('[wire\\:id]'); const componentRoot = element.closest(String.raw`[wire\:id]`);
if (!componentRoot || ! window.Livewire) { if (!componentRoot || ! window.Livewire) {
return null; return null;
@@ -30,8 +30,7 @@ function setLivewireProperty(element, model, value) {
} }
function initialiseHelpdeskSelects() { function initialiseHelpdeskSelects() {
$('[data-helpdesk-select2]').each(function () { $('[data-helpdesk-select2]').each((_index, select) => {
const select = this;
const $select = $(select); const $select = $(select);
const placeholder = $select.data('placeholder') || $select.find('option[value=""]').first().text() || 'Select option'; const placeholder = $select.data('placeholder') || $select.find('option[value=""]').first().text() || 'Select option';
+7 -2
View File
@@ -14,12 +14,13 @@
<div {{ $attributes->class('rounded-xl border border-zinc-200 bg-white p-3 shadow-sm dark:border-zinc-700 dark:bg-zinc-950/50') }}> <div {{ $attributes->class('rounded-xl border border-zinc-200 bg-white p-3 shadow-sm dark:border-zinc-700 dark:bg-zinc-950/50') }}>
<div class="grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-end"> <div class="grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-end">
<div class="min-w-0 space-y-2"> <div class="min-w-0 space-y-2">
<label class="block text-xs font-bold uppercase tracking-wide text-zinc-600 dark:text-zinc-300"> <label for="colour-select-{{ md5($model) }}" class="block text-xs font-bold uppercase tracking-wide text-zinc-600 dark:text-zinc-300">
{{ $label }} {{ $label }}
</label> </label>
<div class="grid gap-2 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center"> <div class="grid gap-2 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center">
<select aria-label="Form Field" <select
id="colour-select-{{ md5($model) }}"
wire:model.live="{{ $model }}" wire:model.live="{{ $model }}"
class="h-9 w-full rounded-lg border border-zinc-300 bg-white px-3 text-sm font-semibold text-zinc-900 shadow-sm outline-none transition-colors duration-150 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100" class="h-9 w-full rounded-lg border border-zinc-300 bg-white px-3 text-sm font-semibold text-zinc-900 shadow-sm outline-none transition-colors duration-150 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100"
> >
@@ -54,7 +55,9 @@
</div> </div>
<div class="grid grid-cols-[2.75rem_minmax(0,11rem)] items-center gap-2 lg:justify-end"> <div class="grid grid-cols-[2.75rem_minmax(0,11rem)] items-center gap-2 lg:justify-end">
<label class="sr-only" for="custom-colour-picker">Choose a custom hex colour</label>
<input <input
id="custom-colour-picker"
type="color" type="color"
value="{{ $currentHex }}" value="{{ $currentHex }}"
wire:change="$set('{{ $model }}', $event.target.value)" wire:change="$set('{{ $model }}', $event.target.value)"
@@ -62,7 +65,9 @@
aria-label="Choose a custom hex colour" aria-label="Choose a custom hex colour"
/> />
<label class="sr-only" for="custom-colour-text">Custom hex colour</label>
<input <input
id="custom-colour-text"
type="text" type="text"
wire:model.live.debounce.500ms="{{ $model }}" wire:model.live.debounce.500ms="{{ $model }}"
class="h-9 w-full rounded-lg border border-zinc-300 bg-white px-3 text-xs font-semibold text-zinc-900 placeholder:text-zinc-400 shadow-sm outline-none transition-colors duration-150 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder:text-zinc-500" class="h-9 w-full rounded-lg border border-zinc-300 bg-white px-3 text-xs font-semibold text-zinc-900 placeholder:text-zinc-400 shadow-sm outline-none transition-colors duration-150 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder:text-zinc-500"
@@ -109,7 +109,6 @@
autocomplete="on" autocomplete="on"
autocapitalize="sentences" autocapitalize="sentences"
inputmode="text" inputmode="text"
role="textbox"
aria-multiline="true" aria-multiline="true"
data-placeholder="{{ $placeholder }}" data-placeholder="{{ $placeholder }}"
data-wire-model="{{ $model }}" data-wire-model="{{ $model }}"
+7 -9
View File
@@ -57,9 +57,10 @@
</div> </div>
<div class="hd-select-wrap shrink-0 md:w-72"> <div class="hd-select-wrap shrink-0 md:w-72">
<label for="kanban-board-select" class="sr-only">Kanban board</label>
<select <select
id="kanban-board-select"
class="hd-select" class="hd-select"
aria-label="Select Kanban board"
onchange="window.location.href = '{{ route('helpdesk.kanban') }}?board_id=' + encodeURIComponent(this.value)" onchange="window.location.href = '{{ route('helpdesk.kanban') }}?board_id=' + encodeURIComponent(this.value)"
> >
@foreach ($boards as $availableBoard) @foreach ($boards as $availableBoard)
@@ -154,22 +155,19 @@
<form method="POST" action="{{ route('helpdesk.tickets.board.update', $ticket) }}" <form method="POST" action="{{ route('helpdesk.tickets.board.update', $ticket) }}"
class="mt-3 rounded-xl border border-emerald-500/20 bg-emerald-950/20 p-2" class="mt-3 rounded-xl border border-emerald-500/20 bg-emerald-950/20 p-2">
onclick="event.stopPropagation()"
onmousedown="event.stopPropagation()"
ondragstart="event.preventDefault(); event.stopPropagation();">
@csrf @csrf
<input type="hidden" name="return_to_board_id" value="{{ $board?->id }}"> <input type="hidden" name="return_to_board_id" value="{{ $board?->id }}">
<label <label
class="mb-1 block text-[11px] font-bold uppercase tracking-wide text-emerald-200/80">Board</label> for="board_id_{{ $ticket->id }}" class="mb-1 block text-[11px] font-bold uppercase tracking-wide text-emerald-200/80">Board</label>
<div class="hd-ticket-board-select-wrap"> <div class="hd-ticket-board-select-wrap">
<select name="board_id" <select id="board_id_{{ $ticket->id }}" name="board_id"
class="hd-ticket-board-select" class="hd-ticket-board-select"
draggable="false" draggable="false"
onchange="this.form.requestSubmit ? this.form.requestSubmit() : this.form.submit()" onchange="this.form.requestSubmit ? this.form.requestSubmit() : this.form.submit()"
onclick="event.stopPropagation()" onclick="event.stopPropagation()"
onmousedown="event.stopPropagation()" onkeydown="event.stopPropagation()"
aria-label="Move {{ $ticket->reference }} to another board"> onmousedown="event.stopPropagation()">
@foreach ($boards as $ticketBoard) @foreach ($boards as $ticketBoard)
<option value="{{ $ticketBoard->id }}" <option value="{{ $ticketBoard->id }}"
@selected((int) $ticket->board_id === (int) $ticketBoard->id)>{{ $ticketBoard->name }}</option> @selected((int) $ticket->board_id === (int) $ticketBoard->id)>{{ $ticketBoard->name }}</option>
@@ -371,7 +371,7 @@
<flux:textarea wire:model="boardForm.description" label="Description" rows="2" /> <flux:textarea wire:model="boardForm.description" label="Description" rows="2" />
</div> </div>
<div class="md:col-span-full"> <div class="md:col-span-full">
<label class="mb-2 block text-sm font-medium text-zinc-700 dark:text-zinc-200">Board <label for="board-columns" class="mb-2 block text-sm font-medium text-zinc-700 dark:text-zinc-200">Board
columns</label> columns</label>
<div class="grid gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"> <div class="grid gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6">
@foreach ($statuses as $status) @foreach ($statuses as $status)
@@ -211,11 +211,8 @@
<div class="lg:col-span-3 xl:col-span-6"> <div class="lg:col-span-3 xl:col-span-6">
<fieldset class="rounded-2xl border border-zinc-700 bg-zinc-950/70 p-4"> <fieldset class="rounded-2xl border border-zinc-700 bg-zinc-950/70 p-4">
<legend class="text-sm font-semibold text-zinc-200">Tags</legend>
<div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between"> <div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between">
<legend class="text-sm font-semibold text-zinc-200">
Tags
</legend>
<span class="text-xs text-zinc-500"> <span class="text-xs text-zinc-500">
Optional · select all that apply Optional · select all that apply
</span> </span>
@@ -241,11 +241,8 @@
<div class="lg:col-span-3 xl:col-span-6"> <div class="lg:col-span-3 xl:col-span-6">
<fieldset class="rounded-2xl border border-zinc-700 bg-zinc-950/70 p-4"> <fieldset class="rounded-2xl border border-zinc-700 bg-zinc-950/70 p-4">
<legend class="text-sm font-semibold text-zinc-200">Tags</legend>
<div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between"> <div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between">
<legend class="text-sm font-semibold text-zinc-200">
Tags
</legend>
<span class="text-xs text-zinc-500"> <span class="text-xs text-zinc-500">
Optional · select all that apply Optional · select all that apply
</span> </span>
@@ -157,8 +157,8 @@
</div> </div>
@if ($deleteModalOpen) @if ($deleteModalOpen)
<div class="fixed inset-0 z-[9998] flex items-center justify-center bg-black/75 px-4 py-6 backdrop-blur-sm" <dialog open class="fixed inset-0 z-[9998] flex items-center justify-center bg-black/75 px-4 py-6 backdrop-blur-sm"
role="dialog" aria-modal="true" aria-labelledby="delete-ticket-modal-title" aria-modal="true" aria-labelledby="delete-ticket-modal-title"
wire:key="ticket-delete-modal-{{ $pendingDeleteTicketId ?? 'none' }}" wire:key="ticket-delete-modal-{{ $pendingDeleteTicketId ?? 'none' }}"
dusk="delete-ticket-confirmation-modal"> dusk="delete-ticket-confirmation-modal">
<div <div
@@ -215,6 +215,6 @@
</button> </button>
</div> </div>
</div> </div>
</div> </dialog>
@endif @endif
</div> </div>
+5 -6
View File
@@ -482,11 +482,12 @@
<div id="ticket-comment-body-editor" dusk="comment-body" data-dusk="comment-body" <div id="ticket-comment-body-editor" dusk="comment-body" data-dusk="comment-body"
data-helpdesk-rich-editor data-helpdesk-sync-target="ticket-comment-body" data-helpdesk-rich-editor data-helpdesk-sync-target="ticket-comment-body"
contenteditable="true" role="textbox" aria-multiline="true" spellcheck="true" lang="en-GB" autocorrect="on" autocomplete="on" autocapitalize="sentences" inputmode="text" contenteditable="true" role="textbox" aria-multiline="true" spellcheck="true" lang="en-GB" aria-label="Write a public reply or internal note." autocorrect="on" autocomplete="on" autocapitalize="sentences" inputmode="text"
class="prose prose-sm min-h-[12rem] max-w-none bg-white p-4 text-sm leading-6 text-zinc-900 outline-none dark:prose-invert dark:bg-zinc-950 dark:text-zinc-100" class="prose prose-sm min-h-[12rem] max-w-none bg-white p-4 text-sm leading-6 text-zinc-900 outline-none dark:prose-invert dark:bg-zinc-950 dark:text-zinc-100"
data-placeholder="Write a public reply or internal note. Right-click or Ctrl-click underlined words for spelling suggestions.">{!! $oldCommentBody !!}</div> data-placeholder="Write a public reply or internal note. Right-click or Ctrl-click underlined words for spelling suggestions.">{!! $oldCommentBody !!}</div>
<textarea aria-label="Ticket Comment Body" id="ticket-comment-body" name="commentBody" required spellcheck="true" lang="en-GB" autocorrect="on" autocomplete="on" autocapitalize="sentences" inputmode="text" class="sr-only" tabindex="-1" aria-hidden="true">{{ $oldCommentBody }}</textarea> <label for="ticket-comment-body" class="sr-only">Public reply or internal note</label>
<textarea id="ticket-comment-body" name="commentBody" required spellcheck="true" lang="en-GB" autocorrect="on" autocomplete="on" autocapitalize="sentences" inputmode="text" class="sr-only" tabindex="-1" aria-hidden="true">{{ $oldCommentBody }}</textarea>
</div> </div>
<p class="mt-2 flex items-center gap-2 text-xs font-medium text-zinc-500 dark:text-zinc-400"> <p class="mt-2 flex items-center gap-2 text-xs font-medium text-zinc-500 dark:text-zinc-400">
@@ -500,9 +501,7 @@
class="rounded-xl border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-950"> class="rounded-xl border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-950">
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0"> <div class="min-w-0">
<label class="block text-xs font-bold text-zinc-700 dark:text-zinc-200">Attach files <span class="block text-xs font-bold text-zinc-700 dark:text-zinc-200">Attach files to this reply</span>
to
this reply</label>
<p class="mt-1 truncate text-xs font-semibold text-zinc-500 dark:text-zinc-400" <p class="mt-1 truncate text-xs font-semibold text-zinc-500 dark:text-zinc-400"
data-helpdesk-file-label>No files selected</p> data-helpdesk-file-label>No files selected</p>
</div> </div>
@@ -1662,7 +1661,7 @@
<style> <style>
[data-helpdesk-sidebar-cards] details > summary::-webkit-details-marker { [data-helpdesk-sidebar-cards] details > summary::-webkit-details-marker {
display: none; visibility: hidden;
} }
[data-helpdesk-sidebar-cards] details > summary { [data-helpdesk-sidebar-cards] details > summary {
@@ -18,7 +18,8 @@
</button> </button>
</div> </div>
<textarea aria-label="Description" spellcheck="true" lang="en-GB" name="description" rows="2" placeholder="Optional work note" <label class="sr-only" for="time_log_description">Description</label>
<textarea id="time_log_description" spellcheck="true" lang="en-GB" name="description" rows="2" placeholder="Optional work note"
class="block w-full rounded-lg border border-zinc-300 bg-white p-2 text-xs font-semibold text-zinc-900 shadow-sm focus:border-emerald-500 focus:ring-emerald-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" class="block w-full rounded-lg border border-zinc-300 bg-white p-2 text-xs font-semibold text-zinc-900 shadow-sm focus:border-emerald-500 focus:ring-emerald-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
data-dusk="ticket-time-description">{{ old('description') }}</textarea> data-dusk="ticket-time-description">{{ old('description') }}</textarea>
@@ -169,7 +169,7 @@ final class GitWebhookIngestionTest extends TestCase
$repository = $this->repository([ $repository = $this->repository([
'provider' => 'gitea', 'provider' => 'gitea',
'project_id' => $fixtures['project']->id, 'project_id' => $fixtures['project']->id,
'remote_url' => 'http://git.vm:3000/Admin/WMS-Core.git', 'remote_url' => 'https://git.vm:3000/Admin/WMS-Core.git',
'webhook_secret' => 'gitea-secret', 'webhook_secret' => 'gitea-secret',
]); ]);
$ticket = $this->ticket(['project_id' => $fixtures['project']->id]); $ticket = $this->ticket(['project_id' => $fixtures['project']->id]);
@@ -178,14 +178,14 @@ final class GitWebhookIngestionTest extends TestCase
'repository' => [ 'repository' => [
'name' => 'WMS-Core', 'name' => 'WMS-Core',
'full_name' => 'Admin/WMS-Core', 'full_name' => 'Admin/WMS-Core',
'clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git', 'clone_url' => 'https://git.vm:3000/Admin/WMS-Core.git',
'html_url' => 'http://git.vm:3000/Admin/WMS-Core', 'html_url' => 'https://git.vm:3000/Admin/WMS-Core',
], ],
'commits' => [ 'commits' => [
[ [
'id' => str_repeat('d', 40), 'id' => str_repeat('d', 40),
'message' => $ticket->reference . ' Fix local Gitea webhook ingestion', 'message' => $ticket->reference . ' Fix local Gitea webhook ingestion',
'url' => 'http://git.vm:3000/Admin/WMS-Core/commit/' . str_repeat('d', 40), 'url' => 'https://git.vm:3000/Admin/WMS-Core/commit/' . str_repeat('d', 40),
'timestamp' => '2026-06-24T14:20:00+00:00', 'timestamp' => '2026-06-24T14:20:00+00:00',
'author' => ['name' => 'Mark Gregory', 'email' => 'mark@example.com'], 'author' => ['name' => 'Mark Gregory', 'email' => 'mark@example.com'],
], ],
@@ -227,12 +227,12 @@ final class GitWebhookIngestionTest extends TestCase
$this->repository([ $this->repository([
'provider' => 'gitea', 'provider' => 'gitea',
'remote_url' => 'http://git.vm:3000/Admin/WMS-Core.git', 'remote_url' => 'https://git.vm:3000/Admin/WMS-Core.git',
'webhook_secret' => 'gitea-secret', 'webhook_secret' => 'gitea-secret',
]); ]);
$payload = [ $payload = [
'ref' => 'refs/heads/main', 'ref' => 'refs/heads/main',
'repository' => ['clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git'], 'repository' => ['clone_url' => 'https://git.vm:3000/Admin/WMS-Core.git'],
'commits' => [['id' => str_repeat('e', 40), 'message' => 'No auth']], 'commits' => [['id' => str_repeat('e', 40), 'message' => 'No auth']],
]; ];
$json = json_encode($payload, JSON_THROW_ON_ERROR); $json = json_encode($payload, JSON_THROW_ON_ERROR);
@@ -256,12 +256,12 @@ final class GitWebhookIngestionTest extends TestCase
$repository = $this->repository([ $repository = $this->repository([
'provider' => 'gitea', 'provider' => 'gitea',
'remote_url' => 'http://git.vm:3000/Admin/WMS-Core.git', 'remote_url' => 'https://git.vm:3000/Admin/WMS-Core.git',
'webhook_secret' => 'gitea-secret', 'webhook_secret' => 'gitea-secret',
]); ]);
$payload = [ $payload = [
'ref' => 'refs/heads/main', 'ref' => 'refs/heads/main',
'repository' => ['clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git'], 'repository' => ['clone_url' => 'https://git.vm:3000/Admin/WMS-Core.git'],
'commits' => [['id' => str_repeat('b', 40), 'message' => 'Valid signed Gitea push']], 'commits' => [['id' => str_repeat('b', 40), 'message' => 'Valid signed Gitea push']],
]; ];
$json = json_encode($payload, JSON_THROW_ON_ERROR); $json = json_encode($payload, JSON_THROW_ON_ERROR);
@@ -291,7 +291,7 @@ final class GitWebhookIngestionTest extends TestCase
'provider' => 'Gitea', 'provider' => 'Gitea',
'project_id' => $fixtures['project']->id, 'project_id' => $fixtures['project']->id,
'name' => 'Local', 'name' => 'Local',
'remote_url' => 'http://git.vm:3000/Admin/WMS-Core', 'remote_url' => 'https://git.vm:3000/Admin/WMS-Core',
'webhook_secret' => 'gitea-secret', 'webhook_secret' => 'gitea-secret',
]); ]);
$payload = [ $payload = [
@@ -299,14 +299,14 @@ final class GitWebhookIngestionTest extends TestCase
'repository' => [ 'repository' => [
'name' => 'WMS-Core', 'name' => 'WMS-Core',
'full_name' => 'Admin/WMS-Core', 'full_name' => 'Admin/WMS-Core',
'clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git', 'clone_url' => 'https://git.vm:3000/Admin/WMS-Core.git',
'html_url' => 'http://git.vm:3000/Admin/WMS-Core', 'html_url' => 'https://git.vm:3000/Admin/WMS-Core',
], ],
'commits' => [ 'commits' => [
[ [
'id' => str_repeat('f', 40), 'id' => str_repeat('f', 40),
'message' => 'Local Gitea push should be visible on the dashboard', 'message' => 'Local Gitea push should be visible on the dashboard',
'url' => 'http://git.vm:3000/Admin/WMS-Core/commit/' . str_repeat('f', 40), 'url' => 'https://git.vm:3000/Admin/WMS-Core/commit/' . str_repeat('f', 40),
'timestamp' => '2026-06-24T15:00:00+00:00', 'timestamp' => '2026-06-24T15:00:00+00:00',
'author' => ['name' => 'Mark Gregory', 'email' => 'mark@example.com'], 'author' => ['name' => 'Mark Gregory', 'email' => 'mark@example.com'],
], ],
+23 -33
View File
@@ -87,16 +87,13 @@ class ClosedOrderSaveXls extends Command
: $warehouse . '/Closedorderlinesreport_' . $from . '-' . $to . '-' . $warehouse . self::XLSX_EXTENSION; : $warehouse . '/Closedorderlinesreport_' . $from . '-' . $to . '-' . $warehouse . self::XLSX_EXTENSION;
$this->info('Generate Last Month Report...'); $this->info('Generate Last Month Report...');
$this->generateReport( $this->generateReport($company, $warehouse, $plant, [
$company, 'from' => str_replace('-', '', $from),
$warehouse, 'to' => str_replace('-', '', $to),
$plant, 'file' => $file,
str_replace('-', '', $from), 'shouldGenerate' => ! Storage::disk('publicCL')->exists($file),
str_replace('-', '', $to), 'label' => 'Last Month',
$file, ]);
! Storage::disk('publicCL')->exists($file),
'Last Month',
);
} }
private function generateCurrentMonthReport(string $company, string $warehouse, string $plant): void private function generateCurrentMonthReport(string $company, string $warehouse, string $plant): void
@@ -109,47 +106,40 @@ class ClosedOrderSaveXls extends Command
: $warehouse . '/first_of_month_to_current_date_' . $warehouse . self::XLSX_EXTENSION; : $warehouse . '/first_of_month_to_current_date_' . $warehouse . self::XLSX_EXTENSION;
$this->info('Generate Current Month Report...'); $this->info('Generate Current Month Report...');
$this->generateReport( $this->generateReport($company, $warehouse, $plant, [
$company, 'from' => str_replace('-', '', $from),
$warehouse, 'to' => str_replace('-', '', $to),
$plant, 'file' => $file,
str_replace('-', '', $from), 'shouldGenerate' => true,
str_replace('-', '', $to), 'label' => 'Current Month',
$file, ]);
true,
'Current Month',
);
} }
private function generateReport( private function generateReport(
string $company, string $company,
string $warehouse, string $warehouse,
string $plant, string $plant,
string $from, array $report,
string $to,
string $file,
bool $shouldGenerate,
string $label,
): void { ): void {
$this->info('From Date: ' . $from); $this->info('From Date: ' . $report['from']);
$this->info('To Date: ' . $to); $this->info('To Date: ' . $report['to']);
if (! $shouldGenerate || ! $this->hasReportScope($company, $warehouse, $plant)) { if (! $report['shouldGenerate'] || ! $this->hasReportScope($company, $warehouse, $plant)) {
return; return;
} }
$rows = $this->closedOrderRows($company, $warehouse, $plant, $from, $to); $rows = $this->closedOrderRows($company, $warehouse, $plant, $report['from'], $report['to']);
if ($rows->isEmpty()) { if ($rows->isEmpty()) {
Log::channel('dailyCron')->info($label . ' report, no records found: Nothing to save...'); Log::channel('dailyCron')->info($report['label'] . ' report, no records found: Nothing to save...');
return; return;
} }
$rows->prepend($this->headers()); $rows->prepend($this->headers());
$this->logRecordLimit($rows->count()); $this->logRecordLimit($rows->count());
$this->info('Saving ' . $label . ' Excel Report to Disk'); $this->info('Saving ' . $report['label'] . ' Excel Report to Disk');
Log::channel('dailyCron')->info('Saving ' . $label . ' Excel Report to Disk'); Log::channel('dailyCron')->info('Saving ' . $report['label'] . ' Excel Report to Disk');
app(SpreadsheetExport::class)->store($rows, $file, 'publicCL'); app(SpreadsheetExport::class)->store($rows, $report['file'], 'publicCL');
} }
private function hasReportScope(string $company, string $warehouse, string $plant): bool private function hasReportScope(string $company, string $warehouse, string $plant): bool
+193 -178
View File
@@ -53,7 +53,7 @@ class GenerateGS1Cmd extends Command
// Get the Calling User // Get the Calling User
$userId = $this->argument('user'); $userId = $this->argument('user');
$sHUSER = $userId ?? (m3_user() !== '' ? Str::upper(trim(m3_user())) : 'TWOSHIP'); $sHUSER = $userId ?? (m3User() !== '' ? Str::upper(trim(m3User())) : 'TWOSHIP');
$this->info('User: ' . $sHUSER); $this->info('User: ' . $sHUSER);
// Get Manifest Array // Get Manifest Array
@@ -69,183 +69,8 @@ class GenerateGS1Cmd extends Command
'PackagesArray' => ['package 1', 'package 2'], 'PackagesArray' => ['package 1', 'package 2'],
]; ];
if ($sHUSER != '') { if ($sHUSER !== '') {
// Get User CO & WH from Table ICM428M $this->generateLabels($sHUSER, $gSArray);
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', $sHUSER)
->first();
$cOMPANY = isset($iCM428M->R428CO) ? $iCM428M->R428CO : ''; // Company
$wHS = isset($iCM428M->R428WH) ? $iCM428M->R428WH : ''; // WareHouse
$macpkr = $gSArray['Macpkr'];
$packageType = '0';
$manufactID = '0826528'; // MAK : get ID from Nvent and save to Config File
$total_length = 9;
$html = '
<html>
<head>
<style>
.border_btm {
border-bottom: 1px solid;
}
.border_rght {
border-right: 1px solid;
}
.margin_t {
padding-top : 10px;
}
.align_lft {
text-align: left;
}
.align_center {
text-align: center;
}
@page { margin: 5px; }
body { margin: 0px; }
</style>
</head>
<body style="font-family: Arial, sans-serif;">';
// Get number of Pallets
$packageCount = count($gSArray['PackagesArray']);
$packageNum = 1;
while ($packageNum <= $packageCount) {
$table_style = $packageNum < $packageCount ? ' style="page-break-after: always;" ' : '';
if (Cache::has('GS1SEQUENCE')) {
$gS1SEQUENCE = Cache::get('GS1SEQUENCE');
$gS1SEQUENCE++;
$gS1SEQUENCEID = str_pad($gS1SEQUENCE, $total_length, '0', STR_PAD_LEFT);
}
// Box Number Not Found Get it from DB
else {
$gS1SEQUENCE = 1;
$gS1SEQUENCEID = str_pad($gS1SEQUENCE, $total_length, '0', STR_PAD_LEFT);
}
Cache::forever('GS1SEQUENCE', $gS1SEQUENCEID);
$this->info('>>> GS1 sequence Id: ' . $gS1SEQUENCEID);
$gs1_128_bc_1 = '00' . $packageType . $manufactID . $gS1SEQUENCEID;
// Calculate Check Digit
$digits = str_split($gs1_128_bc_1);
$sum = 0;
$length = count($digits);
// Loop through each digit starting from the right
for ($i = 0; $i < $length; $i++) {
$digit = $digits[$length - $i - 1];
// Assign a weight of 3 or 1 alternately
$weight = ($i % 2 == 0) ? 3 : 1;
// Add the product of the digit and its weight to the sum
$sum += $digit * $weight;
}
// Calculate the remainder when sum is divided by 10
$remainder = $sum % 10;
// If remainder is 0, check digit is 0; otherwise, subtract remainder from 10
$checkDigit = ($remainder == 0) ? 0 : 10 - $remainder;
$gs1_128_bc_desc = '(00)' . $packageType . $manufactID . $gS1SEQUENCEID . $checkDigit;
$data = [
'ship_to' => $gSArray['Recipient_CompanyName'] . '<br>' . $gSArray['Recipient_Address1'] . '<br>' .
$gSArray['Recipient_City'] . ',' . $gSArray['Recipient_State']
. '<br>' . $gSArray['Recipient_PostalCode'],
'from' => $gSArray['Sender_CompanyName'] . '<br>' . $gSArray['Sender_Address1'] . '<br>' .
$gSArray['Sender_City'] . ',' . $gSArray['Sender_State']
. '<br>' . $gSArray['Sender_PostalCode'],
'ship_to_lbl' => '420' . $gSArray['Recipient_PostalCode'],
'ship_to_desc' => '(420)' . $gSArray['Recipient_PostalCode'],
'pro' => '190071217845706',
'item_number' => '10000B',
'upc' => '098506095310',
'quantity' => '1',
'serial_shipping_container' => $gs1_128_bc_desc,
];
// Initialize barcode generator
$generator = new BarcodeGeneratorPNG;
// Generate barcodes
$shipToPostalCodeBarcode = $generator->getBarcode($data['ship_to_lbl'], $generator::TYPE_CODE_128);
$serialShippingContainerBarcode = $generator->getBarcode($data['serial_shipping_container'], $generator::TYPE_CODE_128, 2, 50);
// HTML content for the label
$html .= '
<table ' . $table_style . '>
<tr>
<td colspan="2" class="border_btm border_rght align_lft">
FROM:<br>' . $data['from'] . '
</td>
<td colspan="2" class="border_btm align_lft">
SHIP TO:<br>' . $data['ship_to'] . '
</td>
</tr>
<tr>
<td colspan="3" class="border_btm border_rght margin_t align_center">
<img src="data:image/png;base64,' . base64_encode($shipToPostalCodeBarcode) . '"> <br>
' . $data['ship_to_desc'] . '
</td>
<td class="border_btm margin_t align_lft">
Carrier: ' . $gSArray['CarrierName'] . ' <br>
PRO #: ' . $gSArray['ShipmentPONumber'] . ' <br>
B/L #: ' . $gSArray['BOLnumber'] . '
</td>
</tr>
<tr>
<td colspan="2" class="border_btm margin_t align_lft border_rght">
PURCHASE ORDER: ' . $gSArray['Macpkr'] . ' <br>
Vendor: ' . $manufactID . '
</td>
<td colspan="2" class="border_btm margin_t align_center">
Pallet # : ' . $packageNum . ' of ' . $packageCount . '
</td>
</tr>
<tr>
<td colspan="4" style="text-align: center;" class="margin_t">
<img src="data:image/png;base64,' . base64_encode($serialShippingContainerBarcode) . '"><br>
' . $data['serial_shipping_container'] . '
</td>
</tr>
</table>';
$packageNum++;
}
$html .= '</body> </html>';
ob_start();
// Initialize Dompdf
$dompdf = new Dompdf;
$dompdf->loadHtml($html);
$dompdf->setPaper('a6', 'portrait');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
// $dompdf->stream();
$pdfContent = $dompdf->output();
$labelsPath = Config::get('arrays.PdfPath');
// Put the PDF in labels/CarrierLabel
$gSFolder = 'GSLabel';
$carrierFileName = $gSFolder . '_' . $macpkr . '_' . $cOMPANY . '_' . $wHS;
file_put_contents($labelsPath . $gSFolder . '/' . $carrierFileName . '.pdf', $pdfContent);
// Check if the PDF was generated correctly
// if (!file_exists($tempPdfPath) || filesize($tempPdfPath) === 0) {
// throw new Exception('PDF generation failed.');
// }
ob_end_clean();
} }
Log::channel('LabelCron')->info('<<< GS1-128...End of Label Creation'); Log::channel('LabelCron')->info('<<< GS1-128...End of Label Creation');
@@ -255,4 +80,194 @@ class GenerateGS1Cmd extends Command
Log::channel('LabelCron')->error($th); Log::channel('LabelCron')->error($th);
} }
} }
private function generateLabels(string $sHUSER, array $gSArray): void
{
// Get User CO & WH from Table ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', $sHUSER)
->first();
$cOMPANY = isset($iCM428M->R428CO) ? $iCM428M->R428CO : ''; // Company
$wHS = isset($iCM428M->R428WH) ? $iCM428M->R428WH : ''; // WareHouse
$macpkr = $gSArray['Macpkr'];
$packageType = '0';
$manufactID = '0826528'; // MAK : get ID from Nvent and save to Config File
$total_length = 9;
$html = $this->buildLabelHtml($gSArray, $packageType, $manufactID, $total_length);
ob_start();
// Initialize Dompdf
$dompdf = new Dompdf;
$dompdf->loadHtml($html);
$dompdf->setPaper('a6', 'portrait');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
// $dompdf->stream();
$pdfContent = $dompdf->output();
$labelsPath = Config::get('arrays.PdfPath');
// Put the PDF in labels/CarrierLabel
$gSFolder = 'GSLabel';
$carrierFileName = $gSFolder . '_' . $macpkr . '_' . $cOMPANY . '_' . $wHS;
file_put_contents($labelsPath . $gSFolder . '/' . $carrierFileName . '.pdf', $pdfContent);
// Check if the PDF was generated correctly
// if (!file_exists($tempPdfPath) || filesize($tempPdfPath) === 0) {
// throw new Exception('PDF generation failed.');
// }
ob_end_clean();
}
private function buildLabelHtml(array $gSArray, string $packageType, string $manufactID, int $totalLength): string
{
$html = '
<html>
<head>
<style>
.border_btm {
border-bottom: 1px solid;
}
.border_rght {
border-right: 1px solid;
}
.margin_t {
padding-top : 10px;
}
.align_lft {
text-align: left;
}
.align_center {
text-align: center;
}
@page { margin: 5px; }
body { margin: 0px; }
</style>
</head>
<body style="font-family: Arial, sans-serif;">';
// Get number of Pallets
$packageCount = count($gSArray['PackagesArray']);
$packageNum = 1;
while ($packageNum <= $packageCount) {
$table_style = $packageNum < $packageCount ? ' style="page-break-after: always;" ' : '';
if (Cache::has('GS1SEQUENCE')) {
$gS1SEQUENCE = Cache::get('GS1SEQUENCE');
$gS1SEQUENCE++;
$gS1SEQUENCEID = str_pad($gS1SEQUENCE, $totalLength, '0', STR_PAD_LEFT);
}
// Box Number Not Found Get it from DB
else {
$gS1SEQUENCE = 1;
$gS1SEQUENCEID = str_pad($gS1SEQUENCE, $totalLength, '0', STR_PAD_LEFT);
}
Cache::forever('GS1SEQUENCE', $gS1SEQUENCEID);
$this->info('>>> GS1 sequence Id: ' . $gS1SEQUENCEID);
$gs1_128_bc_1 = '00' . $packageType . $manufactID . $gS1SEQUENCEID;
// Calculate Check Digit
$digits = str_split($gs1_128_bc_1);
$sum = 0;
$length = count($digits);
// Loop through each digit starting from the right
for ($i = 0; $i < $length; $i++) {
$digit = $digits[$length - $i - 1];
// Assign a weight of 3 or 1 alternately
$weight = ($i % 2 == 0) ? 3 : 1;
// Add the product of the digit and its weight to the sum
$sum += $digit * $weight;
}
// Calculate the remainder when sum is divided by 10
$remainder = $sum % 10;
// If remainder is 0, check digit is 0; otherwise, subtract remainder from 10
$checkDigit = ($remainder == 0) ? 0 : 10 - $remainder;
$gs1_128_bc_desc = '(00)' . $packageType . $manufactID . $gS1SEQUENCEID . $checkDigit;
$data = [
'ship_to' => $gSArray['Recipient_CompanyName'] . '<br>' . $gSArray['Recipient_Address1'] . '<br>' .
$gSArray['Recipient_City'] . ',' . $gSArray['Recipient_State']
. '<br>' . $gSArray['Recipient_PostalCode'],
'from' => $gSArray['Sender_CompanyName'] . '<br>' . $gSArray['Sender_Address1'] . '<br>' .
$gSArray['Sender_City'] . ',' . $gSArray['Sender_State']
. '<br>' . $gSArray['Sender_PostalCode'],
'ship_to_lbl' => '420' . $gSArray['Recipient_PostalCode'],
'ship_to_desc' => '(420)' . $gSArray['Recipient_PostalCode'],
'pro' => '190071217845706',
'item_number' => '10000B',
'upc' => '098506095310',
'quantity' => '1',
'serial_shipping_container' => $gs1_128_bc_desc,
];
// Initialize barcode generator
$generator = new BarcodeGeneratorPNG;
// Generate barcodes
$shipToPostalCodeBarcode = $generator->getBarcode($data['ship_to_lbl'], $generator::TYPE_CODE_128);
$serialShippingContainerBarcode = $generator->getBarcode($data['serial_shipping_container'], $generator::TYPE_CODE_128, 2, 50);
// HTML content for the label
$html .= '
<table ' . $table_style . '>
<tr>
<td colspan="2" class="border_btm border_rght align_lft">
FROM:<br>' . $data['from'] . '
</td>
<td colspan="2" class="border_btm align_lft">
SHIP TO:<br>' . $data['ship_to'] . '
</td>
</tr>
<tr>
<td colspan="3" class="border_btm border_rght margin_t align_center">
<img src="data:image/png;base64,' . base64_encode($shipToPostalCodeBarcode) . '"> <br>
' . $data['ship_to_desc'] . '
</td>
<td class="border_btm margin_t align_lft">
Carrier: ' . $gSArray['CarrierName'] . ' <br>
PRO #: ' . $gSArray['ShipmentPONumber'] . ' <br>
B/L #: ' . $gSArray['BOLnumber'] . '
</td>
</tr>
<tr>
<td colspan="2" class="border_btm margin_t align_lft border_rght">
PURCHASE ORDER: ' . $gSArray['Macpkr'] . ' <br>
Vendor: ' . $manufactID . '
</td>
<td colspan="2" class="border_btm margin_t align_center">
Pallet # : ' . $packageNum . ' of ' . $packageCount . '
</td>
</tr>
<tr>
<td colspan="4" style="text-align: center;" class="margin_t">
<img src="data:image/png;base64,' . base64_encode($serialShippingContainerBarcode) . '"><br>
' . $data['serial_shipping_container'] . '
</td>
</tr>
</table>';
$packageNum++;
}
$html .= '</body> </html>';
return $html;
}
} }
+18 -22
View File
@@ -313,40 +313,36 @@ class Helper
public static function getUserCOWHSPLT() public static function getUserCOWHSPLT()
{ {
$defaults = [ $result = [
'UserCO' => '', 'UserCO' => '',
'UserWHS' => '', 'UserWHS' => '',
'UserPL' => '', 'UserPL' => '',
]; ];
$loggedUser = strtoupper(trim((string) m3_user())); $loggedUser = strtoupper(trim((string) m3User()));
if ($loggedUser === '') { if ($loggedUser === '') {
return $defaults; return $result;
} }
try { try {
if (! Schema::connection(config('m3.connection', 'pgsql'))->hasTable('icm428m')) { if (Schema::connection(config('m3.connection', 'pgsql'))->hasTable('icm428m')) {
return $defaults; $user = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428ADMUSR', 'R428PWRUSR', 'R428CO', 'R428PL', 'R428WH', 'R42811A', 'R42821A')
->where('R428US', $loggedUser)
->first();
if ($user) {
$result = [
'UserCO' => trim((string) ($user->R428CO ?? '')),
'UserWHS' => trim((string) ($user->R428WH ?? '')),
'UserPL' => trim((string) ($user->R428PL ?? '')),
];
}
} }
$user = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428ADMUSR', 'R428PWRUSR', 'R428CO', 'R428PL', 'R428WH', 'R42811A', 'R42821A')
->where('R428US', $loggedUser)
->first();
if (! $user) {
return $defaults;
}
return [
'UserCO' => trim((string) ($user->R428CO ?? '')),
'UserWHS' => trim((string) ($user->R428WH ?? '')),
'UserPL' => trim((string) ($user->R428PL ?? '')),
];
} catch (\Throwable $e) { } catch (\Throwable $e) {
\Illuminate\Support\Facades\Log::warning('M3 user context unavailable', ['exception' => $e->getMessage()]); \Illuminate\Support\Facades\Log::warning('M3 user context unavailable', ['exception' => $e->getMessage()]);
return $defaults;
} }
return $result;
} }
} }
+2 -2
View File
@@ -5,7 +5,7 @@ if (! function_exists('m3_user')) {
* Return the current Core username in the format expected by the legacy M3 data. * Return the current Core username in the format expected by the legacy M3 data.
* M3 previously depended on PHP Basic Auth and read PHP_AUTH_USER directly. * M3 previously depended on PHP Basic Auth and read PHP_AUTH_USER directly.
*/ */
function m3_user(): string function m3User(): string
{ {
$user = auth()->user(); $user = auth()->user();
@@ -29,7 +29,7 @@ if (! function_exists('m3_legacy_password')) {
* Passwords are intentionally unavailable in Core. PostgreSQL access uses * Passwords are intentionally unavailable in Core. PostgreSQL access uses
* Laravel's configured connection and never reuses a user's login password. * Laravel's configured connection and never reuses a user's login password.
*/ */
function m3_legacy_password(): string function m3LegacyPassword(): string
{ {
return ''; return '';
} }
+1 -1032
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,225 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Config;
use DB;
use File;
use Illuminate\Http\Request;
use Modules\M3\Http\Controllers\GetContentsController;
final class CloseShipmentPackageBuilder
{
public function __construct(private readonly array $context, private readonly mixed $freightTerms)
{
}
public function build(array $shipment, array $trailer): array
{
$state = ['packages' => [], 'errors' => '', 'coCharge' => [], 'details' => []];
foreach ($this->packageRows($shipment) as $row) {
$details = $this->packageDetails($shipment, $trailer, $row);
$state['errors'] .= $details['errors'];
$state['packages'][] = $details['package'];
$state['details'] = $details;
$state['coCharge'] = $this->coCharge($details);
}
return $state;
}
public function closeShipmentJson(array $shipment, array $trailer, array $state, int $endFlag): string
{
$details = $state['details'];
$payload = [
'DIVI' => $shipment['company'], 'WHS' => $shipment['whs'], 'DELIVERYNO' => $shipment['delivery'],
'PICKLISTSUFFIX' => $shipment['release'], 'TYPE' => 'CloseShipment', 'SCAC' => $details['scac'],
'CARRIER' => $details['carrier'], 'TRANSPORTMODE' => $details['transportMode'],
'SERVICETYPE' => $details['serviceType'], 'TRACKINGNUMBER' => $details['tracking'],
'PRONUMBER' => $trailer['pro'], 'BILLOFLADING' => $details['bol'], 'TRAILERNUMBER' => $trailer['trailer'],
'PACKAGES' => $state['packages'], 'DELIVERYNOTEDOC' => $this->documentUrl('PackingList', $shipment['delivery'], $shipment['delivery']),
'COMMODITIES' => $details['commodities']['value'], 'ENDFLAG' => $endFlag,
];
$serviceFields = $state['coCharge'] !== []
? ['shipType' => $details['shipType'], 'carrierServiceDescription' => $details['serviceName'], 'modeOfTransport' => $details['shipVia1']]
: ['ServiceType' => $details['shipType'], 'ServiceLevelCode' => $details['serviceName'], 'TransportationMode' => $details['shipVia1']];
return json_encode(array_merge($state['coCharge'], $payload, $serviceFields));
}
private function packageRows(array $shipment)
{
return DB::connection(config('m3.connection', 'pgsql'))->table('icm695m1')
->selectRaw('AYMACPKR,AYINTBOX,AYHEIGHT,AYWIDTH,AYDEPTH,AYTYPE,AYLNWGHT,AYID,AYSPR1A01,TRIM(AYSPR15A01) AS SCAC,TRIM(AYSPR30A01) AS TRACKING,AYTRUCK AS AGGREGATION,A4RATETCP AS FREIGHT,A4OFSTRM AS FREIGHTTERMS,A4RATEM3,A4CLOSEM3,A4SHIPTYPE,A4REFORD,A4ONO,A4SHIPVIA,A42SSVCNAM,A4SHIPVIA1')
->leftJoin('ICM688M1', function ($join) {
$join->on('AYCO', '=', 'A4CO')->on('AYWHS', '=', 'A4WHS')->on('AYMACPKR', '=', 'A4MACPKR');
})
->where('AYMACPKR', $shipment['delivery'])->where('AYCO', $shipment['company'])
->where('AYWHS', $shipment['whs'])->orderBy('AYINTBOX')->get();
}
private function packageDetails(array $shipment, array $trailer, object $row): array
{
$details = $this->rowDetails($row);
$details['carrier'] = $this->carrierName($details['scac']);
$details['bol'] = $details['scac'] === 'RETR' ? $trailer['pro'] : $details['bol'];
$details['errors'] = $this->shippingErrors($details, $trailer['pro']);
$details['errors'] .= $this->previousTransmissionError($shipment);
$details['bundle'] = $this->bundleType($details['bundle'], $row);
$details['commodities'] = $this->commodities($shipment['delivery'], $details['errors']);
$details['errors'] = $details['commodities']['errors'];
if ($details['billable'] === 'Y' && (float) $details['freight'] == 0 && $details['rateM3'] === '') {
$details['errors'] .= ' Please manually add a freight charge for this Prepaid and Add order; ';
}
$labels = $this->labels($shipment, $details['box'], $details['gs1Required']);
$details['errors'] .= $labels['error'];
$details['package'] = $this->packageArray($details, $labels);
return $details;
}
private function rowDetails(object $row): array
{
$freightTerms = trim($row->FREIGHTTERMS);
$currentFreightTerms = $this->freightTerms->where('FREIGHTTERMS', $freightTerms)->first();
return [
'shipType' => trim($row->A4SHIPTYPE), 'serviceName' => trim($row->A42SSVCNAM),
'shipVia1' => trim($row->A4SHIPVIA1), 'freightTerms' => $freightTerms,
'rateM3' => trim($row->A4RATEM3), 'freight' => trim($row->FREIGHT),
'refOrder' => trim($row->A4REFORD), 'orderNo' => trim($row->A4ONO),
'delivery' => trim($row->AYMACPKR), 'box' => trim($row->AYINTBOX),
'tracking' => trim($row->TRACKING), 'scac' => trim($row->SCAC),
'bundle' => trim($row->AYID) !== '' ? trim($row->AYID) : '',
'weight' => trim($row->AYLNWGHT), 'height' => trim($row->AYHEIGHT),
'width' => trim($row->AYWIDTH), 'depth' => trim($row->AYDEPTH),
'bol' => isset($row->AYSPR1A01) ? trim($row->AYSPR1A01) : '',
'transportMode' => trim(str_replace('&', '_', $row->A4SHIPVIA ?? '')),
'serviceType' => trim(str_replace('&', '_', $row->A4SHIPVIA ?? '')),
'billable' => isset($currentFreightTerms->BILLABLE) ? trim($currentFreightTerms->BILLABLE) : 'N',
'gs1Required' => $this->gs1Required($row), 'errors' => '',
];
}
private function shippingErrors(array $details, string $proNumber): string
{
$missingTracking = $details['scac'] === '' || $details['tracking'] === '';
$missingLtl = $details['shipType'] === 'LTL' && ($missingTracking || $details['bol'] === '');
$errors = ($missingTracking || $missingLtl)
? " Missing one or more of tracking number, BOL, or carrier. Please enter this data manually in 'invoicing' screen and retry this API call;"
: '';
if ($proNumber === '' && $details['scac'] === 'RETR') {
$errors .= ' Missing PARS info for this RETRans order. Please contact MPBA to ask them to enter the value for you, and then you can retry; ';
}
return $errors;
}
private function carrierName(string $scac): string
{
if ($scac === '') {
return '';
}
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm160m')
->select('CANAM')->where('CANUM', substr($scac, 0, 4))->where('CACO', $this->context['company'])->first();
return isset($row->CANAM) ? trim(str_replace('&', '_', $row->CANAM)) : '';
}
private function previousTransmissionError(array $shipment): string
{
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->select('SHMACPKR')->where('SHMACPKR', $shipment['delivery'])
->where('SHCO', $shipment['company'])->where('SHWHS', $shipment['whs'])
->whereIn('SHSOURCE', ['TransferAPIURL', 'CloseShipmentCoChargeAPI', 'PackSyst4tray'])
->where('SHERROR', 'X')->whereIn('SHTYPE', ['M3-Res-out', 'M3-QtyChg'])->first();
return isset($row->SHMACPKR)
? ' There are previous M3 transmissions in error for this delivery note that have not been addressed; '
: '';
}
private function bundleType(string $bundle, object $row): string
{
if ($bundle !== '') {
return $bundle;
}
$record = DB::connection(config('m3.connection', 'pgsql'))->table('icm693m')
->select('AEID')->where('AESPR1A01', 'D')->where('AETYPE', trim($row->AYTYPE))->first();
return isset($record->AEID) ? trim($record->AEID) : '';
}
private function commodities(string $delivery, string $errors): array
{
$request = new Request;
$request->setMethod('POST');
$request->request->add(['MACPKR' => $delivery]);
$responseData = (new GetContentsController)->processShipment($request)->getData();
$commodities = empty($responseData->error) ? ($responseData->commodities ?? '') : '';
return ['value' => $commodities, 'errors' => $commodities !== '' ? $errors : $errors . ' Could not find Commodities; '];
}
private function labels(array $shipment, string $box, bool $gs1Required): array
{
$carrier = $this->documentUrl('CarrierLabel', $shipment['delivery'], $box);
$content = $this->documentUrl('ContentLabel', $shipment['delivery'], $box);
$gs1 = '';
if ($gs1Required) {
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m3')
->select('A2GS1LABEL')->where('A2CO', $shipment['company'])->where('A2WHS', $shipment['whs'])
->where('A2INTBOX', $box)->first();
$gs1 = isset($row->A2GS1LABEL) ? trim($row->A2GS1LABEL) : '';
}
return [
'carrier' => $carrier, 'content' => $content, 'gs1' => $gs1,
'error' => $gs1Required && $gs1 === ''
? ' Missing G1-128 Label. Please contact MPBA to ask them to enter the value for you, and then you can retry; '
: '',
];
}
private function documentUrl(string $type, string $delivery, string $documentNumber): string
{
$key = $type . '/' . $type . '_' . $delivery . '_' . $documentNumber . '_' . $this->context['company'] . '_' . $this->context['whs'];
if (! File::exists(Config::get('arrays.PdfPath') . $key . '.pdf')) {
return '';
}
return route('m3.M3GetPDFFromCache.index', ['MACPKR' => $delivery, 'DOCTYPE' => $type]);
}
private function gs1Required(object $row): bool
{
$record = DB::connection(config('m3.connection', 'pgsql'))->table('icm666m')
->select('A1SPR1A01')->where('A1CO', $this->context['company'])->where('A1WHS', $this->context['whs'])
->where('A1DELNO', trim($row->AYMACPKR))->first();
return (int) ($record->A1SPR1A01 ?? 0) === 1;
}
private function packageArray(array $details, array $labels): array
{
return [
'TRACKINGID' => $details['tracking'], 'BUNDLES' => $details['bundle'],
'WEIGHT' => $details['weight'], 'HEIGHT' => $details['height'], 'WIDTH' => $details['width'],
'LENGTH' => $details['depth'], 'SSCC' => $labels['gs1'],
'SHIPLABELDOC' => $labels['carrier'], 'CONTENTLABELDOC' => $labels['content'],
];
}
private function coCharge(array $details): array
{
if ($details['billable'] !== 'Y' || (float) $details['freight'] <= 0 || $details['rateM3'] !== '') {
return [];
}
$discountKey = $details['shipType'] === 'LTL'
? 'arrays.misc.discountCarrierRateForCustomerPriceLTL'
: 'arrays.misc.discountCarrierRateForCustomerPriceParcel';
$shippingToBill = (float) $details['freight'] * (100.00 - Config::get($discountKey)) / 100;
return ['QUANTITY' => round($shippingToBill, 2), 'ORDERCATEGORY' => $details['refOrder'], 'CONUMBER' => $details['orderNo']];
}
}
@@ -0,0 +1,256 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Config;
use DB;
use File;
use Http;
use Illuminate\Http\Request;
use Log;
use Modules\M3\Helpers\Helper;
use Modules\M3\Http\Controllers\GetContentsController;
use Str;
use Throwable;
final class CloseShipmentProcessor
{
private const ZERO_PAD = '000000';
private const SOURCE = 'CloseShipmentCoChargeAPI';
private array $context;
public function __construct(private readonly Request $request)
{
$this->context = $this->makeContext();
}
public function handle()
{
try {
return $this->process();
} catch (Throwable $throwable) {
return $this->exceptionResponse($throwable);
}
}
private function process()
{
if ($this->context['params'] === []) {
$this->log('M3-Res-out', 'No URL Parameters Sent', 'X');
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => 'No URL Parameters Sent',
]);
}
$this->logInitialRequest();
$tokenResult = $this->receiveToken();
if ($tokenResult['response'] !== null) {
return $tokenResult['response'];
}
$shipments = $this->loadShipments();
$shipments = $this->removeAlreadyClosed($shipments);
$freightTerms = DB::connection(config('m3.connection', 'pgsql'))->table('icm321m')
->selectRaw('TRIM(ARCODE) as FREIGHTTERMS,ARCHARGE as BILLABLE')
->get();
return $this->dispatchShipments($shipments, $freightTerms, $tokenResult['token']);
}
private function makeContext(): array
{
$params = $this->request->all();
$manifest = (string) ($params['MANIFEST'] ?? '');
$transType = (string) ($params['TRANSTYPE'] ?? 'M');
return [
'params' => $params,
'whs' => (string) ($params['WHS'] ?? ''),
'company' => (string) ($params['COMPANY'] ?? ''),
'manifest' => $manifest,
'transType' => $transType !== '' ? $transType : 'M',
'shdt' => (string) ($params['SHDT'] ?? ''),
'shtime' => (string) ($params['SHTIME'] ?? ''),
'shtype' => (string) ($params['SHTYPE'] ?? ''),
'user' => m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser',
'delivery' => $manifest !== '' ? $manifest : self::ZERO_PAD,
'jsonIn' => '',
];
}
private function logInitialRequest(): void
{
$this->context['jsonIn'] = json_encode([
'WHS' => $this->context['whs'],
'COMPANY' => $this->context['company'],
'MANIFEST-MACPKR' => $this->context['manifest'],
'TRANSTYPE' => $this->context['transType'],
]);
$this->log('M3-Req-out', $this->context['jsonIn']);
}
private function receiveToken(): array
{
$response = Helper::checkReceiveTokenV2();
$data = $response->getData();
if ((string) ($data->code ?? '') === '200') {
return ['token' => (string) ($data->token ?? ''), 'response' => null];
}
$this->log('M3-Res-out', (string) ($data->message ?? ''), 'X');
return ['token' => '', 'response' => $response];
}
private function loadShipments()
{
$type = $this->context['transType'];
if (! in_array($type, ['M', 'P', 'Z'], true)) {
return collect();
}
if ($type === 'P') {
$this->acknowledgeRetry();
}
$query = $type === 'Z' ? $this->zeroPickQuery() : $this->standardShipmentQuery($type);
return $query->get();
}
private function standardShipmentQuery(string $type)
{
$query = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m1')
->select('AYCO', 'AYWHS', 'AYMACPKR', 'AYRLNO', 'AYAPRF1551')
->distinct()
->where('AYCO', $this->context['company'])
->where('AYWHS', $this->context['whs'])
->orderBy('AYMACPKR');
$field = $type === 'P' ? 'AYMACPKR' : 'AYAPRF1551';
return $query->where($field, $this->context['manifest']);
}
private function zeroPickQuery()
{
return DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->select()->distinct()
->where('A4CO', $this->context['company'])
->where('A4WHS', $this->context['whs'])
->where('A4MACPKR', $this->context['manifest'])
->orderBy('A4MACPKR');
}
private function acknowledgeRetry(): void
{
if ($this->context['shdt'] !== '' && $this->context['shtime'] !== '' && $this->context['shtype'] !== '') {
DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->where('SHMACPKR', $this->context['manifest'])
->where('SHDT', $this->context['shdt'])->where('SHTIME', $this->context['shtime'])
->where('SHCO', $this->context['company'])->where('SHWHS', $this->context['whs'])
->where('SHSOURCE', self::SOURCE)->where('SHERROR', 'X')
->where('SHTYPE', $this->context['shtype'])
->update($this->acknowledgementValues());
}
$this->context['lineAck'] = $this->acknowledgedLines();
}
private function acknowledgedLines(): array
{
$rows = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->where('SHMACPKR', $this->context['manifest'])
->where('SHCO', $this->context['company'])->where('SHWHS', $this->context['whs'])
->where('SHSOURCE', 'PackSyst4tray')->where('SHERROR', 'Z')->where('SHTYPE', 'M3-QtyChg')
->get();
return $rows->map(fn ($row) => Helper::getSubstringBetween($row->SHDATA, 'PickerLine:', ' -'))->all();
}
private function removeAlreadyClosed($shipments)
{
$field = $this->context['transType'] === 'Z' ? 'A4MACPKR' : 'AYMACPKR';
$deliveryNumbers = $shipments->map(fn ($row) => trim($row->{$field}))->all();
if ($deliveryNumbers === []) {
return $shipments;
}
$closed = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->whereIn('SHMACPKR', $deliveryNumbers)
->where('SHCO', $this->context['company'])->where('SHWHS', $this->context['whs'])
->where('SHSOURCE', self::SOURCE)->where('SHERROR', '')
->where('SHTYPE', 'M3-Res-out')->where('SHDATA', 'like', '%Success%')
->pluck('SHMACPKR')->all();
return $shipments->reject(fn ($row) => in_array($row->{$field}, $closed));
}
private function dispatchShipments($shipments, $freightTerms, string $token)
{
if ($shipments->isEmpty()) {
return $this->noRecordsResponse();
}
if ($this->context['transType'] === 'Z') {
return (new CloseShipmentZeroPickProcessor($this->context, $token))->process($shipments);
}
return (new CloseShipmentStandardProcessor($this->context, $token, $freightTerms))->process($shipments);
}
private function noRecordsResponse()
{
$delivery = $this->context['manifest'] !== '' ? $this->context['manifest'] : self::ZERO_PAD;
$this->context['delivery'] = $delivery;
$message = 'No records found in ICM695M1,or URL parameters incomplete';
$this->log('M3-Req-out', $message, 'X');
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => $message . ' For MacPkr/Manifest:' . $delivery,
]);
}
private function exceptionResponse(Throwable $throwable)
{
$isCurlError = str_contains($throwable->getMessage(), 'cURL error');
$message = $isCurlError
? 'Curl Error: Communication with M3 Timed Out'
: 'An Error Occured - ' . substr((string) $throwable, 0, 300);
$this->log('M3-Res-out', $message . ($isCurlError ? ' M3-JSN-out' . $this->context['jsonIn'] : ''), 'X');
if (! $isCurlError) {
Log::error($throwable);
}
return response()->json([
'code' => '405',
'status' => 'error',
'message' => $isCurlError ? $message . '.' : 'Catched Error: ' . $throwable,
]);
}
private function log(string $type, string $message, string $error = ''): void
{
Helper::logToICM699M([
'SHMACPKR' => $this->context['delivery'],
'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'),
'SHERROR' => $error, 'SHINTBOX' => '', 'SHTYPE' => $type, 'SHDATA' => $message,
'SHUSER' => $this->context['user'], 'SHSOURCE' => self::SOURCE,
'SHCO' => $this->context['company'], 'SHWHS' => $this->context['whs'],
]);
}
private function acknowledgementValues(): array
{
return [
'SHERROR' => 'Z', 'SHACKUSER' => $this->context['user'],
'SHACKDATE' => date('Y-m-d'), 'SHACKTIME' => date('H:i:s'),
];
}
}
@@ -0,0 +1,207 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Config;
use DB;
use Http;
use Log;
use Modules\M3\Helpers\Helper;
final class CloseShipmentStandardProcessor
{
private const SOURCE = 'CloseShipmentCoChargeAPI';
private const JSON_CONTENT_TYPE = 'application/json';
private const NULL_M3_RESPONSE = 'M3 API responded with null value';
public function __construct(
private array $context,
private readonly string $token,
private readonly mixed $freightTerms,
) {
}
public function process($shipments)
{
$allErrors = '';
$lastShipment = null;
foreach ($shipments as $shipment) {
$lastShipment = $this->shipmentContext($shipment);
$allErrors .= $this->processShipment($lastShipment);
}
if ($lastShipment !== null) {
$this->closeFalsePositiveErrors($lastShipment);
}
$this->markManifestProcessed();
return $this->resultResponse($allErrors);
}
private function shipmentContext(object $shipment): array
{
return [
'whs' => trim($shipment->AYWHS),
'company' => trim($shipment->AYCO),
'delivery' => trim($shipment->AYMACPKR),
'release' => trim($shipment->AYRLNO),
'manifest' => trim($shipment->AYAPRF1551),
];
}
private function processShipment(array $shipment): string
{
$endFlag = $this->shortPickEndFlag($shipment);
$trailer = $this->trailerContext($shipment);
$builder = new CloseShipmentPackageBuilder($this->context, $this->freightTerms);
$state = $builder->build($shipment, $trailer);
if ($state['packages'] === []) {
return $state['errors'];
}
$json = $builder->closeShipmentJson($shipment, $trailer, $state, $endFlag);
if ($state['errors'] !== '') {
$this->logValidationErrors($shipment, $state['errors']);
return $state['errors'];
}
return $this->sendCloseRequest($shipment, $json, $state['coCharge']);
}
private function shortPickEndFlag(array $shipment): int
{
$rows = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m2')
->select('A5PART', 'A5PACKED', 'A5PICKED', 'A5PKLN', 'A5RSVQTY')->distinct()
->where('A5CO', $shipment['company'])->where('A5WHS', $shipment['whs'])
->where('A5MACPKR', $shipment['delivery'])->where('A5RLNO', $shipment['release'])
->orderBy('A5PART')->get();
foreach ($rows as $row) {
if ((int) ($row->A5RSVQTY ?? 0) - (int) ($row->A5PICKED ?? 0) > 0) {
return 1;
}
}
return 0;
}
private function trailerContext(array $shipment): array
{
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1')
->select('APTRUCKID', 'APTRACKNO', 'APMANIFEST')
->where('APCO', $this->context['company'])->where('APMANIFEST', $shipment['manifest'])
->where('APWHS', $this->context['whs'])->first();
return [
'trailer' => isset($row->APTRUCKID) ? trim($row->APTRUCKID) : '',
'pro' => isset($row->APTRACKNO) ? trim($row->APTRACKNO) : '',
];
}
private function sendCloseRequest(array $shipment, string $json, array $coCharge): string
{
Log::channel('dailyCron')->info('1 >>> JSON Sent for Manifest: ' . (string) (int) $shipment['manifest'] . ' ,Macpicker: ' . $shipment['delivery']);
$this->log($shipment, 'M3-JSN-out', $json);
$response = Http::withHeaders(['accept' => self::JSON_CONTENT_TYPE, 'Content-Type' => self::JSON_CONTENT_TYPE, 'Connection' => 'close'])
->withToken($this->token)->withBody($json, self::JSON_CONTENT_TYPE)
->timeout(Config::get('arrays.curlTimeOut'))->retry(3, 100)
->post(env('ENDPOINTURL') . 'APIFLOWS/Sentinel/TransferAPI?ionapiRespStyle=sync');
Log::channel('dailyCron')->info('2 >>> Close API M3 Responded...');
$result = $this->parseM3Response($response, $shipment, $coCharge !== []);
if ($result['success']) {
$this->log($shipment, 'M3-Res-out', 'Success...' . json_encode($result['responseResult']) . $result['session']);
return '';
}
$this->markShipmentFailed($shipment);
$this->log($shipment, 'M3-Res-out', 'Important-Error: ' . $result['errors'] . json_encode($result['message']) . $result['session'], 'X');
return $result['errors'] . ' ';
}
private function parseM3Response($response, array $shipment, bool $hasCoCharge): array
{
$status = $response->getStatusCode();
$session = $response->hasHeader('x-transaction-id') ? '-sessionID:' . $response->header('x-transaction-id') : '-sessionID';
$contents = json_decode(Helper::sanityzeString($response->getBody()->getContents()));
if ($contents === null || (string) $status !== '200') {
return ['success' => false, 'message' => self::NULL_M3_RESPONSE, 'responseResult' => '', 'errors' => $status, 'session' => $session];
}
$message = $contents->result->CloseShipment;
$apiResult = $message->PickingListClose->Result;
$responseResult = $contents->result;
$errors = Helper::checkM3Errors($responseResult);
if ($apiResult !== 'Error') {
$this->markShipmentSuccessful($shipment, $hasCoCharge);
}
return ['success' => $apiResult !== 'Error', 'message' => $message, 'responseResult' => $responseResult, 'errors' => $errors, 'session' => $session];
}
private function markShipmentSuccessful(array $shipment, bool $hasCoCharge): void
{
$values = $hasCoCharge ? ['A4RATEM3' => 'X', 'A4PICKSTS' => 'A'] : ['A4PICKSTS' => 'A'];
DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->where('A4MACPKR', $shipment['delivery'])->where('A4CO', $shipment['company'])
->where('A4WHS', $shipment['whs'])->update($values);
}
private function markShipmentFailed(array $shipment): void
{
DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->where('A4MACPKR', $shipment['delivery'])->where('A4CO', $shipment['company'])
->where('A4WHS', $shipment['whs'])->update(['A4PICKSTS' => 'a']);
}
private function logValidationErrors(array $shipment, string $errors): void
{
$this->log($shipment, 'M3-Res-out', Helper::removeDuplicateSubstrings($errors), 'X');
}
private function closeFalsePositiveErrors(array $shipment): void
{
DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->where('SHMACPKR', $shipment['delivery'])->where('SHCO', $this->context['company'])
->where('SHWHS', $this->context['whs'])->where('SHSOURCE', self::SOURCE)->where('SHERROR', 'X')
->where('SHTYPE', 'M3-General')->where('SHDATA', 'like', '%CommTCP_read%')
->update(['SHERROR' => 'Z', 'SHACKUSER' => $this->context['user'], 'SHACKDATE' => date('Y-m-d'),
'SHACKTIME' => date('H:i:s'), 'SHACKNOTE' => 'Sentinel automatically closed the error (false positive CommTCP_read)']);
}
private function markManifestProcessed(): void
{
if ($this->context['transType'] !== 'M') {
return;
}
DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1')
->where('APMANIFEST', $this->context['manifest'])->where('APCO', $this->context['company'])
->where('APWHS', $this->context['whs'])->update(['APSPARE2' => 'X']);
}
private function resultResponse(string $errors)
{
if ($errors === '') {
return response()->json(['code' => '200', 'status' => 'Success', 'message' => 'All Operations Successful']);
}
return response()->json([
'code' => '206', 'status' => 'Success With Warnings',
'message' => Helper::removeDuplicateSubstrings($errors) . '; Check Transaction Logs for Errors or Warnings.',
]);
}
private function log(array $shipment, string $type, string $message, string $error = ''): void
{
Helper::logToICM699M([
'SHMACPKR' => $shipment['delivery'], 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'),
'SHERROR' => $error, 'SHINTBOX' => '', 'SHTYPE' => $type, 'SHDATA' => $message,
'SHUSER' => $this->context['user'], 'SHSOURCE' => self::SOURCE,
'SHCO' => $this->context['company'], 'SHWHS' => $this->context['whs'],
]);
}
}
@@ -0,0 +1,133 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Config;
use DB;
use Http;
use Modules\M3\Helpers\Helper;
final class CloseShipmentZeroPickProcessor
{
private const SOURCE = 'CloseShipmentCoChargeAPI';
private const JSON_CONTENT_TYPE = 'application/json';
private const NULL_M3_RESPONSE = 'M3 API responded with null value';
public function __construct(private readonly array $context, private readonly string $token)
{
}
public function process($shipments)
{
$errors = '';
foreach ($shipments as $row) {
$shipment = $this->shipmentContext($row);
$errors .= $this->closeShipment($shipment);
$this->closeFalsePositiveErrors($shipment);
}
return $this->resultResponse($errors);
}
private function shipmentContext(object $row): array
{
return [
'delivery' => trim($row->A4MACPKR),
'whs' => trim($row->A4WHS),
'company' => trim($row->A4CO),
];
}
private function closeShipment(array $shipment): string
{
$json = json_encode([
'DIVI' => $shipment['company'], 'WHS' => $shipment['whs'],
'DELIVERYNO' => $shipment['delivery'], 'TYPE' => 'CloseShipment',
'PICKLISTSUFFIX' => '1', 'ENDFLAG' => '1',
]);
$this->log($shipment, 'M3-JSN-out', $json);
$response = Http::withHeaders([
'accept' => self::JSON_CONTENT_TYPE, 'Content-Type' => self::JSON_CONTENT_TYPE, 'Connection' => 'close',
])->withToken($this->token)->withBody($json, self::JSON_CONTENT_TYPE)
->timeout(Config::get('arrays.curlTimeOut'))->retry(2, 100)
->post(env('ENDPOINTURL') . 'APIFLOWS/Sentinel/TransferAPI?ionapiRespStyle=sync');
$result = $this->parseResponse($response, $shipment);
if ($result['success']) {
$this->log($shipment, 'M3-Res-out', 'Success' . $result['session']);
return '';
}
$this->markFailed($shipment);
$this->log($shipment, 'M3-Res-out', 'Important-Error: ' . $result['errors'] . json_encode($result['message']) . $result['session'], 'X');
return $result['errors'] . '-';
}
private function parseResponse($response, array $shipment): array
{
$contents = json_decode($response->getBody()->getContents());
$session = $response->hasHeader('x-transaction-id') ? '-sessionID:' . $response->header('x-transaction-id') : '-sessionID';
if ($contents === null) {
return ['success' => false, 'message' => self::NULL_M3_RESPONSE, 'errors' => '', 'session' => $session];
}
$message = $contents->result->CloseShipment;
$apiResult = $message->PickingListClose->Result;
$errors = Helper::checkM3Errors($contents->result);
if ($apiResult !== 'Error') {
$this->markSuccessful($shipment);
}
return ['success' => $apiResult !== 'Error', 'message' => $message, 'errors' => $errors, 'session' => $session];
}
private function markSuccessful(array $shipment): void
{
DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->where('A4MACPKR', $shipment['delivery'])->where('A4CO', $shipment['company'])
->where('A4WHS', $shipment['whs'])->where('A4PICKSTS', '!=', 'X')->update(['A4PICKSTS' => 'A']);
}
private function markFailed(array $shipment): void
{
DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->where('A4MACPKR', $shipment['delivery'])->where('A4CO', $shipment['company'])
->where('A4WHS', $shipment['whs'])->where('A4PICKSTS', '!=', 'X')->update(['A4PICKSTS' => 'a']);
}
private function closeFalsePositiveErrors(array $shipment): void
{
DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->where('SHMACPKR', $shipment['delivery'])->where('SHCO', $shipment['company'])
->where('SHWHS', $shipment['whs'])->where('SHSOURCE', self::SOURCE)->where('SHERROR', 'X')
->where('SHTYPE', 'M3-General')->where('SHDATA', 'like', '%CommTCP_read%')
->update([
'SHERROR' => 'Z', 'SHACKUSER' => $this->context['user'], 'SHACKDATE' => date('Y-m-d'),
'SHACKTIME' => date('H:i:s'), 'SHACKNOTE' => 'Sentinel automatically closed the error (false positive CommTCP_read)',
]);
}
private function resultResponse(string $errors)
{
if ($errors === '') {
return response()->json(['code' => '200', 'status' => 'Success', 'message' => 'All Operations Successful']);
}
return response()->json([
'code' => '206', 'status' => 'Success With Warnings',
'message' => $errors . '; Check The Logs for Errors or Warnings.',
]);
}
private function log(array $shipment, string $type, string $message, string $error = ''): void
{
Helper::logToICM699M([
'SHMACPKR' => $shipment['delivery'], 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'),
'SHERROR' => $error, 'SHINTBOX' => '', 'SHTYPE' => $type, 'SHDATA' => $message,
'SHUSER' => $this->context['user'], 'SHSOURCE' => self::SOURCE,
'SHCO' => $shipment['company'], 'SHWHS' => $shipment['whs'],
]);
}
}
+207 -426
View File
@@ -13,30 +13,16 @@ use Str;
class M3Controller extends Controller class M3Controller extends Controller
{ {
private const FIELD_PREFIX = 'Field: '; private const FIELD_PREFIX = 'Field: ';
private const M3_LIST_SUFFIX = ' M3-Lis-JSN'; private const M3_LIST_SUFFIX = ' M3-Lis-JSN';
// Split Data and Write to ICM666M2
public function insertToICM666M2($dataSplit, $commentData) public function insertToICM666M2($dataSplit, $commentData)
{ {
$line = 1;
$commentDataSS = explode("\n", $dataSplit); foreach (explode("\n", $dataSplit) as $substring) {
$aXLINENO = 1; $data = $commentData;
foreach ($commentDataSS as $substring) { $data['AXCOMMENT'] = $substring;
// Process each substring $data['AXLINENO'] = $line++;
$fullData = $commentData; DB::connection(config('m3.connection', 'pgsql'))->table('icm666m2')->insert($data);
$fullData['AXCOMMENT'] = $substring;
$fullData['AXLINENO'] = $aXLINENO;
DB::connection(config('m3.connection', 'pgsql'))->table('icm666m2')->insert($fullData);
$aXLINENO++;
// AXCO ICM666M2 CHARACTER 3
// AXWHS ICM666M2 CHARACTER 3
// AXPLANT ICM666M2 CHARACTER 3
// AXDOCUMENT ICM666M2 CHARACTER 15
// AXCOMMENT ICM666M2 CHARACTER 60
// AXLINENO DECIMAL(3,0)
} }
} }
@@ -45,21 +31,216 @@ class M3Controller extends Controller
dd('Index'); dd('Index');
} }
public function create() public function store(Request $request)
{ {
// This API controller does not expose a create form. $this->configureTimeouts();
$user = m3User() !== '' ? Str::upper(m3User()) : 'WEBUSER';
$json = $request->json()->all();
$context = $this->rootContext($json, $user);
try {
$response = $json === [] ? $this->emptyPayloadResponse($context) : $this->processPayload($json, $context);
} catch (\Throwable $throwable) {
$response = $this->exceptionResponse($context, $json, $throwable);
}
return $response;
} }
public function store(Request $request) private function configureTimeouts(): void
{ {
ini_set('max_execution_time', Config::get('arrays.PHPTimeOut')); ini_set('max_execution_time', Config::get('arrays.PHPTimeOut'));
ini_set('max_input_time', Config::get('arrays.PHPTimeOut')); ini_set('max_input_time', Config::get('arrays.PHPTimeOut'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut')); ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut'));
}
$rEQKPKUS = m3_user() !== '' ? Str::upper(m3_user()) : 'WEBUSER'; private function rootContext(array $json, string $user): array
$sHSOURCE = 'M3ReceiverAPI'; {
$carrier = $json['CarrierCode'] ?? '';
if (($carrier === '' || $carrier === null) && ($json['Warehouse'] ?? '') === '111' && ($json['Company'] ?? '') === '410' && ($json['Plant'] ?? '') === '410') {
$carrier = 'BWAY';
}
return [
'user' => $user, 'source' => 'M3ReceiverAPI', 'company' => $json['Company'] ?? '', 'plant' => $json['Plant'] ?? '',
'warehouse' => $json['Warehouse'] ?? '', 'order' => $json['OrderNumber'] ?? '', 'picker' => $json['PickerNumber'] ?? '',
'prom_date' => $json['PromDate'] ?? '', 'route' => $json['RouteCode'] ?? '', 'category' => $json['OrderCategory'] ?? '',
'release' => $json['ReleaseNumber'] ?? '', 'shipment' => $json['ShipmentNumber'] ?? '', 'currency' => $json['Currency'] ?? '',
'carrier' => $carrier, 'po' => $json['ShipmentPONumber'] ?? '', 'terms' => $json['ShipTerms'] ?? '',
'order_class' => $json['OrderClass'] ?? '', 'gs1' => $json['GS1-128Label'] ?? '0', 'exchange' => $json['CurrencyExchangeRate'] ?? '',
'macpkr' => ($json['PickerNumber'] ?? '') !== '' ? $json['PickerNumber'] : '000000',
];
}
$iCM666MFields = [ private function processPayload(array $json, array $context)
{
$recipient = $this->recipient($json['Recipient'] ?? []);
$this->storeComments($json['Comments'] ?? [], $context);
$validation = $this->storeProducts($json['Contents']['Commodities'] ?? [], $context, $recipient, $json);
if ($validation !== null) {
return $validation;
}
$this->callUpdateProgram($context, $json);
$this->log($context, json_encode($json), '', 'M3-Lis-in');
return response()->json(['code' => '200', 'status' => 'success', 'message' => 'File Received, DB Updated']);
}
private function recipient(array $recipient): array
{
return [
'CompanyName' => $recipient['CompanyName'] ?? '', 'Address1' => $recipient['Address1'] ?? '', 'Address2' => $recipient['Address2'] ?? '',
'Address3' => $recipient['Address3'] ?? '', 'Address4' => $recipient['Address4'] ?? '', 'City' => $recipient['City'] ?? '',
'State' => $recipient['State'] ?? '', 'PostalCode' => $recipient['PostalCode'] ?? '', 'Country' => $recipient['Country'] ?? '',
'CountryName' => $recipient['CountryName'] ?? '', 'BillToId' => $recipient['BillToId'] ?? '', 'ShipToId' => $recipient['ShipToId'] ?? '',
'PersonName' => $recipient['PersonName'] ?? '', 'AccountNo' => $recipient['AccountNo'] ?? '', 'Email' => $recipient['Email'] ?? '',
];
}
private function storeComments(array $comments, array $c): void
{
foreach ($comments as $document => $comment) {
$document = Helper::sanityzeString($document);
if (! in_array($document, ['Picker', 'Packer', 'Carrier', 'Other'], true)) {
continue;
}
$text = Helper::sanityzeString($comment ?? 'null');
if ($text === 'null') {
continue;
}
$this->insertToICM666M2($text, [
'AXCO' => $c['company'], 'AXPLANT' => $c['plant'], 'AXWHS' => $c['warehouse'], 'AXDELNO' => $c['picker'],
'AXONO' => $c['order'], 'AXRLNO' => $c['release'], 'AXSHNO' => $c['shipment'], 'AXDOCUMENT' => $document,
]);
}
}
private function storeProducts(array $products, array $c, array $recipient, array $json)
{
foreach ($products as $product) {
$values = $this->productValues($product, $c, $recipient, $json);
[$insert, $messages] = $this->validatedInsert($values);
if ($messages !== []) {
$this->log($c, json_encode($messages) . self::M3_LIST_SUFFIX . json_encode($json), 'X', 'M3-Lis-spl');
return response()->json($messages);
}
$this->enrichProduct($insert, $c, (string) ($product['PartNumber'] ?? ''));
DB::connection(config('m3.connection', 'pgsql'))->table('icm666m')->insert($insert);
}
return null;
}
private function productValues(array $p, array $c, array $r, array $json): array
{
return array_merge($r, [
'Company' => $c['company'], 'Warehouse' => $c['warehouse'], 'Plant' => $c['plant'], 'PickerNumber' => $c['picker'],
'OrderNumber' => $c['order'], 'ReleaseNumber' => $c['release'], 'ShipmentNumber' => $c['shipment'],
'OrderLine' => $p['OrderLine'] ?? '', 'PartNumber' => $p['PartNumber'] ?? '',
'Description' => isset($p['Description']) ? Str::limit($p['Description'], 25, '') : '', 'Quantity' => $p['Quantity'] ?? '',
'BaseUnitPrice' => $p['BaseUnitPrice'] ?? '', 'Currency' => $c['currency'], 'Zone' => $p['Zone'] ?? '', 'PromDate' => $c['prom_date'],
'RouteCode' => $c['route'], 'PartWeight' => $p['PartWeight'] ?? '', 'SellUOM' => $p['SellUOM'] ?? '', 'PartConv' => $p['PartConv'] ?? '',
'CarrierCode' => $c['carrier'], 'ShipVia' => $json['ShipVia'] ?? '', 'PrimaryLocation' => $p['PrimaryLocation'] ?? '',
'Allocation' => $p['Allocation'] ?? '', 'AllocatedTo' => $p['AllocatedTo'] ?? '', 'LocationSeq' => $p['LocationSeq'] ?? '',
'UOM' => $p['UOM'] ?? '', 'HazMat' => $p['HazMat'] ?? '', 'PartUPC' => $p['PartUPC'] ?? '', 'PartEAN' => $p['PartEAN'] ?? '',
'LotFlag' => $p['LotFlag'] ?? '', 'ManOrd' => $p['ManOrd'] ?? '', 'LotNumber' => $p['LotNumber'] ?? '', 'ProductArea' => $p['ProductArea'] ?? '',
'FreightClass' => $p['FreightClass'] ?? '', 'ShipmentPONumber' => $c['po'], 'ShipmentPOLine' => $p['ShipmentPOLine'] ?? '',
'ShipTerms' => $c['terms'], 'OrderClass' => $c['order_class'], 'LocationType' => $p['LocationType'] ?? '', 'PallSize' => $p['PallSize'] ?? '',
'MasterCarton' => $p['MasterCarton'] ?? '', 'MasterQty' => $p['MasterQty'] ?? '', 'InnerQty' => $p['InnerQty'] ?? '',
'PalletQty' => $p['PalletQty'] ?? '', 'CartonQty' => $p['CartonQty'] ?? '', 'OrderCategory' => $c['category'],
'A1DATEADD' => date('Ymd'), 'A1TIMEADD' => date('His'), 'KitPartNumber' => $p['KitPartNumber'] ?? '',
'OrderLineSuffix' => $p['OrderLineSuffix'] ?? '', 'CustomerPartNumber' => isset($p['CustomerPartNumber']) ? substr($p['CustomerPartNumber'], 0, 20) : '',
'GS1Label' => $c['gs1'], 'CurrencyExchangeRate' => $c['exchange'],
]);
}
private function validatedInsert(array $values): array
{
$insert = []; $messages = [];
foreach ($this->fieldDefinitions() as $key => $definition) {
$value = Helper::sanityzeString($values[$key] ?? '');
$test = Helper::checkLength($value, $definition[1], $definition[2]);
$this->appendValidationMessages($messages, $key, $definition[2], $test);
if ($value !== '') {
$insert[$definition[0]] = $value;
}
}
return [$insert, $messages];
}
private function appendValidationMessages(array &$messages, string $key, string $type, array $test): void
{
if (! ($test[413] ?? false)) {
$messages[] = ['code' => '413', 'status' => 'error', 'message' => self::FIELD_PREFIX . $key . ' is larger then expected'];
}
if (! ($test[411] ?? false)) {
$messages[] = ['code' => '411', 'status' => 'error', 'message' => self::FIELD_PREFIX . $key . ' is empty'];
}
if (! ($test[417] ?? false)) {
$messages[] = ['code' => '417', 'status' => 'error', 'message' => self::FIELD_PREFIX . $key . 'type is different then Expected type: ' . $type];
}
}
private function enrichProduct(array &$insert, array $c, string $part): void
{
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm709m')->select()
->where('AT9CO', $c['company'])->where('AT9PLT', $c['plant'])->where('AT9WHS', $c['warehouse'])->where('AT9PART', $part)->first();
$insert['A1USRN01'] = $row->AT9QTY ?? 0;
$insert['A1MATWRN'] = $row->AT9WRN ?? '';
}
private function callUpdateProgram(array $c, array $json): void
{
if ($c['warehouse'] === '' || $c['picker'] === '' || $c['company'] === '') {
return;
}
$error = '';
try {
$conn = app(PostgresProgramBridge::class);
$conn->setOptions(['persistent' => false, 'stateless' => true, 'debug' => true, 'debugLogFile' => env('TOOLKIT_DEBUG_LOG')]);
$params = [$conn->addParameterChar('in', 3, 'Company', '', $c['company']), $conn->addParameterChar('in', 3, 'Warehouse', '', $c['warehouse'])];
$params[] = $conn->addParameterChar('in', 11, 'PickerNumber', '', ltrim(strval($c['picker']), '0'));
if (! $conn->pgmCall('ICM700CSBM', env('OBJLIB'), $params, [], [])) {
$error = 'Error calling program. Code: ' . $conn->getErrorCode() . ' - Msg: ' . $conn->getErrorMsg();
}
} catch (\Throwable $e) {
$error = $this->programConnectionError($e);
}
if ($error !== '') {
$this->log($c, $error . self::M3_LIST_SUFFIX . json_encode($json), 'X', 'M3-Lis-spl');
}
}
private function programConnectionError(\Throwable $e): string
{
return match ((int) $e->getCode()) {
8001 => 'Could not connect due to wrong user or password.',
42705 => 'Database not found. Try WRKRDBDIRE to check.',
default => 'Could not connect. Error: ' . $e->getCode() . ' ' . $e->getMessage(),
};
}
private function emptyPayloadResponse(array $c)
{
$data = 'No Json File Found...';
Helper::logToICM699M(['SHMACPKR' => $c['macpkr'], 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'Z', 'SHINTBOX' => '',
'SHTYPE' => 'M3-Lis-in', 'SHDATA' => $data, 'SHUSER' => $c['user'], 'SHSOURCE' => $c['source'], 'SHCO' => $c['company'], 'SHWHS' => $c['warehouse'],
'SHACKNOTE' => 'Sentinel automatically closed the error related to this empty data set sent by M3']);
return response()->json(['code' => '415', 'status' => 'error', 'message' => 'No Json File Found']);
}
private function exceptionResponse(array $c, array $json, \Throwable $th)
{
$this->log($c, 'General Catchable Error - ' . substr((string) $th, 0, 300) . self::M3_LIST_SUFFIX . json_encode($json), 'X', 'M3-Lis-in');
return response()->json(['code' => '405', 'status' => 'error', 'message' => 'An Error Occured...']);
}
private function log(array $c, string $message, string $error, string $type): void
{
Helper::logToICM699M(['SHMACPKR' => $c['macpkr'], 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => $error, 'SHINTBOX' => '',
'SHTYPE' => $type, 'SHDATA' => $message, 'SHUSER' => $c['user'], 'SHSOURCE' => $c['source'], 'SHCO' => $c['company'], 'SHWHS' => $c['warehouse']]);
}
private function fieldDefinitions(): array
{
return [
'Company' => ['A1CO', 3, 'string'], 'Company' => ['A1CO', 3, 'string'],
'Warehouse' => ['A1WHS', 3, 'string'], 'Warehouse' => ['A1WHS', 3, 'string'],
'Plant' => ['A1PLANT', 3, 'string'], 'Plant' => ['A1PLANT', 3, 'string'],
@@ -131,405 +312,5 @@ class M3Controller extends Controller
'CurrencyExchangeRate' => ['A1EXGRATE', 18, 'DEC'], 'CurrencyExchangeRate' => ['A1EXGRATE', 18, 'DEC'],
]; ];
try {
$jsonFile = count($request->json()->all()) > 0 ? $request->json()->all() : '';
// Root
$company = isset($jsonFile['Company']) ? $jsonFile['Company'] : '';
$plant = isset($jsonFile['Plant']) ? $jsonFile['Plant'] : '';
$warehouse = isset($jsonFile['Warehouse']) ? $jsonFile['Warehouse'] : '';
$orderNumber = isset($jsonFile['OrderNumber']) ? $jsonFile['OrderNumber'] : '';
$pickerNumber = isset($jsonFile['PickerNumber']) ? $jsonFile['PickerNumber'] : '';
$promDate = isset($jsonFile['PromDate']) ? $jsonFile['PromDate'] : '';
$routeCode = isset($jsonFile['RouteCode']) ? $jsonFile['RouteCode'] : '';
$orderCategory = isset($jsonFile['OrderCategory']) ? $jsonFile['OrderCategory'] : '';
$releaseNumber = isset($jsonFile['ReleaseNumber']) ? $jsonFile['ReleaseNumber'] : '';
$shipmentNumber = isset($jsonFile['ShipmentNumber']) ? $jsonFile['ShipmentNumber'] : '';
$currency = isset($jsonFile['Currency']) ? $jsonFile['Currency'] : '';
$carrierCode = isset($jsonFile['CarrierCode']) ? $jsonFile['CarrierCode'] : '';
$shipmentPONumber = isset($jsonFile['ShipmentPONumber']) ? $jsonFile['ShipmentPONumber'] : ''; // CustPo
$shipTerms = isset($jsonFile['ShipTerms']) ? $jsonFile['ShipTerms'] : '';
$orderClass = isset($jsonFile['OrderClass']) ? $jsonFile['OrderClass'] : '';
$gS1Label = isset($jsonFile['GS1-128Label']) ? $jsonFile['GS1-128Label'] : '0';
$currencyExchangeRate = isset($jsonFile['CurrencyExchangeRate']) ? $jsonFile['CurrencyExchangeRate'] : '';
// Set Carrier BWAY if it's empty for Tilburg only
if (($carrierCode == '' || $carrierCode == null) && $warehouse == '111' && $company == '410' && $plant == '410') {
$carrierCode = 'BWAY';
}
$sHMACPKR = $pickerNumber != '' ? $pickerNumber : '000000';
if ($jsonFile != '') {
// Order Category
/*
1 = Material to manufacturing order
3 = Customer Order
4 = Requisition Order
5 = Distribution Order
9 = Move order / Replenishment
*/
// Recipient
$recipient = $jsonFile['Recipient'];
$companyName = isset($recipient['CompanyName']) ? $recipient['CompanyName'] : '';
$address1 = isset($recipient['Address1']) ? $recipient['Address1'] : '';
$address2 = isset($recipient['Address2']) ? $recipient['Address2'] : '';
$city = isset($recipient['City']) ? $recipient['City'] : '';
$state = isset($recipient['State']) ? $recipient['State'] : '';
$postalCode = isset($recipient['PostalCode']) ? $recipient['PostalCode'] : '';
$country = isset($recipient['Country']) ? $recipient['Country'] : '';
$postalCode = isset($recipient['PostalCode']) ? $recipient['PostalCode'] : '';
$billToId = isset($recipient['BillToId']) ? $recipient['BillToId'] : '';
$shipToId = isset($recipient['ShipToId']) ? $recipient['ShipToId'] : '';
$address3 = isset($recipient['Address3']) ? $recipient['Address3'] : '';
$address4 = isset($recipient['Address4']) ? $recipient['Address4'] : '';
$countryName = isset($recipient['CountryName']) ? $recipient['CountryName'] : '';
$personName = isset($recipient['PersonName']) ? $recipient['PersonName'] : '';
$accountNo = isset($recipient['AccountNo']) ? $recipient['AccountNo'] : '';
$email = isset($recipient['Email']) ? $recipient['Email'] : '';
$a1DATEADD = date('Ymd');
$a1TIMEADD = date('His');
// MycarrierDetails
$shipVia = isset($jsonFile['ShipVia']) ? $jsonFile['ShipVia'] : '';
// Comments
$picker = $packer = $carrier = $other = '';
$aXCO = $company;
$aXPLANT = $plant;
$aXWHS = $warehouse;
$aXDELNO = $pickerNumber;
$aXONO = $orderNumber;
$aXRLNO = $releaseNumber;
$aXSHNO = $shipmentNumber;
$comments = isset($jsonFile['Comments']) ? $jsonFile['Comments'] : '';
if ($comments != '') {
foreach ($comments as $key => $comment) {
$commentData = [];
$aXDOCUMENT = Helper::sanityzeString($key);
$commentData = ['AXCO' => $aXCO, 'AXPLANT' => $aXPLANT, 'AXWHS' => $aXWHS, 'AXDELNO' => $aXDELNO, 'AXONO' => $aXONO,
'AXRLNO' => $aXRLNO, 'AXSHNO' => $aXSHNO, 'AXDOCUMENT' => $aXDOCUMENT];
switch ($aXDOCUMENT) {
case 'Picker':
$picker = isset($comment) ? Helper::sanityzeString($comment) : 'null';
if ($picker != 'null') {
$this->insertToICM666M2($picker, $commentData);
}
break;
case 'Packer':
$packer = isset($comment) ? Helper::sanityzeString($comment) : 'null';
if ($packer != 'null') {
$this->insertToICM666M2($packer, $commentData);
}
break;
case 'Carrier':
$carrier = isset($comment) ? Helper::sanityzeString($comment) : 'null';
if ($carrier != 'null') {
$this->insertToICM666M2($carrier, $commentData);
}
break;
case 'Other':
$other = isset($comment) ? Helper::sanityzeString($comment) : 'null';
if ($other != 'null') {
$this->insertToICM666M2($other, $commentData);
}
break;
default:
break;
}
}
}
// Get Products : commodities And Write ICM666M
$products = $jsonFile['Contents']['Commodities'];
foreach ($products as $key => $product) {
$quantity = isset($product['Quantity']) ? $product['Quantity'] : '';
$description = isset($product['Description']) ? Str::limit($product['Description'], 25, '') : '';
$partNumber = isset($product['PartNumber']) ? $product['PartNumber'] : '';
$orderLine = isset($product['OrderLine']) ? $product['OrderLine'] : '';
$partWeight = isset($product['PartWeight']) ? $product['PartWeight'] : '';
$primaryLocation = isset($product['PrimaryLocation']) ? $product['PrimaryLocation'] : '';
$allocation = isset($product['Allocation']) ? $product['Allocation'] : '';
$allocatedTo = isset($product['AllocatedTo']) ? $product['AllocatedTo'] : '';
$locationSeq = isset($product['LocationSeq']) ? $product['LocationSeq'] : '';
$zone = isset($product['Zone']) ? $product['Zone'] : '';
$baseUnitPrice = isset($product['BaseUnitPrice']) ? $product['BaseUnitPrice'] : '';
$sellUOM = isset($product['SellUOM']) ? $product['SellUOM'] : '';
$partConv = isset($product['PartConv']) ? $product['PartConv'] : '';
$uOM = isset($product['UOM']) ? $product['UOM'] : '';
$hazMat = isset($product['HazMat']) ? $product['HazMat'] : '';
$partUPC = isset($product['PartUPC']) ? $product['PartUPC'] : '';
$partEAN = isset($product['PartEAN']) ? $product['PartEAN'] : '';
$lotFlag = isset($product['LotFlag']) ? $product['LotFlag'] : '';
$manOrd = isset($product['ManOrd']) ? $product['ManOrd'] : '';
$lotNumber = isset($product['LotNumber']) ? $product['LotNumber'] : '';
$productArea = isset($product['ProductArea']) ? $product['ProductArea'] : '';
$freightClass = isset($product['FreightClass']) ? $product['FreightClass'] : '';
$shipmentPOLine = isset($product['ShipmentPOLine']) ? $product['ShipmentPOLine'] : '';
$locationType = isset($product['LocationType']) ? $product['LocationType'] : '';
$pallSize = isset($product['PallSize']) ? $product['PallSize'] : '';
$masterCarton = isset($product['MasterCarton']) ? $product['MasterCarton'] : '';
$masterQty = isset($product['MasterQty']) ? $product['MasterQty'] : '';
$innerQty = isset($product['InnerQty']) ? $product['InnerQty'] : '';
$sonar_pallet_qty = isset($product['PalletQty']) ? $product['PalletQty'] : '';
$cartonQty = isset($product['CartonQty']) ? $product['CartonQty'] : '';
$kitPartNumber = isset($product['KitPartNumber']) ? $product['KitPartNumber'] : '';
$orderLineSuffix = isset($product['OrderLineSuffix']) ? $product['OrderLineSuffix'] : '';
$customerPartNumber = isset($product['CustomerPartNumber']) ? substr($product['CustomerPartNumber'], 0, 20) : ''; // Limit to 20 Chars
// Initialise Table Fields to be filled
$insertArray = [];
$fieldValues = [
'Company' => $company,
'Warehouse' => $warehouse,
'Plant' => $plant,
'PickerNumber' => $pickerNumber,
'OrderNumber' => $orderNumber,
'ReleaseNumber' => $releaseNumber,
'ShipmentNumber' => $shipmentNumber,
'OrderLine' => $orderLine,
'PartNumber' => $partNumber,
'Description' => $description,
'Quantity' => $quantity,
'BaseUnitPrice' => $baseUnitPrice,
'Currency' => $currency,
'Zone' => $zone,
'PromDate' => $promDate,
'RouteCode' => $routeCode,
'BillToId' => $billToId,
'ShipToId' => $shipToId,
'CompanyName' => $companyName,
'Address1' => $address1,
'Address2' => $address2,
'Address3' => $address3,
'Address4' => $address4,
'City' => $city,
'State' => $state,
'PostalCode' => $postalCode,
'Country' => $country,
'CountryName' => $countryName,
'PartWeight' => $partWeight,
'SellUOM' => $sellUOM,
'PartConv' => $partConv,
'CarrierCode' => $carrierCode,
'ShipVia' => $shipVia,
'PrimaryLocation' => $primaryLocation,
'Allocation' => $allocation,
'AllocatedTo' => $allocatedTo,
'LocationSeq' => $locationSeq,
'UOM' => $uOM,
'HazMat' => $hazMat,
'PartUPC' => $partUPC,
'PartEAN' => $partEAN,
'LotFlag' => $lotFlag,
'ManOrd' => $manOrd,
'LotNumber' => $lotNumber,
'ProductArea' => $productArea,
'FreightClass' => $freightClass,
'ShipmentPONumber' => $shipmentPONumber,
'ShipmentPOLine' => $shipmentPOLine,
'ShipTerms' => $shipTerms,
'OrderClass' => $orderClass,
'LocationType' => $locationType,
'PallSize' => $pallSize,
'MasterCarton' => $masterCarton,
'MasterQty' => $masterQty,
'InnerQty' => $innerQty,
'PalletQty' => $sonar_pallet_qty,
'CartonQty' => $cartonQty,
'PersonName' => $personName,
'AccountNo' => $accountNo,
'Email' => $email,
'OrderCategory' => $orderCategory,
'A1DATEADD' => $a1DATEADD,
'A1TIMEADD' => $a1TIMEADD,
'KitPartNumber' => $kitPartNumber,
'OrderLineSuffix' => $orderLineSuffix,
'CustomerPartNumber' => $customerPartNumber,
'GS1Label' => $gS1Label,
'CurrencyExchangeRate' => $currencyExchangeRate,
];
// Check JSON fields if empty or > DB Fields
foreach ($iCM666MFields as $key => $value) {
// Sanitize String
$fieldValue = Helper::sanityzeString($fieldValues[$key] ?? '');
// Check Var Types & Length
$lengthTest = Helper::checkLength($fieldValue, $value[1], $value[2]);
if (! $lengthTest[413] || ! $lengthTest[411] || ! $lengthTest[417]) {
if (! $lengthTest[413]) {
$message[] = ['code' => '413', 'status' => 'error', 'message' => self::FIELD_PREFIX . $key . ' is larger then expected'];
}
if (! $lengthTest[411]) {
$message[] = ['code' => '411', 'status' => 'error', 'message' => self::FIELD_PREFIX . $key . ' is empty'];
}
if (! $lengthTest[417]) {
$message[] = ['code' => '417', 'status' => 'error', 'message' => self::FIELD_PREFIX . $key . 'type is different then Expected type: ' . $value[2]];
}
// Write to Logs Out : Error & JSON
$data = ['SHMACPKR' => $sHMACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Lis-spl',
'SHDATA' => json_encode($message) . self::M3_LIST_SUFFIX . json_encode($jsonFile), 'SHUSER' => $rEQKPKUS, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $company, 'SHWHS' => $warehouse];
Helper::logToICM699M($data);
return response()->json($message);
} elseif ($fieldValue != '') {
$insertArray[$value[0]] = $fieldValue;
}
}
// Check ICM709M
$iCM709MSEL = DB::connection(config('m3.connection', 'pgsql'))->table('icm709m')
->select()
->where('AT9CO', $aXCO)
->where('AT9PLT', $aXPLANT)
->where('AT9WHS', $aXWHS)
->where('AT9PART', $partNumber)
->first();
$palletQty = isset($iCM709MSEL->AT9QTY) ? $iCM709MSEL->AT9QTY : 0;
// dd($palletQty);
// Get Pallet Qty
$insertArray['A1USRN01'] = $palletQty;
// Hazard Material
$hazMat = isset($iCM709MSEL->AT9WRN) ? $iCM709MSEL->AT9WRN : '';
$insertArray['A1MATWRN'] = $hazMat;
// }
// SQL Insert to ICM666M
DB::connection(config('m3.connection', 'pgsql'))->table('icm666m')->insert($insertArray);
}
// Call PGM ICM700CSBM
if ($warehouse != '' && $pickerNumber != '' && $company != '') {
$pGMERRORS = '';
// connect to toolkit using DB2 credentials (can also leave blank for default authority)
try {
$conn = app(PostgresProgramBridge::class);
} catch (\Throwable $e) {
// Determine reason for failure.
// Probably database authentication error or invalid or unreachable database.
$code = $e->getCode();
$msg = $e->getMessage();
switch ($code) {
case 8001:
// "Authorization failure on distributed database connection attempt"
// Usually means a wrong DB2 user or password
$pGMERRORS = 'Could not connect due to wrong user or password.';
break;
case 42705:
$pGMERRORS = 'Database not found. Try WRKRDBDIRE to check.';
break;
default:
$pGMERRORS = 'Could not connect. Error: ' . $code . ' ' . $msg;
break;
}
}
$conn->setOptions(['persistent' => false, 'stateless' => true, 'debug' => true, 'debugLogFile' => env('TOOLKIT_DEBUG_LOG')]);
// Add LIB
// $result = $conn->CLCommand('ADDLIBLE LIB(MPDAT10)');
// define several input/output params
$params = [];
// AddParameterChar($io, $size, $comment, $varName, $value, $varying, $dimension)
// Add Warehouse Param
$params[] = $conn->AddParameterChar('in', 3, 'Company', '', $company);
$params[] = $conn->AddParameterChar('in', 3, 'Warehouse', '', $warehouse);
// AddParameterPackDec($io, $length, $scale, $comment, $varName, $value,$dimension)
// Add Delivery Note Param
$pickerNumberNoZero = ltrim(strval($pickerNumber), '0');
$params[] = $conn->AddParameterChar('in', 11, 'PickerNumber', '', $pickerNumberNoZero);
// Start Time Tracking
// ICM700C EWOBJTST ICM700C ICM700CSBM
$result = $conn->PgmCall('ICM700CSBM', env('OBJLIB'), $params, [], []);
if (! $result) {
$pGMERRORS = 'Error calling program. Code: ' . $conn->getErrorCode() . ' - Msg: ' . $conn->getErrorMsg();
}
if ($pGMERRORS != '') {
// Write to Logs Out : Error & JSON
$data = ['SHMACPKR' => $sHMACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Lis-spl',
'SHDATA' => $pGMERRORS . self::M3_LIST_SUFFIX . json_encode($jsonFile), 'SHUSER' => $rEQKPKUS, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $company, 'SHWHS' => $warehouse];
Helper::logToICM699M($data);
}
}
// Write to Logs Out : Success
$data = ['SHMACPKR' => $sHMACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => '', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Lis-in',
'SHDATA' => json_encode($jsonFile), 'SHUSER' => $rEQKPKUS, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $company, 'SHWHS' => $warehouse];
Helper::logToICM699M($data);
return response()->json([
'code' => '200',
'status' => 'success',
'message' => 'File Received, DB Updated',
]);
} else {
// Write to Logs Out : Error & JSON
$sHACKNOTE = 'Sentinel automatically closed the error related to this empty data set sent by M3';
$data = ['SHMACPKR' => $sHMACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'Z', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Lis-in',
'SHDATA' => 'No Json File Found...', 'SHUSER' => $rEQKPKUS, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $company, 'SHWHS' => $warehouse, 'SHACKNOTE' => $sHACKNOTE];
Helper::logToICM699M($data);
return response()->json([
'code' => '415',
'status' => 'error',
'message' => 'No Json File Found',
]);
}
} catch (\Throwable $th) {
// Write to Logs Out : Error & JSON
$data = ['SHMACPKR' => $sHMACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Lis-in',
'SHDATA' => 'General Catchable Error - ' . substr($th, 0, 300) . self::M3_LIST_SUFFIX . json_encode($jsonFile), 'SHUSER' => $rEQKPKUS, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $company, 'SHWHS' => $warehouse];
Helper::logToICM699M($data);
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'An Error Occured...',
]);
}
}
public function show($id)
{
// This resource endpoint is intentionally unsupported.
}
public function edit($id)
{
// This resource endpoint is intentionally unsupported.
}
public function update(Request $request, $id)
{
// This resource endpoint is intentionally unsupported.
}
public function destroy($id)
{
// This resource endpoint is intentionally unsupported.
} }
} }
+28 -29
View File
@@ -18,37 +18,36 @@ class PdfStorage extends Controller
public function PdfStorageAPI(Request $request): JsonResponse public function PdfStorageAPI(Request $request): JsonResponse
{ {
$this->configureTimeouts(); $this->configureTimeouts();
$context = $this->requestContext($request); $context = $this->requestContext($request);
if ($context === null) { if ($context === null) {
return $this->errorResponse('No URL Parameters Sent', self::ZERO_PAD, '', '', ''); $result = $this->errorResponse('No URL Parameters Sent', self::ZERO_PAD, '', '', '');
} } else {
try {
$this->logRequest($context);
$cacheKey = $this->buildCacheKey($context);
$pdfFileName = (string) Config::get('arrays.PdfPath') . $context['FILENAME'] . '.pdf';
$error = $this->validateStorageRequest($pdfFileName, $cacheKey);
try { if ($error !== null) {
$this->logRequest($context); $result = $this->errorResponse($error, $context['DELIVERYNO'], $context['DOCNUM'], $context['COMPANY'], $context['WHS']);
} else {
$cacheKey = $this->buildCacheKey($context); Cache::forever($cacheKey, base64_encode(File::get($pdfFileName)));
$pdfFileName = (string) Config::get('arrays.PdfPath') . $context['FILENAME'] . '.pdf'; $this->logResult($context, 'PDF File converted to Base64 and Saved to Cache.Key: ' . $cacheKey);
$error = $this->validateStorageRequest($pdfFileName, $cacheKey); $result = response()->json(['code' => '200', 'status' => 'Success', 'cacheKey' => $cacheKey]);
}
if ($error !== null) { } catch (\Throwable $exception) {
return $this->errorResponse($error, $context['DELIVERYNO'], $context['DOCNUM'], $context['COMPANY'], $context['WHS']); $message = 'An Error Occured - ' . substr($exception->getMessage(), 0, 300);
$this->safeLog($context, $message, true);
$result = response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Catched Error: ' . $exception->getMessage(),
]);
} }
Cache::forever($cacheKey, base64_encode(File::get($pdfFileName)));
$this->logResult($context, 'PDF File converted to Base64 and Saved to Cache.Key: ' . $cacheKey);
return response()->json(['code' => '200', 'status' => 'Success', 'cacheKey' => $cacheKey]);
} catch (\Throwable $exception) {
$message = 'An Error Occured - ' . substr($exception->getMessage(), 0, 300);
$this->safeLog($context, $message, true);
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Catched Error: ' . $exception->getMessage(),
]);
} }
return $result;
} }
private function configureTimeouts(): void private function configureTimeouts(): void
@@ -71,7 +70,7 @@ class PdfStorage extends Controller
} }
$context['DELIVERYNO'] = $context['A4MACPKR'] !== '' ? $context['A4MACPKR'] : self::ZERO_PAD; $context['DELIVERYNO'] = $context['A4MACPKR'] !== '' ? $context['A4MACPKR'] : self::ZERO_PAD;
$context['SHUSER'] = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'; $context['SHUSER'] = m3User() !== '' ? Str::upper(m3User()) : 'WebUser';
$context['SHSOURCE'] = 'PdfStorageAPI'; $context['SHSOURCE'] = 'PdfStorageAPI';
return $context; return $context;
@@ -123,7 +122,7 @@ class PdfStorage extends Controller
'SHINTBOX' => $context['DOCNUM'] ?? '', 'SHINTBOX' => $context['DOCNUM'] ?? '',
'SHTYPE' => $type, 'SHTYPE' => $type,
'SHDATA' => $message, 'SHDATA' => $message,
'SHUSER' => $context['SHUSER'] ?? (m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'), 'SHUSER' => $context['SHUSER'] ?? (m3User() !== '' ? Str::upper(m3User()) : 'WebUser'),
'SHSOURCE' => $context['SHSOURCE'] ?? 'PdfStorageAPI', 'SHSOURCE' => $context['SHSOURCE'] ?? 'PdfStorageAPI',
'SHCO' => $context['COMPANY'] ?? '', 'SHCO' => $context['COMPANY'] ?? '',
'SHWHS' => $context['WHS'] ?? '', 'SHWHS' => $context['WHS'] ?? '',
@@ -140,7 +139,7 @@ class PdfStorage extends Controller
'DOCNUM' => $docNum, 'DOCNUM' => $docNum,
'COMPANY' => $company, 'COMPANY' => $company,
'WHS' => $warehouse, 'WHS' => $warehouse,
'SHUSER' => m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser', 'SHUSER' => m3User() !== '' ? Str::upper(m3User()) : 'WebUser',
'SHSOURCE' => 'PdfStorageAPI', 'SHSOURCE' => 'PdfStorageAPI',
]; ];
$this->safeLog($context, 'An Error Occured -' . $message, true); $this->safeLog($context, 'An Error Occured -' . $message, true);
+158 -236
View File
@@ -6,8 +6,6 @@ use Config;
use DB; use DB;
use Http; use Http;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Log;
use Modules\M3\Helpers\Helper; use Modules\M3\Helpers\Helper;
use Modules\M3\Http\Controllers\Controller; use Modules\M3\Http\Controllers\Controller;
use Str; use Str;
@@ -15,262 +13,186 @@ use Str;
class ReplenAPI extends Controller class ReplenAPI extends Controller
{ {
private const ZERO_PAD = '000000'; private const ZERO_PAD = '000000';
private const JSON_CONTENT_TYPE = 'application/json'; private const JSON_CONTENT_TYPE = 'application/json';
private const SOURCE = 'M3InventoryReportReplen';
// M3Replenishement with URL Params
public function M3InventoryReportReplen(Request $request) public function M3InventoryReportReplen(Request $request)
{ {
ini_set('max_execution_time', Config::get('arrays.PHPTimeOut')); $this->configureTimeouts();
ini_set('max_input_time', Config::get('arrays.PHPTimeOut')); $context = $this->requestContext($request);
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut'));
// dd($request->all());
// Log::info( $request->all() );
// Get The Calling User
$sHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser';
$mACPKR = isset($mACPKR) ? $mACPKR : self::ZERO_PAD;
$sHSOURCE = 'M3InventoryReportReplen';
$m3Errors = '';
try { try {
// Get Json Contents $response = $this->processInventoryRequest($context);
$urlParams = count($request->all()) > 0 ? $request->all() : ''; } catch (\Throwable $throwable) {
$response = $this->exceptionResponse($context, $throwable);
}
$cO = isset($urlParams['CO']) ? $urlParams['CO'] : ''; return $response;
$wHS = isset($urlParams['WHS']) ? $urlParams['WHS'] : ''; }
$pARTNO = isset($urlParams['PARTNO']) ? $urlParams['PARTNO'] : '';
$pKLN = isset($urlParams['PKLN']) ? $urlParams['PKLN'] : '';
$rEQPKWS = isset($urlParams['REQPKWS']) ? $urlParams['REQPKWS'] : '';
$rEQKPKUS = isset($urlParams['REQKPKUS']) ? $urlParams['REQKPKUS'] : 'WebUser';
if ($urlParams != '') { private function processInventoryRequest(array $context)
$incomingParams = ['CO' => $cO, 'WHS' => $wHS, 'PARTNO' => $pARTNO, 'PKLN' => $pKLN, 'REQPKWS' => $rEQPKWS, 'REQKPKUS' => $rEQKPKUS]; {
$incomingParams = json_encode($incomingParams); if ($context['params'] === []) {
return $this->missingParametersResponse($context);
}
// Write to Logs : Params In $this->logRequest($context);
$data = ['SHMACPKR' => $mACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => '', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Req-out', $tokenResponse = Helper::checkReceiveTokenV2();
'SHDATA' => $incomingParams, 'SHUSER' => $sHUSER, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $cO, 'SHWHS' => $wHS]; $tokenData = $tokenResponse->getData();
Helper::logToICM699M($data); if ((string) ($tokenData->code ?? '') !== '200') {
$this->log($context, (string) ($tokenData->message ?? ''), 'X', 'M3-Res-out');
return $tokenResponse;
}
$pART = explode(',', $pARTNO); return $this->inventoryResponse($context, (string) ($tokenData->token ?? ''));
foreach ($pART as $value) { }
$pARTS[] = ['PART' => $value, 'LOT' => ''];
}
$jSON = json_encode(['WHS' => $wHS, 'PARTS' => $pARTS]); private function inventoryResponse(array $context, string $token)
{
$result = $this->fetchInventory($context, $token);
if ($result['error'] !== '') {
$this->log($context, $result['status'] . '-' . $result['error'] . $result['session'], 'X', 'M3-Res-out');
return response()->json(['code' => '405', 'status' => 'error', 'message' => $result['error']]);
}
// Write to Logs : Params Sent To M3 $this->log($context, $result['status'] . '-' . $result['json'] . $result['session'], '', 'M3-Res-out');
$data = ['SHMACPKR' => $mACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => '', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Req-out', $rows = $this->flattenInventory($result['result']);
'SHDATA' => 'Sent To M3-' . $jSON, 'SHUSER' => $sHUSER, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $cO, 'SHWHS' => $wHS]; $this->replaceInventory($context, $rows);
Helper::logToICM699M($data);
// Get Token return response()->json(['code' => '200', 'status' => 'Success']);
$tokenResponse = Helper::checkReceiveTokenV2(); }
$tokenData = $tokenResponse->getData();
if ($tokenData->code == '200') {
$ionToken = isset($tokenData->token) ? $tokenData->token : '';
} else {
$sHDATA = isset($tokenData->message) ? $tokenData->message : '';
// Write to Logs : Error
$data = ['SHMACPKR' => $mACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out',
'SHDATA' => $sHDATA, 'SHUSER' => $sHUSER, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $cO, 'SHWHS' => $wHS];
Helper::logToICM699M($data);
return $tokenResponse; private function configureTimeouts(): void
} {
$timeout = Config::get('arrays.PHPTimeOut');
ini_set('max_execution_time', $timeout);
ini_set('max_input_time', $timeout);
ini_set('default_socket_timeout', $timeout);
}
// Call ionapi with Params private function requestContext(Request $request): array
$endPointURL = env('ENDPOINTURL') . 'APIFLOWS/Sentinel/M3InventoryReportList?ionapiRespStyle=sync'; {
$token = $ionToken; $params = $request->all();
return [
'params' => $params,
'co' => (string) ($params['CO'] ?? ''),
'whs' => (string) ($params['WHS'] ?? ''),
'partNo' => (string) ($params['PARTNO'] ?? ''),
'pkln' => (string) ($params['PKLN'] ?? ''),
'reqPkws' => (string) ($params['REQPKWS'] ?? ''),
'reqKpkus' => (string) ($params['REQKPKUS'] ?? 'WebUser'),
'user' => m3User() !== '' ? Str::upper(m3User()) : 'WebUser',
'macpkr' => self::ZERO_PAD,
];
}
$response = Http::withHeaders([ private function logRequest(array $context): void
'accept' => self::JSON_CONTENT_TYPE, {
'Content-Type' => self::JSON_CONTENT_TYPE, $incoming = json_encode([
]) 'CO' => $context['co'], 'WHS' => $context['whs'], 'PARTNO' => $context['partNo'],
->withToken($token) 'PKLN' => $context['pkln'], 'REQPKWS' => $context['reqPkws'], 'REQKPKUS' => $context['reqKpkus'],
->withBody($jSON, self::JSON_CONTENT_TYPE) ]);
->timeout(Config::get('arrays.curlTimeOut')) $this->log($context, (string) $incoming, '', 'M3-Req-out');
->retry(3, 100) $this->log($context, 'Sent To M3-' . $this->inventoryRequestJson($context), '', 'M3-Req-out');
->post($endPointURL); }
$responseStatusCode = $response->getStatusCode(); private function inventoryRequestJson(array $context): string
{
$parts = array_map(static fn (string $part): array => ['PART' => $part, 'LOT' => ''], explode(',', $context['partNo']));
return (string) json_encode(['WHS' => $context['whs'], 'PARTS' => $parts]);
}
$m3SessionId = $response->hasHeader('x-transaction-id') ? '-sessionID:' . $response->header('x-transaction-id') : '-sessionID'; private function fetchInventory(array $context, string $token): array
{
$response = Http::withHeaders(['accept' => self::JSON_CONTENT_TYPE, 'Content-Type' => self::JSON_CONTENT_TYPE])
->withToken($token)
->withBody($this->inventoryRequestJson($context), self::JSON_CONTENT_TYPE)
->timeout(Config::get('arrays.curlTimeOut'))->retry(3, 100)
->post(env('ENDPOINTURL') . 'APIFLOWS/Sentinel/M3InventoryReportList?ionapiRespStyle=sync');
if ($responseStatusCode == '200') { $status = $response->getStatusCode();
// Check M3 Response for Errors $session = $response->hasHeader('x-transaction-id') ? '-sessionID:' . $response->header('x-transaction-id') : '-sessionID';
$m3Errors = ''; if ((string) $status !== '200') {
$responseJSON = $response->getBody()->getContents(); return ['status' => $status, 'session' => $session, 'error' => 'Error: No Response from M3', 'json' => '', 'result' => []];
$jsonSanytized = Helper::sanityzeString($responseJSON); }
// Array
$responseArray = json_decode($jsonSanytized, true);
$responseTMP = $responseArray['result'];
} else {
$m3Errors = 'Error: No Response from M3';
}
// Validate response From M3 $json = Helper::sanityzeString($response->getBody()->getContents());
if ($m3Errors != '') { $decoded = json_decode($json, true);
// Write to Logs : Error return ['status' => $status, 'session' => $session, 'error' => '', 'json' => $json, 'result' => $decoded['result'] ?? []];
$data = ['SHMACPKR' => $mACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', }
'SHDATA' => $responseStatusCode . '-' . $m3Errors . $m3SessionId, 'SHUSER' => $sHUSER, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $cO, 'SHWHS' => $wHS];
Helper::logToICM699M($data);
// Send Response private function flattenInventory(array $result): array
return response()->json([ {
'code' => '405', $rows = [];
'status' => 'error', foreach ($result as $item) {
'message' => 'Error: No Response from M3', $base = array_diff_key($item, ['Locations' => true]);
]); foreach (($item['Locations'] ?? []) as $location) {
} else { $rows[] = array_merge($base, $location);
// Write to Logs : JSON
$data = ['SHMACPKR' => $mACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => '', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out',
'SHDATA' => $responseStatusCode . '-' . $jsonSanytized . $m3SessionId, 'SHUSER' => $sHUSER, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $cO, 'SHWHS' => $wHS];
Helper::logToICM699M($data);
}
$dataToWrite2 = [];
foreach ($responseTMP as $value) {
$dataToWrite = [];
foreach ($value as $key2 => $content) {
if ($key2 != 'Locations') {
$dataToWrite[$key2] = $content;
} else {
foreach ($content as $content2) {
$dataToWrite2[] = array_merge($dataToWrite, $content2);
}
}
}
}
// Delete Old Records from IC630M
foreach ($dataToWrite2 as $value) {
DB::connection(config('m3.connection', 'pgsql'))->table('ic630m')
->where('LLCO', $cO)
->where('LLWHNO', $wHS)
->where('LLPPN', trim($value['PartNumber']))
->delete();
}
foreach ($dataToWrite2 as $value) {
// Check if Location exist in DE640M $value['Location']
$dE640MSQLSEL = DB::connection(config('m3.connection', 'pgsql'))->table('de640m')
->where('LVCO', $cO)
->where('WSELOC', $value['Warehouse'])
->where('VLOCID', $value['Location'])
->first();
$locationExist = isset($dE640MSQLSEL->VLOCID) ? trim($dE640MSQLSEL->VLOCID) : '';
$wHExist = isset($dE640MSQLSEL->WSELOC) ? trim($dE640MSQLSEL->WSELOC) : '';
$cOExist = isset($dE640MSQLSEL->LVCO) ? trim($dE640MSQLSEL->LVCO) : '';
// if No records Founds insert New Record
if ($locationExist == '' && $wHExist == '' && $cOExist == '') {
$sQLInsertPrep640M['LVCO'] = $cO; // Company
$sQLInsertPrep640M['WSELOC'] = $value['Warehouse'];
// $sQLInsertPrep640M['LVUSD3'] = $value['']; // Pallet Size : Not Found in M3 JSON
$sQLInsertPrep640M['VLOCID'] = $value['Location']; // Stock location
$sQLInsertPrep640M['LVSEQN'] = $value['LocationSeq'];
$sQLInsertPrep640M['LVUSD2'] = $value['LocationType'];
$sQLInsertPrep640M['LVZONE'] = $value['Zone']; // Warehouse zone
$sQLInsertPrep640M['LVMAINTTS'] = now(); // Last maintained time stamp
DB::connection(config('m3.connection', 'pgsql'))->table('de640m')->insert($sQLInsertPrep640M);
}
// Update
else {
$sQLUpdatePrep640M['LVSEQN'] = $value['LocationSeq'];
$sQLUpdatePrep640M['LVUSD2'] = $value['LocationType'];
$sQLUpdatePrep640M['LVZONE'] = $value['Zone'];
$sQLUpdatePrep640M['LVMAINTTS'] = now();
DB::connection(config('m3.connection', 'pgsql'))->table('de640m')
->where('LVCO', $cO)
->where('WSELOC', $value['Warehouse'])
->where('VLOCID', $value['Location'])
->update($sQLUpdatePrep640M);
}
// Insert New Records to IC630M
$sQLInsertPrep630M = [];
$sQLInsertPrep630M['LLCO'] = $cO; // Company
$sQLInsertPrep630M['LLWHNO'] = $value['Warehouse']; // Warehouse
$sQLInsertPrep630M['LLPPN'] = $value['PartNumber']; // Part Number
$sQLInsertPrep630M['LLBALANCE'] = $value['Quantity']; // Quantity
$sQLInsertPrep630M['LLSTLC'] = $value['Location']; // Stock location
$sQLInsertPrep630M['LLLTNO'] = $value['LotNumber']; // Lot Number
$sQLInsertPrep630M['LLMAINTTS'] = now(); // Request time timestamp
$sQLInsertPrep630M['LLIZLCF'] = trim($value['LotNumber']) != '' ? '1' : ''; // Lot control flag
$sQLInsertPrep630M['LLSPR10A01'] = $value['Allocation']; // Allocation
$sQLInsertPrep630M['LLSPR15A01'] = $value['AllocatedTo']; // AllocatedTo
$descSanytized = Helper::sanityzeString($value['Description']);
// $descSanytized = str_replace(['–','\u20131'], '-',$value['Description']);
$sQLInsertPrep630M['LLCMDESC'] = $descSanytized; // Description
DB::connection(config('m3.connection', 'pgsql'))->table('ic630m')->insert($sQLInsertPrep630M);
}
// Send Response
return response()->json([
'code' => '200',
'status' => 'Success',
]);
} else {
// Write to Logs Out : Error
$mACPKR = isset($mACPKR) ? $mACPKR : self::ZERO_PAD;
$data = ['SHMACPKR' => $mACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out',
'SHDATA' => 'No URL Parameters Sent', 'SHUSER' => $sHUSER, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $cO, 'SHWHS' => $wHS];
Helper::logToICM699M($data);
// Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => 'No URL Parameters Sent',
]);
}
} catch (\Throwable $th) {
// Write to Logs Out : Error
$mACPKR = isset($mACPKR) ? $mACPKR : self::ZERO_PAD;
$getCurlErrors = str_contains($th->getMessage(), 'cURL error');
if ($getCurlErrors) {
// dd($getCurlErrors);
// Write to Logs Out : Failed
$data = ['SHMACPKR' => $mACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out',
'SHDATA' => 'Curl Error: Communication with M3 Timed Out' . ' M3-JSN-out' . $incomingParams, 'SHUSER' => $sHUSER, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $cO, 'SHWHS' => $wHS];
Helper::logToICM699M($data);
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Curl Error: Communication with M3 Timed Out.',
]);
} else {
// Set SHERROR To Z if Duplicate Key Detected $th
// Read The substring from SHDATA after M3-JSN-out
$sHERROR = 'X';
$word = 'Duplicate key value specified';
$position = strpos($th, $word);
if ($position !== false) {
$sHERROR = 'Z';
}
$data = ['SHMACPKR' => $mACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => $sHERROR, 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out',
'SHDATA' => 'An Error Occured - ' . substr($th, 0, 300), 'SHUSER' => $sHUSER, 'SHSOURCE' => $sHSOURCE, 'SHCO' => $cO, 'SHWHS' => $wHS];
Helper::logToICM699M($data);
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Catched Error: ' . $th,
]);
} }
} }
return $rows;
}
private function replaceInventory(array $context, array $rows): void
{
foreach ($rows as $row) {
DB::connection(config('m3.connection', 'pgsql'))->table('ic630m')
->where('LLCO', $context['co'])->where('LLWHNO', $context['whs'])
->where('LLPPN', trim((string) ($row['PartNumber'] ?? '')))->delete();
}
foreach ($rows as $row) {
$this->upsertLocation($context, $row);
$this->insertInventory($context, $row);
}
}
private function upsertLocation(array $context, array $row): void
{
$connection = DB::connection(config('m3.connection', 'pgsql'));
$query = $connection->table('de640m')->where('LVCO', $context['co'])
->where('WSELOC', $row['Warehouse'] ?? '')->where('VLOCID', $row['Location'] ?? '');
$values = ['LVSEQN' => $row['LocationSeq'] ?? '', 'LVUSD2' => $row['LocationType'] ?? '', 'LVZONE' => $row['Zone'] ?? '', 'LVMAINTTS' => now()];
if ($query->exists()) {
$query->update($values);
return;
}
$connection->table('de640m')->insert(array_merge($values, ['LVCO' => $context['co'], 'WSELOC' => $row['Warehouse'] ?? '', 'VLOCID' => $row['Location'] ?? '']));
}
private function insertInventory(array $context, array $row): void
{
DB::connection(config('m3.connection', 'pgsql'))->table('ic630m')->insert([
'LLCO' => $context['co'], 'LLWHNO' => $row['Warehouse'] ?? '', 'LLPPN' => $row['PartNumber'] ?? '',
'LLBALANCE' => $row['Quantity'] ?? 0, 'LLSTLC' => $row['Location'] ?? '', 'LLLTNO' => $row['LotNumber'] ?? '',
'LLMAINTTS' => now(), 'LLIZLCF' => trim((string) ($row['LotNumber'] ?? '')) !== '' ? '1' : '',
'LLSPR10A01' => $row['Allocation'] ?? '', 'LLSPR15A01' => $row['AllocatedTo'] ?? '',
'LLCMDESC' => Helper::sanityzeString((string) ($row['Description'] ?? '')),
]);
}
private function missingParametersResponse(array $context)
{
$this->log($context, 'No URL Parameters Sent', 'X', 'M3-Res-out');
return response()->json(['code' => '405', 'status' => 'Error', 'message' => 'No URL Parameters Sent']);
}
private function exceptionResponse(array $context, \Throwable $throwable)
{
if (str_contains($throwable->getMessage(), 'cURL error')) {
$this->log($context, 'Curl Error: Communication with M3 Timed Out', 'X', 'M3-Res-out');
return response()->json(['code' => '405', 'status' => 'error', 'message' => 'Curl Error: Communication with M3 Timed Out.']);
}
$error = str_contains($throwable->getMessage(), 'Duplicate key value specified') ? 'Z' : 'X';
$this->log($context, 'An Error Occured - ' . substr($throwable->getMessage(), 0, 300), $error, 'M3-Res-out');
return response()->json(['code' => '405', 'status' => 'error', 'message' => 'Catched Error: ' . $throwable->getMessage()]);
}
private function log(array $context, string $message, string $error, string $type): void
{
Helper::logToICM699M([
'SHMACPKR' => $context['macpkr'], 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => $error,
'SHINTBOX' => '', 'SHTYPE' => $type, 'SHDATA' => $message, 'SHUSER' => $context['user'], 'SHSOURCE' => self::SOURCE,
'SHCO' => $context['co'], 'SHWHS' => $context['whs'],
]);
} }
// End M3InventoryReportReplen
} }
+270 -663
View File
@@ -7,7 +7,6 @@ use DateTime;
use DB; use DB;
use Http; use Http;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Log; use Log;
use Modules\M3\Helpers\Helper; use Modules\M3\Helpers\Helper;
use Modules\M3\Http\Controllers\Controller; use Modules\M3\Http\Controllers\Controller;
@@ -15,684 +14,292 @@ use Str;
class TransferAPI extends Controller class TransferAPI extends Controller
{ {
use TransferApiSupport;
private const ZERO_PAD = '000000'; private const ZERO_PAD = '000000';
private const JSON_CONTENT_TYPE = 'application/json'; private const JSON_CONTENT_TYPE = 'application/json';
private const ORDER_CATEGORY_LABEL = ' Order Category: '; private const ORDER_CATEGORY_LABEL = ' Order Category: ';
private const NO_URL_PARAMETERS = 'No URL Parameters Sent'; private const NO_URL_PARAMETERS = 'No URL Parameters Sent';
// TransferAPIURL : with URL PARAMS
public function TransferAPIURL(Request $request) public function TransferAPIURL(Request $request)
{ {
ini_set('max_execution_time', Config::get('arrays.PHPTimeOut')); $this->configureTimeouts();
ini_set('max_input_time', Config::get('arrays.PHPTimeOut')); $context = $this->transferContext($request);
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut'));
// Get the Calling User
$sHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser';
// Set SHSOURCE
$sHSOURCE = 'TransferAPIURL';
$m3Errors = '';
// Get URL PARAMS
$urlParams = count($request->all()) > 0 ? $request->all() : '';
$dIVI = isset($urlParams['COMPANY']) ? $urlParams['COMPANY'] : '';
$wHS = isset($urlParams['WHS']) ? $urlParams['WHS'] : '';
try { try {
if ($urlParams != '') { $response = $this->processTransfer($context);
$pART = isset($urlParams['PART']) ? $urlParams['PART'] : ''; } catch (\Throwable $throwable) {
$lOCATIONFROM = isset($urlParams['LOCATIONFROM']) ? $urlParams['LOCATIONFROM'] : ''; $response = $this->exceptionResponse($context, $throwable, $context['json_in'] ?? '');
$lOCATIONTO = isset($urlParams['LOCATIONTO']) ? $urlParams['LOCATIONTO'] : '';
$qUANTITY = isset($urlParams['QUANTITY']) ? $urlParams['QUANTITY'] : '';
$oRDERCATEGORY = isset($urlParams['ORDERCATEGORY']) ? $urlParams['ORDERCATEGORY'] : '';
$lOTNUMBER = isset($urlParams['LOTNUMBER']) ? $urlParams['LOTNUMBER'] : '';
$cONUMBER = isset($urlParams['CONUMBER']) ? $urlParams['CONUMBER'] : '';
$cOLINENO = isset($urlParams['COLINENO']) ? $urlParams['COLINENO'] : '';
$dELIVERYNO = isset($urlParams['DELIVERYNO']) ? $urlParams['DELIVERYNO'] : self::ZERO_PAD;
$pICKLISTSUFFIX = isset($urlParams['PICKLISTSUFFIX']) ? $urlParams['PICKLISTSUFFIX'] : '';
$eNDFLAG = isset($urlParams['ENDFLAG']) ? $urlParams['ENDFLAG'] : '';
$tYPE = isset($urlParams['TYPE']) ? $urlParams['TYPE'] : '';
$cOLINESUFFIX = isset($urlParams['COLINESUFFIX']) ? $urlParams['COLINESUFFIX'] : '';
// TYPE :
// DeliveryNote
// S15
// CloseShipment
// COCharge
// Replenishment
// Move
// ORDERCATEGORY : Order Category
// 1 = Material to manufacturing order
// 3 = Customer Order
// 4 = Requisition Order
// 5 = Distribution Order
// 9 = Move order
$dELIVERYNO = isset($dELIVERYNO) ? $dELIVERYNO : self::ZERO_PAD;
$tRANSTYPE = isset($urlParams['TRANSTYPE']) ? $urlParams['TRANSTYPE'] : ''; // R for Retry
$sHDT = isset($urlParams['SHDT']) ? $urlParams['SHDT'] : '';
$sHTIME = isset($urlParams['SHTIME']) ? $urlParams['SHTIME'] : '';
$sHTYPE = isset($urlParams['SHTYPE']) ? $urlParams['SHTYPE'] : '';
// Retry Transfer
if ($tRANSTYPE == 'R') {
// $t = strtotime($sHTIME);
// $SHTIMEHM = date('H:i',$t);
// dd($SHTIMEHM);
// Get the record that contain the payload
$iCM699MS = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->select('SHDATA')
->where('SHMACPKR', $dELIVERYNO)
->where('SHDT', $sHDT)
->where('SHTIME', $sHTIME)
// ->whereRaw("VARCHAR_FORMAT (TIMESTAMP (CURRENT DATE) + MIDNIGHT_SECONDS (SHTIME) SECONDS, 'HH24:MI') = ?", [$SHTIMEHM])
->where('SHCO', $dIVI)
->where('SHWHS', $wHS)
->where('SHSOURCE', $sHSOURCE)
->where('SHERROR', 'X')
->where('SHTYPE', $sHTYPE)
->first();
// Read The substring from SHDATA after M3-JSN-out
$word = 'M3-JSN-out';
$sHDATA = isset($iCM699MS->SHDATA) ? $iCM699MS->SHDATA : '';
$sHDATAJSON = '';
$position = strpos($sHDATA, $word);
// Also Read CommTCP_read
$word2 = 'url:http://127.0.0.1:10005/api/TransferAPIURL?';
$sHDATA2 = isset($iCM699MS->SHDATA) ? $iCM699MS->SHDATA : '';
$position2 = strpos($sHDATA2, $word2);
if ($position !== false) {
$sHDATAJSON = substr($sHDATA, $position + strlen($word));
} elseif ($position2 !== false) {
$sHDATAJSONS = substr($sHDATA2, $position2 + strlen($word2));
$sHDATAJSONA = [];
parse_str($sHDATAJSONS, $sHDATAJSONA);
$sHDATAJSON = json_encode($sHDATAJSONA);
}
// Find the position of the opening and closing curly braces
$startPos = strpos($sHDATAJSON, '{');
$endPos = strrpos($sHDATAJSON, '}');
// Extract the substring within the curly braces
$sHDATAJSON = substr($sHDATAJSON, $startPos, $endPos - $startPos + 1);
$jSONTR = $sHDATAJSON != '' ? json_decode($sHDATAJSON) : '';
if ($jSONTR != '') {
$pART = isset($jSONTR->PART) ? $jSONTR->PART : '';
$lOCATIONFROM = isset($jSONTR->LOCATIONFROM) ? $jSONTR->LOCATIONFROM : '';
$lOCATIONTO = isset($jSONTR->LOCATIONTO) ? $jSONTR->LOCATIONTO : '';
$qUANTITY = isset($jSONTR->QUANTITY) ? $jSONTR->QUANTITY : '';
$oRDERCATEGORY = isset($jSONTR->ORDERCATEGORY) ? $jSONTR->ORDERCATEGORY : '';
$lOTNUMBER = isset($jSONTR->LOTNUMBER) ? $jSONTR->LOTNUMBER : '';
$cONUMBER = isset($jSONTR->CONUMBER) ? $jSONTR->CONUMBER : '';
$cOLINENO = isset($jSONTR->COLINENO) ? $jSONTR->COLINENO : '';
$dELIVERYNO = isset($jSONTR->DELIVERYNO) ? $jSONTR->DELIVERYNO : self::ZERO_PAD;
$pICKLISTSUFFIX = isset($jSONTR->PICKLISTSUFFIX) ? $jSONTR->PICKLISTSUFFIX : '';
$eNDFLAG = isset($jSONTR->ENDFLAG) ? $jSONTR->ENDFLAG : '';
$tYPE = isset($jSONTR->TYPE) ? $jSONTR->TYPE : '';
$cOLINESUFFIX = isset($jSONTR->COLINESUFFIX) ? $jSONTR->COLINESUFFIX : '';
}
// Set SHERROR to Z in ICM699M
DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->where('SHMACPKR', $dELIVERYNO)
->where('SHDT', $sHDT)
->where('SHTIME', $sHTIME)
->where('SHCO', $dIVI)
->where('SHWHS', $wHS)
->where('SHSOURCE', $sHSOURCE)
->where('SHERROR', 'X')
->where('SHTYPE', $sHTYPE)
->update(['SHERROR' => 'Z', 'SHACKUSER' => $sHUSER, 'SHACKDATE' => date('Y-m-d'), 'SHACKTIME' => date('H:i:s')]);
}
// Part Exists : proceed
if ($pART != '') {
// Read from ICM688M2
// Get Parts And Lots from ICM688M2
$iCM688M2SLTMP = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m2')
->select('A5BRADLN', 'A5PKSFX', 'A5PLANT', 'A5SENTPKR')
->distinct()
->where('A5CO', $dIVI)
->where('A5WHS', $wHS)
->where('A5MACPKR', $dELIVERYNO);
// Get Pack Lines
if ($cOLINENO != '') {
$iCM688M2SLTMP->where('A5PKLN', $cOLINENO);
}
$iCM688M2SL = $iCM688M2SLTMP->first();
$a5BRADLINE = isset($iCM688M2SL->A5BRADLN) ? $iCM688M2SL->A5BRADLN : '';
$cOLINESUFFIX = isset($iCM688M2SL->A5PKSFX) ? $iCM688M2SL->A5PKSFX : ''; // Case 551 : $cOLINESUFFIX
$a5PLANT = isset($iCM688M2SL->A5PLANT) ? $iCM688M2SL->A5PLANT : '';
$a5SENTPKR = isset($iCM688M2SL->A5SENTPKR) ? $iCM688M2SL->A5SENTPKR : '';
$jSONIN = json_encode([
'DIVI' => $dIVI,
'WHS' => $wHS,
'PART' => $pART,
'LOCATIONFROM' => $lOCATIONFROM,
'LOCATIONTO' => $lOCATIONTO,
'QUANTITY' => $qUANTITY,
'ORDERCATEGORY' => $oRDERCATEGORY,
'LOTNUMBER' => $lOTNUMBER,
'CONUMBER' => $cONUMBER,
'COLINENO' => $cOLINENO,
'DELIVERYNO' => $dELIVERYNO,
'PICKLISTSUFFIX' => $pICKLISTSUFFIX,
'ENDFLAG' => $eNDFLAG,
'TYPE' => $tYPE,
'COLINESUFFIX' => $cOLINESUFFIX,
], JSON_UNESCAPED_SLASHES);
$jSON = json_encode([
'DIVI' => $dIVI,
'WHS' => $wHS,
'PART' => $pART,
'LOCATIONFROM' => $lOCATIONFROM,
'LOCATIONTO' => $lOCATIONTO,
'QUANTITY' => $qUANTITY,
'ORDERCATEGORY' => $oRDERCATEGORY,
'LOTNUMBER' => $lOTNUMBER,
'CONUMBER' => $cONUMBER,
'COLINENO' => $a5BRADLINE,
'DELIVERYNO' => $dELIVERYNO,
'PICKLISTSUFFIX' => $pICKLISTSUFFIX,
'ENDFLAG' => $eNDFLAG,
'TYPE' => $tYPE,
'COLINESUFFIX' => $cOLINESUFFIX,
], JSON_UNESCAPED_SLASHES);
// Write JSON Params to Logs Out
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => '',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Req-out',
'SHDATA' => $jSON,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
// Get Token
$tokenResponse = Helper::checkReceiveTokenV2();
$tokenData = $tokenResponse->getData();
if ($tokenData->code == '200') {
$ionToken = isset($tokenData->token) ? $tokenData->token : '';
} else {
$sHDATA = isset($tokenData->message) ? $tokenData->message : '';
// Write to Logs : Error
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => '',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => $sHDATA,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
return $tokenResponse;
}
// Call ionapi with Params
$endPointURL = env('ENDPOINTURL') . 'APIFLOWS/Sentinel/TransferAPI?ionapiRespStyle=sync';
$token = $ionToken;
$response = Http::withHeaders([
'accept' => self::JSON_CONTENT_TYPE,
'Content-Type' => self::JSON_CONTENT_TYPE,
])
->withToken($token)
->withBody($jSON, self::JSON_CONTENT_TYPE)
->timeout(Config::get('arrays.curlTimeOut'))
->post($endPointURL);
$responseStatusCode = $response->getStatusCode();
$m3SessionId = $response->hasHeader('x-transaction-id') ? '-sessionID:' . $response->header('x-transaction-id') : '-sessionID';
$m3ResponseTimeTmp = $response->hasHeader('x-response-time') ? ltrim($response->header('x-response-time'), '-') : '0';
$m3ResponseTimeTmp2 = floatval($m3ResponseTimeTmp) / 1000;
$dateTime = new DateTime;
$dateTime->setTimestamp($m3ResponseTimeTmp2);
$m3ResponseTime = $m3ResponseTimeTmp != '' ? '-M3ResponseTime:' . $dateTime->format('d/m/Y H:i:s.v') : '-M3ResponseTime';
$transferStats = $response->transferStats;
$m3transferTime = $transferStats->getTransferTime() != null ? '-M3transferTime:' . $transferStats->getTransferTime() : '-M3transferTime';
if ($responseStatusCode == '200') {
$responseContents = json_decode($response->getBody()->getContents());
$responseResult = $responseContents->result;
$m3JSONResponse = json_encode($responseContents);
// Check M3 Response for Errors
$m3Errors = Helper::checkM3Errors($responseResult);
} else {
$m3JSONResponse = '';
$m3Errors = 'Error: No Response from M3';
}
if ($m3Errors == '') {
// Write to Logs Out : Success
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => '',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => $responseStatusCode . 'TYPE :' . $tYPE . self::ORDER_CATEGORY_LABEL . $oRDERCATEGORY . '-' . $m3JSONResponse . $m3SessionId . $m3ResponseTime . $m3transferTime,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
// Log to ICM799M
DB::connection(config('m3.connection', 'pgsql'))->table('icm799m')
->insert(['IMDIV' => $dIVI,
'IMPLANT' => $a5PLANT,
'IMWHS' => $wHS,
'IMSENTPKR' => $a5SENTPKR,
'IMDELNO' => $dELIVERYNO,
'IMORDER' => $cONUMBER,
'IMLINE' => $cOLINENO,
'IMBRADLN' => $a5BRADLINE,
'IMSUFFIX' => $pICKLISTSUFFIX,
'IMPART' => $pART,
'IMLOT' => $lOTNUMBER,
'IMQTY' => $qUANTITY,
'IMFRLOC' => $lOCATIONFROM,
'IMTOLOC' => $lOCATIONTO,
'IMDATE' => date('Ymd'),
'IMTIME' => date('His'),
]);
return response()->json([
'code' => $responseStatusCode,
'status' => 'Success',
]);
} else {
// Write to Logs Out : Failed
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => $responseStatusCode . '-' . $m3Errors . '-TYPE:' . $tYPE . self::ORDER_CATEGORY_LABEL . $oRDERCATEGORY . '-' . $m3JSONResponse .
'M3-JSN-out' . $jSONIN . $m3SessionId . $m3ResponseTime . $m3transferTime,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
return response()->json([
'code' => '206',
'status' => 'Error',
'message' => $responseStatusCode . '-' . $m3Errors . '- TYPE :' . $tYPE . self::ORDER_CATEGORY_LABEL . $oRDERCATEGORY . '-' . $m3JSONResponse,
]);
}
}
// Part Does not Exist : Failure
else {
// Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => 'Json Payload Not Valid, possible cause: Retry butto was clicked by another user.',
]);
}
} else {
// Write to Logs Out : Error
$dELIVERYNO = isset($dELIVERYNO) ? $dELIVERYNO : self::ZERO_PAD;
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => self::NO_URL_PARAMETERS,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
// Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => self::NO_URL_PARAMETERS,
]);
}
} catch (\Throwable $th) {
$getCurlErrors = str_contains($th->getMessage(), 'cURL error');
if ($getCurlErrors) {
// Write to Logs Out : Failed
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => 'Curl Error: Communication with M3 Timed Out' . ' M3-JSN-out' . $jSONIN,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Curl Error: Communication with M3 Timed Out.',
]);
} else {
// Write to Logs Out : Error
$dELIVERYNO = isset($dELIVERYNO) ? $dELIVERYNO : self::ZERO_PAD;
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Err-out',
'SHDATA' => 'An Error Occured - ' . substr($th, 0, 300),
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
Log::error($th);
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Uncaught Error',
]);
}
} }
}
// End TransferAPIURL
// UpdateShipmentNumber : with URL PARAMS return $response;
}
public function UpdateShipmentNumber(Request $request) public function UpdateShipmentNumber(Request $request)
{ {
ini_set('max_execution_time', Config::get('arrays.PHPTimeOut')); $this->configureTimeouts();
ini_set('max_input_time', Config::get('arrays.PHPTimeOut')); $context = $this->shipmentContext($request);
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut'));
// Get the Calling User
$sHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser';
// Set SHSOURCE
$sHSOURCE = 'UpdateShipmentNumber';
$m3Errors = '';
// Get URL PARAMS
$urlParams = count($request->all()) > 0 ? $request->all() : '';
$dIVI = isset($urlParams['COMPANY']) ? $urlParams['COMPANY'] : '';
$wHS = isset($urlParams['WHS']) ? $urlParams['WHS'] : '';
$pLANT = isset($urlParams['PLANT']) ? $urlParams['PLANT'] : '';
try { try {
if ($urlParams != '') { $response = $this->processShipmentUpdate($context);
$a4SPR15A01 = isset($urlParams['A4SPR15A01']) ? $urlParams['A4SPR15A01'] : ''; } catch (\Throwable $throwable) {
$dELIVERYNO = isset($urlParams['DELIVERYNO']) ? $urlParams['DELIVERYNO'] : ''; $response = $this->exceptionResponse($context, $throwable, $context['json'] ?? '');
$tYPE = 'UpdateShipmentNumber';
$allMacpkrs = [];
if ($a4SPR15A01 != '') {
// Read from ICM688M1
$iCM688M1SLTMP = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->select('A4MACPKR')
->where('A4CO', $dIVI)
->where('A4WHS', $wHS)
->where('A4PLANT', $pLANT)
->where('A4SPR15A01', $a4SPR15A01);
// Get Macpikers also if sent as Parameter
if ($dELIVERYNO != '') {
$iCM688M1SLTMP->where('A4MACPKR', $dELIVERYNO);
}
$iCM688M1SL = $iCM688M1SLTMP->get();
// Loop ICM688M1 to get all A4MACPKR
foreach ($iCM688M1SL as $ICM688M1) {
$allMacpkrs[] = $ICM688M1->A4MACPKR;
}
}
// A4SPR15A01 Exists : proceed
if ($a4SPR15A01 != '' && ! empty($allMacpkrs)) {
$jSON = json_encode([
'DIVI' => $dIVI,
'WHS' => $wHS,
'ShipmentNumber' => [
'Number' => $a4SPR15A01,
'DeliveryNumbers' => $allMacpkrs,
],
'TYPE' => $tYPE,
'ENDFLAG' => '',
], JSON_UNESCAPED_SLASHES);
// Write JSON Params to Logs Out
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => '',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Req-out',
'SHDATA' => $jSON,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
// Get Token
$tokenResponse = Helper::checkReceiveTokenV2();
$tokenData = $tokenResponse->getData();
if ($tokenData->code == '200') {
$ionToken = isset($tokenData->token) ? $tokenData->token : '';
} else {
$sHDATA = isset($tokenData->message) ? $tokenData->message : '';
// Write to Logs : Error
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => '',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => $sHDATA,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
return $tokenResponse;
}
// Call ionapi with Params
$endPointURL = env('ENDPOINTURL') . 'APIFLOWS/Sentinel/TransferAPI?ionapiRespStyle=sync';
$token = $ionToken;
$response = Http::withHeaders([
'accept' => self::JSON_CONTENT_TYPE,
'Content-Type' => self::JSON_CONTENT_TYPE,
])
->withToken($token)
->withBody($jSON, self::JSON_CONTENT_TYPE)
->timeout(Config::get('arrays.curlTimeOut'))
->post($endPointURL);
$responseStatusCode = $response->getStatusCode();
$m3SessionId = $response->hasHeader('x-transaction-id') ? '-sessionID:' . $response->header('x-transaction-id') : '-sessionID';
$m3ResponseTimeTmp = $response->hasHeader('x-response-time') ? ltrim($response->header('x-response-time'), '-') : '0';
$m3ResponseTimeTmp2 = floatval($m3ResponseTimeTmp) / 1000;
$dateTime = new DateTime;
$dateTime->setTimestamp($m3ResponseTimeTmp2);
$m3ResponseTime = $m3ResponseTimeTmp != '' ? '-M3ResponseTime:' . $dateTime->format('d/m/Y H:i:s.v') : '-M3ResponseTime';
$transferStats = $response->transferStats;
$m3transferTime = $transferStats->getTransferTime() != null ? '-M3transferTime:' . $transferStats->getTransferTime() : '-M3transferTime';
if ($responseStatusCode == '200') {
$responseContents = json_decode($response->getBody()->getContents());
$responseResult = $responseContents->result;
$m3JSONResponse = json_encode($responseContents);
// Check M3 Response for Errors
$m3Errors = Helper::checkM3Errors($responseResult);
} else {
$m3JSONResponse = '';
$m3Errors = 'Error: No Response from M3';
}
if ($m3Errors == '') {
// Write to Logs Out : Success
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => '',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => $responseStatusCode . 'TYPE :' . $tYPE . '-' . $m3JSONResponse . $m3SessionId . $m3ResponseTime . $m3transferTime,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
return response()->json([
'code' => $responseStatusCode,
'status' => 'Success',
]);
} else {
// Write to Logs Out : Failed
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => $responseStatusCode . '-' . $m3Errors . '-TYPE:' . $tYPE . '-' . $m3JSONResponse .
'M3-JSN-out' . $jSON . $m3SessionId . $m3ResponseTime . $m3transferTime,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
return response()->json([
'code' => '206',
'status' => 'Error',
'message' => $responseStatusCode . '-' . $m3Errors . '- TYPE :' . $tYPE . '-' . $m3JSONResponse,
]);
}
}
// A4SPR15A01 Does not Exist : Failure
else {
// Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => 'Json Payload Not Valid, missing Parameter or no Macpicker # found.',
]);
}
} else {
// Write to Logs Out : Error
$dELIVERYNO = isset($dELIVERYNO) ? $dELIVERYNO : self::ZERO_PAD;
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => self::NO_URL_PARAMETERS,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
// Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => self::NO_URL_PARAMETERS,
]);
}
} catch (\Throwable $th) {
$getCurlErrors = str_contains($th->getMessage(), 'cURL error');
if ($getCurlErrors) {
// Write to Logs Out : Failed
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Res-out',
'SHDATA' => 'Curl Error: Communication with M3 Timed Out' . ' M3-JSN-out' . $jSON,
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Curl Error: Communication with M3 Timed Out.',
]);
} else {
// Write to Logs Out : Error
$dELIVERYNO = isset($dELIVERYNO) ? $dELIVERYNO : self::ZERO_PAD;
$data = ['SHMACPKR' => $dELIVERYNO,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'M3-Err-out',
'SHDATA' => 'An Error Occured - ' . substr($th, 0, 300),
'SHUSER' => $sHUSER,
'SHSOURCE' => $sHSOURCE,
'SHCO' => $dIVI,
'SHWHS' => $wHS,
];
Helper::logToICM699M($data);
Log::error($th);
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Uncaught Error',
]);
}
} }
return $response;
}
private function transferContext(Request $request): array
{
$params = $request->all();
return [
'params' => $params,
'user' => m3User() !== '' ? Str::upper(m3User()) : 'WebUser',
'source' => 'TransferAPIURL',
'company' => $params['COMPANY'] ?? '',
'warehouse' => $params['WHS'] ?? '',
'delivery' => $params['DELIVERYNO'] ?? self::ZERO_PAD,
];
}
private function shipmentContext(Request $request): array
{
$params = $request->all();
return [
'params' => $params,
'user' => m3User() !== '' ? Str::upper(m3User()) : 'WebUser',
'source' => 'UpdateShipmentNumber',
'company' => $params['COMPANY'] ?? '',
'warehouse' => $params['WHS'] ?? '',
'plant' => $params['PLANT'] ?? '',
'delivery' => $params['DELIVERYNO'] ?? '',
];
}
private function processTransfer(array &$context)
{
if ($context['params'] === []) {
$this->log($context, self::NO_URL_PARAMETERS, 'X', 'M3-Res-out');
return $this->errorResponse('405', self::NO_URL_PARAMETERS);
}
$context = array_merge($context, $this->transferFields($context['params']));
$this->restoreRetryPayload($context);
if ($context['part'] === '') {
return $this->errorResponse('405', 'Json Payload Not Valid, possible cause: Retry butto was clicked by another user.');
}
return $this->executeTransfer($context);
}
private function transferFields(array $params): array
{
return [
'part' => $params['PART'] ?? '', 'from' => $params['LOCATIONFROM'] ?? '',
'to' => $params['LOCATIONTO'] ?? '', 'quantity' => $params['QUANTITY'] ?? '',
'order_category' => $params['ORDERCATEGORY'] ?? '', 'lot' => $params['LOTNUMBER'] ?? '',
'co_number' => $params['CONUMBER'] ?? '', 'co_line' => $params['COLINENO'] ?? '',
'delivery' => $params['DELIVERYNO'] ?? self::ZERO_PAD, 'pick_suffix' => $params['PICKLISTSUFFIX'] ?? '',
'end_flag' => $params['ENDFLAG'] ?? '', 'type' => $params['TYPE'] ?? '',
'co_line_suffix' => $params['COLINESUFFIX'] ?? '', 'trans_type' => $params['TRANSTYPE'] ?? '',
'shdt' => $params['SHDT'] ?? '', 'shtime' => $params['SHTIME'] ?? '', 'shtype' => $params['SHTYPE'] ?? '',
];
}
private function restoreRetryPayload(array &$context): void
{
if ($context['trans_type'] !== 'R') {
return;
}
$record = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->select('SHDATA')->where('SHMACPKR', $context['delivery'])->where('SHDT', $context['shdt'])
->where('SHTIME', $context['shtime'])->where('SHCO', $context['company'])->where('SHWHS', $context['warehouse'])
->where('SHSOURCE', $context['source'])->where('SHERROR', 'X')->where('SHTYPE', $context['shtype'])->first();
$payload = $this->retryPayload($record->SHDATA ?? '');
if ($payload !== []) {
$context = array_merge($context, $this->transferFields($payload));
}
DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->where('SHMACPKR', $context['delivery'])->where('SHDT', $context['shdt'])->where('SHTIME', $context['shtime'])
->where('SHCO', $context['company'])->where('SHWHS', $context['warehouse'])->where('SHSOURCE', $context['source'])
->where('SHERROR', 'X')->where('SHTYPE', $context['shtype'])
->update(['SHERROR' => 'Z', 'SHACKUSER' => $context['user'], 'SHACKDATE' => date('Y-m-d'), 'SHACKTIME' => date('H:i:s')]);
}
private function executeTransfer(array &$context)
{
$line = $this->transferLine($context);
$context['brad_line'] = $line->A5BRADLN ?? '';
$context['co_line_suffix'] = $line->A5PKSFX ?? '';
$context['plant'] = $line->A5PLANT ?? '';
$context['sent_picker'] = $line->A5SENTPKR ?? '';
$context['json_in'] = $this->transferJson($context, false);
$json = $this->transferJson($context, true);
$this->log($context, $json, '', 'M3-Req-out');
$token = $this->token($context);
if (! is_string($token)) {
return $token;
}
$result = $this->callM3($token, $json);
return $this->transferResult($context, $result);
}
private function transferLine(array $context): ?object
{
$query = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m2')
->select('A5BRADLN', 'A5PKSFX', 'A5PLANT', 'A5SENTPKR')->distinct()
->where('A5CO', $context['company'])->where('A5WHS', $context['warehouse'])->where('A5MACPKR', $context['delivery']);
if ($context['co_line'] !== '') {
$query->where('A5PKLN', $context['co_line']);
}
return $query->first();
}
private function transferJson(array $c, bool $useBradLine): string
{
return (string) json_encode([
'DIVI' => $c['company'], 'WHS' => $c['warehouse'], 'PART' => $c['part'], 'LOCATIONFROM' => $c['from'],
'LOCATIONTO' => $c['to'], 'QUANTITY' => $c['quantity'], 'ORDERCATEGORY' => $c['order_category'], 'LOTNUMBER' => $c['lot'],
'CONUMBER' => $c['co_number'], 'COLINENO' => $useBradLine ? $c['brad_line'] : $c['co_line'], 'DELIVERYNO' => $c['delivery'],
'PICKLISTSUFFIX' => $c['pick_suffix'], 'ENDFLAG' => $c['end_flag'], 'TYPE' => $c['type'], 'COLINESUFFIX' => $c['co_line_suffix'],
], JSON_UNESCAPED_SLASHES);
}
private function processShipmentUpdate(array &$context)
{
$shipment = $context['params']['A4SPR15A01'] ?? '';
$pickers = $this->shipmentPickers($context, $shipment);
$invalid = $this->shipmentValidationResponse($context, $shipment, $pickers);
if ($invalid !== null) {
return $invalid;
}
$context['type'] = 'UpdateShipmentNumber';
$context['json'] = (string) json_encode([
'DIVI' => $context['company'], 'WHS' => $context['warehouse'],
'ShipmentNumber' => ['Number' => $shipment, 'DeliveryNumbers' => $pickers],
'TYPE' => $context['type'], 'ENDFLAG' => '',
], JSON_UNESCAPED_SLASHES);
$this->log($context, $context['json'], '', 'M3-Req-out');
$token = $this->token($context);
if (! is_string($token)) {
return $token;
}
return $this->shipmentResult($context, $this->callM3($token, $context['json']));
}
private function shipmentValidationResponse(array $context, string $shipment, array $pickers)
{
if ($context['params'] === []) {
$this->log($context, self::NO_URL_PARAMETERS, 'X', 'M3-Res-out');
return $this->errorResponse('405', self::NO_URL_PARAMETERS);
}
if ($shipment === '' || $pickers === []) {
return $this->errorResponse('405', 'Json Payload Not Valid, missing Parameter or no Macpicker # found.');
}
return null;
}
private function shipmentPickers(array $context, string $shipment): array
{
if ($shipment === '') {
return [];
}
$query = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')->select('A4MACPKR')
->where('A4CO', $context['company'])->where('A4WHS', $context['warehouse'])->where('A4PLANT', $context['plant'])
->where('A4SPR15A01', $shipment);
if ($context['delivery'] !== '') {
$query->where('A4MACPKR', $context['delivery']);
}
return $query->get()->pluck('A4MACPKR')->all();
}
private function token(array $context)
{
$response = Helper::checkReceiveTokenV2();
$data = $response->getData();
if ((string) ($data->code ?? '') === '200') {
return (string) ($data->token ?? '');
}
$this->log($context, (string) ($data->message ?? ''), '', 'M3-Res-out');
return $response;
}
private function callM3(string $token, string $json): array
{
$response = Http::withHeaders(['accept' => self::JSON_CONTENT_TYPE, 'Content-Type' => self::JSON_CONTENT_TYPE])
->withToken($token)->withBody($json, self::JSON_CONTENT_TYPE)->timeout(Config::get('arrays.curlTimeOut'))
->post(env('ENDPOINTURL') . 'APIFLOWS/Sentinel/TransferAPI?ionapiRespStyle=sync');
$status = $response->getStatusCode();
$jsonResponse = '';
$errors = 'Error: No Response from M3';
if ((string) $status === '200') {
$contents = json_decode($response->getBody()->getContents());
$jsonResponse = json_encode($contents);
$errors = Helper::checkM3Errors($contents->result ?? null);
}
return ['status' => $status, 'json' => $jsonResponse, 'errors' => $errors, 'metrics' => $this->responseMetrics($response)];
}
private function transferResult(array $c, array $result)
{
$prefix = $result['status'] . 'TYPE :' . $c['type'] . self::ORDER_CATEGORY_LABEL . $c['order_category'] . '-';
if ($result['errors'] !== '') {
$this->log($c, $result['status'] . '-' . $result['errors'] . '-TYPE:' . $c['type'] . self::ORDER_CATEGORY_LABEL . $c['order_category'] . '-' . $result['json'] . 'M3-JSN-out' . $c['json_in'] . $result['metrics'], 'X', 'M3-Res-out');
return $this->errorResponse('206', $result['status'] . '-' . $result['errors'] . '- TYPE :' . $c['type'] . self::ORDER_CATEGORY_LABEL . $c['order_category'] . '-' . $result['json']);
}
$this->log($c, $prefix . $result['json'] . $result['metrics'], '', 'M3-Res-out');
$this->insertTransferAudit($c);
return response()->json(['code' => $result['status'], 'status' => 'Success']);
}
private function shipmentResult(array $c, array $result)
{
if ($result['errors'] !== '') {
$this->log($c, $result['status'] . '-' . $result['errors'] . '-TYPE:' . $c['type'] . '-' . $result['json'] . 'M3-JSN-out' . $c['json'] . $result['metrics'], 'X', 'M3-Res-out');
return $this->errorResponse('206', $result['status'] . '-' . $result['errors'] . '- TYPE :' . $c['type'] . '-' . $result['json']);
}
$this->log($c, $result['status'] . 'TYPE :' . $c['type'] . '-' . $result['json'] . $result['metrics'], '', 'M3-Res-out');
return response()->json(['code' => $result['status'], 'status' => 'Success']);
}
private function insertTransferAudit(array $c): void
{
DB::connection(config('m3.connection', 'pgsql'))->table('icm799m')->insert([
'IMDIV' => $c['company'], 'IMPLANT' => $c['plant'], 'IMWHS' => $c['warehouse'], 'IMSENTPKR' => $c['sent_picker'],
'IMDELNO' => $c['delivery'], 'IMORDER' => $c['co_number'], 'IMLINE' => $c['co_line'], 'IMBRADLN' => $c['brad_line'],
'IMSUFFIX' => $c['pick_suffix'], 'IMPART' => $c['part'], 'IMLOT' => $c['lot'], 'IMQTY' => $c['quantity'],
'IMFRLOC' => $c['from'], 'IMTOLOC' => $c['to'], 'IMDATE' => date('Ymd'), 'IMTIME' => date('His'),
]);
}
private function log(array $c, string $message, string $error, string $type): void
{
Helper::logToICM699M([
'SHMACPKR' => $c['delivery'] ?: self::ZERO_PAD, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'),
'SHERROR' => $error, 'SHINTBOX' => '', 'SHTYPE' => $type, 'SHDATA' => $message,
'SHUSER' => $c['user'], 'SHSOURCE' => $c['source'], 'SHCO' => $c['company'], 'SHWHS' => $c['warehouse'],
]);
}
private function exceptionResponse(array $context, \Throwable $throwable, string $json)
{
if (str_contains($throwable->getMessage(), 'cURL error')) {
$this->log($context, 'Curl Error: Communication with M3 Timed Out M3-JSN-out' . $json, 'X', 'M3-Res-out');
return response()->json(['code' => '405', 'status' => 'error', 'message' => 'Curl Error: Communication with M3 Timed Out.']);
}
$this->log($context, 'An Error Occured - ' . substr((string) $throwable, 0, 300), 'X', 'M3-Err-out');
Log::error($throwable);
return response()->json(['code' => '405', 'status' => 'error', 'message' => 'Uncaught Error']);
} }
// End UpdateShipmentNumber
} }
@@ -0,0 +1,42 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Config;
trait TransferApiSupport
{
private function configureTimeouts(): void
{
ini_set('max_execution_time', Config::get('arrays.PHPTimeOut'));
ini_set('max_input_time', Config::get('arrays.PHPTimeOut'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut'));
}
private function retryPayload(string $data): array
{
$decoded = json_decode($data, true);
return is_array($decoded) ? $decoded : [];
}
private function responseMetrics($response): string
{
return json_encode([
'status' => $response->status(),
'successful' => $response->successful(),
'failed' => $response->failed(),
'clientError' => $response->clientError(),
'serverError' => $response->serverError(),
]);
}
private function errorResponse(string $code, string $message)
{
return response()->json([
'code' => $code,
'status' => 'error',
'message' => $message,
]);
}
}
+24 -25
View File
@@ -20,7 +20,7 @@ class TwoShipAPIs extends Controller
public function TwoShipUpdateHold(Request $request) public function TwoShipUpdateHold(Request $request)
{ {
$this->configureTimeouts(); $this->configureTimeouts();
$user = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'; $user = m3User() !== '' ? Str::upper(m3User()) : 'WebUser';
$params = $request->all(); $params = $request->all();
$whs = (string) ($params['WHS'] ?? ''); $whs = (string) ($params['WHS'] ?? '');
$co = (string) ($params['COMPANY'] ?? ''); $co = (string) ($params['COMPANY'] ?? '');
@@ -28,33 +28,32 @@ class TwoShipAPIs extends Controller
if ($params === []) { if ($params === []) {
$this->log($macpkr ?: '000000', $co, $whs, $user, 'X', 'No URL Parameters Sent'); $this->log($macpkr ?: '000000', $co, $whs, $user, 'X', 'No URL Parameters Sent');
$result = response()->json(['code' => '405', 'status' => 'Error', 'message' => 'No URL Parameters Sent']);
} else {
try {
$this->logRequest($macpkr, $co, $whs, $user);
$orders = $this->aggregationOrders($macpkr, $co, $whs);
$response = $this->callTwoShip($orders);
$error = $this->responseError($response);
return response()->json(['code' => '405', 'status' => 'Error', 'message' => 'No URL Parameters Sent']); if ($error !== '') {
} $this->log($macpkr, $co, $whs, $user, 'X', $response->status() . '-' . $error);
$result = response()->json(['code' => '405', 'status' => 'error', 'message' => 'Status: ' . $response->status() . '-' . $error]);
try { } else {
$this->logRequest($macpkr, $co, $whs, $user); $this->log($macpkr, $co, $whs, $user, '', $response->body());
$orders = $this->aggregationOrders($macpkr, $co, $whs); $this->markAggregationComplete($orders, $co, $whs);
$response = $this->callTwoShip($orders); $result = response()->json(['code' => '200', 'status' => 'Success']);
$error = $this->responseError($response); }
if ($error !== '') { } catch (\Throwable $th) {
$this->log($macpkr, $co, $whs, $user, 'X', $response->status() . '-' . $error); $message = str_contains($th->getMessage(), 'cURL error')
? 'Curl Error: Communication with 2Ship Timed Out.'
return response()->json(['code' => '405', 'status' => 'error', 'message' => 'Status: ' . $response->status() . '-' . $error]); : '2Ship communication failed.';
$this->log($macpkr, $co, $whs, $user, 'X', $message);
$result = response()->json(['code' => '405', 'status' => 'error', 'message' => $message]);
} }
$this->log($macpkr, $co, $whs, $user, '', $response->body());
$this->markAggregationComplete($orders, $co, $whs);
return response()->json(['code' => '200', 'status' => 'Success']);
} catch (\Throwable $th) {
$message = str_contains($th->getMessage(), 'cURL error')
? 'Curl Error: Communication with 2Ship Timed Out.'
: '2Ship communication failed.';
$this->log($macpkr, $co, $whs, $user, 'X', $message);
return response()->json(['code' => '405', 'status' => 'error', 'message' => $message]);
} }
return $result;
} }
private function configureTimeouts(): void private function configureTimeouts(): void
@@ -77,7 +77,7 @@ class BoxPackToPalletController extends Controller
$box = trim((string) ($request->R659BOX ?? '')); $box = trim((string) ($request->R659BOX ?? ''));
abort_if($box === '', 400, 'Box Not Found'); abort_if($box === '', 400, 'Box Not Found');
$user = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'; $user = m3User() !== '' ? Str::upper(m3User()) : 'WebUser';
$profile = self::getUserWhsCoPlant(); $profile = self::getUserWhsCoPlant();
$validation = self::validateScannedBox($pallet, $box, $profile); $validation = self::validateScannedBox($pallet, $box, $profile);
@@ -268,7 +268,7 @@ class BoxPackToPalletController extends Controller
// Get Logged User CO/WHS/PLANT // Get Logged User CO/WHS/PLANT
public static function getUserWhsCoPlant() public static function getUserWhsCoPlant()
{ {
$sHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : ''; $sHUSER = m3User() !== '' ? Str::upper(trim(m3User())) : '';
// Get users from ICM428M // Get users from ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') $iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL') ->select('R428US', 'R428CO', 'R428WH', 'R428PL')
@@ -25,7 +25,7 @@ class CarrierMaintController extends Controller
if ($schema->hasTable('icm428m')) { if ($schema->hasTable('icm428m')) {
$power_user = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') $power_user = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428PWRUSR', 'R428CO', 'R428PL', 'R428WH') ->select('R428PWRUSR', 'R428CO', 'R428PL', 'R428WH')
->where('R428US', strtoupper(m3_user()))->get()->all(); ->where('R428US', strtoupper(m3User()))->get()->all();
} }
return view('m3::liberty-theme.pages.carrierMaint', ['ICM160M' => $iCM160M, 'power_user' => $power_user]); return view('m3::liberty-theme.pages.carrierMaint', ['ICM160M' => $iCM160M, 'power_user' => $power_user]);
@@ -9,155 +9,156 @@ use Str;
class ExecSummaryController extends Controller class ExecSummaryController extends Controller
{ {
private const WAREHOUSE_IN_CLAUSE = "' and a5whs in (";
public static function index(Request $request) public static function index(Request $request)
{ {
$iC670M = collect(); if ($request->ajax()) {
$wHDDA = ''; return self::ajaxResponse($request);
$cOMPANY = '110';
$pLANT = '144';
$wHS = '322';
try {
// Ajax Call
if ($request->ajax()) {
$wHDDA = isset($request->WHDDA) ? $request->WHDDA : [];
$wHArr = [];
foreach ($wHDDA as $WHDDAV) {
$cO = substr($WHDDAV, 0, 3);
$wHArr[] = substr($WHDDAV, 6, 3);
}
$reportPeriod = isset($request->reportPeriod) ? $request->reportPeriod : ''; // monthly , quarter , year , daily
// Current
$checkSales = isset($request->checkSales) ? $request->checkSales : ''; // on
$checkWeight = isset($request->checkWeight) ? $request->checkWeight : ''; // on
$checkLines = isset($request->checkLines) ? $request->checkLines : ''; // on
if (! empty($wHArr) && $cO != '' && $reportPeriod != '') {
switch ($reportPeriod) {
case 'year':
// Set Dates based on reportPeriod type
$firstDay = isset($request->REPORTDATE) ? \DateTime::createFromFormat('Y-m', $request->REPORTDATE) : '';
$reportDate = $firstDay->modify('first day of January')->format('Ymd');
$lastDay = isset($request->TOREPORTDATE) ? \DateTime::createFromFormat('Y-m', trim($request->TOREPORTDATE)) : '';
$toreportDate = $lastDay->modify('last day of December')->format('Ymd');
// Build SQL
$selectContent = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDate and ayapr8N002<=$toreportDate )
) and a5co='" . $cO . "' and a5whs in(" . implode(',', $wHArr) . ')
)
select substr(ayapr8N002,1,4) as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,sum(a5lnwght*a5packed/a5rsvqty) as weight,sum(dollars) as dollars,a5whs as whs from tempfile
group by substr(ayapr8N002,1,4),a5whs order by 1 ';
break;
case 'quarter':
// Set Dates based on reportPeriod type
$firstDay = isset($request->REPORTDATE) ? \DateTime::createFromFormat('Y-m', $request->REPORTDATE) : '';
$reportDate = $firstDay->modify('first day of January')->format('Ymd');
$lastDay = isset($request->TOREPORTDATE) ? \DateTime::createFromFormat('Y-m', trim($request->TOREPORTDATE)) : '';
$toreportDate = $lastDay->modify('last day of December')->format('Ymd');
// Build SQL
$selectContent = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDate and ayapr8N002<=$toreportDate )
) and a5co='" . $cO . self::WAREHOUSE_IN_CLAUSE . implode(',', $wHArr) . ")
)
select substr(ayapr8N002,3,2) ||'-' || 'Q' || EXTRACT(QUARTER FROM TO_DATE(CAST(ayapr8n002 AS TEXT),'YYYYMMDD')) as years,count(*) as lines,
sum(a5rsvqty) as reserveqty, sum(a5packed) as packed,sum (a5lnwght*a5packed/a5rsvqty) as weight,sum(dollars) as dollars, a5whs as whs
from tempfile
group by substr(ayapr8N002,3,2) ||'-' || 'Q' || EXTRACT(QUARTER FROM TO_DATE(CAST(ayapr8n002 AS TEXT),'YYYYMMDD')),a5whs order by 1 ";
break;
case 'monthly':
// Set Dates based on reportPeriod type
$firstDay = isset($request->REPORTDATE) ? \DateTime::createFromFormat('Y-m', $request->REPORTDATE) : '';
$reportDate = $firstDay->modify('first day of this month')->format('Ymd');
$lastDay = isset($request->TOREPORTDATE) ? \DateTime::createFromFormat('Y-m', trim($request->TOREPORTDATE)) : '';
$toreportDate = $lastDay->modify('last day of this month')->format('Ymd');
// Build SQL
$selectContent = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDate and ayapr8N002<=$toreportDate )
) and a5co='" . $cO . self::WAREHOUSE_IN_CLAUSE . implode(',', $wHArr) . ")
)
select substr(ayapr8N002,1,4) || '-' || substr(ayapr8N002,5,2) as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,
sum(a5lnwght*a5packed/a5rsvqty) as weight,sum(dollars) as dollars, a5whs as whs
from tempfile
group by substr(ayapr8N002,1,4) || '-' || substr(ayapr8N002,5,2),a5whs order by 1 ";
break;
case 'custom':
// Set Dates based on reportPeriod type
$firstDay = isset($request->REPORTDATE) ? \DateTime::createFromFormat('Y-m', $request->REPORTDATE) : '';
$reportDate = $firstDay->modify('first day of this month')->format('Ymd');
$toreportDate = $firstDay->modify('last day of this month')->format('Ymd');
// Build SQL
$selectContent = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDate and ayapr8N002<=$toreportDate )
) and a5co='" . $cO . self::WAREHOUSE_IN_CLAUSE . implode(',', $wHArr) . ')
)
select substr(ayapr8N002,7,2) as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,
sum(a5lnwght*a5packed/a5rsvqty) as weight, sum(dollars) as dollars, a5whs as whs
from tempfile
group by substr(ayapr8N002,7,2),a5whs order by 1 ';
break;
default:
$selectContent = '';
break;
}
$execSummaryRes = DB::connection(config('m3.connection', 'pgsql'))->select($selectContent);
} else {
$execSummaryRes = collect();
}
return response()->json(['success' => true, 'reportPeriod' => $reportPeriod, 'result' => $execSummaryRes, 'checkSales' => $checkSales,
'checkWeight' => $checkWeight, 'checkLines' => $checkLines]);
}
// Init Page
$sHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
if ($sHUSER != '') {
// Get User CO & WH from Table ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', $sHUSER)
->first();
$cOMPANY = isset($iCM428M->R428CO) ? $iCM428M->R428CO : '110'; // Company
$pLANT = isset($iCM428M->R428PL) ? $iCM428M->R428PL : '144'; // Plant
$wHS = isset($iCM428M->R428WH) ? $iCM428M->R428WH : '322'; // WareHouse
}
$wHDDA = $cOMPANY . $pLANT . $wHS;
// dump($wHDDA);
// Warehouse Table
$iC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
} catch (\Throwable $th) {
Log::error($th);
} }
return view('m3::liberty-theme.pages.execSummary', ['IC670M' => $iC670M, 'WHDDA' => $wHDDA]); return self::pageResponse();
// ,'ICM688M2',,'COMPANY','PLANT','WHS','reportDate' }
private static function ajaxResponse(Request $request)
{
[$company, $warehouses] = self::warehouseSelection((array) $request->input('WHDDA', []));
$reportPeriod = (string) $request->input('reportPeriod', '');
$result = collect();
try {
if ($company !== '' && $warehouses !== [] && $reportPeriod !== '') {
$sql = self::summarySql($reportPeriod, $request, $company, $warehouses);
$result = $sql === '' ? collect() : DB::connection(config('m3.connection', 'pgsql'))->select($sql);
}
} catch (\Throwable $throwable) {
Log::error($throwable);
}
return response()->json([
'success' => true,
'reportPeriod' => $reportPeriod,
'result' => $result,
'checkSales' => $request->input('checkSales', ''),
'checkWeight' => $request->input('checkWeight', ''),
'checkLines' => $request->input('checkLines', ''),
]);
}
private static function pageResponse()
{
$warehouses = collect();
$company = '110';
$plant = '144';
$warehouse = '322';
try {
$profile = self::currentUserProfile();
if ($profile !== null) {
$company = $profile->R428CO ?? $company;
$plant = $profile->R428PL ?? $plant;
$warehouse = $profile->R428WH ?? $warehouse;
}
$warehouses = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
} catch (\Throwable $throwable) {
Log::error($throwable);
}
return view('m3::liberty-theme.pages.execSummary', [
'IC670M' => $warehouses,
'WHDDA' => $company . $plant . $warehouse,
]);
}
private static function currentUserProfile(): ?object
{
$user = m3User() !== '' ? Str::upper(trim(m3User())) : '';
if ($user === '') {
return null;
}
return DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', $user)
->first();
}
private static function warehouseSelection(array $selection): array
{
$company = '';
$warehouses = [];
foreach ($selection as $value) {
$company = substr((string) $value, 0, 3);
$warehouses[] = substr((string) $value, 6, 3);
}
return [$company, $warehouses];
}
private static function summarySql(string $period, Request $request, string $company, array $warehouses): string
{
$range = self::dateRange($period, $request);
if ($range === null) {
return '';
}
[$from, $to] = $range;
$warehouseList = implode(',', $warehouses);
$groupExpression = match ($period) {
'year' => 'substr(ayapr8N002,1,4)',
'quarter' => "substr(ayapr8N002,3,2) ||'-' || 'Q' || EXTRACT(QUARTER FROM TO_DATE(CAST(ayapr8n002 AS TEXT),'YYYYMMDD'))",
'monthly' => "substr(ayapr8N002,1,4) || '-' || substr(ayapr8N002,5,2)",
'custom' => 'substr(ayapr8N002,7,2)',
default => '',
};
if ($groupExpression === '') {
return '';
}
return "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only) as ayapr8N002
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1
where AYSTATUS in ('Z','z') and ayapr8N002>=$from and ayapr8N002<=$to
) and a5co='$company' and a5whs in($warehouseList)
)
select $groupExpression as years,count(*) as lines,sum(a5rsvqty) as reserveqty,
sum(a5packed) as packed,sum(a5lnwght*a5packed/a5rsvqty) as weight,
sum(dollars) as dollars,a5whs as whs
from tempfile group by $groupExpression,a5whs order by 1";
}
private static function dateRange(string $period, Request $request): ?array
{
$from = self::monthDate($request->input('REPORTDATE'));
if ($from === null) {
return null;
}
$range = null;
if ($period === 'custom') {
$range = [(clone $from)->modify('first day of this month')->format('Ymd'), (clone $from)->modify('last day of this month')->format('Ymd')];
} else {
$to = self::monthDate($request->input('TOREPORTDATE'));
if ($to !== null) {
$range = match ($period) {
'year', 'quarter' => [(clone $from)->modify('first day of January')->format('Ymd'), (clone $to)->modify('last day of December')->format('Ymd')],
'monthly' => [(clone $from)->modify('first day of this month')->format('Ymd'), (clone $to)->modify('last day of this month')->format('Ymd')],
default => null,
};
}
}
return $range;
}
private static function monthDate(mixed $value): ?\DateTime
{
if (! is_string($value) || trim($value) === '') {
return null;
}
$date = \DateTime::createFromFormat('Y-m', trim($value));
return $date instanceof \DateTime ? $date : null;
} }
} }
@@ -9,7 +9,6 @@ use Str;
class ExecSummaryLineController extends Controller class ExecSummaryLineController extends Controller
{ {
private const WAREHOUSE_IN_CLAUSE = "' and a5whs in (";
private const FIRST_DAY_THIS_MONTH = 'first day of this month'; private const FIRST_DAY_THIS_MONTH = 'first day of this month';
@@ -19,186 +18,158 @@ class ExecSummaryLineController extends Controller
public static function index(Request $request) public static function index(Request $request)
{ {
try { if ($request->ajax()) {
// Ajax Call return self::ajaxResponse($request);
if ($request->ajax()) {
$wHDDA = isset($request->WHDDA) ? $request->WHDDA : [];
$wHArr = [];
foreach ($wHDDA as $WHDDAV) {
$cO = substr($WHDDAV, 0, 3);
$wHArr[] = substr($WHDDAV, 6, 3);
}
$reportPeriod = isset($request->reportPeriod) ? $request->reportPeriod : ''; // monthly , quarter , year , daily
// Current
$checkSales = isset($request->checkSales) ? $request->checkSales : ''; // on
$checkWeight = isset($request->checkWeight) ? $request->checkWeight : ''; // on
$checkLines = isset($request->checkLines) ? $request->checkLines : ''; // on
if (! empty($wHArr) && $cO != '' && $reportPeriod != '') {
switch ($reportPeriod) {
case 'year':
// Set Dates based on reportPeriod type
$firstDay = isset($request->REPORTDATE) ? \DateTime::createFromFormat('Y-m', $request->REPORTDATE) : '';
$reportDate = $firstDay->modify('first day of January')->format('Ymd');
$lastDay = isset($request->TOREPORTDATE) ? \DateTime::createFromFormat('Y-m', trim($request->TOREPORTDATE)) : '';
$toreportDate = $lastDay->modify('last day of December')->format('Ymd');
// Build SQL
$selectContent = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDate and ayapr8N002<=$toreportDate )
) and a5co='" . $cO . "' and a5whs in(" . implode(',', $wHArr) . ')
)
select substr(ayapr8N002,1,4) as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,sum(a5lnwght*a5packed/a5rsvqty) as weight,sum(dollars) as dollars,a5whs as whs from tempfile
group by substr(ayapr8N002,1,4),a5whs order by 1 ';
$execSummaryResLY = [];
break;
case 'quarter':
// Set Dates based on reportPeriod type
$firstDay = isset($request->REPORTDATE) ? \DateTime::createFromFormat('Y-m', $request->REPORTDATE) : '';
$reportDate = $firstDay->modify('first day of January')->format('Ymd');
$lastDay = isset($request->TOREPORTDATE) ? \DateTime::createFromFormat('Y-m', trim($request->TOREPORTDATE)) : '';
$toreportDate = $lastDay->modify('last day of December')->format('Ymd');
// Build SQL
$selectContent = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDate and ayapr8N002<=$toreportDate )
) and a5co='" . $cO . self::WAREHOUSE_IN_CLAUSE . implode(',', $wHArr) . ")
)
select 'Q' || EXTRACT(QUARTER FROM TO_DATE(CAST(ayapr8n002 AS TEXT),'YYYYMMDD')) ||'-' || substr(ayapr8N002,3,2) as years,count(*) as lines,
sum(a5rsvqty) as reserveqty, sum(a5packed) as packed,sum (a5lnwght*a5packed/a5rsvqty) as weight,sum(dollars) as dollars, a5whs as whs
from tempfile
group by 'Q' || EXTRACT(QUARTER FROM TO_DATE(CAST(ayapr8n002 AS TEXT),'YYYYMMDD')) ||'-' || substr(ayapr8N002,3,2),a5whs order by 1 ";
$execSummaryResLY = [];
break;
case 'monthly':
// Set Dates based on reportPeriod type
$firstDay = isset($request->REPORTDATE) ? \DateTime::createFromFormat('Y-m', $request->REPORTDATE) : null;
$lastDay = (clone $firstDay);
$reportDate = $firstDay->modify('-11 months')->modify(self::FIRST_DAY_THIS_MONTH)->format('Ymd'); //
$toreportDate = $lastDay ? $lastDay->modify(self::LAST_DAY_THIS_MONTH)->format('Ymd') : '';
// Last Year Dates
// Same period, but for Last Year (LY)
$reportDateLY = (clone $firstDay)->modify(self::ONE_YEAR_AGO)->modify(self::FIRST_DAY_THIS_MONTH)->format('Ymd');
$toreportDateLY = (clone $lastDay)->modify(self::ONE_YEAR_AGO)->modify(self::LAST_DAY_THIS_MONTH)->format('Ymd');
// Build SQL
$selectContent = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDate and ayapr8N002<=$toreportDate )
) and a5co='" . $cO . self::WAREHOUSE_IN_CLAUSE . implode(',', $wHArr) . ")
)
select substr(ayapr8N002,1,4) || '-' || substr(ayapr8N002,5,2) as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,
sum(a5lnwght*a5packed/a5rsvqty) as weight,sum(dollars) as dollars, a5whs as whs
from tempfile
group by substr(ayapr8N002,1,4) || '-' || substr(ayapr8N002,5,2),a5whs order by 1 ";
$selectContentLY = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDateLY and ayapr8N002<=$toreportDateLY )
) and a5co='" . $cO . self::WAREHOUSE_IN_CLAUSE . implode(',', $wHArr) . ")
)
select substr(ayapr8N002,1,4) || '-' || substr(ayapr8N002,5,2) as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,
sum(a5lnwght*a5packed/a5rsvqty) as weight,sum(dollars) as dollars, a5whs as whs
from tempfile
group by substr(ayapr8N002,1,4) || '-' || substr(ayapr8N002,5,2),a5whs order by 1 ";
$execSummaryResLY = DB::connection(config('m3.connection', 'pgsql'))->select($selectContentLY);
break;
case 'custom':
// Set Dates based on reportPeriod type
$firstDay = isset($request->REPORTDATE) ? \DateTime::createFromFormat('Y-m', $request->REPORTDATE) : '';
$reportDate = $firstDay->modify(self::FIRST_DAY_THIS_MONTH)->format('Ymd');
$toreportDate = $firstDay->modify(self::LAST_DAY_THIS_MONTH)->format('Ymd');
// Last Year Dates
$reportDateLY = (clone $firstDay)->modify(self::ONE_YEAR_AGO)->modify(self::FIRST_DAY_THIS_MONTH)->format('Ymd');
$toreportDateLY = (clone $firstDay)->modify(self::ONE_YEAR_AGO)->modify(self::LAST_DAY_THIS_MONTH)->format('Ymd');
// Build SQL
$selectContent = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDate and ayapr8N002<=$toreportDate )
) and a5co='" . $cO . self::WAREHOUSE_IN_CLAUSE . implode(',', $wHArr) . ')
)
select substr(ayapr8N002,7,2) as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,
sum(a5lnwght*a5packed/a5rsvqty) as weight, sum(dollars) as dollars, a5whs as whs
from tempfile
group by substr(ayapr8N002,7,2),a5whs order by 1 ';
$selectContentLY = "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) as ayapr8N002 from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only)
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where (AYSTATUS in ('Z','z') and ayapr8N002>=$reportDateLY and ayapr8N002<=$toreportDateLY )
) and a5co='" . $cO . self::WAREHOUSE_IN_CLAUSE . implode(',', $wHArr) . ')
)
select substr(ayapr8N002,7,2) as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,
sum(a5lnwght*a5packed/a5rsvqty) as weight, sum(dollars) as dollars, a5whs as whs
from tempfile
group by substr(ayapr8N002,7,2),a5whs order by 1 ';
$execSummaryResLY = DB::connection(config('m3.connection', 'pgsql'))->select($selectContentLY);
break;
default:
$selectContent = '';
break;
}
$execSummaryRes = DB::connection(config('m3.connection', 'pgsql'))->select($selectContent);
} else {
$execSummaryRes = collect();
$execSummaryResLY = collect();
}
return response()->json(['success' => true, 'reportPeriod' => $reportPeriod, 'result' => $execSummaryRes, 'resultLY' => $execSummaryResLY,
'checkSales' => $checkSales, 'checkWeight' => $checkWeight, 'checkLines' => $checkLines]);
}
// Init Page
$sHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
if ($sHUSER != '') {
// Get User CO & WH from Table ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', $sHUSER)
->first();
$cOMPANY = isset($iCM428M->R428CO) ? $iCM428M->R428CO : '110'; // Company
$pLANT = isset($iCM428M->R428PL) ? $iCM428M->R428PL : '144'; // Plant
$wHS = isset($iCM428M->R428WH) ? $iCM428M->R428WH : '322'; // WareHouse
}
$wHDDA = $cOMPANY . $pLANT . $wHS;
// dump($wHDDA);
// Warehouse Table
$iC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
} catch (\Throwable $th) {
Log::error($th);
} }
return view('m3::liberty-theme.pages.execSummaryLine', ['IC670M' => $iC670M, 'WHDDA' => $wHDDA]); return self::pageResponse();
// ,'ICM688M2',,'COMPANY','PLANT','WHS','reportDate' }
private static function ajaxResponse(Request $request)
{
[$company, $warehouses] = self::warehouseSelection((array) $request->input('WHDDA', []));
$period = (string) $request->input('reportPeriod', '');
$result = collect();
$lastYear = collect();
try {
if ($company !== '' && $warehouses !== [] && $period !== '') {
[$sql, $lastYearSql] = self::periodSql($period, $request, $company, $warehouses);
$result = $sql === '' ? collect() : DB::connection(config('m3.connection', 'pgsql'))->select($sql);
$lastYear = $lastYearSql === '' ? [] : DB::connection(config('m3.connection', 'pgsql'))->select($lastYearSql);
}
} catch (\Throwable $throwable) {
Log::error($throwable);
}
return response()->json([
'success' => true, 'reportPeriod' => $period, 'result' => $result, 'resultLY' => $lastYear,
'checkSales' => $request->input('checkSales', ''), 'checkWeight' => $request->input('checkWeight', ''),
'checkLines' => $request->input('checkLines', ''),
]);
}
private static function pageResponse()
{
$company = '110';
$plant = '144';
$warehouse = '322';
$warehouses = collect();
try {
$user = m3User() !== '' ? Str::upper(trim(m3User())) : '';
if ($user !== '') {
$profile = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')->where('R428US', $user)->first();
$company = $profile->R428CO ?? $company;
$plant = $profile->R428PL ?? $plant;
$warehouse = $profile->R428WH ?? $warehouse;
}
$warehouses = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
} catch (\Throwable $throwable) {
Log::error($throwable);
}
return view('m3::liberty-theme.pages.execSummaryLine', ['IC670M' => $warehouses, 'WHDDA' => $company . $plant . $warehouse]);
}
private static function warehouseSelection(array $selection): array
{
$company = '';
$warehouses = [];
foreach ($selection as $value) {
$company = substr((string) $value, 0, 3);
$warehouses[] = substr((string) $value, 6, 3);
}
return [$company, $warehouses];
}
private static function periodSql(string $period, Request $request, string $company, array $warehouses): array
{
return match ($period) {
'year' => self::yearSql($request, $company, $warehouses),
'quarter' => self::quarterSql($request, $company, $warehouses),
'monthly' => self::monthlySql($request, $company, $warehouses),
'custom' => self::customSql($request, $company, $warehouses),
default => ['', ''],
};
}
private static function yearSql(Request $request, string $company, array $warehouses): array
{
$range = self::range($request, 'first day of January', 'last day of December');
return $range === null ? ['', ''] : [self::summarySql($range[0], $range[1], $company, $warehouses, 'substr(ayapr8N002,1,4)'), ''];
}
private static function quarterSql(Request $request, string $company, array $warehouses): array
{
$range = self::range($request, 'first day of January', 'last day of December');
$group = "'Q' || EXTRACT(QUARTER FROM TO_DATE(CAST(ayapr8n002 AS TEXT),'YYYYMMDD')) ||'-' || substr(ayapr8N002,3,2)";
return $range === null ? ['', ''] : [self::summarySql($range[0], $range[1], $company, $warehouses, $group), ''];
}
private static function monthlySql(Request $request, string $company, array $warehouses): array
{
$date = self::monthDate($request->input('REPORTDATE'));
if ($date === null) {
return ['', ''];
}
$end = clone $date;
$from = (clone $date)->modify('-11 months')->modify(self::FIRST_DAY_THIS_MONTH)->format('Ymd');
$to = $end->modify(self::LAST_DAY_THIS_MONTH)->format('Ymd');
$lastFrom = (clone $date)->modify(self::ONE_YEAR_AGO)->modify('-11 months')->modify(self::FIRST_DAY_THIS_MONTH)->format('Ymd');
$lastTo = (clone $date)->modify(self::ONE_YEAR_AGO)->modify(self::LAST_DAY_THIS_MONTH)->format('Ymd');
$group = "substr(ayapr8N002,1,4) || '-' || substr(ayapr8N002,5,2)";
return [self::summarySql($from, $to, $company, $warehouses, $group), self::summarySql($lastFrom, $lastTo, $company, $warehouses, $group)];
}
private static function customSql(Request $request, string $company, array $warehouses): array
{
$date = self::monthDate($request->input('REPORTDATE'));
if ($date === null) {
return ['', ''];
}
$from = (clone $date)->modify(self::FIRST_DAY_THIS_MONTH)->format('Ymd');
$to = (clone $date)->modify(self::LAST_DAY_THIS_MONTH)->format('Ymd');
$lastFrom = (clone $date)->modify(self::ONE_YEAR_AGO)->modify(self::FIRST_DAY_THIS_MONTH)->format('Ymd');
$lastTo = (clone $date)->modify(self::ONE_YEAR_AGO)->modify(self::LAST_DAY_THIS_MONTH)->format('Ymd');
$group = 'substr(ayapr8N002,7,2)';
return [self::summarySql($from, $to, $company, $warehouses, $group), self::summarySql($lastFrom, $lastTo, $company, $warehouses, $group)];
}
private static function range(Request $request, string $fromModifier, string $toModifier): ?array
{
$from = self::monthDate($request->input('REPORTDATE'));
$to = self::monthDate($request->input('TOREPORTDATE'));
if ($from === null || $to === null) {
return null;
}
return [(clone $from)->modify($fromModifier)->format('Ymd'), (clone $to)->modify($toModifier)->format('Ymd')];
}
private static function monthDate(mixed $value): ?\DateTime
{
if (! is_string($value) || trim($value) === '') {
return null;
}
$date = \DateTime::createFromFormat('Y-m', trim($value));
return $date instanceof \DateTime ? $date : null;
}
private static function summarySql(string $from, string $to, string $company, array $warehouses, string $group): string
{
$warehouseList = implode(',', $warehouses);
return "with tempfile as (
select a5rsvqty,a5packed,a5slunpr*a5packed as dollars,a5lnwght,a5whs,
(select max(ayapr8N002) from icm695m1 where a5macpkr=aymacpkr fetch first 1 rows only) as ayapr8N002
from icm688m2 where a5macpkr in (
select distinct aymacpkr from icm695m1 where AYSTATUS in ('Z','z') and ayapr8N002>=$from and ayapr8N002<=$to
) and a5co='$company' and a5whs in($warehouseList)
) select $group as years,count(*) as lines,sum(a5rsvqty) as reserveqty,sum(a5packed) as packed,
sum(a5lnwght*a5packed/a5rsvqty) as weight,sum(dollars) as dollars,a5whs as whs
from tempfile group by $group,a5whs order by 1";
} }
} }
@@ -27,7 +27,28 @@ class FillBOL2Controller extends Controller
// # $bOL = BOL::findOrFail($request); // # $bOL = BOL::findOrFail($request);
$bOL = $request; $bOL = $request;
$data = [ $data = $this->bolData($request, $bOL);
try {
$filledPdf = $this->bolService->fillBillOfLading($data);
return response()->download($filledPdf)->deleteFileAfterSend(true);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
}
private function bolData(Request $request, Request $bOL): array
{
return array_merge(
$this->bolPrimaryData($request, $bOL),
$this->bolRemainingData()
);
}
private function bolPrimaryData(Request $request, Request $bOL): array
{
return [
'bol_number' => $request->input('BOL_number') ?? '1234567890', 'bol_number' => $request->input('BOL_number') ?? '1234567890',
'shipper_no' => $request->input('shipper_no') ?? '1234567890', 'shipper_no' => $request->input('shipper_no') ?? '1234567890',
'date' => now()->format('m/d/Y') ?? '01/01/2000', 'date' => now()->format('m/d/Y') ?? '01/01/2000',
@@ -125,7 +146,13 @@ Line 5',
'LNITEMCLAS17' => '', 'LNITEMCLAS17' => '',
'TYPEANDESCPTN.17' => '', 'TYPEANDESCPTN.17' => '',
'PKGTYPECOUNT17' => '', 'PKGTYPECOUNT17' => '',
'LNITEMWT16' => '', ];
}
private function bolRemainingData(): array
{
return [
'LNITEMWT16' => '',
'CLASSORATE16' => '', 'CLASSORATE16' => '',
'LNITEMCLAS16' => '', 'LNITEMCLAS16' => '',
'TYPEANDESCPTN.16' => '', 'TYPEANDESCPTN.16' => '',
@@ -212,15 +239,7 @@ Line 5',
'BILLTOADDR' => '', 'BILLTOADDR' => '',
'NUMDROPS' => '', 'NUMDROPS' => '',
]; ];
try {
$filledPdf = $this->bolService->fillBillOfLading($data);
return response()->download($filledPdf)->deleteFileAfterSend(true);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
} }
} }
@@ -41,7 +41,30 @@ class FillBOLController extends Controller
dd($error); dd($error);
} }
$result = $pdf->fillForm([ $result = $pdf->fillForm(self::bolFormData($bOL))
->needAppearances() // ensures values are visible in viewer
->saveAs(Storage::disk('public')->path($newfile));
// Always check for errors
if ($result === false) {
var_dump('step 3');
$error = $pdf->getError();
dd($error);
} else {
return response()->file(Storage::disk('public')->path($newfile));
}
}
private static function bolFormData(Request $bOL): array
{
return array_merge(
self::bolPrimaryFields($bOL),
self::bolRemainingFields()
);
}
private static function bolPrimaryFields(Request $bOL): array
{
return [
'DTESHIPED.1' => date('d/m/Y'), 'DTESHIPED.1' => date('d/m/Y'),
'invoice_number' => $bOL['BOL_number'], 'invoice_number' => $bOL['BOL_number'],
'amount_due' => number_format($bOL['amount'], 2), 'amount_due' => number_format($bOL['amount'], 2),
@@ -110,7 +133,13 @@ class FillBOLController extends Controller
'LNITEMCLAS17' => '', 'LNITEMCLAS17' => '',
'TYPEANDESCPTN.17' => '', 'TYPEANDESCPTN.17' => '',
'PKGTYPECOUNT17' => '', 'PKGTYPECOUNT17' => '',
'LNITEMWT16' => '', ];
}
private static function bolRemainingFields(): array
{
return [
'LNITEMWT16' => '',
'CLASSORATE16' => '', 'CLASSORATE16' => '',
'LNITEMCLAS16' => '', 'LNITEMCLAS16' => '',
'TYPEANDESCPTN.16' => '', 'TYPEANDESCPTN.16' => '',
@@ -197,17 +226,7 @@ class FillBOLController extends Controller
'BILLTOADDR' => '', 'BILLTOADDR' => '',
'NUMDROPS' => '', 'NUMDROPS' => '',
])
->needAppearances() // ensures values are visible in viewer ];
->saveAs(Storage::disk('public')->path($newfile));
// Always check for errors
if ($result === false) {
var_dump('step 3');
$error = $pdf->getError();
dd($error);
} else {
return response()->file(Storage::disk('public')->path($newfile));
}
} }
} }
@@ -16,31 +16,31 @@ class M3CallCloseAPIController extends Controller
{ {
self::configureTimeouts(); self::configureTimeouts();
$params = self::validatedParams($request); $params = self::validatedParams($request);
$response = null;
if ($params === null) { if ($params === null) {
Log::channel('dailyCron')->error('Manifest CloseAPI: Manifest, company and warehouse must be defined.'); Log::channel('dailyCron')->error('Manifest CloseAPI: Manifest, company and warehouse must be defined.');
$response = self::errorResponse('Manifest CloseAPI: Manifest, company and warehouse must be defined.');
return self::errorResponse('Manifest CloseAPI: Manifest, company and warehouse must be defined.'); } else {
} try {
$records = self::findOpenManifestRecords($params);
try { if ($records->isEmpty()) {
$records = self::findOpenManifestRecords($params); Log::channel('dailyCron')->info('Manifest CloseAPI: No records found in ICM705M1.');
if ($records->isEmpty()) { $response = self::errorResponse('Manifest CloseAPI: No records found in ICM705M1.');
Log::channel('dailyCron')->info('Manifest CloseAPI: No records found in ICM705M1.'); } else {
$response = self::submitManifest($records->first(), $params['SHUSER']);
return self::errorResponse('Manifest CloseAPI: No records found in ICM705M1.'); }
} catch (\Throwable $exception) {
Log::error($exception);
$response = response()->json([
'code' => '500',
'status' => 'Error',
'message' => 'Manifest Closing failed unexpectedly.',
], 500);
} }
return self::submitManifest($records->first(), $params['SHUSER']);
} catch (\Throwable $exception) {
Log::error($exception);
return response()->json([
'code' => '500',
'status' => 'Error',
'message' => 'Manifest Closing failed unexpectedly.',
], 500);
} }
return $response;
} }
private static function configureTimeouts(): void private static function configureTimeouts(): void
@@ -12,10 +12,10 @@ class M3DeliveryController extends Controller
public static function index(Request $request) public static function index(Request $request)
{ {
$connection = config('m3.connection', 'pgsql'); $connection = config('m3.connection', 'pgsql');
$power_user = (object) ['R428PWRUSR' => 'N', 'R428US' => strtoupper(m3_user()), 'R428CO' => '', 'R428PL' => '', 'R428WH' => '']; $power_user = (object) ['R428PWRUSR' => 'N', 'R428US' => strtoupper(m3User()), 'R428CO' => '', 'R428PL' => '', 'R428WH' => ''];
if (Schema::connection($connection)->hasTable('icm428m')) { if (Schema::connection($connection)->hasTable('icm428m')) {
$power_user = DB::connection($connection)->table('icm428m')->select('R428PWRUSR', 'R428US', 'R428CO', 'R428PL', 'R428WH') $power_user = DB::connection($connection)->table('icm428m')->select('R428PWRUSR', 'R428US', 'R428CO', 'R428PL', 'R428WH')
->where('R428US', strtoupper(m3_user()))->first() ?? $power_user; ->where('R428US', strtoupper(m3User()))->first() ?? $power_user;
} }
$co = $power_user->R428CO; $co = $power_user->R428CO;
$plant = $power_user->R428PL; $plant = $power_user->R428PL;
@@ -73,7 +73,7 @@ class M3ErrorsDispController extends Controller
'STARTDATE' => trim((string) $request->input('STARTDATE', today()->subDays(1)->format('Y-m-d'))), 'STARTDATE' => trim((string) $request->input('STARTDATE', today()->subDays(1)->format('Y-m-d'))),
'ENDDATE' => trim((string) $request->input('ENDDATE', today()->format('Y-m-d'))), 'ENDDATE' => trim((string) $request->input('ENDDATE', today()->format('Y-m-d'))),
'SHOWERRORS' => trim((string) $request->input('SHOWERRORS', 'N')), 'SHOWERRORS' => trim((string) $request->input('SHOWERRORS', 'N')),
'SHUSER' => m3_user() !== '' ? Str::upper(trim(m3_user())) : '', 'SHUSER' => m3User() !== '' ? Str::upper(trim(m3User())) : '',
'SKIPDATES' => trim((string) $request->input('SKIPDATES', 'N')), 'SKIPDATES' => trim((string) $request->input('SKIPDATES', 'N')),
]; ];
} }
@@ -147,7 +147,7 @@ class M3ErrorsDispController extends Controller
$sHTIME = $request->SHTIME != null ? trim($request->SHTIME) : ''; $sHTIME = $request->SHTIME != null ? trim($request->SHTIME) : '';
$sHTYPE = $request->SHTYPE != null ? trim($request->SHTYPE) : ''; $sHTYPE = $request->SHTYPE != null ? trim($request->SHTYPE) : '';
$sHACKNOTE = $request->SHACKNOTE != null ? trim(substr($request->SHACKNOTE, 0, 99)) : ''; $sHACKNOTE = $request->SHACKNOTE != null ? trim(substr($request->SHACKNOTE, 0, 99)) : '';
$sHUSER = m3_user() !== '' ? Str::upper(m3_user()) : ''; $sHUSER = m3User() !== '' ? Str::upper(m3User()) : '';
$cLOSESIM = $request->CLOSESIM != null ? trim($request->CLOSESIM) : 'N'; $cLOSESIM = $request->CLOSESIM != null ? trim($request->CLOSESIM) : 'N';
if ($cLOSESIM == 'N') { if ($cLOSESIM == 'N') {
@@ -242,7 +242,7 @@ class M3ErrorsDispController extends Controller
'endDate' => trim((string) $request->input('ENDDATE', today()->format('Y-m-d'))), 'endDate' => trim((string) $request->input('ENDDATE', today()->format('Y-m-d'))),
'showErrors' => trim((string) $request->input('SHOWERRORS', 'N')), 'showErrors' => trim((string) $request->input('SHOWERRORS', 'N')),
'skipDates' => trim((string) $request->input('SKIPDATES', 'N')), 'skipDates' => trim((string) $request->input('SKIPDATES', 'N')),
'user' => Str::upper(trim(m3_user())), 'user' => Str::upper(trim(m3User())),
]; ];
} }
@@ -12,169 +12,161 @@ class M3GetPDFFromCacheController extends Controller
{ {
public static function index(Request $request) public static function index(Request $request)
{ {
// Parse PDF file and build necessary objects. $state = self::initialState();
$aggno = $aggmsg = '';
$iC670M = $pDFArray = $carrier = $trackingno = $iCM688M1 = [];
$aYMACPKR = $wHDDA = $cOMPANY = $pLANT = $wHS = '';
// PDF File PATH
$pdfFilePath = Config::get('arrays.PdfPath');
try { try {
// Initialise Vars self::loadUserContext($state);
$pDFArray = $trackingno = $carrier = $iCM688M1 = []; self::loadDocuments($request, $state);
} catch (\Throwable $throwable) {
$sHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : ''; report($throwable);
if ($sHUSER != '') {
// Get User CO & WH
// Get user from ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', $sHUSER)
->first();
$cOMPANY = isset($iCM428M->R428CO) ? $iCM428M->R428CO : ''; // Company
$pLANT = isset($iCM428M->R428PL) ? $iCM428M->R428PL : ''; // Plant
$wHS = isset($iCM428M->R428WH) ? $iCM428M->R428WH : ''; // WareHouse
}
// Warehouse Table
$iC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
// ->where('WDCO',$userCO)->where('WDPL',$userPL)->where('WDWHS',$userWH)
// Get URL PARAMS
$urlParams = count($request->all()) > 0 ? $request->all() : '';
// dump($urlParams);
$aYMACPKR = isset($urlParams['MACPKR']) ? trim($urlParams['MACPKR']) : ''; // Delivery note number
$dOCTYPE = isset($urlParams['DOCTYPE']) ? trim($urlParams['DOCTYPE']) : ''; // Document type (PackingList, CarrierLabel, ContentLabel)
$wHDDA = $cOMPANY . $pLANT . $wHS;
// Check Doctype fisrt
$docTypes = $dOCTYPE != '' ? [$dOCTYPE] : ['PackingList', 'ContentLabel', 'CarrierLabel', 'GSLabel']; // document type (PackingList, CarrierLabel, ContentLabel)
if ($urlParams != '') {
// dump($urlParams);
// document number (ie the internal box number, the pick list number)
// Key Combination : COMPANY WHS A4MACPKR DOCTYPE DOCNUM
// Document type (PackingList, CarrierLabel, ContentLabel)
// Get Aggregation Number if it exists
$iCM695M1Agg = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m1')
->select('AYTRUCK')
->where('AYCO', $cOMPANY)
->where('AYWHS', $wHS)
->where('AYMACPKR', $aYMACPKR)
->first();
$aggno = isset($iCM695M1Agg->AYTRUCK) ? trim($iCM695M1Agg->AYTRUCK) : '';
if ($aggno != '' && $aggno != '0') {
$aggmsg = "This delivery note is part of aggregation $aggno ";
$iCM688M1 = DB::connection(config('m3.connection', 'pgsql'))->select("select A4MACPKR from ICM688M1 where A4SHPAGGCD='$aggno'");
// 654 add 'AYSPR15A01', 'AYSPR30A01', 'AYTRUCK' carrier and tracking# and agg#
$iCM695M1 = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m1')
->select('AYCO', 'AYWHS', 'AYMACPKR', 'AYINTBOX', 'AYSPR15A01', 'AYSPR30A01', 'AYTRUCK')
->distinct()
->where('AYCO', $cOMPANY)
->where('AYWHS', $wHS)
// ->where('AYMACPKR',$aYMACPKR)
->where('AYTRUCK', $aggno)
->orderBy('AYMACPKR')
->get();
} else {
// 654 add 'AYSPR15A01', 'AYSPR30A01', 'AYTRUCK' carrier and tracking# and agg#
$iCM695M1 = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m1')
->select('AYCO', 'AYWHS', 'AYMACPKR', 'AYINTBOX', 'AYSPR15A01', 'AYSPR30A01', 'AYTRUCK')
->distinct()
->where('AYCO', $cOMPANY)
->where('AYWHS', $wHS)
->where('AYMACPKR', $aYMACPKR)
->orderBy('AYMACPKR')
->get();
}
if (count($iCM695M1) > 0) {
$tmpCacheArr = $carrier = $trackingno = [];
// Loop the Results from $ICM595M1TMP
foreach ($iCM695M1 as $ICM695M1R) {
$wHS = trim($ICM695M1R->AYWHS);
$cOMPANY = trim($ICM695M1R->AYCO);
$aYMACPKR = trim($ICM695M1R->AYMACPKR);
$dOCNUM = trim($ICM695M1R->AYINTBOX);
// 654
$carrier[] = trim($ICM695M1R->AYSPR15A01);
$trackingno[] = trim($ICM695M1R->AYSPR30A01);
$aggno = trim($ICM695M1R->AYTRUCK);
// Make Key and Test Validity
if ($aYMACPKR != '' && $cOMPANY != '' && $wHS != '') {
$contentLabel = '';
// Get Unique Keys
foreach ($docTypes as $value) {
// Check Parcel vs LTL
if ($value == 'ContentLabel') {
$dOCNUMT = $dOCNUM;
$contentLabel = ! is_numeric($dOCNUM) ? 'LTL' : '';
}
if ($value == 'CarrierLabel') {
$dOCNUMT = $dOCNUM;
// Keep Solon Logic to read LTL
$dOCNUMT2 = $contentLabel == 'LTL' ? $aYMACPKR : $dOCNUM;
if ($dOCNUMT2 != $dOCNUMT) {
$pDFKey2 = $value . '/' . $value . '_' . $aYMACPKR . '_' . $dOCNUMT2 . '_' . $cOMPANY . '_' . $wHS;
$tmpCacheArr[] = $pDFKey2;
}
}
if ($value == 'PackingList') {
$dOCNUMT = $aYMACPKR;
}
// Get GS1 Labels
if ($value == 'GSLabel') {
$pDFKey = $value . '/' . $value . '_' . $aYMACPKR . '_' . $cOMPANY . '_' . $wHS . '_' . $dOCNUM;
// $pDFKey = $value.'/'.$value.'_'.$aYMACPKR.'_'.$cOMPANY.'_'.$wHS;
// }
// else{
// $pDFKey = $value.'/'.$value.'_'.$aYMACPKR.'_'.$cOMPANY.'_'.$wHS.'_'.$dOCNUM;
// }
} else {
$pDFKey = $value . '/' . $value . '_' . $aYMACPKR . '_' . $dOCNUMT . '_' . $cOMPANY . '_' . $wHS;
}
$tmpCacheArr[] = $pDFKey;
}
}
}
// Get unique values from array $pDFArray
$tmpCacheArr = collect($tmpCacheArr)->unique();
foreach ($tmpCacheArr as $value) {
$pdfFileName = $pdfFilePath . $value . '.pdf';
if (File::exists($pdfFileName)) {
$pdfFile = File::get($pdfFileName);
// Transform content to base64
// $base64PDF = $pdfFile;
$base64PDF = base64_encode($pdfFile);
$pDFArray[] = $base64PDF;
}
}
} else {
// Nothing Found
}
}
} catch (\Throwable $th) {
report($th);
} }
return view('m3::liberty-theme.pages.M3GetPDF', ['IC670M' => $iC670M, 'PDFArray' => $pDFArray, 'AYMACPKR' => $aYMACPKR, 'WHDDA' => $wHDDA, 'aggmsg' => $aggmsg, 'carrier' => $carrier, 'trackingno' => $trackingno, 'ICM688M1' => $iCM688M1, 'COMPANY' => $cOMPANY, 'PLANT' => $pLANT, 'WHS' => $wHS]); return view('m3::liberty-theme.pages.M3GetPDF', self::viewData($state));
// compact('IC670M','ViewTitle') }
private static function initialState(): array
{
return [
'aggmsg' => '', 'IC670M' => collect(), 'PDFArray' => [], 'carrier' => [],
'trackingno' => [], 'ICM688M1' => [], 'AYMACPKR' => '', 'WHDDA' => '',
'COMPANY' => '', 'PLANT' => '', 'WHS' => '',
];
}
private static function loadUserContext(array &$state): void
{
$user = m3User() !== '' ? Str::upper(trim(m3User())) : '';
if ($user !== '') {
$profile = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')->where('R428US', $user)->first();
$state['COMPANY'] = $profile->R428CO ?? '';
$state['PLANT'] = $profile->R428PL ?? '';
$state['WHS'] = $profile->R428WH ?? '';
}
$state['WHDDA'] = $state['COMPANY'] . $state['PLANT'] . $state['WHS'];
$state['IC670M'] = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
}
private static function loadDocuments(Request $request, array &$state): void
{
$params = $request->all();
if ($params === []) {
return;
}
$state['AYMACPKR'] = trim((string) ($params['MACPKR'] ?? ''));
$documentType = trim((string) ($params['DOCTYPE'] ?? ''));
$documentTypes = $documentType !== '' ? [$documentType] : ['PackingList', 'ContentLabel', 'CarrierLabel', 'GSLabel'];
$aggregation = self::aggregationNumber($state);
$rows = self::documentRows($state, $aggregation);
if ($rows->isEmpty()) {
return;
}
$keys = self::cacheKeys($rows, $documentTypes, $state);
$state['PDFArray'] = self::readCachedPdfs($keys);
}
private static function aggregationNumber(array &$state): string
{
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m1')
->select('AYTRUCK')->where('AYCO', $state['COMPANY'])->where('AYWHS', $state['WHS'])
->where('AYMACPKR', $state['AYMACPKR'])->first();
$aggregation = trim((string) ($row->AYTRUCK ?? ''));
if ($aggregation !== '' && $aggregation !== '0') {
$state['aggmsg'] = "This delivery note is part of aggregation $aggregation ";
$state['ICM688M1'] = DB::connection(config('m3.connection', 'pgsql'))
->table('icm688m1')->select('A4MACPKR')->where('A4SHPAGGCD', $aggregation)->get()->all();
}
return $aggregation;
}
private static function documentRows(array $state, string $aggregation)
{
$query = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m1')
->select('AYCO', 'AYWHS', 'AYMACPKR', 'AYINTBOX', 'AYSPR15A01', 'AYSPR30A01', 'AYTRUCK')
->distinct()->where('AYCO', $state['COMPANY'])->where('AYWHS', $state['WHS']);
$aggregation !== '' && $aggregation !== '0'
? $query->where('AYTRUCK', $aggregation)
: $query->where('AYMACPKR', $state['AYMACPKR']);
return $query->orderBy('AYMACPKR')->get();
}
private static function cacheKeys(iterable $rows, array $documentTypes, array &$state): array
{
$keys = [];
foreach ($rows as $row) {
self::appendRowKeys($keys, $row, $documentTypes, $state);
}
return array_values(array_unique($keys));
}
private static function appendRowKeys(array &$keys, object $row, array $documentTypes, array &$state): void
{
$warehouse = trim((string) $row->AYWHS);
$company = trim((string) $row->AYCO);
$picker = trim((string) $row->AYMACPKR);
$documentNumber = trim((string) $row->AYINTBOX);
$state['WHS'] = $warehouse;
$state['COMPANY'] = $company;
$state['AYMACPKR'] = $picker;
$state['carrier'][] = trim((string) $row->AYSPR15A01);
$state['trackingno'][] = trim((string) $row->AYSPR30A01);
if ($picker === '' || $company === '' || $warehouse === '') {
return;
}
$contentLabel = '';
foreach ($documentTypes as $type) {
[$key, $extraKey, $contentLabel] = self::documentKey($type, $picker, $documentNumber, $company, $warehouse, $contentLabel);
if ($extraKey !== null) {
$keys[] = $extraKey;
}
$keys[] = $key;
}
}
private static function documentKey(string $type, string $picker, string $number, string $company, string $warehouse, string $contentLabel): array
{
$documentNumber = $number;
$extraKey = null;
if ($type === 'ContentLabel') {
$contentLabel = is_numeric($number) ? '' : 'LTL';
} elseif ($type === 'PackingList') {
$documentNumber = $picker;
} elseif ($type === 'CarrierLabel' && $contentLabel === 'LTL') {
$extraKey = "$type/{$type}_{$picker}_{$picker}_{$company}_{$warehouse}";
}
$key = $type === 'GSLabel'
? "$type/{$type}_{$picker}_{$company}_{$warehouse}_{$number}"
: "$type/{$type}_{$picker}_{$documentNumber}_{$company}_{$warehouse}";
return [$key, $extraKey, $contentLabel];
}
private static function readCachedPdfs(array $keys): array
{
$result = [];
$path = (string) Config::get('arrays.PdfPath');
foreach ($keys as $key) {
$fileName = $path . $key . '.pdf';
if (File::exists($fileName)) {
$result[] = base64_encode(File::get($fileName));
}
}
return $result;
}
private static function viewData(array $state): array
{
return $state;
} }
} }
@@ -16,7 +16,7 @@ class MFOrdersController extends Controller
// Get Users from ICM428M // Get Users from ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') $iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL') ->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', strtoupper(m3_user())) ->where('R428US', '=', strtoupper(m3User()))
->first(); ->first();
$co = $iCM428M->R428CO; $co = $iCM428M->R428CO;
$whs = $iCM428M->R428WH; $whs = $iCM428M->R428WH;
@@ -18,6 +18,10 @@ class OrderLinesOfClosedOrder extends Controller
public static function index(Request $request) public static function index(Request $request)
{ {
$iC670M = $iCM688M2 = []; $iC670M = $iCM688M2 = [];
$wHDDA = '';
$cOMPANY = '110';
$pLANT = '144';
$wHS = '322';
$rEPORTDATEDisp = today()->format('Ymd'); $rEPORTDATEDisp = today()->format('Ymd');
$allReportsUrls = []; $allReportsUrls = [];
try { try {
@@ -283,7 +287,7 @@ class OrderLinesOfClosedOrder extends Controller
if (Storage::disk('publicCL')->exists($fileName)) { if (Storage::disk('publicCL')->exists($fileName)) {
// File exists, retrieve content or URL // File exists, retrieve content or URL
// $content = Storage::disk('publicCL')->get($fileName); // $content = Storage::disk('publicCL')->get($fileName);
$fileNameOnly = strpos($fileName, '/') != false ? substr($fileName, strpos($fileName, '/') + 1) : $fileName; $fileNameOnly = strpos($fileName, '/') !== false ? substr($fileName, strpos($fileName, '/') + 1) : $fileName;
// Download the file // Download the file
return Storage::disk('publicCL')->download($fileName, $fileNameOnly); return Storage::disk('publicCL')->download($fileName, $fileNameOnly);
@@ -13,11 +13,11 @@ class PalletQtyController extends Controller
public static function index(Request $request) public static function index(Request $request)
{ {
$connection = config('m3.connection', 'pgsql'); $connection = config('m3.connection', 'pgsql');
$power_user = (object) ['R428PWRUSR' => 'N', 'R428US' => strtoupper(m3_user()), 'R428CO' => '', 'R428PL' => '', 'R428WH' => '']; $power_user = (object) ['R428PWRUSR' => 'N', 'R428US' => strtoupper(m3User()), 'R428CO' => '', 'R428PL' => '', 'R428WH' => ''];
if (Schema::connection($connection)->hasTable('icm428m')) { if (Schema::connection($connection)->hasTable('icm428m')) {
$power_user = DB::connection($connection)->table('icm428m') $power_user = DB::connection($connection)->table('icm428m')
->select('R428PWRUSR', 'R428US', 'R428CO', 'R428PL', 'R428WH') ->select('R428PWRUSR', 'R428US', 'R428CO', 'R428PL', 'R428WH')
->where('R428US', '=', strtoupper(m3_user()))->first() ?? $power_user; ->where('R428US', '=', strtoupper(m3User()))->first() ?? $power_user;
} }
$co = $power_user->R428CO; $co = $power_user->R428CO;
$plant = $power_user->R428PL; $plant = $power_user->R428PL;
@@ -15,7 +15,7 @@ class PendingClosureController extends Controller
// Get Users from ICM428M // Get Users from ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') $iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL') ->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', strtoupper(m3_user())) ->where('R428US', '=', strtoupper(m3User()))
->first(); ->first();
$user = $iCM428M->R428US; $user = $iCM428M->R428US;
$co = $iCM428M->R428CO; $co = $iCM428M->R428CO;
@@ -19,7 +19,7 @@ class ReplenishSettingsController extends Controller
// $iC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get(); // $iC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
// Get Logged user Id // Get Logged user Id
$userId = m3_user() !== '' ? strtoupper(m3_user()) : ''; $userId = m3User() !== '' ? strtoupper(m3User()) : '';
// $userId = 'E1135818'; // Brad : E1135818 Malak : E2011812 ACOM_PDF // $userId = 'E1135818'; // Brad : E1135818 Malak : E2011812 ACOM_PDF
$connection = config('m3.connection', 'pgsql'); $connection = config('m3.connection', 'pgsql');
$iCM428M = Schema::connection($connection)->hasTable('icm428m') $iCM428M = Schema::connection($connection)->hasTable('icm428m')
@@ -140,7 +140,7 @@ class ReplenishSettingsController extends Controller
// $iC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get(); // $iC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
// Get Logged user Id // Get Logged user Id
$userId = m3_user() !== '' ? strtoupper(m3_user()) : ''; $userId = m3User() !== '' ? strtoupper(m3User()) : '';
// $userId = 'E1135818'; // Brad : E1135818 Malak : E2011812 ACOM_PDF // $userId = 'E1135818'; // Brad : E1135818 Malak : E2011812 ACOM_PDF
$connection = config('m3.connection', 'pgsql'); $connection = config('m3.connection', 'pgsql');
$iCM428M = Schema::connection($connection)->hasTable('icm428m') $iCM428M = Schema::connection($connection)->hasTable('icm428m')
@@ -17,7 +17,7 @@ class ShipToController extends Controller
return Datatables::of($data) return Datatables::of($data)
->addIndexColumn() ->addIndexColumn()
->addColumn('checkbox', function ($row) { ->addColumn('checkbox', function () {
return ''; return '';
}) })
->addColumn('action', function ($row) { ->addColumn('action', function ($row) {
@@ -11,15 +11,10 @@ use Modules\M3\Services\PostgresProgramBridge;
class UserControlController extends Controller class UserControlController extends Controller
{ {
private const AUTH_FAILURE_MESSAGE = 'Could not connect due to wrong user or password.';
private const DATABASE_NOT_FOUND_MESSAGE = 'Database not found. Try WRKRDBDIRE to check.';
private const CONNECTION_ERROR_PREFIX = 'Could not connect. Error: ';
private const PROGRAM_ERROR_PREFIX = 'Error calling program. Code: ';
private const MESSAGE_SEPARATOR = ' Msg: ';
public static function index(Request $request) public static function index(Request $request)
{ {
@@ -46,81 +41,80 @@ class UserControlController extends Controller
// //
public static function saveUserAdd(Request $request) public static function saveUserAdd(Request $request)
{ {
if ($request->R428EMPCDE == '') { if ($request->R428EMPCDE == '') {
$request->R428EMPCDE = 0; $request->R428EMPCDE = 0;
} }
// Insert record to ICM428M $connection = DB::connection(config('m3.connection', 'pgsql'));
// dump($request); $existing = $connection->select("select count(*) as COUNT1 from ICM428M where R428US='$request->R428US'");
// Validate user profile exists on the system if (($existing[0]->COUNT1 ?? 0) == '1') {
$usrprf = [(object) ['COUNT' => 0]]; // PostgreSQL/Core owns M3 profiles; no QSYS2 lookup.
// Validate employee code exists in LBPF file
// if($request->R428EMPCDE>0) {
// $empcde=DB::connection(config('m3.connection', 'pgsql'))->select("select count(*) as COUNT2 from LBPF where EMPNO='$request->R428EMPCDE'");
// if($empcde[0]->COUNT2=="0") {
// #echo "<script>alert('Employee code entered does not exist, please try again');</script>";
// return "error empcd";
// }
// }
$icm428m = DB::connection(config('m3.connection', 'pgsql'))->select("select count(*) as COUNT1 from ICM428M where R428US='$request->R428US'");
if ($icm428m[0]->COUNT1 == '1') {
return 'error exists'; return 'error exists';
} else {
if ($usrprf[0]->COUNT == '1') {
DB::connection(config('m3.connection', 'pgsql'))->insert("insert into ICM428M
(R428CO, R428PL, R428WH, R428STS, R428US, R428FIRST, R428FAMILY, R428EMAIL, R428EMPCDE, R428ADMUSR, R428PWRUSR, MENU2OPT, MENU3OPT, MENU4OPT, MENU5OPT, MENU6OPT, R42811A)
values ('$request->R428CO', '$request->R428PL', '$request->R428WH', '$request->R428STS', '$request->R428US', '$request->R428FIRST',
'$request->R428FAMILY', '$request->R428EMAIL', $request->R428EMPCDE, '$request->R428ADMUSR', '$request->R428PWRUSR',
'NN" . $request->MENU2OPT3 . 'NNN' . $request->MENU2OPT7 . $request->MENU2OPT8 . "N', '" . $request->MENU3OPT1 . $request->MENU3OPT2 . "NNNNNNN',
'" . $request->MENU4OPT1 . $request->MENU4OPT2 . $request->MENU4OPT3 . "NNNNNN',
'" . $request->MENU5OPT1 . $request->MENU5OPT2 . $request->MENU5OPT3 . 'N' . $request->MENU5OPT5 . "NNNN','" . $request->MENU6OPT1 . $request->MENU6OPT2 . $request->MENU6OPT3 . "','" . $request->R42811A . "')");
return 'success';
} else {
// CL program call here (ewobjtst.)ICMUSRPRF parms(R428US, R428FIRST, R428FAMILY)
// connect to toolkit using DB2 credentials (can also leave blank for default authority)
try {
$conn = app(PostgresProgramBridge::class);
} catch (\Throwable $e) {
// Determine reason for failure.
// Probably database authentication error or invalid or unreachable database.
return self::programBridgeConnectionError($e);
}
$conn->setOptions(['persistent' => false, 'stateless' => true, 'debug' => true, 'debugLogFile' => '/www/sentm3test/htdocs/M3/storage/logs/toolkit_debug.log']);
// Add LIB
// $result = $conn->CLCommand('ADDLIBLE LIB(MPDAT10)');
// define several input/output params
$params = [];
$params[] = $conn->AddParameterChar('in', 10, 'User profile', 'E#iUsrPrf', $request->R428US);
$params[] = $conn->AddParameterChar('in', 20, 'First name', 'FIRST', $request->R428FIRST);
$params[] = $conn->AddParameterChar('in', 30, 'Family name', 'FAMILY', $request->R428FAMILY);
// # $result = $conn->PgmCall(Config::get('eproconf.misc.PGM1'), Config::get('eproconf.misc.libname4'), $params, array(), array());
$result = $conn->PgmCall('ICMUSRPRF', 'EWOBJ', $params, [], []);
if (! $result) {
return 'error profile2';
} else {
// Successful Call : User profile created with default password
// dd($result);
// echo "<script>alert('User profile $request->R428US was created on the system with default password.');</script>";
DB::connection(config('m3.connection', 'pgsql'))->insert("insert into ICM428M
(R428CO, R428PL, R428WH, R428STS, R428US, R428FIRST, R428FAMILY, R428EMAIL, R428EMPCDE, R428ADMUSR, R428PWRUSR, MENU2OPT, MENU3OPT, MENU4OPT, MENU5OPT, MENU6OPT, R42811A,R42821A)
values ('$request->R428CO', '$request->R428PL', '$request->R428WH', '$request->R428STS', '$request->R428US', '$request->R428FIRST',
'$request->R428FAMILY', '$request->R428EMAIL', $request->R428EMPCDE, '$request->R428ADMUSR', '$request->R428PWRUSR',
'NN" . $request->MENU2OPT3 . 'NNN' . $request->MENU2OPT7 . $request->MENU2OPT8 . "N', '" . $request->MENU3OPT1 . $request->MENU3OPT2 . "NNNNNNN',
'" . $request->MENU4OPT1 . $request->MENU4OPT2 . $request->MENU4OPT3 . "NNNNNN',
'" . $request->MENU5OPT1 . $request->MENU5OPT2 . $request->MENU5OPT3 . 'N' . $request->MENU5OPT5 . "NNNN','" . $request->MENU6OPT1 . $request->MENU6OPT2 . $request->MENU6OPT3 . "','" . $request->R42811A . "','" . $request->R42821A . "')");
return 'success2';
}
// #kd return "error profile";
}
} }
// PostgreSQL/Core owns M3 profiles; no QSYS2 lookup is currently available.
$profileExists = false;
if ($profileExists) {
self::insertUser($request, false);
return 'success';
}
return self::createSystemProfileAndUser($request);
}
private static function createSystemProfileAndUser(Request $request): string
{
try {
$conn = app(PostgresProgramBridge::class);
} catch (\Throwable $e) {
return self::programBridgeConnectionError($e);
}
$conn->setOptions([
'persistent' => false,
'stateless' => true,
'debug' => true,
'debugLogFile' => '/www/sentm3test/htdocs/M3/storage/logs/toolkit_debug.log',
]);
$params = [
$conn->AddParameterChar('in', 10, 'User profile', 'E#iUsrPrf', $request->R428US),
$conn->AddParameterChar('in', 20, 'First name', 'FIRST', $request->R428FIRST),
$conn->AddParameterChar('in', 30, 'Family name', 'FAMILY', $request->R428FAMILY),
];
if (! $conn->PgmCall('ICMUSRPRF', 'EWOBJ', $params, [], [])) {
return 'error profile2';
}
self::insertUser($request, true);
return 'success2';
}
private static function insertUser(Request $request, bool $includeR42821A): void
{
$values = [
'R428CO' => $request->R428CO,
'R428PL' => $request->R428PL,
'R428WH' => $request->R428WH,
'R428STS' => $request->R428STS,
'R428US' => $request->R428US,
'R428FIRST' => $request->R428FIRST,
'R428FAMILY' => $request->R428FAMILY,
'R428EMAIL' => $request->R428EMAIL,
'R428EMPCDE' => $request->R428EMPCDE,
'R428ADMUSR' => $request->R428ADMUSR,
'R428PWRUSR' => $request->R428PWRUSR,
'MENU2OPT' => 'NN' . $request->MENU2OPT3 . 'NNN' . $request->MENU2OPT7 . $request->MENU2OPT8 . 'N',
'MENU3OPT' => $request->MENU3OPT1 . $request->MENU3OPT2 . 'NNNNNNN',
'MENU4OPT' => $request->MENU4OPT1 . $request->MENU4OPT2 . $request->MENU4OPT3 . 'NNNNNN',
'MENU5OPT' => $request->MENU5OPT1 . $request->MENU5OPT2 . $request->MENU5OPT3 . 'N' . $request->MENU5OPT5 . 'NNNN',
'MENU6OPT' => $request->MENU6OPT1 . $request->MENU6OPT2 . $request->MENU6OPT3,
'R42811A' => $request->R42811A,
];
if ($includeR42821A) {
$values['R42821A'] = $request->R42821A;
}
DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')->insert($values);
} }
// //
@@ -176,19 +170,7 @@ class UserControlController extends Controller
} catch (\Throwable $e) { } catch (\Throwable $e) {
// Determine reason for failure. // Determine reason for failure.
// Probably database authentication error or invalid or unreachable database. // Probably database authentication error or invalid or unreachable database.
$code = $e->getCode(); return self::programBridgeConnectionError($e);
$msg = $e->getMessage();
switch ($code) {
case 8001:
// "Authorization failure on distributed database connection attempt"
// Usually means a wrong DB2 user or password
break;
case 42705:
break;
default:
break;
}
} }
// Add LIB // Add LIB
@@ -213,7 +195,7 @@ class UserControlController extends Controller
$result = $conn->PgmCall('ICMCHGPRF', 'EWOBJ', $params, [], []); $result = $conn->PgmCall('ICMCHGPRF', 'EWOBJ', $params, [], []);
if (! $result) { if (! $result) {
echo "<script>alert($pGMERRORS);</script>"; echo "<script>alert(" . json_encode($conn->getErrorMsg()) . ");</script>";
return 'error'; return 'error';
} }
@@ -250,26 +232,14 @@ class UserControlController extends Controller
try { try {
$conn = app(PostgresProgramBridge::class); $conn = app(PostgresProgramBridge::class);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$code = $e->getCode(); return self::programBridgeConnectionError($e);
$msg = $e->getMessage();
switch ($code) {
case 8001:
// "Authorization failure on distributed database connection attempt"
// Usually means a wrong DB2 user or password
break;
case 42705:
break;
default:
break;
}
} }
$result = $conn->CLCommand('QSYS/CHGUSRPRF USRPRF(' . $request->R428US_PWD . ') PASSWORD(' . $request->NEW_PASSWORD . ') STATUS(*ENABLED) PWDEXP(*NO)'); $result = $conn->CLCommand('QSYS/CHGUSRPRF USRPRF(' . $request->R428US_PWD . ') PASSWORD(' . $request->NEW_PASSWORD . ') STATUS(*ENABLED) PWDEXP(*NO)');
if (! $result) { if (! $result) {
dump($pGMERRORS); dump($conn->getErrorMsg());
echo "<script>alert($pGMERRORS);</script>"; echo "<script>alert(" . json_encode($conn->getErrorMsg()) . ");</script>";
return 'error pwd'; return 'error pwd';
} }
+183 -260
View File
@@ -11,287 +11,210 @@ class UserPickingStats4 extends Controller
{ {
public static function index(Request $request) public static function index(Request $request)
{ {
$context = self::buildContext($request);
self::loadPickingData($context);
self::calculatePerformance($context);
// $start = microtime(true); return $request->format === 'json'
// dd($request); ? self::jsonResponse($context)
// Filter by date : self::pageResponse($request, $context);
if (isset($request->dates)) { }
$date = $request->dates;
$starting = $date;
} else {
$starting = date('Y-m-d', strtotime('-1 days'));
}
// #$min_work_hours_for_break = 4; private static function buildContext(Request $request): array
{
// #$ignore_picks_that_last_longer_than_this_many_hours = 8; $starting = isset($request->dates) ? (string) $request->dates : date('Y-m-d', strtotime('-1 days'));
$ignore_picks_that_last_longer_than_this_many_hours = implode(',', Config::get('arrays.ignore_picks_that_last_longer_than_this_many_hours'));
// $user_array = Config::get('arrays.user_array');
// 618
// Get Logged User CO & WH
// Get Users from ICM428M
$co = '';
$whs = '';
$iCM428M = [];
$schema = Schema::connection(config('m3.connection', 'pgsql')); $schema = Schema::connection(config('m3.connection', 'pgsql'));
if ($schema->hasTable('icm428m')) { $users = self::users($schema);
$profile = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') $user = isset($request->user) ? (string) $request->user : '';
->select('R428US', 'R428CO', 'R428WH') $empno = 0;
->where('R428US', '=', strtoupper(m3_user())) foreach ($users as $candidate) {
->first(); if ($candidate['userprf'] === $user) {
$co = $profile->R428CO ?? ''; $empno = $candidate['empno'];
$whs = $profile->R428WH ?? ''; break;
}
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->selectRaw("R428US as userprf, (trim(R428FIRST) || ' ' || trim(R428FAMILY)) as name, R428EMPCDE as empno")
->where('R428STS', 'A')->where('R428EMPCDE', '!=', 0)
->where('R428CO', $co)->where('R428WH', $whs)->get()->all();
} }
$user_array = []; return [
// while($row = odbc_fetch_object($result)) { 'starting' => $starting,
$x = 0; 'schema' => $schema,
foreach ($iCM428M as $key => $row) { 'users' => $users,
$user_array[$x]['userprf'] = trim($row->USERPRF); 'user' => $user,
$user_array[$x]['name'] = trim($row->NAME); 'empno' => $empno,
$user_array[$x]['empno'] = $row->EMPNO; 'targets' => Config::get('arrays_custom.lph_per_code_array', []),
$x++; 'statistics' => [],
} 'lbpfRows' => [],
array_multisort(array_column($user_array, 'name'), SORT_ASC, $user_array); 'lbpf' => 0,
'monitor' => [],
'totals' => ['value' => 0, 'weight' => 0, 'timediff' => 0, 'picked' => 0, 'entries' => 0],
'performance' => [],
];
}
// fill array of Lines per Hour per Monitor private static function users($schema): array
$lph_per_monitor_array = Config::get('arrays_custom.lph_per_code_array'); {
if (isset($request->user)) { if (! $schema->hasTable('icm428m')) {
$user = $request->user; return [];
$empno = 0;
foreach ($user_array as $key => $val) {
if ($user_array[$key]['userprf'] == $user) {
$empno = $user_array[$key]['empno'];
}
}
} else {
$empno = 0;
$user = '';
} }
// get data from LBPF for user and day $connection = DB::connection(config('m3.connection', 'pgsql'));
$payrollCodes = ''; $profile = $connection->table('icm428m')->select('R428CO', 'R428WH')
ksort($lph_per_monitor_array); ->where('R428US', strtoupper(m3User()))->first();
foreach ($lph_per_monitor_array as $key => $val) { $rows = $connection->table('icm428m')
$payrollCodes .= $key . ','; ->selectRaw("R428US as userprf, (trim(R428FIRST) || ' ' || trim(R428FAMILY)) as name, R428EMPCDE as empno")
->where('R428STS', 'A')->where('R428EMPCDE', '!=', 0)
->where('R428CO', $profile->R428CO ?? '')->where('R428WH', $profile->R428WH ?? '')->get();
$users = [];
foreach ($rows as $row) {
$users[] = ['userprf' => trim($row->USERPRF), 'name' => trim($row->NAME), 'empno' => $row->EMPNO];
} }
$payrollCodes = substr($payrollCodes, 0, -1); usort($users, static fn (array $a, array $b): int => $a['name'] <=> $b['name']);
$yr = substr($starting, 0, 4) - 2000; return $users;
$mth = substr($starting, 5, 2); }
$day = substr($starting, 8, 2);
$sqlstmt = "select oppno, sum(cast (hrs as integer)+ (hrs-cast (hrs as integer))/0.6) as hrs from LBPF
where empno=$empno and lbyr=$yr and lbmo=$mth and lbdy=$day
and oppno in ($payrollCodes)
group by oppno";
$lbpf1 = $schema->hasTable('lbpf') private static function loadPickingData(array &$context): void
? DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt) {
: []; $targets = $context['targets'];
ksort($targets);
$lbpf = 0; $codes = implode(',', array_keys($targets));
foreach ($lbpf1 as $key => $val) { $date = $context['starting'];
$lbpf += $val->HRS; $sql = sprintf(
'select oppno, sum(cast (hrs as integer)+ (hrs-cast (hrs as integer))/0.6) as hrs from LBPF where empno=%d and lbyr=%d and lbmo=%d and lbdy=%d and oppno in (%s) group by oppno',
(int) $context['empno'], (int) substr($date, 0, 4) - 2000, (int) substr($date, 5, 2), (int) substr($date, 8, 2), $codes
);
$context['lbpfRows'] = $context['schema']->hasTable('lbpf')
? DB::connection(config('m3.connection', 'pgsql'))->select($sql) : [];
foreach ($context['lbpfRows'] as $row) {
$context['lbpf'] += $row->HRS;
} }
$sql = "select * from perfstat where dt='$starting' and a5pickcomu='$user' "; if ((isset(request()->dates) || isset(request()->user)) && $context['schema']->hasTable('perfstat')) {
$context['statistics'] = DB::connection(config('m3.connection', 'pgsql'))->table('perfstat')
->where('dt', $date)->where('a5pickcomu', $context['user'])->get()->all();
}
}
if ((isset($request->dates) || isset($request->user)) && $schema->hasTable('perfstat')) { private static function calculatePerformance(array &$context): void
$statistic4 = DB::connection(config('m3.connection', 'pgsql'))->select($sql); {
} else { self::initializeMonitors($context);
$statistic4 = []; if ($context['statistics'] === []) {
return;
} }
// initialize arrays self::accumulateStatistics($context);
$monitor_array = $totals_array = $perf_array = []; $hours = $context['lbpf'];
$totals_array['value'] = 0; $context['performance']['tot_hours'] = $hours;
$totals_array['weight'] = 0; $context['performance']['per_hour_actual'] = $hours != 0 ? $context['totals']['entries'] / $hours : 0;
$totals_array['timediff'] = 0; self::calculateMonitorTargets($context);
$totals_array['picked'] = 0; $target = $context['monitor'][0]['lph_blended_per_activity'] ?? 0;
$totals_array['entries'] = 0; if ($target != 0) {
$diff = 0; $context['performance']['performance_activity'] = $context['performance']['per_hour_actual'] / $target;
foreach ($lbpf1 as $key => $val) {
$monitor_array[$val->OPPNO]['tot_hours'] = $val->HRS;
// Initialize these variables in case not existant in statistic4
$monitor_array[$val->OPPNO]['header'] = 'Mon' . $val->OPPNO;
$monitor_array[$val->OPPNO]['timediff'] = 0;
$monitor_array[$val->OPPNO]['percent_per_monitor'] = 0;
$monitor_array[$val->OPPNO]['percent_per_activity'] = 0;
$monitor_array[$val->OPPNO]['lph_blended_per_activity'] = 0;
$monitor_array[$val->OPPNO]['picked'] = 0;
$monitor_array[$val->OPPNO]['weight'] = 0;
$monitor_array[$val->OPPNO]['lines'] = 0;
} }
if (! empty($statistic4)) { }
// echo "in !empty<br>";
// Initialize the Monitor array
foreach ($statistic4 as $key => $val) {
// Initialize these variables in case not existant in LBPF
$monitor_array[$val->CODE]['header'] = 'Mon' . $statistic4[$key]->CODE;
$monitor_array[$val->CODE]['timediff'] = 0;
$monitor_array[$val->CODE]['percent_per_monitor'] = 0;
$monitor_array[$val->CODE]['percent_per_activity'] = 0;
$monitor_array[$val->CODE]['lph_blended_per_activity'] = 0;
$monitor_array[$val->CODE]['picked'] = 0;
$monitor_array[$val->CODE]['weight'] = 0;
$monitor_array[$val->CODE]['lines'] = 0;
}
// add another element (0) in monitor_array to capture the total blended lph value expected
$monitor_array[0]['lph_blended_per_activity'] = 0;
$monitor_array[0]['header'] = '0';
// cycle thru stats again, and accumulate totals at global level (totals_array) and at monitor level (monitor_array)
foreach ($statistic4 as $key => $val) {
$statistic4[$key]->WEIGHT = round($statistic4[$key]->WEIGHT);
$statistic4[$key]->A5VALUE = round($statistic4[$key]->A5VALUE);
$statistic4[$key]->A5PICKED = round($statistic4[$key]->A5PICKED);
$statistic4[$key]->A5PICKCMTS = date('Y-m-d H:i', strtotime($statistic4[$key]->A5PICKCMTS));
// Calculate difference from previous time entry in seconds and save in TIMEDIFF key of STATISTIC4 array
// $time_now=strtotime($statistic4[$key]->A5PICKCMTSADJ);
$time_now = strtotime($statistic4[$key]->A5CMTSADJ);
if ($key > 0) {
$diff = round(abs($time_now - $time_prev));
if ($diff > $ignore_picks_that_last_longer_than_this_many_hours * 60 * 60) {
$diff = -1;
}
}
$statistic4[$key]->TIMEDIFF = $diff;
if ($diff / 60 < 5) {
$statistic4[$key]->COLOUR = '';
}
if ($diff / 60 >= 5 && $diff / 60 <= 10) {
$statistic4[$key]->COLOUR = 'yellow';
}
if ($diff / 60 > 10 && $diff / 60 <= 15) {
$statistic4[$key]->COLOUR = '#88ace0';
}
if ($diff / 60 > 15) {
$statistic4[$key]->COLOUR = 'red';
}
// save the time for previous entry
// $time_prev=strtotime($statistic4[$key]->A5PICKCMTSADJ);
$time_prev = strtotime($statistic4[$key]->A5CMTSADJ);
// accumulate totals at monitor level
$monitor_array[$val->CODE]['timediff'] += $diff;
$monitor_array[$val->CODE]['picked'] += $statistic4[$key]->A5PICKED;
$monitor_array[$val->CODE]['weight'] += $statistic4[$key]->WEIGHT;
$monitor_array[$val->CODE]['lines']++;
// calculate totals at global level
$totals_array['entries'] = count($statistic4);
$totals_array['weight'] += $statistic4[$key]->WEIGHT;
$totals_array['value'] += $statistic4[$key]->A5VALUE;
$totals_array['timediff'] += $diff;
$totals_array['picked'] += $statistic4[$key]->A5PICKED;
}
// echo "after 2<pre>"; print_r($statistic4);
// build array for Performance with Greg's time entry (at one point we had a [0] array as well)
if (! is_null($lbpf)) {
$perf_array['tot_hours'] = (isset($lbpf) ? $lbpf : 0);
if ($perf_array['tot_hours'] != 0) {
$perf_array['per_hour_actual'] = $totals_array['entries'] / $perf_array['tot_hours'];
} else {
$perf_array['per_hour_actual'] = 0;
}
}
// cycle through monitors array and compute percentage to allocate to each monitor (code)
ksort($monitor_array);
foreach ($monitor_array as $key => $val) {
if ($key != '0') {
if (! isset($monitor_array[$key]['tot_hours'])) {
$monitor_array[$key]['tot_hours'] = 0;
}
if ($totals_array['timediff'] != 0) {
$monitor_array[$key]['percent_per_monitor'] = $monitor_array[$key]['timediff'] / $totals_array['timediff'];
}
if ($lbpf != 0) {
$monitor_array[$key]['percent_per_activity'] = $monitor_array[$key]['tot_hours'] / $lbpf;
}
$monitor_array[$key]['lph_blended_per_activity'] = $monitor_array[$key]['percent_per_activity'] * (isset($lph_per_monitor_array[$key]) ? $lph_per_monitor_array[$key] : 0);
$monitor_array[0]['lph_blended_per_activity'] += $monitor_array[$key]['lph_blended_per_activity'];
}
}
if ($monitor_array[0]['lph_blended_per_activity'] != 0) {
$perf_array['performance_activity'] = $perf_array['per_hour_actual'] / $monitor_array[0]['lph_blended_per_activity'];
}
private static function initializeMonitors(array &$context): void
{
foreach ($context['lbpfRows'] as $row) {
$context['monitor'][$row->OPPNO] = self::monitorDefaults('Mon' . $row->OPPNO, $row->HRS);
} }
foreach ($context['statistics'] as $row) {
// return encoded json if input field named format is json $context['monitor'][$row->CODE] = array_replace(
if ($request->format == 'json') { self::monitorDefaults('Mon' . $row->CODE, 0),
$context['monitor'][$row->CODE] ?? []
if (! array_key_exists('performance_activity', $perf_array)) {
$performance = 0;
} else {
$performance = number_format($perf_array['performance_activity'] * 100, 0);
}
if (! array_key_exists('0', $monitor_array)) {
$blendedTargetLPH = 0;
} else {
$blendedTargetLPH = number_format($monitor_array[0]['lph_blended_per_activity'], 1);
}
if (! array_key_exists('per_hour_actual', $perf_array)) {
$actualLPH = 0;
} else {
$actualLPH = number_format($perf_array['per_hour_actual'], 1);
}
if (! array_key_exists('tot_hours', $perf_array)) {
$grossHours = 0;
} else {
$grossHours = $perf_array['tot_hours'];
}
$json_data = [
'day' => $starting,
'performance' => $performance,
'blendedTargetLPH' => $blendedTargetLPH,
'actualLPH' => $actualLPH,
'grossHours' => $grossHours,
];
return json_encode($json_data);
} else {
// display webpage of statistic version 4
$request->ports = Config::get('arrays.env_ports');
$no_data = 'N';
if (empty($statistic4) && $_SERVER['REQUEST_URI'] != '/UserPickingStats4') {
$no_data = 'Y';
}
return view(
'm3::liberty-theme.pages.UserPickingStats4',
compact('statistic4', 'starting', 'user', 'user_array', 'monitor_array', 'totals_array', 'perf_array', 'request', 'no_data'),
); );
} }
if ($context['statistics'] !== []) {
$context['monitor'][0] = self::monitorDefaults('0', 0);
}
}
private static function monitorDefaults(string $header, float|int $hours): array
{
return ['tot_hours' => $hours, 'header' => $header, 'timediff' => 0, 'percent_per_monitor' => 0,
'percent_per_activity' => 0, 'lph_blended_per_activity' => 0, 'picked' => 0, 'weight' => 0, 'lines' => 0];
}
private static function accumulateStatistics(array &$context): void
{
$previous = null;
$limit = (float) Config::get('arrays.ignore_picks_that_last_longer_than_this_many_hours', 8);
foreach ($context['statistics'] as $row) {
$row->WEIGHT = round($row->WEIGHT);
$row->A5VALUE = round($row->A5VALUE);
$row->A5PICKED = round($row->A5PICKED);
$row->A5PICKCMTS = date('Y-m-d H:i', strtotime($row->A5PICKCMTS));
$now = strtotime($row->A5CMTSADJ);
$diff = $previous === null ? 0 : round(abs($now - $previous));
$diff = $diff > $limit * 3600 ? -1 : $diff;
$row->TIMEDIFF = $diff;
$row->COLOUR = self::durationColour($diff);
$previous = $now;
self::addRowTotals($context, $row, $diff);
}
}
private static function durationColour(float|int $diff): string
{
$minutes = $diff / 60;
return match (true) {
$minutes < 5 => '',
$minutes <= 10 => 'yellow',
$minutes <= 15 => '#88ace0',
default => 'red',
};
}
private static function addRowTotals(array &$context, object $row, float|int $diff): void
{
$monitor = &$context['monitor'][$row->CODE];
$monitor['timediff'] += $diff;
$monitor['picked'] += $row->A5PICKED;
$monitor['weight'] += $row->WEIGHT;
$monitor['lines']++;
$context['totals']['entries']++;
$context['totals']['weight'] += $row->WEIGHT;
$context['totals']['value'] += $row->A5VALUE;
$context['totals']['timediff'] += $diff;
$context['totals']['picked'] += $row->A5PICKED;
}
private static function calculateMonitorTargets(array &$context): void
{
ksort($context['monitor']);
foreach ($context['monitor'] as $code => &$monitor) {
if ((string) $code === '0') {
continue;
}
$monitor['percent_per_monitor'] = $context['totals']['timediff'] != 0
? $monitor['timediff'] / $context['totals']['timediff'] : 0;
$monitor['percent_per_activity'] = $context['lbpf'] != 0 ? $monitor['tot_hours'] / $context['lbpf'] : 0;
$monitor['lph_blended_per_activity'] = $monitor['percent_per_activity'] * ($context['targets'][$code] ?? 0);
$context['monitor'][0]['lph_blended_per_activity'] += $monitor['lph_blended_per_activity'];
}
unset($monitor);
}
private static function jsonResponse(array $context): string
{
return json_encode([
'day' => $context['starting'],
'performance' => number_format(($context['performance']['performance_activity'] ?? 0) * 100, 0),
'blendedTargetLPH' => number_format($context['monitor'][0]['lph_blended_per_activity'] ?? 0, 1),
'actualLPH' => number_format($context['performance']['per_hour_actual'] ?? 0, 1),
'grossHours' => $context['performance']['tot_hours'] ?? 0,
]);
}
private static function pageResponse(Request $request, array $context)
{
$request->ports = Config::get('arrays.env_ports');
$no_data = $context['statistics'] === [] && ($_SERVER['REQUEST_URI'] ?? '') !== '/UserPickingStats4' ? 'Y' : 'N';
return view('m3::liberty-theme.pages.UserPickingStats4', [
'statistic4' => $context['statistics'], 'starting' => $context['starting'], 'user' => $context['user'],
'user_array' => $context['users'], 'monitor_array' => $context['monitor'], 'totals_array' => $context['totals'],
'perf_array' => $context['performance'], 'request' => $request, 'no_data' => $no_data,
]);
} }
} }
@@ -56,7 +56,7 @@ class UserPickingStats5 extends Controller
$profile = DB::connection($connection)->table('icm428m') $profile = DB::connection($connection)->table('icm428m')
->select('R428CO', 'R428WH') ->select('R428CO', 'R428WH')
->where('R428US', strtoupper((string) m3_user())) ->where('R428US', strtoupper((string) m3User()))
->first(); ->first();
$co = trim((string) ($profile->R428CO ?? '')); $co = trim((string) ($profile->R428CO ?? ''));
$whs = trim((string) ($profile->R428WH ?? '')); $whs = trim((string) ($profile->R428WH ?? ''));
@@ -128,7 +128,11 @@ class UserPickingStats5 extends Controller
try { try {
$response = Http::withBasicAuth((string) $request->header('php-auth-user'), (string) $request->header('php-auth-pw')) $response = Http::withBasicAuth((string) $request->header('php-auth-user'), (string) $request->header('php-auth-pw'))
->get($url, ['dates' => $date, 'user' => $request->user, 'format' => 'json']); ->get($url, ['dates' => $date, 'user' => $request->user, 'format' => 'json']);
$data = $response->successful() ? (json_decode($response->body(), true) ?: []) : []; $data = [];
if ($response->successful()) {
$decoded = json_decode($response->body(), true);
$data = is_array($decoded) ? $decoded : [];
}
} catch (\Throwable $e) { } catch (\Throwable $e) {
Log::warning('M3 picking statistics unavailable', ['date' => $date, 'exception' => $e->getMessage()]); Log::warning('M3 picking statistics unavailable', ['date' => $date, 'exception' => $e->getMessage()]);
$data = []; $data = [];
@@ -12,7 +12,7 @@ class UserProfileController extends Controller
{ {
// Get Current USER // Get Current USER
$loggedUser = strtoupper(m3_user()); $loggedUser = strtoupper(m3User());
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') $iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL', 'R428FIRST', 'R428FAMILY') ->select('R428US', 'R428CO', 'R428WH', 'R428PL', 'R428FIRST', 'R428FAMILY')
@@ -29,19 +29,9 @@ class UtilitiesController extends Controller
} catch (\Throwable $e) { } catch (\Throwable $e) {
// Determine reason for failure. // Determine reason for failure.
// Probably database authentication error or invalid or unreachable database. // Probably database authentication error or invalid or unreachable database.
$code = $e->getCode(); Log::error('Unable to connect to the program bridge.', ['exception' => $e]);
$msg = $e->getMessage();
switch ($code) { return response()->json(['error' => 'Program bridge connection failed.'], 503);
case 8001:
// "Authorization failure on distributed database connection attempt"
// Usually means a wrong DB2 user or password
break;
case 42705:
break;
default:
break;
}
} }
$conn->setOptions(['persistent' => false, 'stateless' => true, 'debug' => true, 'debugLogFile' => '/www/sentustesx/htdocs/storage/logs/toolkit_debug.log']); $conn->setOptions(['persistent' => false, 'stateless' => true, 'debug' => true, 'debugLogFile' => '/www/sentustesx/htdocs/storage/logs/toolkit_debug.log']);
@@ -68,16 +58,10 @@ class UtilitiesController extends Controller
// ICM700C EWOBJTST ICM700C ICM700CSBM // ICM700C EWOBJTST ICM700C ICM700CSBM
$result = $conn->PgmCall('ICM700CSBM', 'EWOBJTST', $params, [], []); $result = $conn->PgmCall('ICM700CSBM', 'EWOBJTST', $params, [], []);
if (! $result) { if ($result) {
}
// Successful Call
else {
dump($result); dump($result);
} }
if ($pGMERRORS != '') {
dump($pGMERRORS);
}
// Get Execution Time // Get Execution Time
$time = microtime(true) - $start; $time = microtime(true) - $start;
dump($time); dump($time);
@@ -171,228 +155,250 @@ class UtilitiesController extends Controller
try { try {
Log::channel('LabelCron')->info('>>> Run GS1 Label creation Command...'); Log::channel('LabelCron')->info('>>> Run GS1 Label creation Command...');
// Get the Calling User
// $userId = 'TWOSHIP';
// $sHUSER = isset($userId) ? $userId : (m3_user() !== '' ? Str::upper(trim(m3_user())) : '');
// Get Manifest Array
$gSArray = $request->query(); $gSArray = $request->query();
$sHUSER = trim((string) ($gSArray['User'] ?? ''));
$gS1FileNameNoRoot = '';
$sHUSER = isset($gSArray['User']) ? trim($gSArray['User']) : ''; if ($sHUSER !== '') {
$gS1FileNameNoRoot = self::generateGS128ForUser($gSArray, $sHUSER);
if ($sHUSER != '') {
// Get User CO & WH from Table ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', $sHUSER)
->first();
$cOMPANY = isset($iCM428M->R428CO) ? $iCM428M->R428CO : ''; // Company
$pLANT = isset($iCM428M->R428PL) ? $iCM428M->R428PL : ''; // Plant
$wHS = isset($iCM428M->R428WH) ? $iCM428M->R428WH : ''; // WareHouse
$macpkr = $gSArray['Macpkr'];
$packageType = trim($gSArray['packageType']); // 1 => Pallet , 0 => Carton
$packageTypeDesc = $packageType == 1 ? 'Pallet' : 'Carton';
$manufactID = '0826528'; // MAK : get ID from Nvent and save to Config File
$total_length = 9;
$html = '
<html>
<head>
<style>
.border_btm {
border-bottom: 1px solid;
}
.border_rght {
border-right: 1px solid;
}
.margin_t {
padding-top : 10px;
}
.align_lft {
text-align: left;
}
.align_center {
text-align: center;
}
@page { margin: 5px; }
body { margin: 0px; }
</style>
</head>
<body style="font-family: Arial, sans-serif;">';
// Get number of Pallets
$packageCount = count($gSArray['PackagesArray']);
$packageNum = 1;
// Get GS1 Label MAX ID from ICM695M3 :
$gS1SEQUENCESel = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m3')->max('ID');
$gS1SEQUENCE = isset($gS1SEQUENCESel) ? $gS1SEQUENCESel : 0;
// Loop PackagesArray : Build Table for PDF and Insert to ICM695m3
foreach ($gSArray['PackagesArray'] as $value) {
$table_style = $packageNum < $packageCount ? ' style="page-break-after: always;" ' : '';
$a2INTBOX = trim($value);
$gS1SEQUENCE++;
$gS1SEQUENCEID = str_pad($gS1SEQUENCE, $total_length, '0', STR_PAD_LEFT);
// Build GS1 18 Digits Code
$gs1_128_bc_1 = '00' . $packageType . $manufactID . $gS1SEQUENCEID;
// Calculate Check Digit
$digits = str_split($gs1_128_bc_1);
$sum = 0;
$length = count($digits);
// Loop through each digit starting from the right
for ($i = 0; $i < $length; $i++) {
$digit = $digits[$length - $i - 1];
// Assign a weight of 3 or 1 alternately
$weight = ($i % 2 == 0) ? 3 : 1;
// Add the product of the digit and its weight to the sum
$sum += $digit * $weight;
}
// Calculate the remainder when sum is divided by 10
$remainder = $sum % 10;
// If remainder is 0, check digit is 0; otherwise, subtract remainder from 10
$checkDigit = ($remainder == 0) ? 0 : 10 - $remainder;
// End Check Digit Calculation
// SSCC 18 Digits Code
$gs1_128_bc = $gs1_128_bc_1 . $checkDigit;
$gs1_128_bc_desc = '(00)' . $packageType . $manufactID . $gS1SEQUENCEID . $checkDigit;
$data = [
'ship_to' => $gSArray['Recipient_CompanyName'] . '<br>' . $gSArray['Recipient_Address1'] . '<br>' .
$gSArray['Recipient_City'] . ',' . $gSArray['Recipient_State']
. '<br>' . $gSArray['Recipient_PostalCode'],
'from' => $gSArray['Sender_CompanyName'] . '<br>' . $gSArray['Sender_Address1'] . '<br>' .
$gSArray['Sender_City'] . ',' . $gSArray['Sender_State']
. '<br>' . $gSArray['Sender_PostalCode'],
'ship_to_lbl' => '420' . $gSArray['Recipient_PostalCode'],
'ship_to_desc' => '(420)' . $gSArray['Recipient_PostalCode'],
'pro' => '190071217845706', // MAK : Check missing info with Brad
'item_number' => '10000B',
'upc' => '098506095310',
'quantity' => '1',
'serial_shipping_container' => $gs1_128_bc_desc,
];
// Initialize barcode generator
$generator = new BarcodeGeneratorPNG;
// Generate barcodes
$shipToPostalCodeBarcode = $generator->getBarcode($data['ship_to_lbl'], $generator::TYPE_CODE_128);
$serialShippingContainerBarcode = $generator->getBarcode($data['serial_shipping_container'], $generator::TYPE_CODE_128, 2, 50);
// HTML content for the label
$html .= '
<table ' . $table_style . '>
<tr>
<td colspan="2" class="border_btm border_rght align_lft">
FROM:<br>' . $data['from'] . '
</td>
<td colspan="2" class="border_btm align_lft">
SHIP TO:<br>' . $data['ship_to'] . '
</td>
</tr>
<tr>
<td colspan="3" class="border_btm border_rght margin_t align_center">
<img src="data:image/png;base64,' . base64_encode($shipToPostalCodeBarcode) . '"> <br>
' . $data['ship_to_desc'] . '
</td>
<td class="border_btm margin_t align_lft">
Carrier: ' . $gSArray['CarrierName'] . ' <br>
PRO #: ' . $gSArray['trackingno'] . ' <br>
B/L #: ' . $gSArray['BOLnumber'] . ' <br>
Ref : ' . $a2INTBOX . '
</td>
</tr>
<tr>
<td colspan="4" class="border_btm margin_t align_lft">
PURCHASE ORDER: ' . $gSArray['ShipmentPONumber'] . ' <br>
Vendor: ' . $manufactID . '
</td>
</tr>
<tr>
<td colspan="4" class="border_btm margin_t align_center">
' . $packageTypeDesc . ' # : ' . $packageNum . ' of ' . $packageCount . '
</td>
</tr>
<tr>
<td colspan="4" style="text-align: center;" class="margin_t">
<img src="data:image/png;base64,' . base64_encode($serialShippingContainerBarcode) . '"><br>
' . $data['serial_shipping_container'] . '
</td>
</tr>
</table>';
$packageNum++;
// Insert new Record to ICM695M3 $gs1_128_bc
DB::connection(config('m3.connection', 'pgsql'))->table('icm695m3')
->insert(['A2CO' => $cOMPANY, 'A2WHS' => $wHS, 'A2PLANT' => $pLANT, 'A2MACPKR' => $macpkr, 'A2GS1LABEL' => $gs1_128_bc, 'A2INTBOX' => $a2INTBOX]);
Log::channel('LabelCron')->info('<<< GS1-128...End of Label Creation for Mackpkr: ' . $macpkr);
}
$html .= '</body> </html>';
ob_start();
// Initialize Dompdf
$dompdf = new Dompdf;
$dompdf->loadHtml($html);
$dompdf->setPaper('a6', 'portrait');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
// $dompdf->stream();
$pdfContent = $dompdf->output();
$labelsPath = Config::get('arrays.PdfPath'); // '/www/sentm3live/htdocs/labels/';
// Put the PDF in labels/CarrierLabel
$gSFolder = 'GSLabel';
$carrierFileName = $gSFolder . '_' . $macpkr . '_' . $cOMPANY . '_' . $wHS . '_' . $a2INTBOX;
// LTL
// if ($packageType=='1') {
// $carrierFileName = $gSFolder.'_'.$macpkr.'_'.$cOMPANY.'_'.$wHS;
// }
// # small Packs
// else{
// $carrierFileName = $gSFolder.'_'.$macpkr.'_'.$cOMPANY.'_'.$wHS.'_'.$a2INTBOX;
// }
$gS1FileName = $labelsPath . $gSFolder . '/' . $carrierFileName . '.pdf';
$gS1FileNameNoRoot = $gSFolder . '/' . $carrierFileName;
file_put_contents($gS1FileName, $pdfContent);
ob_end_clean();
} }
// # Insert new Record to ICM695M3 $gs1_128_bc
// DB::connection(config('m3.connection', 'pgsql'))->table('icm695m3')
// ->insert(['A2CO'=> $cOMPANY,'A2WHS'=> $wHS,'A2MACPKR'=> $macpkr,'A2GS1LABEL'=> $gs1_128_bc]);
return json_encode(['result' => 'success', 'GS1File' => $gS1FileNameNoRoot]); return json_encode(['result' => 'success', 'GS1File' => $gS1FileNameNoRoot]);
} catch (\Throwable $th) { } catch (\Throwable $th) {
Log::channel('LabelCron')->error($th); Log::channel('LabelCron')->error($th);
return json_encode(['result' => 'error', 'GS1File' => $th]); return json_encode(['result' => 'error', 'GS1File' => $th]);
} }
} }
private static function generateGS128ForUser(array $gSArray, string $sHUSER): string
{
// Get User CO & WH from Table ICM428M
$iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', $sHUSER)
->first();
$cOMPANY = isset($iCM428M->R428CO) ? $iCM428M->R428CO : ''; // Company
$pLANT = isset($iCM428M->R428PL) ? $iCM428M->R428PL : ''; // Plant
$wHS = isset($iCM428M->R428WH) ? $iCM428M->R428WH : ''; // WareHouse
$macpkr = $gSArray['Macpkr'];
$packageType = trim($gSArray['packageType']); // 1 => Pallet , 0 => Carton
$packageTypeDesc = $packageType == 1 ? 'Pallet' : 'Carton';
$manufactID = '0826528'; // MAK : get ID from Nvent and save to Config File
$total_length = 9;
$html = '
<html>
<head>
<style>
.border_btm {
border-bottom: 1px solid;
}
.border_rght {
border-right: 1px solid;
}
.margin_t {
padding-top : 10px;
}
.align_lft {
text-align: left;
}
.align_center {
text-align: center;
}
@page { margin: 5px; }
body { margin: 0px; }
</style>
</head>
<body style="font-family: Arial, sans-serif;">';
// Get number of Pallets
$packageCount = count($gSArray['PackagesArray']);
$packageNum = 1;
// Get GS1 Label MAX ID from ICM695M3 :
$gS1SEQUENCESel = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m3')->max('ID');
$gS1SEQUENCE = isset($gS1SEQUENCESel) ? $gS1SEQUENCESel : 0;
// Loop PackagesArray : Build Table for PDF and Insert to ICM695m3
foreach ($gSArray['PackagesArray'] as $value) {
$table_style = $packageNum < $packageCount ? ' style="page-break-after: always;" ' : '';
$a2INTBOX = trim($value);
$gS1SEQUENCE++;
$gS1SEQUENCEID = str_pad($gS1SEQUENCE, $total_length, '0', STR_PAD_LEFT);
// Build GS1 18 Digits Code
$gs1_128_bc_1 = '00' . $packageType . $manufactID . $gS1SEQUENCEID;
$checkDigit = self::gs1CheckDigit($gs1_128_bc_1);
// SSCC 18 Digits Code
$gs1_128_bc = $gs1_128_bc_1 . $checkDigit;
$gs1_128_bc_desc = '(00)' . $packageType . $manufactID . $gS1SEQUENCEID . $checkDigit;
$data = [
'ship_to' => $gSArray['Recipient_CompanyName'] . '<br>' . $gSArray['Recipient_Address1'] . '<br>' .
$gSArray['Recipient_City'] . ',' . $gSArray['Recipient_State']
. '<br>' . $gSArray['Recipient_PostalCode'],
'from' => $gSArray['Sender_CompanyName'] . '<br>' . $gSArray['Sender_Address1'] . '<br>' .
$gSArray['Sender_City'] . ',' . $gSArray['Sender_State']
. '<br>' . $gSArray['Sender_PostalCode'],
'ship_to_lbl' => '420' . $gSArray['Recipient_PostalCode'],
'ship_to_desc' => '(420)' . $gSArray['Recipient_PostalCode'],
'pro' => '190071217845706', // MAK : Check missing info with Brad
'item_number' => '10000B',
'upc' => '098506095310',
'quantity' => '1',
'serial_shipping_container' => $gs1_128_bc_desc,
];
// Initialize barcode generator
$generator = new BarcodeGeneratorPNG;
// Generate barcodes
$shipToPostalCodeBarcode = $generator->getBarcode($data['ship_to_lbl'], $generator::TYPE_CODE_128);
$serialShippingContainerBarcode = $generator->getBarcode($data['serial_shipping_container'], $generator::TYPE_CODE_128, 2, 50);
// HTML content for the label
$html .= self::gs128LabelHtml([
'data' => $data,
'gs' => $gSArray,
'internal_box' => $a2INTBOX,
'manufacturer_id' => $manufactID,
'package_type' => $packageTypeDesc,
'package_number' => $packageNum,
'package_count' => $packageCount,
'table_style' => $table_style,
'postal_barcode' => $shipToPostalCodeBarcode,
'container_barcode' => $serialShippingContainerBarcode,
]);
$packageNum++;
// Insert new Record to ICM695M3 $gs1_128_bc
DB::connection(config('m3.connection', 'pgsql'))->table('icm695m3')
->insert(['A2CO' => $cOMPANY, 'A2WHS' => $wHS, 'A2PLANT' => $pLANT, 'A2MACPKR' => $macpkr, 'A2GS1LABEL' => $gs1_128_bc, 'A2INTBOX' => $a2INTBOX]);
Log::channel('LabelCron')->info('<<< GS1-128...End of Label Creation for Mackpkr: ' . $macpkr);
}
$html .= '</body> </html>';
ob_start();
// Initialize Dompdf
$dompdf = new Dompdf;
$dompdf->loadHtml($html);
$dompdf->setPaper('a6', 'portrait');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
// $dompdf->stream();
$pdfContent = $dompdf->output();
$labelsPath = Config::get('arrays.PdfPath'); // '/www/sentm3live/htdocs/labels/';
// Put the PDF in labels/CarrierLabel
$gSFolder = 'GSLabel';
$carrierFileName = $gSFolder . '_' . $macpkr . '_' . $cOMPANY . '_' . $wHS . '_' . $a2INTBOX;
// LTL
// if ($packageType=='1') {
// $carrierFileName = $gSFolder.'_'.$macpkr.'_'.$cOMPANY.'_'.$wHS;
// }
// # small Packs
// else{
// $carrierFileName = $gSFolder.'_'.$macpkr.'_'.$cOMPANY.'_'.$wHS.'_'.$a2INTBOX;
// }
$gS1FileName = $labelsPath . $gSFolder . '/' . $carrierFileName . '.pdf';
$gS1FileNameNoRoot = $gSFolder . '/' . $carrierFileName;
file_put_contents($gS1FileName, $pdfContent);
ob_end_clean();
return $gS1FileNameNoRoot;
}
private static function gs128LabelHtml(array $context): string
{
$data = $context['data'];
$gSArray = $context['gs'];
$a2INTBOX = (string) $context['internal_box'];
$manufactID = (string) $context['manufacturer_id'];
$packageTypeDesc = (string) $context['package_type'];
$packageNum = (int) $context['package_number'];
$packageCount = (int) $context['package_count'];
$table_style = (string) $context['table_style'];
$shipToPostalCodeBarcode = (string) $context['postal_barcode'];
$serialShippingContainerBarcode = (string) $context['container_barcode'];
return '
<table ' . $table_style . '>
<tr>
<td colspan="2" class="border_btm border_rght align_lft">
FROM:<br>' . $data['from'] . '
</td>
<td colspan="2" class="border_btm align_lft">
SHIP TO:<br>' . $data['ship_to'] . '
</td>
</tr>
<tr>
<td colspan="3" class="border_btm border_rght margin_t align_center">
<img src="data:image/png;base64,' . base64_encode($shipToPostalCodeBarcode) . '"> <br>
' . $data['ship_to_desc'] . '
</td>
<td class="border_btm margin_t align_lft">
Carrier: ' . $gSArray['CarrierName'] . ' <br>
PRO #: ' . $gSArray['trackingno'] . ' <br>
B/L #: ' . $gSArray['BOLnumber'] . ' <br>
Ref : ' . $a2INTBOX . '
</td>
</tr>
<tr>
<td colspan="4" class="border_btm margin_t align_lft">
PURCHASE ORDER: ' . $gSArray['ShipmentPONumber'] . ' <br>
Vendor: ' . $manufactID . '
</td>
</tr>
<tr>
<td colspan="4" class="border_btm margin_t align_center">
' . $packageTypeDesc . ' # : ' . $packageNum . ' of ' . $packageCount . '
</td>
</tr>
<tr>
<td colspan="4" style="text-align: center;" class="margin_t">
<img src="data:image/png;base64,' . base64_encode($serialShippingContainerBarcode) . '"><br>
' . $data['serial_shipping_container'] . '
</td>
</tr>
</table>';
}
private static function gs1CheckDigit(string $value): int
{
$digits = str_split($value);
$sum = 0;
$length = count($digits);
for ($i = 0; $i < $length; $i++) {
$digit = (int) $digits[$length - $i - 1];
$weight = $i % 2 === 0 ? 3 : 1;
$sum += $digit * $weight;
}
$remainder = $sum % 10;
return $remainder === 0 ? 0 : 10 - $remainder;
}
public static function generateGS128Tst(Request $request) public static function generateGS128Tst(Request $request)
{ {
print_r('>>> Generate PDF'); print_r('>>> Generate PDF');
@@ -60,7 +60,7 @@ class WhsPerfMetrics extends Controller
private static function loadUserContext(): array private static function loadUserContext(): array
{ {
$connection = config('m3.connection', 'pgsql'); $connection = config('m3.connection', 'pgsql');
$user = Str::upper(trim((string) m3_user())); $user = Str::upper(trim((string) m3User()));
if ($user === '' || ! Schema::connection($connection)->hasTable('icm428m')) { if ($user === '' || ! Schema::connection($connection)->hasTable('icm428m')) {
return ['COMPANY' => '', 'PLANT' => '', 'WHS' => '']; return ['COMPANY' => '', 'PLANT' => '', 'WHS' => ''];
@@ -35,7 +35,7 @@ class WhsePerfController extends Controller
$iCM711M = DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt); $iCM711M = DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt);
$uSER_INFO = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')->selectRaw('R428US as USERPRF, R428STS as STATUS, NULL as DATE_EXPIRED, NULL as EXPIRED')->get()->all(); $uSER_INFO = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')->selectRaw('R428US as USERPRF, R428STS as STATUS, NULL as DATE_EXPIRED, NULL as EXPIRED')->get()->all();
$power_user = DB::connection(config('m3.connection', 'pgsql'))->select("select R428PWRUSR, R428CO, R428PL, R428WH from ICM428M where R428US='" . strtoupper(m3_user()) . "'"); $power_user = DB::connection(config('m3.connection', 'pgsql'))->select("select R428PWRUSR, R428CO, R428PL, R428WH from ICM428M where R428US='" . strtoupper(m3User()) . "'");
$pct_array = Config::get('arrays.whsePerf_targets'); $pct_array = Config::get('arrays.whsePerf_targets');
@@ -58,7 +58,7 @@ class WhsePerfController extends Controller
$iCM711M = DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt); $iCM711M = DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt);
$uSER_INFO = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')->selectRaw('R428US as USERPRF, R428STS as STATUS, NULL as DATE_EXPIRED, NULL as EXPIRED')->get()->all(); $uSER_INFO = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')->selectRaw('R428US as USERPRF, R428STS as STATUS, NULL as DATE_EXPIRED, NULL as EXPIRED')->get()->all();
$power_user = DB::connection(config('m3.connection', 'pgsql'))->select("select R428PWRUSR, R428CO, R428PL, R428WH from ICM428M where R428US='" . strtoupper(m3_user()) . "'"); $power_user = DB::connection(config('m3.connection', 'pgsql'))->select("select R428PWRUSR, R428CO, R428PL, R428WH from ICM428M where R428US='" . strtoupper(m3User()) . "'");
$pct_array = Config::get('arrays.whsePerf_targets'); $pct_array = Config::get('arrays.whsePerf_targets');
+17 -17
View File
@@ -10,8 +10,8 @@ class BasicAuth
{ {
public function handle(Request $request, Closure $next): mixed public function handle(Request $request, Closure $next): mixed
{ {
$login = trim((string) m3_user()); $login = trim((string) m3User());
$password = (string) m3_legacy_password(); $password = (string) m3LegacyPassword();
if ($login !== '' && $this->checkPassword($login, $password, '%%SYSTEM%%')) { if ($login !== '' && $this->checkPassword($login, $password, '%%SYSTEM%%')) {
return $next($request); return $next($request);
@@ -27,26 +27,26 @@ class BasicAuth
} }
$handle = fopen($passwordFile, 'rb'); $handle = fopen($passwordFile, 'rb');
if ($handle === false) { $valid = false;
return false;
}
try { if ($handle !== false) {
while (($line = fgets($handle)) !== false) { try {
$parts = explode(':', trim($line), 2); while (($line = fgets($handle)) !== false) {
if (count($parts) !== 2 || ! hash_equals($parts[0], $login)) { $parts = explode(':', trim($line), 2);
continue; if (count($parts) !== 2 || ! hash_equals($parts[0], $login)) {
continue;
}
$storedHash = trim($parts[1]);
$valid = $storedHash !== '' && password_verify($password, $storedHash);
break;
} }
} finally {
$storedHash = trim($parts[1]); fclose($handle);
return $storedHash !== '' && password_verify($password, $storedHash);
} }
} finally {
fclose($handle);
} }
return false; return $valid;
} }
private function unauthorized(): Response private function unauthorized(): Response
+1 -6
View File
@@ -52,11 +52,6 @@ class ShipTo extends Model
$existingRecord->where($this->getKeyName(), '!=', $this->getKey()); $existingRecord->where($this->getKeyName(), '!=', $this->getKey());
} }
if ($existingRecord->count() > 0) { return $existingRecord->count() === 0;
return false;
} else {
return true;
}
} }
} }
@@ -14,17 +14,6 @@ class RouteServiceProvider extends ServiceProvider
*/ */
protected $moduleNamespace = 'Modules\M3\Http\Controllers'; protected $moduleNamespace = 'Modules\M3\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/** /**
* Define the routes for the application. * Define the routes for the application.
@@ -444,18 +444,11 @@ legend + * {
padding: 0; padding: 0;
} }
::-webkit-file-upload-button {
font: inherit;
}
::file-selector-button { ::file-selector-button {
font: inherit; font: inherit;
} }
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output { output {
display: inline-block; display: inline-block;
@@ -2230,24 +2223,7 @@ div.table-responsive > table > tbody > tr:last-child > td{
background-color: #e9ecef; background-color: #e9ecef;
opacity: 1; opacity: 1;
} }
.form-control::-webkit-file-upload-button, .dataTable-input::-webkit-file-upload-button {
padding: 0.375rem 0.75rem;
margin: -0.375rem -0.75rem;
-webkit-margin-end: 0.75rem;
margin-inline-end: 0.75rem;
color: #212529;
background-color: #e9ecef;
pointer-events: none;
border-color: inherit;
border-style: solid;
border-width: 0;
border-inline-end-width: 1px;
border-radius: 0;
-webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control::file-selector-button, .dataTable-input::file-selector-button { .form-control::file-selector-button, .dataTable-input::file-selector-button {
padding: 0.375rem 0.75rem;
margin: -0.375rem -0.75rem; margin: -0.375rem -0.75rem;
-webkit-margin-end: 0.75rem; -webkit-margin-end: 0.75rem;
margin-inline-end: 0.75rem; margin-inline-end: 0.75rem;
@@ -2260,48 +2236,16 @@ div.table-responsive > table > tbody > tr:last-child > td{
border-inline-end-width: 1px; border-inline-end-width: 1px;
border-radius: 0; border-radius: 0;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
padding: 0.375rem 0.75rem;
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.form-control::-webkit-file-upload-button, .dataTable-input::-webkit-file-upload-button {
-webkit-transition: none;
transition: none;
}
.form-control::file-selector-button, .dataTable-input::file-selector-button { .form-control::file-selector-button, .dataTable-input::file-selector-button {
transition: none; transition: none;
} }
} }
.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button, .dataTable-input:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {
background-color: #dde0e3;
}
.form-control:hover:not(:disabled):not([readonly])::file-selector-button, .dataTable-input:hover:not(:disabled):not([readonly])::file-selector-button { .form-control:hover:not(:disabled):not([readonly])::file-selector-button, .dataTable-input:hover:not(:disabled):not([readonly])::file-selector-button {
background-color: #dde0e3; background-color: #dde0e3;
} }
.form-control::-webkit-file-upload-button, .dataTable-input::-webkit-file-upload-button {
padding: 0.375rem 0.75rem;
margin: -0.375rem -0.75rem;
-webkit-margin-end: 0.75rem;
margin-inline-end: 0.75rem;
color: #212529;
background-color: #e9ecef;
pointer-events: none;
border-color: inherit;
border-style: solid;
border-width: 0;
border-inline-end-width: 1px;
border-radius: 0;
-webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
.form-control::-webkit-file-upload-button, .dataTable-input::-webkit-file-upload-button {
-webkit-transition: none;
transition: none;
}
}
.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button, .dataTable-input:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {
background-color: #dde0e3;
}
.form-control-plaintext { .form-control-plaintext {
display: block; display: block;
width: 100%; width: 100%;
@@ -2324,24 +2268,12 @@ div.table-responsive > table > tbody > tr:last-child > td{
font-size: 0.875rem; font-size: 0.875rem;
border-radius: 0.2rem; border-radius: 0.2rem;
} }
.form-control-sm::-webkit-file-upload-button {
padding: 0.25rem 0.5rem;
margin: -0.25rem -0.5rem;
-webkit-margin-end: 0.5rem;
margin-inline-end: 0.5rem;
}
.form-control-sm::file-selector-button { .form-control-sm::file-selector-button {
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
margin: -0.25rem -0.5rem; margin: -0.25rem -0.5rem;
-webkit-margin-end: 0.5rem; -webkit-margin-end: 0.5rem;
margin-inline-end: 0.5rem; margin-inline-end: 0.5rem;
} }
.form-control-sm::-webkit-file-upload-button {
padding: 0.25rem 0.5rem;
margin: -0.25rem -0.5rem;
-webkit-margin-end: 0.5rem;
margin-inline-end: 0.5rem;
}
.form-control-lg { .form-control-lg {
min-height: calc(1.5em + 1rem + 2px); min-height: calc(1.5em + 1rem + 2px);
@@ -2349,24 +2281,12 @@ div.table-responsive > table > tbody > tr:last-child > td{
font-size: 1.25rem; font-size: 1.25rem;
border-radius: 0.3rem; border-radius: 0.3rem;
} }
.form-control-lg::-webkit-file-upload-button {
padding: 0.5rem 1rem;
margin: -0.5rem -1rem;
-webkit-margin-end: 1rem;
margin-inline-end: 1rem;
}
.form-control-lg::file-selector-button { .form-control-lg::file-selector-button {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
margin: -0.5rem -1rem; margin: -0.5rem -1rem;
-webkit-margin-end: 1rem; -webkit-margin-end: 1rem;
margin-inline-end: 1rem; margin-inline-end: 1rem;
} }
.form-control-lg::-webkit-file-upload-button {
padding: 0.5rem 1rem;
margin: -0.5rem -1rem;
-webkit-margin-end: 1rem;
margin-inline-end: 1rem;
}
textarea.form-control, textarea.dataTable-input { textarea.form-control, textarea.dataTable-input {
min-height: calc(1.5em + 0.75rem + 2px); min-height: calc(1.5em + 0.75rem + 2px);
@@ -3711,19 +3631,16 @@ textarea.form-control-lg {
.dropend .dropdown-toggle::after { .dropend .dropdown-toggle::after {
display: inline-block; display: inline-block;
margin-left: 0.255em; margin-left: 0.255em;
vertical-align: 0.255em;
content: ""; content: "";
border-top: 0.3em solid transparent; border-top: 0.3em solid transparent;
border-right: 0; border-right: 0;
border-bottom: 0.3em solid transparent; border-bottom: 0.3em solid transparent;
border-left: 0.3em solid; border-left: 0.3em solid;
vertical-align: 0;
} }
.dropend .dropdown-toggle:empty::after { .dropend .dropdown-toggle:empty::after {
margin-left: 0; margin-left: 0;
} }
.dropend .dropdown-toggle::after {
vertical-align: 0;
}
.dropstart .dropdown-menu[data-bs-popper] { .dropstart .dropdown-menu[data-bs-popper] {
top: 0; top: 0;
@@ -3733,29 +3650,23 @@ textarea.form-control-lg {
margin-right: 0.125rem; margin-right: 0.125rem;
} }
.dropstart .dropdown-toggle::after { .dropstart .dropdown-toggle::after {
display: inline-block;
margin-left: 0.255em; margin-left: 0.255em;
vertical-align: 0.255em; vertical-align: 0.255em;
content: ""; content: "";
}
.dropstart .dropdown-toggle::after {
display: none; display: none;
} }
.dropstart .dropdown-toggle::before { .dropstart .dropdown-toggle::before {
display: inline-block; display: inline-block;
margin-right: 0.255em; margin-right: 0.255em;
vertical-align: 0.255em;
content: ""; content: "";
border-top: 0.3em solid transparent; border-top: 0.3em solid transparent;
border-right: 0.3em solid; border-right: 0.3em solid;
border-bottom: 0.3em solid transparent; border-bottom: 0.3em solid transparent;
vertical-align: 0;
} }
.dropstart .dropdown-toggle:empty::after { .dropstart .dropdown-toggle:empty::after {
margin-left: 0; margin-left: 0;
} }
.dropstart .dropdown-toggle::before {
vertical-align: 0;
}
.dropdown-divider { .dropdown-divider {
height: 0; height: 0;
@@ -3821,7 +3732,7 @@ textarea.form-control-lg {
} }
.dropdown-menu-dark .dropdown-item:hover, .dropdown-menu-dark .dropdown-item:focus { .dropdown-menu-dark .dropdown-item:hover, .dropdown-menu-dark .dropdown-item:focus {
color: #fff; color: #fff;
background-color: rgba(255, 255, 255, 0.15); background-color: #495057;
} }
.dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active { .dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active {
color: #fff; color: #fff;
@@ -4784,6 +4695,9 @@ textarea.form-control-lg {
padding: 0 0; padding: 0 0;
margin-bottom: 1rem; margin-bottom: 1rem;
list-style: none; list-style: none;
border: 1px solid #f3f3f3;
background-color: transparent;
border-radius: 0.25rem;
} }
.breadcrumb-item + .breadcrumb-item { .breadcrumb-item + .breadcrumb-item {
@@ -4849,9 +4763,6 @@ textarea.form-control-lg {
border-color: #dee2e6; border-color: #dee2e6;
} }
.page-link, .dataTable-pagination a {
padding: 0.375rem 0.75rem;
}
.page-item:first-child .page-link, .page-item:first-child .dataTable-pagination a, .dataTable-pagination .page-item:first-child a, .dataTable-pagination li:first-child .page-link, .dataTable-pagination li:first-child a { .page-item:first-child .page-link, .page-item:first-child .dataTable-pagination a, .dataTable-pagination .page-item:first-child a, .dataTable-pagination li:first-child .page-link, .dataTable-pagination li:first-child a {
border-top-left-radius: 0.25rem; border-top-left-radius: 0.25rem;
@@ -5008,11 +4919,7 @@ textarea.form-control-lg {
color: #101214; color: #101214;
} }
@-webkit-keyframes progress-bar-stripes {
0% {
background-position-x: 1rem;
}
}
@keyframes progress-bar-stripes { @keyframes progress-bar-stripes {
0% { 0% {
@@ -6198,11 +6105,7 @@ textarea.form-control-lg {
color: #000; color: #000;
} }
@-webkit-keyframes spinner-border {
to {
transform: rotate(360deg) /* rtl:ignore */;
}
}
@keyframes spinner-border { @keyframes spinner-border {
to { to {
@@ -6227,15 +6130,7 @@ textarea.form-control-lg {
border-width: 0.2em; border-width: 0.2em;
} }
@-webkit-keyframes spinner-grow {
0% {
transform: scale(0);
}
50% {
opacity: 1;
transform: none;
}
}
@keyframes spinner-grow { @keyframes spinner-grow {
0% { 0% {
@@ -6398,11 +6293,7 @@ textarea.form-control-lg {
animation: placeholder-glow 2s ease-in-out infinite; animation: placeholder-glow 2s ease-in-out infinite;
} }
@-webkit-keyframes placeholder-glow {
50% {
opacity: 0.2;
}
}
@keyframes placeholder-glow { @keyframes placeholder-glow {
50% { 50% {
@@ -6418,12 +6309,7 @@ textarea.form-control-lg {
animation: placeholder-wave 2s linear infinite; animation: placeholder-wave 2s linear infinite;
} }
@-webkit-keyframes placeholder-wave {
100% {
-webkit-mask-position: -200% 0%;
mask-position: -200% 0%;
}
}
@keyframes placeholder-wave { @keyframes placeholder-wave {
100% { 100% {
@@ -7708,7 +7594,7 @@ textarea.form-control-lg {
/* rtl:begin:remove */ /* rtl:begin:remove */
.text-break { .text-break {
word-wrap: break-word !important; word-wrap: break-word !important;
word-break: break-word !important; overflow-wrap: break-word !important;
} }
/* rtl:end:remove */ /* rtl:end:remove */
@@ -11687,19 +11573,6 @@ body {
border-color: #dbe3e6 border-color: #dbe3e6
} }
.breadcrumb {
border: 1px solid #f3f3f3
}
.breadcrumb {
display: flex;
flex-wrap: wrap;
padding: 0.56rem 1.13rem;
margin-bottom: 1rem;
list-style: none;
background-color: transparent;
border-radius: 0.25rem
}
nav { nav {
display: block display: block
@@ -11752,10 +11625,6 @@ nav {
margin-left: 20px; margin-left: 20px;
} }
.breadcrumb.breadcrumb-custom .breadcrumb-item:before {
content: ""
}
.breadcrumb.breadcrumb-custom .breadcrumb-item a:before, .breadcrumb.breadcrumb-custom .breadcrumb-item a:before,
.breadcrumb.breadcrumb-custom .breadcrumb-item a:after { .breadcrumb.breadcrumb-custom .breadcrumb-item a:after {
position: absolute; position: absolute;
@@ -11771,12 +11640,6 @@ a:link {
text-decoration: none text-decoration: none
} }
.breadcrumb-item+.breadcrumb-item::before {
display: inline-block;
padding-right: 0.5rem;
color: #6c757d;
content: "/"
}
.highcharts-figure, .highcharts-data-table table { .highcharts-figure, .highcharts-data-table table {
@@ -102,8 +102,7 @@ $(function() {
customize: function(xlsx,btnObj, tableInst) { customize: function(xlsx,btnObj, tableInst) {
let sheet = xlsx.xl.worksheets['sheet1.xml']; let sheet = xlsx.xl.worksheets['sheet1.xml'];
let cellIndexes = tableInst.cells(".bg-danger").indexes(); let cellIndexes = tableInst.cells(".bg-danger").indexes();
for (let i = 0; i < cellIndexes.length; i++) { for (const cellIndex of cellIndexes) {
let cellIndex = cellIndexes[i];
let tblRow = cellIndex['row']+3; // adjusted from 0 indexed let tblRow = cellIndex['row']+3; // adjusted from 0 indexed
let tblCol = cellIndex['column']; // adjusted from 0 indexed let tblCol = cellIndex['column']; // adjusted from 0 indexed
let xlCol = numberToLetters(tblCol); let xlCol = numberToLetters(tblCol);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
@keyframes dtb-spinner{100%{transform:rotate(360deg)}}@-o-keyframes dtb-spinner{100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes dtb-spinner{100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes dtb-spinner{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes dtb-spinner{100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}div.dt-button-info{position:fixed;top:50%;left:50%;width:400px;margin-top:-100px;margin-left:-200px;background-color:white;border:2px solid #111;box-shadow:3px 3px 8px rgba(0, 0, 0, 0.3);border-radius:3px;text-align:center;z-index:21}div.dt-button-info h2{padding:.5em;margin:0;font-weight:normal;border-bottom:1px solid #ddd;background-color:#f3f3f3}div.dt-button-info>div{padding:1em}button.dtb-hide-drop{display:none !important}div.dt-button-collection-title{text-align:center;padding:.3em 0 .5em;font-size:.9em}div.dt-button-collection-title:empty{display:none}div.dt-button-collection{position:absolute;z-index:2001;width:100%}div.dt-button-collection div.dropdown-menu{display:block;z-index:2002;min-width:100%;padding-left:2px;padding-right:2px}div.dt-button-collection div.dt-button-collection-title{background-color:white;border:1px solid rgba(0, 0, 0, 0.15)}div.dt-button-collection.fixed{position:fixed;top:50%;left:50%;margin-left:-75px;border-radius:0}div.dt-button-collection.fixed.two-column{margin-left:-200px}div.dt-button-collection.fixed.three-column{margin-left:-225px}div.dt-button-collection.fixed.four-column{margin-left:-300px}div.dt-button-collection>:last-child{display:block !important;-webkit-column-gap:8px;-moz-column-gap:8px;-ms-column-gap:8px;-o-column-gap:8px;column-gap:8px}div.dt-button-collection>:last-child>*{-webkit-column-break-inside:avoid;break-inside:avoid}div.dt-button-collection.two-column{width:400px}div.dt-button-collection.two-column>:last-child{padding-bottom:1px;-webkit-column-count:2;-moz-column-count:2;-ms-column-count:2;-o-column-count:2;column-count:2}div.dt-button-collection.three-column{width:450px}div.dt-button-collection.three-column>:last-child{padding-bottom:1px;-webkit-column-count:3;-moz-column-count:3;-ms-column-count:3;-o-column-count:3;column-count:3}div.dt-button-collection.four-column{width:600px}div.dt-button-collection.four-column>:last-child{padding-bottom:1px;-webkit-column-count:4;-moz-column-count:4;-ms-column-count:4;-o-column-count:4;column-count:4}div.dt-button-collection .dt-button{border-radius:0}div.dt-button-collection.fixed{max-width:none}div.dt-button-collection.fixed:before,div.dt-button-collection.fixed:after{display:none}div.dt-button-collection div.dt-btn-split-wrapper{width:100%}div.dt-button-collection button.dt-btn-split-drop-button{width:100%;color:#212529;border:none;background-color:white;border-radius:0px;margin-left:0px !important}div.dt-button-collection button.dt-btn-split-drop-button:focus{border:none;border-radius:0px;outline:none}div.dt-button-collection button.dt-btn-split-drop-button:hover{background-color:#e9ecef}div.dt-button-collection button.dt-btn-split-drop-button:active{background-color:#007bff !important}div.dt-button-background{position:fixed;top:0;left:0;width:100%;height:100%;z-index:999}@media screen and (max-width: 767px){div.dt-buttons{float:none;width:100%;text-align:center;margin-bottom:.5em}div.dt-buttons a.btn{float:none}}div.dt-buttons button.btn.processing,div.dt-buttons div.btn.processing,div.dt-buttons a.btn.processing{color:rgba(0, 0, 0, 0.2)}div.dt-buttons button.btn.processing:after,div.dt-buttons div.btn.processing:after,div.dt-buttons a.btn.processing:after{position:absolute;top:50%;left:50%;width:16px;height:16px;margin:-8px 0 0 -8px;box-sizing:border-box;display:block;content:" ";border:2px solid #282828;border-radius:50%;border-left-color:transparent;border-right-color:transparent;animation:dtb-spinner 1500ms infinite linear;-o-animation:dtb-spinner 1500ms infinite linear;-ms-animation:dtb-spinner 1500ms infinite linear;-webkit-animation:dtb-spinner 1500ms infinite linear;-moz-animation:dtb-spinner 1500ms infinite linear}div.dt-btn-split-wrapper button.dt-btn-split-drop{border-top-right-radius:.25rem !important;border-bottom-right-radius:.25rem !important}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty{cursor:default !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty:before{display:none !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control,table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control{position:relative;padding-left:30px;cursor:pointer}table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before{top:50%;left:5px;height:1em;width:1em;margin-top:-9px;display:block;position:absolute;color:white;border:.15em solid white;border-radius:1em;box-shadow:0 0 .2em #444;box-sizing:content-box;text-align:center;text-indent:0 !important;font-family:"Courier New",Courier,monospace;line-height:1em;content:"+";background-color:#337ab7}table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before{content:"-";background-color:#d33333}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td.dtr-control,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th.dtr-control{padding-left:27px}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td.dtr-control:before,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th.dtr-control:before{left:4px;height:14px;width:14px;border-radius:14px;line-height:14px;text-indent:3px}table.dataTable.dtr-column>tbody>tr>td.dtr-control,table.dataTable.dtr-column>tbody>tr>th.dtr-control,table.dataTable.dtr-column>tbody>tr>td.control,table.dataTable.dtr-column>tbody>tr>th.control{position:relative;cursor:pointer}table.dataTable.dtr-column>tbody>tr>td.dtr-control:before,table.dataTable.dtr-column>tbody>tr>th.dtr-control:before,table.dataTable.dtr-column>tbody>tr>td.control:before,table.dataTable.dtr-column>tbody>tr>th.control:before{top:50%;left:50%;height:.8em;width:.8em;margin-top:-0.5em;margin-left:-0.5em;display:block;position:absolute;color:white;border:.15em solid white;border-radius:1em;box-shadow:0 0 .2em #444;box-sizing:content-box;text-align:center;text-indent:0 !important;font-family:"Courier New",Courier,monospace;line-height:1em;content:"+";background-color:#337ab7}table.dataTable.dtr-column>tbody>tr.parent td.dtr-control:before,table.dataTable.dtr-column>tbody>tr.parent th.dtr-control:before,table.dataTable.dtr-column>tbody>tr.parent td.control:before,table.dataTable.dtr-column>tbody>tr.parent th.control:before{content:"-";background-color:#d33333}table.dataTable>tbody>tr.child{padding:.5em 1em}table.dataTable>tbody>tr.child:hover{background:transparent !important}table.dataTable>tbody>tr.child ul.dtr-details{display:inline-block;list-style-type:none;margin:0;padding:0}table.dataTable>tbody>tr.child ul.dtr-details>li{border-bottom:1px solid #efefef;padding:.5em 0}table.dataTable>tbody>tr.child ul.dtr-details>li:first-child{padding-top:0}table.dataTable>tbody>tr.child ul.dtr-details>li:last-child{border-bottom:none}table.dataTable>tbody>tr.child span.dtr-title{display:inline-block;min-width:75px;font-weight:bold}div.dtr-modal{position:fixed;box-sizing:border-box;top:0;left:0;height:100%;width:100%;z-index:100;padding:10em 1em}div.dtr-modal div.dtr-modal-display{position:absolute;top:0;left:0;bottom:0;right:0;width:50%;height:50%;overflow:auto;margin:auto;z-index:102;overflow:auto;background-color:#f5f5f7;border:1px solid black;border-radius:.5em;box-shadow:0 12px 30px rgba(0, 0, 0, 0.6)}div.dtr-modal div.dtr-modal-content{position:relative;padding:1em}div.dtr-modal div.dtr-modal-close{position:absolute;top:6px;right:6px;width:22px;height:22px;border:1px solid #eaeaea;background-color:#f9f9f9;text-align:center;border-radius:3px;cursor:pointer;z-index:12}div.dtr-modal div.dtr-modal-close:hover{background-color:#eaeaea}div.dtr-modal div.dtr-modal-background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:101;background:rgba(0, 0, 0, 0.6)}@media screen and (max-width: 767px){div.dtr-modal div.dtr-modal-display{width:95%}}div.dtr-bs-modal table.table tr:first-child td{border-top:none}
@@ -0,0 +1,4 @@
/*! Bootstrap integration for DataTables' Responsive
* © SpryMedia Ltd - datatables.net/license
*/
!function(a){var n,o;"function"==typeof define&&define.amd?define(["jquery","datatables.net-bs","datatables.net-responsive"],function(e){return a(e,window,document)}):"object"==typeof exports?(n=require("jquery"),o=function(e,d){d.fn.dataTable||require("datatables.net-bs")(e,d),d.fn.dataTable.Responsive||require("datatables.net-responsive")(e,d)},"undefined"!=typeof window?module.exports=function(e,d){return e=e||window,d=d||n(e),o(e,d),a(d,0,e.document)}:(o(window,n),module.exports=a(n,window,window.document))):a(jQuery,window,document)}(function(i,e,d,a){"use strict";var n=i.fn.dataTable,o=n.Responsive.display,s=o.modal,l=i('<div class="modal fade dtr-bs-modal" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><div class="modal-body"/></div></div></div>');return o.modal=function(t){return function(e,d,a){var n,o;i.fn.modal?d||(t&&t.header&&(o=(n=l.find("div.modal-header")).find("button").detach(),n.empty().append('<h4 class="modal-title">'+t.header(e)+"</h4>").prepend(o)),l.find("div.modal-body").empty().append(a()),l.appendTo("body").modal()):s(e,d,a)}},n});
@@ -0,0 +1 @@
table.dataTable tbody>tr.selected,table.dataTable tbody>tr>.selected{background-color:#0d6efd}table.dataTable.stripe tbody>tr.odd.selected,table.dataTable.stripe tbody>tr.odd>.selected,table.dataTable.display tbody>tr.odd.selected,table.dataTable.display tbody>tr.odd>.selected{background-color:#0d6bf7}table.dataTable.hover tbody>tr.selected:hover,table.dataTable.hover tbody>tr>.selected:hover,table.dataTable.display tbody>tr.selected:hover,table.dataTable.display tbody>tr>.selected:hover{background-color:#0d6af4}table.dataTable.order-column tbody>tr.selected>.sorting_1,table.dataTable.order-column tbody>tr.selected>.sorting_2,table.dataTable.order-column tbody>tr.selected>.sorting_3,table.dataTable.order-column tbody>tr>.selected,table.dataTable.display tbody>tr.selected>.sorting_1,table.dataTable.display tbody>tr.selected>.sorting_2,table.dataTable.display tbody>tr.selected>.sorting_3,table.dataTable.display tbody>tr>.selected{background-color:#0d6cf8}table.dataTable.display tbody>tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_1{background-color:#0c68ef}table.dataTable.display tbody>tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_2{background-color:#0c69f1}table.dataTable.display tbody>tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_3{background-color:#0c6af3}table.dataTable.display tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_1{background-color:#0d6cf8}table.dataTable.display tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_2{background-color:#0d6dfa}table.dataTable.display tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_3{background-color:#0d6efc}table.dataTable.display tbody>tr.odd>.selected,table.dataTable.order-column.stripe tbody>tr.odd>.selected{background-color:#0c68ef}table.dataTable.display tbody>tr.even>.selected,table.dataTable.order-column.stripe tbody>tr.even>.selected{background-color:#0d6cf8}table.dataTable.display tbody>tr.selected:hover>.sorting_1,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_1{background-color:#0c65e8}table.dataTable.display tbody>tr.selected:hover>.sorting_2,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_2{background-color:#0c66ea}table.dataTable.display tbody>tr.selected:hover>.sorting_3,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_3{background-color:#0c67ed}table.dataTable.display tbody>tr:hover>.selected,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.order-column.hover tbody>tr:hover>.selected,table.dataTable.order-column.hover tbody>tr>.selected:hover{background-color:#0c65e8}table.dataTable tbody td.select-checkbox,table.dataTable tbody th.select-checkbox{position:relative}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody td.select-checkbox:after,table.dataTable tbody th.select-checkbox:before,table.dataTable tbody th.select-checkbox:after{display:block;position:absolute;top:1.2em;left:50%;width:12px;height:12px;box-sizing:border-box}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody th.select-checkbox:before{content:" ";margin-top:-2px;margin-left:-6px;border:1px solid black;border-radius:3px}table.dataTable tr.selected td.select-checkbox:after,table.dataTable tr.selected th.select-checkbox:after{content:"✓";font-size:20px;margin-top:-19px;margin-left:-6px;text-align:center;text-shadow:1px 1px #b0bed9,-1px -1px #b0bed9,1px -1px #b0bed9,-1px 1px #b0bed9}table.dataTable.compact tbody td.select-checkbox:before,table.dataTable.compact tbody th.select-checkbox:before{margin-top:-12px}table.dataTable.compact tr.selected td.select-checkbox:after,table.dataTable.compact tr.selected th.select-checkbox:after{margin-top:-16px}div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:.5em}@media screen and (max-width: 640px){div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0;display:block}}table.dataTable tbody tr.selected,table.dataTable tbody th.selected,table.dataTable tbody td.selected{color:white}table.dataTable tbody tr.selected a,table.dataTable tbody th.selected a,table.dataTable tbody td.selected a{color:#a2d4ed}
@@ -0,0 +1,410 @@
.daterangepicker {
position: absolute;
color: inherit;
background-color: #fff;
border-radius: 4px;
border: 1px solid #ddd;
width: 278px;
max-width: none;
padding: 0;
margin-top: 7px;
top: 100px;
left: 20px;
z-index: 3001;
display: none;
font-family: arial;
font-size: 15px;
line-height: 1em;
}
.daterangepicker:before, .daterangepicker:after {
position: absolute;
display: inline-block;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker:before {
top: -7px;
border-right: 7px solid transparent;
border-left: 7px solid transparent;
border-bottom: 7px solid #ccc;
}
.daterangepicker:after {
top: -6px;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
}
.daterangepicker.opensleft:before {
right: 9px;
}
.daterangepicker.opensleft:after {
right: 10px;
}
.daterangepicker.openscenter:before {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.openscenter:after {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.opensright:before {
left: 9px;
}
.daterangepicker.opensright:after {
left: 10px;
}
.daterangepicker.drop-up {
margin-top: -7px;
}
.daterangepicker.drop-up:before {
top: initial;
bottom: -7px;
border-bottom: initial;
border-top: 7px solid #ccc;
}
.daterangepicker.drop-up:after {
top: initial;
bottom: -6px;
border-bottom: initial;
border-top: 6px solid #fff;
}
.daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar {
float: none;
}
.daterangepicker.single .drp-selected {
display: none;
}
.daterangepicker.show-calendar .drp-calendar {
display: block;
}
.daterangepicker.show-calendar .drp-buttons {
display: block;
}
.daterangepicker.auto-apply .drp-buttons {
display: none;
}
.daterangepicker .drp-calendar {
display: none;
max-width: 270px;
}
.daterangepicker .drp-calendar.left {
padding: 8px 0 8px 8px;
}
.daterangepicker .drp-calendar.right {
padding: 8px;
}
.daterangepicker .drp-calendar.single .calendar-table {
border: none;
}
.daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span {
color: #fff;
border: solid black;
border-width: 0 2px 2px 0;
border-radius: 0;
display: inline-block;
padding: 3px;
}
.daterangepicker .calendar-table .next span {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
.daterangepicker .calendar-table .prev span {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
.daterangepicker .calendar-table th, .daterangepicker .calendar-table td {
white-space: nowrap;
text-align: center;
vertical-align: middle;
min-width: 32px;
width: 32px;
height: 24px;
line-height: 24px;
font-size: 12px;
border-radius: 4px;
border: 1px solid transparent;
white-space: nowrap;
cursor: pointer;
}
.daterangepicker .calendar-table {
border: 1px solid #fff;
border-radius: 4px;
background-color: #fff;
}
.daterangepicker .calendar-table table {
width: 100%;
margin: 0;
border-spacing: 0;
border-collapse: collapse;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background-color: #eee;
border-color: transparent;
color: inherit;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
background-color: #fff;
border-color: transparent;
color: #999;
}
.daterangepicker td.in-range {
background-color: #ebf4f8;
border-color: transparent;
color: #000;
border-radius: 0;
}
.daterangepicker td.start-date {
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: transparent;
color: #fff;
}
.daterangepicker th.month {
width: auto;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
cursor: not-allowed;
text-decoration: line-through;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin: 0 auto;
background: #eee;
border: 1px solid #eee;
padding: 2px;
outline: 0;
font-size: 12px;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 4px auto 0 auto;
line-height: 30px;
position: relative;
}
.daterangepicker .calendar-time select.disabled {
color: #ccc;
cursor: not-allowed;
}
.daterangepicker .drp-buttons {
clear: both;
text-align: right;
padding: 8px;
border-top: 1px solid #ddd;
display: none;
line-height: 12px;
vertical-align: middle;
}
.daterangepicker .drp-selected {
display: inline-block;
font-size: 12px;
padding-right: 8px;
}
.daterangepicker .drp-buttons .btn {
margin-left: 8px;
font-size: 12px;
font-weight: bold;
padding: 4px 8px;
}
.daterangepicker.show-ranges.single.rtl .drp-calendar.left {
border-right: 1px solid #ddd;
}
.daterangepicker.show-ranges.single.ltr .drp-calendar.left {
border-left: 1px solid #ddd;
}
.daterangepicker.show-ranges.rtl .drp-calendar.right {
border-right: 1px solid #ddd;
}
.daterangepicker.show-ranges.ltr .drp-calendar.left {
border-left: 1px solid #ddd;
}
.daterangepicker .ranges {
float: none;
text-align: left;
margin: 0;
}
.daterangepicker.show-calendar .ranges {
margin-top: 8px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0 auto;
padding: 0;
width: 100%;
}
.daterangepicker .ranges li {
font-size: 12px;
padding: 8px 12px;
cursor: pointer;
}
.daterangepicker .ranges li:hover {
background-color: #eee;
}
.daterangepicker .ranges li.active {
background-color: #08c;
color: #fff;
}
/* Larger Screen Styling */
@media (min-width: 564px) {
.daterangepicker {
width: auto;
}
.daterangepicker .ranges ul {
width: 140px;
}
.daterangepicker.single .ranges ul {
width: 100%;
}
.daterangepicker.single .drp-calendar.left {
clear: none;
}
.daterangepicker.single .ranges, .daterangepicker.single .drp-calendar {
float: left;
}
.daterangepicker {
direction: ltr;
text-align: left;
}
.daterangepicker .drp-calendar.left {
clear: left;
margin-right: 0;
}
.daterangepicker .drp-calendar.left .calendar-table {
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.daterangepicker .drp-calendar.right {
margin-left: 0;
}
.daterangepicker .drp-calendar.right .calendar-table {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.daterangepicker .drp-calendar.left .calendar-table {
padding-right: 8px;
}
.daterangepicker .ranges, .daterangepicker .drp-calendar {
float: left;
}
}
@media (min-width: 730px) {
.daterangepicker .ranges {
width: auto;
}
.daterangepicker .ranges {
float: left;
}
.daterangepicker.rtl .ranges {
float: right;
}
.daterangepicker .drp-calendar.left {
clear: none !important;
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,117 @@
.flatpickr-monthSelect-months {
margin: 10px 1px 3px 1px;
flex-wrap: wrap;
}
.flatpickr-monthSelect-month {
background: none;
border: 1px solid transparent;
border-radius: 4px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: #393939;
cursor: pointer;
display: inline-block;
font-weight: 400;
margin: 0.5px;
justify-content: center;
padding: 10px;
position: relative;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
text-align: center;
width: 33%;
}
.flatpickr-monthSelect-month.flatpickr-disabled {
color: #eee;
}
.flatpickr-monthSelect-month.flatpickr-disabled:hover,
.flatpickr-monthSelect-month.flatpickr-disabled:focus {
cursor: not-allowed;
background: none !important;
}
.flatpickr-monthSelect-theme-dark {
background: #3f4458;
}
.flatpickr-monthSelect-theme-dark .flatpickr-current-month input.cur-year {
color: #fff;
}
.flatpickr-monthSelect-theme-dark .flatpickr-months .flatpickr-prev-month,
.flatpickr-monthSelect-theme-dark .flatpickr-months .flatpickr-next-month {
color: #fff;
fill: #fff;
}
.flatpickr-monthSelect-theme-dark .flatpickr-monthSelect-month {
color: rgba(255, 255, 255, 0.95);
}
.flatpickr-monthSelect-month.today {
border-color: #959ea9;
}
.flatpickr-monthSelect-month.inRange,
.flatpickr-monthSelect-month.inRange.today,
.flatpickr-monthSelect-month:hover,
.flatpickr-monthSelect-month:focus {
background: #e6e6e6;
cursor: pointer;
outline: 0;
border-color: #e6e6e6;
}
.flatpickr-monthSelect-theme-dark .flatpickr-monthSelect-month.inRange,
.flatpickr-monthSelect-theme-dark .flatpickr-monthSelect-month:hover,
.flatpickr-monthSelect-theme-dark .flatpickr-monthSelect-month:focus {
background: #646c8c;
border-color: #646c8c;
}
.flatpickr-monthSelect-month.today:hover,
.flatpickr-monthSelect-month.today:focus {
background: #959ea9;
border-color: #959ea9;
color: #fff;
}
.flatpickr-monthSelect-month.selected,
.flatpickr-monthSelect-month.startRange,
.flatpickr-monthSelect-month.endRange {
background-color: #569ff7;
box-shadow: none;
color: #fff;
border-color: #569ff7;
}
.flatpickr-monthSelect-month.startRange {
border-radius: 50px 0 0 50px;
}
.flatpickr-monthSelect-month.endRange {
border-radius: 0 50px 50px 0;
}
.flatpickr-monthSelect-month.startRange.endRange {
border-radius: 50px;
}
.flatpickr-monthSelect-month.inRange {
border-radius: 0;
box-shadow: -5px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
}
.flatpickr-monthSelect-theme-dark .flatpickr-monthSelect-month.selected,
.flatpickr-monthSelect-theme-dark .flatpickr-monthSelect-month.startRange,
.flatpickr-monthSelect-theme-dark .flatpickr-monthSelect-month.endRange {
background: #80cbc4;
-webkit-box-shadow: none;
box-shadow: none;
color: #fff;
border-color: #80cbc4;
}
@@ -0,0 +1,301 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.monthSelectPlugin = factory());
}(this, (function () { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var monthToStr = function (monthNumber, shorthand, locale) { return locale.months[shorthand ? "shorthand" : "longhand"][monthNumber]; };
function clearNode(node) {
while (node.firstChild)
node.removeChild(node.firstChild);
}
function getEventTarget(event) {
try {
if (typeof event.composedPath === "function") {
var path = event.composedPath();
return path[0];
}
return event.target;
}
catch (error) {
return event.target;
}
}
var defaultConfig = {
shorthand: false,
dateFormat: "F Y",
altFormat: "F Y",
theme: "light",
};
function monthSelectPlugin(pluginConfig) {
var config = __assign(__assign({}, defaultConfig), pluginConfig);
return function (fp) {
fp.config.dateFormat = config.dateFormat;
fp.config.altFormat = config.altFormat;
var self = { monthsContainer: null };
function clearUnnecessaryDOMElements() {
if (!fp.rContainer)
return;
clearNode(fp.rContainer);
for (var index = 0; index < fp.monthElements.length; index++) {
var element = fp.monthElements[index];
if (!element.parentNode)
continue;
element.parentNode.removeChild(element);
}
}
function build() {
if (!fp.rContainer)
return;
self.monthsContainer = fp._createElement("div", "flatpickr-monthSelect-months");
self.monthsContainer.tabIndex = -1;
buildMonths();
fp.rContainer.appendChild(self.monthsContainer);
fp.calendarContainer.classList.add("flatpickr-monthSelect-theme-" + config.theme);
}
function buildMonths() {
if (!self.monthsContainer)
return;
clearNode(self.monthsContainer);
var frag = document.createDocumentFragment();
for (var i = 0; i < 12; i++) {
var month = fp.createDay("flatpickr-monthSelect-month", new Date(fp.currentYear, i), 0, i);
if (month.dateObj.getMonth() === new Date().getMonth() &&
month.dateObj.getFullYear() === new Date().getFullYear())
month.classList.add("today");
month.textContent = monthToStr(i, config.shorthand, fp.l10n);
month.addEventListener("click", selectMonth);
frag.appendChild(month);
}
self.monthsContainer.appendChild(frag);
if (fp.config.minDate &&
fp.currentYear === fp.config.minDate.getFullYear())
fp.prevMonthNav.classList.add("flatpickr-disabled");
else
fp.prevMonthNav.classList.remove("flatpickr-disabled");
if (fp.config.maxDate &&
fp.currentYear === fp.config.maxDate.getFullYear())
fp.nextMonthNav.classList.add("flatpickr-disabled");
else
fp.nextMonthNav.classList.remove("flatpickr-disabled");
}
function bindEvents() {
fp._bind(fp.prevMonthNav, "click", function (e) {
e.preventDefault();
e.stopPropagation();
fp.changeYear(fp.currentYear - 1);
selectYear();
buildMonths();
});
fp._bind(fp.nextMonthNav, "click", function (e) {
e.preventDefault();
e.stopPropagation();
fp.changeYear(fp.currentYear + 1);
selectYear();
buildMonths();
});
fp._bind(self.monthsContainer, "mouseover", function (e) {
if (fp.config.mode === "range")
fp.onMouseOver(getEventTarget(e), "flatpickr-monthSelect-month");
});
}
function setCurrentlySelected() {
if (!fp.rContainer)
return;
if (!fp.selectedDates.length)
return;
var currentlySelected = fp.rContainer.querySelectorAll(".flatpickr-monthSelect-month.selected");
for (var index = 0; index < currentlySelected.length; index++) {
currentlySelected[index].classList.remove("selected");
}
var targetMonth = fp.selectedDates[0].getMonth();
var month = fp.rContainer.querySelector(".flatpickr-monthSelect-month:nth-child(" + (targetMonth + 1) + ")");
if (month) {
month.classList.add("selected");
}
}
function selectYear() {
var selectedDate = fp.selectedDates[0];
if (selectedDate) {
selectedDate = new Date(selectedDate);
selectedDate.setFullYear(fp.currentYear);
if (fp.config.minDate && selectedDate < fp.config.minDate) {
selectedDate = fp.config.minDate;
}
if (fp.config.maxDate && selectedDate > fp.config.maxDate) {
selectedDate = fp.config.maxDate;
}
fp.currentYear = selectedDate.getFullYear();
}
fp.currentYearElement.value = String(fp.currentYear);
if (fp.rContainer) {
var months = fp.rContainer.querySelectorAll(".flatpickr-monthSelect-month");
months.forEach(function (month) {
month.dateObj.setFullYear(fp.currentYear);
if ((fp.config.minDate && month.dateObj < fp.config.minDate) ||
(fp.config.maxDate && month.dateObj > fp.config.maxDate)) {
month.classList.add("flatpickr-disabled");
}
else {
month.classList.remove("flatpickr-disabled");
}
});
}
setCurrentlySelected();
}
function selectMonth(e) {
e.preventDefault();
e.stopPropagation();
var eventTarget = getEventTarget(e);
if (!(eventTarget instanceof Element))
return;
if (eventTarget.classList.contains("flatpickr-disabled"))
return;
if (eventTarget.classList.contains("notAllowed"))
return; // necessary??
setMonth(eventTarget.dateObj);
if (fp.config.closeOnSelect) {
var single = fp.config.mode === "single";
var range = fp.config.mode === "range" && fp.selectedDates.length === 2;
if (single || range)
fp.close();
}
}
function setMonth(date) {
var selectedDate = new Date(fp.currentYear, date.getMonth(), date.getDate());
var selectedDates = [];
switch (fp.config.mode) {
case "single":
selectedDates = [selectedDate];
break;
case "multiple":
selectedDates.push(selectedDate);
break;
case "range":
if (fp.selectedDates.length === 2) {
selectedDates = [selectedDate];
}
else {
selectedDates = fp.selectedDates.concat([selectedDate]);
selectedDates.sort(function (a, b) { return a.getTime() - b.getTime(); });
}
break;
}
fp.setDate(selectedDates, true);
setCurrentlySelected();
}
var shifts = {
37: -1,
39: 1,
40: 3,
38: -3,
};
function onKeyDown(_, __, ___, e) {
var shouldMove = shifts[e.keyCode] !== undefined;
if (!shouldMove && e.keyCode !== 13) {
return;
}
if (!fp.rContainer || !self.monthsContainer)
return;
var currentlySelected = fp.rContainer.querySelector(".flatpickr-monthSelect-month.selected");
var index = Array.prototype.indexOf.call(self.monthsContainer.children, document.activeElement);
if (index === -1) {
var target = currentlySelected || self.monthsContainer.firstElementChild;
target.focus();
index = target.$i;
}
if (shouldMove) {
self.monthsContainer.children[(12 + index + shifts[e.keyCode]) % 12].focus();
}
else if (e.keyCode === 13 &&
self.monthsContainer.contains(document.activeElement)) {
setMonth(document.activeElement.dateObj);
}
}
function closeHook() {
var _a;
if (((_a = fp.config) === null || _a === void 0 ? void 0 : _a.mode) === "range" && fp.selectedDates.length === 1)
fp.clear(false);
if (!fp.selectedDates.length)
buildMonths();
}
// Help the prev/next year nav honor config.minDate (see 3fa5a69)
function stubCurrentMonth() {
config._stubbedCurrentMonth = fp._initialDate.getMonth();
fp._initialDate.setMonth(config._stubbedCurrentMonth);
fp.currentMonth = config._stubbedCurrentMonth;
}
function unstubCurrentMonth() {
if (!config._stubbedCurrentMonth)
return;
fp._initialDate.setMonth(config._stubbedCurrentMonth);
fp.currentMonth = config._stubbedCurrentMonth;
delete config._stubbedCurrentMonth;
}
function destroyPluginInstance() {
if (self.monthsContainer !== null) {
var months = self.monthsContainer.querySelectorAll(".flatpickr-monthSelect-month");
for (var index = 0; index < months.length; index++) {
months[index].removeEventListener("click", selectMonth);
}
}
}
return {
onParseConfig: function () {
fp.config.enableTime = false;
},
onValueUpdate: setCurrentlySelected,
onKeyDown: onKeyDown,
onReady: [
stubCurrentMonth,
clearUnnecessaryDOMElements,
build,
bindEvents,
setCurrentlySelected,
function () {
fp.config.onClose.push(closeHook);
fp.loadedPlugins.push("monthSelect");
},
],
onDestroy: [
unstubCurrentMonth,
destroyPluginInstance,
function () {
fp.config.onClose = fp.config.onClose.filter(function (hook) { return hook !== closeHook; });
},
],
};
};
}
return monthSelectPlugin;
})));
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,234 @@
.datatable-wrapper.no-header .datatable-container {
border-top: 1px solid #d9d9d9;
}
.datatable-wrapper.no-footer .datatable-container {
border-bottom: 1px solid #d9d9d9;
}
.datatable-top,
.datatable-bottom {
padding: 8px 10px;
}
.datatable-top > nav:first-child,
.datatable-top > div:first-child,
.datatable-bottom > nav:first-child,
.datatable-bottom > div:first-child {
float: left;
}
.datatable-top > nav:last-child,
.datatable-top > div:not(first-child),
.datatable-bottom > nav:last-child,
.datatable-bottom > div:last-child {
float: right;
}
.datatable-selector {
padding: 6px;
}
.datatable-input {
padding: 6px 12px;
}
.datatable-info {
margin: 7px 0;
}
/* PAGER */
.datatable-pagination ul {
margin: 0;
padding-left: 0;
}
.datatable-pagination li {
list-style: none;
float: left;
}
.datatable-pagination li.datatable-hidden {
visibility: hidden;
}
.datatable-pagination a,
.datatable-pagination button {
border: 1px solid transparent;
float: left;
margin-left: 2px;
padding: 6px 12px;
position: relative;
text-decoration: none;
color: #333;
cursor: pointer;
}
.datatable-pagination a:hover,
.datatable-pagination button:hover {
background-color: #d9d9d9;
}
.datatable-pagination .datatable-active a,
.datatable-pagination .datatable-active a:focus,
.datatable-pagination .datatable-active a:hover,
.datatable-pagination .datatable-active button,
.datatable-pagination .datatable-active button:focus,
.datatable-pagination .datatable-active button:hover {
background-color: #d9d9d9;
cursor: default;
}
.datatable-pagination .datatable-ellipsis a,
.datatable-pagination .datatable-disabled a,
.datatable-pagination .datatable-disabled a:focus,
.datatable-pagination .datatable-disabled a:hover,
.datatable-pagination .datatable-ellipsis button,
.datatable-pagination .datatable-disabled button,
.datatable-pagination .datatable-disabled button:focus,
.datatable-pagination .datatable-disabled button:hover {
pointer-events: none;
cursor: default;
}
.datatable-pagination .datatable-disabled a,
.datatable-pagination .datatable-disabled a:focus,
.datatable-pagination .datatable-disabled a:hover,
.datatable-pagination .datatable-disabled button,
.datatable-pagination .datatable-disabled button:focus,
.datatable-pagination .datatable-disabled button:hover {
cursor: not-allowed;
opacity: 0.4;
}
.datatable-pagination .datatable-pagination a,
.datatable-pagination .datatable-pagination button {
font-weight: bold;
}
/* TABLE */
.datatable-table {
max-width: 100%;
width: 100%;
border-spacing: 0;
border-collapse: separate;
}
.datatable-table > tbody > tr > td,
.datatable-table > tbody > tr > th,
.datatable-table > tfoot > tr > td,
.datatable-table > tfoot > tr > th,
.datatable-table > thead > tr > td,
.datatable-table > thead > tr > th {
vertical-align: top;
padding: 8px 10px;
}
.datatable-table > thead > tr > th {
vertical-align: bottom;
text-align: left;
border-bottom: 1px solid #d9d9d9;
}
.datatable-table > tfoot > tr > th {
vertical-align: bottom;
text-align: left;
border-top: 1px solid #d9d9d9;
}
.datatable-table th {
vertical-align: bottom;
text-align: left;
}
.datatable-table th a {
text-decoration: none;
color: inherit;
}
.datatable-table th button,
.datatable-pagination-list button {
color: inherit;
border: 0;
background-color: inherit;
cursor: pointer;
text-align: inherit;
font-family: inherit;
font-weight: inherit;
font-size: inherit;
}
.datatable-sorter, .datatable-filter {
display: inline-block;
height: 100%;
position: relative;
width: 100%;
}
.datatable-sorter::before,
.datatable-sorter::after {
content: "";
height: 0;
width: 0;
position: absolute;
right: 4px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
opacity: 0.2;
}
.datatable-sorter::before {
border-top: 4px solid #000;
bottom: 0px;
}
.datatable-sorter::after {
border-bottom: 4px solid #000;
border-top: 4px solid transparent;
top: 0px;
}
.datatable-ascending .datatable-sorter::after,
.datatable-descending .datatable-sorter::before,
.datatable-ascending .datatable-filter::after,
.datatable-descending .datatable-filter::before {
opacity: 0.6;
}
.datatable-filter::before {
content: "";
position: absolute;
right: 4px;
opacity: 0.2;
width: 0;
height: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-radius: 50%;
border-top: 10px solid #000;
top: 25%;
}
.datatable-filter-active .datatable-filter::before {
opacity: 0.6;
}
.datatable-empty {
text-align: center;
}
.datatable-top::after, .datatable-bottom::after {
clear: both;
content: " ";
display: table;
}
table.datatable-table:focus tr.datatable-cursor > td:first-child {
border-left: 3px blue solid;
}
table.datatable-table:focus {
outline: solid 1px black;
outline-offset: -1px;
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
@php @php
# Get Current USER # Get Current USER
$loggedUser = strtoUpper(m3_user()); $loggedUser = strtoUpper(m3User());
$admin_users = $power_users = $csr = $execUser = ""; $admin_users = $power_users = $csr = $execUser = "";
$UserCO = $UserWHS = $UserPL = ''; $UserCO = $UserWHS = $UserPL = '';
@@ -25,7 +25,7 @@
<div class="col-auto border-start ps-3"> <div class="col-auto border-start ps-3">
<span class="text-muted small">User</span> <span class="text-muted small">User</span>
<span id="hdr-user" class="ms-1"> <span id="hdr-user" class="ms-1">
{{ $palletHeader->R659USER ?? (m3_user() !== '' ? Str::upper(m3_user()) : '—') }} {{ $palletHeader->R659USER ?? (m3User() !== '' ? Str::upper(m3User()) : '—') }}
</span> </span>
</div> </div>
<div class="col-auto border-start ps-3"> <div class="col-auto border-start ps-3">

Some files were not shown because too many files have changed in this diff Show More