fix some bugs from accounting 2

This commit is contained in:
2026-07-28 17:53:44 +03:30
parent 4cd11919f5
commit 19afd28e60
3 changed files with 75 additions and 20 deletions
@@ -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)
}
@@ -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"
/>
<div v-if="isMoney && Number(modelValue.amountAbs) > 10" class="wallet-charge-form__hint">
معادل {{ usePrice(Number(modelValue.amountAbs) / 10, true, 'تومان') }}
<div v-if="isMoney && Number(normalizedAmount) > 10" class="wallet-charge-form__hint">
معادل {{ usePrice(Number(normalizedAmount) / 10, true, 'تومان') }}
</div>
</a-form-item>
@@ -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()
},
@@ -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 قبل از آپدیت سرور: تعدادی ≠ آبشده/کاتد