diff --git a/hesabixCore/src/Controller/PersonsController.php b/hesabixCore/src/Controller/PersonsController.php index c1ed31d..76fbae6 100644 --- a/hesabixCore/src/Controller/PersonsController.php +++ b/hesabixCore/src/Controller/PersonsController.php @@ -897,10 +897,14 @@ class PersonsController extends AbstractController $queryBuilder->orderBy('d.des', $sortDirection); break; case 'persons': + // برای سورت بر اساس اشخاص + if (empty($search)) { $queryBuilder->leftJoin('d.hesabdariRows', 'hr_person') - ->leftJoin('hr_person.person', 'p') - ->groupBy('d.id, d.date, d.code, d.des, d.amount, p.nikename') - ->orderBy('p.nikename', $sortDirection); + ->leftJoin('hr_person.person', 'p'); + } + // استفاده از MIN برای جلوگیری از مشکل GROUP BY + $queryBuilder->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('MIN(p.nikename)', $sortDirection); break; case 'accounts': // سورت بر اساس طرف حساب‌ها (بانک، صندوق، تنخواه) @@ -908,8 +912,8 @@ class PersonsController extends AbstractController ->leftJoin('hr_account.bank', 'ba') ->leftJoin('hr_account.cashdesk', 'cd') ->leftJoin('hr_account.salary', 's') - ->groupBy('d.id, d.date, d.code, d.des, d.amount, ba.name, cd.name, s.name') - ->orderBy('COALESCE(ba.name, cd.name, s.name)', $sortDirection); + ->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('CASE WHEN ba.name IS NOT NULL THEN ba.name WHEN cd.name IS NOT NULL THEN cd.name WHEN s.name IS NOT NULL THEN s.name ELSE \'\' END', $sortDirection); break; default: $queryBuilder->orderBy('d.id', 'DESC'); @@ -923,8 +927,67 @@ class PersonsController extends AbstractController ->setParameter('ids', $ids); } - // دریافت تعداد کل رکوردها - $totalItems = $queryBuilder->select('COUNT(d.id)') + // دریافت تعداد کل رکوردها - ایجاد کوئری جدید بدون GROUP BY + $totalQueryBuilder = $entityManager->getRepository(HesabdariDoc::class)->createQueryBuilder('d') + ->where('d.bid = :bid') + ->andWhere('d.type = :type') + ->andWhere('d.year = :year') + ->andWhere('d.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('type', 'person_receive') + ->setParameter('year', $acc['year']) + ->setParameter('money', $acc['money']); + + // اعمال فیلترهای جست‌وجو و تاریخ + if (isset($params['search']) && !empty($params['search'])) { + $totalQueryBuilder->leftJoin('d.hesabdariRows', 'hr') + ->leftJoin('hr.person', 'p') + ->andWhere( + $totalQueryBuilder->expr()->orX( + 'd.code LIKE :search', + 'd.des LIKE :search', + 'p.nikename LIKE :search' + ) + )->setParameter('search', "%{$params['search']}%"); + } + + // فیلتر تاریخ + if (isset($params['dateFilter']) && $params['dateFilter'] !== 'all') { + $today = $jdate->GetTodayDate(); + switch ($params['dateFilter']) { + case 'today': + $totalQueryBuilder->andWhere('d.date = :today') + ->setParameter('today', $today); + break; + case 'thisWeek': + $dayOfWeek = (int) $jdate->jdate('w', time()); + $startOfWeek = $jdate->shamsiDate(0, 0, -$dayOfWeek); + $endOfWeek = $jdate->shamsiDate(0, 0, 6 - $dayOfWeek); + $totalQueryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfWeek) + ->setParameter('end', $endOfWeek); + break; + case 'thisMonth': + $currentYear = (int) $jdate->jdate('Y', time()); + $currentMonth = (int) $jdate->jdate('n', time()); + $daysInMonth = (int) $jdate->jdate('t', time()); + $startOfMonth = sprintf('%d/%02d/01', $currentYear, $currentMonth); + $endOfMonth = sprintf('%d/%02d/%02d', $currentYear, $currentMonth, $daysInMonth); + $totalQueryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfMonth) + ->setParameter('end', $endOfMonth); + break; + } + } + + // اگر آیتم‌های خاصی درخواست شده‌اند + if (array_key_exists('items', $params)) { + $ids = array_map(function($item) { return $item['id']; }, $params['items']); + $totalQueryBuilder->andWhere('d.id IN (:ids)') + ->setParameter('ids', $ids); + } + + $totalItems = (int) $totalQueryBuilder->select('COUNT(d.id)') ->getQuery() ->getSingleScalarResult(); @@ -1089,6 +1152,60 @@ class PersonsController extends AbstractController break; } + // محاسبه تعداد کل قبل از اعمال GROUP BY + $totalQueryBuilder = $entityManager->getRepository(HesabdariDoc::class)->createQueryBuilder('d') + ->where('d.bid = :bid') + ->andWhere('d.type = :type') + ->andWhere('d.year = :year') + ->andWhere('d.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('type', 'person_receive') + ->setParameter('year', $acc['year']) + ->setParameter('money', $acc['money']); + + // اعمال فیلترهای جست‌وجو و تاریخ برای کوئری تعداد + if (!empty($search)) { + $totalQueryBuilder->leftJoin('d.hesabdariRows', 'hr') + ->leftJoin('hr.person', 'p') + ->andWhere( + $totalQueryBuilder->expr()->orX( + 'd.code LIKE :search', + 'd.des LIKE :search', + 'p.nikename LIKE :search' + ) + )->setParameter('search', "%$search%"); + } + + // فیلتر تاریخ برای کوئری تعداد + switch ($dateFilter) { + case 'today': + $totalQueryBuilder->andWhere('d.date = :today') + ->setParameter('today', $today); + break; + case 'thisWeek': + $dayOfWeek = (int) $jdate->jdate('w', time()); + $startOfWeek = $jdate->shamsiDate(0, 0, -$dayOfWeek); + $endOfWeek = $jdate->shamsiDate(0, 0, 6 - $dayOfWeek); + $totalQueryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfWeek) + ->setParameter('end', $endOfWeek); + break; + case 'thisMonth': + $currentYear = (int) $jdate->jdate('Y', time()); + $currentMonth = (int) $jdate->jdate('n', time()); + $daysInMonth = (int) $jdate->jdate('t', time()); + $startOfMonth = sprintf('%d/%02d/01', $currentYear, $currentMonth); + $endOfMonth = sprintf('%d/%02d/%02d', $currentYear, $currentMonth, $daysInMonth); + $totalQueryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfMonth) + ->setParameter('end', $endOfMonth); + break; + } + + $total = (int) $totalQueryBuilder->select('COUNT(d.id)') + ->getQuery() + ->getSingleScalarResult(); + // اعمال سورت $sortDirection = $sortDesc ? 'DESC' : 'ASC'; switch ($sortBy) { @@ -1106,10 +1223,14 @@ class PersonsController extends AbstractController $queryBuilder->orderBy('d.des', $sortDirection); break; case 'persons': + // برای سورت بر اساس اشخاص + if (empty($search)) { $queryBuilder->leftJoin('d.hesabdariRows', 'hr_person') - ->leftJoin('hr_person.person', 'p') - ->groupBy('d.id, d.date, d.code, d.des, d.amount, p.nikename') - ->orderBy('p.nikename', $sortDirection); + ->leftJoin('hr_person.person', 'p'); + } + // استفاده از MIN برای جلوگیری از مشکل GROUP BY + $queryBuilder->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('MIN(p.nikename)', $sortDirection); break; case 'accounts': // سورت بر اساس طرف حساب‌ها (بانک، صندوق، تنخواه) @@ -1117,21 +1238,14 @@ class PersonsController extends AbstractController ->leftJoin('hr_account.bank', 'ba') ->leftJoin('hr_account.cashdesk', 'cd') ->leftJoin('hr_account.salary', 's') - ->groupBy('d.id, d.date, d.code, d.des, d.amount, ba.name, cd.name, s.name') - ->orderBy('COALESCE(ba.name, cd.name, s.name)', $sortDirection); + ->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('CASE WHEN ba.name IS NOT NULL THEN ba.name WHEN cd.name IS NOT NULL THEN cd.name WHEN s.name IS NOT NULL THEN s.name ELSE \'\' END', $sortDirection); break; default: $queryBuilder->orderBy('d.id', 'DESC'); break; } - // محاسبه تعداد کل - $totalQuery = (clone $queryBuilder) - ->select('COUNT(DISTINCT d.id) as total') - ->getQuery() - ->getSingleResult(); - $total = (int) $totalQuery['total']; - // گرفتن اسناد با صفحه‌بندی $docs = $queryBuilder ->setFirstResult(($page - 1) * $itemsPerPage) @@ -1754,10 +1868,14 @@ class PersonsController extends AbstractController $queryBuilder->orderBy('d.des', $sortDirection); break; case 'persons': + // برای سورت بر اساس اشخاص + if (empty($search)) { $queryBuilder->leftJoin('d.hesabdariRows', 'hr_person') - ->leftJoin('hr_person.person', 'p') - ->groupBy('d.id, d.date, d.code, d.des, d.amount, p.nikename') - ->orderBy('p.nikename', $sortDirection); + ->leftJoin('hr_person.person', 'p'); + } + // استفاده از MIN برای جلوگیری از مشکل GROUP BY + $queryBuilder->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('MIN(p.nikename)', $sortDirection); break; case 'accounts': // سورت بر اساس طرف حساب‌ها (بانک، صندوق، تنخواه) @@ -1765,8 +1883,8 @@ class PersonsController extends AbstractController ->leftJoin('hr_account.bank', 'ba') ->leftJoin('hr_account.cashdesk', 'cd') ->leftJoin('hr_account.salary', 's') - ->groupBy('d.id, d.date, d.code, d.des, d.amount, ba.name, cd.name, s.name') - ->orderBy('COALESCE(ba.name, cd.name, s.name)', $sortDirection); + ->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('CASE WHEN ba.name IS NOT NULL THEN ba.name WHEN cd.name IS NOT NULL THEN cd.name WHEN s.name IS NOT NULL THEN s.name ELSE \'\' END', $sortDirection); break; default: $queryBuilder->orderBy('d.id', 'DESC'); @@ -1916,10 +2034,14 @@ class PersonsController extends AbstractController $queryBuilder->orderBy('d.des', $sortDirection); break; case 'persons': + // برای سورت بر اساس اشخاص + if (empty($search)) { $queryBuilder->leftJoin('d.hesabdariRows', 'hr_person') - ->leftJoin('hr_person.person', 'p') - ->groupBy('d.id, d.date, d.code, d.des, d.amount, p.nikename') - ->orderBy('p.nikename', $sortDirection); + ->leftJoin('hr_person.person', 'p'); + } + // استفاده از MIN برای جلوگیری از مشکل GROUP BY + $queryBuilder->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('MIN(p.nikename)', $sortDirection); break; case 'accounts': // سورت بر اساس طرف حساب‌ها (بانک، صندوق، تنخواه) @@ -1927,8 +2049,8 @@ class PersonsController extends AbstractController ->leftJoin('hr_account.bank', 'ba') ->leftJoin('hr_account.cashdesk', 'cd') ->leftJoin('hr_account.salary', 's') - ->groupBy('d.id, d.date, d.code, d.des, d.amount, ba.name, cd.name, s.name') - ->orderBy('COALESCE(ba.name, cd.name, s.name)', $sortDirection); + ->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('CASE WHEN ba.name IS NOT NULL THEN ba.name WHEN cd.name IS NOT NULL THEN cd.name WHEN s.name IS NOT NULL THEN s.name ELSE \'\' END', $sortDirection); break; default: $queryBuilder->orderBy('d.id', 'DESC'); @@ -1942,8 +2064,67 @@ class PersonsController extends AbstractController ->setParameter('ids', $ids); } - // دریافت تعداد کل رکوردها - $totalItems = $queryBuilder->select('COUNT(d.id)') + // دریافت تعداد کل رکوردها - ایجاد کوئری جدید بدون GROUP BY + $totalQueryBuilder = $entityManager->getRepository(HesabdariDoc::class)->createQueryBuilder('d') + ->where('d.bid = :bid') + ->andWhere('d.type = :type') + ->andWhere('d.year = :year') + ->andWhere('d.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('type', 'person_send') + ->setParameter('year', $acc['year']) + ->setParameter('money', $acc['money']); + + // اعمال فیلترهای جست‌وجو و تاریخ + if (isset($params['search']) && !empty($params['search'])) { + $totalQueryBuilder->leftJoin('d.hesabdariRows', 'hr') + ->leftJoin('hr.person', 'p') + ->andWhere( + $totalQueryBuilder->expr()->orX( + 'd.code LIKE :search', + 'd.des LIKE :search', + 'p.nikename LIKE :search' + ) + )->setParameter('search', "%{$params['search']}%"); + } + + // فیلتر تاریخ + if (isset($params['dateFilter']) && $params['dateFilter'] !== 'all') { + $today = $jdate->GetTodayDate(); + switch ($params['dateFilter']) { + case 'today': + $totalQueryBuilder->andWhere('d.date = :today') + ->setParameter('today', $today); + break; + case 'thisWeek': + $dayOfWeek = (int) $jdate->jdate('w', time()); + $startOfWeek = $jdate->shamsiDate(0, 0, -$dayOfWeek); + $endOfWeek = $jdate->shamsiDate(0, 0, 6 - $dayOfWeek); + $totalQueryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfWeek) + ->setParameter('end', $endOfWeek); + break; + case 'thisMonth': + $currentYear = (int) $jdate->jdate('Y', time()); + $currentMonth = (int) $jdate->jdate('n', time()); + $daysInMonth = (int) $jdate->jdate('t', time()); + $startOfMonth = sprintf('%d/%02d/01', $currentYear, $currentMonth); + $endOfMonth = sprintf('%d/%02d/%02d', $currentYear, $currentMonth, $daysInMonth); + $totalQueryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfMonth) + ->setParameter('end', $endOfMonth); + break; + } + } + + // اگر آیتم‌های خاصی درخواست شده‌اند + if (array_key_exists('items', $params)) { + $ids = array_map(function($item) { return $item['id']; }, $params['items']); + $totalQueryBuilder->andWhere('d.id IN (:ids)') + ->setParameter('ids', $ids); + } + + $totalItems = (int) $totalQueryBuilder->select('COUNT(d.id)') ->getQuery() ->getSingleScalarResult(); @@ -2014,4 +2195,282 @@ class PersonsController extends AbstractController ]); } + #[Route('/api/person/send/list/search', name: 'app_persons_send_list_search', methods: ['POST'])] + public function app_persons_send_list_search( + Request $request, + Access $access, + EntityManagerInterface $entityManager, + Jdate $jdate + ): JsonResponse { + $acc = $access->hasRole('getpay'); + if (!$acc) { + throw $this->createAccessDeniedException(); + } + + // دریافت پارامترها + $params = json_decode($request->getContent(), true) ?? []; + $page = (int) ($params['page'] ?? 1); + $itemsPerPage = (int) ($params['itemsPerPage'] ?? 10); + $search = $params['search'] ?? ''; + $dateFilter = $params['dateFilter'] ?? 'all'; + + // پردازش پارامترهای سورت + $sortBy = 'id'; + $sortDesc = true; + + if (isset($params['sortBy'])) { + if (is_array($params['sortBy']) && !empty($params['sortBy'])) { + // فرمت جدید: [{"key":"date","order":"asc"}] + $sortBy = $params['sortBy'][0]['key'] ?? 'id'; + $sortDesc = ($params['sortBy'][0]['order'] ?? 'desc') === 'desc'; + } else { + // فرمت قدیمی: "date" + $sortBy = $params['sortBy']; + } + } + + if (isset($params['sortDesc'])) { + $sortDesc = (bool) $params['sortDesc']; + } + + // کوئری پایه برای اسناد + $queryBuilder = $entityManager->getRepository(HesabdariDoc::class) + ->createQueryBuilder('d') + ->select('DISTINCT d.id, d.date, d.code, d.des, d.amount') + ->where('d.bid = :bid') + ->andWhere('d.type = :type') + ->andWhere('d.year = :year') + ->andWhere('d.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('type', 'person_send') + ->setParameter('year', $acc['year']) + ->setParameter('money', $acc['money']); + + // جست‌وجو + if (!empty($search)) { + $queryBuilder->leftJoin('d.hesabdariRows', 'hr') + ->leftJoin('hr.person', 'p') + ->andWhere( + $queryBuilder->expr()->orX( + 'd.code LIKE :search', + 'd.des LIKE :search', + 'p.nikename LIKE :search' + ) + )->setParameter('search', "%$search%"); + } + + // فیلتر تاریخ + $today = $jdate->GetTodayDate(); + switch ($dateFilter) { + case 'today': + $queryBuilder->andWhere('d.date = :today') + ->setParameter('today', $today); + break; + case 'thisWeek': + $dayOfWeek = (int) $jdate->jdate('w', time()); + $startOfWeek = $jdate->shamsiDate(0, 0, -$dayOfWeek); + $endOfWeek = $jdate->shamsiDate(0, 0, 6 - $dayOfWeek); + $queryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfWeek) + ->setParameter('end', $endOfWeek); + break; + case 'thisMonth': + $currentYear = (int) $jdate->jdate('Y', time()); + $currentMonth = (int) $jdate->jdate('n', time()); + $daysInMonth = (int) $jdate->jdate('t', time()); + $startOfMonth = sprintf('%d/%02d/01', $currentYear, $currentMonth); + $endOfMonth = sprintf('%d/%02d/%02d', $currentYear, $currentMonth, $daysInMonth); + $queryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfMonth) + ->setParameter('end', $endOfMonth); + break; + case 'all': + default: + break; + } + + // محاسبه تعداد کل قبل از اعمال GROUP BY + $totalQueryBuilder = $entityManager->getRepository(HesabdariDoc::class)->createQueryBuilder('d') + ->where('d.bid = :bid') + ->andWhere('d.type = :type') + ->andWhere('d.year = :year') + ->andWhere('d.money = :money') + ->setParameter('bid', $acc['bid']) + ->setParameter('type', 'person_send') + ->setParameter('year', $acc['year']) + ->setParameter('money', $acc['money']); + + // اعمال فیلترهای جست‌وجو و تاریخ برای کوئری تعداد + if (!empty($search)) { + $totalQueryBuilder->leftJoin('d.hesabdariRows', 'hr') + ->leftJoin('hr.person', 'p') + ->andWhere( + $totalQueryBuilder->expr()->orX( + 'd.code LIKE :search', + 'd.des LIKE :search', + 'p.nikename LIKE :search' + ) + )->setParameter('search', "%$search%"); + } + + // فیلتر تاریخ برای کوئری تعداد + switch ($dateFilter) { + case 'today': + $totalQueryBuilder->andWhere('d.date = :today') + ->setParameter('today', $today); + break; + case 'thisWeek': + $dayOfWeek = (int) $jdate->jdate('w', time()); + $startOfWeek = $jdate->shamsiDate(0, 0, -$dayOfWeek); + $endOfWeek = $jdate->shamsiDate(0, 0, 6 - $dayOfWeek); + $totalQueryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfWeek) + ->setParameter('end', $endOfWeek); + break; + case 'thisMonth': + $currentYear = (int) $jdate->jdate('Y', time()); + $currentMonth = (int) $jdate->jdate('n', time()); + $daysInMonth = (int) $jdate->jdate('t', time()); + $startOfMonth = sprintf('%d/%02d/01', $currentYear, $currentMonth); + $endOfMonth = sprintf('%d/%02d/%02d', $currentYear, $currentMonth, $daysInMonth); + $totalQueryBuilder->andWhere('d.date BETWEEN :start AND :end') + ->setParameter('start', $startOfMonth) + ->setParameter('end', $endOfMonth); + break; + } + + $total = (int) $totalQueryBuilder->select('COUNT(d.id)') + ->getQuery() + ->getSingleScalarResult(); + + // اعمال سورت + $sortDirection = $sortDesc ? 'DESC' : 'ASC'; + switch ($sortBy) { + case 'code': + $queryBuilder->orderBy('d.code', $sortDirection); + break; + case 'date': + // تبدیل تاریخ شمسی به فرمت قابل مقایسه برای سورت + $queryBuilder->orderBy('CONCAT(SUBSTRING(d.date, 1, 4), SUBSTRING(d.date, 6, 2), SUBSTRING(d.date, 9, 2))', $sortDirection); + break; + case 'amount': + $queryBuilder->orderBy('CAST(d.amount AS DECIMAL)', $sortDirection); + break; + case 'des': + $queryBuilder->orderBy('d.des', $sortDirection); + break; + case 'persons': + // برای سورت بر اساس اشخاص + if (empty($search)) { + $queryBuilder->leftJoin('d.hesabdariRows', 'hr_person') + ->leftJoin('hr_person.person', 'p'); + } + // استفاده از MIN برای جلوگیری از مشکل GROUP BY + $queryBuilder->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('MIN(p.nikename)', $sortDirection); + break; + case 'accounts': + // سورت بر اساس طرف حساب‌ها (بانک، صندوق، تنخواه) + $queryBuilder->leftJoin('d.hesabdariRows', 'hr_account') + ->leftJoin('hr_account.bank', 'ba') + ->leftJoin('hr_account.cashdesk', 'cd') + ->leftJoin('hr_account.salary', 's') + ->groupBy('d.id, d.date, d.code, d.des, d.amount') + ->orderBy('CASE WHEN ba.name IS NOT NULL THEN ba.name WHEN cd.name IS NOT NULL THEN cd.name WHEN s.name IS NOT NULL THEN s.name ELSE \'\' END', $sortDirection); + break; + default: + $queryBuilder->orderBy('d.id', 'DESC'); + break; + } + + // گرفتن اسناد با صفحه‌بندی + $docs = $queryBuilder + ->setFirstResult(($page - 1) * $itemsPerPage) + ->setMaxResults($itemsPerPage) + ->getQuery() + ->getArrayResult(); + + // گرفتن اشخاص و طرف حساب‌ها مرتبط + $docIds = array_column($docs, 'id'); + $persons = []; + $accounts = []; + if (!empty($docIds)) { + // کوئری برای اشخاص + $personQuery = $entityManager->createQueryBuilder() + ->select('IDENTITY(hr.doc) as doc_id, p.code as person_code, p.nikename as person_nikename') + ->from('App\Entity\HesabdariRow', 'hr') + ->leftJoin('hr.person', 'p') + ->where('hr.doc IN (:docIds)') + ->andWhere('p.id IS NOT NULL') + ->setParameter('docIds', $docIds) + ->getQuery() + ->getArrayResult(); + + foreach ($personQuery as $row) { + if (!empty($row['person_code'])) { + $persons[$row['doc_id']][] = [ + 'code' => $row['person_code'], + 'nikename' => $row['person_nikename'], + ]; + } + } + + // کوئری برای طرف حساب‌ها (بانک، صندوق، تنخواه گردان) + $accountQuery = $entityManager->createQueryBuilder() + ->select('IDENTITY(hr.doc) as doc_id, hr.bs as amount, + CASE + WHEN hr.bank IS NOT NULL THEN CONCAT(\'بانک: \', ba.name) + WHEN hr.cashdesk IS NOT NULL THEN CONCAT(\'صندوق: \', cd.name) + WHEN hr.salary IS NOT NULL THEN CONCAT(\'تنخواه: \', s.name) + ELSE \'نامشخص\' + END as account_name, + CASE + WHEN hr.bank IS NOT NULL THEN \'bank\' + WHEN hr.cashdesk IS NOT NULL THEN \'cashdesk\' + WHEN hr.salary IS NOT NULL THEN \'salary\' + ELSE \'unknown\' + END as account_type') + ->from('App\Entity\HesabdariRow', 'hr') + ->leftJoin('hr.bank', 'ba') + ->leftJoin('hr.cashdesk', 'cd') + ->leftJoin('hr.salary', 's') + ->where('hr.doc IN (:docIds)') + ->andWhere('hr.bs > 0') + ->andWhere('(hr.bank IS NOT NULL OR hr.cashdesk IS NOT NULL OR hr.salary IS NOT NULL)') + ->setParameter('docIds', $docIds) + ->getQuery() + ->getArrayResult(); + + foreach ($accountQuery as $row) { + if (!empty($row['account_name']) && $row['account_name'] !== 'نامشخص') { + $accounts[$row['doc_id']][] = [ + 'name' => $row['account_name'], + 'type' => $row['account_type'], + 'amount' => $row['amount'], + 'formattedAmount' => number_format($row['amount'], 0, '.', ','), + ]; + } + } + } + + // ساختاردهی خروجی + $items = []; + foreach ($docs as $doc) { + $items[] = [ + 'id' => $doc['id'], + 'date' => $doc['date'], + 'code' => $doc['code'], + 'des' => $doc['des'], + 'amount' => $doc['amount'], + 'persons' => $persons[$doc['id']] ?? [], + 'accounts' => $accounts[$doc['id']] ?? [], + ]; + } + + return $this->json([ + 'items' => $items, + 'total' => $total, + ]); + } + }