add type selection to accounting table

This commit is contained in:
Hesabix 2025-07-25 11:34:36 +00:00
parent cc1515345b
commit 91cf5d4eb6
2 changed files with 116 additions and 6 deletions

View file

@ -882,12 +882,14 @@ class HesabdariController extends AbstractController
$temp[$node->getCode()] = [ $temp[$node->getCode()] = [
'text' => $node->getName(), 'text' => $node->getName(),
'id' => $node->getCode() ?? $node->getId(), 'id' => $node->getCode() ?? $node->getId(),
'type' => $node->getType(),
'children' => $this->getFilteredChildsLabel($entityManager, $node, $business), 'children' => $this->getFilteredChildsLabel($entityManager, $node, $business),
]; ];
} else { } else {
$temp[$node->getCode()] = [ $temp[$node->getCode()] = [
'text' => $node->getName(), 'text' => $node->getName(),
'id' => $node->getCode() ?? $node->getId(), 'id' => $node->getCode() ?? $node->getId(),
'type' => $node->getType(),
]; ];
} }
$temp[$node->getCode()]['is_public'] = $nodeBid === null; $temp[$node->getCode()]['is_public'] = $nodeBid === null;
@ -1033,6 +1035,13 @@ class HesabdariController extends AbstractController
return $this->json(['result' => 0, 'message' => 'نام ردیف حساب و آیدی والد الزامی است'], 400); return $this->json(['result' => 0, 'message' => 'نام ردیف حساب و آیدی والد الزامی است'], 400);
} }
// بررسی نوع تفضیل حساب
$allowedTypes = ['calc', 'person', 'commodity', 'bank', 'salary', 'cashdesk'];
$accountType = $params['accountType'] ?? 'calc';
if (!in_array($accountType, $allowedTypes)) {
return $this->json(['result' => 0, 'message' => 'نوع تفضیل حساب نامعتبر است'], 400);
}
$parentNode = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => $params['parentId']]); $parentNode = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => $params['parentId']]);
if (!$parentNode) { if (!$parentNode) {
return $this->json(['result' => 0, 'message' => 'ردیف حساب والد پیدا نشد'], 404); return $this->json(['result' => 0, 'message' => 'ردیف حساب والد پیدا نشد'], 404);
@ -1058,18 +1067,19 @@ class HesabdariController extends AbstractController
$newNode->setCode($uniqueCode); $newNode->setCode($uniqueCode);
$newNode->setBid($acc['bid']); $newNode->setBid($acc['bid']);
$newNode->setUpper($parentNode); $newNode->setUpper($parentNode);
$newNode->setType('calc'); $newNode->setType($accountType);
$entityManager->persist($newNode); $entityManager->persist($newNode);
$entityManager->flush(); $entityManager->flush();
$log->insert('حسابداری', 'ردیف حساب جدید با کد ' . $newNode->getCode() . ' اضافه شد.', $this->getUser(), $acc['bid']); $log->insert('حسابداری', 'ردیف حساب جدید با کد ' . $newNode->getCode() . ' و نوع ' . $accountType . ' اضافه شد.', $this->getUser(), $acc['bid']);
return $this->json([ return $this->json([
'result' => 1, 'result' => 1,
'node' => [ 'node' => [
'id' => $newNode->getCode(), 'id' => $newNode->getCode(),
'text' => $newNode->getName(), 'text' => $newNode->getName(),
'type' => $newNode->getType(),
'children' => [], 'children' => [],
'is_public' => $newNode->getBid() ? false : true, 'is_public' => $newNode->getBid() ? false : true,
] ]
@ -1110,6 +1120,7 @@ class HesabdariController extends AbstractController
'node' => [ 'node' => [
'id' => $node->getCode(), 'id' => $node->getCode(),
'text' => $node->getName(), 'text' => $node->getName(),
'type' => $node->getType(),
'children' => $this->getChildsLabel($entityManager, $node), 'children' => $this->getChildsLabel($entityManager, $node),
'is_public' => $node->getBid() ? false : true, 'is_public' => $node->getBid() ? false : true,
] ]

View file

@ -17,6 +17,9 @@
<span class="node-label"> <span class="node-label">
{{ node.text }} {{ node.text }}
<span class="account-code">({{ node.id }})</span> <span class="account-code">({{ node.id }})</span>
<span v-if="node.type && node.type !== 'calc'" class="account-type-badge">
{{ getAccountTypeLabel(node.type) }}
</span>
</span> </span>
</template> </template>
<template #after-input="{ node }"> <template #after-input="{ node }">
@ -36,7 +39,7 @@
</Tree> </Tree>
<!-- دیالوگ اضافه کردن زیرمجموعه --> <!-- دیالوگ اضافه کردن زیرمجموعه -->
<v-dialog v-model="addDialog" max-width="400" persistent> <v-dialog v-model="addDialog" max-width="500" persistent>
<v-card> <v-card>
<v-toolbar flat color="success" dark> <v-toolbar flat color="success" dark>
<v-toolbar-title>اضافه کردن زیرمجموعه</v-toolbar-title> <v-toolbar-title>اضافه کردن زیرمجموعه</v-toolbar-title>
@ -62,6 +65,31 @@
<v-card-text class="pt-4"> <v-card-text class="pt-4">
<v-text-field v-model="newNodeText" label="نام ردیف حساب جدید" <v-text-field v-model="newNodeText" label="نام ردیف حساب جدید"
:rules="[v => !!v.trim() || 'این فیلد نمی‌تواند خالی باشد']" :disabled="dialogLoading"></v-text-field> :rules="[v => !!v.trim() || 'این فیلد نمی‌تواند خالی باشد']" :disabled="dialogLoading"></v-text-field>
<v-select
v-model="selectedAccountType"
:items="accountTypes"
item-title="label"
item-value="value"
label="نوع تفضیل حساب"
:disabled="dialogLoading"
class="mt-4"
>
<template v-slot:item="{ props, item }">
<v-list-item v-bind="props">
<template v-slot:prepend>
<v-icon :icon="item.raw.icon" class="mr-2"></v-icon>
</template>
<v-list-item-subtitle>{{ item.raw.description }}</v-list-item-subtitle>
</v-list-item>
</template>
<template v-slot:selection="{ item }">
<div class="d-flex align-center">
<v-icon :icon="item.raw.icon" class="mr-2"></v-icon>
<span>{{ item.raw.label }}</span>
</div>
</template>
</v-select>
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-dialog> </v-dialog>
@ -187,6 +215,46 @@ export default {
const newNodeText = ref(""); const newNodeText = ref("");
const editNodeText = ref(""); const editNodeText = ref("");
const selectedNode = ref(null); const selectedNode = ref(null);
const selectedAccountType = ref("calc");
const accountTypes = ref([
{
value: "calc",
label: "اسناد جاری",
description: "برای ثبت اسناد حسابداری معمولی",
icon: "mdi-calculator"
},
{
value: "person",
label: "اشخاص",
description: "برای ثبت اطلاعات مشتریان، تامین‌کنندگان و کارمندان",
icon: "mdi-account-multiple"
},
{
value: "commodity",
label: "موجودی کالا",
description: "برای ثبت موجودی کالاها و محصولات",
icon: "mdi-package-variant"
},
{
value: "bank",
label: "حساب‌های بانکی",
description: "برای ثبت حساب‌های بانکی و تراکنشات مالی",
icon: "mdi-bank"
},
{
value: "salary",
label: "تنخواه گردان",
description: "برای ثبت تنخواه‌های گردان و هزینه‌های جاری",
icon: "mdi-cash-multiple"
},
{
value: "cashdesk",
label: "صندوق",
description: "برای ثبت صندوق‌های نقدی",
icon: "mdi-cash-register"
}
]);
const checkAccproPlugin = async () => { const checkAccproPlugin = async () => {
try { try {
@ -209,7 +277,9 @@ export default {
const node = data[key]; const node = data[key];
treeData[key] = { treeData[key] = {
...node, ...node,
text: `(${node.id}) ${node.text}` text: `(${node.id}) ${node.text}`,
originalText: node.text, // ذخیره نام اصلی برای استفاده در ویرایش
type: node.type || 'calc' // اضافه کردن نوع تفضیل حساب
}; };
}); });
@ -232,6 +302,7 @@ export default {
const openAddDialog = (node) => { const openAddDialog = (node) => {
selectedNode.value = node; selectedNode.value = node;
newNodeText.value = ""; newNodeText.value = "";
selectedAccountType.value = "calc";
addDialog.value = true; addDialog.value = true;
}; };
@ -242,10 +313,16 @@ export default {
const response = await axios.post("/api/accounting/table/add", { const response = await axios.post("/api/accounting/table/add", {
text: newNodeText.value, text: newNodeText.value,
parentId: selectedNode.value.id, parentId: selectedNode.value.id,
accountType: selectedAccountType.value,
}); });
if (response.data.result === 1) { if (response.data.result === 1) {
const newNode = response.data.node; const newNode = response.data.node;
tree.value[newNode.id] = newNode; tree.value[newNode.id] = {
...newNode,
text: `(${newNode.id}) ${newNode.text}`,
originalText: newNode.text,
type: newNode.type || 'calc',
};
if (!tree.value[selectedNode.value.id].children) { if (!tree.value[selectedNode.value.id].children) {
tree.value[selectedNode.value.id].children = []; tree.value[selectedNode.value.id].children = [];
} }
@ -265,7 +342,8 @@ export default {
const openEditDialog = (node) => { const openEditDialog = (node) => {
selectedNode.value = node; selectedNode.value = node;
editNodeText.value = node.text; // استفاده از نام اصلی ردیف حساب
editNodeText.value = node.originalText || node.text.replace(/^\(\d+\)\s*/, '');
editDialog.value = true; editDialog.value = true;
}; };
@ -281,6 +359,9 @@ export default {
tree.value[selectedNode.value.id] = { tree.value[selectedNode.value.id] = {
...tree.value[selectedNode.value.id], ...tree.value[selectedNode.value.id],
...response.data.node, ...response.data.node,
text: `(${response.data.node.id}) ${response.data.node.text}`,
originalText: response.data.node.text,
type: response.data.node.type || tree.value[selectedNode.value.id].type || 'calc',
}; };
editDialog.value = false; editDialog.value = false;
showMessage("موفقیت", "ردیف حساب با موفقیت ویرایش شد!"); showMessage("موفقیت", "ردیف حساب با موفقیت ویرایش شد!");
@ -328,6 +409,11 @@ export default {
} }
}; };
const getAccountTypeLabel = (type) => {
const accountType = accountTypes.value.find(t => t.value === type);
return accountType ? accountType.label : type;
};
checkAccproPlugin(); checkAccproPlugin();
loadData(); loadData();
@ -346,12 +432,15 @@ export default {
newNodeText, newNodeText,
editNodeText, editNodeText,
selectedNode, selectedNode,
selectedAccountType,
accountTypes,
openAddDialog, openAddDialog,
addNode, addNode,
openEditDialog, openEditDialog,
saveEditNode, saveEditNode,
openDeleteDialog, openDeleteDialog,
confirmDeleteNode, confirmDeleteNode,
getAccountTypeLabel,
}; };
}, },
}; };
@ -393,4 +482,14 @@ export default {
font-size: 0.9em; font-size: 0.9em;
font-family: monospace; font-family: monospace;
} }
.account-type-badge {
background-color: #1976d2;
color: white;
padding: 2px 6px;
border-radius: 12px;
font-size: 0.75em;
margin-right: 8px;
white-space: nowrap;
}
</style> </style>