fix dubkucate codes in bank account

This commit is contained in:
Hesabix 2025-08-07 17:12:37 +00:00
parent 55cf1e5d6d
commit 8ead39e274
6 changed files with 376 additions and 1 deletions

View file

@ -61,6 +61,13 @@ services:
tags:
- { name: kernel.event_listener, event: kernel.exception }
App\EventListener\BankAccountListener:
arguments:
$bankAccountService: '@App\Service\BankAccountService'
$entityManager: '@doctrine.orm.default_entity_manager'
tags:
- { name: doctrine.event_listener, event: postLoad, priority: 100 }
App\Security\AuthenticationFailureHandler:
arguments:
$captchaService: '@App\Service\CaptchaService'

View file

@ -0,0 +1,87 @@
<?php
namespace App\Command;
use App\Entity\Business;
use App\Service\BankAccountService;
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\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:fix-bank-duplicate-codes',
description: 'اصلاح کدهای تکراری حساب‌های بانکی در تمام کسب و کارها'
)]
class FixBankDuplicateCodesCommand extends Command
{
private EntityManagerInterface $entityManager;
private BankAccountService $bankAccountService;
public function __construct(EntityManagerInterface $entityManager, BankAccountService $bankAccountService)
{
parent::__construct();
$this->entityManager = $entityManager;
$this->bankAccountService = $bankAccountService;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title('اصلاح کدهای تکراری حساب‌های بانکی');
// دریافت تمام کسب و کارها
$businesses = $this->entityManager->getRepository(Business::class)->findAll();
$totalFixed = 0;
$totalErrors = 0;
foreach ($businesses as $business) {
$io->section("بررسی کسب و کار: {$business->getName()} (ID: {$business->getId()})");
// بررسی کدهای تکراری
$duplicates = $this->bankAccountService->checkDuplicateCodes($business);
if (empty($duplicates)) {
$io->info('هیچ کد تکراری یافت نشد');
continue;
}
$io->warning("تعداد کدهای تکراری یافت شده: " . count($duplicates));
foreach ($duplicates as $duplicate) {
$io->text("کد تکراری: {$duplicate['code']} - تعداد: {$duplicate['count']}");
foreach ($duplicate['accounts'] as $account) {
$io->text(" - حساب: {$account['name']} (ID: {$account['id']})");
}
}
// اصلاح کدهای تکراری
$fixResult = $this->bankAccountService->fixDuplicateCodes($business);
if ($fixResult['success']) {
$io->success("تعداد اصلاح شده: {$fixResult['fixed_count']}");
$totalFixed += $fixResult['fixed_count'];
} else {
$io->error("خطا در اصلاح کدهای تکراری:");
foreach ($fixResult['errors'] as $error) {
$io->text(" - {$error}");
}
$totalErrors += count($fixResult['errors']);
}
}
$io->newLine();
$io->title('نتیجه نهایی');
$io->success("کل تعداد اصلاح شده: {$totalFixed}");
if ($totalErrors > 0) {
$io->error("کل تعداد خطاها: {$totalErrors}");
}
return Command::SUCCESS;
}
}

View file

@ -140,6 +140,7 @@ class BankController extends AbstractController
if (count_chars(trim($params['name'])) == 0)
return $this->json(['result' => 3]);
if ($code == 0) {
// بررسی وجود حساب با نام یکسان
$data = $entityManager->getRepository(BankAccount::class)->findOneBy([
'name' => $params['name'],
'bid' => $acc['bid']
@ -147,8 +148,35 @@ class BankController extends AbstractController
//check exist before
if ($data)
return $this->json(['result' => 2]);
// تولید کد یکتا
$newCode = $provider->getAccountingCode($request->headers->get('activeBid'), 'bank');
// بررسی وجود حساب با کد یکسان در همان کسب و کار
$existingBankWithCode = $entityManager->getRepository(BankAccount::class)->findOneBy([
'code' => $newCode,
'bid' => $acc['bid']
]);
// اگر کد تکراری باشد، کد جدید تولید می‌کنیم
if ($existingBankWithCode) {
// تولید کد جدید
$newCode = $provider->getAccountingCode($request->headers->get('activeBid'), 'bank');
// بررسی مجدد
$existingBankWithCode = $entityManager->getRepository(BankAccount::class)->findOneBy([
'code' => $newCode,
'bid' => $acc['bid']
]);
// اگر هنوز تکراری باشد، خطا برگردان
if ($existingBankWithCode) {
return $this->json(['result' => 4, 'message' => 'خطا در تولید کد یکتا برای حساب بانکی']);
}
}
$data = new BankAccount();
$data->setCode($provider->getAccountingCode($request->headers->get('activeBid'), 'bank'));
$data->setCode($newCode);
$data->setMoney($acc['money']);
} else {
$data = $entityManager->getRepository(BankAccount::class)->findOneBy([

View file

@ -9,6 +9,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Ignore;
#[ORM\Entity(repositoryClass: BankAccountRepository::class)]
#[ORM\HasLifecycleCallbacks]
class BankAccount
{
#[ORM\Id]
@ -290,4 +291,31 @@ class BankAccount
return $this;
}
/**
* قبل از ذخیره حساب بانکی
*/
#[ORM\PrePersist]
public function prePersist(): void
{
// این متد توسط Event Listener فراخوانی می‌شود
}
/**
* قبل از به‌روزرسانی حساب بانکی
*/
#[ORM\PreUpdate]
public function preUpdate(): void
{
// این متد توسط Event Listener فراخوانی می‌شود
}
/**
* بعد از بارگذاری حساب بانکی
*/
#[ORM\PostLoad]
public function postLoad(): void
{
// این متد توسط Event Listener فراخوانی می‌شود
}
}

View file

@ -0,0 +1,75 @@
<?php
namespace App\EventListener;
use App\Entity\BankAccount;
use App\Service\BankAccountService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\PostLoadEventArgs;
class BankAccountListener
{
private BankAccountService $bankAccountService;
private EntityManagerInterface $entityManager;
private static array $processedBusinesses = [];
public function __construct(BankAccountService $bankAccountService, EntityManagerInterface $entityManager)
{
$this->bankAccountService = $bankAccountService;
$this->entityManager = $entityManager;
}
/**
* بعد از بارگذاری حساب بانکی
*/
public function postLoad(PostLoadEventArgs $event): void
{
$entity = $event->getObject();
if ($entity instanceof BankAccount && $entity->getBid()) {
$this->fixDuplicateCodesInBusiness($entity->getBid());
}
}
/**
* بررسی و اصلاح کدهای تکراری در یک کسب و کار
*/
private function fixDuplicateCodesInBusiness($business): void
{
$businessId = $business->getId();
// اگر این کسب و کار قبلاً پردازش شده، از پردازش مجدد جلوگیری کنیم
if (isset(self::$processedBusinesses[$businessId])) {
return;
}
try {
// بررسی کدهای تکراری
$duplicates = $this->bankAccountService->checkDuplicateCodes($business);
if (!empty($duplicates)) {
// اصلاح کدهای تکراری
$fixResult = $this->bankAccountService->fixDuplicateCodes($business);
if ($fixResult['success'] && $fixResult['fixed_count'] > 0) {
// اگر تغییراتی اعمال شده، EntityManager را flush کنیم
$this->entityManager->flush();
}
}
// علامت‌گذاری که این کسب و کار پردازش شده است
self::$processedBusinesses[$businessId] = true;
} catch (\Exception $e) {
// در صورت خطا، فقط log کنیم و ادامه دهیم
error_log("خطا در بررسی کدهای تکراری حساب بانکی: " . $e->getMessage());
}
}
/**
* پاک کردن cache پردازش شده‌ها
*/
public static function clearProcessedCache(): void
{
self::$processedBusinesses = [];
}
}

View file

@ -0,0 +1,150 @@
<?php
namespace App\Service;
use App\Entity\BankAccount;
use App\Entity\Business;
use Doctrine\ORM\EntityManagerInterface;
class BankAccountService
{
private EntityManagerInterface $entityManager;
private Provider $provider;
public function __construct(EntityManagerInterface $entityManager, Provider $provider)
{
$this->entityManager = $entityManager;
$this->provider = $provider;
}
/**
* بررسی و اصلاح کدهای تکراری حساب‌های بانکی در یک کسب و کار
* @param Business $business
* @return array
*/
public function fixDuplicateCodes(Business $business): array
{
$fixedCount = 0;
$errors = [];
$maxAttempts = 10; // حداکثر تعداد تلاش برای حل کدهای تکراری متوالی
for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
// دریافت تمام حساب‌های بانکی این کسب و کار
$bankAccounts = $this->entityManager->getRepository(BankAccount::class)->findBy([
'bid' => $business
]);
// گروه‌بندی بر اساس کد
$codeGroups = [];
foreach ($bankAccounts as $bankAccount) {
$code = $bankAccount->getCode();
if (!isset($codeGroups[$code])) {
$codeGroups[$code] = [];
}
$codeGroups[$code][] = $bankAccount;
}
$hasDuplicates = false;
// بررسی کدهای تکراری و اصلاح آنها
foreach ($codeGroups as $code => $accounts) {
if (count($accounts) > 1) {
$hasDuplicates = true;
// کد تکراری پیدا شده، اصلاح می‌کنیم
for ($i = 1; $i < count($accounts); $i++) {
$account = $accounts[$i];
// تولید کد جدید
$newCode = $this->provider->getAccountingCode($business->getId(), 'bank');
// بررسی اینکه کد جدید تکراری نباشد
$existingAccount = $this->entityManager->getRepository(BankAccount::class)->findOneBy([
'code' => $newCode,
'bid' => $business
]);
if ($existingAccount) {
// اگر کد جدید هم تکراری باشد، یک بار دیگر تلاش می‌کنیم
$newCode = $this->provider->getAccountingCode($business->getId(), 'bank');
$existingAccount = $this->entityManager->getRepository(BankAccount::class)->findOneBy([
'code' => $newCode,
'bid' => $business
]);
if ($existingAccount) {
$errors[] = "نمی‌توان کد یکتا برای حساب بانکی {$account->getName()} تولید کرد";
continue;
}
}
// تنظیم کد جدید
$account->setCode($newCode);
$this->entityManager->persist($account);
$fixedCount++;
}
}
}
// اگر هیچ کد تکراری وجود نداشت، حلقه را متوقف کنیم
if (!$hasDuplicates) {
break;
}
// ذخیره تغییرات
if ($fixedCount > 0) {
$this->entityManager->flush();
}
}
return [
'fixed_count' => $fixedCount,
'errors' => $errors,
'success' => count($errors) === 0,
'attempts' => $attempt + 1
];
}
/**
* بررسی وجود کدهای تکراری در یک کسب و کار
* @param Business $business
* @return array
*/
public function checkDuplicateCodes(Business $business): array
{
$duplicates = [];
// دریافت تمام حساب‌های بانکی این کسب و کار
$bankAccounts = $this->entityManager->getRepository(BankAccount::class)->findBy([
'bid' => $business
]);
// گروه‌بندی بر اساس کد
$codeGroups = [];
foreach ($bankAccounts as $bankAccount) {
$code = $bankAccount->getCode();
if (!isset($codeGroups[$code])) {
$codeGroups[$code] = [];
}
$codeGroups[$code][] = $bankAccount;
}
// بررسی کدهای تکراری
foreach ($codeGroups as $code => $accounts) {
if (count($accounts) > 1) {
$duplicates[] = [
'code' => $code,
'count' => count($accounts),
'accounts' => array_map(function($account) {
return [
'id' => $account->getId(),
'name' => $account->getName()
];
}, $accounts)
];
}
}
return $duplicates;
}
}