almost finish buy system with new changes in hesabdariDoc
This commit is contained in:
parent
45c03051a0
commit
9af86b989b
35
hesabixCore/migrations/Version20250819234842.php
Normal file
35
hesabixCore/migrations/Version20250819234842.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20250819234842 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE hesabdari_doc CHANGE is_preview is_preview TINYINT(1) DEFAULT 0, CHANGE is_approved is_approved TINYINT(1) DEFAULT 1
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE hesabdari_doc CHANGE is_preview is_preview TINYINT(1) DEFAULT NULL, CHANGE is_approved is_approved TINYINT(1) DEFAULT NULL
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
|
@ -250,7 +250,7 @@ class ApprovalController extends AbstractController
|
||||||
|
|
||||||
foreach ($docIds as $docId) {
|
foreach ($docIds as $docId) {
|
||||||
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
|
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
|
||||||
'id' => $docId,
|
'code' => $docId,
|
||||||
'bid' => $business
|
'bid' => $business
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -378,6 +378,274 @@ class ApprovalController extends AbstractController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/api/approval/approve/buy/{docId}', name: 'api_approval_approve_buy', methods: ['POST'])]
|
||||||
|
public function approveBuyInvoice(
|
||||||
|
$docId,
|
||||||
|
#[CurrentUser] ?User $user,
|
||||||
|
Access $access,
|
||||||
|
LogService $logService,
|
||||||
|
EntityManagerInterface $entityManager
|
||||||
|
): Response {
|
||||||
|
try {
|
||||||
|
$acc = $access->hasRole('buy');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$business = $acc['bid'];
|
||||||
|
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
|
||||||
|
|
||||||
|
if (!$businessSettings->isRequireTwoStepApproval()) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'تأیید دو مرحلهای فعال نیست']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
|
||||||
|
'code' => $docId,
|
||||||
|
'bid' => $business
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$document) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'فاکتور خرید یافت نشد']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$canApprove = $this->canUserApproveBuyInvoice($user, $businessSettings);
|
||||||
|
if (!$canApprove) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتور را ندارید']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$document->setIsPreview(false);
|
||||||
|
$document->setIsApproved(true);
|
||||||
|
$document->setApprovedBy($user);
|
||||||
|
|
||||||
|
$entityManager->persist($document);
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
$logService->insert(
|
||||||
|
'تأیید فاکتور خرید',
|
||||||
|
"فاکتور خرید {$document->getCode()} توسط {$user->getFullName()} تأیید شد",
|
||||||
|
$user,
|
||||||
|
$business
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'فاکتور خرید با موفقیت تأیید شد'
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'خطا در تأیید فاکتور خرید: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/approval/approve/group/buy', name: 'api_approval_approve_group_buy', methods: ['POST'])]
|
||||||
|
public function approveBuyInvoiceGroup(
|
||||||
|
Request $request,
|
||||||
|
#[CurrentUser] ?User $user,
|
||||||
|
Access $access,
|
||||||
|
LogService $logService,
|
||||||
|
EntityManagerInterface $entityManager
|
||||||
|
): Response {
|
||||||
|
try {
|
||||||
|
$acc = $access->hasRole('buy');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$business = $acc['bid'];
|
||||||
|
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
|
||||||
|
|
||||||
|
if (!$businessSettings->isRequireTwoStepApproval()) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'تأیید دو مرحلهای فعال نیست']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$canApprove = $this->canUserApproveBuyInvoice($user, $businessSettings);
|
||||||
|
if (!$canApprove) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتورها را ندارید']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($request->getContent(), true);
|
||||||
|
$docIds = $data['docIds'] ?? [];
|
||||||
|
|
||||||
|
foreach ($docIds as $docId) {
|
||||||
|
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
|
||||||
|
'code' => $docId,
|
||||||
|
'bid' => $business
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$document) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'فاکتور خرید یافت نشد']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($document->isApproved()) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'فاکتور خرید تایید شده است']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$document->setIsPreview(false);
|
||||||
|
$document->setIsApproved(true);
|
||||||
|
$document->setApprovedBy($user);
|
||||||
|
|
||||||
|
$entityManager->persist($document);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
$logService->insert(
|
||||||
|
'تأیید فاکتورهای خرید',
|
||||||
|
"فاکتورهای خرید " . implode(', ', $docIds) . " توسط {$user->getFullName()} تأیید شدند",
|
||||||
|
$user,
|
||||||
|
$business
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'فاکتورهای خرید با موفقیت تأیید شدند'
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'خطا در تأیید فاکتورهای خرید: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/approval/unapprove/buy/{docId}', name: 'api_approval_unapprove_buy', methods: ['POST'])]
|
||||||
|
public function unapproveBuyInvoice(
|
||||||
|
$docId,
|
||||||
|
#[CurrentUser] ?User $user,
|
||||||
|
Access $access,
|
||||||
|
LogService $logService,
|
||||||
|
EntityManagerInterface $entityManager
|
||||||
|
): Response {
|
||||||
|
try {
|
||||||
|
$acc = $access->hasRole('buy');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$business = $acc['bid'];
|
||||||
|
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
|
||||||
|
|
||||||
|
if (!$businessSettings->isRequireTwoStepApproval()) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'تأیید دو مرحلهای فعال نیست']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
|
||||||
|
'code' => $docId,
|
||||||
|
'bid' => $business
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$document) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'فاکتور خرید یافت نشد']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$canApprove = $this->canUserApproveBuyInvoice($user, $businessSettings);
|
||||||
|
if (!$canApprove) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتور را ندارید']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$document->setIsPreview(true);
|
||||||
|
$document->setIsApproved(false);
|
||||||
|
$document->setApprovedBy(null);
|
||||||
|
|
||||||
|
$entityManager->persist($document);
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
$logService->insert(
|
||||||
|
'لغو تأیید فاکتور خرید',
|
||||||
|
"فاکتور خرید {$document->getCode()} توسط {$user->getFullName()} لغو تأیید شد",
|
||||||
|
$user,
|
||||||
|
$business
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'فاکتور خرید با موفقیت لغو تأیید شد'
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'خطا در لغو تأیید فاکتور خرید: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/approval/unapprove/group/buy', name: 'api_approval_unapprove_group_buy', methods: ['POST'])]
|
||||||
|
public function unapproveBuyInvoiceGroup(
|
||||||
|
Request $request,
|
||||||
|
#[CurrentUser] ?User $user,
|
||||||
|
Access $access,
|
||||||
|
LogService $logService,
|
||||||
|
EntityManagerInterface $entityManager
|
||||||
|
): Response {
|
||||||
|
try {
|
||||||
|
$acc = $access->hasRole('buy');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$business = $acc['bid'];
|
||||||
|
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
|
||||||
|
|
||||||
|
if (!$businessSettings->isRequireTwoStepApproval()) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'تأیید دو مرحلهای فعال نیست']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$canApprove = $this->canUserApproveBuyInvoice($user, $businessSettings);
|
||||||
|
if (!$canApprove) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتورها را ندارید']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($request->getContent(), true);
|
||||||
|
$docIds = $data['docIds'] ?? [];
|
||||||
|
|
||||||
|
foreach ($docIds as $docId) {
|
||||||
|
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
|
||||||
|
'code' => $docId,
|
||||||
|
'bid' => $business
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$document) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'فاکتور خرید یافت نشد']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$document->isApproved()) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'فاکتور خرید تایید نشده است']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$document->setIsPreview(true);
|
||||||
|
$document->setIsApproved(false);
|
||||||
|
$document->setApprovedBy(null);
|
||||||
|
|
||||||
|
$entityManager->persist($document);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
$logService->insert(
|
||||||
|
'لغو تأیید فاکتورهای خرید',
|
||||||
|
"فاکتورهای خرید " . implode(', ', $docIds) . " توسط {$user->getFullName()} لغو تأیید شدند",
|
||||||
|
$user,
|
||||||
|
$business
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'فاکتورهای خرید با موفقیت لغو تأیید شدند'
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'خطا در لغو تأیید فاکتورهای خرید: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function canUserApproveDocument(User $user, Business $business, HesabdariDoc $document): bool
|
private function canUserApproveDocument(User $user, Business $business, HesabdariDoc $document): bool
|
||||||
{
|
{
|
||||||
if ($user->getEmail() === $business->getOwner()->getEmail()) {
|
if ($user->getEmail() === $business->getOwner()->getEmail()) {
|
||||||
|
@ -432,4 +700,13 @@ class ApprovalController extends AbstractController
|
||||||
|
|
||||||
return $business->getApproverSellInvoice() === $user->getEmail();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -252,6 +252,9 @@ class BuyController extends AbstractController
|
||||||
return $this->json($extractor->notFound());
|
return $this->json($extractor->notFound());
|
||||||
}
|
}
|
||||||
foreach ($params['items'] as $item) {
|
foreach ($params['items'] as $item) {
|
||||||
|
if (!$item || !isset($item['code'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
||||||
'bid' => $acc['bid'],
|
'bid' => $acc['bid'],
|
||||||
'year' => $acc['year'],
|
'year' => $acc['year'],
|
||||||
|
@ -286,77 +289,177 @@ class BuyController extends AbstractController
|
||||||
return $this->json($extractor->operationSuccess());
|
return $this->json($extractor->operationSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/api/buy/docs/search', name: 'app_buy_docs_search')]
|
#[Route('/api/buy/docs/search', name: 'app_buy_docs_search', methods: ['POST'])]
|
||||||
public function app_buy_docs_search(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
public function app_buy_docs_search(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
||||||
{
|
{
|
||||||
$acc = $access->hasRole('buy');
|
$acc = $access->hasRole('buy');
|
||||||
if (!$acc)
|
if (!$acc)
|
||||||
throw $this->createAccessDeniedException();
|
throw $this->createAccessDeniedException();
|
||||||
|
|
||||||
$params = [];
|
$params = json_decode($request->getContent(), true) ?? [];
|
||||||
if ($content = $request->getContent()) {
|
$searchTerm = $params['search'] ?? '';
|
||||||
$params = json_decode($content, true);
|
$page = max(1, $params['page'] ?? 1);
|
||||||
|
$perPage = max(1, min(100, $params['perPage'] ?? 10));
|
||||||
|
$types = $params['types'] ?? [];
|
||||||
|
$sortBy = $params['sortBy'] ?? [];
|
||||||
|
|
||||||
|
$queryBuilder = $entityManager->createQueryBuilder()
|
||||||
|
->select('DISTINCT d.id, d.dateSubmit, d.date, d.type, d.code, d.des, d.amount')
|
||||||
|
->addSelect('d.isPreview, d.isApproved')
|
||||||
|
->addSelect('u.fullName as submitter')
|
||||||
|
->addSelect('approver.fullName as approvedByName, approver.id as approvedById, approver.email as approvedByEmail')
|
||||||
|
->addSelect('l.code as labelCode, l.label as labelLabel')
|
||||||
|
->from(HesabdariDoc::class, 'd')
|
||||||
|
->leftJoin('d.submitter', 'u')
|
||||||
|
->leftJoin('d.approvedBy', 'approver')
|
||||||
|
->leftJoin('d.InvoiceLabel', 'l')
|
||||||
|
->leftJoin('d.hesabdariRows', 'r')
|
||||||
|
->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', 'buy')
|
||||||
|
->setParameter('money', $acc['money']);
|
||||||
|
|
||||||
|
if ($searchTerm) {
|
||||||
|
$queryBuilder->leftJoin('r.person', 'p')
|
||||||
|
->andWhere(
|
||||||
|
$queryBuilder->expr()->orX(
|
||||||
|
'd.code LIKE :search',
|
||||||
|
'd.des LIKE :search',
|
||||||
|
'd.date LIKE :search',
|
||||||
|
'd.amount LIKE :search',
|
||||||
|
'p.nikename LIKE :search',
|
||||||
|
'p.mobile LIKE :search'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setParameter('search', "%$searchTerm%");
|
||||||
}
|
}
|
||||||
$data = $entityManager->getRepository(HesabdariDoc::class)->findBy([
|
|
||||||
'bid' => $acc['bid'],
|
if (!empty($types)) {
|
||||||
'year' => $acc['year'],
|
$queryBuilder->andWhere('l.code IN (:types)')
|
||||||
'type' => 'buy',
|
->setParameter('types', $types);
|
||||||
'money' => $acc['money']
|
}
|
||||||
], [
|
|
||||||
'id' => 'DESC'
|
// فیلدهای معتبر برای مرتبسازی توی دیتابیس
|
||||||
]);
|
$validDbFields = [
|
||||||
|
'id' => 'd.id',
|
||||||
|
'dateSubmit' => 'd.dateSubmit',
|
||||||
|
'date' => 'd.date',
|
||||||
|
'type' => 'd.type',
|
||||||
|
'code' => 'd.code',
|
||||||
|
'des' => 'd.des',
|
||||||
|
'amount' => 'd.amount',
|
||||||
|
'mdate' => 'd.mdate',
|
||||||
|
'plugin' => 'd.plugin',
|
||||||
|
'refData' => 'd.refData',
|
||||||
|
'shortlink' => 'd.shortlink',
|
||||||
|
'isPreview' => 'd.isPreview',
|
||||||
|
'isApproved' => 'd.isApproved',
|
||||||
|
'approvedBy' => 'd.approvedBy',
|
||||||
|
'submitter' => 'u.fullName',
|
||||||
|
'label' => 'l.label',
|
||||||
|
];
|
||||||
|
|
||||||
|
// اعمال مرتبسازی توی دیتابیس
|
||||||
|
if (!empty($sortBy)) {
|
||||||
|
foreach ($sortBy as $sort) {
|
||||||
|
$key = $sort['key'] ?? 'id';
|
||||||
|
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? 'DESC' : 'ASC';
|
||||||
|
if (isset($validDbFields[$key])) {
|
||||||
|
$queryBuilder->addOrderBy($validDbFields[$key], $direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$queryBuilder->orderBy('d.id', 'DESC');
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalItemsQuery = clone $queryBuilder;
|
||||||
|
$totalItems = $totalItemsQuery->select('COUNT(DISTINCT d.id)')
|
||||||
|
->getQuery()
|
||||||
|
->getSingleScalarResult();
|
||||||
|
|
||||||
|
$queryBuilder->setFirstResult(($page - 1) * $perPage)
|
||||||
|
->setMaxResults($perPage);
|
||||||
|
|
||||||
|
$docs = $queryBuilder->getQuery()->getArrayResult();
|
||||||
|
|
||||||
$dataTemp = [];
|
$dataTemp = [];
|
||||||
foreach ($data as $item) {
|
foreach ($docs as $doc) {
|
||||||
$temp = [
|
$item = [
|
||||||
'id' => $item->getId(),
|
'id' => $doc['id'],
|
||||||
'dateSubmit' => $item->getDateSubmit(),
|
'dateSubmit' => $doc['dateSubmit'],
|
||||||
'date' => $item->getDate(),
|
'date' => $doc['date'],
|
||||||
'type' => $item->getType(),
|
'type' => $doc['type'],
|
||||||
'code' => $item->getCode(),
|
'code' => $doc['code'],
|
||||||
'des' => $item->getDes(),
|
'des' => $doc['des'],
|
||||||
'amount' => $item->getAmount(),
|
'amount' => $doc['amount'],
|
||||||
'submitter' => $item->getSubmitter()->getFullName(),
|
'submitter' => $doc['submitter'],
|
||||||
|
'label' => $doc['labelCode'] ? [
|
||||||
|
'code' => $doc['labelCode'],
|
||||||
|
'label' => $doc['labelLabel']
|
||||||
|
] : null,
|
||||||
|
'isPreview' => $doc['isPreview'],
|
||||||
|
'isApproved' => $doc['isApproved'],
|
||||||
|
'approvedBy' => $doc['approvedByName'] ? [
|
||||||
|
'fullName' => $doc['approvedByName'],
|
||||||
|
'id' => $doc['approvedById'],
|
||||||
|
'email' => $doc['approvedByEmail']
|
||||||
|
] : null,
|
||||||
];
|
];
|
||||||
$mainRow = $entityManager->getRepository(HesabdariRow::class)->getNotEqual($item, 'person');
|
|
||||||
$temp['person'] = '';
|
|
||||||
if ($mainRow)
|
|
||||||
$temp['person'] = Explore::ExplorePerson($mainRow->getPerson());
|
|
||||||
|
|
||||||
$temp['label'] = null;
|
$mainRow = $entityManager->getRepository(HesabdariRow::class)
|
||||||
if ($item->getInvoiceLabel()) {
|
->createQueryBuilder('r')
|
||||||
$temp['label'] = [
|
->where('r.doc = :docId')
|
||||||
'code' => $item->getInvoiceLabel()->getCode(),
|
->andWhere('r.person IS NOT NULL')
|
||||||
'label' => $item->getInvoiceLabel()->getLabel()
|
->setParameter('docId', $doc['id'])
|
||||||
];
|
->setMaxResults(1)
|
||||||
}
|
->getQuery()
|
||||||
|
->getOneOrNullResult();
|
||||||
|
$item['person'] = $mainRow && $mainRow->getPerson() ? Explore::ExplorePerson($mainRow->getPerson()) : null;
|
||||||
|
|
||||||
$temp['relatedDocsCount'] = count($item->getRelatedDocs());
|
// محاسبه پرداختیها
|
||||||
$pays = 0;
|
$relatedDocs = $entityManager->getRepository(HesabdariDoc::class)
|
||||||
foreach ($item->getRelatedDocs() as $relatedDoc) {
|
->createQueryBuilder('rd')
|
||||||
$pays += $relatedDoc->getAmount();
|
->select('SUM(rd.amount) as total_pays, COUNT(rd.id) as count_docs')
|
||||||
}
|
->innerJoin('rd.relatedDocs', 'rel')
|
||||||
$temp['relatedDocsPays'] = $pays;
|
->where('rel.id = :sourceDocId')
|
||||||
|
->andWhere('rd.bid = :bidId')
|
||||||
|
->setParameter('sourceDocId', $doc['id'])
|
||||||
|
->setParameter('bidId', $acc['bid']->getId())
|
||||||
|
->getQuery()
|
||||||
|
->getSingleResult();
|
||||||
|
|
||||||
$temp['commodities'] = [];
|
$item['relatedDocsCount'] = (int) $relatedDocs['count_docs'];
|
||||||
foreach ($item->getHesabdariRows() as $item) {
|
$item['relatedDocsPays'] = $relatedDocs['total_pays'] ?? 0;
|
||||||
if ($item->getRef()->getCode() == '51') {
|
|
||||||
$temp['discountAll'] = $item->getBs();
|
// محاسبه کالاها و تخفیف/هزینه حمل
|
||||||
} elseif ($item->getRef()->getCode() == '90') {
|
$item['commodities'] = [];
|
||||||
$temp['transferCost'] = $item->getBd();
|
$item['discountAll'] = 0;
|
||||||
}
|
$item['transferCost'] = 0;
|
||||||
if ($item->getCommodity()) {
|
|
||||||
$temp['commodities'][] = Explore::ExploreCommodity($item->getCommodity(), $item->getCommdityCount());
|
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy(['doc' => $doc['id']]);
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
if ($row->getRef()->getCode() == '51') {
|
||||||
|
$item['discountAll'] = $row->getBs();
|
||||||
|
} elseif ($row->getRef()->getCode() == '90') {
|
||||||
|
$item['transferCost'] = $row->getBd();
|
||||||
|
} elseif ($row->getCommodity()) {
|
||||||
|
$item['commodities'][] = Explore::ExploreCommodity($row->getCommodity(), $row->getCommdityCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!array_key_exists('discountAll', $temp))
|
|
||||||
$temp['discountAll'] = 0;
|
|
||||||
if (!array_key_exists('transferCost', $temp))
|
|
||||||
$temp['transferCost'] = 0;
|
|
||||||
|
|
||||||
$dataTemp[] = $temp;
|
$dataTemp[] = $item;
|
||||||
}
|
}
|
||||||
return $this->json($dataTemp);
|
|
||||||
|
return $this->json([
|
||||||
|
'items' => $dataTemp,
|
||||||
|
'total' => (int) $totalItems,
|
||||||
|
'page' => $page,
|
||||||
|
'perPage' => $perPage,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/api/buy/posprinter/invoice', name: 'app_buy_posprinter_invoice')]
|
#[Route('/api/buy/posprinter/invoice', name: 'app_buy_posprinter_invoice')]
|
||||||
|
@ -425,6 +528,8 @@ class BuyController extends AbstractController
|
||||||
return $this->json(['id' => $pdfPid]);
|
return $this->json(['id' => $pdfPid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[Route('/api/buy/print/invoice', name: 'app_buy_print_invoice')]
|
#[Route('/api/buy/print/invoice', name: 'app_buy_print_invoice')]
|
||||||
public function app_buy_print_invoice(Printers $printers, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, TemplateRenderer $renderer): JsonResponse
|
public function app_buy_print_invoice(Printers $printers, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, TemplateRenderer $renderer): JsonResponse
|
||||||
{
|
{
|
||||||
|
@ -586,4 +691,43 @@ class BuyController extends AbstractController
|
||||||
}
|
}
|
||||||
return $this->json(['id' => $pdfPid]);
|
return $this->json(['id' => $pdfPid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/api/buy/approve/{code}', name: 'app_buy_approve', methods: ['POST'])]
|
||||||
|
public function approveBuyDoc(string $code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||||
|
{
|
||||||
|
$acc = $access->hasRole('buy');
|
||||||
|
if (!$acc) throw $this->createAccessDeniedException();
|
||||||
|
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
||||||
|
'bid' => $acc['bid'],
|
||||||
|
'code' => $code,
|
||||||
|
'money' => $acc['money']
|
||||||
|
]);
|
||||||
|
if (!$doc) throw $this->createNotFoundException('فاکتور یافت نشد');
|
||||||
|
$doc->setIsPreview(false);
|
||||||
|
$doc->setIsApproved(true);
|
||||||
|
$doc->setApprovedBy($this->getUser());
|
||||||
|
$entityManager->persist($doc);
|
||||||
|
$entityManager->flush();
|
||||||
|
return $this->json(['result' => 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/buy/payment/approve/{code}', name: 'app_buy_payment_approve', methods: ['POST'])]
|
||||||
|
public function approveBuyPayment(string $code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
|
||||||
|
{
|
||||||
|
$acc = $access->hasRole('buy');
|
||||||
|
if (!$acc) throw $this->createAccessDeniedException();
|
||||||
|
$paymentDoc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
||||||
|
'bid' => $acc['bid'],
|
||||||
|
'code' => $code,
|
||||||
|
'money' => $acc['money'],
|
||||||
|
'type' => 'buy_pay'
|
||||||
|
]);
|
||||||
|
if (!$paymentDoc) throw $this->createNotFoundException('سند پرداخت یافت نشد');
|
||||||
|
$paymentDoc->setIsPreview(false);
|
||||||
|
$paymentDoc->setIsApproved(true);
|
||||||
|
$paymentDoc->setApprovedBy($this->getUser());
|
||||||
|
$entityManager->persist($paymentDoc);
|
||||||
|
$entityManager->flush();
|
||||||
|
return $this->json(['result' => 0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,11 +728,11 @@ class HesabdariDoc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Approval fields
|
// Approval fields
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true, options: ['default' => false])]
|
||||||
private ?bool $isPreview = null;
|
private ?bool $isPreview = false;
|
||||||
|
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true, options: ['default' => true])]
|
||||||
private ?bool $isApproved = null;
|
private ?bool $isApproved = true;
|
||||||
|
|
||||||
#[ORM\ManyToOne]
|
#[ORM\ManyToOne]
|
||||||
#[ORM\JoinColumn(nullable: true)]
|
#[ORM\JoinColumn(nullable: true)]
|
||||||
|
|
|
@ -109,7 +109,11 @@ class HesabdariDocRepository extends ServiceEntityRepository
|
||||||
$qb = $this->createQueryBuilder('h');
|
$qb = $this->createQueryBuilder('h');
|
||||||
|
|
||||||
foreach ($criteria as $field => $value) {
|
foreach ($criteria as $field => $value) {
|
||||||
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
|
if ($field === 'bid' && is_object($value)) {
|
||||||
|
$qb->andWhere("h.$field = :$field")->setParameter($field, $value->getId());
|
||||||
|
} else {
|
||||||
|
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$qb->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))');
|
$qb->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))');
|
||||||
|
@ -129,7 +133,11 @@ class HesabdariDocRepository extends ServiceEntityRepository
|
||||||
$qb = $this->createQueryBuilder('h');
|
$qb = $this->createQueryBuilder('h');
|
||||||
|
|
||||||
foreach ($criteria as $field => $value) {
|
foreach ($criteria as $field => $value) {
|
||||||
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
|
if ($field === 'bid' && is_object($value)) {
|
||||||
|
$qb->andWhere("h.$field = :$field")->setParameter($field, $value->getId());
|
||||||
|
} else {
|
||||||
|
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($orderBy) {
|
if ($orderBy) {
|
||||||
|
@ -146,7 +154,11 @@ class HesabdariDocRepository extends ServiceEntityRepository
|
||||||
$qb = $this->createQueryBuilder('h');
|
$qb = $this->createQueryBuilder('h');
|
||||||
|
|
||||||
foreach ($criteria as $field => $value) {
|
foreach ($criteria as $field => $value) {
|
||||||
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
|
if ($field === 'bid' && is_object($value)) {
|
||||||
|
$qb->andWhere("h.$field = :$field")->setParameter($field, $value->getId());
|
||||||
|
} else {
|
||||||
|
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$qb->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))');
|
$qb->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))');
|
||||||
|
@ -175,4 +187,34 @@ class HesabdariDocRepository extends ServiceEntityRepository
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->getResult();
|
->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//include preview
|
||||||
|
public function findByIncludePreview(array $criteria, array $orderBy = null, $limit = null, $offset = null): array
|
||||||
|
{
|
||||||
|
$qb = $this->createQueryBuilder('h');
|
||||||
|
|
||||||
|
foreach ($criteria as $field => $value) {
|
||||||
|
if ($field === 'bid' && is_object($value)) {
|
||||||
|
$qb->andWhere("h.$field = :$field")->setParameter($field, $value->getId());
|
||||||
|
} else {
|
||||||
|
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($orderBy) {
|
||||||
|
foreach ($orderBy as $field => $direction) {
|
||||||
|
$qb->addOrderBy("h.$field", $direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($limit) {
|
||||||
|
$qb->setMaxResults($limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($offset) {
|
||||||
|
$qb->setFirstResult($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb->getQuery()->getResult();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,17 @@
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-list>
|
<v-list>
|
||||||
|
<v-list-subheader color="primary" v-if="checkApprover()">عملیات گروهی تایید</v-list-subheader>
|
||||||
|
<v-list-item v-if="currentTab === 'pending' && checkApprover()" class="text-dark" title="تایید فاکتورهای انتخابی" @click="approveSelectedInvoices">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-icon color="success" icon="mdi-check-decagram"></v-icon>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item v-if="currentTab === 'approved' && checkApprover()" class="text-dark" title="لغو تایید فاکتورهای انتخابی" @click="unapproveSelectedInvoices">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-icon color="red" icon="mdi-cancel"></v-icon>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
<v-list-subheader color="primary">تغییر برچسبها</v-list-subheader>
|
<v-list-subheader color="primary">تغییر برچسبها</v-list-subheader>
|
||||||
<v-list-item v-for="item in types" class="text-dark" :title="'تغییر به ' + item.label" @click="changeLabel(item)">
|
<v-list-item v-for="item in types" class="text-dark" :title="'تغییر به ' + item.label" @click="changeLabel(item)">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
|
@ -43,6 +54,14 @@
|
||||||
</v-menu>
|
</v-menu>
|
||||||
</v-toolbar>
|
</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
|
<v-text-field
|
||||||
hide-details
|
hide-details
|
||||||
|
@ -88,48 +107,50 @@
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
|
|
||||||
<!-- جدول اصلی -->
|
<!-- جدول اصلی -->
|
||||||
<EasyDataTable
|
<v-data-table-server v-model:items-per-page="serverOptions.rowsPerPage" v-model:page="serverOptions.page"
|
||||||
v-model:items-selected="itemsSelected"
|
:headers="visibleHeaders" :items="displayItems" :items-length="displayTotal" :loading="loading"
|
||||||
table-class-name="customize-table"
|
:no-data-text="$t('table.no_data')" v-model="itemsSelected" show-select class="elevation-1 data-table-wrapper" item-value="code"
|
||||||
show-index
|
:max-height="tableHeight" :header-props="{ class: 'custom-header' }" @update:options="updateServerOptions"
|
||||||
alternating
|
multi-sort>
|
||||||
:headers="headers"
|
<template v-slot:item.operation="{ item }">
|
||||||
:items="items"
|
|
||||||
theme-color="#1d90ff"
|
|
||||||
header-text-direction="center"
|
|
||||||
body-text-direction="center"
|
|
||||||
rowsPerPageMessage="تعداد سطر"
|
|
||||||
emptyMessage="اطلاعاتی برای نمایش وجود ندارد"
|
|
||||||
rowsOfPageSeparatorMessage="از"
|
|
||||||
:loading="loading"
|
|
||||||
>
|
|
||||||
<template #item-operation="{ code }">
|
|
||||||
<v-menu>
|
<v-menu>
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn variant="text" size="small" color="error" icon="mdi-menu" v-bind="props" />
|
<v-btn variant="text" size="small" color="error" icon="mdi-menu" v-bind="props" />
|
||||||
</template>
|
</template>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item :to="'/acc/accounting/view/' + code" class="text-dark" title="سند حسابداری">
|
<v-list-item :to="'/acc/accounting/view/' + item.code" class="text-dark" title="سند حسابداری">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="green-darken-4" icon="mdi-file"></v-icon>
|
<v-icon color="green-darken-4" icon="mdi-file"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item :to="'/acc/buy/view/' + code" class="text-dark" title="مشاهده">
|
<v-list-item :to="'/acc/buy/view/' + item.code" class="text-dark" title="مشاهده">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="green-darken-4" icon="mdi-eye"></v-icon>
|
<v-icon color="green-darken-4" icon="mdi-eye"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item @click="openPrintModal(code)" class="text-dark" title="خروجی PDF">
|
<v-list-item @click="openPrintModal(item.code)" class="text-dark" title="خروجی PDF">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon icon="mdi-file-pdf-box"></v-icon>
|
<v-icon icon="mdi-file-pdf-box"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item @click="canEditItem(code)" class="text-dark" title="ویرایش">
|
<v-list-item v-if="canShowApprovalButton(item)" class="text-dark" title="تایید فاکتور"
|
||||||
|
@click="approveInvoice(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)" class="text-dark" title="لغو تایید فاکتور"
|
||||||
|
@click="unapproveInvoice(item.code)">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-icon color="red">mdi-cancel</v-icon>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item @click="canEditItem(item.code)" class="text-dark" title="ویرایش">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon icon="mdi-file-edit"></v-icon>
|
<v-icon icon="mdi-file-edit"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item @click="deleteItem(code)" class="text-dark" title="حذف">
|
<v-list-item @click="deleteItem(item.code)" class="text-dark" title="حذف">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="deep-orange-accent-4" icon="mdi-trash-can"></v-icon>
|
<v-icon color="deep-orange-accent-4" icon="mdi-trash-can"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
|
@ -137,48 +158,56 @@
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
</template>
|
</template>
|
||||||
<template #item-label="{ label }">
|
<template v-slot:item.label="{ item }">
|
||||||
<span v-if="label">
|
<span v-if="item.label">
|
||||||
<span v-if="label.code == 'payed'" class="text-success">{{ label.label }}</span>
|
<span v-if="item.label.code == 'payed'" class="text-success">{{ item.label.label }}</span>
|
||||||
<span v-if="label.code == 'returned'" class="text-danger">{{ label.label }}</span>
|
<span v-if="item.label.code == 'returned'" class="text-danger">{{ item.label.label }}</span>
|
||||||
<span v-if="label.code == 'accepted'" class="text-info">{{ label.label }}</span>
|
<span v-if="item.label.code == 'accepted'" class="text-info">{{ item.label.label }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #item-des="{ des }">
|
<template v-slot:item.des="{ item }">
|
||||||
{{ des.replace("فاکتور خرید:", "") }}
|
{{ item.des.replace("فاکتور خرید:", "") }}
|
||||||
</template>
|
</template>
|
||||||
<template #item-relatedDocsCount="{ relatedDocsCount, relatedDocsPays }">
|
<template v-slot:item.relatedDocsCount="{ item }">
|
||||||
<span v-if="relatedDocsCount != '0'" class="text-success">
|
<span v-if="item.relatedDocsCount != '0'" class="text-success">
|
||||||
<v-icon small>mdi-currency-usd</v-icon>
|
{{ $filters.formatNumber(item.relatedDocsPays) }}
|
||||||
{{ $filters.formatNumber(relatedDocsPays) }}
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #item-amount="{ amount }">
|
<template v-slot:item.amount="{ item }">
|
||||||
<span class="text-dark">
|
<span class="text-dark">
|
||||||
{{ $filters.formatNumber(amount) }}
|
{{ $filters.formatNumber(item.amount) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #item-transferCost="{ transferCost }">
|
<template v-slot:item.transferCost="{ item }">
|
||||||
<span class="text-dark">
|
<span class="text-dark">
|
||||||
{{ $filters.formatNumber(transferCost) }}
|
{{ $filters.formatNumber(item.transferCost) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #item-discountAll="{ discountAll }">
|
<template v-slot:item.discountAll="{ item }">
|
||||||
<span class="text-dark">
|
<span class="text-dark">
|
||||||
{{ $filters.formatNumber(discountAll) }}
|
{{ $filters.formatNumber(item.discountAll) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #item-person="{ person }">
|
<template v-slot:item.person="{ item }">
|
||||||
<router-link :to="'/acc/persons/card/view/' + person.code">
|
<router-link v-if="item.person" :to="'/acc/persons/card/view/' + item.person.code">
|
||||||
{{ person.nikename }}
|
{{ item.person.nikename }}
|
||||||
|
</router-link>
|
||||||
|
<span v-else>-</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>
|
||||||
|
<template v-slot:item.code="{ item }">
|
||||||
|
<router-link :to="'/acc/buy/view/' + item.code">
|
||||||
|
{{ item.code }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
<template #item-code="{ code }">
|
</v-data-table-server>
|
||||||
<router-link :to="'/acc/buy/view/' + code">
|
|
||||||
{{ code }}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</EasyDataTable>
|
|
||||||
|
|
||||||
<!-- مودال چاپ -->
|
<!-- مودال چاپ -->
|
||||||
<v-dialog v-model="printModal" width="auto">
|
<v-dialog v-model="printModal" width="auto">
|
||||||
|
@ -236,48 +265,87 @@
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { ref } from "vue";
|
import { defineComponent, reactive, watch } from "vue";
|
||||||
|
import debounce from "lodash/debounce";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "list",
|
name: "list",
|
||||||
data: () => ({
|
data() {
|
||||||
printModal: false,
|
return {
|
||||||
printOptions: {
|
currentTab: 'approved',
|
||||||
pays: true,
|
printModal: false,
|
||||||
note: true,
|
printOptions: {
|
||||||
bidInfo: true,
|
pays: true,
|
||||||
taxInfo: true,
|
note: true,
|
||||||
discountInfo: true,
|
bidInfo: true,
|
||||||
selectedPrintCode: 0,
|
taxInfo: true,
|
||||||
paper: 'A4-L'
|
discountInfo: true,
|
||||||
|
selectedPrintCode: 0,
|
||||||
|
paper: 'A4-L'
|
||||||
|
},
|
||||||
|
paperSizes: [
|
||||||
|
{ title: 'A4 عمودی', value: 'A4' },
|
||||||
|
{ title: 'A4 افقی', value: 'A4-L' },
|
||||||
|
{ title: 'A5 عمودی', value: 'A5' },
|
||||||
|
{ title: 'A5 افقی', value: 'A5-L' },
|
||||||
|
],
|
||||||
|
business: { requireTwoStepApproval: false, approvers: { buyInvoice: null } },
|
||||||
|
currentUser: { email: '', owner: false },
|
||||||
|
sumSelected: 0,
|
||||||
|
sumTotal: 0,
|
||||||
|
itemsSelected: [],
|
||||||
|
searchValue: '',
|
||||||
|
types: [],
|
||||||
|
loading: false,
|
||||||
|
items: [],
|
||||||
|
itemsApproved: [],
|
||||||
|
itemsPending: [],
|
||||||
|
total: 0,
|
||||||
|
totalApproved: 0,
|
||||||
|
totalPending: 0,
|
||||||
|
serverOptions: reactive({
|
||||||
|
page: 1,
|
||||||
|
rowsPerPage: 10,
|
||||||
|
sortBy: [],
|
||||||
|
}),
|
||||||
|
allHeaders: [
|
||||||
|
{ title: "عملیات", value: "operation", sortable: false, visible: true, width: 100 },
|
||||||
|
{ title: "فاکتور", value: "code", sortable: true, visible: true, width: 120 },
|
||||||
|
{ title: "تاریخ", value: "date", sortable: true, visible: true, width: 120 },
|
||||||
|
{ title: "خریدار", value: "person", sortable: true, visible: true, width: 150 },
|
||||||
|
{ title: "وضعیت تایید", value: "approvalStatus", sortable: true, visible: true, width: 150 },
|
||||||
|
{ title: "تاییدکننده", value: "approvedBy", sortable: true, visible: true, width: 120 },
|
||||||
|
{ title: "تخفیف", value: "discountAll", sortable: true, visible: true, width: 120 },
|
||||||
|
{ title: "حمل و نقل", value: "transferCost", sortable: true, visible: true, width: 120 },
|
||||||
|
{ title: "مبلغ", value: "amount", sortable: true, visible: true, width: 150 },
|
||||||
|
{ title: "پرداختی", value: "relatedDocsCount", sortable: true, visible: true, width: 150 },
|
||||||
|
{ title: "برچسب", value: "label", sortable: true, visible: true, width: 120 },
|
||||||
|
{ title: "شرح", value: "des", sortable: true, visible: true, minWidth: 200 },
|
||||||
|
],
|
||||||
|
tableHeight: 500,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
visibleHeaders() {
|
||||||
|
return this.allHeaders.filter(header => {
|
||||||
|
if ((header.value === 'approvalStatus' || header.value === 'approvedBy') && !this.business.requireTwoStepApproval) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return header.visible;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
paperSizes: [
|
tableHeight() {
|
||||||
{ title: 'A4 عمودی', value: 'A4' },
|
return window.innerHeight - 200;
|
||||||
{ title: 'A4 افقی', value: 'A4-L' },
|
},
|
||||||
{ title: 'A5 عمودی', value: 'A5' },
|
displayItems() {
|
||||||
{ title: 'A5 افقی', value: 'A5-L' },
|
if (!this.business.requireTwoStepApproval) return this.items;
|
||||||
],
|
return this.currentTab === 'pending' ? this.itemsPending : this.itemsApproved;
|
||||||
sumSelected: 0,
|
},
|
||||||
sumTotal: 0,
|
displayTotal() {
|
||||||
itemsSelected: [],
|
if (!this.business.requireTwoStepApproval) return this.total;
|
||||||
searchValue: '',
|
return this.currentTab === 'pending' ? this.totalPending : this.totalApproved;
|
||||||
types: [],
|
},
|
||||||
loading: ref(true),
|
},
|
||||||
items: [],
|
|
||||||
orgItems: [],
|
|
||||||
headers: [
|
|
||||||
{ text: "عملیات", value: "operation" },
|
|
||||||
{ text: "فاکتور", value: "code", sortable: true },
|
|
||||||
{ text: "تاریخ", value: "date", sortable: true },
|
|
||||||
{ text: "خریدار", value: "person", sortable: true },
|
|
||||||
{ text: "تخفیف", value: "discountAll", sortable: true },
|
|
||||||
{ text: "حمل و نقل", value: "transferCost", sortable: true },
|
|
||||||
{ text: "مبلغ", value: "amount", sortable: true },
|
|
||||||
{ text: "پرداختی", value: "relatedDocsCount", sortable: true },
|
|
||||||
{ text: "برچسب", value: "label", width: 100 },
|
|
||||||
{ text: "شرح", value: "des", sortable: true },
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
methods: {
|
methods: {
|
||||||
openPrintModal(code) {
|
openPrintModal(code) {
|
||||||
this.printOptions.selectedPrintCode = code;
|
this.printOptions.selectedPrintCode = code;
|
||||||
|
@ -293,7 +361,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
axios.post('/api/buy/label/change', {
|
axios.post('/api/buy/label/change', {
|
||||||
'items': this.itemsSelected,
|
'items': this.itemsSelected.filter(item => item && typeof item === 'string').map(item => ({ code: item })),
|
||||||
'label': label
|
'label': label
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
@ -315,6 +383,130 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
checkApprover() {
|
||||||
|
return this.business.requireTwoStepApproval && (this.business.approvers.buyInvoice == this.currentUser.email || this.currentUser.owner === true);
|
||||||
|
},
|
||||||
|
async loadBusinessInfo() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/api/business/get/info/' + localStorage.getItem('activeBid'));
|
||||||
|
this.business = response.data || { requireTwoStepApproval: false, approvers: { buyInvoice: null } };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading business info:', error);
|
||||||
|
this.business = { requireTwoStepApproval: false, approvers: { buyInvoice: null } };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadCurrentUser() {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/api/business/get/user/permissions');
|
||||||
|
this.currentUser = response.data || { email: '', owner: false };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading current user:', error);
|
||||||
|
this.currentUser = { email: '', owner: false };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getApprovalStatusText(item) {
|
||||||
|
if (!item || !this.business?.requireTwoStepApproval) return 'تایید دو مرحلهای غیرفعال';
|
||||||
|
if (item.isPreview) return 'در انتظار تایید';
|
||||||
|
if (item.isApproved) return 'تایید شده';
|
||||||
|
return 'تایید شده';
|
||||||
|
},
|
||||||
|
getApprovalStatusColor(item) {
|
||||||
|
if (!item || !this.business?.requireTwoStepApproval) return 'default';
|
||||||
|
if (item.isPreview) return 'warning';
|
||||||
|
if (item.isApproved) return 'success';
|
||||||
|
return 'success';
|
||||||
|
},
|
||||||
|
canShowApprovalButton(item) {
|
||||||
|
if (!this.checkApprover()) return false;
|
||||||
|
if (item?.isApproved) return false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
async approveInvoice(code) {
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
await axios.post(`/api/approval/approve/buy/${code}`);
|
||||||
|
await this.loadData();
|
||||||
|
Swal.fire({ text: 'فاکتور تایید شد', icon: 'success', confirmButtonText: 'قبول' });
|
||||||
|
} catch (error) {
|
||||||
|
Swal.fire({ text: 'خطا در تایید فاکتور: ' + (error.response?.data?.message || error.message), icon: 'error', confirmButtonText: 'قبول' });
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canShowUnapproveButton(item) {
|
||||||
|
return !this.canShowApprovalButton(item) && this.checkApprover();
|
||||||
|
},
|
||||||
|
async unapproveInvoice(code) {
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
await axios.post(`/api/approval/unapprove/buy/${code}`);
|
||||||
|
await this.loadData();
|
||||||
|
Swal.fire({ text: 'تایید فاکتور لغو شد', icon: 'success', confirmButtonText: 'قبول' });
|
||||||
|
} catch (error) {
|
||||||
|
Swal.fire({ text: 'خطا در لغو تایید فاکتور: ' + (error.response?.data?.message || error.message), icon: 'error', confirmButtonText: 'قبول' });
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getItemByCode(code) {
|
||||||
|
return this.items.find(item => item.code === code);
|
||||||
|
},
|
||||||
|
approveSelectedInvoices() {
|
||||||
|
if (this.itemsSelected.length === 0) {
|
||||||
|
Swal.fire({ text: 'هیچ موردی انتخاب نشده است.', icon: 'warning', confirmButtonText: 'قبول' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedInvoices = this.items.filter(inv => this.itemsSelected.includes(inv.code));
|
||||||
|
if (selectedInvoices.some(inv => !(!inv.isApproved && inv.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;
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
await axios.post(`/api/approval/approve/group/buy`, {
|
||||||
|
'docIds': this.itemsSelected.filter(item => item && typeof item === 'string')
|
||||||
|
});
|
||||||
|
Swal.fire({ text: 'فاکتورها تایید شدند.', icon: 'success', confirmButtonText: 'قبول' });
|
||||||
|
this.itemsSelected = [];
|
||||||
|
this.loadData();
|
||||||
|
} catch (e) {
|
||||||
|
Swal.fire({ text: 'خطا در تایید فاکتورها', icon: 'error', confirmButtonText: 'قبول' });
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
unapproveSelectedInvoices() {
|
||||||
|
if (this.itemsSelected.length === 0) {
|
||||||
|
Swal.fire({ text: 'هیچ موردی انتخاب نشده است.', icon: 'warning', confirmButtonText: 'قبول' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedInvoices = this.items.filter(inv => this.itemsSelected.includes(inv.code));
|
||||||
|
if (selectedInvoices.some(inv => !(inv.isApproved && !inv.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;
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
await axios.post(`/api/approval/unapprove/group/buy`, {
|
||||||
|
'docIds': this.itemsSelected.filter(item => item && typeof item === 'string')
|
||||||
|
});
|
||||||
|
Swal.fire({ text: 'تایید فاکتورها لغو شد.', icon: 'success', confirmButtonText: 'قبول' });
|
||||||
|
this.itemsSelected = [];
|
||||||
|
this.loadData();
|
||||||
|
} catch (e) {
|
||||||
|
Swal.fire({ text: 'خطا در لغو تایید فاکتورها', icon: 'error', confirmButtonText: 'قبول' });
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
filterTable() {
|
filterTable() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
let calcItems = [];
|
let calcItems = [];
|
||||||
|
@ -350,27 +542,70 @@ export default {
|
||||||
}
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
loadData() {
|
updateServerOptions(options) {
|
||||||
axios.post("/api/printers/options/info").then((response) => {
|
this.serverOptions.page = options.page;
|
||||||
this.printOptions = response.data.buy;
|
this.serverOptions.rowsPerPage = options.itemsPerPage;
|
||||||
});
|
this.serverOptions.sortBy = options.sortBy || [];
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
debouncedLoadData: debounce(function () {
|
||||||
|
this.loadData();
|
||||||
|
}, 300),
|
||||||
|
|
||||||
axios.post('/api/invoice/types', {
|
async loadData() {
|
||||||
type: 'buy'
|
this.loading = true;
|
||||||
}).then((response) => {
|
try {
|
||||||
this.types = response.data;
|
if (!this.printOptions.selectedPrintCode) {
|
||||||
});
|
const printResponse = await axios.post("/api/printers/options/info");
|
||||||
|
this.printOptions = printResponse.data.buy || this.printOptions;
|
||||||
|
}
|
||||||
|
|
||||||
axios.post('/api/buy/docs/search', {
|
const typesResponse = await axios.post('/api/invoice/types', { type: 'buy' });
|
||||||
type: 'buy'
|
this.types = typesResponse.data.map(t => ({
|
||||||
}).then((response) => {
|
...t,
|
||||||
this.items = response.data;
|
checked: this.types.find(x => x.code === t.code)?.checked ?? false
|
||||||
this.orgItems = response.data;
|
}));
|
||||||
this.items.forEach((item) => {
|
|
||||||
this.sumTotal += parseInt(item.amount);
|
const response = await axios.post('/api/buy/docs/search', {
|
||||||
|
type: 'buy',
|
||||||
|
search: this.searchValue,
|
||||||
|
page: this.serverOptions.page,
|
||||||
|
perPage: this.serverOptions.rowsPerPage,
|
||||||
|
types: this.types.filter(t => t.checked).map(t => t.code),
|
||||||
|
sortBy: this.serverOptions.sortBy,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const all = (response.data.items || []).map(item => ({
|
||||||
|
...item,
|
||||||
|
approvalStatus: this.business.requireTwoStepApproval ?
|
||||||
|
(item.isPreview ? 'pending' : 'approved') : 'disabled',
|
||||||
|
approvedBy: item.approvedBy || null
|
||||||
|
})).filter(item => item.code && typeof item.code !== 'undefined');
|
||||||
|
|
||||||
|
this.items = all;
|
||||||
|
this.total = Number(response.data.total) || 0;
|
||||||
|
|
||||||
|
if (this.business.requireTwoStepApproval) {
|
||||||
|
this.itemsApproved = all.filter(i => i.isApproved === true);
|
||||||
|
this.itemsPending = all.filter(i => i.isPreview === true && i.isApproved !== true);
|
||||||
|
this.totalApproved = this.itemsApproved.length;
|
||||||
|
this.totalPending = this.itemsPending.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sumTotal = this.displayItems.reduce((sum, item) => sum + parseInt(item.amount || 0), 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading data:', error);
|
||||||
|
this.items = [];
|
||||||
|
this.total = 0;
|
||||||
|
this.sumTotal = 0;
|
||||||
|
Swal.fire({
|
||||||
|
text: 'خطا در بارگذاری دادهها: ' + error.message,
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
}
|
||||||
},
|
},
|
||||||
canEditItem(code) {
|
canEditItem(code) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
@ -405,7 +640,7 @@ export default {
|
||||||
if (result.isConfirmed) {
|
if (result.isConfirmed) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
axios.post('/api/accounting/remove/group', {
|
axios.post('/api/accounting/remove/group', {
|
||||||
'items': this.itemsSelected
|
'items': this.itemsSelected.filter(item => item && typeof item === 'string')
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (response.data.result == 1) {
|
if (response.data.result == 1) {
|
||||||
|
@ -474,55 +709,53 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
updateServerOptions(options) {
|
||||||
|
this.serverOptions.page = options.page;
|
||||||
|
this.serverOptions.rowsPerPage = options.itemsPerPage;
|
||||||
|
this.serverOptions.sortBy = options.sortBy || [];
|
||||||
|
this.loadData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeMount() {
|
async beforeMount() {
|
||||||
|
await this.loadBusinessInfo();
|
||||||
|
await this.loadCurrentUser();
|
||||||
this.loadData();
|
this.loadData();
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
itemsSelected: {
|
itemsSelected: {
|
||||||
handler: function (val) {
|
handler: function (val) {
|
||||||
this.sumSelected = 0;
|
this.sumSelected = 0;
|
||||||
this.itemsSelected.forEach((item) => {
|
this.itemsSelected.forEach((code) => {
|
||||||
if (typeof item.amount.valueOf() === "string") {
|
if (code && typeof code === 'string') {
|
||||||
this.sumSelected += parseInt(item.amount.replaceAll(",", ""));
|
const item = this.items.find(item => item.code === code);
|
||||||
} else {
|
if (item && item.amount) {
|
||||||
this.sumSelected += item.amount;
|
if (typeof item.amount === "string") {
|
||||||
|
this.sumSelected += parseInt(item.amount.replaceAll(",", ""));
|
||||||
|
} else {
|
||||||
|
this.sumSelected += item.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
},
|
},
|
||||||
searchValue: {
|
searchValue: {
|
||||||
|
handler() {
|
||||||
|
this.serverOptions.page = 1;
|
||||||
|
this.debouncedLoadData();
|
||||||
|
},
|
||||||
|
immediate: false,
|
||||||
|
},
|
||||||
|
currentTab: {
|
||||||
handler: function (val) {
|
handler: function (val) {
|
||||||
if (this.searchValue == '') {
|
this.loadData();
|
||||||
this.items = this.orgItems;
|
|
||||||
} else {
|
|
||||||
let temp = [];
|
|
||||||
this.orgItems.forEach((item) => {
|
|
||||||
if (item.person.nikename.includes(this.searchValue)) {
|
|
||||||
temp.push(item);
|
|
||||||
} else if (item.date.includes(this.searchValue)) {
|
|
||||||
temp.push(item);
|
|
||||||
} else if (item.amount.toString().includes(this.searchValue)) {
|
|
||||||
temp.push(item);
|
|
||||||
} else if (item.des.includes(this.searchValue)) {
|
|
||||||
temp.push(item);
|
|
||||||
} else if (item.code.includes(this.searchValue)) {
|
|
||||||
temp.push(item);
|
|
||||||
} else if (item.label) {
|
|
||||||
if (item.label.label.includes(this.searchValue)) {
|
|
||||||
temp.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.items = temp;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
deep: false
|
deep: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -506,6 +506,7 @@ export default {
|
||||||
bid: {
|
bid: {
|
||||||
maliyatafzode: 0
|
maliyatafzode: 0
|
||||||
},
|
},
|
||||||
|
business: { requireTwoStepApproval: false, approvers: { buyInvoice: null } },
|
||||||
desSubmit: {
|
desSubmit: {
|
||||||
id: '',
|
id: '',
|
||||||
des: ''
|
des: ''
|
||||||
|
@ -871,6 +872,7 @@ export default {
|
||||||
//load business info
|
//load business info
|
||||||
axios.post('/api/business/get/info/' + localStorage.getItem('activeBid')).then((response) => {
|
axios.post('/api/business/get/info/' + localStorage.getItem('activeBid')).then((response) => {
|
||||||
this.bid = response.data;
|
this.bid = response.data;
|
||||||
|
this.business = response.data || { requireTwoStepApproval: false, approvers: { buyInvoice: null } };
|
||||||
if (this.bid.maliyatafzode == 0) {
|
if (this.bid.maliyatafzode == 0) {
|
||||||
this.maliyatCheck = false;
|
this.maliyatCheck = false;
|
||||||
}
|
}
|
||||||
|
@ -964,7 +966,8 @@ export default {
|
||||||
rows: this.items,
|
rows: this.items,
|
||||||
discountAll: this.data.discountAll,
|
discountAll: this.data.discountAll,
|
||||||
transferCost: this.data.transferCost,
|
transferCost: this.data.transferCost,
|
||||||
update: this.$route.params.id
|
update: this.$route.params.id,
|
||||||
|
business: this.business
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (response.data.code == 0) {
|
if (response.data.code == 0) {
|
||||||
|
|
|
@ -57,6 +57,7 @@ export default defineComponent({
|
||||||
bid: {
|
bid: {
|
||||||
legal_name: '',
|
legal_name: '',
|
||||||
},
|
},
|
||||||
|
business: { requireTwoStepApproval: false, approvers: { buyInvoice: null } },
|
||||||
item: {
|
item: {
|
||||||
doc: {
|
doc: {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
@ -90,6 +91,18 @@ export default defineComponent({
|
||||||
navigator.clipboard.writeText(this.shortlink_url);
|
navigator.clipboard.writeText(this.shortlink_url);
|
||||||
this.copy_label = 'کپی شد !';
|
this.copy_label = 'کپی شد !';
|
||||||
},
|
},
|
||||||
|
getApprovalStatusText(item) {
|
||||||
|
if (!this.business?.requireTwoStepApproval) return 'تایید دو مرحلهای غیرفعال';
|
||||||
|
if (item.isPreview) return 'در انتظار تایید';
|
||||||
|
if (item.isApproved) return 'تایید شده';
|
||||||
|
return 'تایید شده';
|
||||||
|
},
|
||||||
|
getApprovalStatusColor(item) {
|
||||||
|
if (!this.business?.requireTwoStepApproval) return 'default';
|
||||||
|
if (item.isPreview) return 'warning';
|
||||||
|
if (item.isApproved) return 'success';
|
||||||
|
return 'success';
|
||||||
|
},
|
||||||
loadData() {
|
loadData() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.commoditys = [];
|
this.commoditys = [];
|
||||||
|
@ -127,6 +140,7 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
axios.post('/api/business/get/info/' + localStorage.getItem('activeBid')).then((response) => {
|
axios.post('/api/business/get/info/' + localStorage.getItem('activeBid')).then((response) => {
|
||||||
|
this.business = response.data || { requireTwoStepApproval: false, approvers: { buyInvoice: null } };
|
||||||
this.bid = response.data;
|
this.bid = response.data;
|
||||||
});
|
});
|
||||||
axios.post("/api/printers/options/info").then((response) => {
|
axios.post("/api/printers/options/info").then((response) => {
|
||||||
|
@ -220,6 +234,11 @@ export default defineComponent({
|
||||||
</button>
|
</button>
|
||||||
<i class="fas fa-file-invoice-dollar"></i>
|
<i class="fas fa-file-invoice-dollar"></i>
|
||||||
مشاهده فاکتور
|
مشاهده فاکتور
|
||||||
|
<span v-if="business.requireTwoStepApproval" class="ms-2">
|
||||||
|
<v-chip :color="getApprovalStatusColor(item.doc)" size="small">
|
||||||
|
{{ getApprovalStatusText(item.doc) }}
|
||||||
|
</v-chip>
|
||||||
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="block-options">
|
<div class="block-options">
|
||||||
<archive-upload v-if="this.item.doc.id != 0" :docid="this.item.doc.id" doctype="buy" cat="buy"></archive-upload>
|
<archive-upload v-if="this.item.doc.id != 0" :docid="this.item.doc.id" doctype="buy" cat="buy"></archive-upload>
|
||||||
|
|
|
@ -64,7 +64,7 @@
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<!-- Tabs for two-step approval -->
|
<!-- Tabs for two-step approval -->
|
||||||
<div v-if="business.requireTwoStepApproval" class="px-2 pt-2">
|
<div v-if="business.requireTwoStepApproval" class="px-2 pt-2">
|
||||||
<v-tabs v-model="currentTab" color="primary" density="comfortable">
|
<v-tabs v-model="currentTab" color="primary" density="comfortable" grow>
|
||||||
<v-tab value="approved">فاکتورهای تایید شده</v-tab>
|
<v-tab value="approved">فاکتورهای تایید شده</v-tab>
|
||||||
<v-tab value="pending">فاکتورهای در انتظار تایید</v-tab>
|
<v-tab value="pending">فاکتورهای در انتظار تایید</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
|
|
Loading…
Reference in a new issue