diff --git a/app/Services/Care/DemoTenantSeeder.php b/app/Services/Care/DemoTenantSeeder.php index 2e752e1..162cd5a 100644 --- a/app/Services/Care/DemoTenantSeeder.php +++ b/app/Services/Care/DemoTenantSeeder.php @@ -987,10 +987,106 @@ class DemoTenantSeeder } $this->seedDentistryClinicalDemo($organization, $ownerRef); + $this->seedBloodBankInventoryDemo($organization, $ownerRef); return $count; } + /** + * Visit-scoped facility stock for Blood Bank Inventory tab / reports. + * Seeds inventory_note on each open BB visit so findForVisit shows stock without manual entry. + */ + private function seedBloodBankInventoryDemo(Organization $organization, string $ownerRef): void + { + // Specialty departments are provisioned without organization_id; scope via branches. + $branchIds = Branch::owned($ownerRef) + ->where('organization_id', $organization->id) + ->pluck('id'); + + if ($branchIds->isEmpty()) { + return; + } + + $departmentIds = Department::owned($ownerRef) + ->whereIn('branch_id', $branchIds) + ->where('type', 'blood_bank') + ->where('is_active', true) + ->pluck('id'); + + if ($departmentIds->isEmpty()) { + return; + } + + $appointments = Appointment::owned($ownerRef) + ->where('organization_id', $organization->id) + ->whereIn('department_id', $departmentIds) + ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN]) + ->whereNotNull('visit_id') + ->with('visit') + ->orderBy('branch_id') + ->orderBy('id') + ->get(); + + if ($appointments->isEmpty()) { + return; + } + + $clinical = app(SpecialtyClinicalRecordService::class); + /** @var array $branchOrder */ + $branchOrder = []; + + foreach ($appointments as $appointment) { + $visit = $appointment->visit; + if (! $visit) { + continue; + } + + $branchId = (int) $appointment->branch_id; + if (! array_key_exists($branchId, $branchOrder)) { + $branchOrder[$branchId] = count($branchOrder); + } + + $clinical->upsert( + $organization, + $visit, + 'blood_bank', + 'inventory_note', + $this->demoBloodBankInventoryPayload($branchOrder[$branchId]), + $ownerRef, + $ownerRef, + ); + } + } + + /** + * @return array + */ + private function demoBloodBankInventoryPayload(int $branchIndex): array + { + // First branch: low whole-blood + alert flag so Inventory / reports demo alerts. + if ($branchIndex === 0) { + return [ + 'whole_blood_units' => 1, + 'packed_rbc_units' => 14, + 'platelet_units' => 7, + 'ffp_units' => 9, + 'cryo_units' => 4, + 'low_stock_alert' => true, + 'notes' => 'Demo morning stock count — whole blood critically low; replenishment requested.', + ]; + } + + return [ + 'whole_blood_units' => 8 + ($branchIndex * 2), + 'packed_rbc_units' => 18 + $branchIndex, + 'platelet_units' => 10 + $branchIndex, + 'ffp_units' => 12 + $branchIndex, + 'cryo_units' => 5, + 'low_stock_alert' => false, + 'notes' => 'Demo facility stock count for blood bank.', + ]; + } + private function seedDentistryClinicalDemo(Organization $organization, string $ownerRef): void { $departmentIds = Department::owned($ownerRef) diff --git a/tests/Feature/DemoSeedCommandTest.php b/tests/Feature/DemoSeedCommandTest.php index a9739de..68c2143 100644 --- a/tests/Feature/DemoSeedCommandTest.php +++ b/tests/Feature/DemoSeedCommandTest.php @@ -12,6 +12,7 @@ use App\Models\InvestigationType; use App\Models\Organization; use App\Models\Patient; use App\Models\Prescription; +use App\Models\SpecialtyClinicalRecord; use App\Models\User; use App\Services\Care\DemoTenantSeeder; use App\Services\Care\PlanService; @@ -120,6 +121,22 @@ class DemoSeedCommandTest extends TestCase "Expected waiting specialty appointments for [{$key}]" ); } + + $inventoryNotes = SpecialtyClinicalRecord::query() + ->where('owner_ref', $user->public_id) + ->where('module_key', 'blood_bank') + ->where('record_type', 'inventory_note') + ->get(); + $this->assertGreaterThan( + 0, + $inventoryNotes->count(), + 'Expected blood bank inventory_note records after pro demo seed' + ); + $this->assertNotNull($inventoryNotes->first()?->payload['packed_rbc_units'] ?? null); + $this->assertTrue( + $inventoryNotes->contains(fn (SpecialtyClinicalRecord $r) => ! empty($r->payload['low_stock_alert'])), + 'Expected at least one low-stock inventory demo flag' + ); } public function test_free_demo_does_not_enable_specialty_modules(): void