some bug fix
This commit is contained in:
parent
b8ad5f2f49
commit
29e755fd2c
|
|
@ -167,7 +167,7 @@ class AccountingDocService
|
||||||
'code' => $params['id'],
|
'code' => $params['id'],
|
||||||
]);
|
]);
|
||||||
if (!$salary)
|
if (!$salary)
|
||||||
return ['error' => 'حقوق یافت نشد'];
|
return ['error' => 'تنخواه یافت نشد'];
|
||||||
|
|
||||||
// Check if we should include preview documents
|
// Check if we should include preview documents
|
||||||
$includePreview = $params['includePreview'] ?? false;
|
$includePreview = $params['includePreview'] ?? false;
|
||||||
|
|
|
||||||
|
|
@ -441,18 +441,23 @@ class BankController extends AbstractController
|
||||||
'توضیحات',
|
'توضیحات',
|
||||||
'شرح سند',
|
'شرح سند',
|
||||||
'تفصیل',
|
'تفصیل',
|
||||||
|
'طرف حسابها',
|
||||||
'بستانکار',
|
'بستانکار',
|
||||||
'بدهکار',
|
'بدهکار',
|
||||||
'سال مالی',
|
'سال مالی',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
foreach ($transactions as $transaction) {
|
foreach ($transactions as $transaction) {
|
||||||
|
// استخراج طرف حسابها برای این تراکنش
|
||||||
|
$counterpartAccounts = $this->getCounterpartAccountsForTransaction($transaction, $bank, $entityManager);
|
||||||
|
|
||||||
$arrayEntity[] = [
|
$arrayEntity[] = [
|
||||||
$transaction->getId(),
|
$transaction->getId(),
|
||||||
$transaction->getDoc()->getDate(),
|
$transaction->getDoc()->getDate(),
|
||||||
$transaction->getDes(),
|
$transaction->getDes(),
|
||||||
$transaction->getDoc()->getDes(),
|
$transaction->getDoc()->getDes(),
|
||||||
$transaction->getRef()->getName(),
|
$transaction->getRef()->getName(),
|
||||||
|
$counterpartAccounts,
|
||||||
$transaction->getBs(),
|
$transaction->getBs(),
|
||||||
$transaction->getBd(),
|
$transaction->getBd(),
|
||||||
$transaction->getYear()->getlabel()
|
$transaction->getYear()->getlabel()
|
||||||
|
|
@ -466,6 +471,57 @@ class BankController extends AbstractController
|
||||||
return new BinaryFileResponse($filePath);
|
return new BinaryFileResponse($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* استخراج طرف حسابهای مربوط به یک تراکنش
|
||||||
|
*/
|
||||||
|
private function getCounterpartAccountsForTransaction($transaction, $bank, EntityManagerInterface $entityManager): string
|
||||||
|
{
|
||||||
|
$doc = $transaction->getDoc();
|
||||||
|
$bankCode = $bank->getCode();
|
||||||
|
|
||||||
|
// دریافت تمام ردیفهای مربوط به این سند
|
||||||
|
$docRows = $entityManager->getRepository(HesabdariRow::class)
|
||||||
|
->createQueryBuilder('hr')
|
||||||
|
->leftJoin('hr.bank', 'ba')
|
||||||
|
->leftJoin('hr.cashdesk', 'cd')
|
||||||
|
->leftJoin('hr.salary', 's')
|
||||||
|
->leftJoin('hr.person', 'p')
|
||||||
|
->where('hr.doc = :doc')
|
||||||
|
->setParameter('doc', $doc)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$accounts = [];
|
||||||
|
foreach ($docRows as $docRow) {
|
||||||
|
// بررسی اینکه آیا این ردیف طرف حساب است (نه بانک انتخابی)
|
||||||
|
$isCounterpart = false;
|
||||||
|
$accountName = '';
|
||||||
|
|
||||||
|
if ($docRow->getBank()) {
|
||||||
|
if ($docRow->getBank()->getCode() != $bankCode) {
|
||||||
|
$isCounterpart = true;
|
||||||
|
$accountName = 'بانک: ' . $docRow->getBank()->getName();
|
||||||
|
}
|
||||||
|
} elseif ($docRow->getCashdesk()) {
|
||||||
|
$isCounterpart = true;
|
||||||
|
$accountName = 'صندوق: ' . $docRow->getCashdesk()->getName();
|
||||||
|
} elseif ($docRow->getSalary()) {
|
||||||
|
$isCounterpart = true;
|
||||||
|
$accountName = 'تنخواه: ' . $docRow->getSalary()->getName();
|
||||||
|
} elseif ($docRow->getPerson()) {
|
||||||
|
$isCounterpart = true;
|
||||||
|
$accountName = 'شخص: ' . $docRow->getPerson()->getNikename();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isCounterpart) {
|
||||||
|
$amount = $docRow->getBd() > 0 ? $docRow->getBd() : $docRow->getBs();
|
||||||
|
$accounts[] = $accountName . ' (' . number_format($amount, 0, '.', ',') . ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' | ', $accounts);
|
||||||
|
}
|
||||||
|
|
||||||
#[Route('/api/bank/card/list/print', name: 'app_bank_card_list_print')]
|
#[Route('/api/bank/card/list/print', name: 'app_bank_card_list_print')]
|
||||||
public function app_bank_card_list_print(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
public function app_bank_card_list_print(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
||||||
{
|
{
|
||||||
|
|
@ -521,6 +577,12 @@ class BankController extends AbstractController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// اضافه کردن طرف حسابها به هر تراکنش
|
||||||
|
foreach ($transactions as $transaction) {
|
||||||
|
$transaction->counterpartAccounts = $this->getCounterpartAccountsForTransaction($transaction, $bank, $entityManager);
|
||||||
|
}
|
||||||
|
|
||||||
$pid = $provider->createPrint(
|
$pid = $provider->createPrint(
|
||||||
$acc['bid'],
|
$acc['bid'],
|
||||||
$this->getUser(),
|
$this->getUser(),
|
||||||
|
|
|
||||||
|
|
@ -491,18 +491,28 @@ class HesabdariController extends AbstractController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set approval status based on business settings
|
// وضعیت تایید: اگر autoApprove=true ارسال شده باشد، اجباری تایید شود
|
||||||
|
$autoApprove = isset($params['autoApprove']) ? (bool)$params['autoApprove'] : null;
|
||||||
$business = $acc['bid'];
|
$business = $acc['bid'];
|
||||||
if ($business->isRequireTwoStepApproval()) {
|
if ($autoApprove === true) {
|
||||||
// Two-step approval is enabled
|
$doc->setIsPreview(false);
|
||||||
|
$doc->setIsApproved(true);
|
||||||
|
$doc->setApprovedBy($this->getUser());
|
||||||
|
} elseif ($autoApprove === false) {
|
||||||
$doc->setIsPreview(true);
|
$doc->setIsPreview(true);
|
||||||
$doc->setIsApproved(false);
|
$doc->setIsApproved(false);
|
||||||
$doc->setApprovedBy(null);
|
$doc->setApprovedBy(null);
|
||||||
} else {
|
} else {
|
||||||
// Two-step approval is disabled - auto approve
|
// پیشفرض مطابق تنظیمات کسبوکار
|
||||||
$doc->setIsPreview(false);
|
if ($business->isRequireTwoStepApproval()) {
|
||||||
$doc->setIsApproved(true);
|
$doc->setIsPreview(true);
|
||||||
$doc->setApprovedBy($this->getUser());
|
$doc->setIsApproved(false);
|
||||||
|
$doc->setApprovedBy(null);
|
||||||
|
} else {
|
||||||
|
$doc->setIsPreview(false);
|
||||||
|
$doc->setIsApproved(true);
|
||||||
|
$doc->setApprovedBy($this->getUser());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('refData', $params))
|
if (array_key_exists('refData', $params))
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use App\Entity\Storeroom;
|
||||||
use App\Entity\StoreroomItem;
|
use App\Entity\StoreroomItem;
|
||||||
use App\Entity\StoreroomTicket;
|
use App\Entity\StoreroomTicket;
|
||||||
use App\Service\Explore;
|
use App\Service\Explore;
|
||||||
|
use App\Cog\AccountingDocService;
|
||||||
use App\Cog\PersonService;
|
use App\Cog\PersonService;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
|
@ -2926,4 +2927,123 @@ class PersonsController extends AbstractController
|
||||||
return $this->json(['success' => true, 'message' => "$successCount سند پرداخت تایید شد"]);
|
return $this->json(['success' => true, 'message' => "$successCount سند پرداخت تایید شد"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* دریافت طرف حسابهای مربوط به حساب بانکی
|
||||||
|
*/
|
||||||
|
#[Route('/api/person/bank/accounts/list', name: 'app_person_bank_accounts_list')]
|
||||||
|
public function app_bank_accounts_list(
|
||||||
|
Request $request,
|
||||||
|
Access $access,
|
||||||
|
EntityManagerInterface $entityManager,
|
||||||
|
AccountingDocService $accountingDocService
|
||||||
|
): JsonResponse {
|
||||||
|
$acc = $access->hasRole('getpay');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = json_decode($request->getContent(), true) ?? [];
|
||||||
|
$bankCode = $params['bankCode'] ?? null;
|
||||||
|
|
||||||
|
if (!$bankCode) {
|
||||||
|
return $this->json(['error' => 'کد بانک الزامی است'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// استفاده از AccountingDocService برای دریافت ردیفهای حسابداری
|
||||||
|
$searchParams = [
|
||||||
|
'type' => 'bank',
|
||||||
|
'id' => $bankCode,
|
||||||
|
'includePreview' => true // شامل اسناد پیشنمایش هم میشود
|
||||||
|
];
|
||||||
|
|
||||||
|
$rows = $accountingDocService->searchRows($searchParams, $acc);
|
||||||
|
|
||||||
|
if (isset($rows['error'])) {
|
||||||
|
return $this->json(['error' => $rows['error']], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// استخراج طرف حسابها از ردیفها
|
||||||
|
$accounts = [];
|
||||||
|
$processedDocs = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$docId = $row['code']; // کد سند
|
||||||
|
|
||||||
|
// اگر این سند قبلاً پردازش شده، رد کن
|
||||||
|
if (in_array($docId, $processedDocs)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$processedDocs[] = $docId;
|
||||||
|
|
||||||
|
// دریافت سند و ردیفهای مربوط به آن
|
||||||
|
$doc = $entityManager->getRepository(\App\Entity\HesabdariDoc::class)->findOneBy(['code' => $docId]);
|
||||||
|
if (!$doc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$docRows = $entityManager->getRepository(\App\Entity\HesabdariRow::class)
|
||||||
|
->createQueryBuilder('hr')
|
||||||
|
->leftJoin('hr.bank', 'ba')
|
||||||
|
->leftJoin('hr.cashdesk', 'cd')
|
||||||
|
->leftJoin('hr.salary', 's')
|
||||||
|
->leftJoin('hr.person', 'p')
|
||||||
|
->where('hr.doc = :doc')
|
||||||
|
->setParameter('doc', $doc)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$docAccounts = [];
|
||||||
|
foreach ($docRows as $docRow) {
|
||||||
|
// بررسی اینکه آیا این ردیف طرف حساب است (نه بانک انتخابی)
|
||||||
|
$isCounterpart = false;
|
||||||
|
$accountName = '';
|
||||||
|
$accountType = '';
|
||||||
|
$amount = 0;
|
||||||
|
|
||||||
|
if ($docRow->getBank()) {
|
||||||
|
if ($docRow->getBank()->getCode() != $bankCode) {
|
||||||
|
$isCounterpart = true;
|
||||||
|
$accountName = 'بانک: ' . $docRow->getBank()->getName();
|
||||||
|
$accountType = 'bank';
|
||||||
|
}
|
||||||
|
} elseif ($docRow->getCashdesk()) {
|
||||||
|
$isCounterpart = true;
|
||||||
|
$accountName = 'صندوق: ' . $docRow->getCashdesk()->getName();
|
||||||
|
$accountType = 'cashdesk';
|
||||||
|
} elseif ($docRow->getSalary()) {
|
||||||
|
$isCounterpart = true;
|
||||||
|
$accountName = 'تنخواه: ' . $docRow->getSalary()->getName();
|
||||||
|
$accountType = 'salary';
|
||||||
|
} elseif ($docRow->getPerson()) {
|
||||||
|
$isCounterpart = true;
|
||||||
|
$accountName = 'شخص: ' . $docRow->getPerson()->getNikename();
|
||||||
|
$accountType = 'person';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isCounterpart) {
|
||||||
|
$amount = $docRow->getBd() > 0 ? $docRow->getBd() : $docRow->getBs();
|
||||||
|
$docAccounts[] = [
|
||||||
|
'name' => $accountName,
|
||||||
|
'type' => $accountType,
|
||||||
|
'amount' => $amount,
|
||||||
|
'formattedAmount' => number_format($amount, 0, '.', ','),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($docAccounts)) {
|
||||||
|
$accounts[] = [
|
||||||
|
'docId' => $docId,
|
||||||
|
'date' => $row['date'],
|
||||||
|
'type' => $row['type'],
|
||||||
|
'des' => $row['des'],
|
||||||
|
'accounts' => $docAccounts
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json($accounts);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use App\Entity\BankAccount;
|
use App\Entity\BankAccount;
|
||||||
use App\Entity\Cashdesk;
|
use App\Entity\Cashdesk;
|
||||||
|
|
@ -800,11 +801,56 @@ class SellController extends AbstractController
|
||||||
// اولویت با پارامترهای ارسالی است
|
// اولویت با پارامترهای ارسالی است
|
||||||
$printOptions = array_merge($defaultOptions, $params['printOptions'] ?? []);
|
$printOptions = array_merge($defaultOptions, $params['printOptions'] ?? []);
|
||||||
|
|
||||||
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
$docRepo = $entityManager->getRepository(HesabdariDoc::class);
|
||||||
|
$doc = $docRepo->findOneBy([
|
||||||
'bid' => $acc['bid'],
|
'bid' => $acc['bid'],
|
||||||
'code' => $params['code'],
|
'code' => $params['code'],
|
||||||
'money' => $acc['money']
|
'money' => $acc['money']
|
||||||
]);
|
]);
|
||||||
|
// Fallback در صورت ناهمخوانی activeMoney
|
||||||
|
if (!$doc) {
|
||||||
|
$doc = $docRepo->findOneBy([
|
||||||
|
'bid' => $acc['bid'],
|
||||||
|
'code' => $params['code'],
|
||||||
|
'year' => $acc['year']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// Fallback با QueryBuilder بر اساس شناسه BID (عدم تکیه بر شیء)
|
||||||
|
if (!$doc) {
|
||||||
|
try {
|
||||||
|
$doc = $entityManager->createQueryBuilder()
|
||||||
|
->select('d')
|
||||||
|
->from(\App\Entity\HesabdariDoc::class, 'd')
|
||||||
|
->where('d.code = :code')
|
||||||
|
->andWhere('IDENTITY(d.bid) = :bidId')
|
||||||
|
->setParameter('code', (string)($params['code'] ?? ''))
|
||||||
|
->setParameter('bidId', $acc['bid']->getId())
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getOneOrNullResult();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$doc = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback نهایی فقط بر اساس کد (در صورت وجود چند کسبوکار، کنترل امنیت بعدی برقرار است)
|
||||||
|
if (!$doc) {
|
||||||
|
try {
|
||||||
|
$doc = $entityManager->createQueryBuilder()
|
||||||
|
->select('d')
|
||||||
|
->from(\App\Entity\HesabdariDoc::class, 'd')
|
||||||
|
->where('d.code = :code')
|
||||||
|
->setParameter('code', (string)($params['code'] ?? ''))
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getOneOrNullResult();
|
||||||
|
if ($doc && $doc->getBid()->getId() !== $acc['bid']->getId()) {
|
||||||
|
// سند پیدا شد ولی متعلق به کسبوکار دیگری است
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$doc = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!$doc)
|
if (!$doc)
|
||||||
throw $this->createNotFoundException();
|
throw $this->createNotFoundException();
|
||||||
$person = null;
|
$person = null;
|
||||||
|
|
@ -953,6 +999,10 @@ class SellController extends AbstractController
|
||||||
false,
|
false,
|
||||||
$printOptions['paper']
|
$printOptions['paper']
|
||||||
);
|
);
|
||||||
|
// اگر چاپ ابری انتخاب شده، فایل را به صف پرینترها اضافه کن
|
||||||
|
if (!empty($params['printers'])) {
|
||||||
|
$printers->addFile($pdfPid, $acc, "fastSellInvoice");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($params['posPrint'] == true) {
|
if ($params['posPrint'] == true) {
|
||||||
$pid = $provider->createPrint(
|
$pid = $provider->createPrint(
|
||||||
|
|
@ -977,9 +1027,23 @@ class SellController extends AbstractController
|
||||||
'showPercentDiscount' => $doc->getDiscountType() === 'percent',
|
'showPercentDiscount' => $doc->getDiscountType() === 'percent',
|
||||||
'discountPercent' => $doc->getDiscountPercent()
|
'discountPercent' => $doc->getDiscountPercent()
|
||||||
]),
|
]),
|
||||||
false
|
true
|
||||||
);
|
);
|
||||||
$printers->addFile($pid, $acc, "fastSellInvoice");
|
$printers->addFile($pid, $acc, "fastSellPosInvoice");
|
||||||
|
}
|
||||||
|
// چاپ قبض صندوق در صورت نیاز
|
||||||
|
if (!empty($params['posPrintRecp'])) {
|
||||||
|
$pid = $provider->createPrint(
|
||||||
|
$acc['bid'],
|
||||||
|
$this->getUser(),
|
||||||
|
$this->renderView('pdf/posPrinters/cashdesk.html.twig', [
|
||||||
|
'bid' => $acc['bid'],
|
||||||
|
'doc' => $doc,
|
||||||
|
'rows' => $doc->getHesabdariRows(),
|
||||||
|
]),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
$printers->addFile($pid, $acc, "fastSellCashdesk");
|
||||||
}
|
}
|
||||||
return $this->json(['id' => $pdfPid]);
|
return $this->json(['id' => $pdfPid]);
|
||||||
}
|
}
|
||||||
|
|
@ -1587,4 +1651,452 @@ class SellController extends AbstractController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/api/sell/list/excel', name: 'app_sell_list_excel', methods: ['POST'])]
|
||||||
|
public function app_sell_list_excel(
|
||||||
|
Provider $provider,
|
||||||
|
Request $request,
|
||||||
|
Access $access,
|
||||||
|
Log $log,
|
||||||
|
EntityManagerInterface $entityManager,
|
||||||
|
Jdate $jdate
|
||||||
|
): BinaryFileResponse|JsonResponse|StreamedResponse {
|
||||||
|
$acc = $access->hasRole('sell');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = json_decode($request->getContent(), true) ?? [];
|
||||||
|
$searchTerm = $params['search'] ?? '';
|
||||||
|
$types = $params['types'] ?? [];
|
||||||
|
$dateFilter = $params['dateFilter'] ?? 'all';
|
||||||
|
$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', 'sell')
|
||||||
|
->setParameter('money', $acc['money']);
|
||||||
|
|
||||||
|
// اعمال فیلترهای تاریخ
|
||||||
|
$today = $jdate->jdate('Y/m/d', time());
|
||||||
|
if ($dateFilter === 'today') {
|
||||||
|
$queryBuilder->andWhere('d.date = :today')
|
||||||
|
->setParameter('today', $today);
|
||||||
|
} elseif ($dateFilter === 'week') {
|
||||||
|
$weekStart = $jdate->jdate('Y/m/d', strtotime('-6 days'));
|
||||||
|
$queryBuilder->andWhere('d.date BETWEEN :weekStart AND :today')
|
||||||
|
->setParameter('weekStart', $weekStart)
|
||||||
|
->setParameter('today', $today);
|
||||||
|
} elseif ($dateFilter === 'month') {
|
||||||
|
$monthStart = $jdate->jdate('Y/m/01', time());
|
||||||
|
$queryBuilder->andWhere('d.date BETWEEN :monthStart AND :today')
|
||||||
|
->setParameter('monthStart', $monthStart)
|
||||||
|
->setParameter('today', $today);
|
||||||
|
}
|
||||||
|
|
||||||
|
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%");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($types)) {
|
||||||
|
$queryBuilder->andWhere('l.code IN (:types)')
|
||||||
|
->setParameter('types', $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
// اگر آیتمهای خاصی درخواست شدهاند
|
||||||
|
if (array_key_exists('items', $params)) {
|
||||||
|
$codes = array_map(function($item) { return $item['code']; }, $params['items']);
|
||||||
|
$queryBuilder->andWhere('d.code IN (:codes)')
|
||||||
|
->setParameter('codes', $codes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// اعمال مرتبسازی
|
||||||
|
if (!empty($sortBy)) {
|
||||||
|
foreach ($sortBy as $sort) {
|
||||||
|
$key = $sort['key'] ?? 'id';
|
||||||
|
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? 'DESC' : 'ASC';
|
||||||
|
if ($key === 'profit' || $key === 'receivedAmount') {
|
||||||
|
continue; // اینها توی PHP مرتب میشن
|
||||||
|
} elseif (in_array($key, ['id', 'dateSubmit', 'date', 'type', 'code', 'des', 'amount', 'isPreview', 'isApproved'])) {
|
||||||
|
$queryBuilder->addOrderBy('d.' . $key, $direction);
|
||||||
|
} elseif ($key === 'submitter') {
|
||||||
|
$queryBuilder->addOrderBy('u.fullName', $direction);
|
||||||
|
} elseif ($key === 'label') {
|
||||||
|
$queryBuilder->addOrderBy('l.label', $direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$queryBuilder->orderBy('d.id', 'DESC');
|
||||||
|
}
|
||||||
|
|
||||||
|
$docs = $queryBuilder->getQuery()->getArrayResult();
|
||||||
|
|
||||||
|
$dataTemp = [];
|
||||||
|
foreach ($docs as $doc) {
|
||||||
|
$item = [
|
||||||
|
'id' => $doc['id'],
|
||||||
|
'dateSubmit' => $doc['dateSubmit'],
|
||||||
|
'date' => $doc['date'],
|
||||||
|
'type' => $doc['type'],
|
||||||
|
'code' => $doc['code'],
|
||||||
|
'des' => $doc['des'],
|
||||||
|
'amount' => $doc['amount'],
|
||||||
|
'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)
|
||||||
|
->createQueryBuilder('r')
|
||||||
|
->where('r.doc = :docId')
|
||||||
|
->andWhere('r.person IS NOT NULL')
|
||||||
|
->setParameter('docId', $doc['id'])
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getOneOrNullResult();
|
||||||
|
$item['person'] = $mainRow && $mainRow->getPerson() ? [
|
||||||
|
'id' => $mainRow->getPerson()->getId(),
|
||||||
|
'nikename' => $mainRow->getPerson()->getNikename(),
|
||||||
|
'code' => $mainRow->getPerson()->getCode()
|
||||||
|
] : null;
|
||||||
|
|
||||||
|
// استفاده از SQL خام برای محاسبه پرداختیها
|
||||||
|
$sql = "
|
||||||
|
SELECT SUM(rd.amount) as total_pays, COUNT(rd.id) as count_docs
|
||||||
|
FROM hesabdari_doc rd
|
||||||
|
JOIN hesabdari_doc_hesabdari_doc rel ON rel.hesabdari_doc_target = rd.id
|
||||||
|
WHERE rel.hesabdari_doc_source = :sourceDocId
|
||||||
|
AND rd.bid_id = :bidId
|
||||||
|
";
|
||||||
|
$stmt = $entityManager->getConnection()->prepare($sql);
|
||||||
|
$stmt->bindValue('sourceDocId', $doc['id']);
|
||||||
|
$stmt->bindValue('bidId', $acc['bid']->getId());
|
||||||
|
$result = $stmt->executeQuery()->fetchAssociative();
|
||||||
|
|
||||||
|
$relatedDocsPays = $result['total_pays'] ?? 0;
|
||||||
|
$relatedDocsCount = $result['count_docs'] ?? 0;
|
||||||
|
|
||||||
|
$item['relatedDocsCount'] = (int) $relatedDocsCount;
|
||||||
|
$item['relatedDocsPays'] = $relatedDocsPays;
|
||||||
|
$item['profit'] = $this->calculateProfit($doc['id'], $acc, $entityManager);
|
||||||
|
$item['discountAll'] = 0;
|
||||||
|
$item['transferCost'] = 0;
|
||||||
|
|
||||||
|
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy(['doc' => $doc]);
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
if ($row->getRef()->getCode() == '104') {
|
||||||
|
$item['discountAll'] = $row->getBd();
|
||||||
|
} elseif ($row->getRef()->getCode() == '61') {
|
||||||
|
$item['transferCost'] = $row->getBs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dataTemp[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// مرتبسازی توی PHP برای profit و receivedAmount
|
||||||
|
if (!empty($sortBy)) {
|
||||||
|
foreach ($sortBy as $sort) {
|
||||||
|
$key = $sort['key'] ?? 'id';
|
||||||
|
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? SORT_DESC : SORT_ASC;
|
||||||
|
if ($key === 'profit') {
|
||||||
|
usort($dataTemp, function ($a, $b) use ($direction) {
|
||||||
|
return $direction === SORT_ASC ? $a['profit'] - $b['profit'] : $b['profit'] - $a['profit'];
|
||||||
|
});
|
||||||
|
} elseif ($key === 'receivedAmount') {
|
||||||
|
usort($dataTemp, function ($a, $b) use ($direction) {
|
||||||
|
return $direction === SORT_ASC ? $a['relatedDocsPays'] - $b['relatedDocsPays'] : $b['relatedDocsPays'] - $a['relatedDocsPays'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// آمادهسازی دادهها برای Excel
|
||||||
|
$excelData = [];
|
||||||
|
$headers = [
|
||||||
|
'ردیف',
|
||||||
|
'کد فاکتور',
|
||||||
|
'تاریخ',
|
||||||
|
'خریدار',
|
||||||
|
'وضعیت تایید',
|
||||||
|
'تاییدکننده',
|
||||||
|
'تخفیف',
|
||||||
|
'حمل و نقل',
|
||||||
|
'مبلغ',
|
||||||
|
'سود فاکتور',
|
||||||
|
'پرداختی',
|
||||||
|
'برچسب',
|
||||||
|
'شرح'
|
||||||
|
];
|
||||||
|
$excelData[] = $headers;
|
||||||
|
|
||||||
|
foreach ($dataTemp as $index => $item) {
|
||||||
|
$excelData[] = [
|
||||||
|
$index + 1,
|
||||||
|
$item['code'],
|
||||||
|
$item['date'],
|
||||||
|
$item['person'] ? $item['person']['nikename'] : '-',
|
||||||
|
$item['isApproved'] ? 'تایید شده' : ($item['isPreview'] ? 'در انتظار تایید' : 'تایید شده'),
|
||||||
|
$item['approvedBy'] ? $item['approvedBy']['fullName'] : '-',
|
||||||
|
number_format($item['discountAll']),
|
||||||
|
number_format($item['transferCost']),
|
||||||
|
number_format($item['amount']),
|
||||||
|
number_format($item['profit']),
|
||||||
|
number_format($item['relatedDocsPays']),
|
||||||
|
$item['label'] ? $item['label']['label'] : '-',
|
||||||
|
$item['des']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BinaryFileResponse($provider->createExcellFromArray($excelData));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/sell/list/pdf', name: 'app_sell_list_pdf', methods: ['POST'])]
|
||||||
|
public function app_sell_list_pdf(
|
||||||
|
Provider $provider,
|
||||||
|
Request $request,
|
||||||
|
Access $access,
|
||||||
|
Log $log,
|
||||||
|
EntityManagerInterface $entityManager,
|
||||||
|
Jdate $jdate
|
||||||
|
): JsonResponse {
|
||||||
|
$acc = $access->hasRole('sell');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = json_decode($request->getContent(), true) ?? [];
|
||||||
|
$searchTerm = $params['search'] ?? '';
|
||||||
|
$types = $params['types'] ?? [];
|
||||||
|
$dateFilter = $params['dateFilter'] ?? 'all';
|
||||||
|
$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', 'sell')
|
||||||
|
->setParameter('money', $acc['money']);
|
||||||
|
|
||||||
|
// اعمال فیلترهای تاریخ
|
||||||
|
$today = $jdate->jdate('Y/m/d', time());
|
||||||
|
if ($dateFilter === 'today') {
|
||||||
|
$queryBuilder->andWhere('d.date = :today')
|
||||||
|
->setParameter('today', $today);
|
||||||
|
} elseif ($dateFilter === 'week') {
|
||||||
|
$weekStart = $jdate->jdate('Y/m/d', strtotime('-6 days'));
|
||||||
|
$queryBuilder->andWhere('d.date BETWEEN :weekStart AND :today')
|
||||||
|
->setParameter('weekStart', $weekStart)
|
||||||
|
->setParameter('today', $today);
|
||||||
|
} elseif ($dateFilter === 'month') {
|
||||||
|
$monthStart = $jdate->jdate('Y/m/01', time());
|
||||||
|
$queryBuilder->andWhere('d.date BETWEEN :monthStart AND :today')
|
||||||
|
->setParameter('monthStart', $monthStart)
|
||||||
|
->setParameter('today', $today);
|
||||||
|
}
|
||||||
|
|
||||||
|
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%");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($types)) {
|
||||||
|
$queryBuilder->andWhere('l.code IN (:types)')
|
||||||
|
->setParameter('types', $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
// اگر آیتمهای خاصی درخواست شدهاند
|
||||||
|
if (array_key_exists('items', $params)) {
|
||||||
|
$codes = array_map(function($item) { return $item['code']; }, $params['items']);
|
||||||
|
$queryBuilder->andWhere('d.code IN (:codes)')
|
||||||
|
->setParameter('codes', $codes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// اعمال مرتبسازی
|
||||||
|
if (!empty($sortBy)) {
|
||||||
|
foreach ($sortBy as $sort) {
|
||||||
|
$key = $sort['key'] ?? 'id';
|
||||||
|
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? 'DESC' : 'ASC';
|
||||||
|
if ($key === 'profit' || $key === 'receivedAmount') {
|
||||||
|
continue; // اینها توی PHP مرتب میشن
|
||||||
|
} elseif (in_array($key, ['id', 'dateSubmit', 'date', 'type', 'code', 'des', 'amount', 'isPreview', 'isApproved'])) {
|
||||||
|
$queryBuilder->addOrderBy('d.' . $key, $direction);
|
||||||
|
} elseif ($key === 'submitter') {
|
||||||
|
$queryBuilder->addOrderBy('u.fullName', $direction);
|
||||||
|
} elseif ($key === 'label') {
|
||||||
|
$queryBuilder->addOrderBy('l.label', $direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$queryBuilder->orderBy('d.id', 'DESC');
|
||||||
|
}
|
||||||
|
|
||||||
|
$docs = $queryBuilder->getQuery()->getArrayResult();
|
||||||
|
|
||||||
|
$dataTemp = [];
|
||||||
|
foreach ($docs as $doc) {
|
||||||
|
$item = [
|
||||||
|
'id' => $doc['id'],
|
||||||
|
'dateSubmit' => $doc['dateSubmit'],
|
||||||
|
'date' => $doc['date'],
|
||||||
|
'type' => $doc['type'],
|
||||||
|
'code' => $doc['code'],
|
||||||
|
'des' => $doc['des'],
|
||||||
|
'amount' => $doc['amount'],
|
||||||
|
'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)
|
||||||
|
->createQueryBuilder('r')
|
||||||
|
->where('r.doc = :docId')
|
||||||
|
->andWhere('r.person IS NOT NULL')
|
||||||
|
->setParameter('docId', $doc['id'])
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getOneOrNullResult();
|
||||||
|
$item['person'] = $mainRow && $mainRow->getPerson() ? [
|
||||||
|
'id' => $mainRow->getPerson()->getId(),
|
||||||
|
'nikename' => $mainRow->getPerson()->getNikename(),
|
||||||
|
'code' => $mainRow->getPerson()->getCode()
|
||||||
|
] : null;
|
||||||
|
|
||||||
|
// استفاده از SQL خام برای محاسبه پرداختیها
|
||||||
|
$sql = "
|
||||||
|
SELECT SUM(rd.amount) as total_pays, COUNT(rd.id) as count_docs
|
||||||
|
FROM hesabdari_doc rd
|
||||||
|
JOIN hesabdari_doc_hesabdari_doc rel ON rel.hesabdari_doc_target = rd.id
|
||||||
|
WHERE rel.hesabdari_doc_source = :sourceDocId
|
||||||
|
AND rd.bid_id = :bidId
|
||||||
|
";
|
||||||
|
$stmt = $entityManager->getConnection()->prepare($sql);
|
||||||
|
$stmt->bindValue('sourceDocId', $doc['id']);
|
||||||
|
$stmt->bindValue('bidId', $acc['bid']->getId());
|
||||||
|
$result = $stmt->executeQuery()->fetchAssociative();
|
||||||
|
|
||||||
|
$relatedDocsPays = $result['total_pays'] ?? 0;
|
||||||
|
$relatedDocsCount = $result['count_docs'] ?? 0;
|
||||||
|
|
||||||
|
$item['relatedDocsCount'] = (int) $relatedDocsCount;
|
||||||
|
$item['relatedDocsPays'] = $relatedDocsPays;
|
||||||
|
$item['profit'] = $this->calculateProfit($doc['id'], $acc, $entityManager);
|
||||||
|
$item['discountAll'] = 0;
|
||||||
|
$item['transferCost'] = 0;
|
||||||
|
|
||||||
|
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy(['doc' => $doc]);
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
if ($row->getRef()->getCode() == '104') {
|
||||||
|
$item['discountAll'] = $row->getBd();
|
||||||
|
} elseif ($row->getRef()->getCode() == '61') {
|
||||||
|
$item['transferCost'] = $row->getBs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dataTemp[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// مرتبسازی توی PHP برای profit و receivedAmount
|
||||||
|
if (!empty($sortBy)) {
|
||||||
|
foreach ($sortBy as $sort) {
|
||||||
|
$key = $sort['key'] ?? 'id';
|
||||||
|
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? SORT_DESC : SORT_ASC;
|
||||||
|
if ($key === 'profit') {
|
||||||
|
usort($dataTemp, function ($a, $b) use ($direction) {
|
||||||
|
return $direction === SORT_ASC ? $a['profit'] - $b['profit'] : $b['profit'] - $a['profit'];
|
||||||
|
});
|
||||||
|
} elseif ($key === 'receivedAmount') {
|
||||||
|
usort($dataTemp, function ($a, $b) use ($direction) {
|
||||||
|
return $direction === SORT_ASC ? $a['relatedDocsPays'] - $b['relatedDocsPays'] : $b['relatedDocsPays'] - $a['relatedDocsPays'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = $this->renderView('pdf/sell_list.html.twig', [
|
||||||
|
'items' => $dataTemp,
|
||||||
|
'bid' => $acc['bid'],
|
||||||
|
'currentPage' => 1,
|
||||||
|
'totalPages' => 1,
|
||||||
|
'totalItems' => count($dataTemp),
|
||||||
|
'page_title' => 'لیست فاکتورهای فروش'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pdfPid = $provider->createPrint(
|
||||||
|
$acc['bid'],
|
||||||
|
$this->getUser(),
|
||||||
|
$html,
|
||||||
|
false,
|
||||||
|
'A4-L'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->json(['id' => $pdfPid]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
<td class="center item">توضیحات</td>
|
<td class="center item">توضیحات</td>
|
||||||
<td class="center item">شرح سند</td>
|
<td class="center item">شرح سند</td>
|
||||||
<td class="center item">تفصیل</td>
|
<td class="center item">تفصیل</td>
|
||||||
|
<td class="center item">طرف حسابها</td>
|
||||||
<td class="center item">بدهکار</td>
|
<td class="center item">بدهکار</td>
|
||||||
<td class="center item">بستانکار</td>
|
<td class="center item">بستانکار</td>
|
||||||
<td class="center item">سال مالی</td>
|
<td class="center item">سال مالی</td>
|
||||||
|
|
@ -66,6 +67,7 @@
|
||||||
<td class="center item">{{ item.des }}</td>
|
<td class="center item">{{ item.des }}</td>
|
||||||
<td class="center item">{{ item.doc.des }}</td>
|
<td class="center item">{{ item.doc.des }}</td>
|
||||||
<td class="center item">{{ item.ref.name }}</td>
|
<td class="center item">{{ item.ref.name }}</td>
|
||||||
|
<td class="center item">{{ item.counterpartAccounts }}</td>
|
||||||
<td class="center item">{{ item.bd | number_format }}</td>
|
<td class="center item">{{ item.bd | number_format }}</td>
|
||||||
<td class="center item">{{ item.bs | number_format }}</td>
|
<td class="center item">{{ item.bs | number_format }}</td>
|
||||||
<td class="center item">{{ item.year.label }}</td>
|
<td class="center item">{{ item.year.label }}</td>
|
||||||
|
|
|
||||||
53
hesabixCore/templates/pdf/sell_list.html.twig
Normal file
53
hesabixCore/templates/pdf/sell_list.html.twig
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
{% extends "pdf/base.html.twig" %}
|
||||||
|
{% block body %}
|
||||||
|
<div style="width:100%;margin-top:5px;text-align:center;">
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr class="stimol" style="text-align: center; background-color: grey; text-color: white">
|
||||||
|
<td style="width: 35px;">ردیف</td>
|
||||||
|
<td class="center item">کد فاکتور</td>
|
||||||
|
<td class="center item">تاریخ</td>
|
||||||
|
<td class="center item">خریدار</td>
|
||||||
|
<td class="center item">وضعیت تایید</td>
|
||||||
|
<td class="center item">تاییدکننده</td>
|
||||||
|
<td class="center item">تخفیف</td>
|
||||||
|
<td class="center item">حمل و نقل</td>
|
||||||
|
<td class="center item">مبلغ</td>
|
||||||
|
<td class="center item">سود فاکتور</td>
|
||||||
|
<td class="center item">پرداختی</td>
|
||||||
|
<td class="center item">برچسب</td>
|
||||||
|
<td class="center item">شرح</td>
|
||||||
|
</tr>
|
||||||
|
{% for item in items %}
|
||||||
|
<tr class="stimol {% if loop.index is even%}bg-dark text-light{% endif%}">
|
||||||
|
<td class="center item">{{ loop.index + ((currentPage - 1) * 10) }}</td>
|
||||||
|
<td class="center item">{{ item.code }}</td>
|
||||||
|
<td class="center item">{{ item.date }}</td>
|
||||||
|
<td class="center item">{{ item.person ? item.person.nikename : '-' }}</td>
|
||||||
|
<td class="center item">
|
||||||
|
{% if item.isApproved %}
|
||||||
|
تایید شده
|
||||||
|
{% elseif item.isPreview %}
|
||||||
|
در انتظار تایید
|
||||||
|
{% else %}
|
||||||
|
تایید شده
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="center item">{{ item.approvedBy ? item.approvedBy.fullName : '-' }}</td>
|
||||||
|
<td class="center item">{{ item.discountAll | number_format }}</td>
|
||||||
|
<td class="center item">{{ item.transferCost | number_format }}</td>
|
||||||
|
<td class="center item">{{ item.amount | number_format }}</td>
|
||||||
|
<td class="center item">{{ item.profit | number_format }}</td>
|
||||||
|
<td class="center item">{{ item.relatedDocsPays | number_format }}</td>
|
||||||
|
<td class="center item">{{ item.label ? item.label.label : '-' }}</td>
|
||||||
|
<td class="center item">{{ item.des }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div style="margin-top: 20px; text-align: left;">
|
||||||
|
<small>صفحه {{ currentPage }} از {{ totalPages }} - تعداد کل: {{ totalItems }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -191,6 +191,19 @@
|
||||||
{{ item.settlement }}
|
{{ item.settlement }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:item.accounts="{ item }">
|
||||||
|
<div v-for="account in item.accounts" :key="account.name" class="mb-1">
|
||||||
|
<v-chip
|
||||||
|
:color="getAccountColor(account.type)"
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
class="me-1"
|
||||||
|
>
|
||||||
|
{{ account.name }}
|
||||||
|
<span class="ms-1">({{ account.formattedAmount }})</span>
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template v-slot:item.balance="{ item }">
|
<template v-slot:item.balance="{ item }">
|
||||||
<span :class="{
|
<span :class="{
|
||||||
'text-success': -item.balance > 0,
|
'text-success': -item.balance > 0,
|
||||||
|
|
@ -234,6 +247,7 @@ export default {
|
||||||
{ title: this.$t('dialog.invoice_num'), key: "code", align: "center", sortable: true },
|
{ title: this.$t('dialog.invoice_num'), key: "code", align: "center", sortable: true },
|
||||||
{ title: this.$t('dialog.date'), key: "date", align: "center", sortable: true },
|
{ title: this.$t('dialog.date'), key: "date", align: "center", sortable: true },
|
||||||
{ title: this.$t('app.body'), key: "des", align: "center" },
|
{ title: this.$t('app.body'), key: "des", align: "center" },
|
||||||
|
{ title: 'طرف حسابها', key: "accounts", align: "center", sortable: false },
|
||||||
{ title: this.$t('pages.bank_card.detail'), key: "ref", align: "center", sortable: true },
|
{ title: this.$t('pages.bank_card.detail'), key: "ref", align: "center", sortable: true },
|
||||||
{ title: this.$t('pages.bank_card.deposit'), key: "bd", align: "center", sortable: true },
|
{ title: this.$t('pages.bank_card.deposit'), key: "bd", align: "center", sortable: true },
|
||||||
{ title: this.$t('pages.bank_card.withdrawal'), key: "bs", align: "center", sortable: true },
|
{ title: this.$t('pages.bank_card.withdrawal'), key: "bs", align: "center", sortable: true },
|
||||||
|
|
@ -293,10 +307,34 @@ export default {
|
||||||
this.items.forEach((item) => {
|
this.items.forEach((item) => {
|
||||||
item.bs = this.$filters.formatNumber(item.bs)
|
item.bs = this.$filters.formatNumber(item.bs)
|
||||||
item.bd = this.$filters.formatNumber(item.bd)
|
item.bd = this.$filters.formatNumber(item.bd)
|
||||||
|
item.accounts = []; // Initialize accounts array
|
||||||
})
|
})
|
||||||
|
this.loadCounterpartAccounts(id);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
loadCounterpartAccounts(bankCode) {
|
||||||
|
axios.post('/api/person/bank/accounts/list', {
|
||||||
|
bankCode: bankCode
|
||||||
|
}).then((response) => {
|
||||||
|
// Group accounts by document code
|
||||||
|
const accountsByDoc = {};
|
||||||
|
response.data.forEach(doc => {
|
||||||
|
accountsByDoc[doc.docId] = doc.accounts;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add accounts to items
|
||||||
|
this.items.forEach(item => {
|
||||||
|
item.accounts = accountsByDoc[item.code] || [];
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('Error loading counterpart accounts:', error);
|
||||||
|
// Set empty accounts array for all items
|
||||||
|
this.items.forEach(item => {
|
||||||
|
item.accounts = [];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
getTypeRoute(type, code) {
|
getTypeRoute(type, code) {
|
||||||
const routes = {
|
const routes = {
|
||||||
sell: '/acc/sell/view/',
|
sell: '/acc/sell/view/',
|
||||||
|
|
@ -347,6 +385,20 @@ export default {
|
||||||
};
|
};
|
||||||
return labels[type] || type;
|
return labels[type] || type;
|
||||||
},
|
},
|
||||||
|
getAccountColor(type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'bank':
|
||||||
|
return 'primary';
|
||||||
|
case 'cashdesk':
|
||||||
|
return 'success';
|
||||||
|
case 'salary':
|
||||||
|
return 'warning';
|
||||||
|
case 'person':
|
||||||
|
return 'info';
|
||||||
|
default:
|
||||||
|
return 'grey';
|
||||||
|
}
|
||||||
|
},
|
||||||
excellOutput(AllItems = true) {
|
excellOutput(AllItems = true) {
|
||||||
if (AllItems) {
|
if (AllItems) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<small class="mb-2">مبلغ</small>
|
<small class="mb-2">مبلغ</small>
|
||||||
<money3 @change="calc()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
<money3 @change="calcFromAccount()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
||||||
</money3>
|
</money3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -203,7 +203,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<small class="mb-2">مبلغ</small>
|
<small class="mb-2">مبلغ</small>
|
||||||
<money3 @change="calc()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
<money3 @change="calcFromAccount()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
||||||
</money3>
|
</money3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -252,7 +252,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<small class="mb-2">مبلغ</small>
|
<small class="mb-2">مبلغ</small>
|
||||||
<money3 @change="calc()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
<money3 @change="calcFromAccount()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
||||||
</money3>
|
</money3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -417,9 +417,69 @@ export default {
|
||||||
side = parseInt(side) + parseInt(item.amount);
|
side = parseInt(side) + parseInt(item.amount);
|
||||||
});
|
});
|
||||||
this.balance = parseInt(this.sum) - parseInt(side);
|
this.balance = parseInt(this.sum) - parseInt(side);
|
||||||
|
|
||||||
|
// Auto-sync amounts when there's only one person and one account side
|
||||||
|
this.autoSyncAmounts();
|
||||||
|
|
||||||
this.funcCanSubmit();
|
this.funcCanSubmit();
|
||||||
|
|
||||||
},
|
},
|
||||||
|
autoSyncAmounts() {
|
||||||
|
// Check if there's exactly one person and one account side
|
||||||
|
const totalPersons = this.persons.length;
|
||||||
|
const totalBanks = this.banks.length;
|
||||||
|
const totalCashdesks = this.cashdesks.length;
|
||||||
|
const totalSalarys = this.salarys.length;
|
||||||
|
|
||||||
|
const totalAccountSides = totalBanks + totalCashdesks + totalSalarys;
|
||||||
|
|
||||||
|
// Only auto-sync if there's exactly one person and one account side
|
||||||
|
if (totalPersons === 1 && totalAccountSides === 1) {
|
||||||
|
const personAmount = parseInt(this.persons[0].amount) || 0;
|
||||||
|
|
||||||
|
// Sync to the single account side
|
||||||
|
if (totalBanks === 1) {
|
||||||
|
this.banks[0].amount = personAmount;
|
||||||
|
} else if (totalCashdesks === 1) {
|
||||||
|
this.cashdesks[0].amount = personAmount;
|
||||||
|
} else if (totalSalarys === 1) {
|
||||||
|
this.salarys[0].amount = personAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
autoSyncFromAccount() {
|
||||||
|
// Check if there's exactly one person and one account side
|
||||||
|
const totalPersons = this.persons.length;
|
||||||
|
const totalBanks = this.banks.length;
|
||||||
|
const totalCashdesks = this.cashdesks.length;
|
||||||
|
const totalSalarys = this.salarys.length;
|
||||||
|
|
||||||
|
const totalAccountSides = totalBanks + totalCashdesks + totalSalarys;
|
||||||
|
|
||||||
|
// Only auto-sync if there's exactly one person and one account side
|
||||||
|
if (totalPersons === 1 && totalAccountSides === 1) {
|
||||||
|
let accountAmount = 0;
|
||||||
|
|
||||||
|
// Get amount from the single account side
|
||||||
|
if (totalBanks === 1) {
|
||||||
|
accountAmount = parseInt(this.banks[0].amount) || 0;
|
||||||
|
} else if (totalCashdesks === 1) {
|
||||||
|
accountAmount = parseInt(this.cashdesks[0].amount) || 0;
|
||||||
|
} else if (totalSalarys === 1) {
|
||||||
|
accountAmount = parseInt(this.salarys[0].amount) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync to the person
|
||||||
|
this.persons[0].amount = accountAmount;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
calcFromAccount() {
|
||||||
|
// First sync from account to person
|
||||||
|
this.autoSyncFromAccount();
|
||||||
|
|
||||||
|
// Then run normal calculation
|
||||||
|
this.calc();
|
||||||
|
},
|
||||||
funcCanSubmit() {
|
funcCanSubmit() {
|
||||||
//check form can submit
|
//check form can submit
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<small class="mb-2">مبلغ</small>
|
<small class="mb-2">مبلغ</small>
|
||||||
<money3 @change="calc()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
<money3 @change="calcFromAccount()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
||||||
</money3>
|
</money3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -202,7 +202,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<small class="mb-2">مبلغ</small>
|
<small class="mb-2">مبلغ</small>
|
||||||
<money3 @change="calc()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
<money3 @change="calcFromAccount()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
||||||
</money3>
|
</money3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -251,7 +251,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<small class="mb-2">مبلغ</small>
|
<small class="mb-2">مبلغ</small>
|
||||||
<money3 @change="calc()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
<money3 @change="calcFromAccount()" class="form-control" v-model="item.amount" v-bind="currencyConfig">
|
||||||
</money3>
|
</money3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -416,9 +416,69 @@ export default {
|
||||||
side = parseInt(side) + parseInt(item.amount);
|
side = parseInt(side) + parseInt(item.amount);
|
||||||
});
|
});
|
||||||
this.balance = parseInt(this.sum) - parseInt(side);
|
this.balance = parseInt(this.sum) - parseInt(side);
|
||||||
|
|
||||||
|
// Auto-sync amounts when there's only one person and one account side
|
||||||
|
this.autoSyncAmounts();
|
||||||
|
|
||||||
this.funcCanSubmit();
|
this.funcCanSubmit();
|
||||||
|
|
||||||
},
|
},
|
||||||
|
autoSyncAmounts() {
|
||||||
|
// Check if there's exactly one person and one account side
|
||||||
|
const totalPersons = this.persons.length;
|
||||||
|
const totalBanks = this.banks.length;
|
||||||
|
const totalCashdesks = this.cashdesks.length;
|
||||||
|
const totalSalarys = this.salarys.length;
|
||||||
|
|
||||||
|
const totalAccountSides = totalBanks + totalCashdesks + totalSalarys;
|
||||||
|
|
||||||
|
// Only auto-sync if there's exactly one person and one account side
|
||||||
|
if (totalPersons === 1 && totalAccountSides === 1) {
|
||||||
|
const personAmount = parseInt(this.persons[0].amount) || 0;
|
||||||
|
|
||||||
|
// Sync to the single account side
|
||||||
|
if (totalBanks === 1) {
|
||||||
|
this.banks[0].amount = personAmount;
|
||||||
|
} else if (totalCashdesks === 1) {
|
||||||
|
this.cashdesks[0].amount = personAmount;
|
||||||
|
} else if (totalSalarys === 1) {
|
||||||
|
this.salarys[0].amount = personAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
autoSyncFromAccount() {
|
||||||
|
// Check if there's exactly one person and one account side
|
||||||
|
const totalPersons = this.persons.length;
|
||||||
|
const totalBanks = this.banks.length;
|
||||||
|
const totalCashdesks = this.cashdesks.length;
|
||||||
|
const totalSalarys = this.salarys.length;
|
||||||
|
|
||||||
|
const totalAccountSides = totalBanks + totalCashdesks + totalSalarys;
|
||||||
|
|
||||||
|
// Only auto-sync if there's exactly one person and one account side
|
||||||
|
if (totalPersons === 1 && totalAccountSides === 1) {
|
||||||
|
let accountAmount = 0;
|
||||||
|
|
||||||
|
// Get amount from the single account side
|
||||||
|
if (totalBanks === 1) {
|
||||||
|
accountAmount = parseInt(this.banks[0].amount) || 0;
|
||||||
|
} else if (totalCashdesks === 1) {
|
||||||
|
accountAmount = parseInt(this.cashdesks[0].amount) || 0;
|
||||||
|
} else if (totalSalarys === 1) {
|
||||||
|
accountAmount = parseInt(this.salarys[0].amount) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync to the person
|
||||||
|
this.persons[0].amount = accountAmount;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
calcFromAccount() {
|
||||||
|
// First sync from account to person
|
||||||
|
this.autoSyncFromAccount();
|
||||||
|
|
||||||
|
// Then run normal calculation
|
||||||
|
this.calc();
|
||||||
|
},
|
||||||
funcCanSubmit() {
|
funcCanSubmit() {
|
||||||
//check form can submit
|
//check form can submit
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,9 @@ export default defineComponent({
|
||||||
canPdf: true,
|
canPdf: true,
|
||||||
canPrint: true,
|
canPrint: true,
|
||||||
canPrintCashdeskRecp: false,
|
canPrintCashdeskRecp: false,
|
||||||
|
canPos: false,
|
||||||
update: 0,
|
update: 0,
|
||||||
|
printOptions: {},
|
||||||
commodity: [],
|
commodity: [],
|
||||||
selectedCommodity: null,
|
selectedCommodity: null,
|
||||||
tempID: '',
|
tempID: '',
|
||||||
|
|
@ -70,7 +72,8 @@ export default defineComponent({
|
||||||
units: [],
|
units: [],
|
||||||
persons: [],
|
persons: [],
|
||||||
person: {
|
person: {
|
||||||
nikename: ''
|
nikename: '',
|
||||||
|
id: null
|
||||||
},
|
},
|
||||||
cashdesks: [],
|
cashdesks: [],
|
||||||
cashdesk: null,
|
cashdesk: null,
|
||||||
|
|
@ -204,9 +207,7 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
axios.post("/api/printers/options/info").then((response) => {
|
axios.post("/api/printers/options/info").then((response) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.canPdf = response.data.fastsell.pdf;
|
this.printOptions = response.data.sell;
|
||||||
this.canPrintCashdeskRecp = response.data.fastsell.cashdeskTicket;
|
|
||||||
this.canPrint = response.data.fastsell.invoice;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
save() {
|
save() {
|
||||||
|
|
@ -240,7 +241,7 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
if (canAdd) {
|
if (canAdd) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
let outItems = [
|
let outItems: any[] = [
|
||||||
...this.data.items
|
...this.data.items
|
||||||
];
|
];
|
||||||
//save data
|
//save data
|
||||||
|
|
@ -261,7 +262,8 @@ export default defineComponent({
|
||||||
date: this.data.date,
|
date: this.data.date,
|
||||||
des: this.data.des,
|
des: this.data.des,
|
||||||
rows: outItems,
|
rows: outItems,
|
||||||
update: ''
|
update: '',
|
||||||
|
autoApprove: true
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (response.data.result == '1') {
|
if (response.data.result == '1') {
|
||||||
|
|
@ -270,9 +272,10 @@ export default defineComponent({
|
||||||
axios.post('/api/sell/print/invoice', {
|
axios.post('/api/sell/print/invoice', {
|
||||||
code: this.update,
|
code: this.update,
|
||||||
pdf: this.canPdf,
|
pdf: this.canPdf,
|
||||||
posPrint: this.canPrint,
|
printers: this.canPrint,
|
||||||
|
posPrint: this.canPos,
|
||||||
posPrintRecp: this.canPrintCashdeskRecp,
|
posPrintRecp: this.canPrintCashdeskRecp,
|
||||||
printers: this.canPdf
|
printOptions: this.printOptions
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
if (this.canPdf) {
|
if (this.canPdf) {
|
||||||
this.printID = response.data.id;
|
this.printID = response.data.id;
|
||||||
|
|
@ -305,6 +308,7 @@ export default defineComponent({
|
||||||
des: 'دریافت وجه فاکتور',
|
des: 'دریافت وجه فاکتور',
|
||||||
rows: outItems,
|
rows: outItems,
|
||||||
update: '',
|
update: '',
|
||||||
|
autoApprove: true,
|
||||||
related: response.data.doc.code
|
related: response.data.doc.code
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
if (response.data.result == '4') {
|
if (response.data.result == '4') {
|
||||||
|
|
@ -625,6 +629,15 @@ export default defineComponent({
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-12 col-md-4">
|
||||||
|
<span class="form-check form-switch form-check-inline">
|
||||||
|
<input :disabled="this.loading" v-model="canPos" class="form-check-input" type="checkbox">
|
||||||
|
<label class="form-check-label">
|
||||||
|
<i class="fa-solid fa-receipt me-1"></i>
|
||||||
|
صورت حساب POS
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="col-sm-12 col-md-4">
|
<div class="col-sm-12 col-md-4">
|
||||||
<span class="form-check form-switch form-check-inline">
|
<span class="form-check form-switch form-check-inline">
|
||||||
<input :disabled="this.loading" v-model="canPdf" class="form-check-input" type="checkbox">
|
<input :disabled="this.loading" v-model="canPdf" class="form-check-input" type="checkbox">
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,16 @@
|
||||||
<v-btn v-bind="props" icon="mdi-delete" color="danger" @click="deleteItems()"></v-btn>
|
<v-btn v-bind="props" icon="mdi-delete" color="danger" @click="deleteItems()"></v-btn>
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
|
<v-tooltip :text="$t('dialog.export_excel')" location="bottom">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn v-bind="props" icon="mdi-file-excel" color="green" @click="exportToExcel()" :loading="excelLoading"></v-btn>
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
|
<v-tooltip :text="$t('dialog.export_pdf')" location="bottom">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn v-bind="props" icon="mdi-file-pdf-box" color="red" @click="exportToPdf()" :loading="pdfLoading"></v-btn>
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
<v-tooltip v-if="isPluginActive('taxsettings')" text="ارسال گروهی به کارپوشه مودیان" location="bottom">
|
<v-tooltip v-if="isPluginActive('taxsettings')" text="ارسال گروهی به کارپوشه مودیان" location="bottom">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn v-bind="props" icon="mdi-cloud-upload" color="orange" @click="sendBulkToTaxSystem()"
|
<v-btn v-bind="props" icon="mdi-cloud-upload" color="orange" @click="sendBulkToTaxSystem()"
|
||||||
|
|
@ -414,6 +424,8 @@ export default defineComponent({
|
||||||
types: [],
|
types: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
bulkLoading: false,
|
bulkLoading: false,
|
||||||
|
excelLoading: false,
|
||||||
|
pdfLoading: false,
|
||||||
items: [],
|
items: [],
|
||||||
itemsApproved: [],
|
itemsApproved: [],
|
||||||
itemsPending: [],
|
itemsPending: [],
|
||||||
|
|
@ -1069,6 +1081,102 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async exportToExcel() {
|
||||||
|
this.excelLoading = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
search: this.searchValue,
|
||||||
|
types: this.types.filter(t => t.checked).map(t => t.code),
|
||||||
|
dateFilter: this.dateFilter,
|
||||||
|
sortBy: this.serverOptions.sortBy,
|
||||||
|
};
|
||||||
|
|
||||||
|
// اگر آیتمهای خاصی انتخاب شدهاند، فقط آنها را export کن
|
||||||
|
if (this.itemsSelected.length > 0) {
|
||||||
|
params.items = this.itemsSelected.map(code => ({ code }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.post('/api/sell/list/excel', params, {
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileURL = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
|
const fileLink = document.createElement('a');
|
||||||
|
fileLink.href = fileURL;
|
||||||
|
fileLink.setAttribute('download', `لیست_فاکتورهای_فروش_${new Date().toLocaleDateString('fa-IR')}.xlsx`);
|
||||||
|
document.body.appendChild(fileLink);
|
||||||
|
fileLink.click();
|
||||||
|
document.body.removeChild(fileLink);
|
||||||
|
window.URL.revokeObjectURL(fileURL);
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
text: 'فایل اکسل با موفقیت دانلود شد',
|
||||||
|
icon: 'success',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error exporting to Excel:', error);
|
||||||
|
Swal.fire({
|
||||||
|
text: 'خطا در ایجاد فایل اکسل: ' + (error.response?.data?.message || error.message),
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.excelLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async exportToPdf() {
|
||||||
|
this.pdfLoading = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
search: this.searchValue,
|
||||||
|
types: this.types.filter(t => t.checked).map(t => t.code),
|
||||||
|
dateFilter: this.dateFilter,
|
||||||
|
sortBy: this.serverOptions.sortBy,
|
||||||
|
};
|
||||||
|
|
||||||
|
// اگر آیتمهای خاصی انتخاب شدهاند، فقط آنها را export کن
|
||||||
|
if (this.itemsSelected.length > 0) {
|
||||||
|
params.items = this.itemsSelected.map(code => ({ code }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.post('/api/sell/list/pdf', params);
|
||||||
|
|
||||||
|
if (response.data && response.data.id) {
|
||||||
|
const pdfResponse = await axios({
|
||||||
|
method: 'get',
|
||||||
|
url: '/front/print/' + response.data.id,
|
||||||
|
responseType: 'arraybuffer'
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileURL = window.URL.createObjectURL(new Blob([pdfResponse.data]));
|
||||||
|
const fileLink = document.createElement('a');
|
||||||
|
fileLink.href = fileURL;
|
||||||
|
fileLink.setAttribute('download', `لیست_فاکتورهای_فروش_${new Date().toLocaleDateString('fa-IR')}.pdf`);
|
||||||
|
document.body.appendChild(fileLink);
|
||||||
|
fileLink.click();
|
||||||
|
document.body.removeChild(fileLink);
|
||||||
|
window.URL.revokeObjectURL(fileURL);
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
text: 'فایل PDF با موفقیت دانلود شد',
|
||||||
|
icon: 'success',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error('خطا در دریافت شناسه چاپ');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error exporting to PDF:', error);
|
||||||
|
Swal.fire({
|
||||||
|
text: 'خطا در ایجاد فایل PDF: ' + (error.response?.data?.message || error.message),
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.pdfLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadColumnSettings();
|
this.loadColumnSettings();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue