234 lines
6.9 KiB
Vue
234 lines
6.9 KiB
Vue
<template>
|
|
<BasicModal
|
|
v-model:visible="visibleProxy"
|
|
title="ویرایش مقدار برداشت"
|
|
width="28rem"
|
|
wrap-class-name="physical-withdraw-adjust-modal"
|
|
:loading="loading"
|
|
:confirm-loading="saving"
|
|
@ok="submit"
|
|
@close="close"
|
|
>
|
|
<div v-if="loading" class="py-10 text-center text-sm text-gray-500">در حال بارگذاری...</div>
|
|
|
|
<div v-else-if="info && formLines.length" class="space-y-4">
|
|
<div class="rounded-lg bg-gray-50 p-3 text-sm leading-6">
|
|
<div>سند: <span class="font-semibold">#{{ info.withdraw_id }}</span></div>
|
|
<div>مشتری: <span class="font-semibold">{{ info.customer_name || '-' }}</span></div>
|
|
<div v-if="info.customer_mobile">موبایل: {{ info.customer_mobile }}</div>
|
|
</div>
|
|
|
|
<div
|
|
v-for="line in formLines"
|
|
:key="line.transaction_id"
|
|
class="rounded-lg border border-gray-200 p-3"
|
|
>
|
|
<div class="mb-2 text-sm font-semibold">{{ line.title }}</div>
|
|
<div class="mb-3 grid grid-cols-2 gap-2 text-xs text-gray-600">
|
|
<div>مقدار فعلی: <span class="font-medium text-gray-900">{{ formatQty(line.current_value, line) }}</span></div>
|
|
<div>قابل افزودن: <span class="font-medium text-gray-900">{{ formatQty(line.available_add, line) }}</span></div>
|
|
<div>حداکثر مجاز: <span class="font-medium text-gray-900">{{ formatQty(line.max_value, line) }}</span></div>
|
|
<div>حداقل مجاز: <span class="font-medium text-gray-900">{{ formatQty(line.min_value, line) }}</span></div>
|
|
</div>
|
|
|
|
<a-form-item
|
|
class="!mb-0"
|
|
:validate-status="lineError(line.transaction_id) ? 'error' : ''"
|
|
:help="lineError(line.transaction_id)"
|
|
>
|
|
<template #label>
|
|
<AdminLabelWithHelp label="مقدار جدید" :help="physicalWithdrawAdjustValueHelp" />
|
|
</template>
|
|
<BasicInput
|
|
v-model:value="line.valueInput"
|
|
type="number"
|
|
:step="line.allows_decimal ? '0.001' : '1'"
|
|
:place-holder="line.allows_decimal ? 'مثلاً 1.250' : 'مثلاً 2'"
|
|
@change="() => clearLineError(line.transaction_id)"
|
|
/>
|
|
</a-form-item>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else-if="info && !formLines.length" class="py-8 text-center text-sm text-gray-500">
|
|
خطی برای ویرایش یافت نشد.
|
|
</div>
|
|
</BasicModal>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { message } from 'ant-design-vue'
|
|
import AdminLabelWithHelp from '@/components/AdminLabelWithHelp.vue'
|
|
import {
|
|
adjustPhysicalWithdraw,
|
|
getPhysicalWithdrawAdjustInfo,
|
|
type PhysicalWithdrawAdjustInfo,
|
|
type PhysicalWithdrawAdjustLine
|
|
} from '@/api/physicalWithdraw'
|
|
import { physicalWithdrawAdjustValueHelp } from '@/utils/adminHelpCopy'
|
|
|
|
type FormLine = PhysicalWithdrawAdjustLine & { valueInput: string }
|
|
|
|
const props = defineProps<{
|
|
visible: boolean
|
|
withdrawId?: number
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:visible': [value: boolean]
|
|
success: []
|
|
}>()
|
|
|
|
const visibleProxy = computed({
|
|
get: () => props.visible,
|
|
set: (value: boolean) => emit('update:visible', value),
|
|
})
|
|
|
|
const loading = ref(false)
|
|
const saving = ref(false)
|
|
const info = ref<PhysicalWithdrawAdjustInfo | null>(null)
|
|
const formLines = ref<FormLine[]>([])
|
|
const lineErrors = ref<Record<number, string>>({})
|
|
|
|
watch(
|
|
() => [props.visible, props.withdrawId] as const,
|
|
([open, withdrawId]) => {
|
|
if (open && withdrawId) {
|
|
void loadInfo(withdrawId)
|
|
}
|
|
if (!open) {
|
|
resetState()
|
|
}
|
|
}
|
|
)
|
|
|
|
function resetState() {
|
|
info.value = null
|
|
formLines.value = []
|
|
lineErrors.value = {}
|
|
}
|
|
|
|
function formatQty(value: number, line: Pick<PhysicalWithdrawAdjustLine, 'unit' | 'allows_decimal'>) {
|
|
const decimals = line.allows_decimal ? 3 : 0
|
|
const formatted = Number(value).toLocaleString('fa-IR', {
|
|
minimumFractionDigits: 0,
|
|
maximumFractionDigits: decimals
|
|
})
|
|
return `${formatted} ${line.unit}`
|
|
}
|
|
|
|
function lineError(transactionId: number) {
|
|
return lineErrors.value[transactionId] ?? ''
|
|
}
|
|
|
|
function clearLineError(transactionId: number) {
|
|
if (lineErrors.value[transactionId]) {
|
|
const next = { ...lineErrors.value }
|
|
delete next[transactionId]
|
|
lineErrors.value = next
|
|
}
|
|
}
|
|
|
|
function parseValue(raw: string, line: PhysicalWithdrawAdjustLine): number | null {
|
|
const normalized = String(raw ?? '').trim().replace(',', '.')
|
|
if (!normalized) return null
|
|
const value = Number(normalized)
|
|
if (!Number.isFinite(value)) return null
|
|
if (!line.allows_decimal && Math.abs(value - Math.round(value)) > 0.000001) {
|
|
return null
|
|
}
|
|
return value
|
|
}
|
|
|
|
function validateLine(line: FormLine): string | null {
|
|
const value = parseValue(line.valueInput, line)
|
|
if (value == null) {
|
|
return line.allows_decimal ? 'مقدار معتبر وارد کنید' : 'مقدار باید عدد صحیح باشد'
|
|
}
|
|
if (value < line.min_value - 0.000001) {
|
|
return `حداقل مقدار ${formatQty(line.min_value, line)} است`
|
|
}
|
|
if (value > line.max_value + 0.000001) {
|
|
return `حداکثر مقدار ${formatQty(line.max_value, line)} است`
|
|
}
|
|
return null
|
|
}
|
|
|
|
async function loadInfo(withdrawId: number) {
|
|
loading.value = true
|
|
lineErrors.value = {}
|
|
try {
|
|
const data = await getPhysicalWithdrawAdjustInfo(withdrawId)
|
|
info.value = data
|
|
formLines.value = (data.lines ?? []).map((line) => ({
|
|
...line,
|
|
valueInput: String(line.current_value ?? '')
|
|
}))
|
|
} catch (e) {
|
|
console.error(e)
|
|
visibleProxy.value = false
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function close() {
|
|
visibleProxy.value = false
|
|
}
|
|
|
|
async function submit() {
|
|
if (!props.withdrawId || !formLines.value.length) return
|
|
|
|
const errors: Record<number, string> = {}
|
|
const items: Array<{ transaction_id: number; value: number }> = []
|
|
|
|
for (const line of formLines.value) {
|
|
const error = validateLine(line)
|
|
if (error) {
|
|
errors[line.transaction_id] = error
|
|
continue
|
|
}
|
|
const value = parseValue(line.valueInput, line)
|
|
if (value == null) {
|
|
errors[line.transaction_id] = 'مقدار معتبر وارد کنید'
|
|
continue
|
|
}
|
|
if (Math.abs(value - line.current_value) < 0.000001) {
|
|
continue
|
|
}
|
|
items.push({
|
|
transaction_id: line.transaction_id,
|
|
value
|
|
})
|
|
}
|
|
|
|
lineErrors.value = errors
|
|
if (Object.keys(errors).length) {
|
|
message.error('لطفاً مقادیر را اصلاح کنید')
|
|
return
|
|
}
|
|
if (!items.length) {
|
|
message.info('تغییری اعمال نشده است')
|
|
return
|
|
}
|
|
|
|
saving.value = true
|
|
try {
|
|
await adjustPhysicalWithdraw(props.withdrawId, { items })
|
|
message.success('مقادیر با موفقیت بهروزرسانی شد')
|
|
visibleProxy.value = false
|
|
emit('success')
|
|
} catch (e) {
|
|
console.error(e)
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.physical-withdraw-adjust-modal {
|
|
z-index: 1100 !important;
|
|
}
|
|
</style>
|