hesabixCore/webUI/src/views/acc/component/rec.vue

367 lines
14 KiB
Vue
Raw Normal View History

2025-03-30 00:06:17 +03:30
<template>
2025-03-31 02:05:53 +03:30
<v-btn v-if="totalAmount > 0" icon color="error" class="ml-2" @click="dialog = true">
2025-03-30 00:06:17 +03:30
<v-icon>mdi-cash</v-icon>
<v-tooltip activator="parent" location="bottom">ثبت دریافت</v-tooltip>
</v-btn>
<v-dialog v-model="dialog" max-width="950" persistent>
<v-card>
<v-toolbar color="toolbar" flat>
<v-toolbar-title>
<v-icon color="error" left>mdi-cash</v-icon>
ثبت دریافت
</v-toolbar-title>
<v-spacer></v-spacer>
<v-menu bottom>
<template v-slot:activator="{ props }">
<v-btn icon color="success" v-bind="props" :disabled="loading">
<v-icon>mdi-plus</v-icon>
<v-tooltip activator="parent" location="bottom">افزودن دریافت</v-tooltip>
</v-btn>
</template>
<v-list>
<v-list-item @click="addItem('bank')">
<v-list-item-title>حساب بانکی</v-list-item-title>
</v-list-item>
<v-list-item @click="addItem('cashdesk')">
<v-list-item-title>صندوق</v-list-item-title>
</v-list-item>
<v-list-item @click="addItem('salary')">
<v-list-item-title>تنخواه گردان</v-list-item-title>
</v-list-item>
<v-list-item @click="addItem('cheque')">
<v-list-item-title>چک</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-btn icon color="primary" @click="submit" :disabled="loading">
<v-icon>mdi-content-save</v-icon>
<v-tooltip activator="parent" location="bottom">ثبت</v-tooltip>
</v-btn>
<v-btn icon @click="dialog = false" :disabled="loading">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<v-row>
<v-col cols="12" md="5">
<Hdatepicker v-model="date" label="تاریخ" />
</v-col>
<v-col cols="12" md="7">
2025-03-31 02:05:53 +03:30
<v-text-field v-model="des" label="شرح" outlined clearable class="mb-4"></v-text-field>
2025-03-30 00:06:17 +03:30
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
2025-03-31 02:05:53 +03:30
<v-text-field v-model="formattedTotalPays" label="مجموع" readonly outlined></v-text-field>
2025-03-30 00:06:17 +03:30
</v-col>
<v-col cols="12" md="6">
2025-03-31 02:05:53 +03:30
<v-text-field v-model="formattedRemainingAmount" label="باقی مانده" readonly outlined></v-text-field>
2025-03-30 00:06:17 +03:30
</v-col>
</v-row>
2025-03-31 02:05:53 +03:30
<v-data-table :headers="headers" :items="items" :loading="loading" class="elevation-1 mt-2"
:header-props="{ class: 'custom-header' }" :items-per-page="-1" hide-default-footer>
2025-03-30 00:06:17 +03:30
<template v-slot:item.type="{ item }">
<v-icon v-if="item.type === 'bank'">mdi-bank</v-icon>
<v-icon v-if="item.type === 'cashdesk'">mdi-cash-register</v-icon>
<v-icon v-if="item.type === 'salary'">mdi-wallet</v-icon>
<v-icon v-if="item.type === 'cheque'">mdi-checkbook</v-icon>
</template>
<template v-slot:item.selection="{ item }">
2025-03-31 02:05:53 +03:30
<v-select v-if="item.type === 'bank'" v-model="item.bank" :items="listBanks" item-title="name" return-object
label="بانک"></v-select>
<v-select v-if="item.type === 'cashdesk'" v-model="item.cashdesk" :items="listCashdesks" item-title="name"
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>
2025-03-30 00:06:17 +03:30
<template v-if="item.type === 'cheque'">
2025-03-31 02:05:53 +03:30
<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 />
2025-03-30 00:06:17 +03:30
</template>
</template>
<template v-slot:item.bd="{ item }">
2025-03-31 02:05:53 +03:30
<Hnumberinput v-model="item.bd" label="مبلغ" placeholder="0" @update:modelValue="calc" />
2025-03-30 00:06:17 +03:30
</template>
<template v-slot:item.referral="{ item }">
2025-03-31 02:05:53 +03:30
<v-text-field v-model="item.referral" label="ارجاع"></v-text-field>
2025-03-30 00:06:17 +03:30
</template>
<template v-slot:item.des="{ item }">
2025-03-31 02:05:53 +03:30
<v-text-field v-model="item.des" label="شرح"></v-text-field>
2025-03-30 00:06:17 +03:30
</template>
<template v-slot:item.actions="{ item, index }">
2025-03-31 02:05:53 +03:30
<v-btn variant="plain" color="primary" @click="fillWithTotal(item)">
2025-03-30 00:06:17 +03:30
<v-icon>mdi-cash-100</v-icon>
<v-tooltip activator="parent" location="bottom">کل فاکتور</v-tooltip>
</v-btn>
2025-03-31 02:05:53 +03:30
<v-btn variant="plain" color="error" @click="deleteItem(index)">
2025-03-30 00:06:17 +03:30
<v-icon>mdi-trash-can</v-icon>
<v-tooltip activator="parent" location="bottom">حذف</v-tooltip>
</v-btn>
</template>
<template v-slot:no-data>
<v-card-text class="text-center">
هیچ دریافتی ثبت نشده است.
</v-card-text>
</template>
</v-data-table>
</v-card-text>
<v-overlay :model-value="loading" contained class="align-center justify-center">
<v-progress-circular indeterminate size="64" color="primary"></v-progress-circular>
</v-overlay>
</v-card>
</v-dialog>
2025-03-31 02:05:53 +03:30
<v-dialog v-model="successDialog" max-width="400">
<v-card color="success">
<v-card-text class="text-center pa-4">
<v-icon size="large" color="white" class="mb-4">mdi-check-circle</v-icon>
<div class="text-h6 text-white mb-2">ثبت موفق</div>
<div class="text-white">{{ successMessage }}</div>
</v-card-text>
</v-card>
</v-dialog>
2025-03-30 00:06:17 +03:30
</template>
2025-03-21 14:20:43 +03:30
<script lang="ts">
2025-03-30 00:06:17 +03:30
import { defineComponent, ref } from 'vue'
import axios from 'axios'
import { VDataTable } from 'vuetify/components'
import Hdatepicker from '@/components/forms/Hdatepicker.vue'
import Hnumberinput from '@/components/forms/Hnumberinput.vue'
2025-03-21 14:20:43 +03:30
export default defineComponent({
2025-03-30 00:06:17 +03:30
name: 'Rec',
components: {
VDataTable,
Hdatepicker,
Hnumberinput
},
2025-03-21 14:20:43 +03:30
props: {
totalAmount: Number,
originalDoc: String,
person: [String, Number],
2025-03-30 00:06:17 +03:30
windowsState: Object
2025-03-21 14:20:43 +03:30
},
2025-03-30 00:06:17 +03:30
setup() {
const dialog = ref(false)
return { dialog }
},
data: () => ({
submitedDoc: {},
des: '',
items: [],
date: '',
listBanks: [],
listSalarys: [],
listCashdesks: [],
totalPays: 0,
loading: false,
2025-03-31 02:05:53 +03:30
errorMessage: '',
successDialog: false,
successMessage: ''
2025-03-30 00:06:17 +03:30
}),
computed: {
headers() {
return [
{ title: 'نوع', key: 'type', sortable: false, width: '5%' },
{ title: 'انتخاب', key: 'selection', sortable: false, width: '25%' },
{ title: 'مبلغ', key: 'bd', sortable: false, width: '20%' },
{ title: 'ارجاع', key: 'referral', sortable: false, width: '15%' },
{ title: 'شرح', key: 'des', sortable: false, width: '25%' },
{ title: 'عملیات', key: 'actions', sortable: false, width: '10%' }
]
},
remainingAmount() {
return this.totalAmount - this.totalPays
},
formattedTotalPays() {
2025-03-31 02:05:53 +03:30
return this.formatNumber(this.totalPays) || '۰';
2025-03-30 00:06:17 +03:30
},
formattedRemainingAmount() {
2025-03-31 02:05:53 +03:30
return this.formatNumber(this.remainingAmount) || '۰';
2025-03-21 14:20:43 +03:30
}
},
methods: {
2025-03-30 00:06:17 +03:30
formatNumber(value: number | string): string {
2025-03-31 02:05:53 +03:30
if (value === undefined || value === null || value === '') return '۰';
const num = parseInt(value.toString().replace(/[^\d]/g, ''));
return num.toLocaleString('fa-IR') || '۰';
2025-03-30 00:06:17 +03:30
},
2025-03-21 14:20:43 +03:30
fillWithTotal(pay) {
2025-03-30 00:06:17 +03:30
pay.bd = this.totalAmount - this.totalPays
this.calc()
2025-03-21 14:20:43 +03:30
},
addItem(type) {
2025-03-30 00:06:17 +03:30
let obj = {}
let canAdd = true
const uniqueId = Date.now() + Math.random().toString(36).substr(2, 9)
if (type === 'bank') {
if (this.listBanks.length === 0) {
this.errorMessage = 'ابتدا یک حساب بانکی ایجاد کنید.'
canAdd = false
} else {
obj = { uniqueId, id: '', type: 'bank', bank: null, cashdesk: {}, salary: {}, bs: 0, bd: 0, des: '', table: 5, referral: '' }
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
} else if (type === 'cashdesk') {
if (this.listCashdesks.length === 0) {
this.errorMessage = 'ابتدا یک صندوق ایجاد کنید.'
canAdd = false
} else {
obj = { uniqueId, id: '', type: 'cashdesk', bank: {}, cashdesk: null, salary: {}, bs: 0, bd: 0, des: '', table: 121, referral: '' }
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
} else if (type === 'salary') {
if (this.listSalarys.length === 0) {
this.errorMessage = 'ابتدا یک تنخواه گردان ایجاد کنید.'
canAdd = false
} else {
obj = { uniqueId, id: '', type: 'salary', bank: {}, cashdesk: {}, salary: null, bs: 0, bd: 0, des: '', table: 122, referral: '' }
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
} else if (type === 'cheque') {
2025-03-21 14:20:43 +03:30
obj = {
2025-03-30 00:06:17 +03:30
uniqueId,
id: '', type: 'cheque', bank: {}, cashdesk: {}, salary: {}, bs: 0, bd: 0, des: '', table: 125, referral: '',
chequeBank: '', chequeDate: '', chequeSayadNum: '', chequeNum: '', chequeType: 'input', chequeOwner: this.person
2025-03-21 14:20:43 +03:30
}
}
2025-03-30 00:06:17 +03:30
2025-03-21 14:20:43 +03:30
if (canAdd) {
2025-03-30 00:06:17 +03:30
this.items.push(obj)
this.errorMessage = ''
2025-03-21 14:20:43 +03:30
}
},
2025-03-30 00:06:17 +03:30
deleteItem(index) {
this.items.splice(index, 1)
this.calc()
2025-03-21 14:20:43 +03:30
},
2025-03-30 00:06:17 +03:30
async loadData() {
this.loading = true
try {
const [banks, salarys, cashdesks] = await Promise.all([
axios.post('/api/bank/list'),
axios.post('/api/salary/list'),
axios.post('/api/cashdesk/list')
])
this.listBanks = banks.data
this.listSalarys = salarys.data
this.listCashdesks = cashdesks.data
} catch (error) {
this.errorMessage = 'خطا در بارگذاری اطلاعات'
} finally {
this.loading = false
}
2025-03-21 14:20:43 +03:30
},
calc() {
2025-03-30 00:06:17 +03:30
this.totalPays = this.items.reduce((sum, item) => {
const bd = item.bd !== null && item.bd !== undefined ? Number(item.bd) : 0
return sum + bd
}, 0)
2025-03-21 14:20:43 +03:30
},
async submit() {
2025-03-30 00:06:17 +03:30
let errors = []
if (this.totalAmount < this.totalPays) {
errors.push('مبالغ وارد شده بیشتر از مبلغ فاکتور است.')
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
2025-03-21 14:20:43 +03:30
this.items.forEach((element, index) => {
2025-03-30 00:06:17 +03:30
if (element.bd === 0) errors.push(`مبلغ صفر در ردیف ${index + 1} نا معتبر است.`)
if (element.type === 'cheque' && (!element.chequeBank || !element.chequeNum || !element.chequeDate)) {
errors.push(`موارد الزامی ثبت چک در ردیف ${index + 1} وارد نشده است.`)
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
if (element.type === 'bank' && (!element.bank || !Object.keys(element.bank).length)) {
errors.push(`بانک در ردیف ${index + 1} انتخاب نشده است.`)
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
if (element.type === 'salary' && (!element.salary || !Object.keys(element.salary).length)) {
errors.push(`تنخواه گردان در ردیف ${index + 1} انتخاب نشده است.`)
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
if (element.type === 'cashdesk' && (!element.cashdesk || !Object.keys(element.cashdesk).length)) {
errors.push(`صندوق در ردیف ${index + 1} انتخاب نشده است.`)
2025-03-21 14:20:43 +03:30
}
})
2025-03-30 00:06:17 +03:30
if (this.items.length === 0) {
this.errorMessage = 'هیچ دریافتی ثبت نشده است.'
return
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
if (errors.length > 0) {
this.errorMessage = errors.join('\n')
return
2025-03-21 14:20:43 +03:30
}
2025-03-30 00:06:17 +03:30
this.loading = true
this.errorMessage = ''
const rows = [...this.items].map(element => {
if (element.type === 'bank') element.id = element.bank.id
else if (element.type === 'salary') element.id = element.salary.id
else if (element.type === 'cashdesk') element.id = element.cashdesk.id
element.des = element.des || `دریافت وجه فاکتور شماره ${this.originalDoc}`
return element
})
2025-03-21 14:20:43 +03:30
2025-03-30 00:06:17 +03:30
rows.push({
id: this.person,
type: 'person',
bd: 0,
bs: this.totalPays,
table: 3,
des: `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc)}`
})
this.des = this.des || `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc)}`
try {
const response = await axios.post('/api/accounting/insert', {
2025-03-21 14:20:43 +03:30
date: this.date,
des: this.des,
type: 'sell_receive',
update: null,
2025-03-30 00:06:17 +03:30
rows,
related: this.originalDoc
2025-03-21 14:20:43 +03:30
})
2025-03-30 00:06:17 +03:30
2025-03-31 02:05:53 +03:30
if (response.data.result === 1) {
2025-03-30 00:06:17 +03:30
this.submitedDoc = response.data.doc
2025-03-31 02:05:53 +03:30
if (this.windowsState) {
this.windowsState.submited = true
}
this.successMessage = 'سند دریافت با موفقیت ثبت شد'
this.successDialog = true
setTimeout(() => {
this.successDialog = false
this.dialog = false
}, 2000)
this.$emit('submit-success', response.data.doc)
} else {
this.errorMessage = response.data.msg || 'خطا در ثبت سند'
2025-03-30 00:06:17 +03:30
}
} catch (error) {
2025-03-31 02:05:53 +03:30
this.errorMessage = error.response?.data?.message || 'خطا در ثبت سند'
2025-03-30 00:06:17 +03:30
} finally {
this.loading = false
2025-03-21 14:20:43 +03:30
}
}
},
2025-03-31 02:05:53 +03:30
async mounted() {
await this.loadData()
const response = await axios.get('/api/year/get')
this.date = response.data.now // تاریخ جاری شمسی
2025-03-21 14:20:43 +03:30
}
})
</script>
2025-03-30 00:06:17 +03:30
<style scoped>
2025-03-31 02:05:53 +03:30
.v-card.success {
background-color: #4caf50 !important;
}
2025-03-30 00:06:17 +03:30
</style>