From 71764f7e9a83b4038e4de8926d2aeec5b53a209a Mon Sep 17 00:00:00 2001 From: adonis Date: Tue, 23 Jun 2026 22:31:07 +0330 Subject: [PATCH] fix some bugs --- src/api/wallet.ts | 11 +- src/model/customer.ts | 1 + src/model/order.ts | 5 + src/model/product.ts | 11 + src/model/transaction.ts | 3 + src/model/wallet.ts | 33 ++ src/pages/balance/index.vue | 19 +- .../components/DashboardQuickActionsBar.vue | 3 +- .../components/DashboardQuickWalletModal.vue | 61 ++- .../dashboard/composables/useDashboardPage.ts | 12 +- src/pages/dashboard/dashboardSectionCopy.ts | 202 +++++---- src/pages/dashboard/index.vue | 22 +- .../AccountingMobileDetailSheet.vue | 6 +- .../components/AccountingMobileListItem.vue | 3 +- .../menuPages/accounting/components/table.vue | 4 +- .../accounting/hooks/accountingColumns.tsx | 9 +- src/pages/menuPages/accounting/index.vue | 13 +- .../menuPages/configuration/settingGroups.ts | 2 +- .../menuPages/customers/archive/index.vue | 5 +- .../components/CustomerMobileCardList.vue | 21 +- .../components/CustomerMobileDetailSheet.vue | 56 ++- .../base/components/WalletChargeForm.vue | 75 +++- .../customers/base/components/table.vue | 24 +- .../customers/base/hooks/customerColumns.tsx | 342 +++++++-------- src/pages/menuPages/customers/base/index.vue | 159 ++++--- .../customers/base/utils/walletChargeForm.ts | 39 +- .../GiftCardCodeMobileDetailSheet.vue | 3 +- .../components/GiftCardCodeMobileListItem.vue | 3 +- .../components/GiftCardCodesSheet.vue | 3 +- .../components/GiftCardEditorFormBody.vue | 3 +- .../components/GiftCardMobileDetailSheet.vue | 3 +- .../components/GiftCardMobileListItem.vue | 3 +- .../menuPages/giftCards/components/table.vue | 4 +- .../giftCards/hooks/orderColumns.tsx | 9 +- src/pages/menuPages/giftCards/index.vue | 3 +- .../orders/components/OrderEditorFormBody.vue | 7 +- .../components/OrderMobileDetailSheet.vue | 24 +- .../orders/components/OrderMobileListItem.vue | 14 +- .../menuPages/orders/components/table.vue | 18 +- .../menuPages/orders/hooks/orderColumns.tsx | 397 ++++++++---------- src/pages/menuPages/orders/index.vue | 21 +- .../components/PriceMobileDetailSheet.vue | 3 +- .../menuPages/pricing/hooks/priceColumns.tsx | 9 +- .../menuPages/products/archive/index.vue | 5 +- .../menuPages/products/categories/index.vue | 353 +++++++++++++--- .../components/TransactionMobileCardList.vue | 14 +- .../TransactionMobileDetailSheet.vue | 49 ++- .../components/TransactionMobileListItem.vue | 8 +- .../transactions/components/table.vue | 21 +- .../hooks/transactionsColumns.tsx | 344 +++++++-------- src/pages/menuPages/transactions/index.vue | 3 +- .../components/WalletMobileCardList.vue | 18 +- .../components/WalletMobileDetailSheet.vue | 33 +- .../components/WalletMobileListItem.vue | 43 +- .../menuPages/wallet/components/table.vue | 48 ++- .../menuPages/wallet/hooks/walletColumns.tsx | 216 +++------- src/pages/menuPages/wallet/index.vue | 91 ++-- .../components/WithdrawMobileDetailSheet.vue | 3 +- .../menuPages/withdraw/components/table.vue | 4 +- .../withdraw/hooks/withdrawColumns.tsx | 9 +- src/router/routes/accounting.ts | 32 +- src/router/routes/setting.ts | 11 + src/utils/adminMetalLabels.ts | 131 ++++++ src/utils/categorySelect.ts | 2 +- src/utils/config.ts | 5 + src/utils/metalLabels.ts | 36 ++ src/utils/metalWeight.ts | 68 +++ src/utils/weightUnit.ts | 14 + 68 files changed, 1941 insertions(+), 1288 deletions(-) create mode 100644 src/utils/adminMetalLabels.ts create mode 100644 src/utils/metalLabels.ts create mode 100644 src/utils/metalWeight.ts diff --git a/src/api/wallet.ts b/src/api/wallet.ts index 507b84b..aeda732 100644 --- a/src/api/wallet.ts +++ b/src/api/wallet.ts @@ -1,10 +1,19 @@ import type { TableInput } from '@core/type/baseTable' import useAxios from '@core/utils/useAxios' // import { Customer } from '@/model/customer' -import type { Charge, Wallet, Withdraw, SendWeight } from '@/model/wallet' +import type { Charge, SendWeight, Wallet, WalletChargeTarget, Withdraw } from '@/model/wallet' export const getWalletTable = async (params?: TableInput) => await useAxios.post>('/admin/wallet/table', params) + +export const getWalletChargeTargets = async () => + await useAxios.get<{ targets: WalletChargeTarget[] }>('/admin/wallet/charge-targets') + +/** شارژ یکپارچه — نقدی یا محصول (product_id=null یعنی ریالی) */ +export const chargeWalletUnified = async (params: Charge) => + await useAxios.post('/admin/wallet/charge_wallet', params) + +/** @deprecated از chargeWalletUnified استفاده کنید */ export const chargeWallet = async (params: Charge, type: 'gold' | 'money') => type === 'money' ? await useAxios.post('/admin/wallet/charge', params) diff --git a/src/model/customer.ts b/src/model/customer.ts index b58d073..bc2d816 100644 --- a/src/model/customer.ts +++ b/src/model/customer.ts @@ -28,6 +28,7 @@ export interface Customer { wallet: Wallet wallet_cash?: number wallet_gold?: number + wallet_metal_gold?: Record n_code: string | number last_seen_at:string profit_amount: number diff --git a/src/model/order.ts b/src/model/order.ts index e0283af..52d6a4b 100644 --- a/src/model/order.ts +++ b/src/model/order.ts @@ -2,6 +2,11 @@ import type { Customer } from '@/model/customer' export interface Order { amount?: number + amount_display?: number + amount_unit?: string + order_metal_id?: number + order_metal_label?: string + metal_prices?: Record created_at?: string customer?: Customer customer_id?: number diff --git a/src/model/product.ts b/src/model/product.ts index 46d59ad..38d702c 100644 --- a/src/model/product.ts +++ b/src/model/product.ts @@ -58,6 +58,16 @@ export interface SetActiveTime { auto_close_at: string } +export interface MetalBalanceItem { + metal_id: number + label: string + balance: number + goldBalance: number + cashBalance?: number + goldBalanceOfLiman?: number + goldBalanceOfKalhor?: number +} + export interface Balance { cashBalance: number cashBalanceOfKalhor: number @@ -65,6 +75,7 @@ export interface Balance { goldBalance: number goldBalanceOfKalhor: number goldBalanceOfLiman: number + metalBalances?: MetalBalanceItem[] mazanehBalance: number mazanehBalanceOfKalhor: number income_money: number diff --git a/src/model/transaction.ts b/src/model/transaction.ts index 0d61d16..c7eaedf 100644 --- a/src/model/transaction.ts +++ b/src/model/transaction.ts @@ -14,6 +14,9 @@ export interface Transaction { hash: string id: number note: number + metal_gold?: Record + transaction_metal_id?: number + transaction_metal_label?: string order_id: number transaction_type: string type: number diff --git a/src/model/wallet.ts b/src/model/wallet.ts index 95c4a79..fbad0e8 100644 --- a/src/model/wallet.ts +++ b/src/model/wallet.ts @@ -1,5 +1,13 @@ import type { User } from '@/model/user' +export interface WalletMetalBalance { + metal_id: number + label: string + gold: number + goldToCash: number + display_unit?: string +} + export interface Wallet { owner: number id: number @@ -7,9 +15,18 @@ export interface Wallet { cash: number fund_id?: number | null product_id?: number | null + metal_gold?: Record customer: User } +export interface WalletTableSums { + cash?: number + gold?: number + cashToGold?: number + goldToCash?: number + metalBalances?: WalletMetalBalance[] +} + export interface WithdrawTransaction { title?: string gold?: number @@ -37,6 +54,20 @@ export interface Withdraw { export type WalletChargeDirection = 'charge' | 'discharge' +export type WalletChargeTargetKind = 'money' | 'metal' + +export interface WalletChargeTarget { + key: string + kind: WalletChargeTargetKind + label: string + unit: string + product_id?: number | null + metal_id?: number + uses_main_wallet?: boolean + trade_by_quantity?: boolean + group_type?: string +} + export interface Charge { value?: number note?: string @@ -44,6 +75,8 @@ export interface Charge { talabBedehi?: boolean direction?: WalletChargeDirection amountAbs?: number | string + chargeTargetKey?: string | null + product_id?: number | null } export interface WithdrawStatus { diff --git a/src/pages/balance/index.vue b/src/pages/balance/index.vue index c6b557e..3739ec0 100644 --- a/src/pages/balance/index.vue +++ b/src/pages/balance/index.vue @@ -27,13 +27,27 @@ un-checked-children="مشتری" /> +
+
+
+

{{ metalBalanceTitleWithUnit(item.metal_id, item.label) }}

+

+ {{ formatMetalWeightFromGrams(item.goldBalanceOfLiman ?? 0, item.metal_id) }} +

+
+
+
تراز ها
تراز ریالی (ریال)
- تراز طلایی (گرم) + {{ m('تراز طلایی (گرم)') }}
تراز مظنه (ریال) @@ -187,6 +201,9 @@ import { balance, getTaraz } from '@core/utils/price' import { usePrice } from '@core/utils/usePrice' import { onMounted, ref } from 'vue' import { getNow, toJalali } from '@core/utils/useDataTime' +import { m } from '@/utils/metalLabels' +import { metalBalanceTitleWithUnit } from '@/utils/adminMetalLabels' +import { formatMetalWeightFromGrams } from '@/utils/metalWeight' const startTaraz = ref('') const endTaraz = ref('') const selectedItem = ref({}) diff --git a/src/pages/dashboard/components/DashboardQuickActionsBar.vue b/src/pages/dashboard/components/DashboardQuickActionsBar.vue index 84606cd..2734317 100644 --- a/src/pages/dashboard/components/DashboardQuickActionsBar.vue +++ b/src/pages/dashboard/components/DashboardQuickActionsBar.vue @@ -38,7 +38,7 @@ شارژ / دشارژ کیف پول - مدیریت موجودی ریالی و طلایی + {{ metalLabel.manageGoldCash() }} @@ -106,6 +106,7 @@ import type { RouteLocationRaw } from 'vue-router' import BasicActionSheet from '@/components/BasicActionSheet.vue' import DashboardQuickActionBtn from '@/pages/dashboard/components/DashboardQuickActionBtn.vue' import { isMobile } from '@core/utils/is' +import { metalLabel } from '@/utils/adminMetalLabels' type ActionVariant = 'default' | 'accent' | 'danger' | 'primary' type ItemTone = 'slate' | 'emerald' | 'rose' | 'amber' | 'sky' | 'violet' diff --git a/src/pages/dashboard/components/DashboardQuickWalletModal.vue b/src/pages/dashboard/components/DashboardQuickWalletModal.vue index b5f1c76..713a3c2 100644 --- a/src/pages/dashboard/components/DashboardQuickWalletModal.vue +++ b/src/pages/dashboard/components/DashboardQuickWalletModal.vue @@ -2,7 +2,7 @@ -
-
- طلایی -

{{ formatGold(selectedCustomer) }}

-
+
ریالی

{{ formatCash(selectedCustomer) }}

-
- - - ریالی - طلایی - -
- نقد: {{ cashLabel }} - طلا: {{ goldLabel }} + {{ metalLabel.goldUnit() }}: {{ goldLabel }}
@@ -21,6 +21,7 @@ import type { TotalAccounting } from '@/model/accounting' import MobileCard from '@/components/mobile/MobileCard.vue' import { toJalali } from '@core/utils/useDataTime' import { usePrice } from '@core/utils/usePrice' +import { metalLabel } from '@/utils/adminMetalLabels' const props = defineProps<{ item: TotalAccounting diff --git a/src/pages/menuPages/accounting/components/table.vue b/src/pages/menuPages/accounting/components/table.vue index eb83d9a..9a10612 100644 --- a/src/pages/menuPages/accounting/components/table.vue +++ b/src/pages/menuPages/accounting/components/table.vue @@ -31,7 +31,7 @@ diff --git a/src/pages/menuPages/orders/components/OrderMobileListItem.vue b/src/pages/menuPages/orders/components/OrderMobileListItem.vue index 935e1f3..0a2b6df 100644 --- a/src/pages/menuPages/orders/components/OrderMobileListItem.vue +++ b/src/pages/menuPages/orders/components/OrderMobileListItem.vue @@ -6,6 +6,12 @@ {{ directionLabel }} + + {{ item.order_metal_label }} + {{ customerName }} {{ statusLabel }} @@ -13,7 +19,7 @@
- طلا: + مقدار: {{ amountLabel }} {{ totalPriceLabel }} @@ -26,6 +32,7 @@ import type { Order } from '@/model/order' import MobileCard from '@/components/mobile/MobileCard.vue' import { usePrice } from '@core/utils/usePrice' +import { formatAmountWithUnit } from '@/utils/weightUnit' const props = defineProps<{ item: Order & { sts_str?: string; service_charge?: number } @@ -44,9 +51,8 @@ const directionClass = computed(() => const customerName = computed(() => props.item.customer?.name || '-') const amountLabel = computed(() => { - const amount = props.item.amount - if (amount == null) return '-' - return `${Number(amount.toFixed(3))} گرم` + const amount = props.item.amount_display ?? props.item.amount + return formatAmountWithUnit(amount, props.item.amount_unit) }) const totalPriceLabel = computed(() => usePrice(props.item.total_price ?? 0, true, '')) const statusLabel = computed(() => props.item.sts_str || '-') diff --git a/src/pages/menuPages/orders/components/table.vue b/src/pages/menuPages/orders/components/table.vue index 43fd89e..a25d65d 100644 --- a/src/pages/menuPages/orders/components/table.vue +++ b/src/pages/menuPages/orders/components/table.vue @@ -42,7 +42,7 @@ import type { TableInput } from '@core/type/baseTable' import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable' import Icon from '@core/components/BaseIcon/src/Icon.vue' import { changeCustomerActivation, getCustomerTable } from '@/api/customer' -import { cols } from '../hooks/orderColumns' +import { buildOrderColumns } from '../hooks/orderColumns' import { useUserStore } from '@/store/userStore' import { usePrefix } from '@core/composable/usePrefix' import { message } from 'ant-design-vue' @@ -74,22 +74,22 @@ function checkPermission(temp: string) { const table = ref(null) const tblProps = ref({ - columns: cols, - api: (params: TableInput) => { + columns: buildOrderColumns(), + api: async (params: TableInput) => { if (props.customerId) { params.filters = [] params.filters?.push({ - field: "customer_id", - type: "text", + field: 'customer_id', + type: 'text', value: props.customerId, - cr: "and", - op: "eq", + cr: 'and', + op: 'eq', hidden: false }) } - return getOrderTable(params) + const data = await getOrderTable(params) + return data } - //bordered: true }) const loading = ref(false) diff --git a/src/pages/menuPages/orders/hooks/orderColumns.tsx b/src/pages/menuPages/orders/hooks/orderColumns.tsx index f4db434..004b7be 100644 --- a/src/pages/menuPages/orders/hooks/orderColumns.tsx +++ b/src/pages/menuPages/orders/hooks/orderColumns.tsx @@ -1,239 +1,178 @@ import type { BaseTableColumn } from '@core/type/baseTable' -import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime' +import { toJalali } from '@core/utils/useDataTime' import CustomTag from '@/components/CustomTag.vue' import { usePrice } from '@core/utils/usePrice' -/*import BudgetSelect from '@/components/BudgetSelect.vue' -import { useStaticApiListsStore } from '@/store/staticApisStore' -import type { FilterSelect } from '@/model/filterSelect' -import type { DocItem } from '@/model/docType'*/ +import { formatAmountWithUnit } from '@/utils/weightUnit' -//define store -//const store = useStaticApiListsStore() -//document types -/*let docTypes: DocItem[]*/ -/*onMounted(async () => { - docTypes = await store.readyDocTypeList() -}) -const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => { - return { - label: x.title, - value: x.id - } -})*/ +export type OrderMetalColumn = { metal_id: number; label: string; display_unit?: string } -export const cols: BaseTableColumn[] = [ - { - title: 'ردیف', - dataIndex: 'id', - key: 'id', - filterable: false, - sortable: false, - customRender: (record: any) => { - return record.index + 1 - } - }, - { - title: 'کد سفارش', - dataIndex: 'id', - key: 'id', - type: 'Number' - // className: 'column-money' - }, - { - title: 'نوع سفارش', - dataIndex: 'direction', - key: 'direction', - filterable: false, - customRender: ({ record }) => { - switch (record.direction) { - case 2: - return ( -
- - فروش - -
- ) - case 1: - return ( -
- - خرید - -
- ) +export function buildOrderColumns(_metals: OrderMetalColumn[] = []): BaseTableColumn[] { + return [ + { + title: 'ردیف', + dataIndex: 'id', + key: 'id', + filterable: false, + sortable: false, + customRender: (record: any) => { + return record.index + 1 } - } - }, - { - title: 'نام و نام خانوادگی', - dataIndex: 'customer.name', - key: 'customer.name', - type: 'Text' - }, - { - title: 'شماره موبایل', - dataIndex: 'customer.mobile', - key: 'customer.mobile', - type: 'Number' - }, - { - title: 'مقدار (گرم)', - dataIndex: 'amount', - key: 'amount', - type: 'Text' - }, - /*{ - title: 'کارمزد ریالی', - dataIndex: 'service_charge_price', - key: 'service_charge_price', - type: 'Number', - customRender(data) { - return usePrice(data.record?.service_charge_price ?? 0, true, '') - } - // className: 'column-money' - },*/ - { - title: 'کارمزد طلایی', - dataIndex: 'service_charge', - key: 'service_charge', - type: 'Number', - customRender(data) { - return data.record?.service_charge - } - // className: 'column-money' - }, - { - title: 'قیمت هر گرم طلا برای مشتری (ریال)', - dataIndex: 'price', - key: 'price', - type: 'Text', - customRender(data) { - return usePrice(data.record?.price ?? 0, true, '') - } - }, -/* { - title: 'قیمت هر گرم طلا در لیمان', - dataIndex: 'limanPrice', - key: 'limanPrice', - type: 'Text', - customRender(data) { - return usePrice(data.record?.total_price_liman ?? 0, true, '') - } - },*/ - { - title: 'قیمت کل برای مشتری (ریال)', - dataIndex: 'total_price', - key: 'total_price', - type: 'Text', - customRender(data) { - return usePrice(data.record?.total_price ?? 0, true, '') - } - }, - - { - title: 'وضعیت', - dataIndex: 'sts', - key: 'sts', - filterable: false, - customRender: ({ record }) => { - switch (record.sts) { - case 1: - return ( -
- - {record.sts_str} - -
- ) - case 2: - return ( -
- - {record.sts_str} - -
- ) - default: - return ( -
- - {record.sts_str} - -
- ) + }, + { + title: 'کد سفارش', + dataIndex: 'id', + key: 'id', + type: 'Number' + }, + { + title: 'نوع سفارش', + dataIndex: 'direction', + key: 'direction', + filterable: false, + customRender: ({ record }) => { + switch (record.direction) { + case 2: + return ( +
+ + فروش + +
+ ) + case 1: + return ( +
+ + خرید + +
+ ) + } } - } - }, - { - title: 'تاریخ ثبت', - dataIndex: 'created_at', - key: 'created_at', - type: 'DateTime', - customRender: (record: any) => { - return toJalali(record.record.created_at) - } - }, - /*{ - title: 'نقش', - dataIndex: 'user_type_record', - key: 'user_type_record', - type: 'Select', - filterable: false, - sortable: false, - filterComponentProps: { - multiple: true - }, - filterField: 'user_type_obj.id', - filterComponent: ReadyUserTypeSelect, - /!* customRender: (record: any) => { - return record.record.user_type_record?.value - }*!/ - customRender: ({ record }) => { - const items = record.is_admin ? ( - - ادمین + }, + { + title: 'نام و نام خانوادگی', + dataIndex: 'customer.name', + key: 'customer.name', + type: 'Text' + }, + { + title: 'شماره موبایل', + dataIndex: 'customer.mobile', + key: 'customer.mobile', + type: 'Number' + }, + { + title: 'نوع فلز', + dataIndex: 'order_metal_label', + key: 'order_metal_label', + type: 'Text', + filterable: false, + customRender({ record }) { + return ( +
+ + {record.order_metal_label ?? '-'} - ) : ( - record.user_type_obj.map((item: { title: any }, index: any) => { - if (item) { - return ( - - {item?.title} - - ) - } - }) - ) - return
{items}
+
+ ) + } + }, + { + title: 'مقدار', + dataIndex: 'amount', + key: 'amount', + type: 'Text', + customRender({ record }) { + const amount = record.amount_display ?? record.amount + return formatAmountWithUnit(amount, record.amount_unit) + } + }, + { + title: 'کارمزد', + dataIndex: 'service_charge', + key: 'service_charge', + type: 'Number', + customRender({ record }) { + const value = record.service_charge + if (value == null || value === '') { + return '-' } - }, - { - title: 'حوزه', - dataIndex: 'section', - key: 'section' - }, - { - title: 'شماره موبایل', - dataIndex: 'mobile', - type: 'Number', - key: 'mobile' - }, - { - title: 'تاریخ ثبت', - dataIndex: 'created_at', - key: 'created_at', - type: 'DateTime', - customRender: (record: any) => { - return toJalaliNoTime(record.record.created_at) + return formatAmountWithUnit(Number(value), record.amount_unit) + } + }, + { + title: 'قیمت زمان معامله (ریال)', + dataIndex: 'price', + key: 'price', + type: 'Text', + customRender({ record }) { + return usePrice(record.price ?? 0, true, '') + } + }, + { + title: 'قیمت کل برای مشتری (ریال)', + dataIndex: 'total_price', + key: 'total_price', + type: 'Text', + customRender(data) { + return usePrice(data.record?.total_price ?? 0, true, '') + } + }, + { + title: 'وضعیت', + dataIndex: 'sts', + key: 'sts', + filterable: false, + customRender: ({ record }) => { + switch (record.sts) { + case 1: + return ( +
+ + {record.sts_str} + +
+ ) + case 2: + return ( +
+ + {record.sts_str} + +
+ ) + default: + return ( +
+ + {record.sts_str} + +
+ ) } - },*/ - { - title: 'عملیات', - dataIndex: 'action', - key: 'action', - slot: 'action', - fixed: 'right', - filterable: false - } -] + } + }, + { + title: 'تاریخ ثبت', + dataIndex: 'created_at', + key: 'created_at', + type: 'DateTime', + customRender: (record: any) => { + return toJalali(record.record.created_at) + } + }, + { + title: 'عملیات', + dataIndex: 'action', + key: 'action', + slot: 'action', + fixed: 'right', + filterable: false + } + ] +} + +export function getOrderColumns(): BaseTableColumn[] { + return buildOrderColumns() +} diff --git a/src/pages/menuPages/orders/index.vue b/src/pages/menuPages/orders/index.vue index 744ae7c..0a9dc21 100644 --- a/src/pages/menuPages/orders/index.vue +++ b/src/pages/menuPages/orders/index.vue @@ -80,6 +80,8 @@ 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' //loading const loading = ref(false) @@ -182,11 +184,15 @@ function info(val: Order) { infoParams.itemsTitle = 'اطلاعات کاربری' infoParams.items = [ { - key: 'مقدار (گرم)', - value: val.amount ?? '-' + key: 'مقدار', + value: formatAmountWithUnit(val.amount_display ?? val.amount, val.amount_unit) }, { - key: 'قیمت هر گرم طلا برای مشتری (ریال)', + key: 'نوع فلز', + value: val.order_metal_label ?? '-' + }, + { + key: 'قیمت زمان معامله (ریال)', value: usePrice(val.price ?? 0) ?? '-' }, { @@ -194,7 +200,7 @@ function info(val: Order) { value: usePrice(val.total_price ?? 0) ?? '-' }, { - key: 'قیمت هر گرم طلا بدون سود (ریال)', + key: metalLabel.goldGramPriceOriginal(), value: usePrice(val.price_original ?? 0) ?? '-' }, { @@ -226,8 +232,11 @@ function info(val: Order) { value: usePrice(val.service_charge_price ?? 0) },*/ { - key: 'کارمزد طلایی', - value: val.service_charge ?? '-' + ' گرم' + key: 'کارمزد', + value: + val.service_charge != null && val.service_charge !== '' + ? formatAmountWithUnit(Number(val.service_charge), val.amount_unit) + : '-' }, /* { key: 'تاریخ تولد', diff --git a/src/pages/menuPages/pricing/components/PriceMobileDetailSheet.vue b/src/pages/menuPages/pricing/components/PriceMobileDetailSheet.vue index a3587c1..6718b46 100644 --- a/src/pages/menuPages/pricing/components/PriceMobileDetailSheet.vue +++ b/src/pages/menuPages/pricing/components/PriceMobileDetailSheet.vue @@ -4,7 +4,7 @@
- + @@ -32,6 +32,7 @@ import MobileStatCard from '@/components/mobile/MobileStatCard.vue' import { isMobile } from '@core/utils/is' import { toJalaliNoTime } from '@core/utils/useDataTime' import { usePrice } from '@core/utils/usePrice' +import { metalLabel } from '@/utils/adminMetalLabels' const props = defineProps<{ visible: boolean diff --git a/src/pages/menuPages/pricing/hooks/priceColumns.tsx b/src/pages/menuPages/pricing/hooks/priceColumns.tsx index 125f47d..06d73e8 100644 --- a/src/pages/menuPages/pricing/hooks/priceColumns.tsx +++ b/src/pages/menuPages/pricing/hooks/priceColumns.tsx @@ -2,6 +2,7 @@ import type { BaseTableColumn } from '@core/type/baseTable' import { toJalaliNoTime, toTime } from '@core/utils/useDataTime' import CustomTag from '@/components/CustomTag.vue' import { usePrice } from '@core/utils/usePrice' +import { metalLabel } from '@/utils/adminMetalLabels' /*import BudgetSelect from '@/components/BudgetSelect.vue' import { useStaticApiListsStore } from '@/store/staticApisStore' import type { FilterSelect } from '@/model/filterSelect' @@ -21,7 +22,8 @@ const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => { } })*/ -export const cols: BaseTableColumn[] = [ +export function getPriceColumns(): BaseTableColumn[] { + return [ { title: 'ردیف', dataIndex: 'id', @@ -53,7 +55,7 @@ export const cols: BaseTableColumn[] = [ type: 'Text' }, { - title: 'قیمت هر گرم طلا برای مشتری (ریال)', + title: metalLabel.goldGramPriceCustomer(), dataIndex: 'price', key: 'price', type: 'Text', @@ -157,4 +159,5 @@ export const cols: BaseTableColumn[] = [ fixed: 'right', filterable: false }*/ -] + ] +} diff --git a/src/pages/menuPages/products/archive/index.vue b/src/pages/menuPages/products/archive/index.vue index 3503778..7ee2d13 100644 --- a/src/pages/menuPages/products/archive/index.vue +++ b/src/pages/menuPages/products/archive/index.vue @@ -118,9 +118,9 @@ > - +
@@ -162,6 +162,7 @@ import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer' import type { Charge } from '@/model/wallet' import customers from '@/router/routes/customers' import { chargeWallet } from '@/api/wallet' +import { metalLabel } from '@/utils/adminMetalLabels' //loading const loading = ref(false) diff --git a/src/pages/menuPages/products/categories/index.vue b/src/pages/menuPages/products/categories/index.vue index 6b9f942..f311cfe 100644 --- a/src/pages/menuPages/products/categories/index.vue +++ b/src/pages/menuPages/products/categories/index.vue @@ -1,6 +1,6 @@ diff --git a/src/pages/menuPages/transactions/components/TransactionMobileListItem.vue b/src/pages/menuPages/transactions/components/TransactionMobileListItem.vue index 322c962..13c4617 100644 --- a/src/pages/menuPages/transactions/components/TransactionMobileListItem.vue +++ b/src/pages/menuPages/transactions/components/TransactionMobileListItem.vue @@ -6,6 +6,12 @@ {{ typeLabel }} + + {{ item.transaction_metal_label }} + {{ item.type_str || '-' }} {{ dateLabel }} @@ -31,7 +37,7 @@ defineEmits<{ }>() const isMoneyType = computed(() => props.item.type === 4 || props.item.type === 3) -const typeLabel = computed(() => (isMoneyType.value ? 'پول' : 'طلا')) +const typeLabel = computed(() => (isMoneyType.value ? 'پول' : 'فلز')) const typeTagClass = computed(() => isMoneyType.value ? 'border-green-300 bg-green-50 text-green-700' diff --git a/src/pages/menuPages/transactions/components/table.vue b/src/pages/menuPages/transactions/components/table.vue index d22c8ba..1590f85 100644 --- a/src/pages/menuPages/transactions/components/table.vue +++ b/src/pages/menuPages/transactions/components/table.vue @@ -67,7 +67,7 @@ import type {TableInput} from '@core/type/baseTable' import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable' import Icon from '@core/components/BaseIcon/src/Icon.vue' import {changeCustomerActivation, getCustomerTable} from '@/api/customer' -import {cols} from '../hooks/transactionsColumns' +import { buildTransactionsColumns } from '../hooks/transactionsColumns' import {useUserStore} from '@/store/userStore' import {usePrefix} from '@core/composable/usePrefix' import {message} from 'ant-design-vue' @@ -102,23 +102,24 @@ const {prefixCls} = usePrefix('pophoverstatus') const table = ref(null) const tblProps = ref({ - columns: cols, - api: (params: TableInput) => { - // console.log(props.customerId) + columns: buildTransactionsColumns(), + api: async (params: TableInput) => { if (props.customerId) { params.filters = [] params.filters?.push({ - field: "customer_id", - type: "text", + field: 'customer_id', + type: 'text', value: props.customerId, - cr: "and", - op: "eq", + cr: 'and', + op: 'eq', hidden: false }) } - return getTransactionTable(params) + const data = await getTransactionTable(params) + const metals = data?.catalogMetals ?? [] + tblProps.value.columns = buildTransactionsColumns(metals) + return data } - //bordered: true }) const loading = ref(false) /*async function changeCustomerActivationFunction(id: number) { diff --git a/src/pages/menuPages/transactions/hooks/transactionsColumns.tsx b/src/pages/menuPages/transactions/hooks/transactionsColumns.tsx index 801ba8d..031c972 100644 --- a/src/pages/menuPages/transactions/hooks/transactionsColumns.tsx +++ b/src/pages/menuPages/transactions/hooks/transactionsColumns.tsx @@ -1,203 +1,165 @@ -import type {BaseTableColumn} from '@core/type/baseTable' -import {toJalaliNoTime, toTime} from '@core/utils/useDataTime' +import type { BaseTableColumn } from '@core/type/baseTable' +import { toJalaliNoTime } from '@core/utils/useDataTime' import CustomTag from '@/components/CustomTag.vue' -import {usePrice} from '@core/utils/usePrice' -/*import BudgetSelect from '@/components/BudgetSelect.vue' -import { useStaticApiListsStore } from '@/store/staticApisStore' -import type { FilterSelect } from '@/model/filterSelect' -import type { DocItem } from '@/model/docType'*/ +import { usePrice } from '@core/utils/usePrice' +import { metalLabel, transactionMetalAmountTitle } from '@/utils/adminMetalLabels' +import { formatMetalDisplayValuePlain, metalCellValue } from '@/utils/metalWeight' -//define store -//const store = useStaticApiListsStore() -//document types -/*let docTypes: DocItem[]*/ -/*onMounted(async () => { - docTypes = await store.readyDocTypeList() -}) -const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => { - return { - label: x.title, - value: x.id +export type TransactionMetalColumn = { metal_id: number; label: string; display_unit?: string } + +function renderSignedMetalCell(value: number, metalId: number) { + if (!value) { + return ( +
+
0
+
+ ) } -})*/ -export const cols: BaseTableColumn[] = [ - { - title: 'ردیف', - dataIndex: 'id', - key: 'id', - filterable: false, - sortable: false, - customRender: (record: any) => { - return record.index + 1 - } - }, - { - title: 'کد تراکنش', - dataIndex: 'id', - key: 'id', - type: 'Number' - // className: 'column-money' - }, - { - title: 'نام تراکنش', - dataIndex: 'type_str', - key: 'type_str', - type: 'Text' - // className: 'column-money' - }, - { - title: 'علت تراکنش', - dataIndex: 'note', - key: 'note', - type: 'Text', + const cls = value > 0 ? 'text-green' : 'text-red' + const formatted = formatMetalDisplayValuePlain(Math.abs(value), metalId) + return ( +
+
{value > 0 ? formatted : `-${formatted}`}
+
+ ) +} - // className: 'column-money' +function isMoneyTransaction(record: { type?: number }) { + return record.type === 4 || record.type === 3 +} + +export function buildTransactionsColumns(metals: TransactionMetalColumn[] = []): BaseTableColumn[] { + const baseColumns: BaseTableColumn[] = [ + { + title: 'ردیف', + dataIndex: 'id', + key: 'id', + filterable: false, + sortable: false, + customRender: (record: any) => { + return record.index + 1 + } }, { - title: 'نوع تراکنش', - dataIndex: 'active', - key: 'active', - filterable: false, - sortable: false, - customRender: ({record}) => { - return record.type === 4 || record.type === 3 ? ( -
- - پول - -
- ) : ( -
- {/**/} - - طلا - -
- ) - } - }, - { - title: 'نام مشتری', - dataIndex: 'customer.name', - key: 'customer.name', - type: 'Text' - }, - { - title: 'مقدار طلا طی تراکنش (گرم)', - dataIndex: 'gold', - key: 'gold', - type: 'Number', - customRender: ({record}) => { - return record.gold > 0 ? ( -
-
{record.gold}
-
- ) : ( -
- {/**/} -
{record.gold}
-
- ) - } - }, - { - title: 'مقدار پول طی تراکنش (ریال)', - dataIndex: 'cash', - key: 'cash', - type: 'Number', - customRender: ({record}) => { - return record.cash > 0 ? ( -
-
{usePrice(record?.cash ?? 0, true, '')}
-
- ) : ( -
- {/**/} -
{usePrice(record?.cash ?? 0, true, '')}
-
- ) - } - }, - /* { - title: 'شماره سفارش', - dataIndex: 'order_id', - key: 'order_id', - type: 'Text' - },*/ - /* { - title: 'شماره موبایل', - dataIndex: 'mobile', - key: 'mobile', + title: 'کد تراکنش', + dataIndex: 'id', + key: 'id', type: 'Number' - },*/ - /* { - title: 'نام فروشگاه', - dataIndex: 'shop_name', - key: 'shop_name', - type: 'Text' - },*/ + }, { - title: 'تاریخ تراکنش', - dataIndex: 'created_at', - key: 'created_at', - type: 'DateTime', - customRender: (record: any) => { - return toJalaliNoTime(record.record.created_at) - } - } - /*{ - title: 'نقش', - dataIndex: 'user_type_record', - key: 'user_type_record', - type: 'Select', - filterable: false, - sortable: false, - filterComponentProps: { - multiple: true - }, - filterField: 'user_type_obj.id', - filterComponent: ReadyUserTypeSelect, - /!* customRender: (record: any) => { - return record.record.user_type_record?.value - }*!/ - customRender: ({ record }) => { - const items = record.is_admin ? ( - - ادمین + title: 'نام تراکنش', + dataIndex: 'type_str', + key: 'type_str', + type: 'Text' + }, + { + title: 'علت تراکنش', + dataIndex: 'note', + key: 'note', + type: 'Text' + }, + { + title: 'نوع تراکنش', + dataIndex: 'active', + key: 'active', + filterable: false, + sortable: false, + customRender: ({ record }) => { + return isMoneyTransaction(record) ? ( +
+ + پول - ) : ( - record.user_type_obj.map((item: { title: any }, index: any) => { - if (item) { - return ( - - {item?.title} - - ) - } - }) - ) - return
{items}
+
+ ) : ( +
+ + فلز + +
+ ) + } + }, + { + title: 'نوع فلز', + dataIndex: 'transaction_metal_label', + key: 'transaction_metal_label', + filterable: false, + sortable: false, + customRender: ({ record }) => { + if (isMoneyTransaction(record)) { + return '-' } - }, - { - title: 'حوزه', - dataIndex: 'section', - key: 'section' - }, - { - title: 'شماره موبایل', - dataIndex: 'mobile', - type: 'Number', - key: 'mobile' - },*/ - /* { - title: 'عملیات', - dataIndex: 'action', - key: 'action', - slot: 'action', - fixed: 'right', - filterable: false - }*/ -] + return ( +
+ + {record.transaction_metal_label ?? metalLabel.goldUnit()} + +
+ ) + } + }, + { + title: 'نام مشتری', + dataIndex: 'customer.name', + key: 'customer.name', + type: 'Text' + } + ] + + const metalColumns: BaseTableColumn[] = + metals.length > 0 + ? metals.map((metal) => ({ + title: transactionMetalAmountTitle(metal.metal_id, metal.label), + dataIndex: `metal_gold_${metal.metal_id}`, + key: `metal_${metal.metal_id}`, + type: 'Number', + customRender: ({ record }) => renderSignedMetalCell(metalCellValue(record, metal.metal_id), metal.metal_id) + })) + : [ + { + title: metalLabel.goldInTransaction(), + dataIndex: 'gold', + key: 'gold', + type: 'Number', + customRender: ({ record }) => renderSignedMetalCell(Number(record.gold ?? 0), 1) + } + ] + + return [ + ...baseColumns, + ...metalColumns, + { + title: 'مقدار پول طی تراکنش (ریال)', + dataIndex: 'cash', + key: 'cash', + type: 'Number', + customRender: ({ record }) => { + return record.cash > 0 ? ( +
+
{usePrice(record?.cash ?? 0, true, '')}
+
+ ) : ( +
+
{usePrice(record?.cash ?? 0, true, '')}
+
+ ) + } + }, + { + title: 'تاریخ تراکنش', + dataIndex: 'created_at', + key: 'created_at', + type: 'DateTime', + customRender: (record: any) => { + return toJalaliNoTime(record.record.created_at) + } + } + ] +} + +export function getTransactionsColumns(): BaseTableColumn[] { + return buildTransactionsColumns() +} diff --git a/src/pages/menuPages/transactions/index.vue b/src/pages/menuPages/transactions/index.vue index 6510cfb..e20b50c 100644 --- a/src/pages/menuPages/transactions/index.vue +++ b/src/pages/menuPages/transactions/index.vue @@ -93,6 +93,7 @@ import type { Item } from '@/model/Item' import type { Customer } from '@/model/customer' import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer' import type { Transaction } from '@/model/transaction' +import { metalLabel } from '@/utils/adminMetalLabels' //loading const loading = ref(false) @@ -127,7 +128,7 @@ let infoParams = reactive({}) function info(val: Transaction) { infoParams.name = val.customer.name - infoParams.type = val.transaction_type === '1' ? 'نقدی' : 'طلا' + infoParams.type = val.transaction_type === '1' ? 'نقدی' : metalLabel.goldUnit() infoParams.date = toJalaliNoTime(val.created_at) infoParams.time = toTime(val.created_at) infoParams.itemsTitle = 'اطلاعات کاربری' diff --git a/src/pages/menuPages/wallet/components/WalletMobileCardList.vue b/src/pages/menuPages/wallet/components/WalletMobileCardList.vue index 9a262fc..d6e3f5b 100644 --- a/src/pages/menuPages/wallet/components/WalletMobileCardList.vue +++ b/src/pages/menuPages/wallet/components/WalletMobileCardList.vue @@ -15,6 +15,7 @@ v-for="item in items" :key="item.id" :item="item" + :metals="catalogMetals" @open="openSheet" /> @@ -22,36 +23,39 @@ diff --git a/src/pages/menuPages/wallet/components/WalletMobileListItem.vue b/src/pages/menuPages/wallet/components/WalletMobileListItem.vue index 55d13c1..2091db7 100644 --- a/src/pages/menuPages/wallet/components/WalletMobileListItem.vue +++ b/src/pages/menuPages/wallet/components/WalletMobileListItem.vue @@ -11,9 +11,19 @@ ریال: {{ cashLabel }} - - طلا: - {{ goldLabel }} + + {{ metalEntries[0].label }}: + {{ metalEntries[0].display }} + + + +
+ + {{ entry.label }}: + {{ entry.display }}
@@ -24,9 +34,12 @@ import type { Wallet } from '@/model/wallet' import MobileCard from '@/components/mobile/MobileCard.vue' import { usePrice } from '@core/utils/usePrice' +import { formatMetalDisplayValue, metalCellValue } from '@/utils/metalWeight' +import type { WalletMetalColumn } from '../hooks/walletColumns' const props = defineProps<{ item: Wallet + metals?: WalletMetalColumn[] }>() defineEmits<{ @@ -35,9 +48,25 @@ defineEmits<{ const customerName = computed(() => props.item.customer?.name || '-') const cashLabel = computed(() => usePrice(props.item.cash ?? 0, true, '')) -const goldLabel = computed(() => { - const gold = props.item.gold - if (gold == null) return '-' - return `${Number(Number(gold).toFixed(3))} گرم` + +function metalValue(metalId: number): number { + return metalCellValue(props.item as Record, metalId) +} + +const metalEntries = computed(() => { + const metals = props.metals?.length + ? props.metals + : [{ metal_id: 1, label: 'طلا' }] + + return metals + .map((metal) => { + const value = metalValue(metal.metal_id) + return { + metal_id: metal.metal_id, + label: metal.label, + display: formatMetalDisplayValue(value, metal.metal_id) + } + }) + .filter((entry) => metalValue(entry.metal_id) !== 0) }) diff --git a/src/pages/menuPages/wallet/components/table.vue b/src/pages/menuPages/wallet/components/table.vue index fb8c286..b213bf1 100644 --- a/src/pages/menuPages/wallet/components/table.vue +++ b/src/pages/menuPages/wallet/components/table.vue @@ -2,7 +2,7 @@
- -->
مجموع
ریال
-
گرم
-
کیف طلا
-
- {{ usePrice(sums?.goldToCash ?? 0, true, '') }} -
-
- {{ usePrice(sums?.gold ?? 0, true, '') }} -
+
وزن
+ + +
کیف پول
{{ usePrice(sums?.cash ?? 0, true, '') }}
{{ usePrice(sums?.cashToGold ?? 0, true, '') }}
@@ -165,6 +153,8 @@ import type { Item } from '@/model/Item' import type { Customer } from '@/model/customer' import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer' import { usePrice } from '@core/utils/usePrice' +import type { WalletTableSums } from '@/model/wallet' +import { formatMetalDisplayValue } from '@/utils/metalWeight' //loading const loading = ref(false) @@ -172,7 +162,38 @@ const loading = ref(false) //define store const store = useStaticApiListsStore() -const sums = ref() +const sums = ref() + +const mobileMetalRows = computed(() => { + if (sums.value?.metalBalances?.length) { + return sums.value.metalBalances.map((metal) => ({ + metal_id: metal.metal_id, + label: metal.label, + gold: metal.gold ?? 0, + goldToCash: metal.goldToCash ?? 0 + })) + } + + if (sums.value?.gold != null || sums.value?.goldToCash != null) { + return [ + { + metal_id: 1, + label: 'طلا', + gold: sums.value.gold ?? 0, + goldToCash: sums.value.goldToCash ?? 0 + } + ] + } + + return [] +}) + +function sumToneClass(value?: number) { + if (value == null || value >= 0) { + return value === 0 ? 'bg-amber-600' : 'bg-green-600' + } + return 'bg-red-600' +} //define tbl const table = ref(null) diff --git a/src/pages/menuPages/withdraw/components/WithdrawMobileDetailSheet.vue b/src/pages/menuPages/withdraw/components/WithdrawMobileDetailSheet.vue index 6550585..2537c73 100644 --- a/src/pages/menuPages/withdraw/components/WithdrawMobileDetailSheet.vue +++ b/src/pages/menuPages/withdraw/components/WithdrawMobileDetailSheet.vue @@ -68,7 +68,7 @@ - + @@ -107,6 +107,7 @@ import { status } from '@/pages/menuPages/withdraw/information/hooks/status' import { isMobile } from '@core/utils/is' import { toJalali } from '@core/utils/useDataTime' import { usePrice } from '@core/utils/usePrice' +import { metalLabel } from '@/utils/adminMetalLabels' import { toNumber } from 'lodash' import { useRouter } from 'vue-router' diff --git a/src/pages/menuPages/withdraw/components/table.vue b/src/pages/menuPages/withdraw/components/table.vue index 078ee67..7d80d50 100644 --- a/src/pages/menuPages/withdraw/components/table.vue +++ b/src/pages/menuPages/withdraw/components/table.vue @@ -89,7 +89,7 @@ import type { TableInput } from '@core/type/baseTable' import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable' import Icon from '@core/components/BaseIcon/src/Icon.vue' import { changeCustomerActivation, getCustomerTable } from '@/api/customer' -import { cols } from '../hooks/withdrawColumns' +import { getWithdrawColumns } from '../hooks/withdrawColumns' import { useUserStore } from '@/store/userStore' import { usePrefix } from '@core/composable/usePrefix' import { message } from 'ant-design-vue' @@ -133,7 +133,7 @@ const confirmButtonLabel = computed(() => ) const tblProps = computed(() => ({ - columns: cols, + columns: getWithdrawColumns(), api: async (params: TableInput) => { const payload = { ...params, flow_type: props.flowType } lastTableParams.value = payload diff --git a/src/pages/menuPages/withdraw/hooks/withdrawColumns.tsx b/src/pages/menuPages/withdraw/hooks/withdrawColumns.tsx index 14bb7b5..cc1882a 100644 --- a/src/pages/menuPages/withdraw/hooks/withdrawColumns.tsx +++ b/src/pages/menuPages/withdraw/hooks/withdrawColumns.tsx @@ -3,6 +3,7 @@ import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime' import CustomTag from '@/components/CustomTag.vue' import { usePrice } from '@core/utils/usePrice' import WithdrawStatus from "@/pages/menuPages/withdraw/components/WithdrawStatus.vue"; +import { metalLabel } from '@/utils/adminMetalLabels' /*import BudgetSelect from '@/components/BudgetSelect.vue' import { useStaticApiListsStore } from '@/store/staticApisStore' import type { FilterSelect } from '@/model/filterSelect' @@ -22,7 +23,8 @@ const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => { } })*/ -export const cols: BaseTableColumn[] = [ +export function getWithdrawColumns(): BaseTableColumn[] { + return [ { title: 'ردیف', dataIndex: 'id', @@ -82,7 +84,7 @@ export const cols: BaseTableColumn[] = [ } }, { - title: 'طلا', + title: metalLabel.goldUnit(), dataIndex: 'gold', key: 'gold', type: 'Number', @@ -232,4 +234,5 @@ export const cols: BaseTableColumn[] = [ fixed: 'right', filterable: false }*/ -] + ] +} diff --git a/src/router/routes/accounting.ts b/src/router/routes/accounting.ts index 64f2954..f57335d 100644 --- a/src/router/routes/accounting.ts +++ b/src/router/routes/accounting.ts @@ -1,35 +1,13 @@ import type { RouteRecordRaw } from 'vue-router' -const Orders: RouteRecordRaw = { +/** ریدایرکت آدرس قدیمی — منو زیر پیکربندی است */ +const Accounting: RouteRecordRaw = { name: 'accounting', path: '/accounting', - component: () => import('@core/layouts/BasicLayout.vue'), redirect: { name: 'accountingMain' }, meta: { - icon: 'vuesax-linear:calculator', - permissionsKey: 'accounting', - title: 'حسابداری', - disabled: false, - hideMenu: false, - hideChildren: true, - order: 7 - }, - children: [ - { - name: 'accountingMain', - path: '', - component: () => import('@/pages/menuPages/accounting/index.vue'), - meta: { - permissionsKey: 'project', - icon: 'vuesax-linear:empty-wallet-time', - order: 3, - title: 'حسابداری', - disabled: false, - hideMenu: false - //hideChildren: false - } - } - ] + hideMenu: true + } } -export default Orders +export default Accounting diff --git a/src/router/routes/setting.ts b/src/router/routes/setting.ts index b0620d6..ab40460 100644 --- a/src/router/routes/setting.ts +++ b/src/router/routes/setting.ts @@ -136,6 +136,17 @@ const Setting: RouteRecordRaw = { title: 'مدیران', order: 9 } + }, + { + name: 'accountingMain', + path: 'accounting', + component: () => import('@/pages/menuPages/accounting/index.vue'), + meta: { + permissionsKey: 'accounting', + icon: 'vuesax-linear:calculator', + title: 'حسابداری', + order: 10 + } } ] } diff --git a/src/utils/adminMetalLabels.ts b/src/utils/adminMetalLabels.ts new file mode 100644 index 0000000..f2687e8 --- /dev/null +++ b/src/utils/adminMetalLabels.ts @@ -0,0 +1,131 @@ +import { m } from '@/utils/metalLabels' +import { metalDisplayUnit } from '@/utils/metalWeight' + +/** برچسب‌های پرتکرار پنل ادمین که «طلا» در متن دارند */ +export const metalLabel = { + goldBalance: () => m('تراز طلایی'), + goldCommission: () => m('کارمزد طلایی'), + goldWallet: () => m('کیف پول طلایی'), + goldWalletShort: () => m('طلایی'), + goldAmount: () => m('مقدار طلا'), + goldInventory: () => m('موجودی طلایی'), + goldGramPriceCustomer: () => m('قیمت هر گرم طلا برای مشتری (ریال)'), + goldGramPriceOriginal: () => m('قیمت هر گرم طلا بدون سود (ریال)'), + goldGramPriceOriginalSlash: () => m('/قیمت هر گرم طلا بدون سود (ریال)'), + customersGold: () => m('طلای مشتریان'), + goldValueInRial: () => m('ارزش طلا به ریال'), + goldWalletCharge: () => m('شارژ کیف پول طلایی'), + goldWalletBalance: () => m('موجودی طلایی (گرم)'), + goldWalletBalanceGram: () => m('موجودی کیف طلا (گرم)'), + goldInTransaction: () => m('مقدار طلا طی تراکنش (گرم)'), + goldWalletTitle: () => m('کیف پول طلا'), + goldPlaceholder: () => m('مقدار طلا'), + goldUnit: () => m('طلا'), + goldChargeShort: () => m('شارژ طلایی'), + manageGoldCash: () => m('مدیریت موجودی ریالی و طلایی'), + goldGifts: () => m('هدایای طلا'), + goldTradeBuySell: (direction: 1 | 2 | number) => + m(`قیمت ${direction === 1 ? 'خرید' : 'فروش'} طلا:`), + totalGoldWallet: () => m('مجموع موجودی کیف طلا'), + goldWalletTab: () => m('کیف طلا'), +} + +export function walletMetalBalanceTitle(metalId: number, label?: string) { + const unit = metalDisplayUnit(metalId) + switch (metalId) { + case 1: + return m(`موجودی کیف طلا (${unit})`) + case 2: + return m(`موجودی کیف نقره (${unit})`) + case 3: + return m(`موجودی کیف مس (${unit})`) + default: + return label ? `موجودی کیف ${label} (${unit})` : metalLabel.goldWalletBalanceGram() + } +} + +export function customerWalletMetalBalanceTitle(metalId: number, label?: string) { + const unit = metalDisplayUnit(metalId) + switch (metalId) { + case 1: + return m(`موجودی طلایی (${unit})`) + case 2: + return m(`موجودی نقره (${unit})`) + case 3: + return m(`موجودی مس (${unit})`) + default: + return label ? `موجودی ${label} (${unit})` : metalLabel.goldWalletBalance() + } +} + +export function totalMetalWalletTitle(metalId: number, label?: string) { + const unit = metalDisplayUnit(metalId) + switch (metalId) { + case 1: + return m(`مجموع موجودی کیف طلا`) + case 2: + return m(`مجموع موجودی کیف نقره`) + case 3: + return m(`مجموع موجودی کیف مس`) + default: + return label ? `مجموع موجودی کیف ${label}` : metalLabel.totalGoldWallet() + } +} + +export function metalBalanceTitle(metalId: number, label?: string) { + switch (metalId) { + case 1: + return metalLabel.goldBalance() + case 2: + return m('تراز نقره') + case 3: + return m('تراز مس') + default: + return label ? `تراز ${label}` : metalLabel.goldBalance() + } +} + +export function metalBalanceTitleWithUnit(metalId: number, label?: string) { + const unit = metalDisplayUnit(metalId) + return `${metalBalanceTitle(metalId, label)} (${unit})` +} + +export function transactionMetalAmountTitle(metalId: number, label?: string) { + const unit = metalDisplayUnit(metalId) + switch (metalId) { + case 1: + return m(`مقدار طلا طی تراکنش (${unit})`) + case 2: + return m(`مقدار نقره طی تراکنش (${unit})`) + case 3: + return m(`مقدار مس طی تراکنش (${unit})`) + default: + return label ? `مقدار ${label} طی تراکنش (${unit})` : metalLabel.goldInTransaction() + } +} + +export function dealPriceAtTransactionTitle(metalId: number, label?: string) { + switch (metalId) { + case 1: + return m('قیمت زمان معامله طلا (ریال)') + case 2: + return m('قیمت زمان معامله نقره (ریال)') + case 3: + return m('قیمت زمان معامله مس (ریال)') + default: + return label ? `قیمت زمان معامله ${label} (ریال)` : 'قیمت زمان معامله (ریال)' + } +} + +export function metalCommissionTitle(metalId: number, label?: string) { + switch (metalId) { + case 1: + return metalLabel.goldCommission() + case 2: + return m('کارمزد نقره') + case 3: + return m('کارمزد مس') + default: + return label ? `کارمزد ${label}` : metalLabel.goldCommission() + } +} diff --git a/src/utils/categorySelect.ts b/src/utils/categorySelect.ts index ae0892d..01e13b6 100644 --- a/src/utils/categorySelect.ts +++ b/src/utils/categorySelect.ts @@ -17,7 +17,7 @@ export function mapCategoriesToSelectItems( })) } -/** همان منبع جدول دسته‌بندی‌ها — قبل از خواندن، همگام‌سازی می‌کند */ +/** همان منبع جدول دسته‌بندی‌ها — دسته‌های جدید از محصولات اضافه می‌شوند؛ ترتیب دستی حفظ می‌شود */ export async function syncAndLoadCategories(options?: { selectOnly?: boolean metalHint?: string diff --git a/src/utils/config.ts b/src/utils/config.ts index d9bc476..2d1c0e4 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,3 +1,5 @@ +import { setCachedMainMetal } from '@/utils/metalLabels' + export function getConfigApp(key: string) { const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user : {} @@ -29,11 +31,14 @@ async function bootstrapSettingsFromApi(): Promise { const list = Array.isArray(rows) ? rows : [] const isStoreRow = list.find((item) => item.key === 'is_store') const mainWalletRow = list.find((item) => item.key === 'main_wallet_products') + const mainMetalRow = list.find((item) => item.key === 'main_metal') storeModeFromSettings = isStoreRow ? isSwitchOn(isStoreRow.value) : false + setCachedMainMetal(mainMetalRow?.value) const { setCachedMainWalletProductIds } = await import('@/components/mobile/utils/wallet') setCachedMainWalletProductIds(parseMainWalletProductIds(mainWalletRow?.value)) } catch { storeModeFromSettings = false + setCachedMainMetal(null) const { setCachedMainWalletProductIds } = await import('@/components/mobile/utils/wallet') setCachedMainWalletProductIds([]) } finally { diff --git a/src/utils/metalLabels.ts b/src/utils/metalLabels.ts new file mode 100644 index 0000000..d39e36b --- /dev/null +++ b/src/utils/metalLabels.ts @@ -0,0 +1,36 @@ +import { ref } from 'vue' + +export const DEFAULT_MAIN_METAL = 'طلا' + +const mainMetalRef = ref(DEFAULT_MAIN_METAL) + +export function resolveMainMetal(value?: string | number | null): string { + const normalized = value?.toString().trim() + return normalized || DEFAULT_MAIN_METAL +} + +export function setCachedMainMetal(value?: string | number | null): void { + mainMetalRef.value = resolveMainMetal(value) +} + +export function getMainMetal(): string { + return mainMetalRef.value +} + +export function useMainMetalRef() { + return mainMetalRef +} + +/** جایگزینی طلایی/طلای/طلا با نام فلز — بدون صفت مسی/طلایی */ +export function applyMainMetalText(text: string, mainMetal = getMainMetal()): string { + if (!text || mainMetal === DEFAULT_MAIN_METAL) return text + + return text + .replace(/طلایی/g, mainMetal) + .replace(/طلای/g, mainMetal) + .replace(/طلا/g, mainMetal) +} + +export function m(text: string): string { + return applyMainMetalText(text) +} diff --git a/src/utils/metalWeight.ts b/src/utils/metalWeight.ts new file mode 100644 index 0000000..1490faf --- /dev/null +++ b/src/utils/metalWeight.ts @@ -0,0 +1,68 @@ +import { TRADE_WEIGHT_UNIT_GRAM, TRADE_WEIGHT_UNIT_KILOGRAM } from '@/utils/weightUnit' + +export const METAL_ID_GOLD = 1 +export const METAL_ID_SILVER = 2 +export const METAL_ID_COPPER = 3 + +/** واحد نمایش موجودی/تراز هر فلز — مس کیلوگرم، بقیه گرم */ +export function metalDisplayUnit(metalId: number): string { + return metalId === METAL_ID_COPPER ? TRADE_WEIGHT_UNIT_KILOGRAM : TRADE_WEIGHT_UNIT_GRAM +} + +export function isMetalKilogram(metalId: number): boolean { + return metalId === METAL_ID_COPPER +} + +/** تبدیل مقدار ذخیره‌شده به گرم (برای محاسبات ریالی) */ +export function gramsToMetalDisplay(grams: number, metalId: number): number { + return isMetalKilogram(metalId) ? grams / 1000 : grams +} + +/** مقدار نمایشی از گرم */ +export function formatMetalWeightFromGrams( + grams: number, + metalId: number, + decimals = 3 +): string { + const value = gramsToMetalDisplay(grams, metalId) + return formatMetalDisplayValue(value, metalId, decimals) +} + +/** مقدار از قبل در واحد نمایشی فلز */ +export function formatMetalDisplayValue( + value: number, + metalId: number, + decimals = 3 +): string { + const unit = metalDisplayUnit(metalId) + return `${Number(Number(value).toFixed(decimals))} ${unit}` +} + +export function formatMetalDisplayValuePlain( + value: number, + metalId: number, + decimals = 3 +): string { + return Number(Number(value).toFixed(decimals)).toLocaleString('fa-IR', { + maximumFractionDigits: decimals + }) +} + +export function metalCellValue( + record: Record, + metalId: number, + prefix = 'metal_gold' +): number { + const flatKey = `${prefix}_${metalId}` + if (record[flatKey] != null) { + return Number(record[flatKey]) + } + + const mapKey = prefix === 'wallet_metal_gold' ? 'wallet_metal_gold' : 'metal_gold' + const map = record[mapKey] as Record | undefined + if (!map) { + return 0 + } + + return Number(map[String(metalId)] ?? 0) +} diff --git a/src/utils/weightUnit.ts b/src/utils/weightUnit.ts index 6c42f0f..b740d8a 100644 --- a/src/utils/weightUnit.ts +++ b/src/utils/weightUnit.ts @@ -13,3 +13,17 @@ export function resolveTradeWeightUnit(settingValue?: string | number | null) { export function weightFieldLabelForUnit(unit?: string | null) { return isKilogramUnit(unit) ? 'وزن به کیلوگرم' : 'وزن به گرم' } + +export function formatAmountWithUnit( + value: number | null | undefined, + unit?: string | null, + decimals = 3 +): string { + if (value == null || Number.isNaN(Number(value))) { + return '-' + } + + const resolvedUnit = (unit ?? TRADE_WEIGHT_UNIT_GRAM).trim() || TRADE_WEIGHT_UNIT_GRAM + + return `${Number(Number(value).toFixed(decimals))} ${resolvedUnit}` +}