From 65d45e73d10e9e2c3116e192ebe7e3e4490a1bd8 Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Wed, 26 Mar 2025 18:10:38 +0000 Subject: [PATCH] bug fix in cost controller list --- hesabixCore/src/Controller/CostController.php | 350 +++++++------ webUI/package.json | 6 +- webUI/src/components/forms/Hdatepicker.vue | 138 +++-- webUI/src/i18n/calendarLocalConfig.ts | 1 - webUI/src/i18n/i18n.ts | 2 +- webUI/src/main.ts | 11 +- webUI/src/views/acc/App.vue | 26 +- webUI/src/views/acc/accounting/mod.vue | 475 +++++++++--------- webUI/src/views/acc/costs/list.vue | 366 +++++++------- 9 files changed, 746 insertions(+), 629 deletions(-) diff --git a/hesabixCore/src/Controller/CostController.php b/hesabixCore/src/Controller/CostController.php index 322a4c9..21a797d 100644 --- a/hesabixCore/src/Controller/CostController.php +++ b/hesabixCore/src/Controller/CostController.php @@ -166,160 +166,186 @@ class CostController extends AbstractController #[Route('/api/cost/list/search', name: 'app_cost_list_search', methods: ['POST'])] public function searchCostList( - Request $request, - Access $access, - EntityManagerInterface $entityManager, - Jdate $jdate + Request $request, + Access $access, + EntityManagerInterface $entityManager, + Jdate $jdate ): JsonResponse { - $acc = $access->hasRole('cost'); - if (!$acc) { - throw $this->createAccessDeniedException(); - } - - $params = json_decode($request->getContent(), true) ?? []; - - // پارامترهای ورودی - $filters = $params['filters'] ?? []; - $pagination = $params['pagination'] ?? ['page' => 1, 'limit' => 10]; - $sort = $params['sort'] ?? ['sortBy' => 'id', 'sortDesc' => true]; - $type = $params['type'] ?? 'cost'; - - // تنظیم پارامترهای صفحه‌بندی - $page = max(1, $pagination['page'] ?? 1); - $limit = max(1, min(100, $pagination['limit'] ?? 10)); - - // ساخت کوئری پایه - $queryBuilder = $entityManager->createQueryBuilder() - ->select('DISTINCT d.id, d.dateSubmit, d.date, d.type, d.code, d.des, d.amount') - ->addSelect('u.fullName as submitter') - ->from('App\Entity\HesabdariDoc', 'd') - ->leftJoin('d.submitter', 'u') - ->where('d.bid = :bid') - ->andWhere('d.year = :year') - ->andWhere('d.type = :type') - ->andWhere('d.money = :money') - ->setParameter('bid', $acc['bid']) - ->setParameter('year', $acc['year']) - ->setParameter('type', $type) - ->setParameter('money', $acc['money']); - - // اعمال فیلترها - if (!empty($filters)) { - if (isset($filters['search'])) { - $queryBuilder->leftJoin('d.hesabdariRows', 'r') - ->leftJoin('r.person', 'p') - ->leftJoin('r.ref', 't') - ->andWhere( - $queryBuilder->expr()->orX( - 'd.code LIKE :search', - 'd.des LIKE :search', - 'd.date LIKE :search', - 'd.amount LIKE :search', - 'p.nikename LIKE :search', - 't.name LIKE :search' - ) - ) - ->setParameter('search', "%{$filters['search']}%"); - } - - if (isset($filters['dateFrom'])) { - $queryBuilder->andWhere('d.date >= :dateFrom') - ->setParameter('dateFrom', $filters['dateFrom']); - } - - if (isset($filters['dateTo'])) { - $queryBuilder->andWhere('d.date <= :dateTo') - ->setParameter('dateTo', $filters['dateTo']); - } - - if (isset($filters['amount'])) { - $queryBuilder->andWhere('d.amount = :amount') - ->setParameter('amount', $filters['amount']); - } - } - - // اعمال مرتب‌سازی - $sortField = $sort['sortBy'] ?? 'id'; - $sortDirection = ($sort['sortDesc'] ?? true) ? 'DESC' : 'ASC'; - $queryBuilder->orderBy("d.$sortField", $sortDirection); - - // محاسبه تعداد کل نتایج - $totalItemsQuery = clone $queryBuilder; - $totalItems = $totalItemsQuery->select('COUNT(DISTINCT d.id)') - ->getQuery() - ->getSingleScalarResult(); - - // اعمال صفحه‌بندی - $queryBuilder->setFirstResult(($page - 1) * $limit) - ->setMaxResults($limit); - - $docs = $queryBuilder->getQuery()->getArrayResult(); - - $dataTemp = []; - foreach ($docs as $doc) { - $item = [ - 'id' => $doc['id'], - 'dateSubmit' => $doc['dateSubmit'], - 'date' => $doc['date'], - 'type' => $doc['type'], - 'code' => $doc['code'], - 'des' => $doc['des'], - 'amount' => $doc['amount'], - 'submitter' => $doc['submitter'] - ]; - - // دریافت اطلاعات مرکز هزینه و مبلغ - $costDetails = $entityManager->createQueryBuilder() - ->select('t.name as center_name, r.bd as amount') - ->from('App\Entity\HesabdariRow', 'r') - ->join('r.ref', 't') - ->where('r.doc = :docId') - ->andWhere('r.bd != 0') - ->setParameter('docId', $doc['id']) - ->getQuery() - ->getResult(); - - $item['costCenters'] = array_map(function($detail) { - return [ - 'name' => $detail['center_name'], - 'amount' => (int) $detail['amount'] - ]; - }, $costDetails); - - // دریافت اطلاعات شخص مرتبط - $personInfo = $entityManager->createQueryBuilder() - ->select('p.id, p.nikename, p.code') - ->from('App\Entity\HesabdariRow', 'r') - ->join('r.person', 'p') - ->where('r.doc = :docId') - ->andWhere('r.person IS NOT NULL') - ->setParameter('docId', $doc['id']) - ->setMaxResults(1) - ->getQuery() - ->getOneOrNullResult(); - - $item['person'] = $personInfo ? [ - 'id' => $personInfo['id'], - 'nikename' => $personInfo['nikename'], - 'code' => $personInfo['code'] - ] : null; - - $dataTemp[] = $item; - } - - return $this->json([ - 'items' => $dataTemp, - 'total' => (int) $totalItems, - 'page' => $page, - 'limit' => $limit - ]); + $acc = $access->hasRole('cost'); + if (!$acc) { + throw $this->createAccessDeniedException(); + } + + $params = json_decode($request->getContent(), true) ?? []; + + // پارامترهای ورودی + $filters = $params['filters'] ?? []; + $pagination = $params['pagination'] ?? ['page' => 1, 'limit' => 10]; + $sort = $params['sort'] ?? ['sortBy' => 'id', 'sortDesc' => true]; + $type = $params['type'] ?? 'cost'; + + // تنظیم پارامترهای صفحه‌بندی + $page = max(1, $pagination['page'] ?? 1); + $limit = max(1, min(100, $pagination['limit'] ?? 10)); + + // ساخت کوئری پایه + $queryBuilder = $entityManager->createQueryBuilder() + ->select('DISTINCT d.id, d.dateSubmit, d.date, d.type, d.code, d.des, d.amount') + ->addSelect('u.fullName as submitter') + ->from('App\Entity\HesabdariDoc', 'd') + ->leftJoin('d.submitter', 'u') + ->where('d.bid = :bid') + ->andWhere('d.year = :year') + ->andWhere('d.type = :type') + ->andWhere('d.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('year', $acc['year']) + ->setParameter('type', $type) + ->setParameter('money', $acc['money']); + + // اعمال فیلترها + if (!empty($filters)) { + // جستجوی متنی + if (isset($filters['search'])) { + $searchValue = is_array($filters['search']) ? $filters['search']['value'] : $filters['search']; + $queryBuilder->leftJoin('d.hesabdariRows', 'r') + ->leftJoin('r.person', 'p') + ->leftJoin('r.ref', 't') + ->andWhere( + $queryBuilder->expr()->orX( + 'd.code LIKE :search', + 'd.des LIKE :search', + 'd.date LIKE :search', + 'd.amount LIKE :search', + 'p.nikename LIKE :search', + 't.name LIKE :search' + ) + ) + ->setParameter('search', "%{$searchValue}%"); + } + + // فیلتر زمانی + if (isset($filters['timeFilter'])) { + $today = $jdate->jdate('Y/m/d', time()); + switch ($filters['timeFilter']) { + case 'today': + $queryBuilder->andWhere('d.date = :today') + ->setParameter('today', $today); + break; + case 'week': + $weekStart = $jdate->jdate('Y/m/d', strtotime('-6 days')); + $queryBuilder->andWhere('d.date BETWEEN :weekStart AND :today') + ->setParameter('weekStart', $weekStart) + ->setParameter('today', $today); + break; + case 'month': + $monthStart = $jdate->jdate('Y/m/01', time()); + $queryBuilder->andWhere('d.date BETWEEN :monthStart AND :today') + ->setParameter('monthStart', $monthStart) + ->setParameter('today', $today); + break; + case 'custom': + if (isset($filters['dateFrom']) && isset($filters['dateTo'])) { + $queryBuilder->andWhere('d.date BETWEEN :dateFrom AND :dateTo') + ->setParameter('dateFrom', $filters['dateFrom']) + ->setParameter('dateTo', $filters['dateTo']); + } + break; + case 'all': + default: + // بدون فیلتر زمانی اضافه + break; + } + } + + if (isset($filters['amount'])) { + $queryBuilder->andWhere('d.amount = :amount') + ->setParameter('amount', $filters['amount']); + } + } + + // اعمال مرتب‌سازی + $sortField = is_array($sort['sortBy']) ? ($sort['sortBy']['key'] ?? 'id') : ($sort['sortBy'] ?? 'id'); + $sortDirection = ($sort['sortDesc'] ?? true) ? 'DESC' : 'ASC'; + $queryBuilder->orderBy("d.$sortField", $sortDirection); + + // محاسبه تعداد کل نتایج + $totalItemsQuery = clone $queryBuilder; + $totalItems = $totalItemsQuery->select('COUNT(DISTINCT d.id)') + ->getQuery() + ->getSingleScalarResult(); + + // اعمال صفحه‌بندی + $queryBuilder->setFirstResult(($page - 1) * $limit) + ->setMaxResults($limit); + + $docs = $queryBuilder->getQuery()->getArrayResult(); + + $dataTemp = []; + foreach ($docs as $doc) { + $item = [ + 'id' => $doc['id'], + 'dateSubmit' => $doc['dateSubmit'], + 'date' => $doc['date'], + 'type' => $doc['type'], + 'code' => $doc['code'], + 'des' => $doc['des'], + 'amount' => $doc['amount'], + 'submitter' => $doc['submitter'], + ]; + + // دریافت اطلاعات مرکز هزینه و مبلغ + $costDetails = $entityManager->createQueryBuilder() + ->select('t.name as center_name, r.bd as amount') + ->from('App\Entity\HesabdariRow', 'r') + ->join('r.ref', 't') + ->where('r.doc = :docId') + ->andWhere('r.bd != 0') + ->setParameter('docId', $doc['id']) + ->getQuery() + ->getResult(); + + $item['costCenters'] = array_map(function ($detail) { + return [ + 'name' => $detail['center_name'], + 'amount' => (int) $detail['amount'], + ]; + }, $costDetails); + + // دریافت اطلاعات شخص مرتبط + $personInfo = $entityManager->createQueryBuilder() + ->select('p.id, p.nikename, p.code') + ->from('App\Entity\HesabdariRow', 'r') + ->join('r.person', 'p') + ->where('r.doc = :docId') + ->andWhere('r.person IS NOT NULL') + ->setParameter('docId', $doc['id']) + ->setMaxResults(1 ) + ->getQuery() + ->getOneOrNullResult(); + + $item['person'] = $personInfo ? [ + 'id' => $personInfo['id'], + 'nikename' => $personInfo['nikename'], + 'code' => $personInfo['code'], + ] : null; + + $dataTemp[] = $item; + } + + return $this->json([ + 'items' => $dataTemp, + 'total' => (int) $totalItems, + 'page' => $page, + 'limit' => $limit, + ]); } #[Route('/api/costs/list/print', name: 'app_costs_list_print')] public function app_costs_list_print( - Provider $provider, - Request $request, - Access $access, + Provider $provider, + Request $request, + Access $access, EntityManagerInterface $entityManager ): JsonResponse { $acc = $access->hasRole('cost'); @@ -328,7 +354,7 @@ class CostController extends AbstractController } $params = json_decode($request->getContent(), true) ?? []; - + // دریافت آیتم‌های انتخاب شده یا همه آیتم‌ها if (!isset($params['items'])) { $items = $entityManager->getRepository(HesabdariDoc::class)->findBy([ @@ -411,12 +437,12 @@ class CostController extends AbstractController // تنظیم هدرها $sheet->setCellValue('A1', 'ردیف') - ->setCellValue('B1', 'شماره سند') - ->setCellValue('C1', 'تاریخ') - ->setCellValue('D1', 'شرح') - ->setCellValue('E1', 'مرکز هزینه') - ->setCellValue('F1', 'مرکز پرداخت') - ->setCellValue('G1', 'مبلغ (ریال)'); + ->setCellValue('B1', 'شماره سند') + ->setCellValue('C1', 'تاریخ') + ->setCellValue('D1', 'شرح') + ->setCellValue('E1', 'مرکز هزینه') + ->setCellValue('F1', 'مرکز پرداخت') + ->setCellValue('G1', 'مبلغ (ریال)'); // پر کردن داده‌ها $rowNumber = 2; @@ -449,12 +475,12 @@ class CostController extends AbstractController } $sheet->setCellValue('A' . $rowNumber, $index + 1) - ->setCellValue('B' . $rowNumber, $item->getCode()) - ->setCellValue('C' . $rowNumber, $item->getDate()) - ->setCellValue('D' . $rowNumber, $item->getDes()) - ->setCellValue('E' . $rowNumber, $costCenterNames) - ->setCellValue('F' . $rowNumber, $paymentCenter) - ->setCellValue('G' . $rowNumber, number_format($item->getAmount())); + ->setCellValue('B' . $rowNumber, $item->getCode()) + ->setCellValue('C' . $rowNumber, $item->getDate()) + ->setCellValue('D' . $rowNumber, $item->getDes()) + ->setCellValue('E' . $rowNumber, $costCenterNames) + ->setCellValue('F' . $rowNumber, $paymentCenter) + ->setCellValue('G' . $rowNumber, number_format($item->getAmount())); $rowNumber++; } diff --git a/webUI/package.json b/webUI/package.json index 38a290c..f2bb785 100644 --- a/webUI/package.json +++ b/webUI/package.json @@ -15,6 +15,7 @@ "@ckeditor/ckeditor5-image": "^36.0.1", "@ckeditor/ckeditor5-vue": "^4.0.1", "@ckeditor/vite-plugin-ckeditor5": "^0.1.1", + "@date-io/date-fns-jalali": "^3.2.0", "@mdi/font": "^7.4.47", "@syncfusion/ej2-vue-dropdowns": "^21.2.5", "@vuelidate/core": "^2.0.0", @@ -23,8 +24,11 @@ "animate.css": "^4.1.1", "apexcharts": "^4.4.0", "axios": "^1.2.3", + "date-fns": "^4.1.0", + "date-fns-jalali": "^3.2.0-0", "downloadjs": "^1.4.7", "file-saver": "^2.0.5", + "jalali-moment": "^3.3.11", "libphonenumber-js": "^1.10.44", "lodash": "^4.17.21", "maska": "^3.0.4", @@ -66,4 +70,4 @@ "vue-tsc": "^1.0.12" }, "build:pwa": "vue-cli-service build && workbox generateSW workbox-config.js" -} \ No newline at end of file +} diff --git a/webUI/src/components/forms/Hdatepicker.vue b/webUI/src/components/forms/Hdatepicker.vue index 5ee16a0..1e4874a 100644 --- a/webUI/src/components/forms/Hdatepicker.vue +++ b/webUI/src/components/forms/Hdatepicker.vue @@ -1,53 +1,95 @@ - - - \ No newline at end of file diff --git a/webUI/src/i18n/calendarLocalConfig.ts b/webUI/src/i18n/calendarLocalConfig.ts index d229fd5..47a18e3 100644 --- a/webUI/src/i18n/calendarLocalConfig.ts +++ b/webUI/src/i18n/calendarLocalConfig.ts @@ -1,4 +1,3 @@ -import moment from 'moment' export default { data() { return { diff --git a/webUI/src/i18n/i18n.ts b/webUI/src/i18n/i18n.ts index 4673789..7db5008 100644 --- a/webUI/src/i18n/i18n.ts +++ b/webUI/src/i18n/i18n.ts @@ -13,7 +13,7 @@ if(activeLanguageCode == null || activeLanguageCode == undefined){ activeLanguageCode='fa'; } const i18n = createI18n({ - legacy: false, // Vuetify does not support the legacy mode of vue-i18n + legacy: false, locale: activeLanguageCode, fallbackLocale: activeLanguageCode, messages, diff --git a/webUI/src/main.ts b/webUI/src/main.ts index ce5a758..6cdc6f6 100644 --- a/webUI/src/main.ts +++ b/webUI/src/main.ts @@ -6,13 +6,13 @@ import "./registerServiceWorker"; import { vMaska } from "maska/vue" import VueApexCharts from "vue3-apexcharts"; import Uploader from 'vue-media-upload'; +import DateFnsJalaliAdapter from '@date-io/date-fns-jalali'; +import faIR from 'date-fns-jalali/locale/fa-IR'; //pinia import { createPinia } from 'pinia' const pinia = createPinia(); -import { VDateInput } from 'vuetify/labs/VDateInput' - import CKEditor from '@ckeditor/ckeditor5-vue'; // Import translations for the Persian language. import '@ckeditor/ckeditor5-build-classic/build/translations/fa'; @@ -124,6 +124,12 @@ const vuetify = createVuetify({ }, }, }, + date: { + adapter: DateFnsJalaliAdapter, + locale: { + fa:faIR // تنظیم زبان فارسی + }, + }, }); // @ts-ignore @@ -146,7 +152,6 @@ import 'vue-select/dist/vue-select.css'; app.component('v-cob', vSelect) import Hdatepicker from "@/components/forms/Hdatepicker.vue"; import calendarLocalConfig from "@/i18n/calendarLocalConfig"; - app.component('h-date-picker', Hdatepicker); app.use(CKEditor) app.use(Vue3PersianDatetimePicker, { diff --git a/webUI/src/views/acc/App.vue b/webUI/src/views/acc/App.vue index ee23336..2379ccc 100644 --- a/webUI/src/views/acc/App.vue +++ b/webUI/src/views/acc/App.vue @@ -333,7 +333,7 @@ export default { {{ $t('drawer.gets') }} {{ getShortcutKey('/acc/persons/receive/list') - }} + }}