price handling
This commit is contained in:
Vendored
+1
@@ -10,6 +10,7 @@ export {}
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
_index: typeof import('./src/core/components/_sample/_index.vue')['default']
|
||||
AAlert: typeof import('ant-design-vue/es')['Alert']
|
||||
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
||||
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||
AButton: typeof import('ant-design-vue/es')['Button']
|
||||
|
||||
@@ -31,10 +31,13 @@
|
||||
<AButton
|
||||
v-if="showOk"
|
||||
type="primary"
|
||||
html-type="button"
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
:class="[{ 'col-span-8': showCancel }]"
|
||||
size="large"
|
||||
block
|
||||
@click="$emit('ok')"
|
||||
@click="handleOk"
|
||||
>
|
||||
{{ okText }}
|
||||
</AButton>
|
||||
@@ -79,10 +82,13 @@
|
||||
<AButton
|
||||
v-if="showOk"
|
||||
type="primary"
|
||||
html-type="button"
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
:class="[{ 'col-span-8': showCancel }]"
|
||||
size="large"
|
||||
block
|
||||
@click="$emit('ok')"
|
||||
@click="handleOk"
|
||||
>
|
||||
{{ okText }}
|
||||
</AButton>
|
||||
@@ -151,6 +157,10 @@ function handleCancel() {
|
||||
emit('close')
|
||||
visibleProxy.value = false
|
||||
}
|
||||
|
||||
function handleOk() {
|
||||
emit('ok')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
@@ -26,6 +26,19 @@ const headers = {
|
||||
Accept: 'application/json'
|
||||
}
|
||||
|
||||
function formatApiErrorMessage(raw: string): string {
|
||||
const unknownColumn = raw.match(/Unknown column '([^']+)'/)
|
||||
if (unknownColumn) {
|
||||
return `ستون «${unknownColumn[1]}» در دیتابیس وجود ندارد — سرور را بهروز کنید یا با پشتیبانی تماس بگیرید`
|
||||
}
|
||||
|
||||
if (raw.length > 220) {
|
||||
return `${raw.slice(0, 220)}…`
|
||||
}
|
||||
|
||||
return raw
|
||||
}
|
||||
|
||||
const _axios = axios.create({
|
||||
baseURL,
|
||||
headers
|
||||
@@ -82,7 +95,12 @@ _axios.interceptors.response.use(
|
||||
}
|
||||
} else if (error.response) {
|
||||
if (!error.config?.meta?.silent) {
|
||||
message.error('خطا در بارگذاری اطلاعات')
|
||||
const apiMessage = error.response.data?.message
|
||||
message.error(
|
||||
typeof apiMessage === 'string' && apiMessage.trim()
|
||||
? formatApiErrorMessage(apiMessage)
|
||||
: 'خطا در بارگذاری اطلاعات',
|
||||
)
|
||||
}
|
||||
}
|
||||
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
<Collapse class="grid grid-cols-1 gap-2" :activeKey="activeKey" @update:activeKey="$emit('update:activeKey', $event)">
|
||||
<BaseCollapse title="اطلاعات محصول" active-key="0" :actives="activeKey">
|
||||
<template #content>
|
||||
<a-form
|
||||
:model="form"
|
||||
ref="innerFormRef"
|
||||
autocomplete="off"
|
||||
scroll-to-first-error
|
||||
class="product-editor-form"
|
||||
>
|
||||
<div
|
||||
v-if="isMeltedCategory"
|
||||
class="md:flex flex-col gap-3 border-1 border-solid rounded-lg border-gray-200 p-3"
|
||||
@@ -46,10 +53,10 @@
|
||||
<div class="text-xs text-gray-500">
|
||||
مثبت = گرانتر، منفی = ارزانتر نسبت به قیمت مرجع.
|
||||
</div>
|
||||
<a-form-item label="قیمت صفر از provider" name="use_last_price_if_zero" class="!mb-0">
|
||||
<a-form-item label="قیمت صفر از مرجع" name="use_last_price_if_zero" class="!mb-0">
|
||||
<a-switch v-model:checked="form.use_last_price_if_zero" />
|
||||
<span class="mr-2 text-xs text-gray-500">
|
||||
اگر provider قیمت نداد (صفر)، با آخرین قیمت ذخیرهشده معامله شود
|
||||
اگر مرجع قیمت نداد (صفر)، با آخرین قیمت ذخیرهشده معامله شود
|
||||
</span>
|
||||
</a-form-item>
|
||||
</div>
|
||||
@@ -66,10 +73,10 @@
|
||||
<div class="text-xs text-gray-500">
|
||||
قیمت خرید/فروش از قیمت صفحه اصلی اپ محاسبه میشود. مثبت = گرانتر، منفی = ارزانتر.
|
||||
</div>
|
||||
<a-form-item label="قیمت صفر از provider" name="use_last_price_if_zero" class="!mb-0">
|
||||
<a-form-item label="قیمت صفر از مرجع" name="use_last_price_if_zero" class="!mb-0">
|
||||
<a-switch v-model:checked="form.use_last_price_if_zero" />
|
||||
<span class="mr-2 text-xs text-gray-500">
|
||||
اگر provider قیمت نداد (صفر)، با آخرین قیمت ذخیرهشده معامله شود
|
||||
اگر مرجع قیمت نداد (صفر)، با آخرین قیمت ذخیرهشده معامله شود
|
||||
</span>
|
||||
</a-form-item>
|
||||
</div>
|
||||
@@ -125,10 +132,10 @@
|
||||
<div class="text-xs text-gray-500">
|
||||
مثبت = گرانتر، منفی = ارزانتر نسبت به قیمت اتوماتیک. مثلاً ۵ یعنی ۵٪ بالاتر از مرجع.
|
||||
</div>
|
||||
<a-form-item label="قیمت صفر از provider" name="use_last_price_if_zero" class="!mb-0">
|
||||
<a-form-item label="قیمت صفر از مرجع" name="use_last_price_if_zero" class="!mb-0">
|
||||
<a-switch v-model:checked="form.use_last_price_if_zero" />
|
||||
<span class="mr-2 text-xs text-gray-500">
|
||||
اگر provider قیمت نداد (صفر)، با آخرین قیمت ذخیرهشده معامله شود
|
||||
اگر مرجع قیمت نداد (صفر)، با آخرین قیمت ذخیرهشده معامله شود
|
||||
</span>
|
||||
</a-form-item>
|
||||
</div>
|
||||
@@ -172,15 +179,20 @@
|
||||
<a-radio value="2">ندارد</a-radio>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<div v-if="weightOption === '1'" class="flex items-center">
|
||||
<span class="ml-4 ">{{ weightFieldLabel }} :</span>
|
||||
<BasicInput v-model:value="form.amount" type="number" />
|
||||
<div class="mt-1 mr-4" v-if="form.amount && form.amount < 1 && 0 < form.amount && tradeWeightUnit === 'گرم'">معادل {{ form.amount * 1000 }} سوت</div>
|
||||
<div v-if="weightOption === '1'" class="flex items-center md:col-span-2">
|
||||
<a-form-item
|
||||
class="!mb-0 w-full"
|
||||
:label="weightFieldLabel"
|
||||
name="amount"
|
||||
:rules="inputRulesNumber('وزن')"
|
||||
>
|
||||
<BasicInput v-model:value="form.amount" type="number" />
|
||||
</a-form-item>
|
||||
<div class="mt-1 mr-4 shrink-0" v-if="form.amount && form.amount < 1 && 0 < form.amount && tradeWeightUnit === 'گرم'">معادل {{ form.amount * 1000 }} سوت</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="grid md:grid-cols-2 gap-4">
|
||||
<div class="grid md:grid-cols-2 gap-4 mt-2">
|
||||
<a-form-item label="عنوان" name="title" :rules="inputRules()"><BasicInput v-model:value="form.title" /></a-form-item>
|
||||
<a-form-item label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.group_type" :items="categoryItems" valueName="value" keyName="key" />
|
||||
@@ -207,10 +219,10 @@
|
||||
>
|
||||
<BasicInput v-model:value="form.inventory" type="number" />
|
||||
</a-form-item>
|
||||
<a-form-item label="هزینه نقدی موقع خرید" name="cash_fee_buy" :rules="inputRules()">
|
||||
<a-form-item label="هزینه نقدی موقع خرید" name="cash_fee_buy" :rules="requiredPriceRules('هزینه نقدی موقع خرید')">
|
||||
<BasicInput v-model:value="form.cash_fee_buy" addon-before="ریال" type="price" />
|
||||
</a-form-item>
|
||||
<a-form-item label="هزینه نقدی موقع فروش" name="cash_fee_sell" :rules="inputRules()">
|
||||
<a-form-item label="هزینه نقدی موقع فروش" name="cash_fee_sell" :rules="requiredPriceRules('هزینه نقدی موقع فروش')">
|
||||
<BasicInput v-model:value="form.cash_fee_sell" addon-before="ریال" type="price" />
|
||||
</a-form-item>
|
||||
<a-form-item label="زمان شروع فعال سازی" name="auto_open_at">
|
||||
@@ -277,8 +289,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import { Collapse, TimePicker } from 'ant-design-vue'
|
||||
import { inputRules } from '@/utils/inputRules'
|
||||
import { inputRules, inputRulesNumber, requiredPriceRules } from '@/utils/inputRules'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import BaseCollapse from '@/components/BaseCollapse.vue'
|
||||
import IndexImageUploader from '@/components/ProductUploader/IndexImageUploader.vue'
|
||||
@@ -293,9 +306,10 @@ import { resolveTradeWeightUnit, weightFieldLabelForUnit } from '@/utils/weightU
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
|
||||
const innerFormRef = ref<FormInstance>()
|
||||
|
||||
const props = defineProps<{
|
||||
form: any
|
||||
formRef: any
|
||||
methodOption: string
|
||||
weightOption: string
|
||||
meltedPriceOption: string
|
||||
@@ -392,6 +406,19 @@ defineEmits<{
|
||||
(e: 'deleteIndexFile'): void
|
||||
(e: 'refreshPrices'): void
|
||||
}>()
|
||||
|
||||
defineExpose({
|
||||
async validateFields() {
|
||||
if (!innerFormRef.value) {
|
||||
throw {
|
||||
errorFields: [{ errors: ['فرم آماده نیست — لطفاً دوباره تلاش کنید'] }],
|
||||
}
|
||||
}
|
||||
|
||||
return innerFormRef.value.validateFields()
|
||||
},
|
||||
resetFields: () => innerFormRef.value?.resetFields(),
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -11,14 +11,23 @@
|
||||
:showOk="true"
|
||||
okText="ثبت"
|
||||
cancelText="انصراف"
|
||||
@ok="ok"
|
||||
@ok="submitProduct"
|
||||
@cancel="closeProductModal"
|
||||
@close="closeProductModal"
|
||||
>
|
||||
<div v-if="isMobile" class="mb-3 text-base font-700">{{ `${editId ? 'ویرایش' : 'افزودن'} محصول` }}</div>
|
||||
<a-alert
|
||||
v-if="submitError"
|
||||
type="error"
|
||||
:message="submitError"
|
||||
show-icon
|
||||
closable
|
||||
class="mb-3"
|
||||
@close="submitError = ''"
|
||||
/>
|
||||
<ProductEditorFormBody
|
||||
ref="editorFormRef"
|
||||
:form="form"
|
||||
:formRef="formRef"
|
||||
:methodOption="methodOption"
|
||||
:weightOption="weightOption"
|
||||
:activeKey="activeKey_"
|
||||
@@ -51,7 +60,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import locale from 'ant-design-vue/es/date-picker/locale/fa_IR'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -66,9 +74,11 @@ import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import type { Item } from '@/model/Item'
|
||||
import type { MethodePrices, Product } from '@/model/product'
|
||||
import ProductEditorFormBody from '@/pages/menuPages/products/base/components/ProductEditorFormBody.vue'
|
||||
import { validateProductSubmitInput } from '@/pages/menuPages/products/base/productFormValidation'
|
||||
import tbl from './components/table.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
import { referenceProviderTotal } from '@/utils/productEditorPrice'
|
||||
import {
|
||||
applyFileToUploadList,
|
||||
normalizeFileResource,
|
||||
@@ -90,6 +100,7 @@ import { resolveTradeWeightUnit } from '@/utils/weightUnit'
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
const loading = ref(false)
|
||||
const submitError = ref('')
|
||||
const table = ref<null | { fetch: () => void }>(null)
|
||||
const refreshTable = () => table.value?.fetch()
|
||||
|
||||
@@ -107,7 +118,7 @@ const form = ref<Partial<Product>>({
|
||||
auto_open_at: dayjs('00:00:00', 'HH:mm:ss'),
|
||||
metal_id: 1
|
||||
})
|
||||
const formRef = ref<FormInstance>()
|
||||
const editorFormRef = ref<InstanceType<typeof ProductEditorFormBody>>()
|
||||
|
||||
const customLocale = ref({
|
||||
...locale,
|
||||
@@ -143,8 +154,8 @@ let pricePollTimer: ReturnType<typeof setInterval> | undefined
|
||||
const pricesList = computed(() =>
|
||||
pricesData.value.map((x) => ({
|
||||
id: `${x.key ?? x.id ?? x.name}`,
|
||||
name: `${x.name} (${usePrice(x.price)})`
|
||||
}))
|
||||
name: `${x.name} (${usePrice(referenceProviderTotal(x))})`,
|
||||
})),
|
||||
)
|
||||
|
||||
async function fetchPrices() {
|
||||
@@ -329,17 +340,19 @@ async function edit(record: Product) {
|
||||
function closeProductModal() {
|
||||
addUserVisible.value = false
|
||||
editId = undefined
|
||||
submitError.value = ''
|
||||
}
|
||||
|
||||
async function add() {
|
||||
editId = undefined
|
||||
submitError.value = ''
|
||||
addUserVisible.value = true
|
||||
resetData()
|
||||
await loadCategorySelectItems()
|
||||
}
|
||||
|
||||
function resetData() {
|
||||
formRef.value?.resetFields()
|
||||
editorFormRef.value?.resetFields()
|
||||
ProductIndexImage.value = []
|
||||
methodOption.value = '1'
|
||||
weightOption.value = '1'
|
||||
@@ -349,9 +362,9 @@ function resetData() {
|
||||
group_type: '',
|
||||
amount: undefined,
|
||||
percent_fee_buy: 0,
|
||||
cash_fee_buy: 0,
|
||||
cash_fee_buy: undefined,
|
||||
percent_fee_sell: 0,
|
||||
cash_fee_sell: 0,
|
||||
cash_fee_sell: undefined,
|
||||
price_offset_percent: 0,
|
||||
metal_id: 1,
|
||||
direct_price: 0,
|
||||
@@ -369,29 +382,59 @@ function resetData() {
|
||||
}
|
||||
}
|
||||
|
||||
async function ok() {
|
||||
if (!isMeltedTradeGroup(form.value.group_type ?? '')) {
|
||||
if (weightOption.value === '1') {
|
||||
const amount = Number(form.value.amount)
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
message.error('لطفاً وزن محصول را وارد کنید')
|
||||
return
|
||||
}
|
||||
}
|
||||
function showSubmitError(text: string) {
|
||||
submitError.value = text
|
||||
message.error(text, 4)
|
||||
}
|
||||
|
||||
async function submitProduct() {
|
||||
try {
|
||||
await saveProduct()
|
||||
} catch (err: unknown) {
|
||||
console.error(err)
|
||||
const text = err instanceof Error ? err.message : 'خطای غیرمنتظره در ثبت محصول'
|
||||
showSubmitError(text)
|
||||
}
|
||||
}
|
||||
|
||||
async function saveProduct() {
|
||||
if (!activeKey_.value.includes('0')) {
|
||||
activeKey_.value = ['0', ...activeKey_.value]
|
||||
await nextTick()
|
||||
}
|
||||
|
||||
try {
|
||||
await formRef.value?.validateFields()
|
||||
} catch {
|
||||
const businessError = validateProductSubmitInput({
|
||||
form: form.value as Record<string, unknown>,
|
||||
methodOption: methodOption.value,
|
||||
weightOption: weightOption.value,
|
||||
meltedPriceOption: meltedPriceOption.value,
|
||||
isEdit: Boolean(editId),
|
||||
})
|
||||
if (businessError) {
|
||||
showSubmitError(businessError)
|
||||
return
|
||||
}
|
||||
|
||||
if (!editorFormRef.value) {
|
||||
showSubmitError('فرم آماده نیست — لطفاً مودال را ببندید و دوباره باز کنید')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await editorFormRef.value.validateFields()
|
||||
} catch (err: unknown) {
|
||||
const first = (err as { errorFields?: Array<{ errors?: string[] }> })?.errorFields?.[0]?.errors?.[0]
|
||||
showSubmitError(first || 'لطفاً فیلدهای الزامی را تکمیل کنید')
|
||||
return
|
||||
}
|
||||
|
||||
submitError.value = ''
|
||||
loading.value = true
|
||||
try {
|
||||
const payload = cloneDeep(form.value)
|
||||
const limitError = validateProductOrderLimitForm(payload)
|
||||
if (limitError) {
|
||||
message.error(limitError)
|
||||
showSubmitError(limitError)
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
@@ -433,6 +476,8 @@ async function ok() {
|
||||
|
||||
if (isStoreApp() && !isMeltedTradeGroup(payload.group_type)) {
|
||||
payload.inventory = Math.max(0, Number(payload.inventory ?? 0))
|
||||
} else {
|
||||
delete payload.inventory
|
||||
}
|
||||
|
||||
payload.auto_open_at = payload.auto_open_at
|
||||
@@ -463,12 +508,14 @@ async function ok() {
|
||||
|
||||
addUserVisible.value = false
|
||||
editId = undefined
|
||||
submitError.value = ''
|
||||
refreshTable()
|
||||
resetData()
|
||||
message.success('عملیات با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
} catch (e: unknown) {
|
||||
console.error(e)
|
||||
message.error('خطا در ثبت اطلاعات')
|
||||
const apiMessage = (e as { response?: { data?: { message?: string } } })?.response?.data?.message
|
||||
showSubmitError(apiMessage || 'خطا در ثبت اطلاعات')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { isMeltedTradeGroup } from '@/utils/productCategories'
|
||||
|
||||
function isBlank(value: unknown) {
|
||||
return value === undefined || value === null || value === ''
|
||||
}
|
||||
|
||||
function isMissingNumber(value: unknown) {
|
||||
if (isBlank(value)) {
|
||||
return true
|
||||
}
|
||||
|
||||
const num = Number(value)
|
||||
return !Number.isFinite(num)
|
||||
}
|
||||
|
||||
export function validateProductSubmitInput(input: {
|
||||
form: Record<string, unknown>
|
||||
methodOption: string
|
||||
weightOption: string
|
||||
meltedPriceOption: string
|
||||
isEdit: boolean
|
||||
}): string | null {
|
||||
const { form, methodOption, weightOption, meltedPriceOption, isEdit } = input
|
||||
const groupType = String(form.group_type ?? '')
|
||||
const melted = isMeltedTradeGroup(groupType)
|
||||
|
||||
if (!String(form.title ?? '').trim()) {
|
||||
return 'عنوان محصول الزامی است'
|
||||
}
|
||||
|
||||
if (!groupType) {
|
||||
return 'دستهبندی الزامی است'
|
||||
}
|
||||
|
||||
if (isBlank(form.metal_id)) {
|
||||
return 'جنس فلز الزامی است'
|
||||
}
|
||||
|
||||
if (melted || weightOption === '1') {
|
||||
if (!String(form.unit ?? '').trim()) {
|
||||
return 'واحد الزامی است'
|
||||
}
|
||||
}
|
||||
|
||||
if (!melted && weightOption === '1') {
|
||||
const amount = Number(form.amount)
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
return 'وزن محصول را وارد کنید'
|
||||
}
|
||||
}
|
||||
|
||||
if (melted) {
|
||||
if (meltedPriceOption === 'fixed' && isMissingNumber(form.price)) {
|
||||
return 'قیمت ثابت را وارد کنید'
|
||||
}
|
||||
if (meltedPriceOption === 'reference' && !String(form.reading_method ?? '').trim()) {
|
||||
return 'مرجع قیمت را انتخاب کنید'
|
||||
}
|
||||
} else if (methodOption === '1') {
|
||||
if (isMissingNumber(form.price)) {
|
||||
return 'قیمت دستی را وارد کنید'
|
||||
}
|
||||
} else if (methodOption === '2') {
|
||||
if (!String(form.reading_method ?? '').trim()) {
|
||||
return 'مرجع قیمت را انتخاب کنید'
|
||||
}
|
||||
}
|
||||
|
||||
if (isBlank(form.cash_fee_buy)) {
|
||||
return 'هزینه نقدی موقع خرید را وارد کنید'
|
||||
}
|
||||
|
||||
if (isBlank(form.cash_fee_sell)) {
|
||||
return 'هزینه نقدی موقع فروش را وارد کنید'
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -6,6 +6,29 @@ export function inputRules(title?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
/** فیلد قیمت/عدد — خالی رد میشود، صفر مجاز است */
|
||||
export function requiredPriceRules(title?: string) {
|
||||
const label = title || 'مورد بالا'
|
||||
|
||||
return [
|
||||
{
|
||||
validator(_rule: unknown, value: unknown) {
|
||||
if (value === undefined || value === null || String(value).trim() === '') {
|
||||
return Promise.reject(`پر کردن ${label} الزامی است`)
|
||||
}
|
||||
|
||||
const num = Number(String(value).replaceAll(',', ''))
|
||||
if (!Number.isFinite(num)) {
|
||||
return Promise.reject('لطفاً عدد معتبر وارد کنید')
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
},
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export function inputRulesNumber(title?: string) {
|
||||
if (title) {
|
||||
return [
|
||||
|
||||
@@ -23,6 +23,10 @@ export function normalizeReferenceBase(entry: MethodePrices): { base: number; pr
|
||||
let base = Number(entry.price) || 0
|
||||
const profit = Number(entry.profit) || 0
|
||||
|
||||
if (base <= 0) {
|
||||
return { base: 0, profit: 0 }
|
||||
}
|
||||
|
||||
if (entry.name === GOLD_DOT_REFERENCE_NAME) {
|
||||
base = base / 4.3318
|
||||
}
|
||||
@@ -39,6 +43,20 @@ export function adjustedProductPrice(
|
||||
return Math.round(reference * (1 + (Number(offsetPercent) || 0) / 100))
|
||||
}
|
||||
|
||||
/** جمع مرجع — وقتی price صفر است profit نادیده گرفته میشود (مقدار قدیمی) */
|
||||
export function referenceProviderTotal(entry?: Pick<MethodePrices, 'price' | 'profit'> | null): number {
|
||||
if (!entry) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const price = Number(entry.price ?? 0)
|
||||
if (price <= 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return price + Number(entry.profit ?? 0)
|
||||
}
|
||||
|
||||
export function resolveAppBasePrice(lastPrice?: Partial<last> & { price?: number } | null): number {
|
||||
if (!lastPrice) return 0
|
||||
|
||||
@@ -118,7 +136,7 @@ export function computeProductEditorPricePreview(input: {
|
||||
: adjustedProductPrice(fallback, 0, offsetPercent)
|
||||
return { status: 'ready', amount, source: 'آخرین قیمت ذخیرهشده' }
|
||||
}
|
||||
return { status: 'empty', message: 'قیمت provider صفر است' }
|
||||
return { status: 'empty', message: 'قیمت مرجع صفر است' }
|
||||
}
|
||||
|
||||
if (!input.form.reading_method) {
|
||||
@@ -140,7 +158,7 @@ export function computeProductEditorPricePreview(input: {
|
||||
)
|
||||
return fallback > 0
|
||||
? { status: 'ready', amount: fallback, source: 'آخرین قیمت ذخیرهشده' }
|
||||
: { status: 'empty', message: 'قیمت provider صفر است' }
|
||||
: { status: 'empty', message: 'قیمت مرجع صفر است' }
|
||||
}
|
||||
|
||||
if (input.methodOption === '1') {
|
||||
@@ -169,5 +187,5 @@ export function computeProductEditorPricePreview(input: {
|
||||
)
|
||||
return fallback > 0
|
||||
? { status: 'ready', amount: fallback, source: 'آخرین قیمت ذخیرهشده' }
|
||||
: { status: 'empty', message: 'قیمت provider صفر است' }
|
||||
: { status: 'empty', message: 'قیمت مرجع صفر است' }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user