first commit for tax lugin

This commit is contained in:
root 2025-07-17 17:48:19 +00:00
parent 8adfdb09ee
commit c20652b8cd
452 changed files with 1597 additions and 12 deletions

View file

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

View file

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

View file

@ -544,6 +544,7 @@ class BusinessController extends AbstractController
'plugRepservice' => true,
'plugHrmDocs' => true,
'plugGhestaManager' => true,
'plugTaxSettings' => true,
];
} elseif ($perm) {
$result = [
@ -587,6 +588,7 @@ class BusinessController extends AbstractController
'plugAccproPresell' => $perm->isPlugAccproPresell(),
'plugHrmDocs' => $perm->isPlugHrmDocs(),
'plugGhestaManager' => $perm->isPlugGhestaManager(),
'plugTaxSettings' => $perm->isPlugTaxSettings(),
];
}
return $this->json($result);

View file

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

View file

@ -129,6 +129,9 @@ class Permission
#[ORM\Column(nullable: true)]
private ?bool $plugGhestaManager = null;
#[ORM\Column(nullable: true)]
private ?bool $plugTaxSettings = null;
public function getId(): ?int
{
return $this->id;
@ -590,4 +593,16 @@ class Permission
return $this;
}
public function isPlugTaxSettings(): ?bool
{
return $this->plugTaxSettings;
}
public function setPlugTaxSettings(?bool $plugTaxSettings): static
{
$this->plugTaxSettings = $plugTaxSettings;
return $this;
}
}

View file

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

View file

@ -0,0 +1,53 @@
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: "plugin_tax_settings")]
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; }
}

View file

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

View file

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

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

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

0
webUI/LICENSE Normal file → Executable file
View file

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

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

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

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

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

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

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

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

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

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

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

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

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

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

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

View file

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View file

View file

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 235 KiB

View file

View file

View file

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

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

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

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

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

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

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

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

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

0
webUI/public/fonts/vazir/variable/Vazirmatn[wght].ttf Normal file → Executable file
View file

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

View file

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

View file

View file

View file

View file

View file

View file

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