diff --git a/app/Services/PriceListService.php b/app/Services/PriceListService.php index 5baef5f..26c625f 100644 --- a/app/Services/PriceListService.php +++ b/app/Services/PriceListService.php @@ -106,6 +106,7 @@ public function getExcelFieldPointer(): array 19 => 'Vevői megnevezés', 20 => 'Megjegyzés', 21 => 'KREL', + 22 => 'Akció', ]; private array $excelFieldPointer = [ @@ -131,6 +132,7 @@ public function getExcelFieldPointer(): array 'buyerProductName' => 19, 'note' => 20, 'krel' => 21, + 'specialOffer' => 22, ]; @@ -157,6 +159,7 @@ public function getExcelFieldPointer(): array 'buyerProductName' => ['string'], // 19 'note' => ['string'], // 20 'krel' => ['string'], // 21 + 'specialOffer' => ['string'], // 22 ]; private array $excelFieldConverter = [ @@ -165,6 +168,7 @@ public function getExcelFieldPointer(): array 'vat' => ['percentage'], 'note' => ['null'], 'krel' => ['booleanCustom'], + 'specialOffer' => ['booleanCustom'], 'hooreycaId' => ['null'], 'HooreycaUnit' => ['null'], 'HooreycaMultiplier' => ['null'], diff --git a/app/Services/PricelistFileProcessService.php b/app/Services/PricelistFileProcessService.php index 53a0d0b..2341205 100644 --- a/app/Services/PricelistFileProcessService.php +++ b/app/Services/PricelistFileProcessService.php @@ -187,6 +187,15 @@ public function updateStepStatus( private const HEADER_ROW = 3; private const DATA_START_ROW = 5; + /** + * Opcionális (visszafelé kompatibilis) oszlopfejlécek indexei. + * Ha egy ilyen oszlop hiányzik a 3. sorból, az nem blokkoló hiba, csak figyelmeztetés, + * és a hozzá tartozó feldolgozás (pl. akciós jelölés) kimarad. + */ + private const OPTIONAL_HEADER_INDEXES = [ + 22, // Akció (specialOffer) + ]; + /** * Előfeldolgozás (Pre-processing) - Strukturális és technikai ellenőrzés * @@ -384,6 +393,12 @@ private function buildColumnMap(Worksheet $worksheet): array if (isset($headerByName[$normalizedExpected])) { $map[$index] = $headerByName[$normalizedExpected]; $usedColumns[$headerByName[$normalizedExpected]] = true; + } elseif (in_array($index, self::OPTIONAL_HEADER_INDEXES, true)) { + // Opcionális oszlop hiánya nem blokkoló, csak figyelmeztetés (visszafelé kompatibilitás) + $warnings[] = $this->buildWarning( + PreProcessErrorCode::COLUMN_MISSING, + "Hiányzó opcionális oszlopfejléc: '{$expectedLabel}' (a " . self::HEADER_ROW . ". sorban nem található); a feldolgozás e nélkül folytatódik." + ); } else { // Hiányzó kötelező fejléc: blokkoló hiba $errors[] = $this->buildError( @@ -626,7 +641,9 @@ public function validate(PricelistFile $pricelistFile): bool PricelistFileLine::insert($dataToInsert); } - $this->runBusinessValidation($pricelistFile); + // Az "Akció" (specialOffer) opcionális oszlop: csak akkor kezeljük, ha ténylegesen szerepel a fájlban + $hasSpecialOfferColumn = isset($columnMap[22]); + $this->runBusinessValidation($pricelistFile, $hasSpecialOfferColumn); $duration = round(microtime(true) - $startTime, 2); @@ -897,7 +914,7 @@ protected function validateProducer(array $payload, array $producerLookup): ?str * * @param PricelistFile $pricelistFile */ - protected function runBusinessValidation(PricelistFile $pricelistFile): void + protected function runBusinessValidation(PricelistFile $pricelistFile, bool $hasSpecialOfferColumn = false): void { $this->updateStepStatus($pricelistFile, PricelistWorkflowStep::Validation, 'inprogress', 'Üzleti validálás...', 100); @@ -908,7 +925,7 @@ protected function runBusinessValidation(PricelistFile $pricelistFile): void $producerIdToName = $this->getProducerIdMap(); $groupIdToPath = $this->getProductGroupIdMap(); - $pricelistFile->lines()->chunk(1000, function ($lines) use ($groupLookup, $producerLookup, $productLookup, $producerIdToName, $groupIdToPath) { + $pricelistFile->lines()->chunk(1000, function ($lines) use ($groupLookup, $producerLookup, $productLookup, $producerIdToName, $groupIdToPath, $hasSpecialOfferColumn) { $updates = []; foreach ($lines as $line) { @@ -1116,6 +1133,25 @@ protected function runBusinessValidation(PricelistFile $pricelistFile): void ]; } } + + // 5. Akciós (specialOffer) módosulás - csak ha az "Akció" oszlop szerepel a fájlban. + // booleanCustom logika: üres érték => false, bármilyen más érték => true. + if ($hasSpecialOfferColumn) { + $specialOfferRaw = $payload[PriceListService::EXPECTED_HEADERS[22]] ?? null; + $newSpecialOffer = strlen(trim((string)$specialOfferRaw)) > 0; + $oldSpecialOffer = (bool)($existingProduct['specialOffer'] ?? false); + + if ($newSpecialOffer !== $oldSpecialOffer) { + $isUpdated = true; + $diff['specialOffer'] = [ + 'old' => (int)$oldSpecialOffer, + 'new' => (int)$newSpecialOffer, + 'old_label' => $oldSpecialOffer ? 'Akciós' : 'Nem akciós', + 'new_label' => $newSpecialOffer ? 'Akciós' : 'Nem akciós', + 'label' => PriceListService::EXPECTED_HEADERS[22], + ]; + } + } } $updates[] = [ diff --git a/resources/views/layout/speedButtonNavBar.blade.php b/resources/views/layout/speedButtonNavBar.blade.php index 02a0c76..5d8ef5d 100644 --- a/resources/views/layout/speedButtonNavBar.blade.php +++ b/resources/views/layout/speedButtonNavBar.blade.php @@ -121,7 +121,7 @@ 'icon' => 'price_list', 'link' => '/admin/pricelist-files', 'noAjax' => true, - 'roles' => ['developer'], + 'roles' => ['developer','admin'], ], 'delivery' => [ 'name' => 'delivery', diff --git a/tests/Feature/PricelistColumnMappingTest.php b/tests/Feature/PricelistColumnMappingTest.php index 3862597..ba8625b 100644 --- a/tests/Feature/PricelistColumnMappingTest.php +++ b/tests/Feature/PricelistColumnMappingTest.php @@ -81,6 +81,41 @@ function makeWorksheetWithHeaderOrder(array $order): Worksheet expect($result['map'])->toHaveCount(count($expected)); }); +test('a hiányzó opcionális "Akció" oszlop csak figyelmeztetést ad, nem blokkol', function () { + $expected = PriceListService::EXPECTED_HEADERS; + + $worksheet = (new Spreadsheet())->getActiveSheet(); + $col = 1; + foreach ($expected as $fieldIndex => $label) { + if ($fieldIndex === 22) { + // Az "Akció" oszlopot szándékosan kihagyjuk (visszafelé kompatibilis fájl) + continue; + } + $worksheet->setCellValueByColumnAndRow($col, 3, $label); + $col++; + } + + $result = invokeBuildColumnMap($worksheet); + + $hasBlocker = collect($result['errors'])->contains(fn ($error) => ($error['severity'] ?? '') === 'blocker'); + expect($hasBlocker)->toBeFalse(); + expect($result['map'])->not->toHaveKey(22); + expect(collect($result['warnings']))->not->toBeEmpty(); +}); + +test('a jelen lévő "Akció" oszlop leképeződik', function () { + $expected = PriceListService::EXPECTED_HEADERS; + + $worksheet = makeWorksheetWithHeaderOrder(array_keys($expected)); + $result = invokeBuildColumnMap($worksheet); + + expect($result['errors'])->toBe([]); + expect($result['map'])->toHaveKey(22); + + $value = $worksheet->getCellByColumnAndRow($result['map'][22], 5)->getValue(); + expect($value)->toBe('VAL_22'); +}); + test('hiányzó kötelező fejléc blokkoló hibát ad', function () { $expected = PriceListService::EXPECTED_HEADERS;