I'm trying to create a generic data model that will allow for a particular product (indicated by the FK product_id in the sample table below) to specify 0 or more price "factors" (I define "factor" as a unit of price added or subtracted in order to get the total).
So say there is this table:
===============================
price
===============================
price_id (PK)
product_id (FK)
label
operation (ENUM: add, subtract)
type (ENUM: amount, percentage)
value
A book's price might be represented this way:
====================================================================
price_id | product_id | label | operation | type | value
====================================================================
1 | 10 | Price | add | amount | 20
2 | 10 | Discount | subtract | percentage | .25
3 | 10 | Sales Tax | add | percentage | .1
This basically means:
Price: $20.00
Discount: - $5.00 (25%)
--------------------
Sub Total: $15.00
Sales Tax: $1.50 (10%)
------------------------
Total: $16.50
A few questions:
First of all you need a model of price labels, which is simple:
price_labels
id | label
1 | Price
2 | Discount
3 | Tax
Then a slightly modified version of the sample table that you've given:
products_prices
price_id|product_id|label_id|divider|value
1 10 1 1 20
2 10 2 100 -25
3 10 3 100 10
Here I just substituted the label with the corresponding id from the price_labels table as a foreign key. Additionally, I omitted the type field which is trivial since value can be positive or negative float number. I added the divider column to enable the percentage parameter. I think it is more easily read this way as well, since you say (and think) "minus twenty-five percent" not 0.25 .
Now the expression "abstraction" part is a bit more complicated and there could be a lot of solutions.
price_expressions
product_id | date_from | date_until | expression
10 |2011-11-02 04:00:00 |2011-11-12 04:00:00 | (SELECT divider*value from
products_prices
WHERE product_id=%PRODUCT_ID%
AND label_id=1)*
(SELECT 1+value/divider from products_prices
where product_id=%PRODUCT_ID% AND
label_id=2)*
(SELECT 1+value/divider from products_prices
where product_id=%PRODUCT_ID% AND
label_id=3)
In the expression field you can store a complex SQL statement in which you can just replace the %PRODUCT_ID% placeholder with the product_id value from the same row:
SELECT REPLACE(expression,'%PRODUCT_ID%',CAST(product_id AS char))
AS price_expression FROM price_expressions
WHERE product_id = 10 AND date_from>=DATE_OF_PURCHASE
AND date_until<=DATE_OF_PURCHASE
There are two possible variations of this the way I see it:
Hope this helps.
This seems a little over-engineered.
1) Wouldn't the sales tax percentage be a factor of where the item was purchased and not which item was purchased? I could see a field for "IsTaxable", but specifying the rate for each items seems incorrect.
2) Are you sure you need to incur the cost of making this generic? Are you already fairly certain there will be more factors in the future? If not, don't overcomplicate it.
Suggested Design:
- Add columns to the products table for IsTaxable, DiscountPct, and Unit Price.
- Store the Sales tax percentage in another table. Probably the invoice table.
There is a potential functional dependency between label
, operation
and type
. For example, a discount might always imply subtraction and percentage. If so, the data model can be normalized by moving these fields to a separate table with label
as a PK.
BTW, a de-normalized data model may be a legitimate tool for improving performance and/or simplicity.
Here is a model that allows easy "templating":
The final price of a product is calculated by applying the series of steps on PRICE, in order defined by STEP_NO. Multiple products can easily share the same "template" (i.e. the same PRICE_ADJUSTMENT_ID).
You'd need to model a full expression tree, not just a "linear" series of steps. There are several ways to do that, most of them fairly complicated in relational paradigm. Perhaps the simplest one is to keep the data model similar to above, but treat it as Reverse Polish Notation.
For example...
A + B + (A + 10%) - (B - 5%)
...could be represented as:
OPERATION TYPE VALUE
---- ---- -----
value A
value B
add
value A
percentage 10
add
add
value B
percentage 5
subtract
subtract
Are you sure you actually need this kind of functionality?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With