bug fix and add plugin transactions in admin panel
This commit is contained in:
parent
11239f06f1
commit
10bd782904
|
@ -377,4 +377,56 @@ class PluginController extends AbstractController
|
|||
|
||||
return $this->json($extractor->operationSuccess());
|
||||
}
|
||||
|
||||
/**
|
||||
* دریافت لیست تراکنشهای افزونهها (برای ادمین)
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/admin/plugins/transactions",
|
||||
* summary="دریافت لیست تراکنشهای افزونهها (ادمین)",
|
||||
* tags={"Admin Plugins"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="لیست تراکنشها",
|
||||
* @OA\JsonContent(type="array", @OA\Items(ref="#/components/schemas/Plugin"))
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
#[Route('/api/admin/plugins/transactions', name: 'api_admin_plugins_transactions', methods: ["POST"])]
|
||||
public function api_admin_plugins_transactions(Access $access, Jdate $jdate, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$this->checkAccess($access, 'ROLE_ADMIN');
|
||||
|
||||
$plugins = $entityManager->getRepository(Plugin::class)->findBy([], ['dateSubmit' => 'DESC']);
|
||||
$result = [];
|
||||
|
||||
foreach ($plugins as $plugin) {
|
||||
$pluginData = [
|
||||
'id' => $plugin->getId(),
|
||||
'des' => $plugin->getDes(),
|
||||
'price' => $plugin->getPrice(),
|
||||
'dateSubmit' => $jdate->jdate('Y/n/d', $plugin->getDateSubmit()),
|
||||
'dateExpire' => $jdate->jdate('Y/n/d', $plugin->getDateExpire()),
|
||||
'status' => $plugin->getStatus(),
|
||||
'cardPan' => $plugin->getCardPan(),
|
||||
'refID' => $plugin->getRefID()
|
||||
];
|
||||
|
||||
// اضافه کردن نام کسب و کار
|
||||
$business = $entityManager->getRepository('App\Entity\Business')->find($plugin->getBid());
|
||||
if ($business) {
|
||||
$pluginData['businessName'] = $business->getName();
|
||||
}
|
||||
|
||||
// اضافه کردن نام خریدار
|
||||
$submitter = $plugin->getSubmitter();
|
||||
if ($submitter) {
|
||||
$pluginData['submitterName'] = $submitter->getFullName();
|
||||
}
|
||||
|
||||
$result[] = $pluginData;
|
||||
}
|
||||
|
||||
return $this->json($result);
|
||||
}
|
||||
}
|
31
hesabixCore/src/Controller/Plugins/Hrm/DocsController.php
Normal file
31
hesabixCore/src/Controller/Plugins/Hrm/DocsController.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller\Plugins\Hrm;
|
||||
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
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\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
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -391,7 +391,7 @@ class PlugGhestaController extends AbstractController
|
|||
'id' => $item->getId(),
|
||||
'code' => $item->getMainDoc() ? $item->getMainDoc()->getCode() : null,
|
||||
'firstGhestaDate' => $firstGhestaDate,
|
||||
'amount' => $item->getProfitAmount(), // مبلغ کل شامل سود
|
||||
'amount' => $item->getMainDoc() ? $item->getMainDoc()->getAmount() : 0, // مبلغ کل فاکتور
|
||||
'profitAmount' => $item->getProfitAmount(),
|
||||
'profitPercent' => $item->getProfitPercent(),
|
||||
'profitType' => $item->getProfitType(),
|
||||
|
|
|
@ -116,6 +116,12 @@
|
|||
<table style="width:100%;">
|
||||
<tbody>
|
||||
<tr style="text-align:center;">
|
||||
<td class="">
|
||||
<p>
|
||||
<b>مبلغ کل فاکتور:</b>
|
||||
{{ doc.mainDoc.amount|number_format }} ریال
|
||||
</p>
|
||||
</td>
|
||||
<td class="">
|
||||
<p>
|
||||
<b>تعداد اقساط:</b>
|
||||
|
|
|
@ -546,6 +546,14 @@ const fa_lang = {
|
|||
return_sell: "برگشت از فروش",
|
||||
all_types: "همه موارد"
|
||||
},
|
||||
field: {
|
||||
id: "شناسه",
|
||||
date: "تاریخ",
|
||||
employee: "کارمند",
|
||||
amount: "مبلغ",
|
||||
status: "وضعیت",
|
||||
actions: "عملیات"
|
||||
},
|
||||
},
|
||||
app: {
|
||||
loading: "در حال بارگذاری...",
|
||||
|
|
|
@ -146,6 +146,14 @@ const router = createRouter({
|
|||
'login': true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'manager/plugins/transactions',
|
||||
component: () => import('../views/user/manager/settings/pluginTransactions.vue'),
|
||||
meta: {
|
||||
'title': 'تراکنشهای افزونهها',
|
||||
'login': true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'manager/update-core',
|
||||
component: () => import('../views/user/manager/settings/update-core.vue'),
|
||||
|
|
|
@ -109,11 +109,6 @@
|
|||
<v-icon icon="mdi-file-edit"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item class="text-dark" :title="$t('dialog.payment')" @click="onPayment(item)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon icon="mdi-cash"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item class="text-dark" :title="$t('dialog.delete')" @click="onDelete(item)">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="deep-orange-accent-4" icon="mdi-trash-can"></v-icon>
|
||||
|
@ -343,10 +338,6 @@ export default defineComponent({
|
|||
router.push(`/acc/plugins/ghesta/view/${item.id}`)
|
||||
}
|
||||
|
||||
const onPayment = (item) => {
|
||||
router.push(`/acc/plugins/ghesta/payment/${item.id}`)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
|
@ -370,8 +361,7 @@ export default defineComponent({
|
|||
onSearch,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onView,
|
||||
onPayment
|
||||
onView
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -632,112 +632,140 @@ export default {
|
|||
return Math.round(monthlyInterest);
|
||||
},
|
||||
calculatedInstallments() {
|
||||
if (!this.selectedInvoice || !this.installmentData.firstDate) {
|
||||
return [];
|
||||
if (!this.canCalculate) {
|
||||
this.showMessage('لطفا تمام اطلاعات مورد نیاز را وارد کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const totalAmount = this.remainingAmount;
|
||||
const prepayment = Number(this.installmentData.prepayment) || 0;
|
||||
const remainingAmount = totalAmount - prepayment;
|
||||
const interestRate = Number(this.installmentData.interestRate) || 0;
|
||||
const interestType = this.installmentData.interestType;
|
||||
if (this.isCalculating) {
|
||||
return;
|
||||
}
|
||||
|
||||
let count, monthlyPayment;
|
||||
|
||||
if (this.installmentData.calculationType === 'amount') {
|
||||
monthlyPayment = Number(this.installmentData.installmentAmount);
|
||||
if (monthlyPayment <= 0) {
|
||||
this.installments = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.isCalculating = true;
|
||||
this.loading = true;
|
||||
|
||||
// محاسبه تعداد اقساط بر اساس مبلغ هر قسط
|
||||
switch (interestType) {
|
||||
case 'monthly': {
|
||||
// برای سود ساده ماهانه
|
||||
const monthlyInterest = interestRate / 100;
|
||||
const principalPerMonth = remainingAmount / monthlyPayment;
|
||||
const interestPerMonth = monthlyInterest;
|
||||
count = Math.ceil(principalPerMonth / (1 - interestPerMonth));
|
||||
break;
|
||||
}
|
||||
case 'yearly': {
|
||||
// برای سود ساده سالانه
|
||||
const yearlyInterest = interestRate / 100;
|
||||
const monthlyInterest = yearlyInterest / 12;
|
||||
const principalPerMonth = remainingAmount / monthlyPayment;
|
||||
const interestPerMonth = monthlyInterest;
|
||||
count = Math.ceil(principalPerMonth / (1 - interestPerMonth));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
count = 0;
|
||||
}
|
||||
// حفظ وضعیت پرداخت اقساط موجود
|
||||
const existingInstallments = this.installments.reduce((acc, item) => {
|
||||
acc[item.number] = {
|
||||
status: item.status,
|
||||
hesabdariDoc: item.hesabdariDoc
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// محدود کردن تعداد اقساط به یک عدد منطقی
|
||||
count = Math.min(Math.max(count, 1), 360); // حداکثر 30 سال
|
||||
} else {
|
||||
count = Number(this.installmentData.count) || 0;
|
||||
if (count <= 0) {
|
||||
this.installments = [];
|
||||
return;
|
||||
}
|
||||
const totalAmount = this.remainingAmount;
|
||||
const prepayment = Number(this.installmentData.prepayment) || 0;
|
||||
const remainingAmount = totalAmount - prepayment;
|
||||
const interestRate = Number(this.installmentData.interestRate) || 0;
|
||||
const interestType = this.installmentData.interestType;
|
||||
|
||||
switch (interestType) {
|
||||
case 'monthly': {
|
||||
const monthlyInterest = interestRate / 100;
|
||||
const totalInterest = monthlyInterest * count;
|
||||
monthlyPayment = (remainingAmount * (1 + totalInterest)) / count;
|
||||
break;
|
||||
let count, monthlyPayment;
|
||||
|
||||
if (this.installmentData.calculationType === 'amount') {
|
||||
monthlyPayment = Number(this.installmentData.installmentAmount);
|
||||
if (monthlyPayment <= 0) {
|
||||
this.installments = [];
|
||||
this.showMessage('مبلغ هر قسط باید بزرگتر از صفر باشد', 'error');
|
||||
return;
|
||||
}
|
||||
case 'yearly': {
|
||||
const yearlyInterest = interestRate / 100;
|
||||
const monthlyInterest = yearlyInterest / 12;
|
||||
const totalInterest = monthlyInterest * count;
|
||||
monthlyPayment = (remainingAmount * (1 + totalInterest)) / count;
|
||||
break;
|
||||
|
||||
// محاسبه تعداد اقساط بر اساس مبلغ هر قسط
|
||||
switch (interestType) {
|
||||
case 'monthly': {
|
||||
const monthlyInterest = interestRate / 100;
|
||||
const principalPerMonth = remainingAmount / monthlyPayment;
|
||||
const interestPerMonth = monthlyInterest;
|
||||
count = Math.ceil(principalPerMonth / (1 - interestPerMonth));
|
||||
break;
|
||||
}
|
||||
case 'yearly': {
|
||||
const yearlyInterest = interestRate / 100;
|
||||
const monthlyInterest = yearlyInterest / 12;
|
||||
const principalPerMonth = remainingAmount / monthlyPayment;
|
||||
const interestPerMonth = monthlyInterest;
|
||||
count = Math.ceil(principalPerMonth / (1 - interestPerMonth));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
count = 0;
|
||||
}
|
||||
default:
|
||||
monthlyPayment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ایجاد اقساط
|
||||
const newInstallments = [];
|
||||
let currentDate = moment(this.installmentData.firstDate, 'jYYYY/jMM/jDD').toDate();
|
||||
let remainingTotal = remainingAmount;
|
||||
|
||||
for (let i = 1; i <= count; i++) {
|
||||
let installmentAmount;
|
||||
if (i === count) {
|
||||
installmentAmount = remainingTotal;
|
||||
count = Math.min(Math.max(count, 1), 360);
|
||||
} else {
|
||||
installmentAmount = Math.round(monthlyPayment);
|
||||
remainingTotal -= installmentAmount;
|
||||
count = Number(this.installmentData.count) || 0;
|
||||
if (count <= 0) {
|
||||
this.installments = [];
|
||||
this.showMessage('تعداد اقساط باید بزرگتر از صفر باشد', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
switch (interestType) {
|
||||
case 'monthly': {
|
||||
const monthlyInterest = interestRate / 100;
|
||||
const totalInterest = monthlyInterest * count;
|
||||
monthlyPayment = (remainingAmount * (1 + totalInterest)) / count;
|
||||
break;
|
||||
}
|
||||
case 'yearly': {
|
||||
const yearlyInterest = interestRate / 100;
|
||||
const monthlyInterest = yearlyInterest / 12;
|
||||
const totalInterest = monthlyInterest * count;
|
||||
monthlyPayment = (remainingAmount * (1 + totalInterest)) / count;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
monthlyPayment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
newInstallments.push({
|
||||
number: i,
|
||||
amount: installmentAmount,
|
||||
dueDate: moment(currentDate).format('jYYYY/jMM/jDD'),
|
||||
status: 'پرداخت نشده',
|
||||
latePaymentPenalty: this.installmentData.latePaymentPenalty
|
||||
});
|
||||
const newInstallments = [];
|
||||
let currentDate = moment(this.installmentData.firstDate, 'jYYYY/jMM/jDD').toDate();
|
||||
let remainingTotal = remainingAmount;
|
||||
|
||||
currentDate = moment(currentDate).add(1, 'month').toDate();
|
||||
for (let i = 1; i <= count; i++) {
|
||||
let installmentAmount;
|
||||
if (i === count) {
|
||||
installmentAmount = remainingTotal;
|
||||
} else {
|
||||
installmentAmount = Math.round(monthlyPayment);
|
||||
remainingTotal -= installmentAmount;
|
||||
}
|
||||
|
||||
const existingInstallment = existingInstallments[i];
|
||||
newInstallments.push({
|
||||
number: i,
|
||||
amount: installmentAmount,
|
||||
dueDate: moment(currentDate).format('jYYYY/jMM/jDD'),
|
||||
status: existingInstallment ? existingInstallment.status : 'پرداخت نشده',
|
||||
latePaymentPenalty: this.installmentData.latePaymentPenalty,
|
||||
hesabdariDoc: existingInstallment ? existingInstallment.hesabdariDoc : null
|
||||
});
|
||||
|
||||
currentDate = moment(currentDate).add(1, 'month').toDate();
|
||||
}
|
||||
|
||||
if (prepayment > 0) {
|
||||
const existingPrepayment = existingInstallments[0];
|
||||
newInstallments.unshift({
|
||||
number: 0,
|
||||
amount: prepayment,
|
||||
dueDate: moment().format('jYYYY/jMM/jDD'),
|
||||
status: existingPrepayment ? existingPrepayment.status : 'پرداخت شده',
|
||||
latePaymentPenalty: 0,
|
||||
hesabdariDoc: existingPrepayment ? existingPrepayment.hesabdariDoc : null
|
||||
});
|
||||
}
|
||||
|
||||
this.installments = newInstallments;
|
||||
this.showMessage('اقساط با موفقیت محاسبه شد', 'success');
|
||||
} catch (error) {
|
||||
console.error('خطا در محاسبه اقساط:', error);
|
||||
this.showMessage('خطا در محاسبه اقساط', 'error');
|
||||
} finally {
|
||||
this.isCalculating = false;
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
if (prepayment > 0) {
|
||||
newInstallments.unshift({
|
||||
number: 0,
|
||||
amount: prepayment,
|
||||
dueDate: moment().format('jYYYY/jMM/jDD'),
|
||||
status: 'پرداخت شده',
|
||||
latePaymentPenalty: 0
|
||||
});
|
||||
}
|
||||
|
||||
this.installments = newInstallments;
|
||||
},
|
||||
dailyPenaltyAmount() {
|
||||
if (!this.installmentData.installmentAmount) {
|
||||
|
@ -762,6 +790,142 @@ export default {
|
|||
this.snackbar.color = color;
|
||||
this.snackbar.show = true;
|
||||
},
|
||||
calculateInstallments() {
|
||||
if (!this.canCalculate) {
|
||||
this.showMessage('لطفا تمام اطلاعات مورد نیاز را وارد کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isCalculating) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.isCalculating = true;
|
||||
this.loading = true;
|
||||
|
||||
// حفظ وضعیت پرداخت اقساط موجود
|
||||
const existingInstallments = this.installments.reduce((acc, item) => {
|
||||
acc[item.number] = {
|
||||
status: item.status,
|
||||
hesabdariDoc: item.hesabdariDoc
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const totalAmount = this.remainingAmount;
|
||||
const prepayment = Number(this.installmentData.prepayment) || 0;
|
||||
const remainingAmount = totalAmount - prepayment;
|
||||
const interestRate = Number(this.installmentData.interestRate) || 0;
|
||||
const interestType = this.installmentData.interestType;
|
||||
|
||||
let count, monthlyPayment;
|
||||
|
||||
if (this.installmentData.calculationType === 'amount') {
|
||||
monthlyPayment = Number(this.installmentData.installmentAmount);
|
||||
if (monthlyPayment <= 0) {
|
||||
this.installments = [];
|
||||
this.showMessage('مبلغ هر قسط باید بزرگتر از صفر باشد', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// محاسبه تعداد اقساط بر اساس مبلغ هر قسط
|
||||
switch (interestType) {
|
||||
case 'monthly': {
|
||||
const monthlyInterest = interestRate / 100;
|
||||
const principalPerMonth = remainingAmount / monthlyPayment;
|
||||
const interestPerMonth = monthlyInterest;
|
||||
count = Math.ceil(principalPerMonth / (1 - interestPerMonth));
|
||||
break;
|
||||
}
|
||||
case 'yearly': {
|
||||
const yearlyInterest = interestRate / 100;
|
||||
const monthlyInterest = yearlyInterest / 12;
|
||||
const principalPerMonth = remainingAmount / monthlyPayment;
|
||||
const interestPerMonth = monthlyInterest;
|
||||
count = Math.ceil(principalPerMonth / (1 - interestPerMonth));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
count = 0;
|
||||
}
|
||||
|
||||
count = Math.min(Math.max(count, 1), 360);
|
||||
} else {
|
||||
count = Number(this.installmentData.count) || 0;
|
||||
if (count <= 0) {
|
||||
this.installments = [];
|
||||
this.showMessage('تعداد اقساط باید بزرگتر از صفر باشد', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
switch (interestType) {
|
||||
case 'monthly': {
|
||||
const monthlyInterest = interestRate / 100;
|
||||
const totalInterest = monthlyInterest * count;
|
||||
monthlyPayment = (remainingAmount * (1 + totalInterest)) / count;
|
||||
break;
|
||||
}
|
||||
case 'yearly': {
|
||||
const yearlyInterest = interestRate / 100;
|
||||
const monthlyInterest = yearlyInterest / 12;
|
||||
const totalInterest = monthlyInterest * count;
|
||||
monthlyPayment = (remainingAmount * (1 + totalInterest)) / count;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
monthlyPayment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const newInstallments = [];
|
||||
let currentDate = moment(this.installmentData.firstDate, 'jYYYY/jMM/jDD').toDate();
|
||||
let remainingTotal = remainingAmount;
|
||||
|
||||
for (let i = 1; i <= count; i++) {
|
||||
let installmentAmount;
|
||||
if (i === count) {
|
||||
installmentAmount = remainingTotal;
|
||||
} else {
|
||||
installmentAmount = Math.round(monthlyPayment);
|
||||
remainingTotal -= installmentAmount;
|
||||
}
|
||||
|
||||
const existingInstallment = existingInstallments[i];
|
||||
newInstallments.push({
|
||||
number: i,
|
||||
amount: installmentAmount,
|
||||
dueDate: moment(currentDate).format('jYYYY/jMM/jDD'),
|
||||
status: existingInstallment ? existingInstallment.status : 'پرداخت نشده',
|
||||
latePaymentPenalty: this.installmentData.latePaymentPenalty,
|
||||
hesabdariDoc: existingInstallment ? existingInstallment.hesabdariDoc : null
|
||||
});
|
||||
|
||||
currentDate = moment(currentDate).add(1, 'month').toDate();
|
||||
}
|
||||
|
||||
if (prepayment > 0) {
|
||||
const existingPrepayment = existingInstallments[0];
|
||||
newInstallments.unshift({
|
||||
number: 0,
|
||||
amount: prepayment,
|
||||
dueDate: moment().format('jYYYY/jMM/jDD'),
|
||||
status: existingPrepayment ? existingPrepayment.status : 'پرداخت شده',
|
||||
latePaymentPenalty: 0,
|
||||
hesabdariDoc: existingPrepayment ? existingPrepayment.hesabdariDoc : null
|
||||
});
|
||||
}
|
||||
|
||||
this.installments = newInstallments;
|
||||
this.showMessage('اقساط با موفقیت محاسبه شد', 'success');
|
||||
} catch (error) {
|
||||
console.error('خطا در محاسبه اقساط:', error);
|
||||
this.showMessage('خطا در محاسبه اقساط', 'error');
|
||||
} finally {
|
||||
this.isCalculating = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async saveInvoice() {
|
||||
if (!this.selectedInvoice) {
|
||||
this.showMessage('لطفا یک فاکتور انتخاب کنید', 'error');
|
||||
|
@ -833,6 +997,12 @@ export default {
|
|||
},
|
||||
|
||||
editInstallment(item) {
|
||||
// اگر قسط پرداخت شده باشد، اجازه ویرایش ندهیم
|
||||
if (item.isPaid) {
|
||||
this.showMessage('قسط پرداخت شده قابل ویرایش نیست', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.dialog = {
|
||||
show: true,
|
||||
title: 'ویرایش قسط',
|
||||
|
@ -872,6 +1042,12 @@ export default {
|
|||
},
|
||||
|
||||
deleteInstallment(item) {
|
||||
// اگر قسط پرداخت شده باشد، اجازه حذف ندهیم
|
||||
if (item.isPaid) {
|
||||
this.showMessage('قسط پرداخت شده قابل حذف نیست', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.dialog = {
|
||||
show: true,
|
||||
title: 'حذف قسط',
|
||||
|
@ -882,14 +1058,6 @@ export default {
|
|||
onConfirm: () => {
|
||||
const index = this.installments.findIndex(i => i.number === item.number);
|
||||
if (index !== -1) {
|
||||
// اگر قسط پرداخت شده است، اجازه حذف ندهیم
|
||||
if (item.status === 'پرداخت شده') {
|
||||
this.showMessage('قسط پرداخت شده قابل حذف نیست', 'error');
|
||||
this.dialog.show = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// حذف قسط
|
||||
this.installments.splice(index, 1);
|
||||
|
||||
// بهروزرسانی شماره اقساط
|
||||
|
@ -919,133 +1087,6 @@ export default {
|
|||
// در غیر این صورت، تاریخ میلادی را به شمسی تبدیل کن
|
||||
return moment(date).format('jYYYY/jMM/jDD');
|
||||
},
|
||||
calculateInstallments() {
|
||||
if (!this.canCalculate) {
|
||||
this.showMessage('لطفا تمام اطلاعات مورد نیاز را وارد کنید', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isCalculating) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.isCalculating = true;
|
||||
this.loading = true;
|
||||
|
||||
const totalAmount = this.remainingAmount;
|
||||
const prepayment = Number(this.installmentData.prepayment) || 0;
|
||||
const remainingAmount = totalAmount - prepayment;
|
||||
const interestRate = Number(this.installmentData.interestRate) || 0;
|
||||
const interestType = this.installmentData.interestType;
|
||||
|
||||
let count, monthlyPayment;
|
||||
|
||||
if (this.installmentData.calculationType === 'amount') {
|
||||
monthlyPayment = Number(this.installmentData.installmentAmount);
|
||||
if (monthlyPayment <= 0) {
|
||||
this.installments = [];
|
||||
this.showMessage('مبلغ هر قسط باید بزرگتر از صفر باشد', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// محاسبه تعداد اقساط بر اساس مبلغ هر قسط
|
||||
switch (interestType) {
|
||||
case 'monthly': {
|
||||
// برای سود ساده ماهانه
|
||||
const monthlyInterest = interestRate / 100;
|
||||
const principalPerMonth = remainingAmount / monthlyPayment;
|
||||
const interestPerMonth = monthlyInterest;
|
||||
count = Math.ceil(principalPerMonth / (1 - interestPerMonth));
|
||||
break;
|
||||
}
|
||||
case 'yearly': {
|
||||
// برای سود ساده سالانه
|
||||
const yearlyInterest = interestRate / 100;
|
||||
const monthlyInterest = yearlyInterest / 12;
|
||||
const principalPerMonth = remainingAmount / monthlyPayment;
|
||||
const interestPerMonth = monthlyInterest;
|
||||
count = Math.ceil(principalPerMonth / (1 - interestPerMonth));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// محدود کردن تعداد اقساط به یک عدد منطقی
|
||||
count = Math.min(Math.max(count, 1), 360); // حداکثر 30 سال
|
||||
} else {
|
||||
count = Number(this.installmentData.count) || 0;
|
||||
if (count <= 0) {
|
||||
this.installments = [];
|
||||
this.showMessage('تعداد اقساط باید بزرگتر از صفر باشد', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
switch (interestType) {
|
||||
case 'monthly': {
|
||||
const monthlyInterest = interestRate / 100;
|
||||
const totalInterest = monthlyInterest * count;
|
||||
monthlyPayment = (remainingAmount * (1 + totalInterest)) / count;
|
||||
break;
|
||||
}
|
||||
case 'yearly': {
|
||||
const yearlyInterest = interestRate / 100;
|
||||
const monthlyInterest = yearlyInterest / 12;
|
||||
const totalInterest = monthlyInterest * count;
|
||||
monthlyPayment = (remainingAmount * (1 + totalInterest)) / count;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
monthlyPayment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ایجاد اقساط
|
||||
const newInstallments = [];
|
||||
let currentDate = moment(this.installmentData.firstDate, 'jYYYY/jMM/jDD').toDate();
|
||||
let remainingTotal = remainingAmount;
|
||||
|
||||
for (let i = 1; i <= count; i++) {
|
||||
let installmentAmount;
|
||||
if (i === count) {
|
||||
installmentAmount = remainingTotal;
|
||||
} else {
|
||||
installmentAmount = Math.round(monthlyPayment);
|
||||
remainingTotal -= installmentAmount;
|
||||
}
|
||||
|
||||
newInstallments.push({
|
||||
number: i,
|
||||
amount: installmentAmount,
|
||||
dueDate: moment(currentDate).format('jYYYY/jMM/jDD'),
|
||||
status: 'پرداخت نشده',
|
||||
latePaymentPenalty: this.installmentData.latePaymentPenalty
|
||||
});
|
||||
|
||||
currentDate = moment(currentDate).add(1, 'month').toDate();
|
||||
}
|
||||
|
||||
if (prepayment > 0) {
|
||||
newInstallments.unshift({
|
||||
number: 0,
|
||||
amount: prepayment,
|
||||
dueDate: moment().format('jYYYY/jMM/jDD'),
|
||||
status: 'پرداخت شده',
|
||||
latePaymentPenalty: 0
|
||||
});
|
||||
}
|
||||
|
||||
this.installments = newInstallments;
|
||||
this.showMessage('اقساط با موفقیت محاسبه شد', 'success');
|
||||
} catch (error) {
|
||||
console.error('خطا در محاسبه اقساط:', error);
|
||||
this.showMessage('خطا در محاسبه اقساط', 'error');
|
||||
} finally {
|
||||
this.isCalculating = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async loadInvoiceData() {
|
||||
try {
|
||||
this.loading = true;
|
||||
|
@ -1055,7 +1096,7 @@ export default {
|
|||
// تنظیم اطلاعات فاکتور
|
||||
this.selectedInvoice = {
|
||||
id: data.id,
|
||||
code: data.id.toString(),
|
||||
code: data.code,
|
||||
date: moment.unix(data.dateSubmit).format('jYYYY/jMM/jDD'),
|
||||
dateSubmit: data.dateSubmit,
|
||||
des: 'فاکتور اقساطی',
|
||||
|
@ -1063,7 +1104,8 @@ export default {
|
|||
status: 'در انتظار پرداخت',
|
||||
person: data.person,
|
||||
personName: data.person.name || '',
|
||||
personNikename: data.person.nikename || ''
|
||||
personNikename: data.person.nikename || '',
|
||||
relatedDocs: []
|
||||
};
|
||||
|
||||
// تنظیم اطلاعات اقساط
|
||||
|
@ -1077,13 +1119,16 @@ export default {
|
|||
prepayment: 0
|
||||
};
|
||||
|
||||
// تنظیم اقساط
|
||||
// تنظیم اقساط با وضعیت پرداخت صحیح
|
||||
this.installments = data.items.map(item => ({
|
||||
id: item.id,
|
||||
number: parseInt(item.num),
|
||||
amount: parseFloat(item.amount),
|
||||
dueDate: item.date,
|
||||
status: item.hesabdariDoc ? 'پرداخت شده' : 'پرداخت نشده',
|
||||
latePaymentPenalty: parseFloat(data.daysPay)
|
||||
latePaymentPenalty: parseFloat(data.daysPay),
|
||||
hesabdariDoc: item.hesabdariDoc,
|
||||
isPaid: !!item.hesabdariDoc // اضافه کردن فلگ پرداخت شده
|
||||
}));
|
||||
|
||||
// محاسبه مجدد اقساط برای نمایش صحیح
|
||||
|
|
|
@ -36,17 +36,19 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
headers: [
|
||||
{ title: this.$t('field.id'), key: 'id' },
|
||||
{ title: this.$t('field.date'), key: 'date' },
|
||||
{ title: this.$t('field.employee'), key: 'employee' },
|
||||
{ title: this.$t('field.amount'), key: 'amount' },
|
||||
{ title: this.$t('field.status'), key: 'status' },
|
||||
{ title: this.$t('field.actions'), key: 'actions', sortable: false }
|
||||
{ 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: []
|
||||
}
|
||||
|
@ -58,7 +60,7 @@ export default {
|
|||
async loadData() {
|
||||
this.loading = true
|
||||
try {
|
||||
const response = await this.$axios.post('/api/hrm/docs/list')
|
||||
const response = await axios.post('/api/hrm/docs/list')
|
||||
this.items = response.data
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error)
|
||||
|
|
|
@ -78,6 +78,11 @@
|
|||
<v-btn v-bind="props" icon="mdi-delete" variant="text" size="small" color="error" @click="removeRow(index)"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip text="کپی" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon="mdi-content-copy" variant="text" size="small" color="primary" @click="copyRow(index)"></v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
@ -251,6 +256,17 @@ export default {
|
|||
return sum + (Number(row[column]) || 0);
|
||||
}, 0);
|
||||
},
|
||||
copyRow(index) {
|
||||
const rowToCopy = this.tableItems[index];
|
||||
this.tableItems.push({
|
||||
person: null,
|
||||
description: rowToCopy.description,
|
||||
baseSalary: rowToCopy.baseSalary,
|
||||
overtime: rowToCopy.overtime,
|
||||
shift: rowToCopy.shift,
|
||||
night: rowToCopy.night
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -80,31 +80,6 @@
|
|||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<v-card :class="['charge-card', { 'selected': isCustomAmount }]"
|
||||
:elevation="isCustomAmount ? 4 : 1" class="h-100"
|
||||
:color="isCustomAmount ? 'primary' : 'surface'" variant="elevated">
|
||||
<v-card-text class="text-center">
|
||||
<div class="text-h6 mb-2" :class="{ 'text-white': isCustomAmount }">مبلغ دلخواه</div>
|
||||
<v-text-field
|
||||
v-model="customAmount"
|
||||
type="number"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
class="mt-2"
|
||||
:class="{ 'custom-amount-input': isCustomAmount }"
|
||||
placeholder="مبلغ را وارد کنید"
|
||||
@click="selectCustomAmount"
|
||||
@input="handleCustomAmountInput"
|
||||
></v-text-field>
|
||||
<div v-if="customAmount" class="text-caption mt-2"
|
||||
:class="{ 'text-white': isCustomAmount, 'text-medium-emphasis': !isCustomAmount }">
|
||||
با احتساب مالیات: {{ formatPrice(Number(customAmount) * 1.1) }} تومان
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
@ -187,13 +162,11 @@ export default defineComponent({
|
|||
color: 'error'
|
||||
},
|
||||
smsCharge: 100000,
|
||||
customAmount: '',
|
||||
isCustomAmount: false,
|
||||
chargeAmounts: [
|
||||
{ label: '۱۰ هزار تومان', value: 10000 },
|
||||
{ label: '۵۰ هزار تومان', value: 50000 },
|
||||
{ label: '۱۰۰ هزار تومان', value: 100000 },
|
||||
{ label: '۲۰۰ هزار تومان', value: 200000 }
|
||||
{ label: '۲۰۰ هزار تومان', value: 200000 },
|
||||
{ label: '۵۰۰ هزار تومان', value: 500000 }
|
||||
],
|
||||
searchValue: '',
|
||||
loading: true,
|
||||
|
@ -231,7 +204,8 @@ export default defineComponent({
|
|||
},
|
||||
pay() {
|
||||
this.loading = true;
|
||||
axios.post('/api/sms/charge', { price: this.smsCharge })
|
||||
const amountInRial = this.smsCharge * 10; // تبدیل تومان به ریال
|
||||
axios.post('/api/sms/charge', { price: amountInRial })
|
||||
.then((response) => {
|
||||
if (response.data.Success === true) {
|
||||
window.location.href = response.data.targetURL;
|
||||
|
@ -261,15 +235,6 @@ export default defineComponent({
|
|||
formatPrice(price: number): string {
|
||||
return new Intl.NumberFormat('fa-IR').format(price);
|
||||
},
|
||||
selectCustomAmount() {
|
||||
this.isCustomAmount = true;
|
||||
this.smsCharge = Number(this.customAmount);
|
||||
},
|
||||
handleCustomAmountInput() {
|
||||
if (this.isCustomAmount) {
|
||||
this.smsCharge = Number(this.customAmount);
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
this.loadData();
|
||||
|
|
142
webUI/src/views/user/manager/settings/pluginTransactions.vue
Normal file
142
webUI/src/views/user/manager/settings/pluginTransactions.vue
Normal file
|
@ -0,0 +1,142 @@
|
|||
<script>
|
||||
import axios from "axios";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
export default {
|
||||
name: "pluginTransactions",
|
||||
data: () => {
|
||||
return {
|
||||
loading: false,
|
||||
search: '',
|
||||
transactions: [],
|
||||
headers: [
|
||||
{ title: 'نام افزونه', key: 'des', align: 'right' },
|
||||
{ title: 'نام کسب و کار', key: 'businessName', align: 'right' },
|
||||
{ title: 'نام خریدار', key: 'submitterName', align: 'right' },
|
||||
{ title: 'قیمت', key: 'price', align: 'right' },
|
||||
{ title: 'تاریخ خرید', key: 'dateSubmit', align: 'right' },
|
||||
{ title: 'تاریخ انقضا', key: 'dateExpire', align: 'right' },
|
||||
{ title: 'وضعیت', key: 'status', align: 'right' },
|
||||
{ title: 'شماره کارت', key: 'cardPan', align: 'right' },
|
||||
{ title: 'کد پیگیری', key: 'refID', align: 'right' },
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadTransactions() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await axios.post('/api/admin/plugins/transactions');
|
||||
this.transactions = response.data;
|
||||
} catch (error) {
|
||||
Swal.fire({
|
||||
title: 'خطا',
|
||||
text: 'در دریافت اطلاعات تراکنشها مشکلی پیش آمده است',
|
||||
icon: 'error',
|
||||
confirmButtonText: 'باشه'
|
||||
});
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
getStatusText(status) {
|
||||
switch (parseInt(status)) {
|
||||
case 0:
|
||||
return 'در انتظار پرداخت';
|
||||
case 100:
|
||||
return 'پرداخت موفق';
|
||||
case 101:
|
||||
return 'پرداخت ناموفق';
|
||||
case 102:
|
||||
return 'لغو شده';
|
||||
default:
|
||||
return 'نامشخص';
|
||||
}
|
||||
},
|
||||
getStatusColor(status) {
|
||||
switch (parseInt(status)) {
|
||||
case 0:
|
||||
return 'warning';
|
||||
case 100:
|
||||
return 'success';
|
||||
case 101:
|
||||
return 'error';
|
||||
case 102:
|
||||
return 'grey';
|
||||
default:
|
||||
return 'error';
|
||||
}
|
||||
},
|
||||
getStatusIcon(status) {
|
||||
switch (parseInt(status)) {
|
||||
case 0:
|
||||
return 'mdi-clock-outline';
|
||||
case 100:
|
||||
return 'mdi-check-circle';
|
||||
case 101:
|
||||
return 'mdi-close-circle';
|
||||
case 102:
|
||||
return 'mdi-cancel';
|
||||
default:
|
||||
return 'mdi-help-circle';
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadTransactions();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-toolbar color="toolbar" :title="$t('dialog.plugins')">
|
||||
<v-spacer></v-spacer>
|
||||
</v-toolbar>
|
||||
<v-container class="pa-4">
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
label="جستجو"
|
||||
single-line
|
||||
hide-details
|
||||
class="mb-4"
|
||||
density="compact"
|
||||
></v-text-field>
|
||||
</v-card-text>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="transactions"
|
||||
:loading="loading"
|
||||
:search="search"
|
||||
class="elevation-1"
|
||||
>
|
||||
<template v-slot:item.status="{ item }">
|
||||
<v-chip
|
||||
:color="getStatusColor(item.status)"
|
||||
size="small"
|
||||
:prepend-icon="getStatusIcon(item.status)"
|
||||
>
|
||||
{{ getStatusText(item.status) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<template v-slot:item.price="{ item }">
|
||||
{{ item.price.toLocaleString() }} ریال
|
||||
</template>
|
||||
<template v-slot:no-data>
|
||||
<v-alert
|
||||
type="info"
|
||||
text="هیچ تراکنشی یافت نشد"
|
||||
class="ma-4"
|
||||
></v-alert>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.v-data-table {
|
||||
direction: rtl;
|
||||
}
|
||||
</style>
|
|
@ -35,6 +35,13 @@
|
|||
<v-list-item v-for="(item, i) in adminSettings" :prepend-icon="item.icon" :to="item.url"
|
||||
:title="item.text"></v-list-item>
|
||||
</v-list-group>
|
||||
<v-list-group v-if="ROLE_ADMIN == true">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-list-item v-bind="props" prepend-icon="mdi-toy-brick" title="افزونهها"></v-list-item>
|
||||
</template>
|
||||
<v-list-item prepend-icon="mdi-format-list-bulleted" to="/profile/manager/plugins/list" title="فهرست افزونهها"></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-cash-multiple" to="/profile/manager/plugins/transactions" title="تراکنشهای خرید"></v-list-item>
|
||||
</v-list-group>
|
||||
<v-list-item color="primary">
|
||||
<v-list-item-title>
|
||||
<small class="text-primary">{{ siteName }} : {{ hesabix.version }}</small>
|
||||
|
@ -101,13 +108,11 @@ export default defineComponent({
|
|||
{ text: 'کسبوکارها', url: '/profile/manager/business/list', icon: 'mdi-home-city', visible: true },
|
||||
{ text: 'کاربران', url: '/profile/manager/users/list', icon: 'mdi-account-multiple', visible: true },
|
||||
{ text: 'کاربران آنلاین', url: '/profile/manager/users/onlinelist', icon: 'mdi-account-badge', visible: true },
|
||||
{ text: 'افزونهها', url: '/profile/manager/plugins/list', icon: 'mdi-toy-brick', visible: true },
|
||||
{ text: 'به روز رسانی هسته', url: '/profile/manager/update-core', icon: 'mdi-undo', visible: true },
|
||||
{ text: 'تغییرات', url: '/profile/manager/changes/list', icon: 'mdi-cellphone-arrow-down', visible: true },
|
||||
{ text: 'تاریخچه سیستم', url: '/profile/manager/logs/list', icon: 'mdi-history', visible: true },
|
||||
{ text: 'کیف پول', url: '/profile/manager/wallet/list', icon: 'mdi-wallet', visible: true },
|
||||
{ text: 'اطلاعیهها', url: '/profile/manager/statments/list', icon: 'mdi-bell', visible: true },
|
||||
|
||||
],
|
||||
adminSettings: [
|
||||
{ text: 'پیامک', url: '/profile/manager/system/sms/settings', icon: 'mdi-message-alert', visible: true },
|
||||
|
@ -181,4 +186,24 @@ export default defineComponent({
|
|||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.v-list-group__items .v-list-item {
|
||||
padding-right: 32px !important;
|
||||
}
|
||||
|
||||
.v-list-item {
|
||||
min-height: 40px !important;
|
||||
}
|
||||
|
||||
.v-list-item__content {
|
||||
padding-right: 8px !important;
|
||||
}
|
||||
|
||||
.v-list-group__header {
|
||||
padding-right: 16px !important;
|
||||
}
|
||||
|
||||
.v-list-group__items {
|
||||
padding-right: 8px !important;
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue