fix(admin): active-only colleague account code and karat placement

Show montzar colleague account field only for the connected accounting provider (also on customer sync pages), untruncate hesab-ketab menu labels, and move product karat under weight/unit.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-27 18:47:38 +03:30
co-authored by Cursor
parent 827adfe756
commit 00351673f8
6 changed files with 145 additions and 95 deletions
@@ -3,6 +3,8 @@
<PargarWalletBulkSync />
<ColleagueAccountingAccounts provider-only="pargar" />
<!-- ─── بنر آمار ─── -->
<div class="cs-banner">
<div class="cs-banner__title">همگام‌سازی مشتریان</div>
@@ -326,6 +328,7 @@ import {
} from '@/api/pargar'
import { useCustomerImportStore } from '@/store/customerImportStore'
import PargarWalletBulkSync from './PargarWalletBulkSync.vue'
import ColleagueAccountingAccounts from '@/pages/menuPages/colleagueOrders/components/ColleagueAccountingAccounts.vue'
const importStore = useCustomerImportStore()
@@ -1,6 +1,8 @@
<template>
<div class="cs-wrap">
<ColleagueAccountingAccounts provider-only="tahesab" />
<!-- ─── بنر آمار ─── -->
<div class="cs-banner">
<div class="cs-banner__title">همگام‌سازی مشتریان</div>
@@ -323,6 +325,7 @@ import {
type SyncPanelResult,
} from '@/api/tahesab'
import { useCustomerImportStore } from '@/store/customerImportStore'
import ColleagueAccountingAccounts from '@/pages/menuPages/colleagueOrders/components/ColleagueAccountingAccounts.vue'
const importStore = useCustomerImportStore()
@@ -1,6 +1,8 @@
<template>
<div class="cs-wrap">
<ColleagueAccountingAccounts provider-only="zarin" />
<!-- ─── بنر آمار ─── -->
<div class="cs-banner">
<div class="cs-banner__title">همگام‌سازی مشتریان</div>
@@ -349,6 +351,7 @@ import {
type SyncPanelResult,
} from '@/api/zarin'
import { useCustomerImportStore } from '@/store/customerImportStore'
import ColleagueAccountingAccounts from '@/pages/menuPages/colleagueOrders/components/ColleagueAccountingAccounts.vue'
const importStore = useCustomerImportStore()
@@ -1,5 +1,5 @@
<template>
<section class="colleague-accounts">
<section v-if="visible" class="colleague-accounts">
<header class="colleague-accounts__head">
<div>
<h3 class="colleague-accounts__title">کد حسابداری معامله با همکار</h3>
@@ -8,29 +8,25 @@
</p>
</div>
<BaseButton type="primary" :loading="saving" @click="save">
ذخیره کدها
ذخیره کد
</BaseButton>
</header>
<div class="colleague-accounts__grid">
<label
v-for="field in fields"
:key="field.key"
class="colleague-accounts__field"
:class="{ 'colleague-accounts__field--active': field.active }"
>
<span class="colleague-accounts__label">
{{ field.label }}
<span v-if="field.active" class="colleague-accounts__badge">فعال</span>
</span>
<a-input
v-model:value="values[field.key]"
:placeholder="field.placeholder"
allow-clear
class="colleague-accounts__input"
/>
</label>
</div>
<label
v-if="activeField"
class="colleague-accounts__field colleague-accounts__field--active"
>
<span class="colleague-accounts__label">
{{ activeField.label }}
<span class="colleague-accounts__badge">فعال</span>
</span>
<a-input
v-model:value="values[activeField.key]"
:placeholder="activeField.placeholder"
allow-clear
class="colleague-accounts__input"
/>
</label>
</section>
</template>
@@ -42,15 +38,56 @@ import type { Const } from '@/model/configuration'
import {
accountingProviderSetting,
initWalletSettingsFromApi,
priceProviderSetting,
type AccountingProvider,
} from '@/utils/config'
const props = withDefaults(
defineProps<{
/**
* اگر ست شود فقط وقتی provider فعال همین باشد نشان بده
* (مثلاً روی صفحهٔ همگام‌سازی ته‌حساب: provider-only="tahesab")
*/
providerOnly?: AccountingProvider | null
/** پیش‌فرض: فقط وقتی منبع قیمت Montzar است */
requireMontzar?: boolean
}>(),
{
providerOnly: null,
requireMontzar: true,
},
)
const KEYS = {
zarin: 'montzar_account_zarin',
tahesab: 'montzar_account_tahesab',
pargar: 'montzar_account_pargar',
} as const
const META: Record<
Exclude<AccountingProvider, 'none'>,
{ key: string; label: string; placeholder: string; titleFa: string }
> = {
zarin: {
key: KEYS.zarin,
label: 'کد حساب زرین',
placeholder: 'کد حساب همکار در زرین',
titleFa: 'کد حساب همکار در زرین',
},
tahesab: {
key: KEYS.tahesab,
label: 'کد حساب ته‌حساب',
placeholder: 'کد حساب همکار در ته‌حساب',
titleFa: 'کد حساب همکار در ته‌حساب',
},
pargar: {
key: KEYS.pargar,
label: 'کد حساب پرگار',
placeholder: 'کد حساب همکار در پرگار',
titleFa: 'کد حساب همکار در پرگار',
},
}
const values = reactive<Record<string, string>>({
[KEYS.zarin]: '',
[KEYS.tahesab]: '',
@@ -64,30 +101,26 @@ const rowIds = reactive<Record<string, number | null>>({
})
const saving = ref(false)
const loaded = ref(false)
const fields = computed(() => {
const provider = accountingProviderSetting.value as AccountingProvider
return [
{
key: KEYS.zarin,
label: 'کد حساب زرین',
placeholder: 'کد حساب همکار در زرین',
active: provider === 'zarin',
},
{
key: KEYS.tahesab,
label: 'کد حساب ته‌حساب',
placeholder: 'کد حساب همکار در ته‌حساب',
active: provider === 'tahesab',
},
{
key: KEYS.pargar,
label: 'کد حساب پرگار',
placeholder: 'کد حساب همکار در پرگار',
active: provider === 'pargar',
},
]
const activeProvider = computed(() => accountingProviderSetting.value as AccountingProvider)
const activeField = computed(() => {
const provider = activeProvider.value
if (provider === 'none') return null
return META[provider] ?? null
})
const visible = computed(() => {
if (props.requireMontzar && priceProviderSetting.value !== 'montzar') {
return false
}
if (!activeField.value) {
return false
}
if (props.providerOnly && activeProvider.value !== props.providerOnly) {
return false
}
return true
})
async function load() {
@@ -99,31 +132,29 @@ async function load() {
values[key] = row?.value != null ? String(row.value) : ''
rowIds[key] = row?.id != null ? Number(row.id) : null
}
loaded.value = true
}
async function save() {
const field = activeField.value
if (!field) {
message.warning('سیستم حسابداری فعالی انتخاب نشده است')
return
}
saving.value = true
try {
for (const key of Object.values(KEYS)) {
await setConfiguration({
id: rowIds[key] ?? undefined,
key,
value: (values[key] ?? '').trim(),
title_fa:
key === KEYS.zarin
? 'کد حساب همکار در زرین'
: key === KEYS.tahesab
? 'کد حساب همکار در ته‌حساب'
: 'کد حساب همکار در پرگار',
is_active: true,
can_edit: true,
type: 'text',
description:
'کد حساب طرف حساب همکار؛ سند خرید/فروش پوشش‌شده روی Montzar روی این حساب ثبت می‌شود.',
} satisfies Const)
}
message.success('کدهای حساب همکار ذخیره شد')
await setConfiguration({
id: rowIds[field.key] ?? undefined,
key: field.key,
value: (values[field.key] ?? '').trim(),
title_fa: field.titleFa,
is_active: true,
can_edit: true,
type: 'text',
description:
'کد حساب طرف حساب همکار؛ سند خرید/فروش پوشش‌شده روی Montzar روی این حساب ثبت می‌شود.',
} satisfies Const)
message.success('کد حساب همکار ذخیره شد')
await load()
} catch (error) {
console.error(error)
@@ -171,30 +202,17 @@ onMounted(() => {
color: var(--admin-text-secondary, rgba(255, 255, 255, 0.65));
}
.colleague-accounts__grid {
display: grid;
grid-template-columns: 1fr;
gap: 0.875rem;
@media (min-width: 900px) {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.colleague-accounts__field {
display: flex;
max-width: 28rem;
min-width: 0;
flex-direction: column;
gap: 0.4rem;
padding: 0.75rem;
border: 1px solid var(--admin-border, rgba(255, 255, 255, 0.1));
border: 1px solid color-mix(in srgb, #d4af37 55%, transparent);
border-radius: 12px;
background: color-mix(in srgb, var(--admin-bg-muted, #1a1a1f) 80%, transparent);
&--active {
border-color: color-mix(in srgb, #d4af37 55%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, #d4af37 18%, transparent);
}
box-shadow: inset 0 0 0 1px color-mix(in srgb, #d4af37 18%, transparent);
}
.colleague-accounts__label {
+28 -5
View File
@@ -1,12 +1,17 @@
<template>
<div>
<div class="hesab-ketab">
<BasicMainCard>
<div class="flex h-full min-h-0 flex-col md:flex-row">
<BasicMenu width="11rem" :menu="menu" mode="horizontal" class="overflow-auto md:hidden" />
<BasicMenu
width="13rem"
width="100%"
:menu="menu"
class="hidden min-w-[13.5rem] overflow-auto md:inline"
mode="horizontal"
class="hesab-ketab__menu hesab-ketab__menu--mobile overflow-auto md:hidden"
/>
<BasicMenu
width="17rem"
:menu="menu"
class="hesab-ketab__menu hesab-ketab__menu--side hidden min-w-[17rem] overflow-auto md:inline"
/>
<RouterView
:key="route.name"
@@ -50,4 +55,22 @@ const menu = computed(() => {
})
</script>
<style></style>
<style scoped lang="less">
.hesab-ketab__menu {
:deep(.ant-menu-item),
:deep(.ant-menu-submenu-title) {
height: auto !important;
min-height: 2.5rem;
line-height: 1.35 !important;
white-space: normal !important;
padding-block: 0.55rem !important;
}
:deep(.ant-menu-title-content) {
white-space: normal !important;
overflow: visible !important;
text-overflow: clip !important;
line-height: 1.35;
}
}
</style>
@@ -351,16 +351,6 @@
show-count
/>
</a-form-item>
<a-form-item name="karat" class="!mb-0 max-w-xs">
<template #label>
<DashboardLabelWithHelp label="عیار" :help="productKaratHelp" size="sm" />
</template>
<BasicInput
v-model:value="form.karat"
placeholder="مثلاً ۲۴ (اختیاری)"
:maxlength="32"
/>
</a-form-item>
</div>
<!-- وزن و واحد -->
@@ -419,6 +409,16 @@
</template>
<BasicSelect v-model:value="form.unit" :items="unitItems" valueName="value" keyName="value" />
</a-form-item>
<a-form-item name="karat" class="!mb-0">
<template #label>
<DashboardLabelWithHelp label="عیار" :help="productKaratHelp" size="sm" />
</template>
<BasicInput
v-model:value="form.karat"
placeholder="مثلاً ۲۴ (اختیاری)"
:maxlength="32"
/>
</a-form-item>
<a-form-item
v-if="showDirectPriceField"
name="direct_price"