forked from morrning/hesabixCore
Compare commits
11 commits
c20652b8cd
...
574bdd1a5b
Author | SHA1 | Date | |
---|---|---|---|
|
574bdd1a5b | ||
|
7090ff44dc | ||
|
2a8ea8cb4a | ||
|
227767b0d6 | ||
|
2cb7b8945c | ||
|
7762613814 | ||
|
90f5a5b338 | ||
|
b1f8af83f4 | ||
|
0d18762f1e | ||
|
82740193cb | ||
|
ddbbf1f102 |
37
hesabixCore/migrations/Version20241201000000.php
Normal file
37
hesabixCore/migrations/Version20241201000000.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -450,6 +450,15 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,6 +483,15 @@ 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]);
|
||||||
|
@ -592,6 +610,7 @@ 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;
|
||||||
|
|
|
@ -544,6 +544,8 @@ class BusinessController extends AbstractController
|
||||||
'plugRepservice' => true,
|
'plugRepservice' => true,
|
||||||
'plugHrmDocs' => true,
|
'plugHrmDocs' => true,
|
||||||
'plugGhestaManager' => true,
|
'plugGhestaManager' => true,
|
||||||
|
'plugTaxSettings' => true,
|
||||||
|
'inquiry' => true,
|
||||||
];
|
];
|
||||||
} elseif ($perm) {
|
} elseif ($perm) {
|
||||||
$result = [
|
$result = [
|
||||||
|
@ -587,6 +589,8 @@ class BusinessController extends AbstractController
|
||||||
'plugAccproPresell' => $perm->isPlugAccproPresell(),
|
'plugAccproPresell' => $perm->isPlugAccproPresell(),
|
||||||
'plugHrmDocs' => $perm->isPlugHrmDocs(),
|
'plugHrmDocs' => $perm->isPlugHrmDocs(),
|
||||||
'plugGhestaManager' => $perm->isPlugGhestaManager(),
|
'plugGhestaManager' => $perm->isPlugGhestaManager(),
|
||||||
|
'plugTaxSettings' => $perm->isPlugTaxSettings(),
|
||||||
|
'inquiry' => $perm->isInquiry(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return $this->json($result);
|
return $this->json($result);
|
||||||
|
@ -656,6 +660,8 @@ 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);
|
||||||
|
|
|
@ -143,7 +143,8 @@ 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;
|
||||||
|
@ -1071,7 +1072,8 @@ 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'])) {
|
||||||
|
@ -1081,6 +1083,29 @@ class CommodityController extends AbstractController
|
||||||
'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) {
|
||||||
|
|
201
hesabixCore/src/Controller/Plugins/TaxSettingsController.php
Normal file
201
hesabixCore/src/Controller/Plugins/TaxSettingsController.php
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Plugins;
|
||||||
|
|
||||||
|
use App\Service\Access;
|
||||||
|
use App\Service\Extractor;
|
||||||
|
use App\Service\Log;
|
||||||
|
use App\Service\registryMGR;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use App\Entity\PluginTaxsettingsKey;
|
||||||
|
|
||||||
|
class TaxSettingsController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/api/plugins/tax/settings/get', name: 'plugin_tax_settings_get', methods: ['GET'])]
|
||||||
|
public function plugin_tax_settings_get(EntityManagerInterface $em, Access $access): JsonResponse
|
||||||
|
{
|
||||||
|
$acc = $access->hasRole('plugTaxSettings');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$businessId = is_object($acc['bid']) ? $acc['bid']->getId() : $acc['bid'];
|
||||||
|
$userId = $this->getUser()->getId();
|
||||||
|
|
||||||
|
// دریافت تنظیمات از جدول اختصاصی
|
||||||
|
$repo = $em->getRepository(PluginTaxsettingsKey::class);
|
||||||
|
$entity = $repo->findOneBy(['business_id' => $businessId, 'user_id' => $userId]);
|
||||||
|
|
||||||
|
$settings = [
|
||||||
|
'taxMemoryId' => $entity ? $entity->getTaxMemoryId() : '',
|
||||||
|
'economicCode' => $entity ? $entity->getEconomicCode() : '',
|
||||||
|
'privateKey' => $entity ? $entity->getPrivateKey() : '',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->json($settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/plugins/tax/settings/save', name: 'plugin_tax_settings_save', methods: ['POST'])]
|
||||||
|
public function plugin_tax_settings_save(Request $request, registryMGR $registryMGR, Access $access, Log $log, EntityManagerInterface $em): JsonResponse
|
||||||
|
{
|
||||||
|
$acc = $access->hasRole('plugTaxSettings');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $request->getPayload()->all();
|
||||||
|
$businessId = is_object($acc['bid']) ? $acc['bid']->getId() : $acc['bid'];
|
||||||
|
$userId = $this->getUser()->getId();
|
||||||
|
|
||||||
|
// بررسی وجود رکورد قبلی
|
||||||
|
$repo = $em->getRepository(PluginTaxsettingsKey::class);
|
||||||
|
$entity = $repo->findOneBy(['business_id' => $businessId, 'user_id' => $userId]);
|
||||||
|
if (!$entity) {
|
||||||
|
$entity = new PluginTaxsettingsKey();
|
||||||
|
$entity->setBusinessId($businessId);
|
||||||
|
$entity->setUserId($userId);
|
||||||
|
$entity->setCreatedAt(new \DateTime());
|
||||||
|
}
|
||||||
|
$entity->setPrivateKey($params['privateKey'] ?? '');
|
||||||
|
$entity->setTaxMemoryId($params['taxMemoryId'] ?? null);
|
||||||
|
$entity->setEconomicCode($params['economicCode'] ?? null);
|
||||||
|
$entity->setUpdatedAt(new \DateTime());
|
||||||
|
|
||||||
|
$em->persist($entity);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$log->insert('تنظیمات مالیاتی', 'تنظیمات مالیاتی ذخیره شد (در جدول اختصاصی)', $this->getUser(), $businessId);
|
||||||
|
|
||||||
|
return $this->json(['success' => true, 'message' => 'تنظیمات با موفقیت ذخیره شد']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generatePrivateKey(): string
|
||||||
|
{
|
||||||
|
// تولید کلید خصوصی واقعی با OpenSSL
|
||||||
|
$config = [
|
||||||
|
"private_key_bits" => 2048,
|
||||||
|
"private_key_type" => OPENSSL_KEYTYPE_RSA,
|
||||||
|
];
|
||||||
|
|
||||||
|
$res = openssl_pkey_new($config);
|
||||||
|
if (!$res) {
|
||||||
|
throw new \Exception('خطا در تولید کلید خصوصی: ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
$privateKey = '';
|
||||||
|
if (!openssl_pkey_export($res, $privateKey)) {
|
||||||
|
throw new \Exception('خطا در استخراج کلید خصوصی: ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
openssl_pkey_free($res);
|
||||||
|
return $privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generatePublicKey(string $privateKey): string
|
||||||
|
{
|
||||||
|
// استخراج کلید عمومی از کلید خصوصی
|
||||||
|
$res = openssl_pkey_get_private($privateKey);
|
||||||
|
if (!$res) {
|
||||||
|
throw new \Exception('خطا در خواندن کلید خصوصی: ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
$keyDetails = openssl_pkey_get_details($res);
|
||||||
|
if (!$keyDetails) {
|
||||||
|
throw new \Exception('خطا در استخراج جزئیات کلید: ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
openssl_pkey_free($res);
|
||||||
|
return $keyDetails['key'];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/plugins/tax/settings/generate-csr', name: 'plugin_tax_settings_generate_csr', methods: ['POST'])]
|
||||||
|
public function plugin_tax_settings_generate_csr(Request $request, registryMGR $registryMGR, Access $access, Log $log): JsonResponse
|
||||||
|
{
|
||||||
|
$acc = $access->hasRole('plugTaxSettings');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $request->getPayload()->all();
|
||||||
|
|
||||||
|
// بررسی فیلدهای اجباری
|
||||||
|
if (empty($params['nationalId']) || empty($params['nameFa']) || empty($params['nameEn']) || empty($params['email'])) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'تمام فیلدها الزامی هستند'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$privateKey = $this->generatePrivateKey();
|
||||||
|
$publicKey = $this->generatePublicKey($privateKey);
|
||||||
|
$csr = $this->generateCSR($privateKey, $params);
|
||||||
|
|
||||||
|
// هیچ ذخیرهای در دیتابیس انجام نمیشود
|
||||||
|
$businessId = is_object($acc['bid']) ? $acc['bid']->getId() : $acc['bid'];
|
||||||
|
$log->insert('تنظیمات مالیاتی', 'کلید و CSR تولید شد (بدون ذخیره)', $this->getUser(), $businessId);
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'کلید و CSR با موفقیت تولید شد',
|
||||||
|
'privateKey' => $privateKey,
|
||||||
|
'publicKey' => $publicKey,
|
||||||
|
'csr' => $csr
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'خطا در تولید کلید و CSR: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateCSR(string $privateKey, array $params): string
|
||||||
|
{
|
||||||
|
// تولید CSR واقعی با OpenSSL
|
||||||
|
$dn = [
|
||||||
|
"countryName" => "IR",
|
||||||
|
"stateOrProvinceName" => "Tehran",
|
||||||
|
"localityName" => "Tehran",
|
||||||
|
"organizationName" => $params['nameEn'],
|
||||||
|
"organizationalUnitName" => "Tax Department",
|
||||||
|
"commonName" => $params['nameFa'],
|
||||||
|
"emailAddress" => $params['email']
|
||||||
|
];
|
||||||
|
|
||||||
|
// اضافه کردن شناسه ملی به عنوان extension
|
||||||
|
$config = [
|
||||||
|
"req" => [
|
||||||
|
"distinguished_name" => $dn,
|
||||||
|
"req_extensions" => "v3_req",
|
||||||
|
"x509_extensions" => "v3_req"
|
||||||
|
],
|
||||||
|
"v3_req" => [
|
||||||
|
"subjectAltName" => "email:" . $params['email'],
|
||||||
|
"subjectKeyIdentifier" => "hash"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// ایجاد CSR
|
||||||
|
$res = openssl_csr_new($dn, $privateKey, [
|
||||||
|
'config' => $config,
|
||||||
|
'digest_alg' => 'sha256',
|
||||||
|
'req_extensions' => 'v3_req'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$res) {
|
||||||
|
throw new \Exception('خطا در تولید CSR: ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
$csr = '';
|
||||||
|
if (!openssl_csr_export($res, $csr)) {
|
||||||
|
throw new \Exception('خطا در استخراج CSR: ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $csr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,327 @@
|
||||||
|
<?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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,8 +23,9 @@ 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 = $projectDir . '/../hesabixBackup/update_state_' . $uuid . '.json';
|
$stateFile = $gitRoot . '/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);
|
||||||
|
@ -41,7 +42,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
'COMPOSER_HOME' => '/var/www/.composer',
|
'COMPOSER_HOME' => '/var/www/.composer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Process(['php', 'bin/console', 'hesabix:update', $stateFile], $projectDir, $env);
|
$process = new Process(['php', 'hesabixCore/bin/console', 'hesabix:update', $stateFile], $gitRoot, $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' => ''];
|
||||||
|
@ -70,7 +71,9 @@ final class UpdateCoreController extends AbstractController
|
||||||
], 400);
|
], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$stateFile = $this->getParameter('kernel.project_dir') . '/../hesabixBackup/update_state_' . $uuid . '.json';
|
$projectDir = $this->getParameter('kernel.project_dir');
|
||||||
|
$gitRoot = dirname($projectDir);
|
||||||
|
$stateFile = $gitRoot . '/hesabixBackup/update_state_' . $uuid . '.json';
|
||||||
|
|
||||||
if (!file_exists($stateFile)) {
|
if (!file_exists($stateFile)) {
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
|
@ -97,7 +100,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$isRunning) {
|
if (!$isRunning) {
|
||||||
$backupDir = $this->getParameter('kernel.project_dir') . '/../hesabixBackup';
|
$backupDir = $gitRoot . '/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)) {
|
||||||
|
@ -128,7 +131,9 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
$stateFile = $this->getParameter('kernel.project_dir') . '/../hesabixBackup/update_state_' . $uuid . '.json';
|
$projectDir = $this->getParameter('kernel.project_dir');
|
||||||
|
$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');
|
||||||
|
@ -167,13 +172,14 @@ 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'], $projectDir);
|
$currentProcess = new Process(['git', 'rev-parse', 'HEAD'], $gitRoot);
|
||||||
$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'], $projectDir);
|
$targetProcess = new Process(['git', 'ls-remote', 'origin', 'HEAD'], $gitRoot);
|
||||||
$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';
|
||||||
|
@ -428,4 +434,170 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
63
hesabixCore/src/Entity/AccountToShebaInquiry.php
Normal file
63
hesabixCore/src/Entity/AccountToShebaInquiry.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
63
hesabixCore/src/Entity/CardToShebaInquiry.php
Normal file
63
hesabixCore/src/Entity/CardToShebaInquiry.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -129,6 +129,12 @@ class Permission
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
private ?bool $plugGhestaManager = null;
|
private ?bool $plugGhestaManager = null;
|
||||||
|
|
||||||
|
#[ORM\Column(nullable: true)]
|
||||||
|
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;
|
||||||
|
@ -590,4 +596,27 @@ class Permission
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isPlugTaxSettings(): ?bool
|
||||||
|
{
|
||||||
|
return $this->plugTaxSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlugTaxSettings(?bool $plugTaxSettings): static
|
||||||
|
{
|
||||||
|
$this->plugTaxSettings = $plugTaxSettings;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isInquiry(): ?bool
|
||||||
|
{
|
||||||
|
return $this->inquiry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setInquiry(?bool $inquiry): static
|
||||||
|
{
|
||||||
|
$this->inquiry = $inquiry;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
53
hesabixCore/src/Entity/PluginTaxsettingsKey.php
Normal file
53
hesabixCore/src/Entity/PluginTaxsettingsKey.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
#[ORM\Entity]
|
||||||
|
#[ORM\Table(name: "plugin_taxsettings_keys")]
|
||||||
|
class PluginTaxsettingsKey
|
||||||
|
{
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\GeneratedValue]
|
||||||
|
#[ORM\Column(type: "integer")]
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
#[ORM\Column(type: "integer")]
|
||||||
|
private $business_id;
|
||||||
|
|
||||||
|
#[ORM\Column(type: "integer")]
|
||||||
|
private $user_id;
|
||||||
|
|
||||||
|
#[ORM\Column(type: "text", nullable: true)]
|
||||||
|
private $private_key;
|
||||||
|
|
||||||
|
#[ORM\Column(type: "string", length: 64, nullable: true)]
|
||||||
|
private $tax_memory_id;
|
||||||
|
|
||||||
|
#[ORM\Column(type: "string", length: 64, nullable: true)]
|
||||||
|
private $economic_code;
|
||||||
|
|
||||||
|
#[ORM\Column(type: "datetime")]
|
||||||
|
private $created_at;
|
||||||
|
|
||||||
|
#[ORM\Column(type: "datetime")]
|
||||||
|
private $updated_at;
|
||||||
|
|
||||||
|
// Getters and setters ...
|
||||||
|
public function getId() { return $this->id; }
|
||||||
|
public function getBusinessId() { return $this->business_id; }
|
||||||
|
public function setBusinessId($val) { $this->business_id = $val; }
|
||||||
|
public function getUserId() { return $this->user_id; }
|
||||||
|
public function setUserId($val) { $this->user_id = $val; }
|
||||||
|
public function getPrivateKey() { return $this->private_key; }
|
||||||
|
public function setPrivateKey($val) { $this->private_key = $val; }
|
||||||
|
public function getTaxMemoryId() { return $this->tax_memory_id; }
|
||||||
|
public function setTaxMemoryId($val) { $this->tax_memory_id = $val; }
|
||||||
|
public function getEconomicCode() { return $this->economic_code; }
|
||||||
|
public function setEconomicCode($val) { $this->economic_code = $val; }
|
||||||
|
public function getCreatedAt() { return $this->created_at; }
|
||||||
|
public function setCreatedAt($val) { $this->created_at = $val; }
|
||||||
|
public function getUpdatedAt() { return $this->updated_at; }
|
||||||
|
public function setUpdatedAt($val) { $this->updated_at = $val; }
|
||||||
|
}
|
83
hesabixCore/src/Entity/PostalCodeInquiry.php
Normal file
83
hesabixCore/src/Entity/PostalCodeInquiry.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
hesabixCore/src/Repository/CardToShebaInquiryRepository.php
Normal file
46
hesabixCore/src/Repository/CardToShebaInquiryRepository.php
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
hesabixCore/src/Repository/PostalCodeInquiryRepository.php
Normal file
46
hesabixCore/src/Repository/PostalCodeInquiryRepository.php
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
448
hesabixCore/src/Service/Inquiry.php
Normal file
448
hesabixCore/src/Service/Inquiry.php
Normal file
|
@ -0,0 +1,448 @@
|
||||||
|
<?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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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://github.com/morrning/hesabixCore.git || \
|
git remote add origin https://source.hesabix.ir/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://github.com/morrning/hesabixCore.git || \
|
git remote set-url origin https://source.hesabix.ir/morrning/hesabixCore.git || \
|
||||||
handle_error "Failed to update remote repository"
|
handle_error "Failed to update remote repository"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
188
webUI/src/components/widgets/inquiry/postalcode2address.vue
Normal file
188
webUI/src/components/widgets/inquiry/postalcode2address.vue
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PostalCode2Address",
|
||||||
|
props: {
|
||||||
|
postalCode: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialog: false,
|
||||||
|
settings: {
|
||||||
|
inquiryPanelEnable: false,
|
||||||
|
enablePostalCodeToAddress: false,
|
||||||
|
postalCodeToAddressFee: 0
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
result: null,
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
shouldShow() {
|
||||||
|
// اگر تنظیمات هنوز لود نشده، نمایش بده
|
||||||
|
if (!this.settings.inquiryPanelEnable && !this.settings.enablePostalCodeToAddress) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (this.settings.inquiryPanelEnable === '1' || this.settings.inquiryPanelEnable === true) &&
|
||||||
|
(this.settings.enablePostalCodeToAddress === '1' || this.settings.enablePostalCodeToAddress === true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadSettings();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadSettings() {
|
||||||
|
try {
|
||||||
|
console.log('در حال دریافت تنظیمات...');
|
||||||
|
this.loading = true;
|
||||||
|
const response = await axios.get('/api/plugins/inquiry/settings/get');
|
||||||
|
console.log('تنظیمات دریافت شد:', response.data);
|
||||||
|
this.settings = response.data;
|
||||||
|
console.log('shouldShow:', this.shouldShow);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('خطا در دریافت تنظیمات:', error);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openDialog() {
|
||||||
|
this.dialog = true;
|
||||||
|
},
|
||||||
|
closeDialog() {
|
||||||
|
this.dialog = false;
|
||||||
|
},
|
||||||
|
async submitPostalCode() {
|
||||||
|
this.loading = true;
|
||||||
|
this.result = null;
|
||||||
|
this.error = null;
|
||||||
|
try {
|
||||||
|
console.log('ارسال درخواست برای کد پستی:', this.postalCode);
|
||||||
|
const response = await axios.post('/api/plugins/inquiry/postalcode-to-address', {
|
||||||
|
postal_code: this.postalCode
|
||||||
|
});
|
||||||
|
console.log('پاسخ دریافتی:', response.data);
|
||||||
|
if (response.data && response.data.success) {
|
||||||
|
this.result = response.data.data;
|
||||||
|
// ارسال دادهها به کامپوننت والد
|
||||||
|
const addressDataWithPostalCode = {
|
||||||
|
...response.data.data,
|
||||||
|
postalCode: this.postalCode
|
||||||
|
};
|
||||||
|
this.$emit('address-found', addressDataWithPostalCode);
|
||||||
|
|
||||||
|
// نمایش پیام مناسب بر اساس منبع داده
|
||||||
|
const message = response.data.from_cache
|
||||||
|
? 'اطلاعات آدرس از کش دریافت شد (بدون کسر کارمزد)'
|
||||||
|
: 'اطلاعات آدرس با موفقیت دریافت شد';
|
||||||
|
|
||||||
|
// بستن دیالوگ بعد از موفقیت
|
||||||
|
this.dialog = false;
|
||||||
|
|
||||||
|
// نمایش پیام موفقیت
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.showSnackbar(message, 'success');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.error = (response.data && response.data.message) || "خطا در دریافت اطلاعات";
|
||||||
|
|
||||||
|
// مدیریت خطاهای خاص
|
||||||
|
if (response.data && response.data.error_code === 'HTTP_ERROR_504') {
|
||||||
|
this.error = "سرویس استعلام کد پستی در دسترس نیست. لطفاً بعداً تلاش کنید.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('خطا در ارسال درخواست:', e);
|
||||||
|
this.error = "خطا در ارتباط با سرور";
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showSnackbar(text, color = 'success', timeout = 3000) {
|
||||||
|
// ارسال event به کامپوننت والد برای نمایش snackbar
|
||||||
|
this.$emit('show-snackbar', { text, color, timeout });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="shouldShow">
|
||||||
|
<v-tooltip text="تبدیل کد پستی به آدرس" location="top">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn v-bind="props" size="x-small" color="primary" variant="text" @click="openDialog" class="px-1">
|
||||||
|
<v-icon>mdi-map-marker</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
|
|
||||||
|
<v-dialog v-model="dialog" max-width="500px">
|
||||||
|
<v-card>
|
||||||
|
<v-toolbar color="primary" dark>
|
||||||
|
<v-toolbar-title>تبدیل کد پستی به آدرس</v-toolbar-title>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn icon @click="closeDialog">
|
||||||
|
<v-icon>mdi-close</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
|
<v-card-text class="pt-4">
|
||||||
|
<div class="mb-4">
|
||||||
|
<p class="text-body-2">
|
||||||
|
با استفاده از این سرویس میتوانید کد پستی را به آدرس کامل تبدیل کنید.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-alert type="info" variant="tonal" class="mb-4">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-icon>mdi-information</v-icon>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<strong>کارمزد سرویس:</strong> {{ settings.postalCodeToAddressFee.toLocaleString() }} ریال
|
||||||
|
<br>
|
||||||
|
<small class="text-caption">
|
||||||
|
💡 اگر این کد پستی قبلاً استعلام شده باشد، کارمزدی کسر نمیشود
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</v-alert>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
:model-value="postalCode"
|
||||||
|
label="کد پستی"
|
||||||
|
placeholder="مثال: 1234567890"
|
||||||
|
prepend-inner-icon="mdi-mailbox"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
readonly
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
block
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="!postalCode || postalCode.length < 10"
|
||||||
|
@click="submitPostalCode"
|
||||||
|
>
|
||||||
|
تبدیل به آدرس
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<!-- نمایش خطا -->
|
||||||
|
<v-alert
|
||||||
|
v-if="error"
|
||||||
|
type="error"
|
||||||
|
variant="tonal"
|
||||||
|
class="mt-4"
|
||||||
|
border="start"
|
||||||
|
>
|
||||||
|
{{ error }}
|
||||||
|
</v-alert>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -86,6 +86,7 @@ const en_lang = {
|
||||||
cheque_output: "Cheque Output",
|
cheque_output: "Cheque Output",
|
||||||
presells: "Presells",
|
presells: "Presells",
|
||||||
presell_view: "View Presell",
|
presell_view: "View Presell",
|
||||||
|
inquiry: "Inquiries",
|
||||||
hrm: 'HR & Payroll',
|
hrm: 'HR & Payroll',
|
||||||
hrm_docs: 'Payroll Document',
|
hrm_docs: 'Payroll Document',
|
||||||
},
|
},
|
||||||
|
|
|
@ -173,6 +173,7 @@ const fa_lang = {
|
||||||
reports: "گزارشات",
|
reports: "گزارشات",
|
||||||
settings: "تنظیمات",
|
settings: "تنظیمات",
|
||||||
bid_settings: "تنظیمات کسبوکار",
|
bid_settings: "تنظیمات کسبوکار",
|
||||||
|
tax_settings: "تنظیمات مالیاتی",
|
||||||
print_settings: "چاپ اسناد",
|
print_settings: "چاپ اسناد",
|
||||||
user_perms: "کاربران و دسترسیها",
|
user_perms: "کاربران و دسترسیها",
|
||||||
avatar_settings: "نمایه و مهر کسبوکار",
|
avatar_settings: "نمایه و مهر کسبوکار",
|
||||||
|
@ -193,9 +194,12 @@ const fa_lang = {
|
||||||
plugins_invoices: "صورت حسابها",
|
plugins_invoices: "صورت حسابها",
|
||||||
repservice: "مدیریت تعمیرگاه",
|
repservice: "مدیریت تعمیرگاه",
|
||||||
repservice_reqs: "درخواستها",
|
repservice_reqs: "درخواستها",
|
||||||
|
inquiry: "استعلامات",
|
||||||
hrm: 'منابع انسانی',
|
hrm: 'منابع انسانی',
|
||||||
hrm_docs: 'سند حقوق',
|
hrm_docs: 'سند حقوق',
|
||||||
buysellByPerson: "گزارش خرید و فروش های اشخاص",
|
buysellByPerson: "گزارش خرید و فروش های اشخاص",
|
||||||
|
tax_system: "سامانه مودیان مالیاتی",
|
||||||
|
tax_invoices: "صورتحساب ها",
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
month: "{id} ماه",
|
month: "{id} ماه",
|
||||||
|
@ -268,6 +272,19 @@ const fa_lang = {
|
||||||
"fetchError": "خطا در دریافت",
|
"fetchError": "خطا در دریافت",
|
||||||
"cancel": "لغو",
|
"cancel": "لغو",
|
||||||
"confirm": "تأیید",
|
"confirm": "تأیید",
|
||||||
|
"updateSourceTitle": "منبع بهروزرسانی",
|
||||||
|
"updateSourceLabel": "آدرس منبع بهروزرسانی",
|
||||||
|
"changeSourceButton": "تغییر منبع",
|
||||||
|
"sourceUrlRequired": "لطفاً آدرس منبع بهروزرسانی را وارد کنید",
|
||||||
|
"changingSourceMessage": "در حال تغییر منبع بهروزرسانی...",
|
||||||
|
"sourceChangeSuccess": "منبع بهروزرسانی با موفقیت تغییر یافت",
|
||||||
|
"sourceChangeError": "خطایی در تغییر منبع بهروزرسانی رخ داد",
|
||||||
|
"notGitRepository": "این پروژه یک مخزن Git نیست",
|
||||||
|
"invalidRepositoryUrl": "آدرس مخزن نامعتبر است. لطفاً یک آدرس HTTP یا SSH معتبر وارد کنید.",
|
||||||
|
"repositoryUrlError": "خطا در دریافت آدرس مخزن",
|
||||||
|
"repositoryChangeError": "خطا در تغییر آدرس مخزن",
|
||||||
|
"repositoryConnectionError": "خطا در اتصال به مخزن جدید",
|
||||||
|
"repositoryChangeSuccess": "آدرس مخزن با موفقیت تغییر یافت و اتصال برقرار شد",
|
||||||
},
|
},
|
||||||
static: {
|
static: {
|
||||||
not_found: "صفحه مورد نظر یافت نشد",
|
not_found: "صفحه مورد نظر یافت نشد",
|
||||||
|
@ -799,6 +816,19 @@ const fa_lang = {
|
||||||
sms_settings_plug_accpro_pass_cheque_input: "واگذاری چک",
|
sms_settings_plug_accpro_pass_cheque_input: "واگذاری چک",
|
||||||
sms_settings_reject_cheque_input: "برگشت چک",
|
sms_settings_reject_cheque_input: "برگشت چک",
|
||||||
sms_settings_plug_accpro_reject_cheque_input: "برگشت چک",
|
sms_settings_plug_accpro_reject_cheque_input: "برگشت چک",
|
||||||
|
inquiry_zohal_api_key: "کلید API زحل",
|
||||||
|
inquiry_zohal_api_key_des: "کلید API زحل برای دریافت اطلاعات از سامانه زحل",
|
||||||
|
inquiry_zohal_api_key_placeholder: "کلید API زحل",
|
||||||
|
inquiry_zohal_api_key_label: "کلید API زحل",
|
||||||
|
enable_postalcode_to_address: "تبدیل کد پستی به آدرس",
|
||||||
|
postalcode_to_address_fee: "کارمزد تبدیل کد پستی به آدرس",
|
||||||
|
enable_card_to_sheba: "تبدیل شماره کارت به شبا",
|
||||||
|
card_to_sheba_fee: "کارمزد تبدیل شماره کارت به شبا",
|
||||||
|
enable_account_to_sheba: "تبدیل حساب به شبا",
|
||||||
|
account_to_sheba_fee: "کارمزد تبدیل حساب به شبا",
|
||||||
|
inquiry_panel_enable: "فعال سازی پنل سامانه استعلامات",
|
||||||
|
inquiry_panel: "پنل سامانه استعلامات",
|
||||||
|
inquiry_panel_zohal: "زحل",
|
||||||
app_site: "آدرس اینترفیس(رابط کاربری)",
|
app_site: "آدرس اینترفیس(رابط کاربری)",
|
||||||
keywords: "کلیدواژهها با کاما (,) از هم جدا شوند",
|
keywords: "کلیدواژهها با کاما (,) از هم جدا شوند",
|
||||||
zarinpal_api: "کد API زرینپال",
|
zarinpal_api: "کد API زرینپال",
|
||||||
|
|
|
@ -510,6 +510,18 @@ const router = createRouter({
|
||||||
component: () =>
|
component: () =>
|
||||||
import('../views/acc/settings/extramoneys.vue'),
|
import('../views/acc/settings/extramoneys.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'plugins/tax/settings',
|
||||||
|
name: 'business_tax_settings',
|
||||||
|
component: () =>
|
||||||
|
import('../views/acc/tax/tax-settings.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'plugins/tax/invoices/list',
|
||||||
|
name: 'tax_invoices_list',
|
||||||
|
component: () =>
|
||||||
|
import('../views/acc/tax/invoices/list.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'business/logs',
|
path: 'business/logs',
|
||||||
name: 'business_logs',
|
name: 'business_logs',
|
||||||
|
@ -988,6 +1000,12 @@ const router = createRouter({
|
||||||
component: () =>
|
component: () =>
|
||||||
import('../views/acc/plugins/hrm/docs/view.vue'),
|
import('../views/acc/plugins/hrm/docs/view.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'inquiry/panel',
|
||||||
|
name: 'inquiry_panel',
|
||||||
|
component: () =>
|
||||||
|
import('../views/acc/inquiry/panel.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -169,8 +169,9 @@ export default {
|
||||||
{ path: '/acc/business/extramoneys', key: '-', label: this.$t('drawer.extra_moneys'), ctrl: true, shift: true, permission: () => this.permissions.settings && this.isPluginActive('accpro') },
|
{ path: '/acc/business/extramoneys', key: '-', label: this.$t('drawer.extra_moneys'), ctrl: true, shift: true, permission: () => this.permissions.settings && this.isPluginActive('accpro') },
|
||||||
{ path: '/acc/business/logs', key: '=', label: this.$t('drawer.history'), ctrl: true, shift: true, permission: () => this.permissions.log },
|
{ path: '/acc/business/logs', key: '=', label: this.$t('drawer.history'), ctrl: true, shift: true, permission: () => this.permissions.log },
|
||||||
{ path: '/acc/plugin/repservice/order/list', key: '[', label: this.$t('drawer.repservice_reqs'), ctrl: true, shift: true, permission: () => this.permissions.plugRepservice && this.isPluginActive('repservice') },
|
{ path: '/acc/plugin/repservice/order/list', key: '[', label: this.$t('drawer.repservice_reqs'), ctrl: true, shift: true, permission: () => this.permissions.plugRepservice && this.isPluginActive('repservice') },
|
||||||
{ path: '/acc/sms/panel', key: ']', label: this.$t('drawer.sms_panel'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
{ path: '/acc/inquiry/panel', key: ']', label: this.$t('drawer.inquiry'), ctrl: true, shift: true, permission: () => true },
|
||||||
{ path: '/acc/printers/list', key: ';', label: this.$t('drawer.cloud_printers'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
{ path: '/acc/printers/list', key: ';', label: this.$t('drawer.cloud_printers'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
||||||
|
{ path: '/acc/sms/panel', key: '`', label: this.$t('drawer.sms_panel'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
||||||
{ path: '/acc/archive/list', key: '\'', label: this.$t('drawer.archive_files'), ctrl: true, shift: true, permission: () => this.permissions.archiveUpload || this.permissions.archiveMod || this.permissions.archiveDelete },
|
{ path: '/acc/archive/list', key: '\'', label: this.$t('drawer.archive_files'), ctrl: true, shift: true, permission: () => this.permissions.archiveUpload || this.permissions.archiveMod || this.permissions.archiveDelete },
|
||||||
{ path: '/acc/archive/order/new', key: ',', label: this.$t('drawer.archive_order'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
{ path: '/acc/archive/order/new', key: ',', label: this.$t('drawer.archive_order'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
||||||
{ path: '/acc/archive/order/list', key: '.', label: this.$t('drawer.archive_log'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
{ path: '/acc/archive/order/list', key: '.', label: this.$t('drawer.archive_log'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
||||||
|
@ -179,6 +180,8 @@ export default {
|
||||||
{ path: '/acc/plugin-center/invoice', key: '`', label: this.$t('drawer.plugins_invoices'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
{ path: '/acc/plugin-center/invoice', key: '`', label: this.$t('drawer.plugins_invoices'), ctrl: true, shift: true, permission: () => this.permissions.owner },
|
||||||
{ path: '/acc/hrm/docs/list', key: 'H', label: this.$t('drawer.hrm_docs'), ctrl: true, shift: true, permission: () => this.isPluginActive('hrm') && this.permissions.plugHrmDocs },
|
{ path: '/acc/hrm/docs/list', key: 'H', label: this.$t('drawer.hrm_docs'), ctrl: true, shift: true, permission: () => this.isPluginActive('hrm') && this.permissions.plugHrmDocs },
|
||||||
{ path: '/acc/plugins/ghesta/list', key: 'G', label: this.$t('drawer.ghesta_invoices'), ctrl: true, shift: true, permission: () => this.isPluginActive('ghesta') && this.permissions.plugGhestaManager },
|
{ path: '/acc/plugins/ghesta/list', key: 'G', label: this.$t('drawer.ghesta_invoices'), ctrl: true, shift: true, permission: () => this.isPluginActive('ghesta') && this.permissions.plugGhestaManager },
|
||||||
|
{ path: '/acc/plugins/tax/invoices/list', key: 'L', label: this.$t('drawer.tax_invoices'), ctrl: true, shift: true, permission: () => this.permissions.settings && this.isPluginActive('taxsettings') },
|
||||||
|
{ path: '/acc/plugins/tax/settings', key: 'T', label: this.$t('drawer.tax_settings'), ctrl: true, shift: true, permission: () => this.permissions.settings && this.isPluginActive('taxsettings') },
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
restorePermissions(shortcuts) {
|
restorePermissions(shortcuts) {
|
||||||
|
@ -788,6 +791,13 @@ export default {
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list-group>
|
</v-list-group>
|
||||||
|
<v-list-item to="/acc/inquiry/panel">
|
||||||
|
<template v-slot:prepend><v-icon icon="mdi-magnify"></v-icon></template>
|
||||||
|
<v-list-item-title>
|
||||||
|
{{ $t('drawer.inquiry') }}
|
||||||
|
<span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/inquiry/panel') }}</span>
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
<v-list-group v-show="isPluginActive('hrm') && permissions.plugHrmDocs">
|
<v-list-group v-show="isPluginActive('hrm') && permissions.plugHrmDocs">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-list-item class="text-dark" v-bind="props" :title="$t('drawer.hrm')">
|
<v-list-item class="text-dark" v-bind="props" :title="$t('drawer.hrm')">
|
||||||
|
@ -828,6 +838,23 @@ export default {
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list-group>
|
</v-list-group>
|
||||||
|
<v-list-group v-show="isPluginActive('taxsettings') && permissions.plugTaxSettings">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-list-item class="text-dark" v-bind="props" :title="$t('drawer.tax_system')">
|
||||||
|
<template v-slot:prepend><v-icon icon="mdi-file-document-multiple" color="primary"></v-icon></template>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
<v-list-item to="/acc/plugins/tax/invoices/list">
|
||||||
|
<v-list-item-title>
|
||||||
|
{{ $t('drawer.tax_invoices') }}
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item to="/acc/plugins/tax/settings">
|
||||||
|
<v-list-item-title>
|
||||||
|
{{ $t('drawer.tax_settings') }}
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list-group>
|
||||||
<v-list-item class="text-dark" v-if="permissions.owner" to="/acc/sms/panel">
|
<v-list-item class="text-dark" v-if="permissions.owner" to="/acc/sms/panel">
|
||||||
<template v-slot:prepend><v-icon icon="mdi-message-cog" color="primary"></v-icon></template>
|
<template v-slot:prepend><v-icon icon="mdi-message-cog" color="primary"></v-icon></template>
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
|
|
|
@ -211,7 +211,7 @@ export default defineComponent({
|
||||||
v-model="snackbar"
|
v-model="snackbar"
|
||||||
:color="snackbarColor"
|
:color="snackbarColor"
|
||||||
timeout="3000"
|
timeout="3000"
|
||||||
location="top"
|
location="bottom"
|
||||||
>
|
>
|
||||||
{{ snackbarText }}
|
{{ snackbarText }}
|
||||||
<template v-slot:actions>
|
<template v-slot:actions>
|
||||||
|
|
64
webUI/src/views/acc/inquiry/README.md
Normal file
64
webUI/src/views/acc/inquiry/README.md
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# پنل استعلامات
|
||||||
|
|
||||||
|
این بخش شامل قابلیتهای مختلف استعلام و تبدیل اطلاعات است که بر اساس تنظیمات سیستم فعال یا غیرفعال میشوند.
|
||||||
|
|
||||||
|
## قابلیتهای موجود
|
||||||
|
|
||||||
|
### 1. تبدیل کد پستی به آدرس
|
||||||
|
- **وضعیت**: فعال/غیرفعال بر اساس تنظیمات سیستم
|
||||||
|
- **کارمزد**: قابل تنظیم در بخش مدیریت
|
||||||
|
- **API Endpoint**: `/api/plugins/inquiry/postalcode-to-address`
|
||||||
|
- **عملکرد**: تبدیل کد پستی 10 رقمی به آدرس کامل
|
||||||
|
|
||||||
|
### 2. تبدیل شماره کارت به شبا
|
||||||
|
- **وضعیت**: فعال/غیرفعال بر اساس تنظیمات سیستم
|
||||||
|
- **کارمزد**: قابل تنظیم در بخش مدیریت
|
||||||
|
- **API Endpoint**: `/api/plugins/inquiry/card-to-sheba`
|
||||||
|
- **عملکرد**: تبدیل شماره کارت 16 رقمی به شماره شبا (در حال توسعه)
|
||||||
|
|
||||||
|
### 3. تبدیل حساب به شبا
|
||||||
|
- **وضعیت**: فعال/غیرفعال بر اساس تنظیمات سیستم
|
||||||
|
- **کارمزد**: قابل تنظیم در بخش مدیریت
|
||||||
|
- **API Endpoint**: `/api/plugins/inquiry/account-to-sheba`
|
||||||
|
- **عملکرد**: تبدیل شماره حساب بانکی به شماره شبا (در حال توسعه)
|
||||||
|
|
||||||
|
## ویژگیهای رابط کاربری
|
||||||
|
|
||||||
|
### نمایش سرویسهای فعال
|
||||||
|
- نمایش کارتهای رنگی برای هر سرویس فعال
|
||||||
|
- نمایش کارمزد هر سرویس
|
||||||
|
- نشانگذاری وضعیت فعال/غیرفعال
|
||||||
|
|
||||||
|
### دیالوگهای استعلام
|
||||||
|
- فرمهای اعتبارسنجی شده
|
||||||
|
- نمایش کارمزد قبل از استعلام
|
||||||
|
- نمایش نتیجه در قالب پیامهای زیبا
|
||||||
|
|
||||||
|
### مدیریت خطاها
|
||||||
|
- بررسی موجودی کافی
|
||||||
|
- نمایش پیامهای خطای مناسب
|
||||||
|
- لاگ کردن عملیاتها
|
||||||
|
|
||||||
|
## تنظیمات مورد نیاز
|
||||||
|
|
||||||
|
برای فعالسازی این قابلیتها، مدیر سیستم باید در بخش تنظیمات سیستم موارد زیر را تنظیم کند:
|
||||||
|
|
||||||
|
1. **فعالسازی پنل استعلامات**: `inquiryPanelEnable`
|
||||||
|
2. **انتخاب پنل**: `inquiryPanel` (فعلاً فقط زحل)
|
||||||
|
3. **کلید API پنل**: `inquiryZohalAPIKey`
|
||||||
|
4. **فعالسازی هر سرویس**: `enablePostalCodeToAddress`, `enableCardToSheba`, `enableAccountToSheba`
|
||||||
|
5. **تعیین کارمزد**: `postalCodeToAddressFee`, `cardToShebaFee`, `accountToShebaFee`
|
||||||
|
|
||||||
|
## نکات فنی
|
||||||
|
|
||||||
|
- تمام درخواستها نیاز به احراز هویت دارند
|
||||||
|
- کارمزد از موجودی SMS کاربر کسر میشود
|
||||||
|
- نتایج در کش ذخیره میشوند تا از تکرار درخواستهای مشابه جلوگیری شود
|
||||||
|
- تمام عملیات لاگ میشوند
|
||||||
|
|
||||||
|
## توسعه آینده
|
||||||
|
|
||||||
|
- تکمیل سرویسهای تبدیل کارت و حساب به شبا
|
||||||
|
- اضافه کردن سرویسهای جدید
|
||||||
|
- بهبود رابط کاربری
|
||||||
|
- اضافه کردن گزارشگیری
|
1158
webUI/src/views/acc/inquiry/panel.vue
Normal file
1158
webUI/src/views/acc/inquiry/panel.vue
Normal file
File diff suppressed because it is too large
Load diff
|
@ -141,7 +141,11 @@
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<v-text-field v-model="person.postalcode" :label="$t('pages.person.postal_code')" dense
|
<v-text-field v-model="person.postalcode" :label="$t('pages.person.postal_code')" dense
|
||||||
prepend-inner-icon="mdi-mailbox" hide-details />
|
prepend-inner-icon="mdi-mailbox" hide-details>
|
||||||
|
<template v-slot:append-inner>
|
||||||
|
<PostalCode2Address :postal-code="person.postalcode" @address-found="fillAddressFields" @show-snackbar="showSnackbarFromChild" />
|
||||||
|
</template>
|
||||||
|
</v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-textarea v-model="person.address" :label="$t('pages.person.address')" dense
|
<v-textarea v-model="person.address" :label="$t('pages.person.address')" dense
|
||||||
|
@ -211,15 +215,29 @@
|
||||||
<v-overlay :model-value="loading" contained class="align-center justify-center">
|
<v-overlay :model-value="loading" contained class="align-center justify-center">
|
||||||
<v-progress-circular indeterminate size="64" />
|
<v-progress-circular indeterminate size="64" />
|
||||||
</v-overlay>
|
</v-overlay>
|
||||||
|
|
||||||
|
<!-- Snackbar برای نمایش پیامها -->
|
||||||
|
<v-snackbar v-model="snackbar.show" :color="snackbar.color" :timeout="snackbar.timeout">
|
||||||
|
{{ snackbar.text }}
|
||||||
|
<template v-slot:actions>
|
||||||
|
<v-btn color="white" text @click="snackbar.show = false">
|
||||||
|
بستن
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import PostalCode2Address from "@/components/widgets/inquiry/postalcode2address.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "insert",
|
name: "insert",
|
||||||
|
components: {
|
||||||
|
PostalCode2Address
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tabs: '0',
|
tabs: '0',
|
||||||
|
@ -250,6 +268,12 @@ export default {
|
||||||
accounts: [],
|
accounts: [],
|
||||||
prelabel: ref(null),
|
prelabel: ref(null),
|
||||||
speedAccess: false
|
speedAccess: false
|
||||||
|
},
|
||||||
|
snackbar: {
|
||||||
|
show: false,
|
||||||
|
text: '',
|
||||||
|
color: '',
|
||||||
|
timeout: 3000
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -362,7 +386,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post('/api/person/mod/' + this.person.code, this.person);
|
const response = await axios.post('/api/person/mod/' + this.person.code, this.person);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (response.data.result === 2) {
|
if (response.data && response.data.result === 2) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
text: this.$t('pages.person.already_exists'),
|
text: this.$t('pages.person.already_exists'),
|
||||||
icon: 'error',
|
icon: 'error',
|
||||||
|
@ -386,7 +410,72 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
showSnackbar(text, color = 'success', timeout = 3000) {
|
||||||
|
this.snackbar.show = true;
|
||||||
|
this.snackbar.text = text;
|
||||||
|
this.snackbar.color = color;
|
||||||
|
this.snackbar.timeout = timeout;
|
||||||
|
},
|
||||||
|
fillAddressFields(addressData) {
|
||||||
|
// پر کردن فیلدهای آدرس
|
||||||
|
this.person.keshvar = 'ایران'; // کشور به صورت پیشفرض ایران
|
||||||
|
this.person.ostan = addressData.province || '';
|
||||||
|
this.person.shahr = addressData.town || '';
|
||||||
|
this.person.postalcode = addressData.postalCode || '';
|
||||||
|
|
||||||
|
// ساخت آدرس کامل
|
||||||
|
let fullAddress = '';
|
||||||
|
|
||||||
|
if (addressData.district) {
|
||||||
|
fullAddress += addressData.district;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addressData.street) {
|
||||||
|
if (fullAddress) fullAddress += ' - ';
|
||||||
|
fullAddress += addressData.street;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addressData.street2) {
|
||||||
|
if (fullAddress) fullAddress += ' - ';
|
||||||
|
fullAddress += addressData.street2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addressData.number) {
|
||||||
|
if (fullAddress) fullAddress += ' - پلاک ';
|
||||||
|
fullAddress += addressData.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addressData.floor && addressData.floor !== 'همکف') {
|
||||||
|
if (fullAddress) fullAddress += ' - طبقه ';
|
||||||
|
fullAddress += addressData.floor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addressData.side_floor) {
|
||||||
|
if (fullAddress) fullAddress += ' - واحد ';
|
||||||
|
fullAddress += addressData.side_floor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addressData.building_name) {
|
||||||
|
if (fullAddress) fullAddress += ' - ';
|
||||||
|
fullAddress += addressData.building_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addressData.description) {
|
||||||
|
if (fullAddress) fullAddress += ' - ';
|
||||||
|
fullAddress += addressData.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.person.address = fullAddress;
|
||||||
|
|
||||||
|
// نمایش پیام موفقیت
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.showSnackbar('اطلاعات آدرس با موفقیت تکمیل شد', 'success');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showSnackbarFromChild(snackbarData) {
|
||||||
|
this.showSnackbar(snackbarData.text, snackbarData.color, snackbarData.timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -230,7 +230,7 @@
|
||||||
v-model="snackbar.show"
|
v-model="snackbar.show"
|
||||||
:color="snackbar.color"
|
:color="snackbar.color"
|
||||||
:timeout="3000"
|
:timeout="3000"
|
||||||
location="top"
|
location="bottom"
|
||||||
>
|
>
|
||||||
{{ snackbar.text }}
|
{{ snackbar.text }}
|
||||||
<template v-slot:actions>
|
<template v-slot:actions>
|
||||||
|
|
|
@ -241,6 +241,12 @@ const router = createRouter({
|
||||||
component: () =>
|
component: () =>
|
||||||
import ('../views/settings/extramoneys.vue'),
|
import ('../views/settings/extramoneys.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/acc/business/tax-settings',
|
||||||
|
name: 'business_tax_settings',
|
||||||
|
component: () =>
|
||||||
|
import ('../views/settings/tax-settings.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/acc/business/logs',
|
path: '/acc/business/logs',
|
||||||
name: 'business_logs',
|
name: 'business_logs',
|
||||||
|
|
321
webUI/src/views/acc/settings/tax-settings.vue
Normal file
321
webUI/src/views/acc/settings/tax-settings.vue
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-toolbar color="toolbar" title="تنظیمات مالیاتی">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-tooltip text="بازگشت" location="bottom">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
|
||||||
|
icon="mdi-arrow-right" />
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
|
</template>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn :loading="loading" @click="saveSettings()" icon="" color="green">
|
||||||
|
<v-tooltip activator="parent" text="ذخیره تنظیمات" location="bottom" />
|
||||||
|
<v-icon icon="mdi-content-save"></v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
|
<v-container>
|
||||||
|
<v-card :loading="loading" :disabled="loading">
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
@click="showCSRDialog = true"
|
||||||
|
prepend-icon="mdi-key-plus"
|
||||||
|
>
|
||||||
|
ساخت کلید و CSR
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="settings.taxMemoryId"
|
||||||
|
label="شناسه یکتای حافظه مالیاتی"
|
||||||
|
hide-details
|
||||||
|
density="compact"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="settings.economicCode"
|
||||||
|
label="کد اقتصادی"
|
||||||
|
hide-details
|
||||||
|
density="compact"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-row class="mt-4">
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-textarea
|
||||||
|
v-model="settings.privateKey"
|
||||||
|
label="Private Key"
|
||||||
|
rows="15"
|
||||||
|
variant="outlined"
|
||||||
|
hide-details
|
||||||
|
placeholder="کلید خصوصی اینجا قرار میگیرد..."
|
||||||
|
></v-textarea>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-container>
|
||||||
|
|
||||||
|
<!-- Dialog برای ساخت کلید و CSR -->
|
||||||
|
<v-dialog v-model="showCSRDialog" max-width="600px">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title class="text-h6">
|
||||||
|
ساخت کلید و CSR
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="csrForm">
|
||||||
|
<div class="mb-4">
|
||||||
|
<div class="text-subtitle-2 mb-2">شخص</div>
|
||||||
|
<v-radio-group
|
||||||
|
v-model="csrData.personType"
|
||||||
|
inline
|
||||||
|
hide-details
|
||||||
|
>
|
||||||
|
<v-radio
|
||||||
|
v-for="type in personTypes"
|
||||||
|
:key="type.value"
|
||||||
|
:label="type.title"
|
||||||
|
:value="type.value"
|
||||||
|
:disabled="type.value === 'natural'"
|
||||||
|
></v-radio>
|
||||||
|
</v-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="csrData.nationalId"
|
||||||
|
label="شناسه ملی"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="csrData.nameFa"
|
||||||
|
label="نام (فارسی)"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="csrData.nameEn"
|
||||||
|
label="نام (انگلیسی)"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="csrData.email"
|
||||||
|
label="ایمیل"
|
||||||
|
type="email"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn @click="showCSRDialog = false" variant="text">
|
||||||
|
انصراف
|
||||||
|
</v-btn>
|
||||||
|
<v-btn @click="generateCSR()" color="primary" :loading="csrLoading">
|
||||||
|
تایید
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
<v-dialog v-model="showResultDialog" max-width="900px">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title class="text-h6 pb-0">ساخت کلید و CSR</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-alert type="info" color="blue" class="mb-4" icon="mdi-alert">
|
||||||
|
<span class="font-weight-bold">توجه: لطفا این اطلاعات را دانلود کنید و در یک جای امن نگهداری کنید. به دلایل امنیتی اطلاعات شما را نگهداری نمیکنیم، در صورتی که این اطلاعات را گم کنید، امکان بازیابی آن وجود ندارد.</span>
|
||||||
|
</v-alert>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<div class="mb-2 font-weight-bold">CSR</div>
|
||||||
|
<v-textarea readonly rows="10" :value="resultData.csr" variant="outlined"></v-textarea>
|
||||||
|
<v-row class="mt-2">
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="copyToClipboard(resultData.csr)"><v-icon start>mdi-content-copy</v-icon>کپی</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="downloadFile(resultData.csr, 'csr.txt')"><v-icon start>mdi-download</v-icon>دانلود</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<div class="mb-2 font-weight-bold">Public Key</div>
|
||||||
|
<v-textarea readonly rows="10" :value="resultData.publicKey" variant="outlined"></v-textarea>
|
||||||
|
<v-row class="mt-2">
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="copyToClipboard(resultData.publicKey)"><v-icon start>mdi-content-copy</v-icon>کپی</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="downloadFile(resultData.publicKey, 'public_key.txt')"><v-icon start>mdi-download</v-icon>دانلود</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<div class="mb-2 font-weight-bold">Private Key</div>
|
||||||
|
<v-textarea readonly rows="10" :value="resultData.privateKey" variant="outlined"></v-textarea>
|
||||||
|
<v-row class="mt-2">
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="copyToClipboard(resultData.privateKey)"><v-icon start>mdi-content-copy</v-icon>کپی</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="downloadFile(resultData.privateKey, 'private_key.txt')"><v-icon start>mdi-download</v-icon>دانلود</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn @click="showResultDialog = false" color="primary">بستن</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
<v-snackbar
|
||||||
|
v-model="snackbar.show"
|
||||||
|
:color="snackbar.color"
|
||||||
|
:timeout="3000"
|
||||||
|
>
|
||||||
|
{{ snackbar.text }}
|
||||||
|
<template v-slot:actions>
|
||||||
|
<v-btn
|
||||||
|
color="white"
|
||||||
|
variant="text"
|
||||||
|
@click="snackbar.show = false"
|
||||||
|
>
|
||||||
|
بستن
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TaxSettings',
|
||||||
|
data: () => ({
|
||||||
|
loading: false,
|
||||||
|
csrLoading: false,
|
||||||
|
showCSRDialog: false,
|
||||||
|
showResultDialog: false,
|
||||||
|
settings: {
|
||||||
|
taxMemoryId: '',
|
||||||
|
economicCode: '',
|
||||||
|
privateKey: '',
|
||||||
|
},
|
||||||
|
csrData: {
|
||||||
|
personType: 'legal',
|
||||||
|
nationalId: '',
|
||||||
|
nameFa: '',
|
||||||
|
nameEn: '',
|
||||||
|
email: '',
|
||||||
|
},
|
||||||
|
resultData: {
|
||||||
|
csr: '',
|
||||||
|
publicKey: '',
|
||||||
|
privateKey: ''
|
||||||
|
},
|
||||||
|
personTypes: [
|
||||||
|
{ title: 'حقیقی', value: 'natural' },
|
||||||
|
{ title: 'حقوقی', value: 'legal' }
|
||||||
|
],
|
||||||
|
snackbar: {
|
||||||
|
show: false,
|
||||||
|
text: '',
|
||||||
|
color: 'success'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
async loadSettings() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/api/plugins/tax-settings/get');
|
||||||
|
this.settings = {
|
||||||
|
...this.settings,
|
||||||
|
...response.data
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this.showSnackbar('خطا در بارگذاری تنظیمات', 'error');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async saveSettings() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const dataToSave = { ...this.settings };
|
||||||
|
await axios.post('/api/plugins/tax-settings/save', dataToSave);
|
||||||
|
this.showSnackbar('تنظیمات با موفقیت ذخیره شد', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
this.showSnackbar('خطا در ذخیره تنظیمات', 'error');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async generateCSR() {
|
||||||
|
this.csrLoading = true;
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/api/plugins/tax-settings/generate-csr', this.csrData);
|
||||||
|
|
||||||
|
if (response.data.success) {
|
||||||
|
// this.settings.privateKey = response.data.privateKey;
|
||||||
|
// نمایش دیالوگ نتیجه
|
||||||
|
this.resultData.csr = response.data.csr;
|
||||||
|
this.resultData.privateKey = response.data.privateKey;
|
||||||
|
this.resultData.publicKey = response.data.publicKey || '';
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.showCSRDialog = false;
|
||||||
|
this.showSnackbar('کلید و CSR با موفقیت تولید شد', 'success');
|
||||||
|
} else {
|
||||||
|
this.showSnackbar(response.data.message || 'خطا در تولید کلید و CSR', 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showSnackbar('خطا در تولید کلید و CSR', 'error');
|
||||||
|
} finally {
|
||||||
|
this.csrLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copyToClipboard(text) {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
this.showSnackbar('کپی شد');
|
||||||
|
},
|
||||||
|
downloadFile(content, filename) {
|
||||||
|
const blob = new Blob([content], { type: 'text/plain' });
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = URL.createObjectURL(blob);
|
||||||
|
link.download = filename;
|
||||||
|
link.click();
|
||||||
|
URL.revokeObjectURL(link.href);
|
||||||
|
},
|
||||||
|
showSnackbar(text, color = 'success') {
|
||||||
|
this.snackbar = {
|
||||||
|
show: true,
|
||||||
|
text,
|
||||||
|
color
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadSettings();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -59,6 +59,7 @@
|
||||||
</v-alert>
|
</v-alert>
|
||||||
</div>
|
</div>
|
||||||
</v-alert>
|
</v-alert>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" md="4">
|
<v-col cols="12" md="4">
|
||||||
|
@ -161,6 +162,18 @@
|
||||||
:disabled="loadingSwitches.archiveView"
|
:disabled="loadingSwitches.archiveView"
|
||||||
></v-switch>
|
></v-switch>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
<v-list-item>
|
||||||
|
<v-switch
|
||||||
|
v-model="info.inquiry"
|
||||||
|
label="سرویس استعلام"
|
||||||
|
@change="savePerms('inquiry')"
|
||||||
|
hide-details
|
||||||
|
color="success"
|
||||||
|
density="comfortable"
|
||||||
|
:loading="loadingSwitches.inquiry"
|
||||||
|
:disabled="loadingSwitches.inquiry"
|
||||||
|
></v-switch>
|
||||||
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
@ -601,7 +614,32 @@
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
|
||||||
|
<v-row v-if="isPluginActive('taxsettings')" class="mt-4">
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-card-title class="text-h6 font-weight-bold mb-4">افزونه تنظیمات مالیاتی</v-card-title>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<v-card variant="outlined" class="h-100">
|
||||||
|
<v-card-text>
|
||||||
|
<v-list>
|
||||||
|
<v-list-item>
|
||||||
|
<v-switch
|
||||||
|
v-model="info.plugTaxSettings"
|
||||||
|
label="مدیریت تنظیمات مالیاتی"
|
||||||
|
@change="savePerms('plugTaxSettings')"
|
||||||
|
hide-details
|
||||||
|
color="success"
|
||||||
|
density="comfortable"
|
||||||
|
:loading="loadingSwitches.plugTaxSettings"
|
||||||
|
:disabled="loadingSwitches.plugTaxSettings"
|
||||||
|
></v-switch>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
<v-snackbar
|
<v-snackbar
|
||||||
|
@ -679,7 +717,9 @@ export default {
|
||||||
plugNoghreSell: false,
|
plugNoghreSell: false,
|
||||||
plugCCAdmin: false,
|
plugCCAdmin: false,
|
||||||
plugHrmDocs: false,
|
plugHrmDocs: false,
|
||||||
plugGhestaManager: false
|
plugGhestaManager: false,
|
||||||
|
plugTaxSettings: false,
|
||||||
|
inquiry: false
|
||||||
};
|
};
|
||||||
|
|
||||||
axios.post('/api/business/get/user/permissions',
|
axios.post('/api/business/get/user/permissions',
|
||||||
|
|
173
webUI/src/views/acc/tax/invoices/list.vue
Normal file
173
webUI/src/views/acc/tax/invoices/list.vue
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-toolbar color="toolbar" title="صورتحسابهای ارسالی به سامانه مودیان مالیاتی">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-tooltip text="بازگشت" location="bottom">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
|
||||||
|
icon="mdi-arrow-right" />
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
|
</template>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn :loading="loading" @click="loadData()" icon="" color="primary">
|
||||||
|
<v-tooltip activator="parent" text="بازخوانی" location="bottom" />
|
||||||
|
<v-icon icon="mdi-refresh"></v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
|
<v-container>
|
||||||
|
<v-card :loading="loading" :disabled="loading">
|
||||||
|
<v-card-text>
|
||||||
|
<v-alert type="info" color="blue" class="mb-4" icon="mdi-information">
|
||||||
|
<span class="font-weight-bold">این بخش برای نمایش لیست صورتحسابهایی است که به سامانه مودیان مالیاتی ارسال شدهاند.</span>
|
||||||
|
</v-alert>
|
||||||
|
|
||||||
|
<v-data-table
|
||||||
|
:headers="headers"
|
||||||
|
:items="invoices"
|
||||||
|
:loading="loading"
|
||||||
|
class="elevation-1"
|
||||||
|
:items-per-page="10"
|
||||||
|
:items-per-page-options="[10, 25, 50, 100]"
|
||||||
|
>
|
||||||
|
<template v-slot:item.status="{ item }">
|
||||||
|
<v-chip
|
||||||
|
:color="getStatusColor(item.status)"
|
||||||
|
:text="getStatusText(item.status)"
|
||||||
|
size="small"
|
||||||
|
></v-chip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:item.actions="{ item }">
|
||||||
|
<v-btn
|
||||||
|
icon="mdi-eye"
|
||||||
|
variant="text"
|
||||||
|
size="small"
|
||||||
|
@click="viewInvoice(item)"
|
||||||
|
color="primary"
|
||||||
|
></v-btn>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-container>
|
||||||
|
|
||||||
|
<v-snackbar
|
||||||
|
v-model="snackbar.show"
|
||||||
|
:color="snackbar.color"
|
||||||
|
:timeout="3000"
|
||||||
|
>
|
||||||
|
{{ snackbar.text }}
|
||||||
|
<template v-slot:actions>
|
||||||
|
<v-btn
|
||||||
|
color="white"
|
||||||
|
variant="text"
|
||||||
|
@click="snackbar.show = false"
|
||||||
|
>
|
||||||
|
بستن
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TaxInvoicesList',
|
||||||
|
data: () => ({
|
||||||
|
loading: false,
|
||||||
|
invoices: [],
|
||||||
|
headers: [
|
||||||
|
{ title: 'شماره فاکتور', key: 'invoiceNumber', sortable: true },
|
||||||
|
{ title: 'تاریخ', key: 'date', sortable: true },
|
||||||
|
{ title: 'مشتری', key: 'customerName', sortable: true },
|
||||||
|
{ title: 'مبلغ کل', key: 'totalAmount', sortable: true },
|
||||||
|
{ title: 'وضعیت ارسال', key: 'status', sortable: true },
|
||||||
|
{ title: 'تاریخ ارسال', key: 'sentDate', sortable: true },
|
||||||
|
{ title: 'عملیات', key: 'actions', sortable: false }
|
||||||
|
],
|
||||||
|
snackbar: {
|
||||||
|
show: false,
|
||||||
|
text: '',
|
||||||
|
color: 'success'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
async loadData() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
// اینجا باید API مربوط به دریافت لیست صورتحسابهای ارسالی را فراخوانی کنید
|
||||||
|
// const response = await axios.get('/api/plugins/tax-settings/invoices');
|
||||||
|
// this.invoices = response.data;
|
||||||
|
|
||||||
|
// فعلاً دادههای نمونه
|
||||||
|
this.invoices = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
invoiceNumber: 'INV-001',
|
||||||
|
date: '1402/12/15',
|
||||||
|
customerName: 'شرکت نمونه',
|
||||||
|
totalAmount: '1,500,000',
|
||||||
|
status: 'sent',
|
||||||
|
sentDate: '1402/12/16'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
invoiceNumber: 'INV-002',
|
||||||
|
date: '1402/12/14',
|
||||||
|
customerName: 'فروشگاه نمونه',
|
||||||
|
totalAmount: '2,300,000',
|
||||||
|
status: 'pending',
|
||||||
|
sentDate: '-'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} catch (error) {
|
||||||
|
this.showSnackbar('خطا در بارگذاری دادهها', 'error');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusColor(status) {
|
||||||
|
switch (status) {
|
||||||
|
case 'sent':
|
||||||
|
return 'success';
|
||||||
|
case 'pending':
|
||||||
|
return 'warning';
|
||||||
|
case 'failed':
|
||||||
|
return 'error';
|
||||||
|
default:
|
||||||
|
return 'grey';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
switch (status) {
|
||||||
|
case 'sent':
|
||||||
|
return 'ارسال شده';
|
||||||
|
case 'pending':
|
||||||
|
return 'در انتظار';
|
||||||
|
case 'failed':
|
||||||
|
return 'ناموفق';
|
||||||
|
default:
|
||||||
|
return 'نامشخص';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
viewInvoice(item) {
|
||||||
|
// اینجا میتوانید به صفحه جزئیات فاکتور بروید
|
||||||
|
this.showSnackbar('نمایش جزئیات فاکتور: ' + item.invoiceNumber);
|
||||||
|
},
|
||||||
|
showSnackbar(text, color = 'success') {
|
||||||
|
this.snackbar = {
|
||||||
|
show: true,
|
||||||
|
text,
|
||||||
|
color
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
321
webUI/src/views/acc/tax/tax-settings.vue
Normal file
321
webUI/src/views/acc/tax/tax-settings.vue
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-toolbar color="toolbar" title="تنظیمات مالیاتی">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-tooltip text="بازگشت" location="bottom">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
|
||||||
|
icon="mdi-arrow-right" />
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
|
</template>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn :loading="loading" @click="saveSettings()" icon="" color="green">
|
||||||
|
<v-tooltip activator="parent" text="ذخیره تنظیمات" location="bottom" />
|
||||||
|
<v-icon icon="mdi-content-save"></v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
|
<v-container>
|
||||||
|
<v-card :loading="loading" :disabled="loading">
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
@click="showCSRDialog = true"
|
||||||
|
prepend-icon="mdi-key-plus"
|
||||||
|
>
|
||||||
|
ساخت کلید و CSR
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="settings.taxMemoryId"
|
||||||
|
label="شناسه یکتای حافظه مالیاتی"
|
||||||
|
hide-details
|
||||||
|
density="compact"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="settings.economicCode"
|
||||||
|
label="کد اقتصادی"
|
||||||
|
hide-details
|
||||||
|
density="compact"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-row class="mt-4">
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-textarea
|
||||||
|
v-model="settings.privateKey"
|
||||||
|
label="Private Key"
|
||||||
|
rows="15"
|
||||||
|
variant="outlined"
|
||||||
|
hide-details
|
||||||
|
placeholder="کلید خصوصی اینجا قرار میگیرد..."
|
||||||
|
></v-textarea>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-container>
|
||||||
|
|
||||||
|
<!-- Dialog برای ساخت کلید و CSR -->
|
||||||
|
<v-dialog v-model="showCSRDialog" max-width="600px">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title class="text-h6">
|
||||||
|
ساخت کلید و CSR
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="csrForm">
|
||||||
|
<div class="mb-4">
|
||||||
|
<div class="text-subtitle-2 mb-2">شخص</div>
|
||||||
|
<v-radio-group
|
||||||
|
v-model="csrData.personType"
|
||||||
|
inline
|
||||||
|
hide-details
|
||||||
|
>
|
||||||
|
<v-radio
|
||||||
|
v-for="type in personTypes"
|
||||||
|
:key="type.value"
|
||||||
|
:label="type.title"
|
||||||
|
:value="type.value"
|
||||||
|
:disabled="type.value === 'natural'"
|
||||||
|
></v-radio>
|
||||||
|
</v-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="csrData.nationalId"
|
||||||
|
label="شناسه ملی"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="csrData.nameFa"
|
||||||
|
label="نام (فارسی)"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="csrData.nameEn"
|
||||||
|
label="نام (انگلیسی)"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="csrData.email"
|
||||||
|
label="ایمیل"
|
||||||
|
type="email"
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn @click="showCSRDialog = false" variant="text">
|
||||||
|
انصراف
|
||||||
|
</v-btn>
|
||||||
|
<v-btn @click="generateCSR()" color="primary" :loading="csrLoading">
|
||||||
|
تایید
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
<v-dialog v-model="showResultDialog" max-width="900px">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title class="text-h6 pb-0">ساخت کلید و CSR</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-alert type="info" color="blue" class="mb-4" icon="mdi-alert">
|
||||||
|
<span class="font-weight-bold">توجه: لطفا این اطلاعات را دانلود کنید و در یک جای امن نگهداری کنید. به دلایل امنیتی اطلاعات شما را نگهداری نمیکنیم، در صورتی که این اطلاعات را گم کنید، امکان بازیابی آن وجود ندارد.</span>
|
||||||
|
</v-alert>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<div class="mb-2 font-weight-bold">CSR</div>
|
||||||
|
<v-textarea readonly rows="10" :value="resultData.csr" variant="outlined"></v-textarea>
|
||||||
|
<v-row class="mt-2">
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="copyToClipboard(resultData.csr)"><v-icon start>mdi-content-copy</v-icon>کپی</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="downloadFile(resultData.csr, 'csr.txt')"><v-icon start>mdi-download</v-icon>دانلود</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<div class="mb-2 font-weight-bold">Public Key</div>
|
||||||
|
<v-textarea readonly rows="10" :value="resultData.publicKey" variant="outlined"></v-textarea>
|
||||||
|
<v-row class="mt-2">
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="copyToClipboard(resultData.publicKey)"><v-icon start>mdi-content-copy</v-icon>کپی</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="downloadFile(resultData.publicKey, 'public_key.txt')"><v-icon start>mdi-download</v-icon>دانلود</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<div class="mb-2 font-weight-bold">Private Key</div>
|
||||||
|
<v-textarea readonly rows="10" :value="resultData.privateKey" variant="outlined"></v-textarea>
|
||||||
|
<v-row class="mt-2">
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="copyToClipboard(resultData.privateKey)"><v-icon start>mdi-content-copy</v-icon>کپی</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-btn color="success" block @click="downloadFile(resultData.privateKey, 'private_key.txt')"><v-icon start>mdi-download</v-icon>دانلود</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn @click="showResultDialog = false" color="primary">بستن</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
<v-snackbar
|
||||||
|
v-model="snackbar.show"
|
||||||
|
:color="snackbar.color"
|
||||||
|
:timeout="3000"
|
||||||
|
>
|
||||||
|
{{ snackbar.text }}
|
||||||
|
<template v-slot:actions>
|
||||||
|
<v-btn
|
||||||
|
color="white"
|
||||||
|
variant="text"
|
||||||
|
@click="snackbar.show = false"
|
||||||
|
>
|
||||||
|
بستن
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TaxSettings',
|
||||||
|
data: () => ({
|
||||||
|
loading: false,
|
||||||
|
csrLoading: false,
|
||||||
|
showCSRDialog: false,
|
||||||
|
showResultDialog: false,
|
||||||
|
settings: {
|
||||||
|
taxMemoryId: '',
|
||||||
|
economicCode: '',
|
||||||
|
privateKey: '',
|
||||||
|
},
|
||||||
|
csrData: {
|
||||||
|
personType: 'legal',
|
||||||
|
nationalId: '',
|
||||||
|
nameFa: '',
|
||||||
|
nameEn: '',
|
||||||
|
email: '',
|
||||||
|
},
|
||||||
|
resultData: {
|
||||||
|
csr: '',
|
||||||
|
publicKey: '',
|
||||||
|
privateKey: ''
|
||||||
|
},
|
||||||
|
personTypes: [
|
||||||
|
{ title: 'حقیقی', value: 'natural' },
|
||||||
|
{ title: 'حقوقی', value: 'legal' }
|
||||||
|
],
|
||||||
|
snackbar: {
|
||||||
|
show: false,
|
||||||
|
text: '',
|
||||||
|
color: 'success'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
async loadSettings() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/api/plugins/tax-settings/get');
|
||||||
|
this.settings = {
|
||||||
|
...this.settings,
|
||||||
|
...response.data
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this.showSnackbar('خطا در بارگذاری تنظیمات', 'error');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async saveSettings() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const dataToSave = { ...this.settings };
|
||||||
|
await axios.post('/api/plugins/tax-settings/save', dataToSave);
|
||||||
|
this.showSnackbar('تنظیمات با موفقیت ذخیره شد', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
this.showSnackbar('خطا در ذخیره تنظیمات', 'error');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async generateCSR() {
|
||||||
|
this.csrLoading = true;
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/api/plugins/tax-settings/generate-csr', this.csrData);
|
||||||
|
|
||||||
|
if (response.data.success) {
|
||||||
|
// this.settings.privateKey = response.data.privateKey;
|
||||||
|
// نمایش دیالوگ نتیجه
|
||||||
|
this.resultData.csr = response.data.csr;
|
||||||
|
this.resultData.privateKey = response.data.privateKey;
|
||||||
|
this.resultData.publicKey = response.data.publicKey || '';
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.showCSRDialog = false;
|
||||||
|
this.showSnackbar('کلید و CSR با موفقیت تولید شد', 'success');
|
||||||
|
} else {
|
||||||
|
this.showSnackbar(response.data.message || 'خطا در تولید کلید و CSR', 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showSnackbar('خطا در تولید کلید و CSR', 'error');
|
||||||
|
} finally {
|
||||||
|
this.csrLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copyToClipboard(text) {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
this.showSnackbar('کپی شد');
|
||||||
|
},
|
||||||
|
downloadFile(content, filename) {
|
||||||
|
const blob = new Blob([content], { type: 'text/plain' });
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = URL.createObjectURL(blob);
|
||||||
|
link.download = filename;
|
||||||
|
link.click();
|
||||||
|
URL.revokeObjectURL(link.href);
|
||||||
|
},
|
||||||
|
showSnackbar(text, color = 'success') {
|
||||||
|
this.snackbar = {
|
||||||
|
show: true,
|
||||||
|
text,
|
||||||
|
color
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadSettings();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -7,6 +7,12 @@ export default defineComponent({
|
||||||
name: "system",
|
name: "system",
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
|
activeTab: 0,
|
||||||
|
tabs: [
|
||||||
|
{ title: 'تنظیمات پایه', icon: 'mdi-cog' },
|
||||||
|
{ title: 'درگاههای پرداخت', icon: 'mdi-credit-card' },
|
||||||
|
{ title: 'پنل استعلامات', icon: 'mdi-magnify' }
|
||||||
|
],
|
||||||
gatepays: [
|
gatepays: [
|
||||||
{
|
{
|
||||||
title: 'زرینپال',
|
title: 'زرینپال',
|
||||||
|
@ -29,6 +35,13 @@ export default defineComponent({
|
||||||
props: { subtitle: 'bitpay.ir' },
|
props: { subtitle: 'bitpay.ir' },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
inquiryPanel: [
|
||||||
|
{
|
||||||
|
title: 'پنل زحل',
|
||||||
|
value: 'zohal',
|
||||||
|
props: { subtitle: 'zohal.ir' },
|
||||||
|
},
|
||||||
|
],
|
||||||
systemInfo: {
|
systemInfo: {
|
||||||
keywords: '',
|
keywords: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
@ -38,6 +51,15 @@ export default defineComponent({
|
||||||
parsianGatewayAPI: '',
|
parsianGatewayAPI: '',
|
||||||
paypingKey: '',
|
paypingKey: '',
|
||||||
bitpayKey: '',
|
bitpayKey: '',
|
||||||
|
inquiryPanel: '',
|
||||||
|
inquiryZohalAPIKey: '',
|
||||||
|
enablePostalCodeToAddress: false,
|
||||||
|
inquiryPanelEnable: false,
|
||||||
|
postalCodeToAddressFee: 0,
|
||||||
|
enableCardToSheba: false,
|
||||||
|
cardToShebaFee: 0,
|
||||||
|
enableAccountToSheba: false,
|
||||||
|
accountToShebaFee: 0,
|
||||||
},
|
},
|
||||||
loading: true,
|
loading: true,
|
||||||
}
|
}
|
||||||
|
@ -47,12 +69,67 @@ export default defineComponent({
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
axios.post('/api/admin/settings/system/info')
|
axios.post('/api/admin/settings/system/info')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.systemInfo = response.data;
|
// Convert string values to proper types for switches and selects
|
||||||
|
const data = response.data;
|
||||||
|
this.systemInfo = {
|
||||||
|
...data,
|
||||||
|
enablePostalCodeToAddress: data.enablePostalCodeToAddress === '1' || data.enablePostalCodeToAddress === true,
|
||||||
|
inquiryPanelEnable: data.inquiryPanelEnable === '1' || data.inquiryPanelEnable === true,
|
||||||
|
enableCardToSheba: data.enableCardToSheba === '1' || data.enableCardToSheba === true,
|
||||||
|
enableAccountToSheba: data.enableAccountToSheba === '1' || data.enableAccountToSheba === true,
|
||||||
|
activeGateway: data.activeGateway || 'zarinpal',
|
||||||
|
inquiryPanel: data.inquiryPanel || 'zohal'
|
||||||
|
};
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
submit() {
|
submit() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
|
// Validation: if inquiry panel is enabled, a panel must be selected
|
||||||
|
if (this.systemInfo.inquiryPanelEnable && !this.systemInfo.inquiryPanel) {
|
||||||
|
Swal.fire({
|
||||||
|
text: 'در صورت فعال بودن پنل سامانه استعلامات، حتماً باید یک پنل انتخاب شود.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'قبول',
|
||||||
|
});
|
||||||
|
this.loading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation: if postal code to address is enabled, fee must be set
|
||||||
|
if (this.systemInfo.enablePostalCodeToAddress && this.systemInfo.postalCodeToAddressFee < 0) {
|
||||||
|
Swal.fire({
|
||||||
|
text: 'کارمزد تبدیل کد پستی به آدرس نمیتواند منفی باشد.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'قبول',
|
||||||
|
});
|
||||||
|
this.loading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation: if card to sheba is enabled, fee must be set
|
||||||
|
if (this.systemInfo.enableCardToSheba && this.systemInfo.cardToShebaFee < 0) {
|
||||||
|
Swal.fire({
|
||||||
|
text: 'کارمزد تبدیل شماره کارت به شبا نمیتواند منفی باشد.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'قبول',
|
||||||
|
});
|
||||||
|
this.loading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation: if account to sheba is enabled, fee must be set
|
||||||
|
if (this.systemInfo.enableAccountToSheba && this.systemInfo.accountToShebaFee < 0) {
|
||||||
|
Swal.fire({
|
||||||
|
text: 'کارمزد تبدیل حساب به شبا نمیتواند منفی باشد.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'قبول',
|
||||||
|
});
|
||||||
|
this.loading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
axios.post('/api/admin/settings/system/info/save', this.systemInfo).then((resp) => {
|
axios.post('/api/admin/settings/system/info/save', this.systemInfo).then((resp) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (resp.data.result == 1) {
|
if (resp.data.result == 1) {
|
||||||
|
@ -62,6 +139,13 @@ export default defineComponent({
|
||||||
confirmButtonText: 'قبول',
|
confirmButtonText: 'قبول',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
this.loading = false;
|
||||||
|
Swal.fire({
|
||||||
|
text: 'خطا در ذخیره تنظیمات. لطفاً دوباره تلاش کنید.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'قبول',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,46 +159,491 @@ export default defineComponent({
|
||||||
<template>
|
<template>
|
||||||
<v-toolbar color="toolbar" :title="$t('pages.manager.system_settings_basic')">
|
<v-toolbar color="toolbar" :title="$t('pages.manager.system_settings_basic')">
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
<v-tooltip text="ذخیره تنظیمات" location="bottom">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn
|
||||||
|
v-bind="props"
|
||||||
|
icon
|
||||||
|
@click="submit()"
|
||||||
|
:loading="loading"
|
||||||
|
color="primary"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-content-save</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<v-container class="pa-0">
|
<v-container class="pa-0">
|
||||||
<v-card :loading="loading ? 'red' : null" :disabled="loading">
|
<v-card :loading="loading" :disabled="loading">
|
||||||
<v-card-text class="">
|
<v-tabs v-model="activeTab" color="primary" grow>
|
||||||
<v-row class="mb-2">
|
<v-tab
|
||||||
<v-col cols="12" sm="12" md="12">
|
v-for="(tab, index) in tabs"
|
||||||
<v-text-field class="" hide-details="auto" :label="$t('pages.manager.app_site')"
|
:key="index"
|
||||||
v-model="systemInfo.appSite" type="text" prepend-inner-icon="mdi-card-text"
|
:value="index"
|
||||||
:rules="[() => systemInfo.appSite.length > 0 || $t('validator.required')]"></v-text-field>
|
class="text-none"
|
||||||
</v-col>
|
>
|
||||||
<v-col cols="12" sm="12" md="4">
|
<v-icon start>{{ tab.icon }}</v-icon>
|
||||||
<v-select v-model="systemInfo.activeGateway" hide-details="auto" prepend-inner-icon="mdi-signal" :items="gatepays" item-title="title"
|
{{ tab.title }}
|
||||||
item-value="value" label="Select" single-line>
|
</v-tab>
|
||||||
</v-select>
|
</v-tabs>
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="4">
|
<v-window v-model="activeTab">
|
||||||
<v-text-field class="" hide-details="auto" :label="$t('pages.manager.zarinpal_api')"
|
<!-- تب اول: تنظیمات پایه -->
|
||||||
v-model="systemInfo.zarinpal" type="text" prepend-inner-icon="mdi-text"></v-text-field>
|
<v-window-item :value="0">
|
||||||
</v-col>
|
<v-card-text class="pa-8">
|
||||||
<v-col cols="12" sm="12" md="4">
|
<v-row class="mb-6">
|
||||||
<v-text-field class="" hide-details="auto" :label="$t('pages.manager.parsian_api')"
|
<v-col cols="12">
|
||||||
v-model="systemInfo.parsianGatewayAPI" type="text" prepend-inner-icon="mdi-text"></v-text-field>
|
<v-card variant="outlined" class="pa-6" elevation="0">
|
||||||
</v-col>
|
<v-card-text class="pa-0">
|
||||||
<v-col cols="12" sm="12" md="4">
|
<v-row>
|
||||||
<v-text-field class="" hide-details="auto" :label="$t('pages.manager.payping_api')"
|
<v-col cols="12" sm="12" md="8" lg="6">
|
||||||
v-model="systemInfo.paypingKey" type="text" prepend-inner-icon="mdi-text"></v-text-field>
|
<v-text-field
|
||||||
</v-col>
|
class=""
|
||||||
<v-col cols="12" sm="12" md="4">
|
hide-details="auto"
|
||||||
<v-text-field class="" hide-details="auto" :label="$t('pages.manager.bitpay_api')"
|
:label="$t('pages.manager.app_site')"
|
||||||
v-model="systemInfo.bitpayKey" type="text" prepend-inner-icon="mdi-text"></v-text-field>
|
v-model="systemInfo.appSite"
|
||||||
</v-col>
|
type="text"
|
||||||
<v-col cols="12" sm="12" md="12">
|
prepend-inner-icon="mdi-link"
|
||||||
<v-btn type="submit" @click="submit()" color="primary" prepend-icon="mdi-content-save" :loading="loading">
|
:rules="[() => systemInfo.appSite.length > 0 || $t('validator.required')]"
|
||||||
{{ $t('dialog.save') }}
|
variant="outlined"
|
||||||
</v-btn>
|
density="comfortable"
|
||||||
</v-col>
|
placeholder="https://example.com"
|
||||||
</v-row>
|
></v-text-field>
|
||||||
</v-card-text>
|
<div class="text-caption text-medium-emphasis mt-2 d-flex align-center">
|
||||||
|
<v-icon size="16" class="mr-1">mdi-information</v-icon>
|
||||||
|
آدرس اصلی سایت که در سیستم استفاده میشود
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-window-item>
|
||||||
|
|
||||||
|
<!-- تب دوم: درگاههای پرداخت -->
|
||||||
|
<v-window-item :value="1">
|
||||||
|
<v-card-text class="pa-8">
|
||||||
|
<v-row class="mb-6">
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-card variant="outlined" class="pa-6" elevation="0">
|
||||||
|
<v-card-text class="pa-0">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" sm="12" md="6" lg="4">
|
||||||
|
<v-select
|
||||||
|
v-model="systemInfo.activeGateway"
|
||||||
|
hide-details="auto"
|
||||||
|
prepend-inner-icon="mdi-check-circle"
|
||||||
|
:items="gatepays"
|
||||||
|
item-title="title"
|
||||||
|
item-value="value"
|
||||||
|
label="درگاه فعال"
|
||||||
|
single-line
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
></v-select>
|
||||||
|
<div class="text-caption text-medium-emphasis mt-2 d-flex align-center">
|
||||||
|
<v-icon size="16" class="mr-1">mdi-information</v-icon>
|
||||||
|
درگاه پرداخت پیشفرض سیستم
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-divider class="my-6"></v-divider>
|
||||||
|
|
||||||
|
<div class="d-flex align-center mb-4">
|
||||||
|
<v-icon size="20" color="secondary" class="mr-2">mdi-key</v-icon>
|
||||||
|
<h4 class="text-subtitle-1 font-weight-medium">کلیدهای API</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" sm="12" md="6" lg="4">
|
||||||
|
<v-text-field
|
||||||
|
class=""
|
||||||
|
hide-details="auto"
|
||||||
|
:label="$t('pages.manager.zarinpal_api')"
|
||||||
|
v-model="systemInfo.zarinpal"
|
||||||
|
type="text"
|
||||||
|
prepend-inner-icon="mdi-shield-key"
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
placeholder="کلید API زرینپال"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6" lg="4">
|
||||||
|
<v-text-field
|
||||||
|
class=""
|
||||||
|
hide-details="auto"
|
||||||
|
:label="$t('pages.manager.parsian_api')"
|
||||||
|
v-model="systemInfo.parsianGatewayAPI"
|
||||||
|
type="text"
|
||||||
|
prepend-inner-icon="mdi-shield-key"
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
placeholder="کلید API پارسیان"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6" lg="4">
|
||||||
|
<v-text-field
|
||||||
|
class=""
|
||||||
|
hide-details="auto"
|
||||||
|
:label="$t('pages.manager.payping_api')"
|
||||||
|
v-model="systemInfo.paypingKey"
|
||||||
|
type="text"
|
||||||
|
prepend-inner-icon="mdi-shield-key"
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
placeholder="کلید API پیپینگ"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6" lg="4">
|
||||||
|
<v-text-field
|
||||||
|
class=""
|
||||||
|
hide-details="auto"
|
||||||
|
:label="$t('pages.manager.bitpay_api')"
|
||||||
|
v-model="systemInfo.bitpayKey"
|
||||||
|
type="text"
|
||||||
|
prepend-inner-icon="mdi-shield-key"
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
placeholder="کلید API بیتپی"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-window-item>
|
||||||
|
|
||||||
|
<!-- تب سوم: پنل استعلامات -->
|
||||||
|
<v-window-item :value="2">
|
||||||
|
<v-card-text class="pa-8">
|
||||||
|
<v-row class="mb-6">
|
||||||
|
<v-col cols="12">
|
||||||
|
|
||||||
|
<!-- تنظیمات اصلی پنل -->
|
||||||
|
<v-card variant="outlined" class="pa-6 mb-6" elevation="0">
|
||||||
|
<v-card-title class="text-subtitle-1 font-weight-medium pb-3 d-flex align-center">
|
||||||
|
<v-icon start class="mr-2" color="secondary">mdi-cog</v-icon>
|
||||||
|
تنظیمات اصلی
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text class="pa-0">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" sm="12" md="6">
|
||||||
|
<v-switch
|
||||||
|
v-model="systemInfo.inquiryPanelEnable"
|
||||||
|
:label="$t('pages.manager.inquiry_panel_enable')"
|
||||||
|
color="primary"
|
||||||
|
hide-details="auto"
|
||||||
|
inset
|
||||||
|
density="compact"
|
||||||
|
></v-switch>
|
||||||
|
<div class="text-caption text-medium-emphasis mt-1 d-flex align-center">
|
||||||
|
<v-icon size="16" class="mr-1">mdi-information</v-icon>
|
||||||
|
فعال/غیرفعال کردن پنل استعلامات
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6">
|
||||||
|
<v-select
|
||||||
|
v-model="systemInfo.inquiryPanel"
|
||||||
|
hide-details="auto"
|
||||||
|
prepend-inner-icon="mdi-view-dashboard"
|
||||||
|
:items="inquiryPanel"
|
||||||
|
item-title="title"
|
||||||
|
item-value="value"
|
||||||
|
label="انتخاب پنل"
|
||||||
|
single-line
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
></v-select>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="12">
|
||||||
|
<v-text-field
|
||||||
|
class=""
|
||||||
|
hide-details="auto"
|
||||||
|
:label="$t('pages.manager.inquiry_zohal_api_key')"
|
||||||
|
v-model="systemInfo.inquiryZohalAPIKey"
|
||||||
|
type="text"
|
||||||
|
prepend-inner-icon="mdi-key"
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
placeholder="کلید API پنل زحل"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
<!-- سرویسهای تبدیل -->
|
||||||
|
<div class="d-flex align-center mb-6">
|
||||||
|
<div class="d-flex align-center bg-primary-lighten-5 pa-3 rounded-lg">
|
||||||
|
<v-icon size="28" color="primary" class="mr-3">mdi-sync</v-icon>
|
||||||
|
<div>
|
||||||
|
<h4 class="text-h6 font-weight-medium text-primary mb-1">سرویسهای تبدیل</h4>
|
||||||
|
<p class="text-caption text-medium-emphasis mb-0">مدیریت قابلیتهای تبدیل اطلاعات</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-row class="mb-4">
|
||||||
|
<!-- تبدیل کد پستی به آدرس -->
|
||||||
|
<v-col cols="12" sm="12" md="4">
|
||||||
|
<v-card
|
||||||
|
variant="outlined"
|
||||||
|
class="service-card h-100"
|
||||||
|
elevation="0"
|
||||||
|
:class="{ 'service-card-active': systemInfo.enablePostalCodeToAddress }"
|
||||||
|
>
|
||||||
|
<div class="service-card-header bg-success-lighten-5 pa-4">
|
||||||
|
<div class="d-flex align-center justify-space-between mb-3">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon size="32" color="success" class="mr-3">mdi-map-marker</v-icon>
|
||||||
|
<div>
|
||||||
|
<h5 class="text-subtitle-1 font-weight-medium text-success mb-1">تبدیل کد پستی به آدرس</h5>
|
||||||
|
<p class="text-caption text-medium-emphasis mb-0">تبدیل خودکار کد پستی به آدرس کامل</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<v-chip
|
||||||
|
:color="systemInfo.enablePostalCodeToAddress ? 'success' : 'grey'"
|
||||||
|
size="small"
|
||||||
|
variant="flat"
|
||||||
|
>
|
||||||
|
{{ systemInfo.enablePostalCodeToAddress ? 'فعال' : 'غیرفعال' }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-card-text class="pa-4">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" class="mb-3">
|
||||||
|
<v-switch
|
||||||
|
v-model="systemInfo.enablePostalCodeToAddress"
|
||||||
|
:label="$t('pages.manager.enable_postalcode_to_address')"
|
||||||
|
color="success"
|
||||||
|
hide-details="auto"
|
||||||
|
inset
|
||||||
|
density="comfortable"
|
||||||
|
></v-switch>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="systemInfo.postalCodeToAddressFee"
|
||||||
|
:label="$t('pages.manager.postalcode_to_address_fee')"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
prepend-inner-icon="mdi-currency-usd"
|
||||||
|
hide-details="auto"
|
||||||
|
suffix="ریال"
|
||||||
|
density="comfortable"
|
||||||
|
variant="outlined"
|
||||||
|
:disabled="!systemInfo.enablePostalCodeToAddress"
|
||||||
|
:rules="[v => v >= 0 || 'کارمزد نمیتواند منفی باشد']"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<!-- تبدیل شماره کارت به شبا -->
|
||||||
|
<v-col cols="12" sm="12" md="4">
|
||||||
|
<v-card
|
||||||
|
variant="outlined"
|
||||||
|
class="service-card h-100"
|
||||||
|
elevation="0"
|
||||||
|
:class="{ 'service-card-active': systemInfo.enableCardToSheba }"
|
||||||
|
>
|
||||||
|
<div class="service-card-header bg-info-lighten-5 pa-4">
|
||||||
|
<div class="d-flex align-center justify-space-between mb-3">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon size="32" color="info" class="mr-3">mdi-credit-card</v-icon>
|
||||||
|
<div>
|
||||||
|
<h5 class="text-subtitle-1 font-weight-medium text-info mb-1">تبدیل شماره کارت به شبا</h5>
|
||||||
|
<p class="text-caption text-medium-emphasis mb-0">تبدیل شماره کارت بانکی به شماره شبا</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<v-chip
|
||||||
|
:color="systemInfo.enableCardToSheba ? 'info' : 'grey'"
|
||||||
|
size="small"
|
||||||
|
variant="flat"
|
||||||
|
>
|
||||||
|
{{ systemInfo.enableCardToSheba ? 'فعال' : 'غیرفعال' }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-card-text class="pa-4">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" class="mb-3">
|
||||||
|
<v-switch
|
||||||
|
v-model="systemInfo.enableCardToSheba"
|
||||||
|
:label="$t('pages.manager.enable_card_to_sheba')"
|
||||||
|
color="info"
|
||||||
|
hide-details="auto"
|
||||||
|
inset
|
||||||
|
density="comfortable"
|
||||||
|
></v-switch>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="systemInfo.cardToShebaFee"
|
||||||
|
:label="$t('pages.manager.card_to_sheba_fee')"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
prepend-inner-icon="mdi-currency-usd"
|
||||||
|
hide-details="auto"
|
||||||
|
suffix="ریال"
|
||||||
|
density="comfortable"
|
||||||
|
variant="outlined"
|
||||||
|
:disabled="!systemInfo.enableCardToSheba"
|
||||||
|
:rules="[v => v >= 0 || 'کارمزد نمیتواند منفی باشد']"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<!-- تبدیل حساب به شبا -->
|
||||||
|
<v-col cols="12" sm="12" md="4">
|
||||||
|
<v-card
|
||||||
|
variant="outlined"
|
||||||
|
class="service-card h-100"
|
||||||
|
elevation="0"
|
||||||
|
:class="{ 'service-card-active': systemInfo.enableAccountToSheba }"
|
||||||
|
>
|
||||||
|
<div class="service-card-header bg-warning-lighten-5 pa-4">
|
||||||
|
<div class="d-flex align-center justify-space-between mb-3">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon size="32" color="warning" class="mr-3">mdi-bank</v-icon>
|
||||||
|
<div>
|
||||||
|
<h5 class="text-subtitle-1 font-weight-medium text-warning mb-1">تبدیل حساب به شبا</h5>
|
||||||
|
<p class="text-caption text-medium-emphasis mb-0">تبدیل شماره حساب بانکی به شماره شبا</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<v-chip
|
||||||
|
:color="systemInfo.enableAccountToSheba ? 'warning' : 'grey'"
|
||||||
|
size="small"
|
||||||
|
variant="flat"
|
||||||
|
>
|
||||||
|
{{ systemInfo.enableAccountToSheba ? 'فعال' : 'غیرفعال' }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-card-text class="pa-4">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" class="mb-3">
|
||||||
|
<v-switch
|
||||||
|
v-model="systemInfo.enableAccountToSheba"
|
||||||
|
:label="$t('pages.manager.enable_account_to_sheba')"
|
||||||
|
color="warning"
|
||||||
|
hide-details="auto"
|
||||||
|
inset
|
||||||
|
density="comfortable"
|
||||||
|
></v-switch>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="systemInfo.accountToShebaFee"
|
||||||
|
:label="$t('pages.manager.account_to_sheba_fee')"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
prepend-inner-icon="mdi-currency-usd"
|
||||||
|
hide-details="auto"
|
||||||
|
suffix="ریال"
|
||||||
|
density="comfortable"
|
||||||
|
variant="outlined"
|
||||||
|
:disabled="!systemInfo.enableAccountToSheba"
|
||||||
|
:rules="[v => v >= 0 || 'کارمزد نمیتواند منفی باشد']"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-window-item>
|
||||||
|
</v-window>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.service-card {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card-active {
|
||||||
|
border-color: var(--v-primary-base);
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card-active::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 3px;
|
||||||
|
background: linear-gradient(90deg, var(--v-primary-base), var(--v-secondary-base));
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card-header {
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card-header::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* انیمیشن برای chip ها */
|
||||||
|
.v-chip {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-chip:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* استایل برای فیلدهای غیرفعال */
|
||||||
|
.v-text-field--disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* بهبود ظاهر switch */
|
||||||
|
.v-switch {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* استایل برای آیکونها */
|
||||||
|
.service-card .v-icon {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card:hover .v-icon {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -31,7 +31,7 @@
|
||||||
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.distroVersion') }}:</span> {{ systemInfo.distroVersion }}</p>
|
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.distroVersion') }}:</span> {{ systemInfo.distroVersion }}</p>
|
||||||
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.webServer') }}:</span> {{ systemInfo.webServer }}</p>
|
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.webServer') }}:</span> {{ systemInfo.webServer }}</p>
|
||||||
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.dbName') }}:</span> {{ systemInfo.dbName }}</p>
|
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.dbName') }}:</span> {{ systemInfo.dbName }}</p>
|
||||||
<p><span class="font-weight-bold primary--useStateFiletext">{{ $t('updateSoftware.dbVersion') }}:</span> {{ systemInfo.dbVersion }}</p>
|
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.dbVersion') }}:</span> {{ systemInfo.dbVersion }}</p>
|
||||||
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.currentEnv') }}:</span> {{ selectedEnv }}</p>
|
<p><span class="font-weight-bold primary--text">{{ $t('updateSoftware.currentEnv') }}:</span> {{ selectedEnv }}</p>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
@ -76,6 +76,39 @@
|
||||||
<v-window-item>
|
<v-window-item>
|
||||||
<v-card flat>
|
<v-card flat>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
|
<!-- بخش تنظیمات منبع بهروزرسانی -->
|
||||||
|
<v-card class="mb-4" variant="outlined">
|
||||||
|
<v-card-title class="text-subtitle-1">
|
||||||
|
{{ $t('updateSoftware.updateSourceTitle') }}
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="8">
|
||||||
|
<v-text-field
|
||||||
|
v-model="updateSourceUrl"
|
||||||
|
:label="$t('updateSoftware.updateSourceLabel')"
|
||||||
|
placeholder="https://github.com/username/repository.git"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
:disabled="isUpdating || isChangingSource"
|
||||||
|
:loading="isChangingSource"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<v-btn
|
||||||
|
color="secondary"
|
||||||
|
:loading="isChangingSource"
|
||||||
|
:disabled="isUpdating || isChangingSource || !updateSourceUrl.trim()"
|
||||||
|
@click="changeUpdateSource"
|
||||||
|
block
|
||||||
|
>
|
||||||
|
{{ $t('updateSoftware.changeSourceButton') }}
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
<v-row justify="end" class="mb-4">
|
<v-row justify="end" class="mb-4">
|
||||||
<v-col cols="auto">
|
<v-col cols="auto">
|
||||||
<v-btn-group size="small">
|
<v-btn-group size="small">
|
||||||
|
@ -201,6 +234,8 @@ export default {
|
||||||
const isLoadingLogs = ref(false);
|
const isLoadingLogs = ref(false);
|
||||||
const isClearingLogs = ref(false);
|
const isClearingLogs = ref(false);
|
||||||
const isPolling = ref(false);
|
const isPolling = ref(false);
|
||||||
|
const updateSourceUrl = ref('');
|
||||||
|
const isChangingSource = ref(false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isUpdating,
|
isUpdating,
|
||||||
|
@ -229,6 +264,8 @@ export default {
|
||||||
isLoadingLogs,
|
isLoadingLogs,
|
||||||
isClearingLogs,
|
isClearingLogs,
|
||||||
isPolling,
|
isPolling,
|
||||||
|
updateSourceUrl,
|
||||||
|
isChangingSource,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -508,6 +545,32 @@ export default {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async fetchCurrentSource() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/api/admin/updatecore/current-source', {
|
||||||
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
|
timeout: 7200000 // تایماوت 2 ساعته
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.status === 'success') {
|
||||||
|
this.updateSourceUrl = response.data.sourceUrl || '';
|
||||||
|
} else {
|
||||||
|
console.error('Failed to fetch current source:', response.data.message);
|
||||||
|
this.updateSourceUrl = '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch current source:', error);
|
||||||
|
this.updateSourceUrl = '';
|
||||||
|
|
||||||
|
// نمایش پیام خطا به کاربر
|
||||||
|
if (error.response?.data?.message) {
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = error.response.data.message;
|
||||||
|
this.dialogColor = 'error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
async fetchCurrentEnv() {
|
async fetchCurrentEnv() {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('/api/admin/updatecore/current-env', {
|
const response = await axios.get('/api/admin/updatecore/current-env', {
|
||||||
|
@ -570,6 +633,42 @@ export default {
|
||||||
this.isClearingLogs = false;
|
this.isClearingLogs = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async changeUpdateSource() {
|
||||||
|
if (!this.updateSourceUrl.trim()) {
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = this.$t('updateSoftware.sourceUrlRequired');
|
||||||
|
this.dialogColor = 'error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isChangingSource = true;
|
||||||
|
this.showOutput = true;
|
||||||
|
this.output = this.$t('updateSoftware.changingSourceMessage') + '\n';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/api/admin/updatecore/change-source', {
|
||||||
|
sourceUrl: this.updateSourceUrl.trim()
|
||||||
|
}, {
|
||||||
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
|
timeout: 7200000 // تایماوت 2 ساعته
|
||||||
|
});
|
||||||
|
|
||||||
|
this.output += response.data.output || response.data.message + '\n';
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogSuccessTitle');
|
||||||
|
this.dialogMessage = response.data.message || this.$t('updateSoftware.repositoryChangeSuccess');
|
||||||
|
this.dialogColor = 'success';
|
||||||
|
} catch (error) {
|
||||||
|
this.output += 'خطا: ' + (error.response?.data?.message || error.message) + '\n';
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = error.response?.data?.message || this.$t('updateSoftware.sourceChangeError');
|
||||||
|
this.dialogColor = 'error';
|
||||||
|
} finally {
|
||||||
|
this.isChangingSource = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
copyLogsToClipboard() {
|
copyLogsToClipboard() {
|
||||||
const plainLogs = this.systemLogs.replace(/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+\+\d{2}:\d{2}\]/g, '\n[$&]')
|
const plainLogs = this.systemLogs.replace(/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+\+\d{2}:\d{2}\]/g, '\n[$&]')
|
||||||
.replace(/\s+\[\]/g, ' []')
|
.replace(/\s+\[\]/g, ' []')
|
||||||
|
@ -593,6 +692,7 @@ export default {
|
||||||
this.fetchCommits();
|
this.fetchCommits();
|
||||||
this.fetchSystemInfo();
|
this.fetchSystemInfo();
|
||||||
this.fetchCurrentEnv();
|
this.fetchCurrentEnv();
|
||||||
|
this.fetchCurrentSource();
|
||||||
this.buttonText = this.$t('updateSoftware.startButton');
|
this.buttonText = this.$t('updateSoftware.startButton');
|
||||||
this.refreshLogs();
|
this.refreshLogs();
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<td>{{ item.totalIncome }}</td>
|
<td>{{ item.totalIncome }}</td>
|
||||||
<td>{{ calculateStatus(item) }}</td>
|
<td>{{ calculateStatus(item) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<v-tooltip v-if="calculateStatus(item) === 'در صف تسویه'" location="top">
|
<v-tooltip v-if="calculateStatus(item) === 'در انتظار پرداخت'" location="top">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn variant="text" icon v-bind="props" @click="openTransactionDialog(item)">
|
<v-btn variant="text" icon v-bind="props" @click="openTransactionDialog(item)">
|
||||||
<v-icon>mdi-cash-register</v-icon>
|
<v-icon>mdi-cash-register</v-icon>
|
||||||
|
@ -80,6 +80,7 @@
|
||||||
<td>{{ item.bidName }}</td>
|
<td>{{ item.bidName }}</td>
|
||||||
<td>{{ item.bankAcName }}</td>
|
<td>{{ item.bankAcName }}</td>
|
||||||
<td>{{ item.type === 'pay' ? 'پرداخت' : 'دریافت' }}</td>
|
<td>{{ item.type === 'pay' ? 'پرداخت' : 'دریافت' }}</td>
|
||||||
|
<td>{{ $filters.formatNumber(item.amount) }}</td>
|
||||||
<td>{{ item.gatePay }}</td>
|
<td>{{ item.gatePay }}</td>
|
||||||
<td>{{ item.refID }}</td>
|
<td>{{ item.refID }}</td>
|
||||||
<td>{{ item.shaba }}</td>
|
<td>{{ item.shaba }}</td>
|
||||||
|
@ -186,6 +187,7 @@ export default {
|
||||||
{ title: "کسبوکار", key: "bidName", sortable: false, align: 'center' },
|
{ title: "کسبوکار", key: "bidName", sortable: false, align: 'center' },
|
||||||
{ title: "بانک", key: "bankAcName", sortable: false, align: 'center' },
|
{ title: "بانک", key: "bankAcName", sortable: false, align: 'center' },
|
||||||
{ title: "نوع", key: "type", sortable: false, align: 'center' },
|
{ title: "نوع", key: "type", sortable: false, align: 'center' },
|
||||||
|
{ title: "مبلغ", key: "amount", sortable: false, align: 'center' },
|
||||||
{ title: "درگاه پرداخت", key: "gatePay", sortable: false, align: 'center' },
|
{ title: "درگاه پرداخت", key: "gatePay", sortable: false, align: 'center' },
|
||||||
{ title: "شناسه تراکنش", key: "refID", sortable: false, align: 'center' },
|
{ title: "شناسه تراکنش", key: "refID", sortable: false, align: 'center' },
|
||||||
{ title: "شبا", key: "shaba", sortable: false, align: 'center' },
|
{ title: "شبا", key: "shaba", sortable: false, align: 'center' },
|
||||||
|
|
Loading…
Reference in a new issue