A rendszerben 70 duplikált gyártónév halmozódott fel (146 rekord, 1166-ból), mert a legacy import karakterre pontos egyezést követelt: a "Danone " nem találta meg a meglévő "Danone"-t, és felvett egy újat - a nyers, trimmeletlen Excel-értékkel. Gyártóhoz nincs admin felület, ez az egyetlen keletkezési út. Következménye kettős: az árlista feldolgozó valódi változás nélkül is módosulást jelez (a vizsgált fájlban 126 "Módosult" sorból 103 emiatt), a Termék mennyiség statisztika pedig egy gyártóra szűrve a másik ID alá könyvelt tételeket kihagyja a riportból. - új App\Support\NameNormalizer: a normalizálás egyetlen definíciója. Három független pont használja (legacy import, új feldolgozó, jövőbeli összevonó parancs); ha ezek elcsúsznak, az újra duplikált törzsadatot szül. - PriceListService::getProducerByName(): a karakterre pontos találat továbbra is elsőbbséget élvez, de ha nincs, jön a normalizált egyeztetés. Duplikátum esetén a legrégebbi rekordot adja vissza - a termékek arra mutatnak. - PriceListService::addNewProducer(): trimmel mentés előtt - getProducerLookupMap(): kihagyja az archive/deleted státuszú gyártókat. Enélkül a beolvasztott rekord visszakerülne a feloldásba, és a takarítás hatástalan lenne. Ma nem változtat semmin, csak felkészít. Az új feldolgozó lookupjának tie-breakjéhez SZÁNDÉKOSAN nem nyúltam: egy sorral eltüntetné a hamis "Módosult" jelzéseket, de az a funkcióban fedné el az adathibát. A javulás a tényleges adattakarításból jöjjön. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
114 lines
4.5 KiB
PHP
114 lines
4.5 KiB
PHP
<?php
|
|
|
|
use App\Enums\DbStatusFieldEnum;
|
|
use App\Models\Producer;
|
|
use App\Services\PriceListService;
|
|
use App\Services\PricelistFileProcessService;
|
|
use App\Support\NameNormalizer;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
uses(TestCase::class, RefreshDatabase::class);
|
|
|
|
function callPrivate(object $object, string $method, mixed ...$args): mixed
|
|
{
|
|
$reflection = new ReflectionMethod($object, $method);
|
|
$reflection->setAccessible(true);
|
|
|
|
return $reflection->invoke($object, ...$args);
|
|
}
|
|
|
|
function makeProducer(string $name, string $status = DbStatusFieldEnum::active): Producer
|
|
{
|
|
return Producer::create(['name' => $name, 'status' => $status, 'canSee' => 1]);
|
|
}
|
|
|
|
test('a normalizálás összevonja a kis/nagybetű, szóköz és szeparátor eltéréseket', function () {
|
|
expect(NameNormalizer::normalize('Danone'))->toBe('DANONE')
|
|
->and(NameNormalizer::normalize('Danone '))->toBe('DANONE')
|
|
->and(NameNormalizer::normalize(' danone '))->toBe('DANONE')
|
|
->and(NameNormalizer::normalize('Alba-Gel Kft'))->toBe('ALBA-GEL KFT')
|
|
->and(NameNormalizer::normalize('Tej/Sajt'))->toBe('TEJ SAJT')
|
|
->and(NameNormalizer::normalize('Tej_Sajt'))->toBe('TEJ SAJT')
|
|
->and(NameNormalizer::normalize(null))->toBe('');
|
|
});
|
|
|
|
test('a matches() üres nevet nem tekint egyezésnek', function () {
|
|
expect(NameNormalizer::matches('Danone', 'danone '))->toBeTrue()
|
|
->and(NameNormalizer::matches('', ''))->toBeFalse()
|
|
->and(NameNormalizer::matches(null, 'Danone'))->toBeFalse();
|
|
});
|
|
|
|
test('a legacy import megtalálja a meglévő gyártót eltérő írásmód esetén is', function () {
|
|
$existing = makeProducer('Danone');
|
|
|
|
$service = app(PriceListService::class);
|
|
callPrivate($service, 'loadProducers', 'name');
|
|
|
|
// Ezek korábban mind ÚJ gyártót hoztak létre volna - így keletkezett 70 duplikátum.
|
|
foreach (['Danone ', ' danone', 'DANONE', 'Danone '] as $variant) {
|
|
$found = callPrivate($service, 'getProducerByName', $variant);
|
|
|
|
expect($found)->not->toBeFalse()
|
|
->and($found['id'])->toBe($existing->id);
|
|
}
|
|
});
|
|
|
|
test('a legacy import változatlanul hagyja a valóban új gyártót', function () {
|
|
makeProducer('Danone');
|
|
|
|
$service = app(PriceListService::class);
|
|
callPrivate($service, 'loadProducers', 'name');
|
|
|
|
expect(callPrivate($service, 'getProducerByName', 'Bonduelle'))->toBeFalse()
|
|
->and(callPrivate($service, 'getProducerByName', ' '))->toBeFalse();
|
|
});
|
|
|
|
test('a karakterre pontos találat elsőbbséget élvez', function () {
|
|
$old = makeProducer('Voyagex ');
|
|
$new = makeProducer('Voyagex');
|
|
|
|
$service = app(PriceListService::class);
|
|
callPrivate($service, 'loadProducers', 'name');
|
|
|
|
// Ha a fájlban pontosan az egyik rekord neve szerepel, azt kell visszaadni -
|
|
// a normalizált egyeztetés csak tartalék útvonal.
|
|
expect(callPrivate($service, 'getProducerByName', 'Voyagex')['id'])->toBe($new->id)
|
|
->and(callPrivate($service, 'getProducerByName', 'Voyagex ')['id'])->toBe($old->id);
|
|
});
|
|
|
|
test('pontos találat híján duplikátumból a legrégebbi rekordot adja vissza', function () {
|
|
// Ez a takarítás előtti adatállapot: a termékek túlnyomó része a régi rekordra
|
|
// mutat, tehát az a kanonikus. Korábban a feloldás a LEGÚJABBAT választotta.
|
|
$old = makeProducer('Voyagex ');
|
|
$new = makeProducer('Voyagex');
|
|
|
|
$service = app(PriceListService::class);
|
|
callPrivate($service, 'loadProducers', 'name');
|
|
|
|
// Egyik rekord nevével sem egyezik karakterre
|
|
$found = callPrivate($service, 'getProducerByName', 'VOYAGEX');
|
|
|
|
expect($found['id'])->toBe($old->id)
|
|
->and($found['id'])->not->toBe($new->id);
|
|
});
|
|
|
|
test('az új gyártó neve körülvágva kerül mentésre', function () {
|
|
$service = app(PriceListService::class);
|
|
$id = callPrivate($service, 'addNewProducer', ['name' => ' Vadonatúj Gyártó ']);
|
|
|
|
expect(Producer::find($id)->name)->toBe('Vadonatúj Gyártó');
|
|
});
|
|
|
|
test('az archivált gyártó kimarad az árlista feldolgozó feloldásából', function () {
|
|
$active = makeProducer('Danone');
|
|
$merged = makeProducer('Danone ', DbStatusFieldEnum::archive);
|
|
|
|
$lookup = callPrivate(app(PricelistFileProcessService::class), 'getProducerLookupMap');
|
|
|
|
// Enélkül a beolvasztott rekord visszakerülne a feloldásba, és a takarítás
|
|
// hatástalan maradna.
|
|
expect($lookup['DANONE'] ?? null)->toBe($active->id)
|
|
->and(in_array($merged->id, $lookup, true))->toBeFalse();
|
|
});
|