diff --git a/app/Filament/Resources/LocatorInvoiceValidationResource.php b/app/Filament/Resources/LocatorInvoiceValidationResource.php
new file mode 100644
index 0000000..daba71c
--- /dev/null
+++ b/app/Filament/Resources/LocatorInvoiceValidationResource.php
@@ -0,0 +1,697 @@
+schema([
+ Section::make('')
+ ->schema([
+ Forms\Components\Select::make('plant_id')
+ ->label('Plant')
+ ->reactive()
+ ->relationship('plant', 'name')
+ ->disabled(fn (Get $get) => $get('invoice_number'))
+ ->required()
+ ->afterStateUpdated(function ($state, callable $set, callable $get) {
+ $plantId = $get('plant_id');
+ if ($plantId)
+ {
+ $set('plant', $plantId);
+ $set('invoice_number', null);
+ $set('pallet_number', null);
+ $set('serial_number', null);
+ $set('sno_quantity', null);
+ }
+ else
+ {
+ $set('plant', null);
+ $set('invoice_number', null);
+ $set('pallet_number', null);
+ $set('serial_number', null);
+ $set('sno_quantity', null);
+ }
+ }),
+
+ Forms\Components\Hidden::make('plant')
+ ->reactive(),
+
+ Forms\Components\TextInput::make('invoice_number')
+ ->label('Scan Invoice No')
+ ->required()
+ ->reactive()
+ ->extraAttributes([
+ 'x-data' => '{ value: "" }',
+ 'x-model' => 'value',
+ 'x-on:keydown.enter.prevent' => '$wire.processinvoiceSNo()',
+ ])
+ ->readOnly(fn (callable $get) => !$get('plant') || $get('pallet_number') || $get('serial_number'))
+ ->afterStateUpdated(function ($state, callable $set, callable $get) {
+ $plantId = $get('plant');
+ if (!$plantId) {
+ $set('invoice_number', null);
+ $set('pallet_number', null);
+
+ }
+ $set('update_invoice', null);
+ $set('pallet_number', null);
+ $set('serial_number', null);
+ }),
+ Forms\Components\TextInput::make('pallet_number')
+ ->label('Scan Pallet No')
+ ->reactive()
+ // ->readOnly(fn (callable $get) => !$get('plant') || !$get('invoice_number') || $get('serial_number'))
+ ->readOnly(function (callable $get) {
+ $invoiceNumber = $get('invoice_number');
+ $plantId = $get('plant');
+ $invoiceExist = false;
+
+ if (!empty($invoiceNumber) && !empty($plantId)) {
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->get();
+
+ $allScanned = true;
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && !$allScanned) {
+ $invoiceExist = true;
+ }
+ }
+
+ return $invoiceExist == false || !$get('plant') || !$get('invoice_number') || $get('serial_number');
+ })
+ ->extraAttributes([
+ 'wire:keydown.enter.prevent' => 'processPalletNo()',
+ ])
+ ->afterStateUpdated(function ($state, callable $set, callable $get) {
+ $plantId = $get('plant');
+ if (!$plantId) {
+ $set('invoice_number', null);
+ $set('pallet_number', null);
+ }
+ $set('update_locator_invoice', null);
+ }),
+ Forms\Components\TextInput::make('serial_number')
+ ->label('Scan Serial No')
+ ->reactive()
+ // ->readOnly(fn (callable $get) => !$get('plant') || !$get('invoice_number') || $get('pallet_number'))
+ ->readOnly(function (callable $get) {
+ $invoiceNumber = $get('invoice_number');
+ $plantId = $get('plant');
+ $invoiceExist = false;
+
+ if (!empty($invoiceNumber) && !empty($plantId)) {
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->get();
+
+ $allScanned = true;
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && !$allScanned) {
+ $invoiceExist = true;
+ }
+ }
+
+ return $invoiceExist == false || !$get('plant') || !$get('invoice_number') || $get('pallet_number');
+ })
+ ->extraAttributes([
+ 'x-data' => '{ value: "" }',
+ 'x-model' => 'value',
+ 'x-on:keydown.enter.prevent' => '$wire.processSerialNo()',
+ ]),
+ Forms\Components\TextInput::make('sno_quantity')
+ ->label('SNo. Quantity')
+ ->readOnly(),
+
+ //Forms\Components\View::make('forms.components.update-invoice-button'),
+
+ ToggleButtons::make('update_invoice')
+ ->label('Update Invoice?')
+ ->boolean()
+ ->grouped()
+ ->reactive()
+ ->hidden(function (callable $get) {
+ $invoiceNumber = $get('invoice_number');
+ $plantId = $get('plant');
+ $invoiceExist = false;
+
+ if (!empty($invoiceNumber) && !empty($plantId)) {
+
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->get();
+
+ $allScanned = true;
+
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && !$allScanned) {
+ $invoiceExist = true;
+ }
+ }
+
+ return $get('update_invoice') == '0' || $invoiceExist == false || !empty($get('pallet_number')) || !empty($get('serial_number')); //$get('invoice_number') == null
+ }),
+
+ Forms\Components\TextInput::make('id')
+ ->hidden()
+ ->readOnly(),
+
+ ToggleButtons::make('update_locator_invoice')
+ ->label('Update Partial Pallet?')
+ ->boolean()
+ ->grouped()
+ ->reactive()
+ ->hidden(function (callable $get) {
+ $palletNumber = $get('pallet_number');
+ $plantId = $get('plant');
+ $isPalletValid = false;
+
+ if (!empty($palletNumber) && !empty($plantId)) {
+ $pallet = PalletValidation::where('plant_id', $plantId)
+ ->where('pallet_number', $palletNumber)
+ ->first();
+ $isPalletValid = $pallet !== null;
+ }
+
+ return !$isPalletValid || $get('update_locator_invoice') == '0' || !empty($get('serial_number'));
+ })
+ ->afterStateUpdated(function ($state, callable $set, callable $get, $livewire) {
+ $plantId = $get('plant');
+
+ $palletNumber = $get('pallet_number');
+
+ $serialNumber = $get('serial_number');
+
+ $invoiceNumber = $get('invoice_number');
+
+ $operatorName = Filament::auth()->user()->name;
+
+ if ($state !== '1') {
+ $set('update_locator_invoice', '0');
+ $set('pallet_number', null);
+ return;
+ }
+
+ $palletRecord = PalletValidation::where('plant_id', $plantId)
+ ->where('pallet_number', $palletNumber)
+ ->first();
+
+ if (!$palletRecord)
+ {
+ Notification::make()
+ ->title("Pallet number '{$palletNumber}' does not exist.")
+ ->danger()
+ ->send();
+ return;
+ }
+
+ $palletRecords = PalletValidation::where('plant_id', $plantId)
+ ->where('pallet_number', $palletNumber)
+ ->get();
+
+ $allCompleted = true;
+ foreach ($palletRecords as $record) {
+ if ($record->pallet_status != 'Completed') {
+ $allCompleted = false;
+ break;
+ }
+ }
+
+ if (!$allCompleted)
+ {
+ Notification::make()
+ ->title("Pallet number '{$palletNumber}' is not completed in masters")
+ ->danger()
+ ->send();
+ return;
+ }
+
+ // if ($allCompleted)
+ // {
+ // $serialNumbers = $palletRecords->pluck('serial_number')
+ // ->map(function ($serial) {
+ // return trim($serial);
+ // })
+ // ->all();
+
+ // $InvoiceSerialNumbers = LocatorInvoiceValidation::where('plant_id', $plantId)
+ // ->where('invoice_number', $invoiceNumber)
+ // ->pluck('serial_number')
+ // ->all();
+
+ // $missingSerialNumbers = array_diff($InvoiceSerialNumbers, $serialNumbers);
+
+ // $allmatchedSerialNumbers = array_unique(array_merge($InvoiceSerialNumbers, $serialNumbers));
+
+ // if (!empty($missingSerialNumbers))
+ // {
+
+ // $matchedSerialNumbers = array_diff($serialNumbers, $missingSerialNumbers);
+
+ // foreach ($matchedSerialNumbers as $serial)
+ // {
+ // $invoiceRecord = LocatorInvoiceValidation::where('plant_id', $plantId)
+ // ->where('invoice_number', $invoiceNumber)
+ // ->where('serial_number', $serial)
+ // ->first();
+
+ // if ($invoiceRecord)
+ // {
+ // $invoiceRecord->scanned_status = 'Scanned';
+ // $invoiceRecord->pallet_number = $palletNumber;
+ // $invoiceRecord->scanned_at = now();
+ // $invoiceRecord->scanned_by = $operatorName;
+ // $invoiceRecord->save();
+
+ // Notification::make()
+ // ->title("Pallet number '{$palletNumber}' scanned successfully")
+ // ->success()
+ // ->send();
+ // }
+
+ // PalletValidation::where('plant_id', $plantId)
+ // ->where('pallet_number', $palletNumber)
+ // ->where('serial_number', $serial)
+ // ->forceDelete();
+
+ // $livewire('loadData', $invoiceNumber, $plantId);
+ // }
+ // }
+ // else
+ // {
+ // foreach ($allmatchedSerialNumbers as $serial)
+ // {
+ // $invoiceRecord = LocatorInvoiceValidation::where('plant_id', $plantId)
+ // ->where('invoice_number', $invoiceNumber)
+ // ->where('serial_number', $serial)
+ // ->first();
+
+ // if ($invoiceRecord)
+ // {
+ // $invoiceRecord->scanned_status = 'Scanned';
+ // $invoiceRecord->pallet_number = $palletNumber;
+ // $invoiceRecord->scanned_at = now();
+ // $invoiceRecord->scanned_by = $operatorName;
+ // $invoiceRecord->save();
+
+ // Notification::make()
+ // ->title("Pallet number '{$palletNumber}' scanned successfully")
+ // ->success()
+ // ->send();
+ // }
+
+ // PalletValidation::where('plant_id', $plantId)
+ // ->where('pallet_number', $palletNumber)
+ // ->where('serial_number', $serial)
+ // ->forceDelete();
+
+ // $this->dispatch('loadData', $invoiceNumber, $plantId);
+ // }
+ // }
+ // }
+ })
+ ])
+ ->columns(5),
+
+ ]);
+ }
+
+ 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('plant.name')
+ ->label('Plant')
+ ->alignCenter()
+ ->searchable()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('invoice_number')
+ ->label('Invoice Number')
+ ->searchable()
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('serial_number')
+ ->label('Serial Number')
+ ->searchable()
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('pallet_number')
+ ->label('Pallet Number')
+ ->searchable()
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('locator_number')
+ ->label('Locator Number')
+ ->searchable()
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('scanned_status')
+ ->label('Scanned Status')
+ ->searchable()
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('upload_status')
+ ->label('Upload Status')
+ ->searchable()
+ ->alignCenter(),
+ Tables\Columns\TextColumn::make('created_by')
+ ->label('Created By')
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('created_at')
+ ->label('Created At')
+ ->dateTime()
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('updated_by')
+ ->label('Updated By')
+ ->alignCenter()
+ ->sortable()
+ ->toggleable(isToggledHiddenByDefault: true),
+ Tables\Columns\TextColumn::make('updated_at')
+ ->label('Updated At')
+ ->dateTime()
+ ->alignCenter()
+ ->sortable()
+ ->toggleable(isToggledHiddenByDefault: true),
+ Tables\Columns\TextColumn::make('scanned_by')
+ ->label('Scanned By')
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('scanned_at')
+ ->label('Scanned At')
+ ->dateTime()
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('deleted_at')
+ ->dateTime()
+ ->alignCenter()
+ ->sortable()
+ ->toggleable(isToggledHiddenByDefault: true),
+ ])
+
+ ->headerActions([
+ Tables\Actions\Action::make('import_invoice')
+ ->label('Import Invoice')
+ ->form([
+ Select::make('plant_id')
+ ->options(Plant::pluck('name', 'id')->toArray())
+ ->label('Select Plant')
+ ->required()
+ ->default(function () {
+ return optional(InvoiceValidation::latest()->first())->plant_id;
+ })
+ ->afterStateUpdated(function ($state, callable $set, callable $get) {
+ $set('invoice_serial_number', null);
+ })
+ ->reactive(),
+
+ FileUpload::make('invoice_serial_number')
+ ->label('Invoice Serial Number')
+ // ->required()
+ ->preserveFilenames() // <- this keeps the original filename
+ ->storeFiles(false)
+ ->reactive()
+ ->required()
+ ->disk('local')
+ ->visible(fn (Get $get) => !empty($get('plant_id')))
+ ->directory('uploads/temp'),
+ ])
+ ->action(function (array $data) {
+ $uploadedFile = $data['invoice_serial_number'];
+
+ $disk = Storage::disk('local');
+
+ $plantId = $data['plant_id'];
+
+ $originalName = $uploadedFile->getClientOriginalName(); // e.g. 3RA0018732.xlsx
+
+ $invoiceNumber = pathinfo($originalName, PATHINFO_FILENAME);
+ // $invoiceNumber = trim(str_replace('.xlsx', '', $originalName));
+ // $originalNameOnly = pathinfo($originalName, PATHINFO_FILENAME);
+
+ $folderPath = Configuration::where('c_name', 'INVOICE_FOLDER_PATH')
+ ->value('c_value');
+
+ $fullFolderPath = "uploads/$folderPath";
+
+ // Check if the folder exists, if not, create it
+ if (!Storage::disk('local')->exists($fullFolderPath))
+ {
+ Storage::disk('local')->makeDirectory($fullFolderPath);
+ }
+
+ $path = $uploadedFile->storeAs($fullFolderPath, $originalName, 'local');
+
+ $fullPath = Storage::disk('local')->path($path);
+
+ if ($fullPath && file_exists($fullPath))
+ {
+ $rows = Excel::toArray(null, $fullPath)[0];
+
+ if((count($rows) - 1) <= 0)
+ {
+ Notification::make()
+ ->title('Invalid Locator Invoice Found')
+ ->body('Uploaded excel sheet is empty or
contains no valid data.')
+ ->danger()
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $invalidSerialCodes=[];
+ $duplicateSerials = [];
+ $seenSerialNumbers = [];
+ $validRowsFound = false;
+
+ foreach ($rows as $index => $row)
+ {
+ if ($index === 0) continue; // Skip header
+
+ $serialNumber = trim($row[0]);
+
+ if (empty($serialNumber))
+ {
+ continue;
+ }
+ else
+ {
+ if(Str::length($serialNumber) < 13 || !ctype_alnum($serialNumber))
+ {
+ $invalidSerialCodes[] = $serialNumber;
+ }
+ else
+ {
+ if (in_array($serialNumber, $seenSerialNumbers)) {
+ $duplicateSerials[] = $serialNumber;
+ }
+ else
+ {
+ $seenSerialNumbers[] = $serialNumber;
+ $validRowsFound = true;
+ }
+ }
+ }
+ }
+
+ $uniqueSerialCodes = array_unique($invalidSerialCodes);
+
+ if (!empty($uniqueSerialCodes)) {
+ Notification::make()
+ ->title('Invalid Serial Numbers Found')
+ ->body('The following serial numbers should contain minimum 13 digit alpha numeric values:
' . implode(', ', $uniqueSerialCodes))
+ ->danger()
+ ->send();
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $duplicateSerialCodes = array_unique($duplicateSerials);
+
+ if (!empty($duplicateSerialCodes)) {
+ Notification::make()
+ ->title('Duplicate Serial Numbers Found')
+ ->body('The following serial numbers are already exist in imported excel:
' . implode(', ', $duplicateSerialCodes))
+ ->danger()
+ ->send();
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ if (!$validRowsFound) {
+ Notification::make()
+ ->title('Invalid Locator Invoice Found')
+ ->body('Uploaded excel sheet is empty or
contains no valid data.')
+ ->danger()
+ ->send();
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $serialsToCheck = $seenSerialNumbers;
+
+ $existingSerials = LocatorInvoiceValidation::whereIn('serial_number', $serialsToCheck)
+ ->where('invoice_number', '!=', $invoiceNumber)
+ ->where('plant_id', $plantId)
+ ->pluck('serial_number')
+ ->toArray();
+
+ if (!empty($existingSerials)) {
+ Notification::make()
+ ->title('Duplicate Serial Numbers Found')
+ ->body('The following serial numbers already exist with a different invoice number:
' . implode(', ', $existingSerials))
+ ->danger()
+ ->send();
+ if ($disk->exists($path))
+ {
+ $disk->delete($path);
+ }
+ return;
+ }
+ else
+ {
+ // Save full file path to session
+ session(['uploaded_invoice_path' => $fullPath]);
+ Notification::make()
+ ->title('Success: Invoice imported successfully.')
+ ->success()
+ ->send();
+ }
+ }
+ })
+ ->visible(function() {
+ return Filament::auth()->user()->can('view import dispatch serial invoice validation');
+ }),
+
+ ImportAction::make()
+ ->importer(LocatorInvoiceValidationImporter::class)
+ ->visible(function() {
+ return Filament::auth()->user()->can('view import locator invoice validation');
+ }),
+ ExportAction::make()
+ ->exporter(LocatorInvoiceValidationExporter::class)
+ ->visible(function() {
+ return Filament::auth()->user()->can('view export locator invoice validation');
+ }),
+ ])
+
+ ->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\ListLocatorInvoiceValidations::route('/'),
+ 'create' => Pages\CreateLocatorInvoiceValidation::route('/create'),
+ 'view' => Pages\ViewLocatorInvoiceValidation::route('/{record}'),
+ 'edit' => Pages\EditLocatorInvoiceValidation::route('/{record}/edit'),
+ ];
+ }
+
+ public static function getEloquentQuery(): Builder
+ {
+ return parent::getEloquentQuery()
+ ->withoutGlobalScopes([
+ SoftDeletingScope::class,
+ ]);
+ }
+}
diff --git a/app/Filament/Resources/LocatorInvoiceValidationResource/Pages/CreateLocatorInvoiceValidation.php b/app/Filament/Resources/LocatorInvoiceValidationResource/Pages/CreateLocatorInvoiceValidation.php
new file mode 100644
index 0000000..a4e11ba
--- /dev/null
+++ b/app/Filament/Resources/LocatorInvoiceValidationResource/Pages/CreateLocatorInvoiceValidation.php
@@ -0,0 +1,1580 @@
+form->getState()['plant'];
+
+ $plantId = trim($plantId) ?? null;
+
+ $palletNumber = $this->form->getState()['pallet_number'];
+
+ $palletNumber = trim($palletNumber) ?? null;
+
+ $serialNumber = $this->form->getState()['serial_number'];
+
+ $serialNumber = trim($serialNumber) ?? null;
+
+ $invoiceNumber = $this->form->getState()['invoice_number'];
+
+ $invoiceNumber = trim($invoiceNumber) ?? null;
+
+ $updateStatus = $this->form->getState()['update_invoice'] ?? null;
+
+ $user = Filament::auth()->user();
+
+ $operatorName = $user->name;
+
+ if(!$plantId)
+ {
+ Notification::make()
+ ->title('Plant Not Found')
+ ->body("Plant can't be empty!")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ try
+ {
+ // $record1 = LocatorInvoiceValidation::query()
+ // ->where('plant_id', $plantId)
+ // ->where('invoice_number', $invoiceNumber)
+ // ->first();
+
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->get();
+
+ $allScanned = true;
+
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && $allScanned) {
+ Notification::make()
+ ->title('Completed: Locator Invoice')
+ ->body("Locator invoice '$invoiceNumber' completed the scanning process.
Scan the next 'Locator Invoice' to proceed..!")
+ ->info()
+ ->send();
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $invoiceExist = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->count('invoice_number');
+
+ if($invoiceExist <= 0)
+ {
+ $filename = $invoiceNumber . '.xlsx';
+
+ $folderPath = Configuration::where('c_name', 'INVOICE_FOLDER_PATH')
+ ->where('plant_id', $plantId)
+ ->value('c_value');
+
+ $fullFolderPath = "uploads/$folderPath";
+
+ $directory = $fullFolderPath;
+ $disk = Storage::disk('local');
+ $filePath = $directory . '/' . $filename;
+ $fullPath = $disk->path($filePath);
+
+ if ($disk->exists($filePath))
+ {
+ $fullPath = $disk->path($filePath);
+ }
+ else
+ {
+ Notification::make()
+ ->title('Invoice File Not Found')
+ ->body("Import the scanned 'Invoice' file to proceed..!")
+ ->danger()
+ ->seconds(2)
+ ->send();
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => 0,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ $this->dispatch('loadData', '', $plantId);
+ return;
+ }
+
+ if ($fullPath && file_exists($fullPath))
+ {
+ $rows = Excel::toArray(null, $fullPath)[0];
+
+ if((count($rows) - 1) <= 0)
+ {
+ Notification::make()
+ // ->title('Records Not Found')
+ // ->body("Import the valid 'Serial Invoice' file to proceed..!")
+ ->title('Invalid Locator Invoice Found')
+ ->body('Uploaded excel sheet is empty or
contains no valid data.')
+ ->danger()
+ ->send();
+
+ if ($disk->exists($filePath))
+ {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $invalidSerialCodes=[];
+ $duplicateSerials = [];
+ $seenSerialNumbers = [];
+ $validRowsFound = false;
+
+ foreach ($rows as $index => $row)
+ {
+ if ($index === 0) continue; // Skip header
+
+ $serialNumber = trim((string)($row[0] ?? ''));
+
+ if (empty($serialNumber))
+ {
+ continue;
+ }
+ else
+ {
+ if(Str::length($serialNumber) < 13 || !ctype_alnum($serialNumber))
+ {
+ $invalidSerialCodes[] = $serialNumber;
+ }
+ else
+ {
+ if (in_array($serialNumber, $seenSerialNumbers)) {
+ $duplicateSerials[] = $serialNumber;
+ }
+ else
+ {
+ $seenSerialNumbers[] = $serialNumber;
+ $validRowsFound = true;
+ }
+ }
+ }
+ }
+
+ $uniqueSerialCodes = array_unique($invalidSerialCodes);
+
+ if (!empty($uniqueSerialCodes)) {
+ Notification::make()
+ ->title('Invalid Serial Numbers Found')
+ ->body('The following serial numbers should contain minimum 13 digit alpha numeric values:
' . implode(', ', $uniqueSerialCodes))
+ ->danger()
+ ->send();
+ if ($disk->exists($filePath)) {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $duplicateSerialCodes = array_unique($duplicateSerials);
+
+ if (!empty($duplicateSerialCodes)) {
+ Notification::make()
+ ->title('Duplicate Serial Numbers Found')
+ ->body('The following serial numbers are already exist in imported excel:
' . implode(', ', $duplicateSerialCodes))
+ ->danger()
+ ->send();
+ if ($disk->exists($filePath)) {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ if (!$validRowsFound) {
+ Notification::make()
+ ->title('Invalid Locator Invoice Found')
+ ->body('Uploaded excel sheet is empty or
contains no valid data.')
+ ->danger()
+ ->send();
+ if ($disk->exists($filePath)) {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => 0,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $serialsToCheck = $seenSerialNumbers;
+
+ $existingSerials = LocatorInvoiceValidation::whereIn('serial_number', $serialsToCheck)
+ ->where('invoice_number', '!=', $invoiceNumber)
+ ->where('plant_id', $plantId)
+ ->pluck('serial_number')
+ ->toArray();
+
+ if (!empty($existingSerials)) {
+ Notification::make()
+ ->title('Duplicate Serial Numbers Found')
+ ->body('The following serial numbers already exist with a different invoice number:
' . implode(', ', $existingSerials))
+ ->danger()
+ ->send();
+ if ($disk->exists($filePath))
+ {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => 0,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else
+ {
+ $createdAny = false;
+ foreach ($seenSerialNumbers as $serialNumber)
+ {
+ // $deleted = LocatorInvoiceValidation::where('invoice_number', $invoiceNumber)
+ // ->where('serial_number', $serialNumber)
+ // ->where('plant_id', $plantId)
+ // ->where(function($query) {
+ // $query->whereNull('scanned_status')
+ // ->orWhere('scanned_status', '');
+ // })
+ // ->forceDelete();
+
+ // if ($deleted)
+ // {
+ // LocatorInvoiceValidation::create([
+ // 'plant_id' => $plantId,
+ // 'invoice_number' => $invoiceNumber,
+ // 'serial_number' => $serialNumber,
+ // 'created_by' => $operatorName,
+
+ // ]);
+ // $createdAny = true;
+
+ // $this->form->fill([
+ // 'plant_id' => $plantId,
+ // 'plant' => $plantId,
+ // 'invoice_number' => $invoiceNumber,
+ // 'serial_number' => null,
+ // 'sno_quantity' => $this->snoCount,
+ // 'created_by' => $operatorName,
+ // 'scanned_by' => $operatorName,
+ // ]);
+
+ // $this->dispatch('loadData', $invoiceNumber, $plantId);
+ // }
+
+ LocatorInvoiceValidation::create([
+ 'plant_id' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'serial_number' => $serialNumber,
+ 'created_by' => $operatorName,
+ ]);
+
+ $createdAny = true;
+
+ }
+
+ if ($createdAny)
+ {
+ Notification::make()
+ ->title('Success: Invoice inserted successfully.')
+ ->success()
+ ->send();
+ Notification::make()
+ ->title("Start the scanning process for imported invoice number '$invoiceNumber'!")
+ ->info()
+ ->send();
+ if ($disk->exists($filePath))
+ {
+ $disk->delete($filePath);
+ }
+
+ $snoCount = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->count();
+
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ }
+ }
+ }
+ }
+ else if($updateStatus == 1)
+ {
+ $filename = $invoiceNumber . '.xlsx';
+
+ $folderPath = Configuration::where('c_name', 'INVOICE_FOLDER_PATH')
+ ->where('plant_id', $plantId)
+ ->value('c_value');
+
+ $fullFolderPath = "uploads/$folderPath";
+
+ $directory = $fullFolderPath;
+ $disk = Storage::disk('local');
+ $filePath = $directory . '/' . $filename;
+ $fullPath = $disk->path($filePath);
+
+ if ($disk->exists($filePath)) {
+ $fullPath = $disk->path($filePath);
+ }
+ else
+ {
+ Notification::make()
+ ->title('Updated Invoice File Not Found')
+ ->body("Import the updated 'Invoice' file to proceed..!")
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'serial_number' => null,
+ ]);
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ // session()->flash('invoice_valid', true);
+
+ if ($fullPath && file_exists($fullPath))
+ {
+ $rows = Excel::toArray(null, $fullPath)[0];
+
+ if((count($rows) - 1) <= 0)
+ {
+ Notification::make()
+ ->title('Invalid Updated Locator Invoice Found')
+ ->body('Uploaded excel sheet is empty or
contains no valid data.')
+ ->danger()
+ ->send();
+
+ if ($disk->exists($filePath))
+ {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $invalidSerialCodes=[];
+ $duplicateSerials = [];
+ $seenSerialNumbers = [];
+ $validRowsFound = false;
+
+ foreach ($rows as $index => $row)
+ {
+ if ($index === 0) continue; // Skip header
+
+ $serialNumber = trim((string) ($row[0] ?? ''));
+
+ if (empty($serialNumber))
+ {
+ continue;
+ }
+ else
+ {
+ if(Str::length($serialNumber) < 13 || !ctype_alnum($serialNumber))
+ {
+ $invalidSerialCodes[] = $serialNumber;
+ }
+ else
+ {
+ if (in_array($serialNumber, $seenSerialNumbers)) {
+ $duplicateSerials[] = $serialNumber;
+ }
+ else
+ {
+ $seenSerialNumbers[] = $serialNumber;
+ $validRowsFound = true;
+ }
+ }
+ }
+ }
+
+ $uniqueSerialCodes = array_unique($invalidSerialCodes);
+
+ if (!empty($uniqueSerialCodes)) {
+ Notification::make()
+ ->title('Invalid Serial Numbers Found')
+ ->body('The following serial numbers should contain minimum 13 digit alpha numeric values:
' . implode(', ', $uniqueSerialCodes))
+ ->danger()
+ ->send();
+ if ($disk->exists($filePath)) {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $duplicateSerialCodes = array_unique($duplicateSerials);
+
+ if (!empty($duplicateSerialCodes)) {
+ Notification::make()
+ ->title('Duplicate Serial Numbers Found')
+ ->body('The following serial numbers are already exist in imported excel:
' . implode(', ', $duplicateSerialCodes))
+ ->danger()
+ ->send();
+ if ($disk->exists($filePath))
+ {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ if (!$validRowsFound) {
+ Notification::make()
+ ->title('Invalid Updated Locator Invoice Found')
+ ->body('Uploaded excel sheet is empty or
contains no valid data.')
+ ->danger()
+ ->send();
+ if ($disk->exists($filePath)) {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $serialsToCheck = $seenSerialNumbers;
+
+ $existingSerials = LocatorInvoiceValidation::whereIn('serial_number', $serialsToCheck)
+ ->where('invoice_number', '!=', $invoiceNumber)
+ ->where('plant_id', $plantId)
+ ->pluck('serial_number')
+ ->toArray();
+
+ if (!empty($existingSerials)) {
+ Notification::make()
+ ->title('Duplicate Serial Numbers Found')
+ ->body('The following serial numbers already exist with a different invoice number:
' . implode(', ', $existingSerials))
+ ->danger()
+ ->send();
+ if ($disk->exists($filePath)) {
+ $disk->delete($filePath);
+ }
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else
+ {
+ $createdAny = false;
+ foreach ($seenSerialNumbers as $serialNumber)
+ {
+ $existingRecord = LocatorInvoiceValidation::where('invoice_number', $invoiceNumber)
+ ->where('serial_number', $serialNumber)
+ ->where('plant_id', $plantId)
+ ->first();
+
+ if ($existingRecord) {
+ if ($existingRecord->scanned_status != null && $existingRecord->scanned_status != '') {
+ continue;
+ }
+ $existingRecord->forceDelete();
+ }
+
+ LocatorInvoiceValidation::create([
+ 'plant_id' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'serial_number' => $serialNumber,
+ 'created_by' => $operatorName,
+ 'updated_by' => $operatorName,
+ ]);
+
+ $createdAny = true;
+ }
+
+ if ($createdAny)
+ {
+ Notification::make()
+ ->title('Success: Locator invoice updated successfully.')
+ ->success()
+ ->send();
+ Notification::make()
+ ->title("Start the scanning process for updated invoice number '$invoiceNumber'!")
+ ->info()
+ ->send();
+ if ($disk->exists($filePath))
+ {
+ $disk->delete($filePath);
+ }
+
+ $snoCount = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->count();
+
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ }
+
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ }
+ }
+ }
+ else
+ {
+ Notification::make()
+ ->title("Start the scanning process for scanned invoice number '$invoiceNumber'!")
+ ->info()
+ ->send();
+
+ $snoCount = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->count();
+
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ }
+
+ }
+ catch (\Exception $e)
+ {
+ Notification::make()
+ ->title('Error: Locator invoice insertion (or updation).')
+ ->body($e->getMessage())
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $snoCount = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->count();
+
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ }
+ }
+
+ public function processPalletNo()
+ {
+ $plantId = $this->form->getState()['plant'];
+
+ $this->plantId = $plantId;
+
+ $palletNumber = trim($this->form->getState()['pallet_number']);
+
+ $this->pallet_number = $palletNumber;
+
+ $serialNumber = trim($this->form->getState()['serial_number']);
+
+ $this->serial_number = $serialNumber;
+
+ $invoiceNumber = trim($this->form->getState()['invoice_number']);
+
+ $this->invoice_number = $invoiceNumber;
+
+ $operatorName = Filament::auth()->user()->name;
+
+ $updateLocatorStatus = $this->form->getState()['update_locator_invoice'] ?? null;
+
+ $PalletSerialNumbers = [];
+
+ $InvoiceSerialNumbers = [];
+
+ $matchedSerialNumbers = [];
+
+ $UnknownSerialNumbers = [];
+
+ $invExist = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->first();
+ if (!$invExist)
+ {
+ Notification::make()
+ ->title('Invoice Not Found')
+ ->body("Scanned invoice number '$invoiceNumber' does not exist in invoice table!
Import the scanned 'Invoice' file to proceed..!")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else
+ {
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->get();
+
+ $allScanned = true;
+
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && $allScanned) {
+ Notification::make()
+ ->title('Completed: Locator Invoice')
+ ->body("Invoice number '$invoiceNumber' completed the scanning process.
Scan the next 'Locator Invoice' to proceed..!")
+ ->info()
+ ->send();
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ }
+
+ if ($palletNumber == '' || $palletNumber == null)
+ {
+ Notification::make()
+ ->title("Pallet number can't be empty!")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $snoCount = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->count();
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $snoCount = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->count();
+ if (strlen($palletNumber) < 10)
+ {
+ Notification::make()
+ ->title("Pallet number '$palletNumber' must be at least 10 digits.")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $palletRecord = PalletValidation::where('plant_id', $plantId)
+ ->where('pallet_number', $palletNumber)
+ ->first();
+
+ if (!$palletRecord)
+ {
+ Notification::make()
+ ->title("Pallet number '{$palletNumber}' does not exist in pallet table.")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $palletRecords = PalletValidation::where('plant_id', $plantId)
+ ->where('pallet_number', $palletNumber)
+ ->get();
+
+ $allCompleted = true;
+ foreach ($palletRecords as $record) {
+ if ($record->pallet_status != 'Completed') {
+ $allCompleted = false;
+ break;
+ }
+ }
+
+ if (!$allCompleted)
+ {
+ Notification::make()
+ ->title("Pallet number '{$palletNumber}' does not completed the master packing!")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ if ($allCompleted)
+ {
+ $PalletSerialNumbers = $palletRecords->pluck('serial_number')
+ ->map(function ($serial) {
+ return trim($serial);
+ })
+ ->all();
+
+ $InvoiceSerialNumbers = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->where(function ($query) {
+ $query->where('scanned_status', '=', '')
+ ->orWhereNull('scanned_status');
+ })
+ ->pluck('serial_number')
+ ->all();
+
+ $matchedSerialNumbers = array_intersect($PalletSerialNumbers, $InvoiceSerialNumbers);
+
+ if (empty($matchedSerialNumbers))
+ {
+ Notification::make()
+ ->title("Scanned pallet number '$palletNumber' does not have invoice serial numbers.
Scan the valid exist pallet number to start the scanning process..!")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $UnknownSerialNumbers = array_diff($PalletSerialNumbers, $InvoiceSerialNumbers);
+ // $matchedSerialNumbers = array_unique(array_merge($InvoiceSerialNumbers, $PalletSerialNumbers));
+
+ if (!empty($UnknownSerialNumbers))
+ {
+ //hereToContinue...
+ if($updateLocatorStatus == null || $updateLocatorStatus == 0)
+ {
+ // $missingSerialsString = "Missing serial numbers:\n" . implode(",\n", $missingSerialNumbers);
+ $missingSerialsString = "Scanned pallet number '$palletNumber' has '".count($PalletSerialNumbers)."' serial numbers.
But, below mentioned '".count($matchedSerialNumbers)."' serial numbers only exist in invoice,
".implode(", ", $matchedSerialNumbers)."
Press 'Yes' and Click 'Enter' in the pallet text box to proceed with existing serial number!
Press 'No' to cancel the scanning process!";
+
+ if (count($matchedSerialNumbers) > 10)
+ {
+ //$missingSerialsString = "Missing serial numbers: " . count($missingSerialNumbers);
+ $missingSerialsString = "Scanned pallet number '$palletNumber' has '".count($PalletSerialNumbers)."' serial numbers.
But, '".count($matchedSerialNumbers)."' serial numbers only exist in invoice,
Press 'Yes' and Click 'Enter' in the pallet text box to proceed with existing serial number!
Press 'No' to cancel the scanning process!";
+ }
+
+ // $body = "Scanned pallet number '$palletNumber' has ".$missingSerialsString."
Do you want to skip missing serial numbers?
Then Press 'Yes' and Click 'Enter'.";
+
+ Notification::make()
+ ->title("Partial Pallet Serial Numbers Found")
+ ->body($missingSerialsString)
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => $palletNumber,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ }
+ }
+ else
+ {
+ $updLocNam = '';
+ foreach ($matchedSerialNumbers as $serial)
+ {
+ $palletRecord = PalletValidation::where('plant_id', $plantId)->where('pallet_number', $palletNumber)->where('serial_number', $serial)->first();
+ $invoiceRecord = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->where('serial_number', $serial)->first();
+
+ if ($invoiceRecord)
+ {
+ $invoiceRecord->scanned_status = 'Scanned';
+ $invoiceRecord->pallet_number = $palletNumber;
+ $invoiceRecord->locator_number = $palletRecord->locator_number;
+ $invoiceRecord->scanned_at = now();
+ $invoiceRecord->scanned_by = $operatorName;
+ $invoiceRecord->save();
+ }
+
+ if ($updLocNam == '' || $updLocNam == null)
+ {
+ $updLocNam = $palletRecord->locator_number;
+ }
+
+ // PalletValidation::where('plant_id', $plantId)->where('pallet_number', $palletNumber)->where('serial_number', $serial)->forceDelete();
+ $palletRecord->forceDelete();
+ }
+
+ if ($updLocNam != '' && $updLocNam != null)
+ {
+ $locator = Locator::where('locator_number', $updLocNam)->where('plant_id', $plantId)
+ ->first();
+
+ if ($locator && $locator->locator_quantity > 0) {
+ $locator->locator_quantity = $locator->locator_quantity - 1;
+ $locator->updated_at = now();
+ $locator->operator_id = $operatorName;
+ $locator->save();
+ }
+
+ PalletValidation::where('plant_id', $plantId)
+ ->where('locator_number', $updLocNam)
+ ->update([
+ 'locator_quantity' => $locator->locator_quantity,
+ 'updated_at' => now()
+ ]);
+ }
+
+ Notification::make()
+ ->title("All Serial numbers are moved into invoice from the pallet number '$palletNumber'.
Scan the next exist pallet number to start the scanning process..!")
+ ->success()
+ ->send();
+
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->get();
+
+ $allScanned = true;
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && $allScanned) {
+ Notification::make()
+ ->title('Completed: Locator Invoice')
+ ->body("Invoice number '$invoiceNumber' completed the scanning process.
Scan the next 'Locator Invoice' to proceed..!")
+ ->info()
+ ->send();
+
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else
+ {
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ }
+ }
+
+ if($updateLocatorStatus == 1)
+ {
+ if ($allCompleted)
+ {
+ foreach ($matchedSerialNumbers as $serial)
+ {
+ $palletRecord = PalletValidation::where('plant_id', $plantId)->where('pallet_number', $palletNumber)->where('serial_number', $serial)->first();
+ $invoiceRecord = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->where('serial_number', $serial)->first();
+
+ if ($invoiceRecord)
+ {
+ $invoiceRecord->scanned_status = 'Scanned';
+ $invoiceRecord->pallet_number = $palletNumber;
+ $invoiceRecord->locator_number = $palletRecord->locator_number;
+ $invoiceRecord->scanned_at = now();
+ $invoiceRecord->scanned_by = $operatorName;
+ $invoiceRecord->save();
+ }
+
+ // PalletValidation::where('plant_id', $plantId)->where('pallet_number', $palletNumber)->where('serial_number', $serial)->forceDelete();
+ $palletRecord->forceDelete();
+ }
+
+ $succMsgStr = "Below mentioned '".count($matchedSerialNumbers)."' Serial numbers are moved into invoice from the pallet number '$palletNumber'.
".implode(", ", $matchedSerialNumbers)."
Scan the next exist pallet number to start the scanning process..!";
+
+ if (count($matchedSerialNumbers) > 10)
+ {
+ $succMsgStr = "'".count($matchedSerialNumbers)."' Serial numbers are moved into invoice from the pallet number '$palletNumber'.
Scan the next exist pallet number to start the scanning process..!";
+ }
+
+ Notification::make()
+ ->title($succMsgStr)
+ ->success()
+ ->send();
+
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->get();
+
+ $allScanned = true;
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && $allScanned) {
+ Notification::make()
+ ->title('Completed: Locator Invoice')
+ ->body("Invoice number '$invoiceNumber' completed the scanning process.
Scan the next 'Locator Invoice' to proceed..!")
+ ->info()
+ ->send();
+
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else
+ {
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ }
+ else if($updateLocatorStatus == 0)
+ {
+ $updateLocatorStatus == null;
+ }
+ }
+ }
+
+ public function processSerialNo()
+ {
+ $plantId = $this->form->getState()['plant'];
+
+ $this->plantId = $plantId;
+
+ $palletNumber = trim($this->form->getState()['pallet_number']);
+
+ $this->pallet_number = $palletNumber;
+
+ $serialNumber = trim($this->form->getState()['serial_number']);
+
+ $this->serial_number = $serialNumber;
+
+ $invoiceNumber = trim($this->form->getState()['invoice_number']);
+
+ $this->invoice_number = $invoiceNumber;
+
+ $operatorName = Filament::auth()->user()->name;
+
+ $updateLocatorStatus = $this->form->getState()['update_locator_invoice'] ?? null;
+
+ $existSerialNumber = '';
+
+ $InvoiceSerialNumber = '';
+
+ $invExist = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->first();
+ if (!$invExist)
+ {
+ Notification::make()
+ ->title('Invoice Not Found')
+ ->body("Scanned invoice number '$invoiceNumber' does not exist in invoice table!
Import the scanned 'Invoice' file to proceed..!")
+ ->danger()
+ ->send();
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else
+ {
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)
+ ->where('invoice_number', $invoiceNumber)
+ ->get();
+
+ $allScanned = true;
+
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && $allScanned) {
+ Notification::make()
+ ->title('Completed: Locator Invoice')
+ ->body("Invoice number '$invoiceNumber' completed the scanning process.
Scan the next 'Locator Invoice' to proceed..!")
+ ->info()
+ ->send();
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ }
+
+ if ($serialNumber == '' || $serialNumber == null)
+ {
+ Notification::make()
+ ->title("Invalid: Serial Number")
+ ->body("Serial number can't be empty!")
+ ->danger()
+ ->send();
+ $snoCount = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->count();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $snoCount = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->count();
+ if (strlen($serialNumber) < 13)
+ {
+ Notification::make()
+ ->title("Invalid: Serial Number")
+ ->body("Serial number '$serialNumber' must be at least 13 digits and alpha-numeric value only allowed!")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $InvoiceSerialNumber = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->where('serial_number', $serialNumber)->first();
+
+ if (!$InvoiceSerialNumber)
+ {
+ Notification::make()
+ ->title("Unknown: Serial Number")
+ ->body("Serial number '{$serialNumber}' does not exist in invoice number '$invoiceNumber'.")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else if($InvoiceSerialNumber->scanned_status != null && $InvoiceSerialNumber->scanned_status != '')
+ {
+ Notification::make()
+ ->title("Completed: Serial Number")
+ ->body("Serial number '{$serialNumber}' already completed the scanning process for the invoice number '$invoiceNumber'.")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+
+ $existSerialNumber = PalletValidation::where('plant_id', $plantId)->where('serial_number', $serialNumber)->first();
+
+ $updLocQty = false;
+ $succMsg = "Scanned serial number '{$serialNumber}' is moved into invoice.
Scan the next exist serial number to start the scanning process..!";
+ $existPallNum = $existSerialNumber->pallet_number ?? null;
+ $existPallStat = $existSerialNumber->pallet_status ?? null;
+ $existLocatNum = $existSerialNumber->locator_number ?? null;
+ if (!$existSerialNumber)
+ {
+ //skip update locator quantity
+ $updLocQty = false;
+ Notification::make()
+ ->title("Unknown: Serial Number")
+ ->body("Serial number '{$serialNumber}' does not exist in pallet table.")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else if ($existPallNum == null || $existPallNum == '')
+ {
+ //skip update locator quantity
+ $updLocQty = false;
+ $succMsg = "Scanned serial number '{$serialNumber}' is moved into invoice from the locator number '$existLocatNum'.
Scan the next exist serial number to start the scanning process..!";
+ }
+ else if ($existLocatNum == null || $existLocatNum == '')
+ {
+ if ($existPallStat == null || $existPallStat == '')
+ {
+ //skip update locator quantity
+ $updLocQty = false;
+ $succMsg = "Scanned serial number '{$serialNumber}' is moved into invoice from the pallet number '$existPallNum'.
Scan the next exist serial number to start the scanning process..!";
+ Notification::make()
+ ->title("Incompleted Pallet: Serial Number")//Pallet number '{$existPallNum}' does not completed the master packing!
+ ->body("Serial number '{$serialNumber}' exist in pallet number '$existPallNum' and it does not completed the master packing!")
+ ->danger()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else
+ {
+ //skip update locator quantity
+ $updLocQty = false;
+ $succMsg = "Scanned serial number '{$serialNumber}' is moved into invoice from the pallet number '$existPallNum'.
Scan the next exist serial number to start the scanning process..!";
+ }
+ }
+ else if ($existPallNum != null && $existPallNum != '' && $existLocatNum != null && $existLocatNum != '')
+ {
+ //update locator quantity based on condition
+ $snoCount = PalletValidation::where('plant_id', $plantId)->where('pallet_number', $existPallNum)->count();
+ if ($snoCount > 0 && $snoCount == 1)
+ {
+ $updLocQty = true;
+ }
+ else
+ {
+ $updLocQty = false;
+ }
+ $succMsg = "Scanned serial number '{$serialNumber}' is moved into invoice from the pallet number '$existPallNum' in the locator number '$existLocatNum'.
Scan the next exist serial number to start the scanning process..!";
+ }
+
+ if ($InvoiceSerialNumber)
+ {
+ $InvoiceSerialNumber->pallet_number = $existPallNum;
+ $InvoiceSerialNumber->locator_number = $existLocatNum;
+ $InvoiceSerialNumber->scanned_status = 'Scanned';
+ $InvoiceSerialNumber->scanned_at = now();
+ $InvoiceSerialNumber->scanned_by = $operatorName;
+ $InvoiceSerialNumber->save();
+ $existSerialNumber->forceDelete();
+
+ if ($updLocQty)
+ {
+ //hereToContinue
+ $locator = Locator::where('locator_number', $existLocatNum)->where('plant_id', $plantId)->first();
+
+ if ($locator && $locator->locator_quantity > 0) {
+ $locator->locator_quantity = $locator->locator_quantity - 1;
+ $locator->updated_at = now();
+ $locator->operator_id = $operatorName;
+ $locator->save();
+ }
+
+ PalletValidation::where('plant_id', $plantId)->where('locator_number', $existLocatNum)
+ ->update([
+ 'locator_quantity' => $locator->locator_quantity,
+ 'updated_at' => now()
+ ]);
+ }
+
+ Notification::make()
+ ->title($succMsg)
+ ->success()
+ ->send();
+
+ $records = LocatorInvoiceValidation::where('plant_id', $plantId)->where('invoice_number', $invoiceNumber)->get();
+
+ $allScanned = true;
+ foreach ($records as $record)
+ {
+ if (($record->scanned_status == null) || trim($record->scanned_status) == '') {
+ $allScanned = false;
+ break;
+ }
+ }
+
+ if (count($records) > 0 && $allScanned) {
+ Notification::make()
+ ->title('Completed: Locator Invoice')
+ ->body("Invoice number '$invoiceNumber' completed the scanning process.
Scan the next 'Locator Invoice' to proceed..!")
+ ->info()
+ ->send();
+
+ $this->dispatch('loadData', '', $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'update_invoice' => 0,
+ 'serial_number' => null,
+ 'sno_quantity' => null,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ else
+ {
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ }
+ else
+ {
+ Notification::make()
+ ->title("Failed to check Serial number '{$serialNumber}' existence.
Scan the valid serial number to proceed...")
+ ->success()
+ ->send();
+ $this->dispatch('loadData', $invoiceNumber, $plantId);
+ $this->form->fill([
+ 'plant_id' => $plantId,
+ 'plant' => $plantId,
+ 'invoice_number' => $invoiceNumber,
+ 'pallet_number' => null,
+ 'serial_number' => null,
+ 'sno_quantity' => $snoCount,
+ 'created_by' => $operatorName,
+ 'scanned_by' => $operatorName,
+ ]);
+ return;
+ }
+ }
+
+ public function getFormActions(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Filament/Resources/LocatorInvoiceValidationResource/Pages/EditLocatorInvoiceValidation.php b/app/Filament/Resources/LocatorInvoiceValidationResource/Pages/EditLocatorInvoiceValidation.php
new file mode 100644
index 0000000..3a6db6b
--- /dev/null
+++ b/app/Filament/Resources/LocatorInvoiceValidationResource/Pages/EditLocatorInvoiceValidation.php
@@ -0,0 +1,22 @@
+