10.09.2023

What is a transaction in 1s. One-time clients


Each client has his own repeating habits and traditions. Whether it’s a favorite day to shop, preferences for the average cost of a product, or for additional options for a product. All this information will help us encourage the client to make a second purchase and move from a new user to a regular customer.

We are moving from one-time purchases to regular customers

Well, you managed to convert the client. This is cool, but what next? According to statistics, from 30% to 80% of buyers in the e-commerce industry make an order only once during their entire life cycle. In the gaming industry, 60% of customers make a second order. How can we get loyal customers with such disappointing numbers?

This question worries marketers around the world. They work hard, putting all their efforts into converting clients from one-time clients to permanent ones. Be it initial orders in retail or deposits in online games. Why is the second order so important? If a customer makes their second order or makes a second deposit, the likelihood of making a third order increases tenfold compared to customers who have only made one order.

The table below combines data from ten leading ecommerce companies in Europe and the USA. As can be seen from the graph, the probability of making the next transaction increases with the number of current transactions.


Probability of the next transaction depending on the number of current transactions

Many companies group one-time customers into one group and use different techniques and messages to encourage a second purchase. Sounds like a good plan, right? One way or another, all clients in the group are one-time buyers. But we are here to discuss a different approach. This second method involves dividing a group of one-time customers into different segments based on the characteristics of their first transaction. Let's dive deeper and consider the prerequisites for such segmentation.

Day of the week

We'll start by analyzing the behavior of repeat buyers of the top 10 companies and try to understand whether there is a relationship between the day of the first order and the day of the second order. Let's start with an analysis of sports betting. In this theme, the client makes a deposit on the day when the most matches of his favorite team take place, usually on Saturday or Sunday. The likelihood of such clients returning and placing bets these days is quite high.

We collected data and ran tests and the results were the same as we expected. Basically, the second deposit was left on the same day of the week as the first. This can be seen from the maximum probability value located on the diagonal of the square.


Probability of making a second transaction depending on the day of the week of the first transaction in sports betting

The dependency between these two transactions can help better target promotional activities to different cohorts of one-time customers (in this case we have 7 groups based on the day of the first transaction) and remind the second transaction on appropriate days. A more interesting step is to test this hypothesis in retail.


Probability of making a second transaction depending on the day of the week of the first transaction in retail

We can see a similar situation. Customers make their second purchase on the same day of the week as their first. It is important to note that the dispersion in the retail industry was higher than in the sports betting industry. But the dependence itself manifested itself for each company. The most popular day for shopping is Monday, the least popular is Sunday. If we consider the dependencies between orders for only one brand, we get this.


Probability of making a second transaction depending on the day of the week of the first retail transaction for one brand

We have a simple explanation for this behavior in sports betting, but why do we see this result in retail? The reason may be that buyers have certain patterns in their lives. You go to the gym on Thursdays and Fridays, hang out with your family on the weekends, stay late at work on Mondays, and meet friends on Fridays. The buying pattern doesn't look strange considering all the others.

Times of Day

As you might have guessed, we tested similar hypotheses for time of day. Is there a correlation between the time of day of the first order and the second, if the second order was made at least seven days later? We divided the day into 4 periods: night, morning, evening and afternoon - and checked the distribution of second orders for each time period for 6 brands.


Probability of a second order depending on the time of day of the first order

The relationship between the first and second order by time of day seems obvious. Customers who order late at night for the first time are likely to place a second order at the same time.

Cost of goods in the order

As marketers, we strive to increase the number of items ordered. Upselling is a way of life in the marketing world, and if it's not, it should be. But should we always try to upsell a product? Is this the best solution for all of our clients? In our analysis, we examined whether the cost of goods in the second order increases compared to the first.

Different brands are used as data sources, so for each brand we identified separate segments with the value of the product. This resulted in 6 price groups.


Probability of the cost of the second order depending on the cost of the first order

Most of the customers whose orders were placed in the low price range remained in the same range in the second order.

Conclusion

Our analysis above shows what we can learn from the first orders. The main thing we need to remember is that we should not put all one-time clients in one group. It is worth segmenting customers depending on their day of the week and time of purchase, and the cost of the order.
Using these methods and steps will help you better understand how to increase LTV and gain more loyal customers.

2017-08-12

Create custom transactions for maintaining OM objects.

Introduction

I think that many functional SAP consultants have encountered the transaction of maintaining organizational management objects. Namely, the transaction PP01

Using this transaction provides the user with the ability to administer organizational management infotypes for those object types that are used in the business processes being automated. Very often this transaction is used as a single entry point for working with all types of organizational management objects, which, in reality, is not a very good practice. Well, or not very convenient. Although certainly common. Next, I will try to tell you what an alternative there might be.

Table T77S0, group "TCODE"

When setting up OM object objects, you will probably touch the setting located in the following path in SPRO:

IMG: Personnel Management -> Organizational Management -> Basic Settings -> Data Model Enhancement -> Maintain Object Types

Here you create new OM objects, come up with names for them, select icons and define some settings for them... At the moment we are interested in the node " Object Type Key + Transaction"

Part of the setup view will open in front of you T77S0 with filtered group values

Worth paying attention to the group TCODE in which, if you look closely, you can find the technical names of transactions that you most likely had to work with. Moreover, in the column Value indicates the type of object for which a particular transaction is intended.

What is special about these transactions?

By using transactions that are designed to maintain a specific type of object, you no longer need to select these very types of objects that are available, by default, in a transaction PP01. That is, by launching, for example, a transaction PO09, you immediately start working with objects like L

Creating a new transaction for your own organizational management object

In one of my previous posts, I talked about how you can create a new OM object + add a structural search for it

I won’t go far from this material. As a demonstration, I'll create a new transaction to maintain an object 91.

Defining a new object type in T77S0

Define the name of the future transaction in the setup view T77S0

The value "ZP91M" in this case is the name of the future transaction for maintaining the object 91 . Save your changes.

Creating a new transaction to maintain an OM object

Via transaction SE93 create a transaction to maintain your object. Below is a video fragment with the sequence of actions that must be performed to create the corresponding transaction

Note the values ​​that were used for the fields Program, Screen Number,Authorization Object. Now start a new transaction

The user has the opportunity to work only with a certain type of object, which, in a certain sense, can be called convenience, and, if you like, minimizing additional actions to select the desired object.

The title came out catchy, but it boiled over. I’ll say right away that we’ll be talking about 1C. Dear 1C users, you do not know how to work with transactions and do not understand what exceptions are. I came to this conclusion by looking at a large amount of 1C code generated in the wilds of the domestic enterprise. In typical configurations, this is all quite good, but a terrifying amount of custom code is written incompetently from the point of view of working with the database. Have you ever seen the error “This transaction has already encountered errors”? If yes, then the title of the article applies to you too. Let's finally figure out what transactions are and how to handle them correctly when working with 1C.

Why should we sound the alarm?

First, let's figure out what the "Errors have already occurred in this transaction" error is. This is, in fact, an extremely simple thing: you are trying to work with the database inside an already rolled back (cancelled) transaction. For example, somewhere the CancelTransaction method was called, and you are trying to commit it.


Why is that bad? Because this error doesn't tell you anything about where the problem actually happened. When a screenshot with such text comes to support from a user, and especially for server code that no one can interact with interactively, it’s... I wanted to write “critical error,” but I thought it was a buzzword that no one pays attention to anymore... This is an ass. This is a programming error. This is not a random glitch. This is a bug that needs to be fixed immediately. Because when your background server processes go down at night and the company starts rapidly losing money, then “Errors have already occurred in this transaction” is the last thing you want to see in the diagnostic logs.


There is, of course, a possibility that the server’s technological log (it’s turned on in production, right?) will somehow help diagnose the problem, but right now I can’t think of an option off the top of my head - how exactly to find the real cause of this error in it. But the real reason is one - the programmer Vasya received an exception within a transaction and decided that once was not a bad idea, “just think, it’s a mistake, let’s move on.”

What are transactions in 1C

It’s awkward to write about elementary truths, but apparently a little will be necessary. Transactions in 1C are the same as transactions in a DBMS. These are not some special “1C” transactions, these are transactions in the DBMS. According to the general idea of ​​transactions, they can either be executed entirely or not executed at all. All changes to database tables made within a transaction can be immediately undone, as if nothing had happened.


Next, you need to understand that 1C does not support nested transactions. As a matter of fact, they are not supported “in 1C”, but not supported at all. At least those DBMSs that 1C can work with. Nested transactions, for example, do not exist in MS SQL and Postgres. Each “nested” call to StartTransaction simply increments the transaction counter, and each call to “CommitTransaction” simply decreases this counter. This behavior is described in many books and articles, but the conclusions from this behavior are apparently not sufficiently analyzed. Strictly speaking, in SQL there is a so-called. SAVEPOINT, but 1C does not use them, and this thing is quite specific.



Procedure Very Useful and Important Code(List of Directory Links) StartTransaction(); For Each Link From the List of Directory Links Loop Directory Object = Link.GetObject(); Directory Object.SomeField = "I was changed from program code"; Directory object.Write(); EndCycle; CommitTransaction(); End of Procedure

Code in English

Not really. I absolutely don’t want to duplicate examples in English just to amuse fans of holy wars and holy wars.


You probably write code like this, right? The code example provided contains errors. At least three. Do you know which ones? I will say right away about the first one; it is related to object locks and is not directly related to transactions. About the second - a little later. The third error is a deadlock, which will occur during parallel execution of this code, but this is a topic for a separate article; we will not consider it now, so as not to complicate the code. Keyword to google: deadlock controlled locks.


Please note that the code is simple. There is simply a lot of this in your 1C systems. And it contains at least 3 errors at once. Think at your leisure how many errors there are in more complex scenarios for working with transactions written by your 1C programmers :)

Object locks

So, the first mistake. In 1C there are object locks, the so-called “optimistic” and “pessimistic”. I don’t know who coined the term, I would have killed him :). It is absolutely impossible to remember which of them is responsible for what. They have been written about in detail, as well as in other general IT literature.


The essence of the problem is that in the specified code example, a database object is changed, but in another session there may be an interactive user (or a neighboring background thread) who will also change this object. Here, one of you may receive the error "the entry has been modified or deleted." If this happens in an interactive session, the user will scratch his butt, swear, and try to reopen the form. If this happens in a background thread, then you will have to look for it in the logs. And the logbook, as you know, is slow, and only a few people in our industry set up the ELK stack for 1C logs... (we, by the way, are among those who set up and help others set up :))


In short, this is an annoying mistake and better not to have it. Therefore, the development standards clearly state that before changing objects, it is necessary to place an object lock on them using the " Directory object.Lock()". Then the concurrent session (which must also do this) will not be able to start the update operation and will receive the expected, controlled failure.

And now about transactions

We've dealt with the first mistake, let's move on to the second.


If you do not provide exception checking in this method, then an exception (for example, very likely in the “Write()” method) will throw you out of this method without completing the transaction. An exception from the “Write” method can be thrown for a variety of reasons, for example, some application checks in the business logic are triggered, or the above-mentioned object lock occurs. Anyway, the second error says: The code that started the transaction is not responsible for its completion.



That's exactly what I would call this problem. In our static 1C code analyzer based on SonarQube, we even separately built in such diagnostics. Now I’m working on its development, and the imagination of 1C programmers, whose code comes to me for analysis, sometimes leaves me in shock and awe...


Why? Because an exception thrown at the top inside a transaction in 90% of cases will not allow this transaction to be committed and will lead to an error. It should be understood that 1C automatically rolls back an unfinished transaction only after returning from the script code to the platform code level. As long as you are at the 1C code level, the transaction remains active.


Let's go up one level in the call stack:


Procedure ImportantCode() LinkList = GetLinkListWhere(); VeryUsefulAndImportantCode(LinkList); End of Procedure

Look what happens. Our problematic method is called from somewhere outside, higher up the stack. At the level of this method, the developer has no idea whether there will be any transactions inside the Very Useful and Important Code method or not. And if there are, will they all be completed... We are all here for peace and encapsulation, right? The author of the "ImportantCode" method should not think about what exactly happens inside the method he calls. The same one in which the transaction is processed incorrectly. As a result, an attempt to work with the database after an exception has been thrown from within a transaction will most likely result in the following: “In this transaction blah blah...”

Spreading transactions across methods

The second rule of "transaction-safe" code: The transaction reference count at the beginning of the method and at its end must have the same value. You cannot start a transaction in one method and end it in another. It is probably possible to find exceptions to this rule, but this will be some kind of low-level code written by more competent people. In general, you can't write this way.


For example:


Procedure ImportantCode() LinkList = GetLinkListWhere(); VeryUsefulAndImportantCode(LinkList); CommitTransaction(); // A ticket to hell, a serious conversation with the author about our complex labor relations. End of Procedure

The above is unacceptable shit code. You can't write methods so that the caller remembers and keeps track of possible (or probable - who knows) transactions within other methods that it calls. This is a violation of encapsulation and a proliferation of spaghetti code that cannot be traced with one's sanity.


It's especially fun to remember that the real code is much larger than the synthetic 3-line examples. Finding starting and ending transactions at six levels of nesting - this directly motivates intimate conversations with the authors.

Trying to fix the code

Let's go back to the original method and try to fix it. I’ll say right away that we won’t fix the object lock for now, just so as not to complicate the example code.

The first approach of a typical 1C nickname

Typically, 1C programmers know that an exception may be thrown when recording. They are also afraid of exceptions, so they try to catch them all. For example, like this:


Procedure Very Useful and Important Code(List of Directory Links) StartTransaction(); For Each Link From the List of Directory Links Loop Directory Object = Link.GetObject(); Directory Object.SomeField = "I was changed from program code"; AttemptDirectoryObject.Write(); Exception Log.Error("Could not write element %1", Link); Continue; EndAttempt; EndCycle; CommitTransaction(); End of Procedure

Well, things have gotten better, right? After all, now possible recording errors are processed and even logged. Exceptions will no longer be thrown when writing an object. And in the log you can even see on which object, I wasn’t too lazy and included a link in the message instead of the laconic “Error in writing a directory,” as developers who are always in a hurry often like to write. In other words, there is a concern for the user and an increase in competencies.


However, an experienced 1C user here will say that no, it hasn’t gotten better. In fact, nothing has changed, and maybe even got worse. In the “Write()” method, the 1C platform itself will start a write transaction, and this transaction will already be nested in relation to ours. And if, while working with the 1C database, the transaction rolls back (for example, a business logic exception is thrown), then our top-level transaction will still be marked as “corrupted” and cannot be committed. As a result, this code will remain problematic, and when you try to commit it will display “errors have already occurred.”


Now imagine that we are not talking about a small method, but about a deep call stack, where at the very bottom someone took and “released” the started transaction from their method. Top-level procedures may have no idea that anyone down there has started transactions. As a result, the entire code fails with a vague error that is impossible to investigate in principle.


The code that starts a transaction is required to complete or rollback it. Regardless of any exceptions. Each code branch should be examined to see if a method exits without committing or canceling the transaction.

Methods of working with transactions in 1C

It would not be superfluous to remind you what 1C generally provides us with for working with transactions. These are the well-known methods:

  • StartTransaction()
  • CommitTransaction()
  • CancelTransaction()
  • TransactionActive()

The first 3 methods are obvious and do what their names say. The last method returns True if the transaction counter is greater than zero.


And there is an interesting feature. The transaction exit methods (Commit and Cancel) throw exceptions if the transaction count is zero. That is, if you call one of them outside of a transaction, an error will occur.


How to use these methods correctly? It’s very simple: you need to read the rule formulated above:


How to comply with this rule? Let's try:


We already understood above that the Do Something method is potentially dangerous. It may throw some kind of exception, and the transaction will “crawl out” of our method. Okay, let's add a possible exception handler:


StartTransaction(); Try DoSomething(); Exception // but what should I write here? EndAttempt; CommitTransaction();

Great, we caught the error that was occurring, but what should we do about it? Write a message to the log? Well, maybe if the error logging code should be exactly at this level and we are waiting for an error here. And if not? What if we didn't expect any errors here? Then we should just pass that exception up and let another layer of the architecture deal with it. This is done with the "CauseException" operator without arguments. In these Javascripts of yours, this is done in exactly the same way with the throw operator.


StartTransaction(); Try DoSomething(); Exception ThrowException; EndAttempt; CommitTransaction();

So, wait... If we just throw the exception further, then why is there a need for an Attempt at all? Here's why: the rule forces us to ensure the completion of the transaction we started.


StartTransaction(); Try DoSomething(); ExceptionCancelTransaction(); throwException; EndAttempt; CommitTransaction();

Now it seems to be beautiful. However, we remember that we do not trust the Do Something() code. What if the author inside didn’t read this article and doesn’t know how to work with transactions? What if he took it there and called the CancelTransaction method or, on the contrary, committed it? It is very important for us that the exception handler did not throw a new exception, otherwise the original error will be lost and troubleshooting will become impossible. And we remember that the Commit and Cancel methods can throw an exception if the transaction does not exist. This is where the TransactionActive method comes in handy.

Final version

Finally, we can write the correct, "transaction-safe" version of the code. Here he is:


**UPD: the comments suggested a safer option when CommitTransaction is located inside the Attempt block. This particular option is shown here; previously Fixation was located after the Attempt-Exception block.


StartTransaction(); Try DoSomething(); CommitTransaction(); Exception If TransactionIsActive() Then CancelTransaction(); endIf; throwException; EndAttempt;

Wait, but it’s not only “CancelTransaction” that can produce errors. Why then isn't "CommitTransaction" wrapped in the same condition with "TransactionActive"? Again, using the same rule: The code that started the transaction should be responsible for completing it. Our transaction is not necessarily the very first; it can be nested. At our level of abstraction, we are only required to care about our transaction. All others should be of no interest to us. They are strangers, we should not be responsible for them. Precisely they SHOULD NOT. No attempt should be made to determine the actual transaction counter level. This will again break encapsulation and lead to “smearing” transaction management logic. We only checked for activity in the exception handler and only to make sure that our handler will not generate a new exception, “hiding” the old one.

Refactoring checklist

Let's look at some of the most common situations that require code intervention.


Pattern:


StartTransaction(); DoSomething(); CommitTransaction();

Wrap it in a “safe” design with an Attempt, Keep Alive and Throw an Exception.


Pattern:


If NotTransactionActive() ThenStartTransaction()EndIf

Analysis and Refactoring. The author didn't understand what he was doing. It is safe to start nested transactions. There is no need to check the condition, you just need to start the nested transaction. Below in the modulus, it is probably still distorted there with their fixation. This is guaranteed hemorrhoids.


A roughly similar option:


If Transaction is Active() Then CommitTransaction() EndIf

similarly: committing a transaction by condition is strange. Why is there a condition here? What, someone else could have already recorded this transaction? Reason for trial.


Pattern:


StartTransaction() While Select.Next() Loop // reading an object by reference // writing an object EndCycle; CommitTransaction();
  1. introduce controlled locking to avoid deadlock
  2. enter a call to the Block method
  3. wrap in "try" as shown above

Pattern:


StartTransaction() While Select.Next() Loop Attempt Object.Write(); Exception Report("Failed to write"); EndAttempt; EndCycle; CommitTransaction();

This transaction will no longer complete in the event of an exception. There's no point in continuing the cycle. The code needs to be rewritten, checking the original task. Additionally provide a more informative error message.

Finally

I, as you probably already guessed, am one of the people who loves the 1C platform and development on it. Of course, there are complaints about the platform, especially in the Highload environment, but in general, it allows you to inexpensively and quickly develop very high-quality enterprise applications. Providing out of the box an ORM, a GUI, a web interface, Reporting, and much more. In the comments on Habré they usually write all sorts of arrogant things, so guys - the main problem with 1C, as an ecosystem, is not a platform or a vendor. This is too low a threshold for entry, which allows people to enter the industry who do not understand what a computer, database, client-server, network and all that is. 1C has made enterprise application development too easy. In 20 minutes I can write an accounting system for purchases/sales with flexible reports and a web client. After this, it’s easy for me to think to myself that on a larger scale you can write in much the same way. Somehow 1C will do everything internally, I don’t know how, but it will probably do it. Let me write "StartTransaction()"....

Add tags

2017-10-31

How to create a variant for a transaction using SHD0?

Explanation of the question

In one of my notes, I described the sequence of actions that must be performed to hide a field in a transaction. The mechanism by which this operation can be performed is well known to many consultants. Let me remind you that the name of this mechanism is transaction SHD0.

In this note, I want to consider the sequence of actions that must be performed to implement the requirement:

  • Separate access to fields of one infotype for two groups of users working with it, provided that they have the same level of authority for this infotype. In other words, make some fields available to some users, and others to others.

From the source data: the transaction that users work with is PA30. The infotype does not matter in this case. Specifically in my example, I will use the HR Administration infotype.

An infotype is a good example 0002 - "Personal Data"

Let us formulate the problem of the so-called delimitation of powers as follows:

  • Field Mar.Status Infotype 0002 "Personal Data" must be made unavailable for editing (that is, read only);
  • Field Valid From Date of Current Marital Status Infotype 0002 "Personal Data" must be hidden.

Solution of the problem

To solve the problem posed above, it is necessary to perform several sequential actions, which will be discussed below. Briefly:

  1. Create a separate transaction for a group of users who need to change the access level to infotype fields;
  2. Create a screen variant in which to configure the visibility of the fields, followed by assigning the screen variant to the created transaction variant (it turned out too clumsy, but in fact nothing complicated).
  3. Create a variant for this transaction, assigning it a screen variant in which the appropriate field visibility is configured;
  4. Create a custom role in which to include a transaction and assign it to users;
  5. Perform testing.

1. Create a new transaction

Since the main transaction is PA30, let's take it as a basis, copying it into a new one. This can be done through a transaction SE93

In the table T588A copy the existing record with transaction code PA30 for the new one, and try to start the new transaction again

The transaction starts successfully. Let's move on to the next point.

2. Create a variant for a new transaction

We create a variant for a new transaction, in which we define the level of access to the infotype fields. Let me remind you, I need to change the access level of the fields " Mar.Status" And " Valid From Date of Current Marital Status". These actions must be performed in a transaction SHD0.

In field Transaction Code indicate the name of the new transaction (see point #1. Creating a new transaction)

2.1 Creating a screen variant

On the "Screen Variants" tab, you need to create a screen variant used for infotype 0002. In this variant, you must apply the desired display rules for the fields. All you need to know is the corresponding module pool and screen number (to do this, on the infotype editing screen, select from the menu System -> Status)

The following video fragment shows the sequence of actions that must be performed to create a screen variant

The screen option has been created. No changes have been observed yet.

2.2 Assigning a screen variant to a transaction variant

Assign the created screen variant to the transaction variant, and then activate the created transaction variant

2.3 Testing

The test script is very simple:

  • Start the transaction PA30, select the personnel number, open the infotype for editing 0002 and check that the above fields are displayed without changes
  • Start the transaction ZPA30, and perform exactly the same sequence of actions

The difference is obvious, which means the task has been successfully completed.

In preparation for 1C Expert certification, on the eve of two very important and global topics - blocking, I would like to look at something without which the above concepts are impossible - a DBMS transaction.

Transaction- a logically connected, indivisible sequence of actions. The transaction can either be completed in its entirety or not at all. To commit a transaction in the DBMS, the COMMIT method is used.

A typical example of a transaction is the transfer of funds from one account to another:

  1. start a transaction;
  2. read the amount of funds in account number 123;
  3. reduce account balance 123 by 100 rubles;
  4. save account balance number 123;
  5. read the amount of funds in account number 321;
  6. increase your balance by 100 rubles;
  7. record the new amount of funds in account 321;
  8. commit the transaction.

Get 267 video lessons on 1C for free:

As we can see, if a transaction is not completed completely, then it has no meaning.

Key requirements (ACID) for a transactional DBMS

One of the most common sets of requirements for transactions and transactional DBMSs is the ACID (Atomicity, Consistency, Isolation, Durability) set. These are the properties that any transaction must have:

  • Atomicity— no transaction should be recorded partially;
  • Consistency- the system is in a consistent state before the transaction begins and must remain in a consistent state after the transaction is completed;
  • Isolation— during the execution of a transaction, parallel transactions should not affect its result;
  • Durability- in the event of a failure, changes made by a successfully completed transaction must remain saved after the system returns to operation.

Transactions in 1C

Transactions in 1C 8.3 and 8.2 are created both automatically and described by the developers.

You can use the TransactionActive() method to find out whether a transaction is active.

An example of an automatic transaction is processing a document posting, writing a directory item to a database, writing a set of information register records, etc.


2023
ihaednc.ru - Banks. Investment. Insurance. People's ratings. News. Reviews. Loans