All checks were successful
CI / static-tests (push) Successful in 47s
CI / secret-scan (pull_request) Successful in 9s
CI / php-lint (pull_request) Successful in 23s
CI / static-tests (pull_request) Successful in 47s
CI / js-tests (pull_request) Successful in 25s
CI / secret-scan (push) Successful in 10s
CI / php-lint (push) Successful in 22s
CI / js-tests (push) Successful in 29s
Ouvre le domaine commande cote back-office (les commandes existent depuis P4 1a/1b et, depuis la borne L4, sont reellement creees). - App\Order\OrderQueryRepository (read-side, DatabaseInterface seul) : recent($limit) pour la liste admin + salesKpis() (CA encaisse paid+delivered, nb payees, panier moyen, CA/nb du jour, total, repartition par statut). - OrderAdminController : GET /admin/orders, permission order.read, liste lecture seule (numero, mode, chevalet, statut, total ttc, date). Les transitions deliver/cancel restent aux ecrans operationnels (hors back-office MVP). - StatsController : ferme la dette "KPIs de vente differes P4" -> section Ventes dans /admin/stats (CA, commandes payees, panier moyen, total). Hook orderQuery() = seam. - Nav "Commandes" (order.read) reactivee dans la sidebar (etait volontairement absente tant que la route n'existait pas). - Tests : OrderQueryRepositoryDbTest (integration, vraie DB : 3) + OrderAdminControllerTest (double : guard 403/200 + rendu) + StatsControllerTest (section Ventes). 350 PHP verts, PHPStan L6 propre. Routes verifiees live (302 -> login, gardees par SessionGuard).
103 lines
3.7 KiB
PHP
103 lines
3.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Integration;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use Throwable;
|
|
use App\Core\Config;
|
|
use App\Core\Database;
|
|
use App\Order\OrderQueryRepository;
|
|
|
|
/**
|
|
* OrderQueryRepository (read-side admin) contre une vraie MariaDB (schema migre).
|
|
* Auto-skip si WAKDO_DB_TESTS != 1. Insere des commandes connues (order_number
|
|
* prefixe IT-<suffix>) et verifie les KPIs de vente (delta vs baseline) + la liste
|
|
* recente. Nettoyage par prefixe en tearDown.
|
|
*/
|
|
final class OrderQueryRepositoryDbTest extends TestCase
|
|
{
|
|
private Database $db;
|
|
private string $suffix = '';
|
|
|
|
protected function setUp(): void
|
|
{
|
|
if (getenv('WAKDO_DB_TESTS') !== '1') {
|
|
self::markTestSkipped('Tests DB desactives (definir WAKDO_DB_TESTS=1 + DB_*).');
|
|
}
|
|
|
|
$this->db = new Database(new Config());
|
|
|
|
try {
|
|
$this->db->fetch('SELECT 1');
|
|
} catch (Throwable $exception) {
|
|
self::markTestSkipped('Base injoignable: ' . $exception->getMessage());
|
|
}
|
|
|
|
$this->suffix = bin2hex(random_bytes(4));
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if ($this->suffix === '') {
|
|
return;
|
|
}
|
|
$this->db->execute(
|
|
'DELETE FROM customer_order WHERE order_number LIKE :p',
|
|
['p' => 'IT-' . $this->suffix . '%'],
|
|
);
|
|
}
|
|
|
|
private function insertOrder(string $number, string $status, int $ttc): void
|
|
{
|
|
$ht = (int) round($ttc / 1.1);
|
|
$this->db->execute(
|
|
"INSERT INTO customer_order (order_number, idempotency_key, source, service_mode, status, "
|
|
. 'total_ht_cents, total_vat_cents, total_ttc_cents) '
|
|
. "VALUES (:num, :key, 'kiosk', 'takeaway', :st, :ht, :vat, :ttc)",
|
|
['num' => $number, 'key' => $number . '-k', 'st' => $status, 'ht' => $ht, 'vat' => $ttc - $ht, 'ttc' => $ttc],
|
|
);
|
|
}
|
|
|
|
public function testSalesKpisCountsPaidRevenueOnly(): void
|
|
{
|
|
$repo = new OrderQueryRepository($this->db);
|
|
$before = $repo->salesKpis();
|
|
|
|
$this->insertOrder('IT-' . $this->suffix . '-P', 'paid', 1000);
|
|
$this->insertOrder('IT-' . $this->suffix . '-N', 'pending_payment', 500);
|
|
|
|
$after = $repo->salesKpis();
|
|
// Le CA ne compte QUE le paid (pas le pending_payment).
|
|
self::assertSame($before['revenue_cents'] + 1000, $after['revenue_cents']);
|
|
self::assertSame($before['paid_count'] + 1, $after['paid_count']);
|
|
self::assertSame($before['total_orders'] + 2, $after['total_orders']);
|
|
self::assertGreaterThanOrEqual(1, $after['by_status']['paid'] ?? 0);
|
|
self::assertGreaterThanOrEqual(1, $after['by_status']['pending_payment'] ?? 0);
|
|
self::assertSame(intdiv($after['revenue_cents'], max(1, $after['paid_count'])), $after['avg_basket_cents']);
|
|
}
|
|
|
|
public function testRecentListsInsertedOrdersWithExpectedColumns(): void
|
|
{
|
|
$repo = new OrderQueryRepository($this->db);
|
|
$num = 'IT-' . $this->suffix . '-P';
|
|
$this->insertOrder($num, 'paid', 1000);
|
|
|
|
$recent = $repo->recent(200);
|
|
$numbers = array_column($recent, 'order_number');
|
|
self::assertContains($num, $numbers, 'la commande inseree doit apparaitre dans recent()');
|
|
|
|
$row = $recent[(int) array_search($num, $numbers, true)];
|
|
self::assertSame('paid', (string) $row['status']);
|
|
self::assertSame(1000, (int) $row['total_ttc_cents']);
|
|
self::assertSame('takeaway', (string) $row['service_mode']);
|
|
self::assertArrayHasKey('created_at', $row);
|
|
}
|
|
|
|
public function testRecentRespectsLimit(): void
|
|
{
|
|
$repo = new OrderQueryRepository($this->db);
|
|
self::assertLessThanOrEqual(3, count($repo->recent(3)));
|
|
}
|
|
}
|