I propose to make a table of operations with the following structure:
Операции товаров { ID-записи, ID-товара, ID-операции, если есть справочник типовых операций или ее название и т.п. цена операции, Дата начала действия, Дата окончания действия (01.01.3000 для самых последних операций/расценок) }
In the order table or elsewhere, keep the manufacturing date, i.e. the date on which the list of transactions / prices is taken. The required set of operations in any calculations is taken as the ДатаИзготовления between ДатаНачала and ДатаОкончания . When changing the rates for a specific operation from 01/12/2016 in the old record, the end date is set on 01/11/2016 and the same record is added with the start date of 01/12/01 and 01/01/3000. If the operation is no longer performed, then the date of the old record only changes.
Thus, on the one hand, you keep the entire history of changes in transactions / prices and you can count anything. On the other hand, you do not need to keep a complete list of all completed actions for each order.
If, for some reason, pricing is more complicated and the start date is not enough, then you can enter the table "Price Versions", where each version will be assigned an ID and keep the list of operations for the version, and then order that N copies of the goods are made according to version prices so.
PS Another plus of this storage scheme: you can pre-add to the system "prices that will take effect in a month." Working with the system does not have to urgently, in an emergency mode to make prices at the last minute.
Of the minuses: the logic is a little more complicated because of the dates. It may be worthwhile to implement a trigger or other control methods that will not allow setting the expiration date of the pricing lower than it already exists in the recorded orders with this product. Although it can be limited to the rule that the end date cannot be less than the current one.