mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-15 07:15:17 +03:00
Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3eaa7ffa3b | |||
| 98ebb478ee | |||
| fb67b1b80d | |||
| 79db5de6ee | |||
| 23d2cb42de | |||
| 511bfe7fdf | |||
| f91b569564 | |||
| e93185c18b | |||
| 7fa279c354 | |||
| 28a2a34c84 | |||
| f0f1e88aaa | |||
| e7cbf306a0 | |||
| 610720334b | |||
| 5090deb5bc | |||
| 8b0bcf0194 | |||
| 529a5cacae | |||
| 8b0f55f1cc | |||
| 54cbc69a59 | |||
| ac3047cf55 | |||
| 5da154adea | |||
| f4748d76af | |||
| b992cd11c5 | |||
| fc76407877 | |||
| a55195ab66 | |||
| 1eff62de42 | |||
| eb87be5c04 | |||
| 43ed9ba142 | |||
| 588382cec1 | |||
| e0197683e1 | |||
| 96dad75b2f | |||
| 5c734202b6 | |||
| a92bce05fb | |||
| b8ebbf6a6f | |||
| 43ba6ce16c | |||
| 51e37f9874 | |||
| 62e864e474 | |||
| ddebd7b6df | |||
| 3d16e35e80 | |||
| 56822c4533 | |||
| 32bf9c1a3f | |||
| c94f86a9cd | |||
| 8b5fed98e0 | |||
| 9e677cfc61 | |||
| 211a4726d6 | |||
| 91b39b758b | |||
| 4af69f1600 | |||
| 8c7c442705 | |||
| c541d51f33 | |||
| a650325baf | |||
| 6c19846051 | |||
| eef4f4bcea | |||
| 2c553fee98 | |||
| 95e4674825 | |||
| 9751840cc8 | |||
| f257bb428c | |||
| 71e3691cf1 | |||
| 1af318325d | |||
| 986480748e | |||
| 7561faf736 | |||
| 2849087fd9 | |||
| 105171cdc2 | |||
| c9cd0d62ab | |||
| c1a0a54971 | |||
| 927c0827f3 | |||
| 56cd18a6b4 | |||
| 3ac1d425cd | |||
| 3c596f4550 | |||
| 36d29a51a9 | |||
| 11e961c816 | |||
| 05ca810461 | |||
| a0407b74dc | |||
| 3aad254399 | |||
| 07753921be | |||
| ba0c71fa45 | |||
| 33c9fdade0 | |||
| 1c1fe7b2d9 | |||
| 0bd2587e74 | |||
| 6f17b1c2f6 | |||
| 36ad686316 | |||
| 66e37fb8cc | |||
| 99c77e1dde | |||
| 8d6612027f | |||
| c3b67a18cb | |||
| 4d1b66638c | |||
| 363a9f34f2 | |||
| 4f8ce7b747 | |||
| fc48d68ed1 | |||
| 3e34ec0bdd | |||
| fff2e83960 | |||
| 1ff209849f | |||
| 1a8415283e | |||
| db1e78a534 | |||
| a828f1847f | |||
| 3e8159b591 | |||
| 57bb964c1e | |||
| 41c4b6b1f7 | |||
| ffb0ee740d | |||
| f5e487096f | |||
| 6d5c1a0b19 | |||
| b322c02fdb | |||
| 61ef7ac891 | |||
| ba19b4111d | |||
| ded11437c5 | |||
| 5eda7f8eb3 | |||
| f39a0d9c5e | |||
| 2347859bdd |
@@ -1,4 +1,4 @@
|
||||
# form-compile v1.20 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.21 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[string]$JsonPath,
|
||||
@@ -1912,6 +1912,7 @@ function Emit-Element {
|
||||
# input-specific
|
||||
"multiLine"=1;"passwordMode"=1;"choiceButton"=1;"clearButton"=1
|
||||
"spinButton"=1;"dropListButton"=1;"markIncomplete"=1;"skipOnInput"=1;"inputHint"=1
|
||||
"textEdit"=1
|
||||
# label/hyperlink
|
||||
"hyperlink"=1
|
||||
# group-specific
|
||||
@@ -2137,6 +2138,7 @@ function Emit-Input {
|
||||
if ($el.spinButton -eq $true) { X "$inner<SpinButton>true</SpinButton>" }
|
||||
if ($el.dropListButton -eq $true) { X "$inner<DropListButton>true</DropListButton>" }
|
||||
if ($el.markIncomplete -eq $true) { X "$inner<AutoMarkIncomplete>true</AutoMarkIncomplete>" }
|
||||
if ($el.textEdit -eq $false) { X "$inner<TextEdit>false</TextEdit>" }
|
||||
if ($el.skipOnInput -eq $true) { X "$inner<SkipOnInput>true</SkipOnInput>" }
|
||||
$hasAmw = $el.PSObject.Properties.Name -contains 'autoMaxWidth'
|
||||
if ($hasAmw) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# form-compile v1.20 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.21 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import copy
|
||||
@@ -1350,6 +1350,7 @@ KNOWN_KEYS = {
|
||||
"maxWidth", "maxHeight",
|
||||
"multiLine", "passwordMode", "choiceButton", "clearButton",
|
||||
"spinButton", "dropListButton", "markIncomplete", "skipOnInput", "inputHint",
|
||||
"textEdit",
|
||||
"hyperlink",
|
||||
"showTitle", "united", "collapsed",
|
||||
"children", "columns",
|
||||
@@ -1940,6 +1941,8 @@ def emit_input(lines, el, name, eid, indent):
|
||||
lines.append(f'{inner}<DropListButton>true</DropListButton>')
|
||||
if el.get('markIncomplete') is True:
|
||||
lines.append(f'{inner}<AutoMarkIncomplete>true</AutoMarkIncomplete>')
|
||||
if el.get('textEdit') is False:
|
||||
lines.append(f'{inner}<TextEdit>false</TextEdit>')
|
||||
if el.get('skipOnInput') is True:
|
||||
lines.append(f'{inner}<SkipOnInput>true</SkipOnInput>')
|
||||
if 'autoMaxWidth' in el:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# form-validate v1.4 — Validate 1C managed form
|
||||
# form-validate v1.6 — Validate 1C managed form
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -366,13 +366,51 @@ if (-not $stopped) {
|
||||
$dataPath = $dpNode.InnerText.Trim()
|
||||
if (-not $dataPath) { continue }
|
||||
|
||||
# Opaque platform-internal DataPath shapes — not validatable from Form.xml alone:
|
||||
# - bare numeric (e.g. "10", "1000003") — internal index
|
||||
# - "N/M:<uuid>" — metadata reference by UUID
|
||||
if ($dataPath -match '^\d+$' -or $dataPath -match '^\d+/\d+:[0-9a-fA-F-]+$') {
|
||||
continue
|
||||
}
|
||||
|
||||
$pathChecked++
|
||||
|
||||
# Extract root segment of path, strip array indices like [0]
|
||||
$cleanPath = $dataPath -replace '\[\d+\]', ''
|
||||
# Strip leading '~' (current row of DynamicList: ~Список.Поле)
|
||||
if ($cleanPath.StartsWith('~')) { $cleanPath = $cleanPath.Substring(1) }
|
||||
$segments = $cleanPath -split '\.'
|
||||
$rootAttr = $segments[0]
|
||||
|
||||
# Resolve Items.<TableName>.CurrentData.<Field>... — table element, not attribute
|
||||
if ($rootAttr -eq 'Items') {
|
||||
if ($segments.Count -lt 3 -or $segments[2] -ne 'CurrentData') {
|
||||
Report-Warn "[$tag] '$elName': DataPath='$dataPath' — unknown Items.* shape, expected Items.<Table>.CurrentData.*"
|
||||
continue
|
||||
}
|
||||
$tableName = $segments[1]
|
||||
$tableEl = $null
|
||||
foreach ($candidate in $allElements) {
|
||||
if ($candidate.Tag -eq 'Table' -and $candidate.Name -eq $tableName) {
|
||||
$tableEl = $candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $tableEl) {
|
||||
Report-Error "[$tag] '$elName': DataPath='$dataPath' — table element '$tableName' not found"
|
||||
$pathErrors++
|
||||
continue
|
||||
}
|
||||
$tableDpNode = $tableEl.Node.SelectSingleNode("f:DataPath", $nsMgr)
|
||||
if (-not $tableDpNode -or -not $tableDpNode.InnerText.Trim()) {
|
||||
# Table without DataPath — can't resolve further, accept silently
|
||||
continue
|
||||
}
|
||||
$tableDp = $tableDpNode.InnerText.Trim() -replace '\[\d+\]', ''
|
||||
if ($tableDp.StartsWith('~')) { $tableDp = $tableDp.Substring(1) }
|
||||
$rootAttr = ($tableDp -split '\.')[0]
|
||||
}
|
||||
|
||||
if (-not $attrMap.ContainsKey($rootAttr)) {
|
||||
Report-Error "[$tag] '$elName': DataPath='$dataPath' — attribute '$rootAttr' not found"
|
||||
$pathErrors++
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# form-validate v1.4 — Validate 1C managed form
|
||||
# form-validate v1.6 — Validate 1C managed form
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -376,12 +376,44 @@ def main():
|
||||
if not data_path:
|
||||
continue
|
||||
|
||||
# Opaque platform-internal DataPath shapes — not validatable from Form.xml alone:
|
||||
# - bare numeric (e.g. "10", "1000003") — internal index
|
||||
# - "N/M:<uuid>" — metadata reference by UUID
|
||||
if re.match(r'^\d+$', data_path) or re.match(r'^\d+/\d+:[0-9a-fA-F-]+$', data_path):
|
||||
continue
|
||||
|
||||
path_checked += 1
|
||||
|
||||
clean_path = re.sub(r'\[\d+\]', '', data_path)
|
||||
# Strip leading '~' (current row of DynamicList: ~\u0421\u043f\u0438\u0441\u043e\u043a.\u041f\u043e\u043b\u0435)
|
||||
if clean_path.startswith('~'):
|
||||
clean_path = clean_path[1:]
|
||||
segments = clean_path.split(".")
|
||||
root_attr = segments[0]
|
||||
|
||||
# Resolve Items.<TableName>.CurrentData.<Field>... \u2014 table element, not attribute
|
||||
if root_attr == 'Items':
|
||||
if len(segments) < 3 or segments[2] != 'CurrentData':
|
||||
report_warn(f"[{tag}] '{el_name}': DataPath='{data_path}' \u2014 unknown Items.* shape, expected Items.<Table>.CurrentData.*")
|
||||
continue
|
||||
table_name = segments[1]
|
||||
table_el = None
|
||||
for candidate in all_elements:
|
||||
if candidate["Tag"] == 'Table' and candidate["Name"] == table_name:
|
||||
table_el = candidate
|
||||
break
|
||||
if table_el is None:
|
||||
report_error(f"[{tag}] '{el_name}': DataPath='{data_path}' \u2014 table element '{table_name}' not found")
|
||||
path_errors += 1
|
||||
continue
|
||||
table_dp_node = table_el["Node"].find(f"{{{F_NS}}}DataPath")
|
||||
if table_dp_node is None or not (table_dp_node.text or "").strip():
|
||||
continue
|
||||
table_dp = re.sub(r'\[\d+\]', '', (table_dp_node.text or "").strip())
|
||||
if table_dp.startswith('~'):
|
||||
table_dp = table_dp[1:]
|
||||
root_attr = table_dp.split(".")[0]
|
||||
|
||||
if root_attr not in attr_map:
|
||||
report_error(f"[{tag}] '{el_name}': DataPath='{data_path}' \u2014 attribute '{root_attr}' not found")
|
||||
path_errors += 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# meta-compile v1.11 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.12 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -502,6 +502,7 @@ function Parse-AttributeShorthand {
|
||||
fillChecking = if ($val.fillChecking) { "$($val.fillChecking)" } else { "" }
|
||||
indexing = if ($val.indexing) { "$($val.indexing)" } else { "" }
|
||||
multiLine = if ($val.multiLine -eq $true) { $true } else { $false }
|
||||
choiceHistoryOnInput = if ($val.choiceHistoryOnInput) { "$($val.choiceHistoryOnInput)" } else { "" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,7 +823,8 @@ function Emit-Attribute {
|
||||
X "$indent`t`t<CreateOnInput>Auto</CreateOnInput>"
|
||||
X "$indent`t`t<ChoiceForm/>"
|
||||
X "$indent`t`t<LinkByType/>"
|
||||
X "$indent`t`t<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>"
|
||||
$chi = if ($parsed.choiceHistoryOnInput) { $parsed.choiceHistoryOnInput } else { "Auto" }
|
||||
X "$indent`t`t<ChoiceHistoryOnInput>$chi</ChoiceHistoryOnInput>"
|
||||
|
||||
# Use — only for catalog top-level attributes
|
||||
if ($context -eq "catalog") {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# meta-compile v1.11 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.12 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -465,6 +465,7 @@ def parse_attribute_shorthand(val):
|
||||
'fillChecking': str(val['fillChecking']) if val.get('fillChecking') else '',
|
||||
'indexing': str(val['indexing']) if val.get('indexing') else '',
|
||||
'multiLine': True if val.get('multiLine') is True else False,
|
||||
'choiceHistoryOnInput': str(val['choiceHistoryOnInput']) if val.get('choiceHistoryOnInput') else '',
|
||||
}
|
||||
|
||||
def parse_enum_value_shorthand(val):
|
||||
@@ -774,7 +775,8 @@ def emit_attribute(indent, parsed, context):
|
||||
X(f'{indent}\t\t<CreateOnInput>Auto</CreateOnInput>')
|
||||
X(f'{indent}\t\t<ChoiceForm/>')
|
||||
X(f'{indent}\t\t<LinkByType/>')
|
||||
X(f'{indent}\t\t<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||||
chi = parsed.get('choiceHistoryOnInput') or 'Auto'
|
||||
X(f'{indent}\t\t<ChoiceHistoryOnInput>{chi}</ChoiceHistoryOnInput>')
|
||||
if context == 'catalog':
|
||||
X(f'{indent}\t\t<Use>ForItem</Use>')
|
||||
if context not in ('processor', 'processor-tabular'):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# skd-compile v1.21 — Compile 1C DCS from JSON
|
||||
# skd-compile v1.23 — Compile 1C DCS from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[string]$DefinitionFile,
|
||||
@@ -55,7 +55,7 @@ function X {
|
||||
|
||||
function Esc-Xml {
|
||||
param([string]$s)
|
||||
return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"')
|
||||
return $s.Replace('&','&').Replace('<','<').Replace('>','>')
|
||||
}
|
||||
|
||||
function Resolve-QueryValue {
|
||||
@@ -1130,6 +1130,8 @@ function Emit-ParamValue {
|
||||
X "$indent<value xsi:type=`"xs:decimal`">$(Esc-Xml $valStr)</value>"
|
||||
} elseif ($type -match '^string') {
|
||||
X "$indent<value xsi:type=`"xs:string`">$(Esc-Xml $valStr)</value>"
|
||||
} elseif ($type -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.') {
|
||||
X "$indent<value xsi:type=`"dcscor:DesignTimeValue`">$(Esc-Xml $valStr)</value>"
|
||||
} else {
|
||||
# Guess from value
|
||||
if ($valStr -match '^\d{4}-\d{2}-\d{2}T') {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# skd-compile v1.21 — Compile 1C DCS from JSON
|
||||
# skd-compile v1.23 — Compile 1C DCS from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import json
|
||||
@@ -10,7 +10,7 @@ import uuid
|
||||
|
||||
|
||||
def esc_xml(s):
|
||||
return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
|
||||
return s.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||
|
||||
def fmt_dec(v):
|
||||
"""Format decimal: 30.0 → '30', 16.625 → '16.625' (match PS1 output)."""
|
||||
@@ -811,6 +811,8 @@ def emit_param_value(lines, type_str, val, indent):
|
||||
lines.append(f'{indent}<value xsi:type="xs:decimal">{esc_xml(val_str)}</value>')
|
||||
elif type_str and re.match(r'^string', type_str):
|
||||
lines.append(f'{indent}<value xsi:type="xs:string">{esc_xml(val_str)}</value>')
|
||||
elif type_str and re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.', type_str):
|
||||
lines.append(f'{indent}<value xsi:type="dcscor:DesignTimeValue">{esc_xml(val_str)}</value>')
|
||||
else:
|
||||
# Guess from value
|
||||
if re.match(r'^\d{4}-\d{2}-\d{2}T', val_str):
|
||||
|
||||
@@ -54,9 +54,12 @@ Shorthand: `"Имя [Заголовок]: тип @роль #ограничени
|
||||
|
||||
### add-total — добавить итог
|
||||
|
||||
Shorthand: `"<dataPath>: <выражение>"`. Если выражение — известная аггрегатная функция без скобок (`Сумма`, `Количество`, `Минимум`, `Максимум`, `Среднее`), оно автоматически оборачивается в `Func(dataPath)`. Если функция со скобками или произвольное выражение — используется как есть.
|
||||
|
||||
```
|
||||
"Цена: Среднее"
|
||||
"Стоимость: Сумма(Кол * Цена)"
|
||||
"Цена: Среднее" # → Среднее(Цена)
|
||||
"Стоимость: Сумма(Кол * Цена)" # → как есть
|
||||
"Проверка: Проверка" # identity: выражение = Проверка
|
||||
```
|
||||
|
||||
### add-calculated-field — добавить вычисляемое поле
|
||||
@@ -80,24 +83,46 @@ Shorthand: `"Имя [Заголовок]: тип = Выражение #noFilter
|
||||
"Организация: CatalogRef.Организации"
|
||||
```
|
||||
|
||||
Shorthand: `"Имя [Заголовок]: тип = значение @флаги"`. `[Заголовок]` опциональный — добавляет `<title>`.
|
||||
Shorthand: `"Имя [Заголовок]: тип = значение [availableValue=список] [@флаги]"`. `[Заголовок]` опциональный — добавляет `<title>`.
|
||||
|
||||
`@autoDates` генерирует пару скрытых параметров `ДатаНачала`/`ДатаОкончания` для StandardPeriod-параметра — для БСП-отчётов, чтобы получить пару полей «Начало/Конец» в панели быстрых настроек.
|
||||
Флаги:
|
||||
- `@autoDates` — генерирует пару скрытых параметров `ДатаНачала`/`ДатаОкончания` для StandardPeriod-параметра.
|
||||
- `@hidden` — скрывает параметр от пользовательских настроек (для параметров-констант, используемых в запросе).
|
||||
- `@always` — параметр всегда подставляется в запрос. Часто вместе с `@hidden`, но используется и отдельно (для видимых обязательных параметров типа отчётного периода).
|
||||
|
||||
```
|
||||
"ПС: CatalogRef.Контрагенты = Справочник.Контрагенты.ПустаяСсылка @hidden"
|
||||
"Период: StandardPeriod = LastMonth @always"
|
||||
"ПСчет: ChartOfAccountsRef.Хозрасчетный = ПланСчетов.Хозрасчетный.X @hidden @always"
|
||||
"Округление: EnumRef.Округления = Окр1 availableValue=Перечисление.Округления.Окр1: руб., Перечисление.Округления.Окр1000: тыс."
|
||||
```
|
||||
|
||||
`availableValue=` задаёт начальный список допустимых значений. Формат списка: `v1[: p1], v2[: p2], ...` — элементы через `,`, представление после `:`. Если в значении или представлении встречается `,` или `:` — оборачивай в одинарные кавычки `'...'`:
|
||||
|
||||
```
|
||||
"Округление: ... = Окр1 availableValue=Окр1_00: 'руб., коп.', Окр1: руб."
|
||||
```
|
||||
|
||||
### modify-parameter — изменить существующий параметр
|
||||
|
||||
Находит параметр по имени, добавляет/обновляет свойства.
|
||||
Shorthand: `"ИмяПараметра [Заголовок] [ключ=значение]... [@флаги]"`. Находит параметр по имени, обновляет указанные свойства.
|
||||
|
||||
```
|
||||
"ПорядокОкругления use=Always"
|
||||
"ПорядокОкругления [Округление сумм] denyIncompleteValues=true"
|
||||
"ПериодОтчета [Отчетный период]" # только title
|
||||
"ПорядокОкругления availableValue=Перечисление.Округления.Окр1 presentation=руб."
|
||||
"ПорядокОкругления availableValue=Перечисление.Округления.Окр1: руб., Перечисление.Округления.Окр1000: тыс."
|
||||
"СчетПС value=ПланСчетов.Хозрасчетный.КассаПредприятия"
|
||||
"Контрагент @hidden @always"
|
||||
```
|
||||
|
||||
`[Заголовок]` опциональный — устанавливает или заменяет `<title>`. Можно вызывать без других kv-пар, чтобы только обновить title.
|
||||
|
||||
`availableValue=` добавляет один элемент списка допустимых значений (можно несколько через `;;`). Тип значения определяется автоматически (DesignTimeValue для ссылок).
|
||||
`availableValue=` **заменяет весь список** допустимых значений (старые удаляются). Формат и кавычки — те же, что в `add-parameter`.
|
||||
|
||||
`value=` заменяет значение параметра (тип значения подбирается автоматически по объявленному типу параметра).
|
||||
|
||||
Флаги `@hidden` / `@always` — те же, что и в `add-parameter`. Идемпотентны.
|
||||
|
||||
### rename-parameter — переименовать параметр
|
||||
|
||||
@@ -231,13 +256,17 @@ Value — имена ресурсов (как в полях/вычисляемы
|
||||
|
||||
### patch-query — точечная замена в тексте запроса
|
||||
|
||||
Shorthand: `"старое => новое"`. Заменяет все вхождения подстроки. Поддерживает пакетный режим и `-DataSet`.
|
||||
Shorthand: `"старое => новое [@once]"`. По умолчанию заменяет все вхождения подстроки. Поддерживает пакетный режим и `-DataSet`.
|
||||
|
||||
```
|
||||
"СубконтоДт1) В => СубконтоКт1) В"
|
||||
"ЛЕВОЕ СОЕДИНЕНИЕ => ВНУТРЕННЕЕ СОЕДИНЕНИЕ"
|
||||
"КАК ВТ_СтароеИмя => КАК ВТ_НовоеИмя @once"
|
||||
```
|
||||
|
||||
`@once` — упасть с ошибкой, если в запросе не **ровно одно** вхождение. Защищает от случайных замен в комментариях и однотипных идентификаторах.
|
||||
|
||||
Многострочные подстроки поддерживаются — переводы строк в `старое`/`новое` сравниваются буквально (включая отступы).
|
||||
|
||||
### set-outputParameter — установить параметр вывода
|
||||
|
||||
```
|
||||
@@ -249,16 +278,27 @@ Shorthand: `"старое => новое"`. Заменяет все вхожде
|
||||
|
||||
### set-structure — установить структуру варианта
|
||||
|
||||
Shorthand: `"Поле1 > Поле2 > details"`. `details`/`детали` — детальные записи. Заменяет всю структуру. Не поддерживает пакетный режим.
|
||||
Shorthand: `"Поле1 > Поле2 > details"`. `>` — вложенный уровень группировки, `,` — несколько полей в одном уровне, `details` — детальные записи. **Заменяет всю структуру полностью** (включая Selection/order/filter/conditionalAppearance каждой группы). Для точечной модификации полей группировки с сохранением настроек — используй `modify-structure`. Не поддерживает пакетный режим.
|
||||
|
||||
```
|
||||
"Организация > Номенклатура > details"
|
||||
"Валюта, НаименованиеБанка, ИНН"
|
||||
"details"
|
||||
"СчетМеждународногоУчета @name=ДанныеОтчета"
|
||||
```
|
||||
|
||||
`@name=Имя` — присваивает имя группировке (`<dcsset:name>`). Используется для привязки шаблонов через `groupName`.
|
||||
|
||||
### modify-structure — изменить поля группировки существующей группы
|
||||
|
||||
Тот же shorthand что и `set-structure`. Находит группу по `@name=`, заменяет только `<groupItems>` (поля группировки). Selection/order/filter/conditionalAppearance/outputParameters группы сохраняются. Без `@name=` — ошибка.
|
||||
|
||||
```
|
||||
"Валюта @name=ДанныеОтчета"
|
||||
"Валюта, НаименованиеБанка @name=ДанныеОтчета"
|
||||
"details @name=ДанныеОтчета"
|
||||
```
|
||||
|
||||
### modify-field — изменить существующее поле
|
||||
|
||||
Тот же shorthand что и `add-field`. Находит по dataPath, объединяет свойства (непустые переопределяют), сохраняет позицию.
|
||||
@@ -267,6 +307,23 @@ Shorthand: `"Поле1 > Поле2 > details"`. `details`/`детали` — д
|
||||
"Цена [Цена USD]: decimal(10,4) @dimension"
|
||||
```
|
||||
|
||||
### set-field-role — установить роль поля
|
||||
|
||||
Shorthand: `"<dataPath> [@флаги] [kv=значение]"`. **Полностью заменяет** роль поля. Если в значении только dataPath без флагов/kv — удаляет роль.
|
||||
|
||||
```
|
||||
"Сумма" # снять роль полностью
|
||||
"СуммаОстаток @balance" # простая балансовая роль
|
||||
"СуммаНач @balance balanceGroupName=Сумма balanceType=OpeningBalance" # с уточнением
|
||||
"Контрагент @dimension parentDimension=Группа"
|
||||
"Период @period" # роль периода
|
||||
```
|
||||
|
||||
Флаги: `@balance`, `@dimension`, `@account`, `@period`, `@required`, `@autoOrder`, `@ignoreNullValues`.
|
||||
KV: `balanceGroupName`, `balanceType` (OpeningBalance/ClosingBalance), `parentDimension`, `accountTypeExpression`, `orderType` (Asc/Desc), `expression`, `periodNumber`, `periodType`.
|
||||
|
||||
Поддерживает пакетный режим (`;;`).
|
||||
|
||||
### modify-filter — изменить существующий фильтр
|
||||
|
||||
Тот же shorthand что и `add-filter`. Находит по полю, обновляет оператор/значение/флаги. См. правило для `<use>` ниже.
|
||||
@@ -294,6 +351,7 @@ Shorthand: `"Поле1 > Поле2 > details"`. `details`/`детали` — д
|
||||
| `clear-selection` | `*` | Очищает все элементы selection |
|
||||
| `clear-order` | `*` | Очищает все элементы order |
|
||||
| `clear-filter` | `*` | Очищает все элементы filter |
|
||||
| `clear-conditionalAppearance` | `*` | Очищает все правила условного оформления |
|
||||
|
||||
## Верификация
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# skd-info v1.3 — Analyze 1C DCS structure
|
||||
# skd-info v1.4 — Analyze 1C DCS structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
@@ -824,8 +824,14 @@ function Show-Fields {
|
||||
$roleParts = @()
|
||||
if ($role) {
|
||||
foreach ($child in $role.ChildNodes) {
|
||||
if ($child.NodeType -eq "Element" -and $child.InnerText -eq "true") {
|
||||
if ($child.NodeType -ne "Element") { continue }
|
||||
$txt = $child.InnerText.Trim()
|
||||
if ($txt -eq "true") {
|
||||
$roleParts += $child.LocalName
|
||||
} elseif ($txt -eq "false") {
|
||||
# skip default-false flags
|
||||
} else {
|
||||
$roleParts += "$($child.LocalName)=$txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# skd-info v1.3 — Analyze 1C DCS structure
|
||||
# skd-info v1.4 — Analyze 1C DCS structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -777,8 +777,15 @@ def main():
|
||||
role_parts = []
|
||||
if role is not None:
|
||||
for child in role:
|
||||
if isinstance(child.tag, str) and (child.text or "").strip() == "true":
|
||||
if not isinstance(child.tag, str):
|
||||
continue
|
||||
txt = (child.text or "").strip()
|
||||
if txt == "true":
|
||||
role_parts.append(localname(child))
|
||||
elif txt == "false":
|
||||
pass
|
||||
else:
|
||||
role_parts.append(f"{localname(child)}={txt}")
|
||||
info["role"] = ", ".join(role_parts)
|
||||
|
||||
# UseRestriction
|
||||
|
||||
@@ -529,3 +529,7 @@ On error (auto-screenshot taken):
|
||||
- **Cyrillic in bash** — use `cat <<'SCRIPT' | node $RUN exec -` to avoid escaping issues
|
||||
- **Non-breaking spaces** — 1C uses `\u00a0` instead of regular spaces. All matching is normalized internally
|
||||
- **Section panel display** — `navigateSection()` works with any panel position (side, top) but requires "Picture and text" or "Text" display mode. Icon-only mode is not supported — API cannot read section names from icons alone
|
||||
|
||||
## Regression suites
|
||||
|
||||
When the user asks to cover a 1C solution with automated regression — multi-file test suites with assertions, hooks, tags, retries, Allure/JUnit reports, multi-user process tests — switch to the `test` mode. See [regress.md](regress.md) for authoring discipline, recon flow (metadata + live walkthrough via `exec`), per-application folder layout, ready-to-paste templates, and failure triage. Default to ad-hoc `run`/`exec` for single-script automation — `test` is the specialised mode for project-wide coverage.
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
# Regression suite authoring
|
||||
|
||||
Use this when the user asks to cover a 1C solution with automated regression tests, build out a test suite, or run an existing suite and analyse failures. For ad-hoc single-script automation, stay with the `run`/`exec` modes from SKILL.md instead.
|
||||
|
||||
The runner is the same `run.mjs`. The mode is `test`:
|
||||
|
||||
```bash
|
||||
node $RUN test [url] <dir|file> [flags]
|
||||
```
|
||||
|
||||
Tests live next to the project they cover (not inside the skill). Convention: `tests/` at the project root, with `_hooks.mjs` and `webtest.config.mjs` at the suite root. Tests are ES modules with `*.test.mjs` suffix.
|
||||
|
||||
## When to choose `test` over `exec`
|
||||
|
||||
| Goal | Mode |
|
||||
|------|------|
|
||||
| Explore a form, prototype a single step, debug one selector | `exec` (interactive session) |
|
||||
| Reproduce a bug as a failing test before fixing it | `test` |
|
||||
| Cover a feature so future changes are checked automatically | `test` |
|
||||
| Run the project's regression on a new build | `test` |
|
||||
| Generate a screencast walkthrough | `exec` with `startRecording` |
|
||||
|
||||
Don't write a `.test.mjs` for a one-shot user request. Don't drive a regression suite through chained `exec` calls.
|
||||
|
||||
## Before writing tests — recon
|
||||
|
||||
Two layers, in order.
|
||||
|
||||
**1. Static recon — metadata.** Never invent identifiers. For every metadata object the user mentions, run the matching info skill first: `/meta-info` (attributes/tabular sections), `/form-info` (form layout), `/skd-info` (DCS), `/mxl-info` (templates), `/role-info` (rights), `/subsystem-info` (composition / command interface). If the user names objects you can't find — stop and ask.
|
||||
|
||||
**2. Live recon — interactive walkthrough.** For any non-trivial scenario, walk the path live in `exec` mode before transcribing it. Metadata tells you what exists; the live walkthrough tells you what actually happens. Capture from `getFormState()`: exact button names (`'Провести и закрыть'`, not `'Сохранить'`), table section names for multi-grid forms, required fields, places where a real async wait is needed. Then transcribe the working sequence into `*.test.mjs`, wrapping logical chunks in `step('...', async () => { ... })`.
|
||||
|
||||
The mechanics of `exec` / `getFormState` / `fillFields` / `clickElement` are in [SKILL.md](SKILL.md) — read it before recon if you haven't already.
|
||||
|
||||
When live recon is overkill: trivial reads (`navigateSection` + `readTable` + assert non-empty), or scenarios you've already proven once in this session. When it's essential: confirmation dialogs, posting/cancellation flows, reports with custom filters, multi-grid forms, user-customised forms.
|
||||
|
||||
## Suite layout
|
||||
|
||||
**Each application gets its own subfolder under `tests/`.** A single repo may host several independent suites side by side — they must not share `_hooks.mjs` or `webtest.config.mjs`, because each suite restores a different DB, publishes to a different URL, and ships its own test data.
|
||||
|
||||
```
|
||||
tests/
|
||||
<app-name>/ # application regression — one per solution
|
||||
_hooks.mjs
|
||||
webtest.config.mjs
|
||||
_allure/ # optional static Allure config
|
||||
01-login/
|
||||
02-counterparties/
|
||||
...
|
||||
<another-app>/ # second solution, fully isolated
|
||||
```
|
||||
|
||||
Inside the application subfolder, organize by **feature**, not by metadata kind. Numeric prefixes on both folder and file enforce run order — discovery walks recursively and sorts files by full relative path; entries starting with `_` or `.` are skipped (so `_hooks.mjs`, `_allure/` won't be picked up as tests).
|
||||
|
||||
```
|
||||
tests/<app-name>/
|
||||
01-login/
|
||||
01-open-base.test.mjs
|
||||
02-section-navigation.test.mjs
|
||||
02-counterparties/
|
||||
01-create.test.mjs
|
||||
02-edit-phone.test.mjs
|
||||
03-goods-receipt/
|
||||
01-fill.test.mjs
|
||||
02-post.test.mjs
|
||||
05-approval-process/
|
||||
01-end-to-end.test.mjs # multi-user
|
||||
```
|
||||
|
||||
Per-folder `_hooks.mjs` / `webtest.config.mjs` inside the application subfolder are NOT supported — only the application-root copies are loaded.
|
||||
|
||||
## Test file anatomy
|
||||
|
||||
```js
|
||||
export const name = 'Создание контрагента'; // required
|
||||
export const tags = ['catalog', 'create']; // optional, used for filtering + Allure
|
||||
export const timeout = 60000; // optional, default 30000
|
||||
// export const skip = 'pending fix #123'; // optional: true | string
|
||||
// export const only = true; // debug-only — never commit
|
||||
// export const context = 'manager'; // optional, single non-default context
|
||||
// export const contexts = ['clerk', 'manager']; // optional, multi-user test
|
||||
// export const severity = 'critical'; // optional, overrides config severity
|
||||
|
||||
export async function setup(ctx) {
|
||||
// per-test prep — runs before default. Skip if not needed.
|
||||
}
|
||||
|
||||
export async function teardown(ctx) {
|
||||
// per-test cleanup — runs after default, always (even on failure).
|
||||
}
|
||||
|
||||
export default async function(ctx) {
|
||||
const { navigateSection, openCommand, clickElement, fillFields,
|
||||
readTable, closeForm, getFormState,
|
||||
assert, step, log } = ctx;
|
||||
|
||||
await step('Открыть список контрагентов', async () => {
|
||||
await navigateSection('Продажи');
|
||||
await openCommand('Контрагенты');
|
||||
});
|
||||
|
||||
await step('Создать нового контрагента', async () => {
|
||||
await clickElement('Создать');
|
||||
await fillFields({ 'Наименование': 'Тест ' + Date.now() });
|
||||
await clickElement('Записать и закрыть');
|
||||
});
|
||||
|
||||
await step('Убедиться, что элемент появился в списке', async () => {
|
||||
const t = await readTable();
|
||||
assert.tableHasRow(t, r => r['Наименование']?.startsWith('Тест '));
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Step names — in Russian, descriptive.** Step labels surface in the console output, in JSON/JUnit, and as Allure step nodes. Russian-speaking QA reads them. Use a full action phrase (`'Создать нового контрагента'`), not a tag (`'create'`) and not a transliteration. Same applies to `export const name` and `displayName` in `webtest.config.mjs`.
|
||||
|
||||
## `ctx` contract
|
||||
|
||||
The runner injects every `browser.mjs` export into `ctx` (all 1C action functions auto-detect platform errors — see SKILL.md), plus the test utilities below.
|
||||
|
||||
### Test utilities
|
||||
|
||||
```js
|
||||
step(name, fn) // async wrapper. Records start/stop. Nested calls supported.
|
||||
// On throw: marks the step failed, re-throws.
|
||||
// On screenshot='every-step': captures after fn().
|
||||
log(...args) // adds a line to ctx.testInfo's output (goes into JSON / Allure
|
||||
// attachment). Use instead of console.log inside tests.
|
||||
assert.* // see "Assertions" below
|
||||
```
|
||||
|
||||
### `ctx.testInfo` (always set, read-only)
|
||||
|
||||
```js
|
||||
{
|
||||
name, // 'Навигация по разделам' (with params substituted)
|
||||
file, // '01-navigation.test.mjs' (basename)
|
||||
filePath, // relative path inside testDir
|
||||
tags, // ['nav', 'smoke']
|
||||
timeout, // ms
|
||||
attempt, // 1..maxAttempts (1-based)
|
||||
maxAttempts, // 1 + retry
|
||||
param, // { ... } | undefined (only when export const params is set)
|
||||
contexts: { // mirrors config.contexts; includes custom fields like displayName
|
||||
clerk: { url, isolation, displayName, ... },
|
||||
manager: { ... },
|
||||
},
|
||||
primaryContext, // 'clerk' — name of the context active at test entry
|
||||
// (= t.context for single, t.contexts[0] for multi)
|
||||
}
|
||||
```
|
||||
|
||||
### `ctx.testResult` (only in `afterEach`)
|
||||
|
||||
```js
|
||||
{
|
||||
status, // 'passed' | 'failed'
|
||||
duration, // ms
|
||||
attempts, // attempts actually executed
|
||||
error, // { message, step?, screenshot? } | null
|
||||
steps, // array of step results (each: { name, start, stop, status, error?, steps[] })
|
||||
}
|
||||
```
|
||||
|
||||
### Context shape
|
||||
|
||||
- **Single-context (default or `export const context = 'manager'`):** all API on `ctx` top-level — `ctx.clickElement(...)`, `ctx.getFormState()`, etc.
|
||||
- **Multi-context (`export const contexts = ['clerk', 'manager']`):** each name is its own scoped namespace — `ctx.clerk.clickElement(...)`, `ctx.manager.fillFields(...)`. `step`, `assert`, `log`, `testInfo` stay top-level. Scoped methods auto-switch the active page before each call.
|
||||
|
||||
## Assertions
|
||||
|
||||
All on `ctx.assert`. Throw `AssertionError` with `.message`, `.actual`, `.expected`. No dependencies.
|
||||
|
||||
```js
|
||||
// generic
|
||||
assert.ok(value, msg?) // truthy
|
||||
assert.equal(actual, expected, msg?) // ===
|
||||
assert.notEqual(actual, expected, msg?) // !==
|
||||
assert.deepEqual(actual, expected, msg?) // JSON-compare
|
||||
assert.includes(haystack, needle, msg?) // string.includes / array.includes
|
||||
assert.match(string, regex, msg?) // regex.test(string)
|
||||
await assert.throws(asyncFn, msg?) // passes if fn throws (use await)
|
||||
|
||||
// 1C-specific — operate on getFormState() / readTable() output
|
||||
assert.formHasField(state, 'Контрагент', msg?) // state.fields[name] exists
|
||||
assert.formTitle(state, expected, msg?) // state.title includes expected
|
||||
assert.tableHasRow(table, predicate, msg?) // predicate: object (partial match) or fn(row) => bool
|
||||
// object form: { 'Наименование': 'Тест' }
|
||||
// fn form: r => r['Сумма'] > 100
|
||||
assert.tableRowCount(table, expected, msg?) // table.rows.length === expected
|
||||
assert.noErrors(state, msg?) // !state.errors
|
||||
```
|
||||
|
||||
Beyond these, just use plain JS (`throw new Error(...)`) — there's no custom matcher extension API. The 1C-specific helpers are the ones worth preferring over hand-rolled equivalents because their error messages name the actual fields/rows present, which speeds up triage.
|
||||
|
||||
## webtest.config.mjs
|
||||
|
||||
```js
|
||||
export default {
|
||||
// Single-context shorthand:
|
||||
url: 'http://localhost:9191/myapp/ru_RU',
|
||||
|
||||
// OR multi-context:
|
||||
// contexts: {
|
||||
// clerk: { url: 'http://localhost:9191/myapp-clerk/ru_RU', displayName: 'Кладовщик' },
|
||||
// manager: { url: 'http://localhost:9191/myapp-manager/ru_RU', displayName: 'Менеджер' },
|
||||
// },
|
||||
// defaultContext: 'clerk',
|
||||
|
||||
timeout: 30000,
|
||||
retries: 0,
|
||||
screenshot: 'on-failure', // 'every-step' | 'off'
|
||||
record: false,
|
||||
|
||||
// Severity → tags mapping for Allure. Each tag at most one bucket.
|
||||
severity: {
|
||||
critical: ['smoke', 'crud'],
|
||||
minor: ['recording'],
|
||||
},
|
||||
defaultSeverity: 'normal',
|
||||
};
|
||||
```
|
||||
|
||||
CLI flags override config. Use latin context IDs + Russian `displayName` for ergonomics — `ctx.testInfo.contexts.clerk.displayName` is friendlier than mixed-case Cyrillic keys.
|
||||
|
||||
## _hooks.mjs
|
||||
|
||||
Two layers. Infra hooks run without a browser; testlevel hooks receive `ctx`.
|
||||
|
||||
```js
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
// Infra — runs once around the whole suite.
|
||||
export async function prepare({ hookArgs, log, config }) {
|
||||
// hookArgs: everything after `--` on the CLI, as a string[]. Parse yourself.
|
||||
const force = hookArgs.includes('--rebuild-stand');
|
||||
const dataArg = hookArgs.find(a => a.startsWith('--data='))?.slice('--data='.length);
|
||||
log('preparing stand, force=', force, 'data=', dataArg);
|
||||
// Idempotent hash-locks on inputs (config sources, EPF spec, DB dump) keep
|
||||
// warm starts to a liveness probe.
|
||||
}
|
||||
|
||||
export async function cleanup({ log, config }) { /* optional */ }
|
||||
|
||||
// Testlevel — runs with browser ctx.
|
||||
export async function beforeAll(ctx) { /* once after first context opens */ }
|
||||
export async function afterAll(ctx) { /* once before final teardown */ }
|
||||
export async function beforeEach(ctx) { /* ctx.testInfo is set */ }
|
||||
export async function afterEach(ctx) { /* ctx.testInfo + ctx.testResult set */ }
|
||||
|
||||
// Per-context — runs whenever a context is created/closed.
|
||||
export async function afterOpenContext(ctx, name, spec) { /* spec = config.contexts[name] */ }
|
||||
export async function beforeCloseContext(ctx, name, spec) { }
|
||||
```
|
||||
|
||||
Built-in state reset (`dismissPendingErrors` + close all forms) runs after `afterEach` automatically. Don't reimplement it in `afterEach`.
|
||||
|
||||
Pass hook args after `--`:
|
||||
|
||||
```bash
|
||||
node $RUN test tests/<app-name>/ --bail -- --rebuild-stand --data=demo
|
||||
└─runner─┘ └────── hookArgs ─────────┘
|
||||
```
|
||||
|
||||
**Where to put data setup:**
|
||||
- DB restore, publication, EPF build → `prepare()`. Make it idempotent (hash-locks).
|
||||
- Test-specific seed data → per-test `setup`.
|
||||
- Shared session-wide warmup → `beforeAll`.
|
||||
|
||||
## Ready-to-paste patterns
|
||||
|
||||
A minimal CRUD shape is in *Test file anatomy* above — use it as the rhythm for catalog/document tests, swapping in the right section/command/fields. The patterns below cover what's specific to the regression engine, not the browser API (those live in SKILL.md).
|
||||
|
||||
### DCS report
|
||||
|
||||
```js
|
||||
await openCommand('Остатки товаров');
|
||||
// Reset user settings — 1C persists them between sessions.
|
||||
await clickElement('Ещё');
|
||||
await clickElement('Установить стандартные настройки');
|
||||
|
||||
await selectValue('Номенклатура', 'Товар 02'); // auto-enables the filter checkbox
|
||||
await clickElement('Сформировать');
|
||||
await wait(3);
|
||||
const r = await readSpreadsheet();
|
||||
assert.deepEqual(r.headers, ['Номенклатура', 'Количество', 'Сумма']);
|
||||
assert.ok(r.data.length >= 1);
|
||||
assert.ok(r.totals?.['Сумма']);
|
||||
```
|
||||
|
||||
### Multi-user process
|
||||
|
||||
```js
|
||||
export const contexts = ['clerk', 'manager'];
|
||||
|
||||
export default async function({ clerk, manager, step, assert }) {
|
||||
await step('Кладовщик создаёт накладную', async () => {
|
||||
await clerk.navigateSection('Склад');
|
||||
await clerk.openCommand('Приходные накладные');
|
||||
await clerk.clickElement('Создать');
|
||||
await clerk.fillFields({ 'Контрагент': 'ООО Север' });
|
||||
await clerk.clickElement('Записать');
|
||||
});
|
||||
await step('Менеджер утверждает накладную', async () => {
|
||||
await manager.navigateSection('Согласование');
|
||||
await manager.openCommand('На утверждении');
|
||||
await manager.clickElement('ООО Север', { dblclick: true });
|
||||
await manager.clickElement('Утвердить');
|
||||
});
|
||||
await step('Кладовщик видит новый статус', async () => {
|
||||
const s = await clerk.getFormState();
|
||||
assert.equal(s.fields['Статус']?.value, 'Утверждён');
|
||||
});
|
||||
await step('Освободить сессию кладовщика', async () => {
|
||||
await manager.closeContext('clerk'); // free a 1C license for the next test
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Close contexts you no longer need (`manager.closeContext('clerk')`) before the next multi-user test starts — frees a 1C web-client license and stops the previous role from holding state.
|
||||
|
||||
### Failing-test repro
|
||||
|
||||
```js
|
||||
export const name = 'Bug #123: накладная без контрагента не должна проводиться';
|
||||
export const tags = ['bug', 'validation'];
|
||||
|
||||
export default async function({ openCommand, clickElement, getFormState, assert, step }) {
|
||||
await openCommand('Приходные накладные');
|
||||
await clickElement('Создать');
|
||||
await clickElement('Провести');
|
||||
const s = await getFormState();
|
||||
assert.ok(s.errorModal || s.fields['Контрагент']?.required,
|
||||
'Должна быть ошибка валидации или поле помечено обязательным');
|
||||
}
|
||||
```
|
||||
|
||||
Write it red first, hand it to the user, fix the underlying issue, re-run green.
|
||||
|
||||
### Parameterised test
|
||||
|
||||
```js
|
||||
export const name = 'Заполнение поля {type}';
|
||||
export const params = [
|
||||
{ type: 'String', field: 'Наименование', value: 'Тест' },
|
||||
{ type: 'Number', field: 'Цена', value: '100.50' },
|
||||
{ type: 'Date', field: 'ДатаПоступления', value: '01.01.2024' },
|
||||
];
|
||||
|
||||
export default async function({ fillFields, getFormState, assert }, { type, field, value }) {
|
||||
await fillFields({ [field]: value });
|
||||
const state = await getFormState();
|
||||
assert.equal(state.fields[field]?.value, String(value));
|
||||
}
|
||||
```
|
||||
|
||||
Each `params` entry becomes its own test in the report. `{key}` placeholders in `name` get substituted; without placeholders, a `[index]` suffix is added. `ctx.testInfo.param` carries the current row.
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
node $RUN test tests/<app-name>/ # full app suite
|
||||
node $RUN test tests/<app-name>/03-goods-receipt/ # one feature folder
|
||||
node $RUN test tests/<app-name>/02-counterparties/01-create.test.mjs # one file
|
||||
node $RUN test tests/<app-name>/ --tags=smoke # by tag (intersection)
|
||||
node $RUN test tests/<app-name>/ --grep='накладн' # by name regex
|
||||
node $RUN test tests/<app-name>/ --bail --retry=1 # stop on first fail, allow 1 retry
|
||||
node $RUN test tests/<app-name>/ --report=allure-results --format=allure --report-dir=allure-results
|
||||
node $RUN test tests/<app-name>/ -- --rebuild-stand # after `--` → hookArgs
|
||||
```
|
||||
|
||||
Default report is JSON when `--report=…` is given. Allure needs `--format=allure` + a directory. JUnit similarly with `--format=junit`.
|
||||
|
||||
### Allure static config — `_allure/`
|
||||
|
||||
The runner copies `<testDir>/_allure/` into the report directory before generating Allure output. Drop in `categories.json` (regex-based failure classification — useful for 1C-specific buckets: license pool exhaustion, platform exceptions, runner timeouts, assertion failures), `environment.properties` (optional, often emitted dynamically by `prepare()`), `executor.json` (CI metadata, skip locally). The underscore prefix keeps the directory out of test discovery.
|
||||
|
||||
## Severity guidance
|
||||
|
||||
When the user doesn't dictate, default to:
|
||||
|
||||
| Test kind | Severity |
|
||||
|-----------|----------|
|
||||
| Login + section navigation, basic CRUD on covered entities | `critical` (also tag `smoke`) |
|
||||
| Documents posting, report generation, end-to-end processes | `critical` |
|
||||
| Field-level edge cases, formatting, optional flows | `normal` |
|
||||
| Cosmetic / recording / non-functional | `minor` |
|
||||
| Reserved for show-stopper protections | `blocker` (use sparingly) |
|
||||
|
||||
Don't promote everything to `critical` — it loses signal in the Allure dashboard.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- **Sleeps as a substitute for assertions.** `wait(5)` after `openCommand` is fine; `wait(30)` because something flakes is a bug — wait on `getFormState` instead.
|
||||
- **Retry as a substitute for understanding.** "Not found" twice means the data isn't there or the label is wrong. Don't loop.
|
||||
- **Position-based row identification** (`rows[0]`) when the DB has shared seed data. Filter by a unique marker (`Date.now()` suffix) instead.
|
||||
- **Hand-writing reset code in `afterEach`.** The runner already closes forms and dismisses errors after the hook.
|
||||
- **Cross-test state assumptions.** Each test must start from the desktop and seed its own data. Order-of-execution coupling is a regression-suite trap.
|
||||
- **`tags: ['smoke']` on a 90-second test.** Smoke means fast.
|
||||
- **Skipping recon** because "I know what this catalog looks like." The project's customisation almost certainly differs from stock.
|
||||
|
||||
(General browser-API anti-patterns — raw DOM, `clickElement('Закрыть')` instead of `closeForm()` — live in SKILL.md.)
|
||||
|
||||
## After a run — failure triage
|
||||
|
||||
1. Scan the JSON or Allure summary for `failed`.
|
||||
2. For each failure, read `error.message` + `error.step` + screenshot.
|
||||
3. If `error.onecError.stack` is present — it's a 1C exception, look at the platform trace.
|
||||
4. Classify:
|
||||
- **Test bug** — selector wrong, expectation wrong, race with no anchor → fix the test.
|
||||
- **Application bug** — actual misbehaviour reproduced → report to the user with the failing step name and the platform stack.
|
||||
- **Stand flake** — Apache timeout, login form not loading, license shortage → fix the hook idempotency or session-cleanup logic, not the test.
|
||||
5. After fixes, re-run only the affected files before the full suite.
|
||||
|
||||
Report back to the user with the classification, not raw failure dumps.
|
||||
|
||||
## Reference
|
||||
|
||||
- Browser API: [SKILL.md](SKILL.md)
|
||||
- Video and narration: [recording.md](recording.md)
|
||||
@@ -1,4 +1,4 @@
|
||||
// web-test browser v1.9 — Playwright browser management for 1C web client
|
||||
// web-test browser v1.12 — Playwright browser management for 1C web client
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
/**
|
||||
* Playwright browser management for 1C web client.
|
||||
@@ -37,6 +37,16 @@ let lastCaptions = []; // captions from the last completed recording (for addNar
|
||||
let lastRecordingDuration = null; // wall-clock duration of the last recording (seconds)
|
||||
let highlightMode = false;
|
||||
|
||||
// Multi-context registry: name → { context, page, sessionPrefix, seanceId, recorder, lastCaptions, lastRecordingDuration, highlightMode }
|
||||
// Populated by createContext(); module-level vars above mirror the active slot.
|
||||
// connect() does NOT use this Map — it preserves legacy single-session behavior for exec/run/start.
|
||||
const contexts = new Map();
|
||||
let activeContextName = null;
|
||||
// Isolation mode for the current cmdTest session — set by the first createContext call.
|
||||
// 'tab': all contexts share one persistent context (one window, multiple tabs, extension loads reliably).
|
||||
// 'window': each context gets its own BrowserContext (separate window per context, full cookie isolation, extension may not load).
|
||||
let activeMode = null;
|
||||
|
||||
const LOAD_TIMEOUT = 60000;
|
||||
const INIT_TIMEOUT = 60000;
|
||||
const ACTION_WAIT = 2000; // fallback minimum wait
|
||||
@@ -158,31 +168,51 @@ export async function connect(url, { extensionPath } = {}) {
|
||||
return await getPageState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort POST /e1cib/logout on a slot to release the 1C session license.
|
||||
* Silent — if page is closed or session info missing, just returns.
|
||||
* @param {object} slot { page, sessionPrefix, seanceId } from contexts Map
|
||||
* @param {number} [waitMs=500] pause after logout fetch (gives 1C time to process)
|
||||
*/
|
||||
async function _logoutSlot(slot, waitMs = 500) {
|
||||
if (!slot?.page || slot.page.isClosed() || !slot.seanceId || !slot.sessionPrefix) return;
|
||||
try {
|
||||
const logoutUrl = `${slot.sessionPrefix}/e1cib/logout?seanceId=${slot.seanceId}`;
|
||||
await slot.page.evaluate(async (url) => {
|
||||
await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{"root":{}}' });
|
||||
}, logoutUrl);
|
||||
await slot.page.waitForTimeout(waitMs);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully terminate the 1C session and close the browser.
|
||||
* Sends POST /e1cib/logout to release the license before closing.
|
||||
*/
|
||||
export async function disconnect() {
|
||||
// Auto-stop recording if active (prevents orphaned ffmpeg)
|
||||
// Multi-context path: stop recording + logout each slot before closing browser
|
||||
if (contexts.size > 0) {
|
||||
_saveActiveSlot();
|
||||
// Recorder is global — one stop covers all contexts
|
||||
if (recorder) {
|
||||
try { await stopRecording(); } catch {}
|
||||
}
|
||||
for (const [, slot] of contexts.entries()) {
|
||||
await _logoutSlot(slot);
|
||||
}
|
||||
contexts.clear();
|
||||
activeContextName = null;
|
||||
activeMode = null;
|
||||
}
|
||||
|
||||
// Single-session path (connect): auto-stop recording if active
|
||||
if (recorder) {
|
||||
try { await stopRecording(); } catch {}
|
||||
}
|
||||
|
||||
if (browser) {
|
||||
// Graceful logout — release the 1C license
|
||||
if (page && !page.isClosed() && seanceId && sessionPrefix) {
|
||||
try {
|
||||
const logoutUrl = `${sessionPrefix}/e1cib/logout?seanceId=${seanceId}`;
|
||||
await page.evaluate(async (url) => {
|
||||
await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: '{"root":{}}'
|
||||
});
|
||||
}, logoutUrl);
|
||||
await page.waitForTimeout(1000);
|
||||
} catch {}
|
||||
}
|
||||
// Graceful logout — release the 1C license (single-session connect path)
|
||||
await _logoutSlot({ page, sessionPrefix, seanceId }, 1000);
|
||||
await browser.close().catch(() => {});
|
||||
browser = null;
|
||||
page = null;
|
||||
@@ -228,6 +258,203 @@ export function getSession() {
|
||||
return { sessionPrefix, seanceId };
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Multi-context support (used by run.mjs cmdTest only)
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Save current module-level state into the active slot before switching.
|
||||
* No-op if no active slot.
|
||||
*/
|
||||
function _saveActiveSlot() {
|
||||
if (!activeContextName) return;
|
||||
const slot = contexts.get(activeContextName);
|
||||
if (!slot) return;
|
||||
slot.page = page;
|
||||
slot.sessionPrefix = sessionPrefix;
|
||||
slot.seanceId = seanceId;
|
||||
slot.highlightMode = highlightMode;
|
||||
// Note: `recorder`, `lastCaptions`, `lastRecordingDuration` are intentionally NOT
|
||||
// mirrored per-slot. A multi-context recording produces one continuous output file —
|
||||
// the recorder follows the active page via recorder._attachPage(), not per-slot state.
|
||||
}
|
||||
|
||||
/** Load a slot's state into module-level vars and mark it active. */
|
||||
function _activateSlot(name) {
|
||||
const slot = contexts.get(name);
|
||||
if (!slot) throw new Error(`Context "${name}" not found. Create it via createContext() first.`);
|
||||
page = slot.page;
|
||||
sessionPrefix = slot.sessionPrefix;
|
||||
seanceId = slot.seanceId;
|
||||
highlightMode = slot.highlightMode || false;
|
||||
activeContextName = name;
|
||||
}
|
||||
|
||||
/** Attach 1C session listeners to a page, writing into the given slot. */
|
||||
function _attachSessionListeners(pg, slot, name) {
|
||||
pg.on('dialog', dialog => dialog.accept().catch(() => {}));
|
||||
pg.on('request', req => {
|
||||
if (slot.seanceId) return;
|
||||
const m = req.url().match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/e1cib\/.+[?&]seanceId=([^&]+)/);
|
||||
if (m) {
|
||||
slot.sessionPrefix = m[1];
|
||||
slot.seanceId = m[2];
|
||||
if (activeContextName === name) {
|
||||
sessionPrefix = m[1];
|
||||
seanceId = m[2];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create (or navigate) a named browser context.
|
||||
* First call launches Chromium via chromium.launch() (NOT launchPersistentContext) so that
|
||||
* subsequent calls can create additional isolated BrowserContexts in the same process.
|
||||
* Trade-off: 1C browser extension is loaded via --load-extension (process-level) rather than
|
||||
* persistent profile.
|
||||
*
|
||||
* Use this from run.mjs cmdTest only — exec/run/start use connect() and stay on the
|
||||
* legacy persistent-context path.
|
||||
*/
|
||||
export async function createContext(name, url, { extensionPath, isolation = 'tab' } = {}) {
|
||||
if (contexts.has(name)) {
|
||||
await setActiveContext(name);
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: LOAD_TIMEOUT });
|
||||
try { await page.waitForSelector('#themesCell_theme_0', { timeout: INIT_TIMEOUT }); }
|
||||
catch { await page.waitForTimeout(5000); }
|
||||
await closeModals();
|
||||
return await getPageState();
|
||||
}
|
||||
|
||||
if (!['tab', 'window'].includes(isolation)) {
|
||||
throw new Error(`createContext: invalid isolation "${isolation}", expected 'tab' or 'window'`);
|
||||
}
|
||||
if (activeMode && activeMode !== isolation) {
|
||||
throw new Error(`createContext: cannot mix isolation modes — first context used "${activeMode}", "${name}" requested "${isolation}". Use the same mode for all contexts in one run.`);
|
||||
}
|
||||
|
||||
// First context: launch browser. Subsequent: reuse existing.
|
||||
let isFirstContext = !browser;
|
||||
if (isFirstContext) {
|
||||
const extPath = findExtension(extensionPath);
|
||||
const launchArgs = ['--start-maximized'];
|
||||
if (extPath) {
|
||||
launchArgs.push('--disable-extensions-except=' + extPath, '--load-extension=' + extPath);
|
||||
}
|
||||
if (isolation === 'tab') {
|
||||
// Persistent context: extension loads reliably, one window with tabs per context
|
||||
persistentUserDataDir = pathJoin(tmpdir(), 'pw-1c-test-' + Date.now());
|
||||
mkdirSync(persistentUserDataDir, { recursive: true });
|
||||
browser = await chromium.launchPersistentContext(persistentUserDataDir, {
|
||||
headless: false,
|
||||
args: launchArgs,
|
||||
viewport: null,
|
||||
permissions: ['clipboard-read', 'clipboard-write'],
|
||||
});
|
||||
} else {
|
||||
// Window mode: separate BrowserContext per slot, full cookie isolation
|
||||
browser = await chromium.launch({ headless: false, args: launchArgs });
|
||||
}
|
||||
activeMode = isolation;
|
||||
}
|
||||
|
||||
// Save current active before switching
|
||||
_saveActiveSlot();
|
||||
|
||||
// Create slot — page differs by mode
|
||||
let newCtx, newPage;
|
||||
if (activeMode === 'tab') {
|
||||
// Reuse the persistent context for all slots; each slot gets its own page (tab)
|
||||
newCtx = browser;
|
||||
if (isFirstContext) {
|
||||
newPage = browser.pages()[0] || await browser.newPage();
|
||||
} else {
|
||||
newPage = await browser.newPage();
|
||||
}
|
||||
} else {
|
||||
// Window mode: each slot owns its BrowserContext + page
|
||||
newCtx = await browser.newContext({
|
||||
viewport: null,
|
||||
permissions: ['clipboard-read', 'clipboard-write'],
|
||||
});
|
||||
newPage = await newCtx.newPage();
|
||||
}
|
||||
|
||||
const slot = {
|
||||
context: newCtx,
|
||||
page: newPage,
|
||||
sessionPrefix: null,
|
||||
seanceId: null,
|
||||
highlightMode: false,
|
||||
};
|
||||
contexts.set(name, slot);
|
||||
|
||||
_attachSessionListeners(newPage, slot, name);
|
||||
_activateSlot(name);
|
||||
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: LOAD_TIMEOUT });
|
||||
try { await page.waitForSelector('#themesCell_theme_0', { timeout: INIT_TIMEOUT }); }
|
||||
catch { await page.waitForTimeout(5000); }
|
||||
await closeModals();
|
||||
|
||||
return await getPageState();
|
||||
}
|
||||
|
||||
/** Switch the active context. Subsequent browser API calls operate on this context's page. */
|
||||
export async function setActiveContext(name) {
|
||||
if (activeContextName === name) return;
|
||||
if (!contexts.has(name)) throw new Error(`Context "${name}" not found. Available: [${[...contexts.keys()].join(', ')}]`);
|
||||
// If a recording is active, flush the outgoing page's last frame so the gap is filled
|
||||
// up to the moment of the switch (avoids a "jump" in video time).
|
||||
if (recorder && recorder._flushFrames) recorder._flushFrames();
|
||||
_saveActiveSlot();
|
||||
_activateSlot(name);
|
||||
// If the recording is still alive (it lives across slots — we keep the same ffmpeg/output),
|
||||
// re-attach its screencast to the newly active page.
|
||||
if (recorder && recorder._attachPage) {
|
||||
await recorder._attachPage(page);
|
||||
}
|
||||
}
|
||||
|
||||
export function listContexts() {
|
||||
return [...contexts.keys()];
|
||||
}
|
||||
|
||||
export function getActiveContext() {
|
||||
return activeContextName;
|
||||
}
|
||||
|
||||
export function hasContext(name) {
|
||||
return contexts.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a named context: logout, close its page (tab mode) or BrowserContext
|
||||
* (window mode), remove from registry. Cannot close the currently active
|
||||
* context — caller must setActiveContext to another first. This keeps the
|
||||
* recorder/page invariants simple: recorder is always attached to the
|
||||
* active slot, which closeContext never touches.
|
||||
*
|
||||
* @throws if name is not registered or equals the active context.
|
||||
*/
|
||||
export async function closeContext(name) {
|
||||
if (!contexts.has(name)) {
|
||||
throw new Error(`Context "${name}" not found. Available: [${[...contexts.keys()].join(', ')}]`);
|
||||
}
|
||||
if (name === activeContextName) {
|
||||
throw new Error(`closeContext: cannot close the active context "${name}". setActiveContext to another context first.`);
|
||||
}
|
||||
const slot = contexts.get(name);
|
||||
await _logoutSlot(slot);
|
||||
if (activeMode === 'tab') {
|
||||
try { await slot.page.close(); } catch {}
|
||||
} else {
|
||||
try { await slot.context.close(); } catch {}
|
||||
}
|
||||
contexts.delete(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close startup modals and guide tabs.
|
||||
* Strategy: Escape → click default buttons → close extra tabs → repeat.
|
||||
@@ -4861,10 +5088,7 @@ export async function startRecording(outputPath, opts = {}) {
|
||||
const resolvedPath = resolveProjectPath(outputPath);
|
||||
mkdirSync(dirname(resolvedPath), { recursive: true });
|
||||
|
||||
// Create CDP session for screencast
|
||||
const cdp = await page.context().newCDPSession(page);
|
||||
|
||||
// Spawn ffmpeg process
|
||||
// Spawn ffmpeg process — single output file across context switches
|
||||
const ffmpeg = spawn(ffmpegPath, [
|
||||
'-y', // overwrite output
|
||||
'-f', 'image2pipe', // input: piped images
|
||||
@@ -4880,71 +5104,86 @@ export async function startRecording(outputPath, opts = {}) {
|
||||
resolvedPath
|
||||
], { stdio: ['pipe', 'ignore', 'pipe'] });
|
||||
|
||||
let ffmpegError = '';
|
||||
ffmpeg.stderr.on('data', d => { ffmpegError += d.toString(); });
|
||||
ffmpeg.on('error', err => { ffmpegError += err.message; });
|
||||
ffmpeg.on('error', err => { if (recorder) recorder.ffmpegError += err.message; });
|
||||
|
||||
// Listen for screencast frames and pipe to ffmpeg
|
||||
// CDP sends frames only on screen changes, so we duplicate frames
|
||||
// to fill gaps and maintain real-time playback speed
|
||||
const frameDuration = 1000 / fps;
|
||||
let lastFrameTime = null;
|
||||
let lastFrameBuf = null;
|
||||
const speechRate = opts.speechRate || 70; // ms per character for smart TTS wait
|
||||
|
||||
cdp.on('Page.screencastFrame', async ({ data, sessionId }) => {
|
||||
// Frame handler shared across CDP sessions (lives in recorder, not closure):
|
||||
// when the active context switches, we attach a new CDP session and route its
|
||||
// frames to the same ffmpeg pipe — preserving a single continuous timeline.
|
||||
const frameHandler = async ({ data, sessionId }, cdp) => {
|
||||
if (!recorder) return;
|
||||
const buf = Buffer.from(data, 'base64');
|
||||
const now = Date.now();
|
||||
|
||||
if (!ffmpeg.stdin.destroyed) {
|
||||
let framesWritten = 0;
|
||||
if (lastFrameTime && lastFrameBuf) {
|
||||
// Fill the gap with duplicates of the previous frame
|
||||
const gap = now - lastFrameTime;
|
||||
if (recorder.lastFrameTime && recorder.lastFrameBuf) {
|
||||
const gap = now - recorder.lastFrameTime;
|
||||
const dupes = Math.round(gap / frameDuration) - 1;
|
||||
for (let i = 0; i < dupes && i < fps * 30; i++) {
|
||||
ffmpeg.stdin.write(lastFrameBuf);
|
||||
ffmpeg.stdin.write(recorder.lastFrameBuf);
|
||||
framesWritten++;
|
||||
}
|
||||
}
|
||||
ffmpeg.stdin.write(buf);
|
||||
framesWritten++;
|
||||
// Track actual video timeline position (accounts for frame duplication)
|
||||
if (recorder) recorder.videoTimeMs += framesWritten * frameDuration;
|
||||
recorder.videoTimeMs += framesWritten * frameDuration;
|
||||
}
|
||||
|
||||
lastFrameTime = now;
|
||||
lastFrameBuf = buf;
|
||||
recorder.lastFrameTime = now;
|
||||
recorder.lastFrameBuf = buf;
|
||||
try { await cdp.send('Page.screencastFrameAck', { sessionId }); } catch {}
|
||||
});
|
||||
|
||||
// Start the screencast
|
||||
await cdp.send('Page.startScreencast', {
|
||||
format: 'jpeg',
|
||||
quality,
|
||||
everyNthFrame: 1
|
||||
});
|
||||
|
||||
// Expose a frame-writing helper on the recorder object.
|
||||
// During static periods (e.g. smart TTS pauses), CDP may not send screencast
|
||||
// frames. Call _flushFrames() to fill the gap with duplicates of the last frame,
|
||||
// keeping video timeline in sync with wall-clock time.
|
||||
const _flushFrames = () => {
|
||||
if (!lastFrameBuf || !lastFrameTime || ffmpeg.stdin.destroyed) return;
|
||||
const now = Date.now();
|
||||
const gap = now - lastFrameTime;
|
||||
const dupes = Math.round(gap / frameDuration);
|
||||
for (let i = 0; i < dupes; i++) {
|
||||
ffmpeg.stdin.write(lastFrameBuf);
|
||||
if (recorder) recorder.videoTimeMs += frameDuration;
|
||||
}
|
||||
if (dupes > 0) lastFrameTime = now;
|
||||
};
|
||||
|
||||
const speechRate = opts.speechRate || 70; // ms per character for smart TTS wait
|
||||
recorder = { cdp, ffmpeg, startTime: Date.now(), outputPath: resolvedPath, ffmpegError: '', captions: [], videoTimeMs: 0, _flushFrames, speechRate };
|
||||
// Redirect stderr accumulation to the recorder object
|
||||
ffmpeg.stderr.removeAllListeners('data');
|
||||
// Duplicate the last frame to fill wall-clock gaps (static periods, context switches).
|
||||
const _flushFrames = () => {
|
||||
if (!recorder || !recorder.lastFrameBuf || !recorder.lastFrameTime || ffmpeg.stdin.destroyed) return;
|
||||
const now = Date.now();
|
||||
const gap = now - recorder.lastFrameTime;
|
||||
const dupes = Math.round(gap / frameDuration);
|
||||
for (let i = 0; i < dupes; i++) {
|
||||
ffmpeg.stdin.write(recorder.lastFrameBuf);
|
||||
recorder.videoTimeMs += frameDuration;
|
||||
}
|
||||
if (dupes > 0) recorder.lastFrameTime = now;
|
||||
};
|
||||
|
||||
// Attach screencast to a specific page. Stops the old CDP first (if any).
|
||||
// Called by startRecording for the initial page, and by setActiveContext when
|
||||
// the active context changes mid-recording.
|
||||
const _attachPage = async (targetPage) => {
|
||||
if (recorder.cdp) {
|
||||
_flushFrames(); // freeze the last frame of the outgoing page up to "now"
|
||||
try { await recorder.cdp.send('Page.stopScreencast'); } catch {}
|
||||
try { await recorder.cdp.detach(); } catch {}
|
||||
recorder.cdp = null;
|
||||
}
|
||||
const cdp = await targetPage.context().newCDPSession(targetPage);
|
||||
cdp.on('Page.screencastFrame', (ev) => frameHandler(ev, cdp));
|
||||
await cdp.send('Page.startScreencast', { format: 'jpeg', quality, everyNthFrame: 1 });
|
||||
recorder.cdp = cdp;
|
||||
recorder.activePage = targetPage;
|
||||
};
|
||||
|
||||
recorder = {
|
||||
cdp: null,
|
||||
activePage: null,
|
||||
ffmpeg,
|
||||
startTime: Date.now(),
|
||||
outputPath: resolvedPath,
|
||||
ffmpegError: '',
|
||||
captions: [],
|
||||
videoTimeMs: 0,
|
||||
frameDuration,
|
||||
lastFrameTime: null,
|
||||
lastFrameBuf: null,
|
||||
_flushFrames,
|
||||
_attachPage,
|
||||
speechRate,
|
||||
};
|
||||
ffmpeg.stderr.on('data', d => { recorder.ffmpegError += d.toString(); });
|
||||
|
||||
await _attachPage(page);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -74,6 +74,7 @@ python tools/cc-1c-skills/scripts/switch.py
|
||||
| Веб-публикация (Web) | 4 навыка `/web-*` | Публикация баз через Apache, статус, остановка, удаление публикаций | [Подробнее](docs/web-guide.md) |
|
||||
| Тестирование (Web) | `/web-test` | Взаимодействие с веб-клиентом 1С — навигация, формы, таблицы, отчёты, тестирование | [Подробнее](docs/web-test-guide.md) |
|
||||
| Запись видео (Web) | `/web-test` | Запись видеоинструкций с субтитрами, подсветкой и TTS-озвучкой | [Подробнее](docs/web-test-recording-guide.md) |
|
||||
| Регресс прикладного решения (Web) | `/web-test` | Автоматический регресс конфигурации: тесты, проверки, отчёты, прогон после правок | [Подробнее](docs/web-test-regression-guide.md) |
|
||||
| Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — |
|
||||
|
||||
## Требования
|
||||
@@ -255,6 +256,7 @@ docs/
|
||||
├── web-guide.md # Гайд: веб-публикация через Apache
|
||||
├── web-test-guide.md # Гайд: тестирование через веб-клиент
|
||||
├── web-test-recording-guide.md # Гайд: запись видеоинструкций
|
||||
├── web-test-regression-guide.md # Гайд: регресс прикладного решения
|
||||
├── 1c-epf-spec.md # Спецификация XML-формата (EPF)
|
||||
├── 1c-erf-spec.md # Спецификация XML-формата (ERF)
|
||||
├── 1c-form-spec.md # Спецификация управляемых форм
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
# Регрессионное тестирование прикладного решения
|
||||
|
||||
Навык `/web-test` умеет не только разово выполнить сценарий в браузере, но и сопровождать прикладное решение полноценным набором автотестов: каждый тест — отдельный файл, с шагами, проверками, тегами, отчётом и видеозаписью падений. После каждой правки конфигурации модель прогоняет весь набор и показывает, что ожидаемо ведёт себя как раньше, а что сломалось.
|
||||
|
||||
```
|
||||
правка конфигурации → загрузка → обновление → публикация → прогон тестов → отчёт
|
||||
```
|
||||
|
||||
Это про прикладное решение в целом, не про разовую проверку одной формы. Для разовых сценариев («открой накладную, проверь сумму») по-прежнему удобнее интерактивный режим из [web-test-guide.md](web-test-guide.md).
|
||||
|
||||
## Предусловия
|
||||
|
||||
- База опубликована через Apache (`/web-publish`).
|
||||
- Установлен Node.js 18+, зависимости подняты: `cd .claude/skills/web-test/scripts && npm install`.
|
||||
- ffmpeg — нужен только если хотите видеозапись прогона как доказательство падения. Без него падения фиксируются скриншотами. Установка описана в [web-test-recording-guide.md](web-test-recording-guide.md).
|
||||
|
||||
## Как это устроено
|
||||
|
||||
Набор тестов живёт в каталоге `tests/` вашего проекта. Каждое прикладное решение — отдельная подпапка. Внутри подпапки:
|
||||
|
||||
- `_hooks.mjs` — подготовка стенда (восстановление базы, публикация) и общая очистка после прогона. Необязателен.
|
||||
- `webtest.config.mjs` — адрес базы и набор пользователей (например, кладовщик и менеджер для процессов согласования). Необязателен — если в проекте один пользователь и один URL, можно обойтись без него.
|
||||
- Сами тесты — файлы `*.test.mjs`, сгруппированные по функциональным папкам.
|
||||
|
||||
```
|
||||
tests/
|
||||
моя-конфигурация/
|
||||
_hooks.mjs
|
||||
webtest.config.mjs
|
||||
01-вход/
|
||||
01-открытие-базы.test.mjs
|
||||
02-контрагенты/
|
||||
01-создание.test.mjs
|
||||
02-правка-телефона.test.mjs
|
||||
03-поступление-товаров/
|
||||
01-оформление.test.mjs
|
||||
02-проведение.test.mjs
|
||||
04-отчёт-остатки/
|
||||
01-формирование.test.mjs
|
||||
05-согласование/
|
||||
01-полный-цикл.test.mjs
|
||||
```
|
||||
|
||||
Порядок выполнения — по алфавиту, поэтому удобно префиксовать папки и файлы номерами. Это даёт предсказуемый сценарий: сначала вход, потом справочники, потом документы, потом отчёты, в конце — процессы с несколькими пользователями.
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
Самый короткий путь от нуля до зелёного теста — попросить модель пройти ваш сценарий руками и зафиксировать его как тест:
|
||||
|
||||
```
|
||||
> Покрой регрессом справочник Контрагенты в моей конфигурации.
|
||||
> Нужны проверки: создание, правка телефона, удаление.
|
||||
```
|
||||
|
||||
Что сделает модель:
|
||||
|
||||
1. Соберёт информацию о справочнике через `/meta-info` и `/form-info` — посмотрит реквизиты и форму элемента, чтобы знать правильные имена полей.
|
||||
2. Подключится к опубликованной базе в интерактивном режиме и **руками пройдёт** каждый сценарий — создание, правка, удаление. Это нужно, чтобы зафиксировать настоящие имена кнопок, увидеть, какие диалоги показывает 1С, понять, требуется ли подтверждение сохранения.
|
||||
3. Зафиксирует пройденный сценарий как файл `tests/<ваша-конфигурация>/02-контрагенты/01-создание.test.mjs`.
|
||||
4. Запустит его и покажет результат.
|
||||
|
||||
При следующих прогонах ничего этого делать не нужно — модель просто запустит готовый набор.
|
||||
|
||||
## Сценарии работы с моделью
|
||||
|
||||
### Покрытие регрессом доработанного объекта
|
||||
|
||||
```
|
||||
> Я добавил в справочник Номенклатура реквизит "Цена" и "Активен".
|
||||
> Покрой это регрессом — создание, редактирование, фильтрация по активности
|
||||
```
|
||||
|
||||
Модель:
|
||||
- посмотрит структуру справочника и формы (через `/meta-info`, `/form-info`);
|
||||
- интерактивно проверит, как ведут себя новые поля в браузере;
|
||||
- сгенерирует 2-3 тестовых файла под папкой `02-номенклатура/`;
|
||||
- прогонит — покажет, что зелёное, что красное.
|
||||
|
||||
### Тест процесса с несколькими пользователями
|
||||
|
||||
```
|
||||
> Сделай тест для процесса согласования приходных накладных.
|
||||
> Кладовщик создаёт накладную, менеджер утверждает,
|
||||
> кладовщик видит обновлённый статус
|
||||
```
|
||||
|
||||
Модель настроит в `webtest.config.mjs` двух пользователей (с разными URL базы — например, `app-clerk` и `app-manager`), напишет тест, который оркестрирует переключение между ними, и положит его в `05-согласование/`.
|
||||
|
||||
```js
|
||||
export const contexts = ['кладовщик', 'менеджер'];
|
||||
|
||||
export default async function({ кладовщик, менеджер, step, assert }) {
|
||||
await step('Кладовщик создаёт накладную', async () => {
|
||||
await кладовщик.navigateSection('Склад');
|
||||
await кладовщик.openCommand('Приходные накладные');
|
||||
await кладовщик.clickElement('Создать');
|
||||
// ...
|
||||
});
|
||||
await step('Менеджер утверждает', async () => {
|
||||
await менеджер.navigateSection('Согласование');
|
||||
// ...
|
||||
});
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Учтите ограничение по лицензиям 1С: каждый одновременно открытый пользователь — это занятая клиентская лицензия. Если в наборе много многопользовательских тестов, а на стенде лицензий впритык, прогоны начнут спотыкаться на «свободных лицензий не осталось». Модель освобождает сессии между тестами автоматически (закрывает контексты после процессного теста), но если стенд ограничен — закладывайте это в планирование набора: один-два многопользовательских сценария вместо десяти.
|
||||
|
||||
### Воспроизведение ошибки тестом
|
||||
|
||||
```
|
||||
> При проведении накладной без заполненного контрагента у нас не появляется
|
||||
> ошибка валидации, документ просто проводится с пустым контрагентом — это баг.
|
||||
> Зафиксируй это падающим тестом
|
||||
```
|
||||
|
||||
Модель воспроизведёт сценарий, напишет тест с проверкой «должна быть ошибка», получит красный — потом, когда вы поправите конфигурацию и попросите перепрогнать, тест станет зелёным. Это документирует ожидаемое поведение в виде кода.
|
||||
|
||||
### Прогон регресса после изменений
|
||||
|
||||
```
|
||||
> Я обновил расширение, накатил в базу. Прогони регресс
|
||||
```
|
||||
|
||||
Модель запустит весь набор, дождётся завершения и расскажет:
|
||||
- сколько тестов прошло, сколько упало, сколько пропущено;
|
||||
- по каждому упавшему — что именно сломалось (название шага, сообщение об ошибке, ссылка на скриншот);
|
||||
- классифицирует падения: это ошибка в самом тесте (нужно поправить тест), ошибка в приложении (баг, который вы внесли изменением), или нестабильность стенда (Apache не ответил вовремя, лицензия не освободилась).
|
||||
|
||||
```
|
||||
> Прогони только тесты по контрагентам с подробным отчётом
|
||||
```
|
||||
|
||||
Запустит подмножество — фильтр по тегу или папке, с записью JSON-отчёта.
|
||||
|
||||
### Подготовка автономного стенда
|
||||
|
||||
Если вы хотите, чтобы регресс можно было запустить «с нуля» — даже на чистой машине без подготовленной базы, — модель настроит автоматическую подготовку стенда:
|
||||
|
||||
```
|
||||
> Сделай, чтобы перед прогоном тестов база восстанавливалась из эталона,
|
||||
> а после прогона публикация снималась
|
||||
```
|
||||
|
||||
Это пишется один раз в файле `_hooks.mjs`: при запуске тестов запускается подготовка (через навыки `/db-create`, `/db-load-xml`, `/web-publish`), а после — очистка. Внутри предусмотрено кэширование: если ничего не менялось со прошлого прогона, повторная подготовка занимает доли секунды.
|
||||
|
||||
## Пример организации покрытия
|
||||
|
||||
Допустим, у нас условное прикладное решение «Учёт поступлений товаров» — справочники контрагентов и номенклатуры, документ приходной накладной, отчёт остатков, процесс согласования с двумя пользователями. Логично организовать набор так:
|
||||
|
||||
```
|
||||
tests/учёт-поступлений/
|
||||
_hooks.mjs # подготовка: восстановление базы + публикация
|
||||
webtest.config.mjs # URL базы, контексты кладовщика и менеджера
|
||||
01-вход/
|
||||
01-открытие-базы.test.mjs # базовая работоспособность: вход проходит, разделы видны
|
||||
02-навигация-по-разделам.test.mjs # обход всех разделов конфигурации
|
||||
02-контрагенты/
|
||||
01-создание.test.mjs # создание, проверка появления в списке
|
||||
02-редактирование.test.mjs # правка реквизита, проверка сохранения
|
||||
03-удаление.test.mjs # удаление с подтверждением
|
||||
03-номенклатура/
|
||||
01-создание.test.mjs
|
||||
02-фильтр-по-активности.test.mjs # быстрая фильтрация списка
|
||||
04-поступление-товаров/
|
||||
01-оформление.test.mjs # заполнение шапки и табличной части
|
||||
02-проведение.test.mjs # проведение документа, проверка движений
|
||||
03-отмена-проведения.test.mjs
|
||||
04-валидация-обязательных.test.mjs # негативный тест: пустой контрагент → ошибка
|
||||
05-отчёт-остатки/
|
||||
01-формирование.test.mjs
|
||||
02-отбор-по-складу.test.mjs
|
||||
03-расшифровка.test.mjs # переход из ячейки отчёта в исходный документ
|
||||
06-согласование/
|
||||
01-полный-цикл.test.mjs # многопользовательский тест
|
||||
```
|
||||
|
||||
Принципы:
|
||||
|
||||
- **Папки — по бизнес-функции**, не по типу метаданных. Лучше `04-поступление-товаров/` (что делает пользователь), чем `документы/` (что лежит в дереве конфигурации).
|
||||
- **Цифровые префиксы** — на папке и на файле. Гарантируют, что сначала отработают базовые проверки (вход, справочники), потом сложные (документы, отчёты, процессы). При падении базы остальное и так не пройдёт — нет смысла занимать стенд получасом.
|
||||
- **Один файл — одна логически связанная история.** Не «всё про контрагентов в одном файле», а «отдельно создание, отдельно правка, отдельно удаление». Когда падает — сразу видно, какой именно сценарий сломан.
|
||||
- **Негативные тесты тоже есть.** «Документ без контрагента не проводится» — такой же важный регресс, как и позитивный сценарий, особенно после правок в обработчиках проверки заполнения.
|
||||
- **Процессные тесты — в конце.** Они самые хрупкие (зависят от двух сессий, лицензий, синхронизации) и самые длинные. Если упадут — у вас уже есть данные от предыдущих тестов.
|
||||
|
||||
## Анатомия одного теста
|
||||
|
||||
Пользователь, как правило, тест не пишет — генерирует модель. Но прочитать и поправить полезно уметь. Стандартный файл выглядит так:
|
||||
|
||||
```js
|
||||
export const name = 'Создание контрагента';
|
||||
export const tags = ['контрагенты', 'базовая-проверка'];
|
||||
export const timeout = 60000;
|
||||
|
||||
export default async function({
|
||||
navigateSection, openCommand, clickElement, fillFields,
|
||||
readTable, closeForm, assert, step
|
||||
}) {
|
||||
await step('Открыть список контрагентов', async () => {
|
||||
await navigateSection('Продажи');
|
||||
await openCommand('Контрагенты');
|
||||
});
|
||||
|
||||
await step('Создать нового контрагента', async () => {
|
||||
await clickElement('Создать');
|
||||
await fillFields({ 'Наименование': 'ТД Тест', 'ИНН': '7707083893' });
|
||||
await clickElement('Записать и закрыть');
|
||||
});
|
||||
|
||||
await step('Убедиться, что элемент появился в списке', async () => {
|
||||
const t = await readTable();
|
||||
assert.tableHasRow(t, r => r['Наименование'] === 'ТД Тест');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Что здесь есть:
|
||||
|
||||
- **`name`** — человекочитаемое имя теста. Появится в отчёте.
|
||||
- **`tags`** — теги для фильтрации. Можно прогонять не весь набор, а только нужные: `--tags=контрагенты`.
|
||||
- **`timeout`** — сколько максимум тест может идти. По умолчанию 30 секунд, для длинных сценариев увеличиваем.
|
||||
- **Тело теста** — функция, которая получает API браузера (см. [SKILL.md](../.claude/skills/web-test/SKILL.md)) плюс `assert` и `step`.
|
||||
- **`step('имя', async () => {...})`** — обёртка шага. Имена шагов попадают в отчёт, при падении видно, какой именно шаг сломался.
|
||||
- **`assert.*`** — проверки. `assert.tableHasRow`, `assert.equal`, `assert.ok` и т.д. Если проверка не выполнилась — тест считается упавшим.
|
||||
|
||||
Имена шагов и теста — по-русски, описательные. Они показываются и в консоли, и в отчётах.
|
||||
|
||||
## Запуск и отчёты
|
||||
|
||||
### Простой прогон
|
||||
|
||||
```
|
||||
> Прогони регресс
|
||||
```
|
||||
|
||||
Модель запустит весь набор, дождётся, покажет сводку:
|
||||
|
||||
```
|
||||
✓ Открытие базы (2.1s)
|
||||
✓ Создание контрагента (8.4s)
|
||||
✗ Проведение приходной накладной (12.7s)
|
||||
└ Заполнить табличную часть (5.2s)
|
||||
Не найден столбец "Цена" в табличной части "Товары"
|
||||
скриншот: tests/учёт-поступлений/error-shot.png
|
||||
|
||||
23 пройдено, 1 упал, 0 пропущено (3 мин 42 с)
|
||||
```
|
||||
|
||||
### Подробный отчёт
|
||||
|
||||
```
|
||||
> Прогони регресс и сохрани подробный отчёт
|
||||
```
|
||||
|
||||
Модель добавит флаг записи отчёта (JSON или Allure) — потом по нему можно листать историю прогонов, видеть длительности шагов, открывать прикреплённые скриншоты.
|
||||
|
||||
Allure — стандартный визуальный отчёт с категориями падений, графиками, таймлайном. Чтобы посмотреть отчёт после прогона:
|
||||
|
||||
```bash
|
||||
# Allure CLI устанавливается отдельно (npm install -g allure-commandline)
|
||||
allure serve allure-results
|
||||
```
|
||||
|
||||
### Категории падений в Allure
|
||||
|
||||
Без дополнительной настройки Allure складывает все упавшие тесты в один общий список «Defects». Если в прогоне упало 15 тестов, не сразу понятно, что из этого — пятнадцать разных проблем или одна и та же ошибка (например, нехватка лицензии на стенде), которая зацепила пятнадцать тестов подряд.
|
||||
|
||||
Чтобы Allure группировал падения по причинам, рядом с тестами кладётся каталог `_allure/` с файлом `categories.json`. Подчёркивание в имени каталога — чтобы он не воспринимался как папка с тестами; раннер копирует его содержимое в отчёт.
|
||||
|
||||
```
|
||||
tests/моя-конфигурация/
|
||||
_allure/
|
||||
categories.json # классификация падений
|
||||
environment.properties # необязательно: URL, версия 1С, ветка git
|
||||
executor.json # необязательно: метаданные сборки CI
|
||||
_hooks.mjs
|
||||
01-вход/
|
||||
...
|
||||
```
|
||||
|
||||
`categories.json` — это список регулярных выражений, по которым ошибка теста относится к той или иной группе:
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "Нехватка лицензий 1С",
|
||||
"matchedStatuses": ["failed", "broken"],
|
||||
"messageRegex": ".*Не обнаружено свободной лицензии.*" },
|
||||
{ "name": "Ошибка приложения 1С",
|
||||
"matchedStatuses": ["failed"],
|
||||
"messageRegex": ".*(ВызватьИсключение|В поле введены некорректные данные|Произошла ошибка).*" },
|
||||
{ "name": "Элемент не найден",
|
||||
"matchedStatuses": ["failed"],
|
||||
"messageRegex": ".*(clickElement|fillFields|selectValue).*not found.*" },
|
||||
{ "name": "Превышен лимит времени теста",
|
||||
"matchedStatuses": ["failed", "broken"],
|
||||
"messageRegex": "Timeout \\(\\d+ms\\)" },
|
||||
{ "name": "Несовпадение ожидания и факта",
|
||||
"matchedStatuses": ["failed"],
|
||||
"messageRegex": "(Expected|AssertionError).*" }
|
||||
]
|
||||
```
|
||||
|
||||
Когда вы попросите модель в первый раз настроить регресс, она положит шаблонный `categories.json` со стандартными классами. По мере того как вы будете находить новые типичные причины падений (например, специфичные для вашего расширения тексты ошибок), категории дополняются.
|
||||
|
||||
В виджете «Categories» итогового отчёта вы увидите примерно так:
|
||||
|
||||
```
|
||||
Нехватка лицензий 1С — 12 падений
|
||||
Ошибка приложения 1С — 2 падения
|
||||
Несовпадение ожидания и факта — 1 падение
|
||||
```
|
||||
|
||||
— и сразу понятно, что 12 падений — это один стенд-баг, а двумя «ошибками приложения» нужно разобраться по существу.
|
||||
|
||||
Помимо `categories.json` в каталог `_allure/` можно положить ещё два стандартных файла:
|
||||
|
||||
- **`environment.properties`** — список `ключ=значение` (URL базы, версия платформы 1С, имя ветки git, номер сборки). Покажется в отчёте в виджете «Environment». Полезно, когда регресс гоняется на нескольких стендах или после каждого билда — видно, на чём именно был получен результат. Этот файл удобно генерировать прямо в подготовке стенда (`_hooks.mjs`), а не держать статичной копией.
|
||||
- **`executor.json`** — метаданные системы сборки: ссылка на Jenkins-задачу, идентификатор запуска GitHub Actions и т.д. Нужен только если регресс запускается на сервере сборки. При локальном прогоне ничего класть не надо.
|
||||
|
||||
### Прогон части набора
|
||||
|
||||
```
|
||||
> Прогони только тесты по поступлениям товаров
|
||||
> Прогони только базовые проверки
|
||||
> Прогони только упавший вчера тест с проведением накладной
|
||||
```
|
||||
|
||||
Модель выберет нужное подмножество — по папке, по тегу или по имени теста.
|
||||
|
||||
### Принудительная пересборка стенда
|
||||
|
||||
Если хотите, чтобы перед прогоном база восстановилась с нуля:
|
||||
|
||||
```
|
||||
> Прогони регресс с полной пересборкой стенда
|
||||
```
|
||||
|
||||
Это передаст в подготовку флаг типа `--rebuild-stand` — `_hooks.mjs` пересоздаст базу из эталона. Полезно после крупных правок или если подозреваете, что предыдущие прогоны загрязнили данные.
|
||||
|
||||
## Что делать, когда тест упал
|
||||
|
||||
Модель проанализирует падение и отнесёт его к одной из трёх категорий:
|
||||
|
||||
1. **Ошибка в самом тесте.** Например, переименовали реквизит — тест ищет старое имя поля. Решение: модель обновит тест.
|
||||
2. **Ошибка в приложении.** Это и есть то, ради чего регресс существует: что-то поменялось в конфигурации, и сценарий, который раньше работал, теперь не отрабатывает. Модель опишет, что именно произошло, со скриншотом и трассировкой стека 1С, если ошибка была серверной.
|
||||
3. **Нестабильность стенда.** Apache не ответил, не освободилась лицензия, база отвалилась. Это лечится не правкой теста, а починкой подготовки стенда в `_hooks.mjs` или, реже, повторным прогоном с одним повтором.
|
||||
|
||||
Просите модель не «исправь упавший тест», а «разберись с падением» — иначе она может молча подкрутить ожидание под текущее поведение, замаскировав настоящий баг.
|
||||
|
||||
## Полезные подробности
|
||||
|
||||
### Тестовые данные
|
||||
|
||||
В прикладном решении обычно нужны какие-то стартовые данные: пара контрагентов, номенклатура, заведённые организации. Их кладём не в каждый тест, а один раз в подготовку стенда (`_hooks.mjs`) — после восстановления базы загружаются эталонные данные, на которых работают все тесты.
|
||||
|
||||
Если конкретному тесту нужны свои данные (например, документ, который мы будем редактировать), он создаёт их сам в начале и убирает в конце.
|
||||
|
||||
### Имена документов и уникальность
|
||||
|
||||
Тесты прогоняются многократно. Если тест создаёт документ «Накладная-Тест», следующий прогон может натолкнуться на старую запись. Решение — добавлять к имени метку времени:
|
||||
|
||||
```js
|
||||
const метка = 'Тест-' + Date.now();
|
||||
await fillFields({ 'Комментарий': метка });
|
||||
// ...
|
||||
const t = await readTable();
|
||||
assert.tableHasRow(t, r => r['Комментарий'] === метка);
|
||||
```
|
||||
|
||||
Модель это делает автоматически, но если правите тест руками — держите в голове.
|
||||
|
||||
### Видео при падении
|
||||
|
||||
Можно включить запись видео всех тестов — тогда при падении прикладывается не только скриншот, но и MP4 со всей сессией:
|
||||
|
||||
```
|
||||
> Прогони регресс с записью видео
|
||||
```
|
||||
|
||||
Размер прогона при этом растёт (на 2-3 минутах теста выходит 5-10 МБ), но при отладке сложного падения видео экономит кучу времени.
|
||||
|
||||
### Многоязычные конфигурации
|
||||
|
||||
Если у вас есть конфигурация с командами и реквизитами на нескольких языках, тесты пишутся под один язык (как правило, тот, в котором ведётся работа в проде). При смене языка интерфейса в браузере тесты не пройдут — модель видит другие подписи кнопок.
|
||||
|
||||
## Где смотреть дальше
|
||||
|
||||
- API браузера, которое вызывают тесты — [SKILL.md](../.claude/skills/web-test/SKILL.md).
|
||||
- Подробная инструкция для модели по написанию тестов (на английском, технический документ) — [.claude/skills/web-test/regress.md](../.claude/skills/web-test/regress.md).
|
||||
- Интерактивный режим без тестов — [web-test-guide.md](web-test-guide.md).
|
||||
- Запись видеоинструкций — [web-test-recording-guide.md](web-test-recording-guide.md).
|
||||
@@ -0,0 +1,978 @@
|
||||
# Регрессионное тестирование — спецификация
|
||||
|
||||
Техническое описание движка регрессионных тестов: инструмент исполняет описанные кодом пользовательские сценарии в веб-клиенте прикладного решения на платформе 1С и сверяет результат с ожиданиями.
|
||||
|
||||
Смежные документы:
|
||||
- [web-test-regression-guide.md](web-test-regression-guide.md) — пользовательский гайд с быстрым стартом.
|
||||
- [web-test-guide.md](web-test-guide.md) — справочник по browser-API (`clickElement`, `getFormState`, `readTable`, …), который используется внутри тестов.
|
||||
- [web-test-recording-guide.md](web-test-recording-guide.md) — видеозапись, озвучка, overlays.
|
||||
|
||||
---
|
||||
|
||||
## 1. Командная строка
|
||||
|
||||
```
|
||||
node run.mjs test [url] <dir|file> [флаги]
|
||||
```
|
||||
|
||||
| Флаг | По умолчанию | Описание |
|
||||
|------|-------------|----------|
|
||||
| `--tags=smoke,crud` | (все) | Фильтр тестов по тегам (пересечение) |
|
||||
| `--grep=pattern` | (все) | Фильтр тестов по имени (регулярное выражение) |
|
||||
| `--bail` | false | Остановиться при первом падении |
|
||||
| `--retry=N` | 0 | Повторить упавшие тесты N раз |
|
||||
| `--timeout=ms` | 30000 | Таймаут на тест (мс) |
|
||||
| `--report=path` | (нет) | Записать отчёт в файл (JSON или XML для `--format=junit`) |
|
||||
| `--format=fmt` | json | Формат отчёта: `json` / `allure` / `junit` |
|
||||
| `--report-dir=path` | dirname(report) / testDir | Каталог для скриншотов, видео, Allure-результатов |
|
||||
| `--screenshot=strategy` | on-failure | `on-failure` / `every-step` / `off` |
|
||||
| `--record` | false | Записывать видео для каждого теста (mp4 в `--report-dir`) |
|
||||
| `-- <hookArgs…>` | — | Всё после `--` пробрасывается в `_hooks.mjs` как `hookArgs` (см. §6.1) |
|
||||
|
||||
URL необязателен, если в каталоге тестов есть `webtest.config.mjs`. CLI URL переопределяет URL дефолтного контекста.
|
||||
|
||||
### Валидация CLI
|
||||
|
||||
- `--screenshot=<v>` принимается только `on-failure | every-step | off`; при невалидном значении движок выводит ошибку и завершается с ненулевым кодом до старта прогона.
|
||||
- `--format=<v>` принимается только `json | allure | junit`; иначе — завершение с ошибкой.
|
||||
- `--format=junit` требует `--report=<path>` (иначе некуда писать XML); иначе — завершение с ошибкой.
|
||||
|
||||
### Режим выполнения
|
||||
|
||||
1. Загружается `webtest.config.mjs` (если есть).
|
||||
2. Обнаруживаются файлы `*.test.mjs`, читается каждый, извлекаются метаданные.
|
||||
3. Применяются фильтры `--tags` / `--grep` / `only`. Параметризованные тесты разворачиваются.
|
||||
4. Запускается браузер и default-контекст (`chromium.launch()` либо `launchPersistentContext` в зависимости от `isolation`).
|
||||
5. Тесты выполняются последовательно **в алфавитном порядке относительного пути файла** (внутри файла — в порядке экспорта).
|
||||
6. Для каждого теста: лениво создаются нужные `BrowserContext`-ы (`ensureContext`), переключается активный, прогоняются хуки и тело, выполняется встроенный сброс состояния.
|
||||
7. По завершении: финальная очистка контекстов с `beforeCloseContext`-хуками, закрытие браузера, `cleanup()`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Формат тест-модуля
|
||||
|
||||
Каждый файл `*.test.mjs` — ES-модуль.
|
||||
|
||||
### Экспорты
|
||||
|
||||
| Экспорт | Тип | Обязателен | По умолчанию | Описание |
|
||||
|---------|-----|-----------|-------------|----------|
|
||||
| `name` | `string` | да | — | Читаемое имя теста |
|
||||
| `default` | `async function(ctx, param?)` | да | — | Тело теста |
|
||||
| `tags` | `string[]` | нет | `[]` | Теги для фильтрации |
|
||||
| `timeout` | `number` | нет | 30000 | Таймаут теста (мс) |
|
||||
| `skip` | `boolean \| string` | нет | false | Пропустить тест (строка = причина) |
|
||||
| `only` | `boolean` | нет | false | Запустить только этот тест (отладка) |
|
||||
| `context` | `string` | нет | defaultContext | Имя контекста из файла конфигурации |
|
||||
| `contexts` | `string[]` | нет | — | Мульти-пользовательский процессный тест |
|
||||
| `severity` | `string` | нет | — | `blocker` / `critical` / `normal` / `minor` / `trivial` |
|
||||
| `params` | `object[]` | нет | — | Параметризация (см. §13) |
|
||||
| `setup` | `async function(ctx)` | нет | — | Подготовка перед тестом |
|
||||
| `teardown` | `async function(ctx)` | нет | — | Очистка после теста (выполняется всегда) |
|
||||
|
||||
### Пример: тест с одним контекстом
|
||||
|
||||
```js
|
||||
export const name = 'CRUD справочника Контрагенты';
|
||||
export const tags = ['smoke', 'crud', 'catalog'];
|
||||
export const timeout = 45000;
|
||||
|
||||
export default async function({ navigateSection, openCommand, clickElement,
|
||||
fillFields, readTable, closeForm, getFormState, assert, step, log }) {
|
||||
|
||||
await step('Открыть список', async () => {
|
||||
await navigateSection('Склад');
|
||||
await openCommand('Контрагенты');
|
||||
});
|
||||
|
||||
await step('Создать элемент', async () => {
|
||||
await clickElement('Создать');
|
||||
await fillFields({ 'Наименование': 'Тест-' + Date.now() });
|
||||
await clickElement('Записать и закрыть');
|
||||
});
|
||||
|
||||
await step('Проверить в списке', async () => {
|
||||
const table = await readTable();
|
||||
assert.tableHasRow(table, r => r['Наименование']?.startsWith('Тест-'));
|
||||
log('Элемент найден в списке');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Пример: мульти-контекстный процессный тест
|
||||
|
||||
Рекомендация: латинский ID контекста + кириллический `displayName` в `webtest.config.mjs.contexts.<id>.displayName` (см. §7).
|
||||
|
||||
```js
|
||||
export const name = 'Согласование приходной накладной';
|
||||
export const contexts = ['clerk', 'manager'];
|
||||
export const tags = ['process'];
|
||||
|
||||
export default async function({ clerk, manager, step }) {
|
||||
await step('Кладовщик создаёт накладную', async () => {
|
||||
await clerk.navigateSection('Склад');
|
||||
await clerk.openCommand('Приходные накладные');
|
||||
await clerk.clickElement('Создать');
|
||||
await clerk.fillFields({ 'Контрагент': 'ООО Поставщик' });
|
||||
await clerk.clickElement('Записать');
|
||||
});
|
||||
|
||||
await step('Менеджер утверждает', async () => {
|
||||
await manager.navigateSection('Согласование');
|
||||
await manager.openCommand('На утверждении');
|
||||
await manager.clickElement('ООО Поставщик', { dblclick: true });
|
||||
await manager.clickElement('Утвердить');
|
||||
});
|
||||
|
||||
await step('Освобождаем контекст clerk', async () => {
|
||||
await manager.closeContext('clerk'); // освободить лицензию 1С
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Объект контекста
|
||||
|
||||
Каждая тестовая функция получает объект контекста `ctx`.
|
||||
|
||||
### API браузера (все экспорты browser.mjs)
|
||||
|
||||
Все функции обёрнуты авто-обнаружением 1С-ошибок (как в `executeScript`):
|
||||
- При модальной/всплывающей ошибке 1С: скриншот → `fetchErrorStack` → исключение с заполненным `err.onecError`.
|
||||
- Обёрнутые ACTION_FNS: `clickElement`, `fillFields`, `fillField`, `selectValue`, `fillTableRow`, `deleteTableRow`, `openCommand`, `navigateSection`, `navigateLink`, `openFile`, `closeForm`, `filterList`, `unfilterList`.
|
||||
|
||||
Полный список доступных функций (по группам, детальное описание — в [web-test-guide.md](web-test-guide.md)):
|
||||
|
||||
**Навигация:** `navigateSection`, `openCommand`, `switchTab`, `navigateLink`, `openFile`
|
||||
**Состояние:** `getFormState`, `getPageState`, `getSections`, `getCommands`
|
||||
**Таблицы:** `readTable`, `readSpreadsheet`, `fillTableRow`, `deleteTableRow`
|
||||
**Поля:** `fillFields`, `fillField`, `selectValue`
|
||||
**Действия:** `clickElement`, `closeForm`, `filterList`, `unfilterList`
|
||||
**Ошибки:** `fetchErrorStack`
|
||||
**Контексты:** `createContext`, `setActiveContext`, `closeContext`, `listContexts`, `hasContext`, `getActiveContext`
|
||||
**Запись:** `startRecording`, `stopRecording`, `isRecording`, `addNarration`, `getCaptions`
|
||||
**Презентация:** `showCaption`, `hideCaption`, `showTitleSlide`, `hideTitleSlide`, `showImage`, `hideImage`, `highlight`, `unhighlight`, `setHighlight`, `isHighlightMode`
|
||||
**Утилиты:** `screenshot`, `wait`, `getPage`, `getSession`, `readFileSync`, `writeFileSync`
|
||||
|
||||
> `dismissPendingErrors` — внутренняя функция (browser.mjs), на `ctx` не публикуется. Тест её не вызывает напрямую: она срабатывает автоматически перед каждым ACTION_FN и внутри встроенного сброса.
|
||||
|
||||
### Тестовые утилиты
|
||||
|
||||
- `step(name, fn)` — обёртка шага (см. §4)
|
||||
- `assert.*` — хелперы утверждений (см. §5)
|
||||
- `log(...args)` — добавить строку в вывод теста. Строки накапливаются в массив, склеиваются и попадают в JSON `tests[].output`. В Allure-отчёте `output` пишется в `statusDetails.trace` **только для упавших тестов**; для успешных теряется (отдельного вложения не создаётся).
|
||||
|
||||
### Метаданные теста (`ctx.testInfo`)
|
||||
|
||||
Декларативная информация о текущем тесте. Движок выставляет `ctx.testInfo` перед каждой попыткой (до `beforeEach`); хук и тело теста могут читать. Изменять не следует — объект используется самим движком при сборке отчёта.
|
||||
|
||||
```js
|
||||
ctx.testInfo = {
|
||||
name, // 'Навигация по разделам' (с подставленными params)
|
||||
file, // '01-navigation.test.mjs' (basename)
|
||||
filePath, // '01-navigation.test.mjs' (relative к testDir, разделитель '/')
|
||||
tags, // ['nav', 'smoke']
|
||||
timeout, // 60000 (ms)
|
||||
attempt, // 1..maxAttempts (1-based)
|
||||
maxAttempts, // 1 + retry
|
||||
param, // { ... } | undefined (для export const params)
|
||||
contexts: { // объект, всегда 1+ ключей; зеркалит config.contexts
|
||||
a: { url, isolation, ...customFields },
|
||||
b: { ... },
|
||||
},
|
||||
primaryContext, // 'a' — имя контекста, активного на входе в тест
|
||||
// (= t.context для single, t.contexts[0] для multi)
|
||||
}
|
||||
```
|
||||
|
||||
Доступ к специфике контекста: `testInfo.contexts[testInfo.primaryContext].displayName`. `primaryContext` — декларация теста; не зависит от текущего значения `getActiveContext()` (которое может меняться внутри теста).
|
||||
|
||||
### Результат теста в afterEach (`ctx.testResult`)
|
||||
|
||||
Только в `afterEach`. До запуска теста — `null`. После — заполняется движком перед вызовом хука:
|
||||
|
||||
```js
|
||||
ctx.testResult = {
|
||||
status, // 'passed' | 'failed'
|
||||
duration, // ms
|
||||
attempts, // фактически выполнено попыток (1..maxAttempts)
|
||||
error, // { message, step?, screenshot?, onecError? } | null
|
||||
steps, // массив step-результатов (структура — см. §4)
|
||||
}
|
||||
```
|
||||
|
||||
В итоговый JSON-отчёт (`tests[]`) добавляются ещё `name`, `file`, `tags`, `contexts`, `severity`, `start`, `stop`, `output`, `screenshot`, `video` (см. §9). В `afterEach` они недоступны — движок собирает финальную запись после хука.
|
||||
|
||||
### Мульти-контекст
|
||||
|
||||
При `export const contexts = ['a', 'b']`:
|
||||
- `ctx.a` и `ctx.b` — отдельные scoped-объекты, каждый с полным API браузера. Перед каждым вызовом scoped-обёртка переключает активный контекст через `setActiveContext`.
|
||||
- `ctx.step`, `ctx.assert`, `ctx.log`, `ctx.testInfo`, `ctx.testResult` остаются на верхнем уровне.
|
||||
|
||||
При single-context (`export const context = 'X'` или дефолт) API публикуется плоско на `ctx`.
|
||||
|
||||
---
|
||||
|
||||
## 4. step(name, fn) — обёртка шага
|
||||
|
||||
```js
|
||||
await step('Имя шага', async () => {
|
||||
// тело шага
|
||||
});
|
||||
```
|
||||
|
||||
Поведение:
|
||||
- Записывает метку `start` перед `fn()`.
|
||||
- Записывает метку `stop` после `fn()` (успех или ошибка).
|
||||
- При ошибке: устанавливает `status: 'failed'`, прикрепляет сообщение, пробрасывает исключение.
|
||||
- При успехе: устанавливает `status: 'passed'`.
|
||||
- Если стратегия скриншотов `every-step` — делает скриншот после `fn()`.
|
||||
- Вложенные шаги поддерживаются (шаг внутри шага).
|
||||
- Напрямую маппится на шаги Allure.
|
||||
|
||||
Структура данных шага (для отчётов):
|
||||
|
||||
```js
|
||||
{
|
||||
name: 'Имя шага',
|
||||
start: 1712345678000, // мс от эпохи
|
||||
stop: 1712345679200,
|
||||
status: 'passed' | 'failed',
|
||||
error: 'сообщение' | undefined,
|
||||
screenshot: 'путь' | undefined,
|
||||
steps: [] // вложенные шаги
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Утверждения (assertions)
|
||||
|
||||
Простые хелперы без зависимостей. Бросают `AssertionError` со свойствами `.message`, `.actual`, `.expected`.
|
||||
|
||||
### Общие
|
||||
|
||||
```js
|
||||
assert.ok(value, msg?) // истинность
|
||||
assert.equal(actual, expected, msg?) // ===
|
||||
assert.notEqual(actual, expected, msg?) // !==
|
||||
assert.deepEqual(actual, expected, msg?) // сравнение через JSON
|
||||
assert.includes(haystack, needle, msg?) // string/array .includes()
|
||||
assert.match(string, regex, msg?) // regex.test(string)
|
||||
await assert.throws(asyncFn, msg?) // ожидает исключение из async fn
|
||||
```
|
||||
|
||||
### Специфичные для 1С
|
||||
|
||||
```js
|
||||
assert.formHasField(state, fieldName, msg?)
|
||||
// проверяет наличие state.fields[fieldName]; в сообщении об ошибке
|
||||
// перечисляются доступные поля для быстрой диагностики
|
||||
|
||||
assert.formTitle(state, expected, msg?)
|
||||
// проверяет, что state.title содержит expected
|
||||
|
||||
assert.tableHasRow(table, predicate, msg?)
|
||||
// predicate: объект (частичное совпадение по ===) или функция row => bool
|
||||
// объект: assert.tableHasRow(table, { 'Наименование': 'Тест' })
|
||||
// функция: assert.tableHasRow(table, r => r['Сумма'] > 100)
|
||||
|
||||
assert.tableRowCount(table, expected, msg?)
|
||||
// проверяет table.rows.length === expected
|
||||
|
||||
assert.noErrors(state, msg?)
|
||||
// проверяет !state.errors
|
||||
```
|
||||
|
||||
Расширения assert API нет. Для нестандартных проверок — `throw new Error(...)` или комбинация существующих хелперов.
|
||||
|
||||
---
|
||||
|
||||
## 6. Хуки
|
||||
|
||||
Все хуки определяются в `_hooks.mjs` в корне каталога тестов.
|
||||
|
||||
### Три уровня
|
||||
|
||||
**Инфраструктурный уровень** (без браузера):
|
||||
- `prepare({ hookArgs, log, config })` — до подключения (восстановление БД, публикация, загрузка данных).
|
||||
- `cleanup({ hookArgs, log, config })` — после отключения (удаление публикации, очистка).
|
||||
|
||||
Поля параметра:
|
||||
- `hookArgs: string[]` — всё, что в командной строке передано после разделителя `--`, без интерпретации со стороны движка. Хук парсит сам (см. §6.1).
|
||||
- `log: (...args) => void` — функция логирования движка (структурированный вывод с префиксом `[hooks]`). Использовать вместо `console.log`, чтобы не ломать формат отчёта.
|
||||
- `config: object` — разобранный `webtest.config.mjs` (URL контекстов, режим изоляции, правила severity и т.д.).
|
||||
|
||||
**Тестовый уровень** (с контекстом браузера):
|
||||
- `beforeAll(ctx)` — после подключения, перед первым тестом.
|
||||
- `afterAll(ctx)` — после последнего теста, до отключения.
|
||||
- `beforeEach(ctx)` — перед каждым тестом. На входе уже доступен `ctx.testInfo` (см. §3).
|
||||
- `afterEach(ctx)` — после каждого теста. Дополнительно доступен `ctx.testResult` с результатом завершившегося теста.
|
||||
|
||||
**Контекстный уровень** (на каждый browser-контекст, жизненный цикл = создан → удалён):
|
||||
- `afterOpenContext(ctx, name, spec)` — сразу после успешного `createContext`. `spec` — запись из `config.contexts[name]` со всеми пользовательскими полями (`displayName`, `url`, `isolation`, …). Полезно: вставка постоянного DOM-оверлея/бейджа, предварительная навигация в контексте, регистрация телеметрии.
|
||||
- `beforeCloseContext(ctx, name, spec)` — перед `closeContext` (контекст ещё активен и работает). Полезно: сохранение остатков буферов, сбор метрик, последний скриншот. Срабатывает и при явном `ctx.closeContext(name)` из теста, и в финальной очистке движка перед `disconnect`.
|
||||
|
||||
`closeContext(name)` валиден только когда `name !== getActiveContext()` — иначе бросается исключение. В scoped API (`ctx.a.closeContext('b')`) это естественно: scoped-обёртка сначала вызывает `setActiveContext('a')`, потом закрывает `'b'` — целевой контекст всегда неактивен.
|
||||
|
||||
### Подавление ошибок в хуках
|
||||
|
||||
Ошибки в `afterEach`, `teardown`, `afterAll` и `cleanup` ловятся и логируются движком, но не прерывают прогон и не помечают тест/прогон как failed. Логика: пост-хуки очистки должны быть устойчивы к собственным сбоям, чтобы один сломанный `teardown` не приводил к падению остальных тестов по цепочке. Если в этих хуках произошла фатальная для регресса проблема — бросайте отдельный `Error` в `beforeAll`/`beforeEach`, чтобы он прервал прогон, либо проверяйте состояние в самом тесте.
|
||||
|
||||
### Порядок выполнения
|
||||
|
||||
```
|
||||
prepare() // без браузера (восстановление БД, публикация)
|
||||
browser.launch() // запуск процесса браузера
|
||||
createContext(default) // первый контекст создан
|
||||
afterOpenContext(ctx, default) // hook: контекст готов
|
||||
beforeAll(ctx) // браузер готов, default-контекст создан
|
||||
[lazy ensureContext(name)] // для multi-context тестов
|
||||
afterOpenContext(ctx, name)
|
||||
beforeEach(ctx)
|
||||
test.setup(ctx) // подготовка теста
|
||||
test.default(ctx) // тело теста (может вызвать ctx.closeContext)
|
||||
[при ctx.closeContext(x)]: beforeCloseContext(ctx, x) → close(x)
|
||||
test.teardown(ctx) // очистка теста (всегда)
|
||||
afterEach(ctx) // всегда
|
||||
[встроенный сброс] // всегда (для каждого живого контекста теста)
|
||||
…следующий тест…
|
||||
afterAll(ctx)
|
||||
[для каждого оставшегося контекста]: beforeCloseContext(ctx, name, spec)
|
||||
browser.close() // финальный disconnect (без явных closeContext —
|
||||
// контексты умирают вместе с браузером)
|
||||
cleanup() // без браузера (удаление публикации)
|
||||
```
|
||||
|
||||
### Встроенный сброс состояния
|
||||
|
||||
После каждого теста (после `afterEach`) движок гарантирует чистое состояние:
|
||||
|
||||
```js
|
||||
async function resetState(ctx) {
|
||||
try { await ctx.dismissPendingErrors(); } catch {} // no-op на ctx (не экспортируется);
|
||||
// внутренний dismiss всё равно отработает
|
||||
// через ACTION_FN-обёртки ниже
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const state = await ctx.getFormState();
|
||||
if (state.form == null) break; // важно: == null, не !state.form —
|
||||
// form может быть 0 (валидный idx фоновой формы)
|
||||
try { await ctx.closeForm({ save: false }); } catch { break; }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Гарантирует, что каждый тест стартует с чистого рабочего стола, независимо от того, как завершился предыдущий (падение, таймаут, ошибка утверждения). Реимплементировать это в пользовательском `afterEach` не нужно.
|
||||
|
||||
### Пример _hooks.mjs
|
||||
|
||||
```js
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export async function prepare({ hookArgs, log, config }) {
|
||||
const force = hookArgs.includes('--rebuild-stand');
|
||||
const dataArg = hookArgs.find(a => a.startsWith('--data='))?.slice('--data='.length);
|
||||
log('preparing stand, force=', force, 'data=', dataArg);
|
||||
execSync('powershell.exe -File scripts/restore-db.ps1');
|
||||
execSync('powershell.exe -File scripts/publish.ps1');
|
||||
}
|
||||
|
||||
export async function cleanup({ log }) {
|
||||
log('cleaning up stand');
|
||||
execSync('powershell.exe -File scripts/unpublish.ps1');
|
||||
}
|
||||
|
||||
export async function beforeAll(ctx) {
|
||||
// По умолчанию 1С после входа уже показывает дефолтную секцию — навигация
|
||||
// в beforeAll обычно не нужна. Хук удобен для общего setup'а который
|
||||
// должен случиться один раз для всего прогона.
|
||||
}
|
||||
|
||||
export async function afterEach(ctx) {
|
||||
// Доступен ctx.testResult — { status, duration, attempts, error, steps }.
|
||||
// Встроенный сброс состояния выполняется ПОСЛЕ afterEach автоматически.
|
||||
}
|
||||
|
||||
export async function afterOpenContext(ctx, name, spec) {
|
||||
// Удобно для persistent DOM-overlay'я с displayName (видно в видео,
|
||||
// какая вкладка к какому пользователю относится).
|
||||
}
|
||||
|
||||
export async function beforeCloseContext(ctx, name, spec) {
|
||||
// Срабатывает и при ctx.closeContext из теста, и в финальной очистке.
|
||||
}
|
||||
```
|
||||
|
||||
### 6.1. Проброс пользовательских флагов через `--`
|
||||
|
||||
Движок не знает о пользовательских флагах хуков. Чтобы хуки получили разовые параметры без правки `webtest.config.mjs` или окружения, используется стандартная shell-конвенция `--` (как у `npm`, `cargo`, `pytest`): всё, что идёт после `--` в CLI движка, передаётся в `prepare` / `cleanup` через поле `hookArgs: string[]` без интерпретации.
|
||||
|
||||
```
|
||||
node run.mjs test tests/myapp/ --bail -- --rebuild-stand --reload-data
|
||||
└─ runner ─┘ └────── hookArgs ────────┘
|
||||
```
|
||||
|
||||
В этом примере движок получает `--bail`, а `hookArgs` хуков становится `['--rebuild-stand', '--reload-data']`. Парсинг этого массива — ответственность хуков.
|
||||
|
||||
Если разделитель `--` не указан, `hookArgs` — пустой массив. Это позволяет движку и хукам развиваться независимо: новый встроенный флаг движка никогда не пересечётся с пользовательским.
|
||||
|
||||
---
|
||||
|
||||
## 7. Файл конфигурации
|
||||
|
||||
`webtest.config.mjs` в корне каталога тестов. Необязателен — если отсутствует, URL должен быть передан через CLI.
|
||||
|
||||
```js
|
||||
export default {
|
||||
// Контексты: именованные URL для разных пользователей/ролей.
|
||||
// Рекомендация: латинский ID контекста (`clerk`, `manager`) + кириллический
|
||||
// `displayName` для UI/слайдов. Любые пользовательские поля пробрасываются как есть
|
||||
// и доступны хукам через `ctx.testInfo.contexts[name]` (см. §3).
|
||||
contexts: {
|
||||
clerk: { url: 'http://localhost/app-clerk/ru_RU', displayName: 'Кладовщик' },
|
||||
manager: { url: 'http://localhost/app-manager/ru_RU', displayName: 'Менеджер' },
|
||||
admin: { url: 'http://localhost/app-admin/ru_RU', displayName: 'Админ' },
|
||||
},
|
||||
defaultContext: 'clerk',
|
||||
|
||||
// Значения по умолчанию (переопределяются флагами CLI)
|
||||
timeout: 30000,
|
||||
retries: 0,
|
||||
screenshot: 'on-failure', // 'every-step' | 'off'
|
||||
record: false,
|
||||
|
||||
// Дефолтный тег-фильтр. Применяется только если CLI не передал --tags.
|
||||
// Удобно для сценариев «прогон по умолчанию = smoke», при этом --tags=full
|
||||
// (или --tags=) с CLI прозрачно перекрывает.
|
||||
tags: ['smoke'],
|
||||
|
||||
// Дефолтный режим изоляции для контекстов, которые сами его не указали
|
||||
// (config.contexts.<name>.isolation). См. §8.
|
||||
isolation: 'tab', // 'tab' | 'window'
|
||||
|
||||
// Allure severity policy (опционально). Маппинг наоборот: уровень → [теги].
|
||||
// Резолв см. §9 «Авто-эмиссия label-ов».
|
||||
severity: {
|
||||
critical: ['smoke', 'multi-context'],
|
||||
minor: ['recording'],
|
||||
// blocker / trivial — необязательны, можно опустить
|
||||
},
|
||||
defaultSeverity: 'normal',
|
||||
};
|
||||
```
|
||||
|
||||
**Упрощённая форма** (один контекст, без именованных):
|
||||
|
||||
```js
|
||||
export default {
|
||||
url: 'http://localhost/app/ru_RU',
|
||||
timeout: 30000,
|
||||
};
|
||||
```
|
||||
|
||||
### Валидация файла конфигурации
|
||||
|
||||
`severity` валидируется при загрузке:
|
||||
- ключи — только из `blocker | critical | normal | minor | trivial`;
|
||||
- значение каждого ключа — массив тегов;
|
||||
- тег не может одновременно состоять в двух уровнях severity (явная ошибка с указанием конфликта);
|
||||
- `defaultSeverity` — из стандартного набора.
|
||||
|
||||
При нарушении любого правила движок выводит сообщение с указанием конфликта и завершается с ненулевым кодом до запуска тестов.
|
||||
|
||||
Кириллица в ID контекстов работает, но смешанный регистр снижает читаемость кода (`testInfo.contexts.кладовщик.displayName` рядом с `testInfo.contexts.clerk.displayName`). Рекомендуем разделять технический ID и человекочитаемое имя.
|
||||
|
||||
Флаги CLI всегда переопределяют значения из файла конфигурации.
|
||||
|
||||
---
|
||||
|
||||
## 8. Контексты
|
||||
|
||||
### Механизм: Playwright BrowserContext
|
||||
|
||||
Один процесс браузера (`chromium.launch()`), несколько изолированных контекстов. Каждый контекст — отдельная сессия (куки, авторизация, состояние страницы).
|
||||
|
||||
```
|
||||
browser (один процесс chromium)
|
||||
├─ BrowserContext "кладовщик" → page → http://localhost/app-clerk/ru_RU
|
||||
├─ BrowserContext "менеджер" → page → http://localhost/app-mgr/ru_RU
|
||||
└─ BrowserContext "админ" → page → http://localhost/app-admin/ru_RU
|
||||
```
|
||||
|
||||
Преимущества:
|
||||
- **Мгновенное переключение** между пользователями (смена активного `page`).
|
||||
- **Состояние сохраняется** — переключились на менеджера и обратно, у кладовщика все формы остались открытыми.
|
||||
- **Нет переподключений** — каждая сессия живёт независимо.
|
||||
- **Один процесс** — экономия ресурсов по сравнению с несколькими браузерами.
|
||||
|
||||
### Одиночный контекст (по умолчанию)
|
||||
|
||||
Большинство тестов. Один BrowserContext, один пользователь. Тест получает плоский `ctx` со всем API.
|
||||
|
||||
```js
|
||||
export const context = 'manager'; // необязательно, иначе defaultContext
|
||||
export default async function({ clickElement, fillFields, … }) { }
|
||||
```
|
||||
|
||||
### Порядок выполнения и переключение контекста
|
||||
|
||||
Движок НЕ группирует тесты по контексту. Порядок выполнения — алфавитный по полному относительному пути файла (плюс порядок экспорта внутри файла). Для каждого теста:
|
||||
|
||||
1. Через `ensureContext(name)` создаются BrowserContext-ы, упомянутые в `t.context` / `t.contexts` (если ещё не созданы).
|
||||
2. `setActiveContext(primaryContext)` — активный контекст = первый объявленный (для single — `t.context || defaultContext`, для multi — `t.contexts[0]`).
|
||||
3. После теста встроенный сброс пробегает по всем использованным контекстам.
|
||||
|
||||
Контексты живут между тестами: переключение через `setActiveContext` — дешёвое, повторный вход в 1С не требуется. Закрываются явно (`closeContext`) или финальной очисткой движка перед закрытием браузера.
|
||||
|
||||
### Мульти-контекст (процессные тесты)
|
||||
|
||||
```js
|
||||
export const contexts = ['clerk', 'manager'];
|
||||
export default async function({ clerk, manager, step, assert }) { … }
|
||||
```
|
||||
|
||||
Каждый именованный контекст — полноценный scoped-объект API со своим `page`. Тест оркестрирует переключение между пользователями. Состояние каждого пользователя сохраняется между переключениями:
|
||||
|
||||
```js
|
||||
await step('Кладовщик создаёт документ', async () => {
|
||||
await clerk.openCommand('Приходные накладные');
|
||||
await clerk.clickElement('Создать');
|
||||
await clerk.fillFields({ 'Контрагент': 'ООО Поставщик' });
|
||||
await clerk.clickElement('Записать');
|
||||
// кладовщик стоит на форме документа
|
||||
});
|
||||
|
||||
await step('Менеджер утверждает', async () => {
|
||||
await manager.navigateSection('Согласование');
|
||||
await manager.clickElement('Утвердить');
|
||||
});
|
||||
|
||||
await step('Кладовщик проверяет статус', async () => {
|
||||
// страница кладовщика ТА ЖЕ — форма открыта, навигация не нужна
|
||||
const state = await clerk.getFormState();
|
||||
assert.equal(state.fields['Статус']?.value, 'Утверждён');
|
||||
});
|
||||
```
|
||||
|
||||
### Публичный контекстный API
|
||||
|
||||
| Метод | Назначение |
|
||||
|-------|-----------|
|
||||
| `createContext(name, url, { isolation, extensionPath })` | Создаёт BrowserContext и переходит по URL. |
|
||||
| `setActiveContext(name)` | Переключает активный слот; при активной записи дописывает последние кадры старой страницы и переподключает screencast. |
|
||||
| `closeContext(name)` | Выход из 1С + закрытие (`page` для `tab`, `BrowserContext` для `window`), удаляет из реестра. Бросает исключение, если `name === active`. |
|
||||
| `listContexts()` / `hasContext(name)` / `getActiveContext()` | Только для чтения. |
|
||||
|
||||
### Режимы изоляции
|
||||
|
||||
Поле `isolation` задаётся в двух местах:
|
||||
|
||||
- **На уровне контекста:** `config.contexts.<name>.isolation` — приоритет 1.
|
||||
- **На уровне файла конфигурации:** `config.isolation` — применяется к контекстам, у которых своего значения нет. По умолчанию `'tab'`.
|
||||
|
||||
| Режим | Реализация | Окна | Cookies | 1С-расширение |
|
||||
|-------|-----------|------|---------|---------------|
|
||||
| `'tab'` (default) | `launchPersistentContext` + `newPage()` per context | 1 окно, N вкладок | общие по path | загружается надёжно |
|
||||
| `'window'` | `chromium.launch()` + `newContext()` per context | N окон | полная изоляция | может не загружаться |
|
||||
|
||||
Смешивать режимы в одном прогоне нельзя — `createContext` бросает явную ошибку. То есть `config.isolation` фактически становится режимом всего прогона, если хотя бы один контекст явно не переопределил его на тот же режим.
|
||||
|
||||
### Закрытие неактивных контекстов
|
||||
|
||||
`closeContext(name)` нельзя вызвать на активном контексте — будет исключение. В scoped API это естественно: вызывать `manager.closeContext('clerk')` (scoped-обёртка сначала переключает активный на `manager`, потом закрывает `clerk`). Если контекст лишний (роль больше не нужна в рамках теста / прогона) — закрывайте его сразу: освобождает лицензию платформы и снимает нагрузку со следующих тестов.
|
||||
|
||||
---
|
||||
|
||||
## 9. Отчёты
|
||||
|
||||
### JSON (нативный, по умолчанию)
|
||||
|
||||
```json
|
||||
{
|
||||
"runner": "web-test",
|
||||
"url": "http://localhost/app/ru_RU",
|
||||
"startedAt": "2026-04-05T10:00:00.000Z",
|
||||
"finishedAt": "2026-04-05T10:05:30.000Z",
|
||||
"duration": 330.0,
|
||||
"summary": {
|
||||
"total": 25,
|
||||
"passed": 23,
|
||||
"failed": 1,
|
||||
"skipped": 1
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"name": "CRUD справочника Контрагенты",
|
||||
"file": "02-catalog-crud.test.mjs",
|
||||
"tags": ["smoke", "crud"],
|
||||
"contexts": ["clerk"],
|
||||
"severity": "critical",
|
||||
"status": "passed",
|
||||
"start": 1712345678000,
|
||||
"stop": 1712345690300,
|
||||
"duration": 12.3,
|
||||
"attempts": 1,
|
||||
"steps": [
|
||||
{
|
||||
"name": "Открыть список",
|
||||
"start": 1712345678000,
|
||||
"stop": 1712345679200,
|
||||
"status": "passed",
|
||||
"steps": []
|
||||
}
|
||||
],
|
||||
"output": "Элемент найден в списке",
|
||||
"error": null,
|
||||
"screenshot": null,
|
||||
"video": null
|
||||
},
|
||||
{
|
||||
"name": "Обязательное поле",
|
||||
"file": "10-validation.test.mjs",
|
||||
"tags": ["validation"],
|
||||
"contexts": ["clerk"],
|
||||
"status": "failed",
|
||||
"duration": 8.1,
|
||||
"attempts": 2,
|
||||
"steps": [
|
||||
{
|
||||
"name": "Сохранить пустую форму",
|
||||
"start": 1712345700000,
|
||||
"stop": 1712345708100,
|
||||
"status": "failed",
|
||||
"error": "Ожидалось модальное окно ошибки, но форма сохранилась"
|
||||
}
|
||||
],
|
||||
"output": "",
|
||||
"error": {
|
||||
"message": "Ожидалось модальное окно ошибки, но форма сохранилась",
|
||||
"step": "Сохранить пустую форму",
|
||||
"screenshot": "error-shot-10.png"
|
||||
},
|
||||
"screenshot": "error-shot-10.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Allure (`--format=allure --report-dir=allure-results/`)
|
||||
|
||||
Отдельные JSON-файлы для каждого теста в каталоге `allure-results/`:
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "сгенерированный-uuid",
|
||||
"name": "CRUD справочника",
|
||||
"fullName": "02-catalog-crud.test.mjs",
|
||||
"status": "passed",
|
||||
"stage": "finished",
|
||||
"start": 1712345678000,
|
||||
"stop": 1712345690300,
|
||||
"labels": [
|
||||
{ "name": "tag", "value": "smoke" },
|
||||
{ "name": "tag", "value": "crud" },
|
||||
{ "name": "suite", "value": "root" },
|
||||
{ "name": "severity", "value": "critical" }
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"name": "Открыть список",
|
||||
"status": "passed",
|
||||
"start": 1712345678000,
|
||||
"stop": 1712345679200,
|
||||
"steps": []
|
||||
}
|
||||
],
|
||||
"attachments": [
|
||||
{
|
||||
"name": "Скриншот при падении",
|
||||
"source": "uuid-attachment.png",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Скриншоты/видео копируются в `allure-results/` с уникальными именами.
|
||||
|
||||
#### Авто-эмиссия меток
|
||||
|
||||
Движок всегда заполняет следующие метки (`labels`):
|
||||
|
||||
- **`tag`** — по одному на каждый элемент `mod.tags[]`. Готовая фильтрация в Allure-отчёте без дополнительной разметки.
|
||||
- **`suite`** — `dirname(t.filePath)`. Тесты в корне `testDir` идут под `'root'`, тесты в подкаталоге `sales/` — под `'sales'`. Это даёт левую группировку отчёта без ручной разметки.
|
||||
- **`severity`** — резолв в порядке приоритета:
|
||||
1. `export const severity = 'critical'` в самом тесте, **если значение валидное** (одно из `blocker | critical | normal | minor | trivial`). Если экспорт задан, но значение невалидное — пункт пропускается и идём в (3); резолв через теги (пункт 2) при этом **не выполняется** (хотел бы автор иначе — он бы не объявлял `severity`).
|
||||
2. Иначе **максимальный ранг** среди тегов теста (стандартные имена `blocker | critical | normal | minor | trivial` напрямую, либо через `config.severity`-маппинг).
|
||||
3. Иначе `config.defaultSeverity` или `'normal'`.
|
||||
|
||||
Ранги: `blocker(5) > critical(4) > normal(3) > minor(2) > trivial(1)`. Выбор по максимуму не зависит от порядка тегов в `mod.tags`.
|
||||
|
||||
Пример: `tags: ['smoke', 'recording']` + `severity: { critical: ['smoke'], minor: ['recording'] }` → severity = `critical` (5 > 2).
|
||||
|
||||
#### Доп. файлы Allure через `<testDir>/_allure/`
|
||||
|
||||
Движок ищет каталог `_allure/` рядом с тестами и копирует все его файлы в `reportDir` перед генерацией отчёта. Конвенция для статичной настройки Allure, для которой нет места внутри JSON-файла теста:
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|-----------|
|
||||
| `categories.json` | Классификация падений по regex (группировка failed-тестов в виджете Categories — «timeout», «license-flake», «1C modal» и т.п.). |
|
||||
| `environment.properties` | `key=value` строки в виджет Environment (URL, версия 1С, ветка git, номер сборки). Часто формируется динамически из `prepare()`. |
|
||||
| `executor.json` | CI/CD-метаданные (Jenkins URL, GitHub run-id и т.п.). |
|
||||
|
||||
Подчёркивание в имени — параллель `_hooks.mjs` (инфраструктура, не тест). Сборщик тестов пропускает каталог `_allure/` по общему правилу (`startsWith('_')`). Если каталога нет — ничего не происходит, отчёт собирается обычным образом.
|
||||
|
||||
Пример `categories.json` (минимальный):
|
||||
```json
|
||||
[
|
||||
{ "name": "Timeout", "messageRegex": "Timeout \\(\\d+ms\\)" },
|
||||
{ "name": "Assertion", "messageRegex": "(Expected|AssertionError).*" }
|
||||
]
|
||||
```
|
||||
|
||||
### JUnit XML (`--format=junit`)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites name="web-test" tests="25" failures="1" skipped="1" time="330.0">
|
||||
<testsuite name="tests/myapp" tests="25" failures="1" skipped="1">
|
||||
<testcase name="CRUD справочника" classname="02-catalog-crud.test.mjs" time="12.3"/>
|
||||
<testcase name="Обязательное поле" classname="10-validation.test.mjs" time="8.1">
|
||||
<failure message="Ожидалось модальное окно ошибки, но форма сохранилась">
|
||||
Стек вызовов…
|
||||
</failure>
|
||||
<system-out>Скриншот: error-shot-10.png</system-out>
|
||||
</testcase>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Консольный вывод
|
||||
|
||||
```
|
||||
web-test — http://localhost/app/ru_RU
|
||||
Запуск 25 тестов из tests/myapp/
|
||||
|
||||
✓ Навигация по разделам (2.1s)
|
||||
✓ CRUD справочника Контрагенты (12.3s)
|
||||
├ Открыть список (1.2s)
|
||||
├ Создать элемент (8.0s)
|
||||
└ Проверить в списке (3.1s)
|
||||
✗ Обязательное поле (8.1s)
|
||||
├ Открыть форму (2.0s)
|
||||
└ ✗ Сохранить пустую форму (6.1s)
|
||||
Ожидалось модальное окно ошибки, но форма сохранилась
|
||||
скриншот: error-shot-10.png
|
||||
○ Составной тип (skip: не реализовано)
|
||||
|
||||
23 passed, 1 failed, 1 skipped (2m 0.5s)
|
||||
```
|
||||
|
||||
Для passed-тестов выводится одна строка `✓ name (duration)`. Шаги печатаются только для упавших — после строки `✗`, с отступом, плюс сообщение ошибки и путь к скриншоту. Полная картина по шагам — в JSON-отчёте (`--report=…`).
|
||||
|
||||
---
|
||||
|
||||
## 11. Скриншоты и видео
|
||||
|
||||
### Стратегия скриншотов
|
||||
|
||||
| Стратегия | Поведение |
|
||||
|-----------|----------|
|
||||
| `on-failure` (по умолчанию) | Скриншот при падении теста, прикрепляется к ошибке. |
|
||||
| `every-step` | Скриншот в конце каждого `step()`, плюс при падении. |
|
||||
| `off` | Без автоматических скриншотов. |
|
||||
|
||||
Скриншоты сохраняются в каталог отчёта по шаблону `{индекс-теста}-{имя-шага}.png`. В JSON-отчёте — путь относительно каталога отчёта.
|
||||
|
||||
### Видеозапись
|
||||
|
||||
При включённом `--record`:
|
||||
- `startRecording()` перед каждым тестом.
|
||||
- `stopRecording()` после каждого теста.
|
||||
- Видео сохраняется как `{индекс-теста}-{имя-теста}.mp4`.
|
||||
- Прикрепляется к отчёту (Allure: вложение видео).
|
||||
|
||||
Подробности по записи (overlays, captions, narration) — см. [web-test-recording-guide.md](web-test-recording-guide.md).
|
||||
|
||||
---
|
||||
|
||||
## 12. Сброс состояния
|
||||
|
||||
Встроенный механизм, выполняется после `afterEach` (и `teardown`) каждого теста. Псевдокод и условие выхода — в §6 «Встроенный сброс состояния».
|
||||
|
||||
Для мульти-контекстных тестов сброс пробегает по всем живым контекстам, использованным тестом.
|
||||
|
||||
Гарантирует, что каждый тест стартует с чистого рабочего стола, независимо от того, как завершился предыдущий (падение, таймаут, ошибка утверждения).
|
||||
|
||||
---
|
||||
|
||||
## 13. Параметризация
|
||||
|
||||
```js
|
||||
export const name = 'Заполнение поля {type}';
|
||||
export const params = [
|
||||
{ type: 'String', field: 'Наименование', value: 'Тест' },
|
||||
{ type: 'Number', field: 'Цена', value: '100.50' },
|
||||
{ type: 'Date', field: 'ДатаПоступления', value: '01.01.2024' },
|
||||
{ type: 'Boolean',field: 'Активен', value: true },
|
||||
];
|
||||
|
||||
export default async function({ fillFields, getFormState, assert }, { type, field, value }) {
|
||||
await fillFields({ [field]: value });
|
||||
const state = await getFormState();
|
||||
assert.equal(state.fields[field]?.value, String(value));
|
||||
}
|
||||
```
|
||||
|
||||
Параметры разворачиваются в отдельные тесты на этапе discovery:
|
||||
- Имя теста формируется подстановкой через шаблон `{key}` в `mod.name`; если шаблона нет — суффикс `[index]`.
|
||||
- Тест получает `param` вторым аргументом (`default(ctx, param)`).
|
||||
- В отчётах каждый набор — отдельная запись со своим `name` и `param` в `testInfo`.
|
||||
- `ctx.testInfo.param` доступен в теле теста и хуках.
|
||||
|
||||
---
|
||||
|
||||
## 14. Обнаружение тестов
|
||||
|
||||
`testDir` (первый позиционный аргумент после URL) — каталог, в котором живут тесты. Сборщик рекурсивно обходит дерево и собирает файлы по правилам ниже.
|
||||
|
||||
```
|
||||
tests/myapp/
|
||||
_hooks.mjs # пропускается (префикс '_')
|
||||
_allure/ # пропускается (префикс '_')
|
||||
webtest.config.mjs # пропускается (не *.test.mjs)
|
||||
sales/
|
||||
01-order-create.test.mjs
|
||||
02-order-post.test.mjs
|
||||
warehouse/
|
||||
01-receipt.test.mjs
|
||||
```
|
||||
|
||||
### Правила
|
||||
|
||||
| Аспект | Поведение |
|
||||
|--------|-----------|
|
||||
| Обход | Рекурсивный; файлы и каталоги, имя которых начинается на `_` или `.`, пропускаются |
|
||||
| Шаблон имени | Только `*.test.mjs` |
|
||||
| Порядок | Сортировка по полному относительному пути (`sales/01` идёт до `warehouse/01`) |
|
||||
| `file` в отчёте | `relative(testDir, file)` с разделителем `/`, например `sales/01-order-create.test.mjs` |
|
||||
| Фильтр по пути с CLI | `node run.mjs test tests/myapp/sales/` запустит только подкаталог |
|
||||
| Конкретный файл | `node run.mjs test tests/myapp/sales/01-order-create.test.mjs` |
|
||||
|
||||
### Чего НЕТ (сознательное упрощение)
|
||||
|
||||
- **`_hooks.mjs` на уровне подкаталога.** Движок ищет `_hooks.mjs` только в корне `testDir`. Подкаталоги свои хуки не получают.
|
||||
- **`webtest.config.mjs` на уровне подкаталога.** Тоже только в корне.
|
||||
- **Многоуровневой Suite-разметки из дерева каталогов.** Allure-метка `suite` строится только по первому уровню (`dirname(filePath)`); более глубокую группировку делайте через `tags`.
|
||||
- **Контекста по умолчанию на уровне подкаталога.** Каждый тест объявляет `context` / `contexts` сам; от пути контексты не наследуются.
|
||||
|
||||
### Конвенции
|
||||
|
||||
1. **Папки — для организации**, не для механики. Общая подготовка — в глобальном `_hooks.mjs.beforeAll` или в `setup` / `teardown` конкретного теста.
|
||||
2. **Группировку в отчётах** делайте через `tags: ['sales']`, не через путь. Это даёт фильтрацию (`--tags=sales`) и работает в Allure/JUnit без дополнительной разметки.
|
||||
3. **«Запустить только sales»** — двумя путями: `tests/myapp/sales/` (по каталогу) или `--tags=sales` (по тегу).
|
||||
4. **Сортировка по полному пути** означает, что `warehouse/01-x` запустится ПОСЛЕ `sales/02-y`. Для строгого глобального порядка используйте 3-значные префиксы (`010-`/`020-`/…) либо явные теги-фазы.
|
||||
|
||||
---
|
||||
|
||||
## 15. Ошибки и трассировка
|
||||
|
||||
### Авто-обнаружение 1С-ошибок
|
||||
|
||||
Все ACTION_FNS (`clickElement`, `fillFields`, `fillField`, `selectValue`, `fillTableRow`, `deleteTableRow`, `openCommand`, `navigateSection`, `navigateLink`, `openFile`, `closeForm`, `filterList`, `unfilterList`) обёрнуты. После каждого вызова:
|
||||
|
||||
1. Проверяется `state.errors.modal` / `balloon`.
|
||||
2. Если есть — делается скриншот (до того, как `fetchErrorStack` закроет модалку).
|
||||
3. Для модальных ошибок вызывается `fetchErrorStack` (две стратегии — Path 1 для платформенных исключений с кнопкой «Открыть отчёт», Path 2 для `ВызватьИсключение` через гамбургер-меню → О программе → Информация для тех. поддержки; см. [web-test-guide.md](web-test-guide.md)).
|
||||
4. Бросается исключение со структурированным `err.onecError`:
|
||||
```js
|
||||
err.onecError = {
|
||||
step, // имя действия (например 'clickElement')
|
||||
args, // аргументы, с которыми вызывалось
|
||||
errors, // { modal?, balloon? }
|
||||
formState, // снапшот getFormState
|
||||
stack, // { raw, entries: [{ location, code }], timestamp } | null
|
||||
screenshot, // путь к скриншоту
|
||||
};
|
||||
```
|
||||
|
||||
В отчёте это превращается в `error.onecError.stack` для упавшего теста. Разбор причин падения и категории — см. §16.
|
||||
|
||||
### Платформенные модальные диалоги
|
||||
|
||||
`getFormState()` возвращает `platformDialogs` — массив платформенных диалогов (About, Support Info, Error Report). `closeForm()` закрывает их. `dismissPendingErrors()` чистит ожидающие модалки автоматически (вызывается перед каждым ACTION_FN, плюс в встроенном сбросе после теста).
|
||||
|
||||
Модальное окно платформенной ошибки сначала рендерится в переходном состоянии (~1 с), затем перерисовывается в стабильное. `fetchErrorStack` ждёт 1.5 с и перепроверяет `hasReport` перед выбором стратегии.
|
||||
|
||||
### Таймауты
|
||||
|
||||
- Глобальный таймаут теста: `mod.timeout` или `config.timeout` или CLI `--timeout=ms`.
|
||||
- Таймаут срабатывает на уровне теста (`testFn()` + `setup` + `teardown`), не на уровне отдельного `step` или action.
|
||||
- При таймауте: текущий step помечается failed, бросается ошибка с сообщением `Timeout (<N>ms)`, далее запускается `afterEach` и встроенный сброс.
|
||||
|
||||
### Повторы
|
||||
|
||||
При `--retry=N` (или `config.retries`) упавший тест повторяется до `1 + N` раз. Для каждой попытки:
|
||||
- `beforeEach` / `setup` / `default` / `teardown` / `afterEach` + встроенный сброс выполняются заново.
|
||||
- `ctx.testInfo.attempt` инкрементируется.
|
||||
- В отчёте фиксируется `attempts` — фактически выполнено попыток.
|
||||
- Считается passed, если последняя попытка зелёная; иначе failed.
|
||||
|
||||
`beforeAll` / `afterAll` / `prepare` / `cleanup` / `afterOpenContext` / `beforeCloseContext` не повторяются (это жизненный цикл всего прогона или контекста, не теста).
|
||||
|
||||
---
|
||||
|
||||
## 16. Анализ результатов
|
||||
|
||||
### Что лежит в записи об упавшем тесте
|
||||
|
||||
JSON-отчёт (`tests[]`, полная структура — §9) для каждого падения содержит:
|
||||
|
||||
- `error.message` — текст исключения.
|
||||
- `error.step` — имя шага, на котором упало.
|
||||
- `error.screenshot` — путь к скриншоту падения (если стратегия скриншотов не `off`).
|
||||
- `error.onecError` (только для 1С-исключений) — структура с полями: `step` (имя действия, например `clickElement`), `args` (аргументы вызова), `errors` (модальное окно или balloon), `formState` (снимок формы на момент ошибки), `stack` — платформенный стек вызовов 1С с `entries[{location, code}]`.
|
||||
- `steps[]` — пошаговая разбивка с метками времени, у каждого шага свой `status` и `error`.
|
||||
|
||||
В Allure-отчёте те же данные лежат в `statusDetails` (текст ошибки и трассировка), скриншоты и видео — во вложениях, автоматическая группировка по причинам — через `categories.json` (§9).
|
||||
|
||||
### Типовые причины падений
|
||||
|
||||
Большинство падений на 1С-стенде сводится к трём причинам, и их полезно различать при разборе отчёта:
|
||||
|
||||
- **Ошибка в тесте** — селектор не нашёл элемент, ожидание не сошлось, гонка без точки синхронизации. Признаки: падение стабильно повторяется на одном и том же шаге; после правки теста воспроизводимость исчезает. Действие — изменить тест.
|
||||
- **Ошибка в прикладном решении** — реально воспроизведённое некорректное поведение конфигурации. Признаки: упал шаг, имитирующий пользовательскую операцию; в `error.onecError.stack` есть платформенный стек вызовов 1С с указанием на код решения. Действие — передать разработчику конфигурации, тест править не нужно.
|
||||
- **Сбой стенда** — таймаут Apache, форма входа не загрузилась, не хватило веб-лицензий. Признаки: падение на навигации или входе; от прогона к прогону падает «то одно, то другое», без связи с содержанием теста. Действие — править инфраструктуру (`prepare()`, очистка сессий, идемпотентность хуков), не тесты.
|
||||
|
||||
`categories.json` Allure (§9) удобно настраивать именно под эти три категории — regex по `error.message` уже даёт первичную классификацию в виджете Categories.
|
||||
|
||||
---
|
||||
|
||||
## 17. Глоссарий
|
||||
|
||||
| Термин | Определение |
|
||||
|--------|-------------|
|
||||
| **testDir** | Каталог тестов, переданный позиционным аргументом движку. Корень для discovery, `_hooks.mjs`, `webtest.config.mjs`, `_allure/`. |
|
||||
| **Context (BrowserContext)** | Изолированная сессия Playwright. Куки/состояние/страница независимы. В рамках одного теста используется один или несколько контекстов. |
|
||||
| **Active context** | Контекст, на котором сейчас оперируют функции browser-API. Переключается `setActiveContext`. |
|
||||
| **Primary context** | Контекст, активный на входе в тест. Декларация (`mod.context` или `mod.contexts[0]`). Зафиксирован в `testInfo.primaryContext`. |
|
||||
| **Default context** | Контекст из `config.defaultContext` (или единственный URL в упрощённой конфигурации). Используется, если тест не указал `context` / `contexts`. |
|
||||
| **Scoped API** | Объект на `ctx.<name>` в мульти-контекстных тестах — обёртки browser-функций, авто-переключающие контекст перед каждым вызовом. |
|
||||
| **Action function (ACTION_FN)** | Browser-функция, обёрнутая авто-обнаружением 1С-ошибок. Список — в §3. |
|
||||
| **Step** | Логический блок внутри теста, обёрнутый `step(name, fn)`. Маппится на Allure-step, попадает в `report.tests[].steps[]`. |
|
||||
| **Reset state** | Встроенная пост-тестовая очистка: `dismissPendingErrors` + закрытие всех открытых форм до рабочего стола. Выполняется после `afterEach`. |
|
||||
| **hookArgs** | Массив строк, переданных в `prepare` / `cleanup` после CLI-разделителя `--`. Движком не интерпретируются. |
|
||||
| **Severity** | Уровень критичности теста (`blocker / critical / normal / minor / trivial`) для Allure. Резолвится из `mod.severity`, тегов, `config.severity`, `config.defaultSeverity`. |
|
||||
|
||||
---
|
||||
|
||||
## См. также
|
||||
|
||||
- [web-test-guide.md](web-test-guide.md) — browser API (`clickElement`, `getFormState`, `readTable`, …) и интерактивный режим.
|
||||
- [web-test-recording-guide.md](web-test-recording-guide.md) — видеозапись, captions, narration, overlays.
|
||||
- [web-test-regression-guide.md](web-test-regression-guide.md) — пользовательский гайд (на русском, с быстрым стартом).
|
||||
- `/web-test` skill — `.claude/skills/web-test/SKILL.md`, `regress.md` (рабочая шпаргалка для модели).
|
||||
@@ -0,0 +1,251 @@
|
||||
#!/usr/bin/env node
|
||||
// build-webtest-db v0.2 — Собирает синтетическую web-test конфигурацию в постоянные пути
|
||||
// и накатывает её в зарегистрированную базу `webtest` (см. .v8-project.json).
|
||||
//
|
||||
// Двойной режим:
|
||||
// - CLI: node tests/skills/build-webtest-db.mjs [--runtime ...] [--skip-platform]
|
||||
// - Module: import { runSteps, execSkill, getProjectInfo, ... } from './build-webtest-db.mjs'
|
||||
//
|
||||
// CLI:
|
||||
// node tests/skills/build-webtest-db.mjs # пересобрать с нуля
|
||||
// node tests/skills/build-webtest-db.mjs --runtime python
|
||||
// node tests/skills/build-webtest-db.mjs --skip-platform # только XML, без db-create/load/update
|
||||
//
|
||||
// После завершения база готова к /web-publish + web-test сессии.
|
||||
|
||||
import { execFile } from 'child_process';
|
||||
import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { join, resolve, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const ROOT = dirname(__filename);
|
||||
const REPO_ROOT = resolve(ROOT, '../..');
|
||||
const SKILLS = resolve(REPO_ROOT, '.claude/skills');
|
||||
|
||||
// ── Public API ────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Reads .v8-project.json and locates webtest registration.
|
||||
* @returns {{ v8path: string, v8exe: string, webtestDb: object, configSrc: string, dbPath: string }}
|
||||
*/
|
||||
export function getProjectInfo() {
|
||||
const projectFile = join(REPO_ROOT, '.v8-project.json');
|
||||
if (!existsSync(projectFile)) throw new Error('.v8-project.json not found');
|
||||
const proj = JSON.parse(readFileSync(projectFile, 'utf8'));
|
||||
const webtestDb = proj.databases?.find(d => d.id === 'webtest');
|
||||
if (!webtestDb) throw new Error('Database "webtest" not registered in .v8-project.json');
|
||||
const v8path = proj.v8path;
|
||||
const v8exe = join(v8path, '1cv8.exe');
|
||||
const dbPath = webtestDb.path;
|
||||
const configSrc = resolve(REPO_ROOT, webtestDb.configSrc);
|
||||
return { v8path, v8exe, webtestDb, configSrc, dbPath };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a skill script path to an absolute file (chooses .ps1 or .py based on runtime).
|
||||
*/
|
||||
export function resolveScript(scriptRelPath, runtime = 'powershell') {
|
||||
const ext = runtime === 'python' ? '.py' : '.ps1';
|
||||
const full = join(SKILLS, scriptRelPath + ext);
|
||||
if (!existsSync(full)) throw new Error(`Script not found: ${full}`);
|
||||
return full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a single skill script with provided arguments.
|
||||
* @returns {Promise<string>} stdout
|
||||
*/
|
||||
export function execSkill(scriptPath, args, runtime = 'powershell') {
|
||||
return new Promise((res, rej) => {
|
||||
const cmd = runtime === 'python'
|
||||
? [process.env.PYTHON || 'python', [scriptPath, ...args]]
|
||||
: ['powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', scriptPath, ...args]];
|
||||
execFile(cmd[0], cmd[1], { encoding: 'utf8', timeout: 120_000, cwd: REPO_ROOT }, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
rej(new Error(stderr?.trim() || stdout?.trim() || err.message));
|
||||
} else {
|
||||
res(stdout);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces {workDir}/{v8path}/{dbPath} placeholders in a string value.
|
||||
*/
|
||||
export function replacePlaceholders(s, paths) {
|
||||
return String(s)
|
||||
.replace('{workDir}', paths.workDir ?? '')
|
||||
.replace('{v8path}', paths.v8path ?? '')
|
||||
.replace('{dbPath}', paths.dbPath ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an array of build steps.
|
||||
*
|
||||
* Each step: { name, script?, args?, input?, writeFile?, content? }
|
||||
* - writeFile: write content to a file (relative to workDir or absolute), skip script call
|
||||
* - script: relative path under .claude/skills (without extension)
|
||||
* - args: { '-Flag': value | true }, value may contain {workDir}/{v8path}/{dbPath}/{inputFile}
|
||||
* - input: JSON object written to __input.json (referenced by {inputFile} in args)
|
||||
*
|
||||
* @param {Array} steps
|
||||
* @param {{ workDir: string, v8path: string, dbPath: string }} paths
|
||||
* @param {string} runtime 'powershell' | 'python'
|
||||
* @param {(line: string) => void} log
|
||||
* @returns {Promise<{ ok: boolean, elapsed: number, failedAt?: number }>}
|
||||
*/
|
||||
export async function runSteps(steps, paths, runtime, log = console.log) {
|
||||
const t0 = Date.now();
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
const step = steps[i];
|
||||
const stepT0 = Date.now();
|
||||
|
||||
if (step.writeFile) {
|
||||
try {
|
||||
const target = replacePlaceholders(step.writeFile, paths);
|
||||
const abs = target.includes(':') || target.startsWith('/') ? target : join(paths.workDir, target);
|
||||
mkdirSync(dirname(abs), { recursive: true });
|
||||
writeFileSync(abs, step.content ?? '', 'utf8');
|
||||
const ms = Date.now() - stepT0;
|
||||
log(` [${i + 1}/${steps.length}] OK ${step.name} (${(ms / 1000).toFixed(1)}s)`);
|
||||
} catch (e) {
|
||||
log(` [${i + 1}/${steps.length}] FAIL ${step.name}: ${e.message}`);
|
||||
return { ok: false, elapsed: (Date.now() - t0) / 1000, failedAt: i };
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let inputFile = null;
|
||||
if (step.input) {
|
||||
inputFile = join(paths.workDir, '__input.json');
|
||||
writeFileSync(inputFile, JSON.stringify(step.input, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
const script = resolveScript(step.script, runtime);
|
||||
const args = [];
|
||||
for (const [flag, value] of Object.entries(step.args || {})) {
|
||||
args.push(flag);
|
||||
if (value === true) continue;
|
||||
let v = String(value).replace('{inputFile}', inputFile || '');
|
||||
v = replacePlaceholders(v, paths);
|
||||
args.push(v);
|
||||
}
|
||||
|
||||
try {
|
||||
await execSkill(script, args, runtime);
|
||||
if (inputFile && existsSync(inputFile)) rmSync(inputFile);
|
||||
const ms = Date.now() - stepT0;
|
||||
log(` [${i + 1}/${steps.length}] OK ${step.name} (${(ms / 1000).toFixed(1)}s)`);
|
||||
} catch (e) {
|
||||
if (inputFile && existsSync(inputFile)) rmSync(inputFile);
|
||||
log(` [${i + 1}/${steps.length}] FAIL ${step.name}`);
|
||||
log(` ${e.message.split('\n').join('\n ').substring(0, 1500)}`);
|
||||
return { ok: false, elapsed: (Date.now() - t0) / 1000, failedAt: i };
|
||||
}
|
||||
}
|
||||
return { ok: true, elapsed: (Date.now() - t0) / 1000 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the standard platform load steps (db-create + db-load-xml + db-update).
|
||||
*/
|
||||
export function platformLoadSteps() {
|
||||
return [
|
||||
{
|
||||
name: 'db-create: создание файловой ИБ',
|
||||
script: 'db-create/scripts/db-create',
|
||||
args: { '-V8Path': '{v8path}', '-InfoBasePath': '{dbPath}' },
|
||||
},
|
||||
{
|
||||
name: 'db-load-xml: загрузка конфигурации',
|
||||
script: 'db-load-xml/scripts/db-load-xml',
|
||||
args: { '-V8Path': '{v8path}', '-InfoBasePath': '{dbPath}', '-ConfigDir': '{workDir}' },
|
||||
},
|
||||
{
|
||||
name: 'db-update: обновление БД',
|
||||
script: 'db-update/scripts/db-update',
|
||||
args: { '-V8Path': '{v8path}', '-InfoBasePath': '{dbPath}' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the build-webtest-config.test.mjs steps array.
|
||||
*/
|
||||
export async function loadBuildSteps() {
|
||||
const buildModule = await import(`file://${join(ROOT, 'integration/build-webtest-config.test.mjs').replace(/\\/g, '/')}`);
|
||||
return buildModule.steps;
|
||||
}
|
||||
|
||||
// ── CLI ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
async function runCli() {
|
||||
const argv = process.argv.slice(2);
|
||||
const opts = { runtime: 'powershell', skipPlatform: false };
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (a === '--runtime' && argv[i + 1]) { opts.runtime = argv[++i]; continue; }
|
||||
if (a === '--skip-platform') { opts.skipPlatform = true; continue; }
|
||||
if (a === '-h' || a === '--help') {
|
||||
console.log('Usage: build-webtest-db.mjs [--runtime powershell|python] [--skip-platform]');
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
const { v8path, v8exe, configSrc, dbPath } = getProjectInfo();
|
||||
|
||||
if (!opts.skipPlatform && !existsSync(v8exe)) {
|
||||
console.error(`1cv8.exe not found at ${v8exe}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`[build-webtest-db] configSrc: ${configSrc}`);
|
||||
console.log(`[build-webtest-db] dbPath: ${dbPath}`);
|
||||
console.log(`[build-webtest-db] runtime: ${opts.runtime}`);
|
||||
console.log('');
|
||||
|
||||
if (existsSync(configSrc)) {
|
||||
console.log(`Removing existing configSrc...`);
|
||||
rmSync(configSrc, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
||||
}
|
||||
mkdirSync(configSrc, { recursive: true });
|
||||
|
||||
if (!opts.skipPlatform && existsSync(dbPath)) {
|
||||
console.log(`Removing existing IB...`);
|
||||
rmSync(dbPath, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
||||
}
|
||||
|
||||
const buildSteps = await loadBuildSteps();
|
||||
const platformSteps = opts.skipPlatform ? [] : platformLoadSteps();
|
||||
const allSteps = [...buildSteps, ...platformSteps];
|
||||
|
||||
const paths = { workDir: configSrc, v8path, dbPath };
|
||||
const result = await runSteps(allSteps, paths, opts.runtime, console.log);
|
||||
|
||||
console.log('');
|
||||
if (!result.ok) {
|
||||
console.error(`Build FAILED after ${result.elapsed.toFixed(1)}s`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`Build OK (${result.elapsed.toFixed(1)}s)`);
|
||||
console.log('');
|
||||
console.log(` configSrc: ${configSrc}`);
|
||||
if (!opts.skipPlatform) {
|
||||
console.log(` IB: ${dbPath}`);
|
||||
console.log('');
|
||||
console.log(` Next: /web-publish webtest → open in browser`);
|
||||
}
|
||||
}
|
||||
|
||||
// CLI guard: run only when invoked directly, not when imported.
|
||||
const invokedDirectly = process.argv[1]
|
||||
? fileURLToPath(import.meta.url) === resolve(process.argv[1])
|
||||
: false;
|
||||
if (invokedDirectly) {
|
||||
runCli().catch(e => {
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||
<Configuration uuid="UUID-001">
|
||||
<InternalInfo>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>UUID-002</xr:ClassId>
|
||||
<xr:ObjectId>UUID-003</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>UUID-004</xr:ClassId>
|
||||
<xr:ObjectId>UUID-005</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>UUID-006</xr:ClassId>
|
||||
<xr:ObjectId>UUID-007</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>UUID-008</xr:ClassId>
|
||||
<xr:ObjectId>UUID-009</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>UUID-010</xr:ClassId>
|
||||
<xr:ObjectId>UUID-011</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>UUID-012</xr:ClassId>
|
||||
<xr:ObjectId>UUID-013</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>UUID-014</xr:ClassId>
|
||||
<xr:ObjectId>UUID-015</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>TestConfig</Name>
|
||||
<Synonym>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>TestConfig</v8:content>
|
||||
</v8:item>
|
||||
</Synonym>
|
||||
<Comment />
|
||||
<NamePrefix />
|
||||
<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
|
||||
<DefaultRunMode>ManagedApplication</DefaultRunMode>
|
||||
<UsePurposes>
|
||||
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||
</UsePurposes>
|
||||
<ScriptVariant>Russian</ScriptVariant>
|
||||
<DefaultRoles />
|
||||
<Vendor></Vendor>
|
||||
<Version></Version>
|
||||
<UpdateCatalogAddress />
|
||||
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
|
||||
<UseOrdinaryFormInManagedApplication>false</UseOrdinaryFormInManagedApplication>
|
||||
<AdditionalFullTextSearchDictionaries />
|
||||
<CommonSettingsStorage />
|
||||
<ReportsUserSettingsStorage />
|
||||
<ReportsVariantsStorage />
|
||||
<FormDataSettingsStorage />
|
||||
<DynamicListsUserSettingsStorage />
|
||||
<URLExternalDataStorage />
|
||||
<Content />
|
||||
<DefaultReportForm />
|
||||
<DefaultReportVariantForm />
|
||||
<DefaultReportSettingsForm />
|
||||
<DefaultReportAppearanceTemplate />
|
||||
<DefaultDynamicListSettingsForm />
|
||||
<DefaultSearchForm />
|
||||
<DefaultDataHistoryChangeHistoryForm />
|
||||
<DefaultDataHistoryVersionDataForm />
|
||||
<DefaultDataHistoryVersionDifferencesForm />
|
||||
<DefaultCollaborationSystemUsersChoiceForm />
|
||||
<RequiredMobileApplicationPermissions />
|
||||
<UsedMobileApplicationFunctionalities>
|
||||
<app:functionality>
|
||||
<app:functionality>Biometrics</app:functionality>
|
||||
<app:use>true</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>Location</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>BackgroundLocation</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>BluetoothPrinters</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>WiFiPrinters</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>Contacts</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>Calendars</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>PushNotifications</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>LocalNotifications</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>InAppPurchases</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>PersonalComputerFileExchange</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>Ads</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>NumberDialing</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>CallProcessing</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>CallLog</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>AutoSendSMS</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>ReceiveSMS</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>SMSLog</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>Camera</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>Microphone</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>MusicLibrary</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>PictureAndVideoLibraries</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>AudioPlaybackAndVibration</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>BackgroundAudioPlaybackAndVibration</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>InstallPackages</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>OSBackup</app:functionality>
|
||||
<app:use>true</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>ApplicationUsageStatistics</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>BarcodeScanning</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>BackgroundAudioRecording</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>AllFilesAccess</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>Videoconferences</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>NFC</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>DocumentScanning</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>SpeechToText</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>Geofences</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>IncomingShareRequests</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
<app:functionality>
|
||||
<app:functionality>AllIncomingShareRequestsTypesProcessing</app:functionality>
|
||||
<app:use>false</app:use>
|
||||
</app:functionality>
|
||||
</UsedMobileApplicationFunctionalities>
|
||||
<StandaloneConfigurationRestrictionRoles />
|
||||
<MobileApplicationURLs />
|
||||
<AllowedIncomingShareRequestTypes />
|
||||
<MainClientApplicationWindowMode>Normal</MainClientApplicationWindowMode>
|
||||
<DefaultInterface />
|
||||
<DefaultStyle />
|
||||
<DefaultLanguage>Language.Русский</DefaultLanguage>
|
||||
<BriefInformation />
|
||||
<DetailedInformation />
|
||||
<Copyright />
|
||||
<VendorInformationAddress />
|
||||
<ConfigurationInformationAddress />
|
||||
<DataLockControlMode>Managed</DataLockControlMode>
|
||||
<ObjectAutonumerationMode>NotAutoFree</ObjectAutonumerationMode>
|
||||
<ModalityUseMode>DontUse</ModalityUseMode>
|
||||
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
|
||||
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
|
||||
<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
|
||||
<CompatibilityMode>Version8_3_24</CompatibilityMode>
|
||||
<DefaultConstantsForm />
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<Language>Русский</Language>
|
||||
<DataProcessor>ЗапретРучногоВвода</DataProcessor>
|
||||
</ChildObjects>
|
||||
</Configuration>
|
||||
</MetaDataObject>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||
<DataProcessor uuid="UUID-001">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="DataProcessorObject.ЗапретРучногоВвода" category="Object">
|
||||
<xr:TypeId>UUID-002</xr:TypeId>
|
||||
<xr:ValueId>UUID-003</xr:ValueId>
|
||||
</xr:GeneratedType>
|
||||
<xr:GeneratedType name="DataProcessorManager.ЗапретРучногоВвода" category="Manager">
|
||||
<xr:TypeId>UUID-004</xr:TypeId>
|
||||
<xr:ValueId>UUID-005</xr:ValueId>
|
||||
</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>ЗапретРучногоВвода</Name>
|
||||
<Synonym>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Запрет ручного ввода</v8:content>
|
||||
</v8:item>
|
||||
</Synonym>
|
||||
<Comment />
|
||||
<UseStandardCommands>false</UseStandardCommands>
|
||||
<DefaultForm>DataProcessor.ЗапретРучногоВвода.Form.Форма</DefaultForm>
|
||||
<AuxiliaryForm />
|
||||
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||
<ExtendedPresentation />
|
||||
<Explanation />
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<Form>Форма</Form>
|
||||
</ChildObjects>
|
||||
</DataProcessor>
|
||||
</MetaDataObject>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||
<Form uuid="UUID-001">
|
||||
<Properties>
|
||||
<Name>Форма</Name>
|
||||
<Synonym>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Форма</v8:content>
|
||||
</v8:item>
|
||||
</Synonym>
|
||||
<Comment/>
|
||||
<FormType>Managed</FormType>
|
||||
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||
<UsePurposes>
|
||||
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||
<v8:Value xsi:type="app:ApplicationUsePurpose">MobilePlatformApplication</v8:Value>
|
||||
</UsePurposes>
|
||||
<ExtendedPresentation/>
|
||||
</Properties>
|
||||
</Form>
|
||||
</MetaDataObject>
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcssch="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||
<Title>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Запрет ручного ввода</v8:content>
|
||||
</v8:item>
|
||||
</Title>
|
||||
<AutoTitle>false</AutoTitle>
|
||||
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
|
||||
<ChildItems>
|
||||
<InputField name="ОбычноеПоле" id="1">
|
||||
<DataPath>ОбычноеПоле</DataPath>
|
||||
<Title>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Обычное поле</v8:content>
|
||||
</v8:item>
|
||||
</Title>
|
||||
<ContextMenu name="ОбычноеПолеКонтекстноеМеню" id="2"/>
|
||||
<ExtendedTooltip name="ОбычноеПолеРасширеннаяПодсказка" id="3"/>
|
||||
</InputField>
|
||||
<InputField name="ПолеБезРучногоВвода" id="4">
|
||||
<DataPath>ПолеБезРучногоВвода</DataPath>
|
||||
<Title>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Только через выбор</v8:content>
|
||||
</v8:item>
|
||||
</Title>
|
||||
<TextEdit>false</TextEdit>
|
||||
<ContextMenu name="ПолеБезРучногоВводаКонтекстноеМеню" id="5"/>
|
||||
<ExtendedTooltip name="ПолеБезРучногоВводаРасширеннаяПодсказка" id="6"/>
|
||||
</InputField>
|
||||
</ChildItems>
|
||||
<Attributes>
|
||||
<Attribute name="Объект" id="7">
|
||||
<Type>
|
||||
<v8:Type>cfg:DataProcessorObject.ЗапретРучногоВвода</v8:Type>
|
||||
</Type>
|
||||
<MainAttribute>true</MainAttribute>
|
||||
</Attribute>
|
||||
<Attribute name="ОбычноеПоле" id="8">
|
||||
<Title>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Обычное поле</v8:content>
|
||||
</v8:item>
|
||||
</Title>
|
||||
<Type>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</Type>
|
||||
</Attribute>
|
||||
<Attribute name="ПолеБезРучногоВвода" id="9">
|
||||
<Title>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Поле без ручного ввода</v8:content>
|
||||
</v8:item>
|
||||
</Title>
|
||||
<Type>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</Type>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Form>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
#Область ОбработчикиСобытийФормы
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область ОбработчикиСобытийЭлементовФормы
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область ОбработчикиКомандФормы
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область ОбработчикиОповещений
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область СлужебныеПроцедурыИФункции
|
||||
|
||||
#КонецОбласти
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ClientApplicationInterface xmlns="http://v8.1c.ru/8.2/managed-application/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="InterfaceLayouter">
|
||||
<top>
|
||||
<panel id="UUID-001">
|
||||
<uuid>UUID-002</uuid>
|
||||
</panel>
|
||||
</top>
|
||||
<left>
|
||||
<panel id="UUID-003">
|
||||
<uuid>UUID-004</uuid>
|
||||
</panel>
|
||||
</left>
|
||||
<panelDef id="UUID-004"/>
|
||||
<panelDef id="UUID-005"/>
|
||||
<panelDef id="UUID-006"/>
|
||||
<panelDef id="UUID-002"/>
|
||||
<panelDef id="UUID-007"/>
|
||||
</ClientApplicationInterface>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||
<Language uuid="UUID-001">
|
||||
<Properties>
|
||||
<Name>Русский</Name>
|
||||
<Synonym>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Русский</v8:content>
|
||||
</v8:item>
|
||||
</Synonym>
|
||||
<Comment/>
|
||||
<LanguageCode>ru</LanguageCode>
|
||||
</Properties>
|
||||
</Language>
|
||||
</MetaDataObject>
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "Поле ввода с textEdit:false (запрет ручного ввода)",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "meta-compile/scripts/meta-compile",
|
||||
"input": { "type": "DataProcessor", "name": "ЗапретРучногоВвода" },
|
||||
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
|
||||
},
|
||||
{
|
||||
"script": "form-add/scripts/form-add",
|
||||
"args": { "-ObjectPath": "{workDir}/DataProcessors/ЗапретРучногоВвода.xml", "-FormName": "Форма" }
|
||||
}
|
||||
],
|
||||
"params": { "outputPath": "DataProcessors/ЗапретРучногоВвода/Forms/Форма/Ext/Form.xml" },
|
||||
"validatePath": "DataProcessors/ЗапретРучногоВвода/Forms/Форма/Ext/Form.xml",
|
||||
"input": {
|
||||
"title": "Запрет ручного ввода",
|
||||
"elements": [
|
||||
{ "input": "ОбычноеПоле", "path": "ОбычноеПоле", "title": "Обычное поле" },
|
||||
{ "input": "ПолеБезРучногоВвода", "path": "ПолеБезРучногоВвода", "textEdit": false, "title": "Только через выбор" }
|
||||
],
|
||||
"attributes": [
|
||||
{ "name": "Объект", "type": "DataProcessorObject.ЗапретРучногоВвода", "main": true },
|
||||
{ "name": "ОбычноеПоле", "type": "string(100)" },
|
||||
{ "name": "ПолеБезРучногоВвода", "type": "string(100)" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "DataPath с Items.<Table>.CurrentData и ~Атрибут не вызывают ложных ошибок",
|
||||
"setup": "fixture:datapath-currentdata",
|
||||
"params": { "formPath": "DataProcessors/Spec/Forms/Форма" }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Числовые и UUID DataPath не вызывают ложных ошибок",
|
||||
"setup": "fixture:datapath-opaque-refs",
|
||||
"params": { "formPath": "DataProcessors/Opaque/Forms/Форма" }
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||
<Title>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Тест</v8:content>
|
||||
</v8:item>
|
||||
</Title>
|
||||
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
|
||||
<ChildItems>
|
||||
<Table name="Список" id="1">
|
||||
<DataPath>Список</DataPath>
|
||||
<ContextMenu name="СписокКонтекстноеМеню" id="2"/>
|
||||
<AutoCommandBar name="СписокКоманднаяПанель" id="3"/>
|
||||
<SearchStringAddition name="СписокСтрокаПоиска" id="4"/>
|
||||
<ViewStatusAddition name="СписокСостояниеПросмотра" id="5"/>
|
||||
<SearchControlAddition name="СписокУправлениеПоиском" id="6"/>
|
||||
<ChildItems>
|
||||
<InputField name="Ссылка" id="7">
|
||||
<DataPath>Список.Ссылка</DataPath>
|
||||
<ContextMenu name="СсылкаКонтекстноеМеню" id="8"/>
|
||||
<ExtendedTooltip name="СсылкаРасширеннаяПодсказка" id="9"/>
|
||||
</InputField>
|
||||
</ChildItems>
|
||||
</Table>
|
||||
<InputField name="ТекущаяСсылка" id="10">
|
||||
<DataPath>Items.Список.CurrentData.Ссылка</DataPath>
|
||||
<ContextMenu name="ТекущаяСсылкаКонтекстноеМеню" id="11"/>
|
||||
<ExtendedTooltip name="ТекущаяСсылкаРасширеннаяПодсказка" id="12"/>
|
||||
</InputField>
|
||||
<InputField name="ВыбраннаяСсылка" id="13">
|
||||
<DataPath>~Список.Ссылка</DataPath>
|
||||
<ContextMenu name="ВыбраннаяСсылкаКонтекстноеМеню" id="14"/>
|
||||
<ExtendedTooltip name="ВыбраннаяСсылкаРасширеннаяПодсказка" id="15"/>
|
||||
</InputField>
|
||||
</ChildItems>
|
||||
<Attributes>
|
||||
<Attribute name="Список" id="16">
|
||||
<Type>
|
||||
<v8:Type>cfg:DynamicList</v8:Type>
|
||||
</Type>
|
||||
<MainAttribute>true</MainAttribute>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Form>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||
<Title>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Тест</v8:content>
|
||||
</v8:item>
|
||||
</Title>
|
||||
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
|
||||
<ChildItems>
|
||||
<InputField name="ПривязкаПоUUID" id="1">
|
||||
<DataPath>1/0:a917a122-f663-4c45-8de0-fd5104007de3</DataPath>
|
||||
<ContextMenu name="ПривязкаПоUUIDКонтекстноеМеню" id="2"/>
|
||||
<ExtendedTooltip name="ПривязкаПоUUIDРасширеннаяПодсказка" id="3"/>
|
||||
</InputField>
|
||||
<LabelField name="ЧисловаяПривязка" id="4">
|
||||
<DataPath>10</DataPath>
|
||||
<ContextMenu name="ЧисловаяПривязкаКонтекстноеМеню" id="5"/>
|
||||
<ExtendedTooltip name="ЧисловаяПривязкаРасширеннаяПодсказка" id="6"/>
|
||||
</LabelField>
|
||||
<InputField name="Нормальное" id="7">
|
||||
<DataPath>Объект.Наименование</DataPath>
|
||||
<ContextMenu name="НормальноеКонтекстноеМеню" id="8"/>
|
||||
<ExtendedTooltip name="НормальноеРасширеннаяПодсказка" id="9"/>
|
||||
</InputField>
|
||||
</ChildItems>
|
||||
<Attributes>
|
||||
<Attribute name="Объект" id="10">
|
||||
<Type>
|
||||
<v8:Type>cfg:DataProcessorObject.Opaque</v8:Type>
|
||||
</Type>
|
||||
<MainAttribute>true</MainAttribute>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Form>
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
</dataSet>
|
||||
<calculatedField>
|
||||
<dataPath>ИмяРесурса</dataPath>
|
||||
<expression>""</expression>
|
||||
<expression>""</expression>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</dataSet>
|
||||
<calculatedField>
|
||||
<dataPath>ИмяРесурса</dataPath>
|
||||
<expression>""</expression>
|
||||
<expression>""</expression>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
</dataSet>
|
||||
<calculatedField>
|
||||
<dataPath>ИмяРесурса</dataPath>
|
||||
<expression>""</expression>
|
||||
<expression>""</expression>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "add-parameter @hidden @always: useRestriction+availableAsField+use=Always",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Поле ИЗ Регистр КАК Т",
|
||||
"fields": ["Поле: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "add-parameter",
|
||||
"value": "Контрагент: CatalogRef.Контрагенты = Справочник.Контрагенты.ПустаяСсылка @hidden @always"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "add-parameter: ссылочный тип → xsi:type=dcscor:DesignTimeValue",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Счет ИЗ Регистр КАК Т",
|
||||
"fields": ["Счет: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "add-parameter",
|
||||
"value": "Контрагент: CatalogRef.Контрагенты = Справочник.Контрагенты.ПустаяСсылка"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "add-parameter с initial availableValue list (с запятой в кавычках)",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Поле ИЗ Регистр КАК Т",
|
||||
"fields": ["Поле: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "add-parameter",
|
||||
"value": "Округление: EnumRef.Округления = Окр1_00 availableValue=Перечисление.Округления.Окр1_00: 'руб., коп.', Перечисление.Округления.Окр1: руб., Перечисление.Округления.Окр1000: тыс. руб."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "add-total: 'X: X' — identity expression (без обёртки в Func)",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Проверка ИЗ Регистр КАК Т",
|
||||
"fields": ["Проверка: decimal(15,2)"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "add-total",
|
||||
"value": "Проверка: Проверка"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "clear-conditionalAppearance: очистить все правила оформления варианта",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Сумма ИЗ Регистр КАК Т",
|
||||
"fields": ["Сумма: decimal(15,2)"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
},
|
||||
{
|
||||
"script": "skd-edit/scripts/skd-edit",
|
||||
"args": { "-TemplatePath": "{workDir}/Template.xml", "-Operation": "add-conditionalAppearance", "-Value": "ЦветТекста = web:Red when Сумма < 0 ;; ЦветФона = web:LightGreen when Сумма > 1000" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "clear-conditionalAppearance",
|
||||
"value": "*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Контрагент</dataPath>
|
||||
<field>Контрагент</field>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Контрагент</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Counterparty</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<role>
|
||||
<dcscom:dimension>true</dcscom:dimension>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
<appearance>
|
||||
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
|
||||
<dcscor:parameter>Формат</dcscor:parameter>
|
||||
<dcscor:value xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>ЧДЦ=2</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>NFD=2</v8:content>
|
||||
</v8:item>
|
||||
</dcscor:value>
|
||||
</dcscor:item>
|
||||
</appearance>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ 1 КАК Контрагент</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Period</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>П</dataPath>
|
||||
<field>П</field>
|
||||
</field>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Имя</dataPath>
|
||||
<field>Имя</field>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ
|
||||
1 КАК П,
|
||||
"literal" КАК Имя</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "NO-OP modify-field на отсутствующее поле — файл не трогаем (regression от XML round-trip бага)",
|
||||
"setup": "fixture:roundtrip-base",
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-field",
|
||||
"value": "НесуществующееПоле [Заголовок]"
|
||||
},
|
||||
"expect": {
|
||||
"stdoutContains": "No changes -- file untouched"
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "modify-parameter availableValue: replace whole list (старый список удалён, новый добавлен)",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Поле ИЗ Регистр КАК Т",
|
||||
"fields": ["Поле: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
},
|
||||
{
|
||||
"script": "skd-edit/scripts/skd-edit",
|
||||
"args": { "-TemplatePath": "{workDir}/Template.xml", "-Operation": "add-parameter", "-Value": "Округление: EnumRef.Округления = Окр1_00 availableValue=Перечисление.Округления.Окр1_00: ст1, Перечисление.Округления.Окр1: ст2, Перечисление.Округления.Окр1000: ст3" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-parameter",
|
||||
"value": "Округление availableValue=Перечисление.Округления.Окр1: руб., Перечисление.Округления.Окр1000: тыс., Перечисление.Округления.Окр1000000: млн."
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,6 @@
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-parameter",
|
||||
"value": "ПорядокОкругления denyIncompleteValues=true use=Always availableValue=Перечисление.Округления.Окр1_00 presentation=руб. коп ;; ПорядокОкругления availableValue=Перечисление.Округления.Окр1 presentation=руб. ;; ПорядокОкругления availableValue=Перечисление.Округления.Окр1000 presentation=тыс. руб"
|
||||
"value": "ПорядокОкругления denyIncompleteValues=true use=Always availableValue=Перечисление.Округления.Окр1_00: руб. коп, Перечисление.Округления.Окр1: руб., Перечисление.Округления.Окр1000: тыс. руб"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "modify-parameter @hidden @always: идемпотентность",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Поле ИЗ Регистр КАК Т",
|
||||
"fields": ["Поле: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
},
|
||||
{
|
||||
"script": "skd-edit/scripts/skd-edit",
|
||||
"args": { "-TemplatePath": "{workDir}/Template.xml", "-Operation": "add-parameter", "-Value": "Контрагент: CatalogRef.Контрагенты = Справочник.Контрагенты.ПустаяСсылка" }
|
||||
},
|
||||
{
|
||||
"script": "skd-edit/scripts/skd-edit",
|
||||
"args": { "-TemplatePath": "{workDir}/Template.xml", "-Operation": "modify-parameter", "-Value": "Контрагент @hidden @always" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-parameter",
|
||||
"value": "Контрагент @hidden @always"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "modify-parameter value=... обновляет существующий <value> (не дублирует)",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Счет ИЗ Регистр КАК Т",
|
||||
"fields": ["Счет: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
},
|
||||
{
|
||||
"script": "skd-edit/scripts/skd-edit",
|
||||
"args": { "-TemplatePath": "{workDir}/Template.xml", "-Operation": "add-parameter", "-Value": "Контрагент: CatalogRef.Контрагенты = Справочник.Контрагенты.ПустаяСсылка" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-parameter",
|
||||
"value": "Контрагент value=Справочник.Контрагенты.НашаОрганизация"
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,6 @@
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-parameter",
|
||||
"value": "ПорядокОкругления use=Always ;; ПорядокОкругления denyIncompleteValues=true ;; ПорядокОкругления availableValue=Перечисление.Округления.Окр1_00 presentation=руб. коп ;; ПорядокОкругления availableValue=Перечисление.Округления.Окр1 presentation=руб."
|
||||
"value": "ПорядокОкругления use=Always ;; ПорядокОкругления denyIncompleteValues=true ;; ПорядокОкругления availableValue=Перечисление.Округления.Окр1_00: руб. коп, Перечисление.Округления.Окр1: руб."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "modify-structure меняет groupItems, сохраняет Selection",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Счет, Т.Сумма, Т.Валюта ИЗ Регистр КАК Т",
|
||||
"fields": ["Счет: string", "Сумма: decimal(15,2)", "Валюта: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
},
|
||||
{
|
||||
"script": "skd-edit/scripts/skd-edit",
|
||||
"args": { "-TemplatePath": "{workDir}/Template.xml", "-Operation": "set-structure", "-Value": "Счет @name=ДанныеОтчета" }
|
||||
},
|
||||
{
|
||||
"script": "skd-edit/scripts/skd-edit",
|
||||
"args": { "-TemplatePath": "{workDir}/Template.xml", "-Operation": "add-selection", "-Value": "Счет @group=ДанныеОтчета ;; Сумма @group=ДанныеОтчета" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-structure",
|
||||
"value": "Валюта @name=ДанныеОтчета"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "patch-query ;;-batch: сегменты триммятся по краям",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.СтароеИмя1, Т.СтароеИмя2 ИЗ Регистр КАК Т",
|
||||
"fields": ["СтароеИмя1: string", "СтароеИмя2: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "patch-query",
|
||||
"value": "СтароеИмя1 => НовоеИмя1 ;; СтароеИмя2 => НовоеИмя2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "patch-query @once: множественные вхождения — ошибка",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Поле1, Т.Поле2 ИЗ Регистр КАК Т ГДЕ Т.Поле1 = Т.Поле2",
|
||||
"fields": ["Поле1: string", "Поле2: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "patch-query",
|
||||
"value": "Поле1 => Новое @once"
|
||||
},
|
||||
"expectError": "expected 1 occurrence"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "patch-query @once: 1 вхождение — успешная замена",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.УникальноеИмя ИЗ Регистр КАК Т",
|
||||
"fields": ["УникальноеИмя: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "patch-query",
|
||||
"value": "УникальноеИмя => НовоеИмя @once"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "modify-field сохраняет en/uk title (multi-lang) при ru-override",
|
||||
"setup": "fixture:multilang-base",
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-field",
|
||||
"value": "Контрагент [Имя контрагента]"
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "modify-parameter сохраняет en title (multi-lang) при ru-override",
|
||||
"setup": "fixture:multilang-base",
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-parameter",
|
||||
"value": "Период [Новый период]"
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "modify-field сохраняет неизвестные дочерние элементы (<appearance> со всеми вложениями) — структура из ERP",
|
||||
"setup": "fixture:multilang-base",
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-field",
|
||||
"value": "Контрагент @dimension"
|
||||
},
|
||||
"expect": {
|
||||
"stdoutContains": "Field \"Контрагент\" modified"
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Многострочный xmlns корня сохраняется после успешной операции",
|
||||
"setup": "fixture:roundtrip-base",
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "set-query",
|
||||
"value": "DS=ВЫБРАТЬ 2 КАК П, "new" КАК Имя"
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "set-field-role: установить балансовую роль с уточнением",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Сумма ИЗ Регистр КАК Т",
|
||||
"fields": ["Сумма: decimal(15,2)"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "set-field-role",
|
||||
"value": "Сумма @balance balanceGroupName=Сумма balanceType=OpeningBalance"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "set-field-role: пустой spec — снимает роль",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Сумма ИЗ Регистр КАК Т",
|
||||
"fields": ["Сумма: decimal(15,2) @balance"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "set-field-role",
|
||||
"value": "Сумма"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "set-structure: запятая в shorthand → несколько GroupItemField",
|
||||
"preRun": [
|
||||
{
|
||||
"script": "skd-compile/scripts/skd-compile",
|
||||
"input": {
|
||||
"dataSets": [{
|
||||
"name": "Основной",
|
||||
"query": "ВЫБРАТЬ Т.Валюта, Т.Банк, Т.ИНН ИЗ Регистр КАК Т",
|
||||
"fields": ["Валюта: string", "Банк: string", "ИНН: string"]
|
||||
}]
|
||||
},
|
||||
"args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "{workDir}/Template.xml" }
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "set-structure",
|
||||
"value": "Валюта, Банк, ИНН @name=ДанныеОтчета"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -65,10 +72,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -53,10 +60,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -41,10 +48,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -52,10 +59,10 @@
|
||||
</dcsset:filter>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Поле</dataPath>
|
||||
<field>Поле</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Поле ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Контрагент</name>
|
||||
<valueType>
|
||||
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.Контрагенты</v8:Type>
|
||||
</valueType>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Справочник.Контрагенты.ПустаяСсылка</value>
|
||||
<useRestriction>true</useRestriction>
|
||||
<availableAsField>false</availableAsField>
|
||||
<use>Always</use>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Счет</dataPath>
|
||||
<field>Счет</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Счет ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Контрагент</name>
|
||||
<valueType>
|
||||
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.Контрагенты</v8:Type>
|
||||
</valueType>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Справочник.Контрагенты.ПустаяСсылка</value>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Поле</dataPath>
|
||||
<field>Поле</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Поле ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Округление</name>
|
||||
<valueType>
|
||||
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:EnumRef.Округления</v8:Type>
|
||||
</valueType>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Окр1_00</value>
|
||||
<availableValue>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Перечисление.Округления.Окр1_00</value>
|
||||
<presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>руб., коп.</v8:content>
|
||||
</v8:item>
|
||||
</presentation>
|
||||
</availableValue>
|
||||
<availableValue>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Перечисление.Округления.Окр1</value>
|
||||
<presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>руб.</v8:content>
|
||||
</v8:item>
|
||||
</presentation>
|
||||
</availableValue>
|
||||
<availableValue>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Перечисление.Округления.Окр1000</value>
|
||||
<presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>тыс. руб.</v8:content>
|
||||
</v8:item>
|
||||
</presentation>
|
||||
</availableValue>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -73,10 +80,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -27,7 +34,7 @@
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemField">
|
||||
<dcsset:field>Поле1</dcsset:field>
|
||||
</dcsset:item>
|
||||
@@ -37,10 +44,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Проверка</dataPath>
|
||||
<field>Проверка</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:decimal</v8:Type>
|
||||
<v8:NumberQualifiers>
|
||||
<v8:Digits>15</v8:Digits>
|
||||
<v8:FractionDigits>2</v8:FractionDigits>
|
||||
<v8:AllowedSign>Any</v8:AllowedSign>
|
||||
</v8:NumberQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Проверка ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<totalField>
|
||||
<dataPath>Проверка</dataPath>
|
||||
<expression>Проверка</expression>
|
||||
</totalField>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -42,10 +49,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Сумма</dataPath>
|
||||
<field>Сумма</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:decimal</v8:Type>
|
||||
<v8:NumberQualifiers>
|
||||
<v8:Digits>15</v8:Digits>
|
||||
<v8:FractionDigits>2</v8:FractionDigits>
|
||||
<v8:AllowedSign>Any</v8:AllowedSign>
|
||||
</v8:NumberQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Сумма ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:conditionalAppearance>
|
||||
</dcsset:conditionalAppearance>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -44,7 +51,7 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:conditionalAppearance>
|
||||
<dcsset:item>
|
||||
<dcsset:selection />
|
||||
<dcsset:selection/>
|
||||
<dcsset:filter>
|
||||
<dcsset:item xsi:type="dcsset:FilterItemComparison">
|
||||
<dcsset:left xsi:type="dcscor:Field">ПараметрыДанных.ПорядокОкругления</dcsset:left>
|
||||
@@ -65,7 +72,7 @@
|
||||
</dcsset:appearance>
|
||||
</dcsset:item>
|
||||
<dcsset:item>
|
||||
<dcsset:selection />
|
||||
<dcsset:selection/>
|
||||
<dcsset:filter>
|
||||
<dcsset:item xsi:type="dcsset:FilterItemGroup">
|
||||
<dcsset:groupType>OrGroup</dcsset:groupType>
|
||||
@@ -96,10 +103,10 @@
|
||||
</dcsset:conditionalAppearance>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>П</dataPath>
|
||||
<field>П</field>
|
||||
</field>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Имя</dataPath>
|
||||
<field>Имя</field>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ
|
||||
1 КАК П,
|
||||
"literal" КАК Имя</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
+10
-3
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -56,10 +63,10 @@
|
||||
</dcsset:dataParameters>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Поле</dataPath>
|
||||
<field>Поле</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Поле ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Округление</name>
|
||||
<valueType>
|
||||
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:EnumRef.Округления</v8:Type>
|
||||
</valueType>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Окр1_00</value>
|
||||
<availableValue>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Перечисление.Округления.Окр1</value>
|
||||
<presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>руб.</v8:content>
|
||||
</v8:item>
|
||||
</presentation>
|
||||
</availableValue>
|
||||
<availableValue>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Перечисление.Округления.Окр1000</value>
|
||||
<presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>тыс.</v8:content>
|
||||
</v8:item>
|
||||
</presentation>
|
||||
</availableValue>
|
||||
<availableValue>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Перечисление.Округления.Окр1000000</value>
|
||||
<presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>млн.</v8:content>
|
||||
</v8:item>
|
||||
</presentation>
|
||||
</availableValue>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -74,10 +81,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Поле</dataPath>
|
||||
<field>Поле</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Поле ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Контрагент</name>
|
||||
<valueType>
|
||||
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.Контрагенты</v8:Type>
|
||||
</valueType>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Справочник.Контрагенты.ПустаяСсылка</value>
|
||||
<useRestriction>true</useRestriction>
|
||||
<availableAsField>false</availableAsField>
|
||||
<use>Always</use>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -53,10 +60,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Счет</dataPath>
|
||||
<field>Счет</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Счет ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Контрагент</name>
|
||||
<valueType>
|
||||
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.Контрагенты</v8:Type>
|
||||
</valueType>
|
||||
<value xsi:type="dcscor:DesignTimeValue">Справочник.Контрагенты.НашаОрганизация</value>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -65,10 +72,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Счет</dataPath>
|
||||
<field>Счет</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Сумма</dataPath>
|
||||
<field>Сумма</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:decimal</v8:Type>
|
||||
<v8:NumberQualifiers>
|
||||
<v8:Digits>15</v8:Digits>
|
||||
<v8:FractionDigits>2</v8:FractionDigits>
|
||||
<v8:AllowedSign>Any</v8:AllowedSign>
|
||||
</v8:NumberQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Валюта</dataPath>
|
||||
<field>Валюта</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Счет, Т.Сумма, Т.Валюта ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:name>ДанныеОтчета</dcsset:name>
|
||||
<dcsset:groupItems>
|
||||
<dcsset:item xsi:type="dcsset:GroupItemField">
|
||||
<dcsset:field>Валюта</dcsset:field>
|
||||
<dcsset:groupType>Items</dcsset:groupType>
|
||||
<dcsset:periodAdditionType>None</dcsset:periodAdditionType>
|
||||
<dcsset:periodAdditionBegin xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionBegin>
|
||||
<dcsset:periodAdditionEnd xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionEnd>
|
||||
</dcsset:item>
|
||||
</dcsset:groupItems>
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemField">
|
||||
<dcsset:field>Счет</dcsset:field>
|
||||
</dcsset:item>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemField">
|
||||
<dcsset:field>Сумма</dcsset:field>
|
||||
</dcsset:item>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>СтароеИмя1</dataPath>
|
||||
<field>СтароеИмя1</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>СтароеИмя2</dataPath>
|
||||
<field>СтароеИмя2</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.НовоеИмя1, Т.НовоеИмя2 ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>УникальноеИмя</dataPath>
|
||||
<field>УникальноеИмя</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.НовоеИмя ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -26,10 +33,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Контрагент</dataPath>
|
||||
<field>Контрагент</field>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Имя контрагента</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Counterparty</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<role>
|
||||
<dcscom:dimension>true</dcscom:dimension>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
<appearance>
|
||||
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
|
||||
<dcscor:parameter>Формат</dcscor:parameter>
|
||||
<dcscor:value xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>ЧДЦ=2</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>NFD=2</v8:content>
|
||||
</v8:item>
|
||||
</dcscor:value>
|
||||
</dcscor:item>
|
||||
</appearance>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ 1 КАК Контрагент</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Period</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Контрагент</dataPath>
|
||||
<field>Контрагент</field>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Контрагент</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Counterparty</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<role>
|
||||
<dcscom:dimension>true</dcscom:dimension>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
<appearance>
|
||||
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
|
||||
<dcscor:parameter>Формат</dcscor:parameter>
|
||||
<dcscor:value xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>ЧДЦ=2</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>NFD=2</v8:content>
|
||||
</v8:item>
|
||||
</dcscor:value>
|
||||
</dcscor:item>
|
||||
</appearance>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ 1 КАК Контрагент</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Новый период</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Period</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Контрагент</dataPath>
|
||||
<field>Контрагент</field>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Контрагент</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Counterparty</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<role>
|
||||
<dcscom:dimension>true</dcscom:dimension>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
<appearance>
|
||||
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
|
||||
<dcscor:parameter>Формат</dcscor:parameter>
|
||||
<dcscor:value xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>ЧДЦ=2</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>NFD=2</v8:content>
|
||||
</v8:item>
|
||||
</dcscor:value>
|
||||
</dcscor:item>
|
||||
</appearance>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ 1 КАК Контрагент</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Period</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>П</dataPath>
|
||||
<field>П</field>
|
||||
</field>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Имя</dataPath>
|
||||
<field>Имя</field>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>DS=ВЫБРАТЬ 2 КАК П, &quot;new&quot; КАК Имя</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -108,10 +115,10 @@
|
||||
</dcsset:dataParameters>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -81,10 +88,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Сумма</dataPath>
|
||||
<field>Сумма</field>
|
||||
<role>
|
||||
<dcscom:balance>true</dcscom:balance>
|
||||
<dcscom:balanceGroupName>Сумма</dcscom:balanceGroupName>
|
||||
<dcscom:balanceType>OpeningBalance</dcscom:balanceType>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:decimal</v8:Type>
|
||||
<v8:NumberQualifiers>
|
||||
<v8:Digits>15</v8:Digits>
|
||||
<v8:FractionDigits>2</v8:FractionDigits>
|
||||
<v8:AllowedSign>Any</v8:AllowedSign>
|
||||
</v8:NumberQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Сумма ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Сумма</dataPath>
|
||||
<field>Сумма</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:decimal</v8:Type>
|
||||
<v8:NumberQualifiers>
|
||||
<v8:Digits>15</v8:Digits>
|
||||
<v8:FractionDigits>2</v8:FractionDigits>
|
||||
<v8:AllowedSign>Any</v8:AllowedSign>
|
||||
</v8:NumberQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Сумма ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -26,10 +33,10 @@
|
||||
</dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>Основной</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Валюта</dataPath>
|
||||
<field>Валюта</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Банк</dataPath>
|
||||
<field>Банк</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>ИНН</dataPath>
|
||||
<field>ИНН</field>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ Т.Валюта, Т.Банк, Т.ИНН ИЗ Регистр КАК Т</query>
|
||||
</dataSet>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:name>ДанныеОтчета</dcsset:name>
|
||||
<dcsset:groupItems>
|
||||
<dcsset:item xsi:type="dcsset:GroupItemField">
|
||||
<dcsset:field>Валюта</dcsset:field>
|
||||
<dcsset:groupType>Items</dcsset:groupType>
|
||||
<dcsset:periodAdditionType>None</dcsset:periodAdditionType>
|
||||
<dcsset:periodAdditionBegin xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionBegin>
|
||||
<dcsset:periodAdditionEnd xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionEnd>
|
||||
</dcsset:item>
|
||||
<dcsset:item xsi:type="dcsset:GroupItemField">
|
||||
<dcsset:field>Банк</dcsset:field>
|
||||
<dcsset:groupType>Items</dcsset:groupType>
|
||||
<dcsset:periodAdditionType>None</dcsset:periodAdditionType>
|
||||
<dcsset:periodAdditionBegin xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionBegin>
|
||||
<dcsset:periodAdditionEnd xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionEnd>
|
||||
</dcsset:item>
|
||||
<dcsset:item xsi:type="dcsset:GroupItemField">
|
||||
<dcsset:field>ИНН</dcsset:field>
|
||||
<dcsset:groupType>Items</dcsset:groupType>
|
||||
<dcsset:periodAdditionType>None</dcsset:periodAdditionType>
|
||||
<dcsset:periodAdditionBegin xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionBegin>
|
||||
<dcsset:periodAdditionEnd xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionEnd>
|
||||
</dcsset:item>
|
||||
</dcsset:groupItems>
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
<dcsset:selection>
|
||||
</dcsset:selection>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
@@ -77,10 +84,10 @@
|
||||
</dcsset:item>
|
||||
</dcsset:groupItems>
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto" />
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemField">
|
||||
<dcsset:field>Счет</dcsset:field>
|
||||
</dcsset:item>
|
||||
|
||||
@@ -0,0 +1,887 @@
|
||||
// build-webtest-config.test.mjs — Integration test: build synthetic configuration for web-test regression
|
||||
// Extends base-config with: diverse field types, hierarchical catalog, two-tab form,
|
||||
// second subsystem, full-rights role.
|
||||
// Steps: cf-init → meta-compile → form-add + form-compile → skd-compile
|
||||
// → subsystem-compile → role-compile → cf-validate
|
||||
|
||||
export const name = 'Сборка конфигурации для web-test';
|
||||
export const setup = 'none';
|
||||
export const cache = 'webtest-config';
|
||||
|
||||
export const steps = [
|
||||
// ── 1. Init empty configuration ──
|
||||
{
|
||||
name: 'cf-init: пустая конфигурация',
|
||||
script: 'cf-init/scripts/cf-init',
|
||||
args: { '-Name': 'ТестоваяВебКонфигурация', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'cf-validate/scripts/cf-validate', flag: '-ConfigPath' },
|
||||
},
|
||||
|
||||
// ── 2. Metadata objects ──
|
||||
|
||||
// Справочник Контрагенты — простой, для CRUD и ссылочных полей
|
||||
{
|
||||
name: 'meta-compile: Справочник Контрагенты',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Catalog', name: 'Контрагенты',
|
||||
codeLength: 9, descriptionLength: 100,
|
||||
attributes: [
|
||||
{ name: 'ИНН', type: 'String', length: 12 },
|
||||
{ name: 'Телефон', type: 'String', length: 20 },
|
||||
{ name: 'Адрес', type: 'String', length: 200 },
|
||||
{ name: 'КодКПП', type: 'String', length: 9 },
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Catalogs/Контрагенты' },
|
||||
},
|
||||
|
||||
// Справочник Организации — маленький список с быстрым выбором (selectValue dropdown)
|
||||
{
|
||||
name: 'meta-compile: Справочник Организации',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Catalog', name: 'Организации',
|
||||
codeLength: 9, descriptionLength: 100,
|
||||
quickChoice: true,
|
||||
attributes: [
|
||||
{ name: 'ИНН', type: 'String', length: 12 },
|
||||
{ name: 'КПП', type: 'String', length: 9 },
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Catalogs/Организации' },
|
||||
},
|
||||
|
||||
// Подчинённый каталог КонтактныеЛица — для теста getFormState.navigation (subordinate-nav)
|
||||
{
|
||||
name: 'meta-compile: Справочник КонтактныеЛица (подчинённый Контрагентам)',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Catalog', name: 'КонтактныеЛица',
|
||||
codeLength: 9, descriptionLength: 100,
|
||||
owners: ['Catalog.Контрагенты'],
|
||||
attributes: [
|
||||
{ name: 'Должность', type: 'String', length: 100 },
|
||||
{ name: 'Телефон', type: 'String', length: 20 },
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Catalogs/КонтактныеЛица' },
|
||||
},
|
||||
|
||||
// Справочник Номенклатура — иерархический, все типы полей
|
||||
{
|
||||
name: 'meta-compile: Справочник Номенклатура',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Catalog', name: 'Номенклатура',
|
||||
codeLength: 11, descriptionLength: 150,
|
||||
hierarchical: true,
|
||||
attributes: [
|
||||
{ name: 'Артикул', type: 'String', length: 25 },
|
||||
{ name: 'Цена', type: 'Number', length: 15, precision: 2 },
|
||||
{ name: 'Активен', type: 'Boolean' },
|
||||
{ name: 'ДатаПоступления', type: 'Date' },
|
||||
{ name: 'Комментарий', type: 'String' },
|
||||
{ name: 'ЕдиницаИзмерения', type: 'String', length: 10 },
|
||||
{ name: 'ВидНоменклатуры', type: 'EnumRef.ВидыНоменклатуры' },
|
||||
{ name: 'КатегорияЦены', type: 'EnumRef.КатегорииЦен' },
|
||||
{ name: 'СпособУчёта', type: 'EnumRef.СпособыУчёта' },
|
||||
],
|
||||
fillChecking: { 'Description': 'ShowError' },
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Catalogs/Номенклатура' },
|
||||
},
|
||||
|
||||
// Перечисление ВидыНоменклатуры
|
||||
{
|
||||
name: 'meta-compile: Перечисление ВидыНоменклатуры',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Enum', name: 'ВидыНоменклатуры',
|
||||
values: ['Товар', 'Услуга', 'Работа'],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Enums/ВидыНоменклатуры' },
|
||||
},
|
||||
|
||||
// Перечисление КатегорииЦен — для будущего radio-button теста (fillFields branch #3)
|
||||
{
|
||||
name: 'meta-compile: Перечисление КатегорииЦен',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Enum', name: 'КатегорииЦен',
|
||||
values: ['Розничная', 'Оптовая', 'Закупочная'],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Enums/КатегорииЦен' },
|
||||
},
|
||||
|
||||
// Перечисление СпособыУчёта — для radio с видом Tumbler (fillFields branch #3)
|
||||
{
|
||||
name: 'meta-compile: Перечисление СпособыУчёта',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Enum', name: 'СпособыУчёта',
|
||||
values: ['ПоСреднему', 'ФИФО'],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Enums/СпособыУчёта' },
|
||||
},
|
||||
|
||||
// Документ ПриходнаяНакладная — шапка + ТЧ
|
||||
{
|
||||
name: 'meta-compile: Документ ПриходнаяНакладная',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Document', name: 'ПриходнаяНакладная',
|
||||
attributes: [
|
||||
{ name: 'Организация', type: 'CatalogRef.Организации' },
|
||||
// choiceHistoryOnInput=DontUse: предотвращает выбор через историю в smoke-тестах
|
||||
// (04-selectvalue/direct-form проверяет open-form path; история обходит его).
|
||||
{ name: 'Контрагент', type: 'CatalogRef.Контрагенты', choiceHistoryOnInput: 'DontUse' },
|
||||
{ name: 'Склад', type: 'String', length: 50 },
|
||||
// Источник — составной тип (для 03-fillfields/composite).
|
||||
// Платформа покажет селектор типа в UI перед выбором значения.
|
||||
{ name: 'Источник', type: 'CatalogRef.Контрагенты + CatalogRef.Номенклатура + CatalogRef.Организации' },
|
||||
// Поставщик — обычная ссылка, но на форме элемент с textEdit:false
|
||||
// (для 03-fillfields/direct-edit-form). Ручной ввод запрещён,
|
||||
// выбор только через pick-кнопку → форма выбора.
|
||||
{ name: 'Поставщик', type: 'CatalogRef.Контрагенты' },
|
||||
// Менеджер — ссылка с дефолтным choiceHistoryOnInput=Auto (история включена,
|
||||
// для 04-selectvalue/show-all-form). После первого выбора платформа
|
||||
// запоминает значение и при повторном вводе показывает dropdown
|
||||
// с историей + кнопку «Показать все» → форма выбора.
|
||||
{ name: 'Менеджер', type: 'CatalogRef.Контрагенты' },
|
||||
{ name: 'Комментарий', type: 'String', length: 200 },
|
||||
],
|
||||
tabularSections: [{
|
||||
name: 'Товары',
|
||||
attributes: [
|
||||
{ name: 'Номенклатура', type: 'CatalogRef.Номенклатура' },
|
||||
{ name: 'Количество', type: 'Number', length: 15, precision: 3 },
|
||||
{ name: 'Цена', type: 'Number', length: 15, precision: 2 },
|
||||
{ name: 'Сумма', type: 'Number', length: 15, precision: 2 },
|
||||
{ name: 'Согласовано', type: 'Boolean' },
|
||||
// Источник — составной тип в ТЧ (для edit-dblclick через выбор типа)
|
||||
{ name: 'Источник', type: 'CatalogRef.Контрагенты + CatalogRef.Номенклатура + CatalogRef.Организации' },
|
||||
],
|
||||
}],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Documents/ПриходнаяНакладная' },
|
||||
},
|
||||
|
||||
// Регистр сведений КурсыВалют (Independent — без регистратора)
|
||||
{
|
||||
name: 'meta-compile: Регистр сведений КурсыВалют',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'InformationRegister', name: 'КурсыВалют',
|
||||
writeMode: 'Independent',
|
||||
dimensions: [
|
||||
{ name: 'Валюта', type: 'String', length: 10 },
|
||||
],
|
||||
resources: [
|
||||
{ name: 'Курс', type: 'Number', length: 10, precision: 4 },
|
||||
{ name: 'Кратность', type: 'Number', length: 10 },
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'InformationRegisters/КурсыВалют' },
|
||||
},
|
||||
|
||||
// Константа ОсновнаяВалюта
|
||||
{
|
||||
name: 'meta-compile: Константа ОсновнаяВалюта',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Constant', name: 'ОсновнаяВалюта',
|
||||
valueType: 'String', length: 10,
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Constants/ОсновнаяВалюта' },
|
||||
},
|
||||
|
||||
// Константа ДанныеЗаполнены — флаг первоначального заполнения фикстур
|
||||
{
|
||||
name: 'meta-compile: Константа ДанныеЗаполнены',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Constant', name: 'ДанныеЗаполнены',
|
||||
valueType: 'Boolean',
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Constants/ДанныеЗаполнены' },
|
||||
},
|
||||
|
||||
// Общий модуль ОбщиеФункции
|
||||
{
|
||||
name: 'meta-compile: Общий модуль ОбщиеФункции',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'CommonModule', name: 'ОбщиеФункции',
|
||||
server: true, serverCall: true, clientManagedApplication: false,
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'CommonModules/ОбщиеФункции' },
|
||||
},
|
||||
{
|
||||
name: 'writeFile: ОбщиеФункции Module.bsl',
|
||||
writeFile: 'CommonModules/ОбщиеФункции/Ext/Module.bsl',
|
||||
content: `Процедура ПоказатьСообщение() Экспорт
|
||||
\tСообщить("Тестовое сообщение");
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура ВызватьТестовоеИсключение() Экспорт
|
||||
\tВызватьИсключение "Тестовое исключение";
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура ЗаполнитьФикстурыЕслиНужно() Экспорт
|
||||
\tЕсли Константы.ДанныеЗаполнены.Получить() Тогда
|
||||
\t\tВозврат;
|
||||
\tКонецЕсли;
|
||||
\tНачатьТранзакцию();
|
||||
\tПопытка
|
||||
\t\tЗаполнитьОрганизации();
|
||||
\t\tЗаполнитьКонтрагентов();
|
||||
\t\tЗаполнитьНоменклатуру();
|
||||
\t\tЗаполнитьДокументы();
|
||||
\t\tКонстанты.ДанныеЗаполнены.Установить(Истина);
|
||||
\t\tЗафиксироватьТранзакцию();
|
||||
\tИсключение
|
||||
\t\tОтменитьТранзакцию();
|
||||
\t\tВызватьИсключение;
|
||||
\tКонецПопытки;
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура ЗаполнитьОрганизации()
|
||||
\tСписок = Новый Массив;
|
||||
\tСписок.Добавить(Новый Структура("Имя,ИНН,КПП", "Альфа", "7800000001", "780000001"));
|
||||
\tСписок.Добавить(Новый Структура("Имя,ИНН,КПП", "Бета", "7800000002", "780000002"));
|
||||
\tДля Каждого Запись Из Список Цикл
|
||||
\t\tЭлемент = Справочники.Организации.СоздатьЭлемент();
|
||||
\t\tЭлемент.Наименование = Запись.Имя;
|
||||
\t\tЭлемент.ИНН = Запись.ИНН;
|
||||
\t\tЭлемент.КПП = Запись.КПП;
|
||||
\t\tЭлемент.Записать();
|
||||
\tКонецЦикла;
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура ЗаполнитьКонтрагентов()
|
||||
\tСписок = Новый Массив;
|
||||
\tСписок.Добавить(Новый Структура("Имя,ИНН", "ООО Север", "7700000001"));
|
||||
\tСписок.Добавить(Новый Структура("Имя,ИНН", "ООО Юг", "7700000002"));
|
||||
\tСписок.Добавить(Новый Структура("Имя,ИНН", "ООО Восток", "7700000003"));
|
||||
\tСписок.Добавить(Новый Структура("Имя,ИНН", "АО Запад", "7700000004"));
|
||||
\tДля Каждого Запись Из Список Цикл
|
||||
\t\tЭлемент = Справочники.Контрагенты.СоздатьЭлемент();
|
||||
\t\tЭлемент.Наименование = Запись.Имя;
|
||||
\t\tЭлемент.ИНН = Запись.ИНН;
|
||||
\t\tЭлемент.Записать();
|
||||
\tКонецЦикла;
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура ЗаполнитьНоменклатуру()
|
||||
\tГруппаТовары = СоздатьГруппуНоменклатуры("Товары");
|
||||
\tГруппаУслуги = СоздатьГруппуНоменклатуры("Услуги");
|
||||
\tДля Сч = 1 По 15 Цикл
|
||||
\t\tЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
|
||||
\t\tЭлемент.Родитель = ГруппаТовары;
|
||||
\t\tЭлемент.Наименование = "Товар " + Формат(Сч, "ЧЦ=2; ЧВН=");
|
||||
\t\tЭлемент.Артикул = "T" + Формат(Сч, "ЧЦ=4; ЧВН=");
|
||||
\t\tЭлемент.Цена = 100 * Сч;
|
||||
\t\tЭлемент.Активен = Истина;
|
||||
\t\tЭлемент.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Товар;
|
||||
\t\tЭлемент.Записать();
|
||||
\tКонецЦикла;
|
||||
\tДля Сч = 1 По 10 Цикл
|
||||
\t\tЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
|
||||
\t\tЭлемент.Родитель = ГруппаУслуги;
|
||||
\t\tЭлемент.Наименование = "Услуга " + Формат(Сч, "ЧЦ=2; ЧВН=");
|
||||
\t\tЭлемент.Артикул = "U" + Формат(Сч, "ЧЦ=4; ЧВН=");
|
||||
\t\tЭлемент.Цена = 500 * Сч;
|
||||
\t\tЭлемент.Активен = Истина;
|
||||
\t\tЭлемент.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Услуга;
|
||||
\t\tЭлемент.Записать();
|
||||
\tКонецЦикла;
|
||||
КонецПроцедуры
|
||||
|
||||
Функция СоздатьГруппуНоменклатуры(Имя)
|
||||
\tГруппа = Справочники.Номенклатура.СоздатьГруппу();
|
||||
\tГруппа.Наименование = Имя;
|
||||
\tГруппа.Записать();
|
||||
\tВозврат Группа.Ссылка;
|
||||
КонецФункции
|
||||
|
||||
Процедура ЗаполнитьДокументы()
|
||||
\tЗапросК = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 5 Контрагенты.Ссылка КАК Контрагент ИЗ Справочник.Контрагенты КАК Контрагенты");
|
||||
\tКонтрагенты = ЗапросК.Выполнить().Выгрузить().ВыгрузитьКолонку("Контрагент");
|
||||
\tЗапросН = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 10 Номенклатура.Ссылка КАК Номенклатура ИЗ Справочник.Номенклатура КАК Номенклатура ГДЕ НЕ Номенклатура.ЭтоГруппа");
|
||||
\tНоменклатура = ЗапросН.Выполнить().Выгрузить().ВыгрузитьКолонку("Номенклатура");
|
||||
\tЕсли Контрагенты.Количество() = 0 Или Номенклатура.Количество() = 0 Тогда
|
||||
\t\tВозврат;
|
||||
\tКонецЕсли;
|
||||
\tЗапросО = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 1 Организации.Ссылка КАК Организация ИЗ Справочник.Организации КАК Организации");
|
||||
\tВыборкаО = ЗапросО.Выполнить().Выбрать();
|
||||
\tОрганизация = Неопределено;
|
||||
\tЕсли ВыборкаО.Следующий() Тогда
|
||||
\t\tОрганизация = ВыборкаО.Организация;
|
||||
\tКонецЕсли;
|
||||
\tДля Сч = 1 По 3 Цикл
|
||||
\t\tДок = Документы.ПриходнаяНакладная.СоздатьДокумент();
|
||||
\t\tДок.Дата = ТекущаяДата();
|
||||
\t\tДок.Организация = Организация;
|
||||
\t\tДок.Контрагент = Контрагенты[(Сч - 1) % Контрагенты.Количество()];
|
||||
\t\tДок.Склад = "Основной";
|
||||
\t\tДля Поз = 1 По 3 Цикл
|
||||
\t\t\tСтрока = Док.Товары.Добавить();
|
||||
\t\t\tСтрока.Номенклатура = Номенклатура[(Сч * Поз) % Номенклатура.Количество()];
|
||||
\t\t\tСтрока.Количество = Поз * 10;
|
||||
\t\t\tСтрока.Цена = Поз * 100;
|
||||
\t\t\tСтрока.Сумма = Строка.Количество * Строка.Цена;
|
||||
\t\tКонецЦикла;
|
||||
\t\tДок.Записать(РежимЗаписиДокумента.Запись);
|
||||
\tКонецЦикла;
|
||||
КонецПроцедуры
|
||||
`,
|
||||
},
|
||||
|
||||
// ManagedApplicationModule — вызывает заполнение фикстур при первом запуске
|
||||
{
|
||||
name: 'writeFile: ManagedApplicationModule.bsl',
|
||||
writeFile: 'Ext/ManagedApplicationModule.bsl',
|
||||
content: `&НаКлиенте
|
||||
Процедура ПриНачалеРаботыСистемы()
|
||||
\tОбщиеФункции.ЗаполнитьФикстурыЕслиНужно();
|
||||
КонецПроцедуры
|
||||
`,
|
||||
},
|
||||
|
||||
// Раскладка панелей (Ext/ClientApplicationInterface.xml) теперь создаётся
|
||||
// самим cf-init с ERP-дефолтом — отдельная запись больше не нужна.
|
||||
|
||||
// Обработка ТестовыеОшибки — для тестов errors balloon/messages/modal (10-validation)
|
||||
{
|
||||
name: 'meta-compile: Обработка ТестовыеОшибки',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'DataProcessor', name: 'ТестовыеОшибки',
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'DataProcessors/ТестовыеОшибки' },
|
||||
},
|
||||
|
||||
// Обработка ДеревоНоменклатуры — реквизит формы ДеревоЗначений с данными
|
||||
// справочника Номенклатура для тестов tree-grid (05-table/direct-edit-form,
|
||||
// 08-hierarchy/tree-edit).
|
||||
{
|
||||
name: 'meta-compile: Обработка ДеревоНоменклатуры',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'DataProcessor', name: 'ДеревоНоменклатуры',
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'DataProcessors/ДеревоНоменклатуры' },
|
||||
},
|
||||
|
||||
// Отчёт ОстаткиТоваров
|
||||
{
|
||||
name: 'meta-compile: Отчёт ОстаткиТоваров',
|
||||
script: 'meta-compile/scripts/meta-compile',
|
||||
input: {
|
||||
type: 'Report', name: 'ОстаткиТоваров',
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'Reports/ОстаткиТоваров' },
|
||||
},
|
||||
|
||||
// ── 3. Forms ──
|
||||
|
||||
// Форма элемента Контрагенты — простая
|
||||
{
|
||||
name: 'form-add: Форма элемента Контрагенты',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/Catalogs/Контрагенты.xml', '-FormName': 'ФормаЭлемента' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма элемента Контрагенты',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Контрагент',
|
||||
attributes: [
|
||||
{ name: 'Объект', type: 'CatalogObject.Контрагенты', main: true },
|
||||
],
|
||||
elements: [
|
||||
{ input: 'Наименование', path: 'Объект.Description', title: 'Наименование' },
|
||||
{ input: 'ИНН', path: 'Объект.ИНН', title: 'ИНН' },
|
||||
{ input: 'Телефон', path: 'Объект.Телефон', title: 'Телефон' },
|
||||
{ input: 'Адрес', path: 'Объект.Адрес', title: 'Адрес' },
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/Catalogs/Контрагенты/Forms/ФормаЭлемента/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'Catalogs/Контрагенты/Forms/ФормаЭлемента/Ext/Form.xml' },
|
||||
},
|
||||
|
||||
// Форма элемента КонтактныеЛица + список — для подчинённого каталога
|
||||
{
|
||||
name: 'form-add: Форма элемента КонтактныеЛица',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/Catalogs/КонтактныеЛица.xml', '-FormName': 'ФормаЭлемента' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма элемента КонтактныеЛица',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Контактное лицо',
|
||||
attributes: [
|
||||
{ name: 'Объект', type: 'CatalogObject.КонтактныеЛица', main: true },
|
||||
],
|
||||
elements: [
|
||||
{ input: 'Владелец', path: 'Объект.Owner', title: 'Контрагент' },
|
||||
{ input: 'Наименование', path: 'Объект.Description', title: 'ФИО' },
|
||||
{ input: 'Должность', path: 'Объект.Должность', title: 'Должность' },
|
||||
{ input: 'Телефон', path: 'Объект.Телефон', title: 'Телефон' },
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/Catalogs/КонтактныеЛица/Forms/ФормаЭлемента/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'Catalogs/КонтактныеЛица/Forms/ФормаЭлемента/Ext/Form.xml' },
|
||||
},
|
||||
{
|
||||
name: 'form-add: Форма списка КонтактныеЛица',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/Catalogs/КонтактныеЛица.xml', '-FormName': 'ФормаСписка', '-Purpose': 'List' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма списка КонтактныеЛица',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Контактные лица',
|
||||
attributes: [
|
||||
{ name: 'Список', type: 'DynamicList', main: true,
|
||||
settings: { mainTable: 'Catalog.КонтактныеЛица', dynamicDataRead: true } },
|
||||
],
|
||||
elements: [
|
||||
{ table: 'Список', path: 'Список', columns: [
|
||||
{ input: 'Description', path: 'Список.Description', title: 'ФИО' },
|
||||
{ input: 'Должность', path: 'Список.Должность', title: 'Должность' },
|
||||
{ input: 'Телефон', path: 'Список.Телефон', title: 'Телефон' },
|
||||
]},
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/Catalogs/КонтактныеЛица/Forms/ФормаСписка/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'Catalogs/КонтактныеЛица/Forms/ФормаСписка/Ext/Form.xml' },
|
||||
},
|
||||
|
||||
// Форма списка Контрагенты — для filterList тестов. КодКПП НЕ выводим
|
||||
// в форму — это покрывает FieldSelector DLB ветку (filterList #5)
|
||||
{
|
||||
name: 'form-add: Форма списка Контрагенты',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/Catalogs/Контрагенты.xml', '-FormName': 'ФормаСписка', '-Purpose': 'List' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма списка Контрагенты',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Контрагенты',
|
||||
attributes: [
|
||||
{ name: 'Список', type: 'DynamicList', main: true,
|
||||
settings: { mainTable: 'Catalog.Контрагенты', dynamicDataRead: true } },
|
||||
],
|
||||
elements: [
|
||||
{ table: 'Список', path: 'Список', columns: [
|
||||
{ input: 'Code', path: 'Список.Code', title: 'Код' },
|
||||
{ input: 'Description', path: 'Список.Description', title: 'Наименование' },
|
||||
{ input: 'ИНН', path: 'Список.ИНН', title: 'ИНН' },
|
||||
{ input: 'Телефон', path: 'Список.Телефон', title: 'Телефон' },
|
||||
{ input: 'Адрес', path: 'Список.Адрес', title: 'Адрес' },
|
||||
]},
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/Catalogs/Контрагенты/Forms/ФормаСписка/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'Catalogs/Контрагенты/Forms/ФормаСписка/Ext/Form.xml' },
|
||||
},
|
||||
|
||||
// Форма элемента Номенклатура — 2 вкладки, все типы полей
|
||||
{
|
||||
name: 'form-add: Форма элемента Номенклатура',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/Catalogs/Номенклатура.xml', '-FormName': 'ФормаЭлемента' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма элемента Номенклатура (2 вкладки)',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Номенклатура',
|
||||
attributes: [
|
||||
{ name: 'Объект', type: 'CatalogObject.Номенклатура', main: true },
|
||||
],
|
||||
elements: [
|
||||
{ pages: 'Страницы', pagesRepresentation: 'TabsOnTop', children: [
|
||||
{ page: 'Основное', title: 'Основное', children: [
|
||||
{ input: 'Наименование', path: 'Объект.Description', title: 'Наименование' },
|
||||
{ input: 'Артикул', path: 'Объект.Артикул', title: 'Артикул' },
|
||||
{ input: 'ВидНоменклатуры', path: 'Объект.ВидНоменклатуры', title: 'Вид номенклатуры' },
|
||||
{ input: 'Цена', path: 'Объект.Цена', title: 'Цена' },
|
||||
{ radio: 'КатегорияЦены', path: 'Объект.КатегорияЦены',
|
||||
title: 'Категория цены',
|
||||
radioButtonType: 'RadioButtons',
|
||||
titleLocation: 'Top',
|
||||
choiceList: [
|
||||
{ value: 'Enum.КатегорииЦен.EnumValue.Розничная', presentation: 'Розничная' },
|
||||
{ value: 'Enum.КатегорииЦен.EnumValue.Оптовая', presentation: 'Оптовая' },
|
||||
{ value: 'Enum.КатегорииЦен.EnumValue.Закупочная', presentation: 'Закупочная' },
|
||||
],
|
||||
},
|
||||
{ radio: 'СпособУчёта', path: 'Объект.СпособУчёта',
|
||||
title: 'Способ учёта',
|
||||
radioButtonType: 'Tumbler',
|
||||
titleLocation: 'Top',
|
||||
choiceList: [
|
||||
{ value: 'Enum.СпособыУчёта.EnumValue.ПоСреднему', presentation: 'По среднему' },
|
||||
{ value: 'Enum.СпособыУчёта.EnumValue.ФИФО', presentation: 'ФИФО' },
|
||||
],
|
||||
},
|
||||
{ check: 'Активен', path: 'Объект.Активен', title: 'Активен' },
|
||||
{ input: 'ДатаПоступления', path: 'Объект.ДатаПоступления', title: 'Дата поступления' },
|
||||
]},
|
||||
{ page: 'Дополнительно', title: 'Дополнительно', children: [
|
||||
{ input: 'ЕдиницаИзмерения', path: 'Объект.ЕдиницаИзмерения', title: 'Единица измерения' },
|
||||
{ input: 'Комментарий', path: 'Объект.Комментарий', title: 'Комментарий' },
|
||||
]},
|
||||
]},
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/Catalogs/Номенклатура/Forms/ФормаЭлемента/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'Catalogs/Номенклатура/Forms/ФормаЭлемента/Ext/Form.xml' },
|
||||
},
|
||||
|
||||
// Форма списка Номенклатура — с колонкой ДатаПоступления для filterList #6 (date pattern)
|
||||
{
|
||||
name: 'form-add: Форма списка Номенклатура',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/Catalogs/Номенклатура.xml', '-FormName': 'ФормаСписка', '-Purpose': 'List' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма списка Номенклатура',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Номенклатура',
|
||||
attributes: [
|
||||
{ name: 'Список', type: 'DynamicList', main: true,
|
||||
settings: { mainTable: 'Catalog.Номенклатура', dynamicDataRead: true } },
|
||||
],
|
||||
elements: [
|
||||
{ table: 'Список', path: 'Список', columns: [
|
||||
{ input: 'Code', path: 'Список.Code', title: 'Код' },
|
||||
{ input: 'Description', path: 'Список.Description', title: 'Наименование' },
|
||||
{ input: 'Артикул', path: 'Список.Артикул', title: 'Артикул' },
|
||||
{ input: 'ВидНоменклатуры', path: 'Список.ВидНоменклатуры', title: 'Вид номенклатуры' },
|
||||
{ input: 'ДатаПоступления', path: 'Список.ДатаПоступления', title: 'Дата поступления' },
|
||||
{ input: 'Цена', path: 'Список.Цена', title: 'Цена' },
|
||||
{ check: 'Активен', path: 'Список.Активен', title: 'Активен' },
|
||||
]},
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/Catalogs/Номенклатура/Forms/ФормаСписка/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'Catalogs/Номенклатура/Forms/ФормаСписка/Ext/Form.xml' },
|
||||
},
|
||||
|
||||
// Форма документа ПриходнаяНакладная
|
||||
{
|
||||
name: 'form-add: Форма документа ПриходнаяНакладная',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/Documents/ПриходнаяНакладная.xml', '-FormName': 'ФормаДокумента' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма документа ПриходнаяНакладная',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Приходная накладная',
|
||||
attributes: [
|
||||
{ name: 'Объект', type: 'DocumentObject.ПриходнаяНакладная', main: true },
|
||||
],
|
||||
elements: [
|
||||
{ input: 'Организация', path: 'Объект.Организация', title: 'Организация' },
|
||||
{ input: 'Контрагент', path: 'Объект.Контрагент', title: 'Контрагент' },
|
||||
{ input: 'Склад', path: 'Объект.Склад', title: 'Склад' },
|
||||
{ input: 'Источник', path: 'Объект.Источник', title: 'Источник' },
|
||||
// textEdit:false — ручной ввод запрещён, только pick → форма выбора
|
||||
{ input: 'Поставщик', path: 'Объект.Поставщик', title: 'Поставщик', textEdit: false },
|
||||
{ input: 'Менеджер', path: 'Объект.Менеджер', title: 'Менеджер' },
|
||||
{ input: 'Комментарий', path: 'Объект.Комментарий', title: 'Комментарий' },
|
||||
{ table: 'Товары', path: 'Объект.Товары', title: 'Товары', changeRowSet: true, columns: [
|
||||
{ input: 'Номенклатура', path: 'Объект.Товары.Номенклатура', title: 'Номенклатура' },
|
||||
{ input: 'Количество', path: 'Объект.Товары.Количество', title: 'Количество' },
|
||||
{ input: 'Цена', path: 'Объект.Товары.Цена', title: 'Цена' },
|
||||
{ input: 'Сумма', path: 'Объект.Товары.Сумма', title: 'Сумма' },
|
||||
{ check: 'Согласовано', path: 'Объект.Товары.Согласовано', title: 'Согласовано' },
|
||||
// Имя элемента отличается от Источник (в шапке) — иначе ContextMenu
|
||||
// companion-имена дублируются в одной форме. form-compile использует
|
||||
// имя элемента, не путь, для генерации companion-имён.
|
||||
{ input: 'ИсточникТЧ', path: 'Объект.Товары.Источник', title: 'Источник' },
|
||||
]},
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/Documents/ПриходнаяНакладная/Forms/ФормаДокумента/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'Documents/ПриходнаяНакладная/Forms/ФормаДокумента/Ext/Form.xml' },
|
||||
},
|
||||
|
||||
// Форма списка ПриходнаяНакладная — с колонкой Контрагент для filterList #7 (reference pattern)
|
||||
{
|
||||
name: 'form-add: Форма списка ПриходнаяНакладная',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/Documents/ПриходнаяНакладная.xml', '-FormName': 'ФормаСписка', '-Purpose': 'List' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма списка ПриходнаяНакладная',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Приходные накладные',
|
||||
attributes: [
|
||||
{ name: 'Список', type: 'DynamicList', main: true,
|
||||
settings: { mainTable: 'Document.ПриходнаяНакладная', dynamicDataRead: true } },
|
||||
],
|
||||
elements: [
|
||||
{ table: 'Список', path: 'Список', columns: [
|
||||
{ input: 'Date', path: 'Список.Date', title: 'Дата' },
|
||||
{ input: 'Number', path: 'Список.Number', title: 'Номер' },
|
||||
{ input: 'Контрагент', path: 'Список.Контрагент', title: 'Контрагент' },
|
||||
{ input: 'Posted', path: 'Список.Posted', title: 'Проведён' },
|
||||
]},
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/Documents/ПриходнаяНакладная/Forms/ФормаСписка/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'Documents/ПриходнаяНакладная/Forms/ФормаСписка/Ext/Form.xml' },
|
||||
},
|
||||
|
||||
// Форма обработки ТестовыеОшибки — кнопки вызова процедур ОбщиеФункции
|
||||
{
|
||||
name: 'form-add: Форма обработки ТестовыеОшибки',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/DataProcessors/ТестовыеОшибки.xml', '-FormName': 'ФормаОбработки' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма обработки ТестовыеОшибки',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Тестовые ошибки',
|
||||
attributes: [
|
||||
{ name: 'Объект', type: 'DataProcessorObject.ТестовыеОшибки', main: true },
|
||||
],
|
||||
elements: [
|
||||
{ button: 'ПоказатьСообщение', command: 'ПоказатьСообщение', title: 'Показать сообщение' },
|
||||
{ button: 'ВызватьИсключение', command: 'ВызватьИсключениеКоманда', title: 'Вызвать исключение' },
|
||||
],
|
||||
commands: [
|
||||
{ name: 'ПоказатьСообщение', action: 'ПоказатьСообщение' },
|
||||
{ name: 'ВызватьИсключениеКоманда', action: 'ВызватьИсключениеКоманда' },
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/DataProcessors/ТестовыеОшибки/Forms/ФормаОбработки/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'DataProcessors/ТестовыеОшибки/Forms/ФормаОбработки/Ext/Form.xml' },
|
||||
},
|
||||
{
|
||||
name: 'writeFile: ТестовыеОшибки form Module.bsl',
|
||||
writeFile: 'DataProcessors/ТестовыеОшибки/Forms/ФормаОбработки/Ext/Form/Module.bsl',
|
||||
content: `&НаКлиенте
|
||||
Процедура ПоказатьСообщение(Команда)
|
||||
\tПоказатьСообщениеНаСервере();
|
||||
КонецПроцедуры
|
||||
|
||||
&НаСервере
|
||||
Процедура ПоказатьСообщениеНаСервере()
|
||||
\tОбщиеФункции.ПоказатьСообщение();
|
||||
КонецПроцедуры
|
||||
|
||||
&НаКлиенте
|
||||
Процедура ВызватьИсключениеКоманда(Команда)
|
||||
\tВызватьИсключениеНаСервере();
|
||||
КонецПроцедуры
|
||||
|
||||
&НаСервере
|
||||
Процедура ВызватьИсключениеНаСервере()
|
||||
\tОбщиеФункции.ВызватьТестовоеИсключение();
|
||||
КонецПроцедуры
|
||||
`,
|
||||
},
|
||||
|
||||
// Форма обработки ДеревоНоменклатуры — tree-grid с двумя колонками
|
||||
{
|
||||
name: 'form-add: Форма обработки ДеревоНоменклатуры',
|
||||
script: 'form-add/scripts/form-add',
|
||||
args: { '-ObjectPath': '{workDir}/DataProcessors/ДеревоНоменклатуры.xml', '-FormName': 'ФормаОбработки' },
|
||||
},
|
||||
{
|
||||
name: 'form-compile: Форма обработки ДеревоНоменклатуры',
|
||||
script: 'form-compile/scripts/form-compile',
|
||||
input: {
|
||||
title: 'Дерево номенклатуры',
|
||||
events: { OnCreateAtServer: 'ПриСозданииНаСервере' },
|
||||
attributes: [
|
||||
{ name: 'Объект', type: 'DataProcessorObject.ДеревоНоменклатуры', main: true },
|
||||
{ name: 'Дерево', type: 'ValueTree', columns: [
|
||||
{ name: 'Номенклатура', type: 'CatalogRef.Номенклатура', title: 'Номенклатура' },
|
||||
{ name: 'Цена', type: 'Number(15,2)', title: 'Цена' },
|
||||
]},
|
||||
],
|
||||
elements: [
|
||||
{ table: 'Дерево', path: 'Дерево', initialTreeView: 'ExpandTopLevel', changeRowSet: true, columns: [
|
||||
{ input: 'Номенклатура', path: 'Дерево.Номенклатура', readOnly: true, title: 'Номенклатура' },
|
||||
{ input: 'Цена', path: 'Дерево.Цена', title: 'Цена' },
|
||||
]},
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/DataProcessors/ДеревоНоменклатуры/Forms/ФормаОбработки/Ext/Form.xml' },
|
||||
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'DataProcessors/ДеревоНоменклатуры/Forms/ФормаОбработки/Ext/Form.xml' },
|
||||
},
|
||||
{
|
||||
name: 'writeFile: ДеревоНоменклатуры form Module.bsl',
|
||||
writeFile: 'DataProcessors/ДеревоНоменклатуры/Forms/ФормаОбработки/Ext/Form/Module.bsl',
|
||||
content: `&НаСервере
|
||||
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
|
||||
\tЗаполнитьУровень(Дерево.ПолучитьЭлементы(), Справочники.Номенклатура.ПустаяСсылка());
|
||||
КонецПроцедуры
|
||||
|
||||
&НаСервере
|
||||
Процедура ЗаполнитьУровень(КоллекцияЭлементов, Родитель)
|
||||
\tЗапрос = Новый Запрос;
|
||||
\tЗапрос.Текст =
|
||||
\t\t"ВЫБРАТЬ
|
||||
\t\t|\tСсылка, ЭтоГруппа, Цена, Наименование
|
||||
\t\t|ИЗ
|
||||
\t\t|\tСправочник.Номенклатура
|
||||
\t\t|ГДЕ
|
||||
\t\t|\tРодитель = &Родитель
|
||||
\t\t|УПОРЯДОЧИТЬ ПО
|
||||
\t\t|\tЭтоГруппа УБЫВ, Наименование";
|
||||
\tЗапрос.УстановитьПараметр("Родитель", Родитель);
|
||||
\tВыборка = Запрос.Выполнить().Выбрать();
|
||||
\tПока Выборка.Следующий() Цикл
|
||||
\t\tНовыйУзел = КоллекцияЭлементов.Добавить();
|
||||
\t\tНовыйУзел.Номенклатура = Выборка.Ссылка;
|
||||
\t\tНовыйУзел.Цена = Выборка.Цена;
|
||||
\t\tЕсли Выборка.ЭтоГруппа Тогда
|
||||
\t\t\tЗаполнитьУровень(НовыйУзел.ПолучитьЭлементы(), Выборка.Ссылка);
|
||||
\t\tКонецЕсли;
|
||||
\tКонецЦикла;
|
||||
КонецПроцедуры
|
||||
`,
|
||||
},
|
||||
|
||||
// ── 4. DCS for report ──
|
||||
// Сначала добавляем макет ОсновнаяСхемаКомпоновкиДанных к отчёту (регистрируется
|
||||
// в Reports/ОстаткиТоваров.xml + автоматически выставляется MainDataCompositionSchema),
|
||||
// затем skd-compile наполняет его содержимым.
|
||||
{
|
||||
name: 'template-add: ОсновнаяСхемаКомпоновкиДанных к отчёту ОстаткиТоваров',
|
||||
script: 'template-add/scripts/add-template',
|
||||
args: {
|
||||
'-ObjectName': 'ОстаткиТоваров',
|
||||
'-TemplateName': 'ОсновнаяСхемаКомпоновкиДанных',
|
||||
'-TemplateType': 'DataCompositionSchema',
|
||||
'-SrcDir': '{workDir}/Reports',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'skd-compile: Схема отчёта ОстаткиТоваров',
|
||||
script: 'skd-compile/scripts/skd-compile',
|
||||
input: {
|
||||
dataSets: [{
|
||||
name: 'НаборДанных',
|
||||
query: 'ВЫБРАТЬ\n\tТовары.Ссылка КАК Документ,\n\tТовары.Номенклатура КАК Номенклатура,\n\tТовары.Количество КАК Количество,\n\tТовары.Цена КАК Цена,\n\tТовары.Сумма КАК Сумма\nИЗ\n\tДокумент.ПриходнаяНакладная.Товары КАК Товары',
|
||||
fields: [
|
||||
{ field: 'Документ', title: 'Документ', type: 'DocumentRef.ПриходнаяНакладная' },
|
||||
{ field: 'Номенклатура', title: 'Номенклатура', type: 'CatalogRef.Номенклатура' },
|
||||
{ field: 'Количество', title: 'Количество', type: 'decimal(15,3)' },
|
||||
{ field: 'Цена', title: 'Цена', type: 'decimal(15,2)' },
|
||||
{ field: 'Сумма', title: 'Сумма', type: 'decimal(15,2)' },
|
||||
],
|
||||
}],
|
||||
totalFields: ['Количество: Сумма', 'Сумма: Сумма'],
|
||||
settingsVariants: [{
|
||||
name: 'Основной',
|
||||
title: 'Остатки товаров',
|
||||
settings: {
|
||||
selection: ['Номенклатура', 'Количество', 'Сумма', 'Auto'],
|
||||
filter: ['Номенклатура = _ @off @user @quickAccess'],
|
||||
structure: 'Номенклатура > details',
|
||||
},
|
||||
}],
|
||||
},
|
||||
args: { '-DefinitionFile': '{inputFile}', '-OutputPath': '{workDir}/Reports/ОстаткиТоваров/Templates/ОсновнаяСхемаКомпоновкиДанных/Ext/Template.xml' },
|
||||
validate: { script: 'skd-validate/scripts/skd-validate', flag: '-TemplatePath', path: 'Reports/ОстаткиТоваров/Templates/ОсновнаяСхемаКомпоновкиДанных/Ext/Template.xml' },
|
||||
},
|
||||
|
||||
// ── 5. Subsystems ──
|
||||
{
|
||||
name: 'subsystem-compile: Подсистема Склад',
|
||||
script: 'subsystem-compile/scripts/subsystem-compile',
|
||||
input: {
|
||||
name: 'Склад',
|
||||
synonym: 'Склад',
|
||||
content: [
|
||||
'Catalog.Организации',
|
||||
'Catalog.Контрагенты',
|
||||
'Catalog.КонтактныеЛица',
|
||||
'Catalog.Номенклатура',
|
||||
'Enum.ВидыНоменклатуры',
|
||||
'Enum.КатегорииЦен',
|
||||
'Enum.СпособыУчёта',
|
||||
'Document.ПриходнаяНакладная',
|
||||
'Report.ОстаткиТоваров',
|
||||
],
|
||||
},
|
||||
args: { '-DefinitionFile': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'subsystem-validate/scripts/subsystem-validate', flag: '-SubsystemPath', path: 'Subsystems/Склад' },
|
||||
},
|
||||
{
|
||||
name: 'subsystem-compile: Подсистема Администрирование',
|
||||
script: 'subsystem-compile/scripts/subsystem-compile',
|
||||
input: {
|
||||
name: 'Администрирование',
|
||||
synonym: 'Администрирование',
|
||||
content: [
|
||||
'InformationRegister.КурсыВалют',
|
||||
'Constant.ОсновнаяВалюта',
|
||||
'DataProcessor.ТестовыеОшибки',
|
||||
'DataProcessor.ДеревоНоменклатуры',
|
||||
],
|
||||
},
|
||||
args: { '-DefinitionFile': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'subsystem-validate/scripts/subsystem-validate', flag: '-SubsystemPath', path: 'Subsystems/Администрирование' },
|
||||
},
|
||||
|
||||
// ── 6. Role with full rights ──
|
||||
{
|
||||
name: 'role-compile: Роль Администратор',
|
||||
script: 'role-compile/scripts/role-compile',
|
||||
input: {
|
||||
name: 'Администратор',
|
||||
objects: [
|
||||
'Catalog.Организации: Read View Add Update Delete',
|
||||
'Catalog.Контрагенты: Read View Add Update Delete',
|
||||
'Catalog.КонтактныеЛица: Read View Add Update Delete',
|
||||
'Catalog.Номенклатура: Read View Add Update Delete',
|
||||
'Document.ПриходнаяНакладная: Read View Add Update Delete Posting UnPosting',
|
||||
'InformationRegister.КурсыВалют: Read View Add Update Delete',
|
||||
'Report.ОстаткиТоваров: Use View',
|
||||
'DataProcessor.ДеревоНоменклатуры: Use View',
|
||||
],
|
||||
},
|
||||
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
|
||||
validate: { script: 'role-validate/scripts/role-validate', flag: '-RightsPath', path: 'Roles/Администратор' },
|
||||
},
|
||||
|
||||
// ── 7. Final validation ──
|
||||
// (meta-compile, subsystem-compile, role-compile уже регистрируют объекты в Configuration.xml)
|
||||
{
|
||||
name: 'cf-validate: Финальная валидация конфигурации',
|
||||
script: 'cf-validate/scripts/cf-validate',
|
||||
args: { '-ConfigPath': '{workDir}' },
|
||||
},
|
||||
];
|
||||
+82
-2
@@ -217,8 +217,14 @@ function createWorkspace(fixturePath, readOnly) {
|
||||
}
|
||||
|
||||
function cleanupWorkspace(ws) {
|
||||
if (!ws.readOnly) {
|
||||
rmSync(ws.path, { recursive: true, force: true });
|
||||
if (ws.readOnly) return;
|
||||
// On Windows, file handles from db-update (1cv8) may linger briefly after the
|
||||
// process exits — rmSync then throws EBUSY. Retry a few times, then swallow:
|
||||
// a leaked tmp dir is preferable to crashing the entire runner.
|
||||
try {
|
||||
rmSync(ws.path, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 });
|
||||
} catch (e) {
|
||||
console.warn(`Warning: failed to clean workspace ${ws.path}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,6 +342,31 @@ function normalizeContent(text, config) {
|
||||
|
||||
// ─── Snapshot comparison ────────────────────────────────────────────────────
|
||||
|
||||
// Capture raw byte contents of every file in dir, keyed by relative path.
|
||||
// Used by idempotency checks to verify byte-equality after a re-run.
|
||||
function snapshotWorkDirBytes(dir) {
|
||||
const files = listFilesRecursive(dir);
|
||||
const map = new Map();
|
||||
for (const rel of files) {
|
||||
map.set(rel, readFileSync(join(dir, rel)));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// Compare two byte-snapshots. Returns null if identical, else a list of diff lines.
|
||||
function diffByteSnapshots(before, after) {
|
||||
const diffs = [];
|
||||
for (const [rel, b1] of before) {
|
||||
if (!after.has(rel)) { diffs.push(`removed: ${rel}`); continue; }
|
||||
const b2 = after.get(rel);
|
||||
if (b1.length !== b2.length || !b1.equals(b2)) diffs.push(`changed: ${rel} (${b1.length} -> ${b2.length} bytes)`);
|
||||
}
|
||||
for (const rel of after.keys()) {
|
||||
if (!before.has(rel)) diffs.push(`added: ${rel}`);
|
||||
}
|
||||
return diffs.length === 0 ? null : diffs;
|
||||
}
|
||||
|
||||
function listFilesRecursive(dir, base = '') {
|
||||
const result = [];
|
||||
if (!existsSync(dir)) return result;
|
||||
@@ -565,6 +596,23 @@ async function runCaseAsync(testCase, opts) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Idempotency check: re-run the same script with the same args and assert
|
||||
// every file in workDir is byte-identical to the first-run output.
|
||||
if (errors.length === 0 && caseData.idempotent && !workspace.readOnly) {
|
||||
const before = snapshotWorkDirBytes(workDir);
|
||||
try {
|
||||
const execCwd = skillConfig.cwd === 'workDir' ? workDir : undefined;
|
||||
await execSkillAsync(opts.runtime, scriptPath, args, execCwd);
|
||||
} catch (e) {
|
||||
errors.push(`Idempotency rerun failed: exitCode=${e.status}\nstderr: ${(e.stderr || '').substring(0, 300)}`);
|
||||
}
|
||||
if (errors.length === 0) {
|
||||
const after = snapshotWorkDirBytes(workDir);
|
||||
const diffs = diffByteSnapshots(before, after);
|
||||
if (diffs) errors.push(`Idempotency: workspace changed on rerun:\n ${diffs.join('\n ')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Post-run validation (on real output, before cleanup)
|
||||
@@ -721,6 +769,22 @@ function runCase(testCase, opts) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Idempotency check: re-run the same script and assert byte-equality.
|
||||
if (errors.length === 0 && caseData.idempotent && !workspace.readOnly) {
|
||||
const before = snapshotWorkDirBytes(workDir);
|
||||
try {
|
||||
const execCwd = skillConfig.cwd === 'workDir' ? workDir : undefined;
|
||||
execSkillRaw(opts.runtime, scriptPath, args, execCwd);
|
||||
} catch (e) {
|
||||
errors.push(`Idempotency rerun failed: exitCode=${e.status}\nstderr: ${(e.stderr || '').substring(0, 300)}`);
|
||||
}
|
||||
if (errors.length === 0) {
|
||||
const after = snapshotWorkDirBytes(workDir);
|
||||
const diffs = diffByteSnapshots(before, after);
|
||||
if (diffs) errors.push(`Idempotency: workspace changed on rerun:\n ${diffs.join('\n ')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Post-run validation (on real output, before cleanup)
|
||||
@@ -944,6 +1008,22 @@ async function runIntegrationTest(test, opts) {
|
||||
const step = test.steps[i];
|
||||
const stepT0 = performance.now();
|
||||
|
||||
// writeFile step: записать содержимое (обычно .bsl модуля) в workDir
|
||||
if (step.writeFile) {
|
||||
try {
|
||||
const target = replacePlaceholders(step.writeFile);
|
||||
const abs = target.includes(':') || target.startsWith('/') ? target : join(workDir, target);
|
||||
mkdirSync(dirname(abs), { recursive: true });
|
||||
writeFileSync(abs, step.content ?? '', 'utf8');
|
||||
const stepElapsed = ((performance.now() - stepT0) / 1000).toFixed(1);
|
||||
stepResults.push({ name: step.name, passed: true, elapsed: `${stepElapsed}s` });
|
||||
} catch (e) {
|
||||
stepResults.push({ name: step.name, passed: false, error: `writeFile failed: ${e.message}` });
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Write input if provided
|
||||
let inputFile = null;
|
||||
if (step.input) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user