From 9074059abd8c15a8e55afe1d7535c5d5ceed8ac0 Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Sun, 2 Nov 2025 10:08:30 +0000 Subject: [PATCH] some bug fixes --- .../src/Controller/BackupController.php | 33 +++++++- .../src/Controller/StoreroomController.php | 84 +++++++++++++++++++ .../pdf/printers/storeroom/exist.html.twig | 6 +- .../commodityCheck/checkByStoreroom.vue | 48 ++++++++++- 4 files changed, 166 insertions(+), 5 deletions(-) diff --git a/hesabixCore/src/Controller/BackupController.php b/hesabixCore/src/Controller/BackupController.php index b495448..c7c1380 100644 --- a/hesabixCore/src/Controller/BackupController.php +++ b/hesabixCore/src/Controller/BackupController.php @@ -244,6 +244,7 @@ class BackupController extends AbstractController 'قیمت خرید', 'قیمت فروش', 'موجودی', + 'ارزش اسمی', 'حداقل سفارش', 'توضیحات', 'تاریخ ثبت' @@ -257,9 +258,38 @@ class BackupController extends AbstractController // دریافت کالاها $commodities = $entityManager->getRepository(\App\Entity\Commodity::class)->findBy(['bid' => $business]); + + // محاسبه تجمیعی موجودی کالا بر اساس ردیف‌های حسابداری تاییدشده + $qb = $entityManager->createQueryBuilder(); + $qb->select('c.id AS cid, SUM(CASE WHEN d.type IN (:plusTypes) THEN COALESCE(r.commdityCount, 0) WHEN d.type IN (:minusTypes) THEN -COALESCE(r.commdityCount, 0) ELSE 0 END) AS stock') + ->from(\App\Entity\HesabdariRow::class, 'r') + ->join('r.doc', 'd') + ->join('r.commodity', 'c') + ->where('r.bid = :bid') + ->andWhere('d.isApproved = :approved') + ->groupBy('c.id') + ->setParameter('bid', $business) + ->setParameter('approved', true) + ->setParameter('plusTypes', ['buy', 'open_balance', 'rfsell']) + ->setParameter('minusTypes', ['sell', 'rfbuy']); + + $stockResults = $qb->getQuery()->getArrayResult(); + $stockMap = []; + foreach ($stockResults as $sr) { + $stockMap[(int)$sr['cid']] = (float)$sr['stock']; + } $row = 2; foreach ($commodities as $commodity) { + // محاسبه موجودی و ارزش اسمی + $count = 0.0; + if (!$commodity->isKhadamat()) { + $count = $stockMap[$commodity->getId()] ?? 0.0; + } + $priceSell = $commodity->getPriceSell(); + $priceSell = is_numeric($priceSell) ? (float)$priceSell : 0.0; + $nominalValue = $count * $priceSell; + $data = [ $commodity->getName(), $commodity->getCode(), @@ -267,7 +297,8 @@ class BackupController extends AbstractController $commodity->getUnit() ? $commodity->getUnit()->getName() : '', $commodity->getPriceBuy(), $commodity->getPriceSell(), - '', // Entity Commodity فیلد موجودی ندارد + $count, + $nominalValue, $commodity->getMinOrderCount(), // حداقل سفارش $commodity->getDes(), '' // Entity Commodity فیلد تاریخ ثبت ندارد diff --git a/hesabixCore/src/Controller/StoreroomController.php b/hesabixCore/src/Controller/StoreroomController.php index bf5f976..751c116 100644 --- a/hesabixCore/src/Controller/StoreroomController.php +++ b/hesabixCore/src/Controller/StoreroomController.php @@ -28,6 +28,9 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Writer\Xlsx; +use Symfony\Component\HttpFoundation\StreamedResponse; class StoreroomController extends AbstractController { @@ -995,6 +998,87 @@ class StoreroomController extends AbstractController return $this->json(['id' => $pdfPid]); } + #[Route('/api/storeroom/exist/export', name: 'app_storeroom_exist_export', methods: ['POST'])] + public function app_storeroom_exist_export(Request $request, Access $access, EntityManagerInterface $entityManager): StreamedResponse + { + $params = []; + if ($content = $request->getContent()) { + $params = json_decode($content, true); + } + + $acc = $access->hasRole('store'); + if (!$acc) { + throw $this->createAccessDeniedException(); + } + + $storeroomId = $params['storeroom'] ?? null; + if (!$storeroomId) { + throw $this->createNotFoundException('انبار انتخاب نشده است'); + } + + $storeroom = $entityManager->getRepository(Storeroom::class)->find($storeroomId); + if (!$storeroom) { + throw $this->createNotFoundException('انبار مورد نظر یافت نشد'); + } + + $items = $params['items'] ?? []; + + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setRightToLeft(true); + + $headers = [ + 'کد', + 'دسته بندی', + 'نام', + 'واحد', + 'ورودی', + 'خروجی', + 'موجودی انبار', + 'نقطه سفارش', + 'وضعیت', + ]; + $sheet->fromArray($headers, null, 'A1'); + + $rowIndex = 2; + foreach ($items as $item) { + $commodity = $item['commodity'] ?? []; + $cat = $commodity['cat'] ?? []; + $unit = $commodity['unit'] ?? []; + $existCount = (int)($item['existCount'] ?? 0); + $orderPoint = (int)($commodity['orderPoint'] ?? 0); + $operation = $existCount < $orderPoint ? 'نیاز به شارژ انبار' : ''; + + $row = [ + $commodity['code'] ?? '', + $cat['name'] ?? '', + $commodity['name'] ?? '', + $unit['name'] ?? '', + $item['input'] ?? 0, + $item['output'] ?? 0, + $existCount, + $orderPoint, + $operation, + ]; + + $sheet->fromArray($row, null, 'A' . $rowIndex); + $rowIndex++; + } + + $fileName = 'موجودی_انبار_' . ($storeroom->getName() ?? 'store') . '.xlsx'; + + $response = new StreamedResponse(function () use ($spreadsheet) { + $writer = new Xlsx($spreadsheet); + $writer->save('php://output'); + }); + + $response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + $response->headers->set('Content-Disposition', 'attachment;filename="' . $fileName . '"'); + $response->headers->set('Cache-Control', 'max-age=0'); + + return $response; + } + #[Route('/api/storeroom/ticket/complete/{id}', name: 'app_storeroom_ticket_complete', methods: ['POST'])] public function app_storeroom_ticket_complete(string $id, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, PluginService $pluginService): JsonResponse { diff --git a/hesabixCore/templates/pdf/printers/storeroom/exist.html.twig b/hesabixCore/templates/pdf/printers/storeroom/exist.html.twig index 9d37bca..eb280fe 100644 --- a/hesabixCore/templates/pdf/printers/storeroom/exist.html.twig +++ b/hesabixCore/templates/pdf/printers/storeroom/exist.html.twig @@ -34,7 +34,7 @@

تاریخ: - {{ "now"|date("Y/m/d H:i") }}

+ {{ Jdate.jdate('Y/m/d H:i', 'now') }}

انبار: @@ -96,9 +96,9 @@ {{ loop.index }} {{ item.commodity.code }} - {{ item.commodity.cat.name }} + {{ item.commodity.cat is defined and item.commodity.cat is not null ? item.commodity.cat.name : '' }} {{ item.commodity.name }} - {{ item.commodity.unit.name }} + {{ item.commodity.unit is defined and item.commodity.unit is not null ? item.commodity.unit.name : '' }} {{ item.input }} {{ item.output }} diff --git a/webUI/src/views/acc/storeroom/commodityCheck/checkByStoreroom.vue b/webUI/src/views/acc/storeroom/commodityCheck/checkByStoreroom.vue index e8947be..42358bc 100755 --- a/webUI/src/views/acc/storeroom/commodityCheck/checkByStoreroom.vue +++ b/webUI/src/views/acc/storeroom/commodityCheck/checkByStoreroom.vue @@ -98,6 +98,44 @@ export default defineComponent({ }) } + const exportExcel = async () => { + if (!storeroom.value) { + alert('لطفا ابتدا انبار را انتخاب کنید') + return + } + loading.value = true + try { + const res = await axios.post('/api/storeroom/exist/export', { + storeroom: storeroom.value, + items: items.value, + }, { responseType: 'blob' }) + + const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) + const url = window.URL.createObjectURL(blob) + const link = document.createElement('a') + + const disposition = res.headers['content-disposition'] || res.headers['Content-Disposition'] + let filename = 'storeroom_exist.xlsx' + if (disposition) { + const match = disposition.match(/filename\*=UTF-8''([^;]+)|filename="?([^";]+)"?/) + const extracted = decodeURIComponent(match?.[1] || match?.[2] || '') + if (extracted) filename = extracted + } + + link.href = url + link.setAttribute('download', filename) + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + window.URL.revokeObjectURL(url) + } catch (error) { + console.error('خطا در خروجی اکسل:', error) + alert('خطا در خروجی اکسل') + } finally { + loading.value = false + } + } + watch(searchValue, filterItems) watch(showZeroTransactions, filterItems) onMounted(loadData) @@ -111,7 +149,8 @@ export default defineComponent({ headers, loadStoreItems, showZeroTransactions, - print + print, + exportExcel } } }) @@ -145,6 +184,13 @@ export default defineComponent({ + + +