1C:Professional training course. Lesson 2-5

Lesson 2-5

Preventing users from adding services into GoodsReceipt document

Let’s see, what we can do here.


This is a piece of code we used in the GoodsReceipt document form module to add a service line into the Materials tabular section:

&AtClient
Procedure AddAService(Command)
	// Insert handler contents.
	NewLine = Object.Materials.Add();
	NewLine.Material = AddAServiceServer();
EndProcedure
&AtServer
Function AddAServiceServer()
	Return(
		Catalogs.MaterialsAndServices.FindByAttribute(
			"MaterialOrService", 
			Enums.MaterialOrService.Service)
	);
EndFunction

This is a piece of code we used in the GoodsReceipt object module to cancel the document writing if it contains services:

Procedure BeforeWrite(Cancel, WriteMode, PostingMode)
	For Each Material In Materials Do
		If Material.Material.MaterialOrService = Enums.MaterialOrService.Service Then
			Message(
				"The document is not saved. 
				|You cannot use services in GoodsReceipt documents. 
				|Please, delete all services from the Materials tabular section."
			);
			Cancel = True;
			Return;
		EndIf;
	EndDo;
EndProcedure

Preventing Services documents from writing services into the BalanceOfMaterials accumulation register

One more yet unresolved issue is to prevent services listed in the Services document tabular section from posting to the BalanceOfMaterials register (because there is no such thing as a service balance).

Here is a code, generated by the Wizard that writes the MaterialsAndServices tabular section rows into the BalanceOfMaterials accumulation register:

Procedure Posting(Cancel, Mode)
	RegisterRecords.BalanceOfMaterials.Write = True;
	For Each CurRowMaterialsAndServices In MaterialsAndServices Do
		Record = RegisterRecords.BalanceOfMaterials.Add();
		Record.RecordType = AccumulationRecordType.Expense;
		Record.Period = Date;
		Record.Material = CurRowMaterialsAndServices.MaterialOrService;
		Record.Warehouse = Warehouse;
		Record.Quantity = CurRowMaterialsAndServices.Quantity;
	EndDo;
EndProcedure

We can check every CurRowMaterialsAndServices in the cycle if its MaterialOrService attribute is equal to Enums.MaterialOrService.Service skip such lines using the Continue command:

Procedure Posting(Cancel, Mode)
	RegisterRecords.BalanceOfMaterials.Write = True;
	For Each CurRowMaterialsAndServices In MaterialsAndServices Do
		If CurRowMaterialsAndServices.MaterialOrService.MaterialOrService = 
			Enums.MaterialOrService.Service Then
				Continue;
		EndIf;
		Record = RegisterRecords.BalanceOfMaterials.Add();
		Record.RecordType = AccumulationRecordType.Expense;
		Record.Period = Date;
		Record.Material = CurRowMaterialsAndServices.MaterialOrService;
		Record.Warehouse = Warehouse;
		Record.Quantity = CurRowMaterialsAndServices.Quantity;
	EndDo;
EndProcedure

The Continue command makes the execution immediately jump to the next cycle, so all the lines below the Continue just won’t be run.

Balance control

As we already mentioned, we need some way to check if there are enough materials in a warehouse to complete an order (i.e. to post the Services document). Basically, there are two ways of doing this: we can perform the check before or after we write a tabular section content into the accumulation register. We recommend you to use the second way because it was proven to be more efficient in most cases.

So we need to write the data into the register and then check if there are any negative balance records.

If you take a look at the Posting event handler of the Services document you won’t find any explicit code writing the accumulation register. The code fills out the register records but never writes them to the infobase. Instead, there is a line telling the Platform to write the register automatically:

RegisterRecords.BalanceOfMaterials.Write = True;

It means that the writing is performed by the Platform **after **the Posting handler is executed, so we have no place to put our balance checking code in. But here is a trick: if we write registers records explicitly the Platform will see they are already written and won’t write them once again.

Resulting Posting event handler code could look like that:

Procedure Posting(Cancel, Mode) 

 // 1. Filling out BalanceOfMaterials recordset
 RegisterRecords.BalanceOfMaterials.Write = True;
 For Each CurRowMaterialsAndServices In MaterialsAndServices Do
  If CurRowMaterialsAndServices.MaterialOrService.MaterialOrService = Enums.MaterialOrService.Service Then
    Continue;
  EndIf;

  Record = RegisterRecords.BalanceOfMaterials.Add();
  Record.RecordType = AccumulationRecordType.Expense;
  Record.Period = Date;
  Record.Material = CurRowMaterialsAndServices.MaterialOrService;
  Record.Warehouse = Warehouse;
  Record.Quantity = CurRowMaterialsAndServices.Quantity;

 EndDo;

 

 //2. Writing down BalanceOfMaterials recordset
 RegisterRecords.BalanceOfMaterials.LockForUpdate = True;
 RegisterRecords.Write(); 

 //3. Checking if there any negative balance records
 Query = New Query;
 Query.Text = 
  "SELECT
  | BalanceOfMaterialsBalance.Material AS Material,
  | BalanceOfMaterialsBalance.Warehouse AS Warehouse,
  | BalanceOfMaterialsBalance.QuantityBalance AS QuantityBalance
  |FROM
  | AccumulationRegister.BalanceOfMaterials.Balance AS BalanceOfMaterialsBalance
  |WHERE
  | BalanceOfMaterialsBalance.QuantityBalance < 0"; 

 QueryResult = Query.Execute();
 SelectionDetailRecords = QueryResult.Select();

 // 4. If the result is not empty, cancel the document's posting
 If SelectionDetailRecords.Count() = 0 Then
  Return;
 EndIf;

 Cancel = True;

 //5. Prepare the error message and display it.
 MessageText = 
  "There is not enough materials on the " + 
  ThisObject.Warehouse.Description + 
  ". See the list of deficits below:" +
  MessageText;
 While SelectionDetailRecords.Next() Do
  MessageText = 
   MessageText + Chars.CR + Chars.LF + 
   SelectionDetailRecords.Material.Description + ": " +
   SelectionDetailRecords.QuantityBalance;
 EndDo;

 Message(MessageText);

EndProcedure

Let’s consider how does it work.

1. Filling out BalanceOfMaterials recordset

This piece of code comes from the previous version of Posting procedure. It fills out BalanceOfMaterials recordset with the MaterialsAndServices tabular section content, skipping the services.

2. Writing down BalanceOfMaterials recordset

The first line sets the mode of locking data while writing them into the infobase. The detailed explanation of this setting is far beyond this course scope, so you just need to remember: you should always use this option if you control the balance after the register recordset was written. Otherwise, users can start getting a deadlock error messages, especially when the system is working under the heavy workload.

The second line explicitly writes all registers’ recordsets. In our case, there is the only BalanceOfMaterials accumulation register recordset. After we write recordsets explicitly, the Platform will not do it automatically again.

3. Checking if there any negative balance records

If our service document uses more materials than we have left in the warehouse, there will be negative balance records in the register’s Balance table. This query retrieves these records. Please note, that we read all negative balance records regardless of whether our Service document has this material in its tabular section or not. Do we need to add a condition restricting the query output to the materials we use in the Services document we are posting now? Well, we definitely do, and this is a topic we will start our next lesson with.

4. If the result is not empty, cancel the document’s posting

If the query returns no records, there are no negative balances in the register. In this case, we don’t need to proceed with the execution and just leave the procedure.

If the query returns at least one record, we have to cancel the document’s posting because there are not enough materials left in a warehouse. So we set the Cancel parameter to True. The procedure will proceed to run, but after it’s done the Platform will cancel the posting process and return an error message to a user.

The important thing to note here is that if we set Cancel to True, all data changes will be rolled back, and the infobase will be restored to the initial state it had before the posting started. In other words, there will be no saved document, no written register recordsets - no nothing. The infobase will look exactly like this document is never posted at all.

This “cleaning-up” is called a transaction rolling back, and it’s performed automatically by the platform and DBMS. The detailed explanation of transactions and their behavior is beyond the scope of the course. For now we need to keep in mind the following:

  • Some event handlers have the Cancel parameter;
  • You need to set Cancel to True if you don’t want this action to proceed (because you have found that there is something wrong with it);
  • After you set Cancel to True, the current procedure continues to run;
  • After the procedure finishes running, the Platform interrupts the current action (no other events’ handlers will be executed) and all data changes made during this action are rolled back.
  • After that, the Platform will show an error message to a user.

5. Prepare the error message and display it

If there are some negative balance records, we need to inform the user. This piece of code prepares the message text and displays it.

Let’s see how does it work




 Lesson 2-4 | Course description | Lesson 3-1

1C:Enterprise Developer's Community