test: cover the paths that were reasoned about but never executed

Fifteen rows across five tasks said the mechanism was there and the test was
not. Each of these is a case where being wrong would be silent.

- the price rows must add up to the final amount. The chain test checks every
  number individually, which stays green if a new row is added and left out of
  the total; this checks the relationship itself.
- a fixed deposit beats a percentage one, and neither can exceed the final
  amount — charging a deposit larger than the bill puts the patient in debt
  before the visit.
- an appointment booked without a service still gets an invoice. Slot mode has
  no service, and without this the financial report is short a row with nothing
  to say which.
- the four accuracy thresholds, each tested on its own boundary. One step off
  and either everything is red (so nobody looks) or nothing is (so the report
  is pointless). Includes a short-running service, since the deviation is
  measured on its absolute value.
- all six policy templates build a policy that survives the normal validation,
  simulation and activation path. A template is a shortcut, not a second road:
  if one of them produced something the validator rejects, a user could create
  a rule in one click that never works.
- simulation leaves nothing pending for a later flush in the same request. That
  is what the finally-rollback-clear is for, and the failure would surface in
  the next operation rather than in the sandbox.

The course controller was reading $this->credits without it being injected —
phpstan caught it; the package-shortfall path had no test yet and would have
500'd on the first course that had a package.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-01 15:16:26 +03:30
co-authored by Claude Opus 5
parent 5c754244f2
commit 2baa2ce7ca
4 changed files with 282 additions and 0 deletions
@@ -15,6 +15,7 @@ use App\Course\Service\CourseStarter;
use App\Doctor\Repository\DoctorRepository;
use App\Package\Repository\PatientPackageRepository;
use App\Patient\Entity\PatientRecord;
use App\Package\Service\CreditLedgerService;
use App\Patient\Repository\PatientRecordRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
@@ -43,6 +44,7 @@ class TreatmentCourseController extends BaseController
private readonly CourseProgressCalculator $progress,
private readonly BranchResolver $branches,
private readonly TenantOwnershipChecker $ownership,
private readonly CreditLedgerService $credits,
) {}
#[Route('/api/v1/treatment-course', name: 'treatment_course_create', methods: ['POST'])]