ver. 0.295: Admin order product editing — add/remove/modify products, AJAX search, stock adjustment

- Order product CRUD in admin panel (add, delete, edit quantity/prices)
- AJAX product search endpoint for order edit form
- Automatic stock adjustment when editing order products
- Transport cost recalculation based on free delivery threshold
- Fix: promo price = 0 when equal to base price (no real promotion)
- Clean up stale temp/ build artifacts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 19:30:38 +01:00
parent de11afb003
commit 21efe28464
73 changed files with 1037 additions and 9560 deletions

View File

@@ -209,6 +209,138 @@ class OrderRepositoryTest extends TestCase
$this->assertSame($expectedPrefix . '006', $number);
}
// --- Order product CRUD tests ---
public function testGetOrderProductReturnsNullForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('get');
$repository = new OrderRepository($mockDb);
$this->assertNull($repository->getOrderProduct(0));
$this->assertNull($repository->getOrderProduct(-1));
}
public function testGetOrderProductReturnsArray(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')
->with('pp_shop_order_products', '*', ['id' => 5])
->willReturn(['id' => 5, 'order_id' => 1, 'name' => 'Test']);
$repository = new OrderRepository($mockDb);
$result = $repository->getOrderProduct(5);
$this->assertIsArray($result);
$this->assertSame(5, $result['id']);
}
public function testAddOrderProductReturnsNullForInvalidOrderId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('insert');
$repository = new OrderRepository($mockDb);
$this->assertNull($repository->addOrderProduct(0, ['name' => 'Test']));
}
public function testAddOrderProductInsertsAndReturnsId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())->method('insert')
->with('pp_shop_order_products', $this->callback(function ($data) {
return $data['order_id'] === 10
&& $data['product_id'] === 5
&& $data['name'] === 'Test Product'
&& $data['quantity'] === 2;
}));
$mockDb->method('id')->willReturn('99');
$repository = new OrderRepository($mockDb);
$result = $repository->addOrderProduct(10, [
'product_id' => 5,
'name' => 'Test Product',
'quantity' => 2,
'price_brutto' => 19.99,
]);
$this->assertSame(99, $result);
}
public function testUpdateOrderProductReturnsFalseForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('update');
$repository = new OrderRepository($mockDb);
$this->assertFalse($repository->updateOrderProduct(0, ['quantity' => 3]));
}
public function testUpdateOrderProductUpdatesFields(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())->method('update')
->with('pp_shop_order_products', $this->callback(function ($data) {
return $data['quantity'] === 3
&& $data['price_brutto'] === 25.50;
}), ['id' => 7]);
$repository = new OrderRepository($mockDb);
$result = $repository->updateOrderProduct(7, [
'quantity' => 3,
'price_brutto' => 25.50,
]);
$this->assertTrue($result);
}
public function testUpdateOrderProductReturnsFalseForEmptyData(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('update');
$repository = new OrderRepository($mockDb);
$this->assertFalse($repository->updateOrderProduct(7, []));
}
public function testDeleteOrderProductReturnsFalseForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('delete');
$repository = new OrderRepository($mockDb);
$this->assertFalse($repository->deleteOrderProduct(0));
}
public function testDeleteOrderProductCallsDelete(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())->method('delete')
->with('pp_shop_order_products', ['id' => 12]);
$repository = new OrderRepository($mockDb);
$this->assertTrue($repository->deleteOrderProduct(12));
}
public function testUpdateTransportCostDoesNothingForInvalidId(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->never())->method('update');
$repository = new OrderRepository($mockDb);
$repository->updateTransportCost(0, 15.0);
}
public function testUpdateTransportCostUpdatesOrder(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())->method('update')
->with('pp_shop_orders', ['transport_cost' => 12.50], ['id' => 5]);
$repository = new OrderRepository($mockDb);
$repository->updateTransportCost(5, 12.50);
}
public function testGenerateOrderNumberStartsAt001(): void
{
$mockDb = $this->createMock(\medoo::class);