fix ذشزنلقخعدی

This commit is contained in:
2026-06-23 16:02:41 +03:30
parent 29c58c4202
commit ac28eda09f
29 changed files with 624 additions and 167 deletions
+27
View File
@@ -7,6 +7,8 @@ import boursecuConvertIcon from '@/assets/img/boursecu-convert-icon.png'
import boursecuWalletIcon from '@/assets/img/boursecu-wallet-icon.png'
import boursecuBuySellIcon from '@/assets/img/boursecu-buy-sell-icon.png'
import boursecuAssetsIcon from '@/assets/img/boursecu-assets-icon.png'
import boursecuHomeIcon from '@/assets/img/boursecu-home-icon.png'
import boursecuSettingsIcon from '@/assets/img/boursecu-settings-icon.png'
import bazretalaLoginLogo from '@/assets/img/bazretala-login-logo.png'
import bazretalaNavLogo from '@/assets/img/bazretala-nav-logo.png'
import bazretalaFundBg from '@/assets/img/bazretala-fund-bg.png'
@@ -53,6 +55,14 @@ export function getBoursecuAssetsIcon() {
return boursecuAssetsIcon
}
export function getBoursecuHomeIcon() {
return boursecuHomeIcon
}
export function getBoursecuSettingsIcon() {
return boursecuSettingsIcon
}
export function getBoursecuActionIconSrc(actionId: string): string | undefined {
const icons: Record<string, string> = {
buySell: boursecuBuySellIcon,
@@ -61,6 +71,8 @@ export function getBoursecuActionIconSrc(actionId: string): string | undefined {
transactions: boursecuTransactionsIcon,
physicalReceive: boursecuPhysicalReceiveIcon,
convert: boursecuConvertIcon,
home: boursecuHomeIcon,
settings: boursecuSettingsIcon,
}
return icons[actionId]
@@ -71,12 +83,26 @@ export function hasCustomLoginLogoLayout() {
}
export const BOURSECU_BRAND_RED = '#bc3c3b'
export const BOURSECU_BRAND_BLUE = '#2178b5'
export const BOURSECU_BRAND_NAV = '#133050'
export const BOURSECU_COPPER_BG = '#b87333'
export const BOURSECU_COPPER_BG_LIGHT = '#fdf0e6'
export const BOURSECU_COPPER_BG_GRADIENT = 'linear-gradient(135deg, #c98a4a 0%, #a86528 100%)'
export function getProjectBrandRed() {
return isBoursecuProject() ? BOURSECU_BRAND_RED : null
}
/** رنگ اصلی دکمه‌ها و چارت — boursecu ثابت آبی، بقیه از تنظیمات tenant */
export function getProjectPrimaryColor() {
if (isBoursecuProject()) {
return BOURSECU_BRAND_BLUE
}
const color = getConfigApp('color')
return color != null ? String(color) : undefined
}
export function resolveProjectColor(color?: string | null) {
if (!color || !isBoursecuProject()) {
return color
@@ -240,6 +266,7 @@ export function applyProjectTheme() {
}
document.documentElement.classList.add('project-boursecu')
document.documentElement.style.setProperty('--App-primary-color', BOURSECU_BRAND_BLUE)
document.documentElement.style.setProperty('--App-brand-nav', BOURSECU_BRAND_NAV)
document.documentElement.style.setProperty('--App-brand-red', BOURSECU_BRAND_RED)
document.documentElement.style.setProperty('--App-brand-red-50', '#f8ecec')
-4
View File
@@ -394,10 +394,6 @@ export function formatMetalAssetsInfoText(metal: WalletMetalTotal): string {
return `${metal.label}: مجموع کیف محصولات (${metal.product_weight} ${unit}) + موجودی کیف اصلی (${metal.melted_weight} ${unit})`
}
function formatRialNumber(value: number): string {
return usePriceWithoutType(value)
}
/** متن توضیح مودال دارایی ریالی */
export function formatRialAssetsInfoText(cashWallet: number, metalRialValue: number): string {
const total = cashWallet + metalRialValue
+19 -2
View File
@@ -86,7 +86,24 @@ export function isMeltedProductUsingAppPrice(product?: {
group_type?: string | null
reading_method?: string | null
direct_price?: number | null
trade_by_quantity?: boolean | number | null
price?: number | null
} | null) {
return isMeltedTradeGroup(product?.group_type)
&& resolveMeltedPriceSource(product) === 'app'
if (!product || resolveMeltedPriceSource(product) !== 'app') {
return false
}
if (isMeltedTradeGroup(product.group_type)) {
return true
}
if (!usesQuantityTrade(product)) {
return false
}
if (Number(product.direct_price) === 1) {
return false
}
return Number(product.price ?? 0) <= 0
}
+49 -2
View File
@@ -42,6 +42,19 @@ export function isKatodMesGroup(groupType?: string | null) {
return normalizeProductGroupType(groupType) === BOURSECU_GROUP_KATOD
}
export function isShamshMesGroup(groupType?: string | null, title?: string | null) {
if (!isBoursecuProject()) {
return false
}
const group = normalizeProductGroupType(groupType)
if (group === 'شمش') {
return true
}
return (title ?? '').includes('شمش')
}
export function isGoldOnlyDealGroup(groupType?: string | null) {
return isMeltedTradeGroup(groupType)
}
@@ -51,6 +64,7 @@ export interface ProductUnitSource {
direct_price?: number | null
group_type?: string | null
amount?: number | null
trade_by_quantity?: boolean | number | null
}
export function isWeightBasedProductUnit(unit?: string | null) {
@@ -111,8 +125,17 @@ export function formatProductWeightAmount(amount?: number | null, product?: Prod
return `${value} ${weightUnit}`
}
/** فقط محصول تعدادی (مثل سکه): «هر عدد X گرم» — معامله مقداری با trade_by_quantity نیست */
export function shouldShowPerUnitWeightHint(product?: ProductUnitSource | null) {
if (!product || !hasProductUnitWeight(product) || usesQuantityTrade(product)) {
return false
}
return isCountBasedProduct(product)
}
export function getProductUnitWeightLabel(product?: ProductUnitSource | null) {
if (!hasProductUnitWeight(product)) {
if (!shouldShowPerUnitWeightHint(product)) {
return null
}
@@ -152,12 +175,16 @@ export function formatProductInventoryBalance(product?: (ProductUnitSource & { w
return `${quantityPart} · هر عدد ${unitWeight}`
}
/** محصول تعدادی: موجودی wallet.gold = تعداد قلم، نه وزن */
/** محصول تعدادی: موجودی wallet.gold = تعداد قلم، نه وزن — معامله مقداری با trade_by_quantity نیست */
export function isCountBasedProduct(product?: ProductUnitSource | null) {
if (!product) {
return false
}
if (usesQuantityTrade(product)) {
return false
}
if (isWeightBasedProductUnit(product.unit)) {
return false
}
@@ -567,3 +594,23 @@ export function getMetalTypeTradeLabel() {
export function getTradePriceUnitLabel() {
return useSettingsStore().metalTypeLabel
}
/** برچسب قیمت حین معامله — معامله مقداری: نام و واحد همان محصول */
export function getTransactionTradePriceLabel(product?: (ProductUnitSource & { title?: string | null }) | null) {
if (product && usesQuantityTrade(product)) {
const unit = getProductQuantityUnit(product)
const title = (product.title ?? '').trim()
return title ? `قیمت هر ${unit} ${title}` : `قیمت هر ${unit}`
}
return `قیمت هر ${getWeightUnit()} ${getTradePriceUnitLabel()}`
}
/** وزن کل زیر مقدار مبادله‌شده — فقط محصول تعدادی (نه trade_by_quantity) */
export function shouldShowTransactionTotalWeightLine(product?: ProductUnitSource | null) {
if (!product || usesQuantityTrade(product) || !hasProductUnitWeight(product)) {
return false
}
return isCountBasedProduct(product)
}
+69 -12
View File
@@ -1,3 +1,4 @@
import { ref } from 'vue'
import { useSettingsStore } from '@/store/settingsStore'
import { lastPrice } from '@/utils/price'
@@ -13,7 +14,13 @@ export interface EffectiveTradingHours {
enforced: boolean
}
let productTradingHours: { start: string; end: string } | null = null
const productTradingHours = ref<{ start: string; end: string } | null>(null)
const productTradingHoursVersion = ref(0)
function normalizeTradingTimeSetting(value: unknown, fallback: string): string {
const raw = String(value ?? '').trim()
return raw || fallback
}
function parseTimeToSeconds(time: string): number {
const parts = time.trim().split(':').map(Number)
@@ -22,20 +29,56 @@ function parseTimeToSeconds(time: string): number {
}
export function formatTradingTimeDisplay(time: string): string {
const parts = time.trim().split(':')
const normalized = normalizeTradingTimeSetting(time, '')
if (!normalized) {
return ''
}
const parts = normalized.split(':')
if (parts.length >= 3) {
return `${parts[0]}:${parts[1]}:${parts[2]}`
}
if (parts.length >= 2) {
return `${parts[0]}:${parts[1]}:00`
}
return time
return normalized
}
function hasProductTradingWindow(start?: string | null, end?: string | null): boolean {
const open = formatTradingTimeDisplay(normalizeTradingTimeSetting(start, '00:00:00'))
const close = formatTradingTimeDisplay(normalizeTradingTimeSetting(end, '00:00:00'))
if (!open || !close) {
return false
}
return open !== close
}
export function setProductTradingHours(start?: string | null, end?: string | null) {
if (start && end) {
productTradingHours = { start, end }
if (!hasProductTradingWindow(start, end)) {
productTradingHours.value = null
productTradingHoursVersion.value += 1
return
}
productTradingHours.value = {
start: normalizeTradingTimeSetting(start, '00:00'),
end: normalizeTradingTimeSetting(end, '23:59'),
}
productTradingHoursVersion.value += 1
}
export function syncProductTradingHoursFromProducts(
products: Array<{ auto_open_at?: string | null; auto_close_at?: string | null }>,
) {
for (const product of products) {
if (hasProductTradingWindow(product.auto_open_at, product.auto_close_at)) {
setProductTradingHours(product.auto_open_at, product.auto_close_at)
return
}
}
setProductTradingHours(null, null)
}
export function getStoreTradingHours(): StoreTradingHours {
@@ -47,13 +90,14 @@ export function getStoreTradingHours(): StoreTradingHours {
String(closeRaw ?? 'false').trim().toLowerCase() === 'true'
return {
start: String(settings.getSettingByKey('startDay') ?? '00:00'),
end: String(settings.getSettingByKey('endDay') ?? '23:59'),
start: normalizeTradingTimeSetting(settings.getSettingByKey('startDay'), '00:00'),
end: normalizeTradingTimeSetting(settings.getSettingByKey('endDay'), '23:59'),
closePriceEnabled,
}
}
export function getEffectiveTradingHours(): EffectiveTradingHours {
void productTradingHoursVersion.value
const settings = getStoreTradingHours()
if (settings.closePriceEnabled) {
return {
@@ -63,10 +107,10 @@ export function getEffectiveTradingHours(): EffectiveTradingHours {
}
}
if (productTradingHours?.start && productTradingHours?.end) {
if (productTradingHours.value?.start && productTradingHours.value?.end) {
return {
start: productTradingHours.start,
end: productTradingHours.end,
start: productTradingHours.value.start,
end: productTradingHours.value.end,
enforced: true,
}
}
@@ -92,10 +136,18 @@ export function isStoreClosedBySchedule(now = new Date()): boolean {
}
export function isStoreClosed(): boolean {
if (lastPrice.value?.closed === true) {
return true
void productTradingHoursVersion.value
const { closePriceEnabled } = getStoreTradingHours()
if (closePriceEnabled) {
if (lastPrice.value?.closed === true) {
return true
}
return isStoreClosedBySchedule()
}
// وقتی close_price خاموش است، صفر بودن قیمت مرجع به معنی بسته بودن فروشگاه نیست.
return isStoreClosedBySchedule()
}
@@ -103,5 +155,10 @@ export function getStoreClosedMessage(): string {
const { start, end } = getEffectiveTradingHours()
const open = formatTradingTimeDisplay(start)
const close = formatTradingTimeDisplay(end)
if (!open || !close) {
return 'فروشگاه در حال حاضر بسته است.'
}
return `فروشگاه در حال حاضر بسته است. ساعت کاری از ${open} تا ${close} می‌باشد.`
}