feat: phase 4 — orders with status workflow, idempotent complete_order RPC, delivery note PDF
This commit is contained in:
@@ -10,11 +10,15 @@ class InsForgeFake
|
||||
/** @var array<string, array<string, array<string, mixed>>> table => id => row */
|
||||
public static array $store = [];
|
||||
|
||||
/** @var list<array{function: string, args: array<string, mixed>}> */
|
||||
public static array $rpcCalls = [];
|
||||
|
||||
public static string $accessToken = '';
|
||||
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$store = [];
|
||||
self::$rpcCalls = [];
|
||||
}
|
||||
|
||||
public static function seed(string $table, array $row): void
|
||||
@@ -67,6 +71,18 @@ class InsForgeFake
|
||||
{
|
||||
Http::fake([
|
||||
'*/api/auth/refresh' => Http::response(['accessToken' => self::$accessToken], 200),
|
||||
'*/api/database/rpc/*' => function (Request $request) {
|
||||
$function = str_replace('/api/database/rpc/', '', (string) parse_url($request->url(), PHP_URL_PATH));
|
||||
$args = $request->data();
|
||||
|
||||
self::$rpcCalls[] = ['function' => $function, 'args' => $args];
|
||||
|
||||
if ($function === 'complete_order') {
|
||||
return self::simulateCompleteOrder($args);
|
||||
}
|
||||
|
||||
return Http::response(['ok' => true], 200);
|
||||
},
|
||||
'*/api/database/records/*' => fn (Request $request) => self::handleRecordsRequest($request),
|
||||
]);
|
||||
}
|
||||
@@ -89,7 +105,12 @@ class InsForgeFake
|
||||
if (str_starts_with((string) $value, 'eq.')) {
|
||||
$column = $key;
|
||||
$expected = substr((string) $value, 3);
|
||||
$rows = array_values(array_filter($rows, fn (array $row): bool => (string) ($row[$column] ?? '') === $expected));
|
||||
|
||||
if ($expected === 'null') {
|
||||
$rows = array_values(array_filter($rows, fn (array $row): bool => ($row[$column] ?? null) === null));
|
||||
} else {
|
||||
$rows = array_values(array_filter($rows, fn (array $row): bool => (string) ($row[$column] ?? '') === $expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,4 +171,63 @@ class InsForgeFake
|
||||
|
||||
return Http::response([], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tiruan semantik server: idempotent via gerbang unik (reference_type, reference_id),
|
||||
* decrement stok sekali, set status completed + history.
|
||||
*/
|
||||
private static function simulateCompleteOrder(array $args)
|
||||
{
|
||||
$orderId = $args['p_order_id'] ?? null;
|
||||
|
||||
if ($orderId === null || ! isset(self::$store['orders'][$orderId])) {
|
||||
return Http::response(['error' => 'ORDER_NOT_FOUND'], 500);
|
||||
}
|
||||
|
||||
$order = self::$store['orders'][$orderId];
|
||||
|
||||
if (($order['order_status'] ?? '') === 'completed') {
|
||||
return Http::response(['ok' => true], 200);
|
||||
}
|
||||
|
||||
if (($order['order_status'] ?? '') !== 'delivering') {
|
||||
return Http::response(['error' => 'INVALID_TRANSITION'], 500);
|
||||
}
|
||||
|
||||
foreach (self::$store['order_items'] ?? [] as $item) {
|
||||
if (($item['order_id'] ?? null) !== $orderId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$alreadyOut = collect(self::$store['stock_movements'] ?? [])->contains(
|
||||
fn (array $m): bool => $m['type'] === 'order_out'
|
||||
&& ($m['reference_id'] ?? null) === $orderId
|
||||
&& ($m['product_id'] ?? null) === $item['product_id'],
|
||||
);
|
||||
|
||||
if ($alreadyOut) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self::seed('stock_movements', [
|
||||
'id' => fake()->uuid(),
|
||||
'product_id' => $item['product_id'],
|
||||
'type' => 'order_out',
|
||||
'quantity' => -1 * (int) $item['quantity'],
|
||||
'reference_type' => 'order',
|
||||
'reference_id' => $orderId,
|
||||
]);
|
||||
|
||||
foreach (self::$store['stocks'] as &$stock) {
|
||||
if (($stock['product_id'] ?? null) === $item['product_id']) {
|
||||
$stock['quantity'] -= (int) $item['quantity'];
|
||||
}
|
||||
}
|
||||
unset($stock);
|
||||
}
|
||||
|
||||
self::$store['orders'][$orderId]['order_status'] = 'completed';
|
||||
|
||||
return Http::response(['ok' => true], 200);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user