Compare commits
10 commits
888c76c633
...
dad184671b
Author | SHA1 | Date | |
---|---|---|---|
|
dad184671b | ||
|
748001b9fa | ||
|
5fc8fa9d67 | ||
|
752295b83a | ||
|
0f9d8915a6 | ||
|
ab07489f57 | ||
|
2f38d5dc6b | ||
|
83baf8ffb4 | ||
|
36802cbfcf | ||
|
cc1fe8216d |
|
@ -23,6 +23,20 @@ services:
|
|||
networks:
|
||||
- hesabix-network
|
||||
restart: unless-stopped
|
||||
command: >
|
||||
bash -c "
|
||||
apt-get update &&
|
||||
apt-get install -y curl unzip php-mbstring php-gd php-soap &&
|
||||
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer &&
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - &&
|
||||
apt-get install -y nodejs &&
|
||||
cd /var/www/html/hesabixCore &&
|
||||
composer install &&
|
||||
cd /var/www/html/webUI &&
|
||||
npm install &&
|
||||
npm run build-only &&
|
||||
apache2-foreground
|
||||
"
|
||||
|
||||
# Database service
|
||||
db:
|
||||
|
@ -34,7 +48,7 @@ services:
|
|||
- MYSQL_PASSWORD=hesabix_password
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./hesabixBackup:/docker-entrypoint-initdb.d
|
||||
- ./hesabixBackup/databasefiles:/docker-entrypoint-initdb.d
|
||||
networks:
|
||||
- hesabix-network
|
||||
healthcheck:
|
||||
|
@ -43,6 +57,12 @@ services:
|
|||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
command: >
|
||||
bash -c "
|
||||
docker-entrypoint.sh mysqld &
|
||||
sleep 30 &&
|
||||
mysql -u root -proot_password hesabix_db < /docker-entrypoint-initdb.d/hesabix-db-default.sql
|
||||
"
|
||||
|
||||
# phpMyAdmin service
|
||||
phpmyadmin:
|
||||
|
|
|
@ -1938,4 +1938,5 @@ class PersonsController extends AbstractController
|
|||
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,16 +7,11 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use App\Entity\PlugGhestaDoc;
|
||||
use App\Entity\PlugGhestaItem;
|
||||
use App\Entity\HesabdariDoc;
|
||||
use App\Entity\PlugHrmDoc;
|
||||
use App\Entity\PlugHrmDocItem;
|
||||
use App\Entity\Person;
|
||||
use App\Service\Access;
|
||||
use App\Service\Provider;
|
||||
use App\Service\Printers;
|
||||
use App\Entity\PrintOptions;
|
||||
use App\Service\Log;
|
||||
use App\Entity\Business;
|
||||
|
||||
class DocsController extends AbstractController
|
||||
{
|
||||
|
@ -28,24 +23,209 @@ class DocsController extends AbstractController
|
|||
}
|
||||
|
||||
#[Route('/api/hrm/docs/list', name: 'hrm_docs_list', methods: ['POST'])]
|
||||
public function list(Request $request): JsonResponse
|
||||
public function list(Request $request, Access $access): JsonResponse
|
||||
{
|
||||
// TODO: پیادهسازی دریافت لیست اسناد حقوق
|
||||
return new JsonResponse([]);
|
||||
try {
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
$params = json_decode($content, true);
|
||||
}
|
||||
|
||||
$acc = $access->hasRole('hrm');
|
||||
|
||||
// دریافت پارامترهای فیلتر
|
||||
$page = $params['page'] ?? 1;
|
||||
$limit = $params['limit'] ?? 20;
|
||||
$search = $params['search'] ?? '';
|
||||
$fromDate = $params['fromDate'] ?? null;
|
||||
$toDate = $params['toDate'] ?? null;
|
||||
$personId = $params['personId'] ?? null;
|
||||
|
||||
// ایجاد کوئری
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
$qb->select('d')
|
||||
->from(PlugHrmDoc::class, 'd')
|
||||
->where('d.business = :bid')
|
||||
->setParameter('bid', $acc['bid']);
|
||||
|
||||
// اعمال فیلترها
|
||||
if ($search) {
|
||||
$qb->andWhere('d.description LIKE :search')
|
||||
->setParameter('search', '%' . $search . '%');
|
||||
}
|
||||
|
||||
if ($fromDate) {
|
||||
$qb->andWhere('d.date >= :fromDate')
|
||||
->setParameter('fromDate', $fromDate);
|
||||
}
|
||||
|
||||
if ($toDate) {
|
||||
$qb->andWhere('d.date <= :toDate')
|
||||
->setParameter('toDate', $toDate);
|
||||
}
|
||||
|
||||
if ($personId) {
|
||||
$qb->andWhere('d.person = :personId')
|
||||
->setParameter('personId', $personId);
|
||||
}
|
||||
|
||||
// محاسبه تعداد کل رکوردها
|
||||
$countQb = clone $qb;
|
||||
$countQb->select('COUNT(d.id)');
|
||||
$total = $countQb->getQuery()->getSingleScalarResult();
|
||||
|
||||
// اعمال مرتبسازی و صفحهبندی
|
||||
$qb->orderBy('d.date', 'DESC')
|
||||
->setFirstResult(($page - 1) * $limit)
|
||||
->setMaxResults($limit);
|
||||
|
||||
$docs = $qb->getQuery()->getResult();
|
||||
|
||||
// تبدیل نتایج به آرایه
|
||||
$result = [];
|
||||
foreach ($docs as $doc) {
|
||||
$result[] = [
|
||||
'id' => $doc->getId(),
|
||||
'date' => $doc->getDate(),
|
||||
'description' => $doc->getDescription(),
|
||||
'creator' => $doc->getCreator() ? [
|
||||
'id' => $doc->getCreator()->getId(),
|
||||
'name' => $doc->getCreator()->getFullName()
|
||||
] : null,
|
||||
'total' => $this->calculateTotalAmount($doc),
|
||||
'accounting_doc' => $doc->getHesabdariDoc() ? 'صدور شده' : 'صدور نشده',
|
||||
'status' => $doc->getHesabdariDoc() ? 'تایید شده' : 'تایید نشده'
|
||||
];
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'data' => $result,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return new JsonResponse(['error' => 'خطا در دریافت لیست اسناد: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateTotalAmount(PlugHrmDoc $doc): int
|
||||
{
|
||||
$total = 0;
|
||||
foreach ($doc->getItems() as $item) {
|
||||
$total += $item->getBaseSalary();
|
||||
$total += $item->getNight();
|
||||
$total += $item->getShift();
|
||||
$total += $item->getOvertime();
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
|
||||
#[Route('/api/hrm/docs/get/{id}', name: 'hrm_docs_get', methods: ['POST'])]
|
||||
public function get(int $id): JsonResponse
|
||||
public function get(int $id, Access $access): JsonResponse
|
||||
{
|
||||
// TODO: پیادهسازی دریافت اطلاعات یک سند حقوق
|
||||
return new JsonResponse([]);
|
||||
try {
|
||||
$acc = $access->hasRole('hrm');
|
||||
|
||||
$doc = $this->entityManager->getRepository(PlugHrmDoc::class)->findOneBy([
|
||||
'id' => $id,
|
||||
'business' => $acc['bid']
|
||||
]);
|
||||
|
||||
if (!$doc) {
|
||||
return new JsonResponse(['error' => 'سند مورد نظر یافت نشد'], 404);
|
||||
}
|
||||
|
||||
$items = [];
|
||||
foreach ($doc->getItems() as $item) {
|
||||
$items[] = [
|
||||
'id' => $item->getId(),
|
||||
'person' => [
|
||||
'id' => $item->getPerson()->getId(),
|
||||
'name' => $item->getPerson()->getNikename(),
|
||||
'code' => $item->getPerson()->getCode(),
|
||||
],
|
||||
'baseSalary' => $item->getBaseSalary(),
|
||||
'overtime' => $item->getOvertime(),
|
||||
'shift' => $item->getShift(),
|
||||
'night' => $item->getNight(),
|
||||
'description' => $item->getDescription()
|
||||
];
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'id' => $doc->getId(),
|
||||
'date' => $doc->getDate(),
|
||||
'description' => $doc->getDescription(),
|
||||
'items' => $items
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return new JsonResponse(['error' => 'خطا در دریافت اطلاعات سند: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
#[Route('/api/hrm/docs/insert', name: 'hrm_docs_insert', methods: ['POST'])]
|
||||
public function insert(Request $request): JsonResponse
|
||||
public function insert(Request $request, Access $access, Log $log): JsonResponse
|
||||
{
|
||||
// TODO: پیادهسازی ثبت سند حقوق جدید
|
||||
return new JsonResponse([]);
|
||||
try {
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
$params = json_decode($content, true);
|
||||
}
|
||||
|
||||
$acc = $access->hasRole('hrm');
|
||||
|
||||
// بررسی دادههای ورودی
|
||||
if (empty($params['date']) || empty($params['description']) || empty($params['items'])) {
|
||||
return new JsonResponse(['error' => 'اطلاعات ناقص است'], 400);
|
||||
}
|
||||
|
||||
// ایجاد سند جدید
|
||||
$doc = new PlugHrmDoc();
|
||||
$doc->setDate($params['date']);
|
||||
$doc->setCreator($this->getUser());
|
||||
$doc->setBusiness($acc['bid']);
|
||||
$doc->setDescription($params['description']);
|
||||
$doc->setCreateDate(time());
|
||||
// افزودن آیتمها
|
||||
foreach ($params['items'] as $itemData) {
|
||||
if (empty($itemData['person'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = new PlugHrmDocItem();
|
||||
$item->setPerson($this->entityManager->getReference(Person::class, $itemData['person']));
|
||||
$item->setBaseSalary($itemData['baseSalary'] ?? 0);
|
||||
$item->setOvertime($itemData['overtime'] ?? 0);
|
||||
$item->setShift($itemData['shift'] ?? 0);
|
||||
$item->setNight($itemData['night'] ?? 0);
|
||||
$item->setDescription($itemData['description'] ?? '');
|
||||
$item->setDoc($doc);
|
||||
|
||||
$this->entityManager->persist($item);
|
||||
}
|
||||
|
||||
$this->entityManager->persist($doc);
|
||||
$this->entityManager->flush();
|
||||
|
||||
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'message' => 'سند با موفقیت ثبت شد',
|
||||
'data' => [
|
||||
'id' => $doc->getId()
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return new JsonResponse(['error' => 'خطا در ثبت سند: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
#[Route('/api/hrm/docs/update', name: 'hrm_docs_update', methods: ['POST'])]
|
||||
|
@ -56,9 +236,47 @@ class DocsController extends AbstractController
|
|||
}
|
||||
|
||||
#[Route('/api/hrm/docs/delete', name: 'hrm_docs_delete', methods: ['POST'])]
|
||||
public function delete(Request $request): JsonResponse
|
||||
public function delete(Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
// TODO: پیادهسازی حذف سند حقوق
|
||||
return new JsonResponse([]);
|
||||
try {
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
$params = json_decode($content, true);
|
||||
}
|
||||
|
||||
$acc = $access->hasRole('hrm');
|
||||
$id = $params['id'] ?? null;
|
||||
|
||||
if (!$id) {
|
||||
return new JsonResponse(['error' => 'شناسه سند الزامی است'], 400);
|
||||
}
|
||||
|
||||
$doc = $entityManager->getRepository(PlugHrmDoc::class)->findOneBy([
|
||||
'id' => $id,
|
||||
'business' => $acc['bid']
|
||||
]);
|
||||
if (!$doc) {
|
||||
return new JsonResponse(['error' => 'سند مورد نظر یافت نشد'], 404);
|
||||
}
|
||||
|
||||
// حذف آیتمهای سند
|
||||
foreach ($doc->getItems() as $item) {
|
||||
$entityManager->remove($item);
|
||||
}
|
||||
|
||||
// حذف سند حسابداری در صورت وجود
|
||||
if($doc->getHesabdariDoc()){
|
||||
$entityManager->remove($doc->getHesabdariDoc());
|
||||
}
|
||||
// حذف سند
|
||||
$entityManager->remove($doc);
|
||||
$entityManager->flush();
|
||||
|
||||
|
||||
return new JsonResponse(['success' => true, 'message' => 'سند با موفقیت حذف شد']);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return new JsonResponse(['error' => 'خطا در حذف سند: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|||
use App\Entity\BankAccount;
|
||||
use App\Entity\Cashdesk;
|
||||
use App\Entity\Salary;
|
||||
use App\Entity\Year;
|
||||
|
||||
class SellController extends AbstractController
|
||||
{
|
||||
|
@ -46,8 +47,16 @@ class SellController extends AbstractController
|
|||
'code' => $code,
|
||||
'money' => $acc['money']
|
||||
]);
|
||||
if (count($doc->getRelatedDocs()) != 0)
|
||||
if (!$doc) {
|
||||
$canEdit = false;
|
||||
}
|
||||
$year = $entityManager->getRepository(Year::class)->findOneBy([
|
||||
'bid' => $acc['bid'],
|
||||
'head' => true
|
||||
]);
|
||||
if ($doc->getYear()->getId() != $year->getId()) {
|
||||
$canEdit = false;
|
||||
}
|
||||
|
||||
$tickets = $entityManager->getRepository(StoreroomTicket::class)->findBy(['doc' => $doc]);
|
||||
if (count($tickets) != 0)
|
||||
|
@ -212,11 +221,11 @@ class SellController extends AbstractController
|
|||
]);
|
||||
$hesabdariRow->setRef($ref);
|
||||
$entityManager->persist($hesabdariRow);
|
||||
|
||||
|
||||
// ذخیره نوع تخفیف و درصد آن
|
||||
$doc->setDiscountType($params['discountType'] ?? 'fixed');
|
||||
if (isset($params['discountPercent'])) {
|
||||
$doc->setDiscountPercent((float)$params['discountPercent']);
|
||||
$doc->setDiscountPercent((float) $params['discountPercent']);
|
||||
}
|
||||
}
|
||||
$doc->setDes($params['des']);
|
||||
|
@ -711,10 +720,10 @@ class SellController extends AbstractController
|
|||
$params['printers'] = $params['printers'] ?? false;
|
||||
$params['pdf'] = $params['pdf'] ?? true;
|
||||
$params['posPrint'] = $params['posPrint'] ?? false;
|
||||
|
||||
|
||||
// دریافت تنظیمات پیشفرض از PrintOptions
|
||||
$printSettings = $entityManager->getRepository(PrintOptions::class)->findOneBy(['bid' => $acc['bid']]);
|
||||
|
||||
|
||||
// تنظیم مقادیر پیشفرض از تنظیمات ذخیره شده
|
||||
$defaultOptions = [
|
||||
'note' => $printSettings ? $printSettings->isSellNote() : true,
|
||||
|
@ -726,7 +735,7 @@ class SellController extends AbstractController
|
|||
'invoiceIndex' => $printSettings ? $printSettings->isSellInvoiceIndex() : true,
|
||||
'businessStamp' => $printSettings ? $printSettings->isSellBusinessStamp() : true
|
||||
];
|
||||
|
||||
|
||||
// اولویت با پارامترهای ارسالی است
|
||||
$printOptions = array_merge($defaultOptions, $params['printOptions'] ?? []);
|
||||
|
||||
|
@ -750,6 +759,23 @@ class SellController extends AbstractController
|
|||
}
|
||||
}
|
||||
$pdfPid = 0;
|
||||
|
||||
// فیلد جدید وضعیت حساب مشتری
|
||||
$personItems = $entityManager->getRepository(HesabdariRow::class)->findBy(['bid' => $acc['bid'], 'person' => $person]);
|
||||
$accountStatus = [];
|
||||
$bs = 0;
|
||||
$bd = 0;
|
||||
foreach ($personItems as $item) {
|
||||
$bs += $item->getBs();
|
||||
$bd += $item->getBd();
|
||||
}
|
||||
if ($bs > $bd) {
|
||||
$accountStatus['label'] = 'بستانکار';
|
||||
$accountStatus['value'] = $bs - $bd;
|
||||
} else {
|
||||
$accountStatus['label'] = 'بدهکار';
|
||||
$accountStatus['value'] = $bd - $bs;
|
||||
}
|
||||
if ($params['pdf'] == true || $params['printers'] == true) {
|
||||
$note = '';
|
||||
if ($printSettings) {
|
||||
|
@ -759,9 +785,10 @@ class SellController extends AbstractController
|
|||
$acc['bid'],
|
||||
$this->getUser(),
|
||||
$this->renderView('pdf/printers/sell.html.twig', [
|
||||
'accountStatus' => $accountStatus,
|
||||
'bid' => $acc['bid'],
|
||||
'doc' => $doc,
|
||||
'rows' => array_map(function($row) {
|
||||
'rows' => array_map(function ($row) {
|
||||
return [
|
||||
'commodity' => $row->getCommodity(),
|
||||
'commodityCount' => $row->getCommdityCount(),
|
||||
|
@ -793,7 +820,7 @@ class SellController extends AbstractController
|
|||
$this->renderView('pdf/posPrinters/justSell.html.twig', [
|
||||
'bid' => $acc['bid'],
|
||||
'doc' => $doc,
|
||||
'rows' => array_map(function($row) {
|
||||
'rows' => array_map(function ($row) {
|
||||
return [
|
||||
'commodity' => $row->getCommodity(),
|
||||
'commodityCount' => $row->getCommdityCount(),
|
||||
|
@ -868,7 +895,8 @@ class SellController extends AbstractController
|
|||
Access $access,
|
||||
Log $log,
|
||||
EntityManagerInterface $entityManager,
|
||||
registryMGR $registryMGR
|
||||
registryMGR $registryMGR,
|
||||
Jdate $jdate
|
||||
): JsonResponse {
|
||||
$params = [];
|
||||
if ($content = $request->getContent()) {
|
||||
|
@ -932,12 +960,22 @@ class SellController extends AbstractController
|
|||
}
|
||||
|
||||
// تنظیم اطلاعات اصلی فاکتور
|
||||
$doc->setDes($params['invoiceDescription']);
|
||||
$doc->setDate($params['invoiceDate']);
|
||||
$doc->setTaxPercent($params['taxPercent'] ?? 0);
|
||||
if (isset($params['invoiceDescription'])) {
|
||||
$doc->setDes($params['invoiceDescription']);
|
||||
} else {
|
||||
$doc->setDes('');
|
||||
}
|
||||
if (isset($params['invoiceDate'])) {
|
||||
$doc->setDate($params['invoiceDate']);
|
||||
} else {
|
||||
$doc->setDate($jdate->jdate('Y/n/d', time()));
|
||||
}
|
||||
if (isset($params['taxPercent'])) {
|
||||
$doc->setTaxPercent($params['taxPercent'] ?? 0);
|
||||
}
|
||||
|
||||
// افزودن هزینه حمل
|
||||
if ($params['shippingCost'] > 0) {
|
||||
if (isset($params['shippingCost']) && $params['shippingCost'] > 0) {
|
||||
$hesabdariRow = new HesabdariRow();
|
||||
$hesabdariRow->setDes('حمل و نقل کالا');
|
||||
$hesabdariRow->setBid($acc['bid']);
|
||||
|
@ -952,17 +990,21 @@ class SellController extends AbstractController
|
|||
|
||||
// افزودن تخفیف کلی
|
||||
$totalDiscount = 0;
|
||||
if ($params['discountType'] === 'percent') {
|
||||
if (isset($params['discountType']) && $params['discountType'] === 'percent') {
|
||||
$totalDiscount = round(($params['totalInvoice'] * $params['discountPercent']) / 100);
|
||||
$doc->setDiscountType('percent');
|
||||
$doc->setDiscountPercent((float)$params['discountPercent']);
|
||||
$doc->setDiscountPercent((float) $params['discountPercent']);
|
||||
} else {
|
||||
$totalDiscount = $params['totalDiscount'];
|
||||
if (isset($params['totalDiscount']) && $params['totalDiscount'] > 0) {
|
||||
$totalDiscount = $params['totalDiscount'];
|
||||
} else {
|
||||
$totalDiscount = 0;
|
||||
}
|
||||
$doc->setDiscountType('fixed');
|
||||
$doc->setDiscountPercent(null);
|
||||
}
|
||||
|
||||
if ($totalDiscount > 0) {
|
||||
if (isset($totalDiscount) && $totalDiscount > 0) {
|
||||
$hesabdariRow = new HesabdariRow();
|
||||
$hesabdariRow->setDes('تخفیف فاکتور');
|
||||
$hesabdariRow->setBid($acc['bid']);
|
||||
|
@ -1046,10 +1088,25 @@ class SellController extends AbstractController
|
|||
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '3']);
|
||||
$hesabdariRow->setRef($ref);
|
||||
|
||||
$person = $entityManager->getRepository(Person::class)->findOneBy([
|
||||
'bid' => $acc['bid'],
|
||||
'id' => $params['customer']
|
||||
]);
|
||||
if (!isset($params['customer']) || $params['customer'] == '') {
|
||||
$person = $entityManager->getRepository(Person::class)->findOneBy([
|
||||
'bid' => $acc['bid'],
|
||||
'nikename' => 'مشتری پیش فرض'
|
||||
]);
|
||||
if (!$person) {
|
||||
$person = new Person();
|
||||
$person->setBid($acc['bid']);
|
||||
$person->setNikename('مشتری پیش فرض');
|
||||
$person->setCode($provider->getAccountingCode($acc['bid'], 'person'));
|
||||
$entityManager->persist($person);
|
||||
}
|
||||
} else {
|
||||
$person = $entityManager->getRepository(Person::class)->findOneBy([
|
||||
'bid' => $acc['bid'],
|
||||
'id' => $params['customer']
|
||||
]);
|
||||
}
|
||||
|
||||
if (!$person) {
|
||||
throw new \Exception('خریدار یافت نشد');
|
||||
}
|
||||
|
@ -1082,10 +1139,10 @@ class SellController extends AbstractController
|
|||
$paymentDoc->setDate($params['invoiceDate']);
|
||||
$paymentDoc->setDes($payment['description'] ?? 'دریافت وجه فاکتور فروش شماره ' . $doc->getCode());
|
||||
$paymentDoc->setAmount($payment['amount']);
|
||||
|
||||
|
||||
// ایجاد ارتباط با فاکتور اصلی
|
||||
$doc->addRelatedDoc($paymentDoc);
|
||||
|
||||
|
||||
// ایجاد سطرهای حسابداری بر اساس نوع پرداخت
|
||||
if ($payment['type'] === 'bank') {
|
||||
// دریافت از طریق حساب بانکی
|
||||
|
@ -1191,7 +1248,8 @@ class SellController extends AbstractController
|
|||
} catch (\Exception $e) {
|
||||
return $this->json([
|
||||
'result' => 0,
|
||||
'message' => $e->getMessage()
|
||||
'message' => $e->getMessage(),
|
||||
'data' => $params
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -1232,7 +1290,7 @@ class SellController extends AbstractController
|
|||
|
||||
// دریافت اسناد پرداخت مرتبط
|
||||
$relatedDocs = $doc->getRelatedDocs();
|
||||
|
||||
|
||||
foreach ($relatedDocs as $relatedDoc) {
|
||||
if ($relatedDoc->getType() === 'sell_receive') {
|
||||
$payment = [
|
||||
|
@ -1275,7 +1333,7 @@ class SellController extends AbstractController
|
|||
$itemDiscountType = $row->getDiscountType() ?? 'fixed';
|
||||
$itemDiscountPercent = $row->getDiscountPercent() ?? 0;
|
||||
$itemTax = $row->getTax() ?? 0;
|
||||
|
||||
|
||||
// محاسبه قیمت واحد و تخفیف
|
||||
if ($itemDiscountType === 'percent' && $itemDiscountPercent > 0) {
|
||||
// محاسبه قیمت اصلی در حالت تخفیف درصدی
|
||||
|
@ -1285,14 +1343,14 @@ class SellController extends AbstractController
|
|||
// محاسبه قیمت اصلی در حالت تخفیف مقداری
|
||||
$originalPrice = $basePrice + $itemDiscount;
|
||||
}
|
||||
|
||||
|
||||
// محاسبه قیمت واحد
|
||||
$unitPrice = $row->getCommdityCount() > 0 ? $originalPrice / $row->getCommdityCount() : 0;
|
||||
|
||||
|
||||
// محاسبه قیمت خالص (بدون مالیات)
|
||||
$netPrice = $basePrice;
|
||||
$totalInvoice += $netPrice;
|
||||
|
||||
|
||||
$items[] = [
|
||||
'name' => [
|
||||
'id' => $row->getCommodity()->getId(),
|
||||
|
|
|
@ -19,7 +19,7 @@ final class DatabaseController extends AbstractController
|
|||
{
|
||||
$this->registryMGR = $registryMGR;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->backupPath = dirname(__DIR__, 2) . '/hesabixBackup/versions';
|
||||
$this->backupPath = dirname(__DIR__, 4) . '/hesabixBackup/versions';
|
||||
}
|
||||
|
||||
#[Route('/api/admin/database/backup/info', name: 'app_admin_database_backup_info', methods: ['GET'])]
|
||||
|
@ -53,6 +53,11 @@ final class DatabaseController extends AbstractController
|
|||
$filename = 'Hesabix-' . time() . '.sql';
|
||||
$filepath = $this->backupPath . '/' . $filename;
|
||||
|
||||
// بررسی دسترسی پوشه مقصد
|
||||
if (!is_writable($this->backupPath)) {
|
||||
throw new \Exception('پوشه مقصد قابل نوشتن نیست: ' . $this->backupPath);
|
||||
}
|
||||
|
||||
// دریافت تنظیمات دیتابیس از EntityManager
|
||||
$connection = $this->entityManager->getConnection();
|
||||
$params = $connection->getParams();
|
||||
|
@ -65,7 +70,7 @@ final class DatabaseController extends AbstractController
|
|||
|
||||
// دستور mysqldump
|
||||
$command = sprintf(
|
||||
'mysqldump -h %s -P %s -u %s -p%s %s > %s',
|
||||
'mysqldump -h %s -P %s -u %s -p%s %s > %s 2>&1',
|
||||
escapeshellarg($dbHost),
|
||||
escapeshellarg($dbPort),
|
||||
escapeshellarg($dbUser),
|
||||
|
@ -90,8 +95,8 @@ final class DatabaseController extends AbstractController
|
|||
'message' => 'پشتیبان با موفقیت ایجاد شد'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return $this->json([
|
||||
'result' => 0,
|
||||
return $this->json([
|
||||
'result' => 0,
|
||||
'message' => 'خطا در ایجاد پشتیبان: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
|
@ -160,12 +165,16 @@ final class DatabaseController extends AbstractController
|
|||
$remoteDir = dirname($remotePath);
|
||||
|
||||
// بررسی دسترسی نوشتن در مسیر
|
||||
$testFile = 'test_' . time() . '.txt';
|
||||
if (!@ftp_put($ftp, $testFile, 'test', FTP_ASCII)) {
|
||||
$testFileLocal = tempnam(sys_get_temp_dir(), 'ftp_test_');
|
||||
file_put_contents($testFileLocal, 'test');
|
||||
$testFileRemote = 'test_' . time() . '.txt';
|
||||
if (!@ftp_put($ftp, $testFileRemote, $testFileLocal, FTP_ASCII)) {
|
||||
unlink($testFileLocal);
|
||||
ftp_close($ftp);
|
||||
throw new \Exception('کاربر FTP دسترسی نوشتن ندارد');
|
||||
}
|
||||
ftp_delete($ftp, $testFile);
|
||||
ftp_delete($ftp, $testFileRemote);
|
||||
unlink($testFileLocal);
|
||||
|
||||
// ایجاد مسیر در صورت عدم وجود
|
||||
$this->createFtpDirectory($ftp, $remoteDir);
|
||||
|
@ -177,7 +186,8 @@ final class DatabaseController extends AbstractController
|
|||
}
|
||||
|
||||
// آپلود فایل
|
||||
if (!ftp_put($ftp, basename($remotePath), $filepath, FTP_BINARY)) {
|
||||
$result = ftp_put($ftp, basename($remotePath), $filepath, FTP_BINARY);
|
||||
if (!$result) {
|
||||
$error = error_get_last();
|
||||
ftp_close($ftp);
|
||||
throw new \Exception('خطا در آپلود فایل به سرور FTP: ' . ($error['message'] ?? 'خطای نامشخص'));
|
||||
|
|
|
@ -155,11 +155,13 @@ final class RegistrySettingsController extends AbstractController
|
|||
}
|
||||
|
||||
// تست دسترسی به مسیر
|
||||
if (!ftp_chdir($ftp, $data['path'])) {
|
||||
if (!@ftp_chdir($ftp, $data['path'])) {
|
||||
$currentDir = ftp_pwd($ftp); // دریافت مسیر فعلی
|
||||
ftp_close($ftp);
|
||||
return $this->json([
|
||||
'success' => false,
|
||||
'message' => 'مسیر مورد نظر قابل دسترسی نیست'
|
||||
'message' => 'مسیر مورد نظر قابل دسترسی نیست',
|
||||
'suggested_path' => $currentDir // پیشنهاد مسیر فعلی
|
||||
], 400);
|
||||
}
|
||||
|
||||
|
|
|
@ -297,6 +297,9 @@ class Business
|
|||
#[ORM\OneToMany(targetEntity: PlugGhestaDoc::class, mappedBy: 'bid', orphanRemoval: true)]
|
||||
private Collection $PlugGhestaDocs;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'business', targetEntity: PlugHrmDoc::class)]
|
||||
private Collection $plugHrmDocs;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->logs = new ArrayCollection();
|
||||
|
@ -339,6 +342,7 @@ class Business
|
|||
$this->hesabdariTables = new ArrayCollection();
|
||||
$this->accountingPackageOrders = new ArrayCollection();
|
||||
$this->PlugGhestaDocs = new ArrayCollection();
|
||||
$this->plugHrmDocs = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
|
@ -2055,4 +2059,31 @@ class Business
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, PlugHrmDoc>
|
||||
*/
|
||||
public function getPlugHrmDocs(): Collection
|
||||
{
|
||||
return $this->plugHrmDocs;
|
||||
}
|
||||
|
||||
public function addPlugHrmDoc(PlugHrmDoc $plugHrmDoc): static
|
||||
{
|
||||
if (!$this->plugHrmDocs->contains($plugHrmDoc)) {
|
||||
$this->plugHrmDocs->add($plugHrmDoc);
|
||||
$plugHrmDoc->setBusiness($this);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removePlugHrmDoc(PlugHrmDoc $plugHrmDoc): static
|
||||
{
|
||||
if ($this->plugHrmDocs->removeElement($plugHrmDoc)) {
|
||||
if ($plugHrmDoc->getBusiness() === $this) {
|
||||
$plugHrmDoc->setBusiness(null);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,12 @@ class HesabdariDoc
|
|||
#[ORM\OneToMany(targetEntity: PlugGhestaDoc::class, mappedBy: 'mainDoc', orphanRemoval: true)]
|
||||
private Collection $plugGhestaDocs;
|
||||
|
||||
/**
|
||||
* @var Collection<int, PlugHrmDoc>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: PlugHrmDoc::class, mappedBy: 'hesabdariDoc')]
|
||||
private Collection $plugHrmDocs;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->hesabdariRows = new ArrayCollection();
|
||||
|
@ -151,6 +157,7 @@ class HesabdariDoc
|
|||
$this->pairDoc = new ArrayCollection();
|
||||
$this->plugGhestaItems = new ArrayCollection();
|
||||
$this->plugGhestaDocs = new ArrayCollection();
|
||||
$this->plugHrmDocs = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
|
@ -689,4 +696,34 @@ class HesabdariDoc
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, PlugHrmDoc>
|
||||
*/
|
||||
public function getPlugHrmDocs(): Collection
|
||||
{
|
||||
return $this->plugHrmDocs;
|
||||
}
|
||||
|
||||
public function addPlugHrmDoc(PlugHrmDoc $plugHrmDoc): static
|
||||
{
|
||||
if (!$this->plugHrmDocs->contains($plugHrmDoc)) {
|
||||
$this->plugHrmDocs->add($plugHrmDoc);
|
||||
$plugHrmDoc->setHesabdariDoc($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removePlugHrmDoc(PlugHrmDoc $plugHrmDoc): static
|
||||
{
|
||||
if ($this->plugHrmDocs->removeElement($plugHrmDoc)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($plugHrmDoc->getHesabdariDoc() === $this) {
|
||||
$plugHrmDoc->setHesabdariDoc(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
144
hesabixCore/src/Entity/PlugHrmDoc.php
Normal file
144
hesabixCore/src/Entity/PlugHrmDoc.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\PlugHrmDocRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: PlugHrmDocRepository::class)]
|
||||
class PlugHrmDoc
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $description = null;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 10)]
|
||||
private ?string $date = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'plugHrmDocs')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?Business $business = null;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?User $creator = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $createDate = null;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'doc', targetEntity: PlugHrmDocItem::class, orphanRemoval: true)]
|
||||
private Collection $items;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'plugHrmDocs')]
|
||||
private ?HesabdariDoc $hesabdariDoc = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->items = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): static
|
||||
{
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDate(): ?string
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public function setDate(string $date): self
|
||||
{
|
||||
$this->date = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBusiness(): ?Business
|
||||
{
|
||||
return $this->business;
|
||||
}
|
||||
|
||||
public function setBusiness(?Business $business): static
|
||||
{
|
||||
$this->business = $business;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreator(): ?User
|
||||
{
|
||||
return $this->creator;
|
||||
}
|
||||
|
||||
public function setCreator(?User $creator): static
|
||||
{
|
||||
$this->creator = $creator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreateDate(): ?int
|
||||
{
|
||||
return $this->createDate;
|
||||
}
|
||||
|
||||
public function setCreateDate(int $createDate): static
|
||||
{
|
||||
$this->createDate = $createDate;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, PlugHrmDocItem>
|
||||
*/
|
||||
public function getItems(): Collection
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function addItem(PlugHrmDocItem $item): static
|
||||
{
|
||||
if (!$this->items->contains($item)) {
|
||||
$this->items->add($item);
|
||||
$item->setDoc($this);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeItem(PlugHrmDocItem $item): static
|
||||
{
|
||||
if ($this->items->removeElement($item)) {
|
||||
if ($item->getDoc() === $this) {
|
||||
$item->setDoc(null);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHesabdariDoc(): ?HesabdariDoc
|
||||
{
|
||||
return $this->hesabdariDoc;
|
||||
}
|
||||
|
||||
public function setHesabdariDoc(?HesabdariDoc $hesabdariDoc): static
|
||||
{
|
||||
$this->hesabdariDoc = $hesabdariDoc;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
125
hesabixCore/src/Entity/PlugHrmDocItem.php
Normal file
125
hesabixCore/src/Entity/PlugHrmDocItem.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\PlugHrmDocItemRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: PlugHrmDocItemRepository::class)]
|
||||
class PlugHrmDocItem
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'items')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?PlugHrmDoc $doc = null;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?Person $person = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $baseSalary = 0;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $overtime = 0;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $shift = 0;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $night = 0;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $description = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getDoc(): ?PlugHrmDoc
|
||||
{
|
||||
return $this->doc;
|
||||
}
|
||||
|
||||
public function setDoc(?PlugHrmDoc $doc): static
|
||||
{
|
||||
$this->doc = $doc;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPerson(): ?Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function setPerson(?Person $person): static
|
||||
{
|
||||
$this->person = $person;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBaseSalary(): ?int
|
||||
{
|
||||
return $this->baseSalary;
|
||||
}
|
||||
|
||||
public function setBaseSalary(int $baseSalary): static
|
||||
{
|
||||
$this->baseSalary = $baseSalary;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOvertime(): ?int
|
||||
{
|
||||
return $this->overtime;
|
||||
}
|
||||
|
||||
public function setOvertime(int $overtime): static
|
||||
{
|
||||
$this->overtime = $overtime;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShift(): ?int
|
||||
{
|
||||
return $this->shift;
|
||||
}
|
||||
|
||||
public function setShift(int $shift): static
|
||||
{
|
||||
$this->shift = $shift;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNight(): ?int
|
||||
{
|
||||
return $this->night;
|
||||
}
|
||||
|
||||
public function setNight(int $night): static
|
||||
{
|
||||
$this->night = $night;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(?string $description): static
|
||||
{
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTotal(): int
|
||||
{
|
||||
return $this->baseSalary + $this->overtime + $this->shift + $this->night;
|
||||
}
|
||||
}
|
15
hesabixCore/src/Repository/PlugHrmDocItemRepository.php
Normal file
15
hesabixCore/src/Repository/PlugHrmDocItemRepository.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\PlugHrmDocItem;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
class PlugHrmDocItemRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, PlugHrmDocItem::class);
|
||||
}
|
||||
}
|
15
hesabixCore/src/Repository/PlugHrmDocRepository.php
Normal file
15
hesabixCore/src/Repository/PlugHrmDocRepository.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\PlugHrmDoc;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
class PlugHrmDocRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, PlugHrmDoc::class);
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@
|
|||
<td class="center item">تفضیل</td>
|
||||
<td class="center item">بدهکار</td>
|
||||
<td class="center item">بستانکار</td>
|
||||
<td class="center item">سال مالی</td>
|
||||
<td class="center item">شرح سند</td>
|
||||
</tr>
|
||||
{% set sumBs = 0 %}
|
||||
{% set sumBd = 0 %}
|
||||
|
@ -96,7 +96,7 @@
|
|||
<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>
|
||||
<td class="center item">{{ item.doc.des }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
@ -199,7 +199,7 @@
|
|||
<tr class="stimol">
|
||||
<td class="item" style="padding:1%">
|
||||
<h4>توضیحات:</h4>
|
||||
{{ note|nl2br }}
|
||||
{{ note|raw }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<p>
|
||||
<b>نام:
|
||||
</b>
|
||||
{{ bid.legalName }}
|
||||
{% if bid.legalName is not empty %}{{ bid.legalName }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
|
@ -78,27 +78,27 @@
|
|||
<b>
|
||||
شناسه ملی:
|
||||
</b>
|
||||
{{ bid.shenasemeli }}
|
||||
{% if bid.shenasemeli is not empty %}{{ bid.shenasemeli }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>شماره ثبت:
|
||||
</b>
|
||||
{{ bid.shomaresabt }}
|
||||
{% if bid.shomaresabt is not empty %}{{ bid.shomaresabt }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>شماره اقتصادی:
|
||||
</b>
|
||||
{{ bid.codeeghtesadi }}
|
||||
{% if bid.codeeghtesadi is not empty %}{{ bid.codeeghtesadi }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>تلفن / نمابر:</b>
|
||||
{{ bid.tel }}
|
||||
{% if bid.tel is not empty %}{{ bid.tel }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -106,17 +106,14 @@
|
|||
<td class="" colspan="1">
|
||||
<p>
|
||||
<b>کد پستی:</b>
|
||||
{{ bid.postalcode }}
|
||||
{% if bid.postalcode is not empty %}{{ bid.postalcode }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="" colspan="3">
|
||||
<p>
|
||||
<b>آدرس:
|
||||
</b>
|
||||
استان
|
||||
{{ bid.ostan }}، شهر
|
||||
{{ bid.shahrestan }}،
|
||||
{{ bid.address }}
|
||||
{% if bid.ostan is not empty %}استان {{ bid.ostan }}{% endif %}{% if bid.shahrestan is not empty %}، شهر {{ bid.shahrestan }}{% endif %}{% if bid.address is not empty %}، {{ bid.address }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -138,34 +135,34 @@
|
|||
<b>نام:
|
||||
</b>
|
||||
{% if person.prelabel is not null %}{{ person.prelabel.label }}{% endif %}
|
||||
{{ person.nikename }}
|
||||
{% if person.nikename is not empty %}{{ person.nikename }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b> شناسه ملی:
|
||||
</b>
|
||||
{{ person.shenasemeli }}
|
||||
{% if person.shenasemeli is not empty %}{{ person.shenasemeli }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>شماره ثبت:
|
||||
</b>
|
||||
{{ person.sabt }}
|
||||
{% if person.sabt is not empty %}{{ person.sabt }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>شماره اقتصادی:
|
||||
</b>
|
||||
{{ person.codeeghtesadi }}
|
||||
{% if person.codeeghtesadi is not empty %}{{ person.codeeghtesadi }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="center">
|
||||
<p>
|
||||
<b>تلفن / نمابر:</b>
|
||||
{{ person.tel }}
|
||||
{% if person.tel is not empty %}{{ person.tel }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -173,17 +170,14 @@
|
|||
<td class="" colspan="1">
|
||||
<p>
|
||||
<b>کد پستی:</b>
|
||||
{{ person.postalcode }}
|
||||
{% if person.postalcode is not empty %}{{ person.postalcode }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="" colspan="3">
|
||||
<p>
|
||||
<b>آدرس:
|
||||
</b>
|
||||
استان
|
||||
{{ person.ostan }}، شهر
|
||||
{{ person.shahr }}،
|
||||
{{ person.address }}
|
||||
{% if person.ostan is not empty %}استان {{ person.ostan }}{% endif %}{% if person.shahr is not empty %}، شهر {{ person.shahr }}{% endif %}{% if person.address is not empty %}، {{ person.address }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -230,7 +224,12 @@
|
|||
<td class="center item">
|
||||
{% if item.commodityCount > 0 %}
|
||||
{% if item.showPercentDiscount %}
|
||||
{% set originalPrice = item.bs / (1 - (item.discountPercent / 100)) %}
|
||||
{% set discountDivisor = 1 - (item.discountPercent / 100) %}
|
||||
{% if discountDivisor <= 0 %}
|
||||
{% set originalPrice = item.bs %}
|
||||
{% else %}
|
||||
{% set originalPrice = item.bs / discountDivisor %}
|
||||
{% endif %}
|
||||
{% set unitPrice = originalPrice / item.commodityCount %}
|
||||
{% else %}
|
||||
{% set originalPrice = item.bs + item.discount %}
|
||||
|
@ -252,7 +251,12 @@
|
|||
</td>
|
||||
<td class="center item">
|
||||
{% if item.showPercentDiscount %}
|
||||
{% set originalPrice = item.bs / (1 - (item.discountPercent / 100)) %}
|
||||
{% set discountDivisor = 1 - (item.discountPercent / 100) %}
|
||||
{% if discountDivisor <= 0 %}
|
||||
{% set originalPrice = item.bs %}
|
||||
{% else %}
|
||||
{% set originalPrice = item.bs / discountDivisor %}
|
||||
{% endif %}
|
||||
{{ originalPrice|round|number_format }} {{ doc.money.shortName }}
|
||||
{% else %}
|
||||
{{ (item.bs + item.discount)|number_format }} {{ doc.money.shortName }}
|
||||
|
@ -300,6 +304,17 @@
|
|||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{# فیلد جدید وضعیت حساب مشتری #}
|
||||
{% if accountStatus is defined %}
|
||||
<h4 class="">
|
||||
وضعیت حساب مشتری با احتساب این فاکتور:
|
||||
{{ accountStatus.value | number_format}}
|
||||
{{ doc.money.shortName }}
|
||||
{{ accountStatus.label }}
|
||||
|
||||
</h4>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</h4>
|
||||
</td>
|
||||
|
|
|
@ -1,72 +1,251 @@
|
|||
<template>
|
||||
<v-container>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
{{ $t('drawer.hrm_docs') }}
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" to="/acc/hrm/docs/mod/">
|
||||
{{ $t('dialog.add_new') }}
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="items"
|
||||
:loading="loading"
|
||||
class="elevation-1"
|
||||
>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-eye"
|
||||
variant="text"
|
||||
size="small"
|
||||
:to="'/acc/hrm/docs/view/' + item.id"
|
||||
></v-btn>
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
variant="text"
|
||||
size="small"
|
||||
:to="'/acc/hrm/docs/mod/' + item.id"
|
||||
></v-btn>
|
||||
<div>
|
||||
<v-toolbar color="toolbar" title="لیست حقوق">
|
||||
<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-data-table>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
<v-spacer></v-spacer>
|
||||
<v-tooltip text="افزودن سند حقوق" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="mdi-plus" variant="text" color="success" :to="'/acc/hrm/docs/mod/'"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</v-toolbar>
|
||||
|
||||
<v-text-field v-model="searchValue" prepend-inner-icon="mdi-magnify" density="compact" hide-details :rounded="false"
|
||||
placeholder="جست و جو ...">
|
||||
</v-text-field>
|
||||
|
||||
<v-data-table :headers="headers" :items="filteredItems" :search="searchValue" :loading="loading"
|
||||
:header-props="{ class: 'custom-header' }" hover>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-tooltip text="مشاهده سند" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon variant="text" color="success" :to="'/acc/hrm/docs/view/' + item.id">
|
||||
<v-icon>mdi-eye</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<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 :title="$t('dialog.view')" :to="'/acc/hrm/docs/view/' + item.id">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="green-darken-4" icon="mdi-eye"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item :title="$t('dialog.edit')" :to="'/acc/hrm/docs/mod/' + item.id">
|
||||
<template v-slot:prepend>
|
||||
<v-icon icon="mdi-file-edit"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="صدور سند حسابداری" :to="'/acc/hrm/docs/accounting/' + item.id">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="primary" icon="mdi-file-document-outline"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item :title="$t('dialog.delete')" @click="openDeleteDialog(item)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="deep-orange-accent-4" icon="mdi-trash-can"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<!-- دیالوگ تأیید حذف -->
|
||||
<v-dialog v-model="deleteDialog" max-width="500">
|
||||
<v-card class="rounded-lg">
|
||||
<v-card-title class="d-flex align-center pa-4">
|
||||
<v-icon color="error" size="large" class="ml-2">mdi-alert-circle-outline</v-icon>
|
||||
<span class="text-h5 font-weight-bold">حذف سند حقوق</span>
|
||||
</v-card-title>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-text class="pa-4">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="text-subtitle-1 mb-2">آیا مطمئن هستید که میخواهید سند زیر را حذف کنید؟</div>
|
||||
|
||||
<v-card variant="outlined" class="mt-2">
|
||||
<v-card-text>
|
||||
<div class="d-flex justify-space-between mb-2">
|
||||
<span class="text-subtitle-2 font-weight-bold">کد سند:</span>
|
||||
<span>{{ selectedItem?.id?.toLocaleString() }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-space-between mb-2">
|
||||
<span class="text-subtitle-2 font-weight-bold">تاریخ:</span>
|
||||
<span>{{ selectedItem?.date }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-space-between mb-2">
|
||||
<span class="text-subtitle-2 font-weight-bold">کارمند:</span>
|
||||
<span>{{ selectedItem?.employee }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-space-between">
|
||||
<span class="text-subtitle-2 font-weight-bold">مبلغ:</span>
|
||||
<span>{{ selectedItem?.amountRaw?.toLocaleString() }}</span>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
color="grey-darken-1"
|
||||
variant="text"
|
||||
@click="deleteDialog = false"
|
||||
:disabled="deleteLoading"
|
||||
>
|
||||
انصراف
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="tonal"
|
||||
@click="confirmDelete"
|
||||
:loading="deleteLoading"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</template>
|
||||
حذف سند
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- اسنکبار برای نمایش پیام -->
|
||||
<v-snackbar v-model="snackbar.show" :color="snackbar.color" timeout="3000">
|
||||
{{ snackbar.message }}
|
||||
<template v-slot:actions>
|
||||
<v-btn variant="text" @click="snackbar.show = false">
|
||||
بستن
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import axios from 'axios'
|
||||
import moment from 'jalali-moment'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
headers: [
|
||||
{ title: this.$t('dialog.field.id'), key: 'id' },
|
||||
{ title: this.$t('dialog.field.date'), key: 'date' },
|
||||
{ title: this.$t('dialog.field.employee'), key: 'employee' },
|
||||
{ title: this.$t('dialog.field.amount'), key: 'amount' },
|
||||
{ title: this.$t('dialog.field.status'), key: 'status' },
|
||||
{ title: this.$t('dialog.field.actions'), key: 'actions', sortable: false }
|
||||
],
|
||||
items: []
|
||||
const searchValue = ref('')
|
||||
const loading = ref(true)
|
||||
const items = ref([])
|
||||
const deleteDialog = ref(false)
|
||||
const deleteLoading = ref(false)
|
||||
const selectedItem = ref(null)
|
||||
const snackbar = ref({
|
||||
show: false,
|
||||
message: '',
|
||||
color: 'success'
|
||||
})
|
||||
|
||||
const headers = [
|
||||
{ title: 'عملیات', key: 'actions' },
|
||||
{ title: 'تاریخ', key: 'date', sortable: true },
|
||||
{ title: 'ایجاد کننده', key: 'employee', sortable: true },
|
||||
{ title: 'مبلغ', key: 'amount', sortable: true },
|
||||
{ title: 'سند حسابداری', key: 'accounting_doc', sortable: true },
|
||||
{ title: 'توضیحات', key: 'description', sortable: true },
|
||||
]
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await axios.post('/api/hrm/docs/list', {
|
||||
search: searchValue.value
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
items.value = response.data.data.map(item => ({
|
||||
...item,
|
||||
amount: item.total ? item.total.toLocaleString('fa-IR') : '0',
|
||||
amountRaw: item.total || 0,
|
||||
employee: item.creator?.name || 'نامشخص',
|
||||
status: item.status
|
||||
}));
|
||||
} else {
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: 'خطا در بارگذاری دادهها',
|
||||
color: 'error'
|
||||
};
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
async loadData() {
|
||||
this.loading = true
|
||||
try {
|
||||
const response = await axios.post('/api/hrm/docs/list')
|
||||
this.items = response.data
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error)
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error);
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: 'خطا در بارگذاری دادهها',
|
||||
color: 'error'
|
||||
};
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const filteredItems = computed(() => {
|
||||
return items.value;
|
||||
});
|
||||
|
||||
const openDeleteDialog = (item) => {
|
||||
selectedItem.value = item
|
||||
deleteDialog.value = true
|
||||
}
|
||||
|
||||
const confirmDelete = async () => {
|
||||
try {
|
||||
deleteLoading.value = true
|
||||
const response = await axios.post('/api/hrm/docs/delete',{id: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 || 'خطا در حذف سند',
|
||||
color: 'error'
|
||||
}
|
||||
this.loading = false
|
||||
}
|
||||
} catch (error) {
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: error.response?.data?.message || 'خطا در ارتباط با سرور',
|
||||
color: 'error'
|
||||
}
|
||||
} finally {
|
||||
deleteLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-data-table {
|
||||
direction: rtl;
|
||||
}
|
||||
</style>
|
|
@ -25,7 +25,7 @@
|
|||
<v-row>
|
||||
<v-col cols="12" sm="6" md="6">
|
||||
<Hdatepicker v-model="form.date" :label="$t('dialog.hrm.date')"
|
||||
:rules="[v => !!v || $t('dialog.hrm.required_fields.date')]" required />
|
||||
:rules="[v => !!v || $t('dialog.hrm.required_fields.date')]" required density="compact" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="6">
|
||||
<v-text-field v-model="form.description" :label="$t('dialog.hrm.description')"
|
||||
|
@ -131,6 +131,9 @@
|
|||
import Hdatepicker from '@/components/forms/Hdatepicker.vue';
|
||||
import Hpersonsearch from '@/components/forms/Hpersonsearch.vue';
|
||||
import Hnumberinput from '@/components/forms/Hnumberinput.vue';
|
||||
import axios from 'axios';
|
||||
import moment from 'jalali-moment';
|
||||
|
||||
export default {
|
||||
components: { Hdatepicker, Hpersonsearch, Hnumberinput },
|
||||
data() {
|
||||
|
@ -150,20 +153,47 @@ export default {
|
|||
tableItems: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const id = this.$route.params.id
|
||||
if (id) {
|
||||
this.isEdit = true
|
||||
this.loadData(id)
|
||||
async mounted() {
|
||||
try {
|
||||
// دریافت تاریخ فعلی
|
||||
const response = await axios.get('/api/year/get');
|
||||
this.form.date = response.data.now;
|
||||
|
||||
const id = this.$route.params.id
|
||||
if (id) {
|
||||
this.isEdit = true
|
||||
await this.loadData(id)
|
||||
}
|
||||
} catch (error) {
|
||||
this.errorMessage = error.response?.data?.error || 'خطا در دریافت تاریخ';
|
||||
this.showError = true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadData(id) {
|
||||
try {
|
||||
const response = await this.$axios.post('/api/hrm/docs/get/' + id)
|
||||
this.form = response.data
|
||||
const response = await axios.post('/api/hrm/docs/get/' + id)
|
||||
const data = response.data.data
|
||||
|
||||
this.form = {
|
||||
date: data.date ? moment(data.date, 'jYYYY/jMM/jDD').format('jYYYY/jMM/jDD') : '',
|
||||
description: data.description || ''
|
||||
}
|
||||
|
||||
this.tableItems = data.items.map(item => ({
|
||||
person: {
|
||||
id: item.person.id,
|
||||
name: item.person.name,
|
||||
code: item.person.code
|
||||
},
|
||||
description: item.description || '',
|
||||
baseSalary: item.baseSalary || 0,
|
||||
overtime: item.overtime || 0,
|
||||
shift: item.shift || 0,
|
||||
night: item.night || 0
|
||||
}))
|
||||
} catch (error) {
|
||||
this.errorMessage = 'خطا در دریافت اطلاعات';
|
||||
this.errorMessage = error.response?.data?.error || 'خطا در دریافت اطلاعات';
|
||||
this.showError = true;
|
||||
}
|
||||
},
|
||||
|
@ -193,16 +223,35 @@ export default {
|
|||
},
|
||||
async save() {
|
||||
try {
|
||||
console.log('Starting save process...');
|
||||
this.loading = true;
|
||||
const url = this.isEdit ? '/api/hrm/docs/update' : '/api/hrm/docs/insert'
|
||||
await this.$axios.post(url, this.form)
|
||||
const url = this.isEdit ? '/api/hrm/docs/update' : '/api/hrm/docs/insert';
|
||||
const data = {
|
||||
date: this.form.date,
|
||||
description: this.form.description,
|
||||
items: this.tableItems.map(item => ({
|
||||
person: item.person?.id,
|
||||
baseSalary: Number(item.baseSalary) || 0,
|
||||
overtime: Number(item.overtime) || 0,
|
||||
shift: Number(item.shift) || 0,
|
||||
night: Number(item.night) || 0,
|
||||
description: item.description || ''
|
||||
}))
|
||||
};
|
||||
console.log('Sending request to:', url);
|
||||
console.log('Request data:', data);
|
||||
|
||||
const response = await axios.post(url, data);
|
||||
console.log('Server response:', response);
|
||||
|
||||
this.successMessage = this.isEdit ? this.$t('dialog.hrm.edit_success') : this.$t('dialog.hrm.save_success');
|
||||
this.showSuccess = true;
|
||||
setTimeout(() => {
|
||||
this.$router.push('/acc/hrm/docs/list')
|
||||
}, 1200)
|
||||
this.$router.push('/acc/hrm/docs/list');
|
||||
}, 1200);
|
||||
} catch (error) {
|
||||
this.errorMessage = this.$t('dialog.hrm.save_error');
|
||||
console.error('Save error:', error);
|
||||
this.errorMessage = error.response?.data?.error || this.$t('dialog.hrm.save_error');
|
||||
this.showError = true;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
|
@ -211,7 +260,7 @@ export default {
|
|||
async confirmDelete() {
|
||||
try {
|
||||
this.loading = true;
|
||||
await this.$axios.post('/api/hrm/docs/delete', { id: this.$route.params.id })
|
||||
await axios.post('/api/hrm/docs/delete', { id: this.$route.params.id })
|
||||
this.successMessage = 'سند با موفقیت حذف شد';
|
||||
this.showSuccess = true;
|
||||
setTimeout(() => {
|
||||
|
@ -318,6 +367,14 @@ export default {
|
|||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
:deep(.v-date-picker__menu) {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
:deep(.v-date-picker__menu__content) {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
.settings-section-card {
|
||||
height: 100%;
|
||||
transition: all 0.3s ease;
|
||||
|
|
|
@ -1,67 +1,164 @@
|
|||
<template>
|
||||
<div class="sticky-container">
|
||||
<v-toolbar color="toolbar" :title="$t('dialog.person_with_det_report')">
|
||||
<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>
|
||||
<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-toolbar>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<Hpersonsearch
|
||||
v-model="selectedPerson"
|
||||
label="شخص"
|
||||
:rules="[v => !!v || 'انتخاب شخص الزامی است']"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<Hdatepicker
|
||||
v-model="startDate"
|
||||
label="تاریخ شروع"
|
||||
:rules="[v => !!v || 'تاریخ شروع الزامی است']"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<Hdatepicker
|
||||
v-model="endDate"
|
||||
label="تاریخ پایان"
|
||||
:rules="[v => !!v || 'تاریخ پایان الزامی است']"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<Hpersonsearch
|
||||
v-model="selectedPerson"
|
||||
label="شخص"
|
||||
:rules="[v => !!v || 'انتخاب شخص الزامی است']"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<Hdatepicker
|
||||
v-model="startDate"
|
||||
label="تاریخ شروع"
|
||||
:rules="[v => !!v || 'تاریخ شروع الزامی است']"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<Hdatepicker
|
||||
v-model="endDate"
|
||||
label="تاریخ پایان"
|
||||
:rules="[v => !!v || 'تاریخ پایان الزامی است']"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<v-data-table-server
|
||||
v-model:items-per-page="serverOptions.rowsPerPage"
|
||||
v-model:page="serverOptions.page"
|
||||
:headers="tableHeaders"
|
||||
:items="items"
|
||||
:items-length="totalItems"
|
||||
:loading="loading"
|
||||
class="elevation-1"
|
||||
:items-per-page-options="[5, 10, 20, 50]"
|
||||
item-value="id"
|
||||
no-data-text="اطلاعاتی برای نمایش وجود ندارد"
|
||||
:header-props="{ class: 'custom-header' }"
|
||||
>
|
||||
<template v-slot:item.index="{ index }">
|
||||
{{ (serverOptions.page - 1) * serverOptions.rowsPerPage + index + 1 }}
|
||||
</template>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn icon size="small" color="primary">
|
||||
<v-icon>mdi-eye</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-slot:item.docType="{ item }">
|
||||
{{ item.docType || '-' }}
|
||||
</template>
|
||||
<template v-slot:item.debit="{ item }">
|
||||
{{ formatNumber(item.debit) }}
|
||||
</template>
|
||||
<template v-slot:item.credit="{ item }">
|
||||
{{ formatNumber(item.credit) }}
|
||||
</template>
|
||||
<template v-slot:item.description="{ item }">
|
||||
{{ item.description }}
|
||||
</template>
|
||||
</v-data-table-server>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Hpersonsearch from '@/components/forms/Hpersonsearch.vue'
|
||||
import Hdatepicker from '@/components/forms/Hdatepicker.vue'
|
||||
import Hpersonsearch from '@/components/forms/Hpersonsearch.vue';
|
||||
import Hdatepicker from '@/components/forms/Hdatepicker.vue';
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
name: 'PersonWithDetReport',
|
||||
components: {
|
||||
Hpersonsearch,
|
||||
Hdatepicker
|
||||
name: 'PersonWithDetReport',
|
||||
components: {
|
||||
Hpersonsearch,
|
||||
Hdatepicker
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedPerson: null,
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
loading: false,
|
||||
items: [],
|
||||
totalItems: 0,
|
||||
serverOptions: {
|
||||
page: 1,
|
||||
rowsPerPage: 10,
|
||||
sortBy: [],
|
||||
},
|
||||
tableHeaders: [
|
||||
{ text: 'ردیف', value: 'index', align: 'center', sortable: false },
|
||||
{ text: 'عملیات', value: 'actions', align: 'center', sortable: false },
|
||||
{ text: 'نوع سند', value: 'docType', align: 'center' },
|
||||
{ text: 'بدهکار', value: 'debit', align: 'center' },
|
||||
{ text: 'بستانکار', value: 'credit', align: 'center' },
|
||||
{ text: 'شرح', value: 'description', align: 'center' },
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
formatNumber(num) {
|
||||
if (!num) return '0';
|
||||
return Number(num).toLocaleString('fa-IR');
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedPerson: null,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
}
|
||||
async fetchData() {
|
||||
if (!this.selectedPerson) {
|
||||
this.items = [];
|
||||
this.totalItems = 0;
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await axios.post('/api/persons/listwithdet', {
|
||||
person: this.selectedPerson.code,
|
||||
startDate: this.startDate,
|
||||
endDate: this.endDate,
|
||||
page: this.serverOptions.page,
|
||||
itemsPerPage: this.serverOptions.rowsPerPage,
|
||||
});
|
||||
this.items = response.data.items || [];
|
||||
this.totalItems = response.data.total || this.items.length;
|
||||
} catch (error) {
|
||||
this.items = [];
|
||||
this.totalItems = 0;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// متدهای مورد نیاز گزارش
|
||||
},
|
||||
watch: {
|
||||
selectedPerson: 'fetchData',
|
||||
startDate: 'fetchData',
|
||||
endDate: 'fetchData',
|
||||
serverOptions: {
|
||||
handler: 'fetchData',
|
||||
deep: true,
|
||||
},
|
||||
mounted() {
|
||||
// کدهای اجرایی در زمان بارگذاری کامپوننت
|
||||
},
|
||||
mounted() {
|
||||
// بارگذاری اولیه اگر شخص انتخاب شده باشد
|
||||
if (this.selectedPerson) {
|
||||
this.fetchData();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* استایلهای مورد نیاز */
|
||||
.sticky-container {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
|
@ -791,7 +791,22 @@ export default {
|
|||
try {
|
||||
// اول تنظیمات را لود میکنیم
|
||||
this.loadSettings();
|
||||
|
||||
|
||||
// دریافت آیدی کسبوکار فعال از localStorage
|
||||
const activeBid = localStorage.getItem('activeBid');
|
||||
if (activeBid) {
|
||||
try {
|
||||
const businessRes = await axios.get(`/api/business/get/info/${activeBid}`);
|
||||
if (businessRes.data && businessRes.data.maliyatafzode) {
|
||||
// فقط اگر فاکتور جدید است مقدار پیشفرض مالیات را ست کن
|
||||
if (!this.$route.params.id) {
|
||||
this.taxPercent = Number(businessRes.data.maliyatafzode);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('خطا در دریافت اطلاعات کسبوکار:', err);
|
||||
}
|
||||
}
|
||||
// بررسی وضعیت پیشنویس
|
||||
this.isNewInvoice = !this.$route.params.id;
|
||||
|
||||
|
|
|
@ -41,6 +41,11 @@ export default defineComponent({
|
|||
recListWindowsState: { submited: false },
|
||||
notes: { count: 0 },
|
||||
bid: { legal_name: '', shortlinks: false },
|
||||
snackbar: {
|
||||
show: false,
|
||||
text: '',
|
||||
color: 'error'
|
||||
},
|
||||
item: {
|
||||
doc: { id: 0, date: null, code: null, des: '', amount: 0, profit: 0, shortLink: null },
|
||||
relatedDocs: [],
|
||||
|
@ -98,6 +103,26 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
async checkCanEdit() {
|
||||
try {
|
||||
const response = await axios.get(`/api/sell/edit/can/${this.$route.params.id}`);
|
||||
if (response.data.result) {
|
||||
this.$router.push(`/acc/sell/mod/${this.$route.params.id}`);
|
||||
} else {
|
||||
this.snackbar = {
|
||||
show: true,
|
||||
text: 'شما مجاز به ویرایش این فاکتور نیستید',
|
||||
color: 'error'
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
this.snackbar = {
|
||||
show: true,
|
||||
text: 'خطا در بررسی دسترسی',
|
||||
color: 'error'
|
||||
};
|
||||
}
|
||||
},
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
this.commoditys = [];
|
||||
|
@ -173,7 +198,7 @@ export default defineComponent({
|
|||
</v-tooltip>
|
||||
</template>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon :to="`/acc/sell/mod/${$route.params.id}`">
|
||||
<v-btn icon @click="checkCanEdit">
|
||||
<v-icon>mdi-pencil</v-icon>
|
||||
<v-tooltip activator="parent" location="bottom">ویرایش</v-tooltip>
|
||||
</v-btn>
|
||||
|
@ -335,6 +360,13 @@ export default defineComponent({
|
|||
</v-window-item>
|
||||
</v-window>
|
||||
</v-container>
|
||||
<v-snackbar
|
||||
v-model="snackbar.show"
|
||||
:color="snackbar.color"
|
||||
timeout="3000"
|
||||
>
|
||||
{{ snackbar.text }}
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
|
Loading…
Reference in a new issue