A few days ago I conducted a partnership seminar - training new employees. Concerned the issue of calculating the totals (registers) of the system, in particular, the calculation of the cost price.
In the evening, one of the listeners sent a link to
an article detailing the issues of cost calculation in MS Axapta .
A request to comment on the main differences in approaches.
The author honestly spent two days at least 8 hours, understanding the text.As a result, I was forced to admit defeat - I was NOT able to figure out how it works.
An incredible amount of special cases, settings, dozens of pages describing how to handle the features of working with each account, cost transfer procedures, and migration of lots.
Having spent two days trying to understand this text, I still did not get a clear understanding of what I need to do if I want to take into account the goods not only in the warehouse, but, for example, in the driver. Or I want to consider the cost price not only for the goods (these could be claims in the guarantee, objects of fixed assets or anything else - yes, the same money).
In addition, some surprise was caused by the presence of special sections describing the closure of the service warehouse and a description of write-off errors during rounding. Why even deduct services from the warehouse?
A two-stage system for dealing with rounding residues also did not fit in my head.
Passage type
It would be more correct to accumulate the amount of errors on some selected account, and then, when transforming the balance, close this account manually. In order to achieve this effect, you need to correct the method inventAdj :: errorAccountOperation (), so that it returns the account of rounding errors you need. I would probably use for this account deviations from the standard cost price. If standard costing is used, then these accounts just need to be written off for deviations, and if not used, then these accounts are not occupied in setting up the warehouse postings and can be adjusted for writing off errors and rounding off. If this scheme suits you, it is enough to change the values ​​of InventAccountType :: InventProfit and InventAccountType :: InventLoss to InventAccountType :: InventStdProfit and InventAccTType :: InventStdLoss in InventAdj :: errorAccountOperation () method, respectively.
on the one hand - they inspire respect, on the other - they inspire horror.
The study of this document, however, gave the idea of ​​publishing on Habré on how the cost calculation is arranged in our country.
I did not describe the details that do not affect the understanding of the essence of the process, if desired, they can be found in the
extract from the documentation .
The cost calculation is probably one of the most difficult to understand processes in our system, and it is also one of the most ordered. IMHO.
')
As a spoiler picture:

In our case, the cost calculation is the process of calculating the cost of a specific accounting unit at each of its movement.
Perhaps it sounds incomprehensible, try to explain.
We use totals (similar to registers in 1C) - some analogue of a multidimensional cube, well, or its relational representation. Accordingly, the total displays the current status of the company’s measurable indicators.
A textbook example - the remnants of the goods in stock. The total, with measurements warehouse and goods and variables quantity and amount. The fact table (which is part of the total) contains the actual data on the changes - we call them transactions, the accountant - the transactions. The cost calculation system is not limited to the actual cost calculation, but at the platform level it is generalized before calculating the values ​​of variable totals in accordance with one or another algorithm. Such algorithms (or drivers in our terminology) are tied to the results (one for the total).
Let's return to the first diagram.
A special service (in this case, not a Windows service, but a service in terms of an application server as a class) participates in the calculation process. TotalCalculator reads the transactions from the fact table, transfers them to the appropriate drivers, and applies the changes to the fact table.
In turn, transactions contain values ​​for dimensions and deltas for variables. Some of the dimensions and variables may not contain a value at the time the transaction is created. To be precise, we have 2 fact tables - operational and detailed, which are distinguished by the fact that the detailed is filled in during the work of the totals calculator. In this case, there is a certain access monopoly, which allows, for example, to use bitmap indexes, and also perform other optimization tricks.
The empty value of a variable in a transaction means that it should be calculated later. For example, when a product is written off from a warehouse, it is prompt (without executing the FIFO algorithm) that it is impossible to calculate the cost at which goods are written off. Therefore, the value of the variable quantity is indicated, and the amount is not indicated.
In turn, when purchasing a product (simplifying) the cost is known, and is equal to the purchase price. In this case, the transaction will indicate the amount and amount.
Thus, the driver for the total stock balance implements the FIFO algorithm.
But the driver for the total account balances implements a modified FIFO for the cost of money, taking into account the presence of an overdraft (this assumes the existence of transactions that write off money that was not previously debited). Proper implementation and organization of drivers would have been impossible if it were not for double-record support - each transaction knows the pair to it. In a pair, one transaction is always outgoing, the other is incoming (this is determined by the signs of the values ​​of the variables, more precisely, the outgoing transaction is always negative, and the incoming one is positive).
The driver receives a pair of entries at the input, and its task is to return the calculated wiring.
Accordingly, the algorithm for calculating the cost of FIFO looks like this:
, , , . .
For the cost of money it happens that an outgoing transaction spends money when there is an empty queue of lots. In this case, we use the current (on the day of the transaction) rate of the Central Bank of the Russian Federation (in the variant for Russia).
Similarly, for the results with a different purpose, the drivers use other algorithms. In a real
Ultima eCommerceERP configuration
, no more than 10 drivers are enough. Thus, information on the calculation of the results is stored in a neatly structured form, laid out in several drivers, each of which implements a clearly defined functionality.
As a result :
- The cost calculation logic is isolated in the total drivers.
- Simplified unit testing functionality
- Safe code is now written faster
- Reduced the “input threshold” of the developer to make changes, increasing the number of developers who can implement the functionality
Regarding performance, in one of the installations of the system several million transactions per hour are processed in this way.