-
Notifications
You must be signed in to change notification settings - Fork 3
by code
The logic requirements can be summarized in the following rule-based specification:
Rule.constraint(validate=Customer, as_condition=lambda row: row.Balance <= row.CreditLimit,
error_msg="balance ({row.Balance}) exceeds credit ({row.CreditLimit})")
Rule.sum(derive=Customer.Balance, as_sum_of=Order.AmountTotal,
where=lambda row: row.ShippedDate is None)
Rule.sum(derive=Order.AmountTotal, as_sum_of=OrderDetail.Amount)
Rule.formula(derive=OrderDetail.Amount, as_expression=lambda row: row.UnitPrice * row.Quantity)
Rule.copy(derive=OrderDetail.UnitPrice, from_parent=Product.UnitPrice)
Rule.formula(derive=OrderDetail.ShippedDate, as_expression=lambda row: row.OrderHeader.ShippedDate)
Rule.sum(derive=Product.UnitsShipped, as_sum_of=OrderDetail.Quantity,
where="row.ShippedDate is not None")
Rule.formula(derive=Product.UnitsInStock, calling=units_shipped)
The specification addresses around a dozen transactions. Here we look at:
- Add Order (Check Credit) - enter an order/orderdetails, and rollup to AmountTotal / Balance to check CreditLimit
- Ship / Unship an Order (Adjust Balance) - when an Order's DateShippped is changed, adjust the Customers balance
These required about 200 lines of code (not counting shareable logic in utils):
- 20 in
__init__.py
to set up listenerscode here
- 90 in
nw_logic/order_code.py
for Order code (code here) - 90 in
nw_logic/order_detail_code.py
for OrderDetail code (code here)
The by-hand code does not address the cascade of ShippedDate into OrderDetails, nor its adjustment to Products.
Here we focus on placing an order, and checking credit. The focus here is on multi-level roll-ups, to compute the balance and check it against the credit.
Execution begins in trans_tests/add_order.py
.
The import statement from nw.nw_logic import session
runs
nw_logic/__init__
,
which opens the database and registers the listeners.
When add_order.py
issues commit, sqlalchemy invokes the
listeners
.
These forward before_flush
events to
nw_logic/order_details_code.py
and nw_logic/order_code.py
.
Adjustments require we access/update rows that were not in the dirty list. Observations
- for attached parents (like customer, arising
from the
upd_order_shipped
test), updates are automatic. They do not, however, trigger flush logic. - for non-attached parents (like customer, arising
from the
add_order
test)- you need to read them
- add them to the session
- and explicitly invoke their update logic
(
customer_update(customer, old_customer, a_session)
)
Here we explore old values - we need
to see what the ShippedDate
was, and
what it is changed to:
- If it changed from empty to shipped, we need to decrease the balance.
- If it changed from shipped to empty, we decrease the balance.
Observe the service to get an old_row, so you can code
things like if a_row.ShippedDate != an_old_row.ShippedDate:
Flow is as described above, reaching nw_logic/order_logic.py
Note the call to logic_engine/util.get_old_row
.

Technology
Explore Implementation