feat(payments): automatically notify admin and show existing invoice details when selecting student

This commit is contained in:
2026-08-23 16:55:56 +03:30
parent 966b066358
commit 7cb2b957a7
+51 -12
View File
@@ -119,15 +119,28 @@
<!-- Duplicate Payment Warning -->
<div
v-if="duplicateWarning && duplicateWarning.hasDuplicate"
class="p-3 border-round flex align-items-start gap-2 duplicate-warning-box"
class="p-3 border-round flex flex-column gap-2 duplicate-warning-box"
>
<i class="pi pi-exclamation-triangle text-xl text-yellow-400 mt-1 flex-shrink-0"></i>
<div class="flex flex-column gap-1">
<span class="font-bold text-yellow-300 text-sm">هشدار: برای این دانشجو قبلاً در این کلاس صورتحساب ثبت شده است.</span>
<span class="text-xs text-yellow-200" v-if="duplicateWarning.payments && duplicateWarning.payments.length">
صورتحساب قبلی با کد {{ duplicateWarning.payments.map(p => p.uniqueCode || p._id).join('، ') }} موجود است.
<div class="flex align-items-center gap-2">
<i class="pi pi-exclamation-triangle text-xl text-yellow-500 flex-shrink-0"></i>
<span class="font-bold text-sm text-yellow-800 dark:text-yellow-200">
توجه: برای این دانشجو قبلاً در این کلاس صورتحساب ثبت شده است.
</span>
</div>
<div class="flex flex-column gap-2 mt-1 text-xs" v-if="duplicateWarning.payments && duplicateWarning.payments.length">
<div
v-for="p in duplicateWarning.payments"
:key="p._id"
class="duplicate-item p-2 border-round flex flex-wrap align-items-center justify-content-between gap-2"
>
<div class="flex align-items-center gap-2 flex-wrap">
<span>کد صورتحساب: <strong>{{ p.uniqueCode || p._id }}</strong></span>
<span v-if="p.amount">مبلغ: <strong>{{ toPersianDigits((p.amount || 0).toLocaleString()) }} تومان</strong></span>
<span v-if="p.createdAt" class="text-color-secondary">تاریخ: {{ formatJalali(p.createdAt) }}</span>
</div>
<StatusTag v-if="p.status" :value="p.status" />
</div>
</div>
</div>
<div class="flex flex-column gap-2">
@@ -316,7 +329,7 @@ import DatePicker from 'vue3-persian-datetime-picker';
const { toPersianDigits, formatJalali, toGregorianIso, getTodayJalali } = usePersianDate();
const { showSuccess, showError } = useToast();
const { showSuccess, showError, showWarn } = useToast();
const {
items,
@@ -334,6 +347,7 @@ const usersList = ref([]);
const classesList = ref([]);
const isCreating = ref(false);
const duplicateWarning = ref(null);
let lastDuplicateAlertKey = '';
const createForm = reactive({
user: null,
@@ -418,24 +432,43 @@ const checkDuplicateInvoice = async (userIdVal = null) => {
const targetUserId = extractId(userIdVal) || extractId(createForm.user);
if (!targetUserId || !createForm.classes || !createForm.classes.length) {
duplicateWarning.value = null;
lastDuplicateAlertKey = '';
return;
}
try {
const classIds = Array.isArray(createForm.classes)
? createForm.classes.map(extractId).filter(Boolean).join(',')
? createForm.classes.map(extractId).filter(Boolean).sort().join(',')
: (extractId(createForm.classes) || '');
if (!classIds) {
duplicateWarning.value = null;
lastDuplicateAlertKey = '';
return;
}
const checkKey = `${targetUserId}_${classIds}`;
const res = await paymentApi.checkDuplicate({
userId: targetUserId,
classes: classIds
});
duplicateWarning.value = res.data?.data || res.data || res;
const result = res.data?.data || res.data || res;
duplicateWarning.value = result;
if (result && result.hasDuplicate) {
if (lastDuplicateAlertKey !== checkKey) {
lastDuplicateAlertKey = checkKey;
const studentObj = usersList.value.find((u) => String(u._id || u.id) === String(targetUserId));
const studentName = studentObj?.name || studentObj?.fullName || 'این دانشجو';
const codes = (result.payments || []).map((p) => p.uniqueCode || p._id).filter(Boolean).join('، ');
const codeSuffix = codes ? ` (کد: ${codes})` : '';
showWarn(`برای ${studentName} قبلاً در این کلاس صورتحساب ثبت شده است${codeSuffix}.`, 'اطلاعیه صورتحساب قبلی');
}
} else {
lastDuplicateAlertKey = '';
}
} catch (err) {
console.error('checkDuplicateInvoice error:', err);
duplicateWarning.value = null;
lastDuplicateAlertKey = '';
}
};
@@ -446,6 +479,7 @@ watch(
await checkDuplicateInvoice(newUser);
} else {
duplicateWarning.value = null;
lastDuplicateAlertKey = '';
}
},
{ deep: true, immediate: true }
@@ -507,6 +541,7 @@ const resetCreateForm = () => {
createForm.dueDate = getTodayJalali();
hasDiscount.value = false;
duplicateWarning.value = null;
lastDuplicateAlertKey = '';
createNotify.sms = true;
createNotify.email = true;
createNotify.bot = true;
@@ -691,8 +726,12 @@ onMounted(() => {
<style scoped>
.duplicate-warning-box {
background: rgba(234, 179, 8, 0.15) !important;
border: 1px solid rgba(234, 179, 8, 0.6) !important;
color: #fef08a !important;
background: rgba(245, 158, 11, 0.12) !important;
border: 1px solid rgba(245, 158, 11, 0.45) !important;
}
.duplicate-item {
background: rgba(245, 158, 11, 0.08);
border: 1px dashed rgba(245, 158, 11, 0.35);
}
</style>