From 19afd28e600529d2e30e4358f599d10afb18f3d5 Mon Sep 17 00:00:00 2001 From: adonis Date: Tue, 28 Jul 2026 17:53:44 +0330 Subject: [PATCH] fix some bugs from accounting 2 --- .../customer/CustomerWalletBalancePanel.vue | 22 +++--- .../base/components/WalletChargeForm.vue | 68 ++++++++++++++++--- .../base/components/WalletChargeModalBody.vue | 5 +- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/components/customer/CustomerWalletBalancePanel.vue b/src/components/customer/CustomerWalletBalancePanel.vue index 857b529..d2c17d1 100644 --- a/src/components/customer/CustomerWalletBalancePanel.vue +++ b/src/components/customer/CustomerWalletBalancePanel.vue @@ -239,6 +239,13 @@ function inventoryCards() { return [...(summary.value.inventory_cards ?? []), ...(summary.value.count_products ?? [])] } +function isCountChargeTarget(target: ChargeTarget) { + if (target.kind === 'money') return false + if (target.allows_decimal === false) return true + if (target.unit === 'عدد') return true + return target.trade_by_quantity === false && !isMeltedTradeGroup(target.group_type) +} + function balanceForTarget(target: ChargeTarget) { if (!summary.value) return '—' if (target.kind === 'money') { @@ -246,18 +253,15 @@ function balanceForTarget(target: ChargeTarget) { } const card = inventoryCards().find((row) => Number(row.product_id) === Number(target.product_id)) - if (card) return formatCard(card) - // تعدادی: همیشه «عدد» — نه گرم (وزن هر قطعه در فیلد unit محصول است) - const isCount = - target.allows_decimal === false || - target.unit === 'عدد' || - (target.trade_by_quantity === false && !isMeltedTradeGroup(target.group_type)) - - if (isCount) { - return '۰ عدد' + // اولویت با نوع شارژ (تعدادی) — حتی اگر کارت دارایی اشتباه «گرم» برچسب زده باشد + if (isCountChargeTarget(target)) { + const qty = Number(card?.quantity ?? 0) + return `${qty.toLocaleString('fa-IR', { maximumFractionDigits: 0 })} عدد` } + if (card) return formatCard(card) + return formatMetalDisplayValue(0, target.metal_id ?? 1) } diff --git a/src/pages/menuPages/customers/base/components/WalletChargeForm.vue b/src/pages/menuPages/customers/base/components/WalletChargeForm.vue index 62a4e57..28b7483 100644 --- a/src/pages/menuPages/customers/base/components/WalletChargeForm.vue +++ b/src/pages/menuPages/customers/base/components/WalletChargeForm.vue @@ -37,11 +37,12 @@ v-model:value="modelValue.amountAbs" :addon-before="resolvedUnit" :type="resolvedInputType" - :step="integerOnly ? 1 : undefined" + :step="requiresInteger ? 1 : undefined" :place-holder="amountPlaceholder" + @onchange="onAmountChange" /> -
- معادل {{ usePrice(Number(modelValue.amountAbs) / 10, true, 'تومان') }} +
+ معادل {{ usePrice(Number(normalizedAmount) / 10, true, 'تومان') }}
@@ -84,7 +85,8 @@ const resolvedUnit = computed( () => props.unitLabel ?? (props.variant === 'money' ? 'ریال' : 'گرم'), ) const isMoney = computed(() => props.isMoney ?? props.variant === 'money') -const integerOnly = computed(() => props.integerOnly === true) +/** نقدی و تعدادی — بدون اعشار */ +const requiresInteger = computed(() => isMoney.value || props.integerOnly === true) const resolvedInputType = computed(() => (isMoney.value ? 'price' : 'number')) const isDischarge = computed(() => props.modelValue.direction === 'discharge') @@ -114,7 +116,7 @@ const noteLabel = computed(() => (isDischarge.value ? 'علت دشارژ' : 'ع const amountPlaceholder = computed(() => { if (isMoney.value) return 'مثلاً ۱۰۰٬۰۰۰' - if (integerOnly.value) return 'مثلاً ۱' + if (requiresInteger.value) return 'مثلاً ۱' return 'مثلاً ۰.۵' }) @@ -122,16 +124,64 @@ const notePlaceholder = computed(() => isDischarge.value ? 'دلیل کسر موجودی' : 'دلیل افزایش موجودی', ) +/** ارقام فارسی/عربی → لاتین؛ جداکننده هزار حذف؛ ممیز یکسان */ +function normalizeAmountRaw(raw: string | number | undefined | null): string { + return String(raw ?? '') + .trim() + .replace(/[۰-۹]/g, (d) => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d))) + .replace(/[٠-٩]/g, (d) => String('٠١٢٣٤٥٦٧٨٩'.indexOf(d))) + .replace(/[٬,\s]/g, '') + .replace(/٫/g, '.') +} + +function hasDecimalSeparator(raw: string): boolean { + // بعد از حذف جداکننده هزار، نقطه = اعشار + return raw.includes('.') +} + +function parseAmount(raw: string | number | undefined | null): number { + const normalized = normalizeAmountRaw(raw) + if (!normalized) return 0 + const num = Number(normalized) + return Number.isFinite(num) ? Math.abs(num) : NaN +} + +const normalizedAmount = computed(() => parseAmount(props.modelValue.amountAbs)) + +function onAmountChange(raw: string | number) { + if (!requiresInteger.value) return + const text = normalizeAmountRaw(raw) + if (!hasDecimalSeparator(text)) return + // قطع اعشار — فقط بخش صحیح بماند + const whole = text.split('.')[0] ?? '' + props.modelValue.amountAbs = whole === '' ? undefined : whole +} + const amountRules = computed(() => [ ...inputRules('مقدار'), { validator: (_rule: unknown, value: string | number | undefined) => { - const num = Math.abs(Number(value) || 0) - if (!num) { + const raw = normalizeAmountRaw(value) + if (!raw) { return Promise.reject('مقدار باید بیشتر از صفر باشد') } - if (integerOnly.value && Math.abs(num - Math.round(num)) > 0.000001) { - return Promise.reject('برای محصول تعدادی فقط عدد صحیح مجاز است') + if (requiresInteger.value && hasDecimalSeparator(raw)) { + return Promise.reject( + isMoney.value + ? 'برای مبلغ ریالی فقط عدد صحیح مجاز است' + : 'برای محصول تعدادی فقط عدد صحیح مجاز است', + ) + } + const num = parseAmount(raw) + if (!Number.isFinite(num) || !num) { + return Promise.reject('مقدار باید بیشتر از صفر باشد') + } + if (requiresInteger.value && Math.abs(num - Math.round(num)) > 0.000001) { + return Promise.reject( + isMoney.value + ? 'برای مبلغ ریالی فقط عدد صحیح مجاز است' + : 'برای محصول تعدادی فقط عدد صحیح مجاز است', + ) } return Promise.resolve() }, diff --git a/src/pages/menuPages/customers/base/components/WalletChargeModalBody.vue b/src/pages/menuPages/customers/base/components/WalletChargeModalBody.vue index 89a91ed..de1fd76 100644 --- a/src/pages/menuPages/customers/base/components/WalletChargeModalBody.vue +++ b/src/pages/menuPages/customers/base/components/WalletChargeModalBody.vue @@ -113,10 +113,11 @@ const targetOptions = computed(() => })), ) -/** تعدادی: فقط عدد صحیح — از allows_decimal سرور یا واحد «عدد» */ +/** تعدادی یا نقدی: فقط عدد صحیح */ const isIntegerOnlyTarget = computed(() => { const target = props.selectedTarget - if (!target || target.kind === 'money') return false + if (!target) return false + if (target.kind === 'money') return true if (typeof target.allows_decimal === 'boolean') return !target.allows_decimal if (target.unit === 'عدد') return true // fallback قبل از آپدیت سرور: تعدادی ≠ آبشده/کاتد