Compare commits

...
32 Commits
Author SHA1 Message Date
Mark Gregory 1602bdd72e new source export command 2026-09-16 17:10:47 +01:00
Mark Gregory 14357ade4d updates to M3 module 2026-09-16 16:55:33 +01:00
Mark Gregory 0237b1dc2a updates to M3 module 2026-09-16 16:51:59 +01:00
Mark Gregory ce06aa9fac updates to M3 module 2026-09-16 16:46:15 +01:00
Mark Gregory 13a7e804af updates to M3 module 2026-09-16 15:33:20 +01:00
Mark Gregory d1a83c9b09 updates to M3 module 2026-09-16 14:28:17 +01:00
Mark Gregory 3410ab85fe new deployer command 2026-09-16 13:22:16 +01:00
Mark Gregory fac5df8cc3 new deployer command 2026-09-16 12:58:18 +01:00
Mark Gregory 21ee6e951a new deployer command 2026-09-16 12:58:04 +01:00
Mark Gregory f02f801b31 new deployer command 2026-09-16 12:36:03 +01:00
Mark Gregory a137ffe852 new deployer command 2026-09-16 12:32:50 +01:00
Mark Gregory 856038c762 new deployer command 2026-09-16 12:28:01 +01:00
Mark Gregory 5d58e35275 new deployer command 2026-09-16 12:14:43 +01:00
Mark Gregory 16fe344eef new deployer command 2026-09-16 12:11:57 +01:00
Mark Gregory 4375fc729f new deployer command 2026-09-16 12:01:06 +01:00
Mark Gregory 37a116437e new deployer command 2026-09-16 11:59:22 +01:00
Mark Gregory 56b5cb91b0 new deployer command 2026-09-16 11:55:55 +01:00
Mark Gregory ce79962289 new deployer command 2026-09-16 11:53:35 +01:00
Mark Gregory 5aeeb87b21 new deployer command 2026-09-16 11:41:19 +01:00
Mark Gregory 744968120a new deployer command 2026-09-16 11:41:15 +01:00
Mark Gregory 8c0b214a9d new deployer command 2026-09-16 11:40:29 +01:00
Mark Gregory 279d2761e3 new deployer command 2026-09-16 11:38:53 +01:00
Mark Gregory 20bdb82ef9 new deployer command 2026-09-16 11:36:20 +01:00
Mark Gregory 66e38cfe52 new deployer command 2026-09-16 11:33:44 +01:00
Mark Gregory 8105d1cb6a new deployer command 2026-09-16 11:28:40 +01:00
Mark Gregory 84a628277d new deployer command 2026-09-16 11:16:10 +01:00
Mark Gregory 45a370ed91 new deployer command 2026-09-16 11:01:02 +01:00
Mark Gregory 0756b1de79 new deployer command 2026-09-16 10:59:08 +01:00
Mark Gregory cf2b61f964 new deployer command 2026-09-16 10:55:04 +01:00
Mark Gregory 449fb0bf91 new deployer command 2026-09-16 10:52:27 +01:00
Mark Gregory ee8bd29bd6 new deployer command 2026-09-16 10:47:03 +01:00
Mark Gregory 01c21d12b9 Add npm package lock 2026-09-16 10:46:38 +01:00
273 changed files with 78017 additions and 1645 deletions
+1 -1
View File
@@ -26,7 +26,6 @@ yarn-error.log
/.nova
/.vscode
/.zed
package-lock.json
composer.lock
/provision/
/storage/logs/
@@ -39,3 +38,4 @@ composer.lock
/storage/framework/
/storage/debugbar/
/phpunit.dusk.xml
/sonarqube-issues.csv
@@ -64,6 +64,7 @@
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light">
<tr>
<th>Metric</th>
@@ -64,6 +64,7 @@
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light">
<tr>
<th>When</th>
@@ -63,9 +63,7 @@ class AuditTrailServiceProvider extends ServiceProvider
$commandPath = __DIR__ . '/../Console';
$commands = collect(File::allFiles($commandPath))
->map(function ($file) {
$class = 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
return $class;
return 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
})
->filter(function ($class) {
return class_exists($class);
@@ -14,18 +14,6 @@ class RouteServiceProvider extends ServiceProvider
*/
protected $moduleNamespace = 'Modules\AuditTrail\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.
*
@@ -64,6 +64,7 @@
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light">
<tr>
<th>Carrier</th>
@@ -26,40 +26,37 @@ class DeleteOpenAiFilesCommand extends Command
return self::FAILURE;
}
$service = new OpenAiFileService((string) $setting->api_key);
try {
if ((bool) $this->option('dry-run')) {
$files = $service->listFiles();
$this->info('Found ' . count($files) . ' OpenAI API file(s).');
foreach ($files as $file) {
$this->line(sprintf(
'- %s %s',
(string) ($file['id'] ?? 'unknown'),
(string) ($file['filename'] ?? 'unnamed'),
));
}
return self::SUCCESS;
}
$result = $service->deleteAllFiles();
$this->info("Listed: {$result['listed']}");
$this->info("Deleted: {$result['deleted']}");
$this->info("Failed: {$result['failed']}");
foreach ($result['failures'] as $failure) {
$this->error($failure['file_id'] . ': ' . $failure['error']);
}
return $result['failed'] === 0 ? self::SUCCESS : self::FAILURE;
return $this->executeFor(new OpenAiFileService((string) $setting->api_key));
} catch (Throwable $exception) {
$this->error($exception->getMessage());
return self::FAILURE;
}
}
private function executeFor(OpenAiFileService $service): int
{
if ((bool) $this->option('dry-run')) {
$files = $service->listFiles();
$this->info('Found ' . count($files) . ' OpenAI API file(s).');
foreach ($files as $file) {
$this->line(sprintf('- %s %s', (string) ($file['id'] ?? 'unknown'), (string) ($file['filename'] ?? 'unnamed')));
}
return self::SUCCESS;
}
$result = $service->deleteAllFiles();
$this->info("Listed: {$result['listed']}");
$this->info("Deleted: {$result['deleted']}");
$this->info("Failed: {$result['failed']}");
foreach ($result['failures'] as $failure) {
$this->error($failure['file_id'] . ': ' . $failure['error']);
}
return $result['failed'] === 0 ? self::SUCCESS : self::FAILURE;
}
}
@@ -16,6 +16,5 @@ class ChatGPTDatabaseSeeder extends Seeder
{
Model::unguard();
// $this->call("OthersTableSeeder");
}
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Modules\ChatGPT\Exceptions;
use RuntimeException;
final class OpenAiFileException extends RuntimeException {}
@@ -79,9 +79,7 @@ class ChatGPTServiceProvider extends ServiceProvider
$commandPath = __DIR__ . '/../Console';
$commands = collect(File::allFiles($commandPath))
->map(function ($file) {
$class = 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
return $class;
return 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
})
->filter(function ($class) {
return class_exists($class);
@@ -14,18 +14,6 @@ class RouteServiceProvider extends ServiceProvider
*/
protected $moduleNamespace = 'Modules\ChatGPT\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.
*
@@ -82,6 +82,7 @@
<div class="mt-5 overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 text-sm">
<caption class="sr-only">Data table</caption>
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">
<tr>
<th class="px-4 py-3">Name</th>
@@ -4,7 +4,7 @@ namespace Modules\ChatGPT\Services;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;
use RuntimeException;
use Modules\ChatGPT\Exceptions\OpenAiFileException;
class OpenAiFileService
{
@@ -33,7 +33,7 @@ class OpenAiFileService
->get(self::BASE_URL . '/files', $query);
if ($response->failed()) {
throw new RuntimeException('OpenAI file list request failed: ' . $response->body());
throw new OpenAiFileException('OpenAI file list request failed: ' . $response->body());
}
$payload = $response->json();
@@ -63,7 +63,7 @@ class OpenAiFileService
->delete(self::BASE_URL . '/files/' . rawurlencode($fileId));
if ($response->failed()) {
throw new RuntimeException("OpenAI file delete request failed for {$fileId}: " . $response->body());
throw new OpenAiFileException("OpenAI file delete request failed for {$fileId}: " . $response->body());
}
return $response->json() ?? [];
@@ -90,7 +90,7 @@ class OpenAiFileService
try {
$this->deleteFile($fileId);
$deleted++;
} catch (RuntimeException $exception) {
} catch (OpenAiFileException $exception) {
$failures[] = [
'file_id' => $fileId,
'error' => $exception->getMessage(),
@@ -11,6 +11,8 @@ use Modules\Warehouse\app\Models\Warehouse;
class CompanyAddressController extends Controller
{
private const MAX_STRING = 'max:255';
public function updateCompany(Request $request, Company $company)
{
$this->upsertAddress($request, $company);
@@ -40,12 +42,12 @@ class CompanyAddressController extends Controller
{
$data = $request->validate([
'label' => ['nullable', 'string', 'max:80'],
'attention' => ['nullable', 'string', 'max:255'],
'line_1' => ['required', 'string', 'max:255'],
'line_2' => ['nullable', 'string', 'max:255'],
'line_3' => ['nullable', 'string', 'max:255'],
'locality' => ['required', 'string', 'max:255'],
'administrative_area' => ['nullable', 'string', 'max:255'],
'attention' => ['nullable', 'string', self::MAX_STRING],
'line_1' => ['required', 'string', self::MAX_STRING],
'line_2' => ['nullable', 'string', self::MAX_STRING],
'line_3' => ['nullable', 'string', self::MAX_STRING],
'locality' => ['required', 'string', self::MAX_STRING],
'administrative_area' => ['nullable', 'string', self::MAX_STRING],
'postal_code' => ['nullable', 'string', 'max:32'],
'country_id' => ['required', Rule::exists('countries', 'id')->where('is_active', true)],
'country_subdivision_id' => ['nullable', 'exists:country_subdivisions,id'],
@@ -45,7 +45,7 @@ class CompanyController extends Controller
*/
public function store(Request $request)
{
$company = Company::create($this->validated($request));
Company::create($this->validated($request));
return redirect()
->route('platform.companies.index')
@@ -109,6 +109,7 @@
<div class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
<table class="min-w-full text-sm">
<caption class="sr-only">Data table</caption>
<thead>
<tr class="border-b border-zinc-200 dark:border-zinc-800">
<th class="p-4 text-left">Name</th>
@@ -64,6 +64,7 @@
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light">
<tr>
<th>Setting</th>
File diff suppressed because it is too large Load Diff
@@ -11,257 +11,12 @@ class IsoCountriesSeeder extends Seeder
public function run(): void
{
$now = Carbon::now();
$countries = [
['alpha2' => 'AD', 'alpha3' => 'AND', 'numeric' => '020', 'name' => 'Andorra', 'official_name' => 'Principality of Andorra', 'common_name' => null, 'flag_emoji' => '🇦🇩'],
['alpha2' => 'AE', 'alpha3' => 'ARE', 'numeric' => '784', 'name' => 'United Arab Emirates', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇦🇪'],
['alpha2' => 'AF', 'alpha3' => 'AFG', 'numeric' => '004', 'name' => 'Afghanistan', 'official_name' => 'Islamic Republic of Afghanistan', 'common_name' => null, 'flag_emoji' => '🇦🇫'],
['alpha2' => 'AG', 'alpha3' => 'ATG', 'numeric' => '028', 'name' => 'Antigua and Barbuda', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇦🇬'],
['alpha2' => 'AI', 'alpha3' => 'AIA', 'numeric' => '660', 'name' => 'Anguilla', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇦🇮'],
['alpha2' => 'AL', 'alpha3' => 'ALB', 'numeric' => '008', 'name' => 'Albania', 'official_name' => 'Republic of Albania', 'common_name' => null, 'flag_emoji' => '🇦🇱'],
['alpha2' => 'AM', 'alpha3' => 'ARM', 'numeric' => '051', 'name' => 'Armenia', 'official_name' => 'Republic of Armenia', 'common_name' => null, 'flag_emoji' => '🇦🇲'],
['alpha2' => 'AO', 'alpha3' => 'AGO', 'numeric' => '024', 'name' => 'Angola', 'official_name' => 'Republic of Angola', 'common_name' => null, 'flag_emoji' => '🇦🇴'],
['alpha2' => 'AQ', 'alpha3' => 'ATA', 'numeric' => '010', 'name' => 'Antarctica', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇦🇶'],
['alpha2' => 'AR', 'alpha3' => 'ARG', 'numeric' => '032', 'name' => 'Argentina', 'official_name' => 'Argentine Republic', 'common_name' => null, 'flag_emoji' => '🇦🇷'],
['alpha2' => 'AS', 'alpha3' => 'ASM', 'numeric' => '016', 'name' => 'American Samoa', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇦🇸'],
['alpha2' => 'AT', 'alpha3' => 'AUT', 'numeric' => '040', 'name' => 'Austria', 'official_name' => 'Republic of Austria', 'common_name' => null, 'flag_emoji' => '🇦🇹'],
['alpha2' => 'AU', 'alpha3' => 'AUS', 'numeric' => '036', 'name' => 'Australia', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇦🇺'],
['alpha2' => 'AW', 'alpha3' => 'ABW', 'numeric' => '533', 'name' => 'Aruba', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇦🇼'],
['alpha2' => 'AX', 'alpha3' => 'ALA', 'numeric' => '248', 'name' => 'Åland Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇦🇽'],
['alpha2' => 'AZ', 'alpha3' => 'AZE', 'numeric' => '031', 'name' => 'Azerbaijan', 'official_name' => 'Republic of Azerbaijan', 'common_name' => null, 'flag_emoji' => '🇦🇿'],
['alpha2' => 'BA', 'alpha3' => 'BIH', 'numeric' => '070', 'name' => 'Bosnia and Herzegovina', 'official_name' => 'Republic of Bosnia and Herzegovina', 'common_name' => null, 'flag_emoji' => '🇧🇦'],
['alpha2' => 'BB', 'alpha3' => 'BRB', 'numeric' => '052', 'name' => 'Barbados', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇧🇧'],
['alpha2' => 'BD', 'alpha3' => 'BGD', 'numeric' => '050', 'name' => 'Bangladesh', 'official_name' => 'People\'s Republic of Bangladesh', 'common_name' => null, 'flag_emoji' => '🇧🇩'],
['alpha2' => 'BE', 'alpha3' => 'BEL', 'numeric' => '056', 'name' => 'Belgium', 'official_name' => 'Kingdom of Belgium', 'common_name' => null, 'flag_emoji' => '🇧🇪'],
['alpha2' => 'BF', 'alpha3' => 'BFA', 'numeric' => '854', 'name' => 'Burkina Faso', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇧🇫'],
['alpha2' => 'BG', 'alpha3' => 'BGR', 'numeric' => '100', 'name' => 'Bulgaria', 'official_name' => 'Republic of Bulgaria', 'common_name' => null, 'flag_emoji' => '🇧🇬'],
['alpha2' => 'BH', 'alpha3' => 'BHR', 'numeric' => '048', 'name' => 'Bahrain', 'official_name' => 'Kingdom of Bahrain', 'common_name' => null, 'flag_emoji' => '🇧🇭'],
['alpha2' => 'BI', 'alpha3' => 'BDI', 'numeric' => '108', 'name' => 'Burundi', 'official_name' => 'Republic of Burundi', 'common_name' => null, 'flag_emoji' => '🇧🇮'],
['alpha2' => 'BJ', 'alpha3' => 'BEN', 'numeric' => '204', 'name' => 'Benin', 'official_name' => 'Republic of Benin', 'common_name' => null, 'flag_emoji' => '🇧🇯'],
['alpha2' => 'BL', 'alpha3' => 'BLM', 'numeric' => '652', 'name' => 'Saint Barthélemy', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇧🇱'],
['alpha2' => 'BM', 'alpha3' => 'BMU', 'numeric' => '060', 'name' => 'Bermuda', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇧🇲'],
['alpha2' => 'BN', 'alpha3' => 'BRN', 'numeric' => '096', 'name' => 'Brunei Darussalam', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇧🇳'],
['alpha2' => 'BO', 'alpha3' => 'BOL', 'numeric' => '068', 'name' => 'Bolivia, Plurinational State of', 'official_name' => 'Plurinational State of Bolivia', 'common_name' => 'Bolivia', 'flag_emoji' => '🇧🇴'],
['alpha2' => 'BQ', 'alpha3' => 'BES', 'numeric' => '535', 'name' => 'Bonaire, Sint Eustatius and Saba', 'official_name' => 'Bonaire, Sint Eustatius and Saba', 'common_name' => null, 'flag_emoji' => '🇧🇶'],
['alpha2' => 'BR', 'alpha3' => 'BRA', 'numeric' => '076', 'name' => 'Brazil', 'official_name' => 'Federative Republic of Brazil', 'common_name' => null, 'flag_emoji' => '🇧🇷'],
['alpha2' => 'BS', 'alpha3' => 'BHS', 'numeric' => '044', 'name' => 'Bahamas', 'official_name' => 'Commonwealth of the Bahamas', 'common_name' => null, 'flag_emoji' => '🇧🇸'],
['alpha2' => 'BT', 'alpha3' => 'BTN', 'numeric' => '064', 'name' => 'Bhutan', 'official_name' => 'Kingdom of Bhutan', 'common_name' => null, 'flag_emoji' => '🇧🇹'],
['alpha2' => 'BV', 'alpha3' => 'BVT', 'numeric' => '074', 'name' => 'Bouvet Island', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇧🇻'],
['alpha2' => 'BW', 'alpha3' => 'BWA', 'numeric' => '072', 'name' => 'Botswana', 'official_name' => 'Republic of Botswana', 'common_name' => null, 'flag_emoji' => '🇧🇼'],
['alpha2' => 'BY', 'alpha3' => 'BLR', 'numeric' => '112', 'name' => 'Belarus', 'official_name' => 'Republic of Belarus', 'common_name' => null, 'flag_emoji' => '🇧🇾'],
['alpha2' => 'BZ', 'alpha3' => 'BLZ', 'numeric' => '084', 'name' => 'Belize', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇧🇿'],
['alpha2' => 'CA', 'alpha3' => 'CAN', 'numeric' => '124', 'name' => 'Canada', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇨🇦'],
['alpha2' => 'CC', 'alpha3' => 'CCK', 'numeric' => '166', 'name' => 'Cocos (Keeling) Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇨🇨'],
['alpha2' => 'CD', 'alpha3' => 'COD', 'numeric' => '180', 'name' => 'Congo, The Democratic Republic of the', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇨🇩'],
['alpha2' => 'CF', 'alpha3' => 'CAF', 'numeric' => '140', 'name' => 'Central African Republic', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇨🇫'],
['alpha2' => 'CG', 'alpha3' => 'COG', 'numeric' => '178', 'name' => 'Congo', 'official_name' => 'Republic of the Congo', 'common_name' => null, 'flag_emoji' => '🇨🇬'],
['alpha2' => 'CH', 'alpha3' => 'CHE', 'numeric' => '756', 'name' => 'Switzerland', 'official_name' => 'Swiss Confederation', 'common_name' => null, 'flag_emoji' => '🇨🇭'],
['alpha2' => 'CI', 'alpha3' => 'CIV', 'numeric' => '384', 'name' => 'Côte d\'Ivoire', 'official_name' => 'Republic of Côte d\'Ivoire', 'common_name' => null, 'flag_emoji' => '🇨🇮'],
['alpha2' => 'CK', 'alpha3' => 'COK', 'numeric' => '184', 'name' => 'Cook Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇨🇰'],
['alpha2' => 'CL', 'alpha3' => 'CHL', 'numeric' => '152', 'name' => 'Chile', 'official_name' => 'Republic of Chile', 'common_name' => null, 'flag_emoji' => '🇨🇱'],
['alpha2' => 'CM', 'alpha3' => 'CMR', 'numeric' => '120', 'name' => 'Cameroon', 'official_name' => 'Republic of Cameroon', 'common_name' => null, 'flag_emoji' => '🇨🇲'],
['alpha2' => 'CN', 'alpha3' => 'CHN', 'numeric' => '156', 'name' => 'China', 'official_name' => 'People\'s Republic of China', 'common_name' => null, 'flag_emoji' => '🇨🇳'],
['alpha2' => 'CO', 'alpha3' => 'COL', 'numeric' => '170', 'name' => 'Colombia', 'official_name' => 'Republic of Colombia', 'common_name' => null, 'flag_emoji' => '🇨🇴'],
['alpha2' => 'CR', 'alpha3' => 'CRI', 'numeric' => '188', 'name' => 'Costa Rica', 'official_name' => 'Republic of Costa Rica', 'common_name' => null, 'flag_emoji' => '🇨🇷'],
['alpha2' => 'CU', 'alpha3' => 'CUB', 'numeric' => '192', 'name' => 'Cuba', 'official_name' => 'Republic of Cuba', 'common_name' => null, 'flag_emoji' => '🇨🇺'],
['alpha2' => 'CV', 'alpha3' => 'CPV', 'numeric' => '132', 'name' => 'Cabo Verde', 'official_name' => 'Republic of Cabo Verde', 'common_name' => null, 'flag_emoji' => '🇨🇻'],
['alpha2' => 'CW', 'alpha3' => 'CUW', 'numeric' => '531', 'name' => 'Curaçao', 'official_name' => 'Curaçao', 'common_name' => null, 'flag_emoji' => '🇨🇼'],
['alpha2' => 'CX', 'alpha3' => 'CXR', 'numeric' => '162', 'name' => 'Christmas Island', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇨🇽'],
['alpha2' => 'CY', 'alpha3' => 'CYP', 'numeric' => '196', 'name' => 'Cyprus', 'official_name' => 'Republic of Cyprus', 'common_name' => null, 'flag_emoji' => '🇨🇾'],
['alpha2' => 'CZ', 'alpha3' => 'CZE', 'numeric' => '203', 'name' => 'Czechia', 'official_name' => 'Czech Republic', 'common_name' => null, 'flag_emoji' => '🇨🇿'],
['alpha2' => 'DE', 'alpha3' => 'DEU', 'numeric' => '276', 'name' => 'Germany', 'official_name' => 'Federal Republic of Germany', 'common_name' => null, 'flag_emoji' => '🇩🇪'],
['alpha2' => 'DJ', 'alpha3' => 'DJI', 'numeric' => '262', 'name' => 'Djibouti', 'official_name' => 'Republic of Djibouti', 'common_name' => null, 'flag_emoji' => '🇩🇯'],
['alpha2' => 'DK', 'alpha3' => 'DNK', 'numeric' => '208', 'name' => 'Denmark', 'official_name' => 'Kingdom of Denmark', 'common_name' => null, 'flag_emoji' => '🇩🇰'],
['alpha2' => 'DM', 'alpha3' => 'DMA', 'numeric' => '212', 'name' => 'Dominica', 'official_name' => 'Commonwealth of Dominica', 'common_name' => null, 'flag_emoji' => '🇩🇲'],
['alpha2' => 'DO', 'alpha3' => 'DOM', 'numeric' => '214', 'name' => 'Dominican Republic', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇩🇴'],
['alpha2' => 'DZ', 'alpha3' => 'DZA', 'numeric' => '012', 'name' => 'Algeria', 'official_name' => 'People\'s Democratic Republic of Algeria', 'common_name' => null, 'flag_emoji' => '🇩🇿'],
['alpha2' => 'EC', 'alpha3' => 'ECU', 'numeric' => '218', 'name' => 'Ecuador', 'official_name' => 'Republic of Ecuador', 'common_name' => null, 'flag_emoji' => '🇪🇨'],
['alpha2' => 'EE', 'alpha3' => 'EST', 'numeric' => '233', 'name' => 'Estonia', 'official_name' => 'Republic of Estonia', 'common_name' => null, 'flag_emoji' => '🇪🇪'],
['alpha2' => 'EG', 'alpha3' => 'EGY', 'numeric' => '818', 'name' => 'Egypt', 'official_name' => 'Arab Republic of Egypt', 'common_name' => null, 'flag_emoji' => '🇪🇬'],
['alpha2' => 'EH', 'alpha3' => 'ESH', 'numeric' => '732', 'name' => 'Western Sahara', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇪🇭'],
['alpha2' => 'ER', 'alpha3' => 'ERI', 'numeric' => '232', 'name' => 'Eritrea', 'official_name' => 'the State of Eritrea', 'common_name' => null, 'flag_emoji' => '🇪🇷'],
['alpha2' => 'ES', 'alpha3' => 'ESP', 'numeric' => '724', 'name' => 'Spain', 'official_name' => 'Kingdom of Spain', 'common_name' => null, 'flag_emoji' => '🇪🇸'],
['alpha2' => 'ET', 'alpha3' => 'ETH', 'numeric' => '231', 'name' => 'Ethiopia', 'official_name' => 'Federal Democratic Republic of Ethiopia', 'common_name' => null, 'flag_emoji' => '🇪🇹'],
['alpha2' => 'FI', 'alpha3' => 'FIN', 'numeric' => '246', 'name' => 'Finland', 'official_name' => 'Republic of Finland', 'common_name' => null, 'flag_emoji' => '🇫🇮'],
['alpha2' => 'FJ', 'alpha3' => 'FJI', 'numeric' => '242', 'name' => 'Fiji', 'official_name' => 'Republic of Fiji', 'common_name' => null, 'flag_emoji' => '🇫🇯'],
['alpha2' => 'FK', 'alpha3' => 'FLK', 'numeric' => '238', 'name' => 'Falkland Islands (Malvinas)', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇫🇰'],
['alpha2' => 'FM', 'alpha3' => 'FSM', 'numeric' => '583', 'name' => 'Micronesia, Federated States of', 'official_name' => 'Federated States of Micronesia', 'common_name' => null, 'flag_emoji' => '🇫🇲'],
['alpha2' => 'FO', 'alpha3' => 'FRO', 'numeric' => '234', 'name' => 'Faroe Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇫🇴'],
['alpha2' => 'FR', 'alpha3' => 'FRA', 'numeric' => '250', 'name' => 'France', 'official_name' => 'French Republic', 'common_name' => null, 'flag_emoji' => '🇫🇷'],
['alpha2' => 'GA', 'alpha3' => 'GAB', 'numeric' => '266', 'name' => 'Gabon', 'official_name' => 'Gabonese Republic', 'common_name' => null, 'flag_emoji' => '🇬🇦'],
['alpha2' => 'GB', 'alpha3' => 'GBR', 'numeric' => '826', 'name' => 'United Kingdom', 'official_name' => 'United Kingdom of Great Britain and Northern Ireland', 'common_name' => null, 'flag_emoji' => '🇬🇧'],
['alpha2' => 'GD', 'alpha3' => 'GRD', 'numeric' => '308', 'name' => 'Grenada', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇩'],
['alpha2' => 'GE', 'alpha3' => 'GEO', 'numeric' => '268', 'name' => 'Georgia', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇪'],
['alpha2' => 'GF', 'alpha3' => 'GUF', 'numeric' => '254', 'name' => 'French Guiana', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇫'],
['alpha2' => 'GG', 'alpha3' => 'GGY', 'numeric' => '831', 'name' => 'Guernsey', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇬'],
['alpha2' => 'GH', 'alpha3' => 'GHA', 'numeric' => '288', 'name' => 'Ghana', 'official_name' => 'Republic of Ghana', 'common_name' => null, 'flag_emoji' => '🇬🇭'],
['alpha2' => 'GI', 'alpha3' => 'GIB', 'numeric' => '292', 'name' => 'Gibraltar', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇮'],
['alpha2' => 'GL', 'alpha3' => 'GRL', 'numeric' => '304', 'name' => 'Greenland', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇱'],
['alpha2' => 'GM', 'alpha3' => 'GMB', 'numeric' => '270', 'name' => 'Gambia', 'official_name' => 'Republic of the Gambia', 'common_name' => null, 'flag_emoji' => '🇬🇲'],
['alpha2' => 'GN', 'alpha3' => 'GIN', 'numeric' => '324', 'name' => 'Guinea', 'official_name' => 'Republic of Guinea', 'common_name' => null, 'flag_emoji' => '🇬🇳'],
['alpha2' => 'GP', 'alpha3' => 'GLP', 'numeric' => '312', 'name' => 'Guadeloupe', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇵'],
['alpha2' => 'GQ', 'alpha3' => 'GNQ', 'numeric' => '226', 'name' => 'Equatorial Guinea', 'official_name' => 'Republic of Equatorial Guinea', 'common_name' => null, 'flag_emoji' => '🇬🇶'],
['alpha2' => 'GR', 'alpha3' => 'GRC', 'numeric' => '300', 'name' => 'Greece', 'official_name' => 'Hellenic Republic', 'common_name' => null, 'flag_emoji' => '🇬🇷'],
['alpha2' => 'GS', 'alpha3' => 'SGS', 'numeric' => '239', 'name' => 'South Georgia and the South Sandwich Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇸'],
['alpha2' => 'GT', 'alpha3' => 'GTM', 'numeric' => '320', 'name' => 'Guatemala', 'official_name' => 'Republic of Guatemala', 'common_name' => null, 'flag_emoji' => '🇬🇹'],
['alpha2' => 'GU', 'alpha3' => 'GUM', 'numeric' => '316', 'name' => 'Guam', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇬🇺'],
['alpha2' => 'GW', 'alpha3' => 'GNB', 'numeric' => '624', 'name' => 'Guinea-Bissau', 'official_name' => 'Republic of Guinea-Bissau', 'common_name' => null, 'flag_emoji' => '🇬🇼'],
['alpha2' => 'GY', 'alpha3' => 'GUY', 'numeric' => '328', 'name' => 'Guyana', 'official_name' => 'Republic of Guyana', 'common_name' => null, 'flag_emoji' => '🇬🇾'],
['alpha2' => 'HK', 'alpha3' => 'HKG', 'numeric' => '344', 'name' => 'Hong Kong', 'official_name' => 'Hong Kong Special Administrative Region of China', 'common_name' => null, 'flag_emoji' => '🇭🇰'],
['alpha2' => 'HM', 'alpha3' => 'HMD', 'numeric' => '334', 'name' => 'Heard Island and McDonald Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇭🇲'],
['alpha2' => 'HN', 'alpha3' => 'HND', 'numeric' => '340', 'name' => 'Honduras', 'official_name' => 'Republic of Honduras', 'common_name' => null, 'flag_emoji' => '🇭🇳'],
['alpha2' => 'HR', 'alpha3' => 'HRV', 'numeric' => '191', 'name' => 'Croatia', 'official_name' => 'Republic of Croatia', 'common_name' => null, 'flag_emoji' => '🇭🇷'],
['alpha2' => 'HT', 'alpha3' => 'HTI', 'numeric' => '332', 'name' => 'Haiti', 'official_name' => 'Republic of Haiti', 'common_name' => null, 'flag_emoji' => '🇭🇹'],
['alpha2' => 'HU', 'alpha3' => 'HUN', 'numeric' => '348', 'name' => 'Hungary', 'official_name' => 'Hungary', 'common_name' => null, 'flag_emoji' => '🇭🇺'],
['alpha2' => 'ID', 'alpha3' => 'IDN', 'numeric' => '360', 'name' => 'Indonesia', 'official_name' => 'Republic of Indonesia', 'common_name' => null, 'flag_emoji' => '🇮🇩'],
['alpha2' => 'IE', 'alpha3' => 'IRL', 'numeric' => '372', 'name' => 'Ireland', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇮🇪'],
['alpha2' => 'IL', 'alpha3' => 'ISR', 'numeric' => '376', 'name' => 'Israel', 'official_name' => 'State of Israel', 'common_name' => null, 'flag_emoji' => '🇮🇱'],
['alpha2' => 'IM', 'alpha3' => 'IMN', 'numeric' => '833', 'name' => 'Isle of Man', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇮🇲'],
['alpha2' => 'IN', 'alpha3' => 'IND', 'numeric' => '356', 'name' => 'India', 'official_name' => 'Republic of India', 'common_name' => null, 'flag_emoji' => '🇮🇳'],
['alpha2' => 'IO', 'alpha3' => 'IOT', 'numeric' => '086', 'name' => 'British Indian Ocean Territory', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇮🇴'],
['alpha2' => 'IQ', 'alpha3' => 'IRQ', 'numeric' => '368', 'name' => 'Iraq', 'official_name' => 'Republic of Iraq', 'common_name' => null, 'flag_emoji' => '🇮🇶'],
['alpha2' => 'IR', 'alpha3' => 'IRN', 'numeric' => '364', 'name' => 'Iran, Islamic Republic of', 'official_name' => 'Islamic Republic of Iran', 'common_name' => 'Iran', 'flag_emoji' => '🇮🇷'],
['alpha2' => 'IS', 'alpha3' => 'ISL', 'numeric' => '352', 'name' => 'Iceland', 'official_name' => 'Republic of Iceland', 'common_name' => null, 'flag_emoji' => '🇮🇸'],
['alpha2' => 'IT', 'alpha3' => 'ITA', 'numeric' => '380', 'name' => 'Italy', 'official_name' => 'Italian Republic', 'common_name' => null, 'flag_emoji' => '🇮🇹'],
['alpha2' => 'JE', 'alpha3' => 'JEY', 'numeric' => '832', 'name' => 'Jersey', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇯🇪'],
['alpha2' => 'JM', 'alpha3' => 'JAM', 'numeric' => '388', 'name' => 'Jamaica', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇯🇲'],
['alpha2' => 'JO', 'alpha3' => 'JOR', 'numeric' => '400', 'name' => 'Jordan', 'official_name' => 'Hashemite Kingdom of Jordan', 'common_name' => null, 'flag_emoji' => '🇯🇴'],
['alpha2' => 'JP', 'alpha3' => 'JPN', 'numeric' => '392', 'name' => 'Japan', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇯🇵'],
['alpha2' => 'KE', 'alpha3' => 'KEN', 'numeric' => '404', 'name' => 'Kenya', 'official_name' => 'Republic of Kenya', 'common_name' => null, 'flag_emoji' => '🇰🇪'],
['alpha2' => 'KG', 'alpha3' => 'KGZ', 'numeric' => '417', 'name' => 'Kyrgyzstan', 'official_name' => 'Kyrgyz Republic', 'common_name' => null, 'flag_emoji' => '🇰🇬'],
['alpha2' => 'KH', 'alpha3' => 'KHM', 'numeric' => '116', 'name' => 'Cambodia', 'official_name' => 'Kingdom of Cambodia', 'common_name' => null, 'flag_emoji' => '🇰🇭'],
['alpha2' => 'KI', 'alpha3' => 'KIR', 'numeric' => '296', 'name' => 'Kiribati', 'official_name' => 'Republic of Kiribati', 'common_name' => null, 'flag_emoji' => '🇰🇮'],
['alpha2' => 'KM', 'alpha3' => 'COM', 'numeric' => '174', 'name' => 'Comoros', 'official_name' => 'Union of the Comoros', 'common_name' => null, 'flag_emoji' => '🇰🇲'],
['alpha2' => 'KN', 'alpha3' => 'KNA', 'numeric' => '659', 'name' => 'Saint Kitts and Nevis', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇰🇳'],
['alpha2' => 'KP', 'alpha3' => 'PRK', 'numeric' => '408', 'name' => 'Korea, Democratic People\'s Republic of', 'official_name' => 'Democratic People\'s Republic of Korea', 'common_name' => 'North Korea', 'flag_emoji' => '🇰🇵'],
['alpha2' => 'KR', 'alpha3' => 'KOR', 'numeric' => '410', 'name' => 'Korea, Republic of', 'official_name' => null, 'common_name' => 'South Korea', 'flag_emoji' => '🇰🇷'],
['alpha2' => 'KW', 'alpha3' => 'KWT', 'numeric' => '414', 'name' => 'Kuwait', 'official_name' => 'State of Kuwait', 'common_name' => null, 'flag_emoji' => '🇰🇼'],
['alpha2' => 'KY', 'alpha3' => 'CYM', 'numeric' => '136', 'name' => 'Cayman Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇰🇾'],
['alpha2' => 'KZ', 'alpha3' => 'KAZ', 'numeric' => '398', 'name' => 'Kazakhstan', 'official_name' => 'Republic of Kazakhstan', 'common_name' => null, 'flag_emoji' => '🇰🇿'],
['alpha2' => 'LA', 'alpha3' => 'LAO', 'numeric' => '418', 'name' => 'Lao People\'s Democratic Republic', 'official_name' => null, 'common_name' => 'Laos', 'flag_emoji' => '🇱🇦'],
['alpha2' => 'LB', 'alpha3' => 'LBN', 'numeric' => '422', 'name' => 'Lebanon', 'official_name' => 'Lebanese Republic', 'common_name' => null, 'flag_emoji' => '🇱🇧'],
['alpha2' => 'LC', 'alpha3' => 'LCA', 'numeric' => '662', 'name' => 'Saint Lucia', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇱🇨'],
['alpha2' => 'LI', 'alpha3' => 'LIE', 'numeric' => '438', 'name' => 'Liechtenstein', 'official_name' => 'Principality of Liechtenstein', 'common_name' => null, 'flag_emoji' => '🇱🇮'],
['alpha2' => 'LK', 'alpha3' => 'LKA', 'numeric' => '144', 'name' => 'Sri Lanka', 'official_name' => 'Democratic Socialist Republic of Sri Lanka', 'common_name' => null, 'flag_emoji' => '🇱🇰'],
['alpha2' => 'LR', 'alpha3' => 'LBR', 'numeric' => '430', 'name' => 'Liberia', 'official_name' => 'Republic of Liberia', 'common_name' => null, 'flag_emoji' => '🇱🇷'],
['alpha2' => 'LS', 'alpha3' => 'LSO', 'numeric' => '426', 'name' => 'Lesotho', 'official_name' => 'Kingdom of Lesotho', 'common_name' => null, 'flag_emoji' => '🇱🇸'],
['alpha2' => 'LT', 'alpha3' => 'LTU', 'numeric' => '440', 'name' => 'Lithuania', 'official_name' => 'Republic of Lithuania', 'common_name' => null, 'flag_emoji' => '🇱🇹'],
['alpha2' => 'LU', 'alpha3' => 'LUX', 'numeric' => '442', 'name' => 'Luxembourg', 'official_name' => 'Grand Duchy of Luxembourg', 'common_name' => null, 'flag_emoji' => '🇱🇺'],
['alpha2' => 'LV', 'alpha3' => 'LVA', 'numeric' => '428', 'name' => 'Latvia', 'official_name' => 'Republic of Latvia', 'common_name' => null, 'flag_emoji' => '🇱🇻'],
['alpha2' => 'LY', 'alpha3' => 'LBY', 'numeric' => '434', 'name' => 'Libya', 'official_name' => 'Libya', 'common_name' => null, 'flag_emoji' => '🇱🇾'],
['alpha2' => 'MA', 'alpha3' => 'MAR', 'numeric' => '504', 'name' => 'Morocco', 'official_name' => 'Kingdom of Morocco', 'common_name' => null, 'flag_emoji' => '🇲🇦'],
['alpha2' => 'MC', 'alpha3' => 'MCO', 'numeric' => '492', 'name' => 'Monaco', 'official_name' => 'Principality of Monaco', 'common_name' => null, 'flag_emoji' => '🇲🇨'],
['alpha2' => 'MD', 'alpha3' => 'MDA', 'numeric' => '498', 'name' => 'Moldova, Republic of', 'official_name' => 'Republic of Moldova', 'common_name' => 'Moldova', 'flag_emoji' => '🇲🇩'],
['alpha2' => 'ME', 'alpha3' => 'MNE', 'numeric' => '499', 'name' => 'Montenegro', 'official_name' => 'Montenegro', 'common_name' => null, 'flag_emoji' => '🇲🇪'],
['alpha2' => 'MF', 'alpha3' => 'MAF', 'numeric' => '663', 'name' => 'Saint Martin (French part)', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇲🇫'],
['alpha2' => 'MG', 'alpha3' => 'MDG', 'numeric' => '450', 'name' => 'Madagascar', 'official_name' => 'Republic of Madagascar', 'common_name' => null, 'flag_emoji' => '🇲🇬'],
['alpha2' => 'MH', 'alpha3' => 'MHL', 'numeric' => '584', 'name' => 'Marshall Islands', 'official_name' => 'Republic of the Marshall Islands', 'common_name' => null, 'flag_emoji' => '🇲🇭'],
['alpha2' => 'MK', 'alpha3' => 'MKD', 'numeric' => '807', 'name' => 'North Macedonia', 'official_name' => 'Republic of North Macedonia', 'common_name' => null, 'flag_emoji' => '🇲🇰'],
['alpha2' => 'ML', 'alpha3' => 'MLI', 'numeric' => '466', 'name' => 'Mali', 'official_name' => 'Republic of Mali', 'common_name' => null, 'flag_emoji' => '🇲🇱'],
['alpha2' => 'MM', 'alpha3' => 'MMR', 'numeric' => '104', 'name' => 'Myanmar', 'official_name' => 'Republic of Myanmar', 'common_name' => null, 'flag_emoji' => '🇲🇲'],
['alpha2' => 'MN', 'alpha3' => 'MNG', 'numeric' => '496', 'name' => 'Mongolia', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇲🇳'],
['alpha2' => 'MO', 'alpha3' => 'MAC', 'numeric' => '446', 'name' => 'Macao', 'official_name' => 'Macao Special Administrative Region of China', 'common_name' => null, 'flag_emoji' => '🇲🇴'],
['alpha2' => 'MP', 'alpha3' => 'MNP', 'numeric' => '580', 'name' => 'Northern Mariana Islands', 'official_name' => 'Commonwealth of the Northern Mariana Islands', 'common_name' => null, 'flag_emoji' => '🇲🇵'],
['alpha2' => 'MQ', 'alpha3' => 'MTQ', 'numeric' => '474', 'name' => 'Martinique', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇲🇶'],
['alpha2' => 'MR', 'alpha3' => 'MRT', 'numeric' => '478', 'name' => 'Mauritania', 'official_name' => 'Islamic Republic of Mauritania', 'common_name' => null, 'flag_emoji' => '🇲🇷'],
['alpha2' => 'MS', 'alpha3' => 'MSR', 'numeric' => '500', 'name' => 'Montserrat', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇲🇸'],
['alpha2' => 'MT', 'alpha3' => 'MLT', 'numeric' => '470', 'name' => 'Malta', 'official_name' => 'Republic of Malta', 'common_name' => null, 'flag_emoji' => '🇲🇹'],
['alpha2' => 'MU', 'alpha3' => 'MUS', 'numeric' => '480', 'name' => 'Mauritius', 'official_name' => 'Republic of Mauritius', 'common_name' => null, 'flag_emoji' => '🇲🇺'],
['alpha2' => 'MV', 'alpha3' => 'MDV', 'numeric' => '462', 'name' => 'Maldives', 'official_name' => 'Republic of Maldives', 'common_name' => null, 'flag_emoji' => '🇲🇻'],
['alpha2' => 'MW', 'alpha3' => 'MWI', 'numeric' => '454', 'name' => 'Malawi', 'official_name' => 'Republic of Malawi', 'common_name' => null, 'flag_emoji' => '🇲🇼'],
['alpha2' => 'MX', 'alpha3' => 'MEX', 'numeric' => '484', 'name' => 'Mexico', 'official_name' => 'United Mexican States', 'common_name' => null, 'flag_emoji' => '🇲🇽'],
['alpha2' => 'MY', 'alpha3' => 'MYS', 'numeric' => '458', 'name' => 'Malaysia', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇲🇾'],
['alpha2' => 'MZ', 'alpha3' => 'MOZ', 'numeric' => '508', 'name' => 'Mozambique', 'official_name' => 'Republic of Mozambique', 'common_name' => null, 'flag_emoji' => '🇲🇿'],
['alpha2' => 'NA', 'alpha3' => 'NAM', 'numeric' => '516', 'name' => 'Namibia', 'official_name' => 'Republic of Namibia', 'common_name' => null, 'flag_emoji' => '🇳🇦'],
['alpha2' => 'NC', 'alpha3' => 'NCL', 'numeric' => '540', 'name' => 'New Caledonia', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇳🇨'],
['alpha2' => 'NE', 'alpha3' => 'NER', 'numeric' => '562', 'name' => 'Niger', 'official_name' => 'Republic of the Niger', 'common_name' => null, 'flag_emoji' => '🇳🇪'],
['alpha2' => 'NF', 'alpha3' => 'NFK', 'numeric' => '574', 'name' => 'Norfolk Island', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇳🇫'],
['alpha2' => 'NG', 'alpha3' => 'NGA', 'numeric' => '566', 'name' => 'Nigeria', 'official_name' => 'Federal Republic of Nigeria', 'common_name' => null, 'flag_emoji' => '🇳🇬'],
['alpha2' => 'NI', 'alpha3' => 'NIC', 'numeric' => '558', 'name' => 'Nicaragua', 'official_name' => 'Republic of Nicaragua', 'common_name' => null, 'flag_emoji' => '🇳🇮'],
['alpha2' => 'NL', 'alpha3' => 'NLD', 'numeric' => '528', 'name' => 'Netherlands', 'official_name' => 'Kingdom of the Netherlands', 'common_name' => null, 'flag_emoji' => '🇳🇱'],
['alpha2' => 'NO', 'alpha3' => 'NOR', 'numeric' => '578', 'name' => 'Norway', 'official_name' => 'Kingdom of Norway', 'common_name' => null, 'flag_emoji' => '🇳🇴'],
['alpha2' => 'NP', 'alpha3' => 'NPL', 'numeric' => '524', 'name' => 'Nepal', 'official_name' => 'Federal Democratic Republic of Nepal', 'common_name' => null, 'flag_emoji' => '🇳🇵'],
['alpha2' => 'NR', 'alpha3' => 'NRU', 'numeric' => '520', 'name' => 'Nauru', 'official_name' => 'Republic of Nauru', 'common_name' => null, 'flag_emoji' => '🇳🇷'],
['alpha2' => 'NU', 'alpha3' => 'NIU', 'numeric' => '570', 'name' => 'Niue', 'official_name' => 'Niue', 'common_name' => null, 'flag_emoji' => '🇳🇺'],
['alpha2' => 'NZ', 'alpha3' => 'NZL', 'numeric' => '554', 'name' => 'New Zealand', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇳🇿'],
['alpha2' => 'OM', 'alpha3' => 'OMN', 'numeric' => '512', 'name' => 'Oman', 'official_name' => 'Sultanate of Oman', 'common_name' => null, 'flag_emoji' => '🇴🇲'],
['alpha2' => 'PA', 'alpha3' => 'PAN', 'numeric' => '591', 'name' => 'Panama', 'official_name' => 'Republic of Panama', 'common_name' => null, 'flag_emoji' => '🇵🇦'],
['alpha2' => 'PE', 'alpha3' => 'PER', 'numeric' => '604', 'name' => 'Peru', 'official_name' => 'Republic of Peru', 'common_name' => null, 'flag_emoji' => '🇵🇪'],
['alpha2' => 'PF', 'alpha3' => 'PYF', 'numeric' => '258', 'name' => 'French Polynesia', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇵🇫'],
['alpha2' => 'PG', 'alpha3' => 'PNG', 'numeric' => '598', 'name' => 'Papua New Guinea', 'official_name' => 'Independent State of Papua New Guinea', 'common_name' => null, 'flag_emoji' => '🇵🇬'],
['alpha2' => 'PH', 'alpha3' => 'PHL', 'numeric' => '608', 'name' => 'Philippines', 'official_name' => 'Republic of the Philippines', 'common_name' => null, 'flag_emoji' => '🇵🇭'],
['alpha2' => 'PK', 'alpha3' => 'PAK', 'numeric' => '586', 'name' => 'Pakistan', 'official_name' => 'Islamic Republic of Pakistan', 'common_name' => null, 'flag_emoji' => '🇵🇰'],
['alpha2' => 'PL', 'alpha3' => 'POL', 'numeric' => '616', 'name' => 'Poland', 'official_name' => 'Republic of Poland', 'common_name' => null, 'flag_emoji' => '🇵🇱'],
['alpha2' => 'PM', 'alpha3' => 'SPM', 'numeric' => '666', 'name' => 'Saint Pierre and Miquelon', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇵🇲'],
['alpha2' => 'PN', 'alpha3' => 'PCN', 'numeric' => '612', 'name' => 'Pitcairn', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇵🇳'],
['alpha2' => 'PR', 'alpha3' => 'PRI', 'numeric' => '630', 'name' => 'Puerto Rico', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇵🇷'],
['alpha2' => 'PS', 'alpha3' => 'PSE', 'numeric' => '275', 'name' => 'Palestine, State of', 'official_name' => 'the State of Palestine', 'common_name' => null, 'flag_emoji' => '🇵🇸'],
['alpha2' => 'PT', 'alpha3' => 'PRT', 'numeric' => '620', 'name' => 'Portugal', 'official_name' => 'Portuguese Republic', 'common_name' => null, 'flag_emoji' => '🇵🇹'],
['alpha2' => 'PW', 'alpha3' => 'PLW', 'numeric' => '585', 'name' => 'Palau', 'official_name' => 'Republic of Palau', 'common_name' => null, 'flag_emoji' => '🇵🇼'],
['alpha2' => 'PY', 'alpha3' => 'PRY', 'numeric' => '600', 'name' => 'Paraguay', 'official_name' => 'Republic of Paraguay', 'common_name' => null, 'flag_emoji' => '🇵🇾'],
['alpha2' => 'QA', 'alpha3' => 'QAT', 'numeric' => '634', 'name' => 'Qatar', 'official_name' => 'State of Qatar', 'common_name' => null, 'flag_emoji' => '🇶🇦'],
['alpha2' => 'RE', 'alpha3' => 'REU', 'numeric' => '638', 'name' => 'Réunion', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇷🇪'],
['alpha2' => 'RO', 'alpha3' => 'ROU', 'numeric' => '642', 'name' => 'Romania', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇷🇴'],
['alpha2' => 'RS', 'alpha3' => 'SRB', 'numeric' => '688', 'name' => 'Serbia', 'official_name' => 'Republic of Serbia', 'common_name' => null, 'flag_emoji' => '🇷🇸'],
['alpha2' => 'RU', 'alpha3' => 'RUS', 'numeric' => '643', 'name' => 'Russian Federation', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇷🇺'],
['alpha2' => 'RW', 'alpha3' => 'RWA', 'numeric' => '646', 'name' => 'Rwanda', 'official_name' => 'Rwandese Republic', 'common_name' => null, 'flag_emoji' => '🇷🇼'],
['alpha2' => 'SA', 'alpha3' => 'SAU', 'numeric' => '682', 'name' => 'Saudi Arabia', 'official_name' => 'Kingdom of Saudi Arabia', 'common_name' => null, 'flag_emoji' => '🇸🇦'],
['alpha2' => 'SB', 'alpha3' => 'SLB', 'numeric' => '090', 'name' => 'Solomon Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇸🇧'],
['alpha2' => 'SC', 'alpha3' => 'SYC', 'numeric' => '690', 'name' => 'Seychelles', 'official_name' => 'Republic of Seychelles', 'common_name' => null, 'flag_emoji' => '🇸🇨'],
['alpha2' => 'SD', 'alpha3' => 'SDN', 'numeric' => '729', 'name' => 'Sudan', 'official_name' => 'Republic of the Sudan', 'common_name' => null, 'flag_emoji' => '🇸🇩'],
['alpha2' => 'SE', 'alpha3' => 'SWE', 'numeric' => '752', 'name' => 'Sweden', 'official_name' => 'Kingdom of Sweden', 'common_name' => null, 'flag_emoji' => '🇸🇪'],
['alpha2' => 'SG', 'alpha3' => 'SGP', 'numeric' => '702', 'name' => 'Singapore', 'official_name' => 'Republic of Singapore', 'common_name' => null, 'flag_emoji' => '🇸🇬'],
['alpha2' => 'SH', 'alpha3' => 'SHN', 'numeric' => '654', 'name' => 'Saint Helena, Ascension and Tristan da Cunha', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇸🇭'],
['alpha2' => 'SI', 'alpha3' => 'SVN', 'numeric' => '705', 'name' => 'Slovenia', 'official_name' => 'Republic of Slovenia', 'common_name' => null, 'flag_emoji' => '🇸🇮'],
['alpha2' => 'SJ', 'alpha3' => 'SJM', 'numeric' => '744', 'name' => 'Svalbard and Jan Mayen', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇸🇯'],
['alpha2' => 'SK', 'alpha3' => 'SVK', 'numeric' => '703', 'name' => 'Slovakia', 'official_name' => 'Slovak Republic', 'common_name' => null, 'flag_emoji' => '🇸🇰'],
['alpha2' => 'SL', 'alpha3' => 'SLE', 'numeric' => '694', 'name' => 'Sierra Leone', 'official_name' => 'Republic of Sierra Leone', 'common_name' => null, 'flag_emoji' => '🇸🇱'],
['alpha2' => 'SM', 'alpha3' => 'SMR', 'numeric' => '674', 'name' => 'San Marino', 'official_name' => 'Republic of San Marino', 'common_name' => null, 'flag_emoji' => '🇸🇲'],
['alpha2' => 'SN', 'alpha3' => 'SEN', 'numeric' => '686', 'name' => 'Senegal', 'official_name' => 'Republic of Senegal', 'common_name' => null, 'flag_emoji' => '🇸🇳'],
['alpha2' => 'SO', 'alpha3' => 'SOM', 'numeric' => '706', 'name' => 'Somalia', 'official_name' => 'Federal Republic of Somalia', 'common_name' => null, 'flag_emoji' => '🇸🇴'],
['alpha2' => 'SR', 'alpha3' => 'SUR', 'numeric' => '740', 'name' => 'Suriname', 'official_name' => 'Republic of Suriname', 'common_name' => null, 'flag_emoji' => '🇸🇷'],
['alpha2' => 'SS', 'alpha3' => 'SSD', 'numeric' => '728', 'name' => 'South Sudan', 'official_name' => 'Republic of South Sudan', 'common_name' => null, 'flag_emoji' => '🇸🇸'],
['alpha2' => 'ST', 'alpha3' => 'STP', 'numeric' => '678', 'name' => 'Sao Tome and Principe', 'official_name' => 'Democratic Republic of Sao Tome and Principe', 'common_name' => null, 'flag_emoji' => '🇸🇹'],
['alpha2' => 'SV', 'alpha3' => 'SLV', 'numeric' => '222', 'name' => 'El Salvador', 'official_name' => 'Republic of El Salvador', 'common_name' => null, 'flag_emoji' => '🇸🇻'],
['alpha2' => 'SX', 'alpha3' => 'SXM', 'numeric' => '534', 'name' => 'Sint Maarten (Dutch part)', 'official_name' => 'Sint Maarten (Dutch part)', 'common_name' => null, 'flag_emoji' => '🇸🇽'],
['alpha2' => 'SY', 'alpha3' => 'SYR', 'numeric' => '760', 'name' => 'Syrian Arab Republic', 'official_name' => null, 'common_name' => 'Syria', 'flag_emoji' => '🇸🇾'],
['alpha2' => 'SZ', 'alpha3' => 'SWZ', 'numeric' => '748', 'name' => 'Eswatini', 'official_name' => 'Kingdom of Eswatini', 'common_name' => null, 'flag_emoji' => '🇸🇿'],
['alpha2' => 'TC', 'alpha3' => 'TCA', 'numeric' => '796', 'name' => 'Turks and Caicos Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇹🇨'],
['alpha2' => 'TD', 'alpha3' => 'TCD', 'numeric' => '148', 'name' => 'Chad', 'official_name' => 'Republic of Chad', 'common_name' => null, 'flag_emoji' => '🇹🇩'],
['alpha2' => 'TF', 'alpha3' => 'ATF', 'numeric' => '260', 'name' => 'French Southern Territories', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇹🇫'],
['alpha2' => 'TG', 'alpha3' => 'TGO', 'numeric' => '768', 'name' => 'Togo', 'official_name' => 'Togolese Republic', 'common_name' => null, 'flag_emoji' => '🇹🇬'],
['alpha2' => 'TH', 'alpha3' => 'THA', 'numeric' => '764', 'name' => 'Thailand', 'official_name' => 'Kingdom of Thailand', 'common_name' => null, 'flag_emoji' => '🇹🇭'],
['alpha2' => 'TJ', 'alpha3' => 'TJK', 'numeric' => '762', 'name' => 'Tajikistan', 'official_name' => 'Republic of Tajikistan', 'common_name' => null, 'flag_emoji' => '🇹🇯'],
['alpha2' => 'TK', 'alpha3' => 'TKL', 'numeric' => '772', 'name' => 'Tokelau', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇹🇰'],
['alpha2' => 'TL', 'alpha3' => 'TLS', 'numeric' => '626', 'name' => 'Timor-Leste', 'official_name' => 'Democratic Republic of Timor-Leste', 'common_name' => null, 'flag_emoji' => '🇹🇱'],
['alpha2' => 'TM', 'alpha3' => 'TKM', 'numeric' => '795', 'name' => 'Turkmenistan', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇹🇲'],
['alpha2' => 'TN', 'alpha3' => 'TUN', 'numeric' => '788', 'name' => 'Tunisia', 'official_name' => 'Republic of Tunisia', 'common_name' => null, 'flag_emoji' => '🇹🇳'],
['alpha2' => 'TO', 'alpha3' => 'TON', 'numeric' => '776', 'name' => 'Tonga', 'official_name' => 'Kingdom of Tonga', 'common_name' => null, 'flag_emoji' => '🇹🇴'],
['alpha2' => 'TR', 'alpha3' => 'TUR', 'numeric' => '792', 'name' => 'Türkiye', 'official_name' => 'Republic of Türkiye', 'common_name' => null, 'flag_emoji' => '🇹🇷'],
['alpha2' => 'TT', 'alpha3' => 'TTO', 'numeric' => '780', 'name' => 'Trinidad and Tobago', 'official_name' => 'Republic of Trinidad and Tobago', 'common_name' => null, 'flag_emoji' => '🇹🇹'],
['alpha2' => 'TV', 'alpha3' => 'TUV', 'numeric' => '798', 'name' => 'Tuvalu', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇹🇻'],
['alpha2' => 'TW', 'alpha3' => 'TWN', 'numeric' => '158', 'name' => 'Taiwan, Province of China', 'official_name' => 'Taiwan, Province of China', 'common_name' => 'Taiwan', 'flag_emoji' => '🇹🇼'],
['alpha2' => 'TZ', 'alpha3' => 'TZA', 'numeric' => '834', 'name' => 'Tanzania, United Republic of', 'official_name' => 'United Republic of Tanzania', 'common_name' => 'Tanzania', 'flag_emoji' => '🇹🇿'],
['alpha2' => 'UA', 'alpha3' => 'UKR', 'numeric' => '804', 'name' => 'Ukraine', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇺🇦'],
['alpha2' => 'UG', 'alpha3' => 'UGA', 'numeric' => '800', 'name' => 'Uganda', 'official_name' => 'Republic of Uganda', 'common_name' => null, 'flag_emoji' => '🇺🇬'],
['alpha2' => 'UM', 'alpha3' => 'UMI', 'numeric' => '581', 'name' => 'United States Minor Outlying Islands', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇺🇲'],
['alpha2' => 'US', 'alpha3' => 'USA', 'numeric' => '840', 'name' => 'United States', 'official_name' => 'United States of America', 'common_name' => null, 'flag_emoji' => '🇺🇸'],
['alpha2' => 'UY', 'alpha3' => 'URY', 'numeric' => '858', 'name' => 'Uruguay', 'official_name' => 'Eastern Republic of Uruguay', 'common_name' => null, 'flag_emoji' => '🇺🇾'],
['alpha2' => 'UZ', 'alpha3' => 'UZB', 'numeric' => '860', 'name' => 'Uzbekistan', 'official_name' => 'Republic of Uzbekistan', 'common_name' => null, 'flag_emoji' => '🇺🇿'],
['alpha2' => 'VA', 'alpha3' => 'VAT', 'numeric' => '336', 'name' => 'Holy See (Vatican City State)', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇻🇦'],
['alpha2' => 'VC', 'alpha3' => 'VCT', 'numeric' => '670', 'name' => 'Saint Vincent and the Grenadines', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇻🇨'],
['alpha2' => 'VE', 'alpha3' => 'VEN', 'numeric' => '862', 'name' => 'Venezuela, Bolivarian Republic of', 'official_name' => 'Bolivarian Republic of Venezuela', 'common_name' => 'Venezuela', 'flag_emoji' => '🇻🇪'],
['alpha2' => 'VG', 'alpha3' => 'VGB', 'numeric' => '092', 'name' => 'Virgin Islands, British', 'official_name' => 'British Virgin Islands', 'common_name' => null, 'flag_emoji' => '🇻🇬'],
['alpha2' => 'VI', 'alpha3' => 'VIR', 'numeric' => '850', 'name' => 'Virgin Islands, U.S.', 'official_name' => 'Virgin Islands of the United States', 'common_name' => null, 'flag_emoji' => '🇻🇮'],
['alpha2' => 'VN', 'alpha3' => 'VNM', 'numeric' => '704', 'name' => 'Viet Nam', 'official_name' => 'Socialist Republic of Viet Nam', 'common_name' => 'Vietnam', 'flag_emoji' => '🇻🇳'],
['alpha2' => 'VU', 'alpha3' => 'VUT', 'numeric' => '548', 'name' => 'Vanuatu', 'official_name' => 'Republic of Vanuatu', 'common_name' => null, 'flag_emoji' => '🇻🇺'],
['alpha2' => 'WF', 'alpha3' => 'WLF', 'numeric' => '876', 'name' => 'Wallis and Futuna', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇼🇫'],
['alpha2' => 'WS', 'alpha3' => 'WSM', 'numeric' => '882', 'name' => 'Samoa', 'official_name' => 'Independent State of Samoa', 'common_name' => null, 'flag_emoji' => '🇼🇸'],
['alpha2' => 'YE', 'alpha3' => 'YEM', 'numeric' => '887', 'name' => 'Yemen', 'official_name' => 'Republic of Yemen', 'common_name' => null, 'flag_emoji' => '🇾🇪'],
['alpha2' => 'YT', 'alpha3' => 'MYT', 'numeric' => '175', 'name' => 'Mayotte', 'official_name' => null, 'common_name' => null, 'flag_emoji' => '🇾🇹'],
['alpha2' => 'ZA', 'alpha3' => 'ZAF', 'numeric' => '710', 'name' => 'South Africa', 'official_name' => 'Republic of South Africa', 'common_name' => null, 'flag_emoji' => '🇿🇦'],
['alpha2' => 'ZM', 'alpha3' => 'ZMB', 'numeric' => '894', 'name' => 'Zambia', 'official_name' => 'Republic of Zambia', 'common_name' => null, 'flag_emoji' => '🇿🇲'],
['alpha2' => 'ZW', 'alpha3' => 'ZWE', 'numeric' => '716', 'name' => 'Zimbabwe', 'official_name' => 'Republic of Zimbabwe', 'common_name' => null, 'flag_emoji' => '🇿🇼'],
];
/** @var list<array<string, mixed>> $countries */
$countries = json_decode(
file_get_contents(dirname(__DIR__) . '/Data/iso_countries.json'),
true,
flags: JSON_THROW_ON_ERROR,
);
foreach ($countries as $index => $country) {
Country::query()->updateOrCreate(
@@ -8,6 +8,12 @@ use Modules\Countries\Support\Iso3166;
class StoreCountryRequest extends FormRequest
{
private const ISO3 = 'size:3';
private const MAX_STRING = 'max:255';
private const MAX_SHORT = 'max:16';
/**
* Authorize country reference-data administration.
*/
@@ -38,29 +44,29 @@ class StoreCountryRequest extends FormRequest
{
return [
'alpha2' => ['required', 'string', 'size:2', 'uppercase', 'unique:countries,alpha2'],
'alpha3' => ['required', 'string', 'size:3', 'uppercase', 'unique:countries,alpha3'],
'numeric' => ['required', 'string', 'size:3', 'unique:countries,numeric'],
'name' => ['required', 'string', 'max:255'],
'official_name' => ['nullable', 'string', 'max:255'],
'common_name' => ['nullable', 'string', 'max:255'],
'display_name' => ['nullable', 'string', 'max:255'],
'native_name' => ['nullable', 'string', 'max:255'],
'capital' => ['nullable', 'string', 'max:255'],
'flag_emoji' => ['nullable', 'string', 'max:16'],
'calling_code' => ['nullable', 'string', 'max:16'],
'tld' => ['nullable', 'string', 'max:16'],
'currency_code' => ['nullable', 'string', 'size:3', 'uppercase'],
'currency_name' => ['nullable', 'string', 'max:255'],
'currency_symbol' => ['nullable', 'string', 'max:16'],
'alpha3' => ['required', 'string', self::ISO3, 'uppercase', 'unique:countries,alpha3'],
'numeric' => ['required', 'string', self::ISO3, 'unique:countries,numeric'],
'name' => ['required', 'string', self::MAX_STRING],
'official_name' => ['nullable', 'string', self::MAX_STRING],
'common_name' => ['nullable', 'string', self::MAX_STRING],
'display_name' => ['nullable', 'string', self::MAX_STRING],
'native_name' => ['nullable', 'string', self::MAX_STRING],
'capital' => ['nullable', 'string', self::MAX_STRING],
'flag_emoji' => ['nullable', 'string', self::MAX_SHORT],
'calling_code' => ['nullable', 'string', self::MAX_SHORT],
'tld' => ['nullable', 'string', self::MAX_SHORT],
'currency_code' => ['nullable', 'string', self::ISO3, 'uppercase'],
'currency_name' => ['nullable', 'string', self::MAX_STRING],
'currency_symbol' => ['nullable', 'string', self::MAX_SHORT],
'languages' => ['nullable', 'json'],
'timezones' => ['nullable', 'json'],
'latitude' => ['nullable', 'numeric', 'between:-90,90'],
'longitude' => ['nullable', 'numeric', 'between:-180,180'],
'flag_svg_url' => ['nullable', 'url', 'max:255'],
'flag_png_url' => ['nullable', 'url', 'max:255'],
'flag_svg_url' => ['nullable', 'url', self::MAX_STRING],
'flag_png_url' => ['nullable', 'url', self::MAX_STRING],
'ioc_code' => ['nullable', 'string', 'max:8'],
'fifa_code' => ['nullable', 'string', 'max:8'],
'postal_code_format' => ['nullable', 'string', 'max:255'],
'postal_code_format' => ['nullable', 'string', self::MAX_STRING],
'address_format' => ['nullable', 'string', 'max:5000'],
'is_active' => ['required', 'boolean'],
'iso_status' => ['required', 'string', Rule::in(Iso3166::statuses())],
@@ -70,10 +76,10 @@ class StoreCountryRequest extends FormRequest
'continent' => ['nullable', 'string', 'max:64'],
'region' => ['nullable', 'string', 'max:100'],
'subregion' => ['nullable', 'string', 'max:100'],
'intermediate_region' => ['nullable', 'string', 'max:255'],
'intermediate_region' => ['nullable', 'string', self::MAX_STRING],
'sort_order' => ['nullable', 'integer', 'min:0', 'max:999999'],
'source' => ['nullable', 'string', 'max:255'],
'source_version' => ['nullable', 'string', 'max:255'],
'source' => ['nullable', 'string', self::MAX_STRING],
'source_version' => ['nullable', 'string', self::MAX_STRING],
'last_synced_at' => ['nullable', 'date'],
'metadata' => ['nullable', 'json'],
];
@@ -8,6 +8,12 @@ use Modules\Countries\Support\Iso3166;
class UpdateCountryRequest extends FormRequest
{
private const ISO3 = 'size:3';
private const MAX_STRING = 'max:255';
private const MAX_SHORT = 'max:16';
/**
* Authorize country reference-data administration.
*/
@@ -40,29 +46,29 @@ class UpdateCountryRequest extends FormRequest
return [
'alpha2' => ['required', 'string', 'size:2', 'uppercase', Rule::unique('countries', 'alpha2')->ignore($countryId)],
'alpha3' => ['required', 'string', 'size:3', 'uppercase', Rule::unique('countries', 'alpha3')->ignore($countryId)],
'numeric' => ['required', 'string', 'size:3', Rule::unique('countries', 'numeric')->ignore($countryId)],
'name' => ['required', 'string', 'max:255'],
'official_name' => ['nullable', 'string', 'max:255'],
'common_name' => ['nullable', 'string', 'max:255'],
'display_name' => ['nullable', 'string', 'max:255'],
'native_name' => ['nullable', 'string', 'max:255'],
'capital' => ['nullable', 'string', 'max:255'],
'flag_emoji' => ['nullable', 'string', 'max:16'],
'calling_code' => ['nullable', 'string', 'max:16'],
'tld' => ['nullable', 'string', 'max:16'],
'currency_code' => ['nullable', 'string', 'size:3', 'uppercase'],
'currency_name' => ['nullable', 'string', 'max:255'],
'currency_symbol' => ['nullable', 'string', 'max:16'],
'alpha3' => ['required', 'string', self::ISO3, 'uppercase', Rule::unique('countries', 'alpha3')->ignore($countryId)],
'numeric' => ['required', 'string', self::ISO3, Rule::unique('countries', 'numeric')->ignore($countryId)],
'name' => ['required', 'string', self::MAX_STRING],
'official_name' => ['nullable', 'string', self::MAX_STRING],
'common_name' => ['nullable', 'string', self::MAX_STRING],
'display_name' => ['nullable', 'string', self::MAX_STRING],
'native_name' => ['nullable', 'string', self::MAX_STRING],
'capital' => ['nullable', 'string', self::MAX_STRING],
'flag_emoji' => ['nullable', 'string', self::MAX_SHORT],
'calling_code' => ['nullable', 'string', self::MAX_SHORT],
'tld' => ['nullable', 'string', self::MAX_SHORT],
'currency_code' => ['nullable', 'string', self::ISO3, 'uppercase'],
'currency_name' => ['nullable', 'string', self::MAX_STRING],
'currency_symbol' => ['nullable', 'string', self::MAX_SHORT],
'languages' => ['nullable', 'json'],
'timezones' => ['nullable', 'json'],
'latitude' => ['nullable', 'numeric', 'between:-90,90'],
'longitude' => ['nullable', 'numeric', 'between:-180,180'],
'flag_svg_url' => ['nullable', 'url', 'max:255'],
'flag_png_url' => ['nullable', 'url', 'max:255'],
'flag_svg_url' => ['nullable', 'url', self::MAX_STRING],
'flag_png_url' => ['nullable', 'url', self::MAX_STRING],
'ioc_code' => ['nullable', 'string', 'max:8'],
'fifa_code' => ['nullable', 'string', 'max:8'],
'postal_code_format' => ['nullable', 'string', 'max:255'],
'postal_code_format' => ['nullable', 'string', self::MAX_STRING],
'address_format' => ['nullable', 'string', 'max:5000'],
'is_active' => ['required', 'boolean'],
'iso_status' => ['required', 'string', Rule::in(Iso3166::statuses())],
@@ -72,10 +78,10 @@ class UpdateCountryRequest extends FormRequest
'continent' => ['nullable', 'string', 'max:64'],
'region' => ['nullable', 'string', 'max:100'],
'subregion' => ['nullable', 'string', 'max:100'],
'intermediate_region' => ['nullable', 'string', 'max:255'],
'intermediate_region' => ['nullable', 'string', self::MAX_STRING],
'sort_order' => ['nullable', 'integer', 'min:0', 'max:999999'],
'source' => ['nullable', 'string', 'max:255'],
'source_version' => ['nullable', 'string', 'max:255'],
'source' => ['nullable', 'string', self::MAX_STRING],
'source_version' => ['nullable', 'string', self::MAX_STRING],
'last_synced_at' => ['nullable', 'date'],
'metadata' => ['nullable', 'json'],
];
@@ -77,6 +77,7 @@
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-800">
<caption class="sr-only">Data table</caption>
<thead class="bg-zinc-50 dark:bg-zinc-950">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">
@@ -117,6 +117,7 @@
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-800">
<caption class="sr-only">Data table</caption>
<thead class="bg-zinc-50 dark:bg-zinc-950">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Area
@@ -159,6 +160,7 @@
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-800">
<caption class="sr-only">Data table</caption>
<thead class="bg-zinc-50 dark:bg-zinc-950">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Code
+6 -4
View File
@@ -5,6 +5,8 @@ use Modules\Countries\Http\Controllers\CountriesController;
use Modules\Countries\Http\Controllers\CountrySyncController;
use Modules\Countries\Http\Controllers\ReferenceDataController;
const COUNTRY_ROUTE = '/{country}';
Route::prefix('countries')->name('countries.')->middleware(['web', 'auth'])->group(function (): void {
Route::get('/', CountriesController::class)->name('index');
Route::get('/create', [CountriesController::class, 'create'])->name('create');
@@ -15,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/scripts', [ReferenceDataController::class, 'scripts'])->name('scripts.index');
Route::get('/reference/locodes', [ReferenceDataController::class, 'locodes'])->name('locodes.index');
Route::get('/{country:alpha2}', [CountriesController::class, 'show'])->name('show');
Route::get('/{country:alpha2}/edit', [CountriesController::class, 'edit'])->name('edit');
Route::put('/{country:alpha2}', [CountriesController::class, 'update'])->name('update');
Route::delete('/{country:alpha2}', [CountriesController::class, 'destroy'])->name('destroy');
Route::get(COUNTRY_ROUTE, [CountriesController::class, 'show'])->name('show');
Route::get(COUNTRY_ROUTE . '/edit', [CountriesController::class, 'edit'])->name('edit');
Route::put(COUNTRY_ROUTE, [CountriesController::class, 'update'])->name('update');
Route::delete(COUNTRY_ROUTE, [CountriesController::class, 'destroy'])->name('destroy');
});
+1 -1
View File
@@ -57,7 +57,7 @@ final class Iso3166
{
$normalised = str_pad((string) $code, 3, '0', STR_PAD_LEFT);
if (! preg_match('/^[0-9]{3}$/', $normalised)) {
if (! preg_match('/^\d{3}$/', $normalised)) {
throw new InvalidArgumentException('ISO 3166-1 numeric codes must contain exactly three digits.');
}
@@ -64,6 +64,7 @@
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light">
<tr>
<th>Document</th>
@@ -15,6 +15,10 @@ use Modules\Documentation\Models\DocumentationDocument;
class DocumentationController extends Controller
{
private const string STORAGE_DISK_CONFIG = 'documentation.storage_disk';
private const string UPLOAD_PATH_CONFIG = 'documentation.upload_path';
/**
* Display the documentation dashboard and document list.
*/
@@ -170,7 +174,7 @@ class DocumentationController extends Controller
{
abort_if(blank($document->attachment_path), 404);
return Storage::disk($document->attachment_disk ?: config('documentation.storage_disk', 'public'))
return Storage::disk($document->attachment_disk ?: config(self::STORAGE_DISK_CONFIG, 'public'))
->download($document->attachment_path, $document->attachment_name);
}
@@ -202,27 +206,19 @@ class DocumentationController extends Controller
*/
private function extractMarkdownBody(Request $request): ?string
{
if (! config('documentation.import_markdown_attachments', true)) {
return null;
}
$file = $request->file('attachment');
$canImport = config('documentation.import_markdown_attachments', true)
&& $file !== null
&& $this->isMarkdownUpload($request)
&& $file->getSize() <= (int) config('documentation.max_markdown_import_bytes', 1048576);
if ($file === null || ! $this->isMarkdownUpload($request)) {
return null;
}
if ($file->getSize() > (int) config('documentation.max_markdown_import_bytes', 1048576)) {
if (! $canImport) {
return null;
}
$content = file_get_contents($file->getRealPath());
if ($content === false) {
return null;
}
return trim($content);
return $content === false ? null : trim($content);
}
/**
@@ -250,8 +246,8 @@ class DocumentationController extends Controller
private function storeAttachment(Request $request): array
{
$file = $request->file('attachment');
$disk = config('documentation.storage_disk', 'public');
$path = $file->store(config('documentation.upload_path', 'documentation'), $disk);
$disk = config(self::STORAGE_DISK_CONFIG, 'public');
$path = $file->store(config(self::UPLOAD_PATH_CONFIG, 'documentation'), $disk);
return [
'attachment_disk' => $disk,
@@ -271,14 +267,14 @@ class DocumentationController extends Controller
return;
}
$disk = config('documentation.storage_disk', 'public');
$disk = config(self::STORAGE_DISK_CONFIG, 'public');
foreach ((array) $request->file('attachments') as $file) {
if ($file === null || ! $file->isValid()) {
continue;
}
$path = $file->store(config('documentation.upload_path', 'documentation'), $disk);
$path = $file->store(config(self::UPLOAD_PATH_CONFIG, 'documentation'), $disk);
$document->attachments()->create([
'disk' => $disk,
@@ -297,7 +293,7 @@ class DocumentationController extends Controller
private function deleteAttachment(DocumentationDocument $document): void
{
if (filled($document->attachment_path)) {
Storage::disk($document->attachment_disk ?: config('documentation.storage_disk', 'public'))->delete($document->attachment_path);
Storage::disk($document->attachment_disk ?: config(self::STORAGE_DISK_CONFIG, 'public'))->delete($document->attachment_path);
}
}
@@ -52,18 +52,16 @@ class DocumentationAttachment extends Model
{
$bytes = (int) $this->size;
$label = number_format($bytes / 1048576, 1) . ' MB';
if ($bytes <= 0) {
return 'Unknown size';
$label = 'Unknown size';
} elseif ($bytes < 1024) {
$label = $bytes . ' B';
} elseif ($bytes < 1048576) {
$label = number_format($bytes / 1024, 1) . ' KB';
}
if ($bytes < 1024) {
return $bytes . ' B';
}
if ($bytes < 1048576) {
return number_format($bytes / 1024, 1) . ' KB';
}
return number_format($bytes / 1048576, 1) . ' MB';
return $label;
}
}
@@ -14,18 +14,6 @@ class RouteServiceProvider extends ServiceProvider
*/
protected $moduleNamespace = 'Modules\Documentation\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.
*
@@ -21,6 +21,7 @@
</div>
<div class="doc-card-body">
<table class="doc-table">
<caption class="sr-only">Data table</caption>
<thead>
<tr>
<th>Name</th>
@@ -63,6 +63,7 @@
</form>
<table class="doc-table">
<caption class="sr-only">Data table</caption>
<thead>
<tr>
<th>Document</th>
+9 -7
View File
@@ -6,6 +6,8 @@ use Illuminate\Support\Facades\Route;
use Modules\Documentation\Http\Controllers\DocumentationCategoryController;
use Modules\Documentation\Http\Controllers\DocumentationController;
const DOCUMENT_ROUTE = '/documents/{document}';
Route::middleware(['web', 'auth'])
->prefix('documentation')
->name('documentation.')
@@ -15,13 +17,13 @@ Route::middleware(['web', 'auth'])
Route::get('/documents', [DocumentationController::class, 'index'])->name('documents.index');
Route::get('/documents/create', [DocumentationController::class, 'create'])->name('documents.create');
Route::post('/documents', [DocumentationController::class, 'store'])->name('documents.store');
Route::get('/documents/{document}', [DocumentationController::class, 'show'])->name('documents.show');
Route::get('/documents/{document}/edit', [DocumentationController::class, 'edit'])->name('documents.edit');
Route::put('/documents/{document}', [DocumentationController::class, 'update'])->name('documents.update');
Route::delete('/documents/{document}', [DocumentationController::class, 'destroy'])->name('documents.destroy');
Route::get('/documents/{document}/download', [DocumentationController::class, 'download'])->name('documents.download');
Route::get('/documents/{document}/attachments/{attachment}/download', [DocumentationController::class, 'downloadAttachment'])->name('documents.attachments.download');
Route::delete('/documents/{document}/attachments/{attachment}', [DocumentationController::class, 'destroyAttachment'])->name('documents.attachments.destroy');
Route::get(DOCUMENT_ROUTE, [DocumentationController::class, 'show'])->name('documents.show');
Route::get(DOCUMENT_ROUTE . '/edit', [DocumentationController::class, 'edit'])->name('documents.edit');
Route::put(DOCUMENT_ROUTE, [DocumentationController::class, 'update'])->name('documents.update');
Route::delete(DOCUMENT_ROUTE, [DocumentationController::class, 'destroy'])->name('documents.destroy');
Route::get(DOCUMENT_ROUTE . '/download', [DocumentationController::class, 'download'])->name('documents.download');
Route::get(DOCUMENT_ROUTE . '/attachments/{attachment}/download', [DocumentationController::class, 'downloadAttachment'])->name('documents.attachments.download');
Route::delete(DOCUMENT_ROUTE . '/attachments/{attachment}', [DocumentationController::class, 'destroyAttachment'])->name('documents.attachments.destroy');
Route::resource('categories', DocumentationCategoryController::class)->except(['show']);
});
@@ -17,6 +17,8 @@ use Modules\Helpdesk\Models\Ticket;
*/
final class TicketFactory extends Factory
{
private const FACTORY_BODY = self::FACTORY_BODY;
/**
* The model name for the factory.
*
@@ -74,9 +76,9 @@ final class TicketFactory extends Factory
$put('reference', 'HD-' . now()->format('Y') . '-' . str_pad((string) $this->faker->unique()->numberBetween(1, 9999), 4, '0', STR_PAD_LEFT));
$put('subject', 'Factory helpdesk ticket');
$put('title', 'Factory helpdesk ticket');
$put('body', '<p>Factory helpdesk ticket body.</p>');
$put('details', '<p>Factory helpdesk ticket body.</p>');
$put('description', '<p>Factory helpdesk ticket body.</p>');
$put('body', self::FACTORY_BODY);
$put('details', self::FACTORY_BODY);
$put('description', self::FACTORY_BODY);
$put('search_text', 'factory helpdesk ticket body');
$put('last_activity_at', now());
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Modules\Helpdesk\Exceptions;
use RuntimeException;
final class TicketPersistenceException extends RuntimeException {}
@@ -15,6 +15,20 @@ use Modules\Helpdesk\Services\GitWebhookIngestionService;
final class GitWebhookController
{
private const REJECTED = 'rejected';
private const SIGNATURE_FAILED = 'Git webhook signature or token validation failed.';
private const SHA256_PREFIX = 'sha256=';
private const SHA1_PREFIX = 'sha1=';
private const REQUEST_ID_HEADER = 'X-Request-Id';
private const GITHUB_DELIVERY_HEADER = 'X-GitHub-Delivery';
private const GITHUB_EVENT_HEADER = 'X-GitHub-Event';
/**
* Ingest a signed Git provider webhook for a registered Helpdesk repository.
*/
@@ -24,34 +38,31 @@ final class GitWebhookController
$event = $this->eventName($request, $provider);
$deliveryId = $this->deliveryId($request, $provider);
$rawPayload = $request->getContent();
$status = 202;
$payload = [];
if (! $repository->is_active) {
$result = ['status' => 'rejected', 'message' => 'Repository is inactive.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1];
$service->logWebhook($repository, $provider, $deliveryId, $event, $result);
return response()->json($result, 403);
$result = $this->rejectedResult('Repository is inactive.');
$status = 403;
} elseif (! $this->hasValidSignature($request, $repository, $provider, $rawPayload)) {
$result = $this->rejectedResult(self::SIGNATURE_FAILED);
$status = 403;
} else {
try {
/** @var array<string, mixed> $payload */
$payload = json_decode($rawPayload, true, 512, JSON_THROW_ON_ERROR);
$result = $service->ingest($repository, $payload, $provider, $deliveryId, $event);
} catch (JsonException) {
$result = $this->rejectedResult('Invalid JSON webhook payload.');
$status = 400;
}
}
if (! $this->hasValidSignature($request, $repository, $provider, $rawPayload)) {
$result = ['status' => 'rejected', 'message' => 'Git webhook signature or token validation failed.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1];
$service->logWebhook($repository, $provider, $deliveryId, $event, $result);
return response()->json($result, 403);
if ($status !== 202) {
$service->logWebhook($repository, $provider, $deliveryId, $event, $result, $payload);
}
try {
/** @var array<string, mixed> $payload */
$payload = json_decode($rawPayload, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException) {
$result = ['status' => 'rejected', 'message' => 'Invalid JSON webhook payload.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1];
$service->logWebhook($repository, $provider, $deliveryId, $event, $result);
return response()->json($result, 400);
}
$result = $service->ingest($repository, $payload, $provider, $deliveryId, $event);
return response()->json($result, 202);
return response()->json($result, $status);
}
/**
@@ -63,42 +74,34 @@ final class GitWebhookController
$event = $this->eventName($request, $provider);
$deliveryId = $this->deliveryId($request, $provider);
$rawPayload = $request->getContent();
$status = 202;
$repository = null;
try {
/** @var array<string, mixed> $payload */
$payload = json_decode($rawPayload, true, 512, JSON_THROW_ON_ERROR);
$repository = $this->repositoryForGiteaPayload($payload);
if (! $repository instanceof GitRepository) {
$result = $this->rejectedResult('No active Helpdesk Git repository matched the Gitea webhook payload.');
$status = 404;
} elseif (! $this->hasValidGiteaSecurity($request, $repository, $provider, $rawPayload)) {
$result = $this->rejectedResult(self::SIGNATURE_FAILED);
$status = 403;
} else {
$result = $service->ingest($repository, $payload, $provider, $deliveryId, $event);
}
} catch (JsonException) {
return response()->json([
'status' => 'rejected',
'message' => 'Invalid JSON webhook payload.',
'commit_count' => 0,
'linked_count' => 0,
'ignored_count' => 1,
], 400);
$payload = [];
$result = $this->rejectedResult('Invalid JSON webhook payload.');
$status = 400;
}
$repository = $this->repositoryForGiteaPayload($payload);
if (! $repository instanceof GitRepository) {
return response()->json([
'status' => 'rejected',
'message' => 'No active Helpdesk Git repository matched the Gitea webhook payload.',
'commit_count' => 0,
'linked_count' => 0,
'ignored_count' => 1,
], 404);
}
if (! $this->hasValidGiteaSecurity($request, $repository, $provider, $rawPayload)) {
$result = ['status' => 'rejected', 'message' => 'Git webhook signature or token validation failed.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1];
if ($repository instanceof GitRepository && $status !== 202) {
$service->logWebhook($repository, $provider, $deliveryId, $event, $result, $payload);
return response()->json($result, 403);
}
$result = $service->ingest($repository, $payload, $provider, $deliveryId, $event);
return response()->json($result, 202);
return response()->json($result, $status);
}
/**
@@ -143,8 +146,8 @@ final class GitWebhookController
$sha256Signature = (string) $request->header('X-Hub-Signature-256', '');
$sha1Signature = (string) $request->header('X-Hub-Signature', '');
return hash_equals('sha256=' . hash_hmac('sha256', $rawPayload, $secret), $sha256Signature)
|| hash_equals('sha1=' . hash_hmac('sha1', $rawPayload, $secret), $sha1Signature);
return hash_equals(self::SHA256_PREFIX . hash_hmac('sha256', $rawPayload, $secret), $sha256Signature)
|| hash_equals(self::SHA1_PREFIX . hash_hmac('sha1', $rawPayload, $secret), $sha1Signature);
}
/**
@@ -158,8 +161,8 @@ final class GitWebhookController
$sha1 = hash_hmac('sha1', $rawPayload, $secret);
return hash_equals($sha256, $giteaSignature)
|| hash_equals('sha256=' . $sha256, $giteaSignature)
|| hash_equals('sha1=' . $sha1, $giteaSignature)
|| hash_equals(self::SHA256_PREFIX . $sha256, $giteaSignature)
|| hash_equals(self::SHA1_PREFIX . $sha1, $giteaSignature)
|| $this->hasValidGitHubSignature($request, $secret, $rawPayload)
|| $this->hasValidGenericSignature($request, $secret, $rawPayload);
}
@@ -171,7 +174,7 @@ final class GitWebhookController
{
$signature = (string) $request->header('X-Hub-Signature', '');
return hash_equals('sha256=' . hash_hmac('sha256', $rawPayload, $secret), $signature)
return hash_equals(self::SHA256_PREFIX . hash_hmac('sha256', $rawPayload, $secret), $signature)
|| hash_equals($secret, (string) $request->header('X-Helpdesk-Token', ''));
}
@@ -184,8 +187,8 @@ final class GitWebhookController
$signature = (string) ($request->header('X-Hub-Signature-256') ?: $request->header('X-Hub-Signature'));
return hash_equals($secret, $token)
|| hash_equals('sha256=' . hash_hmac('sha256', $rawPayload, $secret), $signature)
|| hash_equals('sha1=' . hash_hmac('sha1', $rawPayload, $secret), $signature);
|| hash_equals(self::SHA256_PREFIX . hash_hmac('sha256', $rawPayload, $secret), $signature)
|| hash_equals(self::SHA1_PREFIX . hash_hmac('sha1', $rawPayload, $secret), $signature);
}
/**
@@ -230,63 +233,94 @@ final class GitWebhookController
->where('is_active', true)
->whereRaw('LOWER(provider) IN (?, ?)', ['gitea', 'git'])
->get();
$matchedRepository = null;
if ($repositories->isEmpty()) {
Log::warning('Helpdesk Gitea webhook received but no active Gitea/git repository is configured.');
} else {
$normalisedPayloadValues = $this->normalisedPayloadRepositoryValues($payload);
$matchedRepository = $repositories->first(
fn (GitRepository $repository): bool => $this->repositoryMatchesPayload($repository, $normalisedPayloadValues),
);
return null;
if (! $matchedRepository instanceof GitRepository && $repositories->count() === 1) {
$matchedRepository = $repositories->first();
} elseif (! $matchedRepository instanceof GitRepository) {
Log::warning('Helpdesk Gitea webhook did not match any configured repository.', [
'payload_repository' => Arr::get($payload, 'repository.full_name') ?: Arr::get($payload, 'repository.clone_url') ?: Arr::get($payload, 'repository.html_url'),
'configured_repository_count' => $repositories->count(),
]);
}
}
$payloadValues = array_values(array_filter([
return $matchedRepository instanceof GitRepository ? $matchedRepository : null;
}
/** @param array<string, mixed> $payload
* @return list<string>
*/
private function normalisedPayloadRepositoryValues(array $payload): array
{
$name = Arr::get($payload, 'repository.name');
$owner = Arr::get($payload, 'repository.owner.username');
return $this->normaliseRepositoryValues([
Arr::get($payload, 'repository.full_name'),
Arr::get($payload, 'repository.name'),
$name,
Arr::get($payload, 'repository.html_url'),
Arr::get($payload, 'repository.clone_url'),
Arr::get($payload, 'repository.ssh_url'),
Arr::get($payload, 'repository.url'),
Arr::get($payload, 'repository.owner.username') && Arr::get($payload, 'repository.name')
? Arr::get($payload, 'repository.owner.username') . '/' . Arr::get($payload, 'repository.name')
: null,
], static fn (mixed $value): bool => is_scalar($value) && trim((string) $value) !== ''));
$owner && $name ? $owner . '/' . $name : null,
]);
}
$normalisedPayloadValues = array_values(array_filter(array_map(
fn (mixed $value): ?string => $this->normaliseRepositoryIdentity((string) $value),
$payloadValues,
)));
/** @param list<string> $payloadValues */
private function repositoryMatchesPayload(GitRepository $repository, array $payloadValues): bool
{
$repositoryValues = $this->normaliseRepositoryValues([
$repository->remote_url,
$repository->name,
Str::of((string) $repository->remote_url)->afterLast(':')->beforeLast('.git')->value(),
]);
foreach ($repositories as $repository) {
$repositoryValues = array_filter([
$repository->remote_url,
$repository->name,
Str::of((string) $repository->remote_url)->afterLast(':')->beforeLast('.git')->value(),
], static fn (mixed $value): bool => is_scalar($value) && trim((string) $value) !== '');
$normalisedRepositoryValues = array_values(array_filter(array_map(
fn (mixed $value): ?string => $this->normaliseRepositoryIdentity((string) $value),
$repositoryValues,
)));
foreach ($normalisedPayloadValues as $payloadValue) {
foreach ($normalisedRepositoryValues as $repositoryValue) {
if ($payloadValue === $repositoryValue
|| str_ends_with($repositoryValue, '/' . $payloadValue)
|| str_ends_with($payloadValue, '/' . $repositoryValue)) {
return $repository;
}
foreach ($payloadValues as $payloadValue) {
foreach ($repositoryValues as $repositoryValue) {
if ($this->repositoryIdentitiesMatch($payloadValue, $repositoryValue)) {
return true;
}
}
}
if ($repositories->count() === 1) {
return $repositories->first();
return false;
}
/** @param array<int, mixed> $values
* @return list<string>
*/
private function normaliseRepositoryValues(array $values): array
{
$normalised = [];
foreach ($values as $value) {
if (! is_scalar($value) || trim((string) $value) === '') {
continue;
}
$identity = $this->normaliseRepositoryIdentity((string) $value);
if ($identity !== null) {
$normalised[] = $identity;
}
}
Log::warning('Helpdesk Gitea webhook did not match any configured repository.', [
'payload_repository' => Arr::get($payload, 'repository.full_name') ?: Arr::get($payload, 'repository.clone_url') ?: Arr::get($payload, 'repository.html_url'),
'configured_repository_count' => $repositories->count(),
]);
return $normalised;
}
return null;
private function repositoryIdentitiesMatch(string $payloadValue, string $repositoryValue): bool
{
return $payloadValue === $repositoryValue
|| str_ends_with($repositoryValue, '/' . $payloadValue)
|| str_ends_with($payloadValue, '/' . $repositoryValue);
}
/**
@@ -325,17 +359,29 @@ final class GitWebhookController
return implode('/', array_slice($segments, -2));
}
/** @return array{status: string, message: string, commit_count: int, linked_count: int, ignored_count: int} */
private function rejectedResult(string $message): array
{
return [
'status' => self::REJECTED,
'message' => $message,
'commit_count' => 0,
'linked_count' => 0,
'ignored_count' => 1,
];
}
/**
* Read the event name from provider-specific headers.
*/
private function eventName(Request $request, string $provider): ?string
{
return match ($provider) {
'github' => $request->header('X-GitHub-Event'),
'github' => $request->header(self::GITHUB_EVENT_HEADER),
'gitlab' => $request->header('X-Gitlab-Event'),
'gitea' => $request->header('X-Gitea-Event') ?: $request->header('X-Gitea-Event-Type') ?: $request->header('X-GitHub-Event'),
'gitea' => $request->header('X-Gitea-Event') ?: $request->header('X-Gitea-Event-Type') ?: $request->header(self::GITHUB_EVENT_HEADER),
'bitbucket' => $request->header('X-Event-Key'),
default => $request->header('X-Helpdesk-Event') ?: $request->header('X-GitHub-Event') ?: $request->header('X-Gitlab-Event') ?: 'push',
default => $request->header('X-Helpdesk-Event') ?: $request->header(self::GITHUB_EVENT_HEADER) ?: $request->header('X-Gitlab-Event') ?: 'push',
};
}
@@ -345,11 +391,11 @@ final class GitWebhookController
private function deliveryId(Request $request, string $provider): ?string
{
return match ($provider) {
'github' => $request->header('X-GitHub-Delivery'),
'gitlab' => $request->header('X-Gitlab-Event-UUID') ?: $request->header('X-Request-Id'),
'gitea' => $request->header('X-Gitea-Delivery') ?: $request->header('X-GitHub-Delivery') ?: $request->header('X-Request-Id'),
'bitbucket' => $request->header('X-Request-UUID') ?: $request->header('X-Request-Id'),
default => $request->header('X-Helpdesk-Delivery') ?: $request->header('X-GitHub-Delivery') ?: $request->header('X-Request-Id'),
'github' => $request->header(self::GITHUB_DELIVERY_HEADER),
'gitlab' => $request->header('X-Gitlab-Event-UUID') ?: $request->header(self::REQUEST_ID_HEADER),
'gitea' => $request->header('X-Gitea-Delivery') ?: $request->header(self::GITHUB_DELIVERY_HEADER) ?: $request->header(self::REQUEST_ID_HEADER),
'bitbucket' => $request->header('X-Request-UUID') ?: $request->header(self::REQUEST_ID_HEADER),
default => $request->header('X-Helpdesk-Delivery') ?: $request->header(self::GITHUB_DELIVERY_HEADER) ?: $request->header(self::REQUEST_ID_HEADER),
};
}
}
@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
use Modules\Helpdesk\Exceptions\TicketPersistenceException;
use Modules\Helpdesk\Models\KanbanBoard;
use Modules\Helpdesk\Models\Status;
use Modules\Helpdesk\Models\Ticket;
@@ -68,7 +69,7 @@ final class TicketBoardController extends Controller
->update($updates);
if ($affected < 1) {
throw new \RuntimeException('Ticket board update did not persist.');
throw new TicketPersistenceException('Ticket board update did not persist.');
}
return Ticket::query()
@@ -25,51 +25,52 @@ final class TicketCommentDeleteController extends Controller
abort_unless((int) $comment->ticket_id === (int) $ticket->id, 404);
DB::transaction(function () use ($comment, $ticket, $user): void {
$updates = [];
if (Schema::hasColumn('helpdesk_comments', 'tombstoned_at')) {
$updates['tombstoned_at'] = now();
}
if (Schema::hasColumn('helpdesk_comments', 'deleted_by')) {
$updates['deleted_by'] = (int) $user->id;
}
if (Schema::hasColumn('helpdesk_comments', 'delete_reason')) {
$updates['delete_reason'] = 'Deleted by MPBA platform administrator.';
}
if (Schema::hasColumn('helpdesk_comments', 'sync_sequence')) {
$updates['sync_sequence'] = ((int) ($comment->sync_sequence ?? 0)) + 1;
}
if (Schema::hasColumn('helpdesk_comments', 'sync_hash')) {
$updates['sync_hash'] = hash('sha256', implode('|', [
(string) ($comment->guid ?? $comment->id),
'deleted',
now()->toISOString(),
]));
}
if (Schema::hasColumn('helpdesk_comments', 'updated_at')) {
$updates['updated_at'] = now();
}
if ($updates !== []) {
DB::table('helpdesk_comments')
->where('id', $comment->id)
->update($updates);
} else {
$comment->delete();
}
if (Schema::hasColumn('helpdesk_tickets', 'last_activity_at')) {
$ticket->forceFill(['last_activity_at' => now()])->save();
}
$this->deleteComment($comment, (int) $user->id);
$this->touchTicket($ticket);
});
return redirect()
->route('helpdesk.tickets.show', $ticket)
->with('ticket-comment-deleted', 'Response deleted.');
}
private function deleteComment(Comment $comment, int $userId): void
{
$updates = $this->tombstoneUpdates($comment, $userId);
if ($updates === []) {
$comment->delete();
return;
}
DB::table('helpdesk_comments')->where('id', $comment->id)->update($updates);
}
/** @return array<string, mixed> */
private function tombstoneUpdates(Comment $comment, int $userId): array
{
$now = now();
$candidates = [
'tombstoned_at' => $now,
'deleted_by' => $userId,
'delete_reason' => 'Deleted by MPBA platform administrator.',
'sync_sequence' => ((int) ($comment->sync_sequence ?? 0)) + 1,
'sync_hash' => hash('sha256', implode('|', [(string) ($comment->guid ?? $comment->id), 'deleted', $now->toISOString()])),
'updated_at' => $now,
];
return array_filter(
$candidates,
static fn (mixed $value, string $column): bool => Schema::hasColumn('helpdesk_comments', $column),
ARRAY_FILTER_USE_BOTH,
);
}
private function touchTicket(Ticket $ticket): void
{
if (Schema::hasColumn('helpdesk_tickets', 'last_activity_at')) {
$ticket->forceFill(['last_activity_at' => now()])->save();
}
}
}
@@ -35,62 +35,71 @@ final class TicketTimeLogController extends Controller
$loggedAt = now();
DB::transaction(function () use ($ticket, $request, $minutes, $description, $loggedAt): void {
if (Schema::hasColumn($ticket->getTable(), 'time_spent_minutes')) {
$ticket->increment('time_spent_minutes', $minutes, [
'last_activity_at' => $loggedAt,
]);
} elseif (Schema::hasColumn($ticket->getTable(), 'last_activity_at')) {
$ticket->forceFill(['last_activity_at' => $loggedAt])->save();
}
if (Schema::hasTable('helpdesk_time_logs')) {
DB::table('helpdesk_time_logs')->insert([
'ticket_id' => $ticket->getKey(),
'user_id' => $request->user()->getKey(),
'minutes' => $minutes,
'description' => $description,
'logged_at' => $loggedAt,
'created_at' => $loggedAt,
'updated_at' => $loggedAt,
]);
}
if (Schema::hasTable('helpdesk_ticket_time_logs')) {
$payload = [
'ticket_id' => $ticket->getKey(),
'user_id' => $request->user()->getKey(),
'minutes' => $minutes,
'description' => $description,
'created_at' => $loggedAt,
'updated_at' => $loggedAt,
];
if (Schema::hasColumn('helpdesk_ticket_time_logs', 'started_at')) {
$payload['started_at'] = $loggedAt->copy()->subMinutes($minutes);
}
if (Schema::hasColumn('helpdesk_ticket_time_logs', 'ended_at')) {
$payload['ended_at'] = $loggedAt;
}
if (Schema::hasColumn('helpdesk_ticket_time_logs', 'logged_at')) {
$payload['logged_at'] = $loggedAt;
}
if (Schema::hasColumn('helpdesk_ticket_time_logs', 'duration_seconds')) {
$payload['duration_seconds'] = $minutes * 60;
}
if (Schema::hasColumn('helpdesk_ticket_time_logs', 'seconds')) {
$payload['seconds'] = $minutes * 60;
}
DB::table('helpdesk_ticket_time_logs')->insert($payload);
}
$this->updateTicketActivity($ticket, $minutes, $loggedAt);
$this->storeLegacyTimeLog($ticket, $request, $minutes, $description, $loggedAt);
$this->storeTicketTimeLog($ticket, $request, $minutes, $description, $loggedAt);
});
return redirect()
->route('helpdesk.tickets.show', $ticket)
->with('ticket-action-updated', 'Time logged successfully.');
}
private function updateTicketActivity(Ticket $ticket, int $minutes, $loggedAt): void
{
if (Schema::hasColumn($ticket->getTable(), 'time_spent_minutes')) {
$ticket->increment('time_spent_minutes', $minutes, ['last_activity_at' => $loggedAt]);
} elseif (Schema::hasColumn($ticket->getTable(), 'last_activity_at')) {
$ticket->forceFill(['last_activity_at' => $loggedAt])->save();
}
}
private function storeLegacyTimeLog(Ticket $ticket, Request $request, int $minutes, ?string $description, $loggedAt): void
{
if (! Schema::hasTable('helpdesk_time_logs')) {
return;
}
DB::table('helpdesk_time_logs')->insert([
'ticket_id' => $ticket->getKey(),
'user_id' => $request->user()->getKey(),
'minutes' => $minutes,
'description' => $description,
'logged_at' => $loggedAt,
'created_at' => $loggedAt,
'updated_at' => $loggedAt,
]);
}
private function storeTicketTimeLog(Ticket $ticket, Request $request, int $minutes, ?string $description, $loggedAt): void
{
if (! Schema::hasTable('helpdesk_ticket_time_logs')) {
return;
}
$payload = [
'ticket_id' => $ticket->getKey(),
'user_id' => $request->user()->getKey(),
'minutes' => $minutes,
'description' => $description,
'created_at' => $loggedAt,
'updated_at' => $loggedAt,
];
$optional = [
'started_at' => $loggedAt->copy()->subMinutes($minutes),
'ended_at' => $loggedAt,
'logged_at' => $loggedAt,
'duration_seconds' => $minutes * 60,
'seconds' => $minutes * 60,
];
foreach ($optional as $column => $value) {
if (Schema::hasColumn('helpdesk_ticket_time_logs', $column)) {
$payload[$column] = $value;
}
}
DB::table('helpdesk_ticket_time_logs')->insert($payload);
}
}
+8 -7
View File
@@ -17,6 +17,8 @@ use Modules\Helpdesk\Services\WorkflowNotificationService;
final class KanbanBoard extends Component
{
private const OWNER_RELATION = self::OWNER_RELATION;
public ?int $board_id = null;
public int $refreshNonce = 0;
@@ -98,7 +100,7 @@ final class KanbanBoard extends Component
public function moveTicketToBoard(int|string $ticketId, int|string|null $boardId, WorkflowNotificationService $workflow): void
{
$ticket = Ticket::query()->with('board')->visibleTo(Auth::user())->findOrFail((int) $ticketId);
$board = KanbanBoardModel::query()->with(['statuses', 'owner:id,name,email'])->findOrFail((int) $boardId);
$board = KanbanBoardModel::query()->with(['statuses', self::OWNER_RELATION])->findOrFail((int) $boardId);
$oldBoardName = $ticket->board?->name ?? 'No board';
$this->authorize('update', $ticket);
@@ -156,8 +158,7 @@ final class KanbanBoard extends Component
*/
public function selectBoard(int|string|null $value): void
{
$this->board_id = is_numeric($value) ? (int) $value : null;
$this->refreshBoard();
$this->updatedBoardId($value);
}
/**
@@ -166,7 +167,7 @@ final class KanbanBoard extends Component
public function render(): View
{
$boards = KanbanBoardModel::query()
->with(['statuses', 'owner:id,name,email'])
->with(['statuses', self::OWNER_RELATION])
->orderBy('sort_order')
->orderBy('name')
->get();
@@ -176,10 +177,10 @@ final class KanbanBoard extends Component
if ($board instanceof KanbanBoardModel && $board->statuses->isEmpty()) {
$this->ensureBoardHasStatuses($board);
$board->load(['statuses', 'owner:id,name,email']);
$board->load(['statuses', self::OWNER_RELATION]);
$boards = KanbanBoardModel::query()
->with(['statuses', 'owner:id,name,email'])
->with(['statuses', self::OWNER_RELATION])
->orderBy('sort_order')
->orderBy('name')
->get();
@@ -206,7 +207,7 @@ final class KanbanBoard extends Component
}
return Ticket::query()
->with(['user:id,name,email', 'owner:id,name,email', 'agent:id,name,email', 'status', 'priority', 'category', 'project', 'team', 'product', 'tags', 'checklistItems', 'board'])
->with(['user:id,name,email', self::OWNER_RELATION, 'agent:id,name,email', 'status', 'priority', 'category', 'project', 'team', 'product', 'tags', 'checklistItems', 'board'])
->visibleTo(Auth::user())
->where('board_id', $board->id)
->latest('last_activity_at')
+80 -58
View File
@@ -31,6 +31,30 @@ use Modules\Helpdesk\Support\Color;
final class SettingsManager extends Component
{
private const RULE_MAX_40 = 'max:40';
private const RULE_MAX_80 = 'max:80';
private const RULE_MAX_100 = 'max:100';
private const RULE_MAX_120 = 'max:120';
private const RULE_MAX_160 = 'max:160';
private const RULE_MAX_180 = 'max:180';
private const RULE_MAX_220 = 'max:220';
private const RULE_MAX_500 = 'max:500';
private const RULE_MIN_0 = 'min:0';
private const RULE_TEAM_EXISTS = 'exists:helpdesk_teams,id';
private const RULE_PROJECT_EXISTS = 'exists:helpdesk_projects,id';
private const RULE_CATEGORY_EXISTS = 'exists:helpdesk_categories,id';
public string $section = 'statuses';
public int|string|null $default_helpdesk_company_id = null;
@@ -126,10 +150,10 @@ final class SettingsManager extends Component
public function saveStatus(): void
{
$data = $this->validate([
'statusForm.name' => ['required', 'string', 'max:80'],
'statusForm.slug' => ['nullable', 'string', 'max:100'],
'statusForm.name' => ['required', 'string', self::RULE_MAX_80],
'statusForm.slug' => ['nullable', 'string', self::RULE_MAX_100],
'statusForm.color' => ['required', 'string', Color::validationRule()],
'statusForm.sort_order' => ['required', 'integer', 'min:0'],
'statusForm.sort_order' => ['required', 'integer', self::RULE_MIN_0],
'statusForm.is_closed' => ['boolean'],
'statusForm.is_default' => ['boolean'],
])['statusForm'];
@@ -167,10 +191,10 @@ final class SettingsManager extends Component
public function savePriority(): void
{
$data = $this->validate([
'priorityForm.name' => ['required', 'string', 'max:80'],
'priorityForm.slug' => ['nullable', 'string', 'max:100'],
'priorityForm.name' => ['required', 'string', self::RULE_MAX_80],
'priorityForm.slug' => ['nullable', 'string', self::RULE_MAX_100],
'priorityForm.color' => ['required', 'string', Color::validationRule()],
'priorityForm.sort_order' => ['required', 'integer', 'min:0'],
'priorityForm.sort_order' => ['required', 'integer', self::RULE_MIN_0],
'priorityForm.is_default' => ['boolean'],
])['priorityForm'];
@@ -209,7 +233,7 @@ final class SettingsManager extends Component
$data = $this->validate([
'categoryForm.company_id' => ['nullable', 'integer'],
'categoryForm.name' => ['required', 'string', 'max:120'],
'categoryForm.name' => ['required', 'string', self::RULE_MAX_120],
'categoryForm.slug' => ['nullable', 'string', 'max:140'],
'categoryForm.color' => ['required', 'string', Color::validationRule()],
'categoryForm.description' => ['nullable', 'string'],
@@ -251,7 +275,7 @@ final class SettingsManager extends Component
$data = $this->validate([
'teamForm.company_id' => ['nullable', 'integer'],
'teamForm.name' => ['required', 'string', 'max:120'],
'teamForm.name' => ['required', 'string', self::RULE_MAX_120],
'teamForm.slug' => ['nullable', 'string', 'max:140'],
'teamForm.color' => ['required', 'string', Color::validationRule()],
'teamForm.description' => ['nullable', 'string'],
@@ -293,9 +317,9 @@ final class SettingsManager extends Component
$data = $this->validate([
'projectForm.company_id' => ['nullable', 'integer'],
'projectForm.team_id' => ['nullable', 'integer', 'exists:helpdesk_teams,id'],
'projectForm.title' => ['required', 'string', 'max:160'],
'projectForm.slug' => ['nullable', 'string', 'max:180'],
'projectForm.team_id' => ['nullable', 'integer', self::RULE_TEAM_EXISTS],
'projectForm.title' => ['required', 'string', self::RULE_MAX_160],
'projectForm.slug' => ['nullable', 'string', self::RULE_MAX_180],
'projectForm.color' => ['required', 'string', Color::validationRule()],
'projectForm.description' => ['nullable', 'string'],
'projectForm.is_default' => ['boolean'],
@@ -335,12 +359,12 @@ final class SettingsManager extends Component
$this->normalizeNullableFormValues('boardForm', ['owner_id']);
$data = $this->validate([
'boardForm.name' => ['required', 'string', 'max:160'],
'boardForm.slug' => ['nullable', 'string', 'max:180'],
'boardForm.name' => ['required', 'string', self::RULE_MAX_160],
'boardForm.slug' => ['nullable', 'string', self::RULE_MAX_180],
'boardForm.description' => ['nullable', 'string'],
'boardForm.owner_id' => ['nullable', 'integer', 'exists:users,id'],
'boardForm.is_default' => ['boolean'],
'boardForm.sort_order' => ['required', 'integer', 'min:0'],
'boardForm.sort_order' => ['required', 'integer', self::RULE_MIN_0],
'boardForm.status_ids' => ['array'],
'boardForm.status_ids.*' => ['integer', 'exists:helpdesk_statuses,id'],
])['boardForm'];
@@ -403,12 +427,12 @@ final class SettingsManager extends Component
$data = $this->validate([
'repositoryForm.company_id' => ['nullable', 'integer'],
'repositoryForm.project_id' => ['nullable', 'integer', 'exists:helpdesk_projects,id'],
'repositoryForm.name' => ['required', 'string', 'max:160'],
'repositoryForm.provider' => ['required', 'string', 'max:40'],
'repositoryForm.remote_url' => ['required', 'string', 'max:500'],
'repositoryForm.default_branch' => ['required', 'string', 'max:120'],
'repositoryForm.webhook_secret' => ['nullable', 'string', 'max:120'],
'repositoryForm.project_id' => ['nullable', 'integer', self::RULE_PROJECT_EXISTS],
'repositoryForm.name' => ['required', 'string', self::RULE_MAX_160],
'repositoryForm.provider' => ['required', 'string', self::RULE_MAX_40],
'repositoryForm.remote_url' => ['required', 'string', self::RULE_MAX_500],
'repositoryForm.default_branch' => ['required', 'string', self::RULE_MAX_120],
'repositoryForm.webhook_secret' => ['nullable', 'string', self::RULE_MAX_120],
])['repositoryForm'];
$data['provider'] = strtolower(trim((string) $data['provider']));
@@ -440,14 +464,14 @@ final class SettingsManager extends Component
$this->normalizeNullableFormValues('workflowForm', ['text_body', 'cc', 'bcc']);
$data = $this->validate([
'workflowForm.name' => ['required', 'string', 'max:160'],
'workflowForm.event' => ['required', 'string', 'max:80'],
'workflowForm.subject' => ['required', 'string', 'max:220'],
'workflowForm.name' => ['required', 'string', self::RULE_MAX_160],
'workflowForm.event' => ['required', 'string', self::RULE_MAX_80],
'workflowForm.subject' => ['required', 'string', self::RULE_MAX_220],
'workflowForm.html_body' => ['required', 'string'],
'workflowForm.text_body' => ['nullable', 'string'],
'workflowForm.to_role' => ['required', 'string', 'max:40'],
'workflowForm.cc' => ['nullable', 'string', 'max:500'],
'workflowForm.bcc' => ['nullable', 'string', 'max:500'],
'workflowForm.to_role' => ['required', 'string', self::RULE_MAX_40],
'workflowForm.cc' => ['nullable', 'string', self::RULE_MAX_500],
'workflowForm.bcc' => ['nullable', 'string', self::RULE_MAX_500],
'workflowForm.is_active' => ['boolean'],
])['workflowForm'];
@@ -479,15 +503,15 @@ final class SettingsManager extends Component
$data = $this->validate([
'cannedForm.company_id' => ['nullable', 'integer'],
'cannedForm.category_id' => ['nullable', 'integer', 'exists:helpdesk_categories,id'],
'cannedForm.team_id' => ['nullable', 'integer', 'exists:helpdesk_teams,id'],
'cannedForm.name' => ['required', 'string', 'max:160'],
'cannedForm.slug' => ['nullable', 'string', 'max:180'],
'cannedForm.visibility' => ['required', 'string', 'max:40'],
'cannedForm.subject' => ['nullable', 'string', 'max:220'],
'cannedForm.category_id' => ['nullable', 'integer', self::RULE_CATEGORY_EXISTS],
'cannedForm.team_id' => ['nullable', 'integer', self::RULE_TEAM_EXISTS],
'cannedForm.name' => ['required', 'string', self::RULE_MAX_160],
'cannedForm.slug' => ['nullable', 'string', self::RULE_MAX_180],
'cannedForm.visibility' => ['required', 'string', self::RULE_MAX_40],
'cannedForm.subject' => ['nullable', 'string', self::RULE_MAX_220],
'cannedForm.body' => ['required', 'string'],
'cannedForm.is_active' => ['boolean'],
'cannedForm.sort_order' => ['required', 'integer', 'min:0'],
'cannedForm.sort_order' => ['required', 'integer', self::RULE_MIN_0],
])['cannedForm'];
$data['slug'] = $this->slug($data['slug'] ?: $data['name']);
$this->persistSettingModel(CannedResponse::class, $this->cannedForm['id'], $data);
@@ -519,9 +543,9 @@ final class SettingsManager extends Component
$data = $this->validate([
'slaForm.company_id' => ['nullable', 'integer'],
'slaForm.priority_id' => ['nullable', 'integer', 'exists:helpdesk_priorities,id'],
'slaForm.category_id' => ['nullable', 'integer', 'exists:helpdesk_categories,id'],
'slaForm.name' => ['required', 'string', 'max:160'],
'slaForm.slug' => ['nullable', 'string', 'max:180'],
'slaForm.category_id' => ['nullable', 'integer', self::RULE_CATEGORY_EXISTS],
'slaForm.name' => ['required', 'string', self::RULE_MAX_160],
'slaForm.slug' => ['nullable', 'string', self::RULE_MAX_180],
'slaForm.first_response_minutes' => ['required', 'integer', 'min:1'],
'slaForm.resolution_minutes' => ['required', 'integer', 'min:1'],
'slaForm.business_hours_only' => ['boolean'],
@@ -560,20 +584,20 @@ final class SettingsManager extends Component
$data = $this->validate([
'automationForm.company_id' => ['nullable', 'integer'],
'automationForm.name' => ['required', 'string', 'max:160'],
'automationForm.slug' => ['nullable', 'string', 'max:180'],
'automationForm.name' => ['required', 'string', self::RULE_MAX_160],
'automationForm.slug' => ['nullable', 'string', self::RULE_MAX_180],
'automationForm.match_sender' => ['nullable', 'string', 'max:190'],
'automationForm.match_subject' => ['nullable', 'string', 'max:220'],
'automationForm.match_subject' => ['nullable', 'string', self::RULE_MAX_220],
'automationForm.match_mailbox' => ['nullable', 'string', 'max:190'],
'automationForm.match_category_id' => ['nullable', 'integer', 'exists:helpdesk_categories,id'],
'automationForm.match_category_id' => ['nullable', 'integer', self::RULE_CATEGORY_EXISTS],
'automationForm.set_priority_id' => ['nullable', 'integer', 'exists:helpdesk_priorities,id'],
'automationForm.set_category_id' => ['nullable', 'integer', 'exists:helpdesk_categories,id'],
'automationForm.set_team_id' => ['nullable', 'integer', 'exists:helpdesk_teams,id'],
'automationForm.set_project_id' => ['nullable', 'integer', 'exists:helpdesk_projects,id'],
'automationForm.set_category_id' => ['nullable', 'integer', self::RULE_CATEGORY_EXISTS],
'automationForm.set_team_id' => ['nullable', 'integer', self::RULE_TEAM_EXISTS],
'automationForm.set_project_id' => ['nullable', 'integer', self::RULE_PROJECT_EXISTS],
'automationForm.set_status_id' => ['nullable', 'integer', 'exists:helpdesk_statuses,id'],
'automationForm.set_agent_id' => ['nullable', 'integer', 'exists:users,id'],
'automationForm.is_active' => ['boolean'],
'automationForm.sort_order' => ['required', 'integer', 'min:0'],
'automationForm.sort_order' => ['required', 'integer', self::RULE_MIN_0],
])['automationForm'];
$data['slug'] = $this->slug($data['slug'] ?: $data['name']);
$this->persistSettingModel(AutomationRule::class, $this->automationForm['id'], $data);
@@ -604,11 +628,11 @@ final class SettingsManager extends Component
$data = $this->validate([
'productForm.company_id' => ['nullable', 'integer'],
'productForm.name' => ['required', 'string', 'max:160'],
'productForm.slug' => ['nullable', 'string', 'max:180'],
'productForm.name' => ['required', 'string', self::RULE_MAX_160],
'productForm.slug' => ['nullable', 'string', self::RULE_MAX_180],
'productForm.type' => ['required', 'string', 'max:60'],
'productForm.version' => ['nullable', 'string', 'max:80'],
'productForm.release_channel' => ['nullable', 'string', 'max:80'],
'productForm.version' => ['nullable', 'string', self::RULE_MAX_80],
'productForm.release_channel' => ['nullable', 'string', self::RULE_MAX_80],
'productForm.description' => ['nullable', 'string'],
'productForm.is_active' => ['boolean'],
])['productForm'];
@@ -641,8 +665,8 @@ final class SettingsManager extends Component
$data = $this->validate([
'tagForm.company_id' => ['nullable', 'integer'],
'tagForm.name' => ['required', 'string', 'max:80'],
'tagForm.slug' => ['nullable', 'string', 'max:100'],
'tagForm.name' => ['required', 'string', self::RULE_MAX_80],
'tagForm.slug' => ['nullable', 'string', self::RULE_MAX_100],
'tagForm.color' => ['required', 'string', Color::validationRule()],
])['tagForm'];
$data['slug'] = $this->slug($data['slug'] ?: $data['name']);
@@ -675,10 +699,10 @@ final class SettingsManager extends Component
$data = $this->validate([
'checklistTemplateForm.company_id' => ['nullable', 'integer'],
'checklistTemplateForm.category_id' => ['nullable', 'integer', 'exists:helpdesk_categories,id'],
'checklistTemplateForm.project_id' => ['nullable', 'integer', 'exists:helpdesk_projects,id'],
'checklistTemplateForm.name' => ['required', 'string', 'max:160'],
'checklistTemplateForm.slug' => ['nullable', 'string', 'max:180'],
'checklistTemplateForm.category_id' => ['nullable', 'integer', self::RULE_CATEGORY_EXISTS],
'checklistTemplateForm.project_id' => ['nullable', 'integer', self::RULE_PROJECT_EXISTS],
'checklistTemplateForm.name' => ['required', 'string', self::RULE_MAX_160],
'checklistTemplateForm.slug' => ['nullable', 'string', self::RULE_MAX_180],
'checklistTemplateForm.items_text' => ['required', 'string'],
'checklistTemplateForm.is_active' => ['boolean'],
])['checklistTemplateForm'];
@@ -855,10 +879,8 @@ final class SettingsManager extends Component
return $model;
}
/** @var TModel $model */
$model = $modelClass::query()->create($data);
return $model;
/** @var TModel */
return $modelClass::query()->create($data);
} catch (QueryException $exception) {
throw ValidationException::withMessages([
'settings' => 'The record could not be saved. Check the slug is unique for this section/company and try again.',
+5 -3
View File
@@ -22,6 +22,8 @@ use Modules\Helpdesk\Services\TicketService;
final class TicketCreate extends Component
{
private const COMPANY_NULL_ORDER = 'company_id is null';
use WithFileUploads;
public ?int $company_id = null;
@@ -188,7 +190,7 @@ final class TicketCreate extends Component
$query->where('company_id', $this->company_id)
->orWhereNull('company_id');
}))
->orderByRaw('company_id is null')
->orderByRaw(self::COMPANY_NULL_ORDER)
->orderBy('name')
->value('id');
}
@@ -201,7 +203,7 @@ final class TicketCreate extends Component
$query->where('company_id', $this->company_id)
->orWhereNull('company_id');
}))
->orderByRaw('company_id is null')
->orderByRaw(self::COMPANY_NULL_ORDER)
->orderBy('name')
->value('id');
}
@@ -218,7 +220,7 @@ final class TicketCreate extends Component
$query->where('team_id', $this->team_id)
->orWhereNull('team_id');
}))
->orderByRaw('company_id is null')
->orderByRaw(self::COMPANY_NULL_ORDER)
->orderByRaw('team_id is null')
->orderBy('title')
->value('id');
+16 -26
View File
@@ -515,19 +515,17 @@ final class TicketShow extends Component
*/
public function firstResponseSlaClasses(): string
{
$classes = 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-100';
if ($this->firstResponseWasBreached()) {
return 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100';
$classes = 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100';
} elseif ($this->ticket->first_response_at !== null) {
$classes = 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100';
} elseif ($this->firstResponseIsDueSoon()) {
$classes = 'border-amber-300 bg-amber-50 text-amber-950 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100';
}
if ($this->ticket->first_response_at !== null) {
return 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100';
}
if ($this->firstResponseIsDueSoon()) {
return 'border-amber-300 bg-amber-50 text-amber-950 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100';
}
return 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-100';
return $classes;
}
/**
@@ -567,19 +565,17 @@ final class TicketShow extends Component
*/
public function resolutionSlaClasses(): string
{
$classes = 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-100';
if ($this->resolutionWasBreached()) {
return 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100';
$classes = 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100';
} elseif ($this->ticket->completed_at !== null) {
$classes = 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100';
} elseif ($this->resolutionIsDueSoon()) {
$classes = 'border-amber-300 bg-amber-50 text-amber-950 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100';
}
if ($this->ticket->completed_at !== null) {
return 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100';
}
if ($this->resolutionIsDueSoon()) {
return 'border-amber-300 bg-amber-50 text-amber-950 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100';
}
return 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-100';
return $classes;
}
/**
@@ -774,12 +770,6 @@ final class TicketShow extends Component
/**
* Add a case-insensitive LIKE condition that works on PostgreSQL and SQLite tests.
*/
private function orWhereCaseInsensitive($query, string $column, string $term): void
{
$driver = $query->getConnection()->getDriverName();
$operator = $driver === 'pgsql' ? $this->gitCommitLikeOperator() : 'LIKE';
$query->orWhere($column, $operator, '%' . $term . '%');
}
/**
* Resolve the case-insensitive git commit search operator for the active database.
+3 -1
View File
@@ -11,6 +11,8 @@ final class TicketPolicy
{
public function viewAny(User $user): bool
{
unset($user);
return true;
}
@@ -23,7 +25,7 @@ final class TicketPolicy
public function create(User $user): bool
{
return true;
return $user->exists;
}
public function update(User $user, Ticket $ticket): bool
@@ -648,10 +648,9 @@ span.flatpickr-weekday {
gap: .5rem;
border-radius: .85rem;
padding: .62rem 1.05rem;
font-size: .875rem;
font-size: .95rem;
line-height: 1.25rem;
font-weight: 800;
font-size: .95rem;
color: #ffffff !important;
text-decoration: none !important;
border: 1px solid rgba(255,255,255,.14);
@@ -53,10 +53,14 @@
<section class="overflow-hidden rounded-2xl border border-zinc-700 bg-zinc-950 shadow-sm">
<div class="overflow-auto">
<table class="min-w-full border-collapse font-mono text-xs leading-6">
<caption class="sr-only">Source code for {{ $attachmentName }}</caption>
<thead class="sr-only">
<tr><th scope="col">Line</th><th scope="col">Source</th></tr>
</thead>
<tbody>
@foreach ($lines as $index => $line)
<tr class="group hover:bg-zinc-900/80">
<td class="select-none border-r border-zinc-800 bg-zinc-900 px-4 text-right text-zinc-500">{{ $index + 1 }}</td>
<th scope="row" class="select-none border-r border-zinc-800 bg-zinc-900 px-4 text-right font-normal text-zinc-500">{{ $index + 1 }}</th>
<td class="whitespace-pre px-4 text-zinc-100"><code>{{ $line }}</code></td>
</tr>
@endforeach
@@ -1,5 +1,6 @@
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<caption class="sr-only">Data table</caption>
<thead
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
<tr>
@@ -1,5 +1,6 @@
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<caption class="sr-only">Data table</caption>
<thead
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
<tr>
@@ -1,5 +1,6 @@
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<caption class="sr-only">Data table</caption>
<thead
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
<tr>
@@ -1,5 +1,6 @@
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<caption class="sr-only">Data table</caption>
<thead
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
<tr>
@@ -393,6 +393,7 @@
<div
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<caption class="sr-only">Data table</caption>
<thead
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
<tr>
@@ -543,6 +544,7 @@
<div
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<caption class="sr-only">Data table</caption>
<thead
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
<tr>
@@ -709,6 +711,7 @@
<div
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<caption class="sr-only">Data table</caption>
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase text-zinc-500 dark:bg-zinc-800">
<tr>
<th class="px-4 py-3">Policy</th>
@@ -59,6 +59,7 @@
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<caption class="sr-only">Data table</caption>
<thead
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
<tr>
@@ -1657,7 +1657,7 @@
<style>
[data-helpdesk-sidebar-cards] details > summary::-webkit-details-marker {
display: none;
}
[data-helpdesk-sidebar-cards] details > summary {
+24 -37
View File
@@ -45,16 +45,13 @@ final class AttachmentService
$size = (int) Storage::disk($disk)->size($path);
}
Attachment::query()->create($this->attachmentAttributes(
ticket: $ticket,
comment: $comment,
userId: $userId,
disk: $disk,
path: $path,
originalName: $originalName,
mimeType: $mimeType,
size: $size,
));
Attachment::query()->create($this->attachmentAttributes($ticket, $comment, $userId, [
'disk' => $disk,
'path' => $path,
'original_name' => $originalName,
'mime_type' => $mimeType,
'size' => $size,
]));
}
}
@@ -72,29 +69,29 @@ final class AttachmentService
*
* @return array<string, mixed>
*/
private function attachmentAttributes(Ticket $ticket, ?Comment $comment, int $userId, string $disk, string $path, string $originalName, string $mimeType, int $size): array
private function attachmentAttributes(Ticket $ticket, ?Comment $comment, int $userId, array $file): array
{
$attributes = [
'ticket_id' => $ticket->id,
'comment_id' => $comment?->id,
'user_id' => $userId,
'disk' => $disk,
'path' => $path,
'original_name' => $originalName,
'mime_type' => $mimeType,
'size' => $size,
'disk' => $file['disk'],
'path' => $file['path'],
'original_name' => $file['original_name'],
'mime_type' => $file['mime_type'],
'size' => $file['size'],
];
if (Schema::hasColumn('helpdesk_attachments', 'extension')) {
$attributes['extension'] = strtolower((string) pathinfo($originalName, PATHINFO_EXTENSION));
$attributes['extension'] = strtolower((string) pathinfo($file['original_name'], PATHINFO_EXTENSION));
}
if (Schema::hasColumn('helpdesk_attachments', 'category')) {
$attributes['category'] = $this->categoryFor($originalName, $mimeType);
$attributes['category'] = $this->categoryFor($file['original_name'], $file['mime_type']);
}
if (Schema::hasColumn('helpdesk_attachments', 'checksum_sha256') && Storage::disk($disk)->exists($path)) {
$stream = Storage::disk($disk)->readStream($path);
if (Schema::hasColumn('helpdesk_attachments', 'checksum_sha256') && Storage::disk($file['disk'])->exists($file['path'])) {
$stream = Storage::disk($file['disk'])->readStream($file['path']);
if (is_resource($stream)) {
$context = hash_init('sha256');
@@ -114,23 +111,13 @@ final class AttachmentService
{
$extension = strtolower((string) pathinfo($originalName, PATHINFO_EXTENSION));
if (str_starts_with($mimeType, 'image/') || in_array($extension, ['png', 'jpg', 'jpeg', 'webp', 'gif'], true)) {
return 'image';
}
if ($extension === 'pdf' || $mimeType === 'application/pdf') {
return 'pdf';
}
if (in_array($extension, ['zip', 'tar', 'gz', '7z', 'rar'], true)) {
return 'archive';
}
if (in_array($extension, config('helpdesk.allowed_source_extensions', []), true)) {
return 'source';
}
return 'file';
return match (true) {
str_starts_with($mimeType, 'image/'), in_array($extension, ['png', 'jpg', 'jpeg', 'webp', 'gif'], true) => 'image',
$extension === 'pdf', $mimeType === 'application/pdf' => 'pdf',
in_array($extension, ['zip', 'tar', 'gz', '7z', 'rar'], true) => 'archive',
in_array($extension, config('helpdesk.allowed_source_extensions', []), true) => 'source',
default => 'file',
};
}
/**
@@ -29,18 +29,7 @@ final class AutomationRuleService
return;
}
$updates = array_filter([
'priority_id' => $rule->set_priority_id,
'category_id' => $rule->set_category_id,
'team_id' => $rule->set_team_id,
'project_id' => $rule->set_project_id,
'status_id' => $rule->set_status_id,
'agent_id' => $rule->set_agent_id,
], static fn ($value): bool => $value !== null);
if ($updates !== []) {
$ticket->forceFill($updates + ['last_activity_at' => now()])->save();
}
$this->applyRuleUpdates($rule, $ticket);
});
return $ticket->fresh() ?? $ticket;
@@ -48,19 +37,30 @@ final class AutomationRuleService
private function matches(AutomationRule $rule, Ticket $ticket, string $sender, string $mailbox): bool
{
if ($rule->match_sender && ! Str::contains(Str::lower($sender), Str::lower($rule->match_sender))) {
return false;
}
if ($rule->match_subject && ! Str::contains(Str::lower($ticket->subject), Str::lower($rule->match_subject))) {
return false;
}
if ($rule->match_mailbox && ! Str::contains(Str::lower($mailbox), Str::lower($rule->match_mailbox))) {
return false;
}
if ($rule->match_category_id && (int) $ticket->category_id !== (int) $rule->match_category_id) {
return false;
}
return $this->containsWhenConfigured($sender, $rule->match_sender)
&& $this->containsWhenConfigured($ticket->subject, $rule->match_subject)
&& $this->containsWhenConfigured($mailbox, $rule->match_mailbox)
&& (! $rule->match_category_id || (int) $ticket->category_id === (int) $rule->match_category_id);
}
return true;
private function containsWhenConfigured(string $value, ?string $match): bool
{
return ! $match || Str::contains(Str::lower($value), Str::lower($match));
}
private function applyRuleUpdates(AutomationRule $rule, Ticket $ticket): void
{
$updates = array_filter([
'priority_id' => $rule->set_priority_id,
'category_id' => $rule->set_category_id,
'team_id' => $rule->set_team_id,
'project_id' => $rule->set_project_id,
'status_id' => $rule->set_status_id,
'agent_id' => $rule->set_agent_id,
], static fn ($value): bool => $value !== null);
if ($updates !== []) {
$ticket->forceFill($updates + ['last_activity_at' => now()])->save();
}
}
}
@@ -23,6 +23,8 @@ use Throwable;
final class GitWebhookIngestionService
{
private const PUSH_HOOK = 'push hook';
/**
* Matches the generated Helpdesk reference format, for example HD-2026-00042.
*/
@@ -173,10 +175,10 @@ final class GitWebhookIngestionService
return match ($normalisedProvider) {
'github' => in_array($normalisedEvent, ['push', ''], true),
'gitlab' => in_array($normalisedEvent, ['push hook', 'push', ''], true),
'gitea' => in_array($normalisedEvent, ['push', 'push hook', ''], true),
'gitlab' => in_array($normalisedEvent, [self::PUSH_HOOK, 'push', ''], true),
'gitea' => in_array($normalisedEvent, ['push', self::PUSH_HOOK, ''], true),
'bitbucket' => in_array($normalisedEvent, ['repo push', 'push', ''], true),
default => in_array($normalisedEvent, ['push', 'repo push', 'push hook', ''], true),
default => in_array($normalisedEvent, ['push', 'repo push', self::PUSH_HOOK, ''], true),
};
}
@@ -214,23 +216,14 @@ final class GitWebhookIngestionService
{
$commits = $payload['commits'] ?? null;
if (is_array($commits) && $commits !== []) {
return array_values(array_filter($commits, static fn (mixed $commit): bool => is_array($commit)));
if (! is_array($commits) || $commits === []) {
$headCommit = $payload['head_commit'] ?? null;
$commits = is_array($headCommit) ? [$headCommit] : Arr::get($payload, 'push.changes.0.commits', []);
}
$headCommit = $payload['head_commit'] ?? null;
if (is_array($headCommit)) {
return [$headCommit];
}
$bitbucketCommits = Arr::get($payload, 'push.changes.0.commits', []);
if (is_array($bitbucketCommits)) {
return array_values(array_filter($bitbucketCommits, static fn (mixed $commit): bool => is_array($commit)));
}
return [];
return is_array($commits)
? array_values(array_filter($commits, static fn (mixed $commit): bool => is_array($commit)))
: [];
}
/**
+97 -68
View File
@@ -10,6 +10,7 @@ use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Modules\Helpdesk\Exceptions\TicketPersistenceException;
use Modules\Helpdesk\Models\Comment;
use Modules\Helpdesk\Models\KanbanBoard;
use Modules\Helpdesk\Models\Status;
@@ -45,7 +46,7 @@ final class TicketService
}
}
throw new \RuntimeException('Unable to create a unique Helpdesk ticket reference.');
throw new TicketPersistenceException('Unable to create a unique Helpdesk ticket reference.');
}
/**
@@ -56,32 +57,8 @@ final class TicketService
*/
private function createInsideTransaction(User $user, array $data, array $files = []): Ticket
{
$defaultStatusSlug = (string) config('helpdesk.default_status_slug', 'new');
$status = Schema::hasTable('helpdesk_statuses') && Schema::hasColumn('helpdesk_statuses', 'is_default')
? Status::query()->where('is_default', true)->orderBy('sort_order')->first()
: null;
if (! $status) {
$status = Status::query()->where('slug', $defaultStatusSlug)->first();
}
if (! $status) {
$status = Status::query()->create([
'name' => 'New',
'slug' => $defaultStatusSlug,
'color' => 'sky',
'sort_order' => 1,
'is_closed' => false,
]);
}
$boardId = $data['board_id'] ?? null;
if (blank($boardId) && Schema::hasTable('helpdesk_kanban_boards')) {
$boardId = KanbanBoard::query()
->where('is_default', true)
->orderBy('sort_order')
->value('id') ?: KanbanBoard::query()->orderBy('sort_order')->value('id');
}
$status = $this->resolveInitialStatus();
$boardId = $this->resolveBoardId($data['board_id'] ?? null);
/** @var Ticket $ticket */
$ticket = Ticket::query()->create([
@@ -102,9 +79,7 @@ final class TicketService
'last_activity_at' => now(),
]);
if (! empty($data['tag_ids']) && is_array($data['tag_ids'])) {
$ticket->tags()->sync(array_values(array_unique(array_map('intval', $data['tag_ids']))));
}
$this->syncTags($ticket, $data['tag_ids'] ?? null);
$ticket = $this->automation->apply($ticket, $data);
$ticket = $this->sla->applyPolicy($ticket);
@@ -117,6 +92,43 @@ final class TicketService
return $ticket;
}
private function resolveInitialStatus(): Status
{
$defaultStatusSlug = (string) config('helpdesk.default_status_slug', 'new');
$status = Schema::hasTable('helpdesk_statuses') && Schema::hasColumn('helpdesk_statuses', 'is_default')
? Status::query()->where('is_default', true)->orderBy('sort_order')->first()
: null;
$status ??= Status::query()->where('slug', $defaultStatusSlug)->first();
return $status ?? Status::query()->create([
'name' => 'New',
'slug' => $defaultStatusSlug,
'color' => 'sky',
'sort_order' => 1,
'is_closed' => false,
]);
}
private function resolveBoardId(mixed $boardId): mixed
{
if (! blank($boardId) || ! Schema::hasTable('helpdesk_kanban_boards')) {
return $boardId;
}
return KanbanBoard::query()->where('is_default', true)->orderBy('sort_order')->value('id')
?: KanbanBoard::query()->orderBy('sort_order')->value('id');
}
private function syncTags(Ticket $ticket, mixed $tagIds): void
{
if (! is_array($tagIds) || $tagIds === []) {
return;
}
$ticket->tags()->sync(array_values(array_unique(array_map('intval', $tagIds))));
}
/**
* Determine whether a database error was caused by the ticket reference unique index.
*/
@@ -137,52 +149,69 @@ final class TicketService
public function comment(Ticket $ticket, User $user, string $body, bool $internal = false, array $files = [], ?int $parentId = null, ?string $subject = null): Comment
{
return DB::transaction(function () use ($ticket, $user, $body, $internal, $files, $parentId, $subject): Comment {
$parent = $parentId === null
? null
: Comment::query()
->where('ticket_id', $ticket->id)
->findOrFail($parentId);
$guid = (string) Str::uuid();
$unid = strtoupper(hash('sha256', $guid . '|' . $ticket->id . '|' . microtime(true)));
$parentUnid = $parent?->unid;
$rootUnid = $parent?->root_unid ?: $parentUnid ?: $unid;
$plainTextBody = trim(preg_replace('/\s+/', ' ', strip_tags($body)) ?: '');
$payload = [
'parent_id' => $parentId,
'user_id' => $user->id,
'body' => $body,
'body_text' => $plainTextBody,
'is_internal' => $internal,
'guid' => $guid,
'unid' => $unid,
'parent_unid' => $parentUnid,
'root_unid' => $rootUnid,
'originator_id' => $unid . ':1',
'sync_sequence' => 1,
'sync_hash' => hash('sha256', $body),
'source_system' => 'wms-helpdesk',
];
if (Schema::hasColumn('helpdesk_comments', 'subject')) {
$payload['subject'] = trim((string) ($subject ?: ($parentId === null ? 'Response' : 'RE: Response')));
}
$parent = $this->resolveParentComment($ticket, $parentId);
$payload = $this->commentPayload($ticket, $user, $body, $internal, $parent, $parentId, $subject);
/** @var Comment $comment */
$comment = $ticket->comments()->create($payload);
$ticket->forceFill(['last_activity_at' => now()])->save();
if (! $internal) {
$this->sla->recordPublicResponse($ticket, $user);
}
$this->attachments->storeMany($ticket, $files, (int) $user->id, $comment);
$this->workflow->dispatch($ticket->fresh(['status', 'priority', 'category', 'project', 'team']) ?? $ticket, $parentId === null ? 'ticket.commented' : 'ticket.reply_created', $user);
$this->afterCommentCreated($ticket, $user, $comment, $files, $internal, $parentId);
return $comment->fresh(['user', 'attachments']) ?? $comment;
});
}
private function resolveParentComment(Ticket $ticket, ?int $parentId): ?Comment
{
return $parentId === null
? null
: Comment::query()->where('ticket_id', $ticket->id)->findOrFail($parentId);
}
/** @return array<string, mixed> */
private function commentPayload(Ticket $ticket, User $user, string $body, bool $internal, ?Comment $parent, ?int $parentId, ?string $subject): array
{
$guid = (string) Str::uuid();
$unid = strtoupper(hash('sha256', $guid . '|' . $ticket->id . '|' . microtime(true)));
$parentUnid = $parent?->unid;
$payload = [
'parent_id' => $parentId,
'user_id' => $user->id,
'body' => $body,
'body_text' => trim(preg_replace('/\s+/', ' ', strip_tags($body)) ?: ''),
'is_internal' => $internal,
'guid' => $guid,
'unid' => $unid,
'parent_unid' => $parentUnid,
'root_unid' => $parent?->root_unid ?: $parentUnid ?: $unid,
'originator_id' => $unid . ':1',
'sync_sequence' => 1,
'sync_hash' => hash('sha256', $body),
'source_system' => 'wms-helpdesk',
];
if (Schema::hasColumn('helpdesk_comments', 'subject')) {
$defaultSubject = $parentId === null ? 'Response' : 'RE: Response';
$payload['subject'] = trim((string) ($subject ?: $defaultSubject));
}
return $payload;
}
/** @param array<int, UploadedFile> $files */
private function afterCommentCreated(Ticket $ticket, User $user, Comment $comment, array $files, bool $internal, ?int $parentId): void
{
$ticket->forceFill(['last_activity_at' => now()])->save();
if (! $internal) {
$this->sla->recordPublicResponse($ticket, $user);
}
$this->attachments->storeMany($ticket, $files, (int) $user->id, $comment);
$event = $parentId === null ? 'ticket.commented' : 'ticket.reply_created';
$freshTicket = $ticket->fresh(['status', 'priority', 'category', 'project', 'team']) ?? $ticket;
$this->workflow->dispatch($freshTicket, $event, $user);
}
/**
* Close a ticket.
*/
@@ -34,7 +34,7 @@ final class WorkflowNotificationService
$recipient = $this->recipient($template->to_role, $ticket, $actor);
if ($recipient === null || blank($recipient->email ?? null)) {
$this->log($template, $ticket, $event, null, 'skipped', 'No recipient email was available.');
$this->log($template, $ticket, $event, ['recipient' => null, 'status' => 'skipped', 'error' => 'No recipient email was available.']);
return;
}
@@ -42,7 +42,6 @@ final class WorkflowNotificationService
$trackingToken = Str::random(48);
$subject = $this->renderText($template->subject, $ticket, $actor);
$html = $this->wrapHtml($this->renderText($template->html_body, $ticket, $actor), $ticket, $trackingToken);
$text = $this->renderText($template->text_body ?: strip_tags($template->html_body), $ticket, $actor);
try {
Mail::html($html, function ($message) use ($recipient, $template, $subject): void {
@@ -57,10 +56,10 @@ final class WorkflowNotificationService
}
});
$this->log($template, $ticket, $event, (string) $recipient->email, 'sent', null, $subject, $trackingToken);
$this->log($template, $ticket, $event, ['recipient' => (string) $recipient->email, 'status' => 'sent', 'subject' => $subject, 'tracking_token' => $trackingToken]);
} catch (Throwable $exception) {
Log::warning('Helpdesk workflow email failed.', ['event' => $event, 'ticket_id' => $ticket->id, 'error' => $exception->getMessage()]);
$this->log($template, $ticket, $event, (string) $recipient->email, 'failed', $exception->getMessage(), $subject);
$this->log($template, $ticket, $event, ['recipient' => (string) $recipient->email, 'status' => 'failed', 'error' => $exception->getMessage(), 'subject' => $subject]);
}
});
}
@@ -158,17 +157,18 @@ final class WorkflowNotificationService
return '<img src="' . e($url) . '" width="1" height="1" alt="" style="display:none" />';
}
private function log(WorkflowTemplate $template, Ticket $ticket, string $event, ?string $recipient, string $status, ?string $error = null, ?string $subject = null, ?string $trackingToken = null): void
/** @param array{recipient:?string,status:string,error?:?string,subject?:?string,tracking_token?:?string} $details */
private function log(WorkflowTemplate $template, Ticket $ticket, string $event, array $details): void
{
WorkflowLog::query()->create([
'template_id' => $template->id,
'ticket_id' => $ticket->id,
'event' => $event,
'recipient' => $recipient,
'subject' => $subject,
'status' => $status,
'tracking_token' => $trackingToken,
'error' => $error,
'recipient' => $details['recipient'],
'subject' => $details['subject'] ?? null,
'status' => $details['status'],
'tracking_token' => $details['tracking_token'] ?? null,
'error' => $details['error'] ?? null,
]);
}
}
@@ -22,14 +22,12 @@ trait BuildsHelpdeskFixtures
{
protected function user(array $attributes = []): User
{
/** @var User $user */
$user = User::factory()->create(array_merge([
/** @var User */
return User::factory()->create(array_merge([
'name' => 'Helpdesk User',
'email' => fake()->unique()->safeEmail(),
'password' => Hash::make('password'),
], $attributes));
return $user;
}
protected function agent(array $attributes = []): User
@@ -64,6 +64,7 @@
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light">
<tr>
<th>Part Number</th>
+98
View File
@@ -0,0 +1,98 @@
<?php
// M3 now uses Core's PostgreSQL connection. No per-user DB credentials are required.
return [
#'nonWorkingHourCode' => ['73,85,89,90,91,92,86,99'],
'autorefresh_all' => ['00:15'],
'autorefresh_notall' => ['00:15'],
'payrollCodes' => ['33,49,75,94,98,102,171'],
'min_work_hours_for_break' => ['4'],
'ignore_picks_that_last_longer_than_this_many_hours' => ['8'],
'env_ports' => ['test'=>'10004','live'=>'10005'],
'hold_code_array' => ['A'=>'Admin Hold',
'C'=>'AR Past Due',
'D'=>'Over Credit Limit',
'E'=>'Over Order Limit',
'F'=>'Address Check Fail',
'Q'=>'Quote Hold',
'H'=>'A/R Cust Hold',
'I'=>'EDI Hold',
'P'=>'EDI Price Hold',
'Z'=>'Haz Hold',
'X'=>'CID Hold',
'W'=>'Web Hold',
'T'=>'Duplicate Hold'],
'user_array' => '', //$user_array,
'misc' => [
'discountCarrierRateForCustomerPriceLTL'=>0.00, # This is the percent off list that we want to charge customers
'discountCarrierRateForCustomerPriceParcel'=>0.00, # This is the percent off list that we want to charge customers - to use in ship confirm
],
'transxTypes' =>[
'ICM710E' => 'Splitting delivery orders',
'ICM710E13' => 'Pre-Splitting interco delivery orders',
'ICM710E12' => 'Pre-Splitting customer delivery orders',
'M3InventoryReportListURL' => 'Retrieve inventory on hand',
'M3InventoryReportReplen' => 'Retrieve Replenishments',
'M3ReceiverAPI' => 'Post Splitting delivery orders',
'PdfStorageAPI' => 'PDF document storage',
'PackSyst4tray' => 'Change of qty picked after picking',
'CloseShipmentCoChargeAPI' => 'Closing delivery orders',
'TransferAPIURL' => 'Move goods to Pack/Dock locations',
'OP705API' => 'Closing a manifest',
'CloseShipmentCoChargeAPIMfest' => 'Batch closing for M3 manifests at night',
'M3-General' => 'General - contact MPBA',
'UpdateShipmentNumber' => 'Update Shipment Number',
],
'curlTimeOut' => 30,
'PHPTimeOut' => 600,
'PHPTimeOutCL'=> 1000,
'maxRecDL' => 20000,
'maxRecCL' => 55000,
'BadChars' => ['–','\t','\n','\u20134807','\u20131','\u201333','\u2013671' ,'Ÿ','á', 'é', 'í', 'ó' ,'ú','“','”'],
'PdfPath' => env('M3_LABELS_PATH', storage_path('app/m3/labels') . DIRECTORY_SEPARATOR), # Warning : The path for PDF storage is shared by all Instances and should not be altered
'DUMMYPDF' => '',
'uom_size' => ['110-142-303'=>'mm',
'110-143-321'=>'mm',
'110-143-322'=>'in'], # unit of mesure for boxes lengths mm or in
'uom_wt' => ['110-142-303'=>'kg',
'110-143-321'=>'kg',
'110-143-322'=>'lbs'], # unit of mesure for boxes weight kg or lbs
#STATUS ARRAY FOR ICM688M1
'status_ICM688M1_array' => [
"P"=>"Pending error checking by splitting (OP510E2)",
"H"=>"Hold as error found (OP510E3)",
"L"=>"Ready to pick",
"I"=>"In picking",
"F"=>"In packing",
"X"=>"Cancelled",
"C"=>"Packed",
"B"=>"Loaded",
"a"=>"Manually removed from Sentinel ship confirm screen",
"A"=>"ShipConfirmed" ],
'whsePerf_targets' => [
'all_notes'=>'90', 'all_dollars'=>'85', 'all_weight'=>'90',
'ltl_notes'=>'90', 'ltl_dollars'=>'95', 'ltl_weight'=>'99',
'parcel_notes'=>'90', 'parcel_dollars'=>'75', 'parcel_weight'=>'80'
],
'2Ship' => [
# 2Ship WS_Key
# live $TwoShip_Key="FED945D1-5DC7-415E-BDAB-16347E7C1304";
# test $TwoShip_Key="45027F2A-EDA6-4C16-8AD4-9BC70CE94AB8";
'TwoShip_Key' => "45027F2A-EDA6-4C16-8AD4-9BC70CE94AB8",
'end_point' =>"https://euroapi.2ship.com/api/",
'hold_update' =>"UpdateOnHoldOrder_V1",
],
];
+40
View File
@@ -0,0 +1,40 @@
<?php
# PayRollCodes are the keys (left) to read LBPF, lines per hours (right)
return [
'lph_per_code_array' => ['16'=>'30',
'33'=>'12',
'49'=>'25',
'66'=>'60',
'75'=>'15',
'94'=>'40',
'98'=>'8',
'102'=>'10',
'171'=>'17'
]
];
# '75'=>'26',
# '75'=>'23',
# '33'=>'13', #2025-08-04
# Found in BLDPERFST rpg pgm in EWRPGSRC / EWSRCTST
# PICKING stats
# when a5sentview in ('7','9') then 33
# when a5sentview = 5 then 49
# when a5sentview = 12 then 98
# when a5sentview in ('2','10') then 75
# when a5sentview in ('1','3','4','11') then 94
# when a5sentview = 8 then 102
# when a5sentview = 6 then 171
# else 999
# PACKING stats
# case when (select AYTYPE from ICM695M1
# where AYINTBOX=AZINTBOX
# and AYSENTPKR=AZSENTPKR FETCH FIRST ROW ONLY)
# in ('','BX') then '16' else '66' end as AYTYPE,
?>
+3 -3
View File
@@ -2,7 +2,7 @@
return [
'name' => 'M3',
'features' =>
[
],
'connection' => env('M3_DB_CONNECTION', env('DB_CONNECTION', 'pgsql')),
'labels_path' => env('M3_LABELS_PATH', storage_path('app/m3/labels')),
'closing_orders_path' => env('M3_CLOSING_ORDERS_PATH', storage_path('app/m3/closing-orders')),
];
+144
View File
@@ -0,0 +1,144 @@
<?php
namespace Modules\M3\Console;
use Illuminate\Console\Command;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Log;
use Str;
use Modules\M3\Http\Controllers\API\CloseShipment;
class CallCloseAPI extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:CallCloseAPI {user?} {manifest?} {co?} {whs?} ';
protected $description = 'Call Close API';
public function __construct()
{
parent::__construct();
}
public function handle()
{
//$this->info('Start CallCloseAPI...');
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'));
try {
# Get the Calling User
$userId = $this->argument('user');
$SHUSER = isset($userId) ? $userId : (m3_user() !== '' ? Str::upper(trim(m3_user())) : '');
# Get the Manifest Number
$manifest = $this->argument('manifest');
$MANIFESTNum = isset($manifest) ? $manifest : '';
$co = $this->argument('co');
$APCO = isset($co) ? $co : '';
$whs = $this->argument('whs');
$APWHS = isset($whs) ? $whs : '';
// if ($SHUSER!='') {
// Log::channel('dailyCron')->info("Cron...Start CallCloseAPI...For Manifest#: ".$MANIFESTNum); // ".$SHUSER."
if( $MANIFESTNum==''){
# Get Macpickers from ICM705M1
$ICM705M1 = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1')
->select('APMANIFEST','APCO','APWHS')
->where('APSPARE2','')
->where('APMANIFEST','!=','0')
->whereRaw('EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - APLDCLOZ)) / 60 > 15')
->orderBy('APMANIFEST','desc')
->get();
// dump($ICM705M1);
if (count($ICM705M1)>0) {
# Loop the Results from $ICM705M1
foreach ($ICM705M1 as $key => $ICM705M1R) {
$MANIFESTNum = $ICM705M1R->APMANIFEST;
$USERCO = isset($ICM705M1R->APCO) ? $ICM705M1R->APCO : '';
$USERWH = isset($ICM705M1R->APWHS) ? $ICM705M1R->APWHS : '';
# Update ICM705M1 and set APSPARE2 to P
$ICM705M1U = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1')
->where('APMANIFEST',$MANIFESTNum)
->where('APCO',$USERCO)
->where('APWHS',$USERWH)
->update(["APSPARE2"=>"P"]);
$myRequest = new Request();
$myRequest->setMethod('POST');
$myRequest->request->add([
'WHS' => $USERWH,
'COMPANY' => $USERCO,
'TRANSTYPE' => 'M',
'MANIFEST' => $MANIFESTNum
]);
$callCloseShipment = CloseShipment::CloseShipmentCoChargeAPI($myRequest);
$responseData = $callCloseShipment->getData();
$jsonData = json_encode($responseData);
Log::channel('dailyCron')->info("Close Shipment API Response for Manifest:".(string)(int)$MANIFESTNum.'-'.$jsonData);
}
}
}
# Call Closing for a specific Manifest
else{
# Get Macpickers from ICM705M1
$ICM705M1 = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1')
->select('APMANIFEST','APCO','APWHS')
->where('APSPARE2','')
->where('APCO',$APCO)
->where('APWHS',$APWHS)
->where('APMANIFEST','=',$MANIFESTNum)
->first();
# Update ICM705M1 and set APSPARE2 to P
$ICM705M1U = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1')
->where('APMANIFEST',$MANIFESTNum)
->where('APCO',$APCO)
->where('APWHS',$APWHS)
->update(["APSPARE2"=>"P"]);
$myRequest = new Request();
$myRequest->setMethod('POST');
$myRequest->request->add([
'WHS' => $APWHS ,
'COMPANY' => $APCO,
'TRANSTYPE' => 'M',
'MANIFEST' => $MANIFESTNum
]);
$callCloseShipment = CloseShipment::CloseShipmentCoChargeAPI($myRequest);
$responseData = $callCloseShipment->getData();
$jsonData = json_encode($responseData);
Log::channel('dailyCron')->info("Close Shipment API Response for Manifest:".$MANIFESTNum.'-'.$jsonData);
return 1;
}
// }
// else{
// Log::channel('dailyCron')->error('A user must be defined...');
// $this->error('A user must be defined...');
// }
}
catch (\Throwable $th) {
// Log::error($th);
Log::channel('dailyCron')->error($th);
}
}
}
+261
View File
@@ -0,0 +1,261 @@
<?php
namespace Modules\M3\Console;
use Illuminate\Console\Command;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Log;
use Str;
use Excel;
use Storage;
class ClosedOrderSaveXls extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:ClosedOrderSaveXls {user?} {co?} {whs?} {pl?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Save Orders Closed Lines to Excel';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
//$this->info('Start CallCloseAPI...');
ini_set('max_execution_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('max_input_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOutCL'));
ini_set('memory_limit', '1024M');
try {
Log::channel('dailyCron')->info("Cron...Start Save Orders Closed Lines to Excel");
# Get the Calling User
$userId = $this->argument('user');
$SHUSER = isset($userId) ? $userId : (m3_user() !== '' ? Str::upper(trim(m3_user())) : '');
$CO = $this->argument('co');
$COMPANY = isset($CO) ? trim($CO) : '';
$PWHS = $this->argument('whs');
$WHS = isset($PWHS) ? trim($PWHS) : '';
$PPL = $this->argument('pl');
$PLANT = isset($PPL) ? trim($PPL) : '';
# Generate last Month Report
$date = new \DateTime('FIRST DAY OF THIS MONTH');
$date->modify('-1 month');
$fromPreviousMonth = $date->format('Y-m-d');
$toPreviousMonth = $date->modify('last day of this month')->format('Y-m-d');
$reportDate = \DateTime::createFromFormat('Y-m-d', $fromPreviousMonth )->format('Ymd');
$toreportDate = \DateTime::createFromFormat('Y-m-d', $toPreviousMonth )->format('Ymd');
$reportDateWD = $fromPreviousMonth;
$toreportDateWD = $toPreviousMonth;
if ($WHS == '321') {
$prevMonthFile = 'Closedorderlinesreport_'.$reportDateWD.'-'.$toreportDateWD.'.xlsx';
}
else {
$prevMonthFile = $WHS.'/Closedorderlinesreport_'.$reportDateWD.'-'.$toreportDateWD.'-'.$WHS.'.xlsx';
}
$this->info("Generate Last Month Report...");
$this->info("From Date: ".$reportDate);
$this->info("To Date: ".$toreportDate);
# Check that the Excel file Exist
$prevMonthFileExist = Storage::disk('publicCL')->exists($prevMonthFile) ? false: true ;
$this->info("Prev Month File Exist: " .$prevMonthFileExist);
if($reportDate!='' && $COMPANY!='' && $WHS!='' && $PLANT!='' && $prevMonthFileExist ){
# Case 729: Add Currency code A1SHCURR FROM ICM666M
$ICM688M2 = DB::connection(config('m3.connection', 'pgsql'))
->select("
SELECT (SHDT || ' ' || SHTIME) AS DATECLOSED, A5MACPKR, A5ONO,
A4OFSTRM,
A5PMPWHT*A5PACKED AS WEIGHT,
A4SCAC,
A4SHIPTYPE,
(SELECT A4REFORD FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4REFORD,
A5PART, A5RSVQTY, A5PACKED, A5RSVQTY - A5PACKED AS CALCULATED_COLUMN,
A5SLUNPR * A5PACKED AS ANOTHER_CALCULATED_COLUMN,
(SELECT A4SHSHNM FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4SHSHNM ,
CASE WHEN SHDT IS NULL THEN (SELECT SHDATA FROM ICM699M WHERE SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHSOURCE='CloseShipmentCoChargeAPI' ORDER BY SHDT DESC, SHTIME DESC FETCH FIRST 1 ROWS ONLY) ELSE '' END AS REASON,
A1SHCURR,
CASE
WHEN (SELECT A4SHIPTYPE FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) = 'LTL'
THEN (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceLTL'))/100 ."
ELSE (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceParcel') )/100 ."
END AS FREIGHT
FROM ICM688M2
LEFT JOIN ICM699M ON SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHDATA LIKE 'Success%' AND SHSOURCE='CloseShipmentCoChargeAPI'
LEFT JOIN ICM666M ON A1DELNO=A5MACPKR AND A1ONO=A5ONO AND A1PKLN=A5BRADLN
LEFT JOIN ICM688M1 ON A4MACPKR=A5MACPKR
WHERE A5MACPKR IN (
SELECT DISTINCT AYMACPKR
FROM ICM695M1
WHERE AYSTATUS IN ('Z','z') AND AYAPR8N002 BETWEEN $reportDate AND $toreportDate
)
AND A5CO=$COMPANY AND A5WHS=$WHS AND A5PLANT=$PLANT
");
// $newItem = ["A5MACPKR" => 'Delivery #', "A5ONO" => 'Order #', "A4REFORD" => 'Order Type',"A5PART" => 'Part #',"A5RSVQTY" => 'Reserved',
// "A5PACKED" => 'Packed',"CALCULATED_COLUMN" => 'Back Order Qty',"ANOTHER_CALCULATED_COLUMN" => 'Value Packed $',"A4SHSHNM" => 'Customer name',
// "SHDT" => 'Date',"SHTIME" => 'Time',"REASON" => 'Failure Reason','A1SHCURR'=> 'Currency Code','FREIGHT'=> 'Freight'];
$newItem = ["DATECLOSED" => 'Close Date',"A5MACPKR" => 'Delivery #', "A5ONO" => 'Order #',
"A4OFSTRM" => 'Terms',"WEIGHT" => 'Weight',"A4SCAC" => 'Carrier',"A4SHIPTYPE" => 'Mode (Parcel, Air, TL or LTL)',
"A4REFORD" => 'Order Type',"A5PART" => 'Part #',"A5RSVQTY" => 'Reserved',
"A5PACKED" => 'Packed',"CALCULATED_COLUMN" => 'Back Order Qty',"ANOTHER_CALCULATED_COLUMN" => 'Value Packed $',"A4SHSHNM" => 'Customer name',
"REASON" => 'Failure Reason','A1SHCURR'=> 'Currency Code','FREIGHT'=> 'Freight'];
$ICM688M2Col = collect($ICM688M2);
$ICM688M2Col->prepend($newItem);
# Count records before exporting to excel : Max 55K records are allowed
$ICM688M2Count = $ICM688M2Col->count();
# Set Max allowed records
$maxRecords = Config::get('arrays.maxRecCL');
if ($ICM688M2Count>$maxRecords) {
Log::channel('dailyCron')->info('Maximum allowed records for Excel export is: '.number_format($maxRecords).' current records: '.number_format($ICM688M2Count));
}
}
else{
$ICM688M2Col = collect();
}
if ($ICM688M2Col->count()>0) {
$this->info("Saving Last Month Excel Report to Disk");
Log::channel('dailyCron')->info("Saving Last Month Excel Report to Disk");
# Store the File to Storage
app(\Modules\M3\Services\SpreadsheetExport::class)->store($ICM688M2Col, $prevMonthFile, 'publicCL');
# Other File Formats : \Maatwebsite\Excel\Excel::CSV , ,\Maatwebsite\Excel\Excel::ODS
}
else{
Log::channel('dailyCron')->info("Monthly report, no records found: Nothing to save...");
}
# Generate current Month Report
$now = new \DateTime();
$first_day_of_month = $now->format('Y-m-01');
$current_date = $now->format('Y-m-d');
$reportDate2 = \DateTime::createFromFormat('Y-m-d', $first_day_of_month )->format('Ymd');
$toreportDate2 = \DateTime::createFromFormat('Y-m-d', $current_date )->format('Ymd');
if ($WHS == '321') {
$currentMonthFile = 'first_of_month_to_current_date.xlsx';
}
else{
$currentMonthFile = $WHS.'/first_of_month_to_current_date_'.$WHS.'.xlsx';
}
$this->info("Generate Current Month Report...");
$this->info("From Date: ".$reportDate2);
$this->info("To Date : ".$toreportDate2);
if($reportDate2!='' && $COMPANY!='' && $WHS!='' && $PLANT!=''){
$this->info(">>> Proceed...");
# Case 729: Add Currency code A1SHCURR FROM ICM666M
$ICM688M2 = DB::connection(config('m3.connection', 'pgsql'))
->select("
SELECT (SHDT || ' ' || SHTIME) AS DATECLOSED, A5MACPKR, A5ONO,
A4OFSTRM,
A5PMPWHT*A5PACKED AS WEIGHT,
A4SCAC,
A4SHIPTYPE,
(SELECT A4REFORD FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4REFORD,
A5PART, A5RSVQTY, A5PACKED, A5RSVQTY - A5PACKED AS CALCULATED_COLUMN,
A5SLUNPR * A5PACKED AS ANOTHER_CALCULATED_COLUMN,
(SELECT A4SHSHNM FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4SHSHNM ,
CASE WHEN SHDT IS NULL THEN (SELECT SHDATA FROM ICM699M WHERE SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHSOURCE='CloseShipmentCoChargeAPI' ORDER BY SHDT DESC, SHTIME DESC FETCH FIRST 1 ROWS ONLY) ELSE '' END AS REASON,
A1SHCURR,
CASE
WHEN (SELECT A4SHIPTYPE FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) = 'LTL'
THEN (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceLTL'))/100 ."
ELSE (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceParcel') )/100 ."
END AS FREIGHT
FROM ICM688M2
LEFT JOIN ICM699M ON SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHDATA LIKE 'Success%' AND SHSOURCE='CloseShipmentCoChargeAPI'
LEFT JOIN ICM666M ON A1DELNO=A5MACPKR AND A1ONO=A5ONO AND A1PKLN=A5BRADLN
LEFT JOIN ICM688M1 ON A4MACPKR=A5MACPKR
WHERE A5MACPKR IN (
SELECT DISTINCT AYMACPKR
FROM ICM695M1
WHERE AYSTATUS IN ('Z','z') AND AYAPR8N002 BETWEEN $reportDate2 AND $toreportDate2
)
AND A5CO=$COMPANY AND A5WHS=$WHS AND A5PLANT=$PLANT
");
// $newItem2 = ["A5MACPKR" => 'Delivery #', "A5ONO" => 'Order #', "A4REFORD" => 'Order Type',"A5PART" => 'Part #',"A5RSVQTY" => 'Reserved',
// "A5PACKED" => 'Packed',"CALCULATED_COLUMN" => 'Back Order Qty',"ANOTHER_CALCULATED_COLUMN" => 'Value Packed $',"A4SHSHNM" => 'Customer name',
// "SHDT" => 'Date',"SHTIME" => 'Time',"REASON" => 'Failure Reason','A1SHCURR'=> 'Currency Code','FREIGHT'=> 'Freight'];
$newItem2 = ["DATECLOSED" => 'Close Date',"A5MACPKR" => 'Delivery #', "A5ONO" => 'Order #',
"A4OFSTRM" => 'Terms',"WEIGHT" => 'Weight',"A4SCAC" => 'Carrier',"A4SHIPTYPE" => 'Mode (Parcel, Air, TL or LTL)',
"A4REFORD" => 'Order Type',"A5PART" => 'Part #',"A5RSVQTY" => 'Reserved',
"A5PACKED" => 'Packed',"CALCULATED_COLUMN" => 'Back Order Qty',"ANOTHER_CALCULATED_COLUMN" => 'Value Packed $',"A4SHSHNM" => 'Customer name',
"REASON" => 'Failure Reason','A1SHCURR'=> 'Currency Code','FREIGHT'=> 'Freight'];
$ICM688M2Col2 = collect($ICM688M2);
$ICM688M2Col2->prepend($newItem2);
# Count records before exporting to excel : Max 55K records are allowed
$ICM688M2Count = $ICM688M2Col2->count();
# Set Max allowed records
$maxRecords = Config::get('arrays.maxRecCL');
if ($ICM688M2Count>$maxRecords) {
Log::channel('dailyCron')->info('Maximum allowed records for Excel export is: '.number_format($maxRecords).' current records: '.number_format($ICM688M2Count));
}
}
else{
$ICM688M2Col2 = collect();
}
if ($ICM688M2Col2->count()>0) {
$this->info("Saving Current Month Excel Report to Disk");
Log::channel('dailyCron')->info("Saving Current Month Excel Report to Disk");
# Store the File to Storage
app(\Modules\M3\Services\SpreadsheetExport::class)->store($ICM688M2Col2, $currentMonthFile, 'publicCL');
}
else{
Log::channel('dailyCron')->info("Daily Report, no records found: Nothing to save...");
}
Log::channel('dailyCron')->info("Cron...End Save Orders Closed Lines to Excel");
$this->info("Cron...End Save Orders Closed Lines to Excel");
}
catch (\Throwable $th) {
// Log::error($th);
Log::channel('dailyCron')->error($th);
}
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace Modules\M3\Console;
use Illuminate\Console\Command;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Log;
use Str;
use Storage;
use File;
class DeleteOldLabels extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:DeleteOldLabelsCmd';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Delete Old Labels Command';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle(): void
{
//$this->info('Start CallCloseAPI...');
ini_set('max_execution_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('max_input_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOutCL'));
ini_set('memory_limit', '1024M');
try {
$this->info("Start Delete Old Labels Command...");
Log::channel('dailyCron')->info("Cron...Start Delete Old Labels Command...");
$labelsDirectoryTmp = '/www/sentm3live/htdocs/labels/';
$subFolders = ['CarrierLabel/','ContentLabel/','PackingList/']; // ,'CarrierLabel/','ContentLabel','PackingList',''
$daysOld = 180; // Number of days to consider a file old : 180 =Six Months
$filesToDeleteArr = [];
foreach ($subFolders as $key => $subFolder) {
$labelsDirectory = $labelsDirectoryTmp.$subFolder;
$this->info("Getting Files From Directory: ".$labelsDirectory);
Log::channel('dailyCron')->info("Getting Files From Directory: ".$labelsDirectory);
exec('find '.$labelsDirectory.' -ctime +180 -type f -exec rm {} \;');
# exec('find '.$labelsDirectory.' -ctime +190 -type f -print | wc -l');
}
$labelsFolder = $labelsDirectoryTmp;
# Get Files from the specified Directory
$filesToDelete = File::allFiles($labelsFolder);
foreach ($filesToDelete as $file) {
$lastAccessTime = new \DateTime("@".$file->getATime());
$now = new \DateTime();
$diff = $now->diff($lastAccessTime)->days;
if ($diff >= $daysOld) {
$this->info("File: ".$file." is Days Old: ".$diff);
// Log::channel('dailyCron')->info("File: ".$file." is Days Old: ".$diff);
# Add the File to Array
$filesToDeleteArr[] = $file;
}
}
// $this->info("Files to Delete...");
// dump($filesToDeleteArr);
# Delete Old Files
File::delete($filesToDeleteArr);
Log::channel('dailyCron')->info("Cron...End Delete Old Labels Command...");
$this->info("End Delete Old Labels Command...");
}
catch (\Throwable $th) {
// Log::error($th);
Log::channel('dailyCron')->error($th);
}
}
}
+289
View File
@@ -0,0 +1,289 @@
<?php
namespace Modules\M3\Console;
use Illuminate\Console\Command;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Log;
use Str;
use File;
use DateTime;
use Storage;
use Cache;
use Picqer\Barcode\BarcodeGeneratorPNG;
use Dompdf\Dompdf;
use Ayeo\Barcode;
use PDF;
use App;
use Zebra\Client;
use Zebra\Zpl\Image;
use Zebra\Zpl\Builder;
use Zebra\Zpl\GdDecoder;
class GenerateGS1Cmd extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:GenerateGS1Cmd {user?} {--gsarray=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate GS1-128 Label';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
//$this->info('Start CallCloseAPI...');
ini_set('max_execution_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('max_input_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOutCL'));
ini_set('memory_limit', '1024M');
try {
$this->info('>>> Run GS1 Label creation Command...');
Log::channel('LabelCron')->info(">>> Run GS1 Label creation Command...");
# Get the Calling User
$userId = $this->argument('user');
$SHUSER = isset($userId) ? $userId : (m3_user() !== '' ? Str::upper(trim(m3_user())) : 'TWOSHIP');
$this->info('User: '.$SHUSER);
# Get Manifest Array
$GSArray1 = $this->option('gsarray');
$GSArray = json_decode($GSArray1, true);
// dump($GSArray);
# MAK : Used for debug only
$GSArray = ["Macpkr"=> "55555","Recipient_Address1"=>"9571 Pan American DR", "Recipient_Address2"=>"C/O AEE Solar","Recipient_City"=>"El Paso",
"Recipient_CompanyName"=>"SNAPRACK EL PASO PRODUCTION","Recipient_CountryCode"=>"US","Recipient_PostalCode"=>"79927","Recipient_State"=>"TX",
"Sender_Address1"=>"6800 Arnold Miller Pkwy","Sender_Address2"=>"","Sender_City"=>"Solon",
"Sender_CompanyName"=>"Erico International","Sender_Country"=>"US","Sender_PostalCode"=>"44139","Sender_State"=>"OH",
"CarrierName"=>"R&L Carriers","trackingno"=>"306539838","BOLnumber"=>"693103848064454","ShipmentPONumber"=>"4523039092",
"PackagesArray"=> ["package 1" , "package 2"]
];
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();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; # User
$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 = '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;
foreach ($GSArray['PackagesArray'] as $key => $value) {
$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 = $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',
'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>';
// dump($html);
// App::call('App\Http\Controllers\utilitiesContoller@generateGS128');
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();
// dump($pdfContent);
$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");
$this->info("<<< GS1-128...End of Label Creation");
}
catch (\Throwable $th) {
// Log::error($th);
Log::channel('LabelCron')->error($th);
}
}
}
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace Modules\M3\Console;
use Illuminate\Console\Command;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Log;
use Str;
use Modules\M3\Http\Controllers\utilitiesContoller;
class UtilitiesCmd extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:UtilitiesCmd';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Utilities Command';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle(): void
{
//$this->info('Start CallCloseAPI...');
ini_set('max_execution_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('max_input_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOutCL'));
ini_set('memory_limit', '1024M');
try {
$this->info("Start Utilities Command...");
// $this->info('User: '.$SHUSER);
$myRequest = new Request();
utilitiesContoller::fixICM799MSentPKR($myRequest);
// Log::channel('dailyCron')->info("Cron...End Save Orders Closed Lines to Excel");
$this->info("End Utilities Command...");
}
catch (\Throwable $th) {
// Log::error($th);
Log::channel('dailyCron')->error($th);
}
}
}
+195
View File
@@ -0,0 +1,195 @@
<?php
namespace Modules\M3\Console;
use Illuminate\Console\Command;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Log;
use Str;
use Storage;
class WHSMetricsSaveXls extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:WHSMetricsSaveXls {user?} {co?} {whs?} {pl?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Save WHS Metrics to Excel';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
//$this->info('Start CallCloseAPI...');
ini_set('max_execution_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('max_input_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOutCL'));
ini_set('memory_limit', '1024M');
try {
Log::channel('dailyCron')->info("Cron...Start Save WHS Metrics to Excel");
# Get the Calling User
$userId = $this->argument('user');
$SHUSER = isset($userId) ? $userId : (m3_user() !== '' ? Str::upper(trim(m3_user())) : '');
$CO = $this->argument('co');
// $COMPANY = isset($CO) ? trim($CO) : '110';
$COMPANY = '110';
$PWHS = $this->argument('whs');
// $WHS = isset($PWHS) ? trim($PWHS) : '321';
$WHS = '321';
$PPL = $this->argument('pl');
// $PLANT = isset($PPL) ? trim($PPL) : '143';
$PLANT = '143';
$date = new \DateTime();
$DateToday = $date->format('Y-m-d');
# Define File Name
$whsPerfFile = 'WhsPerf/'.$WHS.'/WhsPerfMetrics_'.$DateToday.'-'.$WHS.'.xlsx';
Log::channel('dailyCron')->info(">>> File Name:");
Log::channel('dailyCron')->info($whsPerfFile);
$this->info("Generate Today WHS Performance Report...");
if( $COMPANY!='' && $WHS!='' && $PLANT!='' ){
# Get Header Content
$ICM700MHeadSel = DB::connection(config('m3.connection', 'pgsql'))
->select("
WITH TEMPFILE AS (
SELECT A.*,A4TPCKWGHT,A4NOPICLNS,A4PICKSTS,
CASE WHEN UPPER(A4PICKSTS)<>'A' THEN 'Open' ELSE 'Closed' END AS STATUS,
CASE WHEN A4TPCKWGHT>16000 THEN 'FTL' ELSE A4SHIPTYPE END AS A4SHIPTYPE,
(SELECT SUM(A5RSVQTY*A5SLUNPR) FROM ICM688M2 WHERE A4MACPKR=A5MACPKR) AS DOLLARS
FROM ICM700M A LEFT JOIN ICM688M1 B ON A4MACPKR=A3MACPKR
WHERE A3MSMTDATE= (SELECT MAX(A3MSMTDATE) FROM ICM700M) AND A3CO='$COMPANY' AND A3WHS='$WHS' AND A3PLT='$PLANT'
)
SELECT A4SHIPTYPE,STATUS,SOURCE,COUNT(*) AS PICKERS, SUM(A4TPCKWGHT) AS WEIGHT,SUM(A4NOPICLNS) AS LINES,SUM(DOLLARS) AS DOLLARS
FROM TEMPFILE
GROUP BY A4SHIPTYPE,STATUS,SOURCE
ORDER BY 1,3,2
");
// dd($ICM700MHeadSel);
$newItem = ["A4SHIPTYPE" => 'Method', "STATUS" => 'Status', "SOURCE" => 'Source', "PICKERS" => 'Pickers',
"WEIGHT" => 'Weight', "LINES" => 'Lines', "DOLLARS" => 'Dollars'];
# Trim All values
$ICM700MHeadCol = collect($ICM700MHeadSel)->map(function ($item) {
foreach ($item as $key => $value) {
if (is_string($value)) {
$item->{$key} = trim($value);
}
}
return $item;
});
# Prepend Header to Collection
$ICM700MHeadCol->prepend($newItem);
// $this->info($ICM700MHeadCol);
// Log::channel('dailyCron')->info(">>> Header Content");
// Log::channel('dailyCron')->info($ICM700MHeadCol);
# Count records before exporting to excel
$ICM700MHeadCount = $ICM700MHeadCol->count();
# Get Details Content
$ICM700MDetailsSel = DB::connection(config('m3.connection', 'pgsql'))
->select("
WITH TEMPFILE AS (
SELECT A3CO,A3WHS,A3PLT,A3MACPKR,A4PICKCRTS,SOURCE,
(SELECT TO_TIMESTAMP(CAST(SHDT AS TEXT) || ' ' || CAST(SHTIME AS TEXT), 'YYYY-MM-DD HH24:MI:SS') FROM ICM699M WHERE SHMACPKR=A4MACPKR AND SHTYPE='Lisnr-Hold' ORDER BY SHDT DESC, SHTIME DESC FETCH FIRST 1 ROWS ONLY) AS CARRIERASSIGNED,
A4ONO,A4SHSHNM,A4SCAC,A42SSVCNAM,A4PICKSTS,
CASE WHEN UPPER(A4PICKSTS)<>'A' THEN 'Open' ELSE 'Closed' END AS STATUS,
CASE WHEN A4TPCKWGHT>16000 THEN 'FTL' ELSE A4SHIPTYPE END AS A4SHIPTYPE,
A4TPCKWGHT,A4NOPICLNS,
(SELECT SUM(A5RSVQTY*A5SLUNPR) FROM ICM688M2 WHERE A4MACPKR=A5MACPKR) AS DOLLARS
FROM ICM700M A
LEFT JOIN ICM688M1 B ON A4MACPKR=A3MACPKR
WHERE A3MSMTDATE=(SELECT MAX(A3MSMTDATE) FROM ICM700M) AND A3CO='$COMPANY' AND A3WHS='$WHS' AND A3PLT='$PLANT'
)
SELECT * FROM TEMPFILE
");
$newItem2 = ["A3CO" => 'Co', "A3WHS" => 'Whs', "A3PLT" => 'Plt', "A3MACPKR" => 'Delivery No', "A4PICKCRTS" => 'Date Time Arrived in Sentinel',
"SOURCE" => 'Source', "CARRIERASSIGNED" => 'Date carrier Assigned', "A4ONO" => 'Order No', "A4SHSHNM" => 'Customer name',"A4SCAC" => 'Carrier',
"A42SSVCNAM" => 'Service', "A4PICKSTS" => 'Picker Status', "STATUS" => 'Open or Closed', "A4SHIPTYPE" => 'Method',
"A4TPCKWGHT" => 'Weight', "A4NOPICLNS" => 'Lines' , "DOLLARS" => 'Dollars' ];
# Trim all values
$ICM700MDetailsCol = collect($ICM700MDetailsSel)->map(function ($item) {
foreach ($item as $key => $value) {
if (is_string($value)) {
$item->{$key} = trim($value);
}
}
return $item;
});
# Prepend Header
$ICM700MDetailsCol->prepend($newItem2);
# Count records before exporting to excel
$ICM700MDetailsCount = $ICM700MDetailsCol->count();
}
else{
$ICM700MDetailsCol = collect();
}
// Log::channel('dailyCron')->info(">>> Detail Content");
// Log::channel('dailyCron')->info($ICM700MDetailsCol);
# Save Head and Details to XLS file
if ($ICM700MHeadCount>0 && $ICM700MDetailsCount>0) {
$this->info("Saving Today WHS Performance Report to Disk...");
Log::channel('dailyCron')->info("Saving Today WHS Performance Report to Disk...");
# Store both collections in one XLSX workbook using Core's PhpSpreadsheet dependency.
app(\Modules\M3\Services\SpreadsheetExport::class)->storeSheets([
'Summary' => $ICM700MHeadCol,
'Details' => $ICM700MDetailsCol,
], $whsPerfFile, 'publicLabels');
}
else{
Log::channel('dailyCron')->info("Today WHS Performance Report, no records found: Nothing to save...");
}
Log::channel('dailyCron')->info("Cron...End Save Today WHS Performance Report to Excel");
$this->info("Cron...End Save Today WHS Performance Report to Excel");
}
catch (\Throwable $th) {
// Log::error($th);
Log::channel('dailyCron')->error($th);
}
}
}
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::connection(config('m3.connection', 'pgsql'))->create('m3_program_requests', function (Blueprint $table): void {
$table->bigIncrements('id');
$table->string('type', 20);
$table->string('operation', 255);
$table->string('library', 255)->nullable();
$table->jsonb('payload')->nullable();
$table->string('requested_by', 255)->nullable();
$table->string('status', 30)->default('pending')->index();
$table->text('result')->nullable();
$table->text('error')->nullable();
$table->timestamp('processed_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::connection(config('m3.connection', 'pgsql'))->dropIfExists('m3_program_requests');
}
};
+25
View File
@@ -0,0 +1,25 @@
<?php
// app/Helpers/USStates.php
namespace Modules\M3\Helpers;
class CAProvinces
{
public static function all() {
return [
'AB' => 'Alberta',
'BC' => 'British Columbia',
'MB' => 'Manitoba',
'NB' => 'New Brunswick',
'NL' => 'Newfoundland and Labrador',
'NS' => 'Nova Scotia',
'NT' => 'Northwest Territories',
'NU' => 'Nunavut',
'ON' => 'Ontario',
'PE' => 'Prince Edward Island',
'QC' => 'Quebec',
'SK' => 'Saskatchewan',
'YT' => 'Yukon'
];
}
}
+380 -7
View File
@@ -1,12 +1,385 @@
<?php
use Modules\M3\Support\ModuleInfo;
if (! function_exists('m3_feature_enabled')) {
/**
* Check if a feature is enabled in the config.
*/
function m3_feature_enabled(string $key): bool
namespace Modules\M3\Helpers;
use Gate;
use DB;
use Config;
use Form;
use Auth;
use Redirect;
use Mail;
use Log;
use Cache;
use Str;
use File;
class Helper
{
# Check M3 API Token
public static function checkReceiveToken(){
ini_set('max_execution_time', Config::get('arrays.PHPTimeOut'));
# Clear Cache
// Cache::forget('token');
# Check if cached Token is still Valid
$token = Cache::get('token');
if (isset($token)) {
$IonToken = $token;
// Log::info("Cached Token: ".$IonToken);
}
else{
### CURL
### Generate Token
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => env('ENDPOINTURLSSO').env('URLTOKEN'),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => 'grant_type='.env('GRANT_TYPE').'&password='.env('PASSWORD').'&username='.env('USERNAME').'&client_secret='.env('CLIENT_SECRET').
'&client_id='.env('CLIENT_ID'),
CURLOPT_HTTPHEADER => array(
'Content-Type: application/x-www-form-urlencoded',
'charset:UTF-8',
'Accept: application/json'
),
));
$response = curl_exec($curl);
// dd($response);
$responseJ = json_decode($response);
if($response === false ){
return response()->json([
'code' =>'400',
'status' =>'Failed',
'message' =>'Token Verification Error...'.curl_error($curl)
]);
}
else if(isset($responseJ->access_token) ){
$IonToken = $responseJ->access_token;
// Log::info("New Token: " . $IonToken);
Cache::put('token', $IonToken, now()->addMinutes(59));
}
else{
// dd($response);
return response()->json([
'code' =>'400',
'status' =>'Failed',
'message' =>'Token Verification Error...M3 response:'.$response
]);
}
curl_close($curl);
}
return $IonToken;
// return response()->json([
// 'code' => '200',
// 'token' => $IonToken
// ]);
}
# Check M3 API Token V2
public static function checkReceiveTokenV2(){
ini_set('max_execution_time', Config::get('arrays.PHPTimeOut'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut'));
# Clear Cache
// Cache::forget('token');
# Check if cached Token is still Valid
$token = Cache::get('token');
if (isset($token)) {
$IonToken = $token;
// Log::info("Cached Token: ".$IonToken);
}
else{
### CURL
### Generate Token
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => env('ENDPOINTURLSSO').env('URLTOKEN'),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => 'grant_type='.env('GRANT_TYPE').'&password='.env('PASSWORD').'&username='.env('USERNAME').'&client_secret='.env('CLIENT_SECRET').
'&client_id='.env('CLIENT_ID'),
CURLOPT_HTTPHEADER => array(
'Content-Type: application/x-www-form-urlencoded',
'charset:UTF-8',
'Accept: application/json'
),
));
$response = curl_exec($curl);
// dd($response);
$responseJ = json_decode($response);
curl_close($curl);
if($response === false ){
# Clear Cache
Cache::forget('token');
# Return Error
return response()->json([
'code' =>'400',
'status' =>'Failed',
'message' =>'Token Verification Error...'.curl_error($curl)
]);
}
else if(isset($responseJ->access_token) ){
$IonToken = $responseJ->access_token;
// Log::info("New Token: " . $IonToken);
Cache::put('token', $IonToken, now()->addMinutes(59));
}
else{
# Clear Cache
Cache::forget('token');
return response()->json([
'code' =>'400',
'status' =>'Failed',
'message' =>'Token Verification Error...M3 response:'.$response
]);
}
}
// return $IonToken;
return response()->json([
'code' => '200',
'token' => $IonToken
]);
}
# Read M3 JSON and Analyse it for Errors
public static function checkM3Errors($responseResult){
// dd($responseResult);
# M3 Response Types
$M3TypesResponse = ['CloseShipment','Move','COCharge','Replenish','MoveDock'];
# CloseShipment Types And Contents
$M3CloseShipmentResp = ['AddPackage','PickingListClose','TrailerNo','TrackingNo','ContentLabel', 'DeliveryNote','ShipLabel'];
// 'ContentLabel', 'DeliveryNote','ShipLabel'
$ErrorMessages = '';
$typesErrors =0;
foreach ($M3TypesResponse as $key => $Type) {
// dump($responseResult->$Type);
if ($Type=='CloseShipment') {
// dump($Type);
$CloseShipmentErr = 0;
$M3RespClose = $responseResult->$Type;
foreach ($M3CloseShipmentResp as $key => $closeType) {
$ErrorMessage = $M3RespClose->$closeType->ErrorMessage;
$ErrorMessage = $ErrorMessage=="" ? "" : $ErrorMessage;
$Result = $M3RespClose->$closeType->Result;
$ResultT = $M3RespClose->$closeType->Result;
$Result = $Result=="N/A" || $Result=="OK" ? "" : $Result;
if ($ErrorMessage!='' || $Result!='') {
$ErrorMessages .= $closeType.'-'.$Result.'-'.$ErrorMessage.' ';
}
// dump($closeType);// dump($ErrorMessage);// dump($Result);
if ($ResultT=="N/A") {
$CloseShipmentErr++;
}
}
}
else if($Type=='MoveDock'){
$MoveDockRes = isset($responseResult->$Type->Move) ? $responseResult->$Type->Move : '';
$ErrorMessage = isset($MoveDockRes->ErrorMessage) ? $MoveDockRes->ErrorMessage :'';
$ErrorMessage = $ErrorMessage=="" ? "" : $ErrorMessage;
$Result = isset($MoveDockRes->Result) ? $MoveDockRes->Result : "";
$ResultT = $Result;
$Result = $Result=="N/A" || $Result=="OK" ? "" : $Result;
if ($ErrorMessage!='' || $Result!='') {
$ErrorMessages .= $Type.'-'.$Result.'-'.$ErrorMessage.' ';
}
// dump($Type);// dump($ErrorMessage);// dump($Result);// dump($responseResult->$Type);
if ($ResultT=="N/A") {
$typesErrors++;
}
}
else{
$ErrorMessage = isset($responseResult->$Type->ErrorMessage) ? $responseResult->$Type->ErrorMessage :'';
$ErrorMessage = $ErrorMessage=="" ? "" : $ErrorMessage;
$Result = isset($responseResult->$Type->Result) ? $responseResult->$Type->Result : "";
$ResultT = $Result;
$Result = $Result=="N/A" || $Result=="OK" ? "" : $Result;
if ($ErrorMessage!='' || $Result!='') {
$ErrorMessages .= $Type.'-'.$Result.'-'.$ErrorMessage.' ';
}
// dump($Type); // dump($ErrorMessage); // dump($Result); // dump($responseResult->$Type);
if ($ResultT=="N/A") {
$typesErrors++;
}
}
}
// dd($ErrorMessages);
if ($CloseShipmentErr == count($M3CloseShipmentResp) && $typesErrors == count($M3TypesResponse)-1) {
$ErrorMessages .= '-M3 did not call any method or type ';
}
return $ErrorMessages;
}
# Log All Success And Error to ICM699M
public static function logToICM699M($data){
ini_set('max_execution_time', Config::get('arrays.PHPTimeOut'));
$insertSQL = DB::connection(config('m3.connection', 'pgsql'))->table('ICM699M')->insert($data);
// dump($data);
// dump($insertSQL);
}
# Get Substring in string between 2 Substrings
public static function getSubstringBetween($string, $start, $end) {
$startPos = strpos($string, $start);
if ($startPos === false) {
return '';
}
$endPos = strpos($string, $end, $startPos + strlen($start));
if ($endPos === false) {
return '';
}
$substring = substr($string, $startPos + strlen($start), $endPos - ($startPos + strlen($start)));
return $substring;
}
public static function checkLength($JSONL,$DBL,$TYPE)
{
return (bool) config(ModuleInfo::nameLower().".features.{$key}", false);
# Check type
$JSONType = gettype($JSONL);
switch ($JSONType) {
case 'string':
$JSONTypeConv = 'string';
break;
case 'double':
$JSONTypeConv = 'number';
break;
case 'integer':
$JSONTypeConv = 'number';
break;
default:
$JSONTypeConv = 'null';
break;
}
$TYPE = $TYPE == 'integer' || $TYPE == 'DEC' ? 'number' : $TYPE;
// Log::info( $JSONL .' L: '. strlen($JSONL) .' DBL: '. $DBL .' Type:'.$JSONType.':'.$TYPE );
// Check Json Field should be < DB Field
$LengthOK[413] = ( strlen($JSONL) <= $DBL ) ? true : false ;
// Empty Fields
$LengthOK[411] = true; //!empty($JSONL) ? true : false ;
// Wrong Types
$LengthOK[417] = true; //$JSONTypeConv == $TYPE ? true : false ;
// Log::info($LengthOK);
return $LengthOK;
}
public static function sanityzeString($StrTosanityze)
{
$badChars = Config::get('arrays.BadChars');
$goodString = trim($StrTosanityze);
$goodString = str_replace("'", "",$goodString);
$goodString = preg_replace('/\x{f0e0}/u', '', $goodString);
$goodString = preg_replace('/\\\\u201[0-9A-F]+/i', '', $goodString);
$goodString = str_replace($badChars, '',$goodString);
$goodString = iconv("UTF-8", "ISO-8859-1//IGNORE", $goodString);
$goodString = iconv("ISO-8859-1", "UTF-8", $goodString);
# MAK : New Automatic Detection for encoding and conversion
# Detect the encoding
// $encoding = mb_detect_encoding($goodString, mb_list_encodings(), true);
// Log::info($goodString);
# If encoding is detected, convert the string
// if ($encoding !== false) {
// // Log::info($encoding);
// // Log::info($goodString);
// $goodString = mb_convert_encoding($goodString, "UTF-8", $encoding);
// } else {
// $goodString = "";
// }
// dump($goodString);
return $goodString;
}
# Remove Duplicate SubString
public static function removeDuplicateSubstrings($str) {
$result = '';
$seenPhrases = [];
$phrases = explode(' ', $str);
foreach ($phrases as $phrase) {
if (!in_array($phrase, $seenPhrases)) {
$result .= $phrase . ' ';
$seenPhrases[] = $phrase;
}
}
return trim($result);
}
public static function getUserCOWHSPLT(){
$defaults = [
'UserCO' => '',
'UserWHS' => '',
'UserPL' => '',
];
$loggedUser = strtoupper(trim((string) m3_user()));
if ($loggedUser === '') {
return $defaults;
}
try {
if (!\Illuminate\Support\Facades\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) {
return $defaults;
}
return [
'UserCO' => trim((string) ($user->R428CO ?? '')),
'UserWHS' => trim((string) ($user->R428WH ?? '')),
'UserPL' => trim((string) ($user->R428PL ?? '')),
];
} catch (\Throwable $e) {
\Illuminate\Support\Facades\Log::warning('M3 user context unavailable', ['exception' => $e->getMessage()]);
return $defaults;
}
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
if (! function_exists('m3_user')) {
/**
* 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.
*/
function m3_user(): string
{
$user = auth()->user();
$value = $user?->username
?? $user?->user_name
?? $user?->name
?? $user?->email
?? request()->server('PHP_AUTH_USER')
?? '';
if (str_contains((string) $value, '@')) {
$value = strstr((string) $value, '@', true) ?: $value;
}
return strtoupper(trim((string) $value));
}
}
if (! function_exists('m3_legacy_password')) {
/**
* Passwords are intentionally unavailable in Core. PostgreSQL access uses
* Laravel's configured connection and never reuses a user's login password.
*/
function m3_legacy_password(): string
{
return '';
}
}
@@ -0,0 +1,11 @@
<?php
if (! function_exists('subdirAsset')) {
function subdirAsset($path){
return asset( (App::environment('production') ? env('APP_DIR') : '')."/".$path);
}
}
if (! function_exists('subdirMix')) {
function subdirMix($path){
return mix( (App::environment('production') ? env('APP_DIR') : '')."/".$path);
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
// app/Helpers/USStates.php
namespace Modules\M3\Helpers;
class USStates
{
public static function all()
{
return [
'AL' => 'Alabama',
'AK' => 'Alaska',
'AZ' => 'Arizona',
'AR' => 'Arkansas',
'CA' => 'California',
'CO' => 'Colorado',
'CT' => 'Connecticut',
'DE' => 'Delaware',
'FL' => 'Florida',
'GA' => 'Georgia',
'HI' => 'Hawaii',
'ID' => 'Idaho',
'IL' => 'Illinois',
'IN' => 'Indiana',
'IA' => 'Iowa',
'KS' => 'Kansas',
'KY' => 'Kentucky',
'LA' => 'Louisiana',
'ME' => 'Maine',
'MD' => 'Maryland',
'MA' => 'Massachusetts',
'MI' => 'Michigan',
'MN' => 'Minnesota',
'MS' => 'Mississippi',
'MO' => 'Missouri',
'MT' => 'Montana',
'NE' => 'Nebraska',
'NV' => 'Nevada',
'NH' => 'New Hampshire',
'NJ' => 'New Jersey',
'NM' => 'New Mexico',
'NY' => 'New York',
'NC' => 'North Carolina',
'ND' => 'North Dakota',
'OH' => 'Ohio',
'OK' => 'Oklahoma',
'OR' => 'Oregon',
'PA' => 'Pennsylvania',
'RI' => 'Rhode Island',
'SC' => 'South Carolina',
'SD' => 'South Dakota',
'TN' => 'Tennessee',
'TX' => 'Texas',
'UT' => 'Utah',
'VT' => 'Vermont',
'VA' => 'Virginia',
'WA' => 'Washington',
'WV' => 'West Virginia',
'WI' => 'Wisconsin',
'WY' => 'Wyoming',
'DC' => 'District of Columbia'
];
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,464 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Modules\M3\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use DB;
use Config;
use Str;
use Log;
use Modules\M3\Helpers\Helper;
class M3Controller extends Controller
{
# Split Data and Write to ICM666M2
public function insertToICM666M2($dataSplit, $commentData)
{
// dump($commentData);
// dump($dataSplit);
$commentDataSS = explode("\n", $dataSplit);
$AXLINENO = 1;
foreach ($commentDataSS as $substring) {
# Process each substring
$fullData = $commentData;
$fullData['AXCOMMENT'] = $substring;
$fullData['AXLINENO'] = $AXLINENO;
// dump($fullData);
$insertSQL = 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)
}
}
public function index(){ dd("Index"); }
public function create(){}
public function store(Request $request)
{
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'));
$REQKPKUS = m3_user() !== '' ? Str::upper(m3_user()) : 'WEBUSER';
$SHSOURCE = 'M3ReceiverAPI';
$ICM666MFields = [
'Company' =>[ 'A1CO' ,3 , 'string' ],
'Warehouse' =>[ 'A1WHS', 3, 'string'],
'Plant' =>[ 'A1PLANT', 3, 'string'],
'PickerNumber' =>[ 'A1DELNO',11,'integer'],
'OrderNumber' =>[ 'A1ONO' ,10, 'string'],
'ReleaseNumber' =>[ 'A1RLNO' , 3,'string'], # Pick list suffix in ICM666M
'ShipmentNumber' =>[ 'A1SHNO' ,7,'string'],
'OrderLine' =>[ 'A1PKLN',6,'integer'],
'PartNumber' =>[ 'A1PART', 15,'string'],
'Description' =>[ 'A1PARTDES', 120,'string'],
'Quantity' =>[ 'A1RSVQTY', 22,'DEC'],
'BaseUnitPrice' =>[ 'A1SLUNPR', 22,'DEC'],
'Currency' =>[ 'A1SHCURR',3,'string'],
'Zone' =>[ 'A1LVZONE', 2,'string'],
'PromDate' =>[ 'A1PROMDTE',8,'integer'],
'RouteCode' =>[ 'A1SHSRT' ,6,'string'],
'BillToId' =>[ 'A1BILLTO' ,10,'string'],
'ShipToId' =>[ 'A1SHIPTO',10,'string'],
'CompanyName' =>[ 'A1SHSHNM',36,'string'],
'Address1' =>[ 'A1SHSHA1', 36,'string'],
'Address2' =>[ 'A1SHSHA2', 36,'string'],
'Address3' =>[ 'A1SHSHA3', 36,'string'],
'Address4' =>[ 'A1SHSHA4', 36,'string'],
'City' =>[ 'A1SHSHCI', 20,'string'],
'State' =>[ 'A1SHSHST', 2,'string'],
'PostalCode' =>[ 'A1SHSHZP', 10,'string'],
'Country' =>[ 'A1SHSHCT',3,'string'],
'CountryName' =>[ 'A1SHP2CNTY', 40,'string'],
'PartWeight' =>[ 'A1PRTWGT', 15,'integer'],
'SellUOM' =>[ 'A1SLUOM', 3,'string'],
'PartConv' =>[ 'A1PARTCONV', 25,'DEC'],
'CarrierCode' =>[ 'A1CARRIER', 6,'string'],
'ShipVia' =>[ 'A1SHPVIA', 3,'string'],
'PrimaryLocation' =>[ 'A1PRIMLOC', 10,'string'],
'Allocation' =>[ 'A1SPR10A01', 10,'string'],
'AllocatedTo' =>[ 'A1SPR15A01', 10,'string'],
'LocationSeq' =>[ 'A1LOCSEQ', 10,'string'],
'UOM' =>[ 'A1UOM' , 3,'string'],
'HazMat' =>[ 'A1MATWRN', 4,'string'],
'PartUPC' =>[ 'A1UPC', 30,'string'],
'PartEAN' =>[ 'A1EAN', 30,'string'],
'LotFlag' =>[ 'A1IZLCF', 1,'string'],
'ManOrd' =>[ 'A1MONUM', 30,'string'],
'LotNumber' =>[ 'A1LOTNUM', 30,'string'],
'ProductArea' =>[ 'A1LVUSD1', 15,'string'],
'FreightClass' =>[ 'A1WBFRCT' , 10,'string'],
'ShipmentPONumber' =>[ 'A1SOPONO', 20,'string'],
'ShipmentPOLine' =>[ 'A1SOPOLN' , 7,'integer'],
'ShipTerms' =>[ 'A1OFSTRM', 3,'string'],
// 'ExchangeRate' =>[ 'A1EXGRATE', 18,'DEC'],
'OrderClass' =>[ 'A1SHORCL', 3,'string'],
'LocationType' =>[ 'A1LVUSD2', 2,'string'],
'PallSize' =>[ 'A1LVUSD3', 10,'string'],
'MasterCarton' =>[ 'A1USRLD1', 30,'string'],
'MasterQty' =>[ 'A1WBSHQY', 25,'DEC'],
'InnerQty' =>[ 'A1WBPKQY', 25,'DEC'],
'PalletQty' =>[ 'A1USRN01', 24,'DEC'],
'CartonQty' =>[ 'A1USRN31', 25,'DEC'],
'PersonName' =>[ 'A1PERNAME', 30,'string'],
'AccountNo' =>[ 'A1ACCNUM', 20,'string'],
'Email' =>[ 'A1EMAIL', 100,'string'],
'OrderCategory' =>[ 'A1REFORD', 1,'string'],
'A1DATEADD' =>[ 'A1DATEADD', 12,'string'],
'A1TIMEADD' =>[ 'A1TIMEADD', 12,'string'],
'KitPartNumber' =>[ 'A1KITPART', 15,'string'],
'OrderLineSuffix' =>[ 'A1PKSFX', 6,'DEC'],
'CustomerPartNumber'=>[ 'A1CUSTPART', 20,'string'],
'GS1Label' => ['A1SPR1A01', 1, 'string'],
'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"] : '';
// $ExchangeRate = isset($jsonFile["ExchangeRate"]) ? $jsonFile["ExchangeRate"] : '';
$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];
// dump($commentData);
switch ($AXDOCUMENT) {
case 'Picker':
$Picker = isset($comment) ? Helper::sanityzeString($comment) : 'null';
// dump($Picker);
if ($Picker!='null') {
$this->insertToICM666M2($Picker,$commentData);
}
break;
case 'Packer':
$Packer = isset($comment) ? Helper::sanityzeString($comment) : 'null';
// dump($Packer);
if ($Packer!='null') {
$this->insertToICM666M2($Packer,$commentData);
}
break;
case 'Carrier':
$Carrier = isset($comment) ? Helper::sanityzeString($comment) : 'null';
// dump($Carrier);
if ($Carrier!='null') {
$this->insertToICM666M2($Carrier,$commentData);
}
break;
case 'Other':
$Other = isset($comment) ? Helper::sanityzeString($comment) : 'null';
// dump($Other);
if ($Other!='null') {
$this->insertToICM666M2($Other,$commentData);
}
break;
}
}
}
# Get Products : commodities And Write ICM666M
$tmpProd = "";
$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']: '';
$PickerLine = isset($product['PickerLine']) ? $product['PickerLine']: '';
$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'] : '';
$FrgnUnitPrice = isset($product['FrgnUnitPrice']) ? $product['FrgnUnitPrice'] : '';
$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']: '';
$PalletQty = 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 = [];
# Check JSON fields if empty or > DB Fields
foreach ($ICM666MFields as $key => $value) {
$keyVar = $key;
# Sanitize String
$$keyVar = Helper::sanityzeString($$keyVar);
# Check Var Types & Length
$LengthTest = Helper::checkLength($$keyVar, $value[1], $value[2]);
if (!$LengthTest[413] || !$LengthTest[411] || !$LengthTest[417]) {
// Log::error($key .' :: '. $value[0].' :: '. $value[1] .' JSONF: '.$$keyVar .' Type: '.$value[2] );
if(!$LengthTest[413]){
$message[] = [ 'code'=>'413', 'status'=>'error', 'message'=>'Field: '.$key.' is larger then expected'];
}
if(!$LengthTest[411]){
$message[] = [ 'code'=>'411', 'status'=>'error', 'message'=>'Field: '.$key.' is empty'];
}
if(!$LengthTest[417]){
$message[] = [ 'code'=>'417', 'status'=>'error', 'message'=>'Field: '.$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).' M3-Lis-JSN'.json_encode($jsonFile) ,'SHUSER'=> $REQKPKUS,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$Company,'SHWHS'=>$Warehouse ];
Helper::logToICM699M($data);
return response()->json($message);
} else if ($$keyVar != '') {
$InsertArray[$value[0]] = $$keyVar;
}
}
// Log::info($InsertArray);
# 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;
// if ($PalletQty!='') {
// $InsertArray['A1USRN01']=$palletQty;
// }
# SQL Insert to ICM666M
$insertSQL = DB::connection(config('m3.connection', 'pgsql'))->table('icm666m')->insert($InsertArray);
}
# Call PGM ICM700CSBM
if ($Warehouse!='' && $PickerNumber!='' && $Company!='') {
$PGMERRORS = '';
$AWREQN = '';
# connect to toolkit using DB2 credentials (can also leave blank for default authority)
try {
$conn = app(\Modules\M3\Services\PostgresProgramBridge::class);
} catch (Exception $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(array('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 = [];
# $params[] = $conn->AddParameterChar('out',7,'eqNumber');
# 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);
// $params[] = $conn->AddParameterZoned('in', 11, 0, 'ParamIn');
# Start Time Tracking
$start = microtime(true);
# ICM700C EWOBJTST ICM700C ICM700CSBM
$result = $conn->PgmCall('ICM700CSBM', env('OBJLIB'), $params, array(), array());
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 .' M3-Lis-JSN'.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).' M3-Lis-JSN'.json_encode($jsonFile) ,'SHUSER'=> $REQKPKUS,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$Company,'SHWHS'=>$Warehouse ];
Helper::logToICM699M($data);
// Log::error($th);
return response()->json([
'code'=>'405',
'status'=>'error',
'message'=>'An Error Occured...'
]);
}
}
public function show($id){}
public function edit($id){}
public function update(Request $request, $id){}
public function destroy($id){}
}
@@ -0,0 +1,453 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Modules\M3\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use DB;
use Config;
use Str;
use Log;
use Http;
use Cache;
use DateTime;
use Smalot\PdfParser\Parser;
use File;
use Modules\M3\Helpers\Helper;
use Json;
class M3InventoryReport extends Controller
{
# M3InventoryReport for Single Part
public function M3InventoryReport(Request $request){
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'));
// dd($request->json()->all());
# Get the Calling User
$SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WEBUSER';
# Set SHSOURCE
$SHSOURCE = 'M3InventoryReport';
$M3Errors = '';
$DELIVERYNO = isset($DELIVERYNO) ? $DELIVERYNO : '000000';
try {
# Get Json Contents
$jsonFile = count($request->json()->all())>0 ? $request->json()->all() : '';
if($jsonFile!=''){
$WHS = isset($jsonFile["WHS"]) ? $jsonFile["WHS"] : '';
$PART = isset($jsonFile["PART"]) ? $jsonFile["PART"] : '';
$LOT = isset($jsonFile["LOT"]) ? $jsonFile["LOT"] : '';
}
else {
# code...
}
# 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'=>'000000','SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> '','SHINTBOX'=> '','SHTYPE'=> 'M3-Res-out' ,
'SHDATA'=> $SHDATA ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>'','SHWHS'=>$WHS];
Helper::logToICM699M($data);
return $tokenResponse;
}
# Call ionapi with Params
$endPointURL = env('ENDPOINTURL').'APIFLOWS/Sentinel/M3InventoryReport?ionapiRespStyle=sync';
$token = $IonToken;
$JSON = [
"WHS" => $WHS,
"PART"=> $PART,
"LOT" => $LOT
];
$response = Http::withHeaders([
'accept' => 'application/json',
'Content-Type' => 'application/json'
])
->withToken($token)
->timeout(Config::get('arrays.curlTimeOut'))
->post($endPointURL, $JSON);
$responseStatusCode = $response->getStatusCode();
$M3SessionId = $response->hasHeader('x-transaction-id') ? "-sessionID:". $response->header('x-transaction-id') : '-sessionID';
if ($responseStatusCode =='200') {
# Check M3 Response for Errors
$M3Errors = '';
$responseJSON = $response->getBody()->getContents();
$JsonSanytized = Helper::sanityzeString($responseJSON);
}
else{
$M3Errors = 'Error: No Response from M3';
}
if ($M3Errors!='') {
# Write to Logs : Error
$data = ['SHMACPKR'=>$DELIVERYNO,'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'=>'','SHWHS'=>$WHS ];
Helper::logToICM699M($data);
# Send Response
return response()->json([
'code'=>'405',
'status'=>'error',
'message'=>$M3Errors
]);
}
else{
# 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.$JsonSanytized.'-'.json_encode($JSON).$M3SessionId ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,
'SHCO'=>'','SHWHS'=>$WHS];
Helper::logToICM699M($data);
return response()->json([
'code'=>$responseStatusCode,
'status'=>'Success',
'message'=>json_decode($JsonSanytized)
]);
}
}
catch (\Throwable $th) {
// Log::error($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'=>'','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
$data = ['SHMACPKR'=>$DELIVERYNO,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> 'X','SHINTBOX'=> '','SHTYPE'=> 'M3-Res-out' ,
'SHDATA'=>'An Error Occured - '.substr($th,0,300) ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE];
Helper::logToICM699M($data);
return response()->json([
'code'=>'405',
'status'=>'error',
'message'=>'Catched Error: '.$th
]);
}
}
}
# End M3InventoryReport
# M3InventoryReportList with URL Params
public function M3InventoryReportListURL(Request $request){
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'));
# Set API Name
$SHSOURCE = 'M3InventoryReportListURL';
# Get The Calling User
$SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser';
$M3Errors = '';
try {
# Get Json Contents
$urlParams = count($request->all())>0 ? $request->all() : '';
$REQPKWS = isset($urlParams["REQPKWS"]) ? $urlParams["REQPKWS"] : '';
$REQKPKUS = isset($urlParams["REQKPKUS"]) ? $urlParams["REQKPKUS"] : 'WebUser';
$CO = isset($urlParams["CO"]) ? $urlParams["CO"] : '';
$WHS = isset($urlParams["WHS"]) ? $urlParams["WHS"] : '';
$MACPKR = isset($urlParams["MACPKR"]) ? $urlParams["MACPKR"] : '000000'; # Delivery Number
$PKLN = isset($urlParams["PKLN"]) ? $urlParams["PKLN"] : '';
if($urlParams!=''){
$IncomingParams = ['CO'=> $CO,'WHS'=> $WHS,'MACPKR'=> $MACPKR,'PKLN'=> $PKLN,'REQPKWS'=> $REQPKWS,'REQKPKUS'=> $REQKPKUS];
$IncomingParams = json_encode($IncomingParams);
# Write to Logs : Params In
$data = ['SHMACPKR'=>$MACPKR,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> '','SHINTBOX'=> '','SHTYPE'=> 'M3-Req-out' ,
'SHDATA'=>$IncomingParams ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$CO,'SHWHS'=>$WHS ];
Helper::logToICM699M($data);
# Get Parts And Lots from ICM688M2
$ICM688M2SLTMP = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m2')
->select('A5WHS', 'A5PART','A5LOTNUM','A5SENTPKR')
->distinct()
->where('A5CO',$CO)
->where('A5WHS',$WHS)
->where('A5MACPKR',$MACPKR);
# Get Pack Lines
if($PKLN!=''){
$ICM688M2SLTMP->where('A5PKLN',$PKLN);
}
$ICM688M2SL = $ICM688M2SLTMP->get();
// dd($ICM688M2SL);
if( count($ICM688M2SL)>0 ){
# Update ICM688M1 : Add Timestamp
$ICM688M1TS = now();
DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->where('A4MACPKR',$MACPKR)
->update(['A4INRFSHTS'=>$ICM688M1TS]);
$PICKERARR = [];
foreach ($ICM688M2SL as $key => $ICM688M2) {
$PART = trim($ICM688M2->A5PART);
$LOT = trim($ICM688M2->A5LOTNUM);
$PARTS[] = ["PART"=> $PART , "LOT"=>$LOT];
$PICKERARR[$PART] = $ICM688M2->A5SENTPKR;
}
// Log::info($PARTS);
// dump($PARTS);
$JSON = json_encode( ["WHS" => $WHS,"PARTS"=> $PARTS ] );
// Log::info($JSON);
# 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'=>$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;
}
# Call ionapi with Params
$endPointURL = env('ENDPOINTURL').'APIFLOWS/Sentinel/M3InventoryReportList?ionapiRespStyle=sync';
$token = $IonToken;
$response = Http::withHeaders([
'accept' => 'application/json',
'Content-Type' => 'application/json'
])
->withToken($token)
->withBody($JSON, 'application/json')
->timeout(Config::get('arrays.curlTimeOut'))
->post($endPointURL);
$M3SessionId = $response->hasHeader('x-transaction-id') ? "-sessionID:". $response->header('x-transaction-id') : '-sessionID';
$responseStatusCode = $response->getStatusCode();
if ($responseStatusCode =='200') {
# Check M3 Response for Errors
$M3Errors = '';
$responseJSON = $response->getBody()->getContents();
$JsonSanytized = Helper::sanityzeString($responseJSON);
# Array
$responseArray = json_decode($JsonSanytized,true);
$responseTMP = $responseArray['result'];
}
else{
$M3Errors = 'Error: No Response from M3';
}
# Validate response From M3
if ($M3Errors!='') {
# 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'=>$responseStatusCode.'-'.$M3Errors.$M3SessionId ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$CO,'SHWHS'=>$WHS ];
Helper::logToICM699M($data);
# Send Response
return response()->json([
'code'=>'405',
'status'=>'error',
'message'=>'Error: No Response from M3'
]);
}
else{
# 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 $key => $value) {
// dump($value);
$dataToWrite = [];
foreach ($value as $key2 => $content) {
if($key2!='Locations'){
$dataToWrite[$key2] = $content;
}
else{
foreach ($content as $key3 => $content2) {
//dump($content2);
$dataToWrite2[] = array_merge($dataToWrite,$content2);
}
}
}
}
// LOG::info($dataToWrite2);
# Delete Old Records from IC630M
foreach ($dataToWrite2 as $key => $value) {
$ICM630MDeleteSQL = DB::connection(config('m3.connection', 'pgsql'))->table('ic630m')
->where('LLCO',$CO)
->where('LLWHNO',$WHS)
->where('LLPPN',trim($value['PartNumber']))
->delete();
}
foreach ($dataToWrite2 as $key => $value) {
# Check if Location exist in DE640M ("LVCO", "WSELOC", "VLOCID")
$DE640MSQLSEL = DB::connection(config('m3.connection', 'pgsql'))->table('de640m')
->where('LVCO',$CO)
->where('WSELOC',$value['Warehouse'])
->where('VLOCID',$value['Location'])
->first();
// dd($DE640MSQLSEL);
$LocationExist = isset($DE640MSQLSEL->VLOCID) ? trim($DE640MSQLSEL->VLOCID) : '';
$WHExist = isset($DE640MSQLSEL->WSELOC) ? trim($DE640MSQLSEL->WSELOC) : '';
$COExist = isset($DE640MSQLSEL->LVCO) ? trim($DE640MSQLSEL->LVCO) : '';
// dd($LocationExist .': '.$WHExist.': '.$COExist);
# if No records Founds insert New Record
if ( $LocationExist=='' && $WHExist=='' && $COExist=='' ) {
$SQLInsertPrep640M['LVCO'] = $CO; # Company
$SQLInsertPrep640M['WSELOC'] = $value['Warehouse'];
// $SQLInsertPrep640M['LVDELNO'] = $MACPKR; # M3 Delivery note number DELETED
// $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['LLDELNO'] = $MACPKR; # M3 Delivery note number DELETED
$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['LLCMDESC'] = $value['Description']; # Description
$SQLInsertPrep630M['LLSPR10A01'] = isset($value['Allocation']) ? $value['Allocation'] : ''; # Allocation
$SQLInsertPrep630M['LLSPR15A01'] = isset($value['AllocatedTo']) ? $value['AllocatedTo'] : ''; # AllocatedTo
DB::connection(config('m3.connection', 'pgsql'))->table('ic630m')->insert($SQLInsertPrep630M);
}
// dump($SQLInsertPrep);
#Send Response
return response()->json([
'code' => '200',
'status' => 'Success'
]);
}
else{
# Write to Logs Out : Error
$MACPKR = isset($MACPKR) ? $MACPKR : '000000';
$data = ['SHMACPKR'=>$MACPKR,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> 'X','SHINTBOX'=> '','SHTYPE'=> 'M3-Res-out' ,
'SHDATA'=>'No Records Found in ICM688M2' ,'SHUSER'=> $REQKPKUS,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$CO,'SHWHS'=>$WHS ];
Helper::logToICM699M($data);
# Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message'=> 'No Records Found in ICM688M2'
]);
}
}
else {
# Write to Logs Out : Error
$MACPKR = isset($MACPKR) ? $MACPKR : '000000';
$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'=> $REQKPKUS,'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 : '000000';
$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'=>$th->getMessage().' 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'=> $REQKPKUS,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$CO,'SHWHS'=>$WHS];
Helper::logToICM699M($data);
Log::error($th);
return response()->json([
'code'=>'405',
'status'=>'error',
'message'=>'Catched Error: '.$th
]);
}
}
}
# End M3InventoryReportListURL
}
@@ -0,0 +1,166 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Modules\M3\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use DB;
use Config;
use Str;
use Log;
use Http;
use Cache;
use DateTime;
use Smalot\PdfParser\Parser;
use File;
use Modules\M3\Helpers\Helper;
class PdfStorage extends Controller
{
# PdfStorageAPI : PDF file read and save to Cache
public function PdfStorageAPI(Request $request){
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'));
// dd($request->all());
# Get the Calling User
$SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser';
$SHSOURCE = 'PdfStorageAPI';
# PDF File PATH
$pdfFilePath = Config::get('arrays.PdfPath');
try {
# Get URL PARAMS
$urlParams = count($request->all())>0 ? $request->all() : '';
// dd($urlParams);
$COMPANY = isset($urlParams["COMPANY"]) ? trim($urlParams["COMPANY"]) : ''; # Company
$WHS = isset($urlParams["WHS"]) ? trim($urlParams["WHS"]) : ''; # WareHouse
$DOCTYPE = isset($urlParams["DOCTYPE"]) ? trim($urlParams["DOCTYPE"]) : ''; # document type (PackingList, CarrierLabel, ContentLabel)
$FILENAME = isset($urlParams["FILENAME"]) ? trim($urlParams["FILENAME"]) : ''; # PDF File name
$DOCNUM = isset($urlParams["DOCNUM"]) ? trim($urlParams["DOCNUM"]) : ''; # document number (ie the internal box number, the pick list number)
$A4MACPKR = isset($urlParams["A4MACPKR"]) ? trim($urlParams["A4MACPKR"]) : ''; # Delivery note number
if($urlParams!=''){
# Key Combination : COMPANY WHS A4MACPKR DOCTYPE DOCNUM
# Document type (PackingList, CarrierLabel, ContentLabel)
$DELIVERYNO = isset($A4MACPKR) ? $A4MACPKR : '000000';
$DELIVERYNO = $DELIVERYNO!="" && $DELIVERYNO!=null ? $DELIVERYNO : '000000';
# Write to Logs : Params In
$IncomingParams = ['COMPANY'=> $COMPANY,'WHS'=> $WHS,'DOCTYPE'=> $DOCTYPE,'FILENAME'=> $FILENAME,'A4MACPKR'=> $A4MACPKR,'DOCNUM' => $DOCNUM];
$data = ['SHMACPKR'=>$DELIVERYNO,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> '','SHINTBOX'=> $DOCNUM,'SHTYPE'=> 'M3-Req-out' ,
'SHDATA'=>json_encode($IncomingParams) ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$COMPANY,'SHWHS'=>$WHS ];
Helper::logToICM699M($data);
# Make Key and Test Validity
if ($A4MACPKR!='' && $DOCTYPE!='' && $DOCNUM!='' && $COMPANY!='' && $WHS!=''){
$cacheKey = $A4MACPKR.$DOCTYPE.$DOCNUM.$COMPANY.$WHS;
}
else{
$cacheKey ='';
}
# Read PDF File and Encode to Base64
$pdfFileName = $pdfFilePath.$FILENAME.'.pdf';
// dd($pdfFileName);
// dd(File::exists($pdfFileName));
if (File::exists($pdfFileName) && $cacheKey!='') {
// dd(File::exists($pdfFileName));
// Parse PDF file and build necessary objects.
$parser = new \Smalot\PdfParser\Parser();
// $pdf = $parser->parseFile('/www/sentm3test/htdocs/labels/E1232824-10826596.pdf');
// dump($pdf);
// $text = $pdf->getText();
// dump("Text Only");
// dump($text);
$pdfFile = File::get($pdfFileName);
// Transform content to base64
$base64PDF = base64_encode($pdfFile);
// Output base64 string
// dump($base64PDF);
# Save Cache
Cache::forever($cacheKey, $base64PDF);
// Cache::put($cacheKey, $base64PDF, now()->addMinutes(60));
# Retrieve Cache
// $cachedPdfFile = Cache::get($cacheKey);
// dump("Retrieve Base64 Encoded PDF saved in Cache:");
// dump($cachedPdfFile);
// $pdfData = substr($cachedPdfFile,0,50);
# remove File from Cache
// Cache::put(pdfFileName, 'value', -5);
// Cache::forget($pdfFileName);
# Base64 Decode and Display
// $pdf = $parser->parseContent(base64_decode($base64PDF));
// $text = $pdf->getText();
// dump($text);
# Write to Logs Out : Success
$data = ['SHMACPKR'=>$DELIVERYNO,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> '','SHINTBOX'=> $DOCNUM,'SHTYPE'=> 'M3-Res-out' ,
'SHDATA'=>'PDF File converted to Base64 and Saved to Cache.Key: '.$cacheKey,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$COMPANY,'SHWHS'=>$WHS];
// dd($data);
Helper::logToICM699M($data);
return response()->json([
'code' => '200',
'status' => 'Success',
'cacheKey'=> $cacheKey
// 'PDFData' => $pdfData
]);
}
else {
$SHDATA = File::exists($pdfFileName) ? '' : 'PDF File Not Found-';
$SHDATA = $cacheKey!='' ? $SHDATA.'' : $SHDATA.'Cache Key is missing Params';
# Write to Logs Out : Error
$data = ['SHMACPKR'=>$DELIVERYNO,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> 'X','SHINTBOX'=> $DOCNUM,'SHTYPE'=> 'M3-Res-out' ,
'SHDATA'=>'An Error Occured -'.$SHDATA,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$COMPANY,'SHWHS'=>$WHS];
Helper::logToICM699M($data);
# Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message'=> $SHDATA
]);
}
}
else {
# Write to Logs Out : Error
$DELIVERYNO = isset($DELIVERYNO) ? $DELIVERYNO : '000000';
$data = ['SHMACPKR'=>$DELIVERYNO,'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'=>$COMPANY,'SHWHS'=>$WHS];
Helper::logToICM699M($data);
# Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message'=> 'No URL Parameters Sent'
]);
}
}
catch (\Throwable $th) {
// Log::error($th);
# Write to Logs Out : Error
$DELIVERYNO = isset($DELIVERYNO) ? $DELIVERYNO : '000000';
$data = ['SHMACPKR'=>$DELIVERYNO,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> 'X','SHINTBOX'=> '','SHTYPE'=> 'M3-Res-out' ,
'SHDATA'=>'An Error Occured - '.substr($th,0,300) ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$COMPANY,'SHWHS'=>$WHS];
Helper::logToICM699M($data);
return response()->json([
'code'=>'405',
'status'=>'error',
'message'=>'Catched Error: '.$th
]);
}
}
# End PdfStorageAPI
}
@@ -0,0 +1,294 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Modules\M3\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use DB;
use Config;
use Str;
use Log;
use Http;
use Cache;
use DateTime;
use Smalot\PdfParser\Parser;
use File;
use Modules\M3\Helpers\Helper;
class ReplenAPI extends Controller
{
# M3Replenishement with URL Params
public function M3InventoryReportReplen(Request $request){
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'));
// dd($request->all());
// Log::info( $request->all() );
# Get The Calling User
$SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser';
$MACPKR = isset($MACPKR) ? $MACPKR : '000000';
$SHSOURCE = 'M3InventoryReportReplen';
$M3Errors = '';
try {
# Get Json Contents
$urlParams = count($request->all())>0 ? $request->all() : '';
$CO = isset($urlParams["CO"]) ? $urlParams["CO"] : '';
$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!=''){
$IncomingParams = ['CO'=> $CO,'WHS'=> $WHS,'PARTNO'=> $PARTNO,'PKLN'=> $PKLN,'REQPKWS'=> $REQPKWS,'REQKPKUS'=> $REQKPKUS];
$IncomingParams = json_encode($IncomingParams);
# Write to Logs : Params In
$data = ['SHMACPKR'=>$MACPKR,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> '','SHINTBOX'=> '','SHTYPE'=> 'M3-Req-out' ,
'SHDATA'=>$IncomingParams ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$CO,'SHWHS'=>$WHS ];
Helper::logToICM699M($data);
$PART = explode( ',',$PARTNO);
foreach( $PART as $key => $value){
$PARTS[] = ["PART"=> $value,"LOT"=>""];
}
// dump($PARTS);
$JSON = json_encode( ["WHS" => $WHS,"PARTS"=> $PARTS ] );
# Write to Logs : Params Sent To M3
$data = ['SHMACPKR'=>$MACPKR,'SHDT'=> date('Y-m-d'),'SHTIME'=> date('H:i:s') ,'SHERROR'=> '','SHINTBOX'=> '','SHTYPE'=> 'M3-Req-out' ,
'SHDATA'=>'Sent To M3-'.$JSON ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$CO,'SHWHS'=>$WHS ];
Helper::logToICM699M($data);
# Get Token
$tokenResponse = Helper::checkReceiveTokenV2();
$tokenData = $tokenResponse->getData();
// dd($IonToken->code);
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;
}
# Call ionapi with Params
$endPointURL = env('ENDPOINTURL').'APIFLOWS/Sentinel/M3InventoryReportList?ionapiRespStyle=sync';
$token = $IonToken;
$response = Http::
withHeaders([
'accept' => 'application/json',
'Content-Type' => 'application/json'
])
->withToken($token)
->withBody($JSON, 'application/json')
->timeout(Config::get('arrays.curlTimeOut'))
->retry(3, 100)
->post($endPointURL);
$responseStatusCode = $response->getStatusCode();
$M3SessionId = $response->hasHeader('x-transaction-id') ? "-sessionID:". $response->header('x-transaction-id') : '-sessionID';
if ($responseStatusCode =='200') {
# Check M3 Response for Errors
$M3Errors = '';
$responseJSON = $response->getBody()->getContents();
$JsonSanytized = Helper::sanityzeString($responseJSON);
# Array
$responseArray = json_decode($JsonSanytized,true);
// LOG::info( $responseArray);
$responseTMP = $responseArray['result'];
// LOG::info($responseTMP);
}
else{
$M3Errors = 'Error: No Response from M3';
}
# Validate response From M3
if ($M3Errors!='') {
# 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'=>$responseStatusCode.'-'.$M3Errors.$M3SessionId ,'SHUSER'=> $SHUSER,'SHSOURCE'=> $SHSOURCE,'SHCO'=>$CO,'SHWHS'=>$WHS ];
Helper::logToICM699M($data);
# Send Response
return response()->json([
'code'=>'405',
'status'=>'error',
'message'=>'Error: No Response from M3'
]);
}
else{
# 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 $key => $value) {
// dump($value);
$dataToWrite = [];
foreach ($value as $key2 => $content) {
if($key2!='Locations'){
$dataToWrite[$key2] = $content;
}
else{
foreach ($content as $key3 => $content2) {
//dump($content2);
$dataToWrite2[] = array_merge($dataToWrite,$content2);
}
}
}
}
// LOG::info($dataToWrite2);
# Delete Old Records from IC630M
foreach ($dataToWrite2 as $key => $value) {
$ICM630MDeleteSQL = DB::connection(config('m3.connection', 'pgsql'))->table('ic630m')
->where('LLCO',$CO)
->where('LLWHNO',$WHS)
->where('LLPPN',trim($value['PartNumber']))
->delete();
}
foreach ($dataToWrite2 as $key => $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();
// dd($DE640MSQLSEL);
$LocationExist = isset($DE640MSQLSEL->VLOCID) ? trim($DE640MSQLSEL->VLOCID) : '';
$WHExist = isset($DE640MSQLSEL->WSELOC) ? trim($DE640MSQLSEL->WSELOC) : '';
$COExist = isset($DE640MSQLSEL->LVCO) ? trim($DE640MSQLSEL->LVCO) : '';
// dd($LocationExist .': '.$WHExist.': '.$COExist);
# 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);
}
// dump($SQLInsertPrep);
#Send Response
return response()->json([
'code' => '200',
'status' => 'Success'
]);
}
else {
# Write to Logs Out : Error
$MACPKR = isset($MACPKR) ? $MACPKR : '000000';
$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 : '000000';
$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);
// Log::error($th);
return response()->json([
'code'=>'405',
'status'=>'error',
'message'=>'Catched Error: '.$th
]);
}
}
}
# End M3InventoryReportReplen
}
@@ -0,0 +1,712 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Modules\M3\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use DB;
use Config;
use Str;
use Log;
use Http;
use Cache;
use DateTime;
use Smalot\PdfParser\Parser;
use File;
use Modules\M3\Helpers\Helper;
class TransferAPI extends Controller
{
# TransferAPIURL : with URL PARAMS
public function TransferAPIURL(Request $request)
{
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'));
// dd($request->all());
# 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 {
if ($urlParams != '') {
$PART = isset($urlParams["PART"]) ? $urlParams["PART"] : '';
$LOCATIONFROM = isset($urlParams["LOCATIONFROM"]) ? $urlParams["LOCATIONFROM"] : '';
$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"] : '000000';
$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 : '000000';
$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();
// dump($ICM699MS->SHDATA);
# 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));
} else if ($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);
// dd($SHDATAJSON);
$JSONTR = $SHDATAJSON != '' ? json_decode($SHDATAJSON) : '';
// dd($JSONTR);
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 : '000000';
$PICKLISTSUFFIX = isset($JSONTR->PICKLISTSUFFIX) ? $JSONTR->PICKLISTSUFFIX : '';
$ENDFLAG = isset($JSONTR->ENDFLAG) ? $JSONTR->ENDFLAG : '';
$TYPE = isset($JSONTR->TYPE) ? $JSONTR->TYPE : '';
$COLINESUFFIX = isset($JSONTR->COLINESUFFIX) ? $JSONTR->COLINESUFFIX : '';
}
// dd($JSONTR);
# Set SHERROR to Z in ICM699M
$ICM699MU = 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();
// dd($ICM688M2SL->A5BRADLN);
$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);
// dd($JSON);
# 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' => 'application/json',
'Content-Type' => 'application/json'
])
->withToken($token)
->withBody($JSON, 'application/json')
->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; //$M3ResponseTimeTmp / 1000;
$dateTime = new DateTime();
$dateTime->setTimestamp($M3ResponseTimeTmp2);
// dump( $dateTime->format('d/m/Y H:i:s.v') );
$M3ResponseTime = $M3ResponseTimeTmp != '' ? '-M3ResponseTime:' . $dateTime->format('d/m/Y H:i:s.v') : '-M3ResponseTime';
// dump($M3ResponseTime);
$transferStats = $response->transferStats;
$M3transferTime = $transferStats->getTransferTime() != null ? "-M3transferTime:" . $transferStats->getTransferTime() : "-M3transferTime";
// dd($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 . ' Order Category: ' . $ORDERCATEGORY . '-' . $M3JSONResponse . $M3SessionId . $M3ResponseTime . $M3transferTime,
'SHUSER' => $SHUSER,
'SHSOURCE' => $SHSOURCE,
'SHCO' => $DIVI,
'SHWHS' => $WHS
];
Helper::logToICM699M($data);
# Log to ICM799M
$ICM799MINS = 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 . ' Order Category: ' . $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 . ' Order Category: ' . $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 : '000000';
$data = ['SHMACPKR' => $DELIVERYNO,
'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' => $DIVI,
'SHWHS' => $WHS
];
Helper::logToICM699M($data);
# Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => 'No URL Parameters Sent'
]);
}
} catch (\Throwable $th) {
// Log::error($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 : '000000';
$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
public function UpdateShipmentNumber(Request $request)
{
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'));
// dd($request->all());
# 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"] : '';
// dump($urlParams);
try {
if ($urlParams != '') {
$A4SPR15A01 = isset($urlParams["A4SPR15A01"]) ? $urlParams["A4SPR15A01"] : '';
$DELIVERYNO = isset($urlParams["DELIVERYNO"]) ? $urlParams["DELIVERYNO"] : '';
$TYPE = 'UpdateShipmentNumber';
$allMacpkrs = [];
// dd($A4SPR15A01);
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 $key => $ICM688M1) {
$allMacpkrs[] = $ICM688M1->A4MACPKR;
}
}
# A4SPR15A01 Exists : proceed
if ($A4SPR15A01 != '' && count($allMacpkrs)>0) {
$JSON = json_encode([
"DIVI" => $DIVI,
"WHS" => $WHS,
"ShipmentNumber" => [
"Number" => $A4SPR15A01,
"DeliveryNumbers" => $allMacpkrs
],
"TYPE" => $TYPE,
"ENDFLAG" => "",
], JSON_UNESCAPED_SLASHES);
// dd($JSON);
# 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' => 'application/json',
'Content-Type' => 'application/json'
])
->withToken($token)
->withBody($JSON, 'application/json')
->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; //$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 : '000000';
$data = ['SHMACPKR' => $DELIVERYNO,
'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' => $DIVI,
'SHWHS' => $WHS
];
Helper::logToICM699M($data);
# Return Error
return response()->json([
'code' => '405',
'status' => 'Error',
'message' => 'No URL Parameters Sent'
]);
}
} catch (\Throwable $th) {
// Log::error($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 : '000000';
$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 UpdateShipmentNumber
}
@@ -0,0 +1,288 @@
<?php
namespace Modules\M3\Http\Controllers\API;
use Modules\M3\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use DB;
use Config;
use Str;
use Log;
use Http;
use Cache;
use DateTime;
use Smalot\PdfParser\Parser;
use File;
use Modules\M3\Helpers\Helper;
class TwoShipAPIs extends Controller
{
# TwoShipUpdateHold with URL Params
public function TwoShipUpdateHold(Request $request)
{
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'));
// dd($request->all());
// Log::info( $request->all() );
# Get The Calling User
$SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser';
$SHSOURCE = 'TwoShipUpdateHold';
$TwoShipErrors = '';
$urlParams = count($request->all()) > 0 ? $request->all() : '';
$WHS = isset($urlParams["WHS"]) ? $urlParams["WHS"] : '';
$CO = isset($urlParams["COMPANY"]) ? $urlParams["COMPANY"] : '';
$MACPKR = isset($urlParams["MACPKR"]) ? $urlParams["MACPKR"] : '';
try {
if ($urlParams != '') {
$IncomingParams = ['CO' => $CO, 'WHS' => $WHS, 'MACPKR' => $MACPKR];
$IncomingParams = json_encode($IncomingParams);
# Write to Logs : Params In
$data = ['SHMACPKR' => $MACPKR,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => '',
'SHINTBOX' => '',
'SHTYPE' => 'AggStopReq',
'SHDATA' => $IncomingParams,
'SHUSER' => $SHUSER,
'SHSOURCE' => $SHSOURCE,
'SHCO' => $CO,
'SHWHS' => $WHS
];
Helper::logToICM699M($data);
# First Step : Get MACPKRs list if available
# select a4macpkr from icm688m1 where a4shpaggcd in (select a4shpaggcd from icm688m1 where a4macpkr=233709) and a4shpaggcd<>0 and a4concmplt<>'y' and a4co=410 and a4whs=111
$A4MACPKRSel = DB::connection(config('m3.connection', 'pgsql'))->select(
"select a4macpkr from icm688m1 where a4shpaggcd in (select a4shpaggcd from icm688m1 where a4macpkr={$MACPKR})
and a4shpaggcd<>0 and a4concmplt<>'y' and a4co='{$CO}' and a4whs='{$WHS}' "
);
// dd($A4MACPKRSel);
if (count($A4MACPKRSel)>0){
foreach ($A4MACPKRSel as $key => $A4MACPKRR) {
$A4MACPKRArr[] = $A4MACPKRR->A4MACPKR;
}
}
else{
$A4MACPKRArr[] = $MACPKR;
}
// dump($A4MACPKRArr);
# Call 2ship API with Params
$WS_key = Config::get('arrays.2Ship.TwoShip_Key');
$end_point = Config::get('arrays.2Ship.end_point');
$API = Config::get('arrays.2Ship.hold_update');
$ShipOnHoldOrder_url = $end_point . $API;
# Build Json Data
$data = [
'WS_Key' => $WS_key,
'OrderNumbers' => [implode(',', $A4MACPKRArr)],
// 'ShipmentId' => 3,
'FindBy' => 'ByMultipleOrderNumbers',
'DataToBeUpdated' => [
'UpdateAggregationSettings' => true,
'OrderAggregationSettingsInfo' => [
'NotConsideredAggregation' => true,
],
],
];
$json_string = json_encode($data);
// dump($json_string);
#######
# Initialise the cURL var
$ch = curl_init();
# Get the response from cURL
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_URL, $ShipOnHoldOrder_url);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
#When CURLOPT_SSL_VERIFYPEER is enabled (value=1), and the verification fails to prove that the certificate is authentic, the connection fails.
#When the option is zero, the peer certificate verification succeeds regardless.
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
#When CURLOPT_SSL_VERIFYHOST is 2, that certificate must indicate that the server is the server to which you meant to connect,
#or the connection fails. Simply put, it means it has to have the same name in the certificate as is in the URL you operate against.
# Set data content type
curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
array(
'Content-Type: application/json',
'Content-Length: ' . strlen($json_string)
)
);
# Post data
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_string);
# Set timeout
curl_setopt($ch, CURLOPT_TIMEOUT, Config::get('arrays.PHPTimeOut'));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, Config::get('arrays.PHPTimeOut'));
# if set true, cURL will follow redirects
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
# Execute the request
$response = curl_exec($ch);
# Verifying error
$info = curl_getinfo($ch);
# Get status
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
# Output the error code
# $CurlError= curl_error($ch);
$CurlError = str_replace("'", " ", curl_error($ch));
# Close the session
unset($ch);
// dump($response);
// dump($status);
// dump($CurlError);
if ($status == '200' && $CurlError=='') {
$responseJSON = json_decode($response);
// dump($responseJSON);
if($responseJSON->Success==false){
$TwoShipErrors = '2Ship Error: '.$responseJSON->GlobalError;
}
} else {
$TwoShipErrors = 'Error: No Response from 2Ship';
$TwoShipErrors .= ' -Curl Error: '.$CurlError;
}
# Validate response From 2Ship
# Error
if ($TwoShipErrors != '') {
// dump($TwoShipErrors);
# Write to Logs : Error
$data = ['SHMACPKR' => $MACPKR,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'AggStopRsp',
'SHDATA' => $status . '-' . $TwoShipErrors,
'SHUSER' => $SHUSER,
'SHSOURCE' => $SHSOURCE,
'SHCO' => $CO,
'SHWHS' => $WHS
];
Helper::logToICM699M($data);
# Send Response
return response()->json([
'code' => '405',
'status' => 'error',
'message' => 'Status: '.$status . '-' . $TwoShipErrors
]);
}
# Success
else{
# Write to Logs : JSON
$data = ['SHMACPKR' => $MACPKR,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => '',
'SHINTBOX' => '',
'SHTYPE' => 'AggStopRsp',
'SHDATA' => $response,
'SHUSER' => $SHUSER,
'SHSOURCE' => $SHSOURCE,
'SHCO' => $CO,
'SHWHS' => $WHS
];
Helper::logToICM699M($data);
# Update ICM688M1 set a4concmplt to 'y'
DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->whereIn('A4MACPKR',$A4MACPKRArr)
->where('A4CO',$CO)
->where('A4WHS',$WHS)
->update(['A4CONCMPLT'=>'Y']);
}
# Send Response
return response()->json([
'code' => '200',
'status' => 'Success'
]);
}
# Error : No URL params
else {
# Write to Logs Out : Error
$MACPKR = isset($MACPKR) ? $MACPKR : '000000';
$data = ['SHMACPKR' => $MACPKR,
'SHDT' => date('Y-m-d'),
'SHTIME' => date('H:i:s'),
'SHERROR' => 'X',
'SHINTBOX' => '',
'SHTYPE' => 'AggStopRsp',
'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
$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' => 'AggStopRsp',
'SHDATA' => 'Curl Error: Communication with 2Ship Timed Out' . ' 2Ship-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 2Ship Timed Out.'
]);
}
}
}
# End TwoShipUpdateHold
}
@@ -0,0 +1,314 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Str;
class BoxPackToPalletController extends Controller
{
public static function index(Request $request)
{
return view('m3::liberty-theme.pages.BoxPackToPallet');
}
# Load Existing Boxes from ICM659M
public static function getBoxes(Request $request)
{
$R659PALT = $request->R659PALT ?? abort(404, 'Manifest Not Found');
$userWhsCoPlant = self::getUserWhsCoPlant();
// dump($userWhsCoPlant);
$boxes = DB::connection(config('m3.connection', 'pgsql'))->table('ICM659M as p')
->select(
'p.R659PALT',
'p.R659BOX',
'p.R659LOADED',
'p.R659USER',
'p.R659DATE',
'p.R659TIME',
'c.AYMACPKR',
'c.AYSPR15A01',
'cv.A4APR8N001',
'cv.A4SHSHCT',
'cnv.R652SORTK as CONVEYOR_NAME'
)
->leftJoin('ICM695M1 as c', 'p.R659BOX', '=', 'c.AYINTBOX')
->leftJoin('ICM688M1 as cv', 'c.AYMACPKR', '=', 'cv.A4MACPKR')
->leftJoin('ICM652M as cnv', 'cv.A4APR8N001', '=', 'cnv.R652SEQ')
->where('p.R659PALT', $R659PALT)
->where('p.R659CO', trim($userWhsCoPlant["USERCO"]))
->where('p.R659WHS', trim($userWhsCoPlant["USERWHS"]))
->where('p.R659PLANT', trim($userWhsCoPlant["USERPL"]))
->orderBy('p.R659DATE', 'desc')
->orderBy('p.R659TIME', 'desc')
->distinct()
->get()
->unique('R659BOX')
->values();
// dump($boxes);
if ($request->ajax()) {
return response()->json([
'success' => true,
'data' => $boxes,
]);
}
$palletHeader = $boxes->first();
// dump($boxes);
return view('m3::liberty-theme.pages.BoxPackToPalletScan', compact('boxes', 'palletHeader', 'R659PALT'));
}
# Scan Boxes
public static function scanBoxes(Request $request)
{
$R659PALT = $request->R659PALT ?? abort(400, 'Pallet Not Found');
$R659BOX = trim($request->R659BOX) ?? abort(400, 'Box Not Found');
$SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser';
$userWhsCoPlant = self::getUserWhsCoPlant();
$boxMessage = '';
# Step 1: Check for duplicate — same pallet + box combo
$exists = DB::connection(config('m3.connection', 'pgsql'))
->table('icm659m')
->select('R659PALT')
->where('R659PALT', $R659PALT)
->where('R659BOX', $R659BOX)
->where('R659CO', trim($userWhsCoPlant["USERCO"]))
->where('R659WHS', trim($userWhsCoPlant["USERWHS"]))
->where('R659PLANT', trim($userWhsCoPlant["USERPL"]))
->first();
if ($exists) {
$boxMessage = "&bull; Box {$R659BOX} is already assigned to Pallet {$exists->R659PALT}.";
}
# Step 2: Check if Box exists in ICM695M1 (with carrier + conveyor joined)
$ICM695M1Sel = DB::connection(config('m3.connection', 'pgsql'))
->table('ICM695M1 as c')
->select(
'c.AYINTBOX',
'c.AYSPR15A01',
'c.AYTYPE',
'c.AYMACPKR',
'cv.A4APR8N001',
'cnv.R652SORTK as CONVEYOR_NAME'
)
->leftJoin('ICM688M1 as cv', 'c.AYMACPKR', '=', 'cv.A4MACPKR')
->leftJoin('ICM652M as cnv', 'cv.A4APR8N001', '=', 'cnv.R652SEQ')
->where('c.AYINTBOX', $R659BOX)
->where('c.AYCO', trim($userWhsCoPlant["USERCO"]))
->where('c.AYWHS', trim($userWhsCoPlant["USERWHS"]))
->where('c.AYPLANT', trim($userWhsCoPlant["USERPL"]))
->first();
$boxExistInICM695M = isset($ICM695M1Sel->AYINTBOX);
$boxisPalletICM695M = isset($ICM695M1Sel->AYTYPE) && $ICM695M1Sel->AYTYPE != 'BX';
if (!$boxExistInICM695M) {
$br = $boxMessage == '' ? '' : '<br>';
$boxMessage .= $br . "&bull; Box {$R659BOX} does not exist in ICM695M1 or it belongs to another Company.";
}
if ($boxExistInICM695M && $boxisPalletICM695M) {
$br = $boxMessage == '' ? '' : '<br>';
$boxMessage .= $br . "&bull; Scanned barcode {$R659BOX} is not a Box.";
}
# Step 3: Check that all boxes on the pallet share the same Carrier and Conveyor
if ($boxExistInICM695M && !$boxisPalletICM695M && !$exists) {
$firstPalletBox = DB::connection(config('m3.connection', 'pgsql'))
->table('ICM659M as p')
->select(
'c.AYSPR15A01',
'cnv.R652SORTK as CONVEYOR_NAME'
)
->leftJoin('ICM695M1 as c', 'p.R659BOX', '=', 'c.AYINTBOX')
->leftJoin('ICM688M1 as cv', 'c.AYMACPKR', '=', 'cv.A4MACPKR')
->leftJoin('ICM652M as cnv', 'cv.A4APR8N001', '=', 'cnv.R652SEQ')
->where('p.R659PALT', $R659PALT)
->where('p.R659CO', trim($userWhsCoPlant["USERCO"]))
->where('p.R659WHS', trim($userWhsCoPlant["USERWHS"]))
->where('p.R659PLANT', trim($userWhsCoPlant["USERPL"]))
->first();
if ($firstPalletBox) {
$incomingCarrier = trim($ICM695M1Sel->AYSPR15A01 ?? '');
$incomingConveyor = trim($ICM695M1Sel->CONVEYOR_NAME ?? '');
$palletCarrier = trim($firstPalletBox->AYSPR15A01 ?? '');
$palletConveyor = trim($firstPalletBox->CONVEYOR_NAME ?? '');
if ($incomingCarrier !== $palletCarrier) {
$br = $boxMessage == '' ? '' : '<br>';
$boxMessage .= $br . "&bull; Box {$R659BOX} has a different Carrier ({$incomingCarrier}) than the boxes already on Pallet {$R659PALT} ({$palletCarrier}).";
}
if ($incomingConveyor !== $palletConveyor) {
$br = $boxMessage == '' ? '' : '<br>';
$boxMessage .= $br . "&bull; Box {$R659BOX} has a different Conveyor ({$incomingConveyor}) than the boxes already on Pallet {$R659PALT} ({$palletConveyor}).";
}
}
}
if ($exists || !$boxExistInICM695M || $boxisPalletICM695M || $boxMessage !== '') {
return response()->json([
'success' => false,
'message' => $boxMessage,
], 409);
}
# Step 4: Insert new box record
DB::connection(config('m3.connection', 'pgsql'))
->table('icm659m')
->insert([
'R659PALT' => $R659PALT,
'R659BOX' => $R659BOX,
'R659LOADED' => '',
'R659USER' => $SHUSER,
'R659DATE' => date('Y-m-d'),
'R659TIME' => date('H:i:s'),
'R659CO' => trim($userWhsCoPlant["USERCO"]),
'R659WHS' => trim($userWhsCoPlant["USERWHS"]),
'R659PLANT' => trim($userWhsCoPlant["USERPL"]),
]);
# Step 5: Return the full refreshed box list
$boxes = DB::connection(config('m3.connection', 'pgsql'))->table('ICM659M as p')
->select(
'p.R659PALT',
'p.R659BOX',
'p.R659LOADED',
'p.R659USER',
'p.R659DATE',
'p.R659TIME',
'c.AYMACPKR',
'c.AYSPR15A01',
'cv.A4APR8N001',
'cv.A4SHSHCT',
'cnv.R652SORTK as CONVEYOR_NAME'
)
->leftJoin('ICM695M1 as c', 'p.R659BOX', '=', 'c.AYINTBOX')
->leftJoin('ICM688M1 as cv', 'c.AYMACPKR', '=', 'cv.A4MACPKR')
->leftJoin('ICM652M as cnv', 'cv.A4APR8N001', '=', 'cnv.R652SEQ')
->where('p.R659PALT', $R659PALT)
->where('p.R659CO', trim($userWhsCoPlant["USERCO"]))
->where('p.R659WHS', trim($userWhsCoPlant["USERWHS"]))
->where('p.R659PLANT', trim($userWhsCoPlant["USERPL"]))
->orderBy('p.R659DATE', 'desc')
->orderBy('p.R659TIME', 'desc')
->distinct()
->get()
->unique('R659BOX')
->values();
return response()->json([
'success' => true,
'message' => "&bull; Box {$R659BOX} added to Pallet {$R659PALT}.",
'data' => $boxes,
]);
}
# Delete Box
public static function deleteBox(Request $request)
{
$R659PALT = $request->R659PALT ?? abort(400, 'Pallet Not Found');
$R659BOX = trim($request->R659BOX) ?? abort(400, 'Box Not Found');
$userWhsCoPlant = self::getUserWhsCoPlant();
# Guard: only allow delete if R659LOADED is empty
$record = DB::connection(config('m3.connection', 'pgsql'))
->table('icm659m')
->where('R659PALT', $R659PALT)
->where('R659BOX', $R659BOX)
->first();
if (!$record) {
return response()->json([
'success' => false,
'message' => "Box {$R659BOX} not found on Pallet {$R659PALT}.",
], 404);
}
if (!empty(trim($record->R659LOADED))) {
return response()->json([
'success' => false,
'message' => "Box {$R659BOX} is already loaded and cannot be removed.",
], 403);
}
DB::connection(config('m3.connection', 'pgsql'))
->table('icm659m')
->where('R659PALT', $R659PALT)
->where('R659BOX', $R659BOX)
->delete();
# Return refreshed list
$boxes = DB::connection(config('m3.connection', 'pgsql'))->table('ICM659M as p')
->select(
'p.R659PALT',
'p.R659BOX',
'p.R659LOADED',
'p.R659USER',
'p.R659DATE',
'p.R659TIME',
'c.AYMACPKR',
'c.AYSPR15A01',
'cv.A4APR8N001',
'cv.A4SHSHCT',
'cnv.R652SORTK as CONVEYOR_NAME'
)
->leftJoin('ICM695M1 as c', 'p.R659BOX', '=', 'c.AYINTBOX')
->leftJoin('ICM688M1 as cv', 'c.AYMACPKR', '=', 'cv.A4MACPKR')
->leftJoin('ICM652M as cnv', 'cv.A4APR8N001', '=', 'cnv.R652SEQ')
->where('p.R659PALT', $R659PALT)
->where('p.R659CO', trim($userWhsCoPlant["USERCO"]))
->where('p.R659WHS', trim($userWhsCoPlant["USERWHS"]))
->where('p.R659PLANT', trim($userWhsCoPlant["USERPL"]))
->orderBy('p.R659DATE', 'desc')
->orderBy('p.R659TIME', 'desc')
->distinct()
->get()
->unique('R659BOX')
->values();
return response()->json([
'success' => true,
'message' => "Box {$R659BOX} removed from Pallet {$R659PALT}.",
'data' => $boxes,
]);
}
# Get Logged User CO/WHS/PLANT
public static function getUserWhsCoPlant()
{
$SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
# Get users from ICM428M
$ICM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH', 'R428PL')
->where('R428US', '=', $SHUSER)
->first();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : '';
$USERCO = isset($ICM428M->R428CO) ? $ICM428M->R428CO : '';
$USERWH = isset($ICM428M->R428WH) ? $ICM428M->R428WH : '';
$R428PL = isset($ICM428M->R428PL) ? $ICM428M->R428PL : '';
$userWhsCoPlant = [
"R428US" => $R428US,
"USERCO" => $USERCO,
"USERWHS" => $USERWH,
"USERPL" => $R428PL,
];
return $userWhsCoPlant;
}
}
@@ -0,0 +1,13 @@
<?php
namespace Modules\M3\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
@@ -0,0 +1,302 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
// use Modules\M3\Models\ICM709M;
//
class GetContentsController extends Controller
{
public function processShipment(Request $request)
{
# Get Mackpkr from Request
$macpacPicker = isset($request->MACPKR) ? trim($request->MACPKR) : '';
# ESN1
# $macpacPicker='888568'; // LTL example
# $macpacPicker='894337'; // another LTL example with smaller orders, therefore pallets are more mixed
# $macpacPicker='1225311'; // parcel example
# ESN2
# $macpacPicker='748101'; // LTL example
try {
$icm704mArray = $this->buildNMFCCodesArray();
$shipmentData = $this->getShipmentData($macpacPicker);
if (empty($shipmentData['orderNumber'])) {
return response()->json(['error' => "Picker {$macpacPicker} was not found in ICM688M1"]);
}
# Get Packages
$packages = $this->getPackagesData($shipmentData['macpkr_list']);
# Add NMFC to Packages
$packagesWithNMFC = $this->addNMFCDataToPackages($packages, $macpacPicker, $icm704mArray);
# Get Commodities
$commodities = $this->getCommoditiesData(
$shipmentData['company'],
$shipmentData['warehouse'],
$macpacPicker,
$packages['package_595m1'],
$icm704mArray
);
return response()->json([
'error' => '',
'shipmentData' => $shipmentData,
'packages' => $packagesWithNMFC,
'commodities' => $commodities
]);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
}
protected function buildNMFCCodesArray()
{
$results = DB::table('ICM704M')->get();
return $results->mapWithKeys(function ($row) {
// dump($row);
return [
trim($row->AONMIT) => [
'LTLClassId' => number_format(trim($row->AOFCID), 1),
'NMFCItem' => trim($row->AONMIT),
'NMFCSub' => trim($row->AONMSB),
'SkidDescription' => trim($row->AOSKDE)
]
];
})->all();
}
protected function getShipmentData($macpacPicker)
{
$result = DB::select("
SELECT
SUM(A4TPCKWGHT) as total_weight,
A4MACPKR, A4SHSHA1, A4SHSHA2, A4SHSHCI, A4SHSHNM,
A4SHSHCT, A4SHSHZP, A4SHSHST, A4ONO, A4STCN, A4CO,
A4WHS, A4SHIPVIA, A4SLCO, A4SLWHS, A4CATPID, A42SSVCCOD,
A4CARRIER, A4SHPAGGCD, A4SALESLOC, A4SHCSPO, A4OFSTRM,
A4SCAC, A4PLANT
FROM ICM688M1
WHERE A4MACPKR = ?
GROUP BY A4MACPKR, A4SHSHA1, A4SHSHA2, A4SHSHCI, A4SHSHNM,
A4SHSHCT, A4SHSHZP, A4SHSHST, A4ONO, A4RLNO, A4SHNO,
A4STCN, A4CO, A4WHS, A4SHIPVIA, A4SLCO, A4SLWHS,
A4CATPID, A42SSVCCOD, A4CARRIER, A4SHPAGGCD,
A4SALESLOC, A4SHCSPO, A4OFSTRM, A4SCAC, A4PLANT",
[$macpacPicker]
);
if (empty($result)) {
return [];
}
$row = $result[0];
$aggCode = trim($row->A4SHPAGGCD);
return [
'orderNumber' => trim($row->A4MACPKR),
'recipient' => [
'address1' => trim($row->A4SHSHA1),
'address2' => trim($row->A4SHSHA2),
'city' => trim($row->A4SHSHCI),
'companyName' => trim($row->A4SHSHNM),
'country' => trim($row->A4SHSHCT),
'postalCode' => trim($row->A4SHSHZP),
'state' => trim($row->A4SHSHST),
],
'shipmentReference' => trim($row->A4ONO),
'company' => trim($row->A4CO),
'warehouse' => trim($row->A4WHS),
'macpkr_list' => $macpacPicker,
'shipment_list' => "'" . trim($row->A4ONO) . "'",
'po_list' => "'" . trim($row->A4SHCSPO) . "'",
'order_list' => "'" . trim($row->A4ONO) . "'"
];
}
protected function getPackagesData($macpkrList)
{
$results = DB::select("
SELECT
AYINTBOX,
MAX(AYHEIGHT) as height,
MAX(AYWIDTH) as width,
MAX(AYDEPTH) as depth,
SUM(AYLNWGHT) as weight
FROM ICM695M1
WHERE AYMACPKR IN (?)
GROUP BY AYINTBOX
ORDER BY AYINTBOX",
[$macpkrList]
);
$packages = [];
$package595m1 = [];
$packagesGS1Array = [];
foreach ($results as $index => $row) {
// dump($row);
$internalBox = trim($row->AYINTBOX);
$package595m1[$internalBox] = [
'INDEX' => $index,
'HEIGHT' => $row->HEIGHT,
'WIDTH' => $row->WIDTH,
'DEPTH' => $row->DEPTH,
'WEIGHT' => $row->WEIGHT
];
$packages[] = [
'Width' => $row->WIDTH,
'Height' => $row->HEIGHT,
'Length' => $row->DEPTH,
'Weight' => $row->WEIGHT,
'LicencePlate' => $internalBox,
'Reference1' => $internalBox,
'WeightType' => 0,
'DimensionType' => 0
];
$packagesGS1Array[] = $internalBox;
}
return [
'packages' => $packages,
'package_595m1' => $package595m1,
'packagesGS1Array' => $packagesGS1Array
];
}
protected function addNMFCDataToPackages($packages, $macpkrList, $icm704mArray)
{
$nmfcResults = DB::select("
WITH tempfile AS (
SELECT
(SELECT A5WBFRCT FROM ICM688M2
WHERE A5MACPKR IN (?) AND A5PART=AZPART
FETCH FIRST 1 ROWS ONLY) as WBFRCT,
az.*
FROM ICM695M2 az
WHERE AZMACPKR IN (?)
)
SELECT azintbox, MIN(WBFRCT) as MIN_WBFRCT
FROM tempfile
GROUP BY azintbox
ORDER BY 1",
[$macpkrList, $macpkrList]
);
foreach ($nmfcResults as $index => $row) {
$wbfrct = trim($row->MIN_WBFRCT);
$packages['packages'][$index]['SkidDesc'] =
$icm704mArray[$wbfrct]['SkidDescription'] ?? 'CLIPS, FASTENERS OR MOUNTS';
}
return $packages;
}
protected function getCommoditiesData($company, $warehouse, $macpkrList, $package595m1, $icm704mArray)
{
$results = DB::select("
WITH tempfile1 AS (
SELECT
A5PART,
MAX(A5CMDESC) as A5CMDESC,
MAX(A5MUTMS) as A5MUTMS,
MAX(A5PMPWHT) as A5PMPWHT,
MAX(A5SLUNPR) as A5SLUNPR,
TRIM(MIN(A5WBFRCT)) as A5WBFRCT
FROM ICM688M2
WHERE A5CO = ? AND A5WHS = ? AND A5MACPKR IN (?) AND A5PACKED > 0
GROUP BY A5PART
),
tempfile2 AS (
SELECT
AZINTBOX,
AZPART,
SUM(AZPACKED) as AZPACKED,
MAX(SUBSTR(AZSPR15A01,1,4)) as HAZARDOUS
FROM ICM695M2
WHERE AZCO = ? AND AZWHS = ? AND AZMACPKR IN (?) AND AZPACKED <> 0
GROUP BY AZINTBOX, AZPART
)
SELECT
A5PART, A5CMDESC, A5MUTMS,
A5PMPWHT * AZPACKED as WEIGHt,
AZPACKED, AZINTBOX,
A5WBFRCT, HAZARDOUS, A5SLUNPR
FROM tempfile1
LEFT JOIN tempfile2 ON AZPART = A5PART
ORDER BY A5PART",
[$company, $warehouse, $macpkrList, $company, $warehouse, $macpkrList]
);
$commodities = [];
$prevPartNumber = '';
$index = -1;
foreach ($results as $row) {
$partNumber = trim($row->A5PART);
if ($partNumber !== $prevPartNumber) {
$index++;
$quantityIndex = 0;
$wbfrct = trim($row->A5WBFRCT);
$nmfcData = $icm704mArray[$wbfrct] ?? [
'LTLClassId' => '50',
'NMFCItem' => '93490',
'NMFCSub' => ''
];
$commodities[] = [
'Description' => trim($row->A5CMDESC),
'PartNumber' => $partNumber,
'SKUOrItemOrUPC' => $partNumber,
'MadeInCountryCode' => 'US',
'UnitValue' => $row->A5SLUNPR == '.000' ? 1 : (float) $row->A5SLUNPR,
'TotalWeight' => (float) $row->WEIGHT,
'QuantityUnitOfMeasure' => trim($row->A5MUTMS) ?: 'EA',
'Quantity' => (float) $row->AZPACKED,
'LTLClassId' => $nmfcData['LTLClassId'],
'NMFCItem' => $nmfcData['NMFCItem'],
'NMFCSub' => $nmfcData['NMFCSub'],
'DangerousGoodsInfo' => [
'ContainsDangerousGoods' => trim($row->HAZARDOUS) === 'H4.1'
],
'QuantityAssignment' => [
[
'PackageIndex' => $package595m1[trim($row->AZINTBOX)]['INDEX'],
'Quantity' => (float) $row->AZPACKED
]
]
];
} else {
$quantityIndex++;
$commodities[$index]['TotalWeight'] += (float) $row->WEIGHT;
$commodities[$index]['Quantity'] += (float) $row->AZPACKED;
$commodities[$index]['QuantityAssignment'][] = [
'PackageIndex' => $package595m1[trim($row->AZINTBOX)]['INDEX'],
'Quantity' => (float) $row->AZPACKED
];
}
$prevPartNumber = $partNumber;
}
return $commodities;
}
}
@@ -0,0 +1,113 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Log;
use Str;
use Artisan;
use Modules\M3\Http\Controllers\API\CloseShipment;
class M3CallCloseAPIController extends Controller
{
public static function index(Request $request){
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'));
try {
# Get the Calling User
# Get URL PARAMS
$urlParams = count($request->all())>0 ? $request->all() : '';
// dd($urlParams);
$SHUSER = isset($urlParams["SHUSER"]) ? $urlParams["SHUSER"] : '';
$MANIFEST = isset($urlParams["MANIFEST"]) ? $urlParams["MANIFEST"] : '';
$APCO = isset($urlParams["APCO"]) ? $urlParams["APCO"] : '';
$APWHS = isset($urlParams["APWHS"]) ? $urlParams["APWHS"] : '';
if ($MANIFEST!='' && $APCO!='' && $APWHS!='') { // $SHUSER!='' &&
// Log::channel('dailyCron')->info("Cron...Start CallCloseAPI...For user:".$SHUSER." Manifest#: ".$MANIFEST);
# Get Logged User CO & WH
# Get users from ICM428M
// $ICM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
// ->select('R428US','R428CO', 'R428WH')
// ->where('R428US','=',$SHUSER)
// ->first();
// $R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : '';
// $USERCO = isset($ICM428M->R428CO) ? $ICM428M->R428CO : '';
// $USERWH = isset($ICM428M->R428WH) ? $ICM428M->R428WH : '';
// dump($R428US.':'.$USERCO.':'.$USERWH);
# Get Macpickers from ICM705M1
# SELECT APMANIFEST FROM ICM705M1 WHERE APSPARE2='' AND APCO='110' AND APWHS='321'
$ICM705M1 = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1')
->select('APMANIFEST','APCO','APWHS')
->where('APSPARE2','')
->where('APCO',$APCO)
->where('APWHS',$APWHS)
->where('APMANIFEST','=',$MANIFEST)
->whereRaw('EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - APLDCLOZ)) / 60 > 15')
->orderBy('APMANIFEST')
->get();
// dump($ICM705M1);
if (count($ICM705M1)>0) {
# Loop the Results from $ICM705M1
foreach ($ICM705M1 as $key => $ICM705M1R) {
# Send CO and WHS to CallCloseAPI
$USERCO = isset($ICM705M1R->APCO) ? $ICM705M1R->APCO : '';
$USERWH = isset($ICM705M1R->APWHS) ? $ICM705M1R->APWHS : '';
$callCloseShipment = Artisan::call('command:CallCloseAPI', ['user'=> $SHUSER ,'manifest' => $ICM705M1R->APMANIFEST ,
'co' => $USERCO ,'whs' => $USERWH]);
// dump($callCloseShipment);
if ($callCloseShipment==1) {
return response()->json([
'code' => '200',
'status' => 'Success',
'message'=> 'Manifest Closing submitted'
]);
}
else{
return response()->json([
'code' => '206',
'status' => 'Failure',
'message'=> 'Manifest Closing Failed'
]);
}
}
}
else{
Log::channel('dailyCron')->info("Manifest CloseAPI: No records found in ICM705M1...");
return response()->json([
'code' => '206',
'status' => 'Error',
'message'=> "Manifest CloseAPI: No records found in ICM705M1..."
]);
}
}
else{
Log::channel('dailyCron')->error('Manifest CloseAPI: A User and Manifest must be defined...');
return response()->json([
'code' => '206',
'status' => 'Error',
'message'=> 'Manifest CloseAPI: A User and Manifest must be defined...'
]);
}
}
catch (\Throwable $th) {
Log::error($th);
}
}
}
+13 -1
View File
@@ -10,6 +10,7 @@ class M3Controller extends Controller
{
/**
* Display a listing of the resource.
*
* @return Renderable
*/
public function index()
@@ -19,6 +20,7 @@ class M3Controller extends Controller
/**
* Show the form for creating a new resource.
*
* @return Renderable
*/
public function create()
@@ -28,7 +30,9 @@ class M3Controller extends Controller
/**
* Store a newly created resource in storage.
*
* @param Request $request
*
* @return Renderable
*/
public function store(Request $request)
@@ -38,7 +42,9 @@ class M3Controller extends Controller
/**
* Show the specified resource.
*
* @param int $id
*
* @return Renderable
*/
public function show($id)
@@ -48,7 +54,9 @@ class M3Controller extends Controller
/**
* Show the form for editing the specified resource.
*
* @param int $id
*
* @return Renderable
*/
public function edit($id)
@@ -58,8 +66,10 @@ class M3Controller extends Controller
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param int $id
* @param int $id
*
* @return Renderable
*/
public function update(Request $request, $id)
@@ -69,7 +79,9 @@ class M3Controller extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
*
* @return Renderable
*/
public function destroy($id)
@@ -0,0 +1,68 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Modules\M3\Models\ICM664M;
class M3DeliveryController extends Controller
{
public static function index(Request $request) {
$connection = config('m3.connection', 'pgsql');
$power_user = (object) ['R428PWRUSR'=>'N','R428US'=>strtoUpper(m3_user()),'R428CO'=>'','R428PL'=>'','R428WH'=>''];
if (\Illuminate\Support\Facades\Schema::connection($connection)->hasTable('icm428m')) {
$power_user = DB::connection($connection)->table('icm428m')->select('R428PWRUSR','R428US','R428CO','R428PL','R428WH')
->where('R428US',strtoUpper(m3_user()))->first() ?? $power_user;
}
$co=$power_user->R428CO; $plant=$power_user->R428PL; $whs=$power_user->R428WH;
$ICM664M = \Illuminate\Support\Facades\Schema::connection($connection)->hasTable('icm664m')
? DB::connection($connection)->table('icm664m')->where('AVCO',$co)->where('AVPLANT',$plant)->where('AVWHS',$whs)->get() : collect();
$IC670M = \Illuminate\Support\Facades\Schema::connection($connection)->hasTable('ic670m')
? DB::connection($connection)->table('ic670m')->where('WDCO',$co)->where('WDPL',$plant)->where('WDWHS',$whs)->get() : collect();
$shipvia = \Illuminate\Support\Facades\Schema::connection($connection)->hasTable('icm319m')
? DB::connection($connection)->table('icm319m')->select('AXCODE','AXDESC')->orderBy('AXCODE')->get() : collect();
return view('m3::liberty-theme.pages.M3Delivery', compact('ICM664M','IC670M','power_user','shipvia'));
}
//
// Process data from Add modal
//
public static function saveM3DeliveryAdd(Request $request) {
$icm664m=DB::connection(config('m3.connection', 'pgsql'))->select("select count(*) as COUNT1 from ICM664M
where AVCO='$request->AVCO' and AVWHS='$request->AVWHS' and AVPLANT='$request->AVPLANT' and AVDELMETH='$request->AVDELMETH'");
if($icm664m[0]->COUNT1=="1") {
return "exists";
} else {
DB::connection(config('m3.connection', 'pgsql'))->insert("insert into ICM664M
(AVCO, AVPLANT, AVWHS, AVDELMETH, AVSHPVIA, AVSHSRT, AVSHORCL)
values ('$request->AVCO', '$request->AVPLANT', '$request->AVWHS', '$request->AVDELMETH',
'$request->AVSHPVIA', '$request->AVSHSRT', '$request->AVSHORCL')");
return "success";
}
}
//
// Process data from Edit modal
//
public static function saveM3DeliveryEdit(Request $request) {
#dd($request);
// Update record to ICM664M
DB::connection(config('m3.connection', 'pgsql'))->update("update ICM664M
set AVSHPVIA='$request->AVSHPVIA', AVSHSRT='$request->AVSHSRT', AVSHORCL='$request->AVSHORCL'
where AVCO='$request->AVCO' and AVPLANT='$request->AVPLANT' and AVWHS='$request->AVWHS' and AVDELMETH='$request->AVDELMETH'");
return "success";
}
}
@@ -0,0 +1,348 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Datatables;
use Str;
use Excel;
class M3ErrorsDispController extends Controller
{
public static function index(Request $request)
{
// dump($request->all());
$macpkr = isset($request->macpkr) ? trim($request->macpkr) : "";
$shsource = isset($request->shsource) ? trim($request->shsource) : "";
$showerrors = isset($request->showerrors) ? trim($request->showerrors) : "N";
$skipdates = isset($request->skipdates) ? trim($request->skipdates) : "N";
# Get users from ICM428M
$connection = config('m3.connection', 'pgsql');
$ICM428M = \Illuminate\Support\Facades\Schema::connection($connection)->hasTable('icm428m')
? DB::connection($connection)->table('icm428m')->select('R428US', 'R428FIRST', 'R428FAMILY')->get()
: collect();
$ICM428MCOL = collect($ICM428M);
$ICM428MCOL->filter(function ($value) {
return $value->R428US = trim($value->R428US);
});
$ICM428MCOL->filter(function ($value) {
return $value->R428FIRST = trim($value->R428FIRST);
});
$ICM428MCOL->filter(function ($value) {
return $value->R428FAMILY = trim($value->R428FAMILY);
});
return view('m3::liberty-theme.pages.M3ErrorsDisplay', compact('macpkr', 'shsource', 'showerrors', 'skipdates', 'ICM428MCOL'));
}
public function datatables(Request $request)
{
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'));
ini_set('memory_limit', '1024M');
// dump($request->all());
$SHMACPKR = null != $request->SHMACPKR ? trim($request->SHMACPKR) : '';
$SHSOURCE = null != $request->SHSOURCE ? trim($request->SHSOURCE) : '';
$STARTDATE = null != $request->STARTDATE ? trim($request->STARTDATE) : today()->subDays(1)->format('Y-m-d');
$ENDDATE = null != $request->ENDDATE ? trim($request->ENDDATE) : today()->format('Y-m-d');
$SHOWERRORS = null != $request->SHOWERRORS ? trim($request->SHOWERRORS) : 'N';
$SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
$SKIPDATES = null != $request->SKIPDATES ? trim($request->SKIPDATES) : 'N';
# Get Logged User CO & WH
# Get users from ICM428M
$ICM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH')
->where('R428US', '=', $SHUSER)
->first();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : '';
$USERCO = isset($ICM428M->R428CO) ? $ICM428M->R428CO : '';
$USERWH = isset($ICM428M->R428WH) ? $ICM428M->R428WH : '';
# Merge date and time fields
$mergedDateTime = DB::raw(" ICM699M.SHDT || ' ' || ICM699M.SHTIME AS SHDATETIME");
$ICM699M = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->select(
'ICM699M.*',
'ICM699M.SHACKDATE',
'ICM699M.SHACKTIME',
'ICM699M.SHACKUSER',
$mergedDateTime,
'ICM428M.R428FIRST',
'ICM428M.R428FAMILY',
'ICM688M1.A4ONO',
'ICM688M1.A4SHSHNM',
'ICM666M.A1ONO',
'ICM666M.A1SHSHNM'
)
->distinct()
->leftJoin('ICM428M', 'ICM699M.SHUSER', '=', 'ICM428M.R428US')
->leftJoin('ICM688M1', 'ICM699M.SHMACPKR', '=', 'ICM688M1.A4MACPKR')
->leftJoin('ICM666M', 'ICM699M.SHMACPKR', '=', 'ICM666M.A1DELNO');
if ($R428US != '') {
$ICM699M->whereIn('SHCO', [$USERCO, '']);
$ICM699M->whereIn('SHWHS', [$USERWH, '']);
} else {
$ICM699M->where('SHCO', null);
$ICM699M->where('SHWHS', null);
}
if ($SHMACPKR != '') {
$ICM699M->where('SHMACPKR', $SHMACPKR);
}
if ($SHSOURCE != '') {
$ICM699M->where('SHSOURCE', $SHSOURCE);
}
# Disable PDFStorage Search
$ICM699M->where('SHSOURCE', '!=', 'PdfStorageAPI');
if ($SHMACPKR == '' && $SKIPDATES == 'N') {
$ICM699M->whereBetween('SHDT', [$STARTDATE, $ENDDATE]);
}
if ($SHOWERRORS == 'Y') {
$ICM699M->where('SHERROR', 'X');
}
# Get only M3 Logs
$ICM699M->where('SHTYPE', 'like', '%M3%');
return Datatables::of($ICM699M)->addIndexColumn()->make(true);
}
public static function update699M(Request $request)
{
// dump($request->all());
$SHMACPKR = null != $request->SHMACPKR ? trim($request->SHMACPKR) : '';
$SHSOURCE = null != $request->SHSOURCE ? trim($request->SHSOURCE) : '';
$SHCO = null != $request->SHCO ? trim($request->SHCO) : '';
$SHWHS = null != $request->SHWHS ? trim($request->SHWHS) : '';
$SHDT = null != $request->SHDT ? trim($request->SHDT) : '';
$SHTIME = null != $request->SHTIME ? trim($request->SHTIME) : '';
$SHTYPE = null != $request->SHTYPE ? trim($request->SHTYPE) : '';
$SHACKNOTE = null != $request->SHACKNOTE ? trim(substr($request->SHACKNOTE, 0, 99)) : '';
$SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : '';
$CLOSESIM = null != $request->CLOSESIM ? trim($request->CLOSESIM) : 'N';
if ($CLOSESIM == 'N') {
$ICM699MU = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->where('SHMACPKR', $SHMACPKR)
->where('SHDT', $SHDT)
->where('SHTIME', $SHTIME)
->where('SHCO', $SHCO)
->where('SHWHS', $SHWHS)
->where('SHSOURCE', $SHSOURCE)
->where('SHERROR', 'X')
->where('SHTYPE', $SHTYPE)
->update(["SHERROR" => "Z", "SHACKUSER" => $SHUSER, "SHACKDATE" => date('Y-m-d'), "SHACKTIME" => date('H:i:s'), "SHACKNOTE" => $SHACKNOTE]);
} else {
$ICM699MU = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->where('SHCO', $SHCO)
->where('SHWHS', $SHWHS)
->where('SHSOURCE', $SHSOURCE)
->where('SHERROR', 'X')
->where('SHTYPE', $SHTYPE)
->update(["SHERROR" => "Z", "SHACKUSER" => $SHUSER, "SHACKDATE" => date('Y-m-d'), "SHACKTIME" => date('H:i:s'), "SHACKNOTE" => $SHACKNOTE]);
}
if ($SHSOURCE == "CloseShipmentCoChargeAPI") {
# Update ICM688M1 : Change A4PICKSTS
DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')
->where('A4MACPKR', $SHMACPKR)
->where('A4CO', $SHCO)
->where('A4WHS', $SHWHS)
->update(['A4PICKSTS' => 'a']);
}
return response()->json(['success' => true]);
}
# Export To Excel
public static function exporttoExcel(Request $request)
{
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'));
ini_set('memory_limit', '1024M');
try {
// dd($request->all());
$SHMACPKR = null != $request->SHMACPKR ? trim($request->SHMACPKR) : '';
$SHSOURCE = null != $request->SHSOURCE ? trim($request->SHSOURCE) : '';
$STARTDATE = null != $request->STARTDATE ? trim($request->STARTDATE) : today()->subDays(1)->format('Y-m-d');
$ENDDATE = null != $request->ENDDATE ? trim($request->ENDDATE) : today()->format('Y-m-d');
$SHOWERRORS = null != $request->SHOWERRORS ? trim($request->SHOWERRORS) : 'N';
$SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
$SKIPDATES = null != $request->SKIPDATES ? trim($request->SKIPDATES) : 'N';
# Get Logged User CO & WH
# Get users from ICM428M
$ICM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428CO', 'R428WH')
->where('R428US', '=', $SHUSER)
->first();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : '';
$USERCO = isset($ICM428M->R428CO) ? $ICM428M->R428CO : '';
$USERWH = isset($ICM428M->R428WH) ? $ICM428M->R428WH : '';
# Get users from ICM428M
$ICM428MAll = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US', 'R428FIRST', 'R428FAMILY')
->get();
$ICM428MCOL = collect($ICM428MAll);
$ICM428MCOL->filter(function ($value) {
return $value->R428US = trim($value->R428US);
});
$ICM428MCOL->filter(function ($value) {
return $value->R428FIRST = trim($value->R428FIRST);
});
$ICM428MCOL->filter(function ($value) {
return $value->R428FAMILY = trim($value->R428FAMILY);
});
# Merge date and time fields
$mergedDateTime = DB::raw(" ICM699M.SHDT || ' ' || ICM699M.SHTIME AS SHDATETIME");
$ICM699M = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->select(
DB::raw('COALESCE(ICM688M1.A4ONO, ICM666M.A1ONO) AS DELIVERY_NUM '),
'ICM699M.SHMACPKR',
DB::raw('COALESCE(ICM688M1.A4SHSHNM, ICM666M.A1SHSHNM) AS CUSTOMER '),
$mergedDateTime,
DB::raw("CASE WHEN ICM699M.SHERROR !='Z' THEN TRIM(ICM699M.SHERROR) ELSE 'Closed' END AS ERROR"),
'ICM699M.SHTYPE',
'ICM699M.SHSOURCE',
DB::raw("CASE WHEN ICM428M.R428FIRST IS NOT NULL THEN TRIM(ICM428M.R428FIRST) ||' '|| TRIM(ICM428M.R428FAMILY) ELSE TRIM(ICM699M.SHUSER) END AS USER"),
'ICM699M.SHCODE',
'ICM699M.SHCO',
'ICM699M.SHWHS',
DB::raw(" ICM699M.SHACKDATE ||' '||ICM699M.SHACKTIME AS ACKDATE"),
'ICM699M.SHACKUSER',
'ICM699M.SHINTBOX',
'ICM699M.SHACKNOTE',
DB::raw('SUBSTR(ICM699M.SHDATA, 1, 200)')
)
->distinct()
->leftJoin('ICM428M', 'ICM699M.SHUSER', '=', 'ICM428M.R428US')
->leftJoin('ICM688M1', 'ICM699M.SHMACPKR', '=', 'ICM688M1.A4MACPKR')
->leftJoin('ICM666M', 'ICM699M.SHMACPKR', '=', 'ICM666M.A1DELNO');
# Get Records Count only
$ICM699MC = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m');
if ($R428US != '') {
$ICM699M->whereIn('SHCO', [$USERCO, '']);
$ICM699M->whereIn('SHWHS', [$USERWH, '']);
$ICM699MC->whereIn('SHCO', [$USERCO, '']);
$ICM699MC->whereIn('SHWHS', [$USERWH, '']);
} else {
$ICM699M->where('SHCO', null);
$ICM699M->where('SHWHS', null);
$ICM699MC->where('SHCO', null);
$ICM699MC->where('SHWHS', null);
}
if ($SHMACPKR != '') {
$ICM699M->where('SHMACPKR', $SHMACPKR);
$ICM699MC->where('SHMACPKR', $SHMACPKR);
}
if ($SHSOURCE != '') {
$ICM699M->where('SHSOURCE', $SHSOURCE);
$ICM699MC->where('SHSOURCE', $SHSOURCE);
}
# Disable PDFStorage Search
$ICM699M->where('SHSOURCE', '!=', 'PdfStorageAPI');
$ICM699MC->where('SHSOURCE', '!=', 'PdfStorageAPI');
if ($SHMACPKR == '' && $SKIPDATES == 'N') {
$ICM699M->whereBetween('SHDT', [$STARTDATE, $ENDDATE]);
$ICM699MC->whereBetween('SHDT', [$STARTDATE, $ENDDATE]);
}
if ($SHOWERRORS == 'Y') {
$ICM699M->where('SHERROR', 'X');
$ICM699MC->where('SHERROR', 'X');
}
# Get only M3 Logs
$ICM699M->where('SHTYPE', 'like', '%M3%');
$ICM699MC->where('SHTYPE', 'like', '%M3%');
# Count records before exporting to excel : Max 20K records are allowed
$ICM699MCount = $ICM699MC->count();
// dd($ICM699MCount);
# Set Max allowed records
$maxRecords = Config::get('arrays.maxRecDL');
if ($ICM699MCount > $maxRecords) {
return redirect()->back()->withInput()->with('message', 'Maximum allowed records for Excel Download is: ' . number_format($maxRecords) .
' current records: ' . number_format($ICM699MCount) . ' , change your search criteria.');
}
# Select Records and export them to Excel
$ICM699MSel = $ICM699M->get();
$ICM699MCol = collect($ICM699MSel);
# Get Transaction Types
$transxTypes = Config::get('arrays.transxTypes');
$ICM699MCol->filter(function ($value) use ($transxTypes) {
return $value->SHSOURCE = isset($transxTypes[trim($value->SHSOURCE)]) ? $transxTypes[trim($value->SHSOURCE)] : $value->SHSOURCE;
});
# Get User Names
$ICM699MCol->filter(function ($value) use ($ICM428MCOL) {
$UserTemp = $ICM428MCOL->firstWhere('R428US', trim($value->SHACKUSER));
$SHACKUSER = isset($UserTemp->R428FIRST) ? $UserTemp->R428FIRST . ' ' . $UserTemp->R428FAMILY : trim($value->SHACKUSER);
return $value->SHACKUSER = $SHACKUSER;
});
# Prepend Header To Excel
$newItem = ["SHMACPKR" => 'Delivery Note',
"DELIVERY_NUM" => 'Order #',
"CUSTOMER" => 'Customer',
"SHDATETIME" => 'Date',
"ERROR" => 'Error',
"SHTYPE" => 'Request Type',
"SHSOURCE" => 'Source of transaction',
"USER" => 'User',
"SHCODE" => 'Request or Response Data',
"SHCO" => 'Division',
"SHWHS" => 'Warehouse',
"ACKDATE" => 'Acknowledge/Retry Date',
"SHACKUSER" => 'Acknowledged/Retried by User',
"SHINTBOX" => 'Internal Box Number',
"SHACKNOTE" => 'Note',
"SHDATA" => 'Error description'
];
$ICM699MCol->prepend($newItem);
// dd($ICM699MCol);
# Download Excel File
return app(\Modules\M3\Services\SpreadsheetExport::class)->download($ICM699MCol, 'transactionLog_' . $STARTDATE . '-' . $ENDDATE . '.xlsx');
} catch (\Throwable $th) {
Log::error($th);
}
}
}
@@ -0,0 +1,219 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
// use Datatables;
use Smalot\PdfParser\Parser;
use File;
use Cache;
use Str;
class M3GetPDFFromCacheController extends Controller
{
public static function index(Request $request){
// Parse PDF file and build necessary objects.
$parser = new \Smalot\PdfParser\Parser();
$aggno = $aggmsg = "";
$IC670M = $PDFArray = $carrier = $trackingno = $ICM688M1 = [];
$AYMACPKR = $WHDDA = $COMPANY = $PLANT = $WHS = '';
#$carrier = $trackingno = "";
# PDF File PATH
$pdfFilePath = Config::get('arrays.PdfPath');
try {
# Initialise Vars
$PDFArray = $trackingno = $carrier = $ICM688M1 = [];
$SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
// dd($SHUSER);
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();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : '';
$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;
// dump($WHDDA);
# 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();
// dump($ICM695M1Agg);
$aggno = isset($ICM695M1Agg->AYTRUCK) ? trim($ICM695M1Agg->AYTRUCK) : '';
if($aggno!="" && $aggno!="0") {
// dump($aggno);
$aggmsg="This delivery note is part of aggregation $aggno ";
$ICM688M1 = DB::connection(config('m3.connection', 'pgsql'))->select("select A4MACPKR from ICM688M1 where A4SHPAGGCD='$aggno'");
#dump($ICM688M1);
// foreach($ICM688M1 as $key => $ICM688M1R) {
// $aggmsg.=' <a href="M3GetPDFFromCache?MACPKR='.$ICM688M1R->A4MACPKR.'&COMPANY='.$COMPANY.'&PLANT='.$PLANT.'&WHS='.$WHS.'">'.$ICM688M1R->A4MACPKR.'</a>';
// }
// $aggmsg.=" )";
#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();
}
#dump($ICM695M1);
$allLabels = [];
if (count($ICM695M1)>0) {
$tmpCacheArr = $carrier = $trackingno = [];
# Loop the Results from $ICM595M1TMP
foreach ($ICM695M1 as $key => $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!=''){
//echo "<script>alert('2');</script>";
$CarrierLabel = '';
$ContentLabel = '';
$PackingList = '';
$cacheKeyPackingList = $cacheKeyCarrierLabel = $cacheKeyContentLabel = '';
# Get Unique Keys
foreach ($docTypes as $key => $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;
// if ($ContentLabel=='LTL' ) {
// $PDFKey = $value.'/'.$value.'_'.$AYMACPKR.'_'.$COMPANY.'_'.$WHS;
// }
// else{
// $PDFKey = $value.'/'.$value.'_'.$AYMACPKR.'_'.$COMPANY.'_'.$WHS.'_'.$DOCNUM;
// }
}
else{
$PDFKey = $value.'/'.$value.'_'.$AYMACPKR.'_'.$DOCNUMT.'_'.$COMPANY.'_'.$WHS;
}
$tmpCacheArr[] = $PDFKey;
}
}
}
// dump($tmpCacheArr);
# Get unique values from array $PDFArray
$tmpCacheArr = collect($tmpCacheArr)->unique();
// dump($tmpCacheArr);
foreach($tmpCacheArr as $key => $value){
// $isCarrierLabel = Str::contains($value, 'CarrierLabel');
$pdfFileName = $pdfFilePath.$value.'.pdf';
if(File::exists($pdfFileName)){
// dump($pdfFileName);
$pdfFile = File::get($pdfFileName);
# Transform content to base64
// $base64PDF = $pdfFile;
$base64PDF = base64_encode($pdfFile);
// dd($base64PDF);
$PDFArray[] = $base64PDF;
}
}
// dump($PDFArray);
}
else {
# Nothing Found
}
}
}
catch (\Throwable $th) {
// Log::error($th);
}
# dump($PDFArray);
# dump($AYMACPKR);
# dump($IC670M);
# dump($ICM695M1);
return view('m3::liberty-theme.pages.M3GetPDF',compact('IC670M','PDFArray','AYMACPKR','WHDDA','aggmsg','carrier','trackingno','ICM688M1','COMPANY','PLANT','WHS'));
// compact('IC670M','ViewTitle')
}
}
@@ -0,0 +1,57 @@
<?php
namespace Modules\M3\Http\Controllers;
use DB;
use Config;
use Symfony\Component\HttpFoundation\Request;
class MFOrdersController extends Controller
{
public static function index(Request $request)
{
$MF = isset($request->id) ? trim($request->id) : "";
// dd("MFOrders ".$MF);
# Get Users from ICM428M
$ICM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428US','R428CO', 'R428WH', 'R428PL')
->where('R428US','=',strtoUpper(m3_user()))
->first();
$user = $ICM428M->R428US;
$co = $ICM428M->R428CO;
$whs = $ICM428M->R428WH;
$plant = $ICM428M->R428PL;
# ICM705M1 Statement
$sqlstmt = "select APMANIFEST,APDOCKDOOR,APCARRIER,APTRUCKIDT,APSPARE1,APLDSTRU,APLDSTRD,APLDSTRT,
APLDCLOU,APLDCLOD,APLDCLOT,APCO,APWHS,APPLT,APSPARE2,APLDCLOZ
from ICM705M1
where APMANIFEST = $MF and APCO='$co' and APWHS='$whs' and APPLT='$plant' limit 1";
$ICM705M1 = DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt);
// dump($ICM705M1);
$carrier = isset($ICM705M1[0]->APCARRIER) ? trim($ICM705M1[0]->APCARRIER) : "";
if ($MF!='') {
$status_ICM688M1_array = Config::get('arrays.status_ICM688M1_array');
$sqlstmt = "SELECT AYCO,AYPLANT,AYMACPKR,AYONO,A4SHSHNM,A4PICKSTS,AYSPR15A01,SUM(AYLNWGHT) AS TOTWEIGHT,COUNT(*) AS BOXPAL FROM ICM695M1
LEFT JOIN ICM688M1 ON AYCO=A4CO AND AYPLANT=A4PLANT AND AYMACPKR=A4MACPKR
WHERE AYAPRF1551= $MF and AYCO='$co' and AYWHS='$whs' and AYPLANT='$plant'
GROUP BY AYCO,AYPLANT,AYMACPKR,AYONO,A4SHSHNM,A4PICKSTS,AYSPR15A01";
$ICM695M1 = DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt);
// dump($ICM695M1);
return view('m3::liberty-theme.pages.MFOrders', compact( 'ICM695M1', 'status_ICM688M1_array', 'MF', 'carrier')
);
}
else{
abort(404, 'Manifest Not Found');
}
}
}
@@ -0,0 +1,346 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Str;
use Log;
use Excel;
use Storage;
class OrderLinesOfClosedOrder extends Controller
{
public static function index(Request $request){
$IC670M = $ICM688M2 = [];
$WHDDA = $COMPANY = $PLANT = $WHS = '';
$REPORTDATEDisp = today()->format('Ymd');
$allReportsUrls = [];
try {
$SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
// dd($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();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; # User
$COMPANY = isset($ICM428M->R428CO) ? $ICM428M->R428CO : '110'; # Company
$PLANT = isset($ICM428M->R428PL) ? $ICM428M->R428PL : '144'; # Plant
$WHS = isset($ICM428M->R428WH) ? $ICM428M->R428WH : '322'; # WareHouse
}
# Warehouse Table
$IC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
# Get URL PARAMS
$urlParams = count($request->all())>0 ? $request->all() : '';
// dump($urlParams);
$REPORTDATEDisp = isset($urlParams["REPORTDATEDisp"]) ? \DateTime::createFromFormat('Y-m-d', trim($urlParams["REPORTDATEDisp"]))->format('Ymd') : today()->format('Ymd');
$WHDDA = $COMPANY.$PLANT.$WHS;
// dump($WHDDA);
if($REPORTDATEDisp!='' && $COMPANY!='' && $WHS!='' && $PLANT!=''){
# Case 729: Add Currency code A1SHCURR FROM ICM666M
$selectContent = "
SELECT A5MACPKR, A5ONO,
(SELECT A4REFORD FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4REFORD,
A5PART, A5RSVQTY, A5PACKED, A5RSVQTY - A5PACKED AS CALCULATED_COLUMN,
A5SLUNPR * A5PACKED AS ANOTHER_CALCULATED_COLUMN,
A5PMPWHT*A5PACKED AS WEIGHT,
(SELECT A4SHSHNM FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4SHSHNM ,
SHDT, SHTIME,
CASE WHEN SHDT IS NULL THEN (SELECT SHDATA FROM ICM699M WHERE SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHSOURCE='CloseShipmentCoChargeAPI' ORDER BY SHDT DESC, SHTIME DESC FETCH FIRST 1 ROWS ONLY) ELSE '' END AS REASON,
A1SHCURR,
CASE
WHEN (SELECT A4SHIPTYPE FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) = 'LTL'
THEN (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceLTL'))/100 ."
ELSE (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceParcel') )/100 ."
END AS FREIGHT,
A4SHIPTYPE,
A4SHIPVIA1,
A4SCAC,
A4OFSTRM
FROM ICM688M2
LEFT JOIN ICM699M ON SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHDATA LIKE 'Success%' AND SHSOURCE='CloseShipmentCoChargeAPI'
LEFT JOIN ICM666M ON A1DELNO=A5MACPKR AND A1ONO=A5ONO AND A1PKLN=A5BRADLN
LEFT JOIN ICM688M1 ON A4MACPKR=A5MACPKR
WHERE A5MACPKR IN (
SELECT DISTINCT AYMACPKR
FROM ICM695M1
WHERE AYSTATUS IN ('Z','z') AND AYAPR8N002 = $REPORTDATEDisp
)
AND A5CO=$COMPANY AND A5WHS=$WHS AND A5PLANT=$PLANT";
// dump($selectContent);
$ICM688M2 = DB::connection(config('m3.connection', 'pgsql'))
->select($selectContent);
// dd($ICM688M2);
}
else{
$ICM688M2 = collect();
}
# Get List of Files
$allReportsUrls = [];
if ($WHS == '321') {
$files = Storage::disk('publicCL')->files();
}
else{
$files = Storage::disk('publicCL')->allFiles($WHS);
}
foreach ($files as $file) {
# Get the file URL (if URL is configured in filesystems.php)
$url = Storage::disk('publicCL')->url($file);
$allReportsUrls[$file] = $url;
}
}
catch (\Throwable $th) {
Log::error($th);
}
return view('m3::liberty-theme.pages.OrderLinesOfClosed',compact('IC670M','ICM688M2','WHDDA','COMPANY','PLANT','WHS','REPORTDATEDisp','allReportsUrls'));
}
public static function exporttoExcel(Request $request){
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'));
ini_set('memory_limit', '1024M');
try {
$SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
// dd($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();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; # User
$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();
# Get URL PARAMS
$urlParams = count($request->all())>0 ? $request->all() : '';
// dd($urlParams);
$reportDate = isset($urlParams["REPORTDATE"]) ? \DateTime::createFromFormat('Y-m-d', trim($urlParams["REPORTDATE"]))->format('Ymd') : today()->format('Ymd');
$toreportDate = isset($urlParams["TOREPORTDATE"]) ? \DateTime::createFromFormat('Y-m-d', trim($urlParams["TOREPORTDATE"]))->format('Ymd') : today()->format('Ymd');
$reportDateWD = isset($urlParams["REPORTDATE"]) ? trim($urlParams["REPORTDATE"]) : today()->format('Y-m-d');
$toreportDateWD = isset($urlParams["TOREPORTDATE"]) ? trim($urlParams["TOREPORTDATE"]) : today()->format('Y-m-d');
$WHDDA = $COMPANY.$PLANT.$WHS;
// dump($WHDDA);
if($reportDate!='' && $COMPANY!='' && $WHS!='' && $PLANT!=''){
# Case 729: Add Currency code A1SHCURR FROM ICM666M
$selectContent = "
SELECT (SHDT || ' ' || SHTIME) AS DATECLOSED, A5MACPKR, A5ONO,
A4OFSTRM,
A5PMPWHT*A5PACKED AS WEIGHT,
A4SCAC,
A4SHIPTYPE,
(SELECT A4REFORD FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4REFORD,
A5PART, A5RSVQTY, A5PACKED, A5RSVQTY - A5PACKED AS CALCULATED_COLUMN,
A5SLUNPR * A5PACKED AS ANOTHER_CALCULATED_COLUMN,
(SELECT A4SHSHNM FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4SHSHNM ,
CASE WHEN SHDT IS NULL THEN (SELECT SHDATA FROM ICM699M WHERE SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHSOURCE='CloseShipmentCoChargeAPI' ORDER BY SHDT DESC, SHTIME DESC FETCH FIRST 1 ROWS ONLY) ELSE '' END AS REASON,
A1SHCURR,
CASE
WHEN (SELECT A4SHIPTYPE FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) = 'LTL'
THEN (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceLTL'))/100 ."
ELSE (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceParcel'))/100 ."
END AS FREIGHT
FROM ICM688M2
LEFT JOIN ICM699M ON SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHDATA LIKE 'Success%' AND SHSOURCE='CloseShipmentCoChargeAPI'
LEFT JOIN ICM666M ON A1DELNO=A5MACPKR AND A1ONO=A5ONO AND A1PKLN=A5BRADLN
LEFT JOIN ICM688M1 ON A4MACPKR=A5MACPKR
WHERE A5MACPKR IN (
SELECT DISTINCT AYMACPKR
FROM ICM695M1
WHERE AYSTATUS IN ('Z','z') AND AYAPR8N002 BETWEEN $reportDate AND $toreportDate
)
AND A5CO=$COMPANY AND A5WHS=$WHS AND A5PLANT=$PLANT";
$ICM688M2 = DB::connection(config('m3.connection', 'pgsql'))
->select($selectContent);
$newItem = ["DATECLOSED" => 'Close Date',"A5MACPKR" => 'Delivery #', "A5ONO" => 'Order #',
"A4OFSTRM" => 'Terms',"WEIGHT" => 'Weight',"A4SCAC" => 'Carrier',"A4SHIPTYPE" => 'Mode (Parcel, Air, TL or LTL)',
"A4REFORD" => 'Order Type',"A5PART" => 'Part #',"A5RSVQTY" => 'Reserved',
"A5PACKED" => 'Packed',"CALCULATED_COLUMN" => 'Back Order Qty',"ANOTHER_CALCULATED_COLUMN" => 'Value Packed $',"A4SHSHNM" => 'Customer name',
"REASON" => 'Failure Reason','A1SHCURR'=> 'Currency Code','FREIGHT'=> 'Freight'];
$ICM688M2Col = collect($ICM688M2);
$ICM688M2Col->prepend($newItem);
// dd($ICM688M2Col);
# Count records before exporting to excel : Max 20K records are allowed
$ICM688M2Count = $ICM688M2Col->count();
# Set Max allowed records
$maxRecords = Config::get('arrays.maxRecCL');
if ($ICM688M2Count>$maxRecords) {
return redirect()->back()->withInput()->with('message', 'Maximum allowed records for Excel Download is: '.number_format($maxRecords).
' current records: '.number_format($ICM688M2Count) . ' , change your search criteria.');
}
}
else{
$ICM688M2Col = "";
}
# Download Excel
# Add true at the end to include fields names in the Header
return app(\Modules\M3\Services\SpreadsheetExport::class)->download($ICM688M2Col, 'Closedorderlinesreport_'.$reportDateWD.'-'.$toreportDateWD.'.xlsx');
// return $ICM688M2Col->downloadExcel('Closedorderlinesreport_'.$reportDateWD.'-'.$toreportDateWD.'.csv',\Maatwebsite\Excel\Excel::CSV,false);
}
catch (\Throwable $th) {
Log::error($th);
}
}
public static function StoreExcelToDisk(Request $request){
ini_set('max_execution_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('max_input_time', Config::get('arrays.PHPTimeOutCL'));
ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOutCL'));
ini_set('memory_limit', '1024M');
try {
$SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
// dd($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();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; # User
$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();
# Get URL PARAMS
$urlParams = count($request->all())>0 ? $request->all() : '';
// dd($urlParams);
$reportDate = isset($urlParams["REPORTDATE"]) ? \DateTime::createFromFormat('Y-m-d', trim($urlParams["REPORTDATE"]))->format('Ymd') : today()->format('Ymd');
$toreportDate = isset($urlParams["TOREPORTDATE"]) ? \DateTime::createFromFormat('Y-m-d', trim($urlParams["TOREPORTDATE"]))->format('Ymd') : today()->format('Ymd');
// dump($reportDate);
// dd($toreportDate);
$reportDateWD = isset($urlParams["REPORTDATE"]) ? trim($urlParams["REPORTDATE"]) : today()->format('Y-m-d');
$toreportDateWD = isset($urlParams["TOREPORTDATE"]) ? trim($urlParams["TOREPORTDATE"]) : today()->format('Y-m-d');
$WHDDA = $COMPANY.$PLANT.$WHS;
// dump($WHDDA);
if($reportDate!='' && $COMPANY!='' && $WHS!='' && $PLANT!=''){
# Case 729: Add Currency code A1SHCURR FROM ICM666M
$ICM688M2 = DB::connection(config('m3.connection', 'pgsql'))
->select("
SELECT (SHDT || ' ' || SHTIME) AS DATECLOSED, A5MACPKR, A5ONO,
A4OFSTRM,
A5PMPWHT*A5PACKED AS WEIGHT,
A4SCAC,
A4SHIPTYPE,
(SELECT A4REFORD FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4REFORD,
A5PART, A5RSVQTY, A5PACKED, A5RSVQTY - A5PACKED AS CALCULATED_COLUMN,
A5SLUNPR * A5PACKED AS ANOTHER_CALCULATED_COLUMN,
(SELECT A4SHSHNM FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4SHSHNM ,
CASE WHEN SHDT IS NULL THEN (SELECT SHDATA FROM ICM699M WHERE SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHSOURCE='CloseShipmentCoChargeAPI' ORDER BY SHDT DESC, SHTIME DESC FETCH FIRST 1 ROWS ONLY) ELSE '' END AS REASON,
A1SHCURR,
CASE
WHEN (SELECT A4SHIPTYPE FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) = 'LTL'
THEN (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceLTL'))/100 ."
ELSE (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceParcel'))/100 ."
END AS FREIGHT
FROM ICM688M2
LEFT JOIN ICM699M ON SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHDATA LIKE 'Success%' AND SHSOURCE='CloseShipmentCoChargeAPI'
LEFT JOIN ICM666M ON A1DELNO=A5MACPKR AND A1ONO=A5ONO AND A1PKLN=A5BRADLN
LEFT JOIN ICM688M1 ON A4MACPKR=A5MACPKR
WHERE A5MACPKR IN (
SELECT DISTINCT AYMACPKR
FROM ICM695M1
WHERE AYSTATUS IN ('Z','z') AND AYAPR8N002 BETWEEN $reportDate AND $toreportDate
)
AND A5CO=$COMPANY AND A5WHS=$WHS AND A5PLANT=$PLANT
");
$newItem = ["DATECLOSED" => 'Close Date',"A5MACPKR" => 'Delivery #', "A5ONO" => 'Order #',
"A4OFSTRM" => 'Terms',"WEIGHT" => 'Weight',"A4SCAC" => 'Carrier',"A4SHIPTYPE" => 'Mode (Parcel, Air, TL or LTL)',
"A4REFORD" => 'Order Type',"A5PART" => 'Part #',"A5RSVQTY" => 'Reserved',
"A5PACKED" => 'Packed',"CALCULATED_COLUMN" => 'Back Order Qty',"ANOTHER_CALCULATED_COLUMN" => 'Value Packed $',"A4SHSHNM" => 'Customer name',
"REASON" => 'Failure Reason','A1SHCURR'=> 'Currency Code','FREIGHT'=> 'Freight'];
$ICM688M2Col = collect($ICM688M2);
$ICM688M2Col->prepend($newItem);
// dd($ICM688M2Col);
# Count records before exporting to excel : Max 20K records are allowed
$ICM688M2Count = $ICM688M2Col->count();
# Set Max allowed records
$maxRecords = Config::get('arrays.maxRecCL');
if ($ICM688M2Count>$maxRecords) {
return redirect()->back()->withInput()->with('message', 'Maximum allowed records for Excel Download is: '.number_format($maxRecords).
' current records: '.number_format($ICM688M2Count) . ' , change your search criteria.');
}
}
else{
$ICM688M2Col = "";
}
if ($WHS == '321') {
$prevMonthFile = 'Closedorderlinesreport_'.$reportDateWD.'-'.$toreportDateWD.'.xlsx';
}
else{
$prevMonthFile = $WHS.'/Closedorderlinesreport_'.$reportDateWD.'-'.$toreportDateWD.'-'.$WHS.'.xlsx';
}
# MAK Store the File to Storage
app(\Modules\M3\Services\SpreadsheetExport::class)->store($ICM688M2Col, $prevMonthFile, 'publicCL');
// $ICM688M2Col->storeExcel('Closedorderlinesreport_'.$reportDateWD.'-'.$toreportDateWD.'.csv', 'publicCL',\Maatwebsite\Excel\Excel::CSV);
// $ICM688M2Col->storeExcel('Closedorderlinesreport_'.$reportDateWD.'-'.$toreportDateWD.'.ods', 'publicCL',\Maatwebsite\Excel\Excel::ODS);
// dump('Done Saving Excel...');
return back();
}
catch (\Throwable $th) {
Log::error($th);
}
}
# Download File
public static function downloadFiles(Request $request){
// dd($request->all());
$fileName = $request->filename;
if (Storage::disk('publicCL')->exists($fileName)) {
# File exists, retrieve content or URL
// $content = Storage::disk('publicCL')->get($fileName);
$fileUrl = Storage::disk('publicCL')->url($fileName);
// dd($fileUrl);
$fileNameOnly = strpos($fileName, '/')!= false ? substr($fileName, strpos($fileName, '/') + 1) : $fileName;
# Download the file
return Storage::disk('publicCL')->download($fileName, $fileNameOnly);
} else {
// Download the file (use code below)
}
}
}
@@ -0,0 +1,198 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Modules\M3\Models\ICM658M;
use Datatables;
class ReplenishSettingsController extends Controller
{
public static function index(Request $request)
{
# Replenishment Table
// $REFCAT658 = DB::connection(config('m3.connection', 'pgsql'))->table('icm658m')->get();
# Warehouse Table
// $IC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
# Get Logged user Id
$userId = m3_user() !== '' ? strtoupper(m3_user()) : '' ;
// $userId = 'E1135818'; // Brad : E1135818 Malak : E2011812 ACOM_PDF
$connection = config('m3.connection', 'pgsql');
$ICM428M = \Illuminate\Support\Facades\Schema::connection($connection)->hasTable('icm428m')
? DB::connection($connection)->table('icm428m')->where('R428US',$userId)->where('R428STS','A')->first()
: null;
$userCO = isset($ICM428M->R428CO) ? $ICM428M->R428CO : '';
$userPL = isset($ICM428M->R428PL) ? $ICM428M->R428PL : '';
$userWH = isset($ICM428M->R428WH) ? $ICM428M->R428WH : '' ;
if ($userCO!='') {
# Warehouse Table
$IC670M = \Illuminate\Support\Facades\Schema::connection($connection)->hasTable('ic670m')
? DB::connection($connection)->table('ic670m')
->where('WDCO',$userCO)
->where('WDPL',$userPL)
->where('WDWHS',$userWH)
->get()
: [];
}
else{
// $REFCAT658 =[];
$IC670M =[];
}
// $sqlstmt="SELECT t1.REFKEY,t2.FULLID ,t2.*
// FROM PRDAT.REFERPS t1 LEFT JOIN ( SELECT R658CO,R658PL,R658WH,R658LC,R658PN,R658MQ,R658RQ,R658TL,R658RMQ,
// TRIM(R658WH)||TRIM(R658LC)||TRIM(R658PN) AS FULLID FROM EWOBJTST.ICM658M
// ) t2 ON t1.REFKEY = t2.FULLID WHERE t1.REFCAT = '658'";
// $REFCAT658 = DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt);
// dd($REFCAT658);
return view('m3::liberty-theme.pages.ReplenishSettings', compact('IC670M'));
// 'REFCAT658',
}
public function store(Request $request)
{
// dd( $request->all() );
$formInputs = $request->all();
$formInputs = array_filter($formInputs);
unset($formInputs["_token"]);
$formInputs['R658IZLCF'] = isset($formInputs['R658IZLCF']) ? $formInputs['R658IZLCF'] : '0';
// dd($formInputs);
$ICM658MINS = ICM658M::insert($formInputs);
// dd($ICM658MINS);
return redirect()->route('m3.replenishmentSettings.index');
}
public function update(Request $request)
{
// dd($request->all());
$R658PN = (trim($request->R658PN) ?? trim($request->R658PN)) != null ? trim($request->R658PN) : "";
$R658CO = (trim($request->R658CO) ?? trim($request->R658CO)) != null ? trim($request->R658CO) : "";
$R658PL = (trim($request->R658PL) ?? trim($request->R658PL)) != null ? trim($request->R658PL) : "";
$R658WH = (trim($request->R658WH) ?? trim($request->R658WH)) != null ? trim($request->R658WH) : "";
$R658MQ = trim($request->R658MQ);
$R658RQ = trim($request->R658RQ);
$R658RMQ = trim($request->R658RMQ);
$R658TL = trim($request->R658TL);
$R658IZLCF = (trim($request->R658IZLCF) ?? trim($request->R658IZLCF)) != null ? trim($request->R658IZLCF) : "0";
DB::connection(config('m3.connection', 'pgsql'))->table('icm658m')
->where('R658PN', $R658PN)
->where('R658CO', $R658CO)
->where('R658PL', $R658PL)
->where('R658WH', $R658WH)
->update([ 'R658MQ'=>$R658MQ,'R658RQ'=>$R658RQ,'R658RMQ'=>$R658RMQ,'R658TL'=>$R658TL,'R658IZLCF'=>$R658IZLCF ]);
return redirect()->route('m3.replenishmentSettings.index');
}
public function deleteRec(Request $request)
{
// dd( $request->all() );
$R658PN = trim($request->R658PN);
$R658CO = trim($request->R658CO);
$R658LC = trim($request->R658LC);
$R658PL = trim($request->R658PL);
$R658WH = trim($request->R658WH);
$deleteRecord = DB::connection(config('m3.connection', 'pgsql'))->table('icm658m')
->where('R658PN', $R658PN)
->where('R658CO', $R658CO)
->where('R658LC', $R658LC)
->where('R658PL', $R658PL)
->where('R658WH', $R658WH)
->delete();
return response()->json(['success'=>'Record Deleted']);
}
public function checkRecExit(Request $request)
{
// dd( $request->all() );
$params = array();
parse_str($request['data'], $params);
$paramsObj = (object)$params;
// dd($paramsObj);
$R658PN = trim($paramsObj->R658PN);
$R658CO = trim($paramsObj->R658CO);
$R658LC = trim($paramsObj->R658LC);
$R658PL = trim($paramsObj->R658PL);
$R658WH = trim($paramsObj->R658WH);
$recordExistSQL = DB::connection(config('m3.connection', 'pgsql'))->table('icm658m')
->where('R658PN', $R658PN)
->where('R658CO', $R658CO)
->where('R658LC', $R658LC)
->where('R658PL', $R658PL)
->where('R658WH', $R658WH)
->first();
// dd($recordExists);
$recordExists = $recordExistSQL == null ? true : false;
return response()->json(['recordExists'=>$recordExists]);
}
public function datatables(Request $request){
# Replenishment Table
// $REFCAT658 = DB::connection(config('m3.connection', 'pgsql'))->table('icm658m')->get();
# Warehouse Table
// $IC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
# Get Logged user Id
$userId = m3_user() !== '' ? strtoupper(m3_user()) : '' ;
// $userId = 'E1135818'; // Brad : E1135818 Malak : E2011812 ACOM_PDF
$connection = config('m3.connection', 'pgsql');
$ICM428M = \Illuminate\Support\Facades\Schema::connection($connection)->hasTable('icm428m')
? DB::connection($connection)->table('icm428m')->where('R428US',$userId)->where('R428STS','A')->first()
: null;
$userCO = isset($ICM428M->R428CO) ? $ICM428M->R428CO : '';
$userPL = isset($ICM428M->R428PL) ? $ICM428M->R428PL : '';
$userWH = isset($ICM428M->R428WH) ? $ICM428M->R428WH : '' ;
if ($userCO!='') {
# Replenishment Table
$REFCAT658 = DB::connection(config('m3.connection', 'pgsql'))->table('icm658m')
->select('*')
->where('R658CO',$userCO)
->where('R658PL',$userPL)
->where('R658WH',$userWH)
->orderBy('R658CO','asc');
# Warehouse Table
// $IC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')
// ->where('WDCO',$userCO)
// ->where('WDPL',$userPL)
// ->where('WDWHS',$userWH)
// ->get();
}
else{
$REFCAT658 =[];
// $IC670M =[];
}
// $sqlstmt="SELECT t1.REFKEY,t2.FULLID ,t2.*
// FROM PRDAT.REFERPS t1 LEFT JOIN ( SELECT R658CO,R658PL,R658WH,R658LC,R658PN,R658MQ,R658RQ,R658TL,R658RMQ,
// TRIM(R658WH)||TRIM(R658LC)||TRIM(R658PN) AS FULLID FROM EWOBJTST.ICM658M
// ) t2 ON t1.REFKEY = t2.FULLID WHERE t1.REFCAT = '658'";
// $REFCAT658 = DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt);
// dd($REFCAT658);
// dd( Datatables::of($REFCAT658)->addIndexColumn()->make(true) );
return Datatables::of($REFCAT658)->addIndexColumn()->make(true);
}
}
@@ -0,0 +1,187 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Models\ShipTo;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Datatables;
class ShipToController extends Controller
{
public function index(Request $request)
{
if ($request->ajax()) {
$data = ShipTo::select('*');
return Datatables::of($data)
->addIndexColumn()
->addColumn('checkbox', function ($row) {
return '';
})
->addColumn('action', function ($row) {
$actionBtn = '<a href="' . url('/ship-to/' . trim($row->AUSHSHZP) . '/' . trim($row->AUSHWHS) . '/edit') . '" class="edit btn btn-primary btn-sm">Edit</a> ';
$actionBtn .= '<form method="POST" action="' . url('ship-to/' . urlencode(trim($row->AUSHSHZP)) . '/' . urlencode(trim($row->AUSHWHS))) . '" style="display:inline">';
$actionBtn .= csrf_field() . method_field('DELETE');
$actionBtn .= '<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm(\'Are you sure?\')">Delete</button></form>';
return $actionBtn;
})
->rawColumns(['checkbox', 'action'])
->make(true);
}
return view('m3::liberty-theme.pages.ship-to.index');
}
public function create()
{
# US
$USstates = \Modules\M3\Helpers\USStates::all();
# CA
$CAstates = \Modules\M3\Helpers\CAProvinces::all();
# All States
$states = array_merge($USstates, $CAstates);
return view('m3::liberty-theme.pages.ship-to.create', compact('states'));
}
public function store(Request $request)
{
$validated = $request->validate(
[
'AUSTCN' => 'nullable|max:11',
'AUSHSHNM' => 'required|max:36',
'AUSHSHA1' => 'required|max:36',
'AUSHSHA2' => 'required|max:36',
'AUSHSHA3' => 'nullable|max:36',
'AUSHSHA4' => 'nullable|max:36',
'AUSHSHCI' => 'required|max:20',
'AUSHSHST' => 'required|max:2',
'AUSHSHZP' => 'required|max:10',
'AUSHSHCT' => 'required|max:3',
'AUSHWHS' => 'required|max:3'
],
[
'AUSHSHNM.required' => 'Name is required',
'AUSHSHA1.required' => 'Address 1 is required',
'AUSHSHA2.required' => 'Address 2 is required',
'AUSHSHCI.required' => 'City is required',
'AUSHSHST.required' => 'State is required',
'AUSHSHZP.required' => 'Zip code is required',
'AUSHSHCT.required' => 'Country is required',
'AUSHWHS.required' => 'Warehouse is required',
'AUSTCN.max' => 'Customer Number cannot exceed 11 characters',
'AUSHSHNM.max' => 'Name cannot exceed 36 characters',
'AUSHSHA1.max' => 'Address 1 cannot exceed 36 characters',
'AUSHSHA2.max' => 'Address 2 cannot exceed 36 characters',
'AUSHSHA3.max' => 'Address 3 cannot exceed 36 characters',
'AUSHSHA4.max' => 'Address 4 cannot exceed 36 characters',
'AUSHSHCI.max' => 'City cannot exceed 20 characters',
'AUSHSHST.max' => 'State cannot exceed 2 characters',
'AUSHSHZP.max' => 'Zip code cannot exceed 10 characters',
'AUSHSHCT.max' => 'Country cannot exceed 3 characters',
'AUSHWHS.max' => 'Warehouse cannot exceed 3 characters'
]
);
$validated['AUSTCN'] = $validated['AUSTCN'] ?? ' ';
$model = new ShipTo();
$model->fill($validated);
$success = $model->save();
// ShipTo::create($validated);
if ($success) {
return redirect()->route('m3.ship-to.index')->with('success', 'Record created successfully.');
} else {
return redirect()->back()
->withErrors(['unique' => 'The combination of ZipCode and Warehouse must be unique.'])
->withInput();
}
}
public function edit($aushshzp, $aushwhs)
{
# US
$USstates = \Modules\M3\Helpers\USStates::all();
# CA
$CAstates = \Modules\M3\Helpers\CAProvinces::all();
# All States
$states = array_merge($USstates, $CAstates);
$shipTo = ShipTo::where('AUSHSHZP', $aushshzp)
->where('AUSHWHS', $aushwhs)
->firstOrFail();
return view('m3::liberty-theme.pages.ship-to.edit', compact('shipTo', 'states'));
}
public function update(Request $request, $aushshzp, $aushwhs)
{
$validated = array_map(function ($value) {
return $value === null ? '' : $value;
}, $request->validate(
[
'AUSTCN' => 'nullable|string|max:11',
'AUSHSHNM' => 'required|max:36',
'AUSHSHA1' => 'required|max:36',
'AUSHSHA2' => 'required|max:36',
'AUSHSHA3' => 'nullable|string|max:36',
'AUSHSHA4' => 'nullable|string|max:36',
'AUSHSHCI' => 'required|max:20',
'AUSHSHST' => 'required|max:2',
'AUSHSHCT' => 'required|max:3'
],
[
'AUSHSHNM.required' => 'Name is required',
'AUSHSHA1.required' => 'Address 1 is required',
'AUSHSHA2.required' => 'Address 2 is required',
'AUSHSHCI.required' => 'City is required',
'AUSHSHST.required' => 'State is required',
'AUSHSHZP.required' => 'Zip code is required',
'AUSHSHCT.required' => 'Country is required',
'AUSHWHS.required' => 'Warehouse is required',
'AUSTCN.max' => 'Customer Number cannot exceed 11 characters',
'AUSHSHNM.max' => 'Name cannot exceed 36 characters',
'AUSHSHA1.max' => 'Address 1 cannot exceed 36 characters',
'AUSHSHA2.max' => 'Address 2 cannot exceed 36 characters',
'AUSHSHA3.max' => 'Address 3 cannot exceed 36 characters',
'AUSHSHA4.max' => 'Address 4 cannot exceed 36 characters',
'AUSHSHCI.max' => 'City cannot exceed 20 characters',
'AUSHSHST.max' => 'State cannot exceed 2 characters',
'AUSHSHZP.max' => 'Zip code cannot exceed 10 characters',
'AUSHSHCT.max' => 'Country cannot exceed 3 characters',
'AUSHWHS.max' => 'Warehouse cannot exceed 3 characters'
]
));
$updated = ShipTo::where('AUSHSHZP', $aushshzp)
->where('AUSHWHS', $aushwhs)
->update($validated);
if ($updated) {
return redirect()->route('m3.ship-to.index')->with('success', 'Record updated successfully.');
} else {
return redirect()->route('m3.ship-to.index')->with('error', 'Record could not be updated.');
}
}
public function destroy($aushshzp, $aushwhs)
{
$deleted = ShipTo::where('AUSHSHZP', $aushshzp)
->where('AUSHWHS', $aushwhs)
->delete();
if ($deleted) {
return redirect()->route('m3.ship-to.index')->with('success', 'Record deleted successfully.');
} else {
return redirect()->route('m3.ship-to.index')->with('error', 'Record not found or could not be deleted.');
}
}
}
@@ -0,0 +1,115 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Str;
use Log;
use Excel;
use Storage;
class WhsPerfMetrics extends Controller
{
public static function index(Request $request){
try {
$SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : '';
// dd($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();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; # User
$COMPANY = isset($ICM428M->R428CO) ? $ICM428M->R428CO : '110'; # Company
$PLANT = isset($ICM428M->R428PL) ? $ICM428M->R428PL : '144'; # Plant
$WHS = isset($ICM428M->R428WH) ? $ICM428M->R428WH : '322'; # WareHouse
}
# Warehouse Table
$IC670M = DB::connection(config('m3.connection', 'pgsql'))->table('ic670m')->get();
# Get URL PARAMS
$urlParams = count($request->all())>0 ? $request->all() : '';
// dump($urlParams);
$REPORTDATEDisp = isset($urlParams["REPORTDATEDisp"]) ? \DateTime::createFromFormat('Y-m-d', trim($urlParams["REPORTDATEDisp"]))->format('Ymd') : today()->format('Ymd');
$WHDDA = $COMPANY.$PLANT.$WHS;
// dump($WHDDA);
if($REPORTDATEDisp!='' && $COMPANY!='' && $WHS!='' && $PLANT!=''){
# Case 729: Add Currency code A1SHCURR FROM ICM666M
$selectContent = "
SELECT A5MACPKR, A5ONO,
(SELECT A4REFORD FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4REFORD,
A5PART, A5RSVQTY, A5PACKED, A5RSVQTY - A5PACKED AS CALCULATED_COLUMN,
A5SLUNPR * A5PACKED AS ANOTHER_CALCULATED_COLUMN,
(SELECT A4SHSHNM FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) AS A4SHSHNM ,
SHDT, SHTIME,
CASE WHEN SHDT IS NULL THEN (SELECT SHDATA FROM ICM699M WHERE SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHSOURCE='CloseShipmentCoChargeAPI' ORDER BY SHDT DESC, SHTIME DESC FETCH FIRST 1 ROWS ONLY) ELSE '' END AS REASON,
A1SHCURR,
CASE
WHEN (SELECT A4SHIPTYPE FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) = 'LTL'
THEN (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceLTL'))/100 ."
ELSE (SELECT A4RATETCP FROM ICM688M1 WHERE A4MACPKR=A5MACPKR) * ".(100.00 - Config::get('arrays.misc.discountCarrierRateForCustomerPriceParcel') )/100 ."
END AS FREIGHT
FROM ICM688M2
LEFT JOIN ICM699M ON SHMACPKR=A5MACPKR AND SHTYPE='M3-Res-out' AND SHDATA LIKE 'Success%' AND SHSOURCE='CloseShipmentCoChargeAPI'
LEFT JOIN ICM666M ON A1DELNO=A5MACPKR AND A1ONO=A5ONO AND A1PKLN=A5BRADLN
WHERE A5MACPKR IN (
SELECT DISTINCT AYMACPKR
FROM ICM695M1
WHERE AYSTATUS IN ('Z','z') AND AYAPR8N002 = $REPORTDATEDisp
)
AND A5CO=$COMPANY AND A5WHS=$WHS AND A5PLANT=$PLANT";
// dump($selectContent);
$ICM688M2 = DB::connection(config('m3.connection', 'pgsql'))
->select($selectContent);
// dd($ICM688M2);
}
else{
$ICM688M2 = collect();
}
# Get List of Files
$allReportsUrls = [];
$files = Storage::disk('publicLabels')->allFiles('WhsPerf/'.$WHS);
// $whsPerfFile = 'WhsPerf/'.$WHS.'/WhsPerfMetrics_'.$DateToday.'-'.$WHS.'.xlsx';
foreach ($files as $file) {
# Get the file URL (if URL is configured in filesystems.php)
$url = Storage::disk('publicLabels')->url($file);
$allReportsUrls[$file] = $url;
}
// dd($allReportsUrls);
}
catch (\Throwable $th) {
Log::error($th);
}
return view('m3::liberty-theme.pages.WhsPerfMetrics',compact('IC670M','WHDDA','COMPANY','PLANT','WHS','allReportsUrls'));
}
# Download File
public static function downloadFiles(Request $request){
// dd($request->all());
$fileName = $request->filename;
if (Storage::disk('publicLabels')->exists($fileName)) {
# File exists, retrieve content or URL
$fileUrl = Storage::disk('publicLabels')->url($fileName);
$fileNameOnly = basename($fileName);
# Download the file
return Storage::disk('publicLabels')->download($fileName, $fileNameOnly);
} else {
// Download the file (use code below)
}
}
}
@@ -0,0 +1,12 @@
<?php
namespace Modules\M3\Http\Controllers;
class authController extends Controller
{
public static function logout(){
return view('m3::liberty-theme.pages.logout');
}
}
@@ -0,0 +1,105 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Modules\M3\Models\ICM160M;
use Modules\M3\Helpers\Helper;
class carrierMaintController extends Controller
{
public static function index(Request $request) {
$currentUser = Helper::getUserCOWHSPLT();
$UserCO = $currentUser['UserCO'] ?? '';
$ICM160M = [];
$power_user = [];
$schema = \Illuminate\Support\Facades\Schema::connection(config('m3.connection', 'pgsql'));
if ($schema->hasTable('icm160m')) {
$ICM160M = DB::connection(config('m3.connection', 'pgsql'))->select("
select canum, canam, substr(CATPID,1,3) as TwoshipCarrierCode, substr(CATPID,4,1) as ParcelCarrierCode, caspr10A01, caspr1A01
from icm160m WHERE CACO=?", [$UserCO]);
}
if ($schema->hasTable('icm428m')) {
$power_user = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m')
->select('R428PWRUSR', 'R428CO', 'R428PL', 'R428WH')
->where('R428US', strtoUpper(m3_user()))->get()->all();
}
return view('m3::liberty-theme.pages.carrierMaint', compact('ICM160M','power_user'));
}
# Process data from Add modal
public static function savecarrierAdd(Request $request) {
$currentUser = Helper::getUserCOWHSPLT();
$UserCO = $currentUser['UserCO'] ?? '';
// $UserWHS = $currentUser['UserWHS'] ?? '';
// $UserPL = $currentUser['UserPL'] ?? '';
# Insert record to ICM160M
# Verify record does not already exist in ICM160M
if($request->CANUM == NULL) {
return "null";
}
$icm160m=DB::connection(config('m3.connection', 'pgsql'))->select("select count(*) as COUNT1 from ICM160M where CANUM='$request->CANUM' AND CACO='$UserCO'");
if($icm160m[0]->COUNT1=="1") {
return "exists";
} else {
$CASPR10A01 = isset($request->CASPR10A01) && $request->CASPR10A01 != NULL ? $request->CASPR10A01 : ' ';
$CASPR1A01 = isset($request->CASPR1A01) && $request->CASPR1A01 != NULL ? $request->CASPR1A01 : ' ';
$data=['CANUM'=>$request->CANUM, 'CANAM'=>$request->CANAM,
'CATPID'=>str_pad(substr($request->TWOSHIPCARRIERCODE,0,3),3," ").substr($request->PARCELCARRIERCODE,0,1),
'CASPR10A01'=>$CASPR10A01, 'CASPR1A01'=>$CASPR1A01,'CACO'=>$UserCO]; // ,'CAPLNT'=>$UserPL,'CAWHS'=>$UserWHS
$insertSQL = DB::connection(config('m3.connection', 'pgsql'))->table('icm160m')->insert($data);
if($insertSQL) {
return "success";
}
}
}
public static function savecarrierAdd2(Request $request) {
$currentUser = Helper::getUserCOWHSPLT();
$UserCO = $currentUser['UserCO'] ?? '';
// $UserWHS = $currentUser['UserWHS'] ?? '';
// $UserPL = $currentUser['UserPL'] ?? '';
# Insert record to ICM160M
if($request->CANUM == NULL) {
return "null";
}
$CASPR10A01 = isset($request->CASPR10A01) && $request->CASPR10A01 != NULL ? $request->CASPR10A01 : ' ';
$CASPR1A01 = isset($request->CASPR1A01) && $request->CASPR1A01 != NULL ? $request->CASPR1A01 : ' ';
$data=['CANUM'=>$request->CANUM, 'CANAM'=>$request->CANAM, 'CATPID'=>str_pad(substr($request->TWOSHIPCARRIERCODE,0,3),3," ").substr($request->PARCELCARRIERCODE,0,1),
'CASPR10A01'=>$CASPR10A01, 'CASPR1A01'=>$CASPR1A01,'CACO'=>$UserCO]; // ,'CAPLNT'=>$UserPL,'CAWHS'=>$UserWHS
$insertSQL = DB::connection(config('m3.connection', 'pgsql'))->table('icm160m')->insert($data);
if($insertSQL) {
return "success";
}
}
# Process data from Edit modal
public static function savecarrierEdit(Request $request) {
$currentUser = Helper::getUserCOWHSPLT();
$UserCO = $currentUser['UserCO'] ?? '';
if($request->CANUM == NULL) {
return "null";
}
DB::connection(config('m3.connection', 'pgsql'))->update("update ICM160M
set CANAM='$request->CANAM', CATPID='".str_pad(substr($request->TWOSHIPCARRIERCODE,0,3),3," ").substr($request->PARCELCARRIERCODE,0,1)."',
CASPR10A01='$request->CASPR10A01', CASPR1A01='$request->CASPR1A01'
where CANUM='$request->CANUM' AND CACO='$UserCO'");
return "success";
}
}
@@ -0,0 +1,13 @@
<?php
namespace Modules\M3\Http\Controllers;
use DB;
use Symfony\Component\HttpFoundation\Request;
class dashboardController extends Controller
{
public static function index(){
return view('m3::liberty-theme.pages.dashboard');
// , compact('data','whs','whs_array','starting','ending')
}
}
@@ -0,0 +1,176 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Str;
use Log;
class execSummaryController extends Controller
{
public static function index(Request $request){
$IC670M = collect();
$WHDDA = '';
$COMPANY = '110';
$PLANT = '144';
$WHS = '322';
try {
# Ajax Call
if ($request->ajax()) {
// dump($request->all());
$WHDDA = isset($request->WHDDA) ? $request->WHDDA : array();
$WHArr = array();
foreach ($WHDDA as $key => $WHDDAV) {
$CO = substr($WHDDAV, 0, 3);
// $R428PL = substr($WHDDAV, 3, 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(count($WHArr)>0 && $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."' and a5whs in (".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."' and a5whs in (".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');
// $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."' and a5whs in (".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;
}
// dump($reportDate);
// dump($toreportDate);
// dump($selectContent);
$execSummaryRes = DB::connection(config('m3.connection', 'pgsql'))->select($selectContent);
}
else{
$execSummaryRes = collect();
}
// dump($execSummaryRes);
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())) : '';
// dd($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();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; # User
$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',compact('IC670M','WHDDA'));
// ,'ICM688M2',,'COMPANY','PLANT','WHS','reportDate'
}
}
@@ -0,0 +1,205 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Str;
use Log;
class execSummaryLineController extends Controller
{
public static function index(Request $request){
try {
# Ajax Call
if ($request->ajax()) {
// dump($request->all());
$WHDDA = isset($request->WHDDA) ? $request->WHDDA : array();
$WHArr = array();
foreach ($WHDDA as $key => $WHDDAV) {
$CO = substr($WHDDAV, 0, 3);
// $R428PL = substr($WHDDAV, 3, 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(count($WHArr)>0 && $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."' and a5whs in (".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('first day of this month')->format('Ymd'); //
$toreportDate = $lastDay ? $lastDay->modify('last day of this month')->format('Ymd') : '';
# Last Year Dates
// Same period, but for Last Year (LY)
$reportDateLY = (clone $firstDay)->modify('-1 year')->modify('first day of this month')->format('Ymd');
$toreportDateLY = (clone $lastDay)->modify('-1 year')->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."' and a5whs in (".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."' and a5whs in (".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('first day of this month')->format('Ymd');
$toreportDate = $firstDay->modify('last day of this month')->format('Ymd');
# Last Year Dates
$reportDateLY = (clone $firstDay)->modify('-1 year')->modify('first day of this month')->format('Ymd');
$toreportDateLY = (clone $firstDay)->modify('-1 year')->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."' and a5whs in (".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."' and a5whs in (".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())) : '';
// dd($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();
$R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; # User
$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',compact('IC670M','WHDDA'));
// ,'ICM688M2',,'COMPANY','PLANT','WHS','reportDate'
}
}
@@ -0,0 +1,235 @@
<?php
namespace Modules\M3\Http\Controllers;
use Modules\M3\Http\Controllers\Controller;
use DB;
use Config;
use Storage;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Modules\M3\Services\BillOfLadingService;
use setasign\Fpdi\Fpdi;
class fillBOL2Controller extends Controller
{
protected $bolService;
public function __construct(BillOfLadingService $bolService)
{
$this->bolService = $bolService;
}
public static function index(Request $request) {
return view('m3::liberty-theme.pages.fillBOL2');
}
public function fillBOL(Request $request)
{
## $BOL = BOL::findOrFail($request);
$BOL = $request;
$data = [
'bol_number' => $request->input('BOL_number') ?? '1234567890',
'shipper_no' => $request->input('shipper_no') ?? '1234567890',
'date' => now()->format('m/d/Y') ?? '01/01/2000',
'master_bill_no' => $request->input('master_bill_no') ? 'X' : 'X',
'master_bill_yes' => $request->input('master_bill_yes') ? 'X' : '',
'master_bill_drops' => $request->input('master_bill_drops') ?? '',
'consignor_name' => $request->input('consignor_name') ?? 'ERICO INTERNATIONAL CORP',
'consignor_address1' => $request->input('consignor_address1') ?? '6800 ARNOLD MILLER PARKWAY',
'consignor_address2' => $request->input('consignor_address2') ?? 'SOLON, OH 44139',
'consignor_address3' => $request->input('consignor_address3') ?? 'USA',
'consignee_name' => $request->input('consignee_name') ?? 'ERICO EUROPE BV',
'consignee_address1' => $request->input('consignee_address1') ?? 'JULES VERNEWEG 75',
'consignee_address2' => $request->input('consignee_address2') ?? '',
'consignee_address3' => $request->input('consignee_address3') ?? 'TILBURG . 5015 BG',
'freight_collect' => $request->input('freight_collect') ? 'X' : 'X',
'carrier_name' => $request->input('carrier_name') ?? 'ICL / Expeditors',
'pieces' => $request->input('pieces') ?? '20',
'line_class' => $request->input('line_class') ?? '50',
'line_weight' => $request->input('line_weight') ?? '8320',
'total_pieces' => $request->input('total_pieces') ?? '20',
'total_line_weight' => $request->input('total_line_weight') ?? '8320',
'description' => $request->input('description') ?? 'Package Description sfdfsdfasdfas dfasdfasdfcasdfvasfa clsdalcdfl
Line 2
Line 3
Line 4
Line 5',
'DTESHIPED.1' => date('d/m/Y'),
'invoice_number' => $BOL['BOL_number'],
'amount_due' => number_format($BOL['amount'], 2),
'TYPEANDESCPTN.1' => 'TOTAL: 20 SKIDS 8320 #',
'TYPEANDESCPTN.10' => 'ROTTERDAM ETA: 11/22',
'TYPEANDESCPTN.13' => 'CONTAINER:',
'TYPEANDESCPTN.2' => 'MECHANICAL / CADDY/ ELECTRICAL',
'TYPEANDESCPTN.4' => 'BOOKING: 150744100',
'TYPEANDESCPTN.5' => 'REFERENCE: 3140028046',
'TYPEANDESCPTN.6' => 'VESSEL: INDEPENDENT HORIZON V. 1023E',
'TYPEANDESCPTN.7' => 'SEAL: 637053',
'TYPEANDESCPTN.9' => 'CHESTER, PA ETD: 11/05',
'BOFLADNGAGYCD' => $BOL['BOL_number'],
'CLASSORATE1' => '50',
'CNMETRKLICNSE' => 'ICL / Expeditors',
'CONSIGTONMADDR' => 'ERICO EUROPE BV JULES VERNEWEG 75 TILBURG . 5015 BG',
'DATE' => date('d/m/Y'),
'FRMNMADDR' => 'ERICO INTERNATIONAL CORP 6800 ARNOLD MILLER PARKWAY SOLON, OH 44139 USA',
'LNITEMWT1' => '8320',
'PARTOFMSTRBILL' => 'No',
'PKGTYPECOUNT1' => '20',
'SHPRNMBR' => '3140028046',
'TOTALWT' => '8320',
'TOTPIECES' => '20',
'ckFREIGHTCOLECT' => 'Yes',
'ckPREPAY' => 'Off',
'DTESHIPED.2' => '',
'LNITEMWT24' => '',
'CLASSORATE24' => '',
'LNITEMCLAS24' => '',
'TYPEANDESCPTN.24' => '',
'PKGTYPECOUNT24' => '',
'LNITEMWT23' => '',
'CLASSORATE23' => '',
'LNITEMCLAS23' => '',
'TYPEANDESCPTN.23' => '',
'PKGTYPECOUNT23' => '',
'LNITEMWT22' => '',
'CLASSORATE22' => '',
'LNITEMCLAS22' => '',
'TYPEANDESCPTN.22' => '',
'PKGTYPECOUNT22' => '',
'LNITEMWT21' => '',
'CLASSORATE21' => '',
'LNITEMCLAS21' => '',
'TYPEANDESCPTN.21' => '',
'PKGTYPECOUNT21' => '',
'LNITEMWT20' => '',
'CLASSORATE20' => '',
'LNITEMCLAS20' => '',
'TYPEANDESCPTN.20' => '',
'PKGTYPECOUNT20' => '',
'LNITEMWT19' => '',
'CLASSORATE19' => '',
'LNITEMCLAS19' => '',
'TYPEANDESCPTN.19' => '',
'PKGTYPECOUNT19' => '',
'LNITEMWT18' => '',
'CLASSORATE18' => '',
'LNITEMCLAS18' => '',
'TYPEANDESCPTN.18' => '',
'PKGTYPECOUNT18' => '',
'LNITEMWT17' => '',
'CLASSORATE17' => '',
'LNITEMCLAS17' => '',
'TYPEANDESCPTN.17' => '',
'PKGTYPECOUNT17' => '',
'LNITEMWT16' => '',
'CLASSORATE16' => '',
'LNITEMCLAS16' => '',
'TYPEANDESCPTN.16' => '',
'PKGTYPECOUNT16' => '',
'LNITEMWT15' => '',
'CLASSORATE15' => '',
'LNITEMCLAS15' => '',
'TYPEANDESCPTN.15' => '',
'PKGTYPECOUNT15' => '',
'LNITEMWT14' => '',
'CLASSORAT14' => '',
'LNITEMCLAS14' => '',
'TYPEANDESCPTN.14' => '',
'PKGTYPECOUNT14' => '',
'LNITEMWT13' => '',
'CLASSORATE13' => '',
'LNITEMCLAS13' => '',
'PKGTYPECOUNT13' => '',
'LNITEMWT12' => '',
'CLASSORATE12' => '',
'LNITEMCLAS12' => '',
'TYPEANDESCPTN.12' => '',
'PKGTYPECOUNT12' => '',
'LNITEMWT11' => '',
'CLASSORATE11' => '',
'LNITEMCLAS11' => '',
'TYPEANDESCPTN.11' => '',
'PKGTYPECOUNT11' => '',
'LNITEMWT10' => '',
'CLASSORATE10' => '',
'LNITEMCLAS10' => '',
'PKGTYPECOUNT10' => '',
'LNITEMWT9' => '',
'CLASSORATE9' => '',
'LNITEMCLAS8' => '',
'PKGTYPECOUNT9' => '',
'LNITEMWT8' => '',
'CLASSORATE8' => '',
'TYPEANDESCPTN.8' => '',
'PKGTYPECOUNT8' => '',
'LNITEMWT7' => '',
'CLASSORATE7' => '',
'LNITEMCLAS7' => '',
'PKGTYPECOUNT7' => '',
'LNITEMWT6' => '',
'CLASSORATE6' => '',
'LNITEMCLAS6' => '',
'PKGTYPECOUNT6' => '',
'LNITEMWT5' => '',
'CLASSORATE5' => '',
'LNITEMCLAS5' => '',
'PKGTYPECOUNT5' => '',
'LNITEMWT4' => '',
'CLASSORATE4' => '',
'LNITEMCLAS4' => '',
'PKGTYPECOUNT4' => '',
'LNITEMWT3' => '',
'CLASSORATE3' => '',
'LNITEMCLAS3' => '',
'TYPEANDESCPTN.3' => '',
'PKGTYPECOUNT3' => '',
'LNITEMWT2' => '',
'CLASSORATE2' => '',
'LNITEMCLAS2' => '',
'PKGTYPECOUNT2' => '',
'LNITEMCLAS1' => '',
'HAZLNITEMWT3' => '',
'HAZCLASSORATE3' => '',
'HAZLNITEMCLAS3' => '',
'HAZMATERDESL3' => '',
'HAZMATERL3' => '',
'HAZLNITEMWT2' => '',
'HAZCLASSORATE2' => '',
'HAZLNITEMCLAS2' => '',
'HAZMATERDESL2' => '',
'HAZMATERL2' => '',
'HAZLNITEMWT1' => '',
'HAZCLASSORATE1' => '',
'HAZLNITEMCLAS1' => '',
'HAZMATERDESL1' => '',
'HAZMATERL1' => '',
'24HREMGCYRSPNS' => '',
'CARIERFEDEMPID' => '',
'BILLTOADDR' => '',
'NUMDROPS' => '',
];
try {
$filledPdf = $this->bolService->fillBillOfLading($data);
return response()->download($filledPdf)->deleteFileAfterSend(true);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
}
}

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