progress in webui

This commit is contained in:
Hesabix 2025-06-13 13:19:34 +00:00
parent cc1fe8216d
commit 36802cbfcf
9 changed files with 916 additions and 95 deletions

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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);
}
}

View file

@ -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>

View file

@ -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;