269 lines
6.9 KiB
Vue
269 lines
6.9 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="approval-manager">
|
|||
|
|
<!-- دکمه تأیید -->
|
|||
|
|
<v-btn
|
|||
|
|
v-if="showApprovalButton"
|
|||
|
|
:color="approvalButtonColor"
|
|||
|
|
size="small"
|
|||
|
|
variant="outlined"
|
|||
|
|
@click="showApprovalDialog = true"
|
|||
|
|
:loading="processing"
|
|||
|
|
:disabled="processing"
|
|||
|
|
>
|
|||
|
|
<v-icon size="small" class="me-1">
|
|||
|
|
{{ approvalButtonIcon }}
|
|||
|
|
</v-icon>
|
|||
|
|
{{ approvalButtonText }}
|
|||
|
|
</v-btn>
|
|||
|
|
|
|||
|
|
<!-- دکمه رد -->
|
|||
|
|
<v-btn
|
|||
|
|
v-if="showRejectButton"
|
|||
|
|
color="error"
|
|||
|
|
size="small"
|
|||
|
|
variant="outlined"
|
|||
|
|
@click="showRejectDialog = true"
|
|||
|
|
:loading="processing"
|
|||
|
|
:disabled="processing"
|
|||
|
|
class="ms-2"
|
|||
|
|
>
|
|||
|
|
<v-icon size="small" class="me-1">mdi-close</v-icon>
|
|||
|
|
رد سند
|
|||
|
|
</v-btn>
|
|||
|
|
|
|||
|
|
<!-- دیالوگ تأیید -->
|
|||
|
|
<v-dialog v-model="showApprovalDialog" max-width="500">
|
|||
|
|
<v-card>
|
|||
|
|
<v-card-title class="text-h6">
|
|||
|
|
<v-icon color="success" class="me-2">mdi-check-circle</v-icon>
|
|||
|
|
تأیید سند
|
|||
|
|
</v-card-title>
|
|||
|
|
|
|||
|
|
<v-card-text>
|
|||
|
|
<p>آیا از تأیید این سند اطمینان دارید؟</p>
|
|||
|
|
<div class="mt-3">
|
|||
|
|
<strong>نوع سند:</strong> {{ documentTypeText }}
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-1">
|
|||
|
|
<strong>شماره سند:</strong> {{ documentNumber }}
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-1">
|
|||
|
|
<strong>مبلغ:</strong> {{ formatCurrency(document.amount || 0) }}
|
|||
|
|
</div>
|
|||
|
|
</v-card-text>
|
|||
|
|
|
|||
|
|
<v-card-actions>
|
|||
|
|
<v-spacer></v-spacer>
|
|||
|
|
<v-btn color="grey" @click="showApprovalDialog = false">انصراف</v-btn>
|
|||
|
|
<v-btn
|
|||
|
|
color="success"
|
|||
|
|
@click="handleApproval"
|
|||
|
|
:loading="processing"
|
|||
|
|
>
|
|||
|
|
تأیید
|
|||
|
|
</v-btn>
|
|||
|
|
</v-card-actions>
|
|||
|
|
</v-card>
|
|||
|
|
</v-dialog>
|
|||
|
|
|
|||
|
|
<!-- دیالوگ رد -->
|
|||
|
|
<v-dialog v-model="showRejectDialog" max-width="500">
|
|||
|
|
<v-card>
|
|||
|
|
<v-card-title class="text-h6">
|
|||
|
|
<v-icon color="error" class="me-2">mdi-close-circle</v-icon>
|
|||
|
|
رد سند
|
|||
|
|
</v-card-title>
|
|||
|
|
|
|||
|
|
<v-card-text>
|
|||
|
|
<p>لطفاً دلیل رد این سند را وارد کنید:</p>
|
|||
|
|
<v-textarea
|
|||
|
|
v-model="rejectionReason"
|
|||
|
|
label="دلیل رد"
|
|||
|
|
variant="outlined"
|
|||
|
|
rows="3"
|
|||
|
|
:rules="[v => !!v || 'دلیل رد الزامی است']"
|
|||
|
|
required
|
|||
|
|
></v-textarea>
|
|||
|
|
</v-card-text>
|
|||
|
|
|
|||
|
|
<v-card-actions>
|
|||
|
|
<v-spacer></v-spacer>
|
|||
|
|
<v-btn color="grey" @click="showRejectDialog = false">انصراف</v-btn>
|
|||
|
|
<v-btn
|
|||
|
|
color="error"
|
|||
|
|
@click="handleRejection"
|
|||
|
|
:loading="processing"
|
|||
|
|
:disabled="!rejectionReason"
|
|||
|
|
>
|
|||
|
|
رد سند
|
|||
|
|
</v-btn>
|
|||
|
|
</v-card-actions>
|
|||
|
|
</v-card>
|
|||
|
|
</v-dialog>
|
|||
|
|
|
|||
|
|
<!-- Snackbar برای نمایش پیامها -->
|
|||
|
|
<v-snackbar
|
|||
|
|
v-model="showSnackbar"
|
|||
|
|
:color="snackbarColor"
|
|||
|
|
:timeout="3000"
|
|||
|
|
location="bottom"
|
|||
|
|
>
|
|||
|
|
{{ snackbarText }}
|
|||
|
|
<template v-slot:actions>
|
|||
|
|
<v-btn icon variant="text" @click="showSnackbar = false">
|
|||
|
|
<v-icon>mdi-close</v-icon>
|
|||
|
|
</v-btn>
|
|||
|
|
</template>
|
|||
|
|
</v-snackbar>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { ref, computed } from 'vue'
|
|||
|
|
import {
|
|||
|
|
shouldShowApprovalButton,
|
|||
|
|
getApprovalButtonText,
|
|||
|
|
getDocumentType
|
|||
|
|
} from '@/utils/approvalUtils'
|
|||
|
|
|
|||
|
|
const props = defineProps({
|
|||
|
|
document: {
|
|||
|
|
type: Object,
|
|||
|
|
required: true
|
|||
|
|
},
|
|||
|
|
businessSettings: {
|
|||
|
|
type: Object,
|
|||
|
|
required: true
|
|||
|
|
},
|
|||
|
|
currentUserEmail: {
|
|||
|
|
type: String,
|
|||
|
|
required: true
|
|||
|
|
},
|
|||
|
|
isBusinessOwner: {
|
|||
|
|
type: Boolean,
|
|||
|
|
default: false
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const emit = defineEmits(['approve', 'reject'])
|
|||
|
|
|
|||
|
|
const showApprovalDialog = ref(false)
|
|||
|
|
const showRejectDialog = ref(false)
|
|||
|
|
const rejectionReason = ref('')
|
|||
|
|
const processing = ref(false)
|
|||
|
|
const showSnackbar = ref(false)
|
|||
|
|
const snackbarText = ref('')
|
|||
|
|
const snackbarColor = ref('success')
|
|||
|
|
|
|||
|
|
// محاسبه نمایش دکمهها
|
|||
|
|
const showApprovalButton = computed(() => {
|
|||
|
|
return shouldShowApprovalButton(
|
|||
|
|
props.businessSettings,
|
|||
|
|
props.document,
|
|||
|
|
props.currentUserEmail,
|
|||
|
|
props.isBusinessOwner
|
|||
|
|
)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const showRejectButton = computed(() => {
|
|||
|
|
// فقط مدیر کسب و کار میتواند سند را رد کند
|
|||
|
|
return props.isBusinessOwner && props.document.approved !== false
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// محاسبه متن و رنگ دکمه تأیید
|
|||
|
|
const approvalButtonText = computed(() => getApprovalButtonText(props.document))
|
|||
|
|
const approvalButtonColor = computed(() => {
|
|||
|
|
if (props.document.approved === false) return 'warning'
|
|||
|
|
return 'success'
|
|||
|
|
})
|
|||
|
|
const approvalButtonIcon = computed(() => {
|
|||
|
|
if (props.document.approved === false) return 'mdi-refresh'
|
|||
|
|
return 'mdi-check'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// محاسبه نوع سند
|
|||
|
|
const documentType = computed(() => getDocumentType(props.document))
|
|||
|
|
const documentTypeText = computed(() => {
|
|||
|
|
switch (documentType.value) {
|
|||
|
|
case 'invoice':
|
|||
|
|
return 'فاکتور فروش'
|
|||
|
|
case 'warehouse':
|
|||
|
|
return 'حواله انبار'
|
|||
|
|
case 'financial':
|
|||
|
|
return 'سند مالی'
|
|||
|
|
default:
|
|||
|
|
return 'سند'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const documentNumber = computed(() => {
|
|||
|
|
return props.document.invoiceNumber ||
|
|||
|
|
props.document.warehouseNumber ||
|
|||
|
|
props.document.documentNumber ||
|
|||
|
|
props.document.id ||
|
|||
|
|
'نامشخص'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// مدیریت تأیید
|
|||
|
|
const handleApproval = async () => {
|
|||
|
|
try {
|
|||
|
|
processing.value = true
|
|||
|
|
showApprovalDialog.value = false
|
|||
|
|
|
|||
|
|
await emit('approve', props.document)
|
|||
|
|
|
|||
|
|
showSnackbarText('سند با موفقیت تأیید شد', 'success')
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('خطا در تأیید سند:', error)
|
|||
|
|
showSnackbarText('خطا در تأیید سند', 'error')
|
|||
|
|
} finally {
|
|||
|
|
processing.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// مدیریت رد
|
|||
|
|
const handleRejection = async () => {
|
|||
|
|
try {
|
|||
|
|
processing.value = true
|
|||
|
|
showRejectDialog.value = false
|
|||
|
|
|
|||
|
|
await emit('reject', {
|
|||
|
|
document: props.document,
|
|||
|
|
reason: rejectionReason.value
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
rejectionReason.value = ''
|
|||
|
|
showSnackbarText('سند با موفقیت رد شد', 'success')
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('خطا در رد سند:', error)
|
|||
|
|
showSnackbarText('خطا در رد سند', 'error')
|
|||
|
|
} finally {
|
|||
|
|
processing.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// نمایش پیام
|
|||
|
|
const showSnackbarText = (text, color = 'success') => {
|
|||
|
|
snackbarText.value = text
|
|||
|
|
snackbarColor.value = color
|
|||
|
|
showSnackbar.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// فرمت مبلغ
|
|||
|
|
const formatCurrency = (amount) => {
|
|||
|
|
return new Intl.NumberFormat('fa-IR').format(amount) + ' ریال'
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.approval-manager {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.approval-manager .v-btn {
|
|||
|
|
text-transform: none;
|
|||
|
|
}
|
|||
|
|
</style>
|