fix some bugs 4
This commit is contained in:
@@ -1,5 +1,36 @@
|
||||
import type { Customer } from '@/model/customer'
|
||||
|
||||
export interface OrderFeeBreakdownLine {
|
||||
kind?: string
|
||||
key?: string
|
||||
label?: string
|
||||
is_percent?: boolean
|
||||
raw?: number
|
||||
percent_of?: number | null
|
||||
per_unit?: number
|
||||
amount_units?: number
|
||||
total?: number
|
||||
gold?: number
|
||||
rial?: number
|
||||
detail?: string
|
||||
line_text?: string
|
||||
}
|
||||
|
||||
export interface OrderFeeBreakdown {
|
||||
uses_app_price?: boolean
|
||||
amount_units?: number
|
||||
price_original?: number
|
||||
price_customer?: number
|
||||
base_total?: number
|
||||
customer_total?: number
|
||||
spread_per_unit?: number
|
||||
spread_total?: number
|
||||
service_charge_gold?: number
|
||||
service_charge_rial?: number
|
||||
grand_total?: number
|
||||
lines?: OrderFeeBreakdownLine[]
|
||||
}
|
||||
|
||||
export interface Order {
|
||||
amount?: number
|
||||
amount_display?: number
|
||||
@@ -7,6 +38,19 @@ export interface Order {
|
||||
order_metal_id?: number
|
||||
order_metal_label?: string
|
||||
metal_prices?: Record<string, number>
|
||||
product_id?: number | null
|
||||
product_title?: string | null
|
||||
fee_source?: 'settings' | 'product' | 'app+product' | 'none' | string
|
||||
fee_source_key?: string | null
|
||||
fee_source_label?: string | null
|
||||
fee_breakdown?: OrderFeeBreakdown | null
|
||||
fee_display?: number
|
||||
total_fee_display?: number
|
||||
transactions?: Array<{
|
||||
title?: string | null
|
||||
product_id?: number | null
|
||||
product?: { id?: number; title?: string | null } | null
|
||||
}>
|
||||
created_at?: string
|
||||
customer?: Customer
|
||||
customer_id?: number
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visibleProxy"
|
||||
:title="null"
|
||||
:footer="null"
|
||||
centered
|
||||
:width="modalWidth"
|
||||
destroy-on-close
|
||||
class="order-detail-modal"
|
||||
@cancel="close"
|
||||
>
|
||||
<div v-if="order" class="order-detail">
|
||||
<header class="order-detail__header">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2 mb-1">
|
||||
<p class="order-detail__eyebrow m-0">جزئیات سفارش</p>
|
||||
<span class="order-detail__badge" :class="directionClass">{{ directionLabel }}</span>
|
||||
<span v-if="order.sts_str" class="order-detail__badge order-detail__badge--muted">
|
||||
{{ order.sts_str }}
|
||||
</span>
|
||||
</div>
|
||||
<h2 class="order-detail__title">{{ order.customer?.name || `سفارش #${order.id ?? '-'}` }}</h2>
|
||||
<p class="order-detail__sub">
|
||||
<span>{{ order.customer?.mobile || '-' }}</span>
|
||||
<span v-if="order.id" class="order-detail__dot">·</span>
|
||||
<span v-if="order.id">کد {{ order.id }}</span>
|
||||
<span class="order-detail__dot">·</span>
|
||||
<span class="order-detail__muted">{{ dateLabel }} {{ timeLabel }}</span>
|
||||
</p>
|
||||
<p class="order-detail__product-line m-0">
|
||||
<span class="order-detail__field-label">محصول</span>
|
||||
<strong>{{ productTitle || '—' }}</strong>
|
||||
<span class="order-detail__chip">{{ order.order_metal_label || '—' }}</span>
|
||||
<span v-if="usesAppPrice" class="order-detail__chip order-detail__chip--soft">قیمت اپ</span>
|
||||
<span v-else-if="feeSourceLabel" class="order-detail__chip order-detail__chip--soft">
|
||||
{{ feeSourceLabel }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="order-detail__stats">
|
||||
<article class="order-detail__stat order-detail__stat--accent">
|
||||
<span class="order-detail__stat-label">{{ paymentLabel }}</span>
|
||||
<strong class="order-detail__stat-value">{{ formatOrderRial(paymentAmount) }}</strong>
|
||||
<span class="order-detail__stat-unit">ریال</span>
|
||||
</article>
|
||||
<article class="order-detail__stat">
|
||||
<span class="order-detail__stat-label">مقدار</span>
|
||||
<strong class="order-detail__stat-value">{{ amountLabel }}</strong>
|
||||
</article>
|
||||
<article class="order-detail__stat">
|
||||
<span class="order-detail__stat-label">جمع سود</span>
|
||||
<strong class="order-detail__stat-value">{{ formatOrderRial(grandTotal) }}</strong>
|
||||
<span class="order-detail__stat-unit">ریال</span>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="order-detail__receipt">
|
||||
<div class="order-detail__receipt-head">
|
||||
<h3 class="order-detail__receipt-title m-0">حساب سود</h3>
|
||||
<p class="order-detail__receipt-eq m-0">{{ equationHint }}</p>
|
||||
</div>
|
||||
|
||||
<div class="order-detail__receipt-lines">
|
||||
<div class="order-detail__receipt-line order-detail__receipt-line--base">
|
||||
<div class="order-detail__receipt-line-main">
|
||||
<span class="order-detail__receipt-op" aria-hidden="true"> </span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<strong class="order-detail__receipt-label">بدون سود</strong>
|
||||
<p class="order-detail__receipt-detail m-0">
|
||||
{{ formatOrderRial(order.price_original ?? 0) }} ریال / گرم × {{ amountLabel }}
|
||||
</p>
|
||||
</div>
|
||||
<strong class="order-detail__receipt-amount">
|
||||
{{ formatOrderRial(baseTotal) }}
|
||||
<span class="order-detail__stat-unit">ریال</span>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="receiptLines.length">
|
||||
<div
|
||||
v-for="(line, idx) in receiptLines"
|
||||
:key="`${line.key ?? line.kind}-${idx}`"
|
||||
class="order-detail__receipt-line"
|
||||
>
|
||||
<div class="order-detail__receipt-line-main">
|
||||
<span class="order-detail__receipt-op" aria-hidden="true">{{ profitSign }}</span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<strong class="order-detail__receipt-label">{{ line.label || 'سود' }}</strong>
|
||||
<p class="order-detail__receipt-detail m-0">{{ line.line_text || line.detail || '—' }}</p>
|
||||
</div>
|
||||
<strong class="order-detail__receipt-amount">
|
||||
{{ formatOrderRial(line.total ?? 0) }}
|
||||
<span class="order-detail__stat-unit">ریال</span>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<p v-else class="order-detail__receipt-empty m-0">
|
||||
اقلام سود برای این سفارش ذخیره نشده — سفارش جدید بعد از آپدیت بزنید.
|
||||
</p>
|
||||
|
||||
<div class="order-detail__receipt-line order-detail__receipt-line--result">
|
||||
<div class="order-detail__receipt-line-main">
|
||||
<span class="order-detail__receipt-op" aria-hidden="true">=</span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<strong class="order-detail__receipt-label">قیمت مشتری</strong>
|
||||
<p class="order-detail__receipt-detail m-0">
|
||||
{{ formatOrderRial(order.price ?? 0) }} ریال / گرم × {{ amountLabel }}
|
||||
</p>
|
||||
</div>
|
||||
<strong class="order-detail__receipt-amount order-detail__receipt-amount--focus">
|
||||
{{ formatOrderRial(customerTotal) }}
|
||||
<span class="order-detail__stat-unit">ریال</span>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="order-detail__customer">
|
||||
<div class="order-detail__customer-grid">
|
||||
<div class="order-detail__customer-cell">
|
||||
<span class="order-detail__field-label">کد کاربری</span>
|
||||
<strong>{{ order.customer?.id ?? '—' }}</strong>
|
||||
</div>
|
||||
<div class="order-detail__customer-cell">
|
||||
<span class="order-detail__field-label">شماره ملی</span>
|
||||
<strong>{{ order.customer?.n_code || '—' }}</strong>
|
||||
</div>
|
||||
<div class="order-detail__customer-cell">
|
||||
<span class="order-detail__field-label">تلفن</span>
|
||||
<strong>{{ order.customer?.phone_number || '—' }}</strong>
|
||||
</div>
|
||||
<div class="order-detail__customer-cell">
|
||||
<span class="order-detail__field-label">استان</span>
|
||||
<strong>{{ order.customer?.province || '—' }}</strong>
|
||||
</div>
|
||||
<div class="order-detail__customer-cell">
|
||||
<span class="order-detail__field-label">شهر</span>
|
||||
<strong>{{ order.customer?.city || '—' }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Order } from '@/model/order'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||
import { formatAmountWithUnit } from '@/utils/weightUnit'
|
||||
import {
|
||||
formatOrderRial,
|
||||
orderFeeBaseTotal,
|
||||
orderFeeBreakdown,
|
||||
orderFeeBreakdownLines,
|
||||
orderFeeCustomerTotal,
|
||||
orderFeeGrandTotal,
|
||||
resolveOrderFeeSourceLabel,
|
||||
resolveOrderProductTitle
|
||||
} from '../utils/orderDetailDisplay'
|
||||
|
||||
type OrderRow = Order & {
|
||||
sts_str?: string
|
||||
transactions?: Array<{
|
||||
title?: string | null
|
||||
product_id?: number | null
|
||||
product?: { id?: number; title?: string | null } | null
|
||||
}>
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
order: OrderRow | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'close'): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const modalWidth = computed(() => (isMobile.value ? '96vw' : '72rem'))
|
||||
|
||||
const order = computed(() => props.order)
|
||||
|
||||
const directionLabel = computed(() => (order.value?.direction === 2 ? 'فروش' : 'خرید'))
|
||||
const directionClass = computed(() =>
|
||||
order.value?.direction === 2 ? 'order-detail__badge--sell' : 'order-detail__badge--buy'
|
||||
)
|
||||
|
||||
const dateLabel = computed(() =>
|
||||
order.value?.created_at ? toJalaliNoTime(order.value.created_at) : '—'
|
||||
)
|
||||
const timeLabel = computed(() => (order.value?.created_at ? toTime(order.value.created_at) : ''))
|
||||
|
||||
const paymentLabel = computed(() =>
|
||||
order.value?.direction === 2 ? 'مبلغ دریافتی مشتری' : 'مبلغ پرداختی مشتری'
|
||||
)
|
||||
const paymentAmount = computed(() =>
|
||||
Number(order.value?.payment_amount ?? order.value?.total_price ?? 0)
|
||||
)
|
||||
|
||||
const amountLabel = computed(() => {
|
||||
if (!order.value) return '—'
|
||||
return formatAmountWithUnit(order.value.amount_display ?? order.value.amount, order.value.amount_unit)
|
||||
})
|
||||
|
||||
const productTitle = computed(() => (order.value ? resolveOrderProductTitle(order.value) : ''))
|
||||
const feeSourceLabel = computed(() => (order.value ? resolveOrderFeeSourceLabel(order.value) : ''))
|
||||
const breakdown = computed(() => (order.value ? orderFeeBreakdown(order.value) : null))
|
||||
const usesAppPrice = computed(() => Boolean(breakdown.value?.uses_app_price))
|
||||
const receiptLines = computed(() => (order.value ? orderFeeBreakdownLines(order.value) : []))
|
||||
const grandTotal = computed(() => (order.value ? orderFeeGrandTotal(order.value) : 0))
|
||||
const baseTotal = computed(() => (order.value ? orderFeeBaseTotal(order.value) : 0))
|
||||
const customerTotal = computed(() => (order.value ? orderFeeCustomerTotal(order.value) : 0))
|
||||
const isSell = computed(() => order.value?.direction === 2)
|
||||
const profitSign = computed(() => (isSell.value ? '−' : '+'))
|
||||
const equationHint = computed(() =>
|
||||
isSell.value
|
||||
? 'بدون سود − سود = قیمت مشتری'
|
||||
: 'بدون سود + سود = قیمت مشتری'
|
||||
)
|
||||
|
||||
function close() {
|
||||
emit('update:visible', false)
|
||||
emit('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.order-detail {
|
||||
@apply flex flex-col gap-3 pt-0;
|
||||
}
|
||||
|
||||
.order-detail__muted,
|
||||
.order-detail__field-label {
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.order-detail__header {
|
||||
@apply flex items-start justify-between gap-3 rounded-xl px-4 py-3;
|
||||
background: var(--admin-stat-muted, rgba(255, 255, 255, 0.04));
|
||||
}
|
||||
|
||||
.order-detail__eyebrow {
|
||||
@apply text-xs;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.order-detail__title {
|
||||
@apply m-0 text-lg font-bold;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.order-detail__sub {
|
||||
@apply m-0 mt-0.5 flex flex-wrap items-center gap-1 text-sm text-primary;
|
||||
}
|
||||
|
||||
.order-detail__dot {
|
||||
@apply opacity-50;
|
||||
}
|
||||
|
||||
.order-detail__product-line {
|
||||
@apply mt-2 flex flex-wrap items-center gap-2 text-sm;
|
||||
}
|
||||
|
||||
.order-detail__product-line strong {
|
||||
@apply font-bold;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.order-detail__badge {
|
||||
@apply rounded-lg border px-2 py-0.5 text-xs font-600;
|
||||
}
|
||||
|
||||
.order-detail__badge--buy {
|
||||
@apply border-green-300/40 text-green-500;
|
||||
background: rgba(34, 197, 94, 0.12);
|
||||
}
|
||||
|
||||
.order-detail__badge--sell {
|
||||
@apply border-red-300/40 text-red-400;
|
||||
background: rgba(248, 113, 113, 0.12);
|
||||
}
|
||||
|
||||
.order-detail__badge--muted {
|
||||
border-color: var(--admin-border);
|
||||
color: var(--admin-text-secondary);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.order-detail__stats {
|
||||
@apply grid grid-cols-1 gap-2 sm:grid-cols-3;
|
||||
}
|
||||
|
||||
.order-detail__stat {
|
||||
@apply rounded-xl px-4 py-2.5;
|
||||
border: 1px solid var(--admin-border);
|
||||
}
|
||||
|
||||
.order-detail__stat--accent {
|
||||
border-color: rgba(34, 197, 94, 0.35);
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
}
|
||||
|
||||
.order-detail__stat-label,
|
||||
.order-detail__field-label {
|
||||
@apply block text-xs;
|
||||
}
|
||||
|
||||
.order-detail__stat-value {
|
||||
@apply mt-0.5 block text-lg font-bold;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.order-detail__stat--accent .order-detail__stat-value {
|
||||
@apply text-green-500;
|
||||
}
|
||||
|
||||
.order-detail__stat-unit {
|
||||
@apply text-xs;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.order-detail__chip {
|
||||
@apply rounded-lg px-2 py-0.5 text-xs font-600;
|
||||
border: 1px solid var(--admin-border);
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.order-detail__chip--soft {
|
||||
color: var(--admin-text-secondary);
|
||||
font-weight: 500;
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
.order-detail__receipt {
|
||||
@apply rounded-xl px-4 py-3;
|
||||
border: 1px solid var(--admin-border);
|
||||
background: var(--admin-stat-muted, rgba(255, 255, 255, 0.03));
|
||||
}
|
||||
|
||||
.order-detail__receipt-head {
|
||||
@apply mb-3;
|
||||
}
|
||||
|
||||
.order-detail__receipt-title {
|
||||
@apply text-sm font-bold;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.order-detail__receipt-eq {
|
||||
@apply mt-0.5 text-xs;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.order-detail__receipt-lines {
|
||||
@apply flex flex-col gap-2;
|
||||
}
|
||||
|
||||
.order-detail__receipt-line {
|
||||
@apply rounded-lg px-3 py-2;
|
||||
border: 1px solid var(--admin-border);
|
||||
}
|
||||
|
||||
.order-detail__receipt-line--base {
|
||||
background: var(--admin-stat-muted, rgba(255, 255, 255, 0.02));
|
||||
}
|
||||
|
||||
.order-detail__receipt-line--result {
|
||||
border-color: rgba(34, 197, 94, 0.35);
|
||||
background: rgba(34, 197, 94, 0.08);
|
||||
}
|
||||
|
||||
.order-detail__receipt-line-main {
|
||||
@apply flex items-start gap-2;
|
||||
}
|
||||
|
||||
.order-detail__receipt-op {
|
||||
@apply mt-0.5 w-3 shrink-0 text-center text-sm font-bold text-primary;
|
||||
}
|
||||
|
||||
.order-detail__receipt-label {
|
||||
@apply block text-sm;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.order-detail__receipt-detail {
|
||||
@apply mt-0.5 text-xs leading-relaxed;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.order-detail__receipt-amount {
|
||||
@apply shrink-0 text-sm font-bold;
|
||||
color: var(--admin-text);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.order-detail__receipt-amount--focus {
|
||||
@apply text-green-500;
|
||||
}
|
||||
|
||||
.order-detail__receipt-empty {
|
||||
@apply text-xs;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.order-detail__customer {
|
||||
@apply rounded-xl px-4 py-2.5;
|
||||
border: 1px solid var(--admin-border);
|
||||
}
|
||||
|
||||
.order-detail__customer-grid {
|
||||
@apply grid grid-cols-2 gap-x-4 gap-y-2 md:grid-cols-3 lg:grid-cols-5;
|
||||
}
|
||||
|
||||
.order-detail__customer-cell strong {
|
||||
@apply mt-0.5 block text-sm font-600;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.order-detail-modal.ant-modal {
|
||||
max-width: min(72rem, 96vw);
|
||||
}
|
||||
|
||||
.order-detail-modal .ant-modal-body {
|
||||
padding: 0.75rem 1.25rem 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -36,17 +36,40 @@
|
||||
:label="row.direction === 2 ? 'مبلغ دریافتی مشتری (ریال)' : 'مبلغ پرداختی مشتری (ریال)'"
|
||||
:value="usePrice(row.payment_amount ?? row.total_price ?? 0, true, '')"
|
||||
/>
|
||||
<MobileStatCard bordered label="کارمزد (طلا)" :value="serviceChargeLabel(row)" />
|
||||
<MobileStatCard bordered label="کارمزد (ریال)" :value="usePrice(row.service_charge_price ?? 0, true, '')" />
|
||||
<MobileStatCard bordered label="قیمت کل بدون سود (ریال)" :value="usePrice(row.total_price_original ?? 0, true, '')" />
|
||||
<MobileStatCard bordered label="سود واحد (ریال)" :value="usePrice(row.fee ?? 0, true, '')" />
|
||||
<MobileStatCard
|
||||
bordered
|
||||
variant="warning"
|
||||
label="جمع سود معامله (ریال)"
|
||||
:value="usePrice(orderFeeGrandTotal(row), true, '')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="orderFeeBreakdownLines(row).length" class="space-y-2 rounded-xl border border-[var(--admin-border)] p-3">
|
||||
<p class="m-0 text-sm font-bold">فاکتور سود</p>
|
||||
<div
|
||||
v-for="(line, idx) in orderFeeBreakdownLines(row)"
|
||||
:key="`${line.key ?? line.kind}-${idx}`"
|
||||
class="text-xs"
|
||||
>
|
||||
<p class="m-0 font-600">+ {{ line.label }}</p>
|
||||
<p class="m-0 mt-0.5 opacity-70">{{ line.line_text || line.detail }}</p>
|
||||
<p class="m-0 mt-0.5 font-bold">{{ usePrice(Number(line.total ?? 0), true, '') }} ریال</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="محصول" :value="productTitle(row) || '-'" />
|
||||
<MobileDetailRow label="نوع فلز" :value="row.order_metal_label ?? '-'" />
|
||||
<MobileDetailRow label="منبع اختلاف قیمت" :value="feeSourceLabel(row) || '-'" />
|
||||
<MobileDetailRow label="کد سفارش" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="شماره موبایل" :value="row.customer?.mobile || '-'" />
|
||||
<MobileDetailRow label="سود کل (ریال)" :value="usePrice(row.total_fee ?? 0, true, '')" />
|
||||
<MobileDetailRow
|
||||
label="سود اختلاف قیمت (ریال)"
|
||||
:value="usePrice(orderProfitTotal(row), true, '')"
|
||||
/>
|
||||
<MobileDetailRow label="کارمزد (طلا)" :value="serviceChargeLabel(row)" />
|
||||
<MobileDetailRow label="کارمزد (ریال)" :value="usePrice(row.service_charge_price ?? 0, true, '')" />
|
||||
<MobileDetailRow label="کد کاربری" :value="row.customer?.id ?? '-'" />
|
||||
<MobileDetailRow label="شماره ملی" :value="row.customer?.n_code || '-'" />
|
||||
<MobileDetailRow label="شماره تلفن" :value="row.customer?.phone_number || '-'" />
|
||||
@@ -68,12 +91,24 @@ import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
import { formatAmountWithUnit } from '@/utils/weightUnit'
|
||||
import {
|
||||
orderFeeBreakdownLines,
|
||||
orderFeeGrandTotal,
|
||||
orderProfitTotal,
|
||||
resolveOrderFeeSourceLabel,
|
||||
resolveOrderProductTitle
|
||||
} from '../utils/orderDetailDisplay'
|
||||
|
||||
type OrderRow = Order & {
|
||||
sts_str?: string
|
||||
service_charge?: number | string
|
||||
service_charge_price?: number | string
|
||||
payment_amount?: number | string
|
||||
transactions?: Array<{
|
||||
title?: string | null
|
||||
product_id?: number | null
|
||||
product?: { id?: number; title?: string | null } | null
|
||||
}>
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -130,4 +165,12 @@ function serviceChargeLabel(row: OrderRow) {
|
||||
if (Number.isNaN(value)) return String(row.service_charge)
|
||||
return formatAmountWithUnit(value, row.amount_unit)
|
||||
}
|
||||
|
||||
function productTitle(row: OrderRow) {
|
||||
return resolveOrderProductTitle(row)
|
||||
}
|
||||
|
||||
function feeSourceLabel(row: OrderRow) {
|
||||
return resolveOrderFeeSourceLabel(row)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -54,15 +54,10 @@
|
||||
@calculate-cash="calculateCash"
|
||||
/>
|
||||
</BasicActionSheet>
|
||||
<BasicInfoModal
|
||||
<OrderDetailModal
|
||||
v-model:visible="userInfoVisible"
|
||||
:order="selectedOrderInfo"
|
||||
@close="userInfoVisible = false"
|
||||
:title="infoParams.title"
|
||||
:date="infoParams.date"
|
||||
:time="infoParams.time"
|
||||
:type="infoParams.type"
|
||||
:name="infoParams.name"
|
||||
:items="infoParams.items"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -73,24 +68,19 @@ import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||
import { ref } from 'vue'
|
||||
import tbl from './components/table.vue'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import OrderEditorFormBody from './components/OrderEditorFormBody.vue'
|
||||
import OrderDetailModal from './components/OrderDetailModal.vue'
|
||||
import { message } from 'ant-design-vue/es'
|
||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||
import { confirmDeleteItem } from '@/utils/confirmModal'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import { deleteCustomer, getCustomersList } from '@/api/customer'
|
||||
import type { Order } from '@/model/order'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
import { getLastPrice } from '@/api/price'
|
||||
import type { last } from '@/model/price'
|
||||
import { createOrder } from '@/api/order'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||
import { formatAmountWithUnit } from '@/utils/weightUnit'
|
||||
import { initWalletSettingsFromApi } from '@/utils/config'
|
||||
|
||||
//loading
|
||||
@@ -198,99 +188,10 @@ function stopGetPrice() {
|
||||
}
|
||||
|
||||
//info
|
||||
let infoParams = reactive<Info>({})
|
||||
const selectedOrderInfo = ref<Order | null>(null)
|
||||
|
||||
function info(val: Order) {
|
||||
infoParams.name = val.customer.name
|
||||
infoParams.type = val.customer.mobile
|
||||
infoParams.date = toJalaliNoTime(val.created_at)
|
||||
infoParams.time = toTime(val.created_at)
|
||||
infoParams.itemsTitle = 'اطلاعات کاربری'
|
||||
infoParams.items = [
|
||||
{
|
||||
key: 'مقدار',
|
||||
value: formatAmountWithUnit(val.amount_display ?? val.amount, val.amount_unit)
|
||||
},
|
||||
{
|
||||
key: 'نوع فلز',
|
||||
value: val.order_metal_label ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'قیمت زمان معامله (ریال)',
|
||||
value: usePrice(val.price ?? 0) ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'قیمت کل برای مشتری (ریال)',
|
||||
value: usePrice(val.total_price ?? 0) ?? '-'
|
||||
},
|
||||
{
|
||||
key: metalLabel.goldGramPriceOriginal(),
|
||||
value: usePrice(val.price_original ?? 0) ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'قیمت کل بدون سود (ریال)',
|
||||
value: usePrice(val.total_price_original ?? 0) ?? '-'
|
||||
},
|
||||
/* {
|
||||
key: 'قیمت کل لیمان (ریال)',
|
||||
value: usePrice(val.total_price_liman ?? 0) ?? '-'
|
||||
},*/
|
||||
{
|
||||
key: 'سود هر گرم (ریال)',
|
||||
value: usePrice(val.fee ?? 0) ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'سود کل (ریال)',
|
||||
value: usePrice(val.total_fee ?? 0) ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'کد کاربری',
|
||||
value: val.customer.id ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'شماره ملی',
|
||||
value: val.customer.n_code ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'کارمزد (طلا)',
|
||||
value:
|
||||
val.service_charge != null && val.service_charge !== ''
|
||||
? formatAmountWithUnit(Number(val.service_charge), val.amount_unit)
|
||||
: '-'
|
||||
},
|
||||
{
|
||||
key: 'کارمزد (ریال)',
|
||||
value: usePrice(Number(val.service_charge_price ?? 0)) ?? '-'
|
||||
},
|
||||
{
|
||||
key: val.direction === 2 ? 'مبلغ دریافتی مشتری (ریال)' : 'مبلغ پرداختی مشتری (ریال)',
|
||||
value: usePrice(val.payment_amount ?? val.total_price ?? 0) ?? '-'
|
||||
},
|
||||
/* {
|
||||
key: 'تاریخ تولد',
|
||||
value: toJalaliNoTime(val.customer.birth_date) ?? '-'
|
||||
},*/
|
||||
/* {
|
||||
key: 'نام فروشگاه',
|
||||
value: val.customer.shop_name ?? '-'
|
||||
},*/
|
||||
{
|
||||
key: 'شماره تلفن',
|
||||
value: val.customer.phone_number ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'نام استان',
|
||||
value: val.customer.province ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'نام شهر',
|
||||
value: val.customer.city ?? '-'
|
||||
}
|
||||
/* {
|
||||
key: 'نام چاپی',
|
||||
value: val.print_name ?? '-'
|
||||
}*/
|
||||
]
|
||||
selectedOrderInfo.value = val
|
||||
userInfoVisible.value = true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import type { Order, OrderFeeBreakdown, OrderFeeBreakdownLine } from '@/model/order'
|
||||
|
||||
type TxLike = {
|
||||
title?: string | null
|
||||
product_id?: number | null
|
||||
product?: { id?: number; title?: string | null } | null
|
||||
}
|
||||
|
||||
export function resolveOrderProductTitle(order: Order & { transactions?: TxLike[] }): string {
|
||||
const direct = (order.product_title ?? '').trim()
|
||||
if (direct) return direct
|
||||
|
||||
const txs = order.transactions ?? []
|
||||
for (const tx of txs) {
|
||||
const fromProduct = (tx.product?.title ?? '').trim()
|
||||
if (fromProduct) return fromProduct
|
||||
}
|
||||
|
||||
for (const tx of txs) {
|
||||
const title = (tx.title ?? '').trim()
|
||||
if (title.startsWith('خرید ')) return title.slice(5).trim()
|
||||
if (title.startsWith('فروش ')) return title.slice(5).trim()
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
export function resolveOrderFeeSourceLabel(order: Order): string {
|
||||
const labeled = (order.fee_source_label ?? '').trim()
|
||||
if (labeled) return labeled
|
||||
|
||||
if (order.fee_source === 'app+product') {
|
||||
return order.direction === 2
|
||||
? 'قیمت اپ (sell_fee) + تب اختلاف محصول'
|
||||
: 'قیمت اپ (buy_fee) + تب اختلاف محصول'
|
||||
}
|
||||
if (order.fee_source === 'settings') {
|
||||
const key = order.fee_source_key || (order.direction === 2 ? 'sell_fee' : 'buy_fee')
|
||||
return order.direction === 2
|
||||
? `تنظیمات سراسری · کسر قیمت فروش (${key})`
|
||||
: `تنظیمات سراسری · اضافه قیمت خرید (${key})`
|
||||
}
|
||||
if (order.fee_source === 'product') {
|
||||
return order.direction === 2
|
||||
? 'تب اختلاف محصول · هزینه فروش (cash_fee_sell)'
|
||||
: 'تب اختلاف محصول · هزینه خرید (cash_fee_buy)'
|
||||
}
|
||||
|
||||
const per = orderProfitPerUnit(order)
|
||||
if (per > 0) {
|
||||
return `اختلاف قیمت ذخیرهشده روی سفارش (${per.toLocaleString('fa-IR')} ریال/گرم)`
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
export function orderProfitPerUnit(order: Order): number {
|
||||
return Math.abs(Number(order.fee_display ?? order.fee ?? 0))
|
||||
}
|
||||
|
||||
export function orderProfitTotal(order: Order): number {
|
||||
return Math.abs(Number(order.total_fee_display ?? order.total_fee ?? 0))
|
||||
}
|
||||
|
||||
export function orderFeeBreakdown(order: Order): OrderFeeBreakdown | null {
|
||||
const raw = order.fee_breakdown
|
||||
if (!raw || typeof raw !== 'object') return null
|
||||
return raw
|
||||
}
|
||||
|
||||
export function orderFeeBreakdownLines(order: Order): OrderFeeBreakdownLine[] {
|
||||
return orderFeeBreakdown(order)?.lines ?? []
|
||||
}
|
||||
|
||||
export function orderFeeGrandTotal(order: Order): number {
|
||||
const breakdown = orderFeeBreakdown(order)
|
||||
if (breakdown && breakdown.grand_total != null) {
|
||||
return Math.round(Math.abs(Number(breakdown.grand_total)))
|
||||
}
|
||||
const spread = orderProfitTotal(order)
|
||||
const service = Math.abs(Number(order.service_charge_price ?? 0))
|
||||
return Math.round(spread + service)
|
||||
}
|
||||
|
||||
export function orderFeeBaseTotal(order: Order): number {
|
||||
const breakdown = orderFeeBreakdown(order)
|
||||
if (breakdown?.base_total != null) {
|
||||
return Math.round(Math.abs(Number(breakdown.base_total)))
|
||||
}
|
||||
return Math.round(Math.abs(Number(order.total_price_original ?? 0)))
|
||||
}
|
||||
|
||||
export function orderFeeCustomerTotal(order: Order): number {
|
||||
const breakdown = orderFeeBreakdown(order)
|
||||
if (breakdown?.customer_total != null) {
|
||||
return Math.round(Math.abs(Number(breakdown.customer_total)))
|
||||
}
|
||||
return Math.round(Math.abs(Number(order.total_price ?? 0)))
|
||||
}
|
||||
|
||||
/** مبالغ ریالی — بدون اعشار تا usePrice ویرگول اشتباه نگذارد */
|
||||
export function formatOrderRial(value: number | string | null | undefined): string {
|
||||
const n = Math.round(Math.abs(Number(value ?? 0)))
|
||||
if (!Number.isFinite(n)) return '0'
|
||||
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
Reference in New Issue
Block a user