addOption('dry-run', null, InputOption::VALUE_NONE, 'فقط گزارش بده، چیزی ننویس'); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $dryRun = (bool) $input->getOption('dry-run'); $drifted = 0; $rows = []; foreach ($this->invoiceRepo->findAll() as $invoice) { if ($invoice->getStatus() === Invoice::STATUS_VOID || $invoice->getPatientSessionId() === null) { continue; } $session = $this->sessionRepo->find($invoice->getPatientSessionId()); if ($session === null) { continue; } $before = $invoice->getTotalRials(); // مبلغِ درست از خودِ منطقِ ساخت می‌آید، نه از یک محاسبهٔ موازی در این دستور. $after = $dryRun ? $this->previewTotal($session, $invoice) : $this->invoiceService->syncFromSession($session, $invoice->getEntityType(), $invoice->getEntityId())?->getTotalRials() ?? $before; if ($before === $after) { continue; } $drifted++; $rows[] = [$invoice->getUuid(), $before, $after]; } if ($rows !== []) { $io->table(['invoice', 'before (rials)', 'after (rials)'], $rows); } $io->success(sprintf( $dryRun ? '%d صورتحساب منحرف است (چیزی نوشته نشد)' : '%d صورتحساب هم‌تراز شد', $drifted, )); return Command::SUCCESS; } /** * مبلغِ درستِ همین صورتحساب بدون ذخیره — برای `--dry-run`. * روی همان entity کار می‌کند و در پایان چیزی flush نمی‌شود. */ private function previewTotal(\App\Patient\Entity\PatientSession $session, Invoice $invoice): int { $this->em->beginTransaction(); try { $synced = $this->invoiceService->syncFromSession($session, $invoice->getEntityType(), $invoice->getEntityId()); return $synced?->getTotalRials() ?? $invoice->getTotalRials(); } finally { $this->em->rollback(); } } }