bug fix and add some new options
This commit is contained in:
parent
914c01ed44
commit
194bc613d3
|
@ -14,6 +14,12 @@ use Symfony\Component\HttpFoundation\JsonResponse;
|
|||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Exception;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
|
||||
class CashdeskController extends AbstractController
|
||||
{
|
||||
|
@ -268,4 +274,132 @@ class CashdeskController extends AbstractController
|
|||
'total' => count($transactions)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Route('/api/cashdesk/card/list/excel', name: 'app_cashdesk_card_list_excel')]
|
||||
public function app_cashdesk_card_list_excel(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): BinaryFileResponse|JsonResponse|StreamedResponse
|
||||
{
|
||||
$acc = $access->hasRole('cashdesk');
|
||||
if (!$acc)
|
||||
throw $this->createAccessDeniedException();
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
$params = json_decode($content, true);
|
||||
}
|
||||
if (!array_key_exists('code', $params))
|
||||
throw $this->createNotFoundException();
|
||||
$cashdesk = $entityManager->getRepository(Cashdesk::class)->findOneBy(['bid' => $acc['bid'], 'code' => $params['code']]);
|
||||
if (!$cashdesk)
|
||||
throw $this->createNotFoundException();
|
||||
if (!array_key_exists('items', $params)) {
|
||||
$transactions = $entityManager->getRepository(HesabdariRow::class)->findBy([
|
||||
'bid' => $acc['bid'],
|
||||
'cashdesk' => $cashdesk,
|
||||
'year'=>$acc['year']
|
||||
]);
|
||||
} else {
|
||||
$transactions = [];
|
||||
foreach ($params['items'] as $param) {
|
||||
$prs = $entityManager->getRepository(HesabdariRow::class)->findOneBy([
|
||||
'id' => $param['id'],
|
||||
'bid' => $acc['bid'],
|
||||
'cashdesk' => $cashdesk,
|
||||
'year' => $acc['year']
|
||||
]);
|
||||
if ($prs) {
|
||||
$transactions[] = $prs;
|
||||
}
|
||||
}
|
||||
}
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$activeWorksheet = $spreadsheet->getActiveSheet();
|
||||
$arrayEntity = [
|
||||
[
|
||||
'شماره تراکنش',
|
||||
'تاریخ',
|
||||
'توضیحات',
|
||||
'شرح سند',
|
||||
'تفضیل',
|
||||
'بستانکار',
|
||||
'بدهکار',
|
||||
'سال مالی',
|
||||
]
|
||||
];
|
||||
foreach ($transactions as $transaction) {
|
||||
$arrayEntity[] = [
|
||||
$transaction->getId(),
|
||||
$transaction->getDoc()->getDate(),
|
||||
$transaction->getDes(),
|
||||
$transaction->getDoc()->getDes(),
|
||||
$transaction->getRef()->getName(),
|
||||
$transaction->getBs(),
|
||||
$transaction->getBd(),
|
||||
$transaction->getYear()->getlabel()
|
||||
];
|
||||
}
|
||||
$activeWorksheet->fromArray($arrayEntity, null, 'A1');
|
||||
$activeWorksheet->setRightToLeft(true);
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$fileName = 'کارت حساب صندوق ' . $cashdesk->getName() . '.xlsx';
|
||||
$filePath = __DIR__ . '/../../var/' . $fileName;
|
||||
$writer->save($filePath);
|
||||
|
||||
$response = new BinaryFileResponse($filePath);
|
||||
$response->setContentDisposition(
|
||||
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
|
||||
$fileName
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
|
||||
#[Route('/api/cashdesk/card/list/print', name: 'app_cashdesk_card_list_print')]
|
||||
public function app_cashdesk_card_list_print(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('cashdesk');
|
||||
if (!$acc)
|
||||
throw $this->createAccessDeniedException();
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
$params = json_decode($content, true);
|
||||
}
|
||||
if (!array_key_exists('code', $params))
|
||||
throw $this->createNotFoundException();
|
||||
$cashdesk = $entityManager->getRepository(Cashdesk::class)->findOneBy(['bid' => $acc['bid'], 'code' => $params['code']]);
|
||||
if (!$cashdesk)
|
||||
throw $this->createNotFoundException();
|
||||
|
||||
if (!array_key_exists('items', $params)) {
|
||||
$transactions = $entityManager->getRepository(HesabdariRow::class)->findBy([
|
||||
'bid' => $acc['bid'],
|
||||
'cashdesk' => $cashdesk,
|
||||
'year'=>$acc['year']
|
||||
]);
|
||||
} else {
|
||||
$transactions = [];
|
||||
foreach ($params['items'] as $param) {
|
||||
$prs = $entityManager->getRepository(HesabdariRow::class)->findOneBy([
|
||||
'id' => $param['id'],
|
||||
'bid' => $acc['bid'],
|
||||
'cashdesk' => $cashdesk,
|
||||
'year'=>$acc['year']
|
||||
]);
|
||||
if ($prs) {
|
||||
$transactions[] = $prs;
|
||||
}
|
||||
}
|
||||
}
|
||||
$pid = $provider->createPrint(
|
||||
$acc['bid'],
|
||||
$this->getUser(),
|
||||
$this->renderView('pdf/cashdesk_card.html.twig', [
|
||||
'page_title' => 'کارت حساب' . ' ' . $cashdesk->getName(),
|
||||
'bid' => $acc['bid'],
|
||||
'items' => $transactions,
|
||||
'cashdesk' => $cashdesk
|
||||
])
|
||||
);
|
||||
return $this->json(['id' => $pid]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use App\Repository\HesabdariTableRepository;
|
||||
|
||||
class HesabdariController extends AbstractController
|
||||
{
|
||||
|
@ -179,96 +180,192 @@ class HesabdariController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/accounting/search', name: 'app_accounting_search')]
|
||||
public function app_accounting_search(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
$params = json_decode($content, true);
|
||||
}
|
||||
if (!array_key_exists('type', $params))
|
||||
$this->createNotFoundException();
|
||||
$roll = '';
|
||||
if ($params['type'] == 'person_receive' || $params['type'] == 'person_send')
|
||||
$roll = 'person';
|
||||
elseif ($params['type'] == 'cost')
|
||||
$roll = 'cost';
|
||||
elseif ($params['type'] == 'income')
|
||||
$roll = 'income';
|
||||
elseif ($params['type'] == 'buy')
|
||||
$roll = 'buy';
|
||||
elseif ($params['type'] == 'rfbuy')
|
||||
$roll = 'plugAccproRfbuy';
|
||||
elseif ($params['type'] == 'transfer')
|
||||
$roll = 'bankTransfer';
|
||||
elseif ($params['type'] == 'sell')
|
||||
$roll = 'sell';
|
||||
elseif ($params['type'] == 'rfsell')
|
||||
$roll = 'plugAccproRfsell';
|
||||
elseif ($params['type'] == 'all')
|
||||
$roll = 'accounting';
|
||||
else
|
||||
$this->createNotFoundException();
|
||||
|
||||
$acc = $access->hasRole($roll);
|
||||
if (!$acc)
|
||||
#[Route('/api/accounting/search', name: 'app_hesabdari_search', methods: ['POST'])]
|
||||
public function search(
|
||||
Request $request,
|
||||
Access $access,
|
||||
EntityManagerInterface $entityManager,
|
||||
HesabdariTableRepository $hesabdariTableRepository,
|
||||
Jdate $jdate
|
||||
): JsonResponse {
|
||||
$acc = $access->hasRole('acc');
|
||||
if (!$acc) {
|
||||
throw $this->createAccessDeniedException();
|
||||
if ($params['type'] == 'all') {
|
||||
$data = $entityManager->getRepository(HesabdariDoc::class)->findBy([
|
||||
'bid' => $acc['bid'],
|
||||
'year' => $acc['year'],
|
||||
'money' => $acc['money']
|
||||
], [
|
||||
'id' => 'DESC'
|
||||
]);
|
||||
} else {
|
||||
$data = $entityManager->getRepository(HesabdariDoc::class)->findBy([
|
||||
'bid' => $acc['bid'],
|
||||
'year' => $acc['year'],
|
||||
'type' => $params['type'],
|
||||
'money' => $acc['money']
|
||||
], [
|
||||
'id' => 'DESC'
|
||||
]);
|
||||
}
|
||||
|
||||
$params = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
// Input parameters
|
||||
$filters = $params['filters'] ?? [];
|
||||
$pagination = $params['pagination'] ?? ['page' => 1, 'limit' => 10];
|
||||
$sort = $params['sort'] ?? ['sortBy' => 'id', 'sortDesc' => true];
|
||||
$type = $params['type'] ?? 'all';
|
||||
|
||||
// Set pagination parameters
|
||||
$page = max(1, $pagination['page'] ?? 1);
|
||||
$limit = max(1, min(100, $pagination['limit'] ?? 10));
|
||||
|
||||
// Build base query
|
||||
$queryBuilder = $entityManager->createQueryBuilder()
|
||||
->select('DISTINCT d.id, d.dateSubmit, d.date, d.type, d.code, d.des, d.amount')
|
||||
->addSelect('u.fullName as submitter')
|
||||
->from('App\Entity\HesabdariDoc', 'd')
|
||||
->leftJoin('d.submitter', 'u')
|
||||
->leftJoin('d.hesabdariRows', 'r')
|
||||
->leftJoin('r.ref', 't')
|
||||
->where('d.bid = :bid')
|
||||
->andWhere('d.year = :year')
|
||||
->andWhere('d.money = :money')
|
||||
->setParameter('bid', $acc['bid'])
|
||||
->setParameter('year', $acc['year'])
|
||||
->setParameter('money', $acc['money']);
|
||||
|
||||
// Add type filter if not 'all'
|
||||
if ($type !== 'all') {
|
||||
$queryBuilder->andWhere('d.type = :type')
|
||||
->setParameter('type', $type);
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
if (!empty($filters)) {
|
||||
// Text search
|
||||
if (isset($filters['search'])) {
|
||||
$searchValue = is_array($filters['search']) ? $filters['search']['value'] : $filters['search'];
|
||||
$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',
|
||||
't.name LIKE :search',
|
||||
't.code LIKE :search'
|
||||
)
|
||||
)
|
||||
->setParameter('search', "%{$searchValue}%");
|
||||
}
|
||||
|
||||
// Account filter
|
||||
if (isset($filters['account'])) {
|
||||
$accountCodes = $hesabdariTableRepository->findAllSubAccountCodes($filters['account'], $acc['bid']->getId());
|
||||
if (!empty($accountCodes)) {
|
||||
$queryBuilder->andWhere('t.code IN (:accountCodes)')
|
||||
->setParameter('accountCodes', $accountCodes);
|
||||
} else {
|
||||
$queryBuilder->andWhere('1 = 0');
|
||||
}
|
||||
}
|
||||
|
||||
// Time filter
|
||||
if (isset($filters['timeFilter'])) {
|
||||
$today = $jdate->jdate('Y/m/d', time());
|
||||
switch ($filters['timeFilter']) {
|
||||
case 'today':
|
||||
$queryBuilder->andWhere('d.date = :today')
|
||||
->setParameter('today', $today);
|
||||
break;
|
||||
case 'week':
|
||||
$weekStart = $jdate->jdate('Y/m/d', strtotime('-6 days'));
|
||||
$queryBuilder->andWhere('d.date BETWEEN :weekStart AND :today')
|
||||
->setParameter('weekStart', $weekStart)
|
||||
->setParameter('today', $today);
|
||||
break;
|
||||
case 'month':
|
||||
$monthStart = $jdate->jdate('Y/m/01', time());
|
||||
$queryBuilder->andWhere('d.date BETWEEN :monthStart AND :today')
|
||||
->setParameter('monthStart', $monthStart)
|
||||
->setParameter('today', $today);
|
||||
break;
|
||||
case 'custom':
|
||||
if (isset($filters['date']) && isset($filters['date']['from']) && isset($filters['date']['to'])) {
|
||||
// تبدیل تاریخهای شمسی به میلادی
|
||||
$fromDate = $filters['date']['from'];
|
||||
$toDate = $filters['date']['to'];
|
||||
|
||||
// اطمینان از فرمت صحیح تاریخها
|
||||
if (strpos($fromDate, '/') !== false && strpos($toDate, '/') !== false) {
|
||||
$queryBuilder->andWhere('d.date BETWEEN :dateFrom AND :dateTo')
|
||||
->setParameter('dateFrom', $fromDate)
|
||||
->setParameter('dateTo', $toDate);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
$sortField = is_array($sort['sortBy']) ? ($sort['sortBy']['key'] ?? 'id') : ($sort['sortBy'] ?? 'id');
|
||||
$sortDirection = ($sort['sortDesc'] ?? true) ? 'DESC' : 'ASC';
|
||||
$queryBuilder->orderBy("d.$sortField", $sortDirection);
|
||||
|
||||
// Calculate total items
|
||||
$totalItemsQuery = clone $queryBuilder;
|
||||
$totalItems = $totalItemsQuery->select('COUNT(DISTINCT d.id)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
|
||||
// Apply pagination
|
||||
$queryBuilder->setFirstResult(($page - 1) * $limit)
|
||||
->setMaxResults($limit);
|
||||
|
||||
$docs = $queryBuilder->getQuery()->getArrayResult();
|
||||
|
||||
$dataTemp = [];
|
||||
foreach ($data as $item) {
|
||||
$temp = [
|
||||
'id' => $item->getId(),
|
||||
'dateSubmit' => $item->getDateSubmit(),
|
||||
'date' => $item->getDate(),
|
||||
'type' => $item->getType(),
|
||||
'code' => $item->getCode(),
|
||||
'des' => $item->getDes(),
|
||||
'amount' => $item->getAmount(),
|
||||
'submitter' => $item->getSubmitter()->getFullName(),
|
||||
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'],
|
||||
];
|
||||
if ($params['type'] == 'rfsell' || $params['type'] == 'rfbuy' || $params['type'] == 'buy' || $params['type'] == 'sell') {
|
||||
$mainRow = $entityManager->getRepository(HesabdariRow::class)->getNotEqual($item, 'person');
|
||||
$temp['person'] = '';
|
||||
if ($mainRow)
|
||||
$temp['person'] = Explore::ExplorePerson($mainRow->getPerson());
|
||||
|
||||
// Get related person info if applicable
|
||||
if (in_array($doc['type'], ['rfsell', 'rfbuy', 'buy', 'sell'])) {
|
||||
$personInfo = $entityManager->createQueryBuilder()
|
||||
->select('p.id, p.nikename, p.code')
|
||||
->from('App\Entity\HesabdariRow', 'r')
|
||||
->join('r.person', 'p')
|
||||
->where('r.doc = :docId')
|
||||
->andWhere('r.person IS NOT NULL')
|
||||
->setParameter('docId', $doc['id'])
|
||||
->setMaxResults(1)
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
|
||||
$item['person'] = $personInfo ? [
|
||||
'id' => $personInfo['id'],
|
||||
'nikename' => $personInfo['nikename'],
|
||||
'code' => $personInfo['code'],
|
||||
] : null;
|
||||
}
|
||||
|
||||
$temp['label'] = null;
|
||||
if ($item->getInvoiceLabel()) {
|
||||
$temp['label'] = [
|
||||
'code' => $item->getInvoiceLabel()->getCode(),
|
||||
'label' => $item->getInvoiceLabel()->getLabel()
|
||||
];
|
||||
}
|
||||
//get status of doc
|
||||
$temp['status'] = 'تسویه نشده';
|
||||
$pays = 0;
|
||||
foreach ($item->getRelatedDocs() as $relatedDoc) {
|
||||
$pays += $relatedDoc->getAmount();
|
||||
}
|
||||
if ($item->getAmount() <= $pays)
|
||||
$temp['status'] = 'تسویه شده';
|
||||
// Get payment status
|
||||
$pays = $entityManager->createQueryBuilder()
|
||||
->select('SUM(rd.amount) as total_pays')
|
||||
->from('App\Entity\HesabdariDoc', 'd')
|
||||
->leftJoin('d.relatedDocs', 'rd')
|
||||
->where('d.id = :docId')
|
||||
->setParameter('docId', $doc['id'])
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
|
||||
$dataTemp[] = $temp;
|
||||
$item['status'] = ($pays && $pays >= $doc['amount']) ? 'تسویه شده' : 'تسویه نشده';
|
||||
|
||||
$dataTemp[] = $item;
|
||||
}
|
||||
return $this->json($dataTemp);
|
||||
|
||||
return $this->json([
|
||||
'items' => $dataTemp,
|
||||
'total' => (int) $totalItems,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -943,15 +943,20 @@ class PersonsController extends AbstractController
|
|||
], $acc['money']);
|
||||
} else {
|
||||
$transactions = [];
|
||||
foreach ($params['items'] as $param) {
|
||||
$prs = $entityManager->getRepository(HesabdariRow::class)->findByJoinMoney([
|
||||
'id' => $param['id'],
|
||||
'bid' => $acc['bid'],
|
||||
'person' => $person,
|
||||
'year' => $acc['year'],
|
||||
], $acc['money']);
|
||||
if (count($prs) != 0) {
|
||||
$transactions[] = $prs[0];
|
||||
if (is_array($params['items'])) {
|
||||
foreach ($params['items'] as $param) {
|
||||
$id = is_array($param) ? ($param['id'] ?? null) : $param;
|
||||
if ($id !== null) {
|
||||
$prs = $entityManager->getRepository(HesabdariRow::class)->findByJoinMoney([
|
||||
'id' => $id,
|
||||
'bid' => $acc['bid'],
|
||||
'person' => $person,
|
||||
'year' => $acc['year'],
|
||||
], $acc['money']);
|
||||
if (count($prs) != 0) {
|
||||
$transactions[] = $prs[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1011,15 +1016,20 @@ class PersonsController extends AbstractController
|
|||
], $acc['money']);
|
||||
} else {
|
||||
$transactions = [];
|
||||
foreach ($params['items'] as $param) {
|
||||
$prs = $entityManager->getRepository(HesabdariRow::class)->findByJoinMoney([
|
||||
'id' => $param['id'],
|
||||
'bid' => $acc['bid'],
|
||||
'person' => $person,
|
||||
'year' => $acc['year'],
|
||||
], $acc['money']);
|
||||
if (count($prs) != 0) {
|
||||
$transactions[] = $prs[0];
|
||||
if (is_array($params['items'])) {
|
||||
foreach ($params['items'] as $param) {
|
||||
$id = is_array($param) ? ($param['id'] ?? null) : $param;
|
||||
if ($id !== null) {
|
||||
$prs = $entityManager->getRepository(HesabdariRow::class)->findByJoinMoney([
|
||||
'id' => $id,
|
||||
'bid' => $acc['bid'],
|
||||
'person' => $person,
|
||||
'year' => $acc['year'],
|
||||
], $acc['money']);
|
||||
if (count($prs) != 0) {
|
||||
$transactions[] = $prs[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
116
hesabixCore/templates/pdf/cashdesk_card.html.twig
Normal file
116
hesabixCore/templates/pdf/cashdesk_card.html.twig
Normal file
|
@ -0,0 +1,116 @@
|
|||
{% extends "pdf/base.html.twig" %}
|
||||
{% block body %}
|
||||
<div style="width:100%; border:1px solid black;border-radius: 8px;margin-top:5px;text-align:center;">
|
||||
<div class="tg-wrap" style="width:100%;border-radius: 8px 8px 0px 0px;text-align:center;background-color:gray">
|
||||
<b style="color:white;">کارت حساب صندوق</b>
|
||||
</div>
|
||||
<table style="width:100%;">
|
||||
<tbody>
|
||||
<tr style="text-align:center;">
|
||||
<td class="">
|
||||
<p>
|
||||
<b>نام:
|
||||
</b>
|
||||
{{ cashdesk.name }}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>
|
||||
کد حسابداری:
|
||||
</b>
|
||||
{{ cashdesk.code }}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>شرح:
|
||||
</b>
|
||||
{{ cashdesk.des }}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="width:100%;margin-top:5px;text-align:center;">
|
||||
<table style="width:100%;">
|
||||
<tbody>
|
||||
<tr 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>
|
||||
</tr>
|
||||
{% set sumBs = 0 %}
|
||||
{% set sumBd = 0 %}
|
||||
{% for item in items %}
|
||||
{% set sumBs = sumBs + item.bs %}
|
||||
{% set sumBd = sumBd + item.bd %}
|
||||
<tr class="stimol">
|
||||
<td class="center item">{{ loop.index }}</td>
|
||||
<td class="center item">{{ item.doc.code }}</td>
|
||||
<td class="center item">{{ item.doc.date }}</td>
|
||||
<td class="center item">{{ item.des }}</td>
|
||||
<td class="center item">{{ item.doc.des }}</td>
|
||||
<td class="center item">{{ item.ref.name }}</td>
|
||||
<td class="center item">{{ item.bd | number_format }}</td>
|
||||
<td class="center item">{{ item.bs | number_format }}</td>
|
||||
<td class="center item">{{ item.year.label }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div style="width:100%; border:1px solid black;border-radius: 8px;margin-top:5px;text-align:center;">
|
||||
<div class="tg-wrap" style="width:100%;border-radius: 8px 8px 0px 0px;text-align:center;background-color:gray">
|
||||
<b style="color:white;">وضعیت حساب</b>
|
||||
</div>
|
||||
<table style="width:100%;">
|
||||
<tbody>
|
||||
<tr style="text-align:center;">
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>جمع برداشت:
|
||||
</b>
|
||||
{{ sumBs | number_format }}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>جمع واریز:
|
||||
</b>
|
||||
{{ sumBd | number_format }}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>تراز حساب:
|
||||
</b>
|
||||
<span>{{ (sumBs - sumBd) | abs |number_format }}</span>
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b> وضعیت:
|
||||
</b>
|
||||
{% if sumBs > sumBd%}
|
||||
برداشت
|
||||
{% elseif sumBs == sumBd %}
|
||||
تسویه شده
|
||||
{% else %}
|
||||
واریز
|
||||
{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -255,7 +255,7 @@
|
|||
</h4>
|
||||
<ul class="">
|
||||
<li class="">
|
||||
{{note}}
|
||||
{{note | raw}}
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
|
@ -255,7 +255,7 @@
|
|||
</h4>
|
||||
<ul class="">
|
||||
<li class="">
|
||||
{{note}}
|
||||
{{note | raw}}
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
|
@ -255,7 +255,7 @@
|
|||
</h4>
|
||||
<ul class="">
|
||||
<li class="">
|
||||
{{note}}
|
||||
{{note | raw}}
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
|
@ -236,33 +236,33 @@
|
|||
{% set originalPrice = item.bs + item.discount %}
|
||||
{% set unitPrice = originalPrice / item.commodityCount %}
|
||||
{% endif %}
|
||||
{{ unitPrice|round|number_format }}
|
||||
{{ unitPrice|round|number_format }} {{ doc.money.shortName }}
|
||||
{% else %}
|
||||
0
|
||||
0 {{ doc.money.shortName }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if printOptions.discountInfo %}
|
||||
<td class="center item">
|
||||
{% if item.showPercentDiscount %}
|
||||
{{ item.discountPercent }}%
|
||||
({{ (item.bs * item.commodityCount * item.discountPercent / 100)|round|number_format }})
|
||||
({{ (item.bs * item.commodityCount * item.discountPercent / 100)|round|number_format }} {{ doc.money.shortName }})
|
||||
{% else %}
|
||||
{{ item.discount|number_format }}
|
||||
{{ item.discount|number_format }} {{ doc.money.shortName }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="center item">
|
||||
{% if item.showPercentDiscount %}
|
||||
{% set originalPrice = item.bs / (1 - (item.discountPercent / 100)) %}
|
||||
{{ originalPrice|round|number_format }}
|
||||
{{ originalPrice|round|number_format }} {{ doc.money.shortName }}
|
||||
{% else %}
|
||||
{{ (item.bs + item.discount)|number_format }}
|
||||
{{ (item.bs + item.discount)|number_format }} {{ doc.money.shortName }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if printOptions.taxInfo %}
|
||||
<td class="center item">{{ item.tax | number_format}}</td>
|
||||
<td class="center item">{{ item.tax | number_format}} {{ doc.money.shortName }}</td>
|
||||
{% endif %}
|
||||
<td class="center item">{{ item.bs| number_format }}</td>
|
||||
<td class="center item">{{ item.bs| number_format }} {{ doc.money.shortName }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
@ -296,7 +296,7 @@
|
|||
</h4>
|
||||
<ul class="">
|
||||
<li class="">
|
||||
{{note}}
|
||||
{{note | raw}}
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
@ -306,25 +306,25 @@
|
|||
<td class="item" style="width:15%;padding:1%">
|
||||
<h4>
|
||||
تخفیف:
|
||||
{{discount | number_format}}
|
||||
{{discount | number_format}} {{ doc.money.shortName }}
|
||||
</h4>
|
||||
<h4>
|
||||
مالیات:
|
||||
{{taxAll | number_format}}
|
||||
{{taxAll | number_format}} {{ doc.money.shortName }}
|
||||
</h4>
|
||||
<h4>
|
||||
حمل و نقل:
|
||||
{{transfer | number_format}}
|
||||
{{transfer | number_format}} {{ doc.money.shortName }}
|
||||
</h4>
|
||||
{% if doc.amount != (doc.amount + discount) %}
|
||||
<h4>
|
||||
جمع بدون تخفیف:
|
||||
{{ (doc.amount + discount) | number_format}}
|
||||
{{ (doc.amount + discount) | number_format}} {{ doc.money.shortName }}
|
||||
</h4>
|
||||
{% endif %}
|
||||
<h4>
|
||||
جمع کل:
|
||||
{{ doc.amount | number_format }}
|
||||
{{ doc.amount | number_format }} {{ doc.money.shortName }}
|
||||
</h4>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "hesabix",
|
||||
"version": "0.48.0",
|
||||
"version": "0.49.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
|
|
@ -86,6 +86,11 @@ export default {
|
|||
returnObject: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tableType: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: (value) => ['cost', 'income'].includes(value)
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
@ -135,7 +140,7 @@ export default {
|
|||
async fetchTreeData() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await axios.get('/api/accounting/table/childs/cost');
|
||||
const response = await axios.get(`/api/accounting/table/childs/${this.tableType}`);
|
||||
this.treeItems = response.data;
|
||||
|
||||
if (this.modelValue) {
|
||||
|
|
|
@ -100,6 +100,7 @@ const fa_lang = {
|
|||
ultimate_package: 'بستههای نامحدود',
|
||||
sell_chart: "فروش هفته گذشته",
|
||||
bankaccounts_transactions: "کارت حساب بانک",
|
||||
cashdesk_transactions: "کارت حساب صندوق",
|
||||
print_queue: "صف چاپ",
|
||||
open_balance: "تراز افتتاحیه",
|
||||
sell_invoices_long: "فاکتورهای فروش",
|
||||
|
|
|
@ -18,7 +18,89 @@
|
|||
</v-toolbar>
|
||||
|
||||
<v-text-field v-model="searchValue" prepend-inner-icon="mdi-magnify" density="compact" hide-details :rounded="false"
|
||||
placeholder="جست و جو ..."></v-text-field>
|
||||
placeholder="جست و جو ...">
|
||||
<template v-slot:append-inner>
|
||||
<v-menu :close-on-content-click="false">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-icon v-bind="props" size="sm" color="primary">
|
||||
<v-icon>mdi-filter</v-icon>
|
||||
<v-tooltip activator="parent" :text="$t('dialog.filters')" location="bottom" />
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-subheader color="primary">
|
||||
<v-icon>mdi-filter</v-icon>
|
||||
{{ $t('dialog.filters') }}
|
||||
</v-list-subheader>
|
||||
|
||||
<!-- فیلتر درختی حسابها -->
|
||||
<v-list-item>
|
||||
<v-list-item-title class="text-dark mb-2">
|
||||
فیلتر حساب:
|
||||
<v-btn
|
||||
v-if="selectedAccountId"
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="text"
|
||||
class="ms-2"
|
||||
@click="resetAccountFilter"
|
||||
>
|
||||
<v-icon size="small" class="me-1">mdi-refresh</v-icon>
|
||||
بازنشانی
|
||||
</v-btn>
|
||||
</v-list-item-title>
|
||||
<hesabdari-tree-view
|
||||
v-model="selectedAccountId"
|
||||
:show-sub-tree="true"
|
||||
:selectable-only="false"
|
||||
@select="handleAccountSelect"
|
||||
@account-selected="handleAccountSelected"
|
||||
/>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider class="my-2"></v-divider>
|
||||
|
||||
<!-- فیلتر بازه زمانی -->
|
||||
<v-list-item>
|
||||
<v-list-item-title class="text-dark mb-2">
|
||||
</v-list-item-title>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-checkbox
|
||||
v-model="timeFilters.find(f => f.value === 'custom').checked"
|
||||
label="بازه زمانی"
|
||||
@change="handleCustomDateFilterChange"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" v-if="timeFilters.find(f => f.value === 'custom').checked">
|
||||
<Hdatepicker
|
||||
v-model="dateRange.from"
|
||||
label="از تاریخ"
|
||||
@update:model-value="handleDateRangeChange"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" v-if="timeFilters.find(f => f.value === 'custom').checked">
|
||||
<Hdatepicker
|
||||
v-model="dateRange.to"
|
||||
label="تا تاریخ"
|
||||
@update:model-value="handleDateRangeChange"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
||||
<!-- فیلترهای زمانی -->
|
||||
<v-list-item v-for="(filter, index) in timeFilters.filter(f => f.value !== 'custom')" :key="index" class="text-dark">
|
||||
<template v-slot:title>
|
||||
<v-checkbox v-model="filter.checked" :label="filter.label" @change="applyTimeFilter(filter.value)"
|
||||
hide-details />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-text-field>
|
||||
|
||||
<v-data-table :headers="headers" :items="filteredItems" :search="searchValue" :loading="loading"
|
||||
:header-props="{ class: 'custom-header' }" hover>
|
||||
|
@ -151,8 +233,11 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ref, onMounted, computed, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
import moment from 'jalali-moment'
|
||||
import HesabdariTreeView from '@/components/forms/HesabdariTreeView.vue'
|
||||
import Hdatepicker from '@/components/forms/Hdatepicker.vue'
|
||||
|
||||
const searchValue = ref('')
|
||||
const loading = ref(true)
|
||||
|
@ -167,6 +252,22 @@ const snackbar = ref({
|
|||
})
|
||||
const plugins = ref({})
|
||||
|
||||
// فیلترهای زمانی
|
||||
const timeFilters = ref([
|
||||
{ label: 'امروز', value: 'today', checked: false },
|
||||
{ label: 'این هفته', value: 'week', checked: false },
|
||||
{ label: 'این ماه', value: 'month', checked: false },
|
||||
{ label: 'بازه زمانی', value: 'custom', checked: false },
|
||||
{ label: 'همه', value: 'all', checked: true },
|
||||
])
|
||||
|
||||
const timeFilter = ref('all')
|
||||
const selectedAccountId = ref(null)
|
||||
const dateRange = ref({
|
||||
from: moment().locale('fa').subtract(1, 'days').format('YYYY/MM/DD'),
|
||||
to: moment().locale('fa').format('YYYY/MM/DD')
|
||||
})
|
||||
|
||||
const headers = [
|
||||
{ title: 'وضعیت', key: 'state', sortable: true },
|
||||
{ title: 'عملیات', key: 'operation' },
|
||||
|
@ -181,12 +282,155 @@ const isPluginActive = (plugName) => {
|
|||
return plugins.value[plugName] !== undefined
|
||||
}
|
||||
|
||||
// متدهای مدیریت فیلتر حساب
|
||||
const handleAccountSelect = (account) => {
|
||||
if (account) {
|
||||
selectedAccountId.value = account.code
|
||||
loadData()
|
||||
}
|
||||
}
|
||||
|
||||
const handleAccountSelected = (account) => {
|
||||
if (account) {
|
||||
loadData()
|
||||
}
|
||||
}
|
||||
|
||||
// متد ریست کردن فیلتر حساب
|
||||
const resetAccountFilter = () => {
|
||||
selectedAccountId.value = null
|
||||
loadData()
|
||||
}
|
||||
|
||||
// متدهای مدیریت فیلتر زمانی
|
||||
const handleDateRangeChange = () => {
|
||||
if (dateRange.value.from && dateRange.value.to) {
|
||||
const fromDate = moment(dateRange.value.from, 'jYYYY/jMM/jDD').locale('fa')
|
||||
const toDate = moment(dateRange.value.to, 'jYYYY/jMM/jDD').locale('fa')
|
||||
|
||||
if (fromDate.isAfter(toDate)) {
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: 'تاریخ شروع نمیتواند بعد از تاریخ پایان باشد',
|
||||
color: 'error'
|
||||
}
|
||||
dateRange.value = {
|
||||
from: null,
|
||||
to: null
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
loadData()
|
||||
}
|
||||
}
|
||||
|
||||
const handleCustomDateFilterChange = (checked) => {
|
||||
if (checked) {
|
||||
timeFilters.value.forEach(filter => {
|
||||
if (filter.value !== 'custom') {
|
||||
filter.checked = false
|
||||
}
|
||||
})
|
||||
timeFilter.value = 'custom'
|
||||
|
||||
dateRange.value = {
|
||||
from: moment().locale('fa').subtract(1, 'days').format('YYYY/MM/DD'),
|
||||
to: moment().locale('fa').format('YYYY/MM/DD')
|
||||
}
|
||||
|
||||
loadData()
|
||||
} else {
|
||||
timeFilters.value.forEach(filter => {
|
||||
filter.checked = filter.value === 'all'
|
||||
})
|
||||
timeFilter.value = 'all'
|
||||
dateRange.value = {
|
||||
from: null,
|
||||
to: null
|
||||
}
|
||||
loadData()
|
||||
}
|
||||
}
|
||||
|
||||
const applyTimeFilter = (value) => {
|
||||
timeFilters.value.forEach((filter) => {
|
||||
filter.checked = filter.value === value
|
||||
})
|
||||
timeFilter.value = value
|
||||
|
||||
if (value !== 'custom') {
|
||||
dateRange.value = {
|
||||
from: null,
|
||||
to: null
|
||||
}
|
||||
}
|
||||
|
||||
loadData()
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
const filters = {}
|
||||
if (searchValue.value.trim()) {
|
||||
filters.search = { value: searchValue.value.trim() }
|
||||
}
|
||||
if (timeFilter.value) {
|
||||
filters.timeFilter = timeFilter.value
|
||||
|
||||
if (timeFilter.value === 'custom' && dateRange.value.from && dateRange.value.to) {
|
||||
const fromDate = moment(dateRange.value.from, 'jYYYY/jMM/jDD').locale('fa').format('YYYY/MM/DD')
|
||||
const toDate = moment(dateRange.value.to, 'jYYYY/jMM/jDD').locale('fa').format('YYYY/MM/DD')
|
||||
|
||||
filters.date = {
|
||||
from: fromDate,
|
||||
to: toDate
|
||||
}
|
||||
} else {
|
||||
const today = moment().locale('fa').format('YYYY/MM/DD')
|
||||
switch (timeFilter.value) {
|
||||
case 'today':
|
||||
filters.date = {
|
||||
from: today,
|
||||
to: today
|
||||
}
|
||||
break
|
||||
case 'week':
|
||||
filters.date = {
|
||||
from: moment().locale('fa').subtract(6, 'days').format('YYYY/MM/DD'),
|
||||
to: today
|
||||
}
|
||||
break
|
||||
case 'month':
|
||||
filters.date = {
|
||||
from: moment().locale('fa').startOf('jMonth').format('YYYY/MM/DD'),
|
||||
to: today
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedAccountId.value) {
|
||||
filters.account = selectedAccountId.value
|
||||
}
|
||||
|
||||
const response = await axios.post('/api/accounting/search', {
|
||||
type: 'all'
|
||||
type: 'all',
|
||||
filters,
|
||||
pagination: {
|
||||
page: 1,
|
||||
limit: 100
|
||||
},
|
||||
sort: {
|
||||
sortBy: 'id',
|
||||
sortDesc: true
|
||||
}
|
||||
})
|
||||
items.value = response.data.map(item => ({
|
||||
|
||||
items.value = response.data.items.map(item => ({
|
||||
...item,
|
||||
amount: item.amount.toLocaleString(),
|
||||
amountRaw: item.amount
|
||||
|
@ -195,6 +439,11 @@ const loadData = async () => {
|
|||
} catch (error) {
|
||||
console.error('Error loading data:', error)
|
||||
loading.value = false
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: 'خطا در بارگذاری دادهها: ' + (error.response?.data?.detail || error.message),
|
||||
color: 'error'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,20 +490,17 @@ const confirmDelete = async () => {
|
|||
deleteLoading.value = true
|
||||
const response = await axios.delete(`/api/hesabdari/direct/doc/delete/${selectedItem.value.id}`)
|
||||
if (response.data.success) {
|
||||
// حذف آیتم از لیست
|
||||
const index = items.value.findIndex(item => item.id === selectedItem.value.id)
|
||||
if (index !== -1) {
|
||||
items.value.splice(index, 1)
|
||||
}
|
||||
deleteDialog.value = false
|
||||
// نمایش پیام موفقیت
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: 'سند با موفقیت حذف شد',
|
||||
color: 'success'
|
||||
}
|
||||
} else {
|
||||
// نمایش پیام خطا
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: response.data.message || 'خطا در حذف سند',
|
||||
|
@ -262,7 +508,6 @@ const confirmDelete = async () => {
|
|||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// نمایش پیام خطا
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: error.response?.data?.message || 'خطا در ارتباط با سرور',
|
||||
|
@ -273,6 +518,13 @@ const confirmDelete = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// اضافه کردن watch برای تغییرات تاریخها
|
||||
watch([() => dateRange.value.from, () => dateRange.value.to], () => {
|
||||
if (timeFilter.value === 'custom') {
|
||||
handleDateRangeChange()
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
loadPlugins()
|
||||
|
|
|
@ -1,89 +1,133 @@
|
|||
<template>
|
||||
<div class="block block-content-full ">
|
||||
<div id="fixed-header" class="block-header block-header-default bg-gray-light pt-2 pb-1">
|
||||
<h3 class="block-title text-primary-dark">
|
||||
<button @click="$router.back()" type="button"
|
||||
class="float-start d-none d-sm-none d-md-block btn btn-sm btn-link text-warning">
|
||||
<i class="fa fw-bold fa-arrow-right"></i>
|
||||
</button>
|
||||
تراکنش های صندوق
|
||||
</h3>
|
||||
</div>
|
||||
<div class="block-content pt-1 pb-3">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 m-0 p-0">
|
||||
<div class="col-sm-12 col-md-6 mb-1">
|
||||
<div class="card push">
|
||||
<div class="card-header border-bottom-0 bg-primary-dark text-light">
|
||||
<h3 class="block-title"> گردش حساب <small class="text-info-light">{{ selectedObjectItem.name }}</small>
|
||||
</h3>
|
||||
<v-toolbar color="toolbar" :title="$t('drawer.cashdesk_transactions')">
|
||||
<template v-slot:prepend>
|
||||
<v-tooltip :text="$t('dialog.back')" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
|
||||
icon="mdi-arrow-right" />
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
<v-spacer></v-spacer>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="" color="red">
|
||||
<v-tooltip activator="parent" :text="$t('dialog.export_pdf')" location="bottom" />
|
||||
<v-icon icon="mdi-file-pdf-box"></v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-subheader color="primary">{{ $t('dialog.export_pdf') }}</v-list-subheader>
|
||||
<v-list-item class="text-dark" :title="$t('dialog.selected')" @click="print(false)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="green-darken-4" icon="mdi-check"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item class="text-dark" :title="$t('dialog.selected_all')" @click="print(true)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="indigo-darken-4" icon="mdi-expand-all"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="" color="green">
|
||||
<v-tooltip activator="parent" :text="$t('dialog.export_excel')" location="bottom" />
|
||||
<v-icon icon="mdi-file-excel-box"></v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-subheader color="primary">{{ $t('dialog.export_excel') }}</v-list-subheader>
|
||||
<v-list-item class="text-dark" :title="$t('dialog.selected')" @click="excellOutput(false)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="green-darken-4" icon="mdi-check"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item class="text-dark" :title="$t('dialog.selected_all')" @click="excellOutput(true)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="indigo-darken-4" icon="mdi-expand-all"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-toolbar>
|
||||
<v-row class="pa-1">
|
||||
<v-col cols="12" sm="12" md="12">
|
||||
<v-card :loading="loading">
|
||||
<v-card-text>
|
||||
<v-row class="">
|
||||
<v-col cols="12" sm="12" md="12">
|
||||
<small class="mb-2">صندوق</small>
|
||||
<v-cob dir="rtl" :options="objectItems" label="name" v-model="selectedObjectItem"
|
||||
@option:selected="updateRoute(selectedObjectItem.code)">
|
||||
<template #no-options="{ search, searching, loading }">
|
||||
نتیجهای یافت نشد!
|
||||
</template>
|
||||
</v-cob>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="4">
|
||||
<div class="fw-bold mb-2">کد حسابداری: <small class="text-primary">{{ selectedObjectItem.code }}</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<small class="mb-2">صندوق</small>
|
||||
<v-cob dir="rtl" :options="objectItems" label="name" v-model="selectedObjectItem"
|
||||
@option:selected="updateRoute(selectedObjectItem.code)">
|
||||
<template #no-options="{ search, searching, loading }">
|
||||
نتیجهای یافت نشد!
|
||||
</template>
|
||||
</v-cob>
|
||||
<hr />
|
||||
<div class="fw-bold mb-2">کد حسابداری: <small class="text-primary">{{ selectedObjectItem.code }}</small>
|
||||
</div>
|
||||
<div class="fw-bold mb-2">نام : <small class="text-primary">{{ selectedObjectItem.name }}</small></div>
|
||||
<div class="fw-bold mb-2">شرح: <small class="text-primary">{{ selectedObjectItem.des }}</small></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<h3>تراکنش ها:</h3>
|
||||
<div class="mb-1">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text"><i class="fa fa-search"></i></span>
|
||||
<input v-model="searchValue" class="form-control" type="text" placeholder="جست و جو ...">
|
||||
</div>
|
||||
</div>
|
||||
<EasyDataTable table-class-name="customize-table" show-index alternating :search-value="searchValue" :headers="headers" :items="items"
|
||||
theme-color="#1d90ff" header-text-direction="center" body-text-direction="center"
|
||||
rowsPerPageMessage="تعداد سطر" emptyMessage="اطلاعاتی برای نمایش وجود ندارد" rowsOfPageSeparatorMessage="از"
|
||||
:loading="loading">
|
||||
<template #item-operation="{ code }">
|
||||
<router-link class="text-success" :to="'/acc/accounting/view/' + code">
|
||||
<i class="fa fa-eye px-1"></i>
|
||||
</router-link>
|
||||
<div class="fw-bold mb-2">نام : <small class="text-primary">{{ selectedObjectItem.name }}</small></div>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="4">
|
||||
<div class="fw-bold mb-2">شرح: <small class="text-primary">{{ selectedObjectItem.des }}</small></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="12">
|
||||
<v-text-field :loading="loading" color="green" class="mb-0 pt-0 rounded-0" hide-details="auto" density="compact"
|
||||
:placeholder="$t('dialog.search_txt')" v-model="searchValue" type="text" clearable>
|
||||
<template v-slot:prepend-inner>
|
||||
<v-tooltip location="bottom" :text="$t('dialog.search')">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-icon v-bind="props" color="danger" icon="mdi-magnify"></v-icon>
|
||||
</template>
|
||||
</EasyDataTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<EasyDataTable table-class-name="customize-table" show-index alternating v-model:items-selected="itemsSelected"
|
||||
:search-value="searchValue" :headers="headers" :items="items" theme-color="#1d90ff"
|
||||
header-text-direction="center" body-text-direction="center" rowsPerPageMessage="تعداد سطر"
|
||||
emptyMessage="اطلاعاتی برای نمایش وجود ندارد" rowsOfPageSeparatorMessage="از" :loading="loading">
|
||||
<template #item-operation="{ code }">
|
||||
<router-link class="text-success" :to="'/acc/accounting/view/' + code">
|
||||
<i class="fa fa-eye px-1"></i>
|
||||
</router-link>
|
||||
</template>
|
||||
</EasyDataTable>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import { ref } from "vue";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
export default {
|
||||
name: "card",
|
||||
data: () => {
|
||||
return {
|
||||
searchValue: '',
|
||||
objectItems: [{
|
||||
name: ''
|
||||
}],
|
||||
selectedObjectItem: {},
|
||||
itemsSelected: [],
|
||||
items: [],
|
||||
loading: ref(true),
|
||||
selectedObjectItem: {
|
||||
id: '',
|
||||
code: 0,
|
||||
name: '',
|
||||
},
|
||||
objectItems: [],
|
||||
loading: true,
|
||||
headers: [
|
||||
{ text: "عملیات", value: "operation" },
|
||||
{ text: "تاریخ", value: "date", 'sortable': true },
|
||||
{ text: "شرح", value: "des" },
|
||||
{ text: "تفضیل", value: "ref", 'sortable': true },
|
||||
{ text: "بدهکار", value: "bd", 'sortable': true },
|
||||
{ text: "بستانکار", value: "bs", 'sortable': true },
|
||||
{ text: "واریز", value: "bd", 'sortable': true },
|
||||
{ text: "برداشت", value: "bs", 'sortable': true },
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -96,7 +140,9 @@ export default {
|
|||
this.loadData();
|
||||
},
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
axios.post('/api/cashdesk/list').then((response) => {
|
||||
this.loading = false;
|
||||
this.objectItems = response.data;
|
||||
if (this.$route.params.id != '') {
|
||||
this.loadObject(this.$route.params.id);
|
||||
|
@ -126,7 +172,98 @@ export default {
|
|||
})
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
excellOutput(AllItems = true) {
|
||||
if (AllItems) {
|
||||
this.loading = true;
|
||||
axios({
|
||||
method: 'post',
|
||||
url: '/api/cashdesk/card/list/excel',
|
||||
data: { 'code': this.selectedObjectItem.code },
|
||||
responseType: 'arraybuffer',
|
||||
}).then((response) => {
|
||||
this.loading = false;
|
||||
var FILE = window.URL.createObjectURL(new Blob([response.data]));
|
||||
var fileURL = window.URL.createObjectURL(new Blob([response.data]));
|
||||
var fileLink = document.createElement('a');
|
||||
|
||||
fileLink.href = fileURL;
|
||||
fileLink.setAttribute('download', 'کارت حساب صندوق ' + this.selectedObjectItem.name + '.xlsx');
|
||||
document.body.appendChild(fileLink);
|
||||
fileLink.click();
|
||||
})
|
||||
}
|
||||
else {
|
||||
if (this.itemsSelected.length === 0) {
|
||||
Swal.fire({
|
||||
text: 'هیچ آیتمی انتخاب نشده است.',
|
||||
icon: 'info',
|
||||
confirmButtonText: 'قبول'
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.loading = true;
|
||||
axios({
|
||||
method: 'post',
|
||||
url: '/api/cashdesk/card/list/excel',
|
||||
responseType: 'arraybuffer',
|
||||
data: {
|
||||
'code': this.selectedObjectItem.code,
|
||||
'items': this.itemsSelected
|
||||
}
|
||||
}).then((response) => {
|
||||
this.loading = false;
|
||||
var FILE = window.URL.createObjectURL(new Blob([response.data]));
|
||||
var fileURL = window.URL.createObjectURL(new Blob([response.data]));
|
||||
var fileLink = document.createElement('a');
|
||||
|
||||
fileLink.href = fileURL;
|
||||
fileLink.setAttribute('download', 'کارت حساب صندوق ' + this.selectedObjectItem.name + '.xlsx');
|
||||
document.body.appendChild(fileLink);
|
||||
fileLink.click();
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
print(AllItems = true) {
|
||||
if (this.selectedObjectItem == null) {
|
||||
Swal.fire({
|
||||
text: 'هیچ آیتمی انتخاب نشده است.',
|
||||
icon: 'info',
|
||||
confirmButtonText: 'قبول'
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (AllItems) {
|
||||
this.loading = true;
|
||||
axios.post('/api/cashdesk/card/list/print', { 'code': this.selectedObjectItem.code }).then((response) => {
|
||||
this.printID = response.data.id;
|
||||
this.loading = false;
|
||||
window.open(this.$API_URL + '/front/print/' + this.printID, '_blank', 'noreferrer');
|
||||
})
|
||||
}
|
||||
else {
|
||||
if (this.itemsSelected.length === 0) {
|
||||
Swal.fire({
|
||||
text: 'هیچ آیتمی انتخاب نشده است.',
|
||||
icon: 'info',
|
||||
confirmButtonText: 'قبول'
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.loading = true;
|
||||
axios.post('/api/cashdesk/card/list/print', {
|
||||
'code': this.selectedObjectItem.code,
|
||||
'items': this.itemsSelected
|
||||
}).then((response) => {
|
||||
this.loading = false;
|
||||
this.printID = response.data.id;
|
||||
window.open(this.$API_URL + '/front/print/' + this.printID, '_blank', 'noreferrer');
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,147 +1,329 @@
|
|||
<template>
|
||||
<div class="block block-content-full ">
|
||||
<div id="fixed-header" class="block-header block-header-default bg-gray-light pt-2 pb-1">
|
||||
<h3 class="block-title text-primary-dark">
|
||||
<button @click="$router.back()" type="button" class="float-start d-none d-sm-none d-md-block btn btn-sm btn-link text-warning">
|
||||
<i class="fa fw-bold fa-arrow-right"></i>
|
||||
</button>
|
||||
<i class="fa fa-bank px-2"></i>
|
||||
صندوق ها </h3>
|
||||
<div class="block-options">
|
||||
<router-link to="/acc/cashdesk/mod/" class="block-options-item">
|
||||
<span class="fa fa-plus fw-bolder"></span>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block-content pt-1 pb-3">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 m-0 p-0">
|
||||
<div class="mb-1">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text"><i class="fa fa-search"></i></span>
|
||||
<input v-model="searchValue" class="form-control" type="text" placeholder="جست و جو ...">
|
||||
</div>
|
||||
</div>
|
||||
<EasyDataTable table-class-name="customize-table"
|
||||
show-index
|
||||
alternating
|
||||
:search-value="searchValue"
|
||||
:headers="headers"
|
||||
:items="items"
|
||||
theme-color="#1d90ff"
|
||||
header-text-direction="center"
|
||||
body-text-direction="center"
|
||||
rowsPerPageMessage="تعداد سطر"
|
||||
emptyMessage="اطلاعاتی برای نمایش وجود ندارد"
|
||||
rowsOfPageSeparatorMessage="از"
|
||||
:loading = "loading"
|
||||
>
|
||||
<template #item-operation="{ code }">
|
||||
<button aria-expanded="false" aria-haspopup="true" class="btn btn-sm btn-link"
|
||||
data-bs-toggle="dropdown" id="dropdown-align-center-alt-primary" type="button">
|
||||
<i class="fa-solid fa-ellipsis"></i>
|
||||
</button>
|
||||
<div aria-labelledby="dropdown-align-center-outline-primary" class="dropdown-menu dropdown-menu-end"
|
||||
style="">
|
||||
<router-link class="dropdown-item" :to="'/acc/cashdesk/card/view/' + code">
|
||||
<i class="fa fa-eye text-success pe-2"></i>
|
||||
مشاهده
|
||||
</router-link>
|
||||
<router-link class="dropdown-item" :to="'/acc/cashdesk/mod/' + code">
|
||||
<i class="fa fa-edit pe-2"></i>
|
||||
ویرایش
|
||||
</router-link>
|
||||
<button type="button" @click="deleteItem(code)" class="dropdown-item text-danger">
|
||||
<i class="fa fa-trash pe-2"></i>
|
||||
حذف
|
||||
</button>
|
||||
</div>
|
||||
<v-toolbar color="toolbar" :title="$t('drawer.cashdesks')">
|
||||
<template v-slot:prepend>
|
||||
<v-tooltip :text="$t('dialog.back')" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text" icon="mdi-arrow-right" />
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
<v-spacer />
|
||||
|
||||
<v-slide-group show-arrows>
|
||||
<v-slide-group-item>
|
||||
<v-tooltip :text="$t('dialog.add_new')" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="mdi-plus" color="primary" to="/acc/cashdesk/mod/" />
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</v-slide-group-item>
|
||||
|
||||
<v-slide-group-item>
|
||||
<v-tooltip :text="$t('dialog.column_settings')" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="mdi-table-cog" color="primary" @click="showColumnDialog = true" />
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</v-slide-group-item>
|
||||
</v-slide-group>
|
||||
</v-toolbar>
|
||||
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
:loading="loading"
|
||||
color="green"
|
||||
class="mb-0 pt-0 rounded-0"
|
||||
hide-details="auto"
|
||||
density="compact"
|
||||
:rounded="false"
|
||||
:placeholder="$t('dialog.search_txt')"
|
||||
clearable
|
||||
>
|
||||
<template v-slot:prepend-inner>
|
||||
<v-tooltip location="bottom" :text="$t('dialog.search')">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-icon v-bind="props" color="danger" icon="mdi-magnify" />
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
</v-text-field>
|
||||
|
||||
<v-data-table
|
||||
:headers="visibleHeaders"
|
||||
:items="items"
|
||||
:loading="loading"
|
||||
:search="search"
|
||||
class="elevation-1 text-center"
|
||||
:header-props="{ class: 'custom-header' }"
|
||||
>
|
||||
<template v-slot:item="{ item }">
|
||||
<tr>
|
||||
<td v-if="isColumnVisible('operation')" class="text-center">
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn variant="text" size="small" color="error" icon="mdi-menu" v-bind="props" />
|
||||
</template>
|
||||
<template #item-name="{ name,code }">
|
||||
<router-link :to="'/acc/cashdesk/card/view/' + code">
|
||||
{{name}}
|
||||
</router-link>
|
||||
</template>
|
||||
<template #item-balance="{ balance }">
|
||||
<label class="text-success" v-if="balance >= 0">{{ $filters.formatNumber(balance)}}</label>
|
||||
<label class="text-danger" v-else>{{ $filters.formatNumber( -1 * balance ) }} منفی</label>
|
||||
</template>
|
||||
</EasyDataTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-list>
|
||||
<v-list-item :to="'/acc/cashdesk/card/view/' + item.code">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="success" icon="mdi-eye" />
|
||||
</template>
|
||||
<v-list-item-title>{{ $t('dialog.view') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item :to="'/acc/cashdesk/mod/' + item.code">
|
||||
<template v-slot:prepend>
|
||||
<v-icon icon="mdi-pencil" />
|
||||
</template>
|
||||
<v-list-item-title>{{ $t('dialog.edit') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item @click="confirmDelete(item.code)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="error" icon="mdi-delete" />
|
||||
</template>
|
||||
<v-list-item-title>{{ $t('dialog.delete') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</td>
|
||||
<td v-if="isColumnVisible('code')" class="text-center">{{ formatNumber(item.code) }}</td>
|
||||
<td v-if="isColumnVisible('name')" class="text-center">
|
||||
<router-link :to="'/acc/cashdesk/card/view/' + item.code">
|
||||
{{ item.name }}
|
||||
</router-link>
|
||||
</td>
|
||||
<td v-if="isColumnVisible('balance')" class="text-center">
|
||||
<span :class="Number(item.balance) >= 0 ? 'text-success' : 'text-error'">
|
||||
{{ formatNumber(Math.abs(Number(item.balance))) }}
|
||||
<span v-if="Number(item.balance) < 0">منفی</span>
|
||||
</span>
|
||||
</td>
|
||||
<td v-if="isColumnVisible('des')" class="text-center">{{ item.des }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<v-dialog v-model="showColumnDialog" max-width="500">
|
||||
<v-card>
|
||||
<v-toolbar color="toolbar" :title="$t('dialog.manage_columns')">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="showColumnDialog = false">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col v-for="header in allHeaders" :key="header.key" cols="12" sm="6">
|
||||
<v-checkbox
|
||||
v-model="header.visible"
|
||||
:label="header.title"
|
||||
@change="updateColumnVisibility"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="deleteDialog.show" max-width="400">
|
||||
<v-card>
|
||||
<v-card-title class="text-h6">
|
||||
تأیید حذف
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
آیا برای حذف صندوق مطمئن هستید؟
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" variant="text" @click="deleteDialog.show = false">خیر</v-btn>
|
||||
<v-btn color="error" variant="text" @click="deleteItem">بله</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="messageDialog.show" max-width="400">
|
||||
<v-card>
|
||||
<v-card-title :class="messageDialog.color + ' text-h6'">
|
||||
{{ messageDialog.title }}
|
||||
</v-card-title>
|
||||
<v-card-text class="pt-4">
|
||||
{{ messageDialog.message }}
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" variant="text" @click="messageDialog.show = false">قبول</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import Swal from "sweetalert2";
|
||||
import {ref} from "vue";
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
name: "list",
|
||||
data: ()=>{return {
|
||||
searchValue: '',
|
||||
loading: ref(true),
|
||||
items:[],
|
||||
headers: [
|
||||
{ text: "عملیات", value: "operation", width: "130"},
|
||||
{ text: "کد", value: "code", width: "100px" },
|
||||
{ text: "نام صندوق", value: "name", width: "120px"},
|
||||
{ text: "موجودی()", value: "balance", width: "140px"},
|
||||
{ text: "توضیحات", value: "des", width: "150px"},
|
||||
]
|
||||
}},
|
||||
methods: {
|
||||
loadData(){
|
||||
axios.post('/api/cashdesk/list')
|
||||
.then((response)=>{
|
||||
this.items = response.data;
|
||||
this.loading = false;
|
||||
})
|
||||
},
|
||||
deleteItem(code) {
|
||||
Swal.fire({
|
||||
text: 'آیا برای حذف صندوق مطمئن هستید؟',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'بله',
|
||||
cancelButtonText: `خیر`,
|
||||
}).then((result) => {
|
||||
/* Read more about isConfirmed, isDenied below */
|
||||
if (result.isConfirmed) {
|
||||
axios.post('/api/cashdesk/delete/' + code).then((response) => {
|
||||
if (response.data.result == 1) {
|
||||
let index = 0;
|
||||
for (let z = 0; z < this.items.length; z++) {
|
||||
index++;
|
||||
if (this.items[z]['code'] == code) {
|
||||
this.items.splice(index - 1, 1);
|
||||
}
|
||||
}
|
||||
Swal.fire({
|
||||
text: 'صندوق با موفقیت حذف شد.',
|
||||
icon: 'success',
|
||||
confirmButtonText: 'قبول'
|
||||
});
|
||||
}
|
||||
else if (response.data.result == 2) {
|
||||
Swal.fire({
|
||||
text: 'صندوق به دلیل داشتن تراکنش و اسناد حسابداری مرتبط قابل حذف نیست.',
|
||||
icon: 'error',
|
||||
confirmButtonText: 'قبول'
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.loadData();
|
||||
// Refs
|
||||
const loading = ref(false);
|
||||
const items = ref([]);
|
||||
const search = ref('');
|
||||
const showColumnDialog = ref(false);
|
||||
|
||||
// دیالوگها
|
||||
const deleteDialog = ref({
|
||||
show: false,
|
||||
code: null
|
||||
});
|
||||
|
||||
const messageDialog = ref({
|
||||
show: false,
|
||||
title: '',
|
||||
message: '',
|
||||
color: 'primary'
|
||||
});
|
||||
|
||||
// تابع فرمتکننده اعداد
|
||||
const formatNumber = (value) => {
|
||||
if (!value) return '0';
|
||||
return Number(value).toLocaleString('fa-IR');
|
||||
};
|
||||
|
||||
// تعریف همه ستونها با align مرکز
|
||||
const allHeaders = ref([
|
||||
{ title: "عملیات", key: "operation", align: 'center', sortable: false, width: 100, visible: true },
|
||||
{ title: "کد", key: "code", align: 'center', sortable: true, width: 100, visible: true },
|
||||
{ title: "نام صندوق", key: "name", align: 'center', sortable: true, width: 140, visible: true },
|
||||
{ title: "موجودی", key: "balance", align: 'center', sortable: true, width: 140, visible: true },
|
||||
{ title: "توضیحات", key: "des", align: 'center', sortable: true, width: 150, visible: true },
|
||||
]);
|
||||
|
||||
// ستونهای قابل نمایش
|
||||
const visibleHeaders = computed(() => {
|
||||
return allHeaders.value.filter(header => header.visible);
|
||||
});
|
||||
|
||||
// بررسی نمایش ستون
|
||||
const isColumnVisible = (key) => {
|
||||
return allHeaders.value.find(header => header.key === key)?.visible;
|
||||
};
|
||||
|
||||
// کلید ذخیرهسازی در localStorage
|
||||
const LOCAL_STORAGE_KEY = 'hesabix_cashdesk_table_columns';
|
||||
|
||||
// لود تنظیمات ستونها
|
||||
const loadColumnSettings = () => {
|
||||
const savedSettings = localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||
if (savedSettings) {
|
||||
const visibleColumns = JSON.parse(savedSettings);
|
||||
allHeaders.value.forEach(header => {
|
||||
header.visible = visibleColumns.includes(header.key);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ذخیره تنظیمات ستونها
|
||||
const updateColumnVisibility = () => {
|
||||
const visibleColumns = allHeaders.value
|
||||
.filter(header => header.visible)
|
||||
.map(header => header.key);
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(visibleColumns));
|
||||
};
|
||||
|
||||
// نمایش پیام
|
||||
const showMessage = (message, title = 'پیام', color = 'primary') => {
|
||||
messageDialog.value = {
|
||||
show: true,
|
||||
title,
|
||||
message,
|
||||
color
|
||||
};
|
||||
};
|
||||
|
||||
// تأیید حذف
|
||||
const confirmDelete = (code) => {
|
||||
deleteDialog.value = {
|
||||
show: true,
|
||||
code
|
||||
};
|
||||
};
|
||||
|
||||
// بارگذاری دادهها
|
||||
const loadData = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await axios.post('/api/cashdesk/list');
|
||||
items.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error);
|
||||
showMessage('خطا در بارگذاری دادهها: ' + error.message, 'خطا', 'error');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// حذف آیتم
|
||||
const deleteItem = async () => {
|
||||
const code = deleteDialog.value.code;
|
||||
deleteDialog.value.show = false;
|
||||
|
||||
if (!code) return;
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await axios.post(`/api/cashdesk/delete/${code}`);
|
||||
|
||||
if (response.data.result === 1) {
|
||||
items.value = items.value.filter(item => item.code !== code);
|
||||
showMessage('صندوق با موفقیت حذف شد.', 'موفقیت', 'success');
|
||||
} else if (response.data.result === 2) {
|
||||
showMessage('صندوق به دلیل داشتن تراکنش و اسناد حسابداری مرتبط قابل حذف نیست.', 'خطا', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting item:', error);
|
||||
showMessage('خطا در حذف آیتم: ' + error.message, 'خطا', 'error');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// مانت کامپوننت
|
||||
onMounted(() => {
|
||||
loadColumnSettings();
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
.v-data-table {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* استایل برای وسطچین کردن همه سلولهای جدول */
|
||||
:deep(.v-data-table-header th) {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
:deep(.v-data-table__wrapper table td) {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
/* استایل برای رنگهای متن */
|
||||
.text-success {
|
||||
color: #4caf50 !important;
|
||||
}
|
||||
|
||||
.text-error {
|
||||
color: #ff5252 !important;
|
||||
}
|
||||
|
||||
/* استایل برای لینکها در جدول */
|
||||
:deep(.v-data-table__wrapper table td a) {
|
||||
text-decoration: none;
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
:deep(.v-data-table__wrapper table td a:hover) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
|
@ -103,7 +103,7 @@
|
|||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<Htabletreeselect v-model="item.id" :items="listscosts" label="مرکز هزینه" />
|
||||
<Htabletreeselect v-model="item.id" label="مرکز هزینه" tableType="cost" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<Hnumberinput
|
||||
|
@ -360,7 +360,6 @@ export default {
|
|||
sum: 0,
|
||||
balance: 0,
|
||||
listPersons: [],
|
||||
listscosts: [],
|
||||
listBanks: [],
|
||||
listCashdesks: [],
|
||||
listSalarys: [],
|
||||
|
@ -398,12 +397,7 @@ export default {
|
|||
beforeRouteUpdate(to, from) {
|
||||
this.loadData(to.params.id);
|
||||
},
|
||||
computed: {
|
||||
formattedCostItems() {
|
||||
// تبدیل ساختار درختی به آرایه ساده
|
||||
return this.flattenCostItems(this.listscosts);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
calc() {
|
||||
this.sum = 0;
|
||||
|
@ -553,10 +547,6 @@ export default {
|
|||
this.data.date = response.data.now;
|
||||
})
|
||||
}
|
||||
//get list of items
|
||||
axios.post('/api/accounting/table/childs/cost').then((response) => {
|
||||
this.listscosts = response.data;
|
||||
});
|
||||
|
||||
//get list of banks
|
||||
axios.post('/api/bank/list').then((response) => {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -94,8 +94,7 @@
|
|||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-textarea v-model="settings.sell.noteString" label="یادداشت پایین فاکتور" rows="4"
|
||||
placeholder="این نوشته در پایین فاکتورها چاپ خواهد شد"></v-textarea>
|
||||
<CustomEditor title="یادداشت پایین فاکتور" v-model="settings.sell.noteString" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-select v-model="settings.sell.paper" :items="paperOptions" label="سایز کاغذ و حالت چاپ"></v-select>
|
||||
|
@ -130,8 +129,7 @@
|
|||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-textarea v-model="settings.buy.noteString" label="یادداشت پایین فاکتور" rows="4"
|
||||
placeholder="این نوشته در پایین فاکتورها چاپ خواهد شد"></v-textarea>
|
||||
<CustomEditor title="یادداشت پایین فاکتور" v-model="settings.buy.noteString" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-select v-model="settings.buy.paper" :items="paperOptions" label="سایز کاغذ و حالت چاپ"></v-select>
|
||||
|
@ -166,8 +164,7 @@
|
|||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-textarea v-model="settings.rfbuy.noteString" label="یادداشت پایین فاکتور" rows="4"
|
||||
placeholder="این نوشته در پایین فاکتورها چاپ خواهد شد"></v-textarea>
|
||||
<CustomEditor title="یادداشت پایین فاکتور" v-model="settings.rfbuy.noteString" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-select v-model="settings.rfbuy.paper" :items="paperOptions" label="سایز کاغذ و حالت چاپ"></v-select>
|
||||
|
@ -202,8 +199,7 @@
|
|||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-textarea v-model="settings.rfsell.noteString" label="یادداشت پایین فاکتور" rows="4"
|
||||
placeholder="این نوشته در پایین فاکتورها چاپ خواهد شد"></v-textarea>
|
||||
<CustomEditor title="یادداشت پایین فاکتور" v-model="settings.rfsell.noteString" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-select v-model="settings.rfsell.paper" :items="paperOptions" label="سایز کاغذ و حالت چاپ"></v-select>
|
||||
|
|
Loading…
Reference in a new issue