This commit is contained in:
Hesabix 2025-08-20 16:56:17 +00:00
commit 2dde89e03c
5 changed files with 63 additions and 85 deletions

View file

@ -50,7 +50,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'حواله انبار یافت نشد']);
}
$canApprove = $this->canUserApproveStoreroomTicket($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'storeroom');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این حواله را ندارید']);
}
@ -112,7 +112,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'حواله انبار یافت نشد']);
}
$canApprove = $this->canUserApproveStoreroomTicket($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'storeroom');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این حواله را ندارید']);
}
@ -174,7 +174,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'فاکتور فروش یافت نشد']);
}
$canApprove = $this->canUserApproveSalesInvoice($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'sell');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتور را ندارید']);
}
@ -240,7 +240,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'تأیید دو مرحله‌ای فعال نیست']);
}
$canApprove = $this->canUserApproveSalesInvoice($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'sell');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتورها را ندارید']);
}
@ -333,7 +333,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'فاکتور فروش یافت نشد']);
}
$canApprove = $this->canUserApproveSalesInvoice($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'sell');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتور را ندارید']);
}
@ -408,7 +408,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'فاکتور خرید یافت نشد']);
}
$canApprove = $this->canUserApproveBuyInvoice($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'buy');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتور را ندارید']);
}
@ -461,7 +461,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'تأیید دو مرحله‌ای فعال نیست']);
}
$canApprove = $this->canUserApproveBuyInvoice($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'buy');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتورها را ندارید']);
}
@ -489,7 +489,7 @@ class ApprovalController extends AbstractController
$entityManager->persist($document);
}
$entityManager->flush();
$logService->insert(
@ -542,7 +542,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'فاکتور خرید یافت نشد']);
}
$canApprove = $this->canUserApproveBuyInvoice($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'buy');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتور را ندارید']);
}
@ -595,7 +595,7 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'تأیید دو مرحله‌ای فعال نیست']);
}
$canApprove = $this->canUserApproveBuyInvoice($user, $businessSettings);
$canApprove = $this->canUserApproveDocument($user, $businessSettings, 'buy');
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتورها را ندارید']);
}
@ -623,7 +623,7 @@ class ApprovalController extends AbstractController
$entityManager->persist($document);
}
$entityManager->flush();
$logService->insert(
@ -646,67 +646,29 @@ class ApprovalController extends AbstractController
}
}
private function canUserApproveDocument(User $user, Business $business, HesabdariDoc $document): bool
private function canUserApproveDocument(User $user, Business $business, string $documentType): bool
{
if ($user->getEmail() === $business->getOwner()->getEmail()) {
return true;
}
$documentType = $this->getDocumentType($document);
$approversMap = [
'sell' => 'getApproverSellInvoice',
'buy' => 'getApproverBuyInvoice',
'storeroom' => 'getApproverWarehouseTransfer',
'rfsell' => 'getApproverReturnSell',
'rfbuy' => 'getApproverReturnBuy',
'sell_receive' => 'getApproverReceiveFromPersons',
'buy_send' => 'getApproverPayToPersons',
'hesabdari' => 'getApproverAccountingDocs',
'transfer' => 'getApproverBankTransfers',
];
switch ($documentType) {
case 'invoice':
return $business->getApproverSellInvoice() === $user->getEmail();
case 'warehouse':
return $business->getApproverWarehouseTransfer() === $user->getEmail();
default:
return false;
}
}
private function getDocumentType(HesabdariDoc $document): string
{
$type = $document->getType();
if (strpos($type, 'sell') !== false || strpos($type, 'invoice') !== false) {
return 'invoice';
if (!isset($approversMap[$documentType])) {
return false;
}
if (strpos($type, 'warehouse') !== false || strpos($type, 'storeroom') !== false) {
return 'warehouse';
}
if (strpos($type, 'payment') !== false || strpos($type, 'receipt') !== false || strpos($type, 'hesabdari') !== false) {
return 'financial';
}
return 'unknown';
}
private function canUserApproveStoreroomTicket(User $user, Business $business): bool
{
if ($user->getEmail() === $business->getOwner()->getEmail()) {
return true;
}
return $business->getApproverWarehouseTransfer() === $user->getEmail();
}
private function canUserApproveSalesInvoice(User $user, Business $business): bool
{
if ($user->getEmail() === $business->getOwner()->getEmail()) {
return true;
}
return $business->getApproverSellInvoice() === $user->getEmail();
}
private function canUserApproveBuyInvoice(User $user, Business $business): bool
{
if ($user->getEmail() === $business->getOwner()->getEmail()) {
return true;
}
return $business->getApproverBuyInvoice() === $user->getEmail();
$method = $approversMap[$documentType];
return $business->$method() === $user->getEmail();
}
}

View file

@ -45,7 +45,7 @@ class SellController extends AbstractController
if (!$acc)
throw $this->createAccessDeniedException();
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
'bid' => $acc['bid'],
'code' => $code,
'money' => $acc['money']
@ -1431,7 +1431,7 @@ class SellController extends AbstractController
throw $this->createAccessDeniedException();
}
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
'bid' => $acc['bid'],
'year' => $acc['year'],
'code' => $id,

View file

@ -98,13 +98,13 @@ class HesabdariDocRepository extends ServiceEntityRepository
{
return $this->createQueryBuilder('h')
->andWhere('h.id = :id')
->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))')
->andWhere('h.isApproved = 1')
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
public function findOneBy(array $criteria, array $orderBy = null): ?object
public function findOneBy(array $criteria, ?array $orderBy = null): ?object
{
$qb = $this->createQueryBuilder('h');
@ -116,7 +116,7 @@ class HesabdariDocRepository extends ServiceEntityRepository
}
}
$qb->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))');
$qb->andWhere('h.isApproved = 1');
if ($orderBy) {
foreach ($orderBy as $field => $direction) {
@ -128,7 +128,7 @@ class HesabdariDocRepository extends ServiceEntityRepository
}
//include preview
public function findOneByIncludePreview(array $criteria, array $orderBy = null): ?object
public function findOneByIncludePreview(array $criteria, ?array $orderBy = null): ?object
{
$qb = $this->createQueryBuilder('h');
@ -149,7 +149,7 @@ class HesabdariDocRepository extends ServiceEntityRepository
return $qb->getQuery()->getOneOrNullResult();
}
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null): array
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
{
$qb = $this->createQueryBuilder('h');
@ -161,7 +161,7 @@ class HesabdariDocRepository extends ServiceEntityRepository
}
}
$qb->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))');
$qb->andWhere('h.isApproved = 1');
if ($orderBy) {
foreach ($orderBy as $field => $direction) {
@ -183,13 +183,13 @@ class HesabdariDocRepository extends ServiceEntityRepository
public function findAll(): array
{
return $this->createQueryBuilder('h')
->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))')
->andWhere('h.isApproved = 1')
->getQuery()
->getResult();
}
//include preview
public function findByIncludePreview(array $criteria, array $orderBy = null, $limit = null, $offset = null): array
public function findByIncludePreview(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
{
$qb = $this->createQueryBuilder('h');

View file

@ -129,7 +129,7 @@
<v-btn variant="text" size="small" color="error" icon="mdi-menu" v-bind="props" />
</template>
<v-list>
<v-list-item class="text-dark" :title="$t('dialog.accounting_doc')"
<v-list-item v-if="currentTab === 'approved' && item.isApproved" class="text-dark" :title="$t('dialog.accounting_doc')"
:to="'/acc/accounting/view/' + item.code">
<template v-slot:prepend>
<v-icon color="green-darken-4" icon="mdi-file"></v-icon>
@ -140,7 +140,7 @@
<v-icon color="green-darken-4" icon="mdi-eye"></v-icon>
</template>
</v-list-item>
<v-list-item class="text-dark" :title="$t('dialog.export_pdf')"
<v-list-item v-if="currentTab === 'approved' && item.isApproved" class="text-dark" :title="$t('dialog.export_pdf')"
@click="printOptions.selectedPrintCode = item.code; modal = true;">
<template v-slot:prepend>
<v-icon icon="mdi-file-pdf-box"></v-icon>
@ -651,11 +651,15 @@ export default defineComponent({
async approveInvoice(code) {
try {
this.loading = true;
await axios.post(`/api/approval/approve/sales/${code}`);
const response = await axios.post(`/api/approval/approve/sales/${code}`);
await this.loadData();
Swal.fire({ text: 'فاکتور تایید شد', icon: 'success', confirmButtonText: 'قبول' });
if (response.data.success) {
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 {
@ -668,11 +672,15 @@ export default defineComponent({
async unapproveInvoice(code) {
try {
this.loading = true;
await axios.post(`/api/approval/unapprove/sales/${code}`);
const response = await axios.post(`/api/approval/unapprove/sales/${code}`);
await this.loadData();
Swal.fire({ text: 'تایید فاکتور لغو شد', icon: 'success', confirmButtonText: 'قبول' });
if (response.data.success) {
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 {

View file

@ -566,11 +566,15 @@ const canShowUnapproveButton = (item: Ticket) => {
const approveTicket = async (code: string) => {
try {
loading.value = true;
await axios.post(`/api/approval/approve/storeroom/${code}`);
const response = await axios.post(`/api/approval/approve/storeroom/${code}`);
await loadData();
snackbar.value = { show: true, message: 'حواله تایید شد', color: 'success' };
if (response.data.success) {
snackbar.value = { show: true, message: 'حواله تایید شد', color: 'success' };
} else {
snackbar.value = { show: true, message: response.data.message, color: 'error' };
}
} catch (error: any) {
snackbar.value = { show: true, message: 'خطا در تایید حواله: ' + (error.response?.data?.message || error.message), color: 'error' };
} finally {
@ -581,11 +585,15 @@ const approveTicket = async (code: string) => {
const unapproveTicket = async (code: string) => {
try {
loading.value = true;
await axios.post(`/api/approval/unapprove/storeroom/${code}`);
const response = await axios.post(`/api/approval/unapprove/storeroom/${code}`);
await loadData();
snackbar.value = { show: true, message: 'حواله لغو تایید شد', color: 'success' };
if (response.data.success) {
snackbar.value = { show: true, message: 'حواله لغو تایید شد', color: 'success' };
} else {
snackbar.value = { show: true, message: response.data.message, color: 'error' };
}
} catch (error: any) {
snackbar.value = { show: true, message: 'خطا در لغو تایید حواله: ' + (error.response?.data?.message || error.message), color: 'error' };
} finally {