mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 16:14:54 +03:00
fix(skd-validate): eliminate false positives on real ERP/БП reports
Calibrated against 1106 vendor reports (ERP 8.3.24 + БП 8.3.27).
Three categories of false positive removed:
- CalculatedField with empty <expression/> demoted error→warning.
Three legitimate vendor patterns surfaced:
* sibling totalField with same dataPath provides the formula
(used in cancellation-rate and share-percentage reports)
* groupTemplate references the field as group name
* field exists only as a declarative anchor for settingsVariants
Warning preserved so genuinely-missing formulas still surface.
- Duplicate template name demoted error→warning. Vendor configs ship
reports (БазаНормируемыхРасходов/Выручка) with three <template> blocks
named Макет1 — the platform identifies templates by position, not by
<name>. Warning still flags the collision without failing validation.
- comparisonType whitelist extended with NotInHierarchy and
NotInListByHierarchy. Existing list was missing the negated
hierarchy operators used in 20 of the 1106 reports.
Result: 0 false positives across the corpus, all genuine errors still
caught (verified separately against intentionally-broken fixtures).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -438,6 +438,17 @@ if ($script:stopped) { & $finalize; exit 1 }
|
||||
if ($calcFieldNodes.Count -gt 0) {
|
||||
$cfOk = $true
|
||||
$cfSeen = @{}
|
||||
# Collect totalField dataPaths — an empty calculatedField is legitimate if a
|
||||
# totalField with the same dataPath provides the expression (real-world
|
||||
# pattern in vendor ERP/БП reports for fields visible only in totals).
|
||||
$tfPaths = @{}
|
||||
foreach ($tf in $totalFieldNodes) {
|
||||
$tfDp = $tf.SelectSingleNode("s:dataPath", $ns)
|
||||
if ($tfDp -and $tfDp.InnerText) {
|
||||
$tfPaths[$tfDp.InnerText] = $true
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($cf in $calcFieldNodes) {
|
||||
$dp = $cf.SelectSingleNode("s:dataPath", $ns)
|
||||
$expr = $cf.SelectSingleNode("s:expression", $ns)
|
||||
@@ -457,8 +468,15 @@ if ($calcFieldNodes.Count -gt 0) {
|
||||
}
|
||||
|
||||
if (-not $expr -or -not $expr.InnerText.Trim()) {
|
||||
Report-Error "CalculatedField '$path' has empty expression"
|
||||
$cfOk = $false
|
||||
# Empty expression is legitimate in several vendor patterns:
|
||||
# - totalField with same dataPath provides the calculation
|
||||
# - groupTemplate uses the field as group name (declarative only)
|
||||
# - field is referenced only by settingsVariants for grouping
|
||||
# Surface as warning, not error, to avoid false positives on real
|
||||
# ERP/БП reports while still flagging the unusual shape.
|
||||
if (-not $tfPaths.ContainsKey($path)) {
|
||||
Report-Warn "CalculatedField '$path' has empty expression (declarative-only?)"
|
||||
}
|
||||
}
|
||||
|
||||
# Warn if collides with a dataset field
|
||||
@@ -542,14 +560,16 @@ if ($templateNodes.Count -gt 0) {
|
||||
}
|
||||
$tName = $nameNode.InnerText
|
||||
if ($tplSeen.ContainsKey($tName)) {
|
||||
Report-Error "Duplicate template name: $tName"
|
||||
$tplOk = $false
|
||||
# Vendor configs (ERP/БП) ship templates with repeating names — the
|
||||
# platform identifies them by position/context, not by <name>. Demote
|
||||
# to warning so the check still surfaces the collision without failing.
|
||||
Report-Warn "Duplicate template name: $tName (allowed by platform but ambiguous)"
|
||||
} else {
|
||||
$tplSeen[$tName] = $true
|
||||
}
|
||||
}
|
||||
if ($tplOk) {
|
||||
Report-OK "$($templateNodes.Count) template(s): names unique"
|
||||
Report-OK "$($templateNodes.Count) template(s) found"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,7 +601,8 @@ if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
$validComparisonTypes = @(
|
||||
"Equal","NotEqual","Greater","GreaterOrEqual","Less","LessOrEqual",
|
||||
"InList","NotInList","InHierarchy","InListByHierarchy",
|
||||
"InList","NotInList","InHierarchy","NotInHierarchy",
|
||||
"InListByHierarchy","NotInListByHierarchy",
|
||||
"Contains","NotContains","BeginsWith","NotBeginsWith",
|
||||
"Filled","NotFilled"
|
||||
)
|
||||
|
||||
@@ -434,6 +434,15 @@ if stopped:
|
||||
if len(calc_field_nodes) > 0:
|
||||
cf_ok = True
|
||||
cf_seen = {}
|
||||
# Collect totalField dataPaths — an empty calculatedField is legitimate if a
|
||||
# totalField with the same dataPath provides the expression (real-world
|
||||
# pattern in vendor ERP/БП reports for fields visible only in totals).
|
||||
tf_paths = set()
|
||||
for tf in total_field_nodes:
|
||||
tf_dp = find(tf, "s:dataPath")
|
||||
if tf_dp is not None and inner_text(tf_dp):
|
||||
tf_paths.add(inner_text(tf_dp))
|
||||
|
||||
for cf in calc_field_nodes:
|
||||
dp = find(cf, "s:dataPath")
|
||||
expr = find(cf, "s:expression")
|
||||
@@ -451,8 +460,14 @@ if len(calc_field_nodes) > 0:
|
||||
cf_seen[path] = True
|
||||
|
||||
if expr is None or not text_of(expr):
|
||||
report_error(f"CalculatedField '{path}' has empty expression")
|
||||
cf_ok = False
|
||||
# Empty expression is legitimate in several vendor patterns:
|
||||
# - totalField with same dataPath provides the calculation
|
||||
# - groupTemplate uses the field as group name (declarative only)
|
||||
# - field is referenced only by settingsVariants for grouping
|
||||
# Surface as warning, not error, to avoid false positives on real
|
||||
# ERP/БП reports while still flagging the unusual shape.
|
||||
if path not in tf_paths:
|
||||
report_warn(f"CalculatedField '{path}' has empty expression (declarative-only?)")
|
||||
|
||||
# Warn if collides with a dataset field
|
||||
if path in all_field_paths:
|
||||
@@ -526,12 +541,14 @@ if len(template_nodes) > 0:
|
||||
continue
|
||||
t_name = inner_text(name_node)
|
||||
if t_name in tpl_seen:
|
||||
report_error(f"Duplicate template name: {t_name}")
|
||||
tpl_ok = False
|
||||
# Vendor configs (ERP/БП) ship templates with repeating names — the
|
||||
# platform identifies them by position/context, not by <name>. Demote
|
||||
# to warning so the check still surfaces the collision without failing.
|
||||
report_warn(f"Duplicate template name: {t_name} (allowed by platform but ambiguous)")
|
||||
else:
|
||||
tpl_seen[t_name] = True
|
||||
if tpl_ok:
|
||||
report_ok(f"{len(template_nodes)} template(s): names unique")
|
||||
report_ok(f"{len(template_nodes)} template(s) found")
|
||||
|
||||
# ── 13. GroupTemplate checks ─────────────────────────────────
|
||||
|
||||
@@ -558,7 +575,8 @@ if stopped:
|
||||
|
||||
valid_comparison_types = (
|
||||
"Equal", "NotEqual", "Greater", "GreaterOrEqual", "Less", "LessOrEqual",
|
||||
"InList", "NotInList", "InHierarchy", "InListByHierarchy",
|
||||
"InList", "NotInList", "InHierarchy", "NotInHierarchy",
|
||||
"InListByHierarchy", "NotInListByHierarchy",
|
||||
"Contains", "NotContains", "BeginsWith", "NotBeginsWith",
|
||||
"Filled", "NotFilled",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user