diff --git a/src/components/customer/CustomerWalletBalancePanel.vue b/src/components/customer/CustomerWalletBalancePanel.vue
index 7a17f42..857b529 100644
--- a/src/components/customer/CustomerWalletBalancePanel.vue
+++ b/src/components/customer/CustomerWalletBalancePanel.vue
@@ -143,6 +143,7 @@ import type { ChargeTarget } from '@/model/wallet'
import type { WalletAssetsSummary, WalletInventoryCard, WalletMetalTotal } from '@/model/walletAssets'
import { usePrice } from '@core/utils/usePrice'
import { formatMetalDisplayValue } from '@/utils/metalWeight'
+import { isMeltedTradeGroup } from '@/utils/productCategories'
const props = withDefaults(
defineProps<{
@@ -247,10 +248,16 @@ function balanceForTarget(target: ChargeTarget) {
const card = inventoryCards().find((row) => Number(row.product_id) === Number(target.product_id))
if (card) return formatCard(card)
- const unit = target.unit || (target.trade_by_quantity ? 'عدد' : 'گرم')
- if (target.trade_by_quantity) {
- return `۰ ${unit}`
+ // تعدادی: همیشه «عدد» — نه گرم (وزن هر قطعه در فیلد unit محصول است)
+ const isCount =
+ target.allows_decimal === false ||
+ target.unit === 'عدد' ||
+ (target.trade_by_quantity === false && !isMeltedTradeGroup(target.group_type))
+
+ if (isCount) {
+ return '۰ عدد'
}
+
return formatMetalDisplayValue(0, target.metal_id ?? 1)
}
diff --git a/src/model/wallet.ts b/src/model/wallet.ts
index ed627ec..b721df4 100644
--- a/src/model/wallet.ts
+++ b/src/model/wallet.ts
@@ -116,6 +116,8 @@ export interface ChargeTarget {
metal_id?: number
uses_main_wallet?: boolean
trade_by_quantity?: boolean
+ /** false برای محصولات تعدادی — فقط عدد صحیح */
+ allows_decimal?: boolean
group_type?: string
active?: boolean
}
diff --git a/src/pages/menuPages/customers/base/components/WalletChargeForm.vue b/src/pages/menuPages/customers/base/components/WalletChargeForm.vue
index 0aec76d..62a4e57 100644
--- a/src/pages/menuPages/customers/base/components/WalletChargeForm.vue
+++ b/src/pages/menuPages/customers/base/components/WalletChargeForm.vue
@@ -37,6 +37,7 @@
v-model:value="modelValue.amountAbs"
:addon-before="resolvedUnit"
:type="resolvedInputType"
+ :step="integerOnly ? 1 : undefined"
:place-holder="amountPlaceholder"
/>
@@ -66,11 +67,14 @@ const props = withDefaults(
unitLabel?: string
/** true = ورودی قیمتی (ریال)، false = عددی */
isMoney?: boolean
+ /** محصولات تعدادی — فقط عدد صحیح */
+ integerOnly?: boolean
/** نام کیف پول برای پیشنمایش */
walletLabel?: string
}>(),
{
variant: 'money',
+ integerOnly: false,
},
)
@@ -80,6 +84,7 @@ const resolvedUnit = computed(
() => props.unitLabel ?? (props.variant === 'money' ? 'ریال' : 'گرم'),
)
const isMoney = computed(() => props.isMoney ?? props.variant === 'money')
+const integerOnly = computed(() => props.integerOnly === true)
const resolvedInputType = computed(() => (isMoney.value ? 'price' : 'number'))
const isDischarge = computed(() => props.modelValue.direction === 'discharge')
@@ -107,15 +112,17 @@ const amountLabel = computed(() =>
const noteLabel = computed(() => (isDischarge.value ? 'علت دشارژ' : 'علت شارژ'))
-const amountPlaceholder = computed(() =>
- isMoney.value ? 'مثلاً ۱۰۰٬۰۰۰' : 'مثلاً ۰.۵',
-)
+const amountPlaceholder = computed(() => {
+ if (isMoney.value) return 'مثلاً ۱۰۰٬۰۰۰'
+ if (integerOnly.value) return 'مثلاً ۱'
+ return 'مثلاً ۰.۵'
+})
const notePlaceholder = computed(() =>
isDischarge.value ? 'دلیل کسر موجودی' : 'دلیل افزایش موجودی',
)
-const amountRules = [
+const amountRules = computed(() => [
...inputRules('مقدار'),
{
validator: (_rule: unknown, value: string | number | undefined) => {
@@ -123,10 +130,13 @@ const amountRules = [
if (!num) {
return Promise.reject('مقدار باید بیشتر از صفر باشد')
}
+ if (integerOnly.value && Math.abs(num - Math.round(num)) > 0.000001) {
+ return Promise.reject('برای محصول تعدادی فقط عدد صحیح مجاز است')
+ }
return Promise.resolve()
},
},
-]
+])
defineExpose({
validateFields: () => formRef.value?.validateFields(),
diff --git a/src/pages/menuPages/customers/base/components/WalletChargeModalBody.vue b/src/pages/menuPages/customers/base/components/WalletChargeModalBody.vue
index 05cc77b..89a91ed 100644
--- a/src/pages/menuPages/customers/base/components/WalletChargeModalBody.vue
+++ b/src/pages/menuPages/customers/base/components/WalletChargeModalBody.vue
@@ -42,8 +42,9 @@
@@ -56,6 +57,7 @@ import CustomerWalletBalancePanel from '@/components/customer/CustomerWalletBala
import { getCustomersList } from '@/api/customer'
import type { Charge, ChargeTarget } from '@/model/wallet'
import type { Customer } from '@/model/customer'
+import { isMeltedTradeGroup } from '@/utils/productCategories'
const props = defineProps<{
formCharge: Charge
@@ -111,6 +113,22 @@ const targetOptions = computed(() =>
})),
)
+/** تعدادی: فقط عدد صحیح — از allows_decimal سرور یا واحد «عدد» */
+const isIntegerOnlyTarget = computed(() => {
+ const target = props.selectedTarget
+ if (!target || target.kind === 'money') return false
+ if (typeof target.allows_decimal === 'boolean') return !target.allows_decimal
+ if (target.unit === 'عدد') return true
+ // fallback قبل از آپدیت سرور: تعدادی ≠ آبشده/کاتد
+ return target.trade_by_quantity === false && !isMeltedTradeGroup(target.group_type)
+})
+
+const chargeUnitLabel = computed(() => {
+ if (props.selectedTarget?.kind === 'money') return 'ریال'
+ if (isIntegerOnlyTarget.value) return 'عدد'
+ return props.selectedTarget?.unit ?? 'گرم'
+})
+
function filterCustomer(input: string, option?: { label?: string }) {
return String(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}