Require review before Tahesab wallet sync and harden accounting admin UX.
Single-customer and row sync preview balances with documents before overwrite/keep; bulk sync keeps confirm. Also ship Zarin wallet sync actions, field help, and get_user cache improvements. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-model:visible="visibleProxy"
|
||||
:width="isMobile ? '92vw' : '28rem'"
|
||||
:width="isMobile ? '92vw' : '36rem'"
|
||||
title="تصمیم برای مانده حساب"
|
||||
:custom-footer="true"
|
||||
cancel-text="بعداً"
|
||||
@@ -9,7 +9,7 @@
|
||||
>
|
||||
<div class="flex flex-col gap-3 text-sm" style="color: var(--admin-text)">
|
||||
<p class="m-0" style="color: var(--admin-text-secondary)">
|
||||
موجودی اپ با مانده تهحساب فرق دارد. کدام را اعمال کنیم؟
|
||||
موجودی اپ با مانده {{ sourceLabel }} فرق دارد. دو مانده جمع نمیشوند؛ یکی را پس از بررسی سندها تأیید کنید.
|
||||
</p>
|
||||
<div class="rounded-lg border border-base p-3 space-y-2" style="background: var(--admin-bg-muted)">
|
||||
<div class="flex justify-between gap-2">
|
||||
@@ -17,8 +17,12 @@
|
||||
<strong dir="ltr">{{ formatGold(appGold) }}</strong>
|
||||
</div>
|
||||
<div class="flex justify-between gap-2">
|
||||
<span style="color: var(--admin-text-secondary)">مانده تهحساب (طلا)</span>
|
||||
<strong dir="ltr">{{ formatGold(tahesabGold) }}</strong>
|
||||
<span style="color: var(--admin-text-secondary)">مانده {{ sourceLabel }} (طلا)</span>
|
||||
<strong dir="ltr">{{ formatGold(sourceGoldValue) }}</strong>
|
||||
</div>
|
||||
<div class="flex justify-between gap-2">
|
||||
<span style="color: var(--admin-text-secondary)">اختلاف طلا</span>
|
||||
<strong dir="ltr">{{ formatDeltaGold(goldDeltaValue) }}</strong>
|
||||
</div>
|
||||
<div class="h-px" style="background: var(--admin-border)" />
|
||||
<div class="flex justify-between gap-2">
|
||||
@@ -26,13 +30,62 @@
|
||||
<strong dir="ltr">{{ formatCash(appCash) }}</strong>
|
||||
</div>
|
||||
<div class="flex justify-between gap-2">
|
||||
<span style="color: var(--admin-text-secondary)">مانده تهحساب (ریال)</span>
|
||||
<strong dir="ltr">{{ formatCash(tahesabCash) }}</strong>
|
||||
<span style="color: var(--admin-text-secondary)">مانده {{ sourceLabel }} (ریال)</span>
|
||||
<strong dir="ltr">{{ formatCash(sourceCashValue) }}</strong>
|
||||
</div>
|
||||
<div class="flex justify-between gap-2">
|
||||
<span style="color: var(--admin-text-secondary)">اختلاف ریال</span>
|
||||
<strong dir="ltr">{{ formatDeltaCash(rialDeltaValue) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showDocs" class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<div class="rounded-lg border border-base p-2.5 min-w-0">
|
||||
<p class="m-0 mb-1.5 text-xs font-600">سندهای کیف اپ</p>
|
||||
<p class="m-0 mb-1.5 text-xs" style="color: var(--admin-text-secondary)">آخرین تراکنشهای کیف اصلی</p>
|
||||
<div v-if="walletDocs.length" class="max-h-32 overflow-y-auto flex flex-col gap-1.5">
|
||||
<div
|
||||
v-for="doc in walletDocs"
|
||||
:key="'w-' + doc.id"
|
||||
class="rounded-md border border-base px-2 py-1.5 text-xs"
|
||||
>
|
||||
<div class="font-600 truncate">{{ doc.title || 'تراکنش' }}</div>
|
||||
<div class="flex justify-between gap-2 mt-0.5" style="color: var(--admin-text-secondary)">
|
||||
<span>{{ formatDocDate(doc.created_at) }}</span>
|
||||
<span dir="ltr">{{ formatGold(doc.gold) }} طلا · {{ formatCash(doc.cash) }} ریال</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="m-0 text-xs" style="color: var(--admin-text-secondary)">سندی در کیف ثبت نشده</p>
|
||||
</div>
|
||||
<div class="rounded-lg border border-base p-2.5 min-w-0">
|
||||
<p class="m-0 mb-1.5 text-xs font-600">سندهای {{ sourceLabel }}</p>
|
||||
<p class="m-0 mb-1.5 text-xs" style="color: var(--admin-text-secondary)">آخرین اسناد حسابداری این مشتری</p>
|
||||
<div v-if="sourceDocs.length" class="max-h-32 overflow-y-auto flex flex-col gap-1.5">
|
||||
<div
|
||||
v-for="doc in sourceDocs"
|
||||
:key="'s-' + doc.id"
|
||||
class="rounded-md border border-base px-2 py-1.5 text-xs"
|
||||
>
|
||||
<div class="font-600 truncate">{{ doc.title || 'سند' }}</div>
|
||||
<div class="flex justify-between gap-2 mt-0.5" style="color: var(--admin-text-secondary)">
|
||||
<span>{{ formatDocDate(doc.date || doc.created_at) }}</span>
|
||||
<span dir="ltr">
|
||||
<template v-if="doc.gold != null || doc.cash != null">
|
||||
{{ formatGold(doc.gold) }} طلا · {{ formatCash(doc.cash) }} ریال
|
||||
</template>
|
||||
<template v-else>{{ formatGold(doc.amount) }} · {{ doc.type || '—' }}</template>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="m-0 text-xs" style="color: var(--admin-text-secondary)">سندی از حسابداری نیست</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<a-button type="primary" block :loading="loading === 'overwrite'" :disabled="!!loading" @click="choose('overwrite')">
|
||||
اعمال مانده تهحساب روی کیف
|
||||
اعمال مانده {{ sourceLabel }} روی کیف
|
||||
</a-button>
|
||||
<a-button block :loading="loading === 'keep'" :disabled="!!loading" @click="choose('keep')">
|
||||
نگه داشتن موجودی فعلی اپ
|
||||
@@ -48,6 +101,7 @@
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import BasicModal from '@/components/BasicModal.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import type { TahesabPreviewDoc } from '@/api/tahesab'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
@@ -55,6 +109,13 @@ const props = defineProps<{
|
||||
appCash?: number | null
|
||||
tahesabGold?: number | null
|
||||
tahesabCash?: number | null
|
||||
sourceGold?: number | null
|
||||
sourceCash?: number | null
|
||||
goldDelta?: number | null
|
||||
rialDelta?: number | null
|
||||
sourceLabel?: string
|
||||
walletDocs?: TahesabPreviewDoc[]
|
||||
tahesabDocs?: TahesabPreviewDoc[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -63,6 +124,22 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const loading = ref<'overwrite' | 'keep' | null>(null)
|
||||
const sourceLabel = computed(() => props.sourceLabel || 'تهحساب')
|
||||
const sourceGoldValue = computed(() => props.sourceGold ?? props.tahesabGold)
|
||||
const sourceCashValue = computed(() => props.sourceCash ?? props.tahesabCash)
|
||||
const goldDeltaValue = computed(() => {
|
||||
if (props.goldDelta != null && !Number.isNaN(Number(props.goldDelta))) return Number(props.goldDelta)
|
||||
return Number(sourceGoldValue.value ?? 0) - Number(props.appGold ?? 0)
|
||||
})
|
||||
const rialDeltaValue = computed(() => {
|
||||
if (props.rialDelta != null && !Number.isNaN(Number(props.rialDelta))) return Number(props.rialDelta)
|
||||
return Number(sourceCashValue.value ?? 0) - Number(props.appCash ?? 0)
|
||||
})
|
||||
const walletDocs = computed(() => props.walletDocs ?? [])
|
||||
const sourceDocs = computed(() => props.tahesabDocs ?? [])
|
||||
const showDocs = computed(
|
||||
() => sourceLabel.value === 'تهحساب' || walletDocs.value.length > 0 || sourceDocs.value.length > 0
|
||||
)
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
@@ -86,6 +163,25 @@ function formatCash(v?: number | null) {
|
||||
return Math.round(Number(v)).toLocaleString('fa-IR')
|
||||
}
|
||||
|
||||
function formatDeltaGold(v: number) {
|
||||
if (Number.isNaN(v)) return '—'
|
||||
const sign = v > 0 ? '+' : ''
|
||||
return sign + formatGold(v)
|
||||
}
|
||||
|
||||
function formatDeltaCash(v: number) {
|
||||
if (Number.isNaN(v)) return '—'
|
||||
const sign = v > 0 ? '+' : ''
|
||||
return sign + formatCash(v)
|
||||
}
|
||||
|
||||
function formatDocDate(value?: string | null) {
|
||||
if (!value) return '—'
|
||||
const d = new Date(value)
|
||||
if (Number.isNaN(d.getTime())) return value
|
||||
return d.toLocaleDateString('fa-IR')
|
||||
}
|
||||
|
||||
function close() {
|
||||
visibleProxy.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user