fix edits for borsemes

This commit is contained in:
2026-07-23 12:28:48 +03:30
parent d3727265e8
commit 73feee2048
6 changed files with 210 additions and 58 deletions
@@ -3,10 +3,10 @@
<div class="mb-2 flex flex-wrap items-start justify-between gap-2">
<DashboardSectionHeader
class="!mb-0 min-w-0 flex-1"
title="سود طلایی و ریالی"
:title="sectionTitle"
:help="sectionHelp"
/>
<a-button type="primary" size="small" @click="openSanad">
<a-button v-if="showProfitConversion" type="primary" size="small" @click="openSanad">
ثبت سند تبدیل سود به طلا
</a-button>
</div>
@@ -40,22 +40,48 @@
<!-- محتوای اصلی -->
<div class="dashboard-profit-split__main">
<a-spin :spinning="loading">
<!-- دو کارت خلاصه -->
<!-- کارت‌های خلاصه: هر محصول مقداری + سود ریالی -->
<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
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"
size="sm"
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">
<span class="dashboard-profit-split__amount">{{ formatGram(profitGoldGrams) }}</span>
<span class="dashboard-profit-split__unit">گرم</span>
</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) }} گرم از تبدیل سود ریالی
</p>
</article>
@@ -73,7 +99,7 @@
<span class="dashboard-profit-split__unit">ریال</span>
</p>
<p class="dashboard-profit-split__sub">
<template v-if="profitRialConverted > 0">
<template v-if="showProfitConversion && profitRialConverted > 0">
{{ formatGram(profitGoldConvertedGram) }} گرم تبدیل‌شده به طلا
</template>
<template v-else>
@@ -89,7 +115,16 @@
<thead>
<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
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>
@@ -97,7 +132,7 @@
</thead>
<tbody>
<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>
</tr>
@@ -107,7 +142,19 @@
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="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) }}
</td>
<td class="px-2 py-2 font-semibold" :class="row.profit_rial > 0 ? 'dashboard-profit' : ''">
@@ -132,8 +179,8 @@
</div>
</div>
<!-- سندهای تبدیل سود به طلا -->
<div class="dashboard-profit-split__sanad mt-3">
<!-- سندهای تبدیل سود به طلا — فقط وقتی main_metal طلاست -->
<div v-if="showProfitConversion" class="dashboard-profit-split__sanad mt-3">
<div class="mb-2 flex flex-wrap items-center justify-between gap-2">
<span class="text-sm font-bold dashboard-text">سندهای تبدیل سود ریالی به طلا</span>
<span v-if="conversions.length" class="dashboard-text-muted text-xs">
@@ -189,6 +236,7 @@
<!-- مدال ثبت سند -->
<BasicActionSheet
v-if="showProfitConversion"
v-model:visible="sanadVisible"
title="ثبت سند تبدیل سود به طلا"
modal-width="24rem"
@@ -259,7 +307,7 @@
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { computed, ref } from 'vue'
import moment from 'moment-jalaali'
import { message } from 'ant-design-vue'
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
@@ -273,9 +321,10 @@ import {
getProfitConversions,
storeProfitConversion,
} 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 { toJalali } from '@core/utils/useDataTime'
import { getMainMetal, m } from '@/utils/metalLabels'
const props = defineProps<{
/** قیمت هر گرم برای مقدار پیش‌فرض سند */
@@ -298,19 +347,39 @@ const rangeEnd = ref(defaultEnd())
const activeStart = ref(rangeStart.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 = {
description:
'کارمزد معاملات بر اساس فیلدی که مشتری وارد می‌کند تفکیک می‌شود: وقتی مشتری «مبلغ ریالی» ثابت می‌زند کارمزد از طلا کسر می‌شود (سود طلایی)، و وقتی «وزن» ثابت می‌زند کارمزد از ریال کسر می‌شود (سود ریالی). اعداد بر اساس بازهٔ انتخابی همین بخش محاسبه می‌شوند.',
formula: 'سود طلایی = جمع کارمزدِ کسرشده از طلا (گرم) + گرم سندهای تبدیل سود ریالی | سود ریالی = جمع کارمزدِ کسرشده از ریال − ریال تبدیل‌شده به طلا',
'کارمزد معاملات بر اساس فیلدی که مشتری وارد می‌کند تفکیک می‌شود: وقتی مشتری «مبلغ ریالی» ثابت می‌زند کارمزد از کیف همان محصول مقداری/آبشده کسر می‌شود، و وقتی «وزن» ثابت می‌زند کارمزد از ریال کسر می‌شود (سود ریالی). برای هر محصول مقداری سود جداگانه نمایش داده می‌شود.',
formula:
'سود هر محصول مقداری = جمع کارمزدِ کسرشده از کیف همان محصول | سود ریالی = جمع کارمزدِ کسرشده از ریال',
}
const goldHelp: DashboardHelp = {
description: 'مجموع کارمزدهای کسرشده از طلا به‌علاوهٔ گرم سندهای «تبدیل سود ریالی به طلا» در همین بازه.',
formula: 'کارمزد طلا (گرم) + گرم تبدیل‌شده از سود ریالی',
description: m('مجموع کارمزدهای کسرشده از طلا به‌علاوهٔ گرم سندهای «تبدیل سود ریالی به طلا» در همین بازه.'),
formula: m('کارمزد طلا (گرم) + گرم تبدیل‌شده از سود ریالی'),
}
const rialHelp: DashboardHelp = {
description: 'سود ریالی باقی‌مانده پس از کسر سندهای تبدیل به طلا در همین بازه.',
formula: 'کارمزد ریالی − ریال تبدیل‌شده به طلا',
const quantityProductHelp: DashboardHelp = {
description:
'وقتی مشتری مبلغ ریالی ثابت می‌زند، کارمزد از کیف همین محصول مقداری کم می‌شود؛ این عدد مجموع همان کارمزدها در بازه است.',
formula: 'جمع service_charge سفارش‌های مبلغ‌محور همین محصول',
}
const rialHelp = computed<DashboardHelp>(() => ({
description: showProfitConversion.value
? 'سود ریالی باقی‌مانده پس از کسر سندهای تبدیل به طلا در همین بازه.'
: 'مجموع کارمزدهای کسرشده از ریال در همین بازه.',
formula: showProfitConversion.value
? 'کارمزد ریالی − ریال تبدیل‌شده به طلا'
: 'کارمزد ریالی',
}))
const loading = ref(false)
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 profitRialConverted = computed(() => Number(finance.value?.profitRialConverted ?? 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 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)
@@ -416,6 +495,12 @@ async function loadFinance() {
}
async function loadConversions() {
if (!showProfitConversion.value) {
conversions.value = []
conversionsTotalRial.value = 0
conversionsTotalGram.value = 0
return
}
conversionsLoading.value = true
try {
const res = await getProfitConversions(activeStart.value, activeEnd.value)
@@ -537,16 +622,10 @@ reloadAll()
.dashboard-profit-split__cards {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: 0.5rem;
}
@media (max-width: 640px) {
.dashboard-profit-split__cards {
grid-template-columns: 1fr;
}
}
.dashboard-profit-split__card {
display: flex;
flex-direction: column;
@@ -25,7 +25,7 @@
<span class="dashboard-income-summary__amount">{{ row.amount }}</span>
<span class="dashboard-income-summary__unit">{{ unit }}</span>
</p>
<!-- تفکیک کارمزد به ریالی + طلایی -->
<!-- تفکیک کارمزد به ریالی + مقداری (به‌ازای محصول) -->
<div v-if="row.breakdown" class="dashboard-income-summary__breakdown">
<span class="dashboard-income-summary__breakdown-row">
<span>ریالی</span>
@@ -33,8 +33,21 @@
{{ formatDashboardAmount(rialFee) }} ریال
</span>
</span>
<span class="dashboard-income-summary__breakdown-row">
<span>طلایی</span>
<template v-if="quantityProducts.length">
<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">
{{ formatGram(goldFeeGrams) }} گرم ≈ {{ formatDashboardAmount(goldFeeRial) }} ریال
</span>
@@ -53,6 +66,7 @@ import { dashboardTradingIncomeSummary, dashboardKpi } from '@/pages/dashboard/d
import { DASHBOARD_RIAL_UNIT, formatDashboardAmount } from '@/pages/dashboard/dashboardFormat'
import type { DashboardFinance } from '@/model/dashboard'
import type { DashboardHelp } from '@/pages/dashboard/dashboardSectionCopy'
import { m } from '@/utils/metalLabels'
const props = withDefaults(
defineProps<{
@@ -70,10 +84,12 @@ const unit = DASHBOARD_RIAL_UNIT
const priceDiffRial = computed(() => Number(props.finance?.fullPriceSood ?? 0))
// کارمزد ریالی: از معامله‌های وزن‌محور (fee_asset = rial)
const rialFee = computed(() => Number(props.finance?.profitRial ?? 0))
// کارمزد طلایی: از معامله‌های مبلغ‌محور (fee_asset = gold)
// کارمزد طلایی/فلزی: از معامله‌های مبلغ‌محور (fee_asset = gold)
const goldFeeGrams = computed(() => Number(props.finance?.profitGoldGrams ?? 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 totalRial = computed(() => priceDiffRial.value + commissionRial.value)
@@ -84,8 +100,8 @@ function formatGram(value: number) {
const commissionHelp: DashboardHelp = {
description:
'کارمزد ترکیبی از دو نوع معامله است: در معامله‌های مبلغ‌محور (مشتری مبلغ ریالی ثابت زده) کارمزد از طلا کسر می‌شود، و در معامله‌های وزن‌محور (مشتری وزن ثابت زده) از ریال. این مبلغ مجموع کارمزد ریالی به‌علاوهٔ معادل ریالی کارمزد طلایی است.',
formula: 'کارمزد = کارمزد ریالی + (گرم کارمزد طلایی × قیمت روز)',
'کارمزد ترکیبی از دو نوع معامله است: در معامله‌های مبلغ‌محور (مشتری مبلغ ریالی ثابت زده) کارمزد از کیف همان محصول مقداری کسر می‌شود، و در معامله‌های وزن‌محور از ریال. این مبلغ مجموع کارمزد ریالی به‌علاوهٔ معادل ریالی کارمزد مقداری است.',
formula: 'کارمزد = کارمزد ریالی + جمع (گرم کارمزد هر محصول مقداری × قیمت همان محصول)',
}
const rows = computed(() => [
@@ -101,7 +117,9 @@ const rows = computed(() => [
{
key: 'commission',
label: 'کارمزد',
hint: 'ترکیبی از کارمزد ریالی و طلایی',
hint: quantityProducts.value.length
? 'ترکیبی از کارمزد ریالی و محصولات مقداری'
: m('ترکیبی از کارمزد ریالی و طلایی'),
amount: formatDashboardAmount(commissionRial.value),
help: commissionHelp,
breakdown: true,