diff --git a/src/model/order.ts b/src/model/order.ts index b4d3507..8426b18 100644 --- a/src/model/order.ts +++ b/src/model/order.ts @@ -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 + 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 diff --git a/src/pages/menuPages/orders/components/OrderDetailModal.vue b/src/pages/menuPages/orders/components/OrderDetailModal.vue new file mode 100644 index 0000000..caf899e --- /dev/null +++ b/src/pages/menuPages/orders/components/OrderDetailModal.vue @@ -0,0 +1,440 @@ + + + + + + + diff --git a/src/pages/menuPages/orders/components/OrderMobileDetailSheet.vue b/src/pages/menuPages/orders/components/OrderMobileDetailSheet.vue index f647493..17e8d74 100644 --- a/src/pages/menuPages/orders/components/OrderMobileDetailSheet.vue +++ b/src/pages/menuPages/orders/components/OrderMobileDetailSheet.vue @@ -36,17 +36,40 @@ :label="row.direction === 2 ? 'مبلغ دریافتی مشتری (ریال)' : 'مبلغ پرداختی مشتری (ریال)'" :value="usePrice(row.payment_amount ?? row.total_price ?? 0, true, '')" /> - - - + + + +
+

فاکتور سود

+
+

+ {{ line.label }}

+

{{ line.line_text || line.detail }}

+

{{ usePrice(Number(line.total ?? 0), true, '') }} ریال

+
+ + - + + + @@ -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) +} diff --git a/src/pages/menuPages/orders/index.vue b/src/pages/menuPages/orders/index.vue index 0602aa8..7e5b77b 100644 --- a/src/pages/menuPages/orders/index.vue +++ b/src/pages/menuPages/orders/index.vue @@ -54,15 +54,10 @@ @calculate-cash="calculateCash" /> - @@ -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({}) +const selectedOrderInfo = ref(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 } diff --git a/src/pages/menuPages/orders/utils/orderDetailDisplay.ts b/src/pages/menuPages/orders/utils/orderDetailDisplay.ts new file mode 100644 index 0000000..c7e0818 --- /dev/null +++ b/src/pages/menuPages/orders/utils/orderDetailDisplay.ts @@ -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, ',') +}