feat(shayegan): product item field and customer register in admin

This commit is contained in:
2026-10-28 16:44:24 +03:30
parent 88271dba2a
commit dfc6f8dc64
8 changed files with 127 additions and 5 deletions
+1
View File
@@ -92,6 +92,7 @@ declare module '@vue/runtime-core' {
PargarRetryModal: typeof import('./src/components/PargarRetryModal.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ShayeganCustomerRegisterModal: typeof import('./src/components/ShayeganCustomerRegisterModal.vue')['default']
SmsCreditWidget: typeof import('./src/components/SmsCreditWidget.vue')['default']
SmsNegativeBalanceHost: typeof import('./src/components/SmsNegativeBalanceHost.vue')['default']
SmsNegativeBalanceModal: typeof import('./src/components/SmsNegativeBalanceModal.vue')['default']
+5
View File
@@ -41,3 +41,8 @@ export const shayeganRetry = async (id: number | string, force = false) =>
`/admin/shayegan/${id}/retry`,
{ force }
)
export const registerCustomerInShayegan = async (customerId: number) =>
await useAxios.post<{ ok: boolean; message: string; moshtari_code?: string }>(
`/admin/customer/${customerId}/shayegan/register`
)
@@ -0,0 +1,70 @@
<template>
<BasicModal
v-model:visible="visible"
title="ثبت مشتری در شایگان"
ok-text="ثبت در شایگان"
:loading="loading"
width="32rem"
@ok="confirm"
@close="visible = false"
>
<div class="space-y-3 text-sm">
<div v-for="item in items" :key="item.label" class="flex justify-between gap-3 border-b border-gray-100 pb-2">
<span class="text-gray-500">{{ item.label }}</span>
<span class="text-left break-all">{{ item.value ?? '-' }}</span>
</div>
<p class="m-0 text-xs leading-5" style="color: var(--admin-text-secondary)">
با تأیید، حساب تفصیلی با معین تنظیم‌شده در «حسابداری شایگان» ساخته می‌شود و کد در پروفایل مشتری ذخیره می‌گردد.
</p>
</div>
</BasicModal>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import BasicModal from '@/components/BasicModal.vue'
import { message } from 'ant-design-vue/es'
import { registerCustomerInShayegan } from '@/api/shayegan'
const props = defineProps<{
modelValue: boolean
customerId?: number
customerName?: string
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'success'): void
}>()
const loading = ref(false)
const visible = computed({
get: () => props.modelValue,
set: (value: boolean) => emit('update:modelValue', value)
})
const items = computed(() => [
{ label: 'نام مشتری', value: props.customerName },
{ label: 'شناسه مشتری', value: props.customerId },
])
async function confirm() {
if (!props.customerId) return
loading.value = true
try {
const result = await registerCustomerInShayegan(props.customerId)
if (!result.ok) {
message.error(result.message || 'ثبت مشتری ناموفق بود')
return
}
message.success(result.message || 'مشتری در شایگان ثبت شد')
visible.value = false
emit('success')
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
</script>
@@ -85,6 +85,15 @@
<template #title><span>در زرین ثبت نشده — کلیک برای ثبت</span></template>
<Icon icon="vuesax-linear:profile-add" color="#0284C7" />
</a-tooltip>
<a-tooltip
v-if="shayeganEnabled && accountingProvider === 'shayegan' && !isValidAccountingCode(record.moshtari_code)"
placement="bottom"
class="cursor-pointer"
@click="openShayeganRegister(record)"
>
<template #title><span>در شایگان ثبت نشده — کلیک برای ثبت</span></template>
<Icon icon="vuesax-linear:profile-add" color="#0D9488" />
</a-tooltip>
<a-tooltip
v-if="can('pargar') && pargarEnabled && !isValidAccountingCode(record.pargar_account_id)"
placement="bottom"
@@ -237,6 +246,13 @@
:customer-name="pargarCustomer?.name"
@success="fetch"
/>
<ShayeganCustomerRegisterModal
v-if="shayeganEnabled && accountingProvider === 'shayegan'"
v-model="shayeganRegisterVisible"
:customer-id="shayeganCustomer?.id"
:customer-name="shayeganCustomer?.name"
@success="fetch"
/>
</BasicShowContext>
</div>
</template>
@@ -266,10 +282,12 @@ import { isMobile } from '@core/utils/is'
import TahesabRetryModal from '@/components/TahesabRetryModal.vue'
import ZarinRetryModal from '@/components/ZarinRetryModal.vue'
import PargarRetryModal from '@/components/PargarRetryModal.vue'
import ShayeganCustomerRegisterModal from '@/components/ShayeganCustomerRegisterModal.vue'
import {
tahesabEnabledSetting,
zarinEnabledSetting,
pargarEnabledSetting,
shayeganEnabledSetting,
accountingProviderSetting,
customerDirectSmsEnabledSetting,
customerLevelProfitEnabledSetting,
@@ -297,6 +315,10 @@ const zarinEnabled = zarinEnabledSetting
const pargarRegisterVisible = ref(false)
const pargarCustomer = ref<Customer>()
const pargarEnabled = pargarEnabledSetting
const shayeganRegisterVisible = ref(false)
const shayeganCustomer = ref<Customer>()
const shayeganEnabled = shayeganEnabledSetting
const accountingProvider = accountingProviderSetting
const customerDirectSmsEnabled = customerDirectSmsEnabledSetting
const customerLevelProfitEnabled = customerLevelProfitEnabledSetting
const levelProfitVisible = ref(false)
@@ -305,13 +327,13 @@ const accountingCodeVisible = ref(false)
const accountingCodeCustomer = ref<Customer | null>(null)
const accountingEnabled = computed(() => {
const provider = accountingProviderSetting.value
return provider === 'pargar' || provider === 'tahesab' || provider === 'zarin'
return provider === 'pargar' || provider === 'tahesab' || provider === 'zarin' || provider === 'shayegan'
})
function accountingCodeOf(record: Customer) {
const provider = accountingProviderSetting.value
if (provider === 'pargar') return displayAccountingCode(record.pargar_account_id)
if (provider === 'tahesab' || provider === 'zarin') return displayAccountingCode(record.moshtari_code)
if (provider === 'tahesab' || provider === 'zarin' || provider === 'shayegan') return displayAccountingCode(record.moshtari_code)
return '-'
}
@@ -404,6 +426,11 @@ function openPargarRegister(user: Customer) {
pargarRegisterVisible.value = true
}
function openShayeganRegister(user: Customer) {
shayeganCustomer.value = user
shayeganRegisterVisible.value = true
}
function handleArchive(customer: Customer) {
Modal.confirm({
title: 'آیا مشتری را بایگانی می کنید؟',
@@ -14,7 +14,7 @@ export type CustomerColumnOptions = {
}
function accountingCodeColumn(provider: AccountingProvider | undefined): BaseTableColumn | null {
if (provider === 'pargar' || provider === 'tahesab' || provider === 'zarin') {
if (provider === 'pargar' || provider === 'tahesab' || provider === 'zarin' || provider === 'shayegan') {
const field = provider === 'pargar' ? 'pargar_account_id' : 'moshtari_code'
return {
title: 'کد حسابداری',
+2 -2
View File
@@ -775,7 +775,7 @@ const userInfo = ref<Customer>()
const accountingCodeVisible = computed(() => {
const provider = accountingProviderSetting.value
return provider === 'pargar' || provider === 'tahesab' || provider === 'zarin'
return provider === 'pargar' || provider === 'tahesab' || provider === 'zarin' || provider === 'shayegan'
})
const conditionalOrderEnabled = computed(() => conditionalOrderEnabledSetting.value)
@@ -797,7 +797,7 @@ function onConditionalOrderUpdated(payload: {
const accountingCodeValue = computed(() => {
const provider = accountingProviderSetting.value
if (provider === 'pargar') return displayAccountingCode(userInfo.value?.pargar_account_id)
if (provider === 'tahesab' || provider === 'zarin') return displayAccountingCode(userInfo.value?.moshtari_code)
if (provider === 'tahesab' || provider === 'zarin' || provider === 'shayegan') return displayAccountingCode(userInfo.value?.moshtari_code)
return '-'
})
@@ -481,6 +481,16 @@
</template>
<BasicInput v-model:value="form.inventory" type="number" />
</a-form-item>
<a-form-item
v-if="shayeganEnabled"
name="shayegan_item_number"
class="!mb-0 md:col-span-2"
>
<template #label>
<DashboardLabelWithHelp label="کد کالای شایگان (ItemNumber)" :help="shayeganItemHelp" size="sm" />
</template>
<BasicInput v-model:value="form.shayegan_item_number" placeholder="مثلاً 200000010" />
</a-form-item>
<div
v-if="weightOption === '1' && !isMeltedGroup && form.amount && form.amount < 1 && 0 < form.amount && tradeWeightUnit === 'گرم'"
class="flex items-end pb-1 text-sm text-gray-500"
@@ -690,6 +700,7 @@ import { inputRules, inputRulesNumber, requiredPriceRules } from '@/utils/inputR
import Icon from '@core/components/BaseIcon/src/Icon.vue'
import IndexImageUploader from '@/components/ProductUploader/IndexImageUploader.vue'
import { getConfiguration } from '@/api/configuration'
import { shayeganEnabledSetting } from '@/utils/config'
import { isSwitchOn } from '@/pages/menuPages/configuration/settingTypes'
import type { last } from '@/model/price'
import type { MethodePrices } from '@/model/product'
@@ -767,6 +778,11 @@ const inventoryHelp: DashboardHelp = {
description: 'تعداد موجودی فروشگاه (فقط حالت فروشگاه).',
}
const shayeganItemHelp: DashboardHelp = {
description:
'کد کالا (ItemNumber) در شایگان برای فاکتور همین محصول. خالی = fallback از تنظیم «کد کالای مس».',
}
const cashFeeBuyHelp: DashboardHelp = {
description: 'مبلغ اضافه روی قیمت پایه برای خرید — نقدی (ریال ثابت) یا درصدی از مظنه.',
formula: 'نقدی: +۱۰۰٬۰۰۰ ریال | درصدی: ۰.۵٪ یا ۵٪ از قیمت پایه',
@@ -868,6 +884,7 @@ const props = defineProps<{
}>()
const isStore = ref(Boolean(props.isStore))
const shayeganEnabled = shayeganEnabledSetting
const tradeWeightUnit = ref('گرم')
/** دسته آبشده/کاتد — ساختار فرم ثابت است */
@@ -456,6 +456,7 @@ function resetData() {
has_physical_delivery: false,
min_physical_delivery_quantity: undefined,
inventory: 0,
shayegan_item_number: '',
description: '',
karat: '',
min_order_cash: undefined,
@@ -490,6 +491,7 @@ const DETAILS_FIELDS = new Set([
'unit',
'direct_price',
'inventory',
'shayegan_item_number',
'trade_by_quantity',
'show_in_assets',
'show_on_tv',