1Ci Support Help Center home page
Submit a request
Sign in
  1. 1Ci Support
  2. 1C:Enterprise Platform FAQ
  3. Development

Using 1C:Enterprise script clauses

  • Development
    • 1C:Enterprise script
    • Using 1C:Enterprise script clauses
    • Module formatting
    • Application version control
    • Applied objects
    • Data storages
    • Global configuration requirements
    • Configuration delivery and support
    • Data composition system
    • Query language
See more
  • Using compilation directives
  • Limitations to usage of the Goto operator
  • Using duplicate script fragments
  • Using global variables in modules
  • Defining variable value type
  • PredefinedValue function usage specifics
  • Expression wrapping rules
  • General requirements to 1C:Enterprise script clauses
  • Initializing local variables in advance
  • Event log usage specifics
  • Getting object metadata
  • Catching exceptions in 1C:Enterprise script
  • Form module event handlers defined in 1C:Enterprise script

Using compilation directives

This article describes the standards that apply to compilation directives. All of the listed recommendations are mandatory unless noted otherwise.

Use the following compilation directives only in managed form modules and command modules:

&AtClient 
&AtServer 
&AtServerNoContext

Use preprocessor instructions in other module types.

Limitations to usage of the Goto operator

This article describes the standards that apply to the usage of the Goto operator. All of the listed recommendations are mandatory unless noted otherwise.

This recommendation is optional

1. We recommend that you do not use the Goto operator because this might impact the clarity of your module structure, making the connections between script fragments and the order of their excution harder to understand. We recommend that you use other 1C:Enterprise script operators instead.

 

Incorrect:

 If ChartOfCalculationTypes = Object.ChartOfCalculationTypes Then
  
  Goto ~ChartOfCalculationTypes;
  
 EndIf;

 

Correct:

 If ChartOfCalculationTypes = Object.ChartOfCalculationTypes Then
  
  ProcessChartOfCalculationTypes();
  
 EndIf;

2. Do not use the Goto operator in common modules with the Client (managed application) flag, command modules, and client script of managed form modules because 1C:Enterprise web client does not support this operator.

Using duplicate script fragments

This article describes the standards that apply to duplicating script fragments. All of the listed recommendations are mandatory unless noted otherwise.

1. Duplicate script is defined as script fragments or entire procedures or functions copied during the implementation of functionality that has the same logic as another functionality available in the configuration.

Using duplicate script fragments might cause the following issues:

  • Their bugs are also duplicated.
  • When fixing a bug, one might miss some of its occurences.
  • Fixing bugs becomes more resource-consuming. 
  • The applied solution structure becomes more complex.

Script is often duplicated because the original fragment cannot be accessed (for example, the required fragment is located in a form module and therefore cannot be accessed from another form).

We recommend that you avoid duplicating script fragments whenever possible. It is usually done by refactoring the existing script and moving procedures an functions that you want to reuse from form and object modules to common modules.

2. Remember that duplicating script fragments is acceptable and recommended if future development will lead to significant changes between the original and duplicate script.

Using global variables in modules

This article describes the standards that apply to global variables. All of the listed recommendations are mandatory unless noted otherwise.

1. In most scenarios we recommend that you do not use global variables and use other 1C:Enterprise script tools instead. Since monitoring the visibility (usage) areas of such variables is tricky, they often might cause issues that cannot be easily located.

Examples of incorrect variable usages and exceptions to this rule are provided later in this article. The variable declaration format is described in the Module structure article.

2. This section contains examples of incorrect variable usages in object modules (such as catalog, document, record set, data processor, or report modules).

2.1. We recommend that you use the AdditionalProperties object property for passing parameters between event subscription handlers and for passing parameters from external script to object module event handlers.

Incorrect:

Variable FileConversion Export;
Procedure BeforeWrite(Cancel)

  If FileConversion Then
  ...

EndProcedure

// script that calls the procedure
FileObject.FileConversion = True;
FileObject.Write();

Correct:

Procedure BeforeWrite(Cancel)

  If AdditionalProperties.Property("FileConversion") Then 
  ...

EndProcedure

// script that calls the procedure
FileObject.AdditionalProperties.Insert("FileConversion", True);
FileObject.Write();

At the same time, it is a good practice to use nonexport object module variables, which are not available from external script, for passing internal parameters between object module event handlers.

Example:

Variable PreviousCompanyValue; // value of the "company" attribute before writing the object to the database
Procedure BeforeWrite(Cancel)
  PreviousCompanyValue = ...; // use a query to retrieve the value before writing the object to the database
EndProcedure
Procedure OnWrite(Cancel)
  If PreviousCompanyValue <> Company Then
    // process the attribute value change during writing
    ...
  EndIf;
EndProcedure

2.2. We recommend that you use string constants for processing return codes (errors) in the module logic.

Incorrect:

Variable NoErrors,
Error_FillCheckProcessing, // occurs if the fill check processing returns Cancel
Error_WriteObject, // occurs if an exception is raised while the object is being written
Error_LockObject, // occurs during the attempt to lock the object
Procedure RunRecalculation()
  ...
  Result = ProcessDocuments(...);
  If Result = Error_WriteObject Then
    ...
  ElsIf Result = Error_LockObject Then
    ...
  ElsIf ...
EndProcedure
...
////////////////////////////////////////////////////////////////////////////////
// MODULE INITIALIZATON
NoErrors = 1;
Error_FillCheckProcessing = 2;
Error_WriteObject = 3;
Error_LockObject = 4;

Correct:

Procedure RunRecalculation()
  ...
  Result = ProcessDocuments(...);
  If Result = "ErrorWriteObject" Then
    ...
  ElsIf Результат = "ErrorLockObject" Then
    ...
  ElsIf ...
EndProcedure

2.3. We recommend that you use modules with reusable return values for caching values that have long calculation time and are frequently used in procedures or functions during server calls.

Scenarios where you cannot return the result of a calculation performed in an export function due to security reasons are an exception to this rule. Use local module variables for storing such values.

3. This section contains examples of incorrect variable usages in form modules.

3.1. We recommend that you use modules with reusable return values for caching values that have long calculation time and are frequently used in procedures or functions.

Do not cache static data or data that can be calculated quickly. In particular, do not cache values of predefined items or enumerations in client variables of form modules. Use the PredefinedValue method for getting them on the client.

3.2. Use the following entities for storing intermediate results and for passing them between form procedures and functions:

  • Procedure and function parameters for passing results through the call stack in a single procedure or function call context.
  • Form attributes for storing intemediate results between different calls from the client (note that the values of server form module variables are not preserved between the calls from the client).

Scenarios where client form variables are used for storing intermediate results of form idle handlers, external event handlers, or client form item event handlers are an exception to this rule.
Example:

&AtClient
Variable PictureNumber; // the counter used for naming files while scanning multiple images
...
&AtClient
Procedure ExternalEvent(Source, Event, Data)
 If Source = "TWAIN" And Event = "ImageAcquired" Then
  If PictureNumber = Undefined Then
   PictureNumber = 1;
  EndIf; 
  PictureNumber = PictureNumber + 1;
  // Saving the scanned document to the file named PictureNumber
  // ...
 EndIf; 
EndProcedure

4. Use variables of managed and ordinary applications for storing "client session parameters". For details, see Using session parameters.

Defining variable value type

This article describes the standards that apply to defining varialbe value type. All of the listed recommendations are mandatory unless noted otherwise.

To define a variable value type, use comparison with a type. Do not use any other methods.

Correct:

If TypeOf(Ref) = Type("DocumentRef.GoodsReceipt") Then

Incorrect:

If Ref.Metadata().Name = "GoodsReceipt" Then

PredefinedValue function usage specifics

This article describes the standards that apply to the PredefinedValue function usage. All of the listed recommendations are mandatory unless noted otherwise.

The PredefinedValue global context function is intended for getting references to predefined items of catalogs, charts of characteristic types, or charts of calculation types, enumeration values, or business process route points by their string names.

Example:

BusinessIndividual = PredefinedValue("Enum.BusinessIndividual.Business");

The main purpose of this function is getting predefined values in the script that is executed in the thin client or web client (where objects like CatalogManager.<Catalog name> and EnumManager<Enumeration name> are not available).

Incorrect:

If String(BusinessIndividual) = "Business" Then

Correct:

If BusinessIndividual = PredefinedValue("Enum.BusinessIndividual.Business") Then

See also:

  • Common module naming rules

Expression wrapping rules

This article describes the standards that apply to expression wrapping rules. All of the listed recommendations are mandatory unless noted otherwise.

1. We recommend that you wrap lines longer than 120 characters, except the lines where wrapping is impossible.

2. Wrap long ariphmetical expressions as follows:

  • a single line can contain multiple operands;
  • put ariphmetical operators to the beginning of line (not to the end of the previous line);
  • precede each operand that opens a new line with a standard indent, or align it with the first operand;
  • precede each parameter that opens a new line with a standard indent, or align it with the first parameter.

Example:

Message.Text = String(- SelectionDetailRecords.QuantityBalance) 
               + " units shortage for """ + SelectionDetailRecords.Material
               + """ with """ + SelectionDetailRecords.PropertySet
               + """ property set.";

or

Message.Text = String(- SelectionDetailRecords.QuantityBalance) 
    + " units shortage for """ + SelectionDetailRecords.Material
    + """ with """ + SelectionDetailRecords.PropertySet
    + """ property set.";

3. Wrap long string constants using the special wrapping character at the beginning of each new line:

Query = New Query(
    "SELECT ALLOWED
    | ItemNotes.NotesCount AS NotesCount
    |FROM
    | InformationRegister.ItemNotes AS ItemNotes
    |WHERE
    | ItemNotes.Item = &Item");

or

WarningText = StringFunctionsClientServer.SubstituteParametersInString(
    NStr("en = 'The address classifier is up-to-date.
               |Address data as of %1 is loaded to the application.'"),
    Format(LatestClassifierUpdateDate, "DLF=D"));
DoMessageBox(WarningText);

Do not wrap lines that contain user messages (the UserMessage object).

4. Wrap procedure, function, or method parameters as follows:

  • precede each parameter that opens a new line with a standard indent, or align it with the first parameter;
  • put the last parameter, the closing parenthesis, and the operator separator ";" to a single line;
  • the formatting provided by the autoformat feature in Designer is also acceptable (see paragraph 5 later in this article).

Example:

DocumentNames = New ValueList;
DocumentNames.Add(Metadata.Documents.SalesInvoice.Name,
                  Metadata.Documents.SalesInvoice.Synonym);
DocumentNames.Add(Metadata.Documents.PurchaseInvoice.Name,
                  Metadata.Documents.PurchaseInvoice.Synonym);

or

DocumentNames = New ValueList;
DocumentNames.Add(Metadata.Documents.SalesInvoice.Name,
    Metadata.Documents.SalesInvoice.Synonym);
DocumentNames.Add(Metadata.Documents.PurchaseInvoice.Name,
    Metadata.Documents.PurchaseInvoice.Synonym);

5. Wrap complex logical conditions within If...ElsIf...EndIf as follows:

  • if a line is longer than 120 characters, put each elementary logical condition to a new line;
  • put logical operators (AND, OR) to the beginning of line (not to the end of the previous line);
  • precede each condition with a standard indent, or align it with the first condition (we recommend that you use spaces to set an indent);
  • Put the last condition and the Then keyword to the same line.

Example:

If (OperationType = Enums.OperationTypeReceipt.RetailReceipt)
    OR (OperationType = Enums.OperationTypesReceipt.RetailCommissionReceipt) Then
    Return True;
EndIf

or

If ((ModuleStructure[Index].Block = Enums.ModuleBlockTypes.ProcedureTitle)
    OR(ModuleStructure[Index].Block = Enums.ModuleBlockTypes.FunctionTitle))
    AND(Find(Upper(Modulestructure[Index].Text), BlockKey)>0) Then

6. To have your script comply with the listed recommendations, in addition to the automatic module text formatting when you type, you can format any text that you wrote earlier. To format a text block, select it and then, on the Text menu, point to Block and click Format. The text editor parses the module text and formats it by adding a tab indent to each nested clause, regardless of the original indents (leading spaces). Empty lines also gain indents according to their nesting levels.

General requirements to 1C:Enterprise script clauses

This article describes the requirements to 1C:Enterprise script clauses. All of the listed recommendations are mandatory unless noted otherwise.

1. Use the canonical capitalization (as in the documentation and Syntax Assistant).

Correct:

EndIf

Incorrect:

endIf
ENDIF
endif
Endif

2. In a block of several assignment operators the following alignment is acceptable:

SelectionDialog.FullFileName = FileName;
SelectionDialog.Directory = PathName;
SelectionDialog.Title = NStr("en = 'Select the file with the list of queries'");
SelectionDialog.Filter = NStr("en = 'Query files (*.sel)|*.sel|All files (*.*)|*.*'");
SelectionDialog.DefaultExt = "sel";

You do not have to align the operators throughout the entire module. We recommend that you align the operators located next to each other.

3. Wrap compound logical expressions within If...EndIf according to the expression wrapping rules.

4. To check the result of a function that returns a logical expression, do not use comparisons with logical constants.

Correct:

If IsNew() Then

Incorrect:

If IsNew() = True Then

5. To compare expression results, first assign the results to auxuliary variables, and then compare those variables.

Correct:

Answer = DoQueryBox(NStr("en = 'The data is not saved. Do you want to save it?'"),
    QuestionDialogMode.YesNo, , DialogReturnCode.Yes);
If Answer = DialogReturnCode.Yes Then
    Write();
Else
    Return;
EndIf;

Incorrect:

If DoQueryBox(NStr("en = 'The data is not saved. Do you want to save it?'"), 
    QuestionDialogMode.YesNo, , DialogReturnCode.Yes) = DialogReturnCode.Yes Then
    Write();
Else
    Return;
EndIf;

6. Use the system value sets whenever possible. For example, use Chars.LF instead of Char(10).

See also:

  • Expression wrapping rules
  • Module texts

Initializing local variables in advance

This article describes the standards that apply to local variable initialization. All of the listed recommendations are mandatory unless noted otherwise.

This recommendation is optional

When a script fragment calculates one or several local variable values, we recommend that you initialize the variables in advance, in order to avoid runtime errors that appear when the variable value is Undefined but the script expects a value of specific type.

Incorrect

If Something Then
    MyVariable = 10;
ElsIf
    // more branches
    …
EndIf;
 
… = MyVariable; // if Something is not TRUE, MyVariable might be Undefined

Correct:

MyVariable = 0; // default value
 
If Something Then
    MyVariable = 10;
ElsIf
    // more branches
    …
EndIf;
 
… = MyVariable;  // the variable value is always a number

This recommendation makes sense for long If / ElsIf / Else blocks where variable initializations inside the blocks might be easily overlooked.

Event log usage specifics

This article describes the standards that apply to event log records. All of the listed recommendations are mandatory unless noted otherwise.

1. The event log is intended for recording events that occur during the user work with the infobase. Administrators often need this data, filtered in some way, to learn what hapened during a specific period or which actions a specific user performed.

2. We recommend that you add event log records using 1C:Enterprise script tools when administrators might need diagnostics data about events that are not automatically registered by the platform. For example, you can record events during the interactive execution of business logic, as well as during the execution of background (scheduled) jobs. To simplify the log analysis, a single record must describe a single event, and each record must have a set of mandatory attributes that can be used for filtering the log.

2.1. String ID of the event type. The list of configuration event types can be quite large, so we recommend that you group the types by functionality: "Event group.Event name". For example, use the names "Assignments. New task notification" and "Assignments.Overdue task notification" instead of "flat" names "New task notification" and "Overdue task notification". Event types must be localized, with the main configuration language specified as the last NStr() parameter.

2.2. Log level. Critical events that require administrator attention, such as business logic errors and software malfunctions, have the "Error" level. Possible problems and nonfatal errors have the "Warning" level. Messages informing about successful operation completions have the "Information" level. You can also use the "Note" level for the least important events.

2.3. Comment. A custom text description of the event. For errors this field must contain information required to identify the cause. Do not describe multiple events in a single comment. For example, the following comment to a single event is incorrect:

[01/01/2014 00:00:01] Starting data exchange initialization with "Data exchange dump" 
settings, settings line number:1
[01/01/2014 00:00:02] Completing data exchange initialization (success)
[01/01/2014 00:00:03] Starting data exchange with "Data exchange dump" settings, settings line 
number:1
[01/01/2014 00:00:04] Starting writing changes to the exchange file
[01/01/2014 00:00:05] Completing writing changes to the exchange file (success)
[01/01/2014 00:00:06] Completing data exchange with "Data exchange dump" settings, settings line
number:1
[01/01/2014 00:00:07] Completed: data exchange. 1 object(s) processed.

Instead, write an individual record for each of the events.

The comment text must be localized.

To get exception descriptions for writing to the event log, use the following expression:

DetailErrorDescription(ErrorInfo())

Example of registering a custom event in the "MyFeature" functional subsystem:

Try
    WriteLogEvent(NStr("en='MyFeature.Handling possible error'", DefaultLanguageCode),
        EventLogLevel.Info, , ,
        NStr("en = 'Starting action'"));
    HandlePossibleError(ObjectToProcess);
    WriteLogEvent(NStr("en='MyFeature.Handling possible error'", DefaultLanguageCode),
        EventLogLevel.Info, , ,
        NStr("en = 'Completing action'"));
Except
    WriteLogEvent(NStr("en='MyFeature.Handling possible error'", DefaultLanguageCode),
        EventLogLevel.Error, , ,
        NStr("en = 'Unknown error.'") + Chars.LF + DetailErrorDescription(ErrorInfo()));
EndTry

In this example the DefaultLanguageCode variable stores the default configuration language code. For more information, see Code writing standards for easier localization, paragraph 8.1.

3. Do not use selections from the event log in tasks where the selection performance matters. This is because the selection time gets slower for larger logs.

We recommend that you create a dedicated register for recording events of particular interest, or use specific platform objects (for example, BackgroundJobsManager for getting the background job execution history).

Also keep this recommendation in mind when developing event log reports.

Getting object metadata

This article describes the standards that apply to getting object metadata. All of the listed recommendations are mandatory unless noted otherwise.

1. If you can determine the metadata object type (catalog, document, and so on), get the configuration object metadata using the Metadata method of this object (for objects of referential type, use the Metadata method of the reference).

Do not use the Metadata global context method in this scenario because its execution is significantly slower.

Correct:

CatalogObject.Metadata

Incorrect:

Metadata.Catalogs[CatalogName]
Metadata.FindByFullName("Catalog." + CatalogName)

2. If you cannot determine the metadata object type in advance, use the FindByType method.

Example:

// Get the full metadata object name by reference in the following format: 
// "Catalog.Items", "Document.GoodsReceipt" 
MetadataObjectName = Metadata.FindByType(TypeOf(Ref)).FullName();

Catching exceptions in 1C:Enterprise script

This article describes the standards that apply to exception handling. All of the listed recommendations are mandatory unless noted otherwise.

In general, we recommend that you do not catch exceptions. In particular, do not catch exceptions solely for displaying error messages, for the platform automatically displays an error message for each uncaught exception.

Still, sometimes you might need to catch exceptions. For example, you might want to modify an error message, making it totally clear for end users. But even if you do, you still have to register the error in the event log to ensure that the system administrator can identify that problem and provide the error details by technical support request.

We recommend that you write the detailed error description to the event log and display the brief description to users.

Examples of incorrect exception handling

1. Some server business logic is called from the client during interactive user work:

&AtServer
Procedure PerformAction()
    // Code that raises an exception
    ...
EndProcedure

Incorrect:

// at client
Try
    PerformAction();
Except
    DoMessageBox("Cannot perform the action.");
EndTry;

Correct:

&AtServer Procedure PerformAction() Try // Code that raises an exception ... Except // Write event log record to be analyzed by system administrator WriteLogEvent(NStr("en = 'Performing action'"), EventLogLevel.Error, , , DetailErrorDescription(ErrorInfo())); Raise; EndTry; EndProcedure

and on the client:

Try
    PerformAction();
Except
    MessageText = BriefErrorDescription(ErrorInfo());
    DoMessageBox(NStr("en = 'Cannot perform the action. Reason:'" + Chars.LF + MessageText);
EndTry;

2. In some client business logic:

&AtClient
Procedure CreateFileOnDisk()
    // Code that raises an exception
    ...
EndProcedure

We recommend that you make an additional server call for registering unsuccessful operation execution in the event log:

Try
    // Code that raises an exception
    CreateFileOnDisk();
Except
    MessageText = BriefErrorDescription(ErrorInfo());
    DoMessageBox(NStr("en = 'Cannot perform the action. Reason:'") + Chars.LF + MessageText);
    WriteFileOperationError(DetailErrorDescription(ErrorInfo()));
EndTry;
 
&AtServer
Procedure WriteFileOperationError(...)
    WriteLogEvent(NStr("en = 'Performing action'"),
        EventLogLevel.Error, , ,
        DetailErrorDescription(ErrorInfo()));
EndProcedure

3. Never catch exceptions without notifying the system administrator:

Try
    // Code that raises an exception
    ...
Except
    // Catch exceptions
EndTry;

As a rule, such script clauses introduce problems that are impossible to locate.

Correct:

Try
    // Code that raises an exception
    ...
Except
    // Comment explaining why the exception is caught without notifying end users.
    // ...
    // And write an event log record to noify the administrator.
    WriteLogEvent(NStr("en = 'Performing action'"),
        EventLogLevel.Error, , ,
        DetailErrorDescription(ErrorInfo()));
EndTry;

4. Do not use exceptions for checking the availability of object attributes, methods, templates, and so on. This might lead to errors that are hard to locate, and this also complicates debugging in the "Stop on errors" mode.

Instead of catching exceptions in this scenario we recommed that you:

  • Use metadata operations for explicit checks whether an attribute, template, or another piece of data is available.
  • If the availability of some data depends on library deployment options, reflect this fact in overridable modules.
  • Revise the logic of the methods that catch exceptions. For example, you can implement parameters that define whether some object method or property is accessed.

Incorrect:

Try
    ContextERPServer.GetTemplate("ExchangeComponent");
    Path = ContextERPServer.PathToObject + ".Template.ExchangeComponent");
Except
EndTry;

Correct:

ExchangeComponentTemplate = ContextERPServer.Metadata().Templates.Find("ExchangeComponent");
If ExchangeComponentTemplate <> Undefined Then
    PathToTemplate = ExchangeComponent.FullName();
EndIf;

5. Within transactions, use the following exception handling format:

BeginTransaction();
Try
    Query = New Query("...");
    Selection = Query.Execute().Select();
    While Selection.Next() Do
        ...
    EndDo;
    CommitTransaction();
Except
    RollbackTransaction();
    WriteLogEvent(NStr("en = 'Performing action'"),
        EventLogLevel.Error, , ,
        DetailErrorDescription(ErrorInfo()));
    Raise;
EndTry;

Since an exception does not roll the transaction back immediately but prohibits committing it, each of the BeginTransaction calls must have a matching CommitTransaction or RollbackTransaction call.

Form module event handlers defined in 1C:Enterprise script

We recommend that you assign the Attachable_ prefix to the form module event handlers defined using the SetAction script method.

Example:

Procedure Attachable_AllowEditingObjectAttributes() 
...
Procedure Attachable_ContactInfoBeginSelection() 
...

If you attach a handler not in the form module (for example, you attach it in a common module), a configuration check with the Find unused procedures and functions option returns the following errors:

Catalog._DemoPartners.Form.ItemForm.Form No links to procedure found: 
"Attachable_ContactInfoBeginSelection"

The prefix ensures that you easily identify these handlers in the check result and ignore them.

If you attach a handler in a form module,  a configuration check with the Find unused procedures and functions option does not return errors.

© 2020 1C INTERNATIONAL LLC www.1Ci.com Support policy