From ca043a913f8eefa043e7a2c59d8ea900fd8151ff Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Sun, 17 Aug 2025 17:34:52 +0000 Subject: [PATCH] add some widgets about cheque to dashboard --- .../migrations/Version20250101000000.php | 37 +++ .../src/Controller/ChequeController.php | 131 +++++++++ .../src/Controller/DashboardController.php | 5 + hesabixCore/src/Entity/DashboardSettings.php | 75 +++++ hesabixCore/src/Service/Explore.php | 17 +- .../widgets/ChequesDueSoonWidget.vue | 173 ++++++++++++ .../widgets/ChequesDueTodayWidget.vue | 116 ++++++++ .../widgets/ChequesMonthlyChart.vue | 259 ++++++++++++++++++ .../components/widgets/ChequesStatusChart.vue | 237 ++++++++++++++++ .../widgets/ChequesSummaryWidget.vue | 99 +++++++ webUI/src/views/acc/cheque/list.vue | 2 +- webUI/src/views/acc/dashboard.vue | 46 ++++ 12 files changed, 1195 insertions(+), 2 deletions(-) create mode 100644 hesabixCore/migrations/Version20250101000000.php create mode 100644 webUI/src/components/widgets/ChequesDueSoonWidget.vue create mode 100644 webUI/src/components/widgets/ChequesDueTodayWidget.vue create mode 100644 webUI/src/components/widgets/ChequesMonthlyChart.vue create mode 100644 webUI/src/components/widgets/ChequesStatusChart.vue create mode 100644 webUI/src/components/widgets/ChequesSummaryWidget.vue diff --git a/hesabixCore/migrations/Version20250101000000.php b/hesabixCore/migrations/Version20250101000000.php new file mode 100644 index 0000000..c43d8c0 --- /dev/null +++ b/hesabixCore/migrations/Version20250101000000.php @@ -0,0 +1,37 @@ +addSql('ALTER TABLE dashboard_settings ADD cheques TINYINT(1) DEFAULT NULL'); + $this->addSql('ALTER TABLE dashboard_settings ADD cheques_due_today TINYINT(1) DEFAULT NULL'); + $this->addSql('ALTER TABLE dashboard_settings ADD cheques_status_chart TINYINT(1) DEFAULT NULL'); + $this->addSql('ALTER TABLE dashboard_settings ADD cheques_monthly_chart TINYINT(1) DEFAULT NULL'); + $this->addSql('ALTER TABLE dashboard_settings ADD cheques_due_soon TINYINT(1) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE dashboard_settings DROP cheques'); + $this->addSql('ALTER TABLE dashboard_settings DROP cheques_due_today'); + $this->addSql('ALTER TABLE dashboard_settings DROP cheques_status_chart'); + $this->addSql('ALTER TABLE dashboard_settings DROP cheques_monthly_chart'); + $this->addSql('ALTER TABLE dashboard_settings DROP cheques_due_soon'); + } +} \ No newline at end of file diff --git a/hesabixCore/src/Controller/ChequeController.php b/hesabixCore/src/Controller/ChequeController.php index 33e1769..424b902 100644 --- a/hesabixCore/src/Controller/ChequeController.php +++ b/hesabixCore/src/Controller/ChequeController.php @@ -892,4 +892,135 @@ class ChequeController extends AbstractController 'result' => 'ok' ]); } + + #[Route('/api/cheque/dashboard/stats', name: 'app_cheque_dashboard_stats')] + public function app_cheque_dashboard_stats(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, Jdate $jdate): JsonResponse + { + $acc = $access->hasRole('cheque'); + if (!$acc) + throw $this->createAccessDeniedException(); + + $money = $acc['money']; + $defaultMoey = $acc['bid']->getMoney(); + $defMoney = false; + if ($defaultMoey->getId() == $money->getId()) { + $defMoney = true; + } + + // آمار کلی چک‌ها + $qb = $entityManager->createQueryBuilder(); + $totalInputCheques = $qb->select('COUNT(c.id)') + ->from(Cheque::class, 'c') + ->where('c.bid = :bid') + ->andWhere('c.type = :type') + ->andWhere($defMoney ? '(c.money = :money OR c.money IS NULL)' : 'c.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('type', 'input') + ->setParameter('money', $money) + ->getQuery() + ->getSingleScalarResult(); + + $qb = $entityManager->createQueryBuilder(); + $totalOutputCheques = $qb->select('COUNT(c.id)') + ->from(Cheque::class, 'c') + ->where('c.bid = :bid') + ->andWhere('c.type = :type') + ->andWhere($defMoney ? '(c.money = :money OR c.money IS NULL)' : 'c.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('type', 'output') + ->setParameter('money', $money) + ->getQuery() + ->getSingleScalarResult(); + + // چک‌های سررسید امروز + $today = $jdate->jdate('Y/m/d', time()); + $qb = $entityManager->createQueryBuilder(); + $todayDueCheques = $qb->select('c') + ->from(Cheque::class, 'c') + ->where('c.bid = :bid') + ->andWhere($defMoney ? '(c.money = :money OR c.money IS NULL)' : 'c.money = :money') + ->andWhere('c.date = :today') + ->andWhere('c.status != :rejected') + ->setParameter('bid', $acc['bid']) + ->setParameter('money', $money) + ->setParameter('today', $today) + ->setParameter('rejected', 'برگشت خورده') + ->getQuery() + ->getResult(); + + // چک‌های نزدیک به سررسید (7 روز آینده) + $endDate = $jdate->jdate('Y/m/d', strtotime('+7 days')); + $qb = $entityManager->createQueryBuilder(); + $soonDueCheques = $qb->select('c') + ->from(Cheque::class, 'c') + ->where('c.bid = :bid') + ->andWhere($defMoney ? '(c.money = :money OR c.money IS NULL)' : 'c.money = :money') + ->andWhere('c.date >= :start') + ->andWhere('c.date <= :end') + ->andWhere('c.status != :rejected') + ->setParameter('bid', $acc['bid']) + ->setParameter('money', $money) + ->setParameter('start', $today) + ->setParameter('end', $endDate) + ->setParameter('rejected', 'برگشت خورده') + ->orderBy('c.date', 'ASC') + ->getQuery() + ->getResult(); + + // آمار وضعیت چک‌ها + $qb = $entityManager->createQueryBuilder(); + $statusStats = $qb->select('c.status, COUNT(c.id) as count, SUM(c.amount) as total_amount') + ->from(Cheque::class, 'c') + ->where('c.bid = :bid') + ->andWhere($defMoney ? '(c.money = :money OR c.money IS NULL)' : 'c.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('money', $money) + ->groupBy('c.status') + ->getQuery() + ->getResult(); + + // آمار ماهانه چک‌ها (6 ماه اخیر) - ساده‌سازی شده + $sixMonthsAgo = $jdate->jdate('Y/m', strtotime('-6 months')) . '/01'; + $qb = $entityManager->createQueryBuilder(); + $allCheques = $qb->select('c.date, c.type, c.amount') + ->from(Cheque::class, 'c') + ->where('c.bid = :bid') + ->andWhere($defMoney ? '(c.money = :money OR c.money IS NULL)' : 'c.money = :money') + ->andWhere('c.date >= :sixMonthsAgo') + ->setParameter('bid', $acc['bid']) + ->setParameter('money', $money) + ->setParameter('sixMonthsAgo', $sixMonthsAgo) + ->getQuery() + ->getResult(); + + // پردازش داده‌های ماهانه + $processedMonthlyStats = []; + foreach ($allCheques as $cheque) { + $month = substr($cheque['date'], 0, 7); // YYYY/MM + $key = $month . '_' . $cheque['type']; + + if (!isset($processedMonthlyStats[$key])) { + $processedMonthlyStats[$key] = [ + 'month' => $month, + 'type' => $cheque['type'], + 'count' => 0, + 'total_amount' => 0 + ]; + } + + $processedMonthlyStats[$key]['count']++; + $processedMonthlyStats[$key]['total_amount'] += (float)($cheque['amount'] ?? 0); + } + + $monthlyStats = array_values($processedMonthlyStats); + + return $this->json([ + 'totalInputCheques' => $totalInputCheques, + 'totalOutputCheques' => $totalOutputCheques, + 'todayDueCheques' => Explore::SerializeCheques($todayDueCheques), + 'soonDueCheques' => Explore::SerializeCheques($soonDueCheques), + 'statusStats' => $statusStats, + 'monthlyStats' => $monthlyStats + ]); + } } diff --git a/hesabixCore/src/Controller/DashboardController.php b/hesabixCore/src/Controller/DashboardController.php index fd64da5..68c4a6e 100644 --- a/hesabixCore/src/Controller/DashboardController.php +++ b/hesabixCore/src/Controller/DashboardController.php @@ -68,6 +68,11 @@ class DashboardController extends AbstractController if(array_key_exists('topCostCenters',$params)) $setting->setTopCostCenters($params['topCostCenters']); if(array_key_exists('incomes',$params)) $setting->setIncomes($params['incomes']); if(array_key_exists('topIncomeCenters',$params)) $setting->setTopIncomesChart($params['topIncomeCenters']); + if(array_key_exists('cheques',$params)) $setting->setCheques($params['cheques']); + if(array_key_exists('chequesDueToday',$params)) $setting->setChequesDueToday($params['chequesDueToday']); + if(array_key_exists('chequesStatusChart',$params)) $setting->setChequesStatusChart($params['chequesStatusChart']); + if(array_key_exists('chequesMonthlyChart',$params)) $setting->setChequesMonthlyChart($params['chequesMonthlyChart']); + if(array_key_exists('chequesDueSoon',$params)) $setting->setChequesDueSoon($params['chequesDueSoon']); $entityManagerInterface->persist($setting); $entityManagerInterface->flush(); diff --git a/hesabixCore/src/Entity/DashboardSettings.php b/hesabixCore/src/Entity/DashboardSettings.php index 104fa4b..117467a 100644 --- a/hesabixCore/src/Entity/DashboardSettings.php +++ b/hesabixCore/src/Entity/DashboardSettings.php @@ -66,6 +66,21 @@ class DashboardSettings #[ORM\Column(nullable: true)] private ?bool $topIncomesChart = null; + #[ORM\Column(nullable: true)] + private ?bool $cheques = null; + + #[ORM\Column(nullable: true)] + private ?bool $chequesDueToday = null; + + #[ORM\Column(nullable: true)] + private ?bool $chequesStatusChart = null; + + #[ORM\Column(nullable: true)] + private ?bool $chequesMonthlyChart = null; + + #[ORM\Column(nullable: true)] + private ?bool $chequesDueSoon = null; + public function getId(): ?int { return $this->id; @@ -274,4 +289,64 @@ class DashboardSettings return $this; } + + public function isCheques(): ?bool + { + return $this->cheques; + } + + public function setCheques(?bool $cheques): static + { + $this->cheques = $cheques; + + return $this; + } + + public function isChequesDueToday(): ?bool + { + return $this->chequesDueToday; + } + + public function setChequesDueToday(?bool $chequesDueToday): static + { + $this->chequesDueToday = $chequesDueToday; + + return $this; + } + + public function isChequesStatusChart(): ?bool + { + return $this->chequesStatusChart; + } + + public function setChequesStatusChart(?bool $chequesStatusChart): static + { + $this->chequesStatusChart = $chequesStatusChart; + + return $this; + } + + public function isChequesMonthlyChart(): ?bool + { + return $this->chequesMonthlyChart; + } + + public function setChequesMonthlyChart(?bool $chequesMonthlyChart): static + { + $this->chequesMonthlyChart = $chequesMonthlyChart; + + return $this; + } + + public function isChequesDueSoon(): ?bool + { + return $this->chequesDueSoon; + } + + public function setChequesDueSoon(?bool $chequesDueSoon): static + { + $this->chequesDueSoon = $chequesDueSoon; + + return $this; + } } diff --git a/hesabixCore/src/Service/Explore.php b/hesabixCore/src/Service/Explore.php index afaafe0..751fa77 100644 --- a/hesabixCore/src/Service/Explore.php +++ b/hesabixCore/src/Service/Explore.php @@ -326,7 +326,7 @@ class Explore 'id' => $person->getId(), 'code' => $person->getCode(), 'nikename' => $person->getNikename(), - 'name' => $person->getName(), + 'name' => $person->getName() ?: $person->getNikename(), 'tel' => $person->getTel(), 'mobile' => $person->getmobile(), 'mobile2' => $person->getMobile2(), @@ -633,6 +633,11 @@ class Explore 'topCostCenters' => $item->isTopCostCenters(), 'incomes' => $item->isIncomes(), 'topIncomeCenters' => $item->isTopIncomesChart(), + 'cheques' => $item->isCheques(), + 'chequesDueToday' => $item->isChequesDueToday(), + 'chequesStatusChart' => $item->isChequesStatusChart(), + 'chequesMonthlyChart' => $item->isChequesMonthlyChart(), + 'chequesDueSoon' => $item->isChequesDueSoon(), ]; if ($result['topCommodities'] === null) $result['topCommodities'] = true; @@ -664,6 +669,16 @@ class Explore $result['incomes'] = true; if ($result['topIncomeCenters'] === null) $result['topIncomeCenters'] = true; + if ($result['cheques'] === null) + $result['cheques'] = true; + if ($result['chequesDueToday'] === null) + $result['chequesDueToday'] = true; + if ($result['chequesStatusChart'] === null) + $result['chequesStatusChart'] = true; + if ($result['chequesMonthlyChart'] === null) + $result['chequesMonthlyChart'] = true; + if ($result['chequesDueSoon'] === null) + $result['chequesDueSoon'] = true; return $result; } diff --git a/webUI/src/components/widgets/ChequesDueSoonWidget.vue b/webUI/src/components/widgets/ChequesDueSoonWidget.vue new file mode 100644 index 0000000..74ae508 --- /dev/null +++ b/webUI/src/components/widgets/ChequesDueSoonWidget.vue @@ -0,0 +1,173 @@ + + + + + \ No newline at end of file diff --git a/webUI/src/components/widgets/ChequesDueTodayWidget.vue b/webUI/src/components/widgets/ChequesDueTodayWidget.vue new file mode 100644 index 0000000..18b2b7c --- /dev/null +++ b/webUI/src/components/widgets/ChequesDueTodayWidget.vue @@ -0,0 +1,116 @@ + + + + + \ No newline at end of file diff --git a/webUI/src/components/widgets/ChequesMonthlyChart.vue b/webUI/src/components/widgets/ChequesMonthlyChart.vue new file mode 100644 index 0000000..39a6647 --- /dev/null +++ b/webUI/src/components/widgets/ChequesMonthlyChart.vue @@ -0,0 +1,259 @@ + + + + + \ No newline at end of file diff --git a/webUI/src/components/widgets/ChequesStatusChart.vue b/webUI/src/components/widgets/ChequesStatusChart.vue new file mode 100644 index 0000000..8aba679 --- /dev/null +++ b/webUI/src/components/widgets/ChequesStatusChart.vue @@ -0,0 +1,237 @@ + + + + + \ No newline at end of file diff --git a/webUI/src/components/widgets/ChequesSummaryWidget.vue b/webUI/src/components/widgets/ChequesSummaryWidget.vue new file mode 100644 index 0000000..887d3d5 --- /dev/null +++ b/webUI/src/components/widgets/ChequesSummaryWidget.vue @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file diff --git a/webUI/src/views/acc/cheque/list.vue b/webUI/src/views/acc/cheque/list.vue index 77d23b3..cbe9e70 100755 --- a/webUI/src/views/acc/cheque/list.vue +++ b/webUI/src/views/acc/cheque/list.vue @@ -16,7 +16,7 @@ mdi-file-import - چک‌های واگذار شده + چک‌های پرداختی diff --git a/webUI/src/views/acc/dashboard.vue b/webUI/src/views/acc/dashboard.vue index 804ad0c..3878724 100755 --- a/webUI/src/views/acc/dashboard.vue +++ b/webUI/src/views/acc/dashboard.vue @@ -55,6 +55,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -235,6 +262,10 @@ hide-details inset class="text-caption" /> + + @@ -257,6 +288,11 @@ import SaleChart from "./component/widgets/saleChart.vue"; import TopCommoditiesChart from '@/components/widgets/TopCommoditiesChart.vue'; import TopCostCentersChart from '@/components/widgets/TopCostCentersChart.vue'; import TopIncomeCentersChart from '@/components/widgets/TopIncomeCentersChart.vue'; +import ChequesSummaryWidget from '@/components/widgets/ChequesSummaryWidget.vue'; +import ChequesDueTodayWidget from '@/components/widgets/ChequesDueTodayWidget.vue'; +import ChequesStatusChart from '@/components/widgets/ChequesStatusChart.vue'; +import ChequesMonthlyChart from '@/components/widgets/ChequesMonthlyChart.vue'; +import ChequesDueSoonWidget from '@/components/widgets/ChequesDueSoonWidget.vue'; export default { name: "dashboard", @@ -265,6 +301,11 @@ export default { TopCommoditiesChart, TopCostCentersChart, TopIncomeCentersChart, + ChequesSummaryWidget, + ChequesDueTodayWidget, + ChequesStatusChart, + ChequesMonthlyChart, + ChequesDueSoonWidget, }, data() { const self = this; @@ -294,6 +335,11 @@ export default { topCostCenters: false, incomes: false, topIncomeCenters: false, + cheques: false, + chequesDueToday: false, + chequesStatusChart: false, + chequesMonthlyChart: false, + chequesDueSoon: false, }, }; },