From 62375ab9972bb404e63681962adc7eed5d071ba0 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 20 Jul 2025 18:48:50 +0000 Subject: [PATCH] update for Moadian plugin --- .../Plugins/TaxSettingsController.php | 362 ++++++- .../System/RegistrySettingsController.php | 2 + webUI/src/i18n/fa_lang.ts | 2 +- .../views/acc/plugins/tax/invoices/list.vue | 947 ++++++++++-------- webUI/src/views/acc/sell/list.vue | 137 ++- .../views/user/manager/settings/registry.vue | 10 + 6 files changed, 969 insertions(+), 491 deletions(-) diff --git a/hesabixCore/src/Controller/Plugins/TaxSettingsController.php b/hesabixCore/src/Controller/Plugins/TaxSettingsController.php index 85480d51..65f9a98f 100644 --- a/hesabixCore/src/Controller/Plugins/TaxSettingsController.php +++ b/hesabixCore/src/Controller/Plugins/TaxSettingsController.php @@ -19,7 +19,12 @@ use DateInterval; class TaxSettingsController extends AbstractController { - private $moadian_base_url = 'https://sandboxrc.tax.gov.ir/'; + + private function getMoadianBaseUrl(registryMGR $registryMGR): string + { + $sandboxMode = filter_var($registryMGR->get('system_settings', 'tax_system_sandbox_mode'), FILTER_VALIDATE_BOOLEAN); + return $sandboxMode ? 'https://sandboxrc.tax.gov.ir/' : 'https://rc.tax.gov.ir/'; + } #[Route('/api/plugins/tax/settings/get', name: 'plugin_tax_settings_get', methods: ['GET'])] public function plugin_tax_settings_get(EntityManagerInterface $em, Access $access): JsonResponse @@ -216,8 +221,8 @@ class TaxSettingsController extends AbstractController return $csr; } - #[Route('/api/plugins/tax/settings/send-invoice', name: 'plugin_tax_settings_send_invoice', methods: ['POST'])] - public function plugin_tax_settings_send_invoice(Request $request, Access $access, Log $log, EntityManagerInterface $em): JsonResponse + #[Route('/api/plugins/tax/list/send-invoice', name: 'plugin_tax_list_send_invoice', methods: ['POST'])] + public function plugin_tax_list_send_invoice(Request $request, Access $access, Log $log, EntityManagerInterface $em): JsonResponse { $acc = $access->hasRole('plugTaxSettings'); if (!$acc) { @@ -225,9 +230,9 @@ class TaxSettingsController extends AbstractController } $params = $request->getPayload()->all(); - $invoiceCode = $params['code'] ?? null; + $invoiceCodes = $params['codes'] ?? []; - if (!$invoiceCode) { + if (empty($invoiceCodes)) { return $this->json([ 'success' => false, 'message' => 'کد فاکتور الزامی است' @@ -239,20 +244,6 @@ class TaxSettingsController extends AbstractController $userId = $user instanceof \App\Entity\User ? $user->getId() : null; try { - $invoiceRepo = $em->getRepository(HesabdariDoc::class); - $invoice = $invoiceRepo->findOneBy([ - 'code' => $invoiceCode, - 'bid' => $businessId, - 'type' => 'sell' - ]); - - if (!$invoice) { - return $this->json([ - 'success' => false, - 'message' => 'فاکتور مورد نظر یافت نشد' - ]); - } - $taxRepo = $em->getRepository(PluginTaxsettingsKey::class); $taxSettings = $taxRepo->findOneBy([ 'business_id' => $businessId, @@ -266,28 +257,89 @@ class TaxSettingsController extends AbstractController ]); } - $result = $this->saveInvoiceToSql($invoice, $taxSettings, $em, $businessId, $userId); + $invoiceRepo = $em->getRepository(HesabdariDoc::class); + $results = []; + $successCount = 0; + $errorCount = 0; + $processedCodes = []; - if ($result['success']) { - $log->insert('اضافه به لیست ارسال', 'فاکتور ' . $invoiceCode . ' به لیست ارسال به سامانه مودیان اضافه شد', $this->getUser(), $businessId); + foreach ($invoiceCodes as $invoiceCode) { + $invoiceCode = trim($invoiceCode); + if (empty($invoiceCode)) { + continue; + } - return $this->json([ - 'success' => true, - 'message' => 'فاکتور با موفقیت به لیست ارسال اضافه شد', - 'data' => $result['data'] ?? null - ]); - } else { - return $this->json([ - 'success' => false, - 'message' => $result['message'] ?? 'خطا در اضافه کردن به لیست ارسال' - ]); + try { + $invoice = $invoiceRepo->findOneBy([ + 'code' => $invoiceCode, + 'bid' => $businessId, + 'type' => 'sell' + ]); + + if (!$invoice) { + $results[] = [ + 'code' => $invoiceCode, + 'success' => false, + 'message' => 'فاکتور مورد نظر یافت نشد' + ]; + $errorCount++; + continue; + } + + $result = $this->saveInvoiceToSql($invoice, $taxSettings, $em, $businessId, $userId); + + if ($result['success']) { + $results[] = [ + 'code' => $invoiceCode, + 'success' => true, + 'message' => 'فاکتور با موفقیت به لیست ارسال اضافه شد', + 'data' => $result['data'] ?? null + ]; + $successCount++; + $processedCodes[] = $invoiceCode; + } else { + $results[] = [ + 'code' => $invoiceCode, + 'success' => false, + 'message' => $result['message'] ?? 'خطا در اضافه کردن به لیست ارسال' + ]; + $errorCount++; + } + } catch (\Exception $e) { + $results[] = [ + 'code' => $invoiceCode, + 'success' => false, + 'message' => 'خطا در پردازش فاکتور: ' . $e->getMessage() + ]; + $errorCount++; + } } + + if (!empty($processedCodes)) { + $codesText = implode(', ', $processedCodes); + $log->insert('اضافه به لیست ارسال', 'فاکتورهای ' . $codesText . ' به لیست ارسال به سامانه مودیان اضافه شد', $this->getUser(), $businessId); + } + + $totalProcessed = count($invoiceCodes); + $message = "پردازش {$totalProcessed} فاکتور تکمیل شد. موفق: {$successCount}, ناموفق: {$errorCount}"; + + return $this->json([ + 'success' => true, + 'message' => $message, + 'summary' => [ + 'total' => $totalProcessed, + 'success' => $successCount, + 'error' => $errorCount + ], + 'results' => $results + ]); + } catch (\Exception $e) { - $log->insert('خطا در اضافه به لیست ارسال', 'خطا در اضافه کردن فاکتور ' . $invoiceCode . ' به لیست ارسال: ' . $e->getMessage(), $this->getUser(), $businessId); + $log->insert('خطا در اضافه به لیست ارسال', 'خطا در پردازش فاکتورها: ' . $e->getMessage(), $this->getUser(), $businessId); return $this->json([ 'success' => false, - 'message' => 'خطا در اضافه کردن به لیست ارسال: ' . $e->getMessage() + 'message' => 'خطا در پردازش فاکتورها: ' . $e->getMessage() ]); } } @@ -663,7 +715,7 @@ class TaxSettingsController extends AbstractController } #[Route('/api/plugins/tax/invoice/send/{id}', name: 'plugin_tax_invoice_send', methods: ['POST'])] - public function sendTaxInvoice(int $id, Access $access, Log $log, EntityManagerInterface $em): JsonResponse + public function sendTaxInvoice(int $id, Access $access, Log $log, EntityManagerInterface $em, registryMGR $registryMGR): JsonResponse { $acc = $access->hasRole('plugTaxSettings'); if (!$acc) { @@ -722,7 +774,7 @@ class TaxSettingsController extends AbstractController $privateKey, '', $username, - $this->moadian_base_url + $this->getMoadianBaseUrl($registryMGR) ); $serverInfo = $moadian->getServerInformation(); @@ -741,7 +793,7 @@ class TaxSettingsController extends AbstractController $privateKey, $taxOrgKeyId, $username, - $this->moadian_base_url + $this->getMoadianBaseUrl($registryMGR) ); $token = $moadian->login(); @@ -880,7 +932,7 @@ class TaxSettingsController extends AbstractController } #[Route('/api/plugins/tax/inquire-status', name: 'plugin_tax_inquire_status', methods: ['POST'])] - public function inquireInvoiceStatus(Request $request, Access $access, EntityManagerInterface $em): JsonResponse + public function inquireInvoiceStatus(Request $request, Access $access, EntityManagerInterface $em, registryMGR $registryMGR): JsonResponse { $acc = $access->hasRole('plugTaxSettings'); if (!$acc) { @@ -922,7 +974,7 @@ class TaxSettingsController extends AbstractController $privateKey, $taxOrgKeyId, $username, - $this->moadian_base_url + $this->getMoadianBaseUrl($registryMGR) ); $token = $moadian->login(); @@ -1180,6 +1232,238 @@ class TaxSettingsController extends AbstractController } } + #[Route('/api/plugins/tax/invoice/send-bulk', name: 'plugin_tax_invoice_send_bulk', methods: ['POST'])] + public function sendBulkTaxInvoices(Request $request, Access $access, Log $log, EntityManagerInterface $em, registryMGR $registryMGR): JsonResponse + { + $acc = $access->hasRole('plugTaxSettings'); + if (!$acc) { + throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.'); + } + + $params = $request->getPayload()->all(); + $invoiceIds = $params['ids'] ?? []; + + if (empty($invoiceIds)) { + return $this->json([ + 'success' => false, + 'message' => 'شناسه فاکتورهای مالیاتی الزامی است' + ]); + } + + $businessId = is_object($acc['bid']) ? $acc['bid']->getId() : $acc['bid']; + $user = $this->getUser(); + $userId = $user instanceof \App\Entity\User ? $user->getId() : null; + + $repo = $em->getRepository(PluginTaxsettingsKey::class); + $taxSettings = $repo->findOneBy(['business_id' => $businessId, 'user_id' => $userId]); + + if (!$taxSettings || !$taxSettings->getPrivateKey() || !$taxSettings->getTaxMemoryId()) { + return $this->json([ + 'success' => false, + 'message' => 'تنظیمات مالیاتی تکمیل نشده است. لطفاً ابتدا تنظیمات را تکمیل کنید.' + ]); + } + + try { + $username = $taxSettings->getTaxMemoryId(); + $privateKey = $taxSettings->getPrivateKey(); + + if (!$username || !$privateKey) { + return $this->json([ + 'success' => false, + 'message' => 'تنظیمات مالیاتی تکمیل نشده است. لطفاً ابتدا تنظیمات را تکمیل کنید.' + ]); + } + + $moadian = new \SnappMarketPro\Moadian\Moadian( + '', + $privateKey, + '', + $username, + $this->getMoadianBaseUrl($registryMGR) + ); + + $serverInfo = $moadian->getServerInformation(); + if (!isset($serverInfo['result']['data']['publicKeys'][0])) { + return $this->json([ + 'success' => false, + 'message' => 'خطا در دریافت اطلاعات سرور مودیان' + ]); + } + + $taxOrgPublicKey = $serverInfo['result']['data']['publicKeys'][0]['key']; + $taxOrgKeyId = $serverInfo['result']['data']['publicKeys'][0]['id']; + + $moadian = new \SnappMarketPro\Moadian\Moadian( + $taxOrgPublicKey, + $privateKey, + $taxOrgKeyId, + $username, + $this->getMoadianBaseUrl($registryMGR) + ); + + $token = $moadian->login(); + + if (!$token) { + return $this->json([ + 'success' => false, + 'message' => 'خطا در اتصال به سامانه مودیان، لطفاً تنظیمات را بررسی کنید.' + ]); + } + + $moadian->setToken($token); + + $taxInvoiceRepo = $em->getRepository(PluginTaxInvoice::class); + $results = []; + $successCount = 0; + $errorCount = 0; + $processedCodes = []; + + foreach ($invoiceIds as $id) { + try { + $taxInvoice = $taxInvoiceRepo->findOneBy([ + 'id' => $id, + 'business' => $businessId + ]); + + if (!$taxInvoice) { + $results[] = [ + 'id' => $id, + 'code' => null, + 'success' => false, + 'message' => 'فاکتور مالیاتی مورد نظر یافت نشد' + ]; + $errorCount++; + continue; + } + + $invoiceStatus = $taxInvoice->getStatus(); + + if ($invoiceStatus !== 'pending' && $invoiceStatus !== 'error') { + $results[] = [ + 'id' => $id, + 'code' => $taxInvoice->getInvoiceCode(), + 'success' => false, + 'message' => 'فقط فاکتورهای ارسال نشده یا خطا دار قابل ارسال هستند' + ]; + $errorCount++; + continue; + } + + $invoice = $taxInvoice->getInvoice(); + + if (!$invoice) { + $results[] = [ + 'id' => $id, + 'code' => $taxInvoice->getInvoiceCode(), + 'success' => false, + 'message' => 'فاکتور معتبر نیست' + ]; + $errorCount++; + continue; + } + + $validationResult = $this->validateInvoiceForTax($invoice); + if (!$validationResult['valid']) { + $results[] = [ + 'id' => $id, + 'code' => $taxInvoice->getInvoiceCode(), + 'success' => false, + 'message' => $validationResult['message'] + ]; + $errorCount++; + continue; + } + + $invoiceDto = $this->buildInvoiceDto($invoice, $moadian, $taxSettings->getEconomicCode()); + if (!$invoiceDto) { + $results[] = [ + 'id' => $id, + 'code' => $taxInvoice->getInvoiceCode(), + 'success' => false, + 'message' => 'خطا در آماده‌سازی فاکتور: خطا در ساخت DTO فاکتور' + ]; + $errorCount++; + continue; + } + + $response = $moadian->sendInvoices([$invoiceDto]); + + if (isset($response['result'][0]['referenceNumber']) && !empty($response['result'])) { + $taxInvoice->setStatus('sent'); + $taxInvoice->setTaxSystemInvoiceNumber($response['result'][0]['referenceNumber']); + $taxInvoice->setSentAt(new \DateTimeImmutable()); + $em->persist($taxInvoice); + $em->flush(); + + if ($invoiceStatus === 'error') { + $taxInvoice->setInvoiceType('اصلاحی'); + $em->persist($taxInvoice); + $em->flush(); + } + + $results[] = [ + 'id' => $id, + 'code' => $taxInvoice->getInvoiceCode(), + 'success' => true, + 'message' => 'فاکتور با موفقیت ارسال شد', + 'referenceNumber' => $response['result'][0]['referenceNumber'] ?? null + ]; + $successCount++; + $processedCodes[] = $taxInvoice->getInvoiceCode(); + } else { + $results[] = [ + 'id' => $id, + 'code' => $taxInvoice->getInvoiceCode(), + 'success' => false, + 'message' => 'خطا در ارسال فاکتور: ' . ($response['result'][0]['error'] ?? 'خطای نامشخص') + ]; + $errorCount++; + } + + } catch (\Exception $e) { + $results[] = [ + 'id' => $id, + 'code' => $taxInvoice->getInvoiceCode() ?? null, + 'success' => false, + 'message' => 'خطا در پردازش فاکتور: ' . $e->getMessage() + ]; + $errorCount++; + } + } + + if (!empty($processedCodes)) { + $codesText = implode(', ', $processedCodes); + $log->insert( + 'ارسال گروهی فاکتورهای مالیاتی', + 'فاکتورهای مالیاتی ' . $codesText . ' به سامانه مودیان ارسال شد.', + $this->getUser(), + $businessId + ); + } + + $totalProcessed = count($invoiceIds); + $message = "پردازش {$totalProcessed} فاکتور مالیاتی تکمیل شد. موفق: {$successCount}, ناموفق: {$errorCount}"; + + return $this->json([ + 'success' => $successCount > 0, + 'message' => $message, + 'summary' => [ + 'total' => $totalProcessed, + 'success' => $successCount, + 'error' => $errorCount + ], + 'results' => $results + ]); + + } catch (\Exception $e) { + return $this->json([ + 'success' => false, + 'message' => 'خطا در ارسال گروهی فاکتورهای مالیاتی: ' . $e->getMessage() + ]); + } + } + } diff --git a/hesabixCore/src/Controller/System/RegistrySettingsController.php b/hesabixCore/src/Controller/System/RegistrySettingsController.php index c8edd40e..f8a11b26 100644 --- a/hesabixCore/src/Controller/System/RegistrySettingsController.php +++ b/hesabixCore/src/Controller/System/RegistrySettingsController.php @@ -57,6 +57,7 @@ final class RegistrySettingsController extends AbstractController 'appUrl' => $registryMGR->get('system', 'appUrl'), 'appSlogan' => $registryMGR->get('system', 'appSlogan'), 'verifyMobileViaSms' => filter_var($registryMGR->get('system', 'verifyMobileViaSms'), FILTER_VALIDATE_BOOLEAN), + 'taxSystemSandboxMode' => filter_var($registryMGR->get($rootSystem, 'tax_system_sandbox_mode'), FILTER_VALIDATE_BOOLEAN), // تنظیمات FTP 'ftpEnabled' => filter_var($registryMGR->get($rootSystem, 'ftp_enabled'), FILTER_VALIDATE_BOOLEAN), 'ftpHost' => $registryMGR->get($rootSystem, 'ftp_host') ?: '', @@ -96,6 +97,7 @@ final class RegistrySettingsController extends AbstractController $registryMGR->update('system', 'appUrl', $data['appUrl'] ?? ''); $registryMGR->update('system', 'appSlogan', $data['appSlogan'] ?? ''); $registryMGR->update('system', 'verifyMobileViaSms', $data['verifyMobileViaSms'] ? '1' : '0'); + $registryMGR->update($rootSystem, 'tax_system_sandbox_mode', $data['taxSystemSandboxMode'] ? '1' : '0'); // ذخیره تنظیمات FTP $registryMGR->update($rootSystem, 'ftp_enabled', $data['ftpEnabled'] ? '1' : '0'); $registryMGR->update($rootSystem, 'ftp_host', $data['ftpHost'] ?? ''); diff --git a/webUI/src/i18n/fa_lang.ts b/webUI/src/i18n/fa_lang.ts index 99d09953..126e811e 100755 --- a/webUI/src/i18n/fa_lang.ts +++ b/webUI/src/i18n/fa_lang.ts @@ -571,7 +571,7 @@ const fa_lang = { status: "وضعیت", actions: "عملیات" }, - send_to_tax_system: "ارسال به سامانه مودیان", + send_to_tax_system: "ارسال به کارپوشه مودیان", tax_send_success: "فاکتور با موفقیت به سامانه مودیان ارسال شد.", tax_send_error: "ارسال به سامانه مودیان با خطا مواجه شد.", }, diff --git a/webUI/src/views/acc/plugins/tax/invoices/list.vue b/webUI/src/views/acc/plugins/tax/invoices/list.vue index 19764b22..bfc08c0a 100644 --- a/webUI/src/views/acc/plugins/tax/invoices/list.vue +++ b/webUI/src/views/acc/plugins/tax/invoices/list.vue @@ -10,6 +10,12 @@ + + + @@ -18,37 +24,31 @@ - این بخش برای نمایش لیست صورتحساب‌ هایی است که به سامانه مودیان مالیاتی + این بخش برای نمایش لیست صورتحساب‌ هایی است که به سامانه مودیان + مالیاتی ارسال شده‌اند. - + @@ -75,68 +75,51 @@ - - - \ No newline at end of file + }, + mounted() { + this.loadData(); + } +}; + + + \ No newline at end of file diff --git a/webUI/src/views/acc/sell/list.vue b/webUI/src/views/acc/sell/list.vue index 164e2051..d3e2f0bb 100755 --- a/webUI/src/views/acc/sell/list.vue +++ b/webUI/src/views/acc/sell/list.vue @@ -19,6 +19,18 @@ + + +