Files
clinicpro/src/Billing/Command/ResyncSessionInvoicesCommand.php
T

109 lines
4.3 KiB
PHP

<?php
namespace App\Billing\Command;
use App\Billing\Entity\Invoice;
use App\Billing\Repository\InvoiceRepository;
use App\Billing\Service\InvoiceService;
use App\Patient\Repository\PatientSessionRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* صورتحساب‌هایی که با مراجعهٔ خودشان هم‌تراز نیستند را دوباره می‌سازد.
*
* صورتحساب عکسِ لحظهٔ ساخت بود و ویرایشِ بعدیِ مراجعه به آن نمی‌رسید؛ نتیجه‌اش
* صورتحسابی با مبلغِ قدیمی بود که چون پرداختی‌ها از آن بیشتر بودند «پرداخت‌شده» دیده
* می‌شد، و آمار فهرست پرداخت‌ها (مجموع/تسویه‌نشده) هم از همان ستون‌ها غلط درمی‌آمد.
* از این پس هر ویرایشِ مراجعه صورتحسابش را هم‌تراز می‌کند؛ این دستور فقط برای جبرانِ
* انحرافِ گذشته است.
*
* ddev exec php bin/console app:billing:resync-invoices --dry-run
*/
#[AsCommand(
name: 'app:billing:resync-invoices',
description: 'Rebuild invoices whose totals drifted from their patient session',
)]
class ResyncSessionInvoicesCommand extends Command
{
public function __construct(
private readonly InvoiceRepository $invoiceRepo,
private readonly PatientSessionRepository $sessionRepo,
private readonly InvoiceService $invoiceService,
private readonly EntityManagerInterface $em,
) {
parent::__construct();
}
protected function configure(): void
{
$this->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();
}
}
}