some bug fixes

This commit is contained in:
Hesabix 2025-04-01 14:08:07 +00:00
parent ec17a2f4df
commit 2f3a47fc49
3 changed files with 301 additions and 107 deletions

View file

@ -59,6 +59,7 @@
previous: "", previous: "",
operation: null, operation: null,
activeButton: null, activeButton: null,
waitingForPercent: false,
numberButtons: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."], // دکمههای اعداد numberButtons: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."], // دکمههای اعداد
operatorButtons: ["+", "-", "*", "/", "%"], // دکمههای عملگر operatorButtons: ["+", "-", "*", "/", "%"], // دکمههای عملگر
actionButtons: ["C", "="], // دکمههای عملیاتی actionButtons: ["C", "="], // دکمههای عملیاتی
@ -104,12 +105,25 @@
}, },
setOperation(op) { setOperation(op) {
if (this.current === "") return; if (this.current === "") return;
if (op === "%") {
if (this.operation === "+" || this.operation === "-") {
const base = parseFloat(this.previous);
const percentage = (base * parseFloat(this.current)) / 100;
this.current = percentage.toString();
this.calculate();
} else {
const curr = parseFloat(this.current);
this.current = (curr / 100).toString();
}
} else {
if (this.previous !== "") { if (this.previous !== "") {
this.calculate(); this.calculate();
} }
this.operation = op; this.operation = op;
this.previous = this.current; this.previous = this.current;
this.current = ""; this.current = "";
}
}, },
calculate() { calculate() {
if (this.current === "" || this.previous === "") return; if (this.current === "" || this.previous === "") return;
@ -131,9 +145,10 @@
case "/": case "/":
result = curr !== 0 ? prev / curr : "خطا"; result = curr !== 0 ? prev / curr : "خطا";
break; break;
case "%": }
result = (prev * curr) / 100;
break; if (typeof result === 'number' && !Number.isInteger(result)) {
result = parseFloat(result.toFixed(4));
} }
this.display = result.toString(); this.display = result.toString();
@ -164,12 +179,11 @@
event.preventDefault(); event.preventDefault();
} }
}, },
// تعیین رنگ دکمهها بر اساس نوع
getButtonColor(btn) { getButtonColor(btn) {
if (this.numberButtons.includes(btn)) return "blue darken-5"; // رنگ اعداد if (this.numberButtons.includes(btn)) return "blue darken-5";
if (this.operatorButtons.includes(btn)) return "gray darken-1"; // رنگ عملگرها if (this.operatorButtons.includes(btn)) return "gray darken-1";
if (this.actionButtons.includes(btn)) return "red lighten-1"; // رنگ دکمههای عملیاتی if (this.actionButtons.includes(btn)) return "red lighten-1";
return "grey lighten-4"; // رنگ پیشفرض return "grey lighten-4";
}, },
}, },
}; };
@ -186,9 +200,8 @@
box-shadow: none !important; box-shadow: none !important;
} }
/* تغییر رنگ لحظه‌ای دکمه */
.active-btn { .active-btn {
background-color: #0288d1 !important; /* آبی تیره‌تر برای حالت فعال */ background-color: #0288d1 !important;
color: white !important; color: white !important;
transition: background-color 0.1s ease; transition: background-color 0.1s ease;
} }

View file

@ -4,14 +4,21 @@
<v-tooltip activator="parent" location="bottom">ثبت دریافت</v-tooltip> <v-tooltip activator="parent" location="bottom">ثبت دریافت</v-tooltip>
</v-btn> </v-btn>
<v-dialog v-model="dialog" max-width="950" persistent> <v-dialog
<v-card> v-model="dialog"
:fullscreen="$vuetify.display.mobile"
:max-width="$vuetify.display.mobile ? '' : '950'"
persistent
:class="$vuetify.display.mobile ? 'mobile-dialog' : ''"
>
<v-card :rounded="false" class="d-flex flex-column dialog-card">
<v-toolbar color="toolbar" flat> <v-toolbar color="toolbar" flat>
<v-toolbar-title> <v-toolbar-title>
<v-icon color="error" left>mdi-cash</v-icon> <v-icon color="error" left>mdi-cash</v-icon>
ثبت دریافت ثبت دریافت
</v-toolbar-title> </v-toolbar-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-menu bottom> <v-menu bottom>
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
<v-btn icon color="success" v-bind="props" :disabled="loading"> <v-btn icon color="success" v-bind="props" :disabled="loading">
@ -34,6 +41,7 @@
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
<v-btn icon color="primary" @click="submit" :disabled="loading"> <v-btn icon color="primary" @click="submit" :disabled="loading">
<v-icon>mdi-content-save</v-icon> <v-icon>mdi-content-save</v-icon>
<v-tooltip activator="parent" location="bottom">ثبت</v-tooltip> <v-tooltip activator="parent" location="bottom">ثبت</v-tooltip>
@ -43,7 +51,8 @@
</v-btn> </v-btn>
</v-toolbar> </v-toolbar>
<v-card-text> <v-card-text class="flex-grow-1 overflow-y-auto pa-2">
<v-container class="pa-0">
<v-row> <v-row>
<v-col cols="12" md="5"> <v-col cols="12" md="5">
<Hdatepicker v-model="date" label="تاریخ" /> <Hdatepicker v-model="date" label="تاریخ" />
@ -62,58 +71,123 @@
</v-col> </v-col>
</v-row> </v-row>
<v-data-table :headers="headers" :items="items" :loading="loading" class="elevation-1 mt-2" <div v-if="items.length === 0" class="text-center pa-4">
:header-props="{ class: 'custom-header' }" :items-per-page="-1" hide-default-footer> هیچ دریافتی ثبت نشده است.
<template v-slot:item.type="{ item }"> </div>
<v-icon v-if="item.type === 'bank'">mdi-bank</v-icon>
<v-icon v-if="item.type === 'cashdesk'">mdi-cash-register</v-icon> <v-row v-else>
<v-icon v-if="item.type === 'salary'">mdi-wallet</v-icon> <v-col v-for="(item, index) in items" :key="index" cols="12">
<v-icon v-if="item.type === 'cheque'">mdi-checkbook</v-icon> <v-card :class="{
'bank-card': item.type === 'bank',
'cashdesk-card': item.type === 'cashdesk',
'salary-card': item.type === 'salary',
'cheque-card': item.type === 'cheque'
}" border="sm">
<v-card-item>
<template v-slot:prepend>
<v-icon :color="item.type === 'bank' ? 'blue' :
item.type === 'cashdesk' ? 'green' :
item.type === 'salary' ? 'orange' : 'purple'">
{{ item.type === 'bank' ? 'mdi-bank' :
item.type === 'cashdesk' ? 'mdi-cash-register' :
item.type === 'salary' ? 'mdi-wallet' : 'mdi-checkbook' }}
</v-icon>
</template> </template>
<template v-slot:item.selection="{ item }">
<v-select v-if="item.type === 'bank'" v-model="item.bank" :items="listBanks" item-title="name" return-object <template v-slot:append>
label="بانک"></v-select> <v-btn-group>
<v-select v-if="item.type === 'cashdesk'" v-model="item.cashdesk" :items="listCashdesks" item-title="name" <v-btn variant="text" color="primary" density="comfortable" @click="fillWithTotal(item)">
return-object label="صندوق"></v-select>
<v-select v-if="item.type === 'salary'" v-model="item.salary" :items="listSalarys" item-title="name"
return-object label="تنخواه گردان"></v-select>
<template v-if="item.type === 'cheque'">
<v-text-field class="mb-2 mt-1" v-model="item.chequeNum" label="شماره چک" required></v-text-field>
<v-text-field class="mb-2" v-model="item.chequeSayadNum" label="شماره صیاد" dense required></v-text-field>
<v-text-field class="mb-2" v-model="item.chequeBank" label="بانک صادرکننده" required></v-text-field>
<Hdatepicker class="mb-1" v-model="item.chequeDate" label="تاریخ چک" required />
</template>
</template>
<template v-slot:item.bd="{ item }">
<Hnumberinput v-model="item.bd" label="مبلغ" placeholder="0" @update:modelValue="calc" />
</template>
<template v-slot:item.referral="{ item }">
<v-text-field v-model="item.referral" label="ارجاع"></v-text-field>
</template>
<template v-slot:item.des="{ item }">
<v-text-field v-model="item.des" label="شرح"></v-text-field>
</template>
<template v-slot:item.actions="{ item, index }">
<v-btn variant="plain" color="primary" @click="fillWithTotal(item)">
<v-icon>mdi-cash-100</v-icon> <v-icon>mdi-cash-100</v-icon>
<v-tooltip activator="parent" location="bottom">کل فاکتور</v-tooltip> <v-tooltip activator="parent" location="bottom">کل فاکتور</v-tooltip>
</v-btn> </v-btn>
<v-btn variant="plain" color="error" @click="deleteItem(index)"> <v-btn variant="text" color="error" density="comfortable" @click="deleteItem(index)">
<v-icon>mdi-trash-can</v-icon> <v-icon>mdi-trash-can</v-icon>
<v-tooltip activator="parent" location="bottom">حذف</v-tooltip> <v-tooltip activator="parent" location="bottom">حذف</v-tooltip>
</v-btn> </v-btn>
</v-btn-group>
</template> </template>
<template v-slot:no-data> </v-card-item>
<v-card-text class="text-center">
هیچ دریافتی ثبت نشده است. <v-card-text>
</v-card-text> <v-row class="mt-2">
</template> <v-col cols="12" sm="6">
</v-data-table> <v-select
v-if="item.type === 'bank'"
v-model="item.bank"
:items="listBanks"
item-title="name"
return-object
label="بانک"
variant="outlined"
></v-select>
<v-select
v-if="item.type === 'cashdesk'"
v-model="item.cashdesk"
:items="listCashdesks"
item-title="name"
return-object
label="صندوق"
variant="outlined"
></v-select>
<v-select
v-if="item.type === 'salary'"
v-model="item.salary"
:items="listSalarys"
item-title="name"
return-object
label="تنخواه گردان"
variant="outlined"
></v-select>
</v-col>
<v-col cols="12" sm="6">
<Hnumberinput
v-model="item.bd"
label="مبلغ"
placeholder="0"
@update:modelValue="calc"
variant="outlined"
/>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="item.referral"
label="ارجاع"
variant="outlined"
></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="item.des"
label="شرح"
variant="outlined"
></v-text-field>
</v-col>
</v-row>
<v-row v-if="item.type === 'cheque'" class="mt-2">
<v-col cols="12" sm="6">
<v-text-field v-model="item.chequeNum" label="شماره چک" required variant="outlined"></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field v-model="item.chequeSayadNum" label="شماره صیاد" required variant="outlined"></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field v-model="item.chequeBank" label="بانک صادرکننده" required variant="outlined"></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<Hdatepicker v-model="item.chequeDate" label="تاریخ چک" required />
</v-col>
</v-row>
</v-card-text> </v-card-text>
</v-card>
</v-col>
</v-row>
<v-overlay :model-value="loading" contained class="align-center justify-center"> <v-overlay :model-value="loading" contained class="align-center justify-center">
<v-progress-circular indeterminate size="64" color="primary"></v-progress-circular> <v-progress-circular indeterminate size="64" color="primary"></v-progress-circular>
</v-overlay> </v-overlay>
</v-container>
</v-card-text>
</v-card> </v-card>
</v-dialog> </v-dialog>
@ -126,6 +200,20 @@
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-dialog> </v-dialog>
<v-dialog v-model="errorDialog" max-width="400">
<v-card color="error">
<v-card-text class="text-center pa-4">
<v-icon size="large" color="white" class="mb-4">mdi-alert-circle</v-icon>
<div class="text-h6 text-white mb-2">خطا</div>
<div class="text-white" style="white-space: pre-line">{{ errorMessage }}</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="white" variant="text" @click="errorDialog = false">بستن</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -135,6 +223,23 @@ import { VDataTable } from 'vuetify/components'
import Hdatepicker from '@/components/forms/Hdatepicker.vue' import Hdatepicker from '@/components/forms/Hdatepicker.vue'
import Hnumberinput from '@/components/forms/Hnumberinput.vue' import Hnumberinput from '@/components/forms/Hnumberinput.vue'
interface Item {
id: string | number;
type: string;
bd: number;
bs: number;
table: number;
des: string;
bank?: any;
cashdesk?: any;
salary?: any;
chequeNum?: string;
chequeSayadNum?: string;
chequeBank?: string;
chequeDate?: string;
referral?: string;
}
export default defineComponent({ export default defineComponent({
name: 'Rec', name: 'Rec',
components: { components: {
@ -155,7 +260,7 @@ export default defineComponent({
data: () => ({ data: () => ({
submitedDoc: {}, submitedDoc: {},
des: '', des: '',
items: [], items: [] as Item[],
date: '', date: '',
listBanks: [], listBanks: [],
listSalarys: [], listSalarys: [],
@ -164,7 +269,8 @@ export default defineComponent({
loading: false, loading: false,
errorMessage: '', errorMessage: '',
successDialog: false, successDialog: false,
successMessage: '' successMessage: '',
errorDialog: false
}), }),
computed: { computed: {
headers() { headers() {
@ -265,12 +371,21 @@ export default defineComponent({
}, },
async submit() { async submit() {
let errors = [] let errors = []
if (this.totalAmount < this.totalPays) {
if (this.items.length === 0) {
this.errorMessage = 'هیچ دریافتی ثبت نشده است.';
this.errorDialog = true;
return;
}
if (this.totalAmount && this.totalPays && this.totalAmount < this.totalPays) {
errors.push('مبالغ وارد شده بیشتر از مبلغ فاکتور است.') errors.push('مبالغ وارد شده بیشتر از مبلغ فاکتور است.')
} }
this.items.forEach((element, index) => { this.items.forEach((element: any, index: number) => {
if (element.bd === 0) errors.push(`مبلغ صفر در ردیف ${index + 1} نا معتبر است.`) if (element.bd === 0 || element.bd === null || element.bd === undefined) {
errors.push(`مبلغ صفر در ردیف ${index + 1} نا معتبر است.`)
}
if (element.type === 'cheque' && (!element.chequeBank || !element.chequeNum || !element.chequeDate)) { if (element.type === 'cheque' && (!element.chequeBank || !element.chequeNum || !element.chequeDate)) {
errors.push(`موارد الزامی ثبت چک در ردیف ${index + 1} وارد نشده است.`) errors.push(`موارد الزامی ثبت چک در ردیف ${index + 1} وارد نشده است.`)
} }
@ -285,13 +400,9 @@ export default defineComponent({
} }
}) })
if (this.items.length === 0) {
this.errorMessage = 'هیچ دریافتی ثبت نشده است.'
return
}
if (errors.length > 0) { if (errors.length > 0) {
this.errorMessage = errors.join('\n') this.errorMessage = errors.join('\n')
this.errorDialog = true
return return
} }
@ -305,16 +416,22 @@ export default defineComponent({
return element return element
}) })
rows.push({ if (!this.person) {
throw new Error('شخص انتخاب نشده است')
}
const personRow = {
id: this.person, id: this.person,
type: 'person', type: 'person',
bd: 0, bd: 0,
bs: this.totalPays, bs: this.totalPays,
table: 3, table: 3,
des: `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc)}` des: `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc || '')}`
}) }
this.des = this.des || `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc)}` rows.push(personRow)
this.des = this.des || `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc || '')}`
try { try {
const response = await axios.post('/api/accounting/insert', { const response = await axios.post('/api/accounting/insert', {
@ -364,4 +481,68 @@ export default defineComponent({
.v-card.success { .v-card.success {
background-color: #4caf50 !important; background-color: #4caf50 !important;
} }
/* استایل‌های جدید برای کارت‌ها */
.v-card {
transition: all 0.3s ease;
width: 100%;
max-width: 100%;
overflow-x: hidden;
}
.v-card-text {
overflow-x: hidden;
padding: 16px;
}
/* رنگ‌های متفاوت برای انواع مختلف کارت‌ها */
.v-card.bank-card {
border-right: 4px solid #1976d2;
}
.v-card.cashdesk-card {
border-right: 4px solid #4caf50;
}
.v-card.salary-card {
border-right: 4px solid #ff9800;
}
.v-card.cheque-card {
border-right: 4px solid #9c27b0;
}
.v-card-item {
background-color: rgba(0, 0, 0, 0.02);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.v-btn-group .v-btn {
margin: 0 2px;
}
/* استایل‌های جدید برای رفع مشکل اسکرول */
.mobile-dialog {
overflow: hidden !important;
}
.mobile-dialog :deep(.v-card) {
height: 100vh !important;
max-height: 100vh !important;
}
.mobile-dialog :deep(.v-card-text) {
padding: 8px !important;
overflow-y: auto !important;
height: calc(100vh - 64px) !important; /* 64px ارتفاع toolbar */
}
.mobile-dialog :deep(.v-overlay__content) {
width: 100% !important;
height: 100% !important;
}
:deep(.v-card-text .container) {
max-width: 100% !important;
}
</style> </style>

View file

@ -83,8 +83,8 @@
<v-col cols="12"> <v-col cols="12">
<v-card> <v-card>
<v-card-title>{{ $t('marketing.recent_users') }}</v-card-title> <v-card-title>{{ $t('marketing.recent_users') }}</v-card-title>
<v-data-table :header-props="{ class: 'custom-header' }" :headers="computedHeaders" :items="recentUsers" :loading="loading" hide-default-footer <v-data-table :header-props="{ class: 'custom-header' }" :headers="computedHeaders"
class="elevation-1"> :items="recentUsers" :loading="loading" hide-default-footer class="elevation-1">
<!-- اسلات سفارشی برای ستون وضعیت --> <!-- اسلات سفارشی برای ستون وضعیت -->
<template v-slot:item.active="{ item }"> <template v-slot:item.active="{ item }">
<v-icon :color="item.active ? 'green' : 'red'" small> <v-icon :color="item.active ? 'green' : 'red'" small>
@ -357,7 +357,7 @@ export default {
}, },
}, },
async mounted() { async mounted() {
try {
const res = await axios.post("/api/user/current/info"); const res = await axios.post("/api/user/current/info");
this.user_email = res.data.email; this.user_email = res.data.email;
this.user_fullname = res.data.fullname; this.user_fullname = res.data.fullname;
@ -367,7 +367,7 @@ export default {
? `${window.location.origin + getBasePath()}ms/${this.referralCode}` ? `${window.location.origin + getBasePath()}ms/${this.referralCode}`
: ""; : "";
await this.fetchMarketingData(); await this.fetchMarketingData();
try {} catch (error) { } catch (error) {
this.errorMessage = this.errorMessage =
error.response?.data?.message || this.$t("dialog.error_unknown"); error.response?.data?.message || this.$t("dialog.error_unknown");
this.errorDialog = true; this.errorDialog = true;