ver. 0.292: ShopProduct + ShopPaymentMethod + ShopPromotion + ShopStatuses + ShopTransport frontend migration to Domain

Full migration of front\factory\ — entire directory removed (all 20 classes migrated).
ProductRepository +20 frontend methods, PromotionRepository +5 applyType methods,
TransportRepository +4 cached methods, PaymentMethodRepository +cached frontend methods.
Fix: broken transports_list() in ajax.php replaced with forPaymentMethod().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 21:55:16 +01:00
parent 827b903e1e
commit 89d9e61bec
48 changed files with 1780 additions and 975 deletions

View File

@@ -301,6 +301,92 @@ class PaymentMethodRepositoryTest extends TestCase
$this->assertSame('BANK_TRANSFER', $repository->getApiloPaymentTypeId(3));
}
public function testIsActiveReturnsOneForActivePayment(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with('pp_shop_payment_methods', 'status', ['id' => 2])
->willReturn('1');
$repository = new PaymentMethodRepository($mockDb);
$this->assertSame(1, $repository->isActive(2));
}
public function testIsActiveReturnsZeroForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('get');
$repository = new PaymentMethodRepository($mockDb);
$this->assertSame(0, $repository->isActive(0));
}
public function testFindActiveByIdReturnsNormalizedData(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with('pp_shop_payment_methods', '*', [
'AND' => [
'id' => 3,
'status' => 1,
],
])
->willReturn([
'id' => '3',
'name' => ' Przelew ',
'description' => 'Opis',
'status' => '1',
'apilo_payment_type_id' => '5',
]);
$repository = new PaymentMethodRepository($mockDb);
$result = $repository->findActiveById(3);
$this->assertIsArray($result);
$this->assertSame(3, $result['id']);
$this->assertSame('Przelew', $result['name']);
$this->assertSame(1, $result['status']);
$this->assertSame(5, $result['apilo_payment_type_id']);
}
public function testFindActiveByIdReturnsNullForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('get');
$repository = new PaymentMethodRepository($mockDb);
$this->assertNull($repository->findActiveById(0));
}
public function testAllActiveReturnsEmptyOnNull(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('select')->willReturn(null);
$repository = new PaymentMethodRepository($mockDb);
$this->assertSame([], $repository->allActive());
}
public function testGetApiloPaymentTypeIdReturnsNullForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('get');
$repository = new PaymentMethodRepository($mockDb);
$this->assertNull($repository->getApiloPaymentTypeId(0));
}
public function testForTransportReturnsEmptyForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('query');
$repository = new PaymentMethodRepository($mockDb);
$this->assertSame([], $repository->forTransport(0));
}
public function testForTransportReturnsRows(): void
{
$mockDb = $this->createMock(\medoo::class);

View File

@@ -510,4 +510,371 @@ class ProductRepositoryTest extends TestCase
$this->assertIsArray($result);
$this->assertNull($result['price_brutto_promo']);
}
// =========================================================================
// Frontend methods (migrated from front\factory\ShopProduct)
// =========================================================================
/**
* Test getSkuWithFallback - zwraca SKU bezpośrednio
*/
public function testGetSkuWithFallbackReturnsSku()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with('pp_shop_products', 'sku', ['id' => 10])
->willReturn('SKU-ABC');
$repository = new ProductRepository($mockDb);
$result = $repository->getSkuWithFallback(10);
$this->assertEquals('SKU-ABC', $result);
}
/**
* Test getSkuWithFallback - fallback na parent_id
*/
public function testGetSkuWithFallbackFromParent()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')
->willReturnCallback(function ($table, $column, $where) {
if ($column === 'sku' && $where['id'] === 10) return null;
if ($column === 'parent_id' && $where['id'] === 10) return 5;
if ($column === 'sku' && $where['id'] === 5) return 'SKU-PARENT';
return null;
});
$repository = new ProductRepository($mockDb);
$result = $repository->getSkuWithFallback(10, true);
$this->assertEquals('SKU-PARENT', $result);
}
/**
* Test getSkuWithFallback - zwraca null dla nieprawidłowego ID
*/
public function testGetSkuWithFallbackReturnsNullForInvalidId()
{
$mockDb = $this->createMock(\medoo::class);
$repository = new ProductRepository($mockDb);
$this->assertNull($repository->getSkuWithFallback(0));
$this->assertNull($repository->getSkuWithFallback(-1));
}
/**
* Test getEanWithFallback - zwraca EAN bezpośrednio
*/
public function testGetEanWithFallbackReturnsEan()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with('pp_shop_products', 'ean', ['id' => 10])
->willReturn('1234567890123');
$repository = new ProductRepository($mockDb);
$result = $repository->getEanWithFallback(10);
$this->assertEquals('1234567890123', $result);
}
/**
* Test getEanWithFallback - fallback na parent_id
*/
public function testGetEanWithFallbackFromParent()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')
->willReturnCallback(function ($table, $column, $where) {
if ($column === 'ean' && $where['id'] === 10) return null;
if ($column === 'parent_id' && $where['id'] === 10) return 5;
if ($column === 'ean' && $where['id'] === 5) return 'EAN-PARENT';
return null;
});
$repository = new ProductRepository($mockDb);
$result = $repository->getEanWithFallback(10, true);
$this->assertEquals('EAN-PARENT', $result);
}
/**
* Test isProductActiveCached - zwraca 1 dla aktywnego produktu
*/
public function testIsProductActiveCachedReturnsOneForActive()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with('pp_shop_products', 'status', ['id' => 10])
->willReturn(1);
$repository = new ProductRepository($mockDb);
$this->assertEquals(1, $repository->isProductActiveCached(10));
}
/**
* Test isProductActiveCached - zwraca 0 dla nieaktywnego produktu
*/
public function testIsProductActiveCachedReturnsZeroForInactive()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')->willReturn(0);
$repository = new ProductRepository($mockDb);
$this->assertEquals(0, $repository->isProductActiveCached(10));
}
/**
* Test isProductActiveCached - zwraca 0 dla nieprawidłowego ID
*/
public function testIsProductActiveCachedReturnsZeroForInvalidId()
{
$mockDb = $this->createMock(\medoo::class);
$repository = new ProductRepository($mockDb);
$this->assertEquals(0, $repository->isProductActiveCached(0));
$this->assertEquals(0, $repository->isProductActiveCached(-1));
}
/**
* Test productCategoriesFront - zwraca kategorie
*/
public function testProductCategoriesFrontReturnsCategories()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')
->with('pp_shop_products', 'parent_id', ['id' => 10])
->willReturn(null);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([
['category_id' => 1],
['category_id' => 5],
]);
$mockDb->method('query')->willReturn($mockStmt);
$repository = new ProductRepository($mockDb);
$result = $repository->productCategoriesFront(10);
$this->assertIsArray($result);
$this->assertCount(2, $result);
$this->assertEquals(1, $result[0]['category_id']);
$this->assertEquals(5, $result[1]['category_id']);
}
/**
* Test productCategoriesFront - fallback na parent_id
*/
public function testProductCategoriesFrontUsesParentId()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')
->with('pp_shop_products', 'parent_id', ['id' => 10])
->willReturn(5);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([['category_id' => 3]]);
$mockDb->expects($this->once())
->method('query')
->with(
$this->equalTo('SELECT category_id FROM pp_shop_products_categories WHERE product_id = :pid'),
$this->equalTo([':pid' => 5])
)
->willReturn($mockStmt);
$repository = new ProductRepository($mockDb);
$result = $repository->productCategoriesFront(10);
$this->assertCount(1, $result);
$this->assertEquals(3, $result[0]['category_id']);
}
/**
* Test productCategoriesFront - pusta tablica dla nieprawidłowego ID
*/
public function testProductCategoriesFrontReturnsEmptyForInvalidId()
{
$mockDb = $this->createMock(\medoo::class);
$repository = new ProductRepository($mockDb);
$this->assertEquals([], $repository->productCategoriesFront(0));
$this->assertEquals([], $repository->productCategoriesFront(-1));
}
/**
* Test getWarehouseMessageZero - zwraca wiadomość
*/
public function testGetWarehouseMessageZeroReturnsMessage()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with(
'pp_shop_products_langs',
'warehouse_message_zero',
['AND' => ['product_id' => 10, 'lang_id' => 'pl']]
)
->willReturn('Produkt niedostępny');
$repository = new ProductRepository($mockDb);
$this->assertEquals('Produkt niedostępny', $repository->getWarehouseMessageZero(10, 'pl'));
}
/**
* Test getWarehouseMessageZero - null dla nieprawidłowego ID
*/
public function testGetWarehouseMessageZeroReturnsNullForInvalidId()
{
$mockDb = $this->createMock(\medoo::class);
$repository = new ProductRepository($mockDb);
$this->assertNull($repository->getWarehouseMessageZero(0, 'pl'));
}
/**
* Test getWarehouseMessageNonzero - zwraca wiadomość
*/
public function testGetWarehouseMessageNonzeroReturnsMessage()
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with(
'pp_shop_products_langs',
'warehouse_message_nonzero',
['AND' => ['product_id' => 10, 'lang_id' => 'pl']]
)
->willReturn('Wysyłka w 24h');
$repository = new ProductRepository($mockDb);
$this->assertEquals('Wysyłka w 24h', $repository->getWarehouseMessageNonzero(10, 'pl'));
}
/**
* Test getWarehouseMessageNonzero - null dla nieprawidłowego ID
*/
public function testGetWarehouseMessageNonzeroReturnsNullForInvalidId()
{
$mockDb = $this->createMock(\medoo::class);
$repository = new ProductRepository($mockDb);
$this->assertNull($repository->getWarehouseMessageNonzero(0, 'pl'));
}
/**
* Test topProductIds - zwraca ID aktywnych produktów
*/
public function testTopProductIdsReturnsActiveProducts()
{
$mockDb = $this->createMock(\medoo::class);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([
['sell_count' => 10, 'parent_product_id' => 1],
['sell_count' => 5, 'parent_product_id' => 2],
['sell_count' => 3, 'parent_product_id' => 3],
]);
$mockDb->method('query')->willReturn($mockStmt);
// isProductActiveCached sprawdza status — produkt 2 nieaktywny
$mockDb->method('get')
->willReturnCallback(function ($table, $column, $where) {
if ($column === 'status') {
return $where['id'] === 2 ? 0 : 1;
}
return null;
});
$repository = new ProductRepository($mockDb);
$result = $repository->topProductIds(6);
$this->assertIsArray($result);
$this->assertContains(1, $result);
$this->assertNotContains(2, $result);
$this->assertContains(3, $result);
}
/**
* Test newProductIds - zwraca ID produktów
*/
public function testNewProductIdsReturnsProductIds()
{
$mockDb = $this->createMock(\medoo::class);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([
['id' => 10],
['id' => 20],
['id' => 30],
]);
$mockDb->method('query')->willReturn($mockStmt);
$repository = new ProductRepository($mockDb);
$result = $repository->newProductIds(3);
$this->assertIsArray($result);
$this->assertEquals([10, 20, 30], $result);
}
/**
* Test newProductIds - pusta lista
*/
public function testNewProductIdsReturnsEmptyWhenNoProducts()
{
$mockDb = $this->createMock(\medoo::class);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([]);
$mockDb->method('query')->willReturn($mockStmt);
$repository = new ProductRepository($mockDb);
$this->assertEquals([], $repository->newProductIds(10));
}
/**
* Test promotedProductIdsCached - zwraca promowane produkty
*/
public function testPromotedProductIdsCachedReturnsIds()
{
$mockDb = $this->createMock(\medoo::class);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([
['id' => 5],
['id' => 8],
]);
$mockDb->method('query')->willReturn($mockStmt);
$repository = new ProductRepository($mockDb);
$result = $repository->promotedProductIdsCached(2);
$this->assertIsArray($result);
$this->assertEquals([5, 8], $result);
}
/**
* Test promotedProductIdsCached - pusta lista
*/
public function testPromotedProductIdsCachedReturnsEmptyWhenNone()
{
$mockDb = $this->createMock(\medoo::class);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([]);
$mockDb->method('query')->willReturn($mockStmt);
$repository = new ProductRepository($mockDb);
$this->assertEquals([], $repository->promotedProductIdsCached(6));
}
}

View File

@@ -176,4 +176,202 @@ class PromotionRepositoryTest extends TestCase
$this->assertCount(1, $tree[0]['subcategories']);
$this->assertSame(11, (int)$tree[0]['subcategories'][0]['id']);
}
// =========================================================================
// Frontend: basket promotion logic (migrated from front\factory\ShopPromotion)
// =========================================================================
private function mockPromotion(array $data): object
{
return new class($data) {
private $data;
public function __construct($data) { $this->data = $data; }
public function __get($key) { return isset($this->data[$key]) ? $this->data[$key] : null; }
};
}
private function makeBasket(array $items): array
{
$basket = [];
foreach ($items as $i => $item) {
$basket[$i] = array_merge(['product-id' => $item['id']], $item);
}
return $basket;
}
/**
* Test applyTypeWholeBasket — rabat na cały koszyk
*/
public function testApplyTypeWholeBasketAppliesDiscountToAll(): void
{
$mockDb = $this->createMock(\medoo::class);
// productCategoriesFront zwraca kategorie
$mockDb->method('get')->willReturn(null); // parent_id = null
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([['category_id' => 1]]);
$mockDb->method('query')->willReturn($mockStmt);
$promotion = $this->mockPromotion([
'discount_type' => 1,
'amount' => 10,
'include_coupon' => 0,
'include_product_promo' => 0,
]);
$basket = $this->makeBasket([
['id' => 1],
['id' => 2],
]);
$repository = new PromotionRepository($mockDb);
$result = $repository->applyTypeWholeBasket($basket, $promotion);
$this->assertSame(1, $result[0]['discount_type']);
$this->assertSame(10, $result[0]['discount_amount']);
$this->assertSame(1, $result[1]['discount_type']);
}
/**
* Test applyTypeCategoriesOr — rabat na produkty z kat. 1 lub 2
*/
public function testApplyTypeCategoriesOrAppliesDiscountToMatchingCategories(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')->willReturn(null);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([['category_id' => 5]]);
$mockDb->method('query')->willReturn($mockStmt);
$promotion = $this->mockPromotion([
'categories' => json_encode([5]),
'condition_categories' => json_encode([10]),
'discount_type' => 1,
'amount' => 15,
'include_coupon' => 1,
'include_product_promo' => 0,
]);
$basket = $this->makeBasket([['id' => 1]]);
$repository = new PromotionRepository($mockDb);
$result = $repository->applyTypeCategoriesOr($basket, $promotion);
$this->assertSame(1, $result[0]['discount_type']);
$this->assertSame(15, $result[0]['discount_amount']);
$this->assertSame(1, $result[0]['discount_include_coupon']);
}
/**
* Test applyTypeCategoryCondition — rabat na kat. I jeśli kat. II w koszyku
*/
public function testApplyTypeCategoryConditionAppliesWhenConditionMet(): void
{
$mockDb = $this->createMock(\medoo::class);
$callCount = 0;
$mockDb->method('get')->willReturn(null);
$mockStmt1 = $this->createMock(\PDOStatement::class);
$mockStmt1->method('fetchAll')->willReturnOnConsecutiveCalls(
[['category_id' => 10]], // product 1 — condition category
[['category_id' => 5]], // product 2 — target category
[['category_id' => 10]], // product 1 — check for discount (not matching target)
[['category_id' => 5]] // product 2 — check for discount (matching target)
);
$mockDb->method('query')->willReturn($mockStmt1);
$promotion = $this->mockPromotion([
'categories' => json_encode([5]),
'condition_categories' => json_encode([10]),
'discount_type' => 1,
'amount' => 20,
'include_coupon' => 0,
'include_product_promo' => 0,
]);
$basket = $this->makeBasket([
['id' => 1],
['id' => 2],
]);
$repository = new PromotionRepository($mockDb);
$result = $repository->applyTypeCategoryCondition($basket, $promotion);
// Produkt 2 (kat. 5) powinien mieć rabat
$this->assertSame(1, $result[1]['discount_type']);
$this->assertSame(20, $result[1]['discount_amount']);
}
/**
* Test applyTypeCategoryCondition — brak rabatu gdy warunek niespełniony
*/
public function testApplyTypeCategoryConditionNoDiscountWhenConditionNotMet(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')->willReturn(null);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([['category_id' => 99]]); // nie pasuje do condition_categories
$mockDb->method('query')->willReturn($mockStmt);
$promotion = $this->mockPromotion([
'categories' => json_encode([5]),
'condition_categories' => json_encode([10]),
'discount_type' => 1,
'amount' => 20,
'include_coupon' => 0,
'include_product_promo' => 0,
]);
$basket = $this->makeBasket([['id' => 1]]);
$repository = new PromotionRepository($mockDb);
$result = $repository->applyTypeCategoryCondition($basket, $promotion);
$this->assertArrayNotHasKey('discount_type', $result[0]);
}
/**
* Test applyTypeCategoriesAnd — rabat gdy oba warunki spełnione
*/
public function testApplyTypeCategoriesAndAppliesWhenBothConditionsMet(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')->willReturn(null);
$mockStmt = $this->createMock(\PDOStatement::class);
$mockStmt->method('fetchAll')->willReturnOnConsecutiveCalls(
[['category_id' => 10]], // product 1 — condition_categories ✓
[['category_id' => 5]], // product 2 — categories ✓ (condition_2)
[['category_id' => 10]], // product 1 — check categories ✓ (condition check)
[['category_id' => 5]], // product 2 — check categories ✓ (condition check)
[['category_id' => 10]], // product 1 — discount assignment
[['category_id' => 5]] // product 2 — discount assignment
);
$mockDb->method('query')->willReturn($mockStmt);
$promotion = $this->mockPromotion([
'categories' => json_encode([5]),
'condition_categories' => json_encode([10]),
'discount_type' => 1,
'amount' => 25,
'include_coupon' => 1,
'include_product_promo' => 0,
]);
$basket = $this->makeBasket([
['id' => 1],
['id' => 2],
]);
$repository = new PromotionRepository($mockDb);
$result = $repository->applyTypeCategoriesAnd($basket, $promotion);
$this->assertSame(1, $result[0]['discount_type']);
$this->assertSame(25, $result[0]['discount_amount']);
$this->assertSame(1, $result[1]['discount_type']);
}
}

View File

@@ -331,4 +331,93 @@ class TransportRepositoryTest extends TestCase
$this->assertSame(1, $rows[1]['default']);
$this->assertSame(50, $rows[1]['max_wp']);
}
// =========================================================================
// Frontend methods (migrated from front\factory\ShopTransport)
// =========================================================================
/**
* Test transportCostCached — zwraca koszt transportu
*/
public function testTransportCostCachedReturnsCost(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->willReturn('15.99');
$repository = new TransportRepository($mockDb);
$this->assertSame(15.99, $repository->transportCostCached(1));
}
/**
* Test findActiveByIdCached — zwraca transport
*/
public function testFindActiveByIdCachedReturnsTransport(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->willReturn([
'id' => '3', 'name' => 'DPD', 'status' => '1',
'cost' => '15.99', 'max_wp' => null, 'default' => '0',
'delivery_free' => '1', 'apilo_carrier_account_id' => null, 'o' => '2',
]);
$repository = new TransportRepository($mockDb);
$result = $repository->findActiveByIdCached(3);
$this->assertIsArray($result);
$this->assertSame(3, $result['id']);
$this->assertSame(15.99, $result['cost']);
}
/**
* Test findActiveByIdCached — null dla nieistniejącego
*/
public function testFindActiveByIdCachedReturnsNullForInvalid(): void
{
$mockDb = $this->createMock(\medoo::class);
$repository = new TransportRepository($mockDb);
$this->assertNull($repository->findActiveByIdCached(0));
}
/**
* Test forPaymentMethod — zwraca transporty powiązane z płatnością
*/
public function testForPaymentMethodReturnsTransports(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('select')
->willReturnCallback(function ($table, $columns, $where) {
if ($table === 'pp_shop_transport_payment_methods') {
return [1, 3];
}
if ($table === 'pp_shop_transports') {
return [
['id' => '1', 'name' => 'A', 'status' => '1', 'cost' => '5', 'max_wp' => null, 'default' => '0', 'delivery_free' => '0', 'apilo_carrier_account_id' => null, 'o' => '1'],
];
}
return [];
});
$repository = new TransportRepository($mockDb);
$result = $repository->forPaymentMethod(2);
$this->assertIsArray($result);
$this->assertCount(1, $result);
$this->assertSame(1, $result[0]['id']);
}
/**
* Test forPaymentMethod — pusta tablica dla nieprawidłowego ID
*/
public function testForPaymentMethodReturnsEmptyForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$repository = new TransportRepository($mockDb);
$this->assertEquals([], $repository->forPaymentMethod(0));
}
}