createUser(['ROLE_DOCTOR']); $doctor = new Doctor($owner, 'دکتر'); $this->em->persist($doctor); $this->em->flush(); $patient = $this->createUser(['ROLE_USER']); $record = new PatientRecord('doctor', $doctor->getId(), $patient, 'doctor', $doctor->getId()); $this->em->persist($record); $this->em->flush(); return [$owner, $doctor, $record]; } private function itemFor(Doctor $doctor, string $name, int $price): InventoryItem { $item = new InventoryItem('doctor', $doctor->getId(), $name); $item->setPrice($price)->setStock(100); $this->em->persist($item); $this->em->flush(); return $item; } // ── موفق ──────────────────────────────────────────────────────────────── public function testCreateSessionWithConsumablesPackageAndSessionAt(): void { [$owner, $doctor, $record] = $this->recordFor(); $glasses = $this->itemFor($doctor, 'عینک', 1_200_000); $pencil = $this->itemFor($doctor, 'مداد سفید', 300_000); $package = new InventoryPackage('doctor', $doctor->getId(), 'پکیج زیبایی'); $this->em->persist($package); $this->em->flush(); $res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [ 'visit_price_rials' => 500_000, 'session_at' => 1_760_000_000, 'inventory_package_uuid' => $package->getUuid(), 'consumables' => [ ['inventory_item_uuid' => $glasses->getUuid(), 'quantity' => 2], ['inventory_item_uuid' => $pencil->getUuid()], ], ]); self::assertSame(201, $this->responseCode()); self::assertSame(1_760_000_000, $res['data']['session_at']); self::assertSame($package->getUuid(), $res['data']['inventory_package_uuid']); self::assertSame('پکیج زیبایی', $res['data']['inventory_package_title']); self::assertCount(2, $res['data']['consumables']); self::assertSame(2_700_000, $res['data']['consumables_total_rials']); // 2×1٬200٬000 + 300٬000 $byName = array_column($res['data']['consumables'], null, 'item_name'); self::assertSame(2, $byName['عینک']['quantity']); self::assertSame(2_400_000, $byName['عینک']['line_total_rials']); self::assertSame(1, $byName['مداد سفید']['quantity']); // کالاها بدون پوشش بیمه → کامل روی سهم بیمار self::assertSame(3_200_000, $res['data']['final_price_rials']); // ویزیت 500٬000 + کالاها 2٬700٬000 } public function testConsumablesAppearInSessionsList(): void { [$owner, $doctor, $record] = $this->recordFor(); $item = $this->itemFor($doctor, 'سرنگ', 50_000); $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [ 'visit_price_rials' => 0, 'consumables' => [['inventory_item_uuid' => $item->getUuid(), 'quantity' => 3]], ]); self::assertSame(201, $this->responseCode()); $sessions = $this->authJson('GET', '/api/v1/patient/' . $record->getUuid() . '/sessions', $owner); self::assertCount(1, $sessions['data'][0]['consumables']); self::assertSame(150_000, $sessions['data'][0]['consumables_total_rials']); self::assertSame(150_000, $sessions['data'][0]['patient_debt_rials']); } // ── خطا ───────────────────────────────────────────────────────────────── public function testForeignTenantConsumableAndPackageSilentlySkipped(): void { [$owner, , $record] = $this->recordFor(); // tenant دیگر $otherOwner = $this->createUser(['ROLE_DOCTOR']); $otherDoctor = new Doctor($otherOwner, 'دکتر دیگر'); $this->em->persist($otherDoctor); $this->em->flush(); $foreignItem = $this->itemFor($otherDoctor, 'آمپول', 900_000); $foreignPackage = new InventoryPackage('doctor', $otherDoctor->getId(), 'پکیج دیگری'); $this->em->persist($foreignPackage); $this->em->flush(); $res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [ 'visit_price_rials' => 400_000, 'inventory_package_uuid' => $foreignPackage->getUuid(), 'consumables' => [['inventory_item_uuid' => $foreignItem->getUuid(), 'quantity' => 5]], ]); self::assertSame(201, $this->responseCode()); self::assertCount(0, $res['data']['consumables']); self::assertSame(0, $res['data']['consumables_total_rials']); self::assertNull($res['data']['inventory_package_uuid']); self::assertSame(400_000, $res['data']['final_price_rials']); } public function testUnknownConsumableUuidSkipped(): void { [$owner, , $record] = $this->recordFor(); $res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [ 'visit_price_rials' => 200_000, 'consumables' => [['inventory_item_uuid' => '00000000-0000-0000-0000-000000000000']], ]); self::assertSame(201, $this->responseCode()); self::assertCount(0, $res['data']['consumables']); self::assertSame(200_000, $res['data']['final_price_rials']); } // ── مرزی ──────────────────────────────────────────────────────────────── public function testEmptyConsumablesAndNoSessionAtDefaults(): void { [$owner, , $record] = $this->recordFor(); $res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [ 'visit_price_rials' => 100_000, 'consumables' => [], ]); self::assertSame(201, $this->responseCode()); self::assertSame([], $res['data']['consumables']); self::assertSame(0, $res['data']['consumables_total_rials']); self::assertNull($res['data']['session_at']); self::assertNull($res['data']['inventory_package_uuid']); } public function testZeroQuantityCoercedToOne(): void { [$owner, $doctor, $record] = $this->recordFor(); $item = $this->itemFor($doctor, 'گاز استریل', 80_000); $res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [ 'visit_price_rials' => 0, 'consumables' => [['inventory_item_uuid' => $item->getUuid(), 'quantity' => 0]], ]); self::assertSame(201, $this->responseCode()); self::assertSame(1, $res['data']['consumables'][0]['quantity']); self::assertSame(80_000, $res['data']['consumables_total_rials']); } }