fix edits for borsemes
This commit is contained in:
+22
-2
@@ -31,15 +31,33 @@ export interface DashboardMaster {
|
|||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DashboardQuantityProductProfit {
|
||||||
|
product_id: number
|
||||||
|
title: string
|
||||||
|
group_type?: string | null
|
||||||
|
/** کارمزد کسرشده از کیف همان محصول (گرم/واحد) */
|
||||||
|
grams: number
|
||||||
|
value_rial: number
|
||||||
|
unit?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DashboardDailyProductProfit {
|
||||||
|
product_id: number
|
||||||
|
title: string
|
||||||
|
grams: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface DashboardDailyProfit {
|
export interface DashboardDailyProfit {
|
||||||
date: string
|
date: string
|
||||||
/** سود طلایی روز: کارمزد کسرشده از طلا (گرم) */
|
/** سود فلزی روز: کارمزد کسرشده از فلز (گرم) */
|
||||||
profit_gold: number
|
profit_gold: number
|
||||||
/** سود ریالی روز: کارمزد کسرشده از ریال */
|
/** سود ریالی روز: کارمزد کسرشده از ریال */
|
||||||
profit_rial: number
|
profit_rial: number
|
||||||
/** سود اختلاف قیمت روز (ریال) */
|
/** سود اختلاف قیمت روز (ریال) */
|
||||||
spread_rial: number
|
spread_rial: number
|
||||||
order_count: number
|
order_count: number
|
||||||
|
/** تفکیک سود مقداری بهازای هر محصول در همان روز */
|
||||||
|
profit_by_product?: DashboardDailyProductProfit[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DashboardFinance {
|
export interface DashboardFinance {
|
||||||
@@ -54,7 +72,7 @@ export interface DashboardFinance {
|
|||||||
Gifts?: string
|
Gifts?: string
|
||||||
GiftsPrise?: number
|
GiftsPrise?: number
|
||||||
sumAll?: number
|
sumAll?: number
|
||||||
/** سود طلایی: مجموع کارمزدهای کسرشده از طلا (گرم) */
|
/** سود طلایی/فلزی: مجموع کارمزدهای کسرشده از فلز (گرم) */
|
||||||
profitGoldGrams?: number
|
profitGoldGrams?: number
|
||||||
/** سود ریالی: مجموع کارمزدهای کسرشده از ریال */
|
/** سود ریالی: مجموع کارمزدهای کسرشده از ریال */
|
||||||
profitRial?: number
|
profitRial?: number
|
||||||
@@ -68,6 +86,8 @@ export interface DashboardFinance {
|
|||||||
profitGoldConvertedGram?: number
|
profitGoldConvertedGram?: number
|
||||||
/** ارزش ریالی سود طلایی با قیمت روز */
|
/** ارزش ریالی سود طلایی با قیمت روز */
|
||||||
profitGoldValueRial?: number
|
profitGoldValueRial?: number
|
||||||
|
/** سود کارمزد فلزی بهازای هر محصول مقداری/آبشده */
|
||||||
|
profitQuantityProducts?: DashboardQuantityProductProfit[]
|
||||||
/** تفکیک روزانهٔ سود طلایی/ریالی */
|
/** تفکیک روزانهٔ سود طلایی/ریالی */
|
||||||
daily?: DashboardDailyProfit[]
|
daily?: DashboardDailyProfit[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
<div class="mb-2 flex flex-wrap items-start justify-between gap-2">
|
<div class="mb-2 flex flex-wrap items-start justify-between gap-2">
|
||||||
<DashboardSectionHeader
|
<DashboardSectionHeader
|
||||||
class="!mb-0 min-w-0 flex-1"
|
class="!mb-0 min-w-0 flex-1"
|
||||||
title="سود طلایی و ریالی"
|
:title="sectionTitle"
|
||||||
:help="sectionHelp"
|
:help="sectionHelp"
|
||||||
/>
|
/>
|
||||||
<a-button type="primary" size="small" @click="openSanad">
|
<a-button v-if="showProfitConversion" type="primary" size="small" @click="openSanad">
|
||||||
ثبت سند تبدیل سود به طلا
|
ثبت سند تبدیل سود به طلا
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,22 +40,48 @@
|
|||||||
<!-- محتوای اصلی -->
|
<!-- محتوای اصلی -->
|
||||||
<div class="dashboard-profit-split__main">
|
<div class="dashboard-profit-split__main">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<!-- دو کارت خلاصه -->
|
<!-- کارتهای خلاصه: هر محصول مقداری + سود ریالی -->
|
||||||
<div class="dashboard-profit-split__cards">
|
<div class="dashboard-profit-split__cards">
|
||||||
<article class="dashboard-profit-split__card dashboard-profit-split__card--gold">
|
<article
|
||||||
|
v-for="item in quantityProductCards"
|
||||||
|
:key="item.product_id"
|
||||||
|
class="dashboard-profit-split__card dashboard-profit-split__card--gold"
|
||||||
|
>
|
||||||
<DashboardLabelWithHelp
|
<DashboardLabelWithHelp
|
||||||
label="سود طلایی"
|
:label="`سود ${item.title}`"
|
||||||
|
:help="quantityProductHelp"
|
||||||
|
size="sm"
|
||||||
|
label-class="dashboard-profit-split__card-title"
|
||||||
|
/>
|
||||||
|
<p class="dashboard-profit-split__card-hint">
|
||||||
|
کارمزد کسرشده از کیف این محصول در بازهٔ انتخابی
|
||||||
|
</p>
|
||||||
|
<p class="dashboard-profit-split__value">
|
||||||
|
<span class="dashboard-profit-split__amount">{{ formatGram(item.grams) }}</span>
|
||||||
|
<span class="dashboard-profit-split__unit">{{ item.unit || 'گرم' }}</span>
|
||||||
|
</p>
|
||||||
|
<p class="dashboard-profit-split__sub">≈ {{ formatDashboardAmount(item.value_rial) }} ریال</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article
|
||||||
|
v-if="!quantityProductCards.length"
|
||||||
|
class="dashboard-profit-split__card dashboard-profit-split__card--gold"
|
||||||
|
>
|
||||||
|
<DashboardLabelWithHelp
|
||||||
|
:label="metalProfitLabel"
|
||||||
:help="goldHelp"
|
:help="goldHelp"
|
||||||
size="sm"
|
size="sm"
|
||||||
label-class="dashboard-profit-split__card-title"
|
label-class="dashboard-profit-split__card-title"
|
||||||
/>
|
/>
|
||||||
<p class="dashboard-profit-split__card-hint">کارمزد کسرشده از طلا در بازهٔ انتخابی</p>
|
<p class="dashboard-profit-split__card-hint">
|
||||||
|
کارمزد کسرشده از {{ metalShortLabel }} در بازهٔ انتخابی
|
||||||
|
</p>
|
||||||
<p class="dashboard-profit-split__value">
|
<p class="dashboard-profit-split__value">
|
||||||
<span class="dashboard-profit-split__amount">{{ formatGram(profitGoldGrams) }}</span>
|
<span class="dashboard-profit-split__amount">{{ formatGram(profitGoldGrams) }}</span>
|
||||||
<span class="dashboard-profit-split__unit">گرم</span>
|
<span class="dashboard-profit-split__unit">گرم</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="dashboard-profit-split__sub">≈ {{ formatDashboardAmount(profitGoldValueRial) }} ریال</p>
|
<p class="dashboard-profit-split__sub">≈ {{ formatDashboardAmount(profitGoldValueRial) }} ریال</p>
|
||||||
<p v-if="profitGoldConvertedGram > 0" class="dashboard-profit-split__note">
|
<p v-if="showProfitConversion && profitGoldConvertedGram > 0" class="dashboard-profit-split__note">
|
||||||
شامل {{ formatGram(profitGoldConvertedGram) }} گرم از تبدیل سود ریالی
|
شامل {{ formatGram(profitGoldConvertedGram) }} گرم از تبدیل سود ریالی
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
@@ -73,7 +99,7 @@
|
|||||||
<span class="dashboard-profit-split__unit">ریال</span>
|
<span class="dashboard-profit-split__unit">ریال</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="dashboard-profit-split__sub">
|
<p class="dashboard-profit-split__sub">
|
||||||
<template v-if="profitRialConverted > 0">
|
<template v-if="showProfitConversion && profitRialConverted > 0">
|
||||||
{{ formatGram(profitGoldConvertedGram) }} گرم تبدیلشده به طلا
|
{{ formatGram(profitGoldConvertedGram) }} گرم تبدیلشده به طلا
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -89,7 +115,16 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr class="border-b text-xs">
|
<tr class="border-b text-xs">
|
||||||
<th class="px-2 py-2 text-right">تاریخ</th>
|
<th class="px-2 py-2 text-right">تاریخ</th>
|
||||||
<th class="px-2 py-2 text-right">سود طلایی (گرم)</th>
|
<th
|
||||||
|
v-for="col in quantityProductColumns"
|
||||||
|
:key="col.product_id"
|
||||||
|
class="px-2 py-2 text-right whitespace-nowrap"
|
||||||
|
>
|
||||||
|
سود {{ col.title }}
|
||||||
|
</th>
|
||||||
|
<th v-if="!quantityProductColumns.length" class="px-2 py-2 text-right">
|
||||||
|
{{ metalProfitLabel }} (گرم)
|
||||||
|
</th>
|
||||||
<th class="px-2 py-2 text-right">سود ریالی (ریال)</th>
|
<th class="px-2 py-2 text-right">سود ریالی (ریال)</th>
|
||||||
<th class="px-2 py-2 text-right">سود اختلاف قیمت (ریال)</th>
|
<th class="px-2 py-2 text-right">سود اختلاف قیمت (ریال)</th>
|
||||||
<th class="px-2 py-2 text-right">تعداد سفارش</th>
|
<th class="px-2 py-2 text-right">تعداد سفارش</th>
|
||||||
@@ -97,7 +132,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-if="!dailyRows.length">
|
<tr v-if="!dailyRows.length">
|
||||||
<td colspan="5" class="dashboard-text-muted px-2 py-4 text-center text-xs">
|
<td :colspan="tableColspan" class="dashboard-text-muted px-2 py-4 text-center text-xs">
|
||||||
در این بازه معاملهای ثبت نشده
|
در این بازه معاملهای ثبت نشده
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -107,7 +142,19 @@
|
|||||||
class="border-b hover:bg-[var(--admin-bg-hover)]"
|
class="border-b hover:bg-[var(--admin-bg-hover)]"
|
||||||
>
|
>
|
||||||
<td class="whitespace-nowrap px-2 py-2 text-xs">{{ jalaliDate(row.date) }}</td>
|
<td class="whitespace-nowrap px-2 py-2 text-xs">{{ jalaliDate(row.date) }}</td>
|
||||||
<td class="px-2 py-2 font-semibold" :class="row.profit_gold > 0 ? 'dashboard-profit' : ''">
|
<td
|
||||||
|
v-for="col in quantityProductColumns"
|
||||||
|
:key="`${row.date}-${col.product_id}`"
|
||||||
|
class="px-2 py-2 font-semibold"
|
||||||
|
:class="dailyProductGrams(row, col.product_id) > 0 ? 'dashboard-profit' : ''"
|
||||||
|
>
|
||||||
|
{{ formatGram(dailyProductGrams(row, col.product_id)) }}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
v-if="!quantityProductColumns.length"
|
||||||
|
class="px-2 py-2 font-semibold"
|
||||||
|
:class="row.profit_gold > 0 ? 'dashboard-profit' : ''"
|
||||||
|
>
|
||||||
{{ formatGram(row.profit_gold) }}
|
{{ formatGram(row.profit_gold) }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-2 py-2 font-semibold" :class="row.profit_rial > 0 ? 'dashboard-profit' : ''">
|
<td class="px-2 py-2 font-semibold" :class="row.profit_rial > 0 ? 'dashboard-profit' : ''">
|
||||||
@@ -132,8 +179,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- سندهای تبدیل سود به طلا -->
|
<!-- سندهای تبدیل سود به طلا — فقط وقتی main_metal طلاست -->
|
||||||
<div class="dashboard-profit-split__sanad mt-3">
|
<div v-if="showProfitConversion" class="dashboard-profit-split__sanad mt-3">
|
||||||
<div class="mb-2 flex flex-wrap items-center justify-between gap-2">
|
<div class="mb-2 flex flex-wrap items-center justify-between gap-2">
|
||||||
<span class="text-sm font-bold dashboard-text">سندهای تبدیل سود ریالی به طلا</span>
|
<span class="text-sm font-bold dashboard-text">سندهای تبدیل سود ریالی به طلا</span>
|
||||||
<span v-if="conversions.length" class="dashboard-text-muted text-xs">
|
<span v-if="conversions.length" class="dashboard-text-muted text-xs">
|
||||||
@@ -189,6 +236,7 @@
|
|||||||
|
|
||||||
<!-- مدال ثبت سند -->
|
<!-- مدال ثبت سند -->
|
||||||
<BasicActionSheet
|
<BasicActionSheet
|
||||||
|
v-if="showProfitConversion"
|
||||||
v-model:visible="sanadVisible"
|
v-model:visible="sanadVisible"
|
||||||
title="ثبت سند تبدیل سود به طلا"
|
title="ثبت سند تبدیل سود به طلا"
|
||||||
modal-width="24rem"
|
modal-width="24rem"
|
||||||
@@ -259,7 +307,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
|
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
|
||||||
@@ -273,9 +321,10 @@ import {
|
|||||||
getProfitConversions,
|
getProfitConversions,
|
||||||
storeProfitConversion,
|
storeProfitConversion,
|
||||||
} from '@/api/dashboard'
|
} from '@/api/dashboard'
|
||||||
import type { DashboardFinance, ProfitConversion } from '@/model/dashboard'
|
import type { DashboardDailyProfit, DashboardFinance, ProfitConversion } from '@/model/dashboard'
|
||||||
import type { DashboardHelp } from '@/pages/dashboard/dashboardSectionCopy'
|
import type { DashboardHelp } from '@/pages/dashboard/dashboardSectionCopy'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
|
import { getMainMetal, m } from '@/utils/metalLabels'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
/** قیمت هر گرم برای مقدار پیشفرض سند */
|
/** قیمت هر گرم برای مقدار پیشفرض سند */
|
||||||
@@ -298,19 +347,39 @@ const rangeEnd = ref(defaultEnd())
|
|||||||
const activeStart = ref(rangeStart.value)
|
const activeStart = ref(rangeStart.value)
|
||||||
const activeEnd = ref(rangeEnd.value)
|
const activeEnd = ref(rangeEnd.value)
|
||||||
|
|
||||||
|
const isCopperMainMetal = computed(() => getMainMetal().trim() === 'مس')
|
||||||
|
/** برای مس فقط نمایش سود — بدون ثبت سند تبدیل */
|
||||||
|
const showProfitConversion = computed(() => !isCopperMainMetal.value)
|
||||||
|
|
||||||
|
const metalShortLabel = computed(() => getMainMetal())
|
||||||
|
const metalProfitLabel = computed(() => m('سود طلایی'))
|
||||||
|
const sectionTitle = computed(() =>
|
||||||
|
quantityProductCards.value.length > 0 ? 'سود مقداری و ریالی' : m('سود طلایی و ریالی'),
|
||||||
|
)
|
||||||
|
|
||||||
const sectionHelp: DashboardHelp = {
|
const sectionHelp: DashboardHelp = {
|
||||||
description:
|
description:
|
||||||
'کارمزد معاملات بر اساس فیلدی که مشتری وارد میکند تفکیک میشود: وقتی مشتری «مبلغ ریالی» ثابت میزند کارمزد از طلا کسر میشود (سود طلایی)، و وقتی «وزن» ثابت میزند کارمزد از ریال کسر میشود (سود ریالی). اعداد بر اساس بازهٔ انتخابی همین بخش محاسبه میشوند.',
|
'کارمزد معاملات بر اساس فیلدی که مشتری وارد میکند تفکیک میشود: وقتی مشتری «مبلغ ریالی» ثابت میزند کارمزد از کیف همان محصول مقداری/آبشده کسر میشود، و وقتی «وزن» ثابت میزند کارمزد از ریال کسر میشود (سود ریالی). برای هر محصول مقداری سود جداگانه نمایش داده میشود.',
|
||||||
formula: 'سود طلایی = جمع کارمزدِ کسرشده از طلا (گرم) + گرم سندهای تبدیل سود ریالی | سود ریالی = جمع کارمزدِ کسرشده از ریال − ریال تبدیلشده به طلا',
|
formula:
|
||||||
|
'سود هر محصول مقداری = جمع کارمزدِ کسرشده از کیف همان محصول | سود ریالی = جمع کارمزدِ کسرشده از ریال',
|
||||||
}
|
}
|
||||||
const goldHelp: DashboardHelp = {
|
const goldHelp: DashboardHelp = {
|
||||||
description: 'مجموع کارمزدهای کسرشده از طلا بهعلاوهٔ گرم سندهای «تبدیل سود ریالی به طلا» در همین بازه.',
|
description: m('مجموع کارمزدهای کسرشده از طلا بهعلاوهٔ گرم سندهای «تبدیل سود ریالی به طلا» در همین بازه.'),
|
||||||
formula: 'کارمزد طلا (گرم) + گرم تبدیلشده از سود ریالی',
|
formula: m('کارمزد طلا (گرم) + گرم تبدیلشده از سود ریالی'),
|
||||||
}
|
}
|
||||||
const rialHelp: DashboardHelp = {
|
const quantityProductHelp: DashboardHelp = {
|
||||||
description: 'سود ریالی باقیمانده پس از کسر سندهای تبدیل به طلا در همین بازه.',
|
description:
|
||||||
formula: 'کارمزد ریالی − ریال تبدیلشده به طلا',
|
'وقتی مشتری مبلغ ریالی ثابت میزند، کارمزد از کیف همین محصول مقداری کم میشود؛ این عدد مجموع همان کارمزدها در بازه است.',
|
||||||
|
formula: 'جمع service_charge سفارشهای مبلغمحور همین محصول',
|
||||||
}
|
}
|
||||||
|
const rialHelp = computed<DashboardHelp>(() => ({
|
||||||
|
description: showProfitConversion.value
|
||||||
|
? 'سود ریالی باقیمانده پس از کسر سندهای تبدیل به طلا در همین بازه.'
|
||||||
|
: 'مجموع کارمزدهای کسرشده از ریال در همین بازه.',
|
||||||
|
formula: showProfitConversion.value
|
||||||
|
? 'کارمزد ریالی − ریال تبدیلشده به طلا'
|
||||||
|
: 'کارمزد ریالی',
|
||||||
|
}))
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const finance = ref<DashboardFinance | null>(null)
|
const finance = ref<DashboardFinance | null>(null)
|
||||||
@@ -320,7 +389,17 @@ const profitRial = computed(() => Number(finance.value?.profitRial ?? 0))
|
|||||||
const profitGoldValueRial = computed(() => Number(finance.value?.profitGoldValueRial ?? 0))
|
const profitGoldValueRial = computed(() => Number(finance.value?.profitGoldValueRial ?? 0))
|
||||||
const profitRialConverted = computed(() => Number(finance.value?.profitRialConverted ?? 0))
|
const profitRialConverted = computed(() => Number(finance.value?.profitRialConverted ?? 0))
|
||||||
const profitGoldConvertedGram = computed(() => Number(finance.value?.profitGoldConvertedGram ?? 0))
|
const profitGoldConvertedGram = computed(() => Number(finance.value?.profitGoldConvertedGram ?? 0))
|
||||||
|
const quantityProductCards = computed(() => finance.value?.profitQuantityProducts ?? [])
|
||||||
|
const quantityProductColumns = computed(() => quantityProductCards.value)
|
||||||
const dailyRows = computed(() => finance.value?.daily ?? [])
|
const dailyRows = computed(() => finance.value?.daily ?? [])
|
||||||
|
const tableColspan = computed(
|
||||||
|
() => 4 + (quantityProductColumns.value.length || 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
function dailyProductGrams(row: DashboardDailyProfit, productId: number) {
|
||||||
|
const match = (row.profit_by_product ?? []).find((p) => Number(p.product_id) === productId)
|
||||||
|
return Number(match?.grams ?? 0)
|
||||||
|
}
|
||||||
|
|
||||||
// صفحهبندی جدول روزانه
|
// صفحهبندی جدول روزانه
|
||||||
const dailyPage = ref(1)
|
const dailyPage = ref(1)
|
||||||
@@ -416,6 +495,12 @@ async function loadFinance() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadConversions() {
|
async function loadConversions() {
|
||||||
|
if (!showProfitConversion.value) {
|
||||||
|
conversions.value = []
|
||||||
|
conversionsTotalRial.value = 0
|
||||||
|
conversionsTotalGram.value = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
conversionsLoading.value = true
|
conversionsLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await getProfitConversions(activeStart.value, activeEnd.value)
|
const res = await getProfitConversions(activeStart.value, activeEnd.value)
|
||||||
@@ -537,16 +622,10 @@ reloadAll()
|
|||||||
|
|
||||||
.dashboard-profit-split__cards {
|
.dashboard-profit-split__cards {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.dashboard-profit-split__cards {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-profit-split__card {
|
.dashboard-profit-split__card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<span class="dashboard-income-summary__amount">{{ row.amount }}</span>
|
<span class="dashboard-income-summary__amount">{{ row.amount }}</span>
|
||||||
<span class="dashboard-income-summary__unit">{{ unit }}</span>
|
<span class="dashboard-income-summary__unit">{{ unit }}</span>
|
||||||
</p>
|
</p>
|
||||||
<!-- تفکیک کارمزد به ریالی + طلایی -->
|
<!-- تفکیک کارمزد به ریالی + مقداری (بهازای محصول) -->
|
||||||
<div v-if="row.breakdown" class="dashboard-income-summary__breakdown">
|
<div v-if="row.breakdown" class="dashboard-income-summary__breakdown">
|
||||||
<span class="dashboard-income-summary__breakdown-row">
|
<span class="dashboard-income-summary__breakdown-row">
|
||||||
<span>ریالی</span>
|
<span>ریالی</span>
|
||||||
@@ -33,8 +33,21 @@
|
|||||||
{{ formatDashboardAmount(rialFee) }} ریال
|
{{ formatDashboardAmount(rialFee) }} ریال
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="dashboard-income-summary__breakdown-row">
|
<template v-if="quantityProducts.length">
|
||||||
<span>طلایی</span>
|
<span
|
||||||
|
v-for="item in quantityProducts"
|
||||||
|
:key="item.product_id"
|
||||||
|
class="dashboard-income-summary__breakdown-row"
|
||||||
|
>
|
||||||
|
<span>{{ item.title }}</span>
|
||||||
|
<span class="dashboard-income-summary__breakdown-val">
|
||||||
|
{{ formatGram(item.grams) }} {{ item.unit || 'گرم' }} ≈
|
||||||
|
{{ formatDashboardAmount(item.value_rial) }} ریال
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<span v-else class="dashboard-income-summary__breakdown-row">
|
||||||
|
<span>{{ metalFeeLabel }}</span>
|
||||||
<span class="dashboard-income-summary__breakdown-val">
|
<span class="dashboard-income-summary__breakdown-val">
|
||||||
{{ formatGram(goldFeeGrams) }} گرم ≈ {{ formatDashboardAmount(goldFeeRial) }} ریال
|
{{ formatGram(goldFeeGrams) }} گرم ≈ {{ formatDashboardAmount(goldFeeRial) }} ریال
|
||||||
</span>
|
</span>
|
||||||
@@ -53,6 +66,7 @@ import { dashboardTradingIncomeSummary, dashboardKpi } from '@/pages/dashboard/d
|
|||||||
import { DASHBOARD_RIAL_UNIT, formatDashboardAmount } from '@/pages/dashboard/dashboardFormat'
|
import { DASHBOARD_RIAL_UNIT, formatDashboardAmount } from '@/pages/dashboard/dashboardFormat'
|
||||||
import type { DashboardFinance } from '@/model/dashboard'
|
import type { DashboardFinance } from '@/model/dashboard'
|
||||||
import type { DashboardHelp } from '@/pages/dashboard/dashboardSectionCopy'
|
import type { DashboardHelp } from '@/pages/dashboard/dashboardSectionCopy'
|
||||||
|
import { m } from '@/utils/metalLabels'
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@@ -70,10 +84,12 @@ const unit = DASHBOARD_RIAL_UNIT
|
|||||||
const priceDiffRial = computed(() => Number(props.finance?.fullPriceSood ?? 0))
|
const priceDiffRial = computed(() => Number(props.finance?.fullPriceSood ?? 0))
|
||||||
// کارمزد ریالی: از معاملههای وزنمحور (fee_asset = rial)
|
// کارمزد ریالی: از معاملههای وزنمحور (fee_asset = rial)
|
||||||
const rialFee = computed(() => Number(props.finance?.profitRial ?? 0))
|
const rialFee = computed(() => Number(props.finance?.profitRial ?? 0))
|
||||||
// کارمزد طلایی: از معاملههای مبلغمحور (fee_asset = gold)
|
// کارمزد طلایی/فلزی: از معاملههای مبلغمحور (fee_asset = gold)
|
||||||
const goldFeeGrams = computed(() => Number(props.finance?.profitGoldGrams ?? 0))
|
const goldFeeGrams = computed(() => Number(props.finance?.profitGoldGrams ?? 0))
|
||||||
const goldFeeRial = computed(() => Number(props.finance?.profitGoldValueRial ?? 0))
|
const goldFeeRial = computed(() => Number(props.finance?.profitGoldValueRial ?? 0))
|
||||||
// کارمزد ترکیبی به ریال = کارمزد ریالی + معادل ریالی کارمزد طلایی
|
const quantityProducts = computed(() => props.finance?.profitQuantityProducts ?? [])
|
||||||
|
const metalFeeLabel = computed(() => m('طلایی'))
|
||||||
|
// کارمزد ترکیبی به ریال = کارمزد ریالی + معادل ریالی کارمزد فلزی
|
||||||
const commissionRial = computed(() => rialFee.value + goldFeeRial.value)
|
const commissionRial = computed(() => rialFee.value + goldFeeRial.value)
|
||||||
// جمع سود = اختلاف قیمت + کارمزد ترکیبی
|
// جمع سود = اختلاف قیمت + کارمزد ترکیبی
|
||||||
const totalRial = computed(() => priceDiffRial.value + commissionRial.value)
|
const totalRial = computed(() => priceDiffRial.value + commissionRial.value)
|
||||||
@@ -84,8 +100,8 @@ function formatGram(value: number) {
|
|||||||
|
|
||||||
const commissionHelp: DashboardHelp = {
|
const commissionHelp: DashboardHelp = {
|
||||||
description:
|
description:
|
||||||
'کارمزد ترکیبی از دو نوع معامله است: در معاملههای مبلغمحور (مشتری مبلغ ریالی ثابت زده) کارمزد از طلا کسر میشود، و در معاملههای وزنمحور (مشتری وزن ثابت زده) از ریال. این مبلغ مجموع کارمزد ریالی بهعلاوهٔ معادل ریالی کارمزد طلایی است.',
|
'کارمزد ترکیبی از دو نوع معامله است: در معاملههای مبلغمحور (مشتری مبلغ ریالی ثابت زده) کارمزد از کیف همان محصول مقداری کسر میشود، و در معاملههای وزنمحور از ریال. این مبلغ مجموع کارمزد ریالی بهعلاوهٔ معادل ریالی کارمزد مقداری است.',
|
||||||
formula: 'کارمزد = کارمزد ریالی + (گرم کارمزد طلایی × قیمت روز)',
|
formula: 'کارمزد = کارمزد ریالی + جمع (گرم کارمزد هر محصول مقداری × قیمت همان محصول)',
|
||||||
}
|
}
|
||||||
|
|
||||||
const rows = computed(() => [
|
const rows = computed(() => [
|
||||||
@@ -101,7 +117,9 @@ const rows = computed(() => [
|
|||||||
{
|
{
|
||||||
key: 'commission',
|
key: 'commission',
|
||||||
label: 'کارمزد',
|
label: 'کارمزد',
|
||||||
hint: 'ترکیبی از کارمزد ریالی و طلایی',
|
hint: quantityProducts.value.length
|
||||||
|
? 'ترکیبی از کارمزد ریالی و محصولات مقداری'
|
||||||
|
: m('ترکیبی از کارمزد ریالی و طلایی'),
|
||||||
amount: formatDashboardAmount(commissionRial.value),
|
amount: formatDashboardAmount(commissionRial.value),
|
||||||
help: commissionHelp,
|
help: commissionHelp,
|
||||||
breakdown: true,
|
breakdown: true,
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ import SettingValueField from './SettingValueField.vue'
|
|||||||
import SettingCard from './SettingCard.vue'
|
import SettingCard from './SettingCard.vue'
|
||||||
import { refreshAppModeMenu } from '@/router'
|
import { refreshAppModeMenu } from '@/router'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
import { applyBannerEnabledFromRows, applyTrackingSystemsEnabledFromRows, applyConditionalOrderEnabledFromRows, setBannerEnabledFromSettings, setConditionalOrderEnabledFromSettings, setStoreModeFromSettings, setTrackingSystemsEnabledFromSettings } from '@/utils/config'
|
import { applyBannerEnabledFromRows, applyTrackingSystemsEnabledFromRows, applyConditionalOrderEnabledFromRows, applyPriceProviderFromRows, setBannerEnabledFromSettings, setConditionalOrderEnabledFromSettings, setPriceProviderFromSettings, setStoreModeFromSettings, setTrackingSystemsEnabledFromSettings } from '@/utils/config'
|
||||||
import {
|
import {
|
||||||
EDITABLE_SETTING_GROUPS,
|
EDITABLE_SETTING_GROUPS,
|
||||||
SYSTEM_SETTING_GROUPS,
|
SYSTEM_SETTING_GROUPS,
|
||||||
@@ -278,12 +278,9 @@ const filteredRows = computed(() =>
|
|||||||
sortRows(
|
sortRows(
|
||||||
(baseData.value || []).filter((row) => {
|
(baseData.value || []).filter((row) => {
|
||||||
if (row.id == null || !matchesFilter(row)) return false
|
if (row.id == null || !matchesFilter(row)) return false
|
||||||
if (isSystemMode.value) {
|
if (!isSettingVisible(row.key)) return false
|
||||||
// کارمزد سفارش شرطی فقط وقتی قابلیت سراسری روشن است دیده شود
|
if (isSystemMode.value) return true
|
||||||
if (row.key === 'fee_conditional_order' && !isSettingVisible(row.key)) return false
|
return isEditable(row)
|
||||||
return true
|
|
||||||
}
|
|
||||||
return isEditable(row) && isSettingVisible(row.key)
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -423,14 +420,21 @@ async function initConfig() {
|
|||||||
? await getSystemConfiguration()
|
? await getSystemConfiguration()
|
||||||
: await getEditableConfiguration()) ?? []
|
: await getEditableConfiguration()) ?? []
|
||||||
baseData.value = rows.map(normalizeSettingRow)
|
baseData.value = rows.map(normalizeSettingRow)
|
||||||
if (isSystemMode.value) {
|
applyBannerEnabledFromRows(baseData.value)
|
||||||
applyBannerEnabledFromRows(baseData.value)
|
applyTrackingSystemsEnabledFromRows(baseData.value)
|
||||||
applyTrackingSystemsEnabledFromRows(baseData.value)
|
applyConditionalOrderEnabledFromRows(baseData.value)
|
||||||
applyConditionalOrderEnabledFromRows(baseData.value)
|
applyPriceProviderFromRows(baseData.value)
|
||||||
} else {
|
// در صفحهٔ عادی price_provider (can_edit=0) نیست — از کل تنظیمات بخوان
|
||||||
applyBannerEnabledFromRows(baseData.value)
|
if (!baseData.value.some((row) => row.key === 'price_provider')) {
|
||||||
applyTrackingSystemsEnabledFromRows(baseData.value)
|
try {
|
||||||
applyConditionalOrderEnabledFromRows(baseData.value)
|
const { getConfiguration } = await import('@/api/configuration')
|
||||||
|
const all = await getConfiguration()
|
||||||
|
applyPriceProviderFromRows(Array.isArray(all) ? all : [])
|
||||||
|
} catch {
|
||||||
|
/* کش قبلی میماند */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isSystemMode.value) {
|
||||||
const storeRow = baseData.value.find((row) => row.key === 'is_store')
|
const storeRow = baseData.value.find((row) => row.key === 'is_store')
|
||||||
if (storeRow) {
|
if (storeRow) {
|
||||||
setStoreModeFromSettings(isSwitchOn(storeRow.value))
|
setStoreModeFromSettings(isSwitchOn(storeRow.value))
|
||||||
@@ -459,6 +463,11 @@ function onValueChange(row: Const, value: string) {
|
|||||||
|
|
||||||
drafts.value[row.id] = value
|
drafts.value[row.id] = value
|
||||||
|
|
||||||
|
// بلافاصله گروه Montzar را با انتخاب منبع قیمت همگام کن
|
||||||
|
if (row.key === 'price_provider') {
|
||||||
|
setPriceProviderFromSettings(value)
|
||||||
|
}
|
||||||
|
|
||||||
// longtext هم باید ذخیره شود، فقط با delay بیشتر
|
// longtext هم باید ذخیره شود، فقط با delay بیشتر
|
||||||
scheduleSave(row)
|
scheduleSave(row)
|
||||||
}
|
}
|
||||||
@@ -550,6 +559,9 @@ async function saveSettingRow(row: Const) {
|
|||||||
if (row.key === 'enable_conditional_order') {
|
if (row.key === 'enable_conditional_order') {
|
||||||
setConditionalOrderEnabledFromSettings(isSwitchOn(value))
|
setConditionalOrderEnabledFromSettings(isSwitchOn(value))
|
||||||
}
|
}
|
||||||
|
if (row.key === 'price_provider') {
|
||||||
|
setPriceProviderFromSettings(value)
|
||||||
|
}
|
||||||
savedFlashIds.value.add(id)
|
savedFlashIds.value.add(id)
|
||||||
if (saveTimers.has(id)) clearTimeout(saveTimers.get(id))
|
if (saveTimers.has(id)) clearTimeout(saveTimers.get(id))
|
||||||
saveTimers.set(
|
saveTimers.set(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Const } from '@/model/configuration'
|
import type { Const } from '@/model/configuration'
|
||||||
import { conditionalOrderEnabledSetting, isStoreApp } from '@/utils/config'
|
import { conditionalOrderEnabledSetting, isStoreApp, priceProviderSetting } from '@/utils/config'
|
||||||
|
|
||||||
export type SettingFieldType =
|
export type SettingFieldType =
|
||||||
| 'text'
|
| 'text'
|
||||||
@@ -209,6 +209,10 @@ export function isSettingVisible(key: string | undefined | null): boolean {
|
|||||||
if (HIDDEN_LEGACY_SETTING_KEYS.has(k)) return false
|
if (HIDDEN_LEGACY_SETTING_KEYS.has(k)) return false
|
||||||
if (STORE_ONLY_SETTING_KEYS.has(k) && !isStoreApp()) return false
|
if (STORE_ONLY_SETTING_KEYS.has(k) && !isStoreApp()) return false
|
||||||
if (k === 'fee_conditional_order' && !conditionalOrderEnabledSetting.value) return false
|
if (k === 'fee_conditional_order' && !conditionalOrderEnabledSetting.value) return false
|
||||||
|
// گروه همکار Montzar فقط وقتی منبع قیمت montzar است
|
||||||
|
if ((k.startsWith('montzar_') || k === 'display_montzar') && priceProviderSetting.value !== 'montzar') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,7 +556,7 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
|||||||
sell_fee:
|
sell_fee:
|
||||||
'مبلغ کسر (ریال) از قیمت پایه برای سمت فروش. اگر خالی بگذارید، از «اختلاف پایه» استفاده میشود.',
|
'مبلغ کسر (ریال) از قیمت پایه برای سمت فروش. اگر خالی بگذارید، از «اختلاف پایه» استفاده میشود.',
|
||||||
fee_service_charge_percentage:
|
fee_service_charge_percentage:
|
||||||
'درصد کارمزد هر معامله از مبلغ سفارش (اعشاری؛ مثلاً ۰٫۰۰۵ یعنی نیم در هزار).',
|
'درصد کارمزد هر معامله (اعشاری؛ مثلاً ۰٫۰۰۵). روی همهٔ سفارشها اعمال میشود و با اختلاف قیمت محصول یا buy_fee/sell_fee قاطی نمیشود. محصول مقداری: اگر مبلغ ریالی قفل باشد کارمزد از فلز، اگر وزن قفل باشد از ریال. محصول تعدادی: کارمزد فقط از ریال.',
|
||||||
fee_deduct_from_gold:
|
fee_deduct_from_gold:
|
||||||
'این کلید دیگر روی محاسبه اثری ندارد. مبنای کارمزد اکنون فیلدی است که مشتری وارد میکند: اگر «مبلغ ریالی» را وارد کند (مثلاً دقیقاً ۱۰۰ میلیون ریال) همان مبلغ معامله میشود و کارمزد از طلا کسر میشود؛ اگر «وزن» را وارد کند (مثلاً دقیقاً ۲ گرم) همان وزن معامله میشود و کارمزد از ریال کسر میشود. این قانون برای خرید و فروش یکسان است.',
|
'این کلید دیگر روی محاسبه اثری ندارد. مبنای کارمزد اکنون فیلدی است که مشتری وارد میکند: اگر «مبلغ ریالی» را وارد کند (مثلاً دقیقاً ۱۰۰ میلیون ریال) همان مبلغ معامله میشود و کارمزد از طلا کسر میشود؛ اگر «وزن» را وارد کند (مثلاً دقیقاً ۲ گرم) همان وزن معامله میشود و کارمزد از ریال کسر میشود. این قانون برای خرید و فروش یکسان است.',
|
||||||
require_zero_main_gold_before_other_buy:
|
require_zero_main_gold_before_other_buy:
|
||||||
@@ -872,6 +876,8 @@ export const SETTING_LOCKED_KEYS = new Set([
|
|||||||
'chart_id', 'chart_use_local_data',
|
'chart_id', 'chart_use_local_data',
|
||||||
'external_order_api_enabled',
|
'external_order_api_enabled',
|
||||||
'fee_deduct_from_gold',
|
'fee_deduct_from_gold',
|
||||||
|
'fee_single',
|
||||||
|
'referral_bonus_amount',
|
||||||
'group_sms',
|
'group_sms',
|
||||||
'customer_direct_sms',
|
'customer_direct_sms',
|
||||||
'enable_conditional_order',
|
'enable_conditional_order',
|
||||||
@@ -921,7 +927,7 @@ export const SETTING_EDITABLE_KEYS = new Set([
|
|||||||
'deposit_sms_support_number', 'deposit_support_message_text',
|
'deposit_sms_support_number', 'deposit_support_message_text',
|
||||||
'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
|
'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
|
||||||
'endDay',
|
'endDay',
|
||||||
'fee_free', 'fee_service_charge_percentage', 'fee_single',
|
'fee_free', 'fee_service_charge_percentage',
|
||||||
'gift_for_referral', 'gift_for_referrer',
|
'gift_for_referral', 'gift_for_referrer',
|
||||||
'invite_link_text',
|
'invite_link_text',
|
||||||
'jibit_verification',
|
'jibit_verification',
|
||||||
@@ -930,7 +936,7 @@ export const SETTING_EDITABLE_KEYS = new Set([
|
|||||||
'montzar_mobile', 'montzar_open_buy', 'montzar_open_sell',
|
'montzar_mobile', 'montzar_open_buy', 'montzar_open_sell',
|
||||||
'montzar_password', 'montzar_price', 'montzar_product_id',
|
'montzar_password', 'montzar_price', 'montzar_product_id',
|
||||||
'open_register',
|
'open_register',
|
||||||
'referral_bonus_amount', 'referral_id', 'referral_limit', 'referral_gift_on_first_order',
|
'referral_id', 'referral_limit', 'referral_gift_on_first_order',
|
||||||
'register_jibit_verify', 'register_referral_bonus_message', 'register_referral_sms_to_referrer',
|
'register_jibit_verify', 'register_referral_bonus_message', 'register_referral_sms_to_referrer',
|
||||||
'sell_fee', 'show_last_price', 'show_gold_ounce_price', 'startDay',
|
'sell_fee', 'show_last_price', 'show_gold_ounce_price', 'startDay',
|
||||||
'support_messengers', 'support_messenger_text',
|
'support_messengers', 'support_messenger_text',
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ export const customerDirectSmsEnabledSetting = ref(false)
|
|||||||
/** reactive — منو و UI سفارش شرطی (ادمین + کنترلهای مشتری) */
|
/** reactive — منو و UI سفارش شرطی (ادمین + کنترلهای مشتری) */
|
||||||
export const conditionalOrderEnabledSetting = ref(false)
|
export const conditionalOrderEnabledSetting = ref(false)
|
||||||
|
|
||||||
|
/** reactive — منبع قیمت فعال (montzar / zaarbox / main_product) */
|
||||||
|
export const priceProviderSetting = ref('')
|
||||||
|
|
||||||
export const accountingProviderSetting = ref<AccountingProvider>('none')
|
export const accountingProviderSetting = ref<AccountingProvider>('none')
|
||||||
|
|
||||||
let tahesabEnabledFromSettings: boolean | null = null
|
let tahesabEnabledFromSettings: boolean | null = null
|
||||||
@@ -119,6 +122,10 @@ export function setConditionalOrderEnabledFromSettings(value: boolean): void {
|
|||||||
conditionalOrderEnabledSetting.value = value
|
conditionalOrderEnabledSetting.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setPriceProviderFromSettings(value?: string | null): void {
|
||||||
|
priceProviderSetting.value = (value ?? '').trim().toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
export function applyBannerEnabledFromRows(rows: Array<{ key?: string; value?: string | null }>) {
|
export function applyBannerEnabledFromRows(rows: Array<{ key?: string; value?: string | null }>) {
|
||||||
const row = rows.find((item) => item.key === 'banner_enabled')
|
const row = rows.find((item) => item.key === 'banner_enabled')
|
||||||
setBannerEnabledFromSettings(row ? isSwitchOn(row.value) : false)
|
setBannerEnabledFromSettings(row ? isSwitchOn(row.value) : false)
|
||||||
@@ -143,6 +150,15 @@ export function applyConditionalOrderEnabledFromRows(
|
|||||||
setConditionalOrderEnabledFromSettings(row ? isSwitchOn(row.value) : false)
|
setConditionalOrderEnabledFromSettings(row ? isSwitchOn(row.value) : false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyPriceProviderFromRows(
|
||||||
|
rows: Array<{ key?: string; value?: string | null }>
|
||||||
|
) {
|
||||||
|
const row = rows.find((item) => item.key === 'price_provider')
|
||||||
|
if (row) {
|
||||||
|
setPriceProviderFromSettings(row.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function bootstrapSettingsFromApi(): Promise<void> {
|
async function bootstrapSettingsFromApi(): Promise<void> {
|
||||||
if (settingsBootstrapped) return
|
if (settingsBootstrapped) return
|
||||||
|
|
||||||
@@ -160,6 +176,7 @@ async function bootstrapSettingsFromApi(): Promise<void> {
|
|||||||
applyTrackingSystemsEnabledFromRows(list)
|
applyTrackingSystemsEnabledFromRows(list)
|
||||||
applyCustomerDirectSmsFromRows(list)
|
applyCustomerDirectSmsFromRows(list)
|
||||||
applyConditionalOrderEnabledFromRows(list)
|
applyConditionalOrderEnabledFromRows(list)
|
||||||
|
applyPriceProviderFromRows(list)
|
||||||
setCachedMainMetal(mainMetalRow?.value)
|
setCachedMainMetal(mainMetalRow?.value)
|
||||||
const { setCachedMainWalletProductIds } = await import('@/components/mobile/utils/wallet')
|
const { setCachedMainWalletProductIds } = await import('@/components/mobile/utils/wallet')
|
||||||
setCachedMainWalletProductIds(parseMainWalletProductIds(mainWalletRow?.value))
|
setCachedMainWalletProductIds(parseMainWalletProductIds(mainWalletRow?.value))
|
||||||
|
|||||||
Reference in New Issue
Block a user