From 1783aacfd464175e1e2aab20247ca90f0587aac1 Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Sun, 1 Jun 2025 08:18:12 +0000 Subject: [PATCH] bug fix in ghesta,persons,etc --- .../src/Controller/AdminController.php | 2 + .../src/Controller/PersonsController.php | 53 ++- .../Plugins/PlugGhestaController.php | 72 ++++ .../src/Controller/ReportController.php | 113 ++++++ hesabixCore/src/Controller/SellController.php | 34 +- hesabixCore/src/Service/PayMGR.php | 99 +++++ hesabixCore/src/Service/pdfMGR.php | 6 + .../pdf/plugins/ghesta/report.html.twig | 225 ++++++++++++ .../pdf/posPrinters/justSell.html.twig | 51 ++- .../templates/pdf/printers/buy.html.twig | 2 +- .../pdf/printers/buysell_report.html.twig | 187 ++++++++++ .../templates/pdf/printers/rfbuy.html.twig | 2 +- .../templates/pdf/printers/rfsell.html.twig | 2 +- .../templates/pdf/printers/sell.html.twig | 43 ++- webUI/src/i18n/fa_lang.ts | 2 + webUI/src/router/index.ts | 6 + webUI/src/views/acc/plugins/ghesta/mod.vue | 101 +++++- webUI/src/views/acc/plugins/ghesta/view.vue | 39 ++ .../acc/reports/persons/buysellByPerson.vue | 93 ++++- .../src/views/acc/reports/persons/withdet.vue | 67 ++++ webUI/src/views/acc/reports/reports.vue | 181 +++++++--- webUI/src/views/acc/sell/list.vue | 40 ++- webUI/src/views/acc/sell/mod.vue | 16 +- webUI/src/views/acc/smspanel/smspanel.vue | 340 +++++++++++++----- .../views/user/manager/settings/system.vue | 10 + 25 files changed, 1591 insertions(+), 195 deletions(-) create mode 100644 hesabixCore/templates/pdf/plugins/ghesta/report.html.twig create mode 100644 hesabixCore/templates/pdf/printers/buysell_report.html.twig create mode 100644 webUI/src/views/acc/reports/persons/withdet.vue diff --git a/hesabixCore/src/Controller/AdminController.php b/hesabixCore/src/Controller/AdminController.php index dc092b5..ffef7b9 100644 --- a/hesabixCore/src/Controller/AdminController.php +++ b/hesabixCore/src/Controller/AdminController.php @@ -448,6 +448,7 @@ class AdminController extends AbstractController $resp['footer'] = $item->getFooter(); $resp['activeGateway'] = $registryMGR->get('system', key: 'activeGateway'); $resp['parsianGatewayAPI'] = $registryMGR->get('system', key: 'parsianGatewayAPI'); + $resp['paypingKey'] = $registryMGR->get('system', key: 'paypingKey'); return $this->json($resp); } @@ -470,6 +471,7 @@ class AdminController extends AbstractController $item->setFooter($params['footer']); $registryMGR->update('system', 'activeGateway', $params['activeGateway']); $registryMGR->update('system', 'parsianGatewayAPI', $params['parsianGatewayAPI']); + $registryMGR->update('system', 'paypingKey', $params['paypingKey']); $entityManager->persist($item); $entityManager->flush(); return $this->json(['result' => 1]); diff --git a/hesabixCore/src/Controller/PersonsController.php b/hesabixCore/src/Controller/PersonsController.php index fd1775e..ccacc90 100644 --- a/hesabixCore/src/Controller/PersonsController.php +++ b/hesabixCore/src/Controller/PersonsController.php @@ -124,16 +124,23 @@ class PersonsController extends AbstractController //check exist before if (!$person) { $person = new Person(); - $code = $provider->getAccountingCode($acc['bid'], 'person'); - $exist = $entityManager->getRepository(Person::class)->findOneBy([ - 'code' => $code - ]); - while ($exist) { + $maxAttempts = 10; // حداکثر تعداد تلاش برای تولید کد جدید + $code = null; + + for ($i = 0; $i < $maxAttempts; $i++) { $code = $provider->getAccountingCode($acc['bid'], 'person'); $exist = $entityManager->getRepository(Person::class)->findOneBy([ 'code' => $code ]); + if (!$exist) { + break; + } } + + if ($code === null) { + throw new \Exception('نمی‌توان کد جدیدی برای شخص تولید کرد'); + } + $person->setCode($code); } @@ -270,16 +277,23 @@ class PersonsController extends AbstractController //check exist before if (!$person) { $person = new Person(); - $code = $provider->getAccountingCode($acc['bid'], 'person'); - $exist = $entityManager->getRepository(Person::class)->findOneBy([ - 'code' => $code - ]); - while ($exist) { + $maxAttempts = 10; // حداکثر تعداد تلاش برای تولید کد جدید + $code = null; + + for ($i = 0; $i < $maxAttempts; $i++) { $code = $provider->getAccountingCode($acc['bid'], 'person'); $exist = $entityManager->getRepository(Person::class)->findOneBy([ 'code' => $code ]); + if (!$exist) { + break; + } } + + if ($code === null) { + throw new \Exception('نمی‌توان کد جدیدی برای شخص تولید کرد'); + } + $person->setCode($code); } @@ -520,7 +534,7 @@ class PersonsController extends AbstractController // جست‌وجو (بهبود داده‌شده) if (!empty($search) || $search === '0') { // برای اطمینان از کار با "0" یا خالی $search = trim($search); // حذف فضای خالی اضافی - $queryBuilder->andWhere('p.nikename LIKE :search OR p.name LIKE :search OR p.code LIKE :search') + $queryBuilder->andWhere('p.nikename LIKE :search OR p.name LIKE :search OR p.code LIKE :search OR p.mobile LIKE :search') ->setParameter('search', "%$search%"); } @@ -1567,16 +1581,23 @@ class PersonsController extends AbstractController //check exist before if (!$person) { $person = new Person(); - $code = $provider->getAccountingCode($acc['bid'], 'person'); - $exist = $entityManager->getRepository(Person::class)->findOneBy([ - 'code' => $code - ]); - while ($exist) { + $maxAttempts = 10; // حداکثر تعداد تلاش برای تولید کد جدید + $code = null; + + for ($i = 0; $i < $maxAttempts; $i++) { $code = $provider->getAccountingCode($acc['bid'], 'person'); $exist = $entityManager->getRepository(Person::class)->findOneBy([ 'code' => $code ]); + if (!$exist) { + break; + } } + + if ($code === null) { + throw new \Exception('نمی‌توان کد جدیدی برای شخص تولید کرد'); + } + $person->setCode($code); $person->setNikename($item[0]); $person->setBid($acc['bid']); diff --git a/hesabixCore/src/Controller/Plugins/PlugGhestaController.php b/hesabixCore/src/Controller/Plugins/PlugGhestaController.php index 8387e52..d873db6 100644 --- a/hesabixCore/src/Controller/Plugins/PlugGhestaController.php +++ b/hesabixCore/src/Controller/Plugins/PlugGhestaController.php @@ -13,6 +13,10 @@ 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; class PlugGhestaController extends AbstractController { @@ -416,4 +420,72 @@ class PlugGhestaController extends AbstractController ], 500); } } + + #[Route('/api/plugins/ghesta/print', name: 'plugin_ghesta_print', methods: ['POST'])] + public function plugin_ghesta_print(Printers $printers, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse + { + $acc = $access->hasRole('plugGhestaManager'); + if (!$acc) + throw $this->createAccessDeniedException(); + + $params = json_decode($request->getContent(), true); + $params['pdf'] = $params['pdf'] ?? true; + + // دریافت تنظیمات پیش‌فرض از PrintOptions + $printSettings = $entityManager->getRepository(PrintOptions::class)->findOneBy(['bid' => $acc['bid']]); + + // تنظیم مقادیر پیش‌فرض از تنظیمات ذخیره شده + $defaultOptions = [ + 'note' => $printSettings ? $printSettings->isSellNote() : true, + 'bidInfo' => $printSettings ? $printSettings->isSellBidInfo() : true, + 'paper' => $printSettings ? $printSettings->getSellPaper() : 'A4-L', + 'businessStamp' => $printSettings ? $printSettings->isSellBusinessStamp() : true + ]; + + // اولویت با پارامترهای ارسالی است + $printOptions = array_merge($defaultOptions, $params['printOptions'] ?? []); + + $doc = $entityManager->getRepository(PlugGhestaDoc::class)->findOneBy([ + 'id' => $params['id'], + 'bid' => $acc['bid'] + ]); + + if (!$doc) + throw $this->createNotFoundException(); + + $pdfPid = 0; + if ($params['pdf'] == true) { + $note = ''; + if ($printSettings) { + $note = $printSettings->getSellNoteString(); + } + + // دریافت اطلاعات کسب و کار + $business = $entityManager->getRepository(Business::class)->find($acc['bid']); + + $pdfPid = $provider->createPrint( + $acc['bid'], + $this->getUser(), + $this->renderView('pdf/plugins/ghesta/report.html.twig', [ + 'bid' => $business, + 'doc' => $doc, + 'items' => array_map(function($item) { + return [ + 'date' => $item->getDate(), + 'amount' => $item->getAmount(), + 'num' => $item->getNum(), + 'hesabdariDoc' => $item->getHesabdariDoc() + ]; + }, $doc->getPlugGhestaItems()->toArray()), + 'person' => $doc->getPerson(), + 'printOptions' => $printOptions, + 'note' => $note + ]), + false, + $printOptions['paper'] + ); + } + + return $this->json(['id' => $pdfPid]); + } } \ No newline at end of file diff --git a/hesabixCore/src/Controller/ReportController.php b/hesabixCore/src/Controller/ReportController.php index 6c1faaf..dc91f22 100644 --- a/hesabixCore/src/Controller/ReportController.php +++ b/hesabixCore/src/Controller/ReportController.php @@ -24,6 +24,7 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use App\Entity\PrintOptions; class ReportController extends AbstractController { @@ -581,4 +582,116 @@ class ReportController extends AbstractController return $this->json(['error' => 'An error occurred: ' . $e->getMessage()], 500); } } + + #[Route('/api/report/person/buysell/export/pdf', name: 'app_report_person_buysell_export_pdf')] + public function app_report_person_buysell_export_pdf(Provider $provider, Access $access, Request $request, EntityManagerInterface $entityManagerInterface): BinaryFileResponse|JsonResponse|StreamedResponse + { + $acc = $access->hasRole('report'); + if (!$acc) + throw $this->createAccessDeniedException(); + $params = []; + if ($content = $request->getContent()) { + $params = json_decode($content, true); + } + + $items = []; + foreach ($params['items'] as $param) { + $prs = $entityManagerInterface->getRepository(HesabdariRow::class)->findOneBy([ + 'id' => $param['rowId'], + 'bid' => $acc['bid'] + ]); + if ($prs) + $items[] = $prs; + } + + // دریافت اطلاعات شخص از اولین آیتم + $person = null; + if (count($items) > 0) { + foreach ($items[0]->getDoc()->getHesabdariRows() as $row) { + if ($row->getPerson()) { + $person = $row->getPerson(); + break; + } + } + } + + $response = []; + foreach ($items as $item) { + $temp = [ + 'id' => $item->getCommodity()->getId(), + 'code' => $item->getCommodity()->getCode(), + 'khadamat' => $item->getCommodity()->isKhadamat(), + 'name' => $item->getCommodity()->getName(), + 'unit' => $item->getCommodity()->getUnit()->getName(), + 'count' => $item->getCommdityCount(), + 'date' => $item->getDoc()->getDate(), + 'docCode' => $item->getDoc()->getCode(), + 'type' => $item->getDoc()->getType() + ]; + if ($item->getDoc()->getType() == 'buy') { + $temp['priceAll'] = $item->getBd(); + } elseif ($item->getDoc()->getType() == 'sell') { + $temp['priceAll'] = $item->getBs(); + } + if ($temp['count'] != 0) { + $temp['priceOne'] = $temp['priceAll'] / $temp['count']; + $temp['priceAll'] = number_format($temp['priceAll']); + $temp['priceOne'] = number_format($temp['priceOne']); + $temp['count'] = number_format($temp['count']); + $response[] = $temp; + } + } + + // اضافه کردن شماره ردیف به داده‌ها + $responseWithRow = []; + foreach ($response as $index => $item) { + $responseWithRow[] = [ + 'row' => $index + 1, + 'code' => $item['code'], + 'name' => $item['name'], + 'unit' => $item['unit'], + 'count' => $item['count'], + 'priceOne' => $item['priceOne'], + 'priceAll' => $item['priceAll'], + 'date' => $item['date'], + 'docCode' => $item['docCode'], + 'type' => $item['type'], + 'khadamat' => $item['khadamat'] + ]; + } + + // دریافت تنظیمات چاپ + $printSettings = $entityManagerInterface->getRepository(PrintOptions::class)->findOneBy(['bid' => $acc['bid']]); + + // تنظیم مقادیر پیش‌فرض از تنظیمات ذخیره شده + $defaultOptions = [ + 'note' => $printSettings ? $printSettings->isSellNote() : true, + 'bidInfo' => $printSettings ? $printSettings->isSellBidInfo() : true, + 'taxInfo' => $printSettings ? $printSettings->isSellTaxInfo() : true, + 'discountInfo' => $printSettings ? $printSettings->isSellDiscountInfo() : true, + 'pays' => $printSettings ? $printSettings->isSellPays() : true, + 'paper' => $printSettings ? $printSettings->getSellPaper() : 'A4-L', + 'invoiceIndex' => $printSettings ? $printSettings->isSellInvoiceIndex() : true, + 'businessStamp' => $printSettings ? $printSettings->isSellBusinessStamp() : true + ]; + + // اولویت با پارامترهای ارسالی است + $printOptions = array_merge($defaultOptions, $params['printOptions'] ?? []); + + $pdfPid = $provider->createPrint( + $acc['bid'], + $this->getUser(), + $this->renderView('pdf/printers/buysell_report.html.twig', [ + 'bid' => $acc['bid'], + 'items' => $responseWithRow, + 'printOptions' => $printOptions, + 'note' => $printSettings ? $printSettings->getSellNoteString() : '', + 'person' => $person + ]), + false, + $printOptions['paper'] + ); + + return $this->json(['id' => $pdfPid]); + } } \ No newline at end of file diff --git a/hesabixCore/src/Controller/SellController.php b/hesabixCore/src/Controller/SellController.php index 51a6596..1b28b7b 100644 --- a/hesabixCore/src/Controller/SellController.php +++ b/hesabixCore/src/Controller/SellController.php @@ -747,27 +747,53 @@ class SellController extends AbstractController $this->renderView('pdf/printers/sell.html.twig', [ 'bid' => $acc['bid'], 'doc' => $doc, - 'rows' => $doc->getHesabdariRows(), + 'rows' => array_map(function($row) { + return [ + 'commodity' => $row->getCommodity(), + 'commodityCount' => $row->getCommdityCount(), + 'des' => $row->getDes(), + 'bs' => $row->getBs(), + 'tax' => $row->getTax(), + 'discount' => $row->getDiscount(), + 'showPercentDiscount' => $row->getDiscountType() === 'percent', + 'discountPercent' => $row->getDiscountPercent() + ]; + }, $doc->getHesabdariRows()->toArray()), 'person' => $person, 'printInvoice' => $params['printers'], 'discount' => $discount, 'transfer' => $transfer, 'printOptions' => $printOptions, - 'note' => $note + 'note' => $note, + 'showPercentDiscount' => $doc->getDiscountType() === 'percent', + 'discountPercent' => $doc->getDiscountPercent() ]), false, $printOptions['paper'] ); } if ($params['posPrint'] == true) { - $pid = $provider->createPrint( $acc['bid'], $this->getUser(), $this->renderView('pdf/posPrinters/justSell.html.twig', [ 'bid' => $acc['bid'], 'doc' => $doc, - 'rows' => $doc->getHesabdariRows(), + 'rows' => array_map(function($row) { + return [ + 'commodity' => $row->getCommodity(), + 'commodityCount' => $row->getCommdityCount(), + 'des' => $row->getDes(), + 'bs' => $row->getBs(), + 'tax' => $row->getTax(), + 'discount' => $row->getDiscount(), + 'showPercentDiscount' => $row->getDiscountType() === 'percent', + 'discountPercent' => $row->getDiscountPercent() + ]; + }, $doc->getHesabdariRows()->toArray()), + 'discount' => $discount, + 'showPercentDiscount' => $doc->getDiscountType() === 'percent', + 'discountPercent' => $doc->getDiscountPercent() ]), false ); diff --git a/hesabixCore/src/Service/PayMGR.php b/hesabixCore/src/Service/PayMGR.php index 8933f04..a0acacc 100644 --- a/hesabixCore/src/Service/PayMGR.php +++ b/hesabixCore/src/Service/PayMGR.php @@ -79,6 +79,58 @@ class PayMGR } } } + } elseif ($activeGateway == 'payping') { + $data = array( + 'amount' => $price, + 'returnUrl' => $callback_url, + 'description' => $des, + 'clientRefId' => $orderID + ); + + $ch = curl_init('https://api.payping.ir/v2/pay'); + curl_setopt_array($ch, array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => "", + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 45, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => "POST", + CURLOPT_POSTFIELDS => json_encode($data), + CURLOPT_HTTPHEADER => array( + "accept: application/json", + "authorization: Bearer " . $this->registry->get('system', 'paypingKey'), + "cache-control: no-cache", + "content-type: application/json", + ), + )); + + $response = curl_exec($ch); + $err = curl_error($ch); + $header = curl_getinfo($ch); + curl_close($ch); + + if ($err) { + $res['message'] = 'خطا در ارتباط با پی‌پینگ: ' . $err; + return $res; + } + + if ($header['http_code'] == 200) { + $response = json_decode($response, true); + if (isset($response['code'])) { + $res['code'] = 100; + $res['Success'] = true; + $res['gate'] = 'payping'; + $res['message'] = 'OK'; + $res['authkey'] = $response['code']; + $res['targetURL'] = 'https://api.payping.ir/v2/pay/gotoipg/' . $response['code']; + } else { + $res['message'] = 'خطا در دریافت کد پرداخت از پی‌پینگ'; + } + } elseif ($header['http_code'] == 400) { + $res['message'] = 'خطا در درخواست پرداخت: ' . $response; + } else { + $res['message'] = 'خطا در ارتباط با پی‌پینگ. کد خطا: ' . $header['http_code']; + } } elseif ($activeGateway == 'pec') { ini_set("soap.wsdl_cache_enabled", "0"); $url = "https://pec.shaparak.ir/NewIPGServices/Sale/SaleService.asmx?WSDL"; @@ -150,6 +202,53 @@ class PayMGR } } } + } elseif ($activeGateway == 'payping') { + $refid = $request->get('refid'); + if (!$refid) { + return $res; + } + + $data = array( + 'amount' => $price, + 'refId' => $refid + ); + + $ch = curl_init('https://api.payping.ir/v2/pay/verify'); + curl_setopt_array($ch, array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => "", + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 45, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => "POST", + CURLOPT_POSTFIELDS => json_encode($data), + CURLOPT_HTTPHEADER => array( + "accept: application/json", + "authorization: Bearer " . $this->registry->get('system', 'paypingKey'), + "cache-control: no-cache", + "content-type: application/json", + ), + )); + + $response = curl_exec($ch); + $err = curl_error($ch); + $header = curl_getinfo($ch); + curl_close($ch); + + if ($err) { + return $res; + } + + if ($header['http_code'] == 200) { + $response = json_decode($response, true); + if (isset($refid) && $refid != '') { + $res['Success'] = true; + $res['status'] = 100; + $res['refID'] = $refid; + $res['card_pan'] = ''; // PayPing این اطلاعات را برنمی‌گرداند + return $res; + } + } } elseif ($activeGateway == 'pec') { $confirmUrl = 'https://pec.shaparak.ir/NewIPGServices/Confirm/ConfirmService.asmx?WSDL'; $params = array( diff --git a/hesabixCore/src/Service/pdfMGR.php b/hesabixCore/src/Service/pdfMGR.php index 680c639..788c515 100644 --- a/hesabixCore/src/Service/pdfMGR.php +++ b/hesabixCore/src/Service/pdfMGR.php @@ -55,6 +55,12 @@ class pdfMGR ], 'default_font' => 'vazirmatn', 'tempDir' => $tempDir, + 'margin_left' => 5, + 'margin_right' => 5, + 'margin_top' => 5, + 'margin_bottom' => 5, + 'margin_header' => 2, + 'margin_footer' => 2, 'autoArabic' => true, ]); diff --git a/hesabixCore/templates/pdf/plugins/ghesta/report.html.twig b/hesabixCore/templates/pdf/plugins/ghesta/report.html.twig new file mode 100644 index 0000000..65ccc9c --- /dev/null +++ b/hesabixCore/templates/pdf/plugins/ghesta/report.html.twig @@ -0,0 +1,225 @@ + + + + + + + +
+
+
+ + + + + + + + +
+ + +

{{ bid.name }}

+

گزارش اقساط

+
+

+ شماره فاکتور: + {{ doc.mainDoc.code }}

+
+
+ +
+
+ خریدار +
+ + + + + + + + + + + + + + +
+

+ نام: + {% if person.prelabel is not null %}{{ person.prelabel.label }}{% endif %} + {{ person.nikename }} +

+
+

+ شناسه ملی: + {{ person.shenasemeli }} +

+
+

+ شماره ثبت: + {{ person.sabt }} +

+
+

+ شماره اقتصادی: + {{ person.codeeghtesadi }} +

+
+

+ تلفن / نمابر: + {{ person.tel }} +

+
+

+ کد پستی: + {{ person.postalcode }} +

+
+

+ آدرس: + استان {{ person.ostan }}، شهر {{ person.shahr }}، {{ person.address }} +

+
+
+ +
+
+ اطلاعات اقساط +
+ + + + + + + + + + +
+

+ تعداد اقساط: + {{ doc.count }} +

+
+

+ درصد سود: + {{ doc.profitPercent }}% +

+
+

+ مبلغ سود: + {{ doc.profitAmount|number_format }} ریال +

+
+

+ نوع سود: + {% if doc.profitType == 'yearly' %} + سالانه + {% elseif doc.profitType == 'monthly' %} + ماهانه + {% else %} + روزانه + {% endif %} +

+
+

+ جریمه روزانه: + {{ doc.daysPay }}% +

+
+
+ +
+ + + + + + + + + + + {% for item in items %} + + + + + + + {% endfor %} + +
شماره قسطتاریخمبلغوضعیت
{{ item.num }}{{ item.date|date('Y/m/d') }}{{ item.amount|number_format }} ریال + {% if item.hesabdariDoc %} + پرداخت شده ({{ item.hesabdariDoc.code }}) + {% else %} + پرداخت نشده + {% endif %} +
+
+ + {% if printOptions.note and note %} +
+ + + + + + +
+

توضیحات:

+ {{ note|nl2br }} +
+
+ {% endif %} + +
+ + + + + + + +
+

مهر و امضا خریدار

+
+

مهر و امضا فروشنده:

+
+ {% if printOptions.businessStamp %} + + {% endif %} +
+
+
+
+ + \ No newline at end of file diff --git a/hesabixCore/templates/pdf/posPrinters/justSell.html.twig b/hesabixCore/templates/pdf/posPrinters/justSell.html.twig index ec1590b..612d906 100644 --- a/hesabixCore/templates/pdf/posPrinters/justSell.html.twig +++ b/hesabixCore/templates/pdf/posPrinters/justSell.html.twig @@ -6,7 +6,7 @@ body { margin: 5px; padding: 0; - font-size: 100%; + font-size: 11px; } table { @@ -19,10 +19,12 @@ th, td { border: 1px solid black !important; + font-size: 11px; } h1 { text-align: center; vertical-align: middle; + font-size: 14px; } #logo { @@ -60,7 +62,7 @@ } .items .heading { - font-size: 12.5px; + font-size: 11px; text-transform: uppercase; border-top: 1px solid black; margin-bottom: 4px; @@ -75,7 +77,7 @@ } .items td { - font-size: 12px; + font-size: 11px; text-align: center; vertical-align: bottom; } @@ -89,7 +91,7 @@ text-align: right !important; } .total { - font-size: 13px; + font-size: 11px; border-top: 1px dashed black !important; border-bottom: 1px dashed black !important; } @@ -110,7 +112,7 @@ } section, footer { - font-size: 12px; + font-size: 11px; } tbody, thead, @@ -150,6 +152,7 @@ کالا تعداد فی + قبل تخفیف جمع @@ -159,8 +162,29 @@ {% if row.commodity != null %} {{row.commodity.name}} - {{row.commdityCount}} - {{(row.bs / row.commdityCount) | number_format}} + {{row.commodityCount}} + + {% if row.commodityCount > 0 %} + {% if row.showPercentDiscount %} + {% set originalPrice = row.bs / (1 - (row.discountPercent / 100)) %} + {% set unitPrice = originalPrice / row.commodityCount %} + {% else %} + {% set originalPrice = row.bs + row.discount %} + {% set unitPrice = originalPrice / row.commodityCount %} + {% endif %} + {{ unitPrice|round|number_format }} + {% else %} + 0 + {% endif %} + + + {% if row.showPercentDiscount %} + {% set originalPrice = row.bs / (1 - (row.discountPercent / 100)) %} + {{ originalPrice|round|number_format }} + {% else %} + {{ (row.bs + row.discount)|number_format }} + {% endif %} + {{row.bs | number_format}} {% endif %} @@ -169,6 +193,19 @@ جمع فاکتور {{doc.amount | number_format}} + {% if discount > 0 %} + + تخفیف + + {% if showPercentDiscount %} + {{ discountPercent }}% + ({{ (doc.amount * discountPercent / 100)|round|number_format }}) + {% else %} + {{ discount | number_format }} + {% endif %} + + + {% endif %}
diff --git a/hesabixCore/templates/pdf/printers/buy.html.twig b/hesabixCore/templates/pdf/printers/buy.html.twig index a4d7fd3..64f977a 100644 --- a/hesabixCore/templates/pdf/printers/buy.html.twig +++ b/hesabixCore/templates/pdf/printers/buy.html.twig @@ -276,7 +276,7 @@ {{transfer | number_format}}

- مبلغ کل بدون تخفیف: + جمع بدون تخفیف: {{ (doc.amount + discount) | number_format}}

diff --git a/hesabixCore/templates/pdf/printers/buysell_report.html.twig b/hesabixCore/templates/pdf/printers/buysell_report.html.twig new file mode 100644 index 0000000..7387152 --- /dev/null +++ b/hesabixCore/templates/pdf/printers/buysell_report.html.twig @@ -0,0 +1,187 @@ + + + + + گزارش خرید و فروش + + + +
+
+
+ + + + + + + + +
+ {% if printOptions.invoiceIndex %} + + {% endif %} + +

{{ bid.legalName }}

+

گزارش خرید و فروش

+
+

+ تاریخ چاپ: + {{ "now"|date("Y/m/d") }}

+
+
+ + {% if printOptions.bidInfo %} +
+
+ اطلاعات شخص +
+ + + + + + + + + + + + + + +
+

+ نام: + {% if person.prelabel is not null %}{{ person.prelabel.label }}{% endif %} + {{ person.nikename }} +

+
+

+ شناسه ملی: + {{ person.shenasemeli }} +

+
+

+ شماره ثبت: + {{ person.sabt }} +

+
+

+ شماره اقتصادی: + {{ person.codeeghtesadi }} +

+
+

+ تلفن / نمابر: + {{ person.tel }} +

+
+

+ کد پستی: + {{ person.postalcode }} +

+
+

+ آدرس: + استان {{ person.ostan }}، شهر {{ person.shahr }}، {{ person.address }} +

+
+
+ {% endif %} + +
+ + + + + + + + + + + + + + + + + + {% for item in items %} + + + + + + + + + + + + + + {% endfor %} + +
ردیفکد کالانام کالاواحدتعدادقیمت واحدقیمت کلتاریخشماره سندنوع سندنوع
{{ item.row }}{{ item.code }}{{ item.name }}{{ item.unit }}{{ item.count }}{{ item.priceOne }}{{ item.priceAll }}{{ item.date }}{{ item.docCode }} + {% if item.type == 'buy' %} + خرید + {% elseif item.type == 'sell' %} + فروش + {% elseif item.type == 'rfbuy' %} + برگشت از خرید + {% elseif item.type == 'rfsell' %} + برگشت از فروش + {% endif %} + + {% if item.khadamat %} + خدمات + {% else %} + کالا + {% endif %} +
+
+ +
+ + + + + + +
+

مهر و امضا

+ {% if printOptions.businessStamp %} + + {% endif %} +
+
+
+
+ + \ No newline at end of file diff --git a/hesabixCore/templates/pdf/printers/rfbuy.html.twig b/hesabixCore/templates/pdf/printers/rfbuy.html.twig index 57561b7..1c3e2f0 100644 --- a/hesabixCore/templates/pdf/printers/rfbuy.html.twig +++ b/hesabixCore/templates/pdf/printers/rfbuy.html.twig @@ -276,7 +276,7 @@ {{transfer | number_format}}

- مبلغ کل بدون تخفیف: + جمع بدون تخفیف: {{ (doc.amount + discount) | number_format}}

diff --git a/hesabixCore/templates/pdf/printers/rfsell.html.twig b/hesabixCore/templates/pdf/printers/rfsell.html.twig index 8612f38..832abdc 100644 --- a/hesabixCore/templates/pdf/printers/rfsell.html.twig +++ b/hesabixCore/templates/pdf/printers/rfsell.html.twig @@ -276,7 +276,7 @@ {{transfer | number_format}}

- مبلغ کل بدون تخفیف: + جمع بدون تخفیف: {{ (doc.amount + discount) | number_format}}

diff --git a/hesabixCore/templates/pdf/printers/sell.html.twig b/hesabixCore/templates/pdf/printers/sell.html.twig index 05bee97..1d2b56b 100644 --- a/hesabixCore/templates/pdf/printers/sell.html.twig +++ b/hesabixCore/templates/pdf/printers/sell.html.twig @@ -15,6 +15,16 @@ } .item { height: 30px; + font-size: 11px; + } + h3 { + font-size: 14px; + } + h4 { + font-size: 12px; + } + p { + font-size: 11px; } @@ -191,6 +201,7 @@ مبلغ واحد {% if printOptions.discountInfo %} تخفیف + قبل تخفیف {% endif %} {% if printOptions.taxInfo %} مالیات @@ -213,18 +224,40 @@ {{ item.commodity.name }} {{ item.des }} - {{ item.commdityCount }} + {{ item.commodityCount }} {{ item.commodity.unit.name }} - {% if item.commdityCount > 0 %} - {{ ((item.bs|number_format(0, '.', '') - item.tax|number_format(0, '.', '') + item.discount|number_format(0, '.', '')) / item.commdityCount) | number_format }} + {% if item.commodityCount > 0 %} + {% if item.showPercentDiscount %} + {% set originalPrice = item.bs / (1 - (item.discountPercent / 100)) %} + {% set unitPrice = originalPrice / item.commodityCount %} + {% else %} + {% set originalPrice = item.bs + item.discount %} + {% set unitPrice = originalPrice / item.commodityCount %} + {% endif %} + {{ unitPrice|round|number_format }} {% else %} 0 {% endif %} {% if printOptions.discountInfo %} - {{ item.discount | number_format }} + + {% if item.showPercentDiscount %} + {{ item.discountPercent }}% + ({{ (item.bs * item.commodityCount * item.discountPercent / 100)|round|number_format }}) + {% else %} + {{ item.discount|number_format }} + {% endif %} + + + {% if item.showPercentDiscount %} + {% set originalPrice = item.bs / (1 - (item.discountPercent / 100)) %} + {{ originalPrice|round|number_format }} + {% else %} + {{ (item.bs + item.discount)|number_format }} + {% endif %} + {% endif %} {% if printOptions.taxInfo %} {{ item.tax | number_format}} @@ -285,7 +318,7 @@

{% if doc.amount != (doc.amount + discount) %}

- مبلغ کل بدون تخفیف: + جمع بدون تخفیف: {{ (doc.amount + discount) | number_format}}

{% endif %} diff --git a/webUI/src/i18n/fa_lang.ts b/webUI/src/i18n/fa_lang.ts index 41010a8..e86d377 100644 --- a/webUI/src/i18n/fa_lang.ts +++ b/webUI/src/i18n/fa_lang.ts @@ -286,6 +286,7 @@ const fa_lang = { fetch_data_error: "خطا در گرفتن داده از {url}" }, dialog: { + person_with_det_report: 'گزارش تفضیلی اشخاص', change_password_label: 'تغییر کلمه عبور', download: 'دانلود', delete_group: 'حذف گروهی', @@ -793,6 +794,7 @@ const fa_lang = { keywords: "کلیدواژه‌ها با کاما (,) از هم جدا شوند", zarinpal_api: "کد API زرین‌پال", parsian_api: "کد API درگاه پارسیان", + payping_api: "کد API درگاه پی‌پینگ", scripts: "اسکریپت‌ها", footer_scripts: "اسکریپت‌های فوتر سایت(مثلا اسکریپت شمارنده گوگل و ...)", site_footer: "فوتر سایت با پشتیبانی از HTML", diff --git a/webUI/src/router/index.ts b/webUI/src/router/index.ts index a7d859f..b48744b 100644 --- a/webUI/src/router/index.ts +++ b/webUI/src/router/index.ts @@ -298,6 +298,12 @@ const router = createRouter({ component: () => import('../views/acc/reports/persons/buysellByPerson.vue'), }, + { + path: 'reports/persons/withdet', + name: 'person_withdet', + component: () => + import('../views/acc/reports/persons/withdet.vue'), + }, { path: 'costs/list', name: 'costs_list', diff --git a/webUI/src/views/acc/plugins/ghesta/mod.vue b/webUI/src/views/acc/plugins/ghesta/mod.vue index beb06d0..34d4619 100644 --- a/webUI/src/views/acc/plugins/ghesta/mod.vue +++ b/webUI/src/views/acc/plugins/ghesta/mod.vue @@ -933,13 +933,6 @@ export default { this.isCalculating = true; this.loading = true; - // اگر در حالت ویرایش هستیم و اقساط قبلاً محاسبه شده‌اند، از آنها استفاده کن - if (this.$route.params.id && this.installments.length > 0) { - this.isCalculating = false; - this.loading = false; - return; - } - const totalAmount = this.remainingAmount; const prepayment = Number(this.installmentData.prepayment) || 0; const remainingAmount = totalAmount - prepayment; @@ -1121,6 +1114,13 @@ export default { }, immediate: true }, + 'installmentData.count': { + handler(newVal) { + if (newVal && this.installmentData.calculationType === 'count') { + this.calculateInstallments(); + } + } + }, 'installmentData.interestRate': { handler(newVal, oldVal) { if (newVal && !oldVal) { @@ -1181,4 +1181,91 @@ export default { font-weight: 600; background-color: rgb(var(--v-theme-surface)); } + +:deep(.vpd-main) { + position: fixed !important; + z-index: 999999 !important; + top: 50% !important; + left: 50% !important; + transform: translate(-50%, -50%) !important; + pointer-events: auto !important; +} + +:deep(.vpd-wrapper) { + position: fixed !important; + z-index: 999999 !important; + top: 50% !important; + left: 50% !important; + transform: translate(-50%, -50%) !important; + pointer-events: auto !important; +} + +:deep(.vpd-container) { + position: fixed !important; + z-index: 999999 !important; + top: 50% !important; + left: 50% !important; + transform: translate(-50%, -50%) !important; + pointer-events: auto !important; +} + +:deep(.vpd-content) { + position: fixed !important; + z-index: 999999 !important; + top: 50% !important; + left: 50% !important; + transform: translate(-50%, -50%) !important; + pointer-events: auto !important; +} + +:deep(.vpd-overlay) { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + background: rgba(0, 0, 0, 0.5) !important; + z-index: 999998 !important; + pointer-events: auto !important; +} + +:deep(.v-application) { + position: relative !important; +} + +:deep(.v-application--wrap) { + position: relative !important; +} + +:deep(.v-main) { + position: relative !important; +} + +:deep(.v-main__wrap) { + position: relative !important; +} + +:deep(.v-container) { + position: relative !important; +} + +:deep(.v-row) { + position: relative !important; +} + +:deep(.v-col) { + position: relative !important; +} + +:deep(.v-card) { + position: relative !important; +} + +:deep(.v-field) { + position: relative !important; +} + +:deep(.v-field__input) { + position: relative !important; +} \ No newline at end of file diff --git a/webUI/src/views/acc/plugins/ghesta/view.vue b/webUI/src/views/acc/plugins/ghesta/view.vue index e42cf55..568ba51 100644 --- a/webUI/src/views/acc/plugins/ghesta/view.vue +++ b/webUI/src/views/acc/plugins/ghesta/view.vue @@ -8,6 +8,11 @@ + + +
@@ -640,6 +645,40 @@ export default { } finally { this.loading = false } + }, + async printReport() { + try { + this.loading = true; + const response = await axios.post('/api/plugins/ghesta/print', { + id: this.$route.params.id, + pdf: true + }); + + if (response.data && response.data.id) { + // دریافت فایل PDF + const pdfResponse = await axios({ + method: 'get', + url: '/front/print/' + response.data.id, + responseType: 'arraybuffer' + }); + + // ایجاد لینک دانلود + const fileURL = window.URL.createObjectURL(new Blob([pdfResponse.data])); + const fileLink = document.createElement('a'); + fileLink.href = fileURL; + fileLink.setAttribute('download', `گزارش اقساط ${this.invoice.code}.pdf`); + document.body.appendChild(fileLink); + fileLink.click(); + } else { + throw new Error('خطا در دریافت شناسه چاپ'); + } + } catch (error) { + console.error('خطا در چاپ گزارش:', error); + this.error = 'خطا در چاپ گزارش'; + this.errorDialog = true; + } finally { + this.loading = false; + } } }, created() { diff --git a/webUI/src/views/acc/reports/persons/buysellByPerson.vue b/webUI/src/views/acc/reports/persons/buysellByPerson.vue index 253b5b6..fbe29cf 100644 --- a/webUI/src/views/acc/reports/persons/buysellByPerson.vue +++ b/webUI/src/views/acc/reports/persons/buysellByPerson.vue @@ -10,6 +10,29 @@ + + + + {{ $t('dialog.export_pdf') }} + + + + + + + + +