Skip to content

Commit 5dc8c18

Browse files
authored
Merge pull request #2976 from artbear/DenyIncomplete-230
Правило Не установлен флаг "Запрет незаполненных значений" для измерений регистра - ГОТОВО
2 parents 3eeb4f7 + 704eb1a commit 5dc8c18

File tree

9 files changed

+301
-8
lines changed

9 files changed

+301
-8
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Запрет незаполненных значений (DenyIncompleteValues)
2+
3+
<!-- Блоки выше заполняются автоматически, не трогать -->
4+
## Описание диагностики
5+
<!-- Описание диагностики заполняется вручную. Необходимо понятным языком описать смысл и схему работу -->
6+
Довольно часто при проектировании структуры метаданных соблюдается правило - измерения регистра всегда должны быть заполнены конкретными значениями и не должны быть пустыми.
7+
8+
Проверку на заполненность измерений удобно выполнять с помощью простых встроенных средств платформы, а именно, флага "Запрет незаполненных значений" для измерения регистра. В этом случае платформа самостоятельно будет проверять заполненность измерений и не нужно дополнительно контролировать заполнение измерения ни при программной обработке регистров, ни при интерактивной обработке.
9+
Фактически записи с незаполненным измерением не имеют смысла в информационной базе, каким бы образом они в нее ни попали: в результате интерактивного ввода или в результате выполнения программного кода.
10+
11+
Без установки указанного флага могут возникать различные ситуации, которые приводят к проблемам или усложняют сопровождение систем 1С. Например,
12+
- пользователи смогут интерактивно указать пустые значения
13+
- или разработчики могут при разработке ошибаться, не указывая значения измерения при подготовке записей регистра.
14+
15+
Текущее правило может выдавать ложные срабатывания для измерений, которые могут быть не заполнены.
16+
17+
Правило применяется для следующих регистров:
18+
- сведений
19+
- накопления
20+
- бухгалтерских
21+
- расчетных
22+
23+
## Примеры
24+
<!-- В данном разделе приводятся примеры, на которые диагностика срабатывает, а также можно привести пример, как можно исправить ситуацию -->
25+
26+
## Источники
27+
<!-- Необходимо указывать ссылки на все источники, из которых почерпнута информация для создания диагностики -->
28+
<!-- Примеры источников
29+
30+
* Источник: [Стандарт: Тексты модулей](https://its.1c.ru/db/v8std#content:456:hdoc)
31+
* Полезная информация: [Отказ от использования модальных окон](https://its.1c.ru/db/metod8dev#content:5272:hdoc)
32+
* Источник: [Cognitive complexity, ver. 1.4](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) -->
33+
- [Книга "Разработка интерфейса прикладных решений на платформе "1С:Предприятие 8" - глава "Проверка заполнения и проверка при записи"](https://its.1c.ru/db/pubv8devui#content:225:1)
34+
- [Документация разработчика 1С 8.3 - Свойства измерения (ресурса, реквизита) регистра сведений](https://its.1c.ru/db/v8323doc#bookmark:dev:TI000000349)
35+
- [Документация разработчика 1С 8.3 - Свойства измерения (ресурса, реквизита) регистра накопления](https://its.1c.ru/db/v8323doc#bookmark:dev:TI000000363)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Запрет незаполненных значений (DenyIncompleteValues)
2+
3+
<!-- Блоки выше заполняются автоматически, не трогать -->
4+
## Description
5+
<!-- Описание диагностики заполняется вручную. Необходимо понятным языком описать смысл и схему работу -->
6+
7+
## Examples
8+
<!-- В данном разделе приводятся примеры, на которые диагностика срабатывает, а также можно привести пример, как можно исправить ситуацию -->
9+
10+
## Sources
11+
<!-- Необходимо указывать ссылки на все источники, из которых почерпнута информация для создания диагностики -->
12+
<!-- Примеры источников
13+
14+
* Источник: [Стандарт: Тексты модулей](https://its.1c.ru/db/v8std#content:456:hdoc)
15+
* Полезная информация: [Отказ от использования модальных окон](https://its.1c.ru/db/metod8dev#content:5272:hdoc)
16+
* Источник: [Cognitive complexity, ver. 1.4](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) -->

src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AbstractMetadataDiagnostic.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@
3030

3131
import java.util.ArrayList;
3232
import java.util.List;
33-
import java.util.stream.Collectors;
3433

3534
/**
36-
* Базовый класс для анализа объектов метаданных, когда диагностика региструется на первый токен модуля
35+
* Базовый класс для анализа объектов метаданных, когда диагностика регистрируется на первый токен модуля
3736
*/
3837
public abstract class AbstractMetadataDiagnostic extends AbstractDiagnostic {
3938

@@ -115,12 +114,15 @@ private void checkMetadataWithModules() {
115114
}
116115

117116
private boolean haveMatchingModule(AbstractMDObjectBase mdo) {
118-
var modules = ((AbstractMDObjectBSL) mdo).getModules().stream()
119-
.filter(mdoModule -> OBJECT_MODULES.contains(mdoModule.getModuleType()))
120-
.collect(Collectors.toList());
117+
// чтобы не анализировать несколько раз и не выдавать одинаковые результаты для разных модулей,
118+
// выберем только один модуль, например модуль менеджера
119+
if (documentContext.getModuleType() == ModuleType.ManagerModule){
120+
return true;
121+
}
121122

122-
// чтобы не анализировать несколько раз, выберем только один модуль, например модуль менеджера
123-
return modules.size() == 1 || documentContext.getModuleType() == ModuleType.ManagerModule;
123+
return ((AbstractMDObjectBSL) mdo).getModules().stream()
124+
.filter(mdoModule -> OBJECT_MODULES.contains(mdoModule.getModuleType()))
125+
.count() == 1;
124126
}
125127

126128
/**
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright (c) 2018-2023
5+
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Fedkin <nixel2007@gmail.com> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver.diagnostics;
23+
24+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticMetadata;
25+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticScope;
26+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticSeverity;
27+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticTag;
28+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticType;
29+
import com.github._1c_syntax.bsl.languageserver.utils.MdoRefBuilder;
30+
import com.github._1c_syntax.bsl.types.MDOType;
31+
import com.github._1c_syntax.mdclasses.mdo.AbstractMDObjectBase;
32+
import com.github._1c_syntax.mdclasses.mdo.AbstractMDObjectComplex;
33+
import com.github._1c_syntax.mdclasses.mdo.attributes.Dimension;
34+
import org.jetbrains.annotations.NotNull;
35+
36+
import java.util.List;
37+
import java.util.stream.Stream;
38+
39+
@DiagnosticMetadata(
40+
activatedByDefault = false,
41+
type = DiagnosticType.CODE_SMELL,
42+
severity = DiagnosticSeverity.MAJOR,
43+
minutesToFix = 1,
44+
tags = {
45+
DiagnosticTag.BADPRACTICE
46+
},
47+
scope = DiagnosticScope.BSL
48+
)
49+
public class DenyIncompleteValuesDiagnostic extends AbstractMetadataDiagnostic {
50+
51+
public DenyIncompleteValuesDiagnostic() {
52+
super(List.of(
53+
MDOType.INFORMATION_REGISTER,
54+
MDOType.ACCUMULATION_REGISTER,
55+
MDOType.ACCOUNTING_REGISTER,
56+
MDOType.CALCULATION_REGISTER
57+
));
58+
}
59+
60+
@Override
61+
protected void checkMetadata(AbstractMDObjectBase mdo) {
62+
getWrongDimensions((AbstractMDObjectComplex) mdo)
63+
.forEach((Dimension dimension) -> {
64+
var ownerMDOName = MdoRefBuilder.getLocaleOwnerMdoName(documentContext, mdo);
65+
addDiagnostic(info.getMessage(dimension.getName(), ownerMDOName));
66+
});
67+
}
68+
69+
@NotNull
70+
private static Stream<Dimension> getWrongDimensions(AbstractMDObjectComplex mdo) {
71+
return mdo.getChildren().stream()
72+
.filter(Dimension.class::isInstance)
73+
.map(Dimension.class::cast)
74+
.filter(dimension -> !dimension.isDenyIncompleteValues());
75+
}
76+
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/MdoRefBuilder.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
package com.github._1c_syntax.bsl.languageserver.utils;
2323

2424
import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
25+
import com.github._1c_syntax.bsl.mdo.MD;
26+
import com.github._1c_syntax.bsl.mdo.support.ScriptVariant;
2527
import com.github._1c_syntax.bsl.parser.BSLParser;
2628
import com.github._1c_syntax.bsl.types.MDOType;
2729
import com.github._1c_syntax.bsl.types.MdoReference;
@@ -68,7 +70,7 @@ public String getMdoRef(DocumentContext documentContext, BSLParser.ComplexIdenti
6870
public String getMdoRef(
6971
DocumentContext documentContext,
7072
@Nullable
71-
TerminalNode identifier,
73+
TerminalNode identifier,
7274
List<? extends BSLParser.ModifierContext> modifiers
7375
) {
7476

@@ -91,6 +93,39 @@ public String getMdoRef(
9193
return stringInterner.intern(mdoRef.get());
9294
}
9395

96+
/**
97+
* Получить mdoRef в языке конфигурации
98+
*
99+
* @param documentContext the document context
100+
* @param mdo the mdo
101+
* @return the locale mdoRef
102+
*/
103+
public String getLocaleMdoRef(DocumentContext documentContext, MD mdo) {
104+
final var mdoReference = mdo.getMdoReference();
105+
final String result;
106+
if (documentContext.getServerContext().getConfiguration().getScriptVariant() == ScriptVariant.ENGLISH) {
107+
result = mdoReference.getMdoRef();
108+
} else {
109+
result = mdoReference.getMdoRefRu();
110+
}
111+
return stringInterner.intern(result);
112+
}
113+
114+
/**
115+
* Получить имя родителя метаданного в языке конфигурации.
116+
*
117+
* @param documentContext the document context
118+
* @param mdo the mdo
119+
* @return the locale owner mdo name
120+
*/
121+
public String getLocaleOwnerMdoName(DocumentContext documentContext, MD mdo) {
122+
final var names = getLocaleMdoRef(documentContext, mdo).split("\\.");
123+
if (names.length <= 1) {
124+
return "";
125+
}
126+
return stringInterner.intern(names[0].concat(".").concat(names[1]));
127+
}
128+
94129
private Optional<String> getCommonModuleMdoRef(DocumentContext documentContext, String commonModuleName) {
95130
return documentContext.getServerContext()
96131
.getConfiguration()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
diagnosticMessage=The "Deny incomplete values" flag is not specified for the "%s" dimension of the "%s" metadata
2+
diagnosticName=Deny incomplete values for dimensions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
diagnosticMessage=Не указан флаг "Запрет незаполненных значений" у измерения "%s" метаданного "%s"
2+
diagnosticName=Запрет незаполненных значений у измерений регистров
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright (c) 2018-2023
5+
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Fedkin <nixel2007@gmail.com> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver.diagnostics;
23+
24+
import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
25+
import com.github._1c_syntax.bsl.languageserver.context.ServerContext;
26+
import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterEachTestMethod;
27+
import com.github._1c_syntax.bsl.types.MDOType;
28+
import com.github._1c_syntax.bsl.types.MdoReference;
29+
import com.github._1c_syntax.bsl.types.ModuleType;
30+
import com.github._1c_syntax.mdclasses.mdo.AbstractMDObjectBase;
31+
import com.github._1c_syntax.mdclasses.mdo.MDInformationRegister;
32+
import com.github._1c_syntax.utils.Absolute;
33+
import org.junit.jupiter.api.Test;
34+
35+
import java.io.File;
36+
import java.util.Collections;
37+
import java.util.Optional;
38+
import java.util.Set;
39+
40+
import static com.github._1c_syntax.bsl.languageserver.util.Assertions.assertThat;
41+
import static org.mockito.Mockito.spy;
42+
import static org.mockito.Mockito.when;
43+
44+
@CleanupContextBeforeClassAndAfterEachTestMethod
45+
class DenyIncompleteValuesDiagnosticTest extends AbstractDiagnosticTest<DenyIncompleteValuesDiagnostic> {
46+
DenyIncompleteValuesDiagnosticTest() {
47+
super(DenyIncompleteValuesDiagnostic.class);
48+
}
49+
50+
private static final String PATH_TO_METADATA = "src/test/resources/metadata/designer";
51+
private static final String PATH_TO_MANAGER_MODULE_FILE = "InformationRegisters/РегистрСведений1/Ext/ManagerModule.bsl";
52+
private static final String PATH_TO_OBJECT_MODULE_FILE = "InformationRegisters/РегистрСведений1/Ext/RecordSetModule.bsl";
53+
54+
@Test
55+
void testMdoWithoutModule() {
56+
checkMockHandler(ModuleType.SessionModule, true);
57+
}
58+
59+
@Test
60+
void testMdoWithOneModule() {
61+
checkMockHandler(ModuleType.ManagerModule, false);
62+
}
63+
64+
@Test
65+
void testMdoWithModules() {
66+
var path = Absolute.path(PATH_TO_METADATA);
67+
context.setConfigurationRoot(path);
68+
69+
var managerDocumentContext = addDocumentContext(context, PATH_TO_MANAGER_MODULE_FILE);
70+
var recordSetDocumentContext = addDocumentContext(context, PATH_TO_OBJECT_MODULE_FILE);
71+
72+
final var diagnostics = getDiagnostics(managerDocumentContext);
73+
74+
assertThat(diagnostics, true)
75+
.hasMessageOnRange("Не указан флаг \"Запрет незаполненных значений\" у измерения \"Справочник1\" метаданного \"РегистрСведений.РегистрСведений1\"",
76+
0, 0, 9)
77+
.hasSize(1);
78+
79+
final var diagnostics2 = getDiagnostics(recordSetDocumentContext);
80+
assertThat(diagnostics2, true).isEmpty();
81+
}
82+
83+
private void checkMockHandler(ModuleType type, boolean noneModules) {
84+
initServerContext(Absolute.path(PATH_TO_METADATA));
85+
86+
var documentContext = spy(getDocumentContext());
87+
when(documentContext.getModuleType()).thenReturn(type);
88+
89+
final var mdObjectBase = context.getConfiguration().getChildrenByMdoRef().get(
90+
MdoReference.create(MDOType.INFORMATION_REGISTER,
91+
"РегистрСведений1"));
92+
var spyMdo = spy((MDInformationRegister) mdObjectBase);
93+
94+
when(documentContext.getMdObject()).thenReturn(Optional.of(spyMdo));
95+
96+
if (noneModules){
97+
when(spyMdo.getModules()).thenReturn(Collections.emptyList());
98+
99+
Set<AbstractMDObjectBase> children = Set.of(spyMdo);
100+
101+
var configuration = spy(context.getConfiguration());
102+
when(configuration.getChildren()).thenReturn(children);
103+
var serverContext = spy(documentContext.getServerContext());
104+
when(serverContext.getConfiguration()).thenReturn(configuration);
105+
when(documentContext.getServerContext()).thenReturn(serverContext);
106+
}
107+
108+
final var diagnostics = getDiagnostics(documentContext);
109+
110+
assertThat(diagnostics, true)
111+
.hasMessageOnRange("Не указан флаг \"Запрет незаполненных значений\" у измерения \"Справочник1\" метаданного \"РегистрСведений.РегистрСведений1\"",
112+
0, 0, 9)
113+
.hasSize(1);
114+
}
115+
116+
private DocumentContext addDocumentContext(ServerContext serverContext, String path) {
117+
var file = new File(PATH_TO_METADATA, path);
118+
var uri = Absolute.uri(file);
119+
var documentContext = serverContext.addDocument(uri);
120+
serverContext.rebuildDocument(documentContext);
121+
return documentContext;
122+
}
123+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Процедура Метод1()
2+
КонецПроцедуры

0 commit comments

Comments
 (0)