A d2d-n a megrendelő fájljának megnyitásakor a sorok táblája eltört:
TextColumn::getDescriptionBelow(): Return value must be of type
Htmlable|string|null, array returned.
Nem UI-hiba volt, hanem adatvesztés. Ha egy cellán belül több formázású
szövegrész van (pl. a terméknév egy része félkövér), a PhpSpreadsheet
getValue()-ja RichText objektumot ad vissza. A json_encode ezt ÜRESRE
serializálja (a szövegrészek protected propertyben vannak), így a payloadba
{} kerül, ami visszaolvasva üres tömb - a cella tartalma nyomtalanul eltűnt,
a felület pedig tömböt kapott string helyett.
- normalizeCellValue(): a cella értéke a payloadba kerülés ELŐTT skalárrá
alakul (RichText -> getPlainText, dátum -> szöveg, tömb -> összefűzött
szöveg, __toString-es objektum -> string)
- LinesRelationManager::payloadText(): a MÁR ELMENTETT rekordokban maradt
tömbök nem törhetik el a nézetet - a beolvasási javítás azokat
visszamenőleg nem gyógyítja meg
- isBlankPayloadValue(): a kötelező mező ellenőrzése is_string() alapú volt,
ezért az üres tömböt KITÖLTÖTTNEK látta - egy elveszett terméknév némán
átment a validáláson. Ugyanez a védelem a Hooreyca-blokk vizsgálatában és a
végrehajtás mezőkonverziójában is (utóbbi különben szó szerint "Array"
néven hozta volna létre a terméket).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
109 lines
4.2 KiB
PHP
109 lines
4.2 KiB
PHP
<?php
|
|
|
|
use App\Enums\PricelistFileLineStatusEnum;
|
|
use App\Enums\PricelistFileStatusEnum;
|
|
use App\Filament\Resources\PricelistFiles\RelationManagers\LinesRelationManager;
|
|
use App\Filament\Resources\PricelistFiles\Pages\ViewPricelistFile;
|
|
use App\Models\PricelistFile;
|
|
use App\Models\PricelistFileLine;
|
|
use App\Models\Role;
|
|
use App\Models\Supplier;
|
|
use App\Models\User;
|
|
use App\Services\PriceListService;
|
|
use App\Services\PricelistFileProcessService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
|
use Tests\TestCase;
|
|
|
|
uses(TestCase::class, RefreshDatabase::class);
|
|
|
|
function invokeProtected(string $method, mixed ...$args): mixed
|
|
{
|
|
$service = app(PricelistFileProcessService::class);
|
|
$reflection = new ReflectionMethod($service, $method);
|
|
$reflection->setAccessible(true);
|
|
|
|
return $reflection->invoke($service, ...$args);
|
|
}
|
|
|
|
/**
|
|
* Formázott (rich text) cella: a terméknév egy része félkövér. A PhpSpreadsheet ilyenkor
|
|
* nem stringet, hanem RichText objektumot ad vissza a getValue()-ból.
|
|
*/
|
|
function richTextCellValue(): RichText
|
|
{
|
|
$richText = new RichText();
|
|
$richText->createText('Trappista sajt ');
|
|
$richText->createTextRun('PRÉMIUM')->getFont()->setBold(true);
|
|
|
|
return $richText;
|
|
}
|
|
|
|
test('a rich text cella értéke sima szövegként normalizálódik', function () {
|
|
// json_encode a nyers objektumot üresre serializálná (a szövegrészek protected
|
|
// propertyben vannak), így a payloadba üres tömb kerülne és a szöveg elveszne.
|
|
expect(json_encode(['x' => richTextCellValue()]))->toBe('{"x":{}}');
|
|
|
|
expect(invokeProtected('normalizeCellValue', richTextCellValue()))
|
|
->toBe('Trappista sajt PRÉMIUM');
|
|
});
|
|
|
|
test('a normalizálás a skalárokat változatlanul hagyja', function () {
|
|
expect(invokeProtected('normalizeCellValue', 'Sajt'))->toBe('Sajt')
|
|
->and(invokeProtected('normalizeCellValue', 1250.5))->toBe(1250.5)
|
|
->and(invokeProtected('normalizeCellValue', null))->toBeNull();
|
|
});
|
|
|
|
test('a dátum cella szöveggé alakul, nem tömbbé', function () {
|
|
expect(invokeProtected('normalizeCellValue', new DateTimeImmutable('2026-08-24 10:30:00')))
|
|
->toBe('2026-08-24 10:30:00');
|
|
});
|
|
|
|
test('az üres tömb hiányzó kötelező értéknek számít', function () {
|
|
// A régi rekordokban a rich text cellák üres tömbként landoltak. A korábbi,
|
|
// is_string() alapú vizsgálat ezeket KITÖLTÖTTNEK látta, így egy elveszett
|
|
// terméknév némán átment a kötelező mező ellenőrzésén.
|
|
expect(invokeProtected('isBlankPayloadValue', []))->toBeTrue()
|
|
->and(invokeProtected('isBlankPayloadValue', null))->toBeTrue()
|
|
->and(invokeProtected('isBlankPayloadValue', ' '))->toBeTrue()
|
|
->and(invokeProtected('isBlankPayloadValue', 'Sajt'))->toBeFalse()
|
|
->and(invokeProtected('isBlankPayloadValue', 0))->toBeFalse();
|
|
});
|
|
|
|
test('a már elmentett tömbös payload nem töri el a sorok táblát', function () {
|
|
$supplier = Supplier::factory()->create();
|
|
|
|
$file = PricelistFile::create([
|
|
'filename' => 'arlista.xlsx',
|
|
'supplier_id' => $supplier->id,
|
|
'available_date' => now()->addWeek()->toDateString(),
|
|
'status' => PricelistFileStatusEnum::waiting_for_approval,
|
|
]);
|
|
|
|
// Pontosan az az állapot, ami a d2d-n a nézetet eltörte:
|
|
// TextColumn::getDescriptionBelow(): Return value must be of type
|
|
// Htmlable|string|null, array returned
|
|
PricelistFileLine::create([
|
|
'pricelist_file_id' => $file->id,
|
|
'row_number' => 5,
|
|
'status' => PricelistFileLineStatusEnum::error,
|
|
'payload' => [
|
|
PriceListService::EXPECTED_HEADERS[0] => 'SKU-1',
|
|
PriceListService::EXPECTED_HEADERS[4] => [],
|
|
],
|
|
'validation_messages' => ['name' => 'Hiányzó kötelező mező'],
|
|
]);
|
|
|
|
$user = User::factory()->create();
|
|
$user->addRole(Role::create(['name' => 'admin', 'display_name' => 'Admin']));
|
|
$this->actingAs($user);
|
|
|
|
Livewire::test(LinesRelationManager::class, [
|
|
'ownerRecord' => $file,
|
|
'pageClass' => ViewPricelistFile::class,
|
|
])
|
|
->assertOk()
|
|
->assertSee('SKU-1');
|
|
});
|