From 0b2e4b05f9fd3ade3fbf2d193030a7d915b8b33e Mon Sep 17 00:00:00 2001 From: adonis Date: Tue, 18 Aug 2026 19:06:32 +0330 Subject: [PATCH] fix(settings): skip deploy_settings_const keys in import decision modal Catalog keys from the SQL template write onto this tenant automatically; only extra keys need a per-key decision. Co-authored-by: Cursor --- .../deploy-setting-catalog-keys.ts | 6 ++- .../program-settings.service.ts | 45 +++++++++++++------ 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/program-settings/deploy-setting-catalog-keys.ts b/src/program-settings/deploy-setting-catalog-keys.ts index ce91707..853a5fe 100644 --- a/src/program-settings/deploy-setting-catalog-keys.ts +++ b/src/program-settings/deploy-setting-catalog-keys.ts @@ -1,4 +1,8 @@ -/** کلیدهای deploy_settings_const — داخل باندل کامپایل می‌شود تا ایمپورت به فایل prisma وابسته نباشد. */ +/** + * کلیدهای INSERT در khaneghi_server/database/sql/deploy_settings_const.sql + * منبع قالب ایمپورت سوپرادمین — داخل باندل تا به فایل prisma وابسته نباشد. + * این ۱۹۵ کلید با SQL فعلی هم‌تراز است؛ کلید داخل این مجموعه در مودال «تصمیم برای هر کلید» نمی‌آید. + */ export const DEPLOY_SETTING_CATALOG_KEYS = new Set([ "accounting_provider", "admin_url", diff --git a/src/program-settings/program-settings.service.ts b/src/program-settings/program-settings.service.ts index 0635042..58a78f0 100644 --- a/src/program-settings/program-settings.service.ts +++ b/src/program-settings/program-settings.service.ts @@ -183,7 +183,7 @@ export class ProgramSettingsService implements OnModuleInit { }; } - /** پیش‌نمایش ایمپورت — برای هر کلید تصمیم‌گیری لازم است */ + /** پیش‌نمایش ایمپورت — تصمیم فقط برای کلید خارج از قالب deploy_settings_const */ async previewImport(tenantId: string, mobile: string, password: string) { const tenant = await this.requireKhanegiTenant(tenantId); const mobileNorm = this.normalizeAdminMobile(mobile); @@ -194,7 +194,8 @@ export class ProgramSettingsService implements OnModuleInit { await this.ensureTemplatesSeeded(); const remote = await this.fetchSystemSettings(apiBase, token); const local = await this.listTenantRows(tenantId); - const catalogKeys = await this.catalogKeySet(); + const catalogKeys = this.deployCatalogKeySet(); + const seedCanEdit = this.deployCatalogCanEditMap(); const localByKey = new Map(local.map((row) => [String(row.key), row])); const remoteByKey = new Map( remote @@ -212,6 +213,7 @@ export class ProgramSettingsService implements OnModuleInit { }> = []; for (const [key, row] of remoteByKey) { + if (catalogKeys.has(key)) continue; const localRow = localByKey.get(key); const khanegiValue = String(row.value ?? ''); const superadminValue = localRow ? String(localRow.value ?? '') : null; @@ -225,7 +227,13 @@ export class ProgramSettingsService implements OnModuleInit { }); } - const onlySuperAdmin = [...catalogKeys].filter((key) => !remoteByKey.has(key)); + const onlySuperAdmin = [...catalogKeys].filter((key) => { + if (isRuntimeProgramSettingKey(key)) return false; + if (remoteByKey.has(key)) return false; + const canEdit = seedCanEdit.get(key); + if (canEdit === 1) return false; + return true; + }); return { tenant: this.tenantMeta(tenant), @@ -234,7 +242,7 @@ export class ProgramSettingsService implements OnModuleInit { onlySuperAdminKeys: onlySuperAdmin, importableCount: remoteByKey.size, note: - 'مقدار کلیدهای can_edit=0 خانگی روی همین پروژه نوشته می‌شود. اگر تصمیم نفرستی، پیش‌فرض «فقط این پروژه» است.', + 'کلیدهای قالب deploy_settings_const بدون سؤال روی همین پروژه نوشته می‌شوند. فقط کلید خارج از قالب اینجا می‌آید.', }; } @@ -253,6 +261,7 @@ export class ProgramSettingsService implements OnModuleInit { const { apiBase, token } = await this.loginKhanegiAdmin(tenant, mobileNorm, password); await this.ensureTemplatesSeeded(); const remote = await this.fetchSystemSettings(apiBase, token); + const catalogKeys = this.deployCatalogKeySet(); const remoteByKey = new Map( remote .map((row) => this.normalizeIncoming(row)) @@ -283,7 +292,8 @@ export class ProgramSettingsService implements OnModuleInit { await this.ensureTemplateDefaultsForTenant(tx, tenantId); for (const [key, row] of remoteByKey) { - const scope = decisionMap.get(key) ?? 'this'; + const inCatalog = catalogKeys.has(key); + const scope = inCatalog ? 'this' : (decisionMap.get(key) ?? 'this'); if (scope === 'none') continue; const targetIds = scope === 'all' ? khanegiTenantIds : [tenantId]; @@ -295,7 +305,8 @@ export class ProgramSettingsService implements OnModuleInit { }); } - // کلید جدید از خانگی → وارد قالب هم بشود اگر نبود + if (inCatalog) continue; + const tpl = await tx.settingTemplate.findUnique({ where: { key } }); if (!tpl) { await tx.settingTemplate.create({ @@ -510,21 +521,29 @@ export class ProgramSettingsService implements OnModuleInit { } } - /** کلیدهای deploy const (داخل باندل) + فایل seed + جدول قالب. */ - private async catalogKeySet() { + /** + * قالب ایمپورت = کلیدهای INSERT در deploy_settings_const.sql + * (باندل + JSON هم‌تراز همان فایل). جدول setting_templates منبع قالب نیست. + */ + private deployCatalogKeySet() { const keys = new Set(DEPLOY_SETTING_CATALOG_KEYS); for (const row of this.readSeedRows()) { const key = String(row.key || '').trim(); if (key) keys.add(key); } - const rows = await this.prisma.settingTemplate.findMany({ - where: { deletedAt: null }, - select: { key: true }, - }); - for (const row of rows as Array<{ key: string }>) keys.add(row.key); return keys; } + private deployCatalogCanEditMap() { + const map = new Map(); + for (const row of this.readSeedRows()) { + const key = String(row.key || '').trim(); + if (!key) continue; + map.set(key, this.toSmallInt(row.can_edit, 0)); + } + return map; + } + private async seedEmptyKhanegiTenants() { const tenants = await this.prisma.tenant.findMany({ where: { product: ProductType.khanegi },