progress in salary and add export pdf/excell to that
This commit is contained in:
parent
8bc857c2f8
commit
cc1515345b
|
@ -14,6 +14,9 @@ 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 Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
class SalaryController extends AbstractController
|
||||
{
|
||||
|
@ -66,7 +69,21 @@ class SalaryController extends AbstractController
|
|||
'bid' => $acc['bid'],
|
||||
'code' => $code
|
||||
]);
|
||||
return $this->json(Explore::ExploreSalary($data));
|
||||
$result = Explore::ExploreSalary($data);
|
||||
// محاسبه بدهکار و بستانکار و تراز
|
||||
$bs = 0;
|
||||
$bd = 0;
|
||||
$items = $entityManager->getRepository(HesabdariRow::class)->findBy([
|
||||
'salary' => $data
|
||||
]);
|
||||
foreach ($items as $item) {
|
||||
$bs += $item->getBs();
|
||||
$bd += $item->getBd();
|
||||
}
|
||||
$result['bs'] = $bs;
|
||||
$result['bd'] = $bd;
|
||||
$result['balance'] = $bd - $bs;
|
||||
return $this->json($result);
|
||||
}
|
||||
|
||||
#[Route('/api/salary/mod/{code}', name: 'app_salary_mod')]
|
||||
|
@ -267,4 +284,135 @@ class SalaryController extends AbstractController
|
|||
'total' => count($transactions)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* خروجی اکسل کارت حساب تنخواه گردان
|
||||
*/
|
||||
#[Route('/api/salary/card/list/excel', name: 'app_salary_card_list_excel')]
|
||||
public function app_salary_card_list_excel(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): BinaryFileResponse|JsonResponse|Response
|
||||
{
|
||||
$acc = $access->hasRole('salary');
|
||||
if (!$acc)
|
||||
throw $this->createAccessDeniedException();
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
$params = json_decode($content, true);
|
||||
}
|
||||
if (!array_key_exists('code', $params))
|
||||
throw $this->createNotFoundException();
|
||||
$salary = $entityManager->getRepository(Salary::class)->findOneBy(['bid' => $acc['bid'], 'code' => $params['code']]);
|
||||
if (!$salary)
|
||||
throw $this->createNotFoundException();
|
||||
if (!array_key_exists('items', $params)) {
|
||||
$transactions = $entityManager->getRepository(HesabdariRow::class)->findBy([
|
||||
'bid' => $acc['bid'],
|
||||
'salary' => $salary,
|
||||
'year' => $acc['year'],
|
||||
]);
|
||||
} else {
|
||||
$transactions = [];
|
||||
if (is_array($params['items'])) {
|
||||
foreach ($params['items'] as $param) {
|
||||
$id = is_array($param) ? ($param['id'] ?? null) : $param;
|
||||
if ($id !== null) {
|
||||
$row = $entityManager->getRepository(HesabdariRow::class)->findOneBy([
|
||||
'id' => $id,
|
||||
'bid' => $acc['bid'],
|
||||
'salary' => $salary,
|
||||
'year' => $acc['year'],
|
||||
]);
|
||||
if ($row) {
|
||||
$transactions[] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$activeWorksheet = $spreadsheet->getActiveSheet();
|
||||
$arrayEntity = [
|
||||
[
|
||||
'شماره تراکنش',
|
||||
'تاریخ',
|
||||
'توضیحات',
|
||||
'تفضیل',
|
||||
'بستانکار',
|
||||
'بدهکار',
|
||||
'سال مالی',
|
||||
]
|
||||
];
|
||||
foreach ($transactions as $transaction) {
|
||||
$arrayEntity[] = [
|
||||
$transaction->getId(),
|
||||
$transaction->getDoc()->getDate(),
|
||||
$transaction->getDes(),
|
||||
$transaction->getRef() ? $transaction->getRef()->getName() : '',
|
||||
$transaction->getBs(),
|
||||
$transaction->getBd(),
|
||||
$transaction->getYear() ? $transaction->getYear()->getlabel() : '',
|
||||
];
|
||||
}
|
||||
$activeWorksheet->fromArray($arrayEntity, null, 'A1');
|
||||
$activeWorksheet->setRightToLeft(true);
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$filePath = __DIR__ . '/../../var/' . uniqid('salary_card_', true) . '.xlsx';
|
||||
$writer->save($filePath);
|
||||
return new BinaryFileResponse($filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* خروجی PDF کارت حساب تنخواه گردان
|
||||
*/
|
||||
#[Route('/api/salary/card/list/print', name: 'app_salary_card_list_print')]
|
||||
public function app_salary_card_list_print(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('salary');
|
||||
if (!$acc)
|
||||
throw $this->createAccessDeniedException();
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
$params = json_decode($content, true);
|
||||
}
|
||||
if (!array_key_exists('code', $params))
|
||||
throw $this->createNotFoundException();
|
||||
$salary = $entityManager->getRepository(Salary::class)->findOneBy(['bid' => $acc['bid'], 'code' => $params['code']]);
|
||||
if (!$salary)
|
||||
throw $this->createNotFoundException();
|
||||
if (!array_key_exists('items', $params)) {
|
||||
$transactions = $entityManager->getRepository(HesabdariRow::class)->findBy([
|
||||
'bid' => $acc['bid'],
|
||||
'salary' => $salary,
|
||||
'year' => $acc['year'],
|
||||
]);
|
||||
} else {
|
||||
$transactions = [];
|
||||
if (is_array($params['items'])) {
|
||||
foreach ($params['items'] as $param) {
|
||||
$id = is_array($param) ? ($param['id'] ?? null) : $param;
|
||||
if ($id !== null) {
|
||||
$row = $entityManager->getRepository(HesabdariRow::class)->findOneBy([
|
||||
'id' => $id,
|
||||
'bid' => $acc['bid'],
|
||||
'salary' => $salary,
|
||||
'year' => $acc['year'],
|
||||
]);
|
||||
if ($row) {
|
||||
$transactions[] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$pid = $provider->createPrint(
|
||||
$acc['bid'],
|
||||
$this->getUser(),
|
||||
$this->renderView('pdf/salary_card.html.twig', [
|
||||
'page_title' => 'کارت حساب تنخواه گردان ' . $salary->getName(),
|
||||
'bid' => $acc['bid'],
|
||||
'items' => $transactions,
|
||||
'salary' => $salary
|
||||
])
|
||||
);
|
||||
return $this->json(['id' => $pid]);
|
||||
}
|
||||
}
|
||||
|
|
105
hesabixCore/templates/pdf/salary_card.html.twig
Normal file
105
hesabixCore/templates/pdf/salary_card.html.twig
Normal file
|
@ -0,0 +1,105 @@
|
|||
{% 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>
|
||||
{{ salary.name }}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>کد حسابداری:</b>
|
||||
{{ salary.code }}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center" colspan="3">
|
||||
<p>
|
||||
<b>شرح:</b>
|
||||
{{ salary.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>
|
||||
</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.ref ? 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.doc.des }}</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 %}
|
|
@ -1,65 +1,147 @@
|
|||
<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>
|
||||
</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 }">
|
||||
<v-toolbar color="toolbar" dense flat>
|
||||
<v-btn icon @click="$router.back()" class="d-none d-md-flex">
|
||||
<v-icon>mdi-arrow-right</v-icon>
|
||||
</v-btn>
|
||||
<v-toolbar-title class="text-primary-dark">
|
||||
کارت حساب تنخواه گردان
|
||||
</v-toolbar-title>
|
||||
<v-spacer />
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon color="red">
|
||||
<v-tooltip activator="parent" text="خروجی PDF" location="bottom" />
|
||||
<v-icon icon="mdi-file-pdf-box"></v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-subheader color="primary">خروجی PDF</v-list-subheader>
|
||||
<v-list-item class="text-dark" title="انتخاب شدهها" @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="همه" @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="خروجی اکسل" location="bottom" />
|
||||
<v-icon icon="mdi-file-excel-box"></v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-subheader color="primary">خروجی اکسل</v-list-subheader>
|
||||
<v-list-item class="text-dark" title="انتخاب شدهها" @click="excelOutput(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="همه" @click="excelOutput(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-container fluid class="pa-4">
|
||||
<v-row dense>
|
||||
<v-col cols="12" md="12">
|
||||
<v-autocomplete v-model="selectedSalary" :items="listSalaries" item-title="name" item-value="code"
|
||||
return-object label="انتخاب تنخواه گردان" dense hide-details prepend-inner-icon="mdi-cash"
|
||||
:loading="loading" @update:model-value="updateRoute"
|
||||
class="rounded-lg elevation-2">
|
||||
<template v-slot:no-data>
|
||||
نتیجهای یافت نشد!
|
||||
</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>
|
||||
</v-autocomplete>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card flat outlined class="rounded-lg elevation-2">
|
||||
<v-toolbar color="primary-dark" dense flat class="rounded-t-lg">
|
||||
<v-toolbar-title class="text-white">
|
||||
اطلاعات تنخواه گردان
|
||||
<small class="text-info-light" v-if="selectedSalary">{{ selectedSalary.name }}</small>
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text class="pa-2">
|
||||
<div class="text-subtitle-2">کد حسابداری: <span class="text-primary">{{ selectedSalary.code || '-' }}</span></div>
|
||||
<div class="text-subtitle-2">نام: <span class="text-primary">{{ selectedSalary.name || '-' }}</span></div>
|
||||
<div class="text-subtitle-2">شرح: <span class="text-primary">{{ selectedSalary.des || '-' }}</span></div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card flat outlined class="rounded-lg elevation-2">
|
||||
<v-toolbar color="primary-dark" dense flat class="rounded-t-lg">
|
||||
<v-toolbar-title class="text-white">
|
||||
وضعیت حساب
|
||||
<small class="text-info-light" v-if="selectedSalary">{{ selectedSalary.name }}</small>
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text class="pa-2">
|
||||
<div class="text-subtitle-2">
|
||||
وضعیت حساب:
|
||||
<span :class="{
|
||||
'text-success': selectedSalary.balance > 0,
|
||||
'text-danger': selectedSalary.balance < 0,
|
||||
'text-dark': selectedSalary.balance == 0
|
||||
}">
|
||||
{{ selectedSalary.balance > 0 ? 'بستانکار' : selectedSalary.balance < 0 ? 'بدهکار' : 'تسویه' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-subtitle-2">بستانکار: <span class="text-primary">{{ $filters.formatNumber(selectedSalary.bs) || '-' }}</span></div>
|
||||
<div class="text-subtitle-2">بدهکار: <span class="text-primary">{{ $filters.formatNumber(selectedSalary.bd) || '-' }}</span></div>
|
||||
<div class="text-subtitle-2">تراز حساب: <span class="text-primary">{{ $filters.formatNumber(selectedSalary.balance) || '-' }}</span></div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
</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>
|
||||
<v-row dense>
|
||||
<v-col cols="12">
|
||||
<v-data-table v-model="itemsSelected" :headers="headers" :items="items" :search="searchValue" :loading="loading"
|
||||
show-select dense :items-per-page="25" class="elevation-2 rounded-lg" :header-props="{ class: 'custom-header' }">
|
||||
<template v-slot:top>
|
||||
<v-toolbar flat dense color="grey-lighten-4" class="rounded-t-lg">
|
||||
<v-toolbar-title class="text-subtitle-1">تراکنشها</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-text-field v-model="searchValue" dense hide-details prepend-inner-icon="mdi-magnify" />
|
||||
</v-toolbar>
|
||||
</template>
|
||||
</EasyDataTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-slot:item.operation="{ item }">
|
||||
<v-btn variant="plain" icon size="small" :to="'/acc/accounting/view/' + item.code" color="success">
|
||||
<v-icon small>mdi-eye</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-slot:item.code="{ item }">
|
||||
{{ $filters.formatNumber(item.code) }}
|
||||
</template>
|
||||
<template v-slot:item.bd="{ item }">
|
||||
{{ $filters.formatNumber(item.bd) }}
|
||||
</template>
|
||||
<template v-slot:item.bs="{ item }">
|
||||
{{ $filters.formatNumber(item.bs) }}
|
||||
</template>
|
||||
<template v-slot:no-data>
|
||||
اطلاعاتی برای نمایش وجود ندارد
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-snackbar v-model="snackbar" :timeout="3000" color="info">{{ snackbarText }}</v-snackbar>
|
||||
<v-overlay :value="loading" contained class="align-center justify-center">
|
||||
<v-progress-circular indeterminate size="64" />
|
||||
</v-overlay>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -68,67 +150,151 @@ import { ref } from "vue";
|
|||
|
||||
export default {
|
||||
name: "card",
|
||||
data: () => {
|
||||
data() {
|
||||
return {
|
||||
searchValue: '',
|
||||
objectItems: [{
|
||||
name: ''
|
||||
}],
|
||||
selectedObjectItem: {},
|
||||
listSalaries: [],
|
||||
itemsSelected: [],
|
||||
selectedSalary: { balance: 0, bs: 0, bd: 0 },
|
||||
items: [],
|
||||
loading: ref(true),
|
||||
loading: ref(false),
|
||||
snackbar: false,
|
||||
snackbarText: '',
|
||||
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 },
|
||||
]
|
||||
}
|
||||
{ title: 'عملیات', key: "operation", align: "center", sortable: false },
|
||||
{ title: 'شماره سند', key: "code", align: "center", sortable: true },
|
||||
{ title: 'تاریخ', key: "date", align: "center", sortable: true },
|
||||
{ title: 'شرح', key: "des", align: "center" },
|
||||
{ title: 'تفضیل', key: "ref", align: "center", sortable: true },
|
||||
{ title: 'بدهکار', key: "bd", align: "center", sortable: true },
|
||||
{ title: 'بستانکار', key: "bs", align: "center", sortable: true },
|
||||
],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
updateRoute(id) {
|
||||
this.$router.push(id);
|
||||
this.loadData();
|
||||
showSnackbar(text) {
|
||||
this.snackbarText = text;
|
||||
this.snackbar = true;
|
||||
},
|
||||
loadData() {
|
||||
axios.post('/api/salary/list').then((response) => {
|
||||
this.objectItems = response.data;
|
||||
if (this.$route.params.id != '') {
|
||||
this.loadObject(this.$route.params.id);
|
||||
this.objectItems.forEach((item) => {
|
||||
if (item.code == this.$route.params.id) {
|
||||
this.selectedObjectItem = item;
|
||||
updateRoute() {
|
||||
if (this.selectedSalary && this.selectedSalary.code) {
|
||||
this.$router.push(this.selectedSalary.code);
|
||||
this.loadSalary(this.selectedSalary.code);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.selectedObjectItem = response.data[0];
|
||||
this.loadObject(this.selectedObjectItem.code);
|
||||
}
|
||||
});
|
||||
},
|
||||
loadObject(id) {
|
||||
async loadData() {
|
||||
this.loading = true;
|
||||
axios.post('/api/accounting/rows/search',
|
||||
{
|
||||
type: 'salary',
|
||||
id: id
|
||||
try {
|
||||
const response = await axios.post('/api/salary/list');
|
||||
this.listSalaries = response.data;
|
||||
const id = this.$route.params.id;
|
||||
if (id) {
|
||||
await this.loadSalary(id);
|
||||
} else if (response.data.length > 0) {
|
||||
this.selectedSalary = response.data[0];
|
||||
await this.loadSalary(this.selectedSalary.code);
|
||||
}
|
||||
).then((response) => {
|
||||
this.items = response.data;
|
||||
this.items.forEach((item) => {
|
||||
item.bs = this.$filters.formatNumber(item.bs)
|
||||
item.bd = this.$filters.formatNumber(item.bd)
|
||||
})
|
||||
} catch (error) {
|
||||
this.showSnackbar('خطا در بارگذاری اطلاعات');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async loadSalary(id) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const salaryResponse = await axios.post('/api/salary/info/' + id);
|
||||
this.selectedSalary = salaryResponse.data;
|
||||
const rowsResponse = await axios.post('/api/accounting/rows/search', { type: 'salary', id });
|
||||
this.items = rowsResponse.data;
|
||||
} catch (error) {
|
||||
this.selectedSalary = { balance: 0, bs: 0, bd: 0 };
|
||||
this.items = [];
|
||||
this.showSnackbar('خطا در دریافت اطلاعات تنخواه گردان');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async excelOutput(allItems = true) {
|
||||
if (!allItems && this.itemsSelected.length === 0) {
|
||||
this.showSnackbar('هیچ تراکنشی انتخاب نشده است');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await axios({
|
||||
method: 'post',
|
||||
url: '/api/salary/card/list/excel',
|
||||
data: allItems ? { code: this.selectedSalary.code } : { code: this.selectedSalary.code, items: this.itemsSelected },
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute('download', 'salary-card-view.xlsx');
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
} catch (error) {
|
||||
this.showSnackbar('خطا در دریافت خروجی اکسل');
|
||||
}
|
||||
},
|
||||
async print(allItems = true) {
|
||||
if (!this.selectedSalary) {
|
||||
this.showSnackbar('هیچ تنخواه گردانی انتخاب نشده است');
|
||||
return;
|
||||
}
|
||||
if (!allItems && this.itemsSelected.length === 0) {
|
||||
this.showSnackbar('هیچ تراکنشی انتخاب نشده است');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await axios.post('/api/salary/card/list/print', allItems ? { code: this.selectedSalary.code } : { code: this.selectedSalary.code, items: this.itemsSelected });
|
||||
window.open(this.$API_URL + '/front/print/' + response.data.id, '_blank', 'noreferrer');
|
||||
} catch (error) {
|
||||
this.showSnackbar('خطا در دریافت خروجی PDF');
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.custom-header {
|
||||
background-color: #f5f5f5 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.v-data-table {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.v-card {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.v-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1) !important;
|
||||
}
|
||||
|
||||
.v-autocomplete {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.v-toolbar {
|
||||
border-bottom: 1px solid rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.v-list-item {
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.v-list-item:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
</style>
|
|
@ -1,138 +1,299 @@
|
|||
<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/salary/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/salary/card/view/' + code">
|
||||
<i class="fa fa-eye text-success pe-2"></i>
|
||||
مشاهده
|
||||
</router-link>
|
||||
<router-link class="dropdown-item" :to="'/acc/salary/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.salary')">
|
||||
<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>
|
||||
<template #item-name="{ name, code }">
|
||||
<router-link :to="'/acc/salary/card/view/' + code">
|
||||
{{ name }}
|
||||
</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/salary/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>
|
||||
<v-list>
|
||||
<v-list-item :to="'/acc/salary/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/salary/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/salary/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>
|
||||
<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-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: "70px" },
|
||||
{ text: "نام تنخواهگردان", value: "name", width: "120px" },
|
||||
{ text: "موجودی()", value: "balance", width: "140px" },
|
||||
{ text: "توضیحات", value: "des", width: "150px" },
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadData() {
|
||||
axios.post('/api/salary/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/salary/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: 'قبول'
|
||||
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');
|
||||
};
|
||||
|
||||
const allHeaders = ref([
|
||||
{ title: "عملیات", key: "operation", align: 'center', sortable: false, width: 100, visible: true },
|
||||
{ title: "کد", key: "code", align: 'center', sortable: true, width: 70, visible: true },
|
||||
{ title: "نام تنخواهگردان", key: "name", align: 'center', sortable: true, width: 120, 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;
|
||||
};
|
||||
|
||||
const LOCAL_STORAGE_KEY = 'hesabix_salary_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);
|
||||
});
|
||||
}
|
||||
else if (response.data.result == 2) {
|
||||
Swal.fire({
|
||||
text: 'تنخواهگردان به دلیل داشتن تراکنش و اسناد حسابداری مرتبط قابل حذف نیست.',
|
||||
icon: 'error',
|
||||
confirmButtonText: 'قبول'
|
||||
};
|
||||
|
||||
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/salary/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/salary/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();
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.loadData();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<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>
|
Loading…
Reference in a new issue