almost done two step approval in persons part
This commit is contained in:
parent
3d454a642f
commit
1cef78c6f5
|
|
@ -1117,25 +1117,51 @@ class PersonsController extends AbstractController
|
|||
}
|
||||
|
||||
#[Route('/api/approval/approve/receive/{code}', name: 'app_approval_approve_receive', methods: ['POST'])]
|
||||
public function approveReceiveDoc(string $code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||
public function approveReceiveDoc($code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('getpay');
|
||||
if (!$acc) throw $this->createAccessDeniedException();
|
||||
|
||||
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
||||
'bid' => $acc['bid']->getId(),
|
||||
'code' => $code,
|
||||
'money' => $acc['money']->getId(),
|
||||
'type' => 'person_receive'
|
||||
]);
|
||||
// Debug: بررسی مقادیر
|
||||
$bidId = $acc['bid']->getId();
|
||||
$moneyId = $acc['money']->getId();
|
||||
$yearId = $acc['year']->getId();
|
||||
|
||||
// جستجوی سند با QueryBuilder برای debug بیشتر (بدون سال)
|
||||
$queryBuilder = $entityManager->createQueryBuilder();
|
||||
$doc = $queryBuilder
|
||||
->select('d')
|
||||
->from(HesabdariDoc::class, 'd')
|
||||
->where('d.bid = :bid')
|
||||
->andWhere('d.code = :code')
|
||||
->andWhere('d.type = :type')
|
||||
->setParameter('bid', $acc['bid'])
|
||||
->setParameter('code', $code)
|
||||
->setParameter('type', 'person_receive')
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
|
||||
if (!$doc) {
|
||||
throw $this->createNotFoundException(
|
||||
'سند دریافت یافت نشد'
|
||||
. 'code ' . $code
|
||||
. ' money ' . $acc['money']->getId()
|
||||
. ' year ' . $acc['year']->getId()
|
||||
. ' bid ' . $acc['bid']->getId()
|
||||
);
|
||||
// جستجوی تمام اسناد با همین code برای debug
|
||||
$allDocsWithCode = $entityManager->createQueryBuilder()
|
||||
->select('d')
|
||||
->from(HesabdariDoc::class, 'd')
|
||||
->where('d.code = :code')
|
||||
->setParameter('code', $code)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
$debugInfo = 'سند دریافت یافت نشد - Debug Info: ';
|
||||
$debugInfo .= 'code=' . $code . ', bid=' . $bidId . ', money=' . $moneyId . ', year=' . $yearId;
|
||||
$debugInfo .= ', total docs with this code=' . count($allDocsWithCode);
|
||||
|
||||
foreach ($allDocsWithCode as $debugDoc) {
|
||||
$debugInfo .= ', found doc: bid=' . $debugDoc->getBid()->getId() .
|
||||
' money=' . $debugDoc->getMoney()->getId() .
|
||||
' type=' . $debugDoc->getType();
|
||||
}
|
||||
|
||||
throw $this->createNotFoundException($debugInfo);
|
||||
}
|
||||
|
||||
$doc->setIsPreview(false);
|
||||
|
|
@ -1148,15 +1174,14 @@ class PersonsController extends AbstractController
|
|||
}
|
||||
|
||||
#[Route('/api/approval/unapprove/receive/{code}', name: 'app_approval_unapprove_receive', methods: ['POST'])]
|
||||
public function unapproveReceiveDoc(string $code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||
public function unapproveReceiveDoc($code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('getpay');
|
||||
if (!$acc) throw $this->createAccessDeniedException();
|
||||
|
||||
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
||||
'bid' => $acc['bid']->getId(),
|
||||
'bid' => $acc['bid'],
|
||||
'code' => $code,
|
||||
'money' => $acc['money']->getId(),
|
||||
'type' => 'person_receive'
|
||||
]);
|
||||
if (!$doc) throw $this->createNotFoundException('سند دریافت یافت نشد');
|
||||
|
|
@ -1183,14 +1208,42 @@ class PersonsController extends AbstractController
|
|||
return $this->json(['success' => false, 'message' => 'هیچ سندی انتخاب نشده است']);
|
||||
}
|
||||
|
||||
$docs = $entityManager->getRepository(HesabdariDoc::class)->findBy([
|
||||
'bid' => $acc['bid']->getId(),
|
||||
'code' => $docIds,
|
||||
'money' => $acc['money']->getId(),
|
||||
'type' => 'person_receive'
|
||||
]);
|
||||
// جستجوی اسناد با QueryBuilder برای debug بهتر
|
||||
$queryBuilder = $entityManager->createQueryBuilder();
|
||||
$docs = $queryBuilder
|
||||
->select('d')
|
||||
->from(HesabdariDoc::class, 'd')
|
||||
->where('d.bid = :bid')
|
||||
->andWhere('d.code IN (:codes)')
|
||||
->andWhere('d.type = :type')
|
||||
->setParameter('bid', $acc['bid'])
|
||||
->setParameter('codes', $docIds)
|
||||
->setParameter('type', 'person_receive')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
if (empty($docs)) {
|
||||
return $this->json(['success' => false, 'message' => 'سند دریافت یافت نشد']);
|
||||
// Debug: جستجوی تمام اسناد با این codes
|
||||
$allDocsWithCodes = $entityManager->createQueryBuilder()
|
||||
->select('d')
|
||||
->from(HesabdariDoc::class, 'd')
|
||||
->where('d.code IN (:codes)')
|
||||
->setParameter('codes', $docIds)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
$debugInfo = 'هیچ سند دریافت یافت نشد - Debug Info: ';
|
||||
$debugInfo .= 'requested codes=' . implode(',', $docIds);
|
||||
$debugInfo .= ', bid=' . $acc['bid']->getId();
|
||||
$debugInfo .= ', total docs with these codes=' . count($allDocsWithCodes);
|
||||
|
||||
foreach ($allDocsWithCodes as $debugDoc) {
|
||||
$debugInfo .= ', found doc: code=' . $debugDoc->getCode() .
|
||||
' bid=' . $debugDoc->getBid()->getId() .
|
||||
' type=' . $debugDoc->getType();
|
||||
}
|
||||
|
||||
return $this->json(['success' => false, 'message' => $debugInfo]);
|
||||
}
|
||||
foreach ($docs as $doc) {
|
||||
$doc->setIsPreview(false);
|
||||
|
|
@ -2430,6 +2483,7 @@ class PersonsController extends AbstractController
|
|||
$itemsPerPage = (int) ($params['itemsPerPage'] ?? 10);
|
||||
$search = $params['search'] ?? '';
|
||||
$dateFilter = $params['dateFilter'] ?? 'all';
|
||||
$approvalFilter = $params['approvalFilter'] ?? 'all';
|
||||
|
||||
// پردازش پارامترهای سورت
|
||||
$sortBy = 'id';
|
||||
|
|
@ -2454,16 +2508,30 @@ class PersonsController extends AbstractController
|
|||
$queryBuilder = $entityManager->getRepository(HesabdariDoc::class)
|
||||
->createQueryBuilder('d')
|
||||
->select('DISTINCT d.id, d.date, d.code, d.des, d.amount')
|
||||
->addSelect('d.isPreview, d.isApproved')
|
||||
->addSelect('approver.fullName as approvedByName, approver.id as approvedById, approver.email as approvedByEmail')
|
||||
->leftJoin('d.approvedBy', 'approver')
|
||||
->where('d.bid = :bid')
|
||||
->andWhere('d.type = :type')
|
||||
->andWhere('d.year = :year')
|
||||
->andWhere('d.money = :money')
|
||||
->andWhere('d.isApproved = :isApproved')
|
||||
->setParameter('bid', $acc['bid'])
|
||||
->setParameter('type', 'person_send')
|
||||
->setParameter('year', $acc['year'])
|
||||
->setParameter('money', $acc['money'])
|
||||
->setParameter('isApproved', true);
|
||||
->setParameter('money', $acc['money']);
|
||||
|
||||
// فیلتر تایید
|
||||
if ($approvalFilter === 'approved') {
|
||||
$queryBuilder->andWhere('d.isApproved = true');
|
||||
} elseif ($approvalFilter === 'pending') {
|
||||
$queryBuilder->andWhere('d.isPreview = true AND d.isApproved = false');
|
||||
} else {
|
||||
// اگر تایید دو مرحلهای فعال نیست، فقط اسناد تایید شده را نمایش بده
|
||||
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
|
||||
if (!$business || !$business->isRequireTwoStepApproval()) {
|
||||
$queryBuilder->andWhere('d.isApproved = true');
|
||||
}
|
||||
}
|
||||
|
||||
// جستوجو
|
||||
if (!empty($search)) {
|
||||
|
|
@ -2514,12 +2582,23 @@ class PersonsController extends AbstractController
|
|||
->andWhere('d.type = :type')
|
||||
->andWhere('d.year = :year')
|
||||
->andWhere('d.money = :money')
|
||||
->andWhere('d.isApproved = :isApproved')
|
||||
->setParameter('bid', $acc['bid'])
|
||||
->setParameter('type', 'person_send')
|
||||
->setParameter('year', $acc['year'])
|
||||
->setParameter('money', $acc['money'])
|
||||
->setParameter('isApproved', true);
|
||||
->setParameter('money', $acc['money']);
|
||||
|
||||
// فیلتر تایید برای کوئری تعداد کل
|
||||
if ($approvalFilter === 'approved') {
|
||||
$totalQueryBuilder->andWhere('d.isApproved = true');
|
||||
} elseif ($approvalFilter === 'pending') {
|
||||
$totalQueryBuilder->andWhere('d.isPreview = true AND d.isApproved = false');
|
||||
} else {
|
||||
// اگر تایید دو مرحلهای فعال نیست، فقط اسناد تایید شده را نمایش بده
|
||||
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
|
||||
if (!$business || !$business->isRequireTwoStepApproval()) {
|
||||
$totalQueryBuilder->andWhere('d.isApproved = true');
|
||||
}
|
||||
}
|
||||
|
||||
// اعمال فیلترهای جستوجو و تاریخ برای کوئری تعداد
|
||||
if (!empty($search)) {
|
||||
|
|
@ -2685,6 +2764,13 @@ class PersonsController extends AbstractController
|
|||
'amount' => $doc['amount'],
|
||||
'persons' => $persons[$doc['id']] ?? [],
|
||||
'accounts' => $accounts[$doc['id']] ?? [],
|
||||
'isPreview' => $doc['isPreview'],
|
||||
'isApproved' => $doc['isApproved'],
|
||||
'approvedBy' => $doc['approvedByName'] ? [
|
||||
'fullName' => $doc['approvedByName'],
|
||||
'id' => $doc['approvedById'],
|
||||
'email' => $doc['approvedByEmail']
|
||||
] : null,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -2694,4 +2780,150 @@ class PersonsController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/approval/approve/send/{code}', name: 'app_approval_approve_send', methods: ['POST'])]
|
||||
public function approveSendDoc($code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('getpay');
|
||||
if (!$acc) throw $this->createAccessDeniedException();
|
||||
|
||||
// Debug: بررسی مقادیر
|
||||
$bidId = $acc['bid']->getId();
|
||||
$moneyId = $acc['money']->getId();
|
||||
$yearId = $acc['year']->getId();
|
||||
|
||||
// جستجوی سند با QueryBuilder برای debug بیشتر (بدون سال)
|
||||
$queryBuilder = $entityManager->createQueryBuilder();
|
||||
$doc = $queryBuilder
|
||||
->select('d')
|
||||
->from(HesabdariDoc::class, 'd')
|
||||
->where('d.bid = :bid')
|
||||
->andWhere('d.code = :code')
|
||||
->andWhere('d.type = :type')
|
||||
->setParameter('bid', $acc['bid'])
|
||||
->setParameter('code', $code)
|
||||
->setParameter('type', 'person_send')
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
|
||||
if (!$doc) {
|
||||
// جستجوی تمام اسناد با همین code برای debug
|
||||
$allDocsWithCode = $entityManager->createQueryBuilder()
|
||||
->select('d')
|
||||
->from(HesabdariDoc::class, 'd')
|
||||
->where('d.code = :code')
|
||||
->setParameter('code', $code)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
$debugInfo = 'سند پرداخت یافت نشد - Debug Info: ';
|
||||
$debugInfo .= 'code=' . $code . ', bid=' . $bidId . ', money=' . $moneyId . ', year=' . $yearId;
|
||||
$debugInfo .= ', total docs with this code=' . count($allDocsWithCode);
|
||||
|
||||
foreach ($allDocsWithCode as $debugDoc) {
|
||||
$debugInfo .= ', found doc: bid=' . $debugDoc->getBid()->getId() .
|
||||
' money=' . $debugDoc->getMoney()->getId() .
|
||||
' type=' . $debugDoc->getType();
|
||||
}
|
||||
|
||||
throw $this->createNotFoundException($debugInfo);
|
||||
}
|
||||
|
||||
$doc->setIsPreview(false);
|
||||
$doc->setIsApproved(true);
|
||||
$doc->setApprovedBy($this->getUser());
|
||||
$entityManager->persist($doc);
|
||||
$entityManager->flush();
|
||||
|
||||
return $this->json(['success' => true, 'message' => 'سند پرداخت تایید شد']);
|
||||
}
|
||||
|
||||
#[Route('/api/approval/unapprove/send/{code}', name: 'app_approval_unapprove_send', methods: ['POST'])]
|
||||
public function unapproveSendDoc($code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('getpay');
|
||||
if (!$acc) throw $this->createAccessDeniedException();
|
||||
|
||||
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
||||
'bid' => $acc['bid'],
|
||||
'code' => $code,
|
||||
'type' => 'person_send'
|
||||
]);
|
||||
if (!$doc) throw $this->createNotFoundException('سند پرداخت یافت نشد');
|
||||
|
||||
$doc->setIsPreview(true);
|
||||
$doc->setIsApproved(false);
|
||||
$doc->setApprovedBy(null);
|
||||
$entityManager->persist($doc);
|
||||
$entityManager->flush();
|
||||
|
||||
return $this->json(['success' => true, 'message' => 'تایید سند پرداخت لغو شد']);
|
||||
}
|
||||
|
||||
#[Route('/api/approval/approve/group/send', name: 'app_approval_approve_group_send', methods: ['POST'])]
|
||||
public function approveGroupSendDocs(Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('getpay');
|
||||
if (!$acc) throw $this->createAccessDeniedException();
|
||||
|
||||
$params = json_decode($request->getContent(), true) ?? [];
|
||||
$docIds = $params['docIds'] ?? [];
|
||||
|
||||
if (empty($docIds)) {
|
||||
return $this->json(['success' => false, 'message' => 'هیچ سندی انتخاب نشده است']);
|
||||
}
|
||||
|
||||
// جستجوی اسناد با QueryBuilder برای debug بهتر
|
||||
$queryBuilder = $entityManager->createQueryBuilder();
|
||||
$docs = $queryBuilder
|
||||
->select('d')
|
||||
->from(HesabdariDoc::class, 'd')
|
||||
->where('d.bid = :bid')
|
||||
->andWhere('d.code IN (:codes)')
|
||||
->andWhere('d.type = :type')
|
||||
->setParameter('bid', $acc['bid'])
|
||||
->setParameter('codes', $docIds)
|
||||
->setParameter('type', 'person_send')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
if (empty($docs)) {
|
||||
|
||||
// Debug: جستجوی تمام اسناد برای بررسی مشکل
|
||||
$debugQuery = $entityManager->createQueryBuilder()
|
||||
->select('d.code, d.type, d.bid')
|
||||
->from(HesabdariDoc::class, 'd')
|
||||
->where('d.code IN (:codes)')
|
||||
->setParameter('codes', $docIds)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
$debugInfo = 'اسناد پرداخت یافت نشد - Debug Info: ';
|
||||
$debugInfo .= 'requested codes=' . implode(',', $docIds) . ', bid=' . $acc['bid']->getId();
|
||||
$debugInfo .= ', found docs=' . count($debugQuery);
|
||||
|
||||
foreach ($debugQuery as $debugDoc) {
|
||||
$debugInfo .= ', code=' . $debugDoc['code'] . ' type=' . $debugDoc['type'] . ' bid=' . $debugDoc['bid'];
|
||||
}
|
||||
|
||||
return $this->json(['success' => false, 'message' => $debugInfo]);
|
||||
}
|
||||
|
||||
$successCount = 0;
|
||||
foreach ($docs as $doc) {
|
||||
if ($doc->isPreview() && !$doc->isApproved()) {
|
||||
$doc->setIsPreview(false);
|
||||
$doc->setIsApproved(true);
|
||||
$doc->setApprovedBy($this->getUser());
|
||||
$entityManager->persist($doc);
|
||||
$successCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($successCount > 0) {
|
||||
$entityManager->flush();
|
||||
}
|
||||
|
||||
return $this->json(['success' => true, 'message' => "$successCount سند پرداخت تایید شد"]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,6 @@
|
|||
<v-btn v-bind="props" icon="mdi-plus" color="primary" to="/acc/persons/receive/mod/"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip v-if="checkApprover()" :text="'تایید اسناد انتخابی'" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="mdi-check-decagram" color="success" @click="approveSelectedReceives"
|
||||
:disabled="selectedItems.length === 0" :loading="bulkLoading"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon color="error">
|
||||
|
|
@ -74,6 +68,12 @@
|
|||
<v-btn v-bind="props" icon="mdi-table-cog" color="primary" @click="showColumnDialog = true"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip v-if="checkApprover()" :text="'تایید اسناد انتخابی'" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="mdi-check-decagram" color="success" @click="approveSelectedReceives"
|
||||
:disabled="selectedItems.length === 0" :loading="bulkLoading"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</v-toolbar>
|
||||
<!-- Tabs for two-step approval -->
|
||||
<div v-if="business.requireTwoStepApproval" class="px-2 pt-2">
|
||||
|
|
@ -698,39 +698,43 @@ const canShowUnapproveButton = (item) => {
|
|||
|
||||
const approveReceive = async (code) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await axios.post(`/api/approval/approve/receive/${code}`);
|
||||
|
||||
await loadData();
|
||||
|
||||
if (response.data.success) {
|
||||
// پاک کردن انتخابها و بهروزرسانی فوری جدول
|
||||
selectedItems.value = selectedItems.value.filter(item => item.code !== code);
|
||||
selectAll.value = false;
|
||||
updateSelectedSum();
|
||||
console.log('Before loadData in approveReceive');
|
||||
await loadData();
|
||||
console.log('After loadData in approveReceive');
|
||||
Swal.fire({ text: 'سند دریافت تایید شد', icon: 'success', confirmButtonText: 'قبول' });
|
||||
} else {
|
||||
Swal.fire({ text: response.data.message, icon: 'error', confirmButtonText: 'قبول' });
|
||||
}
|
||||
} catch (error) {
|
||||
Swal.fire({ text: 'خطا در تایید سند: ' + (error.response?.data?.message || error.message), icon: 'error', confirmButtonText: 'قبول' });
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const unapproveReceive = async (code) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await axios.post(`/api/approval/unapprove/receive/${code}`);
|
||||
|
||||
await loadData();
|
||||
|
||||
if (response.data.success) {
|
||||
// پاک کردن انتخابها و بهروزرسانی فوری جدول
|
||||
selectedItems.value = selectedItems.value.filter(item => item.code !== code);
|
||||
selectAll.value = false;
|
||||
updateSelectedSum();
|
||||
console.log('Before loadData in unapproveReceive');
|
||||
await loadData();
|
||||
console.log('After loadData in unapproveReceive');
|
||||
Swal.fire({ text: 'تایید سند لغو شد', icon: 'success', confirmButtonText: 'قبول' });
|
||||
} else {
|
||||
Swal.fire({ text: response.data.message, icon: 'error', confirmButtonText: 'قبول' });
|
||||
}
|
||||
} catch (error) {
|
||||
Swal.fire({ text: 'خطا در لغو تایید سند: ' + (error.response?.data?.message || error.message), icon: 'error', confirmButtonText: 'قبول' });
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -758,14 +762,22 @@ const approveSelectedReceives = async () => {
|
|||
bulkLoading.value = true;
|
||||
|
||||
try {
|
||||
await axios.post(`/api/approval/approve/group/receive`, {
|
||||
const response = await axios.post(`/api/approval/approve/group/receive`, {
|
||||
'docIds': selectedItems.value.map(item => item.code)
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
Swal.fire({ text: 'اسناد تایید شدند.', icon: 'success', confirmButtonText: 'قبول' });
|
||||
selectedItems.value = [];
|
||||
selectAll.value = false;
|
||||
updateSelectedSum();
|
||||
// بهروزرسانی فوری جدول
|
||||
await loadData();
|
||||
} else {
|
||||
Swal.fire({ text: response.data.message || 'خطا در تایید اسناد', icon: 'error', confirmButtonText: 'قبول' });
|
||||
}
|
||||
} catch (e) {
|
||||
Swal.fire({ text: 'خطا در تایید اسناد', icon: 'error', confirmButtonText: 'قبول' });
|
||||
Swal.fire({ text: 'خطا در تایید اسناد: ' + (e.response?.data?.message || e.message), icon: 'error', confirmButtonText: 'قبول' });
|
||||
} finally {
|
||||
bulkLoading.value = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,20 @@
|
|||
<v-btn v-bind="props" icon="mdi-table-cog" color="primary" @click="showColumnDialog = true"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip v-if="checkApprover()" :text="'تایید اسناد انتخابی'" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="mdi-check-decagram" color="success" @click="approveSelectedSends"
|
||||
:disabled="selectedItems.length === 0" :loading="bulkLoading"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</v-toolbar>
|
||||
<!-- Tabs for two-step approval -->
|
||||
<div v-if="business.requireTwoStepApproval" class="px-2 pt-2">
|
||||
<v-tabs v-model="currentTab" color="primary" density="comfortable" grow>
|
||||
<v-tab value="approved">اسناد تایید شده</v-tab>
|
||||
<v-tab value="pending">اسناد در انتظار تایید</v-tab>
|
||||
</v-tabs>
|
||||
</div>
|
||||
<v-text-field
|
||||
hide-details
|
||||
color="green"
|
||||
|
|
@ -154,6 +167,18 @@
|
|||
</template>
|
||||
<v-list-item-title>سند حسابداری</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="canShowApprovalButton(item)" title="تایید سند"
|
||||
@click="approveSend(item.code)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="success">mdi-check-decagram</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="canShowUnapproveButton(item)" title="لغو تایید سند"
|
||||
@click="unapproveSend(item.code)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="red">mdi-cancel</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item :to="{ name: 'person_send_mod', params: { id: item.code }}">
|
||||
<template v-slot:prepend>
|
||||
<v-icon icon="mdi-pencil"></v-icon>
|
||||
|
|
@ -198,6 +223,14 @@
|
|||
<template v-slot:item.amount="{ item }">
|
||||
<span class="text-left">{{ $filters.formatNumber(item.amount) }}</span>
|
||||
</template>
|
||||
<template v-slot:item.approvalStatus="{ item }">
|
||||
<v-chip size="small" :color="getApprovalStatusColor(item)">
|
||||
{{ getApprovalStatusText(item) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<template v-slot:item.approvedBy="{ item }">
|
||||
{{ item.approvedBy?.fullName || '-' }}
|
||||
</template>
|
||||
</v-data-table-server>
|
||||
<v-card class="my-4">
|
||||
<v-card-text>
|
||||
|
|
@ -278,6 +311,10 @@ const sumTotal = ref(0);
|
|||
const sumSelected = ref(0);
|
||||
const sortBy = ref('id');
|
||||
const sortDesc = ref(true);
|
||||
const currentTab = ref('approved');
|
||||
const business = ref({ requireTwoStepApproval: false, approvers: { sendToPersons: null } });
|
||||
const currentUser = ref({ email: '', owner: false });
|
||||
const bulkLoading = ref(false);
|
||||
|
||||
const allHeaders = reactive([
|
||||
{ title: '', key: 'select', sortable: false, visible: true, customizable: false },
|
||||
|
|
@ -288,13 +325,20 @@ const allHeaders = reactive([
|
|||
{ title: 'تاریخ', key: 'date', sortable: true, visible: true },
|
||||
{ title: 'شرح', key: 'des', sortable: true, visible: true },
|
||||
{ title: 'مبلغ', key: 'amount', sortable: true, visible: true },
|
||||
{ title: 'وضعیت تایید', key: 'approvalStatus', sortable: true, visible: true },
|
||||
{ title: 'تاییدکننده', key: 'approvedBy', sortable: true, visible: true },
|
||||
]);
|
||||
|
||||
const customizableHeaders = computed(() =>
|
||||
allHeaders.filter(h => h.customizable !== false && h.key !== 'operation')
|
||||
);
|
||||
const visibleHeaders = computed(() =>
|
||||
allHeaders.filter(h => h.customizable === false || h.visible)
|
||||
allHeaders.filter(h => {
|
||||
if ((h.key === 'approvalStatus' || h.key === 'approvedBy') && !business.value.requireTwoStepApproval) {
|
||||
return false;
|
||||
}
|
||||
return h.customizable === false || h.visible;
|
||||
})
|
||||
);
|
||||
|
||||
const dateFilterOptions = [
|
||||
|
|
@ -338,6 +382,7 @@ const loadData = async (options = null) => {
|
|||
itemsPerPage: options?.itemsPerPage || itemsPerPage.value,
|
||||
search: searchValue.value,
|
||||
dateFilter: dateFilter.value,
|
||||
approvalFilter: business.value.requireTwoStepApproval ? currentTab.value : 'all',
|
||||
sortBy: [{
|
||||
key: sortBy.value,
|
||||
order: sortDesc.value ? 'desc' : 'asc'
|
||||
|
|
@ -587,8 +632,153 @@ watch([page, itemsPerPage], () => {
|
|||
loadData();
|
||||
}, { deep: true });
|
||||
|
||||
watch(currentTab, () => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
// Methods for approval functionality
|
||||
const checkApprover = () => {
|
||||
return business.value.requireTwoStepApproval && (business.value.approvers.sendToPersons == currentUser.value.email || currentUser.value.owner === true);
|
||||
};
|
||||
|
||||
const getApprovalStatusText = (item) => {
|
||||
if (!business.value?.requireTwoStepApproval) return 'تایید دو مرحلهای غیرفعال';
|
||||
|
||||
if (item.isPreview) return 'در انتظار تایید';
|
||||
if (item.isApproved) return 'تایید شده';
|
||||
return 'تایید شده';
|
||||
};
|
||||
|
||||
const getApprovalStatusColor = (item) => {
|
||||
if (!business.value?.requireTwoStepApproval) return 'default';
|
||||
|
||||
if (item.isPreview) return 'warning';
|
||||
if (item.isApproved) return 'success';
|
||||
return 'success';
|
||||
};
|
||||
|
||||
const canShowApprovalButton = (item) => {
|
||||
if (!checkApprover()) return false;
|
||||
if (item?.isApproved) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const canShowUnapproveButton = (item) => {
|
||||
return !canShowApprovalButton(item) && checkApprover();
|
||||
};
|
||||
|
||||
const approveSend = async (code) => {
|
||||
try {
|
||||
const response = await axios.post(`/api/approval/approve/send/${code}`);
|
||||
|
||||
if (response.data.success) {
|
||||
// پاک کردن انتخابها و بهروزرسانی فوری جدول
|
||||
selectedItems.value = selectedItems.value.filter(item => item.code !== code);
|
||||
selectAll.value = false;
|
||||
updateSelectedSum();
|
||||
console.log('Before loadData in approveSend');
|
||||
await loadData();
|
||||
console.log('After loadData in approveSend');
|
||||
Swal.fire({ text: 'سند پرداخت تایید شد', icon: 'success', confirmButtonText: 'قبول' });
|
||||
} else {
|
||||
Swal.fire({ text: response.data.message, icon: 'error', confirmButtonText: 'قبول' });
|
||||
}
|
||||
} catch (error) {
|
||||
Swal.fire({ text: 'خطا در تایید سند: ' + (error.response?.data?.message || error.message), icon: 'error', confirmButtonText: 'قبول' });
|
||||
}
|
||||
};
|
||||
|
||||
const unapproveSend = async (code) => {
|
||||
try {
|
||||
const response = await axios.post(`/api/approval/unapprove/send/${code}`);
|
||||
|
||||
if (response.data.success) {
|
||||
// پاک کردن انتخابها و بهروزرسانی فوری جدول
|
||||
selectedItems.value = selectedItems.value.filter(item => item.code !== code);
|
||||
selectAll.value = false;
|
||||
updateSelectedSum();
|
||||
console.log('Before loadData in unapproveSend');
|
||||
await loadData();
|
||||
console.log('After loadData in unapproveSend');
|
||||
Swal.fire({ text: 'تایید سند لغو شد', icon: 'success', confirmButtonText: 'قبول' });
|
||||
} else {
|
||||
Swal.fire({ text: response.data.message, icon: 'error', confirmButtonText: 'قبول' });
|
||||
}
|
||||
} catch (error) {
|
||||
Swal.fire({ text: 'خطا در لغو تایید سند: ' + (error.response?.data?.message || error.message), icon: 'error', confirmButtonText: 'قبول' });
|
||||
}
|
||||
};
|
||||
|
||||
const approveSelectedSends = async () => {
|
||||
if (selectedItems.value.length === 0) {
|
||||
Swal.fire({ text: 'هیچ موردی انتخاب نشده است.', icon: 'warning', confirmButtonText: 'قبول' });
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedSends = items.value.filter(send => selectedItems.value.some(sel => sel.code === send.code));
|
||||
if (selectedSends.some(send => !(!send.isApproved && send.isPreview))) {
|
||||
Swal.fire({ text: 'برخی اسناد انتخابی تایید شده هستند.', icon: 'warning', confirmButtonText: 'قبول' });
|
||||
return;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: 'تایید اسناد انتخابی',
|
||||
text: 'اسناد انتخابشده تایید خواهند شد.',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'بله',
|
||||
cancelButtonText: 'خیر'
|
||||
}).then(async (r) => {
|
||||
if (!r.isConfirmed) return;
|
||||
bulkLoading.value = true;
|
||||
|
||||
try {
|
||||
const response = await axios.post(`/api/approval/approve/group/send`, {
|
||||
'docIds': selectedItems.value.map(item => item.code)
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
Swal.fire({ text: 'اسناد تایید شدند.', icon: 'success', confirmButtonText: 'قبول' });
|
||||
selectedItems.value = [];
|
||||
selectAll.value = false;
|
||||
updateSelectedSum();
|
||||
// بهروزرسانی فوری جدول
|
||||
await loadData();
|
||||
} else {
|
||||
Swal.fire({ text: response.data.message || 'خطا در تایید اسناد', icon: 'error', confirmButtonText: 'قبول' });
|
||||
}
|
||||
} catch (e) {
|
||||
Swal.fire({ text: 'خطا در تایید اسناد: ' + (e.response?.data?.message || e.message), icon: 'error', confirmButtonText: 'قبول' });
|
||||
} finally {
|
||||
bulkLoading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const loadBusinessInfo = async () => {
|
||||
try {
|
||||
const response = await axios.get('/api/business/get/info/' + localStorage.getItem('activeBid'));
|
||||
business.value = response.data || { requireTwoStepApproval: false, approvers: { sendToPersons: null } };
|
||||
} catch (error) {
|
||||
console.error('Error loading business info:', error);
|
||||
business.value = { requireTwoStepApproval: false, approvers: { sendToPersons: null } };
|
||||
}
|
||||
};
|
||||
|
||||
const loadCurrentUser = async () => {
|
||||
try {
|
||||
const response = await axios.post('/api/business/get/user/permissions');
|
||||
currentUser.value = response.data || { email: '', owner: false };
|
||||
} catch (error) {
|
||||
console.error('Error loading current user:', error);
|
||||
currentUser.value = { email: '', owner: false };
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadColumnSettings();
|
||||
loadBusinessInfo();
|
||||
loadCurrentUser();
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue