Compare commits

..

2 commits

Author SHA1 Message Date
root c20652b8cd first commit for tax lugin 2025-07-17 17:48:19 +00:00
M011N1N6 8adfdb09ee
Update README.md 2025-07-11 10:37:22 +03:30
471 changed files with 1029 additions and 4170 deletions

View file

@ -1,3 +1,8 @@
# توقف فعالیت در گیت‌هاب به دلیل نگرانی‌های اخلاقی
ما به دلیل استفاده مایکروسافت از هوش مصنوعی در تولید سلاح‌های نظامی و آموزش مدل‌های هوش مصنوعی با داده‌های غیرنظامیان، تصمیم گرفتیم تمام فعالیت‌های خود را در پلتفرم گیت‌هاب متوقف کنیم. این تصمیم به منظور پایبندی به اصول اخلاقی و مسئولیت اجتماعی اتخاذ شده است.
برای دسترسی به سورس‌کدها و مشارکت در پروژه‌های ما، لطفاً به وب‌سایت رسمی ما به آدرس [source.hesabix.ir](https://source.hesabix.ir) مراجعه کنید.
با تشکر از حمایت و همراهی شما.
# حسابیکس - نرم‌افزار حسابداری متن‌باز # حسابیکس - نرم‌افزار حسابداری متن‌باز
<img src="https://hesabix.ir/favicon/favicon.svg" alt="Hesabix Logo" width="100" height="100" /> <img src="https://hesabix.ir/favicon/favicon.svg" alt="Hesabix Logo" width="100" height="100" />

View file

@ -109,7 +109,7 @@
"symfony/browser-kit": "7.2.*", "symfony/browser-kit": "7.2.*",
"symfony/css-selector": "7.2.*", "symfony/css-selector": "7.2.*",
"symfony/debug-bundle": "7.2.*", "symfony/debug-bundle": "7.2.*",
"symfony/maker-bundle": "^1.62", "symfony/maker-bundle": "^1.64",
"symfony/phpunit-bridge": "^7.2", "symfony/phpunit-bridge": "^7.2",
"symfony/stopwatch": "7.2.*", "symfony/stopwatch": "7.2.*",
"symfony/web-profiler-bundle": "7.2.*" "symfony/web-profiler-bundle": "7.2.*"

View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "389f897ebd1e0befdd15876e5d6a43a7", "content-hash": "fc8e55a0f3d505b2453542a73030d32c",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@ -11063,21 +11063,21 @@
}, },
{ {
"name": "symfony/maker-bundle", "name": "symfony/maker-bundle",
"version": "v1.62.1", "version": "v1.64.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/maker-bundle.git", "url": "https://github.com/symfony/maker-bundle.git",
"reference": "468ff2708200c95ebc0d85d3174b6c6711b8a590" "reference": "c86da84640b0586e92aee2b276ee3638ef2f425a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/maker-bundle/zipball/468ff2708200c95ebc0d85d3174b6c6711b8a590", "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/c86da84640b0586e92aee2b276ee3638ef2f425a",
"reference": "468ff2708200c95ebc0d85d3174b6c6711b8a590", "reference": "c86da84640b0586e92aee2b276ee3638ef2f425a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/inflector": "^2.0", "doctrine/inflector": "^2.0",
"nikic/php-parser": "^4.18|^5.0", "nikic/php-parser": "^5.0",
"php": ">=8.1", "php": ">=8.1",
"symfony/config": "^6.4|^7.0", "symfony/config": "^6.4|^7.0",
"symfony/console": "^6.4|^7.0", "symfony/console": "^6.4|^7.0",
@ -11100,6 +11100,7 @@
"symfony/http-client": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0",
"symfony/phpunit-bridge": "^6.4.1|^7.0", "symfony/phpunit-bridge": "^6.4.1|^7.0",
"symfony/security-core": "^6.4|^7.0", "symfony/security-core": "^6.4|^7.0",
"symfony/security-http": "^6.4|^7.0",
"symfony/yaml": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0",
"twig/twig": "^3.0|^4.x-dev" "twig/twig": "^3.0|^4.x-dev"
}, },
@ -11135,7 +11136,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/symfony/maker-bundle/issues", "issues": "https://github.com/symfony/maker-bundle/issues",
"source": "https://github.com/symfony/maker-bundle/tree/v1.62.1" "source": "https://github.com/symfony/maker-bundle/tree/v1.64.0"
}, },
"funding": [ "funding": [
{ {
@ -11151,7 +11152,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-01-15T00:21:40+00:00" "time": "2025-06-23T16:12:08+00:00"
}, },
{ {
"name": "symfony/phpunit-bridge", "name": "symfony/phpunit-bridge",
@ -11370,7 +11371,7 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {},
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
@ -11380,6 +11381,6 @@
"ext-fileinfo": "*", "ext-fileinfo": "*",
"ext-iconv": "*" "ext-iconv": "*"
}, },
"platform-dev": [], "platform-dev": {},
"plugin-api-version": "2.6.0" "plugin-api-version": "2.6.0"
} }

View file

@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241201000000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create postal_code_inquiry table';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE postal_code_inquiry (
id INT AUTO_INCREMENT NOT NULL,
postal_code VARCHAR(10) NOT NULL,
address_data JSON NOT NULL,
created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\',
updated_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\',
UNIQUE INDEX UNIQ_POSTAL_CODE (postal_code),
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE postal_code_inquiry');
}
}

View file

@ -430,7 +430,7 @@ class AdminController extends AbstractController
if (array_key_exists('rejectChequeInput', $params['plugAccpro'])) if (array_key_exists('rejectChequeInput', $params['plugAccpro']))
$registryMGR->update('sms', 'plugAccproRejectChequeInput', $params['plugAccpro']['rejectChequeInput']); $registryMGR->update('sms', 'plugAccproRejectChequeInput', $params['plugAccpro']['rejectChequeInput']);
} }
return $this->json(JsonResp::success()); return $this->json(JsonResp::success());
} }
@ -450,15 +450,6 @@ class AdminController extends AbstractController
$resp['parsianGatewayAPI'] = $registryMGR->get('system', key: 'parsianGatewayAPI'); $resp['parsianGatewayAPI'] = $registryMGR->get('system', key: 'parsianGatewayAPI');
$resp['paypingKey'] = $registryMGR->get('system', key: 'paypingKey'); $resp['paypingKey'] = $registryMGR->get('system', key: 'paypingKey');
$resp['bitpayKey'] = $registryMGR->get('system', key: 'bitpayKey'); $resp['bitpayKey'] = $registryMGR->get('system', key: 'bitpayKey');
$resp['inquiryPanel'] = $registryMGR->get('system', key: 'inquiryPanel');
$resp['inquiryZohalAPIKey'] = $registryMGR->get('system', key: 'inquiryZohalAPIKey');
$resp['enablePostalCodeToAddress'] = $registryMGR->get('system', key: 'enablePostalCodeToAddress');
$resp['inquiryPanelEnable'] = $registryMGR->get('system', key: 'inquiryPanelEnable');
$resp['postalCodeToAddressFee'] = $registryMGR->get('system', key: 'postalCodeToAddressFee');
$resp['enableCardToSheba'] = $registryMGR->get('system', key: 'enableCardToSheba');
$resp['cardToShebaFee'] = $registryMGR->get('system', key: 'cardToShebaFee');
$resp['enableAccountToSheba'] = $registryMGR->get('system', key: 'enableAccountToSheba');
$resp['accountToShebaFee'] = $registryMGR->get('system', key: 'accountToShebaFee');
return $this->json($resp); return $this->json($resp);
} }
@ -483,15 +474,6 @@ class AdminController extends AbstractController
$registryMGR->update('system', 'parsianGatewayAPI', $params['parsianGatewayAPI']); $registryMGR->update('system', 'parsianGatewayAPI', $params['parsianGatewayAPI']);
$registryMGR->update('system', 'paypingKey', $params['paypingKey']); $registryMGR->update('system', 'paypingKey', $params['paypingKey']);
$registryMGR->update('system', 'bitpayKey', $params['bitpayKey']); $registryMGR->update('system', 'bitpayKey', $params['bitpayKey']);
$registryMGR->update('system', 'inquiryPanel', $params['inquiryPanel']);
$registryMGR->update('system', 'inquiryZohalAPIKey', $params['inquiryZohalAPIKey']);
$registryMGR->update('system', 'enablePostalCodeToAddress', $params['enablePostalCodeToAddress']);
$registryMGR->update('system', 'inquiryPanelEnable', $params['inquiryPanelEnable']);
$registryMGR->update('system', 'postalCodeToAddressFee', $params['postalCodeToAddressFee']);
$registryMGR->update('system', 'enableCardToSheba', $params['enableCardToSheba']);
$registryMGR->update('system', 'cardToShebaFee', $params['cardToShebaFee']);
$registryMGR->update('system', 'enableAccountToSheba', $params['enableAccountToSheba']);
$registryMGR->update('system', 'accountToShebaFee', $params['accountToShebaFee']);
$entityManager->persist($item); $entityManager->persist($item);
$entityManager->flush(); $entityManager->flush();
return $this->json(['result' => 1]); return $this->json(['result' => 1]);
@ -610,7 +592,6 @@ class AdminController extends AbstractController
$temp['cardPan'] = $item->getCardPan(); $temp['cardPan'] = $item->getCardPan();
$temp['refID'] = $item->getRefID(); $temp['refID'] = $item->getRefID();
$temp['shaba'] = $item->getShaba(); $temp['shaba'] = $item->getShaba();
$temp['amount'] = $item->getAmount();
$temp['dateSubmit'] = $jdate->jdate('Y/n/d H:i', $item->getDateSubmit()); $temp['dateSubmit'] = $jdate->jdate('Y/n/d H:i', $item->getDateSubmit());
$temp['gatePay'] = $item->getGatePay(); $temp['gatePay'] = $item->getGatePay();
$resp[] = $temp; $resp[] = $temp;

View file

@ -545,7 +545,6 @@ class BusinessController extends AbstractController
'plugHrmDocs' => true, 'plugHrmDocs' => true,
'plugGhestaManager' => true, 'plugGhestaManager' => true,
'plugTaxSettings' => true, 'plugTaxSettings' => true,
'inquiry' => true,
]; ];
} elseif ($perm) { } elseif ($perm) {
$result = [ $result = [
@ -590,7 +589,6 @@ class BusinessController extends AbstractController
'plugHrmDocs' => $perm->isPlugHrmDocs(), 'plugHrmDocs' => $perm->isPlugHrmDocs(),
'plugGhestaManager' => $perm->isPlugGhestaManager(), 'plugGhestaManager' => $perm->isPlugGhestaManager(),
'plugTaxSettings' => $perm->isPlugTaxSettings(), 'plugTaxSettings' => $perm->isPlugTaxSettings(),
'inquiry' => $perm->isInquiry(),
]; ];
} }
return $this->json($result); return $this->json($result);
@ -660,8 +658,6 @@ class BusinessController extends AbstractController
$perm->setPlugRepservice($params['plugRepservice']); $perm->setPlugRepservice($params['plugRepservice']);
$perm->setPlugHrmDocs($params['plugHrmDocs']); $perm->setPlugHrmDocs($params['plugHrmDocs']);
$perm->setPlugGhestaManager($params['plugGhestaManager']); $perm->setPlugGhestaManager($params['plugGhestaManager']);
$perm->setPlugTaxSettings($params['plugTaxSettings']);
$perm->setInquiry($params['inquiry']);
$entityManager->persist($perm); $entityManager->persist($perm);
$entityManager->flush(); $entityManager->flush();
$log->insert('تنظیمات پایه', 'ویرایش دسترسی‌های کاربر با پست الکترونیکی ' . $user->getEmail(), $this->getUser(), $business); $log->insert('تنظیمات پایه', 'ویرایش دسترسی‌های کاربر با پست الکترونیکی ' . $user->getEmail(), $this->getUser(), $business);

View file

@ -143,8 +143,7 @@ class CommodityController extends AbstractController
$count += $row->getCommdityCount(); $count += $row->getCommdityCount();
} else { } else {
$count -= $row->getCommdityCount(); $count -= $row->getCommdityCount();
} } }
}
$temp['count'] = $count; $temp['count'] = $count;
} }
return $temp; return $temp;
@ -1072,41 +1071,17 @@ class CommodityController extends AbstractController
if ($content = $request->getContent()) { if ($content = $request->getContent()) {
$params = json_decode($content, true); $params = json_decode($content, true);
} }
if (!array_key_exists('upper', $params) || !array_key_exists('text', $params))
if (!array_key_exists('text', $params))
return $this->json(['result' => -1]); return $this->json(['result' => -1]);
if ($this->isDefaultCategoryName($params['text'])) { if ($this->isDefaultCategoryName($params['text'])) {
return $this->json([ return $this->json([
'result' => 4, 'result' => 4,
'message' => 'این نام برای دسته‌بندی مجاز نیست', 'message' => 'این نام برای دسته‌بندی مجاز نیست',
'errorCode' => 'DEFAULT_CATEGORY_NAME' 'errorCode' => 'DEFAULT_CATEGORY_NAME'
]); ]);
} }
if (!array_key_exists('upper', $params)) {
$upper = $entityManager->getRepository(CommodityCat::class)->findOneBy([
'upper' => null,
'bid' => $acc['bid']
]);
if (!$upper) {
$upper = new CommodityCat();
$upper->setBid($acc['bid']);
$upper->setUpper(null);
$upper->setName('دسته بندی ها');
$upper->setRoot(true);
$entityManager->persist($upper);
$entityManager->flush();
}
$cat = new CommodityCat();
$cat->setBid($acc['bid']);
$cat->setRoot(false);
$cat->setName($params['text']);
$cat->setUpper($upper->getId());
$entityManager->persist($cat);
$entityManager->flush();
return $this->json(['result' => 1, 'id' => $cat->getId()]);
}
$upper = $entityManager->getRepository(CommodityCat::class)->find($params['upper']); $upper = $entityManager->getRepository(CommodityCat::class)->find($params['upper']);
if ($upper) { if ($upper) {
if ($upper->getBid() == $acc['bid']) { if ($upper->getBid() == $acc['bid']) {
@ -1134,22 +1109,22 @@ class CommodityController extends AbstractController
} }
if (!array_key_exists('id', $params) || !array_key_exists('text', $params)) if (!array_key_exists('id', $params) || !array_key_exists('text', $params))
return $this->json(['result' => -1]); return $this->json(['result' => -1]);
if ($this->isDefaultCategoryName($params['text'])) { if ($this->isDefaultCategoryName($params['text'])) {
return $this->json([ return $this->json([
'result' => 4, 'result' => 4,
'message' => 'این نام برای دسته‌بندی مجاز نیست', 'message' => 'این نام برای دسته‌بندی مجاز نیست',
'errorCode' => 'DEFAULT_CATEGORY_NAME' 'errorCode' => 'DEFAULT_CATEGORY_NAME'
]); ]);
} }
$node = $entityManager->getRepository(CommodityCat::class)->find($params['id']); $node = $entityManager->getRepository(CommodityCat::class)->find($params['id']);
if ($node) { if ($node) {
if ($node->getBid() == $acc['bid']) { if ($node->getBid() == $acc['bid']) {
// بررسی دسته‌بندی پیش‌فرض // بررسی دسته‌بندی پیش‌فرض
if ($this->isDefaultCategoryName($node->getName())) { if ($this->isDefaultCategoryName($node->getName())) {
return $this->json([ return $this->json([
'result' => 4, 'result' => 4,
'message' => 'ویرایش دسته‌بندی پیش‌فرض مجاز نیست', 'message' => 'ویرایش دسته‌بندی پیش‌فرض مجاز نیست',
'errorCode' => 'DEFAULT_CATEGORY_EDIT' 'errorCode' => 'DEFAULT_CATEGORY_EDIT'
]); ]);
@ -1613,7 +1588,7 @@ class CommodityController extends AbstractController
// بررسی دسته‌بندی پیش‌فرض // بررسی دسته‌بندی پیش‌فرض
if ($this->isDefaultCategoryName($category->getName())) { if ($this->isDefaultCategoryName($category->getName())) {
return $this->json([ return $this->json([
'Success' => false, 'Success' => false,
'message' => 'حذف دسته‌بندی پیش‌فرض مجاز نیست', 'message' => 'حذف دسته‌بندی پیش‌فرض مجاز نیست',
'errorCode' => 'DEFAULT_CATEGORY_DELETE' 'errorCode' => 'DEFAULT_CATEGORY_DELETE'
], 400); ], 400);

View file

@ -12,6 +12,8 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use App\Entity\PluginTaxsettingsKey; use App\Entity\PluginTaxsettingsKey;
use App\Entity\HesabdariDoc;
use App\Entity\PluginTaxInvoice;
class TaxSettingsController extends AbstractController class TaxSettingsController extends AbstractController
{ {
@ -198,4 +200,285 @@ class TaxSettingsController extends AbstractController
return $csr; 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
{
$acc = $access->hasRole('plugTaxSettings');
if (!$acc) {
throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.');
}
$params = $request->getPayload()->all();
$invoiceCode = $params['code'] ?? null;
if (!$invoiceCode) {
return $this->json([
'success' => false,
'message' => 'کد فاکتور الزامی است'
]);
}
$businessId = is_object($acc['bid']) ? $acc['bid']->getId() : $acc['bid'];
$userId = $this->getUser()->getId();
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,
'user_id' => $userId
]);
if (!$taxSettings || !$taxSettings->getPrivateKey()) {
return $this->json([
'success' => false,
'message' => 'تنظیمات مالیاتی تکمیل نشده است. لطفاً ابتدا تنظیمات را تکمیل کنید.'
]);
}
// اینجا کد ارسال به سامانه مودیان قرار می‌گیرد
// فعلاً فقط پیام موفقیت برمی‌گردانیم
$result = $this->sendInvoiceToTaxSystem($invoice, $taxSettings, $em, $businessId, $userId);
if ($result['success']) {
$log->insert('ارسال به سامانه مودیان', 'فاکتور ' . $invoiceCode . ' به سامانه مودیان ارسال شد', $this->getUser(), $businessId);
return $this->json([
'success' => true,
'message' => 'فاکتور با موفقیت به سامانه مودیان ارسال شد',
'data' => $result['data'] ?? null
]);
} else {
return $this->json([
'success' => false,
'message' => $result['message'] ?? 'خطا در ارسال به سامانه مودیان'
]);
}
} catch (\Exception $e) {
$log->insert('خطا در ارسال به سامانه مودیان', 'خطا در ارسال فاکتور ' . $invoiceCode . ': ' . $e->getMessage(), $this->getUser(), $businessId);
return $this->json([
'success' => false,
'message' => 'خطا در ارسال به سامانه مودیان: ' . $e->getMessage()
]);
}
}
private function sendInvoiceToTaxSystem($invoice, $taxSettings, $em, $businessId, $userId): array
{
try {
// بررسی اینکه آیا این فاکتور قبلاً ارسال شده یا نه
$taxInvoiceRepo = $em->getRepository(PluginTaxInvoice::class);
$existingRecord = $taxInvoiceRepo->findByInvoiceCodeAndBusiness($invoice->getCode(), $businessId);
if ($existingRecord) {
return [
'success' => false,
'message' => 'این فاکتور قبلاً به سامانه مودیان ارسال شده است.'
];
}
// ایجاد رکورد جدید
$taxInvoice = new PluginTaxInvoice();
$taxInvoice->setBusiness($em->getRepository(\App\Entity\Business::class)->find($businessId));
$taxInvoice->setUser($em->getRepository(\App\Entity\User::class)->find($userId));
$taxInvoice->setInvoice($invoice);
$taxInvoice->setInvoiceCode($invoice->getCode());
$taxInvoice->setAmount($invoice->getAmount());
$taxInvoice->setStatus('pending');
// دریافت اطلاعات مشتری
$customerName = null;
$customerId = null;
$rows = $invoice->getHesabdariRows();
foreach ($rows as $row) {
if ($row->getPerson()) {
$customerName = $row->getPerson()->getNikename();
$customerId = $row->getPerson()->getCode();
break;
}
}
$taxInvoice->setCustomerName($customerName);
$taxInvoice->setCustomerId($customerId);
// ذخیره رکورد
$em->persist($taxInvoice);
$em->flush();
// اینجا کد واقعی ارسال به سامانه مودیان قرار می‌گیرد
// فعلاً یک پیام موفقیت برمی‌گردانیم
// مثال کد ارسال به API سامانه مودیان:
/*
$invoiceData =
invoiceNumber => $invoice->getCode(),
date => $invoice->getDate(),
totalAmount => $invoice->getAmount(),
customerName=> $customerName,
customerNationalId' => $customerNationalId,
// سایر اطلاعات فاکتور
];
$response = $this->callTaxSystemAPI($invoiceData, $taxSettings);
if ($response['success']) {
// به‌روزرسانی وضعیت به sent
$taxInvoice->setStatus('sent');
$taxInvoice->setSentAt(new \DateTimeImmutable());
$taxInvoice->setTaxSystemInvoiceNumber($response['data']['invoiceNumber'] ?? null);
$taxInvoice->setTaxSystemReferenceNumber($response['data']['referenceNumber'] ?? null);
$taxInvoice->setResponseData(json_encode($response['data']));
$em->flush();
return [
success' => true,
data' => $response['data] ];
} else {
// به‌روزرسانی وضعیت به failed
$taxInvoice->setStatus('failed');
$taxInvoice->setErrorMessage($response['message']);
$em->flush();
return [
success' => false,
message' => $response['message] ];
}
*/
// فعلاً برای تست، پیام موفقیت برمی‌گردانیم
$taxInvoice->setStatus('sent');
$taxInvoice->setSentAt(new \DateTimeImmutable());
$taxInvoice->setTaxSystemInvoiceNumber('TAX-' . $invoice->getCode());
$taxInvoice->setTaxSystemReferenceNumber('REF-' . $invoice->getCode());
$taxInvoice->setResponseData(json_encode(['status' => 'success', 'message' => 'Test response']));
$em->flush();
return [
'success' => true,
'data' => [
'invoiceNumber' => $invoice->getCode(),
'taxSystemInvoiceNumber' => $taxInvoice->getTaxSystemInvoiceNumber(),
'taxSystemReferenceNumber' => $taxInvoice->getTaxSystemReferenceNumber(),
'sentAt' => $taxInvoice->getSentAt()->format('Y-m-d H:i:s')
]
];
} catch (\Exception $e) {
// در صورت خطا، وضعیت را به failed تغییر دهید
if (isset($taxInvoice)) {
$taxInvoice->setStatus('failed');
$taxInvoice->setErrorMessage($e->getMessage());
$em->flush();
}
return [
'success' => false,
'message' => 'خطا در ارسال به سامانه مودیان: ' . $e->getMessage()
];
}
}
#[Route('/api/plugins/tax/invoices/list', name: 'plugin_tax_settings_invoices_list', methods: ['GET'])]
public function plugin_tax_settings_invoices_list(Request $request, Access $access, EntityManagerInterface $em): JsonResponse
{
$acc = $access->hasRole('plugTaxSettings');
if (!$acc) {
throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.');
}
$businessId = is_object($acc['bid']) ? $acc['bid']->getId() : $acc['bid'];
try {
$taxInvoiceRepo = $em->getRepository(PluginTaxInvoice::class);
$invoices = $taxInvoiceRepo->findByBusiness($businessId);
$result = [];
foreach ($invoices as $taxInvoice) {
$invoice = $taxInvoice->getInvoice();
// دریافت اطلاعات کامل فاکتور اصلی
$invoiceDetails = null;
if ($invoice) {
$invoiceDetails = [
'id' => $invoice->getId(),
'code' => $invoice->getCode(),
'date' => $invoice->getDate(),
'des' => $invoice->getDes(),
'amount' => $invoice->getAmount(),
'type' => $invoice->getType(),
'status' => $invoice->getStatus(),
'shortlink' => $invoice->getShortlink(),
'taxPercent' => $invoice->getTaxPercent(),
'discountType' => $invoice->getDiscountType(),
'discountPercent' => $invoice->getDiscountPercent()
];
}
$result[] = [
'id' => $taxInvoice->getId(),
'invoiceNumber' => $taxInvoice->getInvoiceCode(),
'date' => $invoice ? $invoice->getDate() : null,
'customerName' => $taxInvoice->getCustomerName(),
'customerId' => $taxInvoice->getCustomerId(),
'totalAmount' => $taxInvoice->getAmount(),
'status' => $taxInvoice->getStatus(),
'sentDate' => $taxInvoice->getSentAt() ? $taxInvoice->getSentAt()->format('Y-m-d H:i:s') : null,
'errorMessage' => $taxInvoice->getErrorMessage(),
'createdAt' => $taxInvoice->getCreatedAt()->format('Y-m-d H:i:s'),
'uniqueTaxNumber' => $taxInvoice->getTaxSystemInvoiceNumber(),
'referenceUniqueTaxNumber' => $taxInvoice->getTaxSystemReferenceNumber(),
'invoiceType' => $this->getInvoiceType($invoice),
'invoiceDetails' => $invoiceDetails
];
}
return $this->json([
'success' => true,
'data' => $result
]);
} catch (\Exception $e) {
return $this->json([
'success' => false,
'message' => 'خطا در دریافت لیست فاکتورها: ' . $e->getMessage()
]);
}
}
private function getInvoiceType($invoice): string
{
if (!$invoice) {
return 'اصلی';
}
switch ($invoice->getType()) {
case 'sell':
return 'اصلی';
case 'return_sell':
return 'برگشت از فروش';
case 'correction':
return 'اصلاحی';
case 'cancel':
return 'ابطالی';
default:
return 'اصلی';
}
}
} }

View file

@ -1,327 +0,0 @@
<?php
namespace App\Controller\Plugins\inquiry;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\PlugGhestaDoc;
use App\Entity\PlugGhestaItem;
use App\Entity\HesabdariDoc;
use App\Entity\Person;
use App\Service\Access;
use App\Service\Provider;
use App\Service\Printers;
use App\Entity\PrintOptions;
use App\Service\Log;
use App\Entity\Business;
use App\Service\registryMGR;
use App\Service\Inquiry;
class PlugInquiryMainController extends AbstractController
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
#[Route('/api/plugins/inquiry/settings/get', name: 'plugin_inquiry_settings_get', methods: ['GET'])]
public function plugin_inquiry_settings_get(registryMGR $registryMGR): JsonResponse
{
$resp['inquiryPanel'] = $registryMGR->get('system', key: 'inquiryPanel');
$resp['enablePostalCodeToAddress'] = $registryMGR->get('system', key: 'enablePostalCodeToAddress');
$resp['inquiryPanelEnable'] = $registryMGR->get('system', key: 'inquiryPanelEnable');
$resp['postalCodeToAddressFee'] = $registryMGR->get('system', key: 'postalCodeToAddressFee');
$resp['enableCardToSheba'] = $registryMGR->get('system', key: 'enableCardToSheba');
$resp['cardToShebaFee'] = $registryMGR->get('system', key: 'cardToShebaFee');
$resp['enableAccountToSheba'] = $registryMGR->get('system', key: 'enableAccountToSheba');
$resp['accountToShebaFee'] = $registryMGR->get('system', key: 'accountToShebaFee');
return $this->json($resp);
}
#[Route('/api/plugins/inquiry/postalcode-to-address', name: 'plugin_inquiry_postalcode_to_address', methods: ['POST'])]
public function plugin_inquiry_postalcode_to_address(Inquiry $inquiry, Access $access, Request $request, registryMGR $registryMGR, Log $log): JsonResponse
{
$acc = $access->hasRole('inquiry');
if (!$acc) {
return $this->json([
'success' => false,
'message' => 'شما دسترسی به این سرویس را ندارید'
]);
}
// دریافت کد پستی از درخواست
$data = json_decode($request->getContent(), true);
$postalCode = $data['postal_code'] ?? null;
if (!$postalCode) {
return $this->json([
'success' => false,
'message' => 'کد پستی ارسال نشده است'
]);
}
// فراخوانی سرویس استعلام کد پستی
$result = $inquiry->postalCodeToAddress($postalCode);
// بررسی نتیجه و بازگرداندن پاسخ مناسب
if (isset($result['result']) && $result['result'] == 1) {
$isFromCache = isset($result['response_body']['message']) &&
strpos($result['response_body']['message'], 'از کش') !== false;
// ثبت لاگ بر اساس منبع داده
$logMessage = $isFromCache
? "استعلام کد پستی {$postalCode} از کش (بدون کسر کارمزد)"
: "استعلام کد پستی {$postalCode} از API (کسر کارمزد: " . $registryMGR->get('system', key: 'postalCodeToAddressFee') . " ریال)";
$log->insert(
'استعلام',
$logMessage,
$acc['user'],
$acc['bid']
);
// فقط در صورت عدم وجود در کش، کارمزد کسر شود
if (!$isFromCache) {
if ($acc['bid']->getSmsCharge() < $registryMGR->get('system', key: 'postalCodeToAddressFee')) {
// ثبت لاگ عدم موجودی کافی
$log->insert(
'استعلام',
"عدم موجودی کافی برای استعلام کد پستی {$postalCode}",
$acc['user'],
$acc['bid']
);
return $this->json([
'success' => false,
'message' => 'موجودی شما برای این سرویس کافی نیست'
]);
}
$business = $acc['bid'];
$business->setSmsCharge($business->getSmsCharge() - $registryMGR->get('system', key: 'postalCodeToAddressFee'));
$this->entityManager->persist($business);
$this->entityManager->flush();
}
return $this->json([
'success' => true,
'data' => $result['response_body']['data']['address'] ?? null,
'message' => $result['response_body']['message'] ?? 'موفق',
'from_cache' => $isFromCache
]);
} else {
// ثبت لاگ خطا
$errorMessage = $result['message'] ?? 'خطا در استعلام کد پستی';
$log->insert(
'استعلام',
"خطا در استعلام کد پستی {$postalCode}: {$errorMessage}",
$acc['user'],
$acc['bid']
);
return $this->json([
'success' => false,
'message' => $errorMessage,
'error_code' => $result['error_code'] ?? null
]);
}
}
#[Route('/api/plugins/inquiry/card-to-sheba', name: 'plugin_inquiry_card_to_sheba', methods: ['POST'])]
public function plugin_inquiry_card_to_sheba(Inquiry $inquiry, Access $access, Request $request, registryMGR $registryMGR, Log $log): JsonResponse
{
$acc = $access->hasRole('inquiry');
if (!$acc) {
return $this->json([
'success' => false,
'message' => 'شما دسترسی به این سرویس را ندارید'
]);
}
// بررسی فعال بودن سرویس
if (!$registryMGR->get('system', key: 'enableCardToSheba')) {
return $this->json([
'success' => false,
'message' => 'این سرویس در حال حاضر غیرفعال است'
]);
}
// دریافت شماره کارت از درخواست
$data = json_decode($request->getContent(), true);
$cardNumber = $data['card_number'] ?? null;
if (!$cardNumber) {
return $this->json([
'success' => false,
'message' => 'شماره کارت ارسال نشده است'
]);
}
// فراخوانی سرویس استعلام کارت به شبا
$result = $inquiry->cardToSheba($cardNumber);
// بررسی نتیجه و بازگرداندن پاسخ مناسب
if (isset($result['result']) && $result['result'] == 1) {
$isFromCache = isset($result['response_body']['message']) &&
strpos($result['response_body']['message'], 'از حافظه موقت') !== false;
// ثبت لاگ بر اساس منبع داده
$logMessage = $isFromCache
? "استعلام کارت به شبا {$cardNumber} از حافظه موقت (بدون کسر کارمزد)"
: "استعلام کارت به شبا {$cardNumber} از API (کسر کارمزد: " . $registryMGR->get('system', key: 'cardToShebaFee') . " ریال)";
$log->insert(
'استعلام',
$logMessage,
$acc['user'],
$acc['bid']
);
// فقط در صورت عدم وجود در کش، کارمزد کسر شود
if (!$isFromCache) {
if ($acc['bid']->getSmsCharge() < $registryMGR->get('system', key: 'cardToShebaFee')) {
// ثبت لاگ عدم موجودی کافی
$log->insert(
'استعلام',
"عدم موجودی کافی برای استعلام کارت به شبا {$cardNumber}",
$acc['user'],
$acc['bid']
);
return $this->json([
'success' => false,
'message' => 'موجودی شما برای این سرویس کافی نیست'
]);
}
$business = $acc['bid'];
$business->setSmsCharge($business->getSmsCharge() - $registryMGR->get('system', key: 'cardToShebaFee'));
$this->entityManager->persist($business);
$this->entityManager->flush();
}
return $this->json([
'success' => true,
'data' => $result['response_body']['data'] ?? null,
'message' => $result['response_body']['message'] ?? 'موفق',
'from_cache' => $isFromCache
]);
} else {
// ثبت لاگ خطا
$errorMessage = $result['message'] ?? 'خطا در استعلام کارت به شبا';
$log->insert(
'استعلام',
"خطا در استعلام کارت به شبا {$cardNumber}: {$errorMessage}",
$acc['user'],
$acc['bid']
);
return $this->json([
'success' => false,
'message' => $errorMessage,
'error_code' => $result['error_code'] ?? null
]);
}
}
#[Route('/api/plugins/inquiry/account-to-sheba', name: 'plugin_inquiry_account_to_sheba', methods: ['POST'])]
public function plugin_inquiry_account_to_sheba(Inquiry $inquiry, Access $access, Request $request, registryMGR $registryMGR, Log $log): JsonResponse
{
$acc = $access->hasRole('inquiry');
if (!$acc) {
return $this->json([
'success' => false,
'message' => 'شما دسترسی به این سرویس را ندارید'
]);
}
// بررسی فعال بودن سرویس
if (!$registryMGR->get('system', key: 'enableAccountToSheba')) {
return $this->json([
'success' => false,
'message' => 'این سرویس در حال حاضر غیرفعال است'
]);
}
// دریافت داده‌ها از درخواست
$data = json_decode($request->getContent(), true);
$bankCode = $data['bank_code'] ?? null;
$accountNumber = $data['account_number'] ?? null;
if (!$bankCode || !$accountNumber) {
return $this->json([
'success' => false,
'message' => 'کد بانک و شماره حساب الزامی است'
]);
}
// فراخوانی سرویس استعلام حساب به شبا
$result = $inquiry->accountToSheba($bankCode, $accountNumber);
// بررسی نتیجه و بازگرداندن پاسخ مناسب
if (isset($result['result']) && $result['result'] == 1) {
$isFromCache = isset($result['response_body']['message']) &&
strpos($result['response_body']['message'], 'از حافظه موقت') !== false;
// ثبت لاگ بر اساس منبع داده
$logMessage = $isFromCache
? "استعلام حساب به شبا {$bankCode}/{$accountNumber} از حافظه موقت (بدون کسر کارمزد)"
: "استعلام حساب به شبا {$bankCode}/{$accountNumber} از API (کسر کارمزد: " . $registryMGR->get('system', key: 'accountToShebaFee') . " ریال)";
$log->insert(
'استعلام',
$logMessage,
$acc['user'],
$acc['bid']
);
// فقط در صورت عدم وجود در کش، کارمزد کسر شود
if (!$isFromCache) {
if ($acc['bid']->getSmsCharge() < $registryMGR->get('system', key: 'accountToShebaFee')) {
// ثبت لاگ عدم موجودی کافی
$log->insert(
'استعلام',
"عدم موجودی کافی برای استعلام حساب به شبا {$bankCode}/{$accountNumber}",
$acc['user'],
$acc['bid']
);
return $this->json([
'success' => false,
'message' => 'موجودی شما برای این سرویس کافی نیست'
]);
}
$business = $acc['bid'];
$business->setSmsCharge($business->getSmsCharge() - $registryMGR->get('system', key: 'accountToShebaFee'));
$this->entityManager->persist($business);
$this->entityManager->flush();
}
return $this->json([
'success' => true,
'data' => $result['response_body']['data'] ?? null,
'message' => $result['response_body']['message'] ?? 'موفق',
'from_cache' => $isFromCache
]);
} else {
// ثبت لاگ خطا
$errorMessage = $result['message'] ?? 'خطا در استعلام حساب به شبا';
$log->insert(
'استعلام',
"خطا در استعلام حساب به شبا {$bankCode}/{$accountNumber}: {$errorMessage}",
$acc['user'],
$acc['bid']
);
return $this->json([
'success' => false,
'message' => $errorMessage,
'error_code' => $result['error_code'] ?? null
]);
}
}
}

View file

@ -23,9 +23,8 @@ final class UpdateCoreController extends AbstractController
public function api_admin_updatecore_run(): JsonResponse public function api_admin_updatecore_run(): JsonResponse
{ {
$projectDir = $this->getParameter('kernel.project_dir'); $projectDir = $this->getParameter('kernel.project_dir');
$gitRoot = dirname($projectDir); // رفتن به ریشه پروژه
$uuid = uniqid(); $uuid = uniqid();
$stateFile = $gitRoot . '/hesabixBackup/update_state_' . $uuid . '.json'; $stateFile = $projectDir . '/../hesabixBackup/update_state_' . $uuid . '.json';
if (!file_exists(dirname($stateFile))) { if (!file_exists(dirname($stateFile))) {
mkdir(dirname($stateFile), 0755, true); mkdir(dirname($stateFile), 0755, true);
@ -42,7 +41,7 @@ final class UpdateCoreController extends AbstractController
'COMPOSER_HOME' => '/var/www/.composer', 'COMPOSER_HOME' => '/var/www/.composer',
]); ]);
$process = new Process(['php', 'hesabixCore/bin/console', 'hesabix:update', $stateFile], $gitRoot, $env); $process = new Process(['php', 'bin/console', 'hesabix:update', $stateFile], $projectDir, $env);
$process->setTimeout(7200); // افزایش تایم‌اوت به 2 ساعت $process->setTimeout(7200); // افزایش تایم‌اوت به 2 ساعت
$process->start(function ($type, $buffer) use ($stateFile) { $process->start(function ($type, $buffer) use ($stateFile) {
$state = json_decode(file_get_contents($stateFile), true) ?? ['uuid' => uniqid(), 'log' => '']; $state = json_decode(file_get_contents($stateFile), true) ?? ['uuid' => uniqid(), 'log' => ''];
@ -71,9 +70,7 @@ final class UpdateCoreController extends AbstractController
], 400); ], 400);
} }
$projectDir = $this->getParameter('kernel.project_dir'); $stateFile = $this->getParameter('kernel.project_dir') . '/../hesabixBackup/update_state_' . $uuid . '.json';
$gitRoot = dirname($projectDir);
$stateFile = $gitRoot . '/hesabixBackup/update_state_' . $uuid . '.json';
if (!file_exists($stateFile)) { if (!file_exists($stateFile)) {
return new JsonResponse([ return new JsonResponse([
@ -100,7 +97,7 @@ final class UpdateCoreController extends AbstractController
} }
if (!$isRunning) { if (!$isRunning) {
$backupDir = $gitRoot . '/hesabixBackup'; $backupDir = $this->getParameter('kernel.project_dir') . '/../hesabixBackup';
$stateFiles = glob($backupDir . '/update_state_*.json'); $stateFiles = glob($backupDir . '/update_state_*.json');
foreach ($stateFiles as $file) { foreach ($stateFiles as $file) {
if (is_file($file)) { if (is_file($file)) {
@ -131,9 +128,7 @@ final class UpdateCoreController extends AbstractController
return new JsonResponse(['status' => 'error', 'message' => 'UUID is required'], 400); return new JsonResponse(['status' => 'error', 'message' => 'UUID is required'], 400);
} }
$projectDir = $this->getParameter('kernel.project_dir'); $stateFile = $this->getParameter('kernel.project_dir') . '/../hesabixBackup/update_state_' . $uuid . '.json';
$gitRoot = dirname($projectDir);
$stateFile = $gitRoot . '/hesabixBackup/update_state_' . $uuid . '.json';
return new StreamedResponse(function () use ($stateFile) { return new StreamedResponse(function () use ($stateFile) {
header('Content-Type: text/event-stream'); header('Content-Type: text/event-stream');
@ -172,14 +167,13 @@ final class UpdateCoreController extends AbstractController
public function api_admin_updatecore_commits(): JsonResponse public function api_admin_updatecore_commits(): JsonResponse
{ {
$projectDir = $this->getParameter('kernel.project_dir'); $projectDir = $this->getParameter('kernel.project_dir');
$gitRoot = dirname($projectDir); // رفتن به ریشه پروژه
$currentProcess = new Process(['git', 'rev-parse', 'HEAD'], $gitRoot); $currentProcess = new Process(['git', 'rev-parse', 'HEAD'], $projectDir);
$currentProcess->setTimeout(7200); // افزایش تایم‌اوت $currentProcess->setTimeout(7200); // افزایش تایم‌اوت
$currentProcess->run(); $currentProcess->run();
$currentCommit = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : 'unknown'; $currentCommit = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : 'unknown';
$targetProcess = new Process(['git', 'ls-remote', 'origin', 'HEAD'], $gitRoot); $targetProcess = new Process(['git', 'ls-remote', 'origin', 'HEAD'], $projectDir);
$targetProcess->setTimeout(7200); // افزایش تایم‌اوت $targetProcess->setTimeout(7200); // افزایش تایم‌اوت
$targetProcess->run(); $targetProcess->run();
$targetOutput = $targetProcess->isSuccessful() ? explode("\t", trim($targetProcess->getOutput()))[0] : 'unknown'; $targetOutput = $targetProcess->isSuccessful() ? explode("\t", trim($targetProcess->getOutput()))[0] : 'unknown';
@ -434,170 +428,4 @@ final class UpdateCoreController extends AbstractController
], 500); ], 500);
} }
} }
#[Route('/api/admin/updatecore/current-source', name: 'api_admin_updatecore_current_source', methods: ['GET'])]
public function api_admin_updatecore_current_source(): JsonResponse
{
$projectDir = $this->getParameter('kernel.project_dir');
$gitRoot = dirname($projectDir); // رفتن به ریشه پروژه
$output = '';
try {
// بررسی اینکه آیا پروژه یک مخزن Git است
if (!is_dir($gitRoot . '/.git')) {
return new JsonResponse([
'status' => 'error',
'message' => 'این پروژه یک مخزن Git نیست',
'sourceUrl' => '',
], 400);
}
// دریافت آدرس مخزن origin فعلی
$process = new Process(['git', 'remote', 'get-url', 'origin'], $gitRoot);
$process->setTimeout(7200); // افزایش تایم‌اوت
$process->run();
if (!$process->isSuccessful()) {
return new JsonResponse([
'status' => 'error',
'message' => 'خطا در دریافت آدرس مخزن: ' . $process->getErrorOutput(),
'sourceUrl' => '',
], 500);
}
$sourceUrl = trim($process->getOutput());
$output .= "آدرس مخزن فعلی: $sourceUrl\n";
return new JsonResponse([
'status' => 'success',
'sourceUrl' => $sourceUrl,
'output' => $output,
]);
} catch (\Exception $e) {
return new JsonResponse([
'status' => 'error',
'message' => 'خطا در بررسی مخزن: ' . $e->getMessage(),
'sourceUrl' => '',
], 500);
}
}
#[Route('/api/admin/updatecore/change-source', name: 'api_admin_updatecore_change_source', methods: ['POST'])]
public function api_admin_updatecore_change_source(Request $request): JsonResponse
{
$sourceUrl = $request->getPayload()->get('sourceUrl');
$output = '';
if (!$sourceUrl || !filter_var($sourceUrl, FILTER_VALIDATE_URL) && !preg_match('/^git@[^:]+:[^\/]+\/[^\/]+\.git$/', $sourceUrl)) {
return new JsonResponse([
'status' => 'error',
'message' => 'آدرس مخزن نامعتبر است. لطفاً یک آدرس HTTP یا SSH معتبر وارد کنید.',
'output' => $output,
], 400);
}
$projectDir = $this->getParameter('kernel.project_dir');
$gitRoot = dirname($projectDir); // رفتن به ریشه پروژه
try {
// بررسی اینکه آیا پروژه یک مخزن Git است
if (!is_dir($gitRoot . '/.git')) {
return new JsonResponse([
'status' => 'error',
'message' => 'این پروژه یک مخزن Git نیست',
'output' => $output,
], 400);
}
$output .= "شروع تغییر آدرس مخزن...\n";
// دریافت آدرس مخزن فعلی
$currentProcess = new Process(['git', 'remote', 'get-url', 'origin'], $gitRoot);
$currentProcess->setTimeout(7200);
$currentProcess->run();
$currentUrl = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : '';
if ($currentUrl) {
$output .= "آدرس مخزن فعلی: $currentUrl\n";
}
// تغییر آدرس مخزن origin
$changeProcess = new Process(['git', 'remote', 'set-url', 'origin', $sourceUrl], $gitRoot);
$changeProcess->setTimeout(7200);
$changeProcess->run();
if (!$changeProcess->isSuccessful()) {
return new JsonResponse([
'status' => 'error',
'message' => 'خطا در تغییر آدرس مخزن: ' . $changeProcess->getErrorOutput(),
'output' => $output,
], 500);
}
$output .= "آدرس مخزن به $sourceUrl تغییر یافت\n";
// بررسی اتصال به مخزن جدید
$testProcess = new Process(['git', 'remote', 'show', 'origin'], $gitRoot);
$testProcess->setTimeout(7200);
$testProcess->run();
if (!$testProcess->isSuccessful()) {
return new JsonResponse([
'status' => 'error',
'message' => 'خطا در اتصال به مخزن جدید: ' . $testProcess->getErrorOutput(),
'output' => $output,
], 500);
}
$output .= "اتصال به مخزن جدید با موفقیت برقرار شد\n";
// دریافت اطلاعات مخزن جدید
$fetchProcess = new Process(['git', 'fetch', 'origin'], $gitRoot);
$fetchProcess->setTimeout(7200);
$fetchProcess->run();
if (!$fetchProcess->isSuccessful()) {
$output .= "هشدار: خطا در دریافت اطلاعات از مخزن جدید: " . $fetchProcess->getErrorOutput() . "\n";
} else {
$output .= "اطلاعات مخزن جدید با موفقیت دریافت شد\n";
}
// بررسی branch های موجود
$branchProcess = new Process(['git', 'branch', '-r'], $gitRoot);
$branchProcess->setTimeout(7200);
$branchProcess->run();
if ($branchProcess->isSuccessful()) {
$branches = trim($branchProcess->getOutput());
if ($branches) {
$output .= "شاخه‌های موجود در مخزن جدید:\n$branches\n";
} else {
$output .= "هیچ شاخه‌ای در مخزن جدید یافت نشد\n";
}
}
// پاک کردن کش Git
$cleanProcess = new Process(['git', 'gc', '--prune=now'], $gitRoot);
$cleanProcess->setTimeout(7200);
$cleanProcess->run();
if ($cleanProcess->isSuccessful()) {
$output .= "کش Git پاک شد\n";
}
return new JsonResponse([
'status' => 'success',
'message' => 'آدرس مخزن با موفقیت تغییر یافت و اتصال برقرار شد',
'output' => $output,
]);
} catch (\Exception $e) {
return new JsonResponse([
'status' => 'error',
'message' => 'خطا در تغییر آدرس مخزن: ' . $e->getMessage(),
'output' => $output,
], 500);
}
}
} }

View file

@ -1,63 +0,0 @@
<?php
namespace App\Entity;
use App\Repository\AccountToShebaInquiryRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: AccountToShebaInquiryRepository::class)]
#[ORM\Table(name: 'account_to_sheba_inquiry')]
class AccountToShebaInquiry
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 50, unique: true)]
private ?string $cacheKey = null;
#[ORM\Column(type: 'json')]
private array $shebaData = [];
#[ORM\Column]
private ?\DateTimeImmutable $updatedAt = null;
public function getId(): ?int
{
return $this->id;
}
public function getCacheKey(): ?string
{
return $this->cacheKey;
}
public function setCacheKey(string $cacheKey): static
{
$this->cacheKey = $cacheKey;
return $this;
}
public function getShebaData(): array
{
return $this->shebaData;
}
public function setShebaData(array $shebaData): static
{
$this->shebaData = $shebaData;
return $this;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeImmutable $updatedAt): static
{
$this->updatedAt = $updatedAt;
return $this;
}
}

View file

@ -1,63 +0,0 @@
<?php
namespace App\Entity;
use App\Repository\CardToShebaInquiryRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: CardToShebaInquiryRepository::class)]
#[ORM\Table(name: 'card_to_sheba_inquiry')]
class CardToShebaInquiry
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 16, unique: true)]
private ?string $cardNumber = null;
#[ORM\Column(type: 'json')]
private array $shebaData = [];
#[ORM\Column]
private ?\DateTimeImmutable $updatedAt = null;
public function getId(): ?int
{
return $this->id;
}
public function getCardNumber(): ?string
{
return $this->cardNumber;
}
public function setCardNumber(string $cardNumber): static
{
$this->cardNumber = $cardNumber;
return $this;
}
public function getShebaData(): array
{
return $this->shebaData;
}
public function setShebaData(array $shebaData): static
{
$this->shebaData = $shebaData;
return $this;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeImmutable $updatedAt): static
{
$this->updatedAt = $updatedAt;
return $this;
}
}

View file

@ -132,9 +132,6 @@ class Permission
#[ORM\Column(nullable: true)] #[ORM\Column(nullable: true)]
private ?bool $plugTaxSettings = null; private ?bool $plugTaxSettings = null;
#[ORM\Column(nullable: true)]
private ?bool $inquiry = null;
public function getId(): ?int public function getId(): ?int
{ {
return $this->id; return $this->id;
@ -608,15 +605,4 @@ class Permission
return $this; return $this;
} }
public function isInquiry(): ?bool
{
return $this->inquiry;
}
public function setInquiry(?bool $inquiry): static
{
$this->inquiry = $inquiry;
return $this;
}
} }

View file

@ -0,0 +1,239 @@
<?php
namespace App\Entity;
use App\Repository\PluginTaxInvoiceRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PluginTaxInvoiceRepository::class)]
class PluginTaxInvoice
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?Business $business = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?User $user = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?HesabdariDoc $invoice = null;
#[ORM\Column(length:255)]
private ?string $invoiceCode = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $taxSystemInvoiceNumber = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $taxSystemReferenceNumber = null;
#[ORM\Column(length:255)]
private ?string $status = 'pending'; // pending, sent, failed, confirmed
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $responseData = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $errorMessage = null;
#[ORM\Column]
private ?\DateTimeImmutable $createdAt = null;
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $sentAt = null;
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $confirmedAt = null;
#[ORM\Column(type: Types::DECIMAL, precision: 30, scale: 0)]
private ?string $amount = '0';
#[ORM\Column(length: 255, nullable: true)]
private ?string $customerName = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $customerId = null;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
}
public function getId(): ?int
{
return $this->id;
}
public function getBusiness(): ?Business
{
return $this->business;
}
public function setBusiness(?Business $business): static
{
$this->business = $business;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): static
{
$this->user = $user;
return $this;
}
public function getInvoice(): ?HesabdariDoc
{
return $this->invoice;
}
public function setInvoice(?HesabdariDoc $invoice): static
{
$this->invoice = $invoice;
return $this;
}
public function getInvoiceCode(): ?string
{
return $this->invoiceCode;
}
public function setInvoiceCode(string $invoiceCode): static
{
$this->invoiceCode = $invoiceCode;
return $this;
}
public function getTaxSystemInvoiceNumber(): ?string
{
return $this->taxSystemInvoiceNumber;
}
public function setTaxSystemInvoiceNumber(?string $taxSystemInvoiceNumber): static
{
$this->taxSystemInvoiceNumber = $taxSystemInvoiceNumber;
return $this;
}
public function getTaxSystemReferenceNumber(): ?string
{
return $this->taxSystemReferenceNumber;
}
public function setTaxSystemReferenceNumber(?string $taxSystemReferenceNumber): static
{
$this->taxSystemReferenceNumber = $taxSystemReferenceNumber;
return $this;
}
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(string $status): static
{
$this->status = $status;
return $this;
}
public function getResponseData(): ?string
{
return $this->responseData;
}
public function setResponseData(?string $responseData): static
{
$this->responseData = $responseData;
return $this;
}
public function getErrorMessage(): ?string
{
return $this->errorMessage;
}
public function setErrorMessage(?string $errorMessage): static
{
$this->errorMessage = $errorMessage;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): static
{
$this->createdAt = $createdAt;
return $this;
}
public function getSentAt(): ?\DateTimeImmutable
{
return $this->sentAt;
}
public function setSentAt(?\DateTimeImmutable $sentAt): static
{
$this->sentAt = $sentAt;
return $this;
}
public function getConfirmedAt(): ?\DateTimeImmutable
{
return $this->confirmedAt;
}
public function setConfirmedAt(?\DateTimeImmutable $confirmedAt): static
{
$this->confirmedAt = $confirmedAt;
return $this;
}
public function getAmount(): ?string
{
return $this->amount;
}
public function setAmount(string $amount): static
{
$this->amount = $amount;
return $this;
}
public function getCustomerName(): ?string
{
return $this->customerName;
}
public function setCustomerName(?string $customerName): static
{
$this->customerName = $customerName;
return $this;
}
public function getCustomerId(): ?string
{
return $this->customerId;
}
public function setCustomerId(?string $customerId): static
{
$this->customerId = $customerId;
return $this;
}
}

View file

@ -5,7 +5,7 @@ namespace App\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity] #[ORM\Entity]
#[ORM\Table(name: "plugin_taxsettings_keys")] #[ORM\Table(name: "plugin_tax_settings")]
class PluginTaxsettingsKey class PluginTaxsettingsKey
{ {
#[ORM\Id] #[ORM\Id]

View file

@ -1,83 +0,0 @@
<?php
namespace App\Entity;
use App\Repository\PostalCodeInquiryRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PostalCodeInquiryRepository::class)]
#[ORM\Table(name: 'postal_code_inquiry')]
class PostalCodeInquiry
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 10, unique: true)]
private ?string $postalCode = null;
#[ORM\Column(type: 'json')]
private array $addressData = [];
#[ORM\Column]
private ?\DateTimeImmutable $createdAt = null;
#[ORM\Column]
private ?\DateTimeImmutable $updatedAt = null;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
$this->updatedAt = new \DateTimeImmutable();
}
public function getId(): ?int
{
return $this->id;
}
public function getPostalCode(): ?string
{
return $this->postalCode;
}
public function setPostalCode(string $postalCode): static
{
$this->postalCode = $postalCode;
return $this;
}
public function getAddressData(): array
{
return $this->addressData;
}
public function setAddressData(array $addressData): static
{
$this->addressData = $addressData;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): static
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeImmutable $updatedAt): static
{
$this->updatedAt = $updatedAt;
return $this;
}
}

View file

@ -1,46 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\AccountToShebaInquiry;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<AccountToShebaInquiry>
*
* @method AccountToShebaInquiry|null find($id, $lockMode = null, $lockVersion = null)
* @method AccountToShebaInquiry|null findOneBy(array $criteria, array $orderBy = null)
* @method AccountToShebaInquiry[] findAll()
* @method AccountToShebaInquiry[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class AccountToShebaInquiryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, AccountToShebaInquiry::class);
}
public function findByCacheKey(string $cacheKey): ?AccountToShebaInquiry
{
return $this->findOneBy(['cacheKey' => $cacheKey]);
}
public function save(AccountToShebaInquiry $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function remove(AccountToShebaInquiry $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
}

View file

@ -1,46 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\CardToShebaInquiry;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<CardToShebaInquiry>
*
* @method CardToShebaInquiry|null find($id, $lockMode = null, $lockVersion = null)
* @method CardToShebaInquiry|null findOneBy(array $criteria, array $orderBy = null)
* @method CardToShebaInquiry[] findAll()
* @method CardToShebaInquiry[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class CardToShebaInquiryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, CardToShebaInquiry::class);
}
public function findByCardNumber(string $cardNumber): ?CardToShebaInquiry
{
return $this->findOneBy(['cardNumber' => $cardNumber]);
}
public function save(CardToShebaInquiry $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function remove(CardToShebaInquiry $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace App\Repository;
use App\Entity\PluginTaxInvoice;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<PluginTaxInvoice>
*
* @method PluginTaxInvoice|null find($id, $lockMode = null, $lockVersion = null)
* @method PluginTaxInvoice|null findOneBy(array $criteria, array $orderBy = null)
* @method PluginTaxInvoice findAll()
* @method PluginTaxInvoice findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PluginTaxInvoiceRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PluginTaxInvoice::class);
}
public function save(PluginTaxInvoice $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function remove(PluginTaxInvoice $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
/**
* پیدا کردن فاکتورهای ارسال شده برای یک کسب‌و‌کار
*/
public function findByBusiness($businessId, $limit = null, $offset = null)
{
$qb = $this->createQueryBuilder('pti')
->leftJoin('pti.invoice', 'invoice')
->leftJoin('pti.user', 'user')
->where('pti.business = :businessId')
->setParameter('businessId', $businessId)
->orderBy('pti.createdAt', 'DESC');
if ($limit) {
$qb->setMaxResults($limit);
}
if ($offset) {
$qb->setFirstResult($offset);
}
return $qb->getQuery()->getResult();
}
/**
* پیدا کردن فاکتور بر اساس کد فاکتور و کسب‌و‌کار
*/
public function findByInvoiceCodeAndBusiness($invoiceCode, $businessId)
{
return $this->createQueryBuilder('pti')
->where('pti.invoiceCode = :invoiceCode')
->andWhere('pti.business = :businessId')
->setParameter('invoiceCode', $invoiceCode)
->setParameter('businessId', $businessId)
->getQuery()
->getOneOrNullResult();
}
/**
* آمار فاکتورهای ارسال شده بر اساس وضعیت
*/
public function getStatusStats($businessId)
{
$qb = $this->createQueryBuilder('pti')
->select('pti.status, COUNT(pti.id) as count')
->where('pti.business = :businessId')
->setParameter('businessId', $businessId)
->groupBy('pti.status');
return $qb->getQuery()->getResult();
}
/**
* فاکتورهای با وضعیت مشخص
*/
public function findByStatus($businessId, $status)
{
return $this->createQueryBuilder('pti')
->leftJoin('pti.invoice', 'invoice')
->where('pti.business = :businessId')
->andWhere('pti.status = :status')
->setParameter('businessId', $businessId)
->setParameter('status', $status)
->orderBy('pti.createdAt', 'DESC')
->getQuery()
->getResult();
}
}

View file

@ -1,46 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\PostalCodeInquiry;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<PostalCodeInquiry>
*
* @method PostalCodeInquiry|null find($id, $lockMode = null, $lockVersion = null)
* @method PostalCodeInquiry|null findOneBy(array $criteria, array $orderBy = null)
* @method PostalCodeInquiry[] findAll()
* @method PostalCodeInquiry[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PostalCodeInquiryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PostalCodeInquiry::class);
}
public function findByPostalCode(string $postalCode): ?PostalCodeInquiry
{
return $this->findOneBy(['postalCode' => $postalCode]);
}
public function save(PostalCodeInquiry $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function remove(PostalCodeInquiry $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
}

View file

@ -1,448 +0,0 @@
<?php
namespace App\Service;
use App\Entity\PostalCodeInquiry;
use App\Entity\CardToShebaInquiry;
use App\Entity\AccountToShebaInquiry;
use Doctrine\ORM\EntityManagerInterface;
class Inquiry
{
public function __construct(private EntityManagerInterface $entityManager)
{
}
public function postalCodeToAddress($postalCode)
{
// ابتدا بررسی دیتابیس
$existingInquiry = $this->entityManager->getRepository(PostalCodeInquiry::class)->findByPostalCode($postalCode);
if ($existingInquiry) {
// اگر در دیتابیس موجود است، از آن استفاده کن
$addressData = $existingInquiry->getAddressData();
return [
'result' => 1,
'response_body' => [
'data' => [
'address' => $addressData
],
'message' => 'موفق (از کش)',
'error_code' => null
]
];
}
// اگر در دیتابیس موجود نیست، از API استفاده کن
$registryMGR = new RegistryMGR($this->entityManager);
$inquiryPanel = $registryMGR->get('system', key: 'inquiryPanel');
if($inquiryPanel == 'zohal'){
$inquiryZohalAPIKey = $registryMGR->get('system', key: 'inquiryZohalAPIKey');
// بررسی وجود API Key
if (empty($inquiryZohalAPIKey)) {
return [
'result' => 0,
'message' => 'API Key تنظیم نشده است',
'error_code' => 'API_KEY_MISSING'
];
}
// استفاده از API جدید زحل
$url = "https://service.zohal.io/api/v0/services/inquiry/postal_code_inquiry";
// آماده‌سازی داده‌های JSON
$postData = json_encode([
'postal_code' => $postalCode
]);
// تنظیمات cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($postData),
'Authorization: Bearer ' . $inquiryZohalAPIKey,
'X-API-Key: ' . $inquiryZohalAPIKey
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
// بررسی خطای cURL
if ($curlError) {
return [
'result' => 0,
'message' => 'خطا در ارتباط با سرور: ' . $curlError,
'error_code' => 'CURL_ERROR'
];
}
if ($httpCode === 200) {
$data = json_decode($response, true);
// بررسی کدهای result
if (isset($data['result'])) {
switch ($data['result']) {
case 1:
// موفق - ذخیره در دیتابیس
if (isset($data['response_body']['data']['address'])) {
$this->saveToDatabase($postalCode, $data['response_body']['data']['address']);
}
return $data;
case 4:
return [
'result' => 4,
'message' => 'توکن غیر فعال شده است',
'error_code' => 'TOKEN_INACTIVE'
];
case 5:
return [
'result' => 5,
'message' => 'سرویس در دسترسی نمی‌باشد',
'error_code' => 'SERVICE_UNAVAILABLE'
];
case 6:
return [
'result' => 6,
'message' => 'فراخوانی وب‌سرویس با پارامترهای ورودی صحیح نمی‌باشد',
'error_code' => 'INVALID_PARAMETERS'
];
default:
return [
'result' => $data['result'],
'message' => $data['message'] ?? 'خطای نامشخص',
'error_code' => $data['error_code'] ?? 'UNKNOWN_ERROR'
];
}
}
return $data;
} else {
return [
'result' => 0,
'message' => 'خطا در ارتباط با سرویس استعلام کد پستی (کد خطا: ' . $httpCode . ')',
'error_code' => 'HTTP_ERROR_' . $httpCode
];
}
}
return [
'result' => 0,
'message' => 'سرویس استعلام کد پستی فعال نیست',
'error_code' => 'SERVICE_NOT_ACTIVE'
];
}
public function cardToSheba($cardNumber)
{
// بررسی دیتابیس برای کش
$existingInquiry = $this->entityManager->getRepository(CardToShebaInquiry::class)->findByCardNumber($cardNumber);
if ($existingInquiry) {
// اگر در دیتابیس موجود است، از آن استفاده کن
$shebaData = $existingInquiry->getShebaData();
return [
'result' => 1,
'response_body' => [
'data' => $shebaData,
'message' => 'موفق (از حافظه موقت)',
'error_code' => null
]
];
}
// اگر در دیتابیس موجود نیست، از API استفاده کن
$registryMGR = new RegistryMGR($this->entityManager);
$inquiryPanel = $registryMGR->get('system', key: 'inquiryPanel');
if($inquiryPanel == 'zohal'){
$inquiryZohalAPIKey = $registryMGR->get('system', key: 'inquiryZohalAPIKey');
// بررسی وجود API Key
if (empty($inquiryZohalAPIKey)) {
return [
'result' => 0,
'message' => 'API Key تنظیم نشده است',
'error_code' => 'API_KEY_MISSING'
];
}
// استفاده از API زحل برای تبدیل کارت به شبا
$url = "https://service.zohal.io/api/v0/services/inquiry/card_to_iban";
// آماده‌سازی داده‌های JSON
$postData = json_encode([
'card_number' => $cardNumber
]);
// تنظیمات cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($postData),
'Authorization: Bearer ' . $inquiryZohalAPIKey,
'X-API-Key: ' . $inquiryZohalAPIKey
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
// بررسی خطای cURL
if ($curlError) {
return [
'result' => 0,
'message' => 'خطا در ارتباط با سرور: ' . $curlError,
'error_code' => 'CURL_ERROR'
];
}
if ($httpCode === 200) {
$data = json_decode($response, true);
// بررسی کدهای result
if (isset($data['result'])) {
switch ($data['result']) {
case 1:
// موفق - ذخیره در دیتابیس
if (isset($data['response_body']['data'])) {
$this->saveCardToShebaToDatabase($cardNumber, $data['response_body']['data']);
}
return $data;
case 4:
return [
'result' => 4,
'message' => 'توکن غیر فعال شده است',
'error_code' => 'TOKEN_INACTIVE'
];
case 5:
return [
'result' => 5,
'message' => 'سرویس در دسترسی نمی‌باشد',
'error_code' => 'SERVICE_UNAVAILABLE'
];
case 6:
return [
'result' => 6,
'message' => 'فراخوانی وب‌سرویس با پارامترهای ورودی صحیح نمی‌باشد',
'error_code' => 'INVALID_PARAMETERS'
];
default:
return [
'result' => $data['result'],
'message' => $data['message'] ?? 'خطای نامشخص',
'error_code' => $data['error_code'] ?? 'UNKNOWN_ERROR'
];
}
}
return $data;
} else {
return [
'result' => 0,
'message' => 'خطا در ارتباط با سرویس تبدیل کارت به شبا (کد خطا: ' . $httpCode . ')',
'error_code' => 'HTTP_ERROR_' . $httpCode
];
}
}
return [
'result' => 0,
'message' => 'سرویس تبدیل کارت به شبا فعال نیست',
'error_code' => 'SERVICE_NOT_ACTIVE'
];
}
private function saveToDatabase(string $postalCode, array $addressData): void
{
try {
$inquiry = new PostalCodeInquiry();
$inquiry->setPostalCode($postalCode);
$inquiry->setAddressData($addressData);
$inquiry->setUpdatedAt(new \DateTimeImmutable());
$this->entityManager->persist($inquiry);
$this->entityManager->flush();
// ثبت لاگ ذخیره موفق در دیتابیس
error_log("کد پستی {$postalCode} با موفقیت در کش ذخیره شد");
} catch (\Exception $e) {
// در صورت خطا در ذخیره، فقط لاگ کن و ادامه بده
error_log('خطا در ذخیره استعلام کد پستی: ' . $e->getMessage());
}
}
public function accountToSheba($bankCode, $accountNumber)
{
// بررسی دیتابیس برای کش
$cacheKey = $bankCode . '_' . $accountNumber;
$existingInquiry = $this->entityManager->getRepository(AccountToShebaInquiry::class)->findByCacheKey($cacheKey);
if ($existingInquiry) {
// اگر در دیتابیس موجود است، از آن استفاده کن
$shebaData = $existingInquiry->getShebaData();
return [
'result' => 1,
'response_body' => [
'data' => $shebaData,
'message' => 'موفق (از حافظه موقت)',
'error_code' => null
]
];
}
// اگر در دیتابیس موجود نیست، از API استفاده کن
$registryMGR = new RegistryMGR($this->entityManager);
$inquiryPanel = $registryMGR->get('system', key: 'inquiryPanel');
if($inquiryPanel == 'zohal'){
$inquiryZohalAPIKey = $registryMGR->get('system', key: 'inquiryZohalAPIKey');
// بررسی وجود API Key
if (empty($inquiryZohalAPIKey)) {
return [
'result' => 0,
'message' => 'API Key تنظیم نشده است',
'error_code' => 'API_KEY_MISSING'
];
}
// استفاده از API زحل برای تبدیل حساب به شبا
$url = "https://service.zohal.io/api/v0/services/inquiry/account_to_iban";
// آماده‌سازی داده‌های JSON
$postData = json_encode([
'bank_code' => $bankCode,
'bank_account' => $accountNumber
]);
// تنظیمات cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($postData),
'Authorization: Bearer ' . $inquiryZohalAPIKey,
'X-API-Key: ' . $inquiryZohalAPIKey
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
// بررسی خطای cURL
if ($curlError) {
return [
'result' => 0,
'message' => 'خطا در ارتباط با سرور: ' . $curlError,
'error_code' => 'CURL_ERROR'
];
}
if ($httpCode === 200) {
$data = json_decode($response, true);
// بررسی کدهای result
if (isset($data['result'])) {
switch ($data['result']) {
case 1:
// موفق - ذخیره در دیتابیس
if (isset($data['response_body']['data'])) {
$this->saveAccountToShebaToDatabase($cacheKey, $data['response_body']['data']);
}
return $data;
case 4:
return [
'result' => 4,
'message' => 'توکن غیر فعال شده است',
'error_code' => 'TOKEN_INACTIVE'
];
case 5:
return [
'result' => 5,
'message' => 'سرویس در دسترسی نمی‌باشد',
'error_code' => 'SERVICE_UNAVAILABLE'
];
case 6:
return [
'result' => 6,
'message' => 'فراخوانی وب‌سرویس با پارامترهای ورودی صحیح نمی‌باشد',
'error_code' => 'INVALID_PARAMETERS'
];
default:
return [
'result' => $data['result'],
'message' => $data['message'] ?? 'خطای نامشخص',
'error_code' => $data['error_code'] ?? 'UNKNOWN_ERROR'
];
}
}
return $data;
} else {
return [
'result' => 0,
'message' => 'خطا در ارتباط با سرویس تبدیل حساب به شبا (کد خطا: ' . $httpCode . ')',
'error_code' => 'HTTP_ERROR_' . $httpCode
];
}
}
return [
'result' => 0,
'message' => 'سرویس تبدیل حساب به شبا فعال نیست',
'error_code' => 'SERVICE_NOT_ACTIVE'
];
}
private function saveCardToShebaToDatabase(string $cardNumber, array $shebaData): void
{
try {
$inquiry = new CardToShebaInquiry();
$inquiry->setCardNumber($cardNumber);
$inquiry->setShebaData($shebaData);
$inquiry->setUpdatedAt(new \DateTimeImmutable());
$this->entityManager->persist($inquiry);
$this->entityManager->flush();
// ثبت لاگ ذخیره موفق در دیتابیس
error_log("شماره کارت {$cardNumber} با موفقیت در حافظه موقت ذخیره شد");
} catch (\Exception $e) {
// در صورت خطا در ذخیره، فقط لاگ کن و ادامه بده
error_log('خطا در ذخیره استعلام کارت به شبا: ' . $e->getMessage());
}
}
private function saveAccountToShebaToDatabase(string $cacheKey, array $shebaData): void
{
try {
$inquiry = new AccountToShebaInquiry();
$inquiry->setCacheKey($cacheKey);
$inquiry->setShebaData($shebaData);
$inquiry->setUpdatedAt(new \DateTimeImmutable());
$this->entityManager->persist($inquiry);
$this->entityManager->flush();
// ثبت لاگ ذخیره موفق در دیتابیس
error_log("حساب با کلید {$cacheKey} با موفقیت در حافظه موقت ذخیره شد");
} catch (\Exception $e) {
// در صورت خطا در ذخیره، فقط لاگ کن و ادامه بده
error_log('خطا در ذخیره استعلام حساب به شبا: ' . $e->getMessage());
}
}
}

View file

@ -8,6 +8,15 @@
"ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05" "ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05"
} }
}, },
"doctrine/deprecations": {
"version": "1.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "87424683adc81d7dc305eefec1fced883084aab9"
}
},
"doctrine/doctrine-bundle": { "doctrine/doctrine-bundle": {
"version": "2.10", "version": "2.10",
"recipe": { "recipe": {

View file

@ -791,11 +791,11 @@ install_software() {
# Check if remote origin exists # Check if remote origin exists
if ! git remote get-url origin >/dev/null 2>&1; then if ! git remote get-url origin >/dev/null 2>&1; then
# Add remote repository if it doesn't exist # Add remote repository if it doesn't exist
git remote add origin https://source.hesabix.ir/morrning/hesabixCore.git || \ git remote add origin https://github.com/morrning/hesabixCore.git || \
handle_error "Failed to add remote repository" handle_error "Failed to add remote repository"
else else
# Update remote URL if it exists # Update remote URL if it exists
git remote set-url origin https://source.hesabix.ir/morrning/hesabixCore.git || \ git remote set-url origin https://github.com/morrning/hesabixCore.git || \
handle_error "Failed to update remote repository" handle_error "Failed to update remote repository"
fi fi

0
webUI/.github/workflows/release.yml vendored Normal file → Executable file
View file

0
webUI/.gitignore vendored Normal file → Executable file
View file

0
webUI/LICENSE Normal file → Executable file
View file

0
webUI/env.d.ts vendored Normal file → Executable file
View file

0
webUI/index.html Normal file → Executable file
View file

0
webUI/package.json Normal file → Executable file
View file

0
webUI/public/.htaccess Normal file → Executable file
View file

0
webUI/public/dashmix/dashmix.app.min.js vendored Normal file → Executable file
View file

0
webUI/public/dashmix/dashmix.min.css vendored Normal file → Executable file
View file

0
webUI/public/favicon.ico Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

0
webUI/public/fonts/fontawesome/fa-brands-400.ttf Normal file → Executable file
View file

0
webUI/public/fonts/fontawesome/fa-brands-400.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/fontawesome/fa-regular-400.ttf Normal file → Executable file
View file

0
webUI/public/fonts/fontawesome/fa-regular-400.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/fontawesome/fa-solid-900.ttf Normal file → Executable file
View file

0
webUI/public/fonts/fontawesome/fa-solid-900.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/fontawesome/fa-v4compatibility.ttf Normal file → Executable file
View file

View file

0
webUI/public/fonts/inter/inter-v11-latin-300.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/inter/inter-v11-latin-500.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/inter/inter-v11-latin-600.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/inter/inter-v11-latin-700.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/inter/inter-v11-latin-800.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/inter/inter-v11-latin-900.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/inter/inter-v11-latin-regular.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Black-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Black-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Black-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Black-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Bold-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Bold-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Bold-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Bold-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Light-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Light-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Light-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-Light-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-SemiBold-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-SemiBold-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-SemiBold-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/sahel/Sahel-SemiBold-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/sahel/sahel.css Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Bold-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Bold-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Bold-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Bold-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Light-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Light-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Light-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Light-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Medium-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Medium-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Medium-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Medium-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Thin-FD.eot Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Thin-FD.ttf Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Thin-FD.woff Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/Shabnam-Thin-FD.woff2 Normal file → Executable file
View file

0
webUI/public/fonts/shabnam/shabnam.css Normal file → Executable file
View file

View file

View file

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 235 KiB

View file

View file

View file

0
webUI/public/fonts/vazir/ttf/Vazirmatn-Black.ttf Normal file → Executable file
View file

0
webUI/public/fonts/vazir/ttf/Vazirmatn-Bold.ttf Normal file → Executable file
View file

0
webUI/public/fonts/vazir/ttf/Vazirmatn-ExtraBold.ttf Normal file → Executable file
View file

0
webUI/public/fonts/vazir/ttf/Vazirmatn-ExtraLight.ttf Normal file → Executable file
View file

0
webUI/public/fonts/vazir/ttf/Vazirmatn-Light.ttf Normal file → Executable file
View file

Some files were not shown because too many files have changed in this diff Show more