updates to M3 module

This commit is contained in:
Mark Gregory
2026-09-16 14:28:17 +01:00
parent 3410ab85fe
commit d1a83c9b09
164 changed files with 45562 additions and 273 deletions
+247
View File
@@ -0,0 +1,247 @@
<?php
namespace Modules\M3\Services;
use Storage;
use setasign\Fpdi\Fpdi;
class BillOfLadingService
{
public function fillBillOfLading($data, $outputPath = null)
{
$templatePath = Storage::disk('public')->path('ETN-BOL2.pdf'); //storage_path('app/templates/ETN-BOL.pdf');
$newfile='BOL_' . $data['bol_number'] . '.pdf'; #was using time()
$outputPath = $outputPath ?? Storage::disk('public')->path($newfile); //storage_path('app/filled/BOL_' . $data['bol_number'] . '.pdf');
$pdf = new Fpdi();
// Set source file
$pageCount = $pdf->setSourceFile($templatePath);
// Import first page (you have 5 pages in your PDF)
$tplIdx = $pdf->importPage(1);
$pdf->AddPage();
$pdf->useTemplate($tplIdx);
// Set font
$pdf->SetFont('Helvetica', '', 10);
$pdf->SetTextColor(0, 0, 0);
#$pdf->SetLineWidth(50);
// Fill form fields at specific coordinates
// You'll need to adjust X,Y positions based on your PDF
// Bill of Lading No.
if (isset($data['bol_number'])) {
$pdf->SetXY(126, 44);
$pdf->Write(0, $data['bol_number']);
}
// Date
if (isset($data['date'])) {
$pdf->SetXY(171, 44);
$pdf->Write(0, $data['date']);
}
// Shipper No.
if (isset($data['shipper_no'])) {
$pdf->SetXY(126, 53);
$pdf->Write(0, $data['shipper_no']);
}
// Master Bill No
if (isset($data['master_bill_no'])) {
$pdf->SetXY(172, 54);
$pdf->Write(0, $data['master_bill_no']);
}
// Master Bill Yes
if (isset($data['master_bill_yes'])) {
$pdf->SetXY(183, 54);
$pdf->Write(0, $data['master_bill_yes']);
}
// Master Bill Drops
if (isset($data['master_bill_drops'])) {
$pdf->SetXY(194, 55);
$pdf->Write(0, $data['master_bill_drops']);
}
// Consignor (FROM)
if (isset($data['consignor_name'])) {
$pdf->SetXY(21, 44);
$pdf->Write(0, $data['consignor_name']);
}
if (isset($data['consignor_address1'])) {
$pdf->SetXY(21, 48);
$pdf->Write(0, $data['consignor_address1']);
}
if (isset($data['consignor_address2'])) {
$pdf->SetXY(21, 52);
$pdf->Write(0, $data['consignor_address2']);
}
if (isset($data['consignor_address3'])) {
$pdf->SetXY(21, 56);
$pdf->Write(0, $data['consignor_address3']);
}
// Consignee (TO)
if (isset($data['consignee_name'])) {
$pdf->SetXY(21, 72);
$pdf->Write(0, $data['consignee_name']);
}
if (isset($data['consignee_address1'])) {
$pdf->SetXY(21, 76);
$pdf->Write(0, $data['consignee_address1']);
}
if (isset($data['consignee_address2'])) {
$pdf->SetXY(21, 80);
$pdf->Write(0, $data['consignee_address2']);
}
if (isset($data['consignee_address3'])) {
$pdf->SetXY(21, 84);
$pdf->Write(0, $data['consignee_address3']);
}
//Freight Collect
if (isset($data['freight_collect'])) {
$pdf->SetXY(127, 90);
$pdf->Write(0, $data['freight_collect']);
}
// Carrier Name
if (isset($data['carrier_name'])) {
$pdf->SetXY(10, 100);
$pdf->Write(0, $data['carrier_name']);
}
// Pieces
if (isset($data['pieces'])) {
$pdf->SetXY(6, 142);
$pdf->Write(0, $data['pieces']);
}
// Line class
if (isset($data['line_class'])) {
$pdf->SetXY(145, 142);
$pdf->Write(0, $data['line_class']);
}
// Line weight
if (isset($data['line_weight'])) {
$pdf->SetXY(177, 142);
$pdf->Write(0, $data['line_weight']);
}
// Total pieces
if (isset($data['total_pieces'])) {
$pdf->SetXY(6, 244);
$pdf->Write(0, $data['total_pieces']);
}
// Total weight
if (isset($data['total_line_weight'])) {
$pdf->SetXY(177, 244);
$pdf->Write(0, $data['total_line_weight']);
}
// Description of articles
if (isset($data['description'])) {
$pdf->SetXY(28, 140);
$pdf->MultiCell(67, 4.5, $data['description'], 0, 'L');
}
// Add more pages if needed (pages 2-5)
// Repeat for other pages as needed
$tplIdx = $pdf->importPage(2);
$pdf->AddPage();
$pdf->useTemplate($tplIdx);
// Bill of Lading No.
if (isset($data['bol_number'])) {
$pdf->SetXY(126, 44);
$pdf->Write(0, $data['bol_number']);
}
// Date
if (isset($data['date'])) {
$pdf->SetXY(171, 44);
$pdf->Write(0, $data['date']);
}
// Shipper No.
if (isset($data['shipper_no'])) {
$pdf->SetXY(126, 53);
$pdf->Write(0, $data['shipper_no']);
}
// Master Bill No
if (isset($data['master_bill_no'])) {
$pdf->SetXY(172, 54);
$pdf->Write(0, $data['master_bill_no']);
}
// Master Bill Yes
if (isset($data['master_bill_yes'])) {
$pdf->SetXY(183, 54);
$pdf->Write(0, $data['master_bill_yes']);
}
// Master Bill Drops
if (isset($data['master_bill_drops'])) {
$pdf->SetXY(194, 55);
$pdf->Write(0, $data['master_bill_drops']);
}
// Consignor (FROM)
if (isset($data['consignor_name'])) {
$pdf->SetXY(21, 44);
$pdf->Write(0, $data['consignor_name']);
}
if (isset($data['consignor_address1'])) {
$pdf->SetXY(21, 48);
$pdf->Write(0, $data['consignor_address1']);
}
if (isset($data['consignor_address2'])) {
$pdf->SetXY(21, 52);
$pdf->Write(0, $data['consignor_address2']);
}
if (isset($data['consignor_address3'])) {
$pdf->SetXY(21, 56);
$pdf->Write(0, $data['consignor_address3']);
}
// Consignee (TO)
if (isset($data['consignee_name'])) {
$pdf->SetXY(21, 72);
$pdf->Write(0, $data['consignee_name']);
}
if (isset($data['consignee_address1'])) {
$pdf->SetXY(21, 76);
$pdf->Write(0, $data['consignee_address1']);
}
if (isset($data['consignee_address2'])) {
$pdf->SetXY(21, 80);
$pdf->Write(0, $data['consignee_address2']);
}
if (isset($data['consignee_address3'])) {
$pdf->SetXY(21, 84);
$pdf->Write(0, $data['consignee_address3']);
}
//Freight Collect
if (isset($data['freight_collect'])) {
$pdf->SetXY(127, 90);
$pdf->Write(0, $data['freight_collect']);
}
// Carrier Name
if (isset($data['carrier_name'])) {
$pdf->SetXY(10, 100);
$pdf->Write(0, $data['carrier_name']);
}
# KD : add fields as you need
// Save the PDF
$pdf->Output('F', $outputPath);
return $outputPath;
}
}
@@ -0,0 +1,97 @@
<?php
namespace Modules\M3\Services;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
/**
* PostgreSQL replacement for the old IBM i Toolkit program-call transport.
*
* The legacy M3 code invokes IBM i programs after writing PostgreSQL-backed
* staging data. Core no longer connects to DB2. These requests are persisted
* so a PostgreSQL-native worker/integration can process them without making
* the web request depend on IBM i drivers.
*/
class PostgresProgramBridge
{
private string $errorCode = '';
private string $errorMessage = '';
public function setOptions(array $options): self
{
return $this;
}
public function AddParameterChar(string $io, int $size, string $comment = '', string $name = '', mixed $value = null, mixed ...$rest): array
{
return compact('io', 'size', 'comment', 'name', 'value');
}
public function AddParameterPackDec(string $io, int $length, int $scale, string $comment = '', string $name = '', mixed $value = null, mixed ...$rest): array
{
return compact('io', 'length', 'scale', 'comment', 'name', 'value');
}
public function AddParameterZoned(string $io, int $length, int $scale, string $comment = '', string $name = '', mixed $value = null, mixed ...$rest): array
{
return compact('io', 'length', 'scale', 'comment', 'name', 'value');
}
public function PgmCall(string $program, ?string $library = null, array $parameters = [], array $returnParameters = [], array $options = []): array|false
{
return $this->queue('program', $program, $library, [
'parameters' => $parameters,
'return_parameters' => $returnParameters,
'options' => $options,
]);
}
public function CLCommand(string $command): array|false
{
return $this->queue('command', $command, null, []);
}
public function getErrorCode(): string
{
return $this->errorCode;
}
public function getErrorMsg(): string
{
return $this->errorMessage;
}
private function queue(string $type, string $operation, ?string $library, array $payload): array|false
{
try {
$record = [
'type' => $type,
'operation' => $operation,
'library' => $library,
'payload' => json_encode($payload, JSON_THROW_ON_ERROR),
'requested_by' => function_exists('m3_user') ? m3_user() : null,
'status' => 'pending',
'created_at' => now(),
'updated_at' => now(),
];
if (Schema::connection(config('m3.connection', 'pgsql'))->hasTable('m3_program_requests')) {
$id = DB::connection(config('m3.connection', 'pgsql'))
->table('m3_program_requests')
->insertGetId($record);
} else {
Log::warning('M3 PostgreSQL program bridge table is not migrated; request logged only.', $record);
$id = null;
}
return ['success' => true, 'queued' => true, 'request_id' => $id, 'io_param' => []];
} catch (\Throwable $e) {
$this->errorCode = (string) $e->getCode();
$this->errorMessage = $e->getMessage();
Log::error('Unable to queue M3 program request.', ['exception' => $e]);
return false;
}
}
}
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace Modules\M3\Services;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class SpreadsheetExport
{
public function download(Collection $rows, string $filename): BinaryFileResponse
{
$path = storage_path('app/m3-exports/' . uniqid('', true) . '-' . basename($filename));
$this->write($rows, $path);
return response()->download($path, basename($filename))->deleteFileAfterSend(true);
}
public function store(Collection $rows, string $filename, ?string $disk = null): string
{
$tmp = storage_path('app/m3-exports/' . uniqid('', true) . '-' . basename($filename));
$this->write($rows, $tmp);
if ($disk && config("filesystems.disks.$disk")) {
Storage::disk($disk)->put($filename, file_get_contents($tmp));
@unlink($tmp);
return $filename;
}
return $tmp;
}
public function storeSheets(array $sheets, string $filename, ?string $disk = null): string
{
$book = new Spreadsheet();
$book->removeSheetByIndex(0);
foreach ($sheets as $title => $rows) {
$sheet = $book->createSheet();
$sheet->setTitle(substr((string) $title, 0, 31));
$array = collect($rows)->map(fn ($row) => (array) $row)->values();
if ($array->isNotEmpty()) {
$sheet->fromArray($array->map(fn ($row) => array_values($row))->all(), null, 'A1');
}
}
$tmp = storage_path('app/m3-exports/' . uniqid('', true) . '-' . basename($filename));
if (! is_dir(dirname($tmp))) { mkdir(dirname($tmp), 0775, true); }
(new Xlsx($book))->save($tmp);
if ($disk && config("filesystems.disks.$disk")) {
Storage::disk($disk)->put($filename, file_get_contents($tmp));
@unlink($tmp);
return $filename;
}
return $tmp;
}
private function write(Collection $rows, string $path): void
{
if (! is_dir(dirname($path))) {
mkdir(dirname($path), 0775, true);
}
$sheet = (new Spreadsheet())->getActiveSheet();
$array = $rows->map(fn ($row) => (array) $row)->values();
$headers = $array->first() ? array_keys($array->first()) : [];
if ($headers) {
$sheet->fromArray($headers, null, 'A1');
$sheet->fromArray($array->map(fn ($row) => array_values($row))->all(), null, 'A2');
}
(new Xlsx($sheet->getParent()))->save($path);
}
}