Compare commits

...

2 commits

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

View file

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

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

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