From 2ff6c04b2672fd3f8c9ebb168073f7cd90f7d1ea Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Sun, 9 Mar 2025 18:46:38 +0000 Subject: [PATCH] more bug fix in print doc and pdf generator --- .../src/Controller/GeneralController.php | 43 ++-- .../src/Controller/HesabdariDocController.php | 202 ++++++++++++++++++ hesabixCore/src/Service/pdfMGR.php | 99 +++++---- 3 files changed, 284 insertions(+), 60 deletions(-) create mode 100644 hesabixCore/src/Controller/HesabdariDocController.php diff --git a/hesabixCore/src/Controller/GeneralController.php b/hesabixCore/src/Controller/GeneralController.php index 6b738f7..69140f5 100644 --- a/hesabixCore/src/Controller/GeneralController.php +++ b/hesabixCore/src/Controller/GeneralController.php @@ -3,39 +3,34 @@ namespace App\Controller; use App\Entity\ChangeReport; -use App\Entity\HesabdariDoc; use App\Entity\Statment; -use App\Entity\User; -use App\Service\Access; +use App\Entity\PrinterQueue; use App\Service\Jdate; +use App\Service\PdfMGR; +use App\Service\Provider; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; -use App\Entity\Business; -use App\Entity\PrinterQueue; -use App\Service\pdfMGR; -use App\Service\Provider; class GeneralController extends AbstractController { #[Route('/api/general/stat', name: 'general_stat')] public function general_stat(EntityManagerInterface $entityManager, Jdate $jdate): JsonResponse { - - //get last version number $version = '0.0.1'; $lastUpdateDate = '1399'; $lastUpdateDes = ''; + $last = $entityManager->getRepository(ChangeReport::class)->findOneBy([], ['id' => 'DESC']); if ($last) { $version = $last->getVersion(); $lastUpdateDate = $jdate->jdate('Y/n/d', $last->getDateSubmit()); $lastUpdateDes = $last->getBody(); } - + return $this->json([ 'version' => $version, 'lastUpdateDate' => $lastUpdateDate, @@ -44,7 +39,7 @@ class GeneralController extends AbstractController } #[Route('/api/general/statements', name: 'general_statement')] - public function general_statement(EntityManagerInterface $entityManager, Jdate $jdate): JsonResponse + public function general_statement(EntityManagerInterface $entityManager): JsonResponse { return $this->json($entityManager->getRepository(Statment::class)->findBy([], ['id' => 'DESC'])); } @@ -52,26 +47,30 @@ class GeneralController extends AbstractController #[Route('/api/general/get/time', name: 'general_get_time')] public function general_get_time(Jdate $jdate, Request $request): JsonResponse { - $params = []; - if ($content = $request->getContent()) { - $params = json_decode($content, true); - } - if (array_key_exists('format', $params)) - return $this->json(['timeNow' => $jdate->jdate($params['format'], time()), 'ts' => time()]); - return $this->json(['timeNow' => $jdate->jdate('Y/n/d', time()), 'ts' => time()]); + $params = json_decode($request->getContent(), true) ?? []; + $format = $params['format'] ?? 'Y/n/d'; + return $this->json(['timeNow' => $jdate->jdate($format, time()), 'ts' => time()]); } + #[Route('/front/print/{id}', name: 'app_front_print')] - public function app_front_print(Provider $provider, EntityManagerInterface $entityManager, pdfMGR $pdfMGR, string $id) + public function app_front_print(Provider $provider, EntityManagerInterface $entityManager, PdfMGR $pdfMGR, string $id): Response { $print = $entityManager->getRepository(PrinterQueue::class)->findOneBy(['pid' => $id]); - if (!$print) - throw $this->createNotFoundException(); + + if (!$print) { + return new JsonResponse(['error' => 'Print job not found'], Response::HTTP_NOT_FOUND); + } + + if (empty($print->getView())) { + return new JsonResponse(['error' => 'Empty print content'], Response::HTTP_BAD_REQUEST); + } + if ($print->isPosprint()) { $pdfMGR->streamTwig2PDFInvoiceType($print); } else { $pdfMGR->streamTwig2PDF($print); } - return new Response(''); + return new Response('Print job completed', Response::HTTP_OK); } } diff --git a/hesabixCore/src/Controller/HesabdariDocController.php b/hesabixCore/src/Controller/HesabdariDocController.php new file mode 100644 index 0000000..048477f --- /dev/null +++ b/hesabixCore/src/Controller/HesabdariDocController.php @@ -0,0 +1,202 @@ +em = $em; + $this->access = $access; + $this->extractor = $extractor; + } + + #[Route('/hesabdari/tables', name: 'get_hesabdari_tables', methods: ['GET'])] + public function getHesabdariTables(): JsonResponse + { + $accessData = $this->access->hasRole('accounting'); + if (!$accessData) { + return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403); + } + + $bid = $accessData['bid']; + + // گرفتن همه حساب‌ها (عمومی و خصوصی) + $tables = $this->em->getRepository(HesabdariTable::class)->findBy([ + 'bid' => [$bid, null], // حساب‌های عمومی (null) و خصوصی (bid) + ]); + + // تبدیل به ساختار درختی + $tree = $this->buildTree($tables); + + return new JsonResponse($this->extractor->operationSuccess($tree, 'لیست حساب‌ها با موفقیت دریافت شد')); + } + + private function buildTree(array $tables): array + { + $tree = []; + $map = []; + + // مپ کردن همه حساب‌ها + foreach ($tables as $table) { + $map[$table->getId()] = [ + 'id' => $table->getId(), + 'name' => $table->getName(), + 'code' => $table->getCode(), + 'type' => $table->getType(), + 'bid' => $table->getBid(), + 'children' => [], + ]; + } + + // ساخت درخت + foreach ($tables as $table) { + $node = &$map[$table->getId()]; + $upper = $table->getUpper(); // این یه شیء HesabdariTable یا null هست + $upperId = $upper ? $upper->getId() : null; // اگه upper باشه، idش رو بگیریم + + // اگه upper برابر null یا 0 باشه، ریشه‌ست + if ($upperId === null || $upperId === 0) { + $tree[] = &$node; + } elseif (isset($map[$upperId])) { + $map[$upperId]['children'][] = &$node; + } + } + + return $tree; + } + + #[Route('/hesabdari/doc', name: 'create_hesabdari_doc', methods: ['POST'])] + public function create(Request $request): JsonResponse + { + $accessData = $this->access->hasRole('accounting'); + if (!$accessData) { + return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403); + } + + $data = json_decode($request->getContent(), true); + if (!$data || !isset($data['date']) || !isset($data['rows'])) { + return new JsonResponse($this->extractor->paramsNotSend(), 400); + } + + $doc = new HesabdariDoc(); + $doc->setBid($accessData['bid']); + $doc->setSubmitter($accessData['user']); + $doc->setYear($accessData['year']); + $doc->setMoney($accessData['money']); + $doc->setDate($data['date']); + $doc->setDateSubmit((string) time()); + $doc->setType('doc'); + $doc->setCode($this->generateDocCode($accessData['bid'])); + + $totalBd = 0; + $totalBs = 0; + foreach ($data['rows'] as $rowData) { + $row = new HesabdariRow(); + $row->setDoc($doc); + $row->setRef($this->em->getRepository('App\Entity\HesabdariTable')->find($rowData['ref'])); + $row->setBd($rowData['bd'] ?? '0'); + $row->setBs($rowData['bs'] ?? '0'); + $row->setDes($rowData['des'] ?? null); + $row->setBid($accessData['bid']); + $row->setYear($accessData['year']); + + $totalBd += (int) $row->getBd(); + $totalBs += (int) $row->getBs(); + + $this->em->persist($row); + $doc->addHesabdariRow($row); + } + + if ($totalBd !== $totalBs) { + return new JsonResponse($this->extractor->operationFail('جمع بدهکار و بستانکار باید برابر باشد'), 400); + } + + $doc->setAmount($totalBd); + $this->em->persist($doc); + $this->em->flush(); + + return new JsonResponse($this->extractor->operationSuccess(['id' => $doc->getId()], 'سند با موفقیت ثبت شد')); + } + + #[Route('/hesabdari/doc/{id}', name: 'edit_hesabdari_doc', methods: ['PUT'])] + public function edit(int $id, Request $request): JsonResponse + { + $accessData = $this->access->hasRole('accounting'); + if (!$accessData) { + return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403); + } + + $doc = $this->em->getRepository(HesabdariDoc::class)->find($id); + if (!$doc || $doc->getBid() !== $accessData['bid']) { + return new JsonResponse($this->extractor->notFound(), 404); + } + + $data = json_decode($request->getContent(), true); + if (!$data || !isset($data['date']) || !isset($data['rows'])) { + return new JsonResponse($this->extractor->paramsNotSend(), 400); + } + + $doc->setDate($data['date']); + + foreach ($doc->getHesabdariRows() as $row) { + $this->em->remove($row); + } + $doc->getHesabdariRows()->clear(); + + $totalBd = 0; + $totalBs = 0; + foreach ($data['rows'] as $rowData) { + $row = new HesabdariRow(); + $row->setDoc($doc); + $row->setRef($this->em->getRepository('App\Entity\HesabdariTable')->find($rowData['ref'])); + $row->setBd($rowData['bd'] ?? '0'); + $row->setBs($rowData['bs'] ?? '0'); + $row->setDes($rowData['des'] ?? null); + $row->setBid($accessData['bid']); + $row->setYear($accessData['year']); + + $totalBd += (int) $row->getBd(); + $totalBs += (int) $row->getBs(); + + $this->em->persist($row); + $doc->addHesabdariRow($row); + } + + if ($totalBd !== $totalBs) { + return new JsonResponse($this->extractor->operationFail('جمع بدهکار و بستانکار باید برابر باشد'), 400); + } + + $doc->setAmount($totalBd); + $this->em->persist($doc); + $this->em->flush(); + + return new JsonResponse($this->extractor->operationSuccess(['id' => $doc->getId()], 'سند با موفقیت ویرایش شد')); + } + + private function generateDocCode($business): string + { + $lastDoc = $this->em->getRepository(HesabdariDoc::class)->findOneBy( + ['bid' => $business], + ['code' => 'DESC'] + ); + $newCode = $lastDoc ? ((int) $lastDoc->getCode() + 1) : 1; + return (string) $newCode; + } +} \ No newline at end of file diff --git a/hesabixCore/src/Service/pdfMGR.php b/hesabixCore/src/Service/pdfMGR.php index 22b4a15..7d04984 100644 --- a/hesabixCore/src/Service/pdfMGR.php +++ b/hesabixCore/src/Service/pdfMGR.php @@ -3,39 +3,43 @@ namespace App\Service; use App\Entity\PrinterQueue; -use App\Service\Twig; - use Twig\Environment; -class pdfMGR -{ +use Mpdf\Mpdf; +use Mpdf\Config\ConfigVariables; +use Mpdf\Config\FontVariables; +class PdfMGR +{ private $twig; - public function __construct(Twig $twig) + public function __construct(Environment $twig) { $this->twig = $twig; } - public function streamTwig2PDF(PrinterQueue $printQueue,$configs = []){ + public function streamTwig2PDF(PrinterQueue $printQueue, $configs = []) + { // Load Twig File $template = $this->twig->load('pdf/footer.html.twig'); + $footer = $template->render([]); - // Render HTML - $footer = $template->render([ - - ]); - $size = $printQueue->getPaperSize(); - if(!$size){ $size = 'A4-L'; } - $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults(); + $size = $printQueue->getPaperSize() ?: 'A4-L'; + + $defaultConfig = (new ConfigVariables())->getDefaults(); $fontDirs = $defaultConfig['fontDir']; - $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults(); + $defaultFontConfig = (new FontVariables())->getDefaults(); $fontData = $defaultFontConfig['fontdata']; - $mpdf = new \Mpdf\Mpdf([ - 'mode' => 'utf-8', 'format' => $size, - 'fontDir' => array_merge($fontDirs, [ - dirname(__DIR__) . '/Fonts', - ]), + + $tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mpdf'; + if (!is_dir($tempDir)) { + mkdir($tempDir, 0777, true); + } + + $mpdf = new Mpdf([ + 'mode' => 'utf-8', + 'format' => $size, + 'fontDir' => array_merge($fontDirs, [dirname(__DIR__) . '/Fonts']), 'fontdata' => $fontData + [ 'vazirmatn' => [ 'R' => 'Vazir-Regular-FD.ttf', @@ -45,14 +49,16 @@ class pdfMGR ] ], 'default_font' => 'vazirmatn', - 'tempDir' => sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mpdf', + 'tempDir' => $tempDir, 'autoArabic' => true, ]); - if($printQueue->isFooter()){ + + if ($printQueue->isFooter()) { $mpdf->SetHTMLFooter($footer); } - - $mpdf->WriteHTML($printQueue->getView()); + + $htmlContent = $printQueue->getView() ?: '

محتوای PDF در دسترس نیست.

'; + $mpdf->WriteHTML($htmlContent); $mpdf->SetAutoPageBreak(true); $mpdf->SetTitle('حسابیکس'); $mpdf->Output('Hesabix PrintOut.pdf', 'I'); @@ -60,17 +66,22 @@ class pdfMGR public function streamTwig2PDFInvoiceType(PrinterQueue $printQueue, $configs = []) { - $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults(); + $defaultConfig = (new ConfigVariables())->getDefaults(); $fontDirs = $defaultConfig['fontDir']; - $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults(); + $defaultFontConfig = (new FontVariables())->getDefaults(); $fontData = $defaultFontConfig['fontdata']; - $mpdf = new \Mpdf\Mpdf([ - 'mode' => 'utf-8', 'format' => [80, 300], - 'fontDir' => array_merge($fontDirs, [ - dirname(__DIR__) . '/Fonts', - ]), - 'fontdata' => [ + + $tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mpdf'; + if (!is_dir($tempDir)) { + mkdir($tempDir, 0777, true); + } + + $mpdf = new Mpdf([ + 'mode' => 'utf-8', + 'format' => [80, 300], + 'fontDir' => array_merge($fontDirs, [dirname(__DIR__) . '/Fonts']), + 'fontdata' => $fontData + [ 'vazirmatn' => [ 'R' => 'Vazir-Regular-FD.ttf', 'I' => 'Vazir-Regular-FD.ttf', @@ -79,27 +90,39 @@ class pdfMGR ] ], 'default_font' => 'vazirmatn', - 'tempDir' => sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mpdf', + 'tempDir' => $tempDir, 'setAutoTopMargin' => true, 'autoArabic' => true, 'margin-collapse' => 'collapse|none' ]); + $mpdf->AddPageByArray([ 'margin-left' => 0, 'margin-right' => 0, 'margin-top' => 0, 'margin-bottom' => 0, ]); - $mpdf->WriteHTML($printQueue->getView()); + $htmlContent = $printQueue->getView() ?: '

محتوای PDF در دسترس نیست.

'; + $mpdf->WriteHTML($htmlContent); $mpdf->Output(); } - private function imageToBase64($path) { - $path = $path; + public function savePDF(PrinterQueue $printQueue, string $path) + { + $mpdf = new Mpdf(['mode' => 'utf-8', 'format' => 'A4']); + $htmlContent = $printQueue->getView() ?: '

محتوای PDF در دسترس نیست.

'; + $mpdf->WriteHTML($htmlContent); + $mpdf->Output($path, \Mpdf\Output\Destination::FILE); + } + + private function imageToBase64($path) + { + if (!file_exists($path)) { + return ''; + } $type = pathinfo($path, PATHINFO_EXTENSION); $data = file_get_contents($path); - $base64 = 'data:image/' . $type . ';base64,' . base64_encode($data); - return $base64; + return 'data:image/' . $type . ';base64,' . base64_encode($data); } -} \ No newline at end of file +}