10000, 'safety_milli' => 20000, 'target_milli' => 100000, 'maximum_milli' => 120000, 'lead_days' => 4, 'approval_days' => 1, 'minimum_milli' => 24000, 'pack_milli' => 12000, 'horizon_days' => 30]; } public function test_sgf_executes_the_transferred_purchasing_engine_without_a_source_checkout(): void { $release = Release::current(dirname(__DIR__, 2)); self::assertNotNull($release); self::assertContains('inventory.replenishment_projection', $release['capabilities']); $class = new \ReflectionClass(ReplenishmentProjection::class); self::assertStringStartsWith(realpath(dirname(__DIR__, 2).'/packages/purchasing/releases'), $class->getFileName()); $result = (new ReplenishmentProjection)->calculate($this->policy(), 120000, '2026-11-01'); self::assertSame('2026-11-10', $result['risk_date']); self::assertSame('2026-11-06', $result['order_date']); self::assertSame('2026-11-05', $result['request_date']); self::assertSame(84000, $result['quantity_milli']); } public function test_sgf_preserves_exact_historical_rate_and_lot_expiry_results(): void { $engine = new ReplenishmentProjection; $policy = [...$this->policy(), 'horizon_days' => 3, 'daily_rate' => ['numerator_milli' => 1000, 'denominator_days' => 3]]; $rate = $engine->calculate($policy, 10000, '2026-11-01'); self::assertSame([334, 333, 333], array_column($rate['series'], 'demand_milli')); self::assertSame(9000, $rate['series'][2]['balance_milli']); $lots = $engine->calculate([...$this->policy(), 'horizon_days' => 2], 10000, '2026-11-01', [], [], 0, [], [['quantity' => 10000, 'last_usable_on' => '2026-11-01']]); self::assertSame(0, $lots['series'][1]['expired_milli']); self::assertSame(-10000, $lots['series'][1]['balance_milli']); } }