diff --git a/hesabixCore/config/services.yaml b/hesabixCore/config/services.yaml index b4549d2..e7cdd8f 100644 --- a/hesabixCore/config/services.yaml +++ b/hesabixCore/config/services.yaml @@ -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' diff --git a/hesabixCore/src/Command/FixBankDuplicateCodesCommand.php b/hesabixCore/src/Command/FixBankDuplicateCodesCommand.php new file mode 100644 index 0000000..c2e310b --- /dev/null +++ b/hesabixCore/src/Command/FixBankDuplicateCodesCommand.php @@ -0,0 +1,87 @@ +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; + } +} \ No newline at end of file diff --git a/hesabixCore/src/Controller/BankController.php b/hesabixCore/src/Controller/BankController.php index 2e1bc8f..a4c166f 100644 --- a/hesabixCore/src/Controller/BankController.php +++ b/hesabixCore/src/Controller/BankController.php @@ -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([ diff --git a/hesabixCore/src/Entity/BankAccount.php b/hesabixCore/src/Entity/BankAccount.php index b30a13b..8f5d495 100644 --- a/hesabixCore/src/Entity/BankAccount.php +++ b/hesabixCore/src/Entity/BankAccount.php @@ -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 فراخوانی می‌شود + } } diff --git a/hesabixCore/src/EventListener/BankAccountListener.php b/hesabixCore/src/EventListener/BankAccountListener.php new file mode 100644 index 0000000..b5ce0cc --- /dev/null +++ b/hesabixCore/src/EventListener/BankAccountListener.php @@ -0,0 +1,75 @@ +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 = []; + } +} \ No newline at end of file diff --git a/hesabixCore/src/Service/BankAccountService.php b/hesabixCore/src/Service/BankAccountService.php new file mode 100644 index 0000000..c9b97e8 --- /dev/null +++ b/hesabixCore/src/Service/BankAccountService.php @@ -0,0 +1,150 @@ +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; + } +} \ No newline at end of file