Compare commits
143 Commits
afbbfe2aca
...
ranjith-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dd3b35eb5 | ||
|
|
4129a7a251 | ||
|
|
d6aea93d00 | ||
|
|
fb91bea7c4 | ||
|
|
f970d6eb0f | ||
|
|
b567f3ee02 | ||
|
|
e7fa446fc3 | ||
|
|
f399808249 | ||
|
|
575d3675cf | ||
|
|
7435928fcf | ||
|
|
d6acded332 | ||
|
|
915a766aa0 | ||
|
|
e123dd65b5 | ||
|
|
f420f05179 | ||
|
|
84f165beea | ||
|
|
8d397f11e1 | ||
|
|
19feaba66a | ||
|
|
912b793e8d | ||
|
|
ccd922b5bb | ||
|
|
203d09712b | ||
|
|
d9f531fbc6 | ||
|
|
142094fd00 | ||
|
|
ab86517fc1 | ||
|
|
f66839131e | ||
|
|
7244028d02 | ||
|
|
8104da98d4 | ||
|
|
1d51067355 | ||
|
|
1fd2538048 | ||
|
|
d8fdf02417 | ||
|
|
1031a972de | ||
|
|
f5173d9861 | ||
|
|
9602be624d | ||
|
|
85c7a3e286 | ||
|
|
6071c8b898 | ||
|
|
9bf5337383 | ||
|
|
621cf13565 | ||
|
|
6c334359b2 | ||
|
|
fd444a7749 | ||
|
|
f7a421681e | ||
|
|
8bd64c80d4 | ||
|
|
c4177887d6 | ||
|
|
09772d1984 | ||
|
|
3376d35eaa | ||
|
|
8feb2fc612 | ||
|
|
04d472805f | ||
|
|
f9d2e14210 | ||
|
|
13bef51af5 | ||
|
|
1711ce5646 | ||
|
|
b7da185912 | ||
|
|
4a796a670a | ||
|
|
4577f67d0a | ||
|
|
be2151a072 | ||
|
|
a4251ae532 | ||
|
|
93d55765ae | ||
|
|
17d54cc52e | ||
|
|
61467d88cd | ||
|
|
af0b17e674 | ||
|
|
dd7111a8d9 | ||
|
|
464ee6c3c7 | ||
|
|
984d686182 | ||
|
|
fd87748a38 | ||
|
|
f9aa6cd1ba | ||
|
|
d743b2df26 | ||
|
|
814281a6bf | ||
|
|
dc445b17f5 | ||
|
|
cd553651f3 | ||
|
|
ac20e96358 | ||
|
|
0fb9c91b28 | ||
|
|
21d602d86a | ||
|
|
06628072dc | ||
|
|
cdf9f60ffd | ||
|
|
b419a538dc | ||
|
|
fea15e0d1b | ||
|
|
f5a1e453d5 | ||
|
|
fd6149ccbe | ||
|
|
085a4f72fa | ||
|
|
6b41a27d31 | ||
|
|
648b676453 | ||
|
|
6f4d81025b | ||
|
|
42bbad16aa | ||
|
|
3fcc2de515 | ||
|
|
d14200e40e | ||
|
|
a34322c87a | ||
|
|
547d73e1f4 | ||
|
|
d93d55bb69 | ||
|
|
ad00321dff | ||
|
|
d333324935 | ||
|
|
a1b8cc0eed | ||
|
|
d2a2a35410 | ||
|
|
28503a25c3 | ||
|
|
20b17d446b | ||
|
|
0de50e12b8 | ||
|
|
9767e0547d | ||
|
|
92da8af6d2 | ||
|
|
1adfd59a0e | ||
|
|
92bf1ee401 | ||
|
|
99a605196d | ||
|
|
e075a510d9 | ||
|
|
2a7d012ec8 | ||
|
|
d63e8f7a37 | ||
|
|
9c4679a5a5 | ||
|
|
f6f3ab803c | ||
|
|
64b5c129ee | ||
|
|
955359b92c | ||
|
|
3ffc59f0cf | ||
|
|
9b5e66c834 | ||
|
|
7dbeb0afd3 | ||
|
|
fdfa5d0cf1 | ||
|
|
fce45b5386 | ||
|
|
6a62dce305 | ||
|
|
9ae6dede23 | ||
|
|
2f470c5c54 | ||
|
|
529850f0b9 | ||
|
|
61637bc3d5 | ||
|
|
c248a42a9d | ||
|
|
aeb49c40eb | ||
|
|
8fc963dc0a | ||
|
|
14844f1e1e | ||
|
|
cfc5845768 | ||
|
|
f0a6f924d9 | ||
|
|
f6f9730587 | ||
|
|
b42f5ffe84 | ||
|
|
376899e277 | ||
|
|
e53fb15c01 | ||
|
|
953999459e | ||
|
|
d2ab947b1d | ||
|
|
9b965d5a2b | ||
|
|
f48de19e0d | ||
|
|
48ce416cbf | ||
|
|
39b5cf77e4 | ||
|
|
46649d0459 | ||
|
|
2f593326e5 | ||
|
|
bb9c344e5b | ||
|
|
10e595a4a3 | ||
|
|
31ab56d46c | ||
|
|
a8f566ca04 | ||
|
|
00d9cfc291 | ||
|
|
7e45c611a0 | ||
|
|
7cab79c7e0 | ||
|
|
2d81d41f34 | ||
|
|
4daa72db8e | ||
|
|
504c125ca8 | ||
|
|
79f91bf444 |
62
app/Exports/ProductionPlanExport.php
Normal file
62
app/Exports/ProductionPlanExport.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\FromArray;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
|
||||
class ProductionPlanExport implements FromArray, WithHeadings, WithMapping
|
||||
{
|
||||
|
||||
protected array $data;
|
||||
protected array $dates;
|
||||
|
||||
public function __construct(array $data, array $dates)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->dates = $dates;
|
||||
}
|
||||
|
||||
public function array(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
$headings = [
|
||||
'Plant Name',
|
||||
'Line Name',
|
||||
'Item Code',
|
||||
];
|
||||
|
||||
// Add dynamic headings for each date: Target / Produced
|
||||
foreach ($this->dates as $date) {
|
||||
$headings[] = $date . ' - Target Plan';
|
||||
$headings[] = $date . ' - Produced Quantity';
|
||||
}
|
||||
|
||||
return $headings;
|
||||
}
|
||||
|
||||
public function map($row): array
|
||||
{
|
||||
$mapped = [
|
||||
$row['plant_name'] ?? '',
|
||||
$row['line_name'] ?? '',
|
||||
$row['item_code'] ?? '',
|
||||
];
|
||||
|
||||
// Add daily target and produced quantity for each date
|
||||
foreach ($this->dates as $date) {
|
||||
// $mapped[] = $row['daily_target_dynamic'] ?? 0;
|
||||
$mapped[] = $row['daily_target_dynamic'][$date] ?? '-';
|
||||
$mapped[] = $row['produced_quantity'][$date] ?? 0;
|
||||
}
|
||||
|
||||
return $mapped;
|
||||
}
|
||||
|
||||
}
|
||||
44
app/Filament/Exports/EmployeeMasterExporter.php
Normal file
44
app/Filament/Exports/EmployeeMasterExporter.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Exports;
|
||||
|
||||
use App\Models\EmployeeMaster;
|
||||
use Filament\Actions\Exports\ExportColumn;
|
||||
use Filament\Actions\Exports\Exporter;
|
||||
use Filament\Actions\Exports\Models\Export;
|
||||
|
||||
class EmployeeMasterExporter extends Exporter
|
||||
{
|
||||
protected static ?string $model = EmployeeMaster::class;
|
||||
|
||||
public static function getColumns(): array
|
||||
{
|
||||
return [
|
||||
ExportColumn::make('id')
|
||||
->label('ID'),
|
||||
ExportColumn::make('plant.name'),
|
||||
ExportColumn::make('name'),
|
||||
ExportColumn::make('code'),
|
||||
ExportColumn::make('department'),
|
||||
ExportColumn::make('designation'),
|
||||
ExportColumn::make('email'),
|
||||
ExportColumn::make('mobile_number'),
|
||||
ExportColumn::make('created_at'),
|
||||
ExportColumn::make('updated_at'),
|
||||
ExportColumn::make('created_by'),
|
||||
ExportColumn::make('updated_by'),
|
||||
ExportColumn::make('deleted_at'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getCompletedNotificationBody(Export $export): string
|
||||
{
|
||||
$body = 'Your employee master export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
|
||||
|
||||
if ($failedRowsCount = $export->getFailedRowsCount()) {
|
||||
$body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,8 @@ class StickerDetailExporter extends Exporter
|
||||
->label('ELEMENT ID'),
|
||||
ExportColumn::make('element_type')
|
||||
->label('ELEMENT TYPE'),
|
||||
ExportColumn::make('characteristics_type')
|
||||
->label('CHARACTERISTICS TYPE'),
|
||||
ExportColumn::make('string_value')
|
||||
->label('STRING VALUE'),
|
||||
ExportColumn::make('string_font')
|
||||
@@ -47,6 +49,8 @@ class StickerDetailExporter extends Exporter
|
||||
->label('SHAPE NAME'),
|
||||
ExportColumn::make('shape_pen_size')
|
||||
->label('SHAPE PEN SIZE'),
|
||||
ExportColumn::make('curve_radius')
|
||||
->label('CURVE RADIUS'),
|
||||
ExportColumn::make('shape_x1_value')
|
||||
->label('SHAPE X1 VALUE'),
|
||||
ExportColumn::make('shape_y1_value')
|
||||
@@ -55,10 +59,6 @@ class StickerDetailExporter extends Exporter
|
||||
->label('SHAPE X2 VALUE'),
|
||||
ExportColumn::make('shape_y2_value')
|
||||
->label('SHAPE Y2 VALUE'),
|
||||
ExportColumn::make('image_path')
|
||||
->label('IMAGE PATH'),
|
||||
ExportColumn::make('image_type')
|
||||
->label('IMAGE TYPE'),
|
||||
ExportColumn::make('image_x')
|
||||
->label('IMAGE X'),
|
||||
ExportColumn::make('image_y')
|
||||
|
||||
@@ -21,28 +21,117 @@ class StickerMappingMasterExporter extends Exporter
|
||||
// Increment and return the row number
|
||||
return ++$rowNumber;
|
||||
}),
|
||||
ExportColumn::make('plant.name')
|
||||
->label('PLANT'),
|
||||
ExportColumn::make('item.code')
|
||||
ExportColumn::make('plant.code')
|
||||
->label('PLANT CODE'),
|
||||
ExportColumn::make('itemCharacteristic.item.code')
|
||||
->label('ITEM CODE'),
|
||||
ExportColumn::make('sticker1')
|
||||
->label('STICKER LABEL 1'),
|
||||
ExportColumn::make('sticker2')
|
||||
->label('STICKER LABEL 2'),
|
||||
ExportColumn::make('sticker3')
|
||||
->label('STICKER LABEL 3'),
|
||||
ExportColumn::make('sticker4')
|
||||
->label('STICKER LABEL 4'),
|
||||
ExportColumn::make('sticker5')
|
||||
->label('STICKER LABEL 5'),
|
||||
ExportColumn::make('sticker_structure1_id')
|
||||
->label('STICKER 1')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker1Structure?->sticker_id ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker1_machine_id')
|
||||
->label('WC STICKER 1')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker1Machine?->work_center ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker1_print_ip')
|
||||
->label('STICKER 1 PRINT IP'),
|
||||
ExportColumn::make('sticker_structure2_id')
|
||||
->label('STICKER 2')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker2Structure?->sticker_id ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker2_machine_id')
|
||||
->label('WC STICKER 2')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker2Machine?->work_center ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker2_print_ip')
|
||||
->label('STICKER 2 PRINT IP'),
|
||||
ExportColumn::make('sticker_structure3_id')
|
||||
->label('STICKER 3')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker3Structure?->sticker_id ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker3_machine_id')
|
||||
->label('WC STICKER 3')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker3Machine?->work_center ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker3_print_ip')
|
||||
->label('STICKER 3 PRINT IP'),
|
||||
ExportColumn::make('sticker_structure4_id')
|
||||
->label('STICKER 4')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker4Structure?->sticker_id ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker4_machine_id')
|
||||
->label('WC STICKER 4')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker4Machine?->work_center ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker4_print_ip')
|
||||
->label('STICKER 4 PRINT IP'),
|
||||
ExportColumn::make('sticker_structure5_id')
|
||||
->label('STICKER 5')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker5Structure?->sticker_id ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker5_machine_id')
|
||||
->label('WC STICKER 5')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker5Machine?->work_center ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker5_print_ip')
|
||||
->label('STICKER 5 PRINT IP'),
|
||||
ExportColumn::make('sticker_structure6_id')
|
||||
->label('STICKER 6')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker6Structure?->sticker_id ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker6_machine_id')
|
||||
->label('WC STICKER 6')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker6Machine?->work_center ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker6_print_ip')
|
||||
->label('STICKER 6 PRINT IP'),
|
||||
ExportColumn::make('sticker_structure7_id')
|
||||
->label('STICKER 7')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker7Structure?->sticker_id ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker7_machine_id')
|
||||
->label('WC STICKER 7')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker2Machine?->work_center ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker7_print_ip')
|
||||
->label('STICKER 7 PRINT IP'),
|
||||
ExportColumn::make('sticker_structure8_id')
|
||||
->label('STICKER 8')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker8Structure?->sticker_id ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker8_machine_id')
|
||||
->label('WC STICKER 8')
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->sticker8Machine?->work_center ?? '-';
|
||||
}),
|
||||
ExportColumn::make('sticker8_print_ip')
|
||||
->label('STICKER 8 PRINT IP'),
|
||||
ExportColumn::make('created_at')
|
||||
->label('CREATED AT'),
|
||||
ExportColumn::make('updated_at')
|
||||
->label('UPDATED AT'),
|
||||
ExportColumn::make('created_by')
|
||||
->label('CREATED BY'),
|
||||
ExportColumn::make('updated_by'),
|
||||
ExportColumn::make('deleted_at'),
|
||||
ExportColumn::make('updated_by')
|
||||
->label('UPDATED BY'),
|
||||
ExportColumn::make('deleted_at')
|
||||
->enabledByDefault(false)
|
||||
->label('DELETED AT'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -13,21 +13,43 @@ class StickerStructureDetailExporter extends Exporter
|
||||
|
||||
public static function getColumns(): array
|
||||
{
|
||||
static $rowNumber = 0;
|
||||
return [
|
||||
ExportColumn::make('id')
|
||||
->label('ID'),
|
||||
ExportColumn::make('sticker_id'),
|
||||
ExportColumn::make('sticker_width'),
|
||||
ExportColumn::make('sticker_height'),
|
||||
ExportColumn::make('sticker_lmargin'),
|
||||
ExportColumn::make('sticker_rmargin'),
|
||||
ExportColumn::make('sticker_tmargin'),
|
||||
ExportColumn::make('sticker_bmargin'),
|
||||
ExportColumn::make('created_at'),
|
||||
ExportColumn::make('updated_at'),
|
||||
ExportColumn::make('created_by'),
|
||||
ExportColumn::make('updated_by'),
|
||||
ExportColumn::make('deleted_at'),
|
||||
ExportColumn::make('no')
|
||||
->label('NO')
|
||||
->state(function ($record) use (&$rowNumber) {
|
||||
// Increment and return the row number
|
||||
return ++$rowNumber;
|
||||
}),
|
||||
ExportColumn::make('plant.code')
|
||||
->label('PLANT CODE'),
|
||||
ExportColumn::make('itemCharacteristic.item.code')
|
||||
->label('ITEM CODE'),
|
||||
ExportColumn::make('sticker_id')
|
||||
->label('STICKER ID'),
|
||||
ExportColumn::make('sticker_width')
|
||||
->label('STICKER WIDTH'),
|
||||
ExportColumn::make('sticker_height')
|
||||
->label('STICKER HEIGHT'),
|
||||
ExportColumn::make('sticker_lmargin')
|
||||
->label('STICKER LEFT MARGIN'),
|
||||
ExportColumn::make('sticker_rmargin')
|
||||
->label('STICKER RIGHT MARGIN'),
|
||||
ExportColumn::make('sticker_tmargin')
|
||||
->label('STICKER TOP MARGIN'),
|
||||
ExportColumn::make('sticker_bmargin')
|
||||
->label('STICKER BOTTOM MARGIN'),
|
||||
ExportColumn::make('created_at')
|
||||
->label('CREATED AT'),
|
||||
ExportColumn::make('updated_at')
|
||||
->label('UPDATED AT'),
|
||||
ExportColumn::make('created_by')
|
||||
->label('CREATED BY'),
|
||||
ExportColumn::make('updated_by')
|
||||
->label('UPDATED BY'),
|
||||
ExportColumn::make('deleted_at')
|
||||
->enabledByDefault(false)
|
||||
->label('DELETED AT'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
53
app/Filament/Imports/EmployeeMasterImporter.php
Normal file
53
app/Filament/Imports/EmployeeMasterImporter.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Imports;
|
||||
|
||||
use App\Models\EmployeeMaster;
|
||||
use Filament\Actions\Imports\ImportColumn;
|
||||
use Filament\Actions\Imports\Importer;
|
||||
use Filament\Actions\Imports\Models\Import;
|
||||
|
||||
class EmployeeMasterImporter extends Importer
|
||||
{
|
||||
protected static ?string $model = EmployeeMaster::class;
|
||||
|
||||
public static function getColumns(): array
|
||||
{
|
||||
return [
|
||||
ImportColumn::make('plant')
|
||||
->requiredMapping()
|
||||
->relationship()
|
||||
->rules(['required']),
|
||||
ImportColumn::make('name'),
|
||||
ImportColumn::make('code'),
|
||||
ImportColumn::make('department'),
|
||||
ImportColumn::make('designation'),
|
||||
ImportColumn::make('email')
|
||||
->rules(['email']),
|
||||
ImportColumn::make('mobile_number'),
|
||||
ImportColumn::make('created_by'),
|
||||
ImportColumn::make('updated_by'),
|
||||
];
|
||||
}
|
||||
|
||||
public function resolveRecord(): ?EmployeeMaster
|
||||
{
|
||||
// return EmployeeMaster::firstOrNew([
|
||||
// // Update existing records, matching them by `$this->data['column_name']`
|
||||
// 'email' => $this->data['email'],
|
||||
// ]);
|
||||
|
||||
return new EmployeeMaster();
|
||||
}
|
||||
|
||||
public static function getCompletedNotificationBody(Import $import): string
|
||||
{
|
||||
$body = 'Your employee master import has completed and ' . number_format($import->successful_rows) . ' ' . str('row')->plural($import->successful_rows) . ' imported.';
|
||||
|
||||
if ($failedRowsCount = $import->getFailedRowsCount()) {
|
||||
$body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to import.';
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Filament\Imports;
|
||||
|
||||
use App\Models\Block;
|
||||
use App\Models\Item;
|
||||
use App\Models\Line;
|
||||
use App\Models\Plant;
|
||||
use App\Models\ProductionPlan;
|
||||
@@ -23,11 +24,33 @@ class ProductionPlanImporter extends Importer
|
||||
public static function getColumns(): array
|
||||
{
|
||||
return [
|
||||
ImportColumn::make('created_at')
|
||||
// ImportColumn::make('created_at')
|
||||
// ->requiredMapping()
|
||||
// ->exampleHeader('Created DateTime')
|
||||
// ->example(['01-01-2025 08:00:00', '01-01-2025 19:30:00'])
|
||||
// ->label('Created DateTime')
|
||||
// ->rules(['required']),
|
||||
|
||||
ImportColumn::make('plant')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Created DateTime')
|
||||
->example(['01-01-2025 08:00:00', '01-01-2025 19:30:00'])
|
||||
->label('Created DateTime')
|
||||
->exampleHeader('Plant Code')
|
||||
->example(['1000', '1000'])
|
||||
->label('Plant Code')
|
||||
->relationship(resolveUsing: 'code')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('line')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Line Name')
|
||||
->example(['4 inch pump line', '4 inch pump line'])
|
||||
->label('Line Name')
|
||||
->relationship(resolveUsing: 'name')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('item')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Item Code')
|
||||
->example(['123456', '210987'])
|
||||
->label('Item Code')
|
||||
->relationship(resolveUsing: 'code')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('plan_quantity')
|
||||
->requiredMapping()
|
||||
@@ -36,175 +59,111 @@ class ProductionPlanImporter extends Importer
|
||||
->label('Plan Quantity')
|
||||
->numeric()
|
||||
->rules(['required', 'integer']),
|
||||
ImportColumn::make('production_quantity')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Production Quantity')
|
||||
->example(['0', '0'])
|
||||
->label('Production Quantity')
|
||||
->numeric()
|
||||
->rules(['required', 'integer']),
|
||||
ImportColumn::make('line')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Line Name')
|
||||
->example(['4 inch pump line', '4 inch pump line'])
|
||||
->label('Line Name')
|
||||
->relationship(resolveUsing:'name')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('block_reference')
|
||||
->requiredMapping() // Or optionalMapping() if not always present
|
||||
->exampleHeader('Block Name')
|
||||
->example(['Block A', 'Block A'])
|
||||
->label('Block Name')
|
||||
->rules(['required']), // Or remove if not required
|
||||
ImportColumn::make('shift')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Shift Name') //ID
|
||||
->example(['Day', 'Night']) //'2', '7'
|
||||
->label('Shift Name') // ID
|
||||
->relationship(resolveUsing: 'name')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('plant')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Plant Name')
|
||||
->example(['Ransar Industries-I', 'Ransar Industries-I'])
|
||||
->label('Plant Name')
|
||||
->relationship(resolveUsing:'name')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('updated_at')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Updated DateTime')
|
||||
->example(['01-01-2025 08:00:00', '01-01-2025 19:30:00'])
|
||||
->label('Updated DateTime')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('operator_id')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Operator ID')
|
||||
->example([Filament::auth()->user()->name, Filament::auth()->user()->name])
|
||||
->label('Operator ID')
|
||||
->rules(['required']),
|
||||
// ImportColumn::make('production_quantity')
|
||||
// ->requiredMapping()
|
||||
// ->exampleHeader('Production Quantity')
|
||||
// ->example(['0', '0'])
|
||||
// ->label('Production Quantity')
|
||||
// ->numeric()
|
||||
// ->rules(['required', 'integer']),
|
||||
|
||||
// ImportColumn::make('block_reference')
|
||||
// ->requiredMapping() // Or optionalMapping() if not always present
|
||||
// ->exampleHeader('Block Name')
|
||||
// ->example(['Block A', 'Block A'])
|
||||
// ->label('Block Name')
|
||||
// ->rules(['required']), // Or remove if not required
|
||||
// ImportColumn::make('shift')
|
||||
// ->requiredMapping()
|
||||
// ->exampleHeader('Shift Name') // ID
|
||||
// ->example(['Day', 'Night']) // '2', '7'
|
||||
// ->label('Shift Name') // ID
|
||||
// ->relationship(resolveUsing: 'name')
|
||||
// ->rules(['required']),
|
||||
|
||||
// ImportColumn::make('updated_at')
|
||||
// ->requiredMapping()
|
||||
// ->exampleHeader('Updated DateTime')
|
||||
// ->example(['01-01-2025 08:00:00', '01-01-2025 19:30:00'])
|
||||
// ->label('Updated DateTime')
|
||||
// ->rules(['required']),
|
||||
// ImportColumn::make('operator_id')
|
||||
// ->requiredMapping()
|
||||
// ->exampleHeader('Operator ID')
|
||||
// ->example([Filament::auth()->user()->name, Filament::auth()->user()->name])
|
||||
// ->label('Operator ID')
|
||||
// ->rules(['required']),
|
||||
];
|
||||
}
|
||||
|
||||
public function resolveRecord(): ?ProductionPlan
|
||||
{
|
||||
$warnMsg = [];
|
||||
$plant = Plant::where('name', $this->data['plant'])->first();
|
||||
$plantCod = $this->data['plant'];
|
||||
$itemCod = $this->data['item'];
|
||||
$plant = null;
|
||||
$line = null;
|
||||
$block = null;
|
||||
if (!$plant) {
|
||||
$warnMsg[] = "Plant not found";
|
||||
|
||||
if (Str::length($plantCod) < 4 || ! is_numeric($plantCod) || ! preg_match('/^[1-9]\d{3,}$/', $plantCod)) {
|
||||
$warnMsg[] = 'Invalid plant code found';
|
||||
} else {
|
||||
$plant = Plant::where('code', $plantCod)->first();
|
||||
}
|
||||
else {
|
||||
|
||||
if (! $plant) {
|
||||
$warnMsg[] = 'Plant not found';
|
||||
} else {
|
||||
$line = Line::where('name', $this->data['line'])->where('plant_id', $plant->id)->first();
|
||||
//block_reference
|
||||
$block = Block::where('name', $this->data['block_reference'])->where('plant_id', $plant->id)->first();
|
||||
}
|
||||
if (!$line) {
|
||||
$warnMsg[] = "Line not found";
|
||||
}
|
||||
$shift = null;
|
||||
if (!$block) {
|
||||
$warnMsg[] = "Block not found";
|
||||
}
|
||||
else {
|
||||
$shift = Shift::where('name', $this->data['shift'])->where('plant_id', $plant->id)->where('block_id', $block->id)->first();
|
||||
}
|
||||
//$shift = Shift::where('id', $this->data['shift'])->where('plant_id', $plant->id)->first();
|
||||
if (!$shift) {
|
||||
$warnMsg[] = "Shift not found";
|
||||
}
|
||||
if (Str::length($this->data['plan_quantity']) < 0 || !is_numeric($this->data['plan_quantity']) || $this->data['plan_quantity'] <= 0) {
|
||||
$warnMsg[] = "Invalid plan quantity found";
|
||||
}
|
||||
if (Str::length($this->data['production_quantity']) < 0 || !is_numeric($this->data['production_quantity']) || $this->data['production_quantity'] < 0) {
|
||||
$warnMsg[] = "Invalid production quantity found";
|
||||
}
|
||||
|
||||
$fromDate = $this->data['created_at'];
|
||||
$toDate = $this->data['updated_at'];
|
||||
|
||||
$formats = ['d-m-Y H:i', 'd-m-Y H:i:s']; //'07-05-2025 08:00' or '07-05-2025 08:00:00'
|
||||
|
||||
$fdateTime = null;
|
||||
$tdateTime = null;
|
||||
// Try parsing with multiple formats
|
||||
foreach ($formats as $format) {
|
||||
try {
|
||||
$fdateTime = Carbon::createFromFormat($format, $fromDate);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
// Optionally collect warning messages
|
||||
// $warnMsg[] = "Date format mismatch with format: $format";
|
||||
}
|
||||
if (! $line) {
|
||||
$warnMsg[] = 'Line not found';
|
||||
}
|
||||
|
||||
foreach ($formats as $format) {
|
||||
try {
|
||||
$tdateTime = Carbon::createFromFormat($format, $toDate);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
// Optionally collect warning messages
|
||||
// $warnMsg[] = "Date format mismatch with format: $format";
|
||||
}
|
||||
if (Str::length($itemCod) < 6 || ! is_numeric($itemCod)) {
|
||||
$warnMsg[] = 'Invalid item code found';
|
||||
} else {
|
||||
$item = Item::where('code', $itemCod)->first();
|
||||
}
|
||||
|
||||
$fDateOnly = '';
|
||||
if (!isset($fdateTime)) {
|
||||
// throw new \Exception('Invalid date time format');
|
||||
$warnMsg[] = "Invalid 'Created DateTime' format. Expected DD-MM-YYYY HH:MM:SS";
|
||||
}
|
||||
else {
|
||||
$fDateOnly = $fdateTime->toDateString();
|
||||
}
|
||||
if (!isset($tdateTime)) {
|
||||
$warnMsg[] = "Invalid 'Updated DateTime' format. Expected DD-MM-YYYY HH:MM:SS";
|
||||
if (! $item) {
|
||||
$warnMsg[] = 'Item not found';
|
||||
}
|
||||
|
||||
if (isset($fdateTime) && isset($tdateTime)) {
|
||||
if ($fdateTime->greaterThan($tdateTime)) {
|
||||
$warnMsg[] = "'Created DataTime' is greater than 'Updated DateTime'.";
|
||||
}
|
||||
$plantId = $plant->id;
|
||||
|
||||
$itemAgaPlant = Item::where('plant_id', $plantId)->where('code', $itemCod)->first();
|
||||
|
||||
if(!$itemAgaPlant){
|
||||
$warnMsg[] = 'Item not found against plant code';
|
||||
}
|
||||
|
||||
// if (!$fromDate) {
|
||||
// $warnMsg[] = "Invalid 'Created DateTime' format. Expected DD-MM-YYYY HH:MM:SS";
|
||||
// }
|
||||
// else if (!$toDate) {
|
||||
// $warnMsg[] = "Invalid 'Updated DateTime' format. Expected DD-MM-YYYY HH:MM:SS";
|
||||
// }
|
||||
$user = Filament::auth()->user();
|
||||
|
||||
$user = User::where('name', $this->data['operator_id'])->first();
|
||||
if (!$user) {
|
||||
$warnMsg[] = "Operator ID not found";
|
||||
$operatorName = $user->name;
|
||||
|
||||
if (Str::length($this->data['plan_quantity']) < 0 || ! is_numeric($this->data['plan_quantity']) || $this->data['plan_quantity'] <= 0) {
|
||||
$warnMsg[] = 'Invalid plan quantity found';
|
||||
}
|
||||
|
||||
if (!empty($warnMsg)) {
|
||||
if (! empty($warnMsg)) {
|
||||
throw new RowImportFailedException(implode(', ', $warnMsg));
|
||||
}
|
||||
else { //if (empty($warnMsg))
|
||||
} else {
|
||||
$productionPlan = ProductionPlan::where('plant_id', $plant->id)
|
||||
->where('shift_id', $shift->id)
|
||||
->where('line_id', $line->id)
|
||||
->whereDate('created_at', $fDateOnly)
|
||||
// ->where('plan_quantity', $productionQuantity->plan_quantity)
|
||||
->latest()
|
||||
->first();
|
||||
->where('line_id', $line->id)
|
||||
->where('item_id', $itemAgaPlant->id)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if ($productionPlan) {
|
||||
// if($productionPlan->production_quantity)
|
||||
// {
|
||||
// throw new RowImportFailedException("{$productionPlan->created_at}, {$productionPlan->production_quantity}");
|
||||
// }
|
||||
// $warnMsg[] = "Production plan already exist on '{$fDateOnly}'!";
|
||||
|
||||
$productionPlan->update([
|
||||
'plan_quantity' => $this->data['plan_quantity'],
|
||||
// 'production_quantity' => $productionPlan->production_quantity,
|
||||
// 'created_at' => $productionPlan->created_at,//$fdateTime->format('Y-m-d H:i:s'),
|
||||
// 'updated_at' => $tdateTime->format('Y-m-d H:i:s'),
|
||||
'operator_id' => $this->data['operator_id'],
|
||||
'operator_id' => $operatorName,
|
||||
]);
|
||||
$productionPlan->save();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -212,28 +171,23 @@ class ProductionPlanImporter extends Importer
|
||||
ProductionPlan::updateOrCreate([
|
||||
'plant_id' => $plant->id,
|
||||
'line_id' => $line->id,
|
||||
'shift_id' => $shift->id,
|
||||
'item_id' => $itemAgaPlant->id,
|
||||
// 'shift_id' => $shift->id,
|
||||
'plan_quantity' => $this->data['plan_quantity'],
|
||||
'production_quantity' => $this->data['production_quantity'],
|
||||
'created_at' => $fdateTime->format('Y-m-d H:i:s'),//$this->data['created_at'],
|
||||
'updated_at' => $tdateTime->format('Y-m-d H:i:s'),//$this->data['updated_at'],
|
||||
'operator_id' => $this->data['operator_id'],
|
||||
'created_at' =>now(),
|
||||
'updated_at' => now(),
|
||||
'operator_id' => $operatorName,
|
||||
]);
|
||||
return null;
|
||||
// return ProductionPlan::firstOrNew([
|
||||
// // Update existing records, matching them by `$this->data['column_name']`
|
||||
// 'email' => $this->data['email'],
|
||||
// ]);
|
||||
|
||||
// return new ProductionPlan();
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getCompletedNotificationBody(Import $import): string
|
||||
{
|
||||
$body = 'Your production plan import has completed and ' . number_format($import->successful_rows) . ' ' . str('row')->plural($import->successful_rows) . ' imported.';
|
||||
$body = 'Your production plan import has completed and '.number_format($import->successful_rows).' '.str('row')->plural($import->successful_rows).' imported.';
|
||||
|
||||
if ($failedRowsCount = $import->getFailedRowsCount()) {
|
||||
$body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to import.';
|
||||
$body .= ' '.number_format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to import.';
|
||||
}
|
||||
|
||||
return $body;
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
namespace App\Filament\Imports;
|
||||
|
||||
use App\Models\Plant;
|
||||
use App\Models\StickerDetail;
|
||||
use App\Models\StickerStructureDetail;
|
||||
use App\Models\User;
|
||||
use Filament\Actions\Imports\ImportColumn;
|
||||
use Filament\Actions\Imports\Importer;
|
||||
use Filament\Actions\Imports\Models\Import;
|
||||
use Filament\Actions\Imports\Exceptions\RowImportFailedException;
|
||||
|
||||
class StickerDetailImporter extends Importer
|
||||
{
|
||||
@@ -14,50 +18,295 @@ class StickerDetailImporter extends Importer
|
||||
public static function getColumns(): array
|
||||
{
|
||||
return [
|
||||
ImportColumn::make('stickerStructureDetail')
|
||||
ImportColumn::make('sticker_structure_detail_id')
|
||||
->requiredMapping()
|
||||
->relationship()
|
||||
->exampleHeader('Sticker ID')
|
||||
->example('123456')
|
||||
->label('STICKER ID')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('design_element_type'),
|
||||
ImportColumn::make('element_id'),
|
||||
ImportColumn::make('element_type'),
|
||||
ImportColumn::make('string_value'),
|
||||
ImportColumn::make('string_font'),
|
||||
ImportColumn::make('string_size'),
|
||||
ImportColumn::make('element_colour'),
|
||||
ImportColumn::make('string_align'),
|
||||
ImportColumn::make('string_x_value'),
|
||||
ImportColumn::make('string_y_value'),
|
||||
ImportColumn::make('shape_name'),
|
||||
ImportColumn::make('shape_pen_size'),
|
||||
ImportColumn::make('shape_x1_value'),
|
||||
ImportColumn::make('shape_y1_value'),
|
||||
ImportColumn::make('shape_x2_value'),
|
||||
ImportColumn::make('shape_y2_value'),
|
||||
ImportColumn::make('image_path'),
|
||||
ImportColumn::make('image_type'),
|
||||
ImportColumn::make('image_x'),
|
||||
ImportColumn::make('image_y'),
|
||||
ImportColumn::make('image_width'),
|
||||
ImportColumn::make('image_height'),
|
||||
ImportColumn::make('qr_value'),
|
||||
ImportColumn::make('qr_align'),
|
||||
ImportColumn::make('qr_size'),
|
||||
ImportColumn::make('qr_x_value'),
|
||||
ImportColumn::make('qr_y_value'),
|
||||
ImportColumn::make('created_by'),
|
||||
ImportColumn::make('updated_by'),
|
||||
ImportColumn::make('design_element_type')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Design Element Type')
|
||||
->label('DESIGN ELEMENT TYPE')
|
||||
->example('Text/Shape/Image/QR'),
|
||||
ImportColumn::make('element_id')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Element ID')
|
||||
->label('ELEMENT ID')
|
||||
->example('001'),
|
||||
ImportColumn::make('element_type')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Element Type')
|
||||
->label('ELEMENT TYPE')
|
||||
->example('Static/Dynamic'),
|
||||
ImportColumn::make('characteristics_type')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Characteristics Type')
|
||||
->label('CHARACTERISTICS TYPE')
|
||||
->example('ZMM values'),
|
||||
ImportColumn::make('string_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('String Value')
|
||||
->label('STRING VALUE')
|
||||
->example('1'),
|
||||
ImportColumn::make('string_font')
|
||||
->requiredMapping()
|
||||
->exampleHeader('String Font')
|
||||
->label('STRING FONT')
|
||||
->example('Arial'),
|
||||
ImportColumn::make('string_size')
|
||||
->requiredMapping()
|
||||
->exampleHeader('String Size')
|
||||
->label('STRING SIZE')
|
||||
->example('12'),
|
||||
ImportColumn::make('element_colour')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Element Colour')
|
||||
->label('ELEMENT COLOUR')
|
||||
->example('Black'),
|
||||
ImportColumn::make('string_align')
|
||||
->requiredMapping()
|
||||
->exampleHeader('String Align')
|
||||
->label('STRING ALIGN')
|
||||
->example('Left/Center/Right'),
|
||||
ImportColumn::make('string_x_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('String X Value')
|
||||
->label('STRING X VALUE')
|
||||
->example('10'),
|
||||
ImportColumn::make('string_y_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('String Y Value')
|
||||
->label('STRING Y VALUE')
|
||||
->example('20'),
|
||||
ImportColumn::make('shape_name')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Shape Name')
|
||||
->label('SHAPE NAME')
|
||||
->example('Line/Rectangle/CurvedRectangle'),
|
||||
ImportColumn::make('shape_pen_size')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Shape Pen Size')
|
||||
->label('SHAPE PEN SIZE')
|
||||
->example('0.3'),
|
||||
ImportColumn::make('curve_radius')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Curve Radius')
|
||||
->label('CURVE RADIUS')
|
||||
->example('3'),
|
||||
ImportColumn::make('shape_x1_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Shape X1 Value')
|
||||
->label('SHAPE X1 VALUE')
|
||||
->example('10'),
|
||||
ImportColumn::make('shape_y1_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Shape Y1 Value')
|
||||
->label('SHAPE Y1 VALUE')
|
||||
->example('20'),
|
||||
ImportColumn::make('shape_x2_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Shape X2 Value')
|
||||
->label('SHAPE X2 VALUE')
|
||||
->example('30'),
|
||||
ImportColumn::make('shape_y2_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Shape Y2 Value')
|
||||
->label('SHAPE Y2 VALUE')
|
||||
->example('40'),
|
||||
ImportColumn::make('image_x')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Image X')
|
||||
->label('IMAGE X')
|
||||
->example('15'),
|
||||
ImportColumn::make('image_y')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Image Y')
|
||||
->label('IMAGE Y')
|
||||
->example('25'),
|
||||
ImportColumn::make('image_width')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Image Width')
|
||||
->label('IMAGE WIDTH')
|
||||
->example('100'),
|
||||
ImportColumn::make('image_height')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Image Height')
|
||||
->label('IMAGE HEIGHT')
|
||||
->example('100'),
|
||||
ImportColumn::make('qr_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('QR Value')
|
||||
->label('QR VALUE')
|
||||
->example('246118|53246735267'),
|
||||
ImportColumn::make('qr_align')
|
||||
->requiredMapping()
|
||||
->exampleHeader('QR Align')
|
||||
->label('QR ALIGN')
|
||||
->example('Left/Center/Right'),
|
||||
ImportColumn::make('qr_size')
|
||||
->requiredMapping()
|
||||
->exampleHeader('QR Size')
|
||||
->label('QR SIZE')
|
||||
->example('10'),
|
||||
ImportColumn::make('qr_x_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('QR X Value')
|
||||
->label('QR X VALUE')
|
||||
->example('30'),
|
||||
ImportColumn::make('qr_y_value')
|
||||
->requiredMapping()
|
||||
->exampleHeader('QR Y Value')
|
||||
->label('QR Y VALUE')
|
||||
->example('40'),
|
||||
ImportColumn::make('created_by')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Created By')
|
||||
->label('CREATED BY')
|
||||
->example('RAW001234'),
|
||||
];
|
||||
}
|
||||
|
||||
public function resolveRecord(): ?StickerDetail
|
||||
{
|
||||
// return StickerDetail::firstOrNew([
|
||||
// // Update existing records, matching them by `$this->data['column_name']`
|
||||
// 'email' => $this->data['email'],
|
||||
// ]);
|
||||
|
||||
return new StickerDetail();
|
||||
$warnMsg = [];
|
||||
|
||||
$sticker = $this->data['sticker_structure_detail_id'] ?? null;
|
||||
|
||||
$stickerCode = StickerStructureDetail::where('sticker_id', $sticker)->first();
|
||||
if (!$stickerCode) {
|
||||
$warnMsg[] = "Sticker Id not found in Sticker Structure Detail";
|
||||
}
|
||||
|
||||
$stickerId = $stickerCode->id;
|
||||
|
||||
$user = User::where('name', $this->data['created_by'])->first();
|
||||
if (!$user) {
|
||||
$warnMsg[] = "User not found";
|
||||
}
|
||||
|
||||
$designType = strtolower($this->data['design_element_type'] ?? '');
|
||||
|
||||
$rules = [
|
||||
'text' => [
|
||||
'required' => ['string_x_value', 'string_y_value'],
|
||||
'allowed' => [
|
||||
'string_value',
|
||||
'string_font',
|
||||
'string_size',
|
||||
'string_align',
|
||||
'string_colour',
|
||||
'string_x_value',
|
||||
'string_y_value',
|
||||
],
|
||||
],
|
||||
|
||||
'image' => [
|
||||
'required' => ['image_x', 'image_y', 'image_width', 'image_height'],
|
||||
'allowed' => [
|
||||
'image_x',
|
||||
'image_y',
|
||||
'image_width',
|
||||
'image_height',
|
||||
],
|
||||
],
|
||||
|
||||
'shape' => [
|
||||
'required' => ['shape_name', 'shape_pen_size', 'shape_x1_value', 'shape_y1_value', 'shape_x2_value', 'shape_y2_value'],
|
||||
'allowed' => [
|
||||
'shape_name',
|
||||
'shape_pen_size',
|
||||
'curve_radius',
|
||||
'shape_x1_value',
|
||||
'shape_y1_value',
|
||||
'shape_x2_value',
|
||||
'shape_y2_value',
|
||||
],
|
||||
],
|
||||
|
||||
'qr' => [
|
||||
'required' => ['qr_value', 'qr_x_value', 'qr_y_value', 'qr_size'],
|
||||
'allowed' => [
|
||||
'qr_value',
|
||||
'qr_align',
|
||||
'qr_size',
|
||||
'qr_x_value',
|
||||
'qr_y_value',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (!isset($rules[$designType])) {
|
||||
$warnMsg[] = "Invalid Design Element Type: {$designType}";
|
||||
}
|
||||
|
||||
if (isset($rules[$designType])) {
|
||||
foreach ($rules[$designType]['required'] as $field) {
|
||||
if (empty($this->data[$field])) {
|
||||
$warnMsg[] = ucfirst($designType) . " requires {$field}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$allElementFields = [
|
||||
'string_value','string_font','string_size','string_align','string_colour',
|
||||
'string_x_value','string_y_value',
|
||||
'image_x','image_y','image_width','image_height',
|
||||
'shape_name','shape_pen_size','curve_radius',
|
||||
'shape_x1_value','shape_y1_value','shape_x2_value','shape_y2_value',
|
||||
'qr_value','qr_align','qr_size','qr_x_value','qr_y_value',
|
||||
];
|
||||
|
||||
if (isset($rules[$designType])) {
|
||||
$allowed = $rules[$designType]['allowed'];
|
||||
|
||||
foreach ($allElementFields as $field) {
|
||||
if (!in_array($field, $allowed, true) && !empty($this->data[$field])) {
|
||||
$warnMsg[] = "Field {$field} is not allowed for {$designType} element";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($warnMsg)) {
|
||||
throw new RowImportFailedException(implode(' | ', $warnMsg));
|
||||
}
|
||||
|
||||
StickerDetail::Create([
|
||||
'sticker_structure_detail_id' => $stickerId,
|
||||
'design_element_type' => $this->data['design_element_type'],
|
||||
'element_id' => $this->data['element_id'],
|
||||
'element_type' => $this->data['element_type'],
|
||||
'characteristics_type' => $this->data['characteristics_type'],
|
||||
'string_value' => $this->data['string_value'],
|
||||
'string_font' => $this->data['string_font'],
|
||||
'string_size' => $this->data['string_size'],
|
||||
'element_colour' => $this->data['element_colour'],
|
||||
'string_align' => $this->data['string_align'],
|
||||
'string_x_value' => $this->data['string_x_value'],
|
||||
'string_y_value' => $this->data['string_y_value'],
|
||||
'shape_name' => $this->data['shape_name'],
|
||||
'shape_pen_size' => $this->data['shape_pen_size'],
|
||||
'curve_radius' => $this->data['curve_radius'],
|
||||
'shape_x1_value' => $this->data['shape_x1_value'],
|
||||
'shape_y1_value' => $this->data['shape_y1_value'],
|
||||
'shape_x2_value' => $this->data['shape_x2_value'],
|
||||
'shape_y2_value' => $this->data['shape_y2_value'],
|
||||
'image_x' => $this->data['image_x'],
|
||||
'image_y' => $this->data['image_y'],
|
||||
'image_width' => $this->data['image_width'],
|
||||
'image_height' => $this->data['image_height'],
|
||||
'qr_value' => $this->data['qr_value'],
|
||||
'qr_align' => $this->data['qr_align'],
|
||||
'qr_size' => $this->data['qr_size'],
|
||||
'qr_x_value' => $this->data['qr_x_value'],
|
||||
'qr_y_value' => $this->data['qr_y_value'],
|
||||
'created_by' => $this->data['created_by'],
|
||||
]);
|
||||
|
||||
return null;
|
||||
|
||||
//return new StickerDetail();
|
||||
}
|
||||
|
||||
public static function getCompletedNotificationBody(Import $import): string
|
||||
|
||||
@@ -14,43 +14,56 @@ class StickerMappingMasterImporter extends Importer
|
||||
public static function getColumns(): array
|
||||
{
|
||||
return [
|
||||
ImportColumn::make('plant')
|
||||
ImportColumn::make('plant_id')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Plant Name')
|
||||
->example('Ransar Industries-I')
|
||||
->label('Plant Name')
|
||||
->relationship(resolveUsing:'name')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('item')
|
||||
->numeric()
|
||||
->rules(['required', 'integer']),
|
||||
ImportColumn::make('item_id')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Item Code')
|
||||
->example('630987')
|
||||
->label('Item Code')
|
||||
->relationship(resolveUsing:'code')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker1')
|
||||
->label('Sticker Label 1')
|
||||
->exampleHeader('Sticker Label 1')
|
||||
->example('Label 1')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker2')
|
||||
->label('Sticker Label 2')
|
||||
->exampleHeader('Sticker Label 2')
|
||||
->example('601468'),
|
||||
ImportColumn::make('sticker3')
|
||||
->label('Sticker Label 3')
|
||||
->exampleHeader('Sticker Label 3'),
|
||||
//->example(''),
|
||||
ImportColumn::make('sticker4')
|
||||
->label('Sticker Label 4')
|
||||
->exampleHeader('Sticker Label 4'),
|
||||
ImportColumn::make('sticker5')
|
||||
->label('Sticker Label 5')
|
||||
->exampleHeader('Sticker Label 5'),
|
||||
ImportColumn::make('created_by')
|
||||
->label('Created By')
|
||||
->exampleHeader('Created By'),
|
||||
// ImportColumn::make('updated_by'),
|
||||
->numeric()
|
||||
->rules(['required', 'integer']),
|
||||
ImportColumn::make('sticker1'),
|
||||
ImportColumn::make('sticker1_machine_id')
|
||||
->numeric()
|
||||
->rules(['integer']),
|
||||
ImportColumn::make('sticker1_print_ip'),
|
||||
ImportColumn::make('sticker2'),
|
||||
ImportColumn::make('sticker2_machine_id')
|
||||
->numeric()
|
||||
->rules(['integer']),
|
||||
ImportColumn::make('sticker2_print_ip'),
|
||||
ImportColumn::make('sticker3'),
|
||||
ImportColumn::make('sticker3_machine_id')
|
||||
->numeric()
|
||||
->rules(['integer']),
|
||||
ImportColumn::make('sticker3_print_ip'),
|
||||
ImportColumn::make('sticker4'),
|
||||
ImportColumn::make('sticker4_machine_id')
|
||||
->numeric()
|
||||
->rules(['integer']),
|
||||
ImportColumn::make('sticker4_print_ip'),
|
||||
ImportColumn::make('sticker5'),
|
||||
ImportColumn::make('sticker5_machine_id')
|
||||
->numeric()
|
||||
->rules(['integer']),
|
||||
ImportColumn::make('sticker5_print_ip'),
|
||||
ImportColumn::make('sticker6'),
|
||||
ImportColumn::make('sticker6_machine_id')
|
||||
->numeric()
|
||||
->rules(['integer']),
|
||||
ImportColumn::make('sticker6_print_ip'),
|
||||
ImportColumn::make('sticker7'),
|
||||
ImportColumn::make('sticker7_machine_id')
|
||||
->numeric()
|
||||
->rules(['integer']),
|
||||
ImportColumn::make('sticker7_print_ip'),
|
||||
ImportColumn::make('sticker8'),
|
||||
ImportColumn::make('sticker8_machine_id')
|
||||
->numeric()
|
||||
->rules(['integer']),
|
||||
ImportColumn::make('sticker8_print_ip'),
|
||||
ImportColumn::make('created_by'),
|
||||
ImportColumn::make('updated_by'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
|
||||
namespace App\Filament\Imports;
|
||||
|
||||
use App\Models\Item;
|
||||
use App\Models\ItemCharacteristic;
|
||||
use App\Models\Plant;
|
||||
use App\Models\StickerStructureDetail;
|
||||
use App\Models\User;
|
||||
use Filament\Actions\Imports\ImportColumn;
|
||||
use Filament\Actions\Imports\Importer;
|
||||
use Filament\Actions\Imports\Models\Import;
|
||||
use Filament\Actions\Imports\Exceptions\RowImportFailedException;
|
||||
|
||||
class StickerStructureDetailImporter extends Importer
|
||||
{
|
||||
@@ -14,26 +19,133 @@ class StickerStructureDetailImporter extends Importer
|
||||
public static function getColumns(): array
|
||||
{
|
||||
return [
|
||||
ImportColumn::make('sticker_id'),
|
||||
ImportColumn::make('sticker_width'),
|
||||
ImportColumn::make('sticker_height'),
|
||||
ImportColumn::make('sticker_lmargin'),
|
||||
ImportColumn::make('sticker_rmargin'),
|
||||
ImportColumn::make('sticker_tmargin'),
|
||||
ImportColumn::make('sticker_bmargin'),
|
||||
ImportColumn::make('created_by'),
|
||||
ImportColumn::make('updated_by'),
|
||||
ImportColumn::make('plant')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Plant Code')
|
||||
->example('1000')
|
||||
->label('PLANT CODE')
|
||||
->relationship(resolveUsing: 'code')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('item_characteristic_id')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Item Code')
|
||||
->example('123456')
|
||||
->label('ITEM CODE')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker_id')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Sticker ID')
|
||||
->example('123456')
|
||||
->label('STICKER ID')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker_width')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Sticker Width')
|
||||
->example('90')
|
||||
->label('STICKER WIDTH')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker_height')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Sticker Height')
|
||||
->example('90')
|
||||
->label('STICKER HEIGHT')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker_lmargin')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Sticker Left Margin')
|
||||
->example('0')
|
||||
->label('STICKER LEFT MARGIN')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker_rmargin')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Sticker Right Margin')
|
||||
->example('0')
|
||||
->label('STICKER RIGHT MARGIN')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker_tmargin')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Sticker Top Margin')
|
||||
->example('0')
|
||||
->label('STICKER TOP MARGIN')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('sticker_bmargin')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Sticker Bottom Margin')
|
||||
->example('0')
|
||||
->label('STICKER BOTTOM MARGIN')
|
||||
->rules(['required']),
|
||||
ImportColumn::make('created_by')
|
||||
->requiredMapping()
|
||||
->exampleHeader('Created By')
|
||||
->example('RAW001234')
|
||||
->label('CREATED BY')
|
||||
->rules(['required']),
|
||||
];
|
||||
}
|
||||
|
||||
public function resolveRecord(): ?StickerStructureDetail
|
||||
{
|
||||
// return StickerStructureDetail::firstOrNew([
|
||||
// // Update existing records, matching them by `$this->data['column_name']`
|
||||
// 'email' => $this->data['email'],
|
||||
// ]);
|
||||
$warnMsg = [];
|
||||
|
||||
return new StickerStructureDetail();
|
||||
$plant = Plant::where('code', $this->data['plant'])->first();
|
||||
|
||||
if (!$plant) {
|
||||
$warnMsg[] = "Plant code not found";
|
||||
}
|
||||
|
||||
// $item = null;
|
||||
// if ($plant) {
|
||||
// $item = Item::where('code', $this->data['itemCharacteristic.item'])->where('plant_id', $plant->id)->first();
|
||||
// }
|
||||
// if (!$item) {
|
||||
// $warnMsg[] = "Item not found";
|
||||
// }
|
||||
|
||||
$itemCode = $this->data['item_characteristic_id'] ?? null;
|
||||
|
||||
$item = Item::where('code', $itemCode)->first();
|
||||
if (!$item) {
|
||||
$warnMsg[] = "Item not found";
|
||||
}
|
||||
|
||||
$itemChar = ItemCharacteristic::where('item_id', $item->id)->first();
|
||||
if (!$itemChar) {
|
||||
$warnMsg[] = "Item not found in item characteristic";
|
||||
}
|
||||
|
||||
$user = User::where('name', $this->data['created_by'])->first();
|
||||
if (!$user) {
|
||||
$warnMsg[] = "User not found";
|
||||
}
|
||||
|
||||
if (!empty($warnMsg)) {
|
||||
throw new RowImportFailedException(implode(', ', $warnMsg));
|
||||
}
|
||||
else { //if (empty($warnMsg))
|
||||
$stickerId = StickerStructureDetail::where('sticker_id', $this->data['sticker_id'])
|
||||
->first();
|
||||
|
||||
if ($stickerId) {
|
||||
throw new RowImportFailedException("Sticker ID already exist!");
|
||||
}
|
||||
}
|
||||
|
||||
StickerStructureDetail::Create([
|
||||
'plant_id' => $plant->id,
|
||||
'item_characteristic_id' => $itemChar->id,
|
||||
'sticker_id' => $this->data['sticker_id'],
|
||||
'sticker_width' => $this->data['sticker_width'],
|
||||
'sticker_height' => $this->data['sticker_height'],
|
||||
'sticker_lmargin' => $this->data['sticker_lmargin'],
|
||||
'sticker_rmargin' => $this->data['sticker_rmargin'],
|
||||
'sticker_tmargin' => $this->data['sticker_tmargin'],
|
||||
'sticker_bmargin' => $this->data['sticker_bmargin'],
|
||||
'created_by' => $this->data['created_by'],
|
||||
]);
|
||||
|
||||
return null;
|
||||
|
||||
//return new StickerStructureDetail();
|
||||
}
|
||||
|
||||
public static function getCompletedNotificationBody(Import $import): string
|
||||
|
||||
20
app/Filament/Pages/NotificationSettings.php
Normal file
20
app/Filament/Pages/NotificationSettings.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class NotificationSettings extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static string $view = 'filament.pages.notification-settings';
|
||||
|
||||
|
||||
public static function getScripts(): array
|
||||
{
|
||||
return [
|
||||
asset('js/push.js')
|
||||
];
|
||||
}
|
||||
}
|
||||
163
app/Filament/Pages/ProductionCalender.php
Normal file
163
app/Filament/Pages/ProductionCalender.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\CustomerPoMaster;
|
||||
use App\Models\Plant;
|
||||
use App\Models\ProductionPlan;
|
||||
use App\Models\WireMasterPacking;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Forms\Components\Grid;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\ViewField;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
|
||||
class ProductionCalender extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static string $view = 'filament.pages.production-calender';
|
||||
|
||||
use InteractsWithForms;
|
||||
|
||||
protected $listeners = ['setWorkingDays'];
|
||||
|
||||
public $pId;
|
||||
|
||||
public array $filters = [];
|
||||
|
||||
public function setWorkingDays($days = null)
|
||||
{
|
||||
$this->form->fill([
|
||||
'working_days' => $days ?? 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->statePath('filters')
|
||||
->schema([
|
||||
Section::make('')
|
||||
->schema([
|
||||
Select::make('plant_id')
|
||||
->label('Plant')
|
||||
->reactive()
|
||||
//->options(Plant::pluck('name', 'id'))
|
||||
->options(function (callable $get) {
|
||||
$userHas = Filament::auth()->user()->plant_id;
|
||||
return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::pluck('name', 'id')->toArray();
|
||||
})
|
||||
->columnSpan(['default' => 10, 'sm' => 7])
|
||||
->required()
|
||||
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||
$set('working_days', null);
|
||||
}),
|
||||
TextInput::make('working_days')
|
||||
->label('No. of Working Days')
|
||||
->numeric()
|
||||
->readOnly()
|
||||
->columnSpan(['default' => 10, 'sm' => 2])
|
||||
->required()
|
||||
->minValue(0)
|
||||
->maxValue(31)
|
||||
->placeholder('Enter working days')
|
||||
->id('working_days'),
|
||||
|
||||
Hidden::make('month')
|
||||
->label('Month')
|
||||
->id('month'),
|
||||
|
||||
Hidden::make('year')
|
||||
->label('Year')
|
||||
->id('year'),
|
||||
|
||||
Hidden::make('selected_dates')
|
||||
->label('Selected Dates')
|
||||
->id('selected_dates'),
|
||||
|
||||
ViewField::make('save')
|
||||
->view('forms.save')
|
||||
->columnSpan(['default' => 10, 'sm' => 1]),
|
||||
|
||||
ViewField::make('calendar')
|
||||
->view('forms.calendar')
|
||||
->columnspan(10),
|
||||
])
|
||||
->columns(10)
|
||||
]);
|
||||
}
|
||||
|
||||
public function saveWorkingDays(){
|
||||
$plantId = $this->filters['plant_id'] ?? null;
|
||||
$workingDays = $this->filters['working_days'] ?? null;
|
||||
$month = $this->filters['month'] ?? null;
|
||||
$year = $this->filters['year'] ?? null;
|
||||
$dates = $this->filters['selected_dates'] ?? null;
|
||||
|
||||
if (!$plantId) {
|
||||
Notification::make()
|
||||
->title('Unknown Plant')
|
||||
->body("Please select a plant first!")
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
else if (!$workingDays) {
|
||||
Notification::make()
|
||||
->title('Unknown Working Days')
|
||||
->body("Working days can't be empty!")
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
else if (!$month) {
|
||||
Notification::make()
|
||||
->title('Unknown Month')
|
||||
->body("month can't be empty!")
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
else if (!$year) {
|
||||
Notification::make()
|
||||
->title('Unknown Year')
|
||||
->body("Year can't be empty!")
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$updated = ProductionPlan::where('plant_id', $plantId)
|
||||
->whereMonth('created_at', $month)
|
||||
->whereYear('created_at', $year)
|
||||
->update([
|
||||
'working_days' => $workingDays,
|
||||
'leave_dates' => $dates,
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
if ($updated) {
|
||||
Notification::make()
|
||||
->title('Success')
|
||||
->body("Working days updated successfully!")
|
||||
->success()
|
||||
->send();
|
||||
} else {
|
||||
Notification::make()
|
||||
->title('No Records Updated')
|
||||
->body("No production plans found for this plant and month.")
|
||||
->warning()
|
||||
->send();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
173
app/Filament/Pages/ProductionTarget.php
Normal file
173
app/Filament/Pages/ProductionTarget.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\Plant;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
class ProductionTarget extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static string $view = 'filament.pages.production-target';
|
||||
|
||||
public array $filters = [];
|
||||
|
||||
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->statePath('filters')
|
||||
->schema([
|
||||
Section::make('')
|
||||
->schema([
|
||||
Select::make('plant_id')
|
||||
->label('Plant')
|
||||
->reactive()
|
||||
->options(function (callable $get) {
|
||||
$userHas = Filament::auth()->user()->plant_id;
|
||||
return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::pluck('name', 'id')->toArray();
|
||||
})
|
||||
->required()
|
||||
->afterStateUpdated(function ($state, callable $get, $set) {
|
||||
$set('line_id', null);
|
||||
$set('year', null);
|
||||
$set('month', null);
|
||||
$this->dispatch('loadData',$state, '', '', '');
|
||||
}),
|
||||
Select::make('line_id')
|
||||
->label('Line')
|
||||
->required()
|
||||
->columnSpan(1)
|
||||
->options(function (callable $get) {
|
||||
if (!$get('plant_id')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return \App\Models\Line::where('plant_id', $get('plant_id'))
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
})
|
||||
->reactive()
|
||||
->afterStateUpdated(function ($state, callable $get, $set) {
|
||||
$plantId = $get('plant_id');
|
||||
$set('year', null);
|
||||
$set('month', null);
|
||||
$this->dispatch('loadData',$plantId, $state, '', '');
|
||||
}),
|
||||
Select::make('year')
|
||||
->label('Year')
|
||||
->reactive()
|
||||
->options([
|
||||
'2026' => '2026',
|
||||
'2027' => '2027',
|
||||
'2028' => '2028',
|
||||
'2029' => '2029',
|
||||
'2030' => '2030',
|
||||
'2031' => '2031',
|
||||
'2032' => '2032',
|
||||
'2033' => '2033',
|
||||
'2034' => '2034',
|
||||
'2035' => '2035',
|
||||
'2036' => '2036',
|
||||
'2037' => '2037',
|
||||
'2038' => '2038',
|
||||
'2039' => '2039',
|
||||
'2040' => '2040',
|
||||
])
|
||||
->required()
|
||||
->afterStateUpdated(function ($state, callable $get, $set) {
|
||||
$set('month', null);
|
||||
$plantId = $get('plant_id');
|
||||
$lineId = $get('line_id');
|
||||
$this->dispatch('loadData',$plantId, $lineId, $state, '');
|
||||
}),
|
||||
|
||||
Select::make('month')
|
||||
->label('Month')
|
||||
->reactive()
|
||||
->options([
|
||||
'01' => 'January',
|
||||
'02' => 'February',
|
||||
'03' => 'March',
|
||||
'04' => 'April',
|
||||
'05' => 'May',
|
||||
'06' => 'June',
|
||||
'07' => 'July',
|
||||
'08' => 'August',
|
||||
'09' => 'September',
|
||||
'10' => 'October',
|
||||
'11' => 'November',
|
||||
'12' => 'December',
|
||||
])
|
||||
->required()
|
||||
->afterStateUpdated(function ($state, callable $get) {
|
||||
|
||||
$plantId = $get('plant_id');
|
||||
$lineId = $get('line_id');
|
||||
// $month = $get('month');
|
||||
$year = $get('year');
|
||||
|
||||
$month = (int) $get('month');
|
||||
|
||||
if (!$month) {
|
||||
return;
|
||||
}
|
||||
$this->dispatch('loadData', $plantId, $lineId, $month, $year);
|
||||
}),
|
||||
|
||||
])
|
||||
->columns(4)
|
||||
]);
|
||||
}
|
||||
|
||||
public function export(){
|
||||
|
||||
$plantId = $this->filters['plant_id'] ?? null;
|
||||
$lineId = $this->filters['line_id'] ?? null;
|
||||
$year = $this->filters['year'] ?? null;
|
||||
$month = $this->filters['month'] ?? null;
|
||||
|
||||
if (! $plantId) {
|
||||
Notification::make()
|
||||
->title('Plant')
|
||||
->body("please select plant to export data..!")
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
else if (! $lineId) {
|
||||
Notification::make()
|
||||
->title('Line')
|
||||
->body("please select line to export data..!")
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
else if (! $year) {
|
||||
Notification::make()
|
||||
->title('Year')
|
||||
->body("please select year to export data..!")
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
else if (! $month) {
|
||||
Notification::make()
|
||||
->title('Month')
|
||||
->body("please select month to export data..!")
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dispatch('loadData1' ,$plantId, $lineId, $year, $month);
|
||||
}
|
||||
}
|
||||
163
app/Filament/Pages/RfqDashboard.php
Normal file
163
app/Filament/Pages/RfqDashboard.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Plant;
|
||||
use App\Models\RequestQuotation;
|
||||
use App\Models\RfqTransporterBid;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Widgets\Widget;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
|
||||
|
||||
class RfqDashboard extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static string $view = 'filament.pages.rfq-dashboard';
|
||||
|
||||
protected static ?string $navigationGroup = 'RFQ Dashboard';
|
||||
|
||||
use HasFiltersForm;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
session()->forget(['transport_name']);
|
||||
session()->forget(['rfq_number']);
|
||||
$this->filtersForm->fill([
|
||||
'transport_name' => null,
|
||||
'rfq_number' => null
|
||||
]);
|
||||
}
|
||||
|
||||
public function filtersForm(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->statePath('filters')
|
||||
->schema([
|
||||
Section::make('')
|
||||
->schema([
|
||||
// Select::make('plant')
|
||||
// ->label('Select Plant')
|
||||
// ->reactive()
|
||||
// ->options(function (callable $get) {
|
||||
// $userHas = Filament::auth()->user()->plant_id;
|
||||
// return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::pluck('name', 'id')->toArray();
|
||||
// })
|
||||
// ->afterStateUpdated(function ($state,callable $set) {
|
||||
// session(['selected_plant' => $state]);
|
||||
// // $set('rfq_number', null);
|
||||
// session()->forget('rfq_number');
|
||||
// }),
|
||||
|
||||
Select::make('rfq_number')
|
||||
->label('Select RFQ Number')
|
||||
->reactive()
|
||||
->options(function (callable $get) {
|
||||
|
||||
return RequestQuotation::orderBy('rfq_number')
|
||||
->pluck('rfq_number', 'id')
|
||||
->toArray();
|
||||
})
|
||||
->afterStateUpdated(function ($state, callable $set) {
|
||||
session(['rfq_id' => $state]);
|
||||
$set('transport_name', null);
|
||||
session()->forget('transport_name');
|
||||
}),
|
||||
|
||||
Select::make('transport_name')
|
||||
->label('User name')
|
||||
->reactive()
|
||||
->options(function (callable $get) {
|
||||
$rfqId = $get('rfq_number');
|
||||
|
||||
if (!$rfqId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$user = Filament::auth()->user();
|
||||
|
||||
if ($user->hasRole(['Super Admin', 'Rfq Supervisor'])) {
|
||||
return RfqTransporterBid::query()
|
||||
->where('request_quotation_id', $rfqId)
|
||||
->whereNotNull('transporter_name')
|
||||
->distinct()
|
||||
->pluck('transporter_name', 'transporter_name')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
return RfqTransporterBid::query()
|
||||
->where('request_quotation_id', $rfqId)
|
||||
->where('transporter_name', $user->name)
|
||||
->distinct()
|
||||
->pluck('transporter_name', 'transporter_name')
|
||||
->toArray();
|
||||
})
|
||||
->afterStateUpdated(function ($state, callable $set) {
|
||||
session(['transport_name' => $state]);
|
||||
}),
|
||||
|
||||
// Select::make('transport_name')
|
||||
// ->label('User name')
|
||||
// ->reactive()
|
||||
// ->options(function () {
|
||||
// $user = Filament::auth()->user();
|
||||
|
||||
// if ($user->hasRole(['Super Admin', 'Rfq Supervisor'])) {
|
||||
// return RfqTransporterBid::query()
|
||||
// ->whereNotNull('transporter_name')
|
||||
// ->distinct()
|
||||
// ->pluck('transporter_name', 'transporter_name')
|
||||
// ->toArray();
|
||||
// }
|
||||
|
||||
// return RfqTransporterBid::query()
|
||||
// ->where('transporter_name', $user->name)
|
||||
// ->distinct()
|
||||
// ->pluck('transporter_name', 'transporter_name')
|
||||
// ->toArray();
|
||||
// })
|
||||
// ->afterStateUpdated(function ($state, callable $set) {
|
||||
// session(['transport_name' => $state]);
|
||||
// $set('rfq_number', null);
|
||||
// session()->forget('rfq_number');
|
||||
// }),
|
||||
|
||||
// Select::make('rfq_number')
|
||||
// ->label('Select RFQ Number')
|
||||
// ->reactive()
|
||||
// ->options(function (callable $get) {
|
||||
// $transportName = $get('transport_name');
|
||||
|
||||
// if (!$transportName) {
|
||||
// return [];
|
||||
// }
|
||||
|
||||
// return RequestQuotation::where('transporter_name', $transportName)
|
||||
// ->pluck('rfq_number', 'rfq_number')
|
||||
// ->toArray();
|
||||
// })
|
||||
// ->afterStateUpdated(function ($state) {
|
||||
// session(['rfq_number' => $state]);
|
||||
// }),
|
||||
])
|
||||
->columns(2),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return Auth::check() && Auth::user()->can('view rfq dashboard');
|
||||
}
|
||||
}
|
||||
58
app/Filament/Pages/RfqOverview.php
Normal file
58
app/Filament/Pages/RfqOverview.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\RequestQuotation;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RfqOverview extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static string $view = 'filament.pages.rfq-overview';
|
||||
|
||||
protected static ?string $navigationGroup = 'RFQ Dashboard';
|
||||
|
||||
use HasFiltersForm;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
session()->forget(['rfq_id']);
|
||||
$this->filtersForm->fill([
|
||||
'rfq_id' => null
|
||||
]);
|
||||
}
|
||||
|
||||
public function filtersForm(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->statePath('filters')
|
||||
->schema([
|
||||
Section::make('')
|
||||
->schema([
|
||||
Select::make('rfq_number')
|
||||
->label('Select RFQ Number')
|
||||
->reactive()
|
||||
->options(function (callable $get) {
|
||||
return RequestQuotation::orderBy('rfq_number')
|
||||
->pluck('rfq_number', 'id')
|
||||
->toArray();
|
||||
})
|
||||
->afterStateUpdated(function ($state, callable $set) {
|
||||
session(['rfq_id' => $state]);
|
||||
}),
|
||||
])
|
||||
->columns(1),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return Auth::check() && Auth::user()->can('view rfq overview dashboard');
|
||||
}
|
||||
}
|
||||
17
app/Filament/Pages/Welcome.php
Normal file
17
app/Filament/Pages/Welcome.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class Welcome extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static string $view = 'filament.pages.welcome';
|
||||
|
||||
public function getHeading(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
217
app/Filament/Resources/EmployeeMasterResource.php
Normal file
217
app/Filament/Resources/EmployeeMasterResource.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Exports\EmployeeMasterExporter;
|
||||
use App\Filament\Imports\EmployeeMasterImporter;
|
||||
use App\Filament\Resources\EmployeeMasterResource\Pages;
|
||||
use App\Filament\Resources\EmployeeMasterResource\RelationManagers;
|
||||
use App\Models\EmployeeMaster;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Filament\Tables\Actions\ExportAction;
|
||||
use Filament\Tables\Actions\ImportAction;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class EmployeeMasterResource extends Resource
|
||||
{
|
||||
protected static ?string $model = EmployeeMaster::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\Select::make('plant_id')
|
||||
->label('Plant')
|
||||
->relationship('plant', 'name')
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('name')
|
||||
->label('Name')
|
||||
->required()
|
||||
->reactive()
|
||||
->extraInputAttributes([
|
||||
'oninput' => 'this.value = this.value.replace(/[^a-zA-Z\s]/g, "")',
|
||||
]),
|
||||
Forms\Components\TextInput::make('code')
|
||||
->label('ID')
|
||||
->extraInputAttributes([
|
||||
'oninput' => 'this.value = this.value.replace(/[^a-zA-Z0-9]/g, "")',])
|
||||
->required()
|
||||
->unique(
|
||||
table: 'employee_masters',
|
||||
column: 'code',
|
||||
ignoreRecord: true
|
||||
)
|
||||
->validationMessages([
|
||||
'unique' => 'Duplicate employee code already exists.',
|
||||
]),
|
||||
Forms\Components\TextInput::make('department')
|
||||
->label('Department')
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('designation')
|
||||
->label('Designation')
|
||||
->extraInputAttributes([
|
||||
'oninput' => 'this.value = this.value.replace(/[^a-zA-Z\s]/g, "")',])
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('email')
|
||||
->label('Email')
|
||||
->email()
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('mobile_number')
|
||||
->label('Mobile Number')
|
||||
->length(10)
|
||||
->reactive()
|
||||
->extraInputAttributes([
|
||||
'oninput' => 'this.value = this.value.replace(/[^0-9]/g, "").slice(0, 10)', // blocks non-numbers + limits to 10 chars
|
||||
'maxlength' => 10,
|
||||
])
|
||||
->required()
|
||||
->unique(
|
||||
table: 'employee_masters',
|
||||
column: 'mobile_number',
|
||||
ignoreRecord: true
|
||||
)
|
||||
->validationMessages([
|
||||
'unique' => 'Duplicate mobile number already exists.',
|
||||
]),
|
||||
Forms\Components\Hidden::make('created_by')
|
||||
->label('Created by')
|
||||
->default(Filament::auth()->user()?->name ?? ''),
|
||||
Forms\Components\Hidden::make('updated_by')
|
||||
->label('Updated by')
|
||||
->default(Filament::auth()->user()?->name ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('No.')
|
||||
->label('No.')
|
||||
->getStateUsing(function ($record, $livewire, $column, $rowLoop) {
|
||||
$paginator = $livewire->getTableRecords();
|
||||
$perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
|
||||
$currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;
|
||||
return ($currentPage - 1) * $perPage + $rowLoop->iteration;
|
||||
})
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('plant.name')
|
||||
->numeric()
|
||||
->sortable()
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->label('Name')
|
||||
->sortable()
|
||||
->alignCenter()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('code')
|
||||
->label('Employee ID')
|
||||
->sortable()
|
||||
->alignCenter()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('department')
|
||||
->label('Department')
|
||||
->sortable()
|
||||
->alignCenter()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('designation')
|
||||
->label('Designation')
|
||||
->sortable()
|
||||
->alignCenter()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('email')
|
||||
->label('Email')
|
||||
->sortable()
|
||||
->alignCenter()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('mobile_number')
|
||||
->label('Mobile Number')
|
||||
->sortable()
|
||||
->alignCenter()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true)
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('created_by')
|
||||
->label('Created by')
|
||||
->sortable()
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('updated_by')
|
||||
->label('Updated by')
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true)
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('deleted_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true)
|
||||
->alignCenter(),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TrashedFilter::make(),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ViewAction::make(),
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||
Tables\Actions\RestoreBulkAction::make(),
|
||||
]),
|
||||
])
|
||||
->headerActions([
|
||||
ImportAction::make()
|
||||
->importer(EmployeeMasterImporter::class)
|
||||
->visible(function() {
|
||||
return Filament::auth()->user()->can('view import employee master');
|
||||
}),
|
||||
ExportAction::make()
|
||||
->exporter(EmployeeMasterExporter::class)
|
||||
->visible(function() {
|
||||
return Filament::auth()->user()->can('view export employee master');
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListEmployeeMasters::route('/'),
|
||||
'create' => Pages\CreateEmployeeMaster::route('/create'),
|
||||
'view' => Pages\ViewEmployeeMaster::route('/{record}'),
|
||||
'edit' => Pages\EditEmployeeMaster::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmployeeMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EmployeeMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateEmployeeMaster extends CreateRecord
|
||||
{
|
||||
protected static string $resource = EmployeeMasterResource::class;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmployeeMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EmployeeMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditEmployeeMaster extends EditRecord
|
||||
{
|
||||
protected static string $resource = EmployeeMasterResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\ViewAction::make(),
|
||||
Actions\DeleteAction::make(),
|
||||
Actions\ForceDeleteAction::make(),
|
||||
Actions\RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmployeeMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EmployeeMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListEmployeeMasters extends ListRecords
|
||||
{
|
||||
protected static string $resource = EmployeeMasterResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmployeeMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EmployeeMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewEmployeeMaster extends ViewRecord
|
||||
{
|
||||
protected static string $resource = EmployeeMasterResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use App\Models\InvoiceValidation;
|
||||
use App\Models\Item;
|
||||
use App\Models\Plant;
|
||||
use App\Models\StickerMaster;
|
||||
use App\Notifications\PushAlertNotification;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Concerns\ExposesTableToWidgets;
|
||||
@@ -136,6 +137,10 @@ class CreateInvoiceValidation extends CreateRecord
|
||||
->send();
|
||||
$this->dispatch('playNotificationSound');
|
||||
|
||||
$user1 = Filament::auth()->user();
|
||||
|
||||
$user1->notify(new PushAlertNotification());
|
||||
|
||||
$this->form->fill([
|
||||
'plant_id' => $plantId,
|
||||
'invoice_number' => $invoiceNumber,
|
||||
@@ -2285,6 +2290,7 @@ class CreateInvoiceValidation extends CreateRecord
|
||||
->danger()
|
||||
->seconds(3)
|
||||
->send();
|
||||
|
||||
$this->dispatch('playWarnSound');
|
||||
|
||||
$this->form->fill([
|
||||
|
||||
@@ -82,6 +82,9 @@ class ItemResource extends Resource
|
||||
Forms\Components\TextInput::make('category')
|
||||
->label('Category')
|
||||
->placeholder('Scan the Category'),
|
||||
Forms\Components\TextInput::make('category')
|
||||
->label('Category')
|
||||
->placeholder('Scan the Category'),
|
||||
Forms\Components\TextInput::make('code')
|
||||
->required()
|
||||
->placeholder('Scan the valid code')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@ use AlperenErsoy\FilamentExport\Actions\FilamentExportBulkAction;
|
||||
use App\Filament\Exports\ProductionPlanExporter;
|
||||
use App\Filament\Imports\ProductionPlanImporter;
|
||||
use App\Filament\Resources\ProductionPlanResource\Pages;
|
||||
use App\Filament\Resources\ProductionPlanResource\RelationManagers;
|
||||
use App\Models\Block;
|
||||
use App\Models\Item;
|
||||
use App\Models\Line;
|
||||
use App\Models\Plant;
|
||||
use App\Models\ProductionPlan;
|
||||
@@ -16,19 +16,18 @@ use Carbon\Carbon;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\DateTimePicker;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Actions\ExportAction;
|
||||
use Filament\Tables\Actions\ImportAction;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Tables\Actions\ExportAction;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
|
||||
class ProductionPlanResource extends Resource
|
||||
{
|
||||
@@ -55,22 +54,22 @@ class ProductionPlanResource extends Resource
|
||||
->reactive()
|
||||
->options(function (callable $get) {
|
||||
$userHas = Filament::auth()->user()->plant_id;
|
||||
return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::pluck('name', 'id')->toArray();
|
||||
|
||||
return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray();
|
||||
})
|
||||
->default(function () {
|
||||
return optional(ProductionPlan::latest()->first())->plant_id;
|
||||
})
|
||||
->disabled(fn (Get $get) => !empty($get('id')))
|
||||
->disabled(fn (Get $get) => ! empty($get('id')))
|
||||
// ->afterStateUpdated(fn ($set) => $set('block_name', null))
|
||||
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
$set('block_name', null);
|
||||
if (!$plantId) {
|
||||
if (! $plantId) {
|
||||
$set('ppPlantError', 'Please select a plant first.');
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$set('ppPlantError', null);
|
||||
}
|
||||
})
|
||||
@@ -79,102 +78,6 @@ class ProductionPlanResource extends Resource
|
||||
])
|
||||
->hint(fn ($get) => $get('ppPlantError') ? $get('ppPlantError') : null)
|
||||
->hintColor('danger'),
|
||||
Forms\Components\Select::make('block_name')
|
||||
->required()
|
||||
// ->nullable()
|
||||
->label('Block')
|
||||
->options(function (callable $get) {
|
||||
if (!$get('plant_id')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Block::where('plant_id', $get('plant_id'))
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
})
|
||||
->reactive()
|
||||
->default(function () {
|
||||
$latestShiftId = optional(ProductionPlan::latest()->first())->shift_id;
|
||||
return optional(Shift::where('id', $latestShiftId)->first())->block_id;
|
||||
})
|
||||
//->afterStateUpdated(fn ($set) => $set('shift_id', null))
|
||||
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||
if($get('id'))
|
||||
{
|
||||
$getShift = ProductionPlan::where('id', $get('id'))->first();
|
||||
$getBlock = Shift::where('id', $getShift->shift_id)->first();
|
||||
if($getBlock->block_id)
|
||||
{
|
||||
$set('block_name', $getBlock->block_id);
|
||||
$set('ppBlockError', null);
|
||||
}
|
||||
}
|
||||
|
||||
$blockId = $get('block_name');
|
||||
$set('shift_id', null);
|
||||
|
||||
if (!$blockId) {
|
||||
$set('ppBlockError', 'Please select a block first.');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$set('ppBlockError', null);
|
||||
}
|
||||
})
|
||||
->extraAttributes(fn ($get) => [
|
||||
'class' => $get('ppBlockError') ? 'border-red-500' : '',
|
||||
])
|
||||
->hint(fn ($get) => $get('ppBlockError') ? $get('ppBlockError') : null)
|
||||
->hintColor('danger'),
|
||||
Forms\Components\Select::make('shift_id')
|
||||
->relationship('shift', 'name')
|
||||
->required()
|
||||
// ->nullable()
|
||||
->autofocus(true)
|
||||
->options(function (callable $get) {
|
||||
if (!$get('plant_id') || !$get('block_name')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Shift::where('plant_id', $get('plant_id'))
|
||||
->where('block_id', $get('block_name'))
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
})
|
||||
->reactive()
|
||||
->default(function () {
|
||||
return optional(ProductionPlan::latest()->first())->shift_id;
|
||||
})
|
||||
// ->afterStateUpdated(fn ($set) => $set('line_id', null))
|
||||
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||
if($get('id'))
|
||||
{
|
||||
$getShift = ProductionPlan::where('id', $get('id'))->first();
|
||||
if($getShift->shift_id)
|
||||
{
|
||||
$set('shift_id', $getShift->shift_id);
|
||||
$set('ppShiftError', null);
|
||||
}
|
||||
}
|
||||
|
||||
$curShiftId = $get('shift_id');
|
||||
$set('line_id', null);
|
||||
|
||||
if (!$curShiftId) {
|
||||
$set('ppShiftError', 'Please select a shift first.');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$set('ppShiftError', null);
|
||||
}
|
||||
})
|
||||
->extraAttributes(fn ($get) => [
|
||||
'class' => $get('ppShiftError') ? 'border-red-500' : '',
|
||||
])
|
||||
->hint(fn ($get) => $get('ppShiftError') ? $get('ppShiftError') : null)
|
||||
->hintColor('danger'),
|
||||
Forms\Components\Select::make('line_id')
|
||||
->relationship('line', 'name')
|
||||
->required()
|
||||
@@ -185,7 +88,7 @@ class ProductionPlanResource extends Resource
|
||||
// ->toArray() // Convert collection to array
|
||||
// )
|
||||
->options(function (callable $get) {
|
||||
if (!$get('plant_id') || !$get('block_name') || !$get('shift_id')) {
|
||||
if (! $get('plant_id')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -197,248 +100,257 @@ class ProductionPlanResource extends Resource
|
||||
// ->default(function () {
|
||||
// return optional(ProductionPlan::latest()->first())->line_id;
|
||||
// })
|
||||
// ->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||
// if ($get('id')) {
|
||||
// $getShift = ProductionPlan::where('id', $get('id'))->first();
|
||||
// if ($getShift->line_id) {
|
||||
// $set('line_id', $getShift->line_id);
|
||||
// $set('ppLineError', null);
|
||||
// }
|
||||
// } else {
|
||||
// $currentDT = Carbon::now()->toDateTimeString();
|
||||
// $set('created_at', $currentDT);
|
||||
// $set('update_date', null);
|
||||
// }
|
||||
|
||||
// $lineId = $get('line_id');
|
||||
// // $set('plan_quantity', null);
|
||||
|
||||
// if (! $lineId) {
|
||||
// $set('ppLineError', 'Please select a line first.');
|
||||
|
||||
// return;
|
||||
// } else {
|
||||
// $isUpdate = ! empty($get('id'));
|
||||
// if (! $isUpdate) {
|
||||
// $exists = ProductionPlan::where('plant_id', $get('plant_id'))
|
||||
// ->where('shift_id', $get('shift_id'))
|
||||
// ->where('line_id', $get('line_id'))
|
||||
// ->whereDate('created_at', today())
|
||||
// ->latest()
|
||||
// ->exists();
|
||||
|
||||
// if ($exists) {
|
||||
// $set('line_id', null);
|
||||
// $set('ppLineError', 'Production plan already updated.');
|
||||
|
||||
// return;
|
||||
// } else {
|
||||
// $existShifts = ProductionPlan::where('plant_id', $get('plant_id'))
|
||||
// ->where('shift_id', $get('shift_id'))
|
||||
// ->where('line_id', $get('line_id'))
|
||||
// ->whereDate('created_at', Carbon::yesterday())
|
||||
// ->latest()
|
||||
// ->exists();
|
||||
|
||||
// if ($existShifts) { // if ($existShifts->count() > 0)
|
||||
// // $currentDate = date('Y-m-d');
|
||||
// $yesterday = date('Y-m-d', strtotime('-1 days'));
|
||||
|
||||
// $shiftId = Shift::where('id', $get('shift_id'))
|
||||
// ->first();
|
||||
|
||||
// [$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
// $hRs = (int) $hRs;
|
||||
// // $miNs = (int) $miNs;-*/
|
||||
|
||||
// $totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
// $from_dt = $yesterday.' '.$shiftId->start_time;
|
||||
|
||||
// $to_dt = date('Y-m-d H:i:s', strtotime($from_dt." + $totalMinutes minutes"));
|
||||
|
||||
// $currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// // Check if current date time is within the range
|
||||
// if ($currentDateTime >= $from_dt && $currentDateTime < $to_dt) {
|
||||
// // echo "Choosed a valid shift...";
|
||||
|
||||
// $set('line_id', null);
|
||||
// $set('ppLineError', 'Production plan already updated.');
|
||||
|
||||
// // $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
// return;
|
||||
// } else {
|
||||
// $currentDate = date('Y-m-d');
|
||||
|
||||
// $shiftId = Shift::where('id', $get('shift_id'))
|
||||
// ->first();
|
||||
|
||||
// [$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
// $hRs = (int) $hRs;
|
||||
// // $miNs = (int) $miNs;-*/
|
||||
|
||||
// $totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
// $from_dt = $currentDate.' '.$shiftId->start_time;
|
||||
|
||||
// $to_dt = date('Y-m-d H:i:s', strtotime($from_dt." + $totalMinutes minutes"));
|
||||
|
||||
// $currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// // Check if current date time is within the range
|
||||
// if (! ($currentDateTime >= $from_dt && $currentDateTime < $to_dt)) {
|
||||
// // echo "Choosed a valid shift...";
|
||||
|
||||
// $set('line_id', null);
|
||||
// $set('ppLineError', 'Choosed a invalid shift.');
|
||||
|
||||
// // $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $set('ppLineError', null);
|
||||
|
||||
// return;
|
||||
// } else {
|
||||
// // $currentDate = date('Y-m-d');
|
||||
// $yesterday = date('Y-m-d', strtotime('-1 days'));
|
||||
|
||||
// $shiftId = Shift::where('id', $get('shift_id'))
|
||||
// ->first();
|
||||
|
||||
// [$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
// $hRs = (int) $hRs;
|
||||
// // $miNs = (int) $miNs;-*/
|
||||
|
||||
// $totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
// $from_dt = $yesterday.' '.$shiftId->start_time;
|
||||
|
||||
// $to_dt = date('Y-m-d H:i:s', strtotime($from_dt." + $totalMinutes minutes"));
|
||||
|
||||
// $currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// // Check if current date time is within the range
|
||||
// if ($currentDateTime >= $from_dt && $currentDateTime < $to_dt) {
|
||||
// // echo "Choosed a valid shift...";
|
||||
|
||||
// // here i'm updating created as yesterday
|
||||
// $set('created_at', $from_dt);
|
||||
// $set('update_date', '1');
|
||||
|
||||
// $set('ppLineError', null);
|
||||
|
||||
// // $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
// return;
|
||||
// } else {
|
||||
// $currentDate = date('Y-m-d');
|
||||
|
||||
// $shiftId = Shift::where('id', $get('shift_id'))
|
||||
// ->first();
|
||||
|
||||
// [$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
// $hRs = (int) $hRs;
|
||||
// // $miNs = (int) $miNs;-*/
|
||||
|
||||
// $totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
// $from_dt = $currentDate.' '.$shiftId->start_time;
|
||||
|
||||
// $to_dt = date('Y-m-d H:i:s', strtotime($from_dt." + $totalMinutes minutes"));
|
||||
|
||||
// $currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// // Check if current date time is within the range
|
||||
// if (! ($currentDateTime >= $from_dt && $currentDateTime < $to_dt)) {
|
||||
// // echo "Choosed a valid shift...";
|
||||
|
||||
// $set('line_id', null);
|
||||
// $set('ppLineError', 'Choosed a invalid shift.');
|
||||
|
||||
// // $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $set('ppLineError', null);
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // $exists = ProductionPlan::where('plant_id', $get('plant_id'))
|
||||
// // //->where('shift_id', $get('shift_id'))
|
||||
// // ->where('line_id', $get('line_id'))
|
||||
// // ->whereDate('created_at', today())
|
||||
// // ->latest() // Orders by created_at DESC
|
||||
// // ->first();
|
||||
|
||||
// // if ($exists)
|
||||
// // {
|
||||
// // $existingShifts = ProductionPlan::where('plant_id', $get('plant_id'))
|
||||
// // //->where('shift_id', $get('shift_id'))
|
||||
// // ->where('line_id', $get('line_id'))
|
||||
// // // ->whereDate('created_at', today())
|
||||
// // ->whereDate('created_at', today())
|
||||
// // ->get();
|
||||
|
||||
// // foreach ($existingShifts as $shift) {
|
||||
// // $curShiftId = $shift->shift_id;
|
||||
|
||||
// // $currentDate = date('Y-m-d');
|
||||
|
||||
// // $shiftId = \App\Models\Shift::where('id', $curShiftId)
|
||||
// // ->first();
|
||||
|
||||
// // [$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
// // $hRs = (int) $hRs;
|
||||
// // // $miNs = (int) $miNs;-*/
|
||||
|
||||
// // $totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
// // $from_dt = $currentDate . ' ' . $shiftId->start_time;
|
||||
|
||||
// // $to_dt = date('Y-m-d H:i:s', strtotime($from_dt . " + $totalMinutes minutes"));
|
||||
|
||||
// // $currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// // // Check if current date time is within the range
|
||||
// // if ($currentDateTime >= $from_dt && $currentDateTime < $to_dt) {
|
||||
// // //echo "Choosed a valid shift...";
|
||||
// // // $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
|
||||
// // $set('line_id', null);
|
||||
// // $set('ppLineError', 'Production plan already updated.');
|
||||
// // return;
|
||||
// // }
|
||||
// // // else {
|
||||
// // // $set('ppLineError', 'Choosed a invalid shift...');
|
||||
// // // return;
|
||||
// // // }
|
||||
// // }
|
||||
// // $set('ppLineError', null);
|
||||
// // return;
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
// $set('ppLineError', null);
|
||||
// }
|
||||
// })
|
||||
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||
if($get('id'))
|
||||
{
|
||||
$getShift = ProductionPlan::where('id', $get('id'))->first();
|
||||
if($getShift->line_id)
|
||||
{
|
||||
$set('line_id', $getShift->line_id);
|
||||
$set('ppLineError', null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$currentDT = Carbon::now()->toDateTimeString();
|
||||
$set('created_at', $currentDT);
|
||||
$set('update_date', null);
|
||||
}
|
||||
|
||||
$lineId = $get('line_id');
|
||||
// $set('plan_quantity', null);
|
||||
|
||||
if (!$lineId) {
|
||||
$set('ppLineError', 'Please select a line first.');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$isUpdate = !empty($get('id'));
|
||||
if (!$isUpdate)
|
||||
{
|
||||
$exists = ProductionPlan::where('plant_id', $get('plant_id'))
|
||||
->where('shift_id', $get('shift_id'))
|
||||
->where('line_id', $get('line_id'))
|
||||
->whereDate('created_at', today())
|
||||
->latest()
|
||||
->exists();
|
||||
|
||||
if ($exists)
|
||||
{
|
||||
$set('line_id', null);
|
||||
$set('ppLineError', 'Production plan already updated.');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$existShifts = ProductionPlan::where('plant_id', $get('plant_id'))
|
||||
->where('shift_id', $get('shift_id'))
|
||||
->where('line_id', $get('line_id'))
|
||||
->whereDate('created_at', Carbon::yesterday())
|
||||
->latest()
|
||||
->exists();
|
||||
|
||||
if ($existShifts) //if ($existShifts->count() > 0)
|
||||
{
|
||||
//$currentDate = date('Y-m-d');
|
||||
$yesterday = date('Y-m-d', strtotime('-1 days'));
|
||||
|
||||
$shiftId = Shift::where('id', $get('shift_id'))
|
||||
->first();
|
||||
|
||||
[$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
$hRs = (int) $hRs;
|
||||
// $miNs = (int) $miNs;-*/
|
||||
|
||||
$totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
$from_dt = $yesterday . ' ' . $shiftId->start_time;
|
||||
|
||||
$to_dt = date('Y-m-d H:i:s', strtotime($from_dt . " + $totalMinutes minutes"));
|
||||
|
||||
$currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// Check if current date time is within the range
|
||||
if ($currentDateTime >= $from_dt && $currentDateTime < $to_dt) {
|
||||
//echo "Choosed a valid shift...";
|
||||
|
||||
$set('line_id', null);
|
||||
$set('ppLineError', 'Production plan already updated.');
|
||||
// $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$currentDate = date('Y-m-d');
|
||||
|
||||
$shiftId = Shift::where('id', $get('shift_id'))
|
||||
->first();
|
||||
|
||||
[$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
$hRs = (int) $hRs;
|
||||
// $miNs = (int) $miNs;-*/
|
||||
|
||||
$totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
$from_dt = $currentDate . ' ' . $shiftId->start_time;
|
||||
|
||||
$to_dt = date('Y-m-d H:i:s', strtotime($from_dt . " + $totalMinutes minutes"));
|
||||
|
||||
$currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// Check if current date time is within the range
|
||||
if (!($currentDateTime >= $from_dt && $currentDateTime < $to_dt)) {
|
||||
//echo "Choosed a valid shift...";
|
||||
|
||||
$set('line_id', null);
|
||||
$set('ppLineError', 'Choosed a invalid shift.');
|
||||
// $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$set('ppLineError', null);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//$currentDate = date('Y-m-d');
|
||||
$yesterday = date('Y-m-d', strtotime('-1 days'));
|
||||
|
||||
$shiftId = Shift::where('id', $get('shift_id'))
|
||||
->first();
|
||||
|
||||
[$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
$hRs = (int) $hRs;
|
||||
// $miNs = (int) $miNs;-*/
|
||||
|
||||
$totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
$from_dt = $yesterday . ' ' . $shiftId->start_time;
|
||||
|
||||
$to_dt = date('Y-m-d H:i:s', strtotime($from_dt . " + $totalMinutes minutes"));
|
||||
|
||||
$currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// Check if current date time is within the range
|
||||
if ($currentDateTime >= $from_dt && $currentDateTime < $to_dt) {
|
||||
//echo "Choosed a valid shift...";
|
||||
|
||||
// here i'm updating created as yesterday
|
||||
$set('created_at', $from_dt);
|
||||
$set('update_date', '1');
|
||||
|
||||
$set('ppLineError', null);
|
||||
// $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$currentDate = date('Y-m-d');
|
||||
|
||||
$shiftId = Shift::where('id', $get('shift_id'))
|
||||
->first();
|
||||
|
||||
[$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
$hRs = (int) $hRs;
|
||||
// $miNs = (int) $miNs;-*/
|
||||
|
||||
$totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
$from_dt = $currentDate . ' ' . $shiftId->start_time;
|
||||
|
||||
$to_dt = date('Y-m-d H:i:s', strtotime($from_dt . " + $totalMinutes minutes"));
|
||||
|
||||
$currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// Check if current date time is within the range
|
||||
if (!($currentDateTime >= $from_dt && $currentDateTime < $to_dt)) {
|
||||
//echo "Choosed a valid shift...";
|
||||
|
||||
$set('line_id', null);
|
||||
$set('ppLineError', 'Choosed a invalid shift.');
|
||||
// $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$set('ppLineError', null);
|
||||
return;
|
||||
}
|
||||
|
||||
// $exists = ProductionPlan::where('plant_id', $get('plant_id'))
|
||||
// //->where('shift_id', $get('shift_id'))
|
||||
// ->where('line_id', $get('line_id'))
|
||||
// ->whereDate('created_at', today())
|
||||
// ->latest() // Orders by created_at DESC
|
||||
// ->first();
|
||||
|
||||
// if ($exists)
|
||||
// {
|
||||
// $existingShifts = ProductionPlan::where('plant_id', $get('plant_id'))
|
||||
// //->where('shift_id', $get('shift_id'))
|
||||
// ->where('line_id', $get('line_id'))
|
||||
// // ->whereDate('created_at', today())
|
||||
// ->whereDate('created_at', today())
|
||||
// ->get();
|
||||
|
||||
// foreach ($existingShifts as $shift) {
|
||||
// $curShiftId = $shift->shift_id;
|
||||
|
||||
// $currentDate = date('Y-m-d');
|
||||
|
||||
// $shiftId = \App\Models\Shift::where('id', $curShiftId)
|
||||
// ->first();
|
||||
|
||||
// [$hRs, $miNs] = explode('.', $shiftId->duration) + [0, 0];
|
||||
// $hRs = (int) $hRs;
|
||||
// // $miNs = (int) $miNs;-*/
|
||||
|
||||
// $totalMinutes = $hRs * 60 + $miNs;
|
||||
|
||||
// $from_dt = $currentDate . ' ' . $shiftId->start_time;
|
||||
|
||||
// $to_dt = date('Y-m-d H:i:s', strtotime($from_dt . " + $totalMinutes minutes"));
|
||||
|
||||
// $currentDateTime = date('Y-m-d H:i:s');
|
||||
|
||||
// // Check if current date time is within the range
|
||||
// if ($currentDateTime >= $from_dt && $currentDateTime < $to_dt) {
|
||||
// //echo "Choosed a valid shift...";
|
||||
// // $set('ppLineError', 'Valid (From: '.$from_dt.', To: '.$to_dt.')');
|
||||
|
||||
// $set('line_id', null);
|
||||
// $set('ppLineError', 'Production plan already updated.');
|
||||
// return;
|
||||
// }
|
||||
// // else {
|
||||
// // $set('ppLineError', 'Choosed a invalid shift...');
|
||||
// // return;
|
||||
// // }
|
||||
// }
|
||||
// $set('ppLineError', null);
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
}
|
||||
$set('ppLineError', null);
|
||||
}
|
||||
$set('item_id', null);
|
||||
$set('plan_quantity', null);
|
||||
})
|
||||
->extraAttributes(fn ($get) => [
|
||||
'class' => $get('ppLineError') ? 'border-red-500' : '',
|
||||
])
|
||||
->hint(fn ($get) => $get('ppLineError') ? $get('ppLineError') : null)
|
||||
->hintColor('danger'),
|
||||
Forms\Components\Select::make('item_id')
|
||||
->label('Item')
|
||||
->reactive()
|
||||
->searchable()
|
||||
->required()
|
||||
->options(function (callable $get) {
|
||||
if (! $get('plant_id')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Item::where('plant_id', $get('plant_id'))
|
||||
->pluck('code', 'id')
|
||||
->toArray();
|
||||
}),
|
||||
Forms\Components\TextInput::make('plan_quantity')
|
||||
->required()
|
||||
->integer()
|
||||
@@ -449,21 +361,18 @@ class ProductionPlanResource extends Resource
|
||||
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||
$planQuan = $get('plan_quantity');
|
||||
|
||||
if(!$get('update_date') )
|
||||
{
|
||||
if(!$get('id'))
|
||||
{
|
||||
if (! $get('update_date')) {
|
||||
if (! $get('id')) {
|
||||
$currentDT = Carbon::now()->toDateTimeString();
|
||||
$set('created_at', $currentDT);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$planQuan) {
|
||||
if (! $planQuan) {
|
||||
$set('ppPlanQuanError', 'Scan the valid plan quantity.');
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$set('ppPlanQuanError', null);
|
||||
}
|
||||
})
|
||||
@@ -472,29 +381,19 @@ class ProductionPlanResource extends Resource
|
||||
])
|
||||
->hint(fn ($get) => $get('ppPlanQuanError') ? $get('ppPlanQuanError') : null)
|
||||
->hintColor('danger'),
|
||||
Forms\Components\TextInput::make('production_quantity')
|
||||
->required()
|
||||
->integer()
|
||||
->label('Production Quantity')
|
||||
->readOnly(fn (callable $get) => !$get('id'))
|
||||
->default(0),
|
||||
// Forms\Components\TextInput::make('production_quantity')
|
||||
// ->required()
|
||||
// ->integer()
|
||||
// ->label('Production Quantity')
|
||||
// ->readOnly(fn (callable $get) => ! $get('id'))
|
||||
// ->default(0),
|
||||
Forms\Components\TextInput::make('id')
|
||||
->hidden()
|
||||
->readOnly(),
|
||||
Forms\Components\TextInput::make('update_date')
|
||||
->hidden()
|
||||
->reactive()
|
||||
->readOnly(),
|
||||
Forms\Components\DateTimePicker::make('created_at')
|
||||
->label('Created DateTime')
|
||||
->hidden()
|
||||
->reactive()
|
||||
->required()
|
||||
->readOnly(),
|
||||
Forms\Components\Hidden::make('operator_id')
|
||||
->default(Filament::auth()->user()->name),
|
||||
])
|
||||
->columns(2),
|
||||
])
|
||||
->columns(4),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -529,34 +428,55 @@ class ProductionPlanResource extends Resource
|
||||
$paginator = $livewire->getTableRecords();
|
||||
$perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
|
||||
$currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;
|
||||
|
||||
return ($currentPage - 1) * $perPage + $rowLoop->iteration;
|
||||
}),
|
||||
Tables\Columns\TextColumn::make('plant.name')
|
||||
->label('Plant')
|
||||
->alignCenter()
|
||||
->sortable()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('line.name')
|
||||
->label('Plant')
|
||||
->alignCenter()
|
||||
->sortable()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('item.code')
|
||||
->label('Item')
|
||||
->alignCenter()
|
||||
->sortable()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('plan_quantity')
|
||||
->label('Plan Quantity')
|
||||
->alignCenter()
|
||||
->numeric()
|
||||
->sortable(),
|
||||
->sortable()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('working_days')
|
||||
->label('Working Days')
|
||||
->alignCenter()
|
||||
->numeric()
|
||||
->sortable()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('production_quantity')
|
||||
->label('Production Quantity')
|
||||
->alignCenter()
|
||||
->numeric()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('line.name')
|
||||
->label('Line')
|
||||
->alignCenter()
|
||||
->sortable(),// ->searchable(),
|
||||
Tables\Columns\TextColumn::make('shift.block.name')
|
||||
->label('Block')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('shift.name')
|
||||
->label('Shift')
|
||||
->alignCenter()
|
||||
->sortable(),// ->searchable(),
|
||||
Tables\Columns\TextColumn::make('plant.name')
|
||||
->label('Plant')
|
||||
->alignCenter()
|
||||
->sortable(),// ->searchable(),
|
||||
->sortable()
|
||||
->searchable(),
|
||||
// Tables\Columns\TextColumn::make('line.name')
|
||||
// ->label('Line')
|
||||
// ->alignCenter()
|
||||
// ->sortable(), // ->searchable(),
|
||||
// Tables\Columns\TextColumn::make('shift.block.name')
|
||||
// ->label('Block')
|
||||
// ->alignCenter()
|
||||
// ->sortable(),
|
||||
// Tables\Columns\TextColumn::make('shift.name')
|
||||
// ->label('Shift')
|
||||
// ->alignCenter()
|
||||
// ->sortable(), // ->searchable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->dateTime()
|
||||
@@ -584,7 +504,7 @@ class ProductionPlanResource extends Resource
|
||||
Filter::make('advanced_filters')
|
||||
->label('Advanced Filters')
|
||||
->form([
|
||||
//plant
|
||||
// plant
|
||||
Select::make('Plant')
|
||||
->label('Select Plant')
|
||||
->nullable()
|
||||
@@ -593,7 +513,8 @@ class ProductionPlanResource extends Resource
|
||||
// })
|
||||
->options(function (callable $get) {
|
||||
$userHas = Filament::auth()->user()->plant_id;
|
||||
return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::pluck('name', 'id')->toArray();
|
||||
|
||||
return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray();
|
||||
})
|
||||
->reactive()
|
||||
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||
@@ -602,31 +523,33 @@ class ProductionPlanResource extends Resource
|
||||
$set('Shift', null);
|
||||
}),
|
||||
|
||||
//line
|
||||
// line
|
||||
Select::make('Line')
|
||||
->label('Select line')
|
||||
->nullable()
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('Plant');
|
||||
|
||||
if (!$plantId ) {
|
||||
if (! $plantId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Line::where('plant_id', $plantId)
|
||||
->pluck('name', 'id');
|
||||
->pluck('name', 'id');
|
||||
})
|
||||
->reactive(),
|
||||
|
||||
//block
|
||||
// block
|
||||
Select::make('Block')
|
||||
->label('Select Block')
|
||||
->nullable()
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('Plant');
|
||||
|
||||
if (!$plantId ) {
|
||||
if (! $plantId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Block::where('plant_id', $get('Plant'))->pluck('name', 'id');
|
||||
})
|
||||
->reactive()
|
||||
@@ -634,7 +557,7 @@ class ProductionPlanResource extends Resource
|
||||
$set('Shift', null);
|
||||
}),
|
||||
|
||||
//shift
|
||||
// shift
|
||||
Select::make('Shift')
|
||||
->label('Select Shift')
|
||||
->nullable()
|
||||
@@ -642,7 +565,7 @@ class ProductionPlanResource extends Resource
|
||||
$plantId = $get('Plant');
|
||||
$blockId = $get('Block');
|
||||
|
||||
if (!$plantId || !$blockId) {
|
||||
if (! $plantId || ! $blockId) {
|
||||
return []; // Return empty if plant or block is not selected
|
||||
}
|
||||
|
||||
@@ -669,52 +592,66 @@ class ProductionPlanResource extends Resource
|
||||
return $query->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
if ($plant = $data['Plant'] ?? null) {
|
||||
$query->where('plant_id', $plant);
|
||||
if (! empty($data['Plant'])) {// if ($plant = $data['Plant'] ?? null) {
|
||||
$query->where('plant_id', $data['Plant']);
|
||||
} else {
|
||||
$userHas = Filament::auth()->user()->plant_id;
|
||||
|
||||
if ($userHas && strlen($userHas) > 0) {
|
||||
return $query->whereRaw('1 = 0');
|
||||
}
|
||||
}
|
||||
|
||||
if ($shift = $data['Shift'] ?? null) {
|
||||
$query->where('shift_id', $shift);
|
||||
if (! empty($data['Shift'])) {// if ($shift = $data['Shift'] ?? null) {
|
||||
$query->where('shift_id', $data['Shift']);
|
||||
}
|
||||
|
||||
if ($line = $data['Line'] ?? null) {
|
||||
$query->where('line_id', $line);
|
||||
|
||||
if (! empty($data['Line'])) {// if ($line = $data['Line'] ?? null) {
|
||||
$query->where('line_id', $data['Line']);
|
||||
}
|
||||
|
||||
if ($from = $data['created_from'] ?? null) {
|
||||
$query->where('created_at', '>=', $from);
|
||||
if (! empty($data['created_from'])) {// if ($from = $data['created_from'] ?? null) {
|
||||
$query->where('created_at', '>=', $data['created_from']);
|
||||
}
|
||||
|
||||
if ($to = $data['created_to'] ?? null) {
|
||||
$query->where('created_at', '<=', $to);
|
||||
if (! empty($data['created_to'])) {// if ($to = $data['created_to'] ?? null) {
|
||||
$query->where('created_at', '<=', $data['created_to']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
})
|
||||
->indicateUsing(function (array $data) {
|
||||
$indicators = [];
|
||||
|
||||
if (!empty($data['Plant'])) {
|
||||
$indicators[] = 'Plant: ' . Plant::where('id', $data['Plant'])->value('name');
|
||||
if (! empty($data['Plant'])) {
|
||||
$indicators[] = 'Plant: '.Plant::where('id', $data['Plant'])->value('name');
|
||||
} else {
|
||||
$userHas = Filament::auth()->user()->plant_id;
|
||||
|
||||
if ($userHas && strlen($userHas) > 0) {
|
||||
return 'Plant: Choose plant to filter records.';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($data['Shift'])) {
|
||||
$indicators[] = 'Shift: ' . Shift::where('id', $data['Shift'])->value('name');
|
||||
if (! empty($data['Shift'])) {
|
||||
$indicators[] = 'Shift: '.Shift::where('id', $data['Shift'])->value('name');
|
||||
}
|
||||
|
||||
if (!empty($data['Line'])) {
|
||||
$indicators[] = 'Line: ' . Line::where('id', $data['Line'])->value('name');
|
||||
if (! empty($data['Line'])) {
|
||||
$indicators[] = 'Line: '.Line::where('id', $data['Line'])->value('name');
|
||||
}
|
||||
|
||||
if (!empty($data['created_from'])) {
|
||||
$indicators[] = 'From: ' . $data['created_from'];
|
||||
if (! empty($data['created_from'])) {
|
||||
$indicators[] = 'From: '.$data['created_from'];
|
||||
}
|
||||
|
||||
if (!empty($data['created_to'])) {
|
||||
$indicators[] = 'To: ' . $data['created_to'];
|
||||
if (! empty($data['created_to'])) {
|
||||
$indicators[] = 'To: '.$data['created_to'];
|
||||
}
|
||||
|
||||
return $indicators;
|
||||
})
|
||||
}),
|
||||
])
|
||||
->filtersFormMaxHeight('280px')
|
||||
->actions([
|
||||
@@ -726,7 +663,7 @@ class ProductionPlanResource extends Resource
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||
Tables\Actions\RestoreBulkAction::make(),
|
||||
FilamentExportBulkAction::make('export')
|
||||
FilamentExportBulkAction::make('export'),
|
||||
]),
|
||||
])
|
||||
->headerActions([
|
||||
@@ -734,14 +671,14 @@ class ProductionPlanResource extends Resource
|
||||
->label('Import Production Plans')
|
||||
->color('warning')
|
||||
->importer(ProductionPlanImporter::class)
|
||||
->visible(function() {
|
||||
->visible(function () {
|
||||
return Filament::auth()->user()->can('view import production plan');
|
||||
}),
|
||||
ExportAction::make()
|
||||
->label('Export Production Plans')
|
||||
->color('warning')
|
||||
->exporter(ProductionPlanExporter::class)
|
||||
->visible(function() {
|
||||
->visible(function () {
|
||||
return Filament::auth()->user()->can('view export production plan');
|
||||
}),
|
||||
]);
|
||||
|
||||
295
app/Filament/Resources/RequestQuotationResource.php
Normal file
295
app/Filament/Resources/RequestQuotationResource.php
Normal file
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Exports\RequestQuotationExporter;
|
||||
use App\Filament\Imports\RequestQuotationImporter;
|
||||
use App\Filament\Resources\RequestQuotationResource\Pages;
|
||||
use App\Filament\Resources\RequestQuotationResource\RelationManagers;
|
||||
use App\Models\RequestQuotation;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Filament\Tables\Actions\ImportAction;
|
||||
use Filament\Tables\Actions\ExportAction;
|
||||
|
||||
class RequestQuotationResource extends Resource
|
||||
{
|
||||
protected static ?string $model = RequestQuotation::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
protected static ?string $navigationGroup = 'Request For Quotation';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
$canEdit = Auth::user()?->can('edit_transport_fields');
|
||||
$canEditData = Auth::user()?->can('edit_all_transport_fields');
|
||||
return $form
|
||||
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('rfq_number')
|
||||
->label('RFQ Number')
|
||||
->readOnly()
|
||||
->required(),
|
||||
Forms\Components\Select::make('spot_rate_transport_master_id')
|
||||
->label('Group Name')
|
||||
->relationship('spotRateTransportMaster', 'group_name')
|
||||
->required(),
|
||||
Forms\Components\DateTimePicker::make('rfq_date_time')
|
||||
->label('RFQ Date Time')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('pickup_address')
|
||||
->label('PickUp Address')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('delivery_address')
|
||||
->label('Delivery Address')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('weight')
|
||||
->label('Weight')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('volumetrice_size_inch')
|
||||
->label('Volumetrice')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('type_of_vehicle')
|
||||
->label('Type of Vehicle')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('special_type')
|
||||
->label('Special Type')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('no_of_vehicle')
|
||||
->label('No of Vehicle')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('product_name')
|
||||
->label('Product Name')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('loading_by')
|
||||
->label('Loading By')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('unloading_by')
|
||||
->label('Unloading By')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('pick_and_delivery')
|
||||
->label('Pick and Delivery')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('payment_term')
|
||||
->label('Payment Term')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('paid_topay')
|
||||
->label('Paid Today')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\DateTimePicker::make('require_date_time')
|
||||
->label('Require Date Time')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
Forms\Components\DateTimePicker::make('rfq_rec_on_or_before')
|
||||
->label('RFQ Receive On Or Before')
|
||||
->disabled(fn () => !$canEditData)
|
||||
->required(),
|
||||
// Forms\Components\TextInput::make('transporter_name')
|
||||
// ->label('Transporter Name')
|
||||
// ->disabled(fn () => !($canEdit || $canEditData)),
|
||||
// Forms\Components\TextInput::make('total_freight_charge')
|
||||
// ->label('Total Freight Charge')
|
||||
// ->disabled(fn () => !($canEdit || $canEditData)),
|
||||
// Forms\Components\TextInput::make('transit_day')
|
||||
// ->label('Transit Day')
|
||||
// ->disabled(fn () => !($canEdit || $canEditData)),
|
||||
Forms\Components\Hidden::make('created_by')
|
||||
->label('Created By'),
|
||||
Forms\Components\Hidden::make('updated_by')
|
||||
->label('Updated By'),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('No.')
|
||||
->label('No.')
|
||||
->alignCenter()
|
||||
->getStateUsing(function ($record, $livewire, $column, $rowLoop) {
|
||||
$paginator = $livewire->getTableRecords();
|
||||
$perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
|
||||
$currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;
|
||||
|
||||
return ($currentPage - 1) * $perPage + $rowLoop->iteration;
|
||||
}),
|
||||
Tables\Columns\TextColumn::make('rfq_number')
|
||||
->label('RFQ Number')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('spotRateTransportMaster.group_name')
|
||||
->label('Group Name')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('rfq_date_time')
|
||||
->label('RFQ Date Time')
|
||||
->alignCenter()
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('pickup_address')
|
||||
->label('PickUp Address')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('delivery_address')
|
||||
->label('Delivery Address')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('weight')
|
||||
->label('Weight')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('volumetrice_size_inch')
|
||||
->label('Volumetrice Size Inch')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('type_of_vehicle')
|
||||
->label('Type Of Vehicle')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('product_name')
|
||||
->label('Product Name')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('loading_by')
|
||||
->label('Loading By')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('unloading_by')
|
||||
->label('Unloading By')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('unloading_by')
|
||||
->label('Unloading By')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('pick_and_delivery')
|
||||
->label('Pick and Delivery')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('payment_term')
|
||||
->label('Payment Term')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('paid_topay')
|
||||
->label('Paid Today')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('require_date_time')
|
||||
->label('Require Date Time')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('rfq_rec_on_or_before')
|
||||
->label('RFQ Receive On Or Before')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('transporter_name')
|
||||
->label('Transporter Name')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('total_freight_charge')
|
||||
->label('Total Freight Charge')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('transit_day')
|
||||
->label('Transit Day')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->alignCenter()
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->label('Updated At')
|
||||
->alignCenter()
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('deleted_at')
|
||||
->label('Deleted At')
|
||||
->alignCenter()
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TrashedFilter::make(),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ViewAction::make(),
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||
Tables\Actions\RestoreBulkAction::make(),
|
||||
]),
|
||||
])
|
||||
->headerActions([
|
||||
ImportAction::make()
|
||||
->label('Import RequestQuotation')
|
||||
->color('warning')
|
||||
->importer(RequestQuotationImporter::class)
|
||||
->visible(function() {
|
||||
return Filament::auth()->user()->can('view import request quotation');
|
||||
}),
|
||||
ExportAction::make()
|
||||
->label('Export RequestQuotation')
|
||||
->color('warning')
|
||||
->exporter(RequestQuotationExporter::class)
|
||||
->visible(function() {
|
||||
return Filament::auth()->user()->can('view export request quotation');
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListRequestQuotations::route('/'),
|
||||
'create' => Pages\CreateRequestQuotation::route('/create'),
|
||||
'view' => Pages\ViewRequestQuotation::route('/{record}'),
|
||||
'edit' => Pages\EditRequestQuotation::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\RequestQuotationResource\Pages;
|
||||
|
||||
use App\Filament\Resources\RequestQuotationResource;
|
||||
use App\Models\RequestQuotation;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateRequestQuotation extends CreateRecord
|
||||
{
|
||||
protected static string $resource = RequestQuotationResource::class;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
parent::mount();
|
||||
|
||||
$this->form->fill([
|
||||
'rfq_number' => $this->generateRfqNumber(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function generateRfqNumber(): string
|
||||
{
|
||||
$year = now()->year;
|
||||
|
||||
$lastRfq = RequestQuotation::whereYear('created_at', $year)
|
||||
->orderBy('id', 'desc')
|
||||
->value('rfq_number');
|
||||
|
||||
if ($lastRfq) {
|
||||
$lastNumber = (int) substr($lastRfq, -3);
|
||||
$nextNumber = str_pad($lastNumber + 1, 3, '0', STR_PAD_LEFT);
|
||||
} else {
|
||||
$nextNumber = '001';
|
||||
}
|
||||
|
||||
return "C.R.I-{$year}-{$nextNumber}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\RequestQuotationResource\Pages;
|
||||
|
||||
use App\Filament\Resources\RequestQuotationResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditRequestQuotation extends EditRecord
|
||||
{
|
||||
protected static string $resource = RequestQuotationResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\ViewAction::make(),
|
||||
Actions\DeleteAction::make(),
|
||||
Actions\ForceDeleteAction::make(),
|
||||
Actions\RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\RequestQuotationResource\Pages;
|
||||
|
||||
use App\Filament\Resources\RequestQuotationResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListRequestQuotations extends ListRecords
|
||||
{
|
||||
protected static string $resource = RequestQuotationResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\RequestQuotationResource\Pages;
|
||||
|
||||
use App\Filament\Resources\RequestQuotationResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewRequestQuotation extends ViewRecord
|
||||
{
|
||||
protected static string $resource = RequestQuotationResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
345
app/Filament/Resources/RfqTransporterBidResource.php
Normal file
345
app/Filament/Resources/RfqTransporterBidResource.php
Normal file
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\RfqTransporterBidResource\Pages;
|
||||
use App\Filament\Resources\RfqTransporterBidResource\RelationManagers;
|
||||
use App\Models\RfqTransporterBid;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
|
||||
class RfqTransporterBidResource extends Resource
|
||||
{
|
||||
protected static ?string $model = RfqTransporterBid::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
protected static ?string $navigationGroup = 'Request For Quotation';
|
||||
|
||||
|
||||
public static function getEloquentQuery(): Builder
|
||||
{
|
||||
$user = Filament::auth()->user();
|
||||
|
||||
return parent::getEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
])
|
||||
->when(
|
||||
! $user->hasAnyRole(['Super Admin', 'TransporterBidSupervisor']),
|
||||
fn (Builder $query) => $query
|
||||
->where('transporter_name', $user->name)
|
||||
->whereNotNull('request_quotation_id')
|
||||
);
|
||||
}
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\Select::make('request_quotation_id')
|
||||
->label('RFQ Number')
|
||||
// ->relationship('requestQuotation', 'rfq_number')
|
||||
->relationship(
|
||||
'requestQuotation',
|
||||
'rfq_number',
|
||||
function (Builder $query) {
|
||||
|
||||
$userName = Filament::auth()->user()?->name;
|
||||
|
||||
$masterIds = \App\Models\SpotRateTransportMaster::whereRaw(
|
||||
"user_name::jsonb @> ?",
|
||||
[json_encode([$userName])]
|
||||
)
|
||||
->pluck('id')
|
||||
->unique()
|
||||
->toArray();
|
||||
|
||||
if (empty($masterIds)) {
|
||||
$query->whereRaw('1 = 0');
|
||||
return;
|
||||
}
|
||||
|
||||
$query->whereIn('spot_rate_transport_master_id', $masterIds);
|
||||
}
|
||||
)
|
||||
->reactive()
|
||||
// ->disabled(fn ($record) => !Filament::auth()->user()?->hasAnyRole(['Super Admin', 'TransporterBidSupervisor']))
|
||||
->disabled(fn ($record) =>
|
||||
!Filament::auth()->user()?->hasAnyRole(['Super Admin', 'TransporterBidSupervisor'])
|
||||
&& $record
|
||||
)
|
||||
->afterStateUpdated(function ($state, callable $set) {
|
||||
$rfq = \App\Models\RequestQuotation::find($state);
|
||||
if ($rfq) {
|
||||
$set('pickup_address', $rfq->pickup_address);
|
||||
$set('delivery_address', $rfq->delivery_address);
|
||||
$set('type_of_vehicle', $rfq->type_of_vehicle);
|
||||
$set('weight', $rfq->weight);
|
||||
$set('volumetrice_size_inch', $rfq->volumetrice_size_inch);
|
||||
$set('no_of_vehicle', $rfq->no_of_vehicle);
|
||||
$set('product_name', $rfq->product_name);
|
||||
$set('pick_and_delivery', $rfq->pick_and_delivery);
|
||||
$set('payment_term', $rfq->payment_term);
|
||||
$set('paid_topay', $rfq->paid_topay);
|
||||
$set('loading_by', $rfq->loading_by);
|
||||
$set('unloading_by', $rfq->unloading_by);
|
||||
$set('special_type', $rfq->special_type);
|
||||
$set('rfq_date_time', $rfq->rfq_date_time);
|
||||
$set('require_date_time', $rfq->require_date_time);
|
||||
$set('rfq_rec_on_or_before', $rfq->rfq_rec_on_or_before);
|
||||
}
|
||||
})
|
||||
->afterStateHydrated(function ($state, callable $set) {
|
||||
$rfq = \App\Models\RequestQuotation::find($state);
|
||||
if ($rfq) {
|
||||
$set('pickup_address', $rfq->pickup_address);
|
||||
$set('delivery_address', $rfq->delivery_address);
|
||||
$set('type_of_vehicle', $rfq->type_of_vehicle);
|
||||
$set('weight', $rfq->weight);
|
||||
$set('volumetrice_size_inch', $rfq->volumetrice_size_inch);
|
||||
$set('no_of_vehicle', $rfq->no_of_vehicle);
|
||||
$set('product_name', $rfq->product_name);
|
||||
$set('pick_and_delivery', $rfq->pick_and_delivery);
|
||||
$set('payment_term', $rfq->payment_term);
|
||||
$set('paid_topay', $rfq->paid_topay);
|
||||
$set('loading_by', $rfq->loading_by);
|
||||
$set('unloading_by', $rfq->unloading_by);
|
||||
$set('special_type', $rfq->special_type);
|
||||
$set('rfq_date_time', $rfq->rfq_date_time);
|
||||
$set('require_date_time', $rfq->require_date_time);
|
||||
$set('rfq_rec_on_or_before', $rfq->rfq_rec_on_or_before);
|
||||
}
|
||||
})
|
||||
->required(),
|
||||
Forms\Components\Section::make('RFQ Details')
|
||||
->visible(fn ($get) => $get('request_quotation_id') != null)
|
||||
->reactive()
|
||||
->schema([
|
||||
TextInput::make('pickup_address')->label('Pickup Address')->disabled(),
|
||||
TextInput::make('delivery_address')->label('Delivery Address')->disabled(),
|
||||
TextInput::make('type_of_vehicle')->label('Vehicle Type')->disabled(),
|
||||
TextInput::make('weight')->label('Weight')->disabled(),
|
||||
TextInput::make('volumetrice_size_inch')->label('Volumetric Size')->disabled(),
|
||||
TextInput::make('no_of_vehicle')->label('No. of Vehicle')->disabled(),
|
||||
TextInput::make('product_name')->label('Product Name')->disabled(),
|
||||
TextInput::make('pick_and_delivery')->label('Pick & Delivery')->disabled(),
|
||||
TextInput::make('payment_term')->label('Payment Term')->disabled(),
|
||||
TextInput::make('paid_topay')->label('Paid / To Pay')->disabled(),
|
||||
TextInput::make('loading_by')->label('Loading By')->disabled(),
|
||||
TextInput::make('unloading_by')->label('Unloading By')->disabled(),
|
||||
TextInput::make('special_type')->label('Special Type')->disabled(),
|
||||
TextInput::make('rfq_date_time')->label('RFQ Created On')->disabled(),
|
||||
TextInput::make('require_date_time')->label('Required Date')->disabled(),
|
||||
TextInput::make('rfq_rec_on_or_before')->label('RFQ Received On/Before')->disabled(),
|
||||
]),
|
||||
Forms\Components\TextInput::make('transporter_name')
|
||||
->label('Transporter Name')
|
||||
->readOnly()
|
||||
->default(Filament::auth()->user()?->name)
|
||||
->required()
|
||||
->rule(function (callable $get) {
|
||||
return Rule::unique('rfq_transporter_bids', 'transporter_name')
|
||||
->where('request_quotation_id', $get('request_quotation_id'))
|
||||
->ignore($get('id'));
|
||||
}),
|
||||
Forms\Components\TextInput::make('total_freight_charge')
|
||||
->label('Total Freight Charge')
|
||||
->required()
|
||||
// ->disabled(fn ($record) => $record && $record->created_by != Filament::auth()->user()?->name)
|
||||
// ->hidden(fn ($record) => !Filament::auth()->user()?->hasAnyRole(['Super Admin', 'TransporterBidSupervisor']) && $record->transporter_name != Filament::auth()->user()?->name),
|
||||
->hidden(fn ($record) =>
|
||||
!Filament::auth()->user()?->hasAnyRole(['Super Admin', 'TransporterBidSupervisor'])
|
||||
&& ($record && $record->transporter_name != Filament::auth()->user()?->name) // Ensure $record is not null before checking transporter_name
|
||||
),
|
||||
Forms\Components\TextInput::make('transit_day')
|
||||
->label('Transit Day')
|
||||
->required()
|
||||
// ->disabled(fn ($record) => $record && $record->created_by != Filament::auth()->user()?->name),
|
||||
// ->hidden(fn ($record) => !Filament::auth()->user()?->hasAnyRole(['Super Admin', 'TransporterBidSupervisor']) && $record->transporter_name != Filament::auth()->user()?->name),
|
||||
->hidden(fn ($record) =>
|
||||
!Filament::auth()->user()?->hasAnyRole(['Super Admin', 'TransporterBidSupervisor'])
|
||||
&& ($record && $record->transporter_name != Filament::auth()->user()?->name) // Ensure $record is not null before checking transporter_name
|
||||
),
|
||||
Forms\Components\Hidden::make('created_by')
|
||||
->label('Created By')
|
||||
->default(Filament::auth()->user()?->name),
|
||||
Forms\Components\Hidden::make('updated_by')
|
||||
->label('Updated By')
|
||||
->default(Filament::auth()->user()?->name),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('No.')
|
||||
->label('No.')
|
||||
->alignCenter()
|
||||
->getStateUsing(function ($record, $livewire, $column, $rowLoop) {
|
||||
$paginator = $livewire->getTableRecords();
|
||||
$perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
|
||||
$currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;
|
||||
|
||||
return ($currentPage - 1) * $perPage + $rowLoop->iteration;
|
||||
}),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.rfq_number')
|
||||
->label('RFQ Number')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.pickup_address')
|
||||
->label('PickUp Address')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.delivery_address')
|
||||
->label('Delivery Address')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.type_of_vehicle')
|
||||
->label('Type Of Vehicle')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.weight')
|
||||
->label('Weight')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.volumetrice_size_inch')
|
||||
->label('Volumetrice Size Inch')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.no_of_vehicle')
|
||||
->label('No Of Vehicle')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.product_name')
|
||||
->label('Product Name')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.pick_and_delivery')
|
||||
->label('Pick And Delivery')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.payment_term')
|
||||
->label('Payment Term')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.paid_topay')
|
||||
->label('Paid Today')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.loading_by')
|
||||
->label('Loading By')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.unloading_by')
|
||||
->label('Unloading By')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.special_type')
|
||||
->label('Special Type')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.rfq_date_time')
|
||||
->label('RFQ DateTime')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.require_date_time')
|
||||
->label('RFQ Require DateTime')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('requestQuotation.rfq_rec_on_or_before')
|
||||
->label('RFQ Rec On Or Before')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('created_by')
|
||||
->label('Created By')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('updated_by')
|
||||
->label('Updated By')
|
||||
->searchable()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('deleted_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TrashedFilter::make(),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ViewAction::make(),
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||
Tables\Actions\RestoreBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListRfqTransporterBids::route('/'),
|
||||
'create' => Pages\CreateRfqTransporterBid::route('/create'),
|
||||
'view' => Pages\ViewRfqTransporterBid::route('/{record}'),
|
||||
'edit' => Pages\EditRfqTransporterBid::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
// public static function getEloquentQuery(): Builder
|
||||
// {
|
||||
// return parent::getEloquentQuery()
|
||||
// ->withoutGlobalScopes([
|
||||
// SoftDeletingScope::class,
|
||||
// ]);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\RfqTransporterBidResource\Pages;
|
||||
|
||||
use App\Filament\Resources\RfqTransporterBidResource;
|
||||
use App\Models\RfqTransporterBid;
|
||||
use App\Models\User;
|
||||
use App\Notifications\PushAlertNotification;
|
||||
use Filament\Actions;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateRfqTransporterBid extends CreateRecord
|
||||
{
|
||||
protected static string $resource = RfqTransporterBidResource::class;
|
||||
|
||||
|
||||
protected function afterCreate(): void
|
||||
{
|
||||
$record = $this->record;
|
||||
|
||||
// Calculate rank based on total_freight_charge
|
||||
$rank = RfqTransporterBid::where('request_quotation_id', $this->record->request_quotation_id)
|
||||
->orderBy('total_freight_charge')
|
||||
->pluck('id')
|
||||
->search($this->record->id) + 1;
|
||||
|
||||
$recipients = User::role(['Super Admin', 'Rfq Supervisor'])->get();
|
||||
$currentUser = Filament::auth()->user();
|
||||
|
||||
|
||||
if ($currentUser && ! $recipients->contains('id', $currentUser->id)) {
|
||||
$recipients->push($currentUser);
|
||||
}
|
||||
|
||||
// $user1 = Filament::auth()->user();
|
||||
|
||||
$rfqNumber = $this->record->requestQuotation->rfq_number;
|
||||
$body = "{$currentUser->name} has updated the bid for RFQ No '{$rfqNumber}'. The current rank is #{$rank}.";
|
||||
|
||||
Notification::make()
|
||||
->title('Rank Updated')
|
||||
->body("{$currentUser->name} has updated the bid for RFQ No '{$rfqNumber}'. The current rank is #{$rank}.")
|
||||
->success()
|
||||
->sendToDatabase($recipients);
|
||||
|
||||
// Push notification
|
||||
foreach ($recipients as $user) {
|
||||
$user->notify(
|
||||
new PushAlertNotification(
|
||||
'New Bid Added',
|
||||
$body
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
\Log::info('Create bid notification sent', [
|
||||
'bid_id' => $record->id,
|
||||
'rank' => $rank,
|
||||
'recipients' => $recipients->pluck('id'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\RfqTransporterBidResource\Pages;
|
||||
|
||||
use App\Filament\Resources\RfqTransporterBidResource;
|
||||
use App\Models\RequestQuotation;
|
||||
use App\Models\RfqTransporterBid;
|
||||
use App\Models\SpotRateTransportMaster;
|
||||
use App\Models\User;
|
||||
use App\Notifications\PushAlertNotification;
|
||||
use Filament\Actions;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class EditRfqTransporterBid extends EditRecord
|
||||
{
|
||||
protected static string $resource = RfqTransporterBidResource::class;
|
||||
|
||||
protected function afterSave(): void
|
||||
{
|
||||
if (! $this->record->wasChanged('total_freight_charge')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rank = RfqTransporterBid::where('request_quotation_id', $this->record->request_quotation_id)
|
||||
->orderBy('total_freight_charge')
|
||||
->pluck('id')
|
||||
->search($this->record->id) + 1;
|
||||
|
||||
$requestQuotation = RequestQuotation::findOrFail(
|
||||
$this->record->request_quotation_id
|
||||
);
|
||||
|
||||
$spotRateId = $requestQuotation->spot_rate_transport_master_id;
|
||||
|
||||
$spotRate = SpotRateTransportMaster::findOrFail($spotRateId);
|
||||
|
||||
$userNames = $spotRate->user_name;
|
||||
|
||||
Log::info('User names from spot rate', [
|
||||
'user_name_raw' => $spotRate->user_name,
|
||||
]);
|
||||
|
||||
if (!is_array($userNames)) {
|
||||
Log::warning('user_name is not array, resetting', [
|
||||
'user_name' => $userNames,
|
||||
]);
|
||||
$userNames = [];
|
||||
}
|
||||
|
||||
|
||||
$users = User::whereIn('name', $userNames)->get();
|
||||
|
||||
Log::info('Matched users', [
|
||||
'count' => $users->count(),
|
||||
'user_ids' => $users->pluck('id'),
|
||||
]);
|
||||
|
||||
// $recipients = User::role(['Super Admin', 'Rfq Supervisor', 'TransporterBid Employee'])->get();
|
||||
|
||||
// $recipients1 = User::role(['Super Admin', 'Rfq Supervisor', 'TransporterBid Employee'])->whereHas('pushSubscriptions')->get();
|
||||
|
||||
$currentUser = Filament::auth()->user();
|
||||
|
||||
// if ($currentUser && ! $recipients1->contains('id', $currentUser->id)) {
|
||||
// $recipients1->push($currentUser);
|
||||
// }
|
||||
|
||||
// if ($currentUser && ! $recipients->contains('id', $currentUser->id)) {
|
||||
// $recipients->push($currentUser);
|
||||
// }
|
||||
|
||||
// $user1 = Filament::auth()->user();
|
||||
|
||||
|
||||
$rfqNumber = $this->record->requestQuotation->rfq_number;
|
||||
$body = "{$currentUser->name} has updated the bid for RFQ No '{$rfqNumber}'. The current rank is #{$rank}.";
|
||||
|
||||
// Notification::make()
|
||||
// ->title('Rank Updated')
|
||||
// ->body("{$currentUser->name} current rank is #{$rank}")
|
||||
// ->success()
|
||||
// ->sendToDatabase($recipients);
|
||||
|
||||
// \Log::info('Notification sent', [
|
||||
// 'rank' => $rank,
|
||||
// 'recipients' => $recipients->pluck('id'),
|
||||
// ]);
|
||||
|
||||
Notification::make()
|
||||
->title('Rank Updated')
|
||||
->body("{$currentUser->name} has updated the bid for RFQ No '{$rfqNumber}'. The current rank is #{$rank}.")
|
||||
->success()
|
||||
->sendToDatabase($users);
|
||||
|
||||
// foreach ($recipients1 as $user) {
|
||||
// $user->notify(
|
||||
// new PushAlertNotification(
|
||||
// 'Rank Updated',
|
||||
// $body
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
|
||||
// foreach ($users as $user) {
|
||||
|
||||
// Log::info('Checking push subscription for user', [
|
||||
// 'user_id' => $user->id,
|
||||
// 'name' => $user->name,
|
||||
// 'subscription_count' => $user->pushSubscriptions()->count(),
|
||||
// ]);
|
||||
// if ($user->pushSubscriptions()->exists()) {
|
||||
|
||||
// Log::info('Sending push notification', [
|
||||
// 'user_id' => $user->id,
|
||||
// ]);
|
||||
// $user->notify(
|
||||
// new PushAlertNotification(
|
||||
// 'Rank Updated',
|
||||
// $body
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
// else {
|
||||
// Log::warning('User has NO push subscription', [
|
||||
// 'user_id' => $user->id,
|
||||
// ]);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
foreach ($users as $user) {
|
||||
|
||||
$count = $user->pushSubscriptions()->count();
|
||||
|
||||
Log::info('Checking push subscription for user', [
|
||||
'user_id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'subscription_count' => $count,
|
||||
]);
|
||||
|
||||
if ($count == 0) {
|
||||
Log::warning('User has NO push subscription', [
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
Log::info('Sending push notification', [
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
// ✅ THIS IS ALL YOU NEED
|
||||
$user->notify(new PushAlertNotification(
|
||||
'Rank Updated',
|
||||
$body
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\ViewAction::make(),
|
||||
Actions\DeleteAction::make(),
|
||||
Actions\ForceDeleteAction::make(),
|
||||
Actions\RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\RfqTransporterBidResource\Pages;
|
||||
|
||||
use App\Filament\Resources\RfqTransporterBidResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListRfqTransporterBids extends ListRecords
|
||||
{
|
||||
protected static string $resource = RfqTransporterBidResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\RfqTransporterBidResource\Pages;
|
||||
|
||||
use App\Filament\Resources\RfqTransporterBidResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewRfqTransporterBid extends ViewRecord
|
||||
{
|
||||
protected static string $resource = RfqTransporterBidResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
130
app/Filament/Resources/SpotRateTransportMasterResource.php
Normal file
130
app/Filament/Resources/SpotRateTransportMasterResource.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\SpotRateTransportMasterResource\Pages;
|
||||
use App\Filament\Resources\SpotRateTransportMasterResource\RelationManagers;
|
||||
use App\Models\SpotRateTransportMaster;
|
||||
use App\Models\User;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SpotRateTransportMasterResource extends Resource
|
||||
{
|
||||
protected static ?string $model = SpotRateTransportMaster::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
protected static ?string $navigationGroup = 'Request For Quotation';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('group_name')
|
||||
->label('Group Name')
|
||||
->required(),
|
||||
Forms\Components\Select::make('user_name')
|
||||
->label('User')
|
||||
->multiple()
|
||||
->preload()
|
||||
->reactive()
|
||||
->options(
|
||||
User::pluck('name', 'name')->toArray()
|
||||
)
|
||||
->searchable()
|
||||
->required(),
|
||||
Forms\Components\Hidden::make('id')
|
||||
->label('id'),
|
||||
Forms\Components\Hidden::make('created_by')
|
||||
->label('Created By')
|
||||
->default(Filament::auth()->user()?->name),
|
||||
Forms\Components\Hidden::make('updated_by')
|
||||
->label('Updated By')
|
||||
->default(Filament::auth()->user()?->name),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('No.')
|
||||
->label('No.')
|
||||
->getStateUsing(function ($record, $livewire, $column, $rowLoop) {
|
||||
$paginator = $livewire->getTableRecords();
|
||||
$perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
|
||||
$currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;
|
||||
|
||||
return ($currentPage - 1) * $perPage + $rowLoop->iteration;
|
||||
}),
|
||||
Tables\Columns\TextColumn::make('group_name')
|
||||
->label('Group Name')
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('user_name')
|
||||
->label('User Name')
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->label('Updated At')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('deleted_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TrashedFilter::make(),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ViewAction::make(),
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||
Tables\Actions\RestoreBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListSpotRateTransportMasters::route('/'),
|
||||
'create' => Pages\CreateSpotRateTransportMaster::route('/create'),
|
||||
'view' => Pages\ViewSpotRateTransportMaster::route('/{record}'),
|
||||
'edit' => Pages\EditSpotRateTransportMaster::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SpotRateTransportMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SpotRateTransportMasterResource;
|
||||
use App\Models\SpotRateTransportMaster;
|
||||
use Filament\Actions;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateSpotRateTransportMaster extends CreateRecord
|
||||
{
|
||||
protected static string $resource = SpotRateTransportMasterResource::class;
|
||||
|
||||
protected function beforeCreate(): void
|
||||
{
|
||||
$groupName = $this->data['group_name'] ?? null;
|
||||
$userNames = $this->data['user_name'] ?? [];
|
||||
|
||||
foreach ($userNames as $userName) {
|
||||
|
||||
$query = SpotRateTransportMaster::where('group_name', $groupName)
|
||||
->whereJsonContains('user_name', $userName);
|
||||
|
||||
if ($query->exists()) {
|
||||
|
||||
Notification::make()
|
||||
->title('Duplicate User')
|
||||
->body("User {$userName} already exists in this group.")
|
||||
->danger()
|
||||
->persistent()
|
||||
->send();
|
||||
|
||||
// Prevent create
|
||||
$this->halt();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SpotRateTransportMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SpotRateTransportMasterResource;
|
||||
use App\Models\SpotRateTransportMaster;
|
||||
use Filament\Actions;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditSpotRateTransportMaster extends EditRecord
|
||||
{
|
||||
protected static string $resource = SpotRateTransportMasterResource::class;
|
||||
|
||||
protected function beforeSave(): void
|
||||
{
|
||||
$groupName = $this->data['group_name'] ?? null;
|
||||
$userNames = $this->data['user_name'] ?? [];
|
||||
$recordId = $this->record->id ?? null;
|
||||
|
||||
foreach ($userNames as $userName) {
|
||||
|
||||
$query = SpotRateTransportMaster::where('group_name', $groupName)
|
||||
->whereJsonContains('user_name', $userName);
|
||||
|
||||
// Exclude current record for update
|
||||
if ($recordId) {
|
||||
$query->where('id', '!=', $recordId);
|
||||
}
|
||||
|
||||
if ($query->exists()) {
|
||||
|
||||
Notification::make()
|
||||
->title('Duplicate User')
|
||||
->body("User {$userName} already exists in this group.")
|
||||
->danger()
|
||||
->persistent()
|
||||
->send();
|
||||
|
||||
// Prevent save/update
|
||||
$this->halt();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\ViewAction::make(),
|
||||
Actions\DeleteAction::make(),
|
||||
Actions\ForceDeleteAction::make(),
|
||||
Actions\RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SpotRateTransportMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SpotRateTransportMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListSpotRateTransportMasters extends ListRecords
|
||||
{
|
||||
protected static string $resource = SpotRateTransportMasterResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SpotRateTransportMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SpotRateTransportMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewSpotRateTransportMaster extends ViewRecord
|
||||
{
|
||||
protected static string $resource = SpotRateTransportMasterResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
478
app/Filament/Resources/StickerMappingMasterResource.php
Normal file
478
app/Filament/Resources/StickerMappingMasterResource.php
Normal file
@@ -0,0 +1,478 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Exports\StickerMappingMasterExporter;
|
||||
use App\Filament\Imports\StickerMappingMasterImporter;
|
||||
use App\Filament\Resources\StickerMappingMasterResource\Pages;
|
||||
use App\Filament\Resources\StickerMappingMasterResource\RelationManagers;
|
||||
use App\Models\Item;
|
||||
use App\Models\ItemCharacteristic;
|
||||
use App\Models\Machine;
|
||||
use App\Models\StickerMappingMaster;
|
||||
use App\Models\StickerStructureDetail;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Filament\Tables\Actions\ExportAction;
|
||||
use Filament\Tables\Actions\ImportAction;
|
||||
|
||||
class StickerMappingMasterResource extends Resource
|
||||
{
|
||||
protected static ?string $model = StickerMappingMaster::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
protected static ?string $navigationGroup = 'Customized Sticker Printing';
|
||||
|
||||
// public static function form(Form $form): Form
|
||||
// {
|
||||
// return $form
|
||||
// ->schema([
|
||||
// Forms\Components\TextInput::make('plant_id')
|
||||
// ->required()
|
||||
// ->numeric(),
|
||||
// Forms\Components\TextInput::make('item_id')
|
||||
// ->required()
|
||||
// ->numeric(),
|
||||
// Forms\Components\Textarea::make('sticker1')
|
||||
// ->columnSpanFull(),
|
||||
// Forms\Components\Textarea::make('sticker2')
|
||||
// ->columnSpanFull(),
|
||||
// Forms\Components\Textarea::make('sticker3')
|
||||
// ->columnSpanFull(),
|
||||
// Forms\Components\Textarea::make('sticker4')
|
||||
// ->columnSpanFull(),
|
||||
// Forms\Components\Textarea::make('sticker5')
|
||||
// ->columnSpanFull(),
|
||||
// Forms\Components\Textarea::make('created_by')
|
||||
// ->columnSpanFull(),
|
||||
// Forms\Components\Textarea::make('updated_by')
|
||||
// ->columnSpanFull(),
|
||||
// ]);
|
||||
// }
|
||||
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
/* -------- Basic Mapping -------- */
|
||||
Section::make('Basic Mapping')
|
||||
->schema([
|
||||
Select::make('plant_id')
|
||||
->label('Plant')
|
||||
->reactive()
|
||||
->relationship('plant', 'name')
|
||||
->required(),
|
||||
|
||||
Select::make('item_characteristic_id')
|
||||
->label('Item Code')
|
||||
->reactive()
|
||||
//->relationship('item', 'code')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
|
||||
if (! $plantId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return ItemCharacteristic::query()
|
||||
->whereHas('item', function ($query) use ($plantId) {
|
||||
$query->where('plant_id', $plantId);
|
||||
})
|
||||
->with('item')
|
||||
->get()
|
||||
->mapWithKeys(fn ($ic) => [
|
||||
$ic->id => $ic->item->code,
|
||||
]);
|
||||
})
|
||||
->searchable()
|
||||
->required(),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
/* -------- Sticker 1 -------- */
|
||||
Section::make('Sticker 1')
|
||||
->schema([
|
||||
Select::make('sticker_structure1_id')
|
||||
->label('Sticker ID')
|
||||
->options(function (callable $get) {
|
||||
return StickerStructureDetail::pluck('sticker_id', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Select::make('sticker1_machine_id')
|
||||
->label('Work Center')
|
||||
//->relationship('sticker1Machine', 'name')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sticker1_print_ip')
|
||||
->label('Printer IP')
|
||||
->ipv4(),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
/* -------- Sticker 2 -------- */
|
||||
Section::make('Sticker 2')
|
||||
->schema([
|
||||
Select::make('sticker_structure2_id')
|
||||
->label('Sticker ID')
|
||||
->options(function (callable $get) {
|
||||
return StickerStructureDetail::pluck('sticker_id', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Select::make('sticker2_machine_id')
|
||||
->label('Work Center')
|
||||
// ->relationship('sticker2Machine', 'name')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sticker2_print_ip')
|
||||
->label('Printer IP')
|
||||
->ipv4(),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
/* -------- Sticker 3 -------- */
|
||||
Section::make('Sticker 3')
|
||||
->schema([
|
||||
Select::make('sticker_structure3_id')
|
||||
->label('Sticker ID')
|
||||
->options(function (callable $get) {
|
||||
return StickerStructureDetail::pluck('sticker_id', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Select::make('sticker3_machine_id')
|
||||
->label('Work Center')
|
||||
//->relationship('sticker3Machine', 'name')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sticker3_print_ip')
|
||||
->label('Printer IP')
|
||||
->ipv4(),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
/* -------- Sticker 4 -------- */
|
||||
Section::make('Sticker 4')
|
||||
->schema([
|
||||
Select::make('sticker_structure4_id')
|
||||
->label('Sticker ID')
|
||||
->options(function (callable $get) {
|
||||
return StickerStructureDetail::pluck('sticker_id', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Select::make('sticker4_machine_id')
|
||||
->label('Work Center')
|
||||
//->relationship('sticker4Machine', 'name')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sticker4_print_ip')
|
||||
->label('Printer IP')
|
||||
->ipv4(),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
/* -------- Sticker 5 -------- */
|
||||
Section::make('Sticker 5')
|
||||
->schema([
|
||||
Select::make('sticker_structure5_id')
|
||||
->label('Sticker ID')
|
||||
->options(function (callable $get) {
|
||||
return StickerStructureDetail::pluck('sticker_id', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Select::make('sticker5_machine_id')
|
||||
->label('Work Center')
|
||||
//->relationship('sticker5Machine', 'name')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sticker5_print_ip')
|
||||
->label('Printer IP')
|
||||
->ipv4(),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
/* -------- Sticker 6 -------- */
|
||||
Section::make('Sticker 6')
|
||||
->schema([
|
||||
Select::make('sticker_structure6_id')
|
||||
->label('Sticker ID')
|
||||
->options(function (callable $get) {
|
||||
return StickerStructureDetail::pluck('sticker_id', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Select::make('sticker6_machine_id')
|
||||
->label('Work Center')
|
||||
//->relationship('sticker6Machine', 'name')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sticker6_print_ip')
|
||||
->label('Printer IP')
|
||||
->ipv4(),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
/* -------- Sticker 7 -------- */
|
||||
Section::make('Sticker 7')
|
||||
->schema([
|
||||
Select::make('sticker_structure7_id')
|
||||
->label('Sticker ID')
|
||||
->options(function (callable $get) {
|
||||
return StickerStructureDetail::pluck('sticker_id', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Select::make('sticker7_machine_id')
|
||||
->label('Work Center')
|
||||
//->relationship('sticker7Machine', 'name')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sticker7_print_ip')
|
||||
->label('Printer IP')
|
||||
->ipv4(),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
/* -------- Sticker 8 -------- */
|
||||
Section::make('Sticker 8')
|
||||
->schema([
|
||||
Select::make('sticker_structure8_id')
|
||||
->label('Sticker ID')
|
||||
->options(function (callable $get) {
|
||||
return StickerStructureDetail::pluck('sticker_id', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Select::make('sticker8_machine_id')
|
||||
->label('Work Center')
|
||||
//->relationship('sticker8Machine', 'name')
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sticker8_print_ip')
|
||||
->label('Printer IP')
|
||||
->ipv4(),
|
||||
])
|
||||
->columns(2),
|
||||
]);
|
||||
}
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('No.')
|
||||
->label('No.')
|
||||
->getStateUsing(function ($record, $livewire, $column, $rowLoop) {
|
||||
$paginator = $livewire->getTableRecords();
|
||||
$perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
|
||||
$currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;
|
||||
|
||||
return ($currentPage - 1) * $perPage + $rowLoop->iteration;
|
||||
}),
|
||||
Tables\Columns\TextColumn::make('plant.name')
|
||||
->label('Plant')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('itemCharacteristic.item.code')
|
||||
->label('Item')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('sticker1Structure.sticker_id')
|
||||
->label('Sticker 1')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker1Machine.work_center')
|
||||
->label('WC 1')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker1_print_ip')
|
||||
->label('Printer IP 1')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker2Structure.sticker_id')
|
||||
->label('Sticker 2')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker2Machine.work_center')
|
||||
->label('WC 2')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker2_print_ip')
|
||||
->label('Printer IP 2')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker3Structure.sticker_id')
|
||||
->label('Sticker 3')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker3Machine.work_center')
|
||||
->label('WC 3')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker3_print_ip')
|
||||
->label('Printer IP 3')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker4Structure.sticker_id')
|
||||
->label('Sticker 4')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker4Machine.work_center')
|
||||
->label('WC 4')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker4_print_ip')
|
||||
->label('Printer IP 4')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker5Structure.sticker_id')
|
||||
->label('Sticker 5')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker5Machine.work_center')
|
||||
->label('WC 5')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker5_print_ip')
|
||||
->label('Printer IP 5')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker6Structure.sticker_id')
|
||||
->label('Sticker 6')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker6Machine.work_center')
|
||||
->label('WC 6')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker6_print_ip')
|
||||
->label('Printer IP 6')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker7Structure.sticker_id')
|
||||
->label('Sticker 7')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker7Machine.work_center')
|
||||
->label('WC 7')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker7_print_ip')
|
||||
->label('Printer IP 7')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker8Structure.sticker_id')
|
||||
->label('Sticker 8')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker8Machine.work_center')
|
||||
->label('WC 8')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('sticker8_print_ip')
|
||||
->label('Printer IP 8')
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->alignCenter()
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->label('Updated At')
|
||||
->alignCenter()
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('deleted_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TrashedFilter::make(),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ViewAction::make(),
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||
Tables\Actions\RestoreBulkAction::make(),
|
||||
]),
|
||||
])
|
||||
->headerActions([
|
||||
ImportAction::make()
|
||||
->importer(StickerMappingMasterImporter::class)
|
||||
->label('Import Sticker Mapping Master')
|
||||
->color('warning')
|
||||
->visible(function () {
|
||||
return Filament::auth()->user()->can('view import sticker mapping master');
|
||||
}),
|
||||
ExportAction::make()
|
||||
->exporter(StickerMappingMasterExporter::class)
|
||||
->label('Export Sticker Mapping Master')
|
||||
->color('warning')
|
||||
->visible(function () {
|
||||
return Filament::auth()->user()->can('view export sticker mapping master');
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListStickerMappingMasters::route('/'),
|
||||
'create' => Pages\CreateStickerMappingMaster::route('/create'),
|
||||
'view' => Pages\ViewStickerMappingMaster::route('/{record}'),
|
||||
'edit' => Pages\EditStickerMappingMaster::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\StickerMappingMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\StickerMappingMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateStickerMappingMaster extends CreateRecord
|
||||
{
|
||||
protected static string $resource = StickerMappingMasterResource::class;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\StickerMappingMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\StickerMappingMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditStickerMappingMaster extends EditRecord
|
||||
{
|
||||
protected static string $resource = StickerMappingMasterResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\ViewAction::make(),
|
||||
Actions\DeleteAction::make(),
|
||||
Actions\ForceDeleteAction::make(),
|
||||
Actions\RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\StickerMappingMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\StickerMappingMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListStickerMappingMasters extends ListRecords
|
||||
{
|
||||
protected static string $resource = StickerMappingMasterResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\StickerMappingMasterResource\Pages;
|
||||
|
||||
use App\Filament\Resources\StickerMappingMasterResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewStickerMappingMaster extends ViewRecord
|
||||
{
|
||||
protected static string $resource = StickerMappingMasterResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
165
app/Filament/Resources/StickerValidationResource.php
Normal file
165
app/Filament/Resources/StickerValidationResource.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\StickerValidationResource\Pages;
|
||||
use App\Filament\Resources\StickerValidationResource\RelationManagers;
|
||||
use App\Models\Machine;
|
||||
use App\Models\StickerValidation;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class StickerValidationResource extends Resource
|
||||
{
|
||||
protected static ?string $model = StickerValidation::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
protected static ?string $navigationGroup = 'Customized Sticker Printing';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Section::make('')
|
||||
->schema([
|
||||
Forms\Components\Select::make('plant_id')
|
||||
->label('Plant')
|
||||
->reactive()
|
||||
->relationship('plant', 'name')
|
||||
->required(),
|
||||
Forms\Components\Select::make('machine_id')
|
||||
->label('Work Center')
|
||||
->required()
|
||||
->reactive()
|
||||
->options(function (callable $get) {
|
||||
$plantId = $get('plant_id');
|
||||
if (empty($plantId)) {
|
||||
return [];
|
||||
}
|
||||
return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
|
||||
})
|
||||
->searchable(),
|
||||
Forms\Components\TextInput::make('production_order')
|
||||
->label('Production Order')
|
||||
->reactive()
|
||||
->extraAttributes([
|
||||
'id' => 'production_order_input',
|
||||
'x-data' => '{ value: "" }',
|
||||
'x-model' => 'value',
|
||||
'wire:keydown.enter.prevent' => 'processProOrder(value)',
|
||||
]),
|
||||
Forms\Components\TextInput::make('serial_number')
|
||||
->label('Serial Number')
|
||||
->reactive()
|
||||
->extraAttributes([
|
||||
'id' => 'serial_number_input',
|
||||
'x-data' => '{ value: "" }',
|
||||
'x-model' => 'value',
|
||||
'wire:keydown.enter.prevent' => 'processSno(value)',
|
||||
]),
|
||||
Forms\Components\Hidden::make('created_by')
|
||||
->label('Created By')
|
||||
->default(Filament::auth()->user()?->name),
|
||||
Forms\Components\Hidden::make('updated_by')
|
||||
->label('Updated By')
|
||||
->default(Filament::auth()->user()?->name),
|
||||
])
|
||||
->columns(4),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('No.')
|
||||
->label('No.')
|
||||
->getStateUsing(function ($record, $livewire, $column, $rowLoop) {
|
||||
$paginator = $livewire->getTableRecords();
|
||||
$perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
|
||||
$currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;
|
||||
|
||||
return ($currentPage - 1) * $perPage + $rowLoop->iteration;
|
||||
}),
|
||||
Tables\Columns\TextColumn::make('plant.code')
|
||||
->label('Plant')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('machine.work_center')
|
||||
->label('Work Center')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('production_order')
|
||||
->label('Production Order')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('serial_number')
|
||||
->label('Serial Number')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('status')
|
||||
->label('Status')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('deleted_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TrashedFilter::make(),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ViewAction::make(),
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||
Tables\Actions\RestoreBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListStickerValidations::route('/'),
|
||||
'create' => Pages\CreateStickerValidation::route('/create'),
|
||||
'view' => Pages\ViewStickerValidation::route('/{record}'),
|
||||
'edit' => Pages\EditStickerValidation::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\StickerValidationResource\Pages;
|
||||
|
||||
use App\Filament\Resources\StickerValidationResource;
|
||||
use App\Models\Item;
|
||||
use App\Models\ItemCharacteristic;
|
||||
use App\Models\ProductionQuantity;
|
||||
use App\Models\StickerDetail;
|
||||
use App\Models\StickerMappingMaster;
|
||||
use App\Models\StickerStructureDetail;
|
||||
use App\Models\StickerValidation;
|
||||
use App\Services\StickerPdfService;
|
||||
use Filament\Actions;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateStickerValidation extends CreateRecord
|
||||
{
|
||||
protected static string $resource = StickerValidationResource::class;
|
||||
|
||||
protected static string $view = 'filament.resources.sticker-validation-resource.create-sticker-validation';
|
||||
|
||||
public $ref_number;
|
||||
|
||||
public $plantId;
|
||||
|
||||
public $workCenter;
|
||||
|
||||
public $serNo;
|
||||
|
||||
public function getFormActions(): array
|
||||
{
|
||||
return [
|
||||
$this->getCancelFormAction(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return $this->getResource()::getUrl('create');
|
||||
}
|
||||
|
||||
|
||||
public function processProOrder($value)
|
||||
{
|
||||
$plantId = $this->form->getState()['plant_id'];
|
||||
|
||||
$this->plantId = $plantId;
|
||||
|
||||
$this->ref_number = $value;
|
||||
|
||||
$this->dispatch('refreshEmptySticker', $plantId, $value);
|
||||
|
||||
$this->dispatch('focus-serial-number');
|
||||
}
|
||||
|
||||
public function processSno($serNo)
|
||||
{
|
||||
$plantId = $this->form->getState()['plant_id'];
|
||||
|
||||
$this->plantId = $plantId;
|
||||
|
||||
$workCenter = $this->form->getState()['machine_id'];
|
||||
|
||||
$this->workCenter = $workCenter;
|
||||
|
||||
$this->ref_number = $this->form->getState()['production_order'];
|
||||
|
||||
$this->serNo = $serNo;
|
||||
|
||||
$user = Filament::auth()->user();
|
||||
|
||||
$operatorName = $user->name;
|
||||
|
||||
if (! preg_match('/^([a-zA-Z0-9]{6,})\|([1-9][a-zA-Z0-9]{8,})\|?$/', $this->serNo, $matches)) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title('Invalid Serial QR Format')
|
||||
->body('Scan valid Serial QR code proceed!<br>Sample formats are:<br>123456|1234567890123| OR 123456|1234567890123')
|
||||
->seconds(3)
|
||||
->send();
|
||||
|
||||
$this->dispatch('playWarnSound');
|
||||
|
||||
$this->form->fill([
|
||||
'plant_id' => $this->plantId,
|
||||
'machine_id' => $this->workCenter,
|
||||
'production_order' => $this->ref_number,
|
||||
'serial_number' => null,
|
||||
]);
|
||||
|
||||
$this->dispatch('focus-serial-number');
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$itemCode = $matches[1];
|
||||
$serialNumber = $matches[2];
|
||||
|
||||
$recFound = ProductionQuantity::where('plant_id', $this->plantId)
|
||||
->where('production_order', $this->ref_number)
|
||||
->where('serial_number', $serialNumber)
|
||||
->first();
|
||||
|
||||
if(!$recFound){
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title('Unknown Serial Number')
|
||||
->body("Scanned serial number '$serialNumber' not found for the given plant and production order")
|
||||
->seconds(3)
|
||||
->send();
|
||||
$this->form->fill([
|
||||
'plant_id' => $this->plantId,
|
||||
'machine_id' => $this->workCenter,
|
||||
'production_order' => $this->ref_number,
|
||||
'serial_number' => null,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$duplicate = StickerValidation::where('plant_id', $this->plantId)
|
||||
->where('production_order', $this->ref_number)
|
||||
->where('serial_number', $serialNumber)
|
||||
->first();
|
||||
|
||||
if ($duplicate) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title('Duplicate Serial Number')
|
||||
->body("Serial number $serialNumber already exists for this plant and production order!")
|
||||
->seconds(3)
|
||||
->send();
|
||||
|
||||
$this->form->fill([
|
||||
'plant_id' => $this->plantId,
|
||||
'machine_id' => $this->workCenter,
|
||||
'production_order' => $this->ref_number,
|
||||
'serial_number' => null,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$itemC = Item::where('code', $itemCode)
|
||||
->where('plant_id',$this->plantId)
|
||||
->first();
|
||||
|
||||
$itemId = $itemC->id;
|
||||
|
||||
$item = ItemCharacteristic::where('item_id', $itemId)
|
||||
->where('plant_id',$this->plantId)
|
||||
->first();
|
||||
|
||||
$itemI = $item->id;
|
||||
|
||||
$mapping = StickerMappingMaster::where('plant_id', $this->plantId)
|
||||
->where('item_characteristic_id', $itemI)
|
||||
->first();
|
||||
|
||||
if (!$mapping) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title('Sticker Mapping Not Found')
|
||||
->body("No sticker mapping found for this item and plant.")
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$stickers = [];
|
||||
|
||||
for ($i = 1; $i <= 8; $i++) {
|
||||
$machineColumn = "sticker{$i}_machine_id";
|
||||
$ipColumn = "sticker{$i}_print_ip";
|
||||
$stickerColumn = "sticker_structure{$i}_id";
|
||||
$itemColumn = "item_characteristic_id";
|
||||
|
||||
if (
|
||||
!empty($mapping->$machineColumn) &&
|
||||
!empty($mapping->$stickerColumn)
|
||||
) {
|
||||
$stickers[] = [
|
||||
'machine_id' => $mapping->$machineColumn,
|
||||
'sticker_id' => $mapping->$stickerColumn,
|
||||
'item_characteristic' => $mapping->$itemColumn,
|
||||
'print_ip' => $mapping->$ipColumn,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($stickers)) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title('No Sticker Configuration Found')
|
||||
->body('No sticker and machine mappings configured for this item and plant.')
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
|
||||
StickerValidation::create([
|
||||
'plant_id' => $this->plantId,
|
||||
'machine_id' => $this->workCenter,
|
||||
'production_order' => $this->ref_number ?? null,
|
||||
'serial_number' => $serialNumber,
|
||||
'status' => 'Printed',
|
||||
// 'sticker_id' => $matchedSticker,
|
||||
'created_by' => $operatorName,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Sticker Recorded')
|
||||
->body("Item: $itemCode, Serial: $serialNumber recorded successfully!")
|
||||
->seconds(3)
|
||||
->send();
|
||||
|
||||
$this->form->fill([
|
||||
'plant_id' => $this->plantId,
|
||||
'machine_id' => $this->workCenter,
|
||||
'production_order' => $this->ref_number,
|
||||
'serial_number' => null,
|
||||
]);
|
||||
|
||||
$this->dispatch('refreshEmptySticker', $plantId, $this->ref_number);
|
||||
|
||||
|
||||
// foreach ($stickers as $sticker) {
|
||||
|
||||
// // $printerName = $this->getCupsPrinterNameByIp($sticker['print_ip']);
|
||||
|
||||
// \Log::info("Looking up printer for IP: " . $sticker['print_ip']);
|
||||
// $printerName = $this->getCupsPrinterNameByIp($sticker['print_ip']);
|
||||
// \Log::info("Found printer: " . ($printerName ?? 'NULL'));
|
||||
|
||||
// if (! $printerName) {
|
||||
// Notification::make()
|
||||
// ->danger()
|
||||
// ->title('Printer Not Found')
|
||||
// ->body("No CUPS printer configured for IP: {$sticker['print_ip']}")
|
||||
// ->send();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// $structure = StickerStructureDetail::findOrFail($sticker['sticker_id']);
|
||||
// $itemCharacteristic = ItemCharacteristic::where('plant_id', $this->plantId)
|
||||
// ->where('id', $sticker['item_characteristic'])
|
||||
// ->firstOrFail();
|
||||
|
||||
// $dynamicElements = StickerDetail::where(
|
||||
// 'sticker_structure_detail_id',
|
||||
// $structure->id
|
||||
// )->where('element_type', 'Dynamic')->get();
|
||||
|
||||
|
||||
|
||||
// /** STEP 3: Stream PDF to CUPS (STDIN) */
|
||||
// $process = proc_open(
|
||||
// 'lp -d ' . escapeshellarg($printerName) . ' -o fit-to-page -',
|
||||
// [
|
||||
// ['pipe', 'r'], // STDIN
|
||||
// ['pipe', 'w'], // STDOUT
|
||||
// ['pipe', 'w'], // STDERR
|
||||
// ],
|
||||
// $pipes
|
||||
// );
|
||||
|
||||
|
||||
// if (! is_resource($process)) {
|
||||
// Notification::make()
|
||||
// ->danger()
|
||||
// ->title('Print Failed')
|
||||
// ->body('Unable to start CUPS print process.')
|
||||
// ->send();
|
||||
// return;
|
||||
// // continue;
|
||||
// }
|
||||
|
||||
// $pdfContent = (new StickerPdfService())->generatePdf1(
|
||||
// $structure->sticker_id,
|
||||
// $dynamicElements,
|
||||
// $itemCharacteristic,
|
||||
// $serialNumber,
|
||||
// $serNo
|
||||
// );
|
||||
|
||||
// fwrite($pipes[0], $pdfContent);
|
||||
// fclose($pipes[0]);
|
||||
|
||||
// $stderr = stream_get_contents($pipes[2]);
|
||||
// fclose($pipes[1]);
|
||||
// fclose($pipes[2]);
|
||||
|
||||
// $status = proc_close($process);
|
||||
|
||||
// if ($status != 0) {
|
||||
// Notification::make()
|
||||
// ->danger()
|
||||
// ->title('Print Failed')
|
||||
// ->body("CUPS error: {$stderr}")
|
||||
// ->send();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
foreach ($stickers as $sticker)
|
||||
{
|
||||
|
||||
\Log::info("Looking up printer for IP: " . $sticker['print_ip']);
|
||||
|
||||
$printerName = $this->getCupsPrinterNameByIp($sticker['print_ip']);
|
||||
|
||||
\Log::info("Found printer: " . ($printerName ?? 'NULL'));
|
||||
|
||||
if (! $printerName) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title('Printer Not Found')
|
||||
->body("No CUPS printer configured for IP: {$sticker['print_ip']}")
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$structure = StickerStructureDetail::findOrFail($sticker['sticker_id']);
|
||||
|
||||
$itemCharacteristic = ItemCharacteristic::where('plant_id', $this->plantId)
|
||||
->where('id', $sticker['item_characteristic'])
|
||||
->firstOrFail();
|
||||
|
||||
$dynamicElements = StickerDetail::where(
|
||||
'sticker_structure_detail_id',
|
||||
$structure->id
|
||||
)->where('element_type', 'Dynamic')->get();
|
||||
|
||||
$pdfContent = (new StickerPdfService())->generatePdf1(
|
||||
$structure->sticker_id,
|
||||
$dynamicElements,
|
||||
$itemCharacteristic,
|
||||
$serialNumber,
|
||||
$serNo
|
||||
);
|
||||
|
||||
$tempPdfPath = storage_path('app/temp_sticker_' . uniqid() . '.pdf');
|
||||
file_put_contents($tempPdfPath, $pdfContent);
|
||||
|
||||
exec(
|
||||
"lp -d " . escapeshellarg($printerName) . " " . escapeshellarg($tempPdfPath),
|
||||
$output,
|
||||
$status
|
||||
);
|
||||
|
||||
\Log::info("LP Output:", $output);
|
||||
\Log::info("LP Status: " . $status);
|
||||
|
||||
if ($status != 0) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title('Print Failed')
|
||||
->body("CUPS error while printing.")
|
||||
->send();
|
||||
|
||||
if (file_exists($tempPdfPath)) {
|
||||
unlink($tempPdfPath);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_exists($tempPdfPath)) {
|
||||
unlink($tempPdfPath);
|
||||
}
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Sticker Printed')
|
||||
->body("Sticker for Serial Number: $serialNumber printed successfully!")
|
||||
->seconds(3)
|
||||
->send();
|
||||
|
||||
// [$itemCode, $serialNumber] = explode('|', $serNo);
|
||||
|
||||
// $this->dispatch('open-sticker-pdf', [
|
||||
// 'url' => url("/sticker/pdf/{$itemCode}/{$serialNumber}/$this->plantId/$this->ref_number")
|
||||
// ]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCupsPrinterNameByIp(string $ip): ?string
|
||||
{
|
||||
|
||||
// exec('lpstat -v 2>&1', $output, $status);
|
||||
|
||||
exec('lpstat -h cups:631 -v 2>&1', $output, $status);
|
||||
|
||||
if ($status != 0 || empty($output)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($output as $line){
|
||||
$parts = explode(':', $line, 2);
|
||||
if (count($parts) < 2) continue;
|
||||
|
||||
$printerName = trim(str_replace('device for', '', $parts[0]));
|
||||
$deviceUri = trim($parts[1]);
|
||||
|
||||
if (str_contains($deviceUri, $ip)) {
|
||||
return $printerName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\StickerValidationResource\Pages;
|
||||
|
||||
use App\Filament\Resources\StickerValidationResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditStickerValidation extends EditRecord
|
||||
{
|
||||
protected static string $resource = StickerValidationResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\ViewAction::make(),
|
||||
Actions\DeleteAction::make(),
|
||||
Actions\ForceDeleteAction::make(),
|
||||
Actions\RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\StickerValidationResource\Pages;
|
||||
|
||||
use App\Filament\Resources\StickerValidationResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListStickerValidations extends ListRecords
|
||||
{
|
||||
protected static string $resource = StickerValidationResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\StickerValidationResource\Pages;
|
||||
|
||||
use App\Filament\Resources\StickerValidationResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewStickerValidation extends ViewRecord
|
||||
{
|
||||
protected static string $resource = StickerValidationResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
276
app/Filament/Resources/VisitorEntryResource.php
Normal file
276
app/Filament/Resources/VisitorEntryResource.php
Normal file
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\VisitorEntryResource\Pages;
|
||||
use App\Filament\Resources\VisitorEntryResource\RelationManagers;
|
||||
use App\Models\VisitorEntry;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class VisitorEntryResource extends Resource
|
||||
{
|
||||
protected static ?string $model = VisitorEntry::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('mobile_number')
|
||||
->label('Mobile Number')
|
||||
->length(10)
|
||||
->reactive()
|
||||
->extraInputAttributes([
|
||||
'oninput' => 'this.value = this.value.replace(/[^0-9]/g, "").slice(0, 10)', // blocks non-numbers + limits to 10 chars
|
||||
'maxlength' => 10,
|
||||
])
|
||||
->required()
|
||||
->extraAttributes([
|
||||
'id' => 'mobile_number_input',
|
||||
'x-data' => '{ value: "" }',
|
||||
'x-model' => 'value',
|
||||
'wire:keydown.enter.prevent' => 'processMobile(value)',
|
||||
]),
|
||||
Forms\Components\TextInput::make('name')
|
||||
->label('Name')
|
||||
->required()
|
||||
->reactive()
|
||||
->extraInputAttributes([
|
||||
'oninput' => 'this.value = this.value.replace(/[^a-zA-Z\s]/g, "")',
|
||||
]),
|
||||
Forms\Components\Select::make('type')
|
||||
->label('Type')
|
||||
->reactive()
|
||||
->options([
|
||||
'Student' => 'Student',
|
||||
'Consultant' => 'Consultant',
|
||||
'Vendor' => 'Vendor',
|
||||
'Other' => 'Other',
|
||||
])
|
||||
->required()
|
||||
->dehydrateStateUsing(function ($state, callable $get) {
|
||||
return $state == 'Other'
|
||||
? $get('other_type')
|
||||
: $state;
|
||||
}),
|
||||
Forms\Components\TextInput::make('other_type')
|
||||
->label('Specify Type')
|
||||
->reactive()
|
||||
->visible(fn (callable $get) => $get('type') == 'Other')
|
||||
->required(fn (callable $get) => $get('type') == 'Other')
|
||||
->dehydrated(false),
|
||||
Forms\Components\TextInput::make('company')
|
||||
->label('Company')
|
||||
->required(),
|
||||
Forms\Components\Select::make('department')
|
||||
->label('Employee Department')
|
||||
->options(
|
||||
\App\Models\EmployeeMaster::distinct()
|
||||
->pluck('department', 'department')
|
||||
)
|
||||
->required()
|
||||
->reactive()
|
||||
->afterStateUpdated(function (callable $set) {
|
||||
$set('employee_master_id', null);
|
||||
$set('code', null);
|
||||
}),
|
||||
// Forms\Components\Select::make('employee_master_id')
|
||||
// ->label('Recipient Employee')
|
||||
// ->required()
|
||||
// ->options(function (callable $get) {
|
||||
// $department = $get('department');
|
||||
|
||||
// if (!$department) {
|
||||
// return [];
|
||||
// }
|
||||
|
||||
// return \App\Models\EmployeeMaster::where('department', $department)
|
||||
// ->pluck('name', 'id');
|
||||
// })
|
||||
// ->reactive()
|
||||
// ->afterStateUpdated(function (callable $set, callable $get, ?string $state) {
|
||||
// $department = $get('department');
|
||||
|
||||
// $employee = \App\Models\EmployeeMaster::where('id', $state)
|
||||
// ->where('department', $department)
|
||||
// ->first();
|
||||
|
||||
// $set('code', $employee ? $employee->code : '');
|
||||
// }),
|
||||
|
||||
Forms\Components\Select::make('employee_master_id')
|
||||
->label('Recipient Employee')
|
||||
->required()
|
||||
->options(function (callable $get) {
|
||||
$department = $get('department');
|
||||
// Always load ALL employees, filter by department if set
|
||||
if ($department) {
|
||||
return \App\Models\EmployeeMaster::where('department', $department)
|
||||
->pluck('name', 'id');
|
||||
}
|
||||
// Fallback: load all so fill() can always match the ID
|
||||
return \App\Models\EmployeeMaster::pluck('name', 'id');
|
||||
})
|
||||
->reactive()
|
||||
->afterStateUpdated(function (callable $set, ?string $state) {
|
||||
$employee = \App\Models\EmployeeMaster::find($state);
|
||||
$set('code', $employee?->code ?? '');
|
||||
}),
|
||||
|
||||
Forms\Components\TextInput::make('code')
|
||||
->label('Employee Code')
|
||||
->readOnly(),
|
||||
Forms\Components\Textarea::make('purpose_of_visit')
|
||||
->label('Purpose of Visit')
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('number_of_person')
|
||||
->numeric()
|
||||
->default(1)
|
||||
->required(),
|
||||
Forms\Components\DateTimePicker::make('in_time')
|
||||
->label('In Time'),
|
||||
Forms\Components\DateTimePicker::make('out_time')
|
||||
->label('Out Time'),
|
||||
Forms\Components\View::make('components.webcam-field')
|
||||
->columnSpanFull(),
|
||||
Forms\Components\Hidden::make('photo'),
|
||||
Forms\Components\Hidden::make('created_by')
|
||||
->label('created_by')
|
||||
->default(Filament::auth()->user()?->name),
|
||||
Forms\Components\Hidden::make('updated_by')
|
||||
->label('updated_by')
|
||||
->default(Filament::auth()->user()?->name),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('No.')
|
||||
->label('NO')
|
||||
->alignCenter()
|
||||
->getStateUsing(function ($record, $livewire, $column, $rowLoop) {
|
||||
$paginator = $livewire->getTableRecords();
|
||||
$perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
|
||||
$currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;
|
||||
|
||||
return ($currentPage - 1) * $perPage + $rowLoop->iteration;
|
||||
}),
|
||||
Tables\Columns\ImageColumn::make('photo')
|
||||
->label('Photo')
|
||||
->disk('public')
|
||||
->height(50)
|
||||
->width(50)
|
||||
// ->defaultImageUrl('https://ui-avatars.com/api/?name=Visitor&background=555&color=fff')
|
||||
->defaultImageUrl(asset('images/profile.png'))
|
||||
->alignCenter()
|
||||
->extraImgAttributes(['style' => 'border-radius: 6px; object-fit: cover;']),
|
||||
Tables\Columns\TextColumn::make('type')
|
||||
->label('Visitor Type')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->label('Visitor Name')
|
||||
->sortable()
|
||||
->alignCenter()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('mobile_number')
|
||||
->label('Visitor Mobile Number')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('employeeMaster.name')
|
||||
->label('Recipient Name')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('employeeMaster.code')
|
||||
->label('Receipient ID')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('employeeMaster.department')
|
||||
->label('Receipient Department')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('number_of_person')
|
||||
->label('Number of Person')
|
||||
->numeric()
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('in_time')
|
||||
->label('In Time')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('out_time')
|
||||
->label('Out Time')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true)
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->label('Updated At')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true)
|
||||
->alignCenter(),
|
||||
Tables\Columns\TextColumn::make('deleted_at')
|
||||
->label('Deleted At')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true)
|
||||
->alignCenter(),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TrashedFilter::make(),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ViewAction::make(),
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||
Tables\Actions\RestoreBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListVisitorEntries::route('/'),
|
||||
'create' => Pages\CreateVisitorEntry::route('/create'),
|
||||
'view' => Pages\ViewVisitorEntry::route('/{record}'),
|
||||
'edit' => Pages\EditVisitorEntry::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\VisitorEntryResource\Pages;
|
||||
|
||||
use App\Filament\Resources\VisitorEntryResource;
|
||||
use App\Models\EmployeeMaster;
|
||||
use App\Models\VisitorEntry;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Livewire\Attributes\On;
|
||||
use Storage;
|
||||
|
||||
class CreateVisitorEntry extends CreateRecord
|
||||
{
|
||||
protected static string $resource = VisitorEntryResource::class;
|
||||
|
||||
public $capturedPhoto;
|
||||
|
||||
// #[On('photo-captured')]
|
||||
// public function handlePhotoCapture(string $photo): void
|
||||
// {
|
||||
// // Puts the Base64 photo into the form's data array
|
||||
// $this->data['photo'] = $photo;
|
||||
// }
|
||||
|
||||
public function processMobile($mobile)
|
||||
{
|
||||
$visitor = VisitorEntry::where('mobile_number', $mobile)->latest()->first();
|
||||
|
||||
if ($visitor) {
|
||||
|
||||
$employee = EmployeeMaster::where('id', $visitor->employee_master_id)->first();
|
||||
|
||||
$this->form->fill([
|
||||
'mobile_number' => $mobile ?? '',
|
||||
'name' => $visitor->name ?? '',
|
||||
'company' => $visitor->company ?? '',
|
||||
'type' => $visitor->type ?? '',
|
||||
'department' => $employee->department ?? '',
|
||||
'employee_master_id' => $visitor->employee_master_id->name ?? '',
|
||||
'code' => $employee->code ?? '',
|
||||
]);
|
||||
}
|
||||
else {
|
||||
|
||||
$this->form->fill([
|
||||
'mobile_number' => $mobile ?? '',
|
||||
'name' => $visitor->name ?? '',
|
||||
'company' => $visitor->company ?? '',
|
||||
'type' => $visitor->type ?? '',
|
||||
'department' => $employee->department ?? '',
|
||||
'employee_master_id' => $visitor->employee_master_id->name ?? '',
|
||||
'code' => $employee->code ?? '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// protected function mutateFormDataBeforeCreate(array $data): array
|
||||
// {
|
||||
// if (
|
||||
// !empty($data['photo']) &&
|
||||
// str_starts_with($data['photo'], 'data:image')
|
||||
// ) {
|
||||
// // Step A: Strip the "data:image/jpeg;base64," prefix
|
||||
// $imageData = explode(',', $data['photo'])[1];
|
||||
|
||||
// // Step B: Generate a unique filename
|
||||
// $filename = 'visitor_' . time() . '_' . uniqid() . '.jpg';
|
||||
|
||||
// // Step C: Decode Base64 and save as a real .jpg file
|
||||
// $path = 'visitor-photos/' . $filename;
|
||||
// Storage::disk('public')->put($path, base64_decode($imageData));
|
||||
|
||||
// // Step D: Replace the Base64 string with just the file path
|
||||
// $data['photo'] = $path;
|
||||
// }
|
||||
|
||||
// return $data;
|
||||
// }
|
||||
|
||||
#[On('photo-captured')]
|
||||
public function handlePhotoCapture(string $photo): void
|
||||
{
|
||||
$this->data['photo'] = $photo;
|
||||
\Log::info('WEBCAM: photo-captured event received, length: ' . strlen($photo));
|
||||
}
|
||||
|
||||
// protected function mutateFormDataBeforeCreate(array $data): array
|
||||
// {
|
||||
// \Log::info('WEBCAM: mutateFormDataBeforeCreate called, photo value: ' . substr($data['photo'] ?? 'NULL', 0, 50));
|
||||
|
||||
// if (
|
||||
// !empty($data['photo']) &&
|
||||
// str_starts_with($data['photo'], 'data:image')
|
||||
// ) {
|
||||
// $imageData = explode(',', $data['photo'])[1];
|
||||
// $filename = 'visitor_' . time() . '_' . uniqid() . '.jpg';
|
||||
// $path = 'visitor-photos/' . $filename;
|
||||
// Storage::disk('public')->put($path, base64_decode($imageData));
|
||||
// $data['photo'] = $path;
|
||||
|
||||
// \Log::info('WEBCAM: photo saved to ' . $path);
|
||||
// }
|
||||
|
||||
// return $data;
|
||||
// }
|
||||
|
||||
protected function mutateFormDataBeforeCreate(array $data): array
|
||||
{
|
||||
if (
|
||||
!empty($data['photo']) &&
|
||||
str_starts_with($data['photo'], 'data:image')
|
||||
) {
|
||||
try {
|
||||
$imageData = explode(',', $data['photo'])[1];
|
||||
|
||||
$filename = 'visitor_' . time() . '_' . uniqid() . '.jpg';
|
||||
|
||||
$path = 'visitor-photos/' . $filename;
|
||||
|
||||
$decoded = base64_decode($imageData);
|
||||
|
||||
$saved = Storage::disk('public')->put($path, $decoded);
|
||||
|
||||
\Log::info('PHOTO UPLOAD (PUBLIC):', [
|
||||
'filename' => $filename,
|
||||
'path' => $path,
|
||||
'size_bytes' => strlen($decoded),
|
||||
'saved' => $saved ? 'SUCCESS' : 'FAILED',
|
||||
]);
|
||||
|
||||
$data['photo'] = $path;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('PHOTO UPLOAD ERROR: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function setPhoto(string $photo): void
|
||||
{
|
||||
$this->capturedPhoto = $photo;
|
||||
|
||||
// Change this ↓ to dispatch to parent explicitly
|
||||
$this->dispatch('photo-captured', photo: $photo)->to(\App\Filament\Resources\VisitorEntryResource\Pages\CreateVisitorEntry::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\VisitorEntryResource\Pages;
|
||||
|
||||
use App\Filament\Resources\VisitorEntryResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
use Livewire\Attributes\On;
|
||||
use Storage;
|
||||
|
||||
class EditVisitorEntry extends EditRecord
|
||||
{
|
||||
protected static string $resource = VisitorEntryResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\ViewAction::make(),
|
||||
Actions\DeleteAction::make(),
|
||||
Actions\ForceDeleteAction::make(),
|
||||
Actions\RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
#[On('photo-captured')]
|
||||
public function handlePhotoCapture(string $photo): void
|
||||
{
|
||||
$this->data['photo'] = $photo;
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeSave(array $data): array
|
||||
{
|
||||
if (
|
||||
!empty($data['photo']) &&
|
||||
str_starts_with($data['photo'], 'data:image')
|
||||
) {
|
||||
// Delete the old photo file if one exists
|
||||
$oldPhoto = $this->record->photo;
|
||||
if ($oldPhoto && Storage::disk('public')->exists($oldPhoto)) {
|
||||
Storage::disk('public')->delete($oldPhoto);
|
||||
}
|
||||
|
||||
// Save the new photo
|
||||
$imageData = explode(',', $data['photo'])[1];
|
||||
$filename = 'visitor_' . time() . '_' . uniqid() . '.jpg';
|
||||
$path = 'visitor-photos/' . $filename;
|
||||
Storage::disk('public')->put($path, base64_decode($imageData));
|
||||
|
||||
$data['photo'] = $path;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\VisitorEntryResource\Pages;
|
||||
|
||||
use App\Filament\Resources\VisitorEntryResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListVisitorEntries extends ListRecords
|
||||
{
|
||||
protected static string $resource = VisitorEntryResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\VisitorEntryResource\Pages;
|
||||
|
||||
use App\Filament\Resources\VisitorEntryResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewVisitorEntry extends ViewRecord
|
||||
{
|
||||
protected static string $resource = VisitorEntryResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
146
app/Filament/Widgets/RfqChart.php
Normal file
146
app/Filament/Widgets/RfqChart.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\RequestQuotation;
|
||||
use App\Models\RfqTransporterBid;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
class RfqChart extends BaseWidget
|
||||
{
|
||||
// protected function getStats(): array
|
||||
// {
|
||||
// $transporter = session('transport_name');
|
||||
// $rfqNumber = session('rfq_number');
|
||||
|
||||
// if (!$transporter || !$rfqNumber) {
|
||||
// return [
|
||||
// Stat::make('Total Freight Charge', '-'),
|
||||
// Stat::make('Rank', '-'),
|
||||
// ];
|
||||
// }
|
||||
|
||||
// $selectedRfq = RequestQuotation::query()
|
||||
// ->where('transporter_name', $transporter)
|
||||
// ->where('rfq_number', $rfqNumber)
|
||||
// ->first();
|
||||
|
||||
// if (!$selectedRfq) {
|
||||
// return [
|
||||
// Stat::make('Total Freight Charge', '-'),
|
||||
// Stat::make('Rank', '-'),
|
||||
// ];
|
||||
// }
|
||||
|
||||
// $myAmount = (float) $selectedRfq->total_freight_charge;
|
||||
|
||||
// $rank = RequestQuotation::query()
|
||||
// ->whereRaw(
|
||||
// 'CAST(total_freight_charge AS DECIMAL(10,2)) < ?',
|
||||
// [$myAmount]
|
||||
// )
|
||||
// ->selectRaw('CAST(total_freight_charge AS DECIMAL(10,2))')
|
||||
// ->distinct()
|
||||
// ->count() + 1;
|
||||
|
||||
// $medal = match (true) {
|
||||
// $rank == 1 => '🥇',
|
||||
// $rank == 2 => '🥈',
|
||||
// $rank == 3 => '🥉',
|
||||
// default => '',
|
||||
// };
|
||||
|
||||
// return [
|
||||
// Stat::make(
|
||||
// 'Total Freight Charge',
|
||||
// number_format($selectedRfq->total_freight_charge, 2)
|
||||
// )
|
||||
// ->description('Transporter: ' . $selectedRfq->transporter_name)
|
||||
// ->color($rank == 1 ? 'success' : 'primary'),
|
||||
|
||||
// Stat::make(
|
||||
// 'Rank',
|
||||
// trim("{$medal} #{$rank}")
|
||||
// )
|
||||
// ->description('Among all transporters')
|
||||
// ->color(
|
||||
// $rank == 1 ? 'success' :
|
||||
// ($rank <= 3 ? 'warning' : 'gray')
|
||||
// ),
|
||||
// ];
|
||||
// }
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
$transporter = session('transport_name');
|
||||
$rfqNumber = session('rfq_id');
|
||||
|
||||
if (!$transporter || !$rfqNumber) {
|
||||
return [
|
||||
Stat::make('Total Freight Charge', '-'),
|
||||
Stat::make('Rank', '-'),
|
||||
];
|
||||
}
|
||||
|
||||
$selectedRfq = RfqTransporterBid::query()
|
||||
->where('transporter_name', $transporter)
|
||||
->where('request_quotation_id', $rfqNumber)
|
||||
->first();
|
||||
|
||||
if (!$selectedRfq) {
|
||||
return [
|
||||
Stat::make('Total Freight Charge', '-'),
|
||||
Stat::make('Rank', '-'),
|
||||
];
|
||||
}
|
||||
|
||||
$myAmount = (float) $selectedRfq->total_freight_charge;
|
||||
|
||||
// $rank = RfqTransporterBid::query()
|
||||
// ->whereRaw(
|
||||
// 'CAST(total_freight_charge AS DECIMAL(10,2)) < ?',
|
||||
// [$myAmount]
|
||||
// )
|
||||
// ->selectRaw('CAST(total_freight_charge AS DECIMAL(10,2))')
|
||||
// ->distinct()
|
||||
// ->count() + 1;
|
||||
$rank = RfqTransporterBid::query()
|
||||
->where('request_quotation_id', $rfqNumber) // 🔥 MISSING CONDITION
|
||||
->whereRaw(
|
||||
'CAST(total_freight_charge AS DECIMAL(10,2)) < ?',
|
||||
[$myAmount]
|
||||
)
|
||||
->selectRaw('CAST(total_freight_charge AS DECIMAL(10,2))')
|
||||
->distinct()
|
||||
->count() + 1;
|
||||
|
||||
|
||||
$medal = match (true) {
|
||||
$rank == 1 => '🥇',
|
||||
$rank == 2 => '🥈',
|
||||
$rank == 3 => '🥉',
|
||||
default => '',
|
||||
};
|
||||
|
||||
return [
|
||||
Stat::make(
|
||||
'Total Freight Charge',
|
||||
number_format($selectedRfq->total_freight_charge, 2)
|
||||
)
|
||||
->description('Transporter: ' . $selectedRfq->transporter_name)
|
||||
->color($rank == 1 ? 'success' : 'primary'),
|
||||
|
||||
Stat::make(
|
||||
'Rank',
|
||||
trim("{$medal} #{$rank}")
|
||||
)
|
||||
->description('Among all transporters')
|
||||
->color(
|
||||
$rank == 1 ? 'success' :
|
||||
($rank <= 3 ? 'warning' : 'gray')
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
205
app/Filament/Widgets/RfqRankChart.php
Normal file
205
app/Filament/Widgets/RfqRankChart.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\RfqTransporterBid;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
use Illuminate\Support\Js;
|
||||
|
||||
class RfqRankChart extends ChartWidget
|
||||
{
|
||||
protected static ?string $heading = 'Chart';
|
||||
|
||||
// protected function getData(): array
|
||||
// {
|
||||
// $rfqId = session('rfq_id');
|
||||
|
||||
// if (!$rfqId) {
|
||||
// return [
|
||||
// 'datasets' => [],
|
||||
// 'labels' => [],
|
||||
// ];
|
||||
// }
|
||||
|
||||
// // Get bids ordered by lowest freight charge
|
||||
// $bids = RfqTransporterBid::query()
|
||||
// ->where('request_quotation_id', $rfqId)
|
||||
// ->orderByRaw('CAST(total_freight_charge AS DECIMAL(10,2)) ASC')
|
||||
// ->get();
|
||||
|
||||
// // $labels = [];
|
||||
// // $ranks = [];
|
||||
|
||||
// // $rank = 1;
|
||||
// // foreach ($bids as $bid) {
|
||||
// // $labels[] = $bid->transporter_name;
|
||||
// // $ranks[] = $rank++;
|
||||
// // }
|
||||
|
||||
// // return [
|
||||
// // 'datasets' => [
|
||||
// // [
|
||||
// // 'label' => 'Rank (Lower is Better)',
|
||||
// // 'data' => $ranks,
|
||||
// // 'fill' => false,
|
||||
// // 'tension' => 0.3,
|
||||
// // ],
|
||||
// // ],
|
||||
// // 'labels' => $labels,
|
||||
// // ];
|
||||
// $labels = [];
|
||||
// $ranks = [];
|
||||
// $colors = [];
|
||||
|
||||
// $rank = 1;
|
||||
// foreach ($bids as $bid) {
|
||||
// $labels[] = $bid->transporter_name;
|
||||
// $ranks[] = $rank;
|
||||
|
||||
// // Rank-based colors
|
||||
// $colors[] = match ($rank) {
|
||||
// 1 => '#FFD700', // Gold
|
||||
// 2 => '#C0C0C0', // Silver
|
||||
// 3 => '#CD7F32', // Bronze
|
||||
// default => '#3B82F6', // Blue
|
||||
// };
|
||||
|
||||
// $rank++;
|
||||
// }
|
||||
|
||||
// return [
|
||||
// 'datasets' => [
|
||||
// [
|
||||
// 'label' => 'Rank (1 = Best)',
|
||||
// 'data' => $ranks,
|
||||
|
||||
// // 🎨 Styling
|
||||
// 'borderColor' => '#3B82F6',
|
||||
// 'backgroundColor' => $colors,
|
||||
// 'pointBackgroundColor' => $colors,
|
||||
// 'pointBorderColor' => '#000',
|
||||
// 'pointRadius' => 7,
|
||||
// 'pointHoverRadius' => 10,
|
||||
// 'borderWidth' => 3,
|
||||
// 'tension' => 0.4,
|
||||
// 'fill' => false,
|
||||
// ],
|
||||
// ],
|
||||
// 'labels' => $labels,
|
||||
// ];
|
||||
// }
|
||||
|
||||
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$rfqId = session('rfq_id');
|
||||
|
||||
if (!$rfqId) {
|
||||
return [
|
||||
'datasets' => [],
|
||||
'labels' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 1: Get bids sorted by freight charge (for ranking)
|
||||
*/
|
||||
$rankedBids = RfqTransporterBid::query()
|
||||
->where('request_quotation_id', $rfqId)
|
||||
->orderByRaw('CAST(total_freight_charge AS DECIMAL(10,2)) ASC')
|
||||
->get();
|
||||
|
||||
$rankMap = [];
|
||||
$rank = 1;
|
||||
|
||||
foreach ($rankedBids as $bid) {
|
||||
$rankMap[$bid->id] = $rank++;
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 2: Get bids in natural order (for wave effect)
|
||||
* You can change orderBy to:
|
||||
* - created_at
|
||||
* - transporter_name
|
||||
*/
|
||||
$chartBids = RfqTransporterBid::query()
|
||||
->where('request_quotation_id', $rfqId)
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
$labels = [];
|
||||
$amounts = [];
|
||||
$colors = [];
|
||||
|
||||
$ranks = [];
|
||||
|
||||
foreach ($chartBids as $bid) {
|
||||
$labels[] = $bid->transporter_name;
|
||||
$amounts[] = (float) $bid->total_freight_charge;
|
||||
|
||||
$rank = $rankMap[$bid->id];
|
||||
$ranks[] = $rank;
|
||||
|
||||
$colors[] = match ($rank) {
|
||||
1 => '#FFD700',
|
||||
2 => '#C0C0C0',
|
||||
3 => '#CD7F32',
|
||||
default => '#2563EB',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Freight Charge',
|
||||
'data' => $amounts,
|
||||
'rankData' => $ranks,
|
||||
|
||||
'borderColor' => '#2563EB',
|
||||
'backgroundColor' => $colors,
|
||||
'pointBackgroundColor' => $colors,
|
||||
'pointBorderColor' => '#000',
|
||||
'pointRadius' => 7,
|
||||
'pointHoverRadius' => 11,
|
||||
'borderWidth' => 3,
|
||||
'tension' => 0.45,
|
||||
'fill' => false,
|
||||
],
|
||||
],
|
||||
'labels' => $labels,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getOptions(): array
|
||||
{
|
||||
return [
|
||||
'plugins' => [
|
||||
'datalabels' => [
|
||||
'anchor' => 'start',
|
||||
'align' => 'start',
|
||||
'offset' => -15,
|
||||
'color' => '#000',
|
||||
'font' => [
|
||||
'weight' => 'bold',
|
||||
],
|
||||
'formatter' => Js::from("function(value) { return Number(value); }"),
|
||||
],
|
||||
],
|
||||
'scales' => [
|
||||
'y' => [
|
||||
'beginAtZero' => true,
|
||||
'ticks' => [
|
||||
'stepSize' => 0.5,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'bar';
|
||||
}
|
||||
}
|
||||
@@ -913,6 +913,8 @@ class PdfController extends Controller
|
||||
$itemCharacteristicId = $request->query('item_characteristic_id');
|
||||
$stickerId = trim($stickerId);
|
||||
|
||||
//dd($plantId,$itemCharacteristicId,$stickerId);
|
||||
|
||||
// Normalize "empty" or "" to null
|
||||
$plantId = ($plantId && $plantId != 'empty') ? $plantId : null;
|
||||
$itemCharacteristicId = ($itemCharacteristicId && $itemCharacteristicId != 'empty') ? $itemCharacteristicId : null;
|
||||
@@ -956,4 +958,91 @@ class PdfController extends Controller
|
||||
return $pdfService->generate($stickerId, $elements->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public function generatePdf(Request $request, $stickerId)
|
||||
// {
|
||||
// $plantId = $request->query('plant_id');
|
||||
// $itemCharacteristicId = $request->query('item_characteristic_id');
|
||||
// $stickerId = trim($stickerId);
|
||||
|
||||
// //dd($plantId,$itemCharacteristicId,$stickerId);
|
||||
|
||||
// // Normalize "empty" or "" to null
|
||||
// $plantId = ($plantId && $plantId != 'empty') ? $plantId : null;
|
||||
// $itemCharacteristicId = ($itemCharacteristicId && $itemCharacteristicId != 'empty') ? $itemCharacteristicId : null;
|
||||
|
||||
|
||||
// if ($plantId && $itemCharacteristicId)
|
||||
// {
|
||||
// $plantId = $request->query('plant_id');
|
||||
// $itemCharacteristicId = $request->query('item_characteristic_id');
|
||||
|
||||
|
||||
// $sticId = StickerStructureDetail::where('id', $stickerId)->first();
|
||||
|
||||
// $sId = $sticId->id;
|
||||
|
||||
// $stickerId = $sticId->sticker_id;
|
||||
|
||||
// $dynamicElements = StickerDetail::where('sticker_structure_detail_id', $sId)
|
||||
// ->where('element_type', 'Dynamic')
|
||||
// ->get();
|
||||
|
||||
// $itemCharacteristic = null;
|
||||
|
||||
// if ($plantId && $itemCharacteristicId) {
|
||||
// $itemCharacteristic = ItemCharacteristic::where('plant_id', $plantId)
|
||||
// ->where('id', $itemCharacteristicId)
|
||||
// ->first();
|
||||
// }
|
||||
|
||||
// $pdfService = new StickerPdfService();
|
||||
|
||||
// return $pdfService->generatePdf1(
|
||||
// $stickerId,
|
||||
// $dynamicElements,
|
||||
// $itemCharacteristic
|
||||
// );
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// $elements = StickerStructureDetail::where('sticker_id', $stickerId)
|
||||
// ->first();
|
||||
|
||||
// $pdfService = new StickerPdfService();
|
||||
// return $pdfService->generate($stickerId, $elements->toArray());
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
public function generatePdf(Request $request, $stickerId)
|
||||
{
|
||||
$plantId = $request->query('plant_id');
|
||||
$itemCharacteristicId = $request->query('item_characteristic_id');
|
||||
$serialNumber = $request->query('serial_number');
|
||||
|
||||
if ($plantId && $itemCharacteristicId) {
|
||||
$structure = StickerStructureDetail::findOrFail($stickerId);
|
||||
|
||||
$dynamicElements = StickerDetail::where(
|
||||
'sticker_structure_detail_id',
|
||||
$structure->id
|
||||
)->where('element_type', 'Dynamic')->get();
|
||||
|
||||
$itemCharacteristic = ItemCharacteristic::where('plant_id', $plantId)
|
||||
->where('id', $itemCharacteristicId)
|
||||
->first();
|
||||
|
||||
return (new StickerPdfService())->generatePdf1(
|
||||
$structure->sticker_id,
|
||||
$dynamicElements,
|
||||
$itemCharacteristic,
|
||||
$serialNumber
|
||||
);
|
||||
}
|
||||
|
||||
return abort(404);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
482
app/Livewire/ChatBot.php
Normal file
482
app/Livewire/ChatBot.php
Normal file
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Services\ChatbotService;
|
||||
use App\Services\GeminiChatbotService;
|
||||
use Livewire\Component;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ChatBot extends Component
|
||||
{
|
||||
// ── Panel state ───────────────────────────────────────────────────────────
|
||||
public bool $isOpen = false;
|
||||
|
||||
/**
|
||||
* 'select' → mode-picker screen shown first
|
||||
* 'basic' → structured query UI (production or invoice report)
|
||||
* 'advanced' → free-text natural-language UI (Gemini-powered)
|
||||
*/
|
||||
public string $mode = 'select';
|
||||
|
||||
// ── Basic mode — shared ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Which report the user picked inside basic mode.
|
||||
* '' → report-type picker shown
|
||||
* 'production' → production form
|
||||
* 'invoice' → invoice type-lookup form
|
||||
* 'invoice_status' → invoice scan-status form
|
||||
*/
|
||||
public string $reportType = '';
|
||||
|
||||
public array $plants = [];
|
||||
|
||||
// ── Basic mode — Production report ───────────────────────────────────────
|
||||
public string $result = '';
|
||||
public bool $hasResult = false;
|
||||
|
||||
public ?int $selectedPlantId = null;
|
||||
public ?int $selectedLineId = null;
|
||||
public string $dateFrom = '';
|
||||
public string $dateTo = '';
|
||||
public array $lines = [];
|
||||
|
||||
// ── Basic mode — Invoice report (type lookup) ─────────────────────────────
|
||||
public ?int $invoicePlantId = null;
|
||||
public string $invoiceItemCode = '';
|
||||
public string $invoiceResult = '';
|
||||
public bool $hasInvoiceResult = false;
|
||||
|
||||
// ── Basic mode — Invoice status (scan status) ─────────────────────────────
|
||||
public string $invoiceNumber = '';
|
||||
public string $invoiceStatusResult = ''; // kept for simple error strings
|
||||
public bool $hasInvoiceStatusResult = false;
|
||||
|
||||
/**
|
||||
* Structured result from ChatbotService::getInvoiceData().
|
||||
* Shape: type, message, invoice_number, total, scanned, not_scanned, unscanned_serials[]
|
||||
*/
|
||||
public array $invoiceStatusData = [];
|
||||
|
||||
/** Controls whether all unscanned serials are shown (vs the first 10). */
|
||||
public bool $showAllUnscanned = false;
|
||||
|
||||
// ── Advanced mode ─────────────────────────────────────────────────────────
|
||||
public string $advancedQuestion = '';
|
||||
public string $advancedResult = '';
|
||||
public bool $hasAdvancedResult = false;
|
||||
public bool $isAdvancedLoading = false;
|
||||
|
||||
/**
|
||||
* Conversation history shown in advanced mode.
|
||||
* Each entry: ['role' => 'user'|'assistant', 'content' => '…']
|
||||
*
|
||||
* The full history is passed to GeminiChatbotService on every turn so
|
||||
* Gemini can resolve follow-up messages (e.g. answering a clarification
|
||||
* question) in context.
|
||||
*/
|
||||
public array $chatHistory = [];
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->plants = DB::table('plants')
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('name')
|
||||
->get(['id', 'name'])
|
||||
->toArray();
|
||||
|
||||
$this->dateFrom = now()->startOfMonth()->format('Y-m-d');
|
||||
$this->dateTo = now()->format('Y-m-d');
|
||||
}
|
||||
|
||||
// ── Mode switching ────────────────────────────────────────────────────────
|
||||
|
||||
public function setMode(string $mode): void
|
||||
{
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
public function setReportType(string $type): void
|
||||
{
|
||||
$this->reportType = $type;
|
||||
|
||||
// Clear previous results when switching report type
|
||||
$this->result = '';
|
||||
$this->hasResult = false;
|
||||
$this->invoiceResult = '';
|
||||
$this->hasInvoiceResult = false;
|
||||
$this->invoiceStatusResult = '';
|
||||
$this->hasInvoiceStatusResult = false;
|
||||
$this->invoiceStatusData = [];
|
||||
$this->showAllUnscanned = false;
|
||||
}
|
||||
|
||||
// ── Basic mode — Production helpers ──────────────────────────────────────
|
||||
|
||||
public function updatedSelectedPlantId(): void
|
||||
{
|
||||
$this->selectedLineId = null;
|
||||
$this->lines = [];
|
||||
$this->result = '';
|
||||
$this->hasResult = false;
|
||||
|
||||
if ($this->selectedPlantId) {
|
||||
$this->lines = DB::table('lines')
|
||||
->whereNull('deleted_at')
|
||||
->where('plant_id', $this->selectedPlantId)
|
||||
->orderBy('name')
|
||||
->get(['id', 'name'])
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedSelectedLineId(): void
|
||||
{
|
||||
$this->result = '';
|
||||
$this->hasResult = false;
|
||||
}
|
||||
|
||||
public function fetchProduction(): void
|
||||
{
|
||||
if (! $this->selectedPlantId) {
|
||||
$this->result = 'Please select a plant.';
|
||||
$this->hasResult = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$query = DB::table('production_quantities')
|
||||
->whereNull('deleted_at')
|
||||
->where('plant_id', $this->selectedPlantId)
|
||||
->whereDate('created_at', '>=', $this->dateFrom)
|
||||
->whereDate('created_at', '<=', $this->dateTo);
|
||||
|
||||
if ($this->selectedLineId) {
|
||||
$query->where('line_id', $this->selectedLineId);
|
||||
}
|
||||
|
||||
$count = $query->count();
|
||||
|
||||
$plantName = collect($this->plants)
|
||||
->firstWhere('id', $this->selectedPlantId)?->name ?? 'Unknown Plant';
|
||||
|
||||
$lineName = $this->selectedLineId
|
||||
? (collect($this->lines)->firstWhere('id', $this->selectedLineId)?->name ?? 'Unknown Line')
|
||||
: 'All Lines';
|
||||
|
||||
$from = \Carbon\Carbon::parse($this->dateFrom)->format('d M Y');
|
||||
$to = \Carbon\Carbon::parse($this->dateTo)->format('d M Y');
|
||||
|
||||
$this->result = "Production count for {$plantName} / {$lineName} from {$from} to {$to}: {$count} records.";
|
||||
$this->hasResult = true;
|
||||
}
|
||||
|
||||
// ── Basic mode — Invoice report (type lookup) ─────────────────────────────
|
||||
|
||||
public function updatedInvoicePlantId(): void
|
||||
{
|
||||
$this->invoiceResult = '';
|
||||
$this->hasInvoiceResult = false;
|
||||
}
|
||||
|
||||
public function updatedInvoiceItemCode(): void
|
||||
{
|
||||
$this->invoiceResult = '';
|
||||
$this->hasInvoiceResult = false;
|
||||
}
|
||||
|
||||
public function fetchInvoiceReport(): void
|
||||
{
|
||||
if (! $this->invoicePlantId) {
|
||||
$this->invoiceResult = 'Please select a plant.';
|
||||
$this->hasInvoiceResult = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$itemCode = trim($this->invoiceItemCode);
|
||||
|
||||
if ($itemCode === '') {
|
||||
$this->invoiceResult = 'Please enter an item code.';
|
||||
$this->hasInvoiceResult = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$plantName = collect($this->plants)
|
||||
->firstWhere('id', $this->invoicePlantId)?->name ?? 'Unknown Plant';
|
||||
|
||||
try {
|
||||
$rows = DB::select("
|
||||
WITH plant_item AS (
|
||||
SELECT ? AS user_plant,
|
||||
? AS user_item_code
|
||||
),
|
||||
t1 AS (
|
||||
SELECT
|
||||
plants.id AS plant_id,
|
||||
plants.name AS plant_name,
|
||||
ARRAY_AGG(items.code) AS item_codes
|
||||
FROM plants
|
||||
LEFT JOIN items ON plants.id = items.plant_id
|
||||
GROUP BY plants.id, plants.name
|
||||
),
|
||||
t2 AS (
|
||||
SELECT
|
||||
t1.plant_id,
|
||||
t1.plant_name,
|
||||
CASE
|
||||
WHEN plant_item.user_item_code = ANY(t1.item_codes) THEN 1
|
||||
ELSE 0
|
||||
END AS exists_flag
|
||||
FROM t1
|
||||
CROSS JOIN plant_item
|
||||
WHERE t1.plant_name = plant_item.user_plant
|
||||
),
|
||||
t3 AS (
|
||||
SELECT t2.plant_id, t2.plant_name, t2.exists_flag,
|
||||
plant_item.user_item_code
|
||||
FROM t2
|
||||
LEFT JOIN plant_item ON plant_item.user_plant = t2.plant_name
|
||||
),
|
||||
t4 AS (
|
||||
SELECT items.id AS item_id,
|
||||
t3.plant_id, t3.plant_name, t3.exists_flag, t3.user_item_code
|
||||
FROM t3
|
||||
LEFT JOIN items
|
||||
ON t3.plant_id = items.plant_id
|
||||
AND t3.user_item_code = items.code
|
||||
)
|
||||
SELECT
|
||||
t4.item_id,
|
||||
t4.plant_id,
|
||||
t4.plant_name,
|
||||
t4.exists_flag,
|
||||
t4.user_item_code,
|
||||
COALESCE(sticker_masters.material_type, 0) AS material_type,
|
||||
CASE
|
||||
WHEN sticker_masters.item_id IS NULL
|
||||
THEN 'no match found'
|
||||
WHEN COALESCE(sticker_masters.material_type, 0) = 0
|
||||
THEN 'serial invoice'
|
||||
ELSE 'material invoice'
|
||||
END AS invoice_description
|
||||
FROM t4
|
||||
LEFT JOIN sticker_masters
|
||||
ON sticker_masters.plant_id = t4.plant_id
|
||||
AND sticker_masters.item_id = t4.item_id
|
||||
", [$plantName, $itemCode]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('ChatBot: invoice report query failed', [
|
||||
'plant' => $plantName,
|
||||
'item_code' => $itemCode,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
$this->invoiceResult = "Sorry, I couldn't fetch data. Please try again or contact support.";
|
||||
$this->hasInvoiceResult = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($rows)) {
|
||||
$this->invoiceResult = "No data found for plant \"{$plantName}\". Please verify the plant selection.";
|
||||
$this->hasInvoiceResult = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$row = $rows[0];
|
||||
|
||||
if ((int) $row->exists_flag === 0) {
|
||||
$this->invoiceResult = 'Provided item code does not exist in the item table.';
|
||||
} else {
|
||||
switch ($row->invoice_description) {
|
||||
case 'no match found':
|
||||
$this->invoiceResult = "Item not found in sticker master for the plant {$row->plant_name}.";
|
||||
break;
|
||||
case 'serial invoice':
|
||||
$this->invoiceResult = 'It is a serial invoice item.';
|
||||
break;
|
||||
case 'material invoice':
|
||||
$this->invoiceResult = 'It is a material invoice item.';
|
||||
break;
|
||||
default:
|
||||
$this->invoiceResult = 'Unexpected result. Please contact support.';
|
||||
}
|
||||
}
|
||||
|
||||
$this->hasInvoiceResult = true;
|
||||
}
|
||||
|
||||
// ── Basic mode — Invoice status (scan status) ─────────────────────────────
|
||||
|
||||
public function updatedInvoiceNumber(): void
|
||||
{
|
||||
$this->invoiceStatusResult = '';
|
||||
$this->hasInvoiceStatusResult = false;
|
||||
$this->invoiceStatusData = [];
|
||||
$this->showAllUnscanned = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up how many serials within an invoice have been scanned / not scanned.
|
||||
* Stores structured data in $invoiceStatusData so the blade can render
|
||||
* a "show more" serial-number list without dumping 70+ serials in one blob.
|
||||
*/
|
||||
public function fetchInvoiceStatus(): void
|
||||
{
|
||||
$invoiceNumber = trim(preg_replace('/\s+/', '', $this->invoiceNumber));
|
||||
|
||||
if (empty($invoiceNumber)) {
|
||||
$this->invoiceStatusResult = 'Please enter a valid invoice number.';
|
||||
$this->invoiceStatusData = [];
|
||||
$this->showAllUnscanned = false;
|
||||
$this->hasInvoiceStatusResult = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var \App\Services\ChatbotService $service */
|
||||
$service = app(\App\Services\ChatbotService::class);
|
||||
$data = $service->getInvoiceData($invoiceNumber);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('ChatBot: invoice status fetch failed', [
|
||||
'invoice' => $invoiceNumber,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
$data = [
|
||||
'type' => 'error',
|
||||
'message' => "Sorry, I couldn't fetch data for invoice {$invoiceNumber}. "
|
||||
. 'Please try again or contact support.',
|
||||
'invoice_number' => $invoiceNumber,
|
||||
'total' => 0,
|
||||
'scanned' => 0,
|
||||
'not_scanned' => 0,
|
||||
'unscanned_serials' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$this->invoiceStatusData = $data;
|
||||
$this->invoiceStatusResult = $data['message']; // fallback plain-text copy
|
||||
$this->showAllUnscanned = false;
|
||||
$this->hasInvoiceStatusResult = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the "show all / show less" state for unscanned serial numbers
|
||||
* in the Basic → Invoice Status result card.
|
||||
*/
|
||||
public function toggleShowAllUnscanned(): void
|
||||
{
|
||||
$this->showAllUnscanned = ! $this->showAllUnscanned;
|
||||
}
|
||||
|
||||
// ── Advanced mode (Gemini-powered) ────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Handles a free-text user message in advanced mode.
|
||||
*
|
||||
* Steps:
|
||||
* 1. Appends the user message to chatHistory immediately (UI feedback).
|
||||
* 2. Calls GeminiChatbotService with the full prior history for context.
|
||||
* 3. Gemini classifies the intent, extracts params, and either:
|
||||
* a) runs the appropriate DB query and returns the result, or
|
||||
* b) returns a clarification question if intent is ambiguous.
|
||||
* 4. Appends the assistant reply to chatHistory.
|
||||
*/
|
||||
public function askAdvanced(): void
|
||||
{
|
||||
$question = trim($this->advancedQuestion);
|
||||
|
||||
if (empty($question)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the user's message in the chat bubble immediately
|
||||
$this->chatHistory[] = [
|
||||
'role' => 'user',
|
||||
'content' => $question,
|
||||
];
|
||||
|
||||
$this->advancedQuestion = '';
|
||||
$this->isAdvancedLoading = true;
|
||||
|
||||
try {
|
||||
/** @var GeminiChatbotService $gemini */
|
||||
$gemini = app(GeminiChatbotService::class);
|
||||
|
||||
// Pass history *without* the turn we just appended — the new user
|
||||
// message is passed separately so Gemini sees it as the latest turn.
|
||||
$priorHistory = array_slice($this->chatHistory, 0, -1);
|
||||
$answer = $gemini->processMessage($priorHistory, $question);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('ChatBot: advanced ask failed', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
$answer = 'Sorry, something went wrong. Please try again.';
|
||||
}
|
||||
|
||||
$this->chatHistory[] = [
|
||||
'role' => 'assistant',
|
||||
'content' => $answer,
|
||||
];
|
||||
|
||||
$this->isAdvancedLoading = false;
|
||||
}
|
||||
|
||||
public function clearAdvancedChat(): void
|
||||
{
|
||||
$this->chatHistory = [];
|
||||
$this->advancedQuestion = '';
|
||||
$this->isAdvancedLoading = false;
|
||||
}
|
||||
|
||||
// ── Panel controls ────────────────────────────────────────────────────────
|
||||
|
||||
public function toggleChat(): void
|
||||
{
|
||||
$this->isOpen = ! $this->isOpen;
|
||||
}
|
||||
|
||||
public function resetForm(): void
|
||||
{
|
||||
// Basic mode — shared
|
||||
$this->reportType = '';
|
||||
|
||||
// Basic mode — production
|
||||
$this->selectedPlantId = null;
|
||||
$this->selectedLineId = null;
|
||||
$this->lines = [];
|
||||
$this->result = '';
|
||||
$this->hasResult = false;
|
||||
$this->dateFrom = now()->startOfMonth()->format('Y-m-d');
|
||||
$this->dateTo = now()->format('Y-m-d');
|
||||
|
||||
// Basic mode — invoice type lookup
|
||||
$this->invoicePlantId = null;
|
||||
$this->invoiceItemCode = '';
|
||||
$this->invoiceResult = '';
|
||||
$this->hasInvoiceResult = false;
|
||||
|
||||
// Basic mode — invoice scan status
|
||||
$this->invoiceNumber = '';
|
||||
$this->invoiceStatusResult = '';
|
||||
$this->hasInvoiceStatusResult = false;
|
||||
$this->invoiceStatusData = [];
|
||||
$this->showAllUnscanned = false;
|
||||
|
||||
// Advanced mode
|
||||
$this->clearAdvancedChat();
|
||||
|
||||
// Go back to mode selector
|
||||
$this->mode = 'select';
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.chat-bot');
|
||||
}
|
||||
}
|
||||
236
app/Livewire/ProductionTargetPlan.php
Normal file
236
app/Livewire/ProductionTargetPlan.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Exports\ProductionPlanExport;
|
||||
use App\Models\ProductionPlan;
|
||||
use App\Models\ProductionQuantity;
|
||||
use Livewire\Component;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
|
||||
class ProductionTargetPlan extends Component
|
||||
{
|
||||
|
||||
public $plantId, $lineId, $month, $year;
|
||||
|
||||
public $records = [];
|
||||
|
||||
public $dates = [];
|
||||
|
||||
public $leaveDates = [];
|
||||
|
||||
public $productionPlanDates = '';
|
||||
|
||||
|
||||
protected $listeners = [
|
||||
'loadData' => 'loadProductionData',
|
||||
'loadData1' => 'exportProductionData',
|
||||
];
|
||||
|
||||
public function getMonthDates($month, $year)
|
||||
{
|
||||
$start = Carbon::createFromDate($year, $month, 1);
|
||||
$days = $start->daysInMonth;
|
||||
|
||||
$dates = [];
|
||||
|
||||
for ($i = 1; $i <= $days; $i++) {
|
||||
$dates[] = Carbon::createFromDate($year, $month, $i)
|
||||
->format('Y-m-d');
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
|
||||
public function loadProductionData($plantId, $lineId, $month, $year){
|
||||
|
||||
if (!$plantId || !$lineId || !$month || !$year) {
|
||||
$this->records = [];
|
||||
$this->dates = [];
|
||||
$this->leaveDates = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dates = $this->getMonthDates($month, $year);
|
||||
|
||||
$data = ProductionPlan::query()
|
||||
->join('items', 'items.id', '=', 'production_plans.item_id')
|
||||
->join('lines', 'lines.id', '=', 'production_plans.line_id')
|
||||
->join('plants', 'plants.id', '=', 'production_plans.plant_id')
|
||||
->where('production_plans.plant_id', $plantId)
|
||||
->where('production_plans.line_id', $lineId)
|
||||
->whereMonth('production_plans.created_at', $month)
|
||||
->whereYear('production_plans.created_at', $year)
|
||||
->select(
|
||||
'production_plans.created_at',
|
||||
'production_plans.operator_id',
|
||||
'plants.name as plant',
|
||||
'items.code as item_code',
|
||||
'items.description as item_description',
|
||||
'lines.name as line_name',
|
||||
'production_plans.leave_dates'
|
||||
)
|
||||
->first();
|
||||
|
||||
if ($data && $data->leave_dates) {
|
||||
$this->leaveDates = array_map('trim', explode(',', $data->leave_dates));
|
||||
}
|
||||
|
||||
$producedData = ProductionQuantity::selectRaw("
|
||||
plant_id,
|
||||
line_id,
|
||||
item_id,
|
||||
DATE(created_at) as prod_date,
|
||||
COUNT(*) as total_qty
|
||||
")
|
||||
->where('plant_id', $plantId)
|
||||
->where('line_id', $lineId)
|
||||
->whereMonth('created_at', $month)
|
||||
->whereYear('created_at', $year)
|
||||
->groupBy('plant_id', 'line_id', 'item_id', DB::raw('DATE(created_at)'))
|
||||
->get()
|
||||
->groupBy(function ($row) {
|
||||
return $row->plant_id . '_' . $row->line_id . '_' . $row->item_id;
|
||||
})
|
||||
->map(function ($group) {
|
||||
return $group->keyBy('prod_date');
|
||||
});
|
||||
|
||||
$this->records = ProductionPlan::query()
|
||||
->join('items', 'items.id', '=', 'production_plans.item_id')
|
||||
->join('lines', 'lines.id', '=', 'production_plans.line_id')
|
||||
->join('plants', 'plants.id', '=', 'production_plans.plant_id')
|
||||
->where('production_plans.plant_id', $plantId)
|
||||
->where('production_plans.line_id', $lineId)
|
||||
->whereMonth('production_plans.created_at', $month)
|
||||
->whereYear('production_plans.created_at', $year)
|
||||
->select(
|
||||
'production_plans.item_id',
|
||||
'production_plans.plant_id',
|
||||
'production_plans.line_id',
|
||||
'production_plans.plan_quantity',
|
||||
'production_plans.working_days',
|
||||
'items.code as item_code',
|
||||
'items.description as item_description',
|
||||
'lines.name as line_name',
|
||||
'plants.name as plant_name'
|
||||
)
|
||||
->get()
|
||||
// ->map(function ($row) use ($producedData) {
|
||||
// $row = $row->toArray();
|
||||
|
||||
// $row['daily_target'] = ($row['working_days'] > 0)
|
||||
// ? round($row['plan_quantity'] / $row['working_days'], 2)
|
||||
// : 0;
|
||||
|
||||
// // $key = $row['plant_id'].'_'.$row['line_id'].'_'.$row['item_id'];
|
||||
|
||||
// // foreach ($this->dates as $date) {
|
||||
// // $found = $producedData[$key][$date] ?? null;
|
||||
// // $row['produced_quantity'][$date] = $found->total_qty ?? 0;
|
||||
// // }
|
||||
|
||||
// $remainingDays = $row['working_days'];
|
||||
// $pendingQty = 0;
|
||||
|
||||
// $row['daily_target_dynamic'] = [];
|
||||
// $row['produced_quantity'] = [];
|
||||
|
||||
// $key = $row['plant_id'].'_'.$row['line_id'].'_'.$row['item_id'];
|
||||
|
||||
// foreach ($this->dates as $date) {
|
||||
|
||||
// $found = $producedData[$key][$date] ?? null;
|
||||
// $producedQty = $found->total_qty ?? 0;
|
||||
|
||||
// // today's adjusted target
|
||||
// $todayTarget = $baseDailyTarget;
|
||||
|
||||
// if ($remainingDays > 1 && $pendingQty > 0) {
|
||||
// $todayTarget += $pendingQty / $remainingDays;
|
||||
// }
|
||||
|
||||
// $row['daily_target_dynamic'][$date] = round($todayTarget, 2);
|
||||
// $row['produced_quantity'][$date] = $producedQty;
|
||||
|
||||
// // calculate today's shortfall
|
||||
// $pendingQty += ($todayTarget - $producedQty);
|
||||
|
||||
// if ($pendingQty < 0) {
|
||||
// $pendingQty = 0;
|
||||
// }
|
||||
|
||||
// $remainingDays--;
|
||||
// }
|
||||
|
||||
// return $row;
|
||||
// })
|
||||
->map(function ($row) use ($producedData) {
|
||||
|
||||
$row = $row->toArray();
|
||||
|
||||
$remainingQty = $row['plan_quantity'];
|
||||
$remainingDays = $row['working_days'];
|
||||
|
||||
$row['daily_target_dynamic'] = [];
|
||||
$row['produced_quantity'] = [];
|
||||
|
||||
$key = $row['plant_id'].'_'.$row['line_id'].'_'.$row['item_id'];
|
||||
|
||||
foreach ($this->dates as $date) {
|
||||
|
||||
// Skip leave dates
|
||||
if (in_array($date, $this->leaveDates)) {
|
||||
$row['daily_target_dynamic'][$date] = '-';
|
||||
$row['produced_quantity'][$date] = '-';
|
||||
continue;
|
||||
}
|
||||
|
||||
$todayTarget = $remainingDays > 0
|
||||
? round($remainingQty / $remainingDays, 2)
|
||||
: 0;
|
||||
|
||||
//$todayTarget = $remainingDays > 0
|
||||
// ? $remainingQty / $remainingDays
|
||||
// : 0;
|
||||
|
||||
$producedQty = isset($producedData[$key][$date])
|
||||
? $producedData[$key][$date]->total_qty
|
||||
: 0;
|
||||
|
||||
$row['daily_target_dynamic'][$date] = $todayTarget;
|
||||
$row['produced_quantity'][$date] = $producedQty;
|
||||
|
||||
// Carry forward pending
|
||||
$remainingQty -= $producedQty;
|
||||
if ($remainingQty < 0) {
|
||||
$remainingQty = 0;
|
||||
}
|
||||
|
||||
$remainingDays--;
|
||||
}
|
||||
|
||||
return $row;
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function exportProductionData()
|
||||
{
|
||||
return Excel::download(
|
||||
new ProductionPlanExport($this->records, $this->dates),
|
||||
'production_plan_data.xlsx'
|
||||
);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
// return view('livewire.production-target-plan');
|
||||
return view('livewire.production-target-plan', [
|
||||
'records' => $this->records,
|
||||
'dates' => $this->dates,
|
||||
]);
|
||||
}
|
||||
}
|
||||
65
app/Livewire/StickerValidation.php
Normal file
65
app/Livewire/StickerValidation.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\ProductionQuantity;
|
||||
use Livewire\Component;
|
||||
|
||||
class StickerValidation extends Component
|
||||
{
|
||||
|
||||
public $plantId;
|
||||
|
||||
public $refNumber;
|
||||
|
||||
public $serialNumber;
|
||||
|
||||
public bool $materialInvoice = false;
|
||||
|
||||
public $records = [];
|
||||
|
||||
protected $listeners = [
|
||||
'refreshEmptySticker' => 'loadStickerData',
|
||||
'addStickerToList' => 'loadSticker'
|
||||
];
|
||||
|
||||
public function loadStickerData($plantId, $refNumber)
|
||||
{
|
||||
$this->plantId = $plantId;
|
||||
$this->refNumber = $refNumber;
|
||||
// $this->records = ProductionQuantity::where('plant_id', $plantId)
|
||||
// ->where('production_order', $refNumber)
|
||||
// ->orderBy('created_at', 'asc')
|
||||
// ->get(['serial_number', 'operator_id']);
|
||||
|
||||
$this->records = ProductionQuantity::query()
|
||||
->where('production_quantities.plant_id', $plantId)
|
||||
->where('production_quantities.production_order', $refNumber)
|
||||
->leftJoin(
|
||||
'sticker_validations',
|
||||
'sticker_validations.serial_number',
|
||||
'=',
|
||||
'production_quantities.serial_number'
|
||||
)
|
||||
->orderBy('production_quantities.created_at', 'asc')
|
||||
->get([
|
||||
'production_quantities.serial_number',
|
||||
'production_quantities.operator_id',
|
||||
'sticker_validations.status',
|
||||
'sticker_validations.sticker_id',
|
||||
])
|
||||
->map(function ($row) {
|
||||
$row->status = $row->status ?? '';
|
||||
return $row;
|
||||
});
|
||||
|
||||
//dd($this->records);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.sticker-validation');
|
||||
}
|
||||
}
|
||||
32
app/Livewire/Webcam.php
Normal file
32
app/Livewire/Webcam.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Webcam extends Component
|
||||
{
|
||||
|
||||
public string $capturedPhoto = '';
|
||||
|
||||
// Called from JavaScript when a photo is taken
|
||||
public function setPhoto(string $photo): void
|
||||
{
|
||||
$this->capturedPhoto = $photo;
|
||||
|
||||
// Fires a browser event that the Filament form will listen to
|
||||
$this->dispatch('photo-captured', photo: $photo);
|
||||
}
|
||||
|
||||
// Called from JavaScript when user clicks "Retake"
|
||||
public function clearPhoto(): void
|
||||
{
|
||||
$this->capturedPhoto = '';
|
||||
|
||||
$this->dispatch('photo-captured', photo: '');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.webcam');
|
||||
}
|
||||
}
|
||||
32
app/Models/EmployeeMaster.php
Normal file
32
app/Models/EmployeeMaster.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class EmployeeMaster extends Model
|
||||
{
|
||||
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'plant_id',
|
||||
'name',
|
||||
'code',
|
||||
'department',
|
||||
'designation',
|
||||
'email',
|
||||
'mobile_number',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
public function plant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Plant::class);
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,13 @@ class Item extends Model
|
||||
|
||||
protected $fillable = [
|
||||
'plant_id',
|
||||
'line_id',
|
||||
'category',
|
||||
'code',
|
||||
'description',
|
||||
'hourly_quantity',
|
||||
'uom',
|
||||
'line_capacity',
|
||||
];
|
||||
|
||||
public function plant(): BelongsTo
|
||||
@@ -24,6 +26,11 @@ class Item extends Model
|
||||
return $this->belongsTo(Plant::class);
|
||||
}
|
||||
|
||||
public function line(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Line::class);
|
||||
}
|
||||
|
||||
public function stickerMasters()
|
||||
{
|
||||
return $this->hasMany(StickerMaster::class, 'item_id', 'id');
|
||||
|
||||
@@ -13,6 +13,7 @@ class Line extends Model
|
||||
|
||||
protected $fillable = [
|
||||
"plant_id",
|
||||
"block_id",
|
||||
"name",
|
||||
"type",
|
||||
"group_work_center",
|
||||
@@ -34,6 +35,11 @@ class Line extends Model
|
||||
return $this->belongsTo(Plant::class);
|
||||
}
|
||||
|
||||
public function block(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Block::class);
|
||||
}
|
||||
|
||||
public function testingPanelReadings()
|
||||
{
|
||||
return $this->hasMany(TestingPanelReading::class);
|
||||
|
||||
@@ -12,12 +12,15 @@ class ProductionPlan extends Model
|
||||
|
||||
protected $fillable = [
|
||||
"plant_id",
|
||||
"item_id",
|
||||
"shift_id",
|
||||
"created_at",
|
||||
"line_id",
|
||||
"plan_quantity",
|
||||
"production_quantity",
|
||||
"operator_id",
|
||||
"working_days",
|
||||
"leave_dates",
|
||||
];
|
||||
|
||||
public function plant(): BelongsTo
|
||||
@@ -34,4 +37,9 @@ class ProductionPlan extends Model
|
||||
{
|
||||
return $this->belongsTo(Line::class);
|
||||
}
|
||||
|
||||
public function item(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Item::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ class ProductionQuantity extends Model
|
||||
|
||||
protected $fillable = [
|
||||
"plant_id",
|
||||
"machine_id",
|
||||
"shift_id",
|
||||
"line_id",
|
||||
"item_id",
|
||||
@@ -50,6 +51,11 @@ class ProductionQuantity extends Model
|
||||
return $this->belongsTo(Item::class);
|
||||
}
|
||||
|
||||
public function machine(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Machine::class);
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($productionQuantity) {
|
||||
|
||||
46
app/Models/RequestQuotation.php
Normal file
46
app/Models/RequestQuotation.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class RequestQuotation extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'rfq_number',
|
||||
'rfq_date_time',
|
||||
'pickup_address',
|
||||
'delivery_address',
|
||||
'weight',
|
||||
'volumetrice_size_inch',
|
||||
'type_of_vehicle',
|
||||
'special_type',
|
||||
'no_of_vehicle',
|
||||
'product_name',
|
||||
'loading_by',
|
||||
'unloading_by',
|
||||
'pick_and_delivery',
|
||||
'payment_term',
|
||||
'paid_topay',
|
||||
'require_date_time',
|
||||
'transporter_name',
|
||||
'total_freight_charge',
|
||||
'transit_day',
|
||||
'spot_rate_transport_master_id',
|
||||
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
public function spotRateTransportMaster(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SpotRateTransportMaster::class);
|
||||
}
|
||||
|
||||
}
|
||||
30
app/Models/RfqTransporterBid.php
Normal file
30
app/Models/RfqTransporterBid.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use phpseclib3\Crypt\Common\Formats\Signature\Raw;
|
||||
|
||||
class RfqTransporterBid extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'request_quotation_id',
|
||||
'transporter_name',
|
||||
'total_freight_charge',
|
||||
'transit_day',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
public function requestQuotation(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(RequestQuotation::class);
|
||||
// return $this->belongsTo(RequestQuotation::class, 'request_quotation_id');
|
||||
}
|
||||
}
|
||||
25
app/Models/SpotRateTransportMaster.php
Normal file
25
app/Models/SpotRateTransportMaster.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class SpotRateTransportMaster extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $casts = [
|
||||
'user_name' => 'array',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'group_name',
|
||||
'user_name',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
}
|
||||
@@ -30,8 +30,8 @@ class StickerDetail extends Model
|
||||
'shape_y1_value',
|
||||
'shape_x2_value',
|
||||
'shape_y2_value',
|
||||
'image_path',
|
||||
'image_type',
|
||||
// 'image_path',
|
||||
// 'image_type',
|
||||
'image_x',
|
||||
'image_y',
|
||||
'image_width',
|
||||
|
||||
143
app/Models/StickerMappingMaster.php
Normal file
143
app/Models/StickerMappingMaster.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StickerMappingMaster extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'plant_id',
|
||||
'item_characteristic_id',
|
||||
|
||||
'sticker_structure1_id',
|
||||
'sticker1_machine_id',
|
||||
'sticker1_print_ip',
|
||||
'sticker_structure2_id',
|
||||
'sticker2_machine_id',
|
||||
'sticker2_print_ip',
|
||||
'sticker_structure3_id',
|
||||
'sticker3_machine_id',
|
||||
'sticker3_print_ip',
|
||||
'sticker_structure4_id',
|
||||
'sticker4_machine_id',
|
||||
'sticker4_print_ip',
|
||||
'sticker_structure5_id',
|
||||
'sticker5_machine_id',
|
||||
'sticker5_print_ip',
|
||||
'sticker_structure7_id',
|
||||
'sticker6_machine_id',
|
||||
'sticker6_print_ip',
|
||||
'sticker_structure7_id',
|
||||
'sticker7_machine_id',
|
||||
'sticker7_print_ip',
|
||||
'sticker_structure8_id',
|
||||
'sticker8_machine_id',
|
||||
'sticker8_print_ip',
|
||||
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
public function plant()
|
||||
{
|
||||
return $this->belongsTo(Plant::class);
|
||||
}
|
||||
|
||||
public function itemCharacteristic()
|
||||
{
|
||||
return $this->belongsTo(ItemCharacteristic::class);
|
||||
}
|
||||
|
||||
public function item(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Item::class, 'item_id');
|
||||
}
|
||||
|
||||
public function sticker1Structure()
|
||||
{
|
||||
return $this->belongsTo(StickerStructureDetail::class, 'sticker_structure1_id');
|
||||
}
|
||||
|
||||
public function sticker2Structure()
|
||||
{
|
||||
return $this->belongsTo(StickerStructureDetail::class, 'sticker_structure2_id');
|
||||
}
|
||||
|
||||
public function sticker3Structure()
|
||||
{
|
||||
return $this->belongsTo(StickerStructureDetail::class, 'sticker_structure3_id');
|
||||
}
|
||||
|
||||
public function sticker4Structure()
|
||||
{
|
||||
return $this->belongsTo(StickerStructureDetail::class, 'sticker_structure4_id');
|
||||
}
|
||||
|
||||
public function sticker5Structure()
|
||||
{
|
||||
return $this->belongsTo(StickerStructureDetail::class, 'sticker_structure5_id');
|
||||
}
|
||||
|
||||
public function sticker6Structure()
|
||||
{
|
||||
return $this->belongsTo(StickerStructureDetail::class, 'sticker_structure6_id');
|
||||
}
|
||||
|
||||
public function sticker7Structure()
|
||||
{
|
||||
return $this->belongsTo(StickerStructureDetail::class, 'sticker_structure7_id');
|
||||
}
|
||||
|
||||
public function sticker8Structure()
|
||||
{
|
||||
return $this->belongsTo(StickerStructureDetail::class, 'sticker_structure8_id');
|
||||
}
|
||||
|
||||
// Machine relationships (per sticker)
|
||||
public function sticker1Machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class, 'sticker1_machine_id');
|
||||
}
|
||||
|
||||
public function sticker2Machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class, 'sticker2_machine_id');
|
||||
}
|
||||
|
||||
public function sticker3Machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class, 'sticker3_machine_id');
|
||||
}
|
||||
|
||||
public function sticker4Machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class, 'sticker4_machine_id');
|
||||
}
|
||||
|
||||
public function sticker5Machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class, 'sticker5_machine_id');
|
||||
}
|
||||
|
||||
public function sticker6Machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class, 'sticker6_machine_id');
|
||||
}
|
||||
|
||||
public function sticker7Machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class, 'sticker7_machine_id');
|
||||
}
|
||||
|
||||
public function sticker8Machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class, 'sticker8_machine_id');
|
||||
}
|
||||
}
|
||||
33
app/Models/StickerValidation.php
Normal file
33
app/Models/StickerValidation.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StickerValidation extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'plant_id',
|
||||
'machine_id',
|
||||
'sticker_id',
|
||||
'production_order',
|
||||
'serial_number',
|
||||
'status',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
public function plant()
|
||||
{
|
||||
return $this->belongsTo(Plant::class);
|
||||
}
|
||||
|
||||
public function machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class);
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,13 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use NotificationChannels\WebPush\HasPushSubscriptions;
|
||||
use NotificationChannels\WebPush\PushSubscription;
|
||||
|
||||
class User extends Authenticatable implements FilamentUser
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, HasRoles, Notifiable, SoftDeletes, HasSuperAdmin;
|
||||
use HasFactory, HasRoles, Notifiable, SoftDeletes, HasSuperAdmin, HasPushSubscriptions;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@@ -63,4 +65,9 @@ class User extends Authenticatable implements FilamentUser
|
||||
{
|
||||
return $this->belongsTo(Plant::class);
|
||||
}
|
||||
|
||||
public function pushSubscriptions()
|
||||
{
|
||||
return $this->morphMany(PushSubscription::class, 'subscribable');
|
||||
}
|
||||
}
|
||||
|
||||
30
app/Models/VisitorEntry.php
Normal file
30
app/Models/VisitorEntry.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class VisitorEntry extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'mobile_number',
|
||||
'name',
|
||||
'company',
|
||||
'purpose_of_visit',
|
||||
'type',
|
||||
'in_time',
|
||||
'out_time',
|
||||
'photo',
|
||||
'employee_master_id',
|
||||
'number_of_person'
|
||||
];
|
||||
|
||||
public function employeeMaster(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(EmployeeMaster::class);
|
||||
}
|
||||
}
|
||||
19
app/Models/WebPushSubscription.php
Normal file
19
app/Models/WebPushSubscription.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class WebPushSubscription extends Model
|
||||
{
|
||||
protected $table = 'push_subscriptions';
|
||||
|
||||
protected $fillable = [
|
||||
'subscribable_type',
|
||||
'subscribable_id',
|
||||
'endpoint',
|
||||
'public_key',
|
||||
'auth_token',
|
||||
'content_encoding',
|
||||
];
|
||||
}
|
||||
108
app/Notifications/PushAlertNotification.php
Normal file
108
app/Notifications/PushAlertNotification.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use NotificationChannels\WebPush\WebPushMessage;
|
||||
use NotificationChannels\WebPush\WebPushChannel;
|
||||
|
||||
|
||||
class PushAlertNotification extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
// public function __construct()
|
||||
// {
|
||||
// //
|
||||
// }
|
||||
|
||||
public $title;
|
||||
public $body;
|
||||
|
||||
public function __construct($title, $body)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->body = $body;
|
||||
}
|
||||
|
||||
// public function via($notifiable)
|
||||
// {
|
||||
// return [WebPushChannel::class];
|
||||
// }
|
||||
|
||||
public function via($notifiable)
|
||||
{
|
||||
return [
|
||||
'database', // ✅ Filament toast
|
||||
WebPushChannel::class // ✅ Browser / PWA push
|
||||
];
|
||||
}
|
||||
|
||||
public function toDatabase($notifiable): array
|
||||
{
|
||||
return [
|
||||
'title' => $this->title,
|
||||
'body' => $this->body,
|
||||
];
|
||||
}
|
||||
|
||||
// public function toWebPush($notifiable, $notification)
|
||||
// {
|
||||
// return (new WebPushMessage)
|
||||
// ->title('New Alert 🚨')
|
||||
// ->icon('/pwa-192x192.png')
|
||||
// ->body('You have a new notification')
|
||||
// ->action('Open App', 'open_app')
|
||||
// ->data(['url' => '/admin']);
|
||||
// }
|
||||
|
||||
public function toWebPush($notifiable, $notification)
|
||||
{
|
||||
|
||||
return (new WebPushMessage)
|
||||
->title($this->title)
|
||||
->icon('/pwa-192x192.png')
|
||||
->body($this->body)
|
||||
->action('Open App', 'open_app')
|
||||
->data(['url' => '/admin']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
// public function via(object $notifiable): array
|
||||
// {
|
||||
// return ['mail'];
|
||||
// }
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*/
|
||||
// public function toMail(object $notifiable): MailMessage
|
||||
// {
|
||||
// return (new MailMessage)
|
||||
// ->line('The introduction to the notification.')
|
||||
// ->action('Notification Action', url('/'))
|
||||
// ->line('Thank you for using our application!');
|
||||
// }
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
106
app/Policies/RequestQuotationPolicy.php
Normal file
106
app/Policies/RequestQuotationPolicy.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use App\Models\RequestQuotation;
|
||||
use App\Models\User;
|
||||
|
||||
class RequestQuotationPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('view-any RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, RequestQuotation $requestquotation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('view RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('create RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, RequestQuotation $requestquotation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('update RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, RequestQuotation $requestquotation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('delete RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete any models.
|
||||
*/
|
||||
public function deleteAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('delete-any RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, RequestQuotation $requestquotation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('restore RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore any models.
|
||||
*/
|
||||
public function restoreAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('restore-any RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can replicate the model.
|
||||
*/
|
||||
public function replicate(User $user, RequestQuotation $requestquotation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('replicate RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can reorder the models.
|
||||
*/
|
||||
public function reorder(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('reorder RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, RequestQuotation $requestquotation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('force-delete RequestQuotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete any models.
|
||||
*/
|
||||
public function forceDeleteAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('force-delete-any RequestQuotation');
|
||||
}
|
||||
}
|
||||
106
app/Policies/RfqTransporterBidPolicy.php
Normal file
106
app/Policies/RfqTransporterBidPolicy.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use App\Models\RfqTransporterBid;
|
||||
use App\Models\User;
|
||||
|
||||
class RfqTransporterBidPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('view-any RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, RfqTransporterBid $rfqtransporterbid): bool
|
||||
{
|
||||
return $user->checkPermissionTo('view RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('create RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, RfqTransporterBid $rfqtransporterbid): bool
|
||||
{
|
||||
return $user->checkPermissionTo('update RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, RfqTransporterBid $rfqtransporterbid): bool
|
||||
{
|
||||
return $user->checkPermissionTo('delete RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete any models.
|
||||
*/
|
||||
public function deleteAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('delete-any RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, RfqTransporterBid $rfqtransporterbid): bool
|
||||
{
|
||||
return $user->checkPermissionTo('restore RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore any models.
|
||||
*/
|
||||
public function restoreAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('restore-any RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can replicate the model.
|
||||
*/
|
||||
public function replicate(User $user, RfqTransporterBid $rfqtransporterbid): bool
|
||||
{
|
||||
return $user->checkPermissionTo('replicate RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can reorder the models.
|
||||
*/
|
||||
public function reorder(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('reorder RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, RfqTransporterBid $rfqtransporterbid): bool
|
||||
{
|
||||
return $user->checkPermissionTo('force-delete RfqTransporterBid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete any models.
|
||||
*/
|
||||
public function forceDeleteAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('force-delete-any RfqTransporterBid');
|
||||
}
|
||||
}
|
||||
106
app/Policies/SpotRateTransportMasterPolicy.php
Normal file
106
app/Policies/SpotRateTransportMasterPolicy.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use App\Models\SpotRateTransportMaster;
|
||||
use App\Models\User;
|
||||
|
||||
class SpotRateTransportMasterPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('view-any SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, SpotRateTransportMaster $spotratetransportmaster): bool
|
||||
{
|
||||
return $user->checkPermissionTo('view SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('create SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, SpotRateTransportMaster $spotratetransportmaster): bool
|
||||
{
|
||||
return $user->checkPermissionTo('update SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, SpotRateTransportMaster $spotratetransportmaster): bool
|
||||
{
|
||||
return $user->checkPermissionTo('delete SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete any models.
|
||||
*/
|
||||
public function deleteAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('delete-any SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, SpotRateTransportMaster $spotratetransportmaster): bool
|
||||
{
|
||||
return $user->checkPermissionTo('restore SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore any models.
|
||||
*/
|
||||
public function restoreAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('restore-any SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can replicate the model.
|
||||
*/
|
||||
public function replicate(User $user, SpotRateTransportMaster $spotratetransportmaster): bool
|
||||
{
|
||||
return $user->checkPermissionTo('replicate SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can reorder the models.
|
||||
*/
|
||||
public function reorder(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('reorder SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, SpotRateTransportMaster $spotratetransportmaster): bool
|
||||
{
|
||||
return $user->checkPermissionTo('force-delete SpotRateTransportMaster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete any models.
|
||||
*/
|
||||
public function forceDeleteAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('force-delete-any SpotRateTransportMaster');
|
||||
}
|
||||
}
|
||||
106
app/Policies/StickerValidationPolicy.php
Normal file
106
app/Policies/StickerValidationPolicy.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use App\Models\StickerValidation;
|
||||
use App\Models\User;
|
||||
|
||||
class StickerValidationPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('view-any StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, StickerValidation $stickervalidation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('view StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('create StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, StickerValidation $stickervalidation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('update StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, StickerValidation $stickervalidation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('delete StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete any models.
|
||||
*/
|
||||
public function deleteAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('delete-any StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, StickerValidation $stickervalidation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('restore StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore any models.
|
||||
*/
|
||||
public function restoreAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('restore-any StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can replicate the model.
|
||||
*/
|
||||
public function replicate(User $user, StickerValidation $stickervalidation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('replicate StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can reorder the models.
|
||||
*/
|
||||
public function reorder(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('reorder StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, StickerValidation $stickervalidation): bool
|
||||
{
|
||||
return $user->checkPermissionTo('force-delete StickerValidation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete any models.
|
||||
*/
|
||||
public function forceDeleteAny(User $user): bool
|
||||
{
|
||||
return $user->checkPermissionTo('force-delete-any StickerValidation');
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Providers\Filament;
|
||||
|
||||
use Althinect\FilamentSpatieRolesPermissions\FilamentSpatieRolesPermissionsPlugin;
|
||||
use App\Filament\Pages\InvoiceDashboard;
|
||||
use App\Filament\Pages\NotificationSettings;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Http\Middleware\Authenticate;
|
||||
use Filament\Http\Middleware\AuthenticateSession;
|
||||
@@ -28,6 +29,7 @@ use App\Filament\Auth\CustomLogin as AuthCustomLogin;
|
||||
use App\Filament\Pages\CustomLogin;
|
||||
use Filament\View\PanelsRenderHook;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
|
||||
|
||||
class AdminPanelProvider extends PanelProvider
|
||||
@@ -59,6 +61,7 @@ class AdminPanelProvider extends PanelProvider
|
||||
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
||||
->pages([
|
||||
|
||||
])
|
||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
||||
// ->widgets([
|
||||
@@ -123,6 +126,15 @@ class AdminPanelProvider extends PanelProvider
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
|
||||
FilamentView::registerRenderHook('panels::body.end', function () {
|
||||
if (url()->current() == config('app.url') . '/admin') {
|
||||
return '<script src="' . asset('js/push.js') . '"></script>';
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
FilamentView::registerRenderHook('panels::head.end', function () {
|
||||
// Only inject on the "home" page (or specific route)
|
||||
if (url()->current() == config('app.url') . '/admin') {
|
||||
@@ -149,5 +161,10 @@ class AdminPanelProvider extends PanelProvider
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
FilamentView::registerRenderHook(
|
||||
PanelsRenderHook::BODY_END,
|
||||
fn (): string => Blade::render("@livewire('chat-bot')"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
403
app/Services/ChatbotService.php
Normal file
403
app/Services/ChatbotService.php
Normal file
@@ -0,0 +1,403 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* ChatbotService (Advanced Mode)
|
||||
* ─────────────────────────────────────────────────────────────────────────────
|
||||
* Parses STRUCTURED user inputs via regex — no LLM involved.
|
||||
*
|
||||
* HOW TO ADD A NEW COMMAND:
|
||||
* 1. Add an entry to $handlers with a regex pattern and a handler method name.
|
||||
* 2. Write the private handler method (receives the captured group as $value).
|
||||
* 3. Add a usage example to unknownCommand() so users know about it.
|
||||
*
|
||||
* Pattern convention: /keyword\s*=\s*(.+)/i
|
||||
* - The first capture group is the raw value after "=".
|
||||
* - The handler receives it already trimmed.
|
||||
*/
|
||||
class ChatbotService
|
||||
{
|
||||
/**
|
||||
* Registry of structured-command handlers.
|
||||
*
|
||||
* Each entry:
|
||||
* 'pattern' — PCRE regex; capture group 1 is the extracted value.
|
||||
* 'handler' — name of the private method that processes the command.
|
||||
*
|
||||
* For two-value commands (e.g. invoice report), group 1 and group 2 are
|
||||
* both passed to the handler; single-value handlers simply ignore $value2.
|
||||
*/
|
||||
private array $handlers = [
|
||||
[
|
||||
'pattern' => '/inv(?:oice)?(?:\s*(?:number|num|no\.?))?\s*(?:=|is| |equal\s+to)\s*([^\s,\.]+)/i',
|
||||
'handler' => 'handleInvoice',
|
||||
],
|
||||
|
||||
// ── Invoice report: item type lookup ──────────────────────────────────
|
||||
// Accepts patterns like:
|
||||
// item = 674071 plant = Vahinie Unit 2
|
||||
// item code = 674071 plant = Vahinie Unit 2
|
||||
// check item 674071 for plant Vahinie Unit 2
|
||||
[
|
||||
'pattern' => '/item(?:\s*code)?\s*(?:=|is|:)?\s*([^\s,]+)\s+(?:for\s+)?plant\s*(?:=|is|:)?\s*(.+)/i',
|
||||
'handler' => 'handleInvoiceReport',
|
||||
],
|
||||
|
||||
// ── Add more commands here ────────────────────────────────────────────
|
||||
// Example:
|
||||
// [
|
||||
// 'pattern' => '/^\s*serial\s*=\s*(.+)/i',
|
||||
// 'handler' => 'handleSerial',
|
||||
// ],
|
||||
];
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Public entry point
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Dispatch the user's input to the matching handler.
|
||||
*/
|
||||
public function ask(string $input): string
|
||||
{
|
||||
$input = trim($input);
|
||||
|
||||
foreach ($this->handlers as $entry) {
|
||||
if (preg_match($entry['pattern'], $input, $matches)) {
|
||||
$value = trim($matches[1] ?? '');
|
||||
$value2 = trim($matches[2] ?? '');
|
||||
return $this->{$entry['handler']}($value, $value2);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->unknownCommand($input);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Handler: invoice = <invoice_number>
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Looks up scan status for an invoice number in invoice_validations.
|
||||
* Returns a plain-English string (used by the Advanced / free-text path).
|
||||
* Structured callers should use getInvoiceData() directly.
|
||||
*/
|
||||
private function handleInvoice(string $invoiceNumber, string $_unused = ''): string
|
||||
{
|
||||
$data = $this->getInvoiceData($invoiceNumber);
|
||||
|
||||
// For the plain-text path (advanced mode / ChatbotService::ask()),
|
||||
// reassemble a human-readable sentence from the structured data.
|
||||
if (in_array($data['type'], ['invalid', 'error', 'not_found'], true)) {
|
||||
return $data['message'];
|
||||
}
|
||||
|
||||
if ($data['type'] === 'all_scanned') {
|
||||
$n = $data['total'];
|
||||
$itemWord = $n === 1 ? 'serial number' : 'serial numbers';
|
||||
return "For invoice number {$data['invoice_number']}, all {$n} {$itemWord} "
|
||||
. ($n === 1 ? 'has' : 'have') . ' been scanned. ✅';
|
||||
}
|
||||
|
||||
// partial or none_scanned
|
||||
$total = $data['total'];
|
||||
$scanned = $data['scanned'];
|
||||
$notScan = $data['not_scanned'];
|
||||
$inv = $data['invoice_number'];
|
||||
$itemWord = $total === 1 ? 'serial number' : 'serial numbers';
|
||||
|
||||
if ($scanned === 0) {
|
||||
$msg = "For invoice number {$inv}, there "
|
||||
. ($total === 1 ? 'is' : 'are') . " {$total} {$itemWord} "
|
||||
. 'and none have been scanned.';
|
||||
} else {
|
||||
$msg = "For invoice number {$inv}, there "
|
||||
. ($total === 1 ? 'is' : 'are') . " {$total} {$itemWord} in total. "
|
||||
. "Out of which {$scanned} "
|
||||
. ($scanned === 1 ? 'has' : 'have') . ' been scanned and '
|
||||
. "{$notScan} "
|
||||
. ($notScan === 1 ? 'has' : 'have') . ' not been scanned.';
|
||||
}
|
||||
|
||||
if (! empty($data['unscanned_serials'])) {
|
||||
$msg .= ' Unscanned serial numbers are: '
|
||||
. implode(', ', $data['unscanned_serials']) . '.';
|
||||
}
|
||||
|
||||
return $msg;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Public structured accessor — used by ChatBot (Basic mode)
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Returns structured scan-status data for an invoice number.
|
||||
*
|
||||
* Return shape:
|
||||
* [
|
||||
* 'type' => 'all_scanned' | 'partial' | 'none_scanned'
|
||||
* | 'not_found' | 'error' | 'invalid',
|
||||
* 'message' => string, // one-line human summary (no serial list)
|
||||
* 'invoice_number' => string,
|
||||
* 'total' => int,
|
||||
* 'scanned' => int,
|
||||
* 'not_scanned' => int,
|
||||
* 'unscanned_serials' => string[], // full list — may be large
|
||||
* ]
|
||||
*/
|
||||
public function getInvoiceData(string $invoiceNumber): array
|
||||
{
|
||||
$invoiceNumber = preg_replace('/\s+/', '', $invoiceNumber);
|
||||
|
||||
if (empty($invoiceNumber)) {
|
||||
return [
|
||||
'type' => 'invalid',
|
||||
'message' => 'Please provide a valid invoice number. Example: invoice = 3RA0013333',
|
||||
'invoice_number' => '',
|
||||
'total' => 0,
|
||||
'scanned' => 0,
|
||||
'not_scanned' => 0,
|
||||
'unscanned_serials' => [],
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$rows = DB::select("
|
||||
SELECT
|
||||
COALESCE(scanned_status, 'not scanned') AS status,
|
||||
COUNT(*) AS total_count,
|
||||
STRING_AGG(
|
||||
CASE
|
||||
WHEN scanned_status IS NULL THEN serial_number::text
|
||||
END,
|
||||
', '
|
||||
) AS serial_numbers_not_scanned
|
||||
FROM invoice_validations
|
||||
WHERE invoice_number = ?
|
||||
GROUP BY scanned_status
|
||||
", [$invoiceNumber]);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('ChatbotService: invoice query failed', [
|
||||
'invoice' => $invoiceNumber,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'type' => 'error',
|
||||
'message' => "Sorry, I couldn't fetch data for invoice {$invoiceNumber}. "
|
||||
. 'Please try again or contact support if this keeps happening.',
|
||||
'invoice_number' => $invoiceNumber,
|
||||
'total' => 0,
|
||||
'scanned' => 0,
|
||||
'not_scanned' => 0,
|
||||
'unscanned_serials' => [],
|
||||
];
|
||||
}
|
||||
|
||||
if (empty($rows)) {
|
||||
return [
|
||||
'type' => 'not_found',
|
||||
'message' => "No records found for invoice number {$invoiceNumber}. "
|
||||
. 'Please double-check the invoice number and try again.',
|
||||
'invoice_number' => $invoiceNumber,
|
||||
'total' => 0,
|
||||
'scanned' => 0,
|
||||
'not_scanned' => 0,
|
||||
'unscanned_serials' => [],
|
||||
];
|
||||
}
|
||||
|
||||
// ── Aggregate rows ────────────────────────────────────────────────────
|
||||
$totalScanned = 0;
|
||||
$totalNotScanned = 0;
|
||||
$unscannedSerials = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if ($row->status === 'not scanned') {
|
||||
$totalNotScanned = (int) $row->total_count;
|
||||
if (! empty($row->serial_numbers_not_scanned)) {
|
||||
$unscannedSerials = array_values(
|
||||
array_filter(
|
||||
array_map('trim', explode(',', $row->serial_numbers_not_scanned))
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$totalScanned += (int) $row->total_count;
|
||||
}
|
||||
}
|
||||
|
||||
$grandTotal = $totalScanned + $totalNotScanned;
|
||||
|
||||
// ── All scanned ───────────────────────────────────────────────────────
|
||||
if ($totalNotScanned === 0) {
|
||||
$n = $grandTotal;
|
||||
$itemWord = $n === 1 ? 'serial number' : 'serial numbers';
|
||||
return [
|
||||
'type' => 'all_scanned',
|
||||
'message' => "All {$n} {$itemWord} scanned for invoice {$invoiceNumber}. ✅",
|
||||
'invoice_number' => $invoiceNumber,
|
||||
'total' => $grandTotal,
|
||||
'scanned' => $totalScanned,
|
||||
'not_scanned' => 0,
|
||||
'unscanned_serials' => [],
|
||||
];
|
||||
}
|
||||
|
||||
// ── None / partial scanned ────────────────────────────────────────────
|
||||
$type = $totalScanned === 0 ? 'none_scanned' : 'partial';
|
||||
$itemWord = $grandTotal === 1 ? 'serial number' : 'serial numbers';
|
||||
|
||||
if ($totalScanned === 0) {
|
||||
$summary = "Invoice {$invoiceNumber} — {$grandTotal} {$itemWord}, none scanned yet.";
|
||||
} else {
|
||||
$summary = "Invoice {$invoiceNumber} — {$grandTotal} {$itemWord} total: "
|
||||
. "{$totalScanned} scanned, {$totalNotScanned} not scanned.";
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => $type,
|
||||
'message' => $summary,
|
||||
'invoice_number' => $invoiceNumber,
|
||||
'total' => $grandTotal,
|
||||
'scanned' => $totalScanned,
|
||||
'not_scanned' => $totalNotScanned,
|
||||
'unscanned_serials' => $unscannedSerials,
|
||||
];
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Handler: item = <item_code> plant = <plant_name>
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Determines whether an item is a serial invoice or material invoice
|
||||
* for a given plant, using the sticker_masters table.
|
||||
*
|
||||
* @param string $itemCode Extracted item code (capture group 1)
|
||||
* @param string $plantName Extracted plant name (capture group 2)
|
||||
*/
|
||||
private function handleInvoiceReport(string $itemCode, string $plantName): string
|
||||
{
|
||||
$itemCode = trim($itemCode);
|
||||
$plantName = trim($plantName);
|
||||
|
||||
if (empty($itemCode)) {
|
||||
return 'Please provide an item code. Example: item = 674071 plant = Vahinie Unit 2';
|
||||
}
|
||||
|
||||
if (empty($plantName)) {
|
||||
return 'Please provide a plant name. Example: item = 674071 plant = Vahinie Unit 2';
|
||||
}
|
||||
|
||||
try {
|
||||
$rows = DB::select("
|
||||
WITH plant_item AS (
|
||||
SELECT ? AS user_plant,
|
||||
? AS user_item_code
|
||||
),
|
||||
t1 AS (
|
||||
SELECT
|
||||
plants.id AS plant_id,
|
||||
plants.name AS plant_name,
|
||||
ARRAY_AGG(items.code) AS item_codes
|
||||
FROM plants
|
||||
LEFT JOIN items ON plants.id = items.plant_id
|
||||
GROUP BY plants.id, plants.name
|
||||
),
|
||||
t2 AS (
|
||||
SELECT
|
||||
t1.plant_id,
|
||||
t1.plant_name,
|
||||
CASE
|
||||
WHEN plant_item.user_item_code = ANY(t1.item_codes) THEN 1
|
||||
ELSE 0
|
||||
END AS exists_flag
|
||||
FROM t1
|
||||
CROSS JOIN plant_item
|
||||
WHERE t1.plant_name = plant_item.user_plant
|
||||
),
|
||||
t3 AS (
|
||||
SELECT t2.plant_id, t2.plant_name, t2.exists_flag,
|
||||
plant_item.user_item_code
|
||||
FROM t2
|
||||
LEFT JOIN plant_item ON plant_item.user_plant = t2.plant_name
|
||||
),
|
||||
t4 AS (
|
||||
SELECT items.id AS item_id,
|
||||
t3.plant_id, t3.plant_name, t3.exists_flag, t3.user_item_code
|
||||
FROM t3
|
||||
LEFT JOIN items
|
||||
ON t3.plant_id = items.plant_id
|
||||
AND t3.user_item_code = items.code
|
||||
)
|
||||
SELECT
|
||||
t4.item_id,
|
||||
t4.plant_id,
|
||||
t4.plant_name,
|
||||
t4.exists_flag,
|
||||
t4.user_item_code,
|
||||
COALESCE(sticker_masters.material_type, 0) AS material_type,
|
||||
CASE
|
||||
WHEN sticker_masters.item_id IS NULL
|
||||
THEN 'no match found'
|
||||
WHEN COALESCE(sticker_masters.material_type, 0) = 0
|
||||
THEN 'serial invoice'
|
||||
ELSE 'material invoice'
|
||||
END AS invoice_description
|
||||
FROM t4
|
||||
LEFT JOIN sticker_masters
|
||||
ON sticker_masters.plant_id = t4.plant_id
|
||||
AND sticker_masters.item_id = t4.item_id
|
||||
", [$plantName, $itemCode]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('ChatbotService: invoice report query failed', [
|
||||
'plant' => $plantName,
|
||||
'item_code' => $itemCode,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return "Sorry, I couldn't fetch data for item {$itemCode} in plant {$plantName}. "
|
||||
. 'Please try again or contact support.';
|
||||
}
|
||||
|
||||
if (empty($rows)) {
|
||||
return "No data found for plant \"{$plantName}\". Please check the plant name and try again.";
|
||||
}
|
||||
|
||||
$row = $rows[0];
|
||||
|
||||
if ((int) $row->exists_flag === 0) {
|
||||
return 'Provided item code does not exist in the item table.';
|
||||
}
|
||||
|
||||
return match ($row->invoice_description) {
|
||||
'no match found' => "Item not found in sticker master for the plant {$row->plant_name}.",
|
||||
'serial invoice' => 'It is a serial invoice item.',
|
||||
'material invoice' => 'It is a material invoice item.',
|
||||
default => 'Unexpected result. Please contact support.',
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Fallback for unrecognised input
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private function unknownCommand(string $input): string
|
||||
{
|
||||
return "I didn't recognise that command. Please include a supported keyword with a value after '='.\n\n"
|
||||
. "• Invoice scan status:\n"
|
||||
. " invoice = 3RA0013333\n"
|
||||
. " what is the status of invoice = 3RA0013333\n\n"
|
||||
. "• Invoice type lookup:\n"
|
||||
. " item = 674071 plant = Vahinie Unit 2\n"
|
||||
. " item code = 674071 plant = Vahinie Unit 2\n\n"
|
||||
. 'Any sentence containing keyword = value will work.';
|
||||
}
|
||||
}
|
||||
819
app/Services/GeminiChatbotService.php
Normal file
819
app/Services/GeminiChatbotService.php
Normal file
@@ -0,0 +1,819 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* GeminiChatbotService
|
||||
* ─────────────────────────────────────────────────────────────────────────────
|
||||
* Powers the "Advanced" chatbot mode with plain-English understanding.
|
||||
*
|
||||
* FLOW:
|
||||
* 1. Takes the full chat history + new user message.
|
||||
* 2. Sends them to Gemini with a structured system prompt.
|
||||
* 3. Gemini returns JSON: { task, params, missing, clarification }.
|
||||
* 4. If task is a known, complete query → runs the matching DB handler.
|
||||
* 5. If task is "unknown" → sends the message to Gemini again as a free-form
|
||||
* conversational assistant so it can answer or ask the user what they need.
|
||||
* 6. If required params are missing → Gemini's clarification question is returned.
|
||||
*
|
||||
* SUPPORTED TASKS:
|
||||
* - invoice_status → scan status of an invoice number
|
||||
* - invoice_report → serial/material invoice type for an item + plant
|
||||
* - production_report → production count for a plant / line / date range
|
||||
* - unknown → handled via a free-form Gemini conversation turn
|
||||
*
|
||||
* CONFIGURATION (.env):
|
||||
* GEMINI_API_KEY=your_key_here
|
||||
* GEMINI_MODEL=gemini-2.5-flash-preview ← set whichever model your key supports
|
||||
*
|
||||
* HOW TO ADD A NEW TASK:
|
||||
* 1. Describe it in buildSystemPrompt() under TASKS.
|
||||
* 2. Add a private handle*() method below.
|
||||
* 3. Add a match arm in processMessage().
|
||||
*/
|
||||
class GeminiChatbotService
|
||||
{
|
||||
private string $apiKey;
|
||||
|
||||
/**
|
||||
* Full REST endpoint — model name is part of the URL path, e.g.:
|
||||
* https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview:generateContent
|
||||
*
|
||||
* Built in __construct() from config('services.gemini.model').
|
||||
* Change GEMINI_MODEL in .env to switch models without editing code.
|
||||
*/
|
||||
private string $apiUrl;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->apiKey = config('services.gemini.api_key', '');
|
||||
|
||||
$model = config('services.gemini.model', 'gemini-3-flash-preview');
|
||||
|
||||
$this->apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent";
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Public entry point
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Process a user message in context of the prior conversation.
|
||||
*
|
||||
* @param array $chatHistory Previous turns: [['role'=>'user'|'assistant','content'=>'…'], …]
|
||||
* @param string $userInput The new message just typed.
|
||||
* @return string Plain-text reply to show in the chat bubble.
|
||||
*/
|
||||
public function processMessage(array $chatHistory, string $userInput): string
|
||||
{
|
||||
if (empty($this->apiKey)) {
|
||||
return '⚠️ AI features are not configured. Please set GEMINI_API_KEY in your .env file.';
|
||||
}
|
||||
|
||||
// ── Step 1: Classify the intent ───────────────────────────────────────
|
||||
try {
|
||||
$classification = $this->classifyWithGemini($chatHistory, $userInput);
|
||||
} catch (\RuntimeException $e) {
|
||||
// Surface the specific error directly in the chat bubble
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$task = $classification['task'] ?? 'unknown';
|
||||
$params = $classification['params'] ?? [];
|
||||
$missing = $classification['missing'] ?? [];
|
||||
$clarification = $classification['clarification'] ?? null;
|
||||
|
||||
// ── Step 2: Missing required params → ask user for them ───────────────
|
||||
if (! empty($missing) && ! empty($clarification)) {
|
||||
return $clarification;
|
||||
}
|
||||
|
||||
// ── Step 3: Dispatch to the appropriate handler ───────────────────────
|
||||
return match ($task) {
|
||||
'invoice_status' => $this->handleInvoiceStatus($params),
|
||||
'invoice_report' => $this->handleInvoiceReport($params),
|
||||
'production_report' => $this->handleProductionReport($params),
|
||||
|
||||
// ── Unknown intent: hand off to Gemini as a free-form assistant ──
|
||||
'unknown' => $this->handleUnknown($chatHistory, $userInput, $clarification),
|
||||
|
||||
default => $this->handleUnknown($chatHistory, $userInput, null),
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Gemini API calls
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Phase 1 — Classify the user's intent and extract structured params.
|
||||
*
|
||||
* @return array|null Parsed JSON array, or null on failure.
|
||||
*/
|
||||
/**
|
||||
* @throws \RuntimeException with a user-facing message describing exactly what failed.
|
||||
*/
|
||||
private function classifyWithGemini(array $history, string $userInput): array
|
||||
{
|
||||
$requestBody = [
|
||||
'system_instruction' => [
|
||||
'parts' => [['text' => $this->buildSystemPrompt()]],
|
||||
],
|
||||
'contents' => $this->buildGeminiContents($history, $userInput),
|
||||
'generationConfig' => [
|
||||
'temperature' => 0.1,
|
||||
'responseMimeType' => 'application/json',
|
||||
],
|
||||
];
|
||||
|
||||
// ── HTTP call ─────────────────────────────────────────────────────────
|
||||
try {
|
||||
$response = Http::withHeaders(['Content-Type' => 'application/json'])
|
||||
->timeout(15)
|
||||
->post($this->apiUrl . '?key=' . $this->apiKey, $requestBody);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('GeminiChatbotService: HTTP exception', ['error' => $e->getMessage()]);
|
||||
throw new \RuntimeException(
|
||||
'⚠️ Could not reach the Gemini API. Check your network or firewall. ('
|
||||
. $e->getMessage() . ')'
|
||||
);
|
||||
}
|
||||
|
||||
// ── HTTP-level error ──────────────────────────────────────────────────
|
||||
if (! $response->successful()) {
|
||||
$status = $response->status();
|
||||
$body = $response->body();
|
||||
|
||||
Log::error('GeminiChatbotService: API HTTP error', [
|
||||
'status' => $status,
|
||||
'body' => $body,
|
||||
]);
|
||||
|
||||
// Parse Google's error message when available
|
||||
$googleMsg = $response->json('error.message') ?? $body;
|
||||
|
||||
throw new \RuntimeException(
|
||||
"⚠️ Gemini API returned HTTP {$status}: {$googleMsg}"
|
||||
);
|
||||
}
|
||||
|
||||
// ── Extract text from response ────────────────────────────────────────
|
||||
$text = $response->json('candidates.0.content.parts.0.text');
|
||||
|
||||
if (empty($text)) {
|
||||
// Check for prompt-blocking
|
||||
$blockReason = $response->json('promptFeedback.blockReason');
|
||||
$finishReason = $response->json('candidates.0.finishReason');
|
||||
|
||||
Log::error('GeminiChatbotService: empty response text', [
|
||||
'blockReason' => $blockReason,
|
||||
'finishReason' => $finishReason,
|
||||
'full' => $response->json(),
|
||||
]);
|
||||
|
||||
$hint = $blockReason
|
||||
? "prompt was blocked (reason: {$blockReason})"
|
||||
: ($finishReason ? "finish reason: {$finishReason}" : 'no text returned');
|
||||
|
||||
throw new \RuntimeException("⚠️ Gemini returned no content — {$hint}");
|
||||
}
|
||||
|
||||
// ── JSON decode ───────────────────────────────────────────────────────
|
||||
$clean = preg_replace('/^```json\s*/i', '', trim($text));
|
||||
$clean = preg_replace('/\s*```$/i', '', $clean);
|
||||
$parsed = json_decode($clean, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
Log::error('GeminiChatbotService: JSON decode failed', ['raw' => $text]);
|
||||
throw new \RuntimeException(
|
||||
'⚠️ Gemini returned a non-JSON response: ' . mb_substr($text, 0, 200)
|
||||
);
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Phase 2 (unknown task only) — Ask Gemini to respond conversationally.
|
||||
*
|
||||
* Sends the full chat history + new user message to Gemini as a friendly
|
||||
* factory-operations assistant (no JSON constraint). Gemini can ask for
|
||||
* clarification, answer general questions, or guide the user to one of the
|
||||
* supported tasks.
|
||||
*
|
||||
* @param string|null $hintFromClassification Optional clarification from the
|
||||
* classification step — prepended as assistant context if present.
|
||||
* @return string Plain-text reply from Gemini.
|
||||
*/
|
||||
private function callGeminiConversational(
|
||||
array $history,
|
||||
string $userInput,
|
||||
?string $hintFromClassification = null
|
||||
): ?string {
|
||||
// If the classifier already produced a good clarification question, use it
|
||||
// directly and skip the second API call to save latency + quota.
|
||||
if (! empty($hintFromClassification)) {
|
||||
return $hintFromClassification;
|
||||
}
|
||||
|
||||
$requestBody = [
|
||||
'system_instruction' => [
|
||||
'parts' => [['text' => $this->buildConversationalSystemPrompt()]],
|
||||
],
|
||||
'contents' => $this->buildGeminiContents($history, $userInput),
|
||||
'generationConfig' => [
|
||||
'temperature' => 0.7, // more natural conversational tone
|
||||
'maxOutputTokens' => 400,
|
||||
],
|
||||
];
|
||||
|
||||
try {
|
||||
$response = Http::withHeaders(['Content-Type' => 'application/json'])
|
||||
->timeout(20)
|
||||
->post($this->apiUrl . '?key=' . $this->apiKey, $requestBody);
|
||||
|
||||
if (! $response->successful()) {
|
||||
Log::error('GeminiChatbotService: API error (conversational)', [
|
||||
'status' => $response->status(),
|
||||
'body' => $response->body(),
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $response->json('candidates.0.content.parts.0.text');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('GeminiChatbotService: exception (conversational)', ['error' => $e->getMessage()]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Prompt builders
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* System prompt for Phase 1 (classification) — forces JSON output.
|
||||
*/
|
||||
private function buildSystemPrompt(): string
|
||||
{
|
||||
$today = now()->format('Y-m-d');
|
||||
$startOfMonth = now()->startOfMonth()->format('Y-m-d');
|
||||
|
||||
return <<<PROMPT
|
||||
You are a factory operations assistant that classifies plain-English user queries into structured tasks.
|
||||
|
||||
TASKS:
|
||||
1. "invoice_status"
|
||||
The user wants to check how many serial numbers in an invoice have been scanned / not scanned.
|
||||
Required params: invoice_number
|
||||
Examples:
|
||||
- "check invoice 3RA0013333"
|
||||
- "is invoice 3RA0013333 fully scanned?"
|
||||
- "what's the scan status of 3RA0013333"
|
||||
- "show me unscanned serials for invoice ABC123"
|
||||
|
||||
2. "invoice_report"
|
||||
The user wants to know whether an item is a serial invoice or material invoice for a given plant.
|
||||
Required params: item_code, plant_name
|
||||
Examples:
|
||||
- "check item 674071 for plant Vahinie Unit 2"
|
||||
- "is item 500100 a serial or material invoice in Chennai plant?"
|
||||
- "what type is item code 200300 at Vahinie?"
|
||||
|
||||
3. "production_report"
|
||||
The user wants the production count for a plant and optional line over a date range.
|
||||
Required params: plant_name
|
||||
Optional params: line_name, date_from, date_to
|
||||
Default dates: date_from = {$startOfMonth}, date_to = {$today}
|
||||
Interpret relative dates: "this month", "last week", "yesterday", "today", etc.
|
||||
Examples:
|
||||
- "show production for Chennai plant this month"
|
||||
- "how many units were produced in line 1 of Vahinie Unit 2 last week?"
|
||||
- "production report for all plants from 2024-01-01 to 2024-01-31"
|
||||
|
||||
4. "unknown"
|
||||
The query cannot be clearly matched to any of the above tasks.
|
||||
Use this when the user is asking something general, greeting, asking what the bot can do,
|
||||
asking a follow-up question that doesn't map to a task, or if you need more context.
|
||||
In this case, set "clarification" to a friendly, helpful response — it may be a question,
|
||||
a helpful explanation, or guidance toward the supported tasks.
|
||||
|
||||
CONVERSATION CONTEXT:
|
||||
Consider the full conversation history. If the previous assistant message asked a clarifying question
|
||||
(e.g. "Do you mean Invoice Status or Invoice Report?") and the user is now answering that question,
|
||||
classify accordingly based on the combined context.
|
||||
|
||||
OUTPUT FORMAT (return ONLY this JSON, no markdown fences, no extra text):
|
||||
{
|
||||
"task": "invoice_status | invoice_report | production_report | unknown",
|
||||
"params": {
|
||||
"invoice_number": "...",
|
||||
"item_code": "...",
|
||||
"plant_name": "...",
|
||||
"line_name": "...",
|
||||
"date_from": "YYYY-MM-DD",
|
||||
"date_to": "YYYY-MM-DD"
|
||||
},
|
||||
"missing": ["list of required params that were not found in the user input"],
|
||||
"clarification": "Friendly response for unknown tasks or missing-param prompts. Set to null when task is clear and params are complete."
|
||||
}
|
||||
|
||||
RULES:
|
||||
- Only include params relevant to the detected task.
|
||||
- If a required param is missing, add it to "missing" and set a helpful "clarification" asking only for that missing value.
|
||||
- If task is "unknown", always set "missing" to [] and put your full helpful response in "clarification".
|
||||
- When task is clear and all required params are present, set "missing" to [] and "clarification" to null.
|
||||
PROMPT;
|
||||
}
|
||||
|
||||
/**
|
||||
* System prompt for Phase 2 (conversational fallback) — plain-text output.
|
||||
*
|
||||
* Used only when the classifier returns "unknown" AND produced no clarification.
|
||||
*/
|
||||
private function buildConversationalSystemPrompt(): string
|
||||
{
|
||||
return <<<PROMPT
|
||||
You are a helpful factory operations assistant integrated into an internal management panel.
|
||||
|
||||
You can perform these tasks when the user gives you enough information:
|
||||
• Invoice Status — check how many serial numbers in an invoice have been scanned and list any unscanned ones. Requires: invoice number.
|
||||
• Invoice Report — find out whether an item is a serial invoice or material invoice for a given plant. Requires: item code and plant name.
|
||||
• Production Report — get the production count for a plant, optionally filtered by line and date range. Requires: plant name.
|
||||
|
||||
When the user's request does not match any of the above:
|
||||
- Answer general questions helpfully and concisely.
|
||||
- If you need more information to perform a task, ask for only the missing detail.
|
||||
- Guide the user toward one of the supported tasks when relevant.
|
||||
- Keep replies short and conversational (2-4 sentences max).
|
||||
- Do NOT mention JSON, APIs, or technical internals.
|
||||
PROMPT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Livewire chat history + new user message into Gemini's contents array.
|
||||
* Gemini uses "model" for the assistant role (not "assistant").
|
||||
*/
|
||||
private function buildGeminiContents(array $history, string $userInput): array
|
||||
{
|
||||
$contents = [];
|
||||
|
||||
// Include the last 8 turns at most to stay within token limits
|
||||
foreach (array_slice($history, -8) as $msg) {
|
||||
$contents[] = [
|
||||
'role' => $msg['role'] === 'user' ? 'user' : 'model',
|
||||
'parts' => [['text' => $msg['content']]],
|
||||
];
|
||||
}
|
||||
|
||||
$contents[] = [
|
||||
'role' => 'user',
|
||||
'parts' => [['text' => $userInput]],
|
||||
];
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Task handlers
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Invoice Status — delegate to ChatbotService (regex-based, same as basic mode).
|
||||
*/
|
||||
private function handleInvoiceStatus(array $params): string
|
||||
{
|
||||
$invoiceNumber = trim(preg_replace('/\s+/', '', $params['invoice_number'] ?? ''));
|
||||
|
||||
if (empty($invoiceNumber)) {
|
||||
return 'I need the invoice number to check the scan status. What is the invoice number?';
|
||||
}
|
||||
|
||||
/** @var ChatbotService $svc */
|
||||
$svc = app(ChatbotService::class);
|
||||
|
||||
return $svc->ask("invoice = {$invoiceNumber}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoice Report — resolves plant name with fuzzy matching, then runs the
|
||||
* same CTE query as ChatBot::fetchInvoiceReport() directly.
|
||||
*
|
||||
* We bypass ChatbotService::ask() here so that resolvePlant()'s multi-strategy
|
||||
* fuzzy logic is applied rather than the simpler LIKE inside ChatbotService.
|
||||
*/
|
||||
private function handleInvoiceReport(array $params): string
|
||||
{
|
||||
$itemCode = trim($params['item_code'] ?? '');
|
||||
$plantName = trim($params['plant_name'] ?? '');
|
||||
|
||||
if (empty($itemCode)) {
|
||||
return 'I need the item code to look up the invoice type. What is the item code?';
|
||||
}
|
||||
|
||||
if (empty($plantName)) {
|
||||
return 'I need the plant name to look up the invoice type. Which plant are you asking about?';
|
||||
}
|
||||
|
||||
// ── Fuzzy-resolve the plant name ──────────────────────────────────────
|
||||
$plant = $this->resolvePlant($plantName);
|
||||
|
||||
if ($plant === null) {
|
||||
return "I couldn't find a plant matching \"{$plantName}\". "
|
||||
. 'Please check the plant name and try again.';
|
||||
}
|
||||
|
||||
// ── Run the same CTE as ChatBot::fetchInvoiceReport() ─────────────────
|
||||
try {
|
||||
$rows = DB::select("
|
||||
WITH plant_item AS (
|
||||
SELECT ? AS user_plant,
|
||||
? AS user_item_code
|
||||
),
|
||||
t1 AS (
|
||||
SELECT
|
||||
plants.id AS plant_id,
|
||||
plants.name AS plant_name,
|
||||
ARRAY_AGG(items.code) AS item_codes
|
||||
FROM plants
|
||||
LEFT JOIN items ON plants.id = items.plant_id
|
||||
GROUP BY plants.id, plants.name
|
||||
),
|
||||
t2 AS (
|
||||
SELECT
|
||||
t1.plant_id,
|
||||
t1.plant_name,
|
||||
CASE
|
||||
WHEN plant_item.user_item_code = ANY(t1.item_codes) THEN 1
|
||||
ELSE 0
|
||||
END AS exists_flag
|
||||
FROM t1
|
||||
CROSS JOIN plant_item
|
||||
WHERE t1.plant_name = plant_item.user_plant
|
||||
),
|
||||
t3 AS (
|
||||
SELECT t2.plant_id, t2.plant_name, t2.exists_flag,
|
||||
plant_item.user_item_code
|
||||
FROM t2
|
||||
LEFT JOIN plant_item ON plant_item.user_plant = t2.plant_name
|
||||
),
|
||||
t4 AS (
|
||||
SELECT items.id AS item_id,
|
||||
t3.plant_id, t3.plant_name, t3.exists_flag, t3.user_item_code
|
||||
FROM t3
|
||||
LEFT JOIN items
|
||||
ON t3.plant_id = items.plant_id
|
||||
AND t3.user_item_code = items.code
|
||||
)
|
||||
SELECT
|
||||
t4.item_id,
|
||||
t4.plant_id,
|
||||
t4.plant_name,
|
||||
t4.exists_flag,
|
||||
t4.user_item_code,
|
||||
COALESCE(sticker_masters.material_type, 0) AS material_type,
|
||||
CASE
|
||||
WHEN sticker_masters.item_id IS NULL
|
||||
THEN 'no match found'
|
||||
WHEN COALESCE(sticker_masters.material_type, 0) = 0
|
||||
THEN 'serial invoice'
|
||||
ELSE 'material invoice'
|
||||
END AS invoice_description
|
||||
FROM t4
|
||||
LEFT JOIN sticker_masters
|
||||
ON sticker_masters.plant_id = t4.plant_id
|
||||
AND sticker_masters.item_id = t4.item_id
|
||||
", [$plant->name, $itemCode]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('GeminiChatbotService: invoice report query failed', [
|
||||
'plant' => $plant->name,
|
||||
'item_code' => $itemCode,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
return "Sorry, I couldn't fetch data. Please try again or contact support.";
|
||||
}
|
||||
|
||||
if (empty($rows)) {
|
||||
return "No data found for plant \"{$plant->name}\". Please verify the plant name.";
|
||||
}
|
||||
|
||||
$row = $rows[0];
|
||||
|
||||
if ((int) $row->exists_flag === 0) {
|
||||
return 'The provided item code does not exist in the item table.';
|
||||
}
|
||||
|
||||
return match ($row->invoice_description) {
|
||||
'serial invoice' => 'It is a serial invoice item.',
|
||||
'material invoice' => 'It is a material invoice item.',
|
||||
'no match found' => "Item not found in sticker master for plant {$plant->name}.",
|
||||
default => 'Unexpected result. Please contact support.',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Production Report — resolves plant/line names to IDs and runs the count query.
|
||||
* Mirrors ChatBot::fetchProduction() but works with plain names instead of IDs,
|
||||
* using resolvePlant() for robust fuzzy matching.
|
||||
*/
|
||||
private function handleProductionReport(array $params): string
|
||||
{
|
||||
$plantName = trim($params['plant_name'] ?? '');
|
||||
$lineName = trim($params['line_name'] ?? '');
|
||||
$dateFrom = $params['date_from'] ?? now()->startOfMonth()->format('Y-m-d');
|
||||
$dateTo = $params['date_to'] ?? now()->format('Y-m-d');
|
||||
|
||||
if (empty($plantName)) {
|
||||
return 'I need a plant name to fetch the production report. Which plant are you asking about?';
|
||||
}
|
||||
|
||||
// ── Fuzzy-resolve the plant name ──────────────────────────────────────
|
||||
$plant = $this->resolvePlant($plantName);
|
||||
|
||||
if ($plant === null) {
|
||||
return "I couldn't find a plant matching \"{$plantName}\". "
|
||||
. 'Please check the plant name and try again.';
|
||||
}
|
||||
|
||||
// ── Base query ────────────────────────────────────────────────────────
|
||||
$query = DB::table('production_quantities')
|
||||
->whereNull('deleted_at')
|
||||
->where('plant_id', $plant->id)
|
||||
->whereDate('created_at', '>=', $dateFrom)
|
||||
->whereDate('created_at', '<=', $dateTo);
|
||||
|
||||
$lineLabel = 'All Lines';
|
||||
|
||||
// ── Optionally filter by line (fuzzy LIKE match) ──────────────────────
|
||||
if (! empty($lineName)) {
|
||||
$line = $this->resolveLine($lineName, $plant->id);
|
||||
|
||||
if ($line === null) {
|
||||
return "I couldn't find a line matching \"{$lineName}\" "
|
||||
. "in plant \"{$plant->name}\". Please check the line name.";
|
||||
}
|
||||
|
||||
$query->where('line_id', $line->id);
|
||||
$lineLabel = $line->name;
|
||||
}
|
||||
|
||||
try {
|
||||
$count = $query->count();
|
||||
} catch (\Exception $e) {
|
||||
Log::error('GeminiChatbotService: production query failed', [
|
||||
'plant' => $plant->name,
|
||||
'line' => $lineLabel,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return "Sorry, I couldn't fetch production data for {$plant->name}. "
|
||||
. 'Please try again or contact support.';
|
||||
}
|
||||
|
||||
$from = \Carbon\Carbon::parse($dateFrom)->format('d M Y');
|
||||
$to = \Carbon\Carbon::parse($dateTo)->format('d M Y');
|
||||
|
||||
return "📊 Production count for {$plant->name} / {$lineLabel} "
|
||||
. "from {$from} to {$to}: {$count} records.";
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Fuzzy name resolvers
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Resolve a user-supplied plant name to the best matching DB row.
|
||||
*
|
||||
* Strategy cascade (stops at first hit):
|
||||
* 1. Exact case-insensitive match → "ransar industries-i" == "Ransar Industries-I"
|
||||
* 2. Normalised LIKE match → strips hyphens/spaces, swaps I↔1
|
||||
* 3. Every significant word present (LIKE) → "ransar unit 2" matches "Ransar Industries Unit 2"
|
||||
* 4. Best token-overlap score → picks the DB row sharing the most words
|
||||
*
|
||||
* @return object|null stdClass with {id, name} or null if no match.
|
||||
*/
|
||||
private function resolvePlant(string $userInput): ?object
|
||||
{
|
||||
$allPlants = DB::table('plants')
|
||||
->whereNull('deleted_at')
|
||||
->get(['id', 'name']);
|
||||
|
||||
$norm = $this->normaliseForMatching($userInput);
|
||||
|
||||
// ── Strategy 1: exact normalised match ────────────────────────────────
|
||||
foreach ($allPlants as $plant) {
|
||||
if ($this->normaliseForMatching($plant->name) === $norm) {
|
||||
return $plant;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Strategy 2: normalised LIKE (user input contained in plant name or vice-versa) ──
|
||||
foreach ($allPlants as $plant) {
|
||||
$dbNorm = $this->normaliseForMatching($plant->name);
|
||||
if (str_contains($dbNorm, $norm) || str_contains($norm, $dbNorm)) {
|
||||
return $plant;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Strategy 3: all significant user words appear in the plant name ───
|
||||
$userTokens = $this->significantTokens($norm);
|
||||
|
||||
if (count($userTokens) >= 1) {
|
||||
foreach ($allPlants as $plant) {
|
||||
$dbNorm = $this->normaliseForMatching($plant->name);
|
||||
$allFound = true;
|
||||
foreach ($userTokens as $token) {
|
||||
if (! str_contains($dbNorm, $token)) {
|
||||
$allFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($allFound) {
|
||||
return $plant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Strategy 4: best token-overlap score ─────────────────────────────
|
||||
$bestPlant = null;
|
||||
$bestScore = 0;
|
||||
|
||||
foreach ($allPlants as $plant) {
|
||||
$dbTokens = $this->significantTokens($this->normaliseForMatching($plant->name));
|
||||
$shared = count(array_intersect($userTokens, $dbTokens));
|
||||
|
||||
// Require at least half the user tokens to match to avoid false positives
|
||||
$threshold = max(1, (int) ceil(count($userTokens) / 2));
|
||||
|
||||
if ($shared >= $threshold && $shared > $bestScore) {
|
||||
$bestScore = $shared;
|
||||
$bestPlant = $plant;
|
||||
}
|
||||
}
|
||||
|
||||
return $bestPlant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a user-supplied line name within a specific plant.
|
||||
* Uses the same normalisation + token strategies as resolvePlant().
|
||||
*
|
||||
* @return object|null stdClass with {id, name} or null if no match.
|
||||
*/
|
||||
private function resolveLine(string $userInput, int $plantId): ?object
|
||||
{
|
||||
$allLines = DB::table('lines')
|
||||
->whereNull('deleted_at')
|
||||
->where('plant_id', $plantId)
|
||||
->get(['id', 'name']);
|
||||
|
||||
$norm = $this->normaliseForMatching($userInput);
|
||||
|
||||
// Strategy 1: exact normalised
|
||||
foreach ($allLines as $line) {
|
||||
if ($this->normaliseForMatching($line->name) === $norm) {
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 2: normalised LIKE
|
||||
foreach ($allLines as $line) {
|
||||
$dbNorm = $this->normaliseForMatching($line->name);
|
||||
if (str_contains($dbNorm, $norm) || str_contains($norm, $dbNorm)) {
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 3: all user tokens found in line name
|
||||
$userTokens = $this->significantTokens($norm);
|
||||
|
||||
foreach ($allLines as $line) {
|
||||
$dbNorm = $this->normaliseForMatching($line->name);
|
||||
$allFound = true;
|
||||
foreach ($userTokens as $token) {
|
||||
if (! str_contains($dbNorm, $token)) {
|
||||
$allFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($allFound) {
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 4: best token-overlap
|
||||
$bestLine = null;
|
||||
$bestScore = 0;
|
||||
|
||||
foreach ($allLines as $line) {
|
||||
$dbTokens = $this->significantTokens($this->normaliseForMatching($line->name));
|
||||
$shared = count(array_intersect($userTokens, $dbTokens));
|
||||
$threshold = max(1, (int) ceil(count($userTokens) / 2));
|
||||
|
||||
if ($shared >= $threshold && $shared > $bestScore) {
|
||||
$bestScore = $shared;
|
||||
$bestLine = $line;
|
||||
}
|
||||
}
|
||||
|
||||
return $bestLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise a plant/line name for fuzzy comparison:
|
||||
* - lowercase
|
||||
* - replace Roman numeral suffixes I/II/III/IV → 1/2/3/4 (and vice-versa digits → numerals as a canonical form)
|
||||
* - collapse hyphens, underscores, extra spaces into a single space
|
||||
* - strip leading/trailing whitespace
|
||||
*
|
||||
* Both the user input AND the DB value are passed through this before comparing,
|
||||
* so the comparison is always apples-to-apples.
|
||||
*/
|
||||
private function normaliseForMatching(string $value): string
|
||||
{
|
||||
$v = strtolower($value);
|
||||
|
||||
// 1. Punctuation/separators → space
|
||||
$v = str_replace(['-', '_', '.', ','], ' ', $v);
|
||||
|
||||
// 2. Split any letter→digit or digit→letter boundary with a space.
|
||||
// e.g. "industries1" → "industries 1", "unit2" → "unit 2", "2unit" → "2 unit"
|
||||
// This must happen BEFORE Roman numeral conversion so isolated digits are
|
||||
// already separated from words.
|
||||
$v = preg_replace('/([a-z])(\d)/', '$1 $2', $v);
|
||||
$v = preg_replace('/(\d)([a-z])/', '$1 $2', $v);
|
||||
|
||||
// 3. Convert standalone Roman numerals to digits.
|
||||
// Applied AFTER splitting so "industries" is never touched —
|
||||
// the \b boundary ensures only whole tokens are matched.
|
||||
// Order matters: longer patterns first (iii before ii before i).
|
||||
$romanMap = [
|
||||
'/\bviii\b/' => '8',
|
||||
'/\bvii\b/' => '7',
|
||||
'/\bvi\b/' => '6',
|
||||
'/\biv\b/' => '4',
|
||||
'/\biii\b/' => '3',
|
||||
'/\bii\b/' => '2',
|
||||
'/\bv\b/' => '5',
|
||||
'/\bi\b/' => '1', // last — single i only after all others consumed
|
||||
];
|
||||
foreach ($romanMap as $pattern => $digit) {
|
||||
$v = preg_replace($pattern, $digit, $v);
|
||||
}
|
||||
|
||||
// 4. Collapse multiple spaces
|
||||
$v = preg_replace('/\s+/', ' ', $v);
|
||||
|
||||
return trim($v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a normalised string into significant tokens (drops noise words).
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
private function significantTokens(string $normalised): array
|
||||
{
|
||||
$stopWords = ['and', 'the', 'of', 'for', 'at', 'in', 'a'];
|
||||
$tokens = explode(' ', $normalised);
|
||||
|
||||
return array_values(array_filter($tokens, function (string $t) use ($stopWords) {
|
||||
return strlen($t) >= 2 && ! in_array($t, $stopWords, true);
|
||||
}));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Unknown task — let Gemini respond conversationally.
|
||||
*
|
||||
* If the classification step already produced a useful clarification string
|
||||
* (e.g. "Could you clarify — are you asking about scan status or invoice type?"),
|
||||
* we return that directly without a second API call.
|
||||
* Otherwise we hit Gemini again with a conversational system prompt.
|
||||
*/
|
||||
private function handleUnknown(
|
||||
array $chatHistory,
|
||||
string $userInput,
|
||||
?string $clarificationFromClassifier
|
||||
): string {
|
||||
$reply = $this->callGeminiConversational(
|
||||
$chatHistory,
|
||||
$userInput,
|
||||
$clarificationFromClassifier
|
||||
);
|
||||
|
||||
return $reply
|
||||
?? "I'm not sure I understood that. I can help you with:\n\n"
|
||||
. "• Invoice Status — scan progress of an invoice\n"
|
||||
. "• Invoice Report — serial vs material type for an item\n"
|
||||
. "• Production Report — unit count for a plant / line\n\n"
|
||||
. "What would you like to check?";
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,16 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Item;
|
||||
use App\Models\ItemCharacteristic;
|
||||
use App\Models\ProductionQuantity;
|
||||
use App\Models\StickerDetail;
|
||||
use App\Models\StickerMappingMaster;
|
||||
use App\Models\StickerStructureDetail;
|
||||
use App\Models\StickerValidation;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use TCPDF;
|
||||
|
||||
@@ -447,11 +452,10 @@ class StickerPdfService
|
||||
}
|
||||
|
||||
// return $pdf->Output('sticker.pdf', 'S');
|
||||
$pdfContent = $pdf->Output('sticker.pdf', 'S');
|
||||
$pdfContent = $pdf->Output('', 'S'); // 'S' returns string
|
||||
|
||||
return (new Response($pdfContent, 200))
|
||||
->header('Content-Type', 'application/pdf')
|
||||
->header('Content-Disposition', 'inline; filename="sticker.pdf"');
|
||||
// Encode as base64
|
||||
return base64_encode($pdfContent);
|
||||
|
||||
}
|
||||
|
||||
@@ -788,6 +792,349 @@ class StickerPdfService
|
||||
// ->header('Content-Disposition', 'inline; filename="sticker.pdf"');
|
||||
}
|
||||
|
||||
public function generatePdf1(string $stickerId, Collection $dynamicElements, ?ItemCharacteristic $itemCharacteristic, ?string $serialNumber, $serNo)
|
||||
{
|
||||
|
||||
$dynamicValueMap = [];
|
||||
$itemCode = $itemCharacteristic?->item?->code ?? '';
|
||||
|
||||
foreach ($dynamicElements as $element) {
|
||||
|
||||
$column = $element->characteristics_type;
|
||||
|
||||
$value = '';
|
||||
|
||||
if (
|
||||
$itemCharacteristic &&
|
||||
$column &&
|
||||
Schema::hasColumn('item_characteristics', $column)
|
||||
) {
|
||||
$value = $itemCharacteristic->{$column};
|
||||
}
|
||||
|
||||
$dynamicValueMap[$element->id] = [
|
||||
'design_type' => $element->design_element_type, // Text / Image
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
$structure = StickerStructureDetail::where('sticker_id', $stickerId)
|
||||
->first();
|
||||
|
||||
$structureId = $structure->id;
|
||||
$elements = StickerDetail::where(
|
||||
'sticker_structure_detail_id',
|
||||
$structureId
|
||||
)->get();
|
||||
|
||||
$width = (float) $structure->sticker_width;
|
||||
$height = (float) $structure->sticker_height;
|
||||
|
||||
$pdf = new TCPDF('P', 'mm', [$width, $height], true, 'UTF-8', false);
|
||||
|
||||
$pdf->setPrintHeader(false);
|
||||
$pdf->setPrintFooter(false);
|
||||
|
||||
$pdf->SetMargins(
|
||||
(float) $structure->sticker_lmargin,
|
||||
(float) $structure->sticker_tmargin,
|
||||
(float) $structure->sticker_rmargin,
|
||||
(float) $structure->sticker_bmargin,
|
||||
);
|
||||
$pdf->SetAutoPageBreak(false, 0);
|
||||
|
||||
$pdf->AddPage();
|
||||
|
||||
// if (!empty($serialNumber)) {
|
||||
// $pdf->SetFont('helvetica', 'B', 10);
|
||||
// $pdf->SetTextColor(0, 0, 0);
|
||||
|
||||
// // HARD-CODED POSITION (mm)
|
||||
// $x = 40; // change as needed
|
||||
// $y = 60; // change as needed
|
||||
|
||||
// $pdf->Text($x, $y, (string) $serialNumber);
|
||||
// }
|
||||
|
||||
$pdf->SetFont('helvetica', 'B', 10);
|
||||
|
||||
foreach ($elements as $row) {
|
||||
|
||||
switch ($row->design_element_type) {
|
||||
|
||||
case 'Text':
|
||||
|
||||
$pdf->SetFont(
|
||||
$row->string_font ?? 'helvetica',
|
||||
'',
|
||||
(int) ($row->string_size ?? 10)
|
||||
);
|
||||
|
||||
$pdf->SetTextColor(
|
||||
...$this->hexToRgb($row->element_colour ?? '#000000')
|
||||
);
|
||||
|
||||
$textValue = $row->string_value ?? '';
|
||||
|
||||
if (
|
||||
$row->element_type == 'Dynamic' &&
|
||||
isset($dynamicValueMap[$row->id])
|
||||
) {
|
||||
$textValue = $dynamicValueMap[$row->id]['value'];
|
||||
}
|
||||
|
||||
$pdf->Text(
|
||||
(float) ($row->string_x_value ?? 0),
|
||||
(float) ($row->string_y_value ?? 0),
|
||||
(string) $textValue
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case 'QR':
|
||||
if (
|
||||
($row->element_type) == 'Dynamic'
|
||||
) {
|
||||
$qrContent = $serNo ?? '';
|
||||
$pdf->write2DBarcode(
|
||||
$qrContent,
|
||||
'QRCODE,H',
|
||||
(float) ($row->qr_x_value ?? 0),
|
||||
(float) ($row->qr_y_value ?? 0),
|
||||
(float) ($row->qr_size ?? 10),
|
||||
(float) ($row->qr_size ?? 10)
|
||||
);
|
||||
break;
|
||||
}
|
||||
else{
|
||||
$pdf->write2DBarcode(
|
||||
$row->qr_value ?? '',
|
||||
'QRCODE,H',
|
||||
(float) ($row->qr_x_value ?? 0),
|
||||
(float) ($row->qr_y_value ?? 0),
|
||||
(float) ($row->qr_size ?? 10),
|
||||
(float) ($row->qr_size ?? 10)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 'Image':
|
||||
|
||||
$imagePath = null;
|
||||
|
||||
if (
|
||||
$row->element_type == 'Dynamic' &&
|
||||
isset($dynamicValueMap[$row->id]) &&
|
||||
! empty($dynamicValueMap[$row->id]['value'])
|
||||
) {
|
||||
$imageName = strtolower($dynamicValueMap[$row->id]['value']).'.png';
|
||||
|
||||
$imagePath = public_path('images/'.ltrim($imageName, '/'));
|
||||
}
|
||||
|
||||
$pdf->Image(
|
||||
$imagePath,
|
||||
(float) ($row->image_x ?? 0),
|
||||
(float) ($row->image_y ?? 0),
|
||||
(float) ($row->image_width ?? 0),
|
||||
(float) ($row->image_height ?? 0)
|
||||
);
|
||||
break;
|
||||
case 'Shape':
|
||||
if ($row->shape_name == 'Line') {
|
||||
$pdf->SetLineWidth((float) ($row->shape_pen_size ?? 0.3));
|
||||
|
||||
if (isset($row->element_colour)) {
|
||||
$rgb = $this->hexToRgb($row->element_colour);
|
||||
$pdf->SetDrawColor($rgb[0], $rgb[1], $rgb[2]);
|
||||
} else {
|
||||
$pdf->SetDrawColor(0, 0, 0);
|
||||
}
|
||||
|
||||
$pdf->Line(
|
||||
(float) $row->shape_x1_value,
|
||||
(float) $row->shape_y1_value,
|
||||
(float) $row->shape_x2_value,
|
||||
(float) $row->shape_y2_value
|
||||
);
|
||||
} elseif ($row->shape_name == 'Rectangle') {
|
||||
$pdf->SetLineWidth((float) ($row->shape_pen_size ?? 0.3));
|
||||
|
||||
if (isset($row->element_colour)) {
|
||||
$rgb = $this->hexToRgb($row->element_colour);
|
||||
$pdf->SetDrawColor($rgb[0], $rgb[1], $rgb[2]);
|
||||
} else {
|
||||
$pdf->SetDrawColor(0, 0, 0);
|
||||
}
|
||||
|
||||
$x1 = (float) $row->shape_x1_value;
|
||||
$y1 = (float) $row->shape_y1_value;
|
||||
$x2 = (float) $row->shape_x2_value;
|
||||
$y2 = (float) $row->shape_y2_value;
|
||||
|
||||
$x = min($x1, $x2);
|
||||
$y = min($y1, $y2);
|
||||
$width = abs($x2 - $x1);
|
||||
$height = abs($y2 - $y1);
|
||||
|
||||
$pdf->Rect($x, $y, $width, $height, 'D');
|
||||
} elseif ($row->shape_name == 'CurvedRectangle') {
|
||||
$pdf->SetLineWidth((float) ($row->shape_pen_size ?? 0.3));
|
||||
|
||||
if (isset($row->element_colour)) {
|
||||
$rgb = $this->hexToRgb($row->element_colour);
|
||||
$pdf->SetDrawColor($rgb[0], $rgb[1], $rgb[2]);
|
||||
} else {
|
||||
$pdf->SetDrawColor(0, 0, 0);
|
||||
}
|
||||
|
||||
$x1 = (float) $row->shape_x1_value;
|
||||
$y1 = (float) $row->shape_y1_value;
|
||||
$x2 = (float) $row->shape_x2_value;
|
||||
$y2 = (float) $row->shape_y2_value;
|
||||
|
||||
$x = min($x1, $x2);
|
||||
$y = min($y1, $y2);
|
||||
$width = abs($x2 - $x1);
|
||||
$height = abs($y2 - $y1);
|
||||
|
||||
// radius in mm
|
||||
// $radius = 3;
|
||||
$radius = (float) $row->curve_radius;
|
||||
|
||||
$pdf->RoundedRect(
|
||||
$x,
|
||||
$y,
|
||||
$width,
|
||||
$height,
|
||||
$radius,
|
||||
'1111', // ← round all 4 corners (default)
|
||||
'D'
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// $pdfContent = $pdf->Output('', 'S'); // 'S' returns string
|
||||
|
||||
// // Encode as base64
|
||||
// return base64_encode($pdfContent);
|
||||
|
||||
|
||||
// $pdfContent = $pdf->Output('', 'S');
|
||||
|
||||
// return response($pdfContent)
|
||||
// ->header('Content-Type', 'application/pdf')
|
||||
// ->header('Content-Disposition', 'inline; filename="sticker.pdf"');
|
||||
$pdfContent = $pdf->Output('', 'S'); // 'S' returns the PDF as a string
|
||||
|
||||
// Return the PDF as a response
|
||||
try {
|
||||
$pdfContent = $pdf->Output('', 'S'); // 'S' returns the PDF as a string
|
||||
return response($pdfContent)
|
||||
->header('Content-Type', 'application/pdf')
|
||||
->header('Content-Disposition', 'inline; filename="sticker.pdf"');
|
||||
} catch (\Exception $e) {
|
||||
Log::error('PDF generation failed: '.$e->getMessage());
|
||||
abort(500, 'Failed to generate PDF');
|
||||
}
|
||||
}
|
||||
|
||||
public function generatePdfBySerial($item, $serNo, $plantId, $refNumber)
|
||||
{
|
||||
$recFound = ProductionQuantity::where('plant_id', $plantId)
|
||||
->where('production_order', $refNumber)
|
||||
->where('serial_number', $serNo)
|
||||
->first();
|
||||
|
||||
if (!$recFound) {
|
||||
abort(404, 'Serial not found');
|
||||
}
|
||||
|
||||
$duplicate = StickerValidation::where('plant_id', $plantId)
|
||||
->where('production_order', $refNumber)
|
||||
->where('serial_number', $serNo)
|
||||
->first();
|
||||
|
||||
$itemC = Item::where('code', $item)
|
||||
->where('plant_id', $plantId)
|
||||
->first();
|
||||
|
||||
if (!$itemC) {
|
||||
abort(404, 'Item not found');
|
||||
}
|
||||
|
||||
$item = ItemCharacteristic::where('item_id', $itemC->id)
|
||||
->where('plant_id', $plantId)
|
||||
->first();
|
||||
|
||||
if (!$item) {
|
||||
abort(404, 'Item characteristic not found');
|
||||
}
|
||||
|
||||
$mapping = StickerMappingMaster::where('plant_id', $plantId)
|
||||
->where('item_characteristic_id', $item->id)
|
||||
->first();
|
||||
|
||||
if (!$mapping) {
|
||||
abort(404, 'Sticker mapping not found');
|
||||
}
|
||||
|
||||
$structure = StickerStructureDetail::findOrFail($mapping->sticker_structure1_id);
|
||||
|
||||
$dynamicElements = StickerDetail::where(
|
||||
'sticker_structure_detail_id',
|
||||
$structure->id
|
||||
)->where('element_type', 'Dynamic')->get();
|
||||
|
||||
return $this->generatePdf1(
|
||||
$structure->sticker_id,
|
||||
$dynamicElements,
|
||||
$item,
|
||||
$serNo,
|
||||
$serNo
|
||||
);
|
||||
|
||||
// return response($pdf)
|
||||
// ->header('Content-Type', 'application/pdf')
|
||||
// ->header('Content-Disposition', 'inline; filename="sticker.pdf"');
|
||||
}
|
||||
|
||||
// public function printStickersToUSB(array $stickers, int $plantId, ?string $serialNumber)
|
||||
// {
|
||||
// $printerPort = 'USB001';
|
||||
|
||||
// foreach ($stickers as $sticker) {
|
||||
|
||||
// $dynamicElements = StickerDetail::where('sticker_structure_detail_id', $sticker['sticker_id'])
|
||||
// ->get();
|
||||
|
||||
// $itemCharacteristic = ItemCharacteristic::find(
|
||||
// $sticker['item_characteristic']
|
||||
// );
|
||||
|
||||
// $pdfContent = $this->generatePdf1(
|
||||
// $sticker['sticker_id'],
|
||||
// $dynamicElements,
|
||||
// $itemCharacteristic,
|
||||
// $serialNumber
|
||||
// );
|
||||
|
||||
// $handle = fopen("{$printerPort}:", "wb");
|
||||
|
||||
// if (! $handle) {
|
||||
// throw new \Exception("Cannot open printer port {$printerPort}");
|
||||
// }
|
||||
|
||||
// fwrite($handle, $pdfContent);
|
||||
// fclose($handle);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// private function hexToRgb($hex)
|
||||
// {
|
||||
// $hex = ltrim($hex, '#');
|
||||
@@ -797,6 +1144,7 @@ class StickerPdfService
|
||||
// hexdec(substr($hex, 4, 2)),
|
||||
// ];
|
||||
// }
|
||||
|
||||
private function hexToRgb($hex)
|
||||
{
|
||||
if (! is_string($hex) || ($hex = trim($hex)) === '') {
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
"althinect/filament-spatie-roles-permissions": "^2.3",
|
||||
"diogogpinto/filament-auth-ui-enhancer": "^1.0",
|
||||
"erag/laravel-pwa": "^1.9",
|
||||
"ffhs/filament-package_ffhs_approvals": "^1.0",
|
||||
"filament/filament": "^3.3",
|
||||
"intervention/image": "^3.11",
|
||||
"irazasyed/telegram-bot-sdk": "^3.15",
|
||||
"laravel-notification-channels/webpush": "^10.4",
|
||||
"laravel/framework": "^11.31",
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/tinker": "^2.9",
|
||||
|
||||
477
composer.lock
generated
477
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "69ede7e9877dd08efdeb795bfb6b7d29",
|
||||
"content-hash": "0e64d9b0a3c4d596ff8cd51b521c0565",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alperenersoy/filament-export",
|
||||
@@ -1810,6 +1810,85 @@
|
||||
},
|
||||
"time": "2025-10-17T16:34:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ffhs/filament-package_ffhs_approvals",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ffhs/filament-package_ffhs_approvals.git",
|
||||
"reference": "712475522b63bf45a9e63a649d391cfe22132818"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ffhs/filament-package_ffhs_approvals/zipball/712475522b63bf45a9e63a649d391cfe22132818",
|
||||
"reference": "712475522b63bf45a9e63a649d391cfe22132818",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"filament/filament": "^3.0",
|
||||
"php": "^8.2",
|
||||
"spatie/laravel-package-tools": "^1.15.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"larastan/larastan": "^3.0",
|
||||
"laravel/pint": "^1.0",
|
||||
"nunomaduro/collision": "^8.0",
|
||||
"orchestra/testbench": "^9.9",
|
||||
"pestphp/pest": "^3.7",
|
||||
"pestphp/pest-plugin-arch": "^3.0",
|
||||
"pestphp/pest-plugin-laravel": "^3.0",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"spatie/laravel-ray": "^1.26"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"Approvals": "Ffhs\\Approvals\\Facades\\Approvals"
|
||||
},
|
||||
"providers": [
|
||||
"Ffhs\\Approvals\\ApprovalsServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ffhs\\Approvals\\": "src/",
|
||||
"Ffhs\\Approvals\\Database\\Factories\\": "database/factories/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kromer Luc",
|
||||
"email": "luc.kromer@ffhs.ch",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Matthew Ballou",
|
||||
"email": "mballou@kirschbaumdevelopment.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "This is my package filament-package_ffhs_approvals",
|
||||
"homepage": "https://github.com/ffhs/filament-package_ffhs_approvals",
|
||||
"keywords": [
|
||||
"ffhs",
|
||||
"filament-package_ffhs_approvals",
|
||||
"kirschbaum-development",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ffhs/filament-package_ffhs_approvals/issues",
|
||||
"source": "https://github.com/ffhs/filament-package_ffhs_approvals"
|
||||
},
|
||||
"time": "2025-07-24T14:32:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/actions",
|
||||
"version": "v3.3.45",
|
||||
@@ -3076,6 +3155,72 @@
|
||||
},
|
||||
"time": "2025-11-13T14:57:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel-notification-channels/webpush",
|
||||
"version": "10.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel-notification-channels/webpush.git",
|
||||
"reference": "a504bcbdd6258091b1fafdef6ca95b0891a47c9e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel-notification-channels/webpush/zipball/a504bcbdd6258091b1fafdef6ca95b0891a47c9e",
|
||||
"reference": "a504bcbdd6258091b1fafdef6ca95b0891a47c9e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/notifications": "^11.0|^12.0",
|
||||
"illuminate/support": "^11.0|^12.0",
|
||||
"minishlink/web-push": "^10.0",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"larastan/larastan": "^3.1",
|
||||
"laravel/pint": "^1.25",
|
||||
"mockery/mockery": "^1.0",
|
||||
"orchestra/testbench": "^9.2|^10.0",
|
||||
"phpunit/phpunit": "^10.5|^11.5.3",
|
||||
"rector/rector": "^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"NotificationChannels\\WebPush\\WebPushServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"NotificationChannels\\WebPush\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Cretu Eusebiu",
|
||||
"email": "me@cretueusebiu.com",
|
||||
"homepage": "http://cretueusebiu.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Joost de Bruijn",
|
||||
"email": "joost@aqualabs.nl",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"description": "Web Push Notifications driver for Laravel.",
|
||||
"homepage": "https://github.com/laravel-notification-channels/webpush",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel-notification-channels/webpush/issues",
|
||||
"source": "https://github.com/laravel-notification-channels/webpush/tree/10.4.0"
|
||||
},
|
||||
"time": "2025-12-19T15:47:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v11.46.1",
|
||||
@@ -4880,6 +5025,73 @@
|
||||
},
|
||||
"time": "2019-10-05T02:44:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "minishlink/web-push",
|
||||
"version": "v10.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/web-push-libs/web-push-php.git",
|
||||
"reference": "08463189d3501cbd78a8625c87ab6680a7397aad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/web-push-libs/web-push-php/zipball/08463189d3501cbd78a8625c87ab6680a7397aad",
|
||||
"reference": "08463189d3501cbd78a8625c87ab6680a7397aad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"guzzlehttp/guzzle": "^7.9.2",
|
||||
"php": ">=8.2",
|
||||
"spomky-labs/base64url": "^2.0.4",
|
||||
"web-token/jwt-library": "^3.4.9|^4.0.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^v3.91.3",
|
||||
"phpstan/phpstan": "^2.1.33",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"phpunit/phpunit": "^11.5.46|^12.5.2",
|
||||
"symfony/polyfill-iconv": "^1.33"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "Optional for performance.",
|
||||
"ext-gmp": "Optional for performance."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Minishlink\\WebPush\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Louis Lagrange",
|
||||
"email": "lagrange.louis@gmail.com",
|
||||
"homepage": "https://github.com/Minishlink"
|
||||
}
|
||||
],
|
||||
"description": "Web Push library for PHP",
|
||||
"homepage": "https://github.com/web-push-libs/web-push-php",
|
||||
"keywords": [
|
||||
"Push API",
|
||||
"WebPush",
|
||||
"notifications",
|
||||
"push",
|
||||
"web"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/web-push-libs/web-push-php/issues",
|
||||
"source": "https://github.com/web-push-libs/web-push-php/tree/v10.0.1"
|
||||
},
|
||||
"time": "2025-12-15T10:04:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "3.9.0",
|
||||
@@ -7696,6 +7908,180 @@
|
||||
],
|
||||
"time": "2025-09-24T06:40:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spomky-labs/base64url",
|
||||
"version": "v2.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Spomky-Labs/base64url.git",
|
||||
"reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Spomky-Labs/base64url/zipball/7752ce931ec285da4ed1f4c5aa27e45e097be61d",
|
||||
"reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "^0.11|^0.12",
|
||||
"phpstan/phpstan-beberlei-assert": "^0.11|^0.12",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.11|^0.12",
|
||||
"phpstan/phpstan-phpunit": "^0.11|^0.12",
|
||||
"phpstan/phpstan-strict-rules": "^0.11|^0.12"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Base64Url\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Florent Morselli",
|
||||
"homepage": "https://github.com/Spomky-Labs/base64url/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Base 64 URL Safe Encoding/Decoding PHP Library",
|
||||
"homepage": "https://github.com/Spomky-Labs/base64url",
|
||||
"keywords": [
|
||||
"base64",
|
||||
"rfc4648",
|
||||
"safe",
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Spomky-Labs/base64url/issues",
|
||||
"source": "https://github.com/Spomky-Labs/base64url/tree/v2.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Spomky",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/FlorentMorselli",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-03T09:10:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spomky-labs/pki-framework",
|
||||
"version": "1.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Spomky-Labs/pki-framework.git",
|
||||
"reference": "f0e9a548df4e3942886adc9b7830581a46334631"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/f0e9a548df4e3942886adc9b7830581a46334631",
|
||||
"reference": "f0e9a548df4e3942886adc9b7830581a46334631",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.10|^0.11|^0.12|^0.13|^0.14",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"ekino/phpstan-banned-code": "^1.0|^2.0|^3.0",
|
||||
"ext-gmp": "*",
|
||||
"ext-openssl": "*",
|
||||
"infection/infection": "^0.28|^0.29|^0.31",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.3",
|
||||
"phpstan/extension-installer": "^1.3|^2.0",
|
||||
"phpstan/phpstan": "^1.8|^2.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0|^2.0",
|
||||
"phpstan/phpstan-phpunit": "^1.1|^2.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.3|^2.0",
|
||||
"phpunit/phpunit": "^10.1|^11.0|^12.0",
|
||||
"rector/rector": "^1.0|^2.0",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"symfony/string": "^6.4|^7.0|^8.0",
|
||||
"symfony/var-dumper": "^6.4|^7.0|^8.0",
|
||||
"symplify/easy-coding-standard": "^12.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "For better performance (or GMP)",
|
||||
"ext-gmp": "For better performance (or BCMath)",
|
||||
"ext-openssl": "For OpenSSL based cyphering"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SpomkyLabs\\Pki\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Joni Eskelinen",
|
||||
"email": "jonieske@gmail.com",
|
||||
"role": "Original developer"
|
||||
},
|
||||
{
|
||||
"name": "Florent Morselli",
|
||||
"email": "florent.morselli@spomky-labs.com",
|
||||
"role": "Spomky-Labs PKI Framework developer"
|
||||
}
|
||||
],
|
||||
"description": "A PHP framework for managing Public Key Infrastructures. It comprises X.509 public key certificates, attribute certificates, certification requests and certification path validation.",
|
||||
"homepage": "https://github.com/spomky-labs/pki-framework",
|
||||
"keywords": [
|
||||
"DER",
|
||||
"Private Key",
|
||||
"ac",
|
||||
"algorithm identifier",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"attribute certificate",
|
||||
"certificate",
|
||||
"certification request",
|
||||
"cryptography",
|
||||
"csr",
|
||||
"decrypt",
|
||||
"ec",
|
||||
"encrypt",
|
||||
"pem",
|
||||
"pkcs",
|
||||
"public key",
|
||||
"rsa",
|
||||
"sign",
|
||||
"signature",
|
||||
"verify",
|
||||
"x.509",
|
||||
"x.690",
|
||||
"x509",
|
||||
"x690"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Spomky-Labs/pki-framework/issues",
|
||||
"source": "https://github.com/Spomky-Labs/pki-framework/tree/1.4.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Spomky",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/FlorentMorselli",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-20T12:57:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.3.0",
|
||||
@@ -10504,6 +10890,95 @@
|
||||
}
|
||||
],
|
||||
"time": "2024-11-21T01:49:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "web-token/jwt-library",
|
||||
"version": "4.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/web-token/jwt-library.git",
|
||||
"reference": "690d4dd47b78f423cb90457f858e4106e1deb728"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/web-token/jwt-library/zipball/690d4dd47b78f423cb90457f858e4106e1deb728",
|
||||
"reference": "690d4dd47b78f423cb90457f858e4106e1deb728",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.12|^0.13|^0.14",
|
||||
"php": ">=8.2",
|
||||
"psr/clock": "^1.0",
|
||||
"spomky-labs/pki-framework": "^1.2.1"
|
||||
},
|
||||
"conflict": {
|
||||
"spomky-labs/jose": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "GMP or BCMath is highly recommended to improve the library performance",
|
||||
"ext-gmp": "GMP or BCMath is highly recommended to improve the library performance",
|
||||
"ext-openssl": "For key management (creation, optimization, etc.) and some algorithms (AES, RSA, ECDSA, etc.)",
|
||||
"ext-sodium": "Sodium is required for OKP key creation, EdDSA signature algorithm and ECDH-ES key encryption with OKP keys",
|
||||
"paragonie/sodium_compat": "Sodium is required for OKP key creation, EdDSA signature algorithm and ECDH-ES key encryption with OKP keys",
|
||||
"spomky-labs/aes-key-wrap": "For all Key Wrapping algorithms (AxxxKW, AxxxGCMKW, PBES2-HSxxx+AyyyKW...)",
|
||||
"symfony/console": "Needed to use console commands",
|
||||
"symfony/http-client": "To enable JKU/X5U support."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jose\\Component\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Florent Morselli",
|
||||
"homepage": "https://github.com/Spomky"
|
||||
},
|
||||
{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/web-token/jwt-framework/contributors"
|
||||
}
|
||||
],
|
||||
"description": "JWT library",
|
||||
"homepage": "https://github.com/web-token",
|
||||
"keywords": [
|
||||
"JOSE",
|
||||
"JWE",
|
||||
"JWK",
|
||||
"JWKSet",
|
||||
"JWS",
|
||||
"Jot",
|
||||
"RFC7515",
|
||||
"RFC7516",
|
||||
"RFC7517",
|
||||
"RFC7518",
|
||||
"RFC7519",
|
||||
"RFC7520",
|
||||
"bundle",
|
||||
"jwa",
|
||||
"jwt",
|
||||
"symfony"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/web-token/jwt-library/issues",
|
||||
"source": "https://github.com/web-token/jwt-library/tree/4.1.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Spomky",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/FlorentMorselli",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-18T14:27:35+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
||||
@@ -18,6 +18,11 @@ return [
|
||||
'token' => env('POSTMARK_TOKEN'),
|
||||
],
|
||||
|
||||
'gemini' => [
|
||||
'api_key' => env('GEMINI_API_KEY'),
|
||||
'model' => env('GEMINI_MODEL', 'gemini-3-flash-preview'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
CREATE TABLE sticker_mapping_masters (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
plant_id BIGINT NOT NULL,
|
||||
item_characteristic_id BIGINT DEFAULT NULL,
|
||||
sticker_structure1_id BIGINT DEFAULT NULL,
|
||||
sticker1_machine_id BIGINT DEFAULT NULL,
|
||||
sticker1_print_ip TEXT DEFAULT NULL,
|
||||
|
||||
sticker_structure2_id BIGINT DEFAULT NULL,
|
||||
sticker2_machine_id BIGINT DEFAULT NULL,
|
||||
sticker2_print_ip TEXT DEFAULT NULL,
|
||||
|
||||
sticker_structure3_id BIGINT DEFAULT NULL,
|
||||
sticker3_machine_id BIGINT DEFAULT NULL,
|
||||
sticker3_print_ip TEXT DEFAULT NULL,
|
||||
|
||||
sticker_structure4_id BIGINT DEFAULT NULL,
|
||||
sticker4_machine_id BIGINT DEFAULT NULL,
|
||||
sticker4_print_ip TEXT DEFAULT NULL,
|
||||
|
||||
sticker_structure5_id BIGINT DEFAULT NULL,
|
||||
sticker5_machine_id BIGINT DEFAULT NULL,
|
||||
sticker5_print_ip TEXT DEFAULT NULL,
|
||||
|
||||
sticker_structure6_id BIGINT DEFAULT NULL,
|
||||
sticker6_machine_id BIGINT DEFAULT NULL,
|
||||
sticker6_print_ip TEXT DEFAULT NULL,
|
||||
|
||||
sticker_structure7_id BIGINT DEFAULT NULL,
|
||||
sticker7_machine_id BIGINT DEFAULT NULL,
|
||||
sticker7_print_ip TEXT DEFAULT NULL,
|
||||
|
||||
sticker_structure8_id BIGINT DEFAULT NULL,
|
||||
sticker8_machine_id BIGINT DEFAULT NULL,
|
||||
sticker8_print_ip TEXT DEFAULT NULL,
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT DEFAULT NULL,
|
||||
updated_by TEXT DEFAULT NULL,
|
||||
deleted_at TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (plant_id) REFERENCES plants(id),
|
||||
FOREIGN KEY (item_characteristic_id) REFERENCES item_characteristics(id),
|
||||
|
||||
FOREIGN KEY (sticker_structure1_id) REFERENCES sticker_structure_details(id),
|
||||
FOREIGN KEY (sticker_structure2_id) REFERENCES sticker_structure_details(id),
|
||||
FOREIGN KEY (sticker_structure3_id) REFERENCES sticker_structure_details(id),
|
||||
FOREIGN KEY (sticker_structure4_id) REFERENCES sticker_structure_details(id),
|
||||
FOREIGN KEY (sticker_structure5_id) REFERENCES sticker_structure_details(id),
|
||||
FOREIGN KEY (sticker_structure6_id) REFERENCES sticker_structure_details(id),
|
||||
FOREIGN KEY (sticker_structure7_id) REFERENCES sticker_structure_details(id),
|
||||
FOREIGN KEY (sticker_structure8_id) REFERENCES sticker_structure_details(id),
|
||||
|
||||
FOREIGN KEY (sticker1_machine_id) REFERENCES machines(id),
|
||||
FOREIGN KEY (sticker2_machine_id) REFERENCES machines(id),
|
||||
FOREIGN KEY (sticker3_machine_id) REFERENCES machines(id),
|
||||
FOREIGN KEY (sticker4_machine_id) REFERENCES machines(id),
|
||||
FOREIGN KEY (sticker5_machine_id) REFERENCES machines(id),
|
||||
FOREIGN KEY (sticker6_machine_id) REFERENCES machines(id),
|
||||
FOREIGN KEY (sticker7_machine_id) REFERENCES machines(id),
|
||||
FOREIGN KEY (sticker8_machine_id) REFERENCES machines(id)
|
||||
);
|
||||
SQL;
|
||||
|
||||
DB::statement($sql);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sticker_mapping_masters');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
CREATE TABLE sticker_validations (
|
||||
id BIGINT GENERATED always AS IDENTITY PRIMARY KEY,
|
||||
plant_id BIGINT NOT NULL,
|
||||
machine_id BIGINT NOT NULL,
|
||||
production_order TEXT DEFAULT NULL,
|
||||
serial_number TEXT DEFAULT NULL,
|
||||
status TEXT DEFAULT NULL,
|
||||
sticker_id TEXT DEFAULT NULL
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT DEFAULT NULL,
|
||||
updated_by TEXT DEFAULT NULL,
|
||||
deleted_at TIMESTAMP,
|
||||
FOREIGN KEY (plant_id) REFERENCES plants (id),
|
||||
FOREIGN KEY (machine_id) REFERENCES machines (id)
|
||||
|
||||
);
|
||||
SQL;
|
||||
DB::statement($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sticker_validations');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
CREATE TABLE spot_rate_transport_masters (
|
||||
id BIGINT GENERATED always AS IDENTITY PRIMARY KEY,
|
||||
|
||||
group_name TEXT NOT NULL,
|
||||
user_name TEXT DEFAULT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT DEFAULT NULL,
|
||||
updated_by TEXT DEFAULT NULL,
|
||||
deleted_at TIMESTAMP
|
||||
|
||||
);
|
||||
SQL;
|
||||
DB::statement($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('spot_rate_transport_masters');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
CREATE TABLE request_quotations (
|
||||
id BIGINT GENERATED always AS IDENTITY PRIMARY KEY,
|
||||
|
||||
spot_rate_transport_master_id BIGINT NOT NULL,
|
||||
rfq_number TEXT NOT NULL,
|
||||
rfq_date_time TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
pickup_address TEXT NOT NULL,
|
||||
delivery_address TEXT NOT NULL,
|
||||
weight TEXT NOT NULL,
|
||||
volumetrice_size_inch TEXT NOT NULL,
|
||||
type_of_vehicle TEXT NOT NULL,
|
||||
special_type TEXT NOT NULL,
|
||||
no_of_vehicle TEXT NOT NULL,
|
||||
product_name TEXT NOT NULL,
|
||||
loading_by TEXT NOT NULL,
|
||||
unloading_by TEXT NOT NULL,
|
||||
pick_and_delivery TEXT NOT NULL,
|
||||
payment_term TEXT NOT NULL,
|
||||
paid_topay TEXT NOT NULL,
|
||||
require_date_time TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
rfq_rec_on_or_before TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
transporter_name TEXT NULL,
|
||||
total_freight_charge TEXT NULL,
|
||||
transit_day TEXT NULL,
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT DEFAULT NULL,
|
||||
updated_by TEXT DEFAULT NULL,
|
||||
deleted_at TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (spot_rate_transport_master_id) REFERENCES spot_rate_transport_masters (id)
|
||||
|
||||
);
|
||||
SQL;
|
||||
DB::statement($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('request_quotations');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
CREATE TABLE rfq_transporter_bids (
|
||||
id BIGINT GENERATED always AS IDENTITY PRIMARY KEY,
|
||||
|
||||
request_quotation_id BIGINT NOT NULL,
|
||||
|
||||
transporter_name TEXT NULL,
|
||||
total_freight_charge TEXT NULL,
|
||||
transit_day TEXT NULL,
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT DEFAULT NULL,
|
||||
updated_by TEXT DEFAULT NULL,
|
||||
deleted_at TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (request_quotation_id) REFERENCES request_quotations(id)
|
||||
|
||||
);
|
||||
SQL;
|
||||
DB::statement($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('rfq_transporter_bids');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
ALTER TABLE production_plans
|
||||
ADD COLUMN item_id BIGINT DEFAULT NULL,
|
||||
ADD CONSTRAINT production_plans_item_id_fkey
|
||||
FOREIGN KEY (item_id) REFERENCES items(id);
|
||||
SQL;
|
||||
|
||||
DB::statement($sql);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Schema::table('production_plans', function (Blueprint $table) {
|
||||
// //
|
||||
// });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
ALTER TABLE lines
|
||||
ADD COLUMN block_id BIGINT DEFAULT NULL,
|
||||
ADD CONSTRAINT lines_block_id_fkey
|
||||
FOREIGN KEY (block_id) REFERENCES blocks(id);
|
||||
SQL;
|
||||
|
||||
DB::statement($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Schema::table('lines', function (Blueprint $table) {
|
||||
// //
|
||||
// });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql1 = <<<'SQL'
|
||||
ALTER TABLE items
|
||||
ADD COLUMN line_id BIGINT DEFAULT NULL,
|
||||
ADD CONSTRAINT items_line_id_fkey
|
||||
FOREIGN KEY (line_id) REFERENCES lines(id);
|
||||
SQL;
|
||||
|
||||
DB::statement($sql1);
|
||||
|
||||
$sql2 = <<<'SQL'
|
||||
ALTER TABLE items
|
||||
ADD COLUMN line_capacity TEXT DEFAULT NULL
|
||||
SQL;
|
||||
|
||||
DB::statement($sql2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Schema::table('items', function (Blueprint $table) {
|
||||
// //
|
||||
// });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
ALTER TABLE production_quantities
|
||||
ADD COLUMN machine_id BIGINT DEFAULT NULL,
|
||||
ADD CONSTRAINT production_quantities_machine_id_fkey
|
||||
FOREIGN KEY (machine_id) REFERENCES machines(id);
|
||||
SQL;
|
||||
|
||||
DB::statement($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Schema::table('production_quantities', function (Blueprint $table) {
|
||||
// //
|
||||
// });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql1 = <<<'SQL'
|
||||
ALTER TABLE production_plans
|
||||
ADD COLUMN working_days TEXT DEFAULT NULL
|
||||
SQL;
|
||||
|
||||
DB::statement($sql1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Schema::table('production_plans', function (Blueprint $table) {
|
||||
// //
|
||||
// });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$sql1 = <<<'SQL'
|
||||
ALTER TABLE production_plans
|
||||
ADD COLUMN leave_dates TEXT DEFAULT NULL
|
||||
SQL;
|
||||
|
||||
DB::statement($sql1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Schema::table('production_plans', function (Blueprint $table) {
|
||||
// //
|
||||
// });
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user