feat(tahesab): three-way mande decision UI including push to Tahesab
Clarify overwrite vs keep vs push-to-tahesab, warn when app balance is zero, and update colleague wait-callback help text. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+8
-1
@@ -254,13 +254,20 @@ export interface TahesabWalletSyncResult {
|
|||||||
/** همگامسازی مانده یک مشتری با تهحساب */
|
/** همگامسازی مانده یک مشتری با تهحساب */
|
||||||
export const syncCustomerWalletFromTahesab = async (
|
export const syncCustomerWalletFromTahesab = async (
|
||||||
customerId: number,
|
customerId: number,
|
||||||
opts?: { overwrite?: boolean; keep?: boolean; ask_if_conflict?: boolean; always_ask?: boolean }
|
opts?: {
|
||||||
|
overwrite?: boolean
|
||||||
|
keep?: boolean
|
||||||
|
push_to_tahesab?: boolean
|
||||||
|
ask_if_conflict?: boolean
|
||||||
|
always_ask?: boolean
|
||||||
|
}
|
||||||
) =>
|
) =>
|
||||||
await useAxios.post<TahesabWalletSyncResult>(
|
await useAxios.post<TahesabWalletSyncResult>(
|
||||||
`/admin/customer/${customerId}/tahesab/sync-wallet`,
|
`/admin/customer/${customerId}/tahesab/sync-wallet`,
|
||||||
{
|
{
|
||||||
overwrite: opts?.overwrite ?? false,
|
overwrite: opts?.overwrite ?? false,
|
||||||
keep: opts?.keep ?? false,
|
keep: opts?.keep ?? false,
|
||||||
|
push_to_tahesab: opts?.push_to_tahesab ?? false,
|
||||||
ask_if_conflict: opts?.ask_if_conflict ?? true,
|
ask_if_conflict: opts?.ask_if_conflict ?? true,
|
||||||
always_ask: opts?.always_ask ?? false,
|
always_ask: opts?.always_ask ?? false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<AdminFieldHelp :text="accountingTahesabBalanceSyncIntro" size="sm" />
|
<AdminFieldHelp :text="accountingTahesabBalanceSyncIntro" size="sm" />
|
||||||
</div>
|
</div>
|
||||||
<p class="tw-bulk__hint">
|
<p class="tw-bulk__hint">
|
||||||
همگام تکی: اول سندها و اختلاف را میبینید، بعد تأیید میکنید. دو مانده جمع نمیشوند.
|
همگام تکی: اول سندها و اختلاف را میبینید، بعد یکی از سه مسیر را تأیید میکنید (تهحساب→اپ، اپ→تهحساب حتی اگر اپ صفر باشد، یا نگه داشتن). دو مانده جمع نمیشوند.
|
||||||
همگام همه فقط کیف خالی را مینشاند؛ تداخل برای تصمیم دستی میماند.
|
همگام همه فقط کیف خالی را مینشاند؛ تداخل برای تصمیم دستی میماند.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,6 +154,7 @@
|
|||||||
<TahesabMandeDecisionModal
|
<TahesabMandeDecisionModal
|
||||||
v-model:visible="decisionVisible"
|
v-model:visible="decisionVisible"
|
||||||
source-label="تهحساب"
|
source-label="تهحساب"
|
||||||
|
:allow-push="true"
|
||||||
:app-gold="decisionResult?.app_gold"
|
:app-gold="decisionResult?.app_gold"
|
||||||
:app-cash="decisionResult?.app_cash"
|
:app-cash="decisionResult?.app_cash"
|
||||||
:tahesab-gold="decisionResult?.tahesab_gold"
|
:tahesab-gold="decisionResult?.tahesab_gold"
|
||||||
@@ -200,7 +201,7 @@ const failed = ref(0)
|
|||||||
const errors = ref<TahesabBulkWalletSyncChunk['errors']>([])
|
const errors = ref<TahesabBulkWalletSyncChunk['errors']>([])
|
||||||
const decisions = ref<TahesabBulkWalletSyncChunk['decisions']>([])
|
const decisions = ref<TahesabBulkWalletSyncChunk['decisions']>([])
|
||||||
const decidingId = ref<number | null>(null)
|
const decidingId = ref<number | null>(null)
|
||||||
const decidingMode = ref<'overwrite' | 'keep' | null>(null)
|
const decidingMode = ref<'overwrite' | 'keep' | 'push' | null>(null)
|
||||||
|
|
||||||
const search = ref('')
|
const search = ref('')
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
@@ -209,7 +210,7 @@ const listTotal = ref(0)
|
|||||||
const listLoading = ref(false)
|
const listLoading = ref(false)
|
||||||
const rows = ref<TahesabWalletLinkedRow[]>([])
|
const rows = ref<TahesabWalletLinkedRow[]>([])
|
||||||
const syncingId = ref<number | null>(null)
|
const syncingId = ref<number | null>(null)
|
||||||
const syncingMode = ref<'ask' | 'overwrite' | 'keep' | null>(null)
|
const syncingMode = ref<'ask' | 'overwrite' | 'keep' | 'push' | null>(null)
|
||||||
const decisionVisible = ref(false)
|
const decisionVisible = ref(false)
|
||||||
const decisionCustomerId = ref<number | null>(null)
|
const decisionCustomerId = ref<number | null>(null)
|
||||||
const decisionResult = ref<TahesabWalletSyncResult['result'] | null>(null)
|
const decisionResult = ref<TahesabWalletSyncResult['result'] | null>(null)
|
||||||
@@ -321,7 +322,7 @@ async function openReview(customerId: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onReviewDecide(mode: 'overwrite' | 'keep') {
|
async function onReviewDecide(mode: 'overwrite' | 'keep' | 'push') {
|
||||||
const customerId = decisionCustomerId.value
|
const customerId = decisionCustomerId.value
|
||||||
decisionVisible.value = false
|
decisionVisible.value = false
|
||||||
if (!customerId) return
|
if (!customerId) return
|
||||||
@@ -337,22 +338,25 @@ function dismissResult() {
|
|||||||
showDetails.value = false
|
showDetails.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyDecision(customerId: number, mode: 'overwrite' | 'keep') {
|
async function applyDecision(customerId: number, mode: 'overwrite' | 'keep' | 'push') {
|
||||||
decidingId.value = customerId
|
decidingId.value = customerId
|
||||||
decidingMode.value = mode
|
decidingMode.value = mode
|
||||||
try {
|
try {
|
||||||
const res = await syncCustomerWalletFromTahesab(customerId, {
|
const res = await syncCustomerWalletFromTahesab(customerId, {
|
||||||
overwrite: mode === 'overwrite',
|
overwrite: mode === 'overwrite',
|
||||||
keep: mode === 'keep',
|
keep: mode === 'keep',
|
||||||
|
push_to_tahesab: mode === 'push',
|
||||||
ask_if_conflict: false,
|
ask_if_conflict: false,
|
||||||
})
|
})
|
||||||
decisions.value = decisions.value.filter(d => d.id !== customerId)
|
decisions.value = decisions.value.filter(d => d.id !== customerId)
|
||||||
needsDecision.value = Math.max(0, decisions.value.length)
|
needsDecision.value = Math.max(0, decisions.value.length)
|
||||||
if (mode === 'keep') message.info(res.message || 'موجودی اپ حفظ شد')
|
if (mode === 'keep') message.info(res.message || 'موجودی اپ حفظ شد')
|
||||||
|
else if (mode === 'push') message.success(res.message || 'مانده اپ روی تهحساب اعمال شد')
|
||||||
else message.success(res.message || 'مانده تهحساب اعمال شد')
|
else message.success(res.message || 'مانده تهحساب اعمال شد')
|
||||||
await loadList()
|
await loadList()
|
||||||
} catch {
|
} catch (e: unknown) {
|
||||||
message.error('اعمال تصمیم ناموفق بود')
|
const data = (e as { response?: { data?: { message?: string } } })?.response?.data
|
||||||
|
message.error(data?.message || 'اعمال تصمیم ناموفق بود')
|
||||||
} finally {
|
} finally {
|
||||||
decidingId.value = null
|
decidingId.value = null
|
||||||
decidingMode.value = null
|
decidingMode.value = null
|
||||||
|
|||||||
@@ -884,7 +884,7 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
|||||||
montzar_open_sell:
|
montzar_open_sell:
|
||||||
'وضعیت واقعی از سرویس خارجی خوانده میشود. روشن: ثبت سفارش فروش از طریق قیمت همکار مجاز است. خاموش: ثبت سفارش فروش از طریق قیمت همکار غیرفعال است.',
|
'وضعیت واقعی از سرویس خارجی خوانده میشود. روشن: ثبت سفارش فروش از طریق قیمت همکار مجاز است. خاموش: ثبت سفارش فروش از طریق قیمت همکار غیرفعال است.',
|
||||||
montzar_order_wait_callback:
|
montzar_order_wait_callback:
|
||||||
'روشن (پیشفرض): بعد از ارسال سفارش به همکار، تا کالبک وضعیت (تأیید=۲ / رد=۴ یا ۶) یا استعلام مسیر وضعیت صبر میشود و کیف ثبت نمیشود. خاموش: مثل قبل با همان پاسخ HTTP همکار سفارش قطعی میشود.',
|
'با منبع قیمت همکار، سفارش همیشه تا کالبک یا استعلام وضعیت در انتظار میماند و کیف بعد از تأیید ثبت میشود. خاموش بودن این سوییچ دیگر مسیر قطعی بلافاصله را باز نمیکند. در رد همکار، SMS به ادمینهای دارای ارسال پیامک میرود.',
|
||||||
montzar_order_status_path:
|
montzar_order_status_path:
|
||||||
'مسیر GET روی دامنه همکار (montzar_base_url) برای استعلام وضعیت اگر کالبک نرسید. پیشفرض /server/customer/order — با شماره سفارش همکار میشود /server/customer/order/40000.',
|
'مسیر GET روی دامنه همکار (montzar_base_url) برای استعلام وضعیت اگر کالبک نرسید. پیشفرض /server/customer/order — با شماره سفارش همکار میشود /server/customer/order/40000.',
|
||||||
montzar_closed:
|
montzar_closed:
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<TahesabMandeDecisionModal
|
<TahesabMandeDecisionModal
|
||||||
v-model:visible="decisionVisible"
|
v-model:visible="decisionVisible"
|
||||||
:source-label="provider === 'zarin' ? 'زرین' : 'تهحساب'"
|
:source-label="provider === 'zarin' ? 'زرین' : 'تهحساب'"
|
||||||
|
:allow-push="provider === 'tahesab'"
|
||||||
:app-gold="decision?.app_gold"
|
:app-gold="decision?.app_gold"
|
||||||
:app-cash="decision?.app_cash"
|
:app-cash="decision?.app_cash"
|
||||||
:tahesab-gold="decision?.tahesab_gold ?? decision?.zarin_gold"
|
:tahesab-gold="decision?.tahesab_gold ?? decision?.zarin_gold"
|
||||||
@@ -120,8 +121,12 @@ async function saveCode(customerId: number, body: Record<string, string>) {
|
|||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyWalletDecision(customerId: number, mode: 'overwrite' | 'keep') {
|
async function applyWalletDecision(customerId: number, mode: 'overwrite' | 'keep' | 'push') {
|
||||||
if (props.provider === 'zarin') {
|
if (props.provider === 'zarin') {
|
||||||
|
if (mode === 'push') {
|
||||||
|
message.warning('اعمال مانده اپ روی زرین در این نسخه پشتیبانی نمیشود')
|
||||||
|
return { ok: false, message: 'پشتیبانی نمیشود' }
|
||||||
|
}
|
||||||
return syncCustomerWalletFromZarin(customerId, {
|
return syncCustomerWalletFromZarin(customerId, {
|
||||||
overwrite: mode === 'overwrite',
|
overwrite: mode === 'overwrite',
|
||||||
keep: mode === 'keep',
|
keep: mode === 'keep',
|
||||||
@@ -131,11 +136,12 @@ async function applyWalletDecision(customerId: number, mode: 'overwrite' | 'keep
|
|||||||
return syncCustomerWalletFromTahesab(customerId, {
|
return syncCustomerWalletFromTahesab(customerId, {
|
||||||
overwrite: mode === 'overwrite',
|
overwrite: mode === 'overwrite',
|
||||||
keep: mode === 'keep',
|
keep: mode === 'keep',
|
||||||
|
push_to_tahesab: mode === 'push',
|
||||||
ask_if_conflict: false,
|
ask_if_conflict: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onDecide(mode: 'overwrite' | 'keep') {
|
async function onDecide(mode: 'overwrite' | 'keep' | 'push') {
|
||||||
const pending = pendingPayload.value
|
const pending = pendingPayload.value
|
||||||
decisionVisible.value = false
|
decisionVisible.value = false
|
||||||
if (!pending) return
|
if (!pending) return
|
||||||
@@ -144,6 +150,7 @@ async function onDecide(mode: 'overwrite' | 'keep') {
|
|||||||
await saveCode(pending.customerId, pending.body)
|
await saveCode(pending.customerId, pending.body)
|
||||||
const res = await applyWalletDecision(pending.customerId, mode)
|
const res = await applyWalletDecision(pending.customerId, mode)
|
||||||
if (mode === 'keep') message.info(res.message || 'موجودی اپ حفظ شد')
|
if (mode === 'keep') message.info(res.message || 'موجودی اپ حفظ شد')
|
||||||
|
else if (mode === 'push') message.success(res.message || 'مانده اپ روی تهحساب اعمال شد')
|
||||||
else message.success(res.message || 'مانده حسابداری روی کیف اعمال شد')
|
else message.success(res.message || 'مانده حسابداری روی کیف اعمال شد')
|
||||||
emit('success')
|
emit('success')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicModal
|
<BasicModal
|
||||||
v-model:visible="visibleProxy"
|
v-model:visible="visibleProxy"
|
||||||
:width="isMobile ? '92vw' : '36rem'"
|
:width="isMobile ? '92vw' : '40rem'"
|
||||||
title="تصمیم برای مانده حساب"
|
title="تصمیم برای مانده حساب"
|
||||||
:custom-footer="true"
|
:custom-footer="true"
|
||||||
cancel-text="بعداً"
|
cancel-text="بعداً"
|
||||||
@close="close"
|
@close="close"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-3 text-sm" style="color: var(--admin-text)">
|
<div class="flex flex-col gap-3 text-sm" style="color: var(--admin-text)">
|
||||||
<p class="m-0" style="color: var(--admin-text-secondary)">
|
<p class="m-0 leading-6" style="color: var(--admin-text-secondary)">
|
||||||
موجودی اپ با مانده {{ sourceLabel }} فرق دارد. دو مانده جمع نمیشوند؛ یکی را پس از بررسی سندها تأیید کنید.
|
موجودی اپ با مانده {{ sourceLabel }} فرق دارد. دو مانده جمع نمیشوند — یکی از سه مسیر زیر را انتخاب کنید.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="appIsEmpty && allowPush"
|
||||||
|
class="rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs leading-5 text-amber-900"
|
||||||
|
>
|
||||||
|
موجودی اپ صفر است. اگر «اعمال مانده اپ روی تهحساب» را بزنید، مانده تهحساب با سند تعدیل همتراز صفر اپ میشود.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="rounded-lg border border-base p-3 space-y-2" style="background: var(--admin-bg-muted)">
|
<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">
|
<div class="flex justify-between gap-2">
|
||||||
<span style="color: var(--admin-text-secondary)">موجودی اپ (طلا)</span>
|
<span style="color: var(--admin-text-secondary)">موجودی اپ (طلا)</span>
|
||||||
@@ -84,12 +92,58 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<a-button type="primary" block :loading="loading === 'overwrite'" :disabled="!!loading" @click="choose('overwrite')">
|
<button
|
||||||
اعمال مانده {{ sourceLabel }} روی کیف
|
type="button"
|
||||||
</a-button>
|
class="mande-choice mande-choice--primary"
|
||||||
<a-button block :loading="loading === 'keep'" :disabled="!!loading" @click="choose('keep')">
|
:disabled="!!loading"
|
||||||
نگه داشتن موجودی فعلی اپ
|
@click="choose('overwrite')"
|
||||||
</a-button>
|
>
|
||||||
|
<span class="mande-choice__title">
|
||||||
|
<a-spin v-if="loading === 'overwrite'" size="small" class="me-1" />
|
||||||
|
اعمال مانده {{ sourceLabel }} روی کیف اپ
|
||||||
|
</span>
|
||||||
|
<span class="mande-choice__hint">
|
||||||
|
موجودی اپ مثل {{ sourceLabel }} میشود. سند تعدیل روی کیف اپ ثبت میشود.
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="allowPush"
|
||||||
|
type="button"
|
||||||
|
class="mande-choice"
|
||||||
|
:class="appIsEmpty ? 'mande-choice--warn' : 'mande-choice--secondary'"
|
||||||
|
:disabled="!!loading"
|
||||||
|
@click="choose('push')"
|
||||||
|
>
|
||||||
|
<span class="mande-choice__title">
|
||||||
|
<a-spin v-if="loading === 'push'" size="small" class="me-1" />
|
||||||
|
اعمال مانده اپ روی {{ sourceLabel }}
|
||||||
|
<span v-if="appIsEmpty" class="mande-choice__badge">اپ صفر</span>
|
||||||
|
</span>
|
||||||
|
<span class="mande-choice__hint">
|
||||||
|
{{
|
||||||
|
appIsEmpty
|
||||||
|
? `مانده ${sourceLabel} با سند تعدیل همتراز صفر اپ میشود. کیف اپ تغییر نمیکند.`
|
||||||
|
: `مانده ${sourceLabel} مثل موجودی اپ میشود. کیف اپ تغییر نمیکند.`
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="mande-choice"
|
||||||
|
:disabled="!!loading"
|
||||||
|
@click="choose('keep')"
|
||||||
|
>
|
||||||
|
<span class="mande-choice__title">
|
||||||
|
<a-spin v-if="loading === 'keep'" size="small" class="me-1" />
|
||||||
|
نگه داشتن وضعیت فعلی
|
||||||
|
</span>
|
||||||
|
<span class="mande-choice__hint">
|
||||||
|
نه کیف اپ عوض میشود نه {{ sourceLabel }}؛ اختلاف برای بعد میماند.
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<a-button type="link" block :disabled="!!loading" @click="close">بعداً</a-button>
|
<a-button type="link" block :disabled="!!loading" @click="close">بعداً</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -103,27 +157,34 @@ import BasicModal from '@/components/BasicModal.vue'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import type { TahesabPreviewDoc } from '@/api/tahesab'
|
import type { TahesabPreviewDoc } from '@/api/tahesab'
|
||||||
|
|
||||||
const props = defineProps<{
|
export type MandeDecisionMode = 'overwrite' | 'keep' | 'push'
|
||||||
visible: boolean
|
|
||||||
appGold?: number | null
|
const props = withDefaults(
|
||||||
appCash?: number | null
|
defineProps<{
|
||||||
tahesabGold?: number | null
|
visible: boolean
|
||||||
tahesabCash?: number | null
|
appGold?: number | null
|
||||||
sourceGold?: number | null
|
appCash?: number | null
|
||||||
sourceCash?: number | null
|
tahesabGold?: number | null
|
||||||
goldDelta?: number | null
|
tahesabCash?: number | null
|
||||||
rialDelta?: number | null
|
sourceGold?: number | null
|
||||||
sourceLabel?: string
|
sourceCash?: number | null
|
||||||
walletDocs?: TahesabPreviewDoc[]
|
goldDelta?: number | null
|
||||||
tahesabDocs?: TahesabPreviewDoc[]
|
rialDelta?: number | null
|
||||||
}>()
|
sourceLabel?: string
|
||||||
|
/** فقط تهحساب: اجازهٔ نوشتن مانده اپ روی حسابداری */
|
||||||
|
allowPush?: boolean
|
||||||
|
walletDocs?: TahesabPreviewDoc[]
|
||||||
|
tahesabDocs?: TahesabPreviewDoc[]
|
||||||
|
}>(),
|
||||||
|
{ allowPush: false }
|
||||||
|
)
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:visible', value: boolean): void
|
(e: 'update:visible', value: boolean): void
|
||||||
(e: 'decide', mode: 'overwrite' | 'keep'): void
|
(e: 'decide', mode: MandeDecisionMode): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const loading = ref<'overwrite' | 'keep' | null>(null)
|
const loading = ref<MandeDecisionMode | null>(null)
|
||||||
const sourceLabel = computed(() => props.sourceLabel || 'تهحساب')
|
const sourceLabel = computed(() => props.sourceLabel || 'تهحساب')
|
||||||
const sourceGoldValue = computed(() => props.sourceGold ?? props.tahesabGold)
|
const sourceGoldValue = computed(() => props.sourceGold ?? props.tahesabGold)
|
||||||
const sourceCashValue = computed(() => props.sourceCash ?? props.tahesabCash)
|
const sourceCashValue = computed(() => props.sourceCash ?? props.tahesabCash)
|
||||||
@@ -140,6 +201,11 @@ const sourceDocs = computed(() => props.tahesabDocs ?? [])
|
|||||||
const showDocs = computed(
|
const showDocs = computed(
|
||||||
() => sourceLabel.value === 'تهحساب' || walletDocs.value.length > 0 || sourceDocs.value.length > 0
|
() => sourceLabel.value === 'تهحساب' || walletDocs.value.length > 0 || sourceDocs.value.length > 0
|
||||||
)
|
)
|
||||||
|
const appIsEmpty = computed(() => {
|
||||||
|
const g = Math.abs(Number(props.appGold ?? 0))
|
||||||
|
const c = Math.abs(Number(props.appCash ?? 0))
|
||||||
|
return g < 0.0005 && c < 1
|
||||||
|
})
|
||||||
|
|
||||||
const visibleProxy = computed({
|
const visibleProxy = computed({
|
||||||
get: () => props.visible,
|
get: () => props.visible,
|
||||||
@@ -186,8 +252,75 @@ function close() {
|
|||||||
visibleProxy.value = false
|
visibleProxy.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function choose(mode: 'overwrite' | 'keep') {
|
function choose(mode: MandeDecisionMode) {
|
||||||
loading.value = mode
|
loading.value = mode
|
||||||
emit('decide', mode)
|
emit('decide', mode)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.mande-choice {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 0.25rem;
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
border: 1px solid var(--admin-border, #e5e7eb);
|
||||||
|
background: var(--admin-surface, #fff);
|
||||||
|
padding: 0.75rem 0.875rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s ease, background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mande-choice:disabled {
|
||||||
|
opacity: 0.65;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mande-choice:not(:disabled):hover {
|
||||||
|
border-color: var(--admin-primary, #10913a);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mande-choice--primary {
|
||||||
|
border-color: var(--admin-primary, #10913a);
|
||||||
|
background: color-mix(in srgb, var(--admin-primary, #10913a) 8%, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mande-choice--secondary:not(:disabled):hover {
|
||||||
|
background: var(--admin-bg-muted, #f8fafc);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mande-choice--warn {
|
||||||
|
border-color: #f59e0b;
|
||||||
|
background: #fffbeb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mande-choice__title {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--admin-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mande-choice__hint {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--admin-text-secondary, #6b7280);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mande-choice__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #fef3c7;
|
||||||
|
color: #92400e;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0.1rem 0.45rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -308,6 +308,7 @@
|
|||||||
<TahesabMandeDecisionModal
|
<TahesabMandeDecisionModal
|
||||||
v-model:visible="tahesabDecisionVisible"
|
v-model:visible="tahesabDecisionVisible"
|
||||||
:source-label="walletDecisionLabel"
|
:source-label="walletDecisionLabel"
|
||||||
|
:allow-push="walletDecisionProvider === 'tahesab'"
|
||||||
:app-gold="tahesabDecision?.app_gold"
|
:app-gold="tahesabDecision?.app_gold"
|
||||||
:app-cash="tahesabDecision?.app_cash"
|
:app-cash="tahesabDecision?.app_cash"
|
||||||
:tahesab-gold="tahesabDecision?.tahesab_gold ?? tahesabDecision?.zarin_gold"
|
:tahesab-gold="tahesabDecision?.tahesab_gold ?? tahesabDecision?.zarin_gold"
|
||||||
@@ -855,7 +856,7 @@ function openTahesabDecision(result?: TahesabWalletSyncResult['result'], provide
|
|||||||
tahesabDecisionVisible.value = true
|
tahesabDecisionVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncTahesabWallet(mode: 'ask' | 'overwrite' | 'keep' = 'ask') {
|
async function syncTahesabWallet(mode: 'ask' | 'overwrite' | 'keep' | 'push' = 'ask') {
|
||||||
if (!userInfo.value?.id || !tahesabWalletCanSync.value || tahesabSyncLoading.value) return
|
if (!userInfo.value?.id || !tahesabWalletCanSync.value || tahesabSyncLoading.value) return
|
||||||
tahesabSyncLoading.value = true
|
tahesabSyncLoading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -877,6 +878,7 @@ async function syncTahesabWallet(mode: 'ask' | 'overwrite' | 'keep' = 'ask') {
|
|||||||
ask_if_conflict: false,
|
ask_if_conflict: false,
|
||||||
overwrite: mode === 'overwrite',
|
overwrite: mode === 'overwrite',
|
||||||
keep: mode === 'keep',
|
keep: mode === 'keep',
|
||||||
|
push_to_tahesab: mode === 'push',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res.needs_decision || res.result?.needs_decision) {
|
if (res.needs_decision || res.result?.needs_decision) {
|
||||||
@@ -885,7 +887,14 @@ async function syncTahesabWallet(mode: 'ask' | 'overwrite' | 'keep' = 'ask') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (res.result?.applied || res.ok) {
|
if (res.result?.applied || res.ok) {
|
||||||
message.success(res.message || 'مانده از تهحساب روی کیف اعمال شد')
|
message.success(
|
||||||
|
res.message ||
|
||||||
|
(mode === 'push'
|
||||||
|
? 'مانده اپ روی تهحساب اعمال شد'
|
||||||
|
: mode === 'keep'
|
||||||
|
? 'موجودی اپ حفظ شد'
|
||||||
|
: 'مانده از تهحساب روی کیف اعمال شد')
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
message.info(res.message || 'تغییری برای اعمال نبود')
|
message.info(res.message || 'تغییری برای اعمال نبود')
|
||||||
}
|
}
|
||||||
@@ -902,18 +911,24 @@ async function syncTahesabWallet(mode: 'ask' | 'overwrite' | 'keep' = 'ask') {
|
|||||||
refreshTable()
|
refreshTable()
|
||||||
|
|
||||||
if (data?.result?.applied || data?.ok) {
|
if (data?.result?.applied || data?.ok) {
|
||||||
message.success(data?.message || 'مانده از تهحساب روی کیف اعمال شد')
|
message.success(data?.message || 'مانده با تهحساب همتراز شد')
|
||||||
} else if (status !== 422) {
|
} else if (status !== 422) {
|
||||||
message.warning(data?.message || 'اعمال مانده از تهحساب ناموفق بود')
|
message.warning(data?.message || 'اعمال مانده با تهحساب ناموفق بود')
|
||||||
|
} else if (data?.message) {
|
||||||
|
message.warning(data.message)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
tahesabSyncLoading.value = false
|
tahesabSyncLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onTahesabMandeDecide(mode: 'overwrite' | 'keep') {
|
async function onTahesabMandeDecide(mode: 'overwrite' | 'keep' | 'push') {
|
||||||
tahesabDecisionVisible.value = false
|
tahesabDecisionVisible.value = false
|
||||||
if (walletDecisionProvider.value === 'zarin') {
|
if (walletDecisionProvider.value === 'zarin') {
|
||||||
|
if (mode === 'push') {
|
||||||
|
message.warning('اعمال مانده اپ روی زرین در این نسخه پشتیبانی نمیشود')
|
||||||
|
return
|
||||||
|
}
|
||||||
await syncZarinWallet(mode)
|
await syncZarinWallet(mode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user