Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

## Навигация

- **Текущий месяц:** [Апрель 2026](#апрель-2026) (ниже)
- **Предыдущий месяц:** [Март 2026](#март-2026) (ниже)
- **Ещё раньше:** [Февраль 2026](#февраль-2026), [Январь 2026](#январь-2026) (ниже)
- **Текущий месяц:** [Май 2026](#май-2026) (ниже)
- **Предыдущий месяц:** [Апрель 2026](#апрель-2026) (ниже)
- **Ещё раньше:** [Март 2026](#март-2026), [Февраль 2026](#февраль-2026), [Январь 2026](#январь-2026) (ниже)
- **Архив по месяцам:**
- [Декабрь 2025](changelogs/2025-12.md)
- [Ноябрь 2025](changelogs/2025-11.md)
Expand All @@ -15,6 +15,19 @@

---

## Май 2026

### Разработка

#### ✨ Добавлено

**Редактор правил доставки — поля из расширенной конфигурации (#215):**
- `ValidationRulesEditor` подгружает поля `msOrder` и `msOrderAddress` из API «поля модели» и активные поля Object Extension для этих классов.
- В списке выбора поля для `validation_rules` доставки доступны кастомные колонки и подписи из админки; служебные поля (id, стоимости, delivery_id и т.д.) отфильтрованы.
- Те же исключения применяются и к ключам Object Extension, чтобы не предлагать служебные имена.

---

## Апрель 2026

### [2026-04-27] 🚀 Версия 1.10.1-beta1
Expand Down
137 changes: 134 additions & 3 deletions vueManager/src/components/ValidationRulesEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,42 @@ import InputText from 'primevue/inputtext'
import Select from 'primevue/select'
import Textarea from 'primevue/textarea'
import ToggleSwitch from 'primevue/toggleswitch'
import { computed, ref, watch } from 'vue'
import { computed, onMounted, ref, watch } from 'vue'

import request from '../request.js'

const { _ } = useLexicon()

/** Technical msOrder columns — not offered as checkout fields for delivery rules */
const ORDER_FIELD_BLOCKLIST = new Set([
'id',
'user_id',
'customer_id',
'token',
'uuid',
'createdon',
'updatedon',
'cost',
'cart_cost',
'delivery_cost',
'weight',
'status_id',
'delivery_id',
'payment_id',
'context',
'properties',
'num',
])

/** Technical msOrderAddress columns */
const ADDRESS_FIELD_BLOCKLIST = new Set(['id', 'order_id', 'createdon', 'updatedon', 'properties'])

const FIELD_GROUP_SORT = { order: 0, address: 1 }

function blocklistForGroup(group) {
return group === 'order' ? ORDER_FIELD_BLOCKLIST : ADDRESS_FIELD_BLOCKLIST
}

// Editor mode: visual or json
const isJsonMode = ref(false)
const jsonText = ref('')
Expand Down Expand Up @@ -49,6 +81,9 @@ const fieldDefinitions = [
{ name: 'text_address', group: 'address' },
]

/** Extra definitions from model fields + Object Extension (filled on mount) */
const extensionFieldDefinitions = ref([])

// Available validation rules from rakit/validation
const ruleDefinitions = [
// Simple rules (no parameters)
Expand Down Expand Up @@ -99,11 +134,107 @@ const ruleDefinitions = [
{ name: 'required_without_all', hasParam: true, paramType: 'text' },
]

const mergedFieldDefinitions = computed(() => [...fieldDefinitions, ...extensionFieldDefinitions.value])

async function loadExtensionFieldDefinitions() {
const staticNames = new Set(fieldDefinitions.map(f => f.name))
const collected = []

const pushModelRows = (rows, model, blocklist) => {
const group = model === 'msOrder' ? 'order' : 'address'
for (const row of rows || []) {
const name = row.name
if (!name || blocklist.has(name) || staticNames.has(name)) {
continue
}
collected.push({
name,
group,
labelHint: row.label_translated || row.label || name,
sortRank: [FIELD_GROUP_SORT[group], row.sort_order ?? 0, name],
})
}
}

try {
const [orderRes, addressRes] = await Promise.all([
request.get('/api/mgr/model-fields', { model: 'msOrder', limit: 500, start: 0 }),
request.get('/api/mgr/model-fields', { model: 'msOrderAddress', limit: 500, start: 0 }),
])
pushModelRows(orderRes.results, 'msOrder', ORDER_FIELD_BLOCKLIST)
pushModelRows(addressRes.results, 'msOrderAddress', ADDRESS_FIELD_BLOCKLIST)
} catch (e) {
console.warn('[ValidationRulesEditor] Failed to load model-fields:', e)
}

try {
const pushExtraRows = (fields, group) => {
const blocklist = blocklistForGroup(group)
for (const f of fields || []) {
if (!f.active) {
continue
}
const name = f.key
if (!name || staticNames.has(name) || blocklist.has(name)) {
continue
}
collected.push({
name,
group,
labelHint: f.label || name,
sortRank: [FIELD_GROUP_SORT[group], 1e6, name],
})
}
}
const [orderExtra, addressExtra] = await Promise.all([
request.get('/api/mgr/extra-fields', { class: 'msOrder' }),
request.get('/api/mgr/extra-fields', { class: 'msOrderAddress' }),
])
pushExtraRows(orderExtra.fields, 'order')
pushExtraRows(addressExtra.fields, 'address')
} catch (e) {
console.warn('[ValidationRulesEditor] Failed to load extra-fields:', e)
}

// validation_rules JSON keys are a single flat namespace (field name → rules)
const byName = new Map()
for (const c of collected) {
if (byName.has(c.name)) {
continue
}
byName.set(c.name, c)
}

const sorted = [...byName.values()].sort((a, b) => {
for (let i = 0; i < 3; i++) {
const av = a.sortRank[i]
const bv = b.sortRank[i]
if (av < bv) {
return -1
}
if (av > bv) {
return 1
}
}
return 0
})

extensionFieldDefinitions.value = sorted.map(c => ({
name: c.name,
group: c.group,
labelHint: c.labelHint,
}))
}

onMounted(() => {
void loadExtensionFieldDefinitions()
})

// Build available fields with localized labels (flat list)
const availableFields = computed(() => {
return fieldDefinitions.map(field => ({
return mergedFieldDefinitions.value.map(field => ({
...field,
label: _(`validation_field_${field.name}`),
label: field.labelHint || _(`validation_field_${field.name}`),
groupLabel: _(`validation_field_group_${field.group}`),
}))
})
Expand Down