Behave Logic Report
TL;DR - Executable Test Suite, Documentation
You can optionally use the Behave test framework to (here is an Agile Approach for using Behave) to capture requirements, and execute a test suite.
-
As shown below, you can then create a wiki Behave Logic Report report that documents your requirements (Features), and the tests (Scenarios) that confirm their proper operation.
- Integrated Logic Documentation: the report integrates your logic, including a logic report showing your logic (rules and Python), and a Logic Log that shows exactly how the rules executed. Logic Doc is transparent to business users, so can further contribute to Agile Collaboration.
Northwind Sample
This is the sample project from API Logic Server, based on the Northwind database (sqlite database located in the database
folder - no installation required):
The sample Scenarios below were chosen to illustrate the basic patterns of using rules. Open the disclosure box ("Tests - and their logic...") to see the implementation and notes.
The following report was created during test suite execution.
Behave Logic Report
Feature: About Sample
Scenario: Transaction Processing
Scenario: Transaction Processing
Given Sample Database
When Transactions are submitted
Then Enforce business policies with Logic (rules + code)
Tests - and their logic - are transparent.. click to see Logic
Rules Used in Scenario: Transaction Processing
Category
1. Constraint Function: <function declare_logic.<locals>.valid_category_description at 0x10b83fba0>
The following rules have been activate
- 2024-07-12 14:57:05,237 - logic_logger - DEBU
Rule Bank[0x10a431ca0] (loaded 2024-07-12 14:56:46.929015
Mapped Class[Customer] rules
Constraint Function: None
Constraint Function: None
Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x10b83fd80>
RowEvent Customer.customer_defaults()
Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x10b9596c0>
Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None
Mapped Class[Employee] rules
Constraint Function: None
Constraint Function: <function declare_logic.<locals>.raise_over_20_percent at 0x10b959940>
Copy to: EmployeeAudi
Mapped Class[Category] rules
Constraint Function: <function declare_logic.<locals>.valid_category_description at 0x10b83fba0>
Mapped Class[Order] rules
Derive Order.AmountTotal as Sum(OrderDetail.Amount Where None
RowEvent Order.send_order_to_shipping()
RowEvent Order.congratulate_sales_rep()
RowEvent Order.do_not_ship_empty_orders()
Constraint Function: <function declare_logic.<locals>.ship_ready_orders_only at 0x10b9591c0>
RowEvent Order.order_defaults()
Derive Order.OrderDetailCount as Count(<class 'database.models.OrderDetail'> Where None
RowEvent Order.clone_order()
Derive Order.OrderDate as Formula (1): as_expression=lambda row: datetime.datetime.now()
Mapped Class[OrderDetail] rules
Derive OrderDetail.Amount as Formula (1): as_expression=lambda row: row.UnitPrice * row.Qua [...
Derive OrderDetail.UnitPrice as Copy(Product.UnitPrice
RowEvent OrderDetail.order_detail_defaults()
Derive OrderDetail.ShippedDate as Formula (2): row.Order.ShippedDat
Mapped Class[Product] rules
Derive Product.UnitsInStock as Formula (1): <function
Derive Product.UnitsShipped as Sum(OrderDetail.Quantity Where <function declare_logic.<locals>.<lambda> at 0x10b959580>
Logic Bank - 32 rules loaded - 2024-07-12 14:57:05,243 - logic_logger - INF
Logic Bank - 32 rules loaded - 2024-07-12 14:57:05,243 - logic_logger - INF
Logic Phase: ROW LOGIC (session=0x10dbbc710) (sqlalchemy before_flush) - 2024-07-12 14:57:06,076 - logic_logger - INF
..Shipper[1] {Delete - client} Id: 1, CompanyName: Speedy Express, Phone: (503) 555-9831 row: 0x10dbbce30 session: 0x10dbbc710 ins_upd_dlt: dlt - 2024-07-12 14:57:06,077 - logic_logger - INF
Logic Phase: ROW LOGIC (session=0x10dcac9e0) (sqlalchemy before_flush) - 2024-07-12 14:57:06,151 - logic_logger - INF
..Category[1] {Update - client} Id: 1, CategoryName: Beverages, Description: [Soft drinks, coffees, teas, beers, and ales-->] x, Client_id: 1 row: 0x10dcad6a0 session: 0x10dcac9e0 ins_upd_dlt: upd - 2024-07-12 14:57:06,151 - logic_logger - INF
..Category[1] {Constraint Failure: Description cannot be 'x'} Id: 1, CategoryName: Beverages, Description: [Soft drinks, coffees, teas, beers, and ales-->] x, Client_id: 1 row: 0x10dcad6a0 session: 0x10dcac9e0 ins_upd_dlt: upd - 2024-07-12 14:57:06,152 - logic_logger - INF
Feature: Application Integration
Scenario: GET Customer
Scenario: GET Customer
Given Customer Account: VINET
When GET Orders API
Then VINET retrieved
Scenario: GET Department
Scenario: GET Department
Given Department 2
When GET Department with SubDepartments API
Then SubDepartments returned
Feature: Authorization
Scenario: Grant
Scenario: Grant
Given NW Test Database
When u1 GETs Categories
Then Only 1 is returned
Scenario: Multi-tenant
Scenario: Multi-tenant
Given NW Test Database
When sam GETs Customers
Then only 3 are returned
Scenario: Global Filters
Scenario: Global Filters
Given NW Test Database
When sam GETs Departments
Then only 8 are returned
Scenario: Global Filters With Grants
Scenario: Global Filters With Grants
Given NW Test Database
When s1 GETs Customers
Then only 1 customer is returned
Scenario: CRUD Permissions
Scenario: CRUD Permissions
Given NW Test Database
When r1 deletes a Shipper
Then Operation is Refused
Feature: Optimistic Locking
Scenario: Get Category
Scenario: Get Category
Given Category: 1
When Get Cat1
Then Expected Cat1 Checksum
Scenario: Valid Checksum
Scenario: Valid Checksum
Given Category: 1
When Patch Valid Checksum
Then Valid Checksum, Invalid Description
Scenario: Missing Checksum
Scenario: Missing Checksum
Given Category: 1
When Patch Missing Checksum
Then Valid Checksum, Invalid Description
Scenario: Invalid Checksum
Scenario: Invalid Checksum
Given Category: 1
When Patch Invalid Checksum
Then Invalid Checksum
Feature: Place Order
Scenario: Order Made Not Ready
Scenario: Order Made Not Ready
Given Customer Account: ALFKI
When Ready Flag is Reset
Then Logic Decreases Balance
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Order Made Not Ready
We reset Order.Ready
.
This removes the order from contingent derivations (e.g., the Customer.Balance
),
and constraints.
Key Takeaway: adjustment from change in qualification condition
Rules Used in Scenario: Order Made Not Ready
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
3. RowEvent Customer.customer_defaults()
4. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
5. RowEvent Order.order_defaults()
6. RowEvent Order.clone_order()
7. RowEvent Order.congratulate_sales_rep()
8. RowEvent Order.do_not_ship_empty_orders()
9. RowEvent Order.send_order_to_shipping()
Logic Phase: ROW LOGIC (session=0x12061d190) (sqlalchemy before_flush) - 2024-08-31 21:14:53,887 - logic_logger - INF
..Order[11011] {Update - client} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [True-->] False, OrderDetailCount: 2, CloneFromOrder: None row: 0x12061f860 session: 0x12061d190 ins_upd_dlt: upd - 2024-08-31 21:14:53,888 - logic_logger - INF
..Order[11011] {Prune Formula: OrderDate [[]]} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [True-->] False, OrderDetailCount: 2, CloneFromOrder: None row: 0x12061f860 session: 0x12061d190 ins_upd_dlt: upd - 2024-08-31 21:14:53,889 - logic_logger - INF
....Customer[ALFKI] {Update - Adjusting Customer: Balance} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 1142.0000000000, CreditLimit: 2300.0000000000, OrderCount: 15, UnpaidOrderCount: 10, Client_id: 1 row: 0x1207563c0 session: 0x12061d190 ins_upd_dlt: upd - 2024-08-31 21:14:53,893 - logic_logger - INF
Logic Phase: COMMIT LOGIC (session=0x12061d190) - 2024-08-31 21:14:53,896 - logic_logger - INF
..Order[11011] {Commit Event} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [True-->] False, OrderDetailCount: 2, CloneFromOrder: None row: 0x12061f860 session: 0x12061d190 ins_upd_dlt: upd - 2024-08-31 21:14:53,897 - logic_logger - INF
..Order[11011] {Commit Event} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [True-->] False, OrderDetailCount: 2, CloneFromOrder: None row: 0x12061f860 session: 0x12061d190 ins_upd_dlt: upd - 2024-08-31 21:14:53,897 - logic_logger - INF
Logic Phase: AFTER_FLUSH LOGIC (session=0x12061d190) - 2024-08-31 21:14:53,903 - logic_logger - INF
..Order[11011] {AfterFlush Event} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [True-->] False, OrderDetailCount: 2, CloneFromOrder: None row: 0x12061f860 session: 0x12061d190 ins_upd_dlt: upd - 2024-08-31 21:14:53,904 - logic_logger - INF
Scenario: Order Made Ready
Scenario: Order Made Ready
Given Customer Account: ALFKI
When Ready Flag is Set
Then Logic Increases Balance
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Order Made Ready
This illustrates the ready flag pattern: 1. Add a ready flag to the Order 2. Make logic contingent on the ready flag: * Customer.Balance is increased only if the Order is ready * Empty Orders are not rejected
This enables the user to submit multiple transactions (add order details, alter them etc), before making the order ready (like a checkout).
Until then, Customer's Balance adjustments, or empty orders constraints do not fire.
Key Takeaway: the ready flag defers constraints/derivations until the user is ready.
Key Takeaway: adjustment from change in qualification condition
Rules Used in Scenario: Order Made Ready
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
3. RowEvent Customer.customer_defaults()
4. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
5. RowEvent Order.order_defaults()
6. RowEvent Order.clone_order()
7. RowEvent Order.congratulate_sales_rep()
8. RowEvent Order.do_not_ship_empty_orders()
9. RowEvent Order.send_order_to_shipping()
Logic Phase: ROW LOGIC (session=0x12078f650) (sqlalchemy before_flush) - 2024-08-31 21:14:54,247 - logic_logger - INF
..Order[11011] {Update - client} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [False-->] True, OrderDetailCount: 2, CloneFromOrder: None row: 0x12078d280 session: 0x12078f650 ins_upd_dlt: upd - 2024-08-31 21:14:54,248 - logic_logger - INF
..Order[11011] {Prune Formula: OrderDate [[]]} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [False-->] True, OrderDetailCount: 2, CloneFromOrder: None row: 0x12078d280 session: 0x12078f650 ins_upd_dlt: upd - 2024-08-31 21:14:54,249 - logic_logger - INF
....Customer[ALFKI] {Update - Adjusting Customer: Balance} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [1142.0000000000-->] 2102.0000000000, CreditLimit: 2300.0000000000, OrderCount: 15, UnpaidOrderCount: 10, Client_id: 1 row: 0x12078fd40 session: 0x12078f650 ins_upd_dlt: upd - 2024-08-31 21:14:54,251 - logic_logger - INF
Logic Phase: COMMIT LOGIC (session=0x12078f650) - 2024-08-31 21:14:54,254 - logic_logger - INF
..Order[11011] {Commit Event} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [False-->] True, OrderDetailCount: 2, CloneFromOrder: None row: 0x12078d280 session: 0x12078f650 ins_upd_dlt: upd - 2024-08-31 21:14:54,255 - logic_logger - INF
..Order[11011] {Commit Event} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [False-->] True, OrderDetailCount: 2, CloneFromOrder: None row: 0x12078d280 session: 0x12078f650 ins_upd_dlt: upd - 2024-08-31 21:14:54,255 - logic_logger - INF
Logic Phase: AFTER_FLUSH LOGIC (session=0x12078f650) - 2024-08-31 21:14:54,257 - logic_logger - INF
..Order[11011] {AfterFlush Event} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [False-->] True, OrderDetailCount: 2, CloneFromOrder: None row: 0x12078d280 session: 0x12078f650 ins_upd_dlt: upd - 2024-08-31 21:14:54,258 - logic_logger - INF
..Order[11011] {Sending Order to Shipping << not activated >>} Id: 11011, CustomerId: ALFKI, EmployeeId: 3, OrderDate: 2014-04-09, RequiredDate: 2014-05-07, ShippedDate: None, ShipVia: 1, Freight: 1.2100000000, ShipName: Alfred's Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 960.00, Country: None, City: None, Ready: [False-->] True, OrderDetailCount: 2, CloneFromOrder: None row: 0x12078d280 session: 0x12078f650 ins_upd_dlt: upd - 2024-08-31 21:14:54,267 - logic_logger - INF
Scenario: Good Order Custom Service
Scenario: Good Order Custom Service
Given Customer Account: ALFKI
When Good Order Placed
Then Logic adjusts Balance (demo: chain up)
Then Logic adjusts Products Reordered
Then Logic sends email to salesrep
Then Logic sends kafka message
Then Logic adjusts aggregates down on delete order
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Good Order Custom Service
Familiar logic patterns:
- Constrain a derived result (Check Credit)
- Chain up, to adjust parent sum/count aggregates (AmountTotal, Balance)
- Events for Lib Access (Kafka, email messages)
Logic Design ("Cocktail Napkin Design")
- Customer.Balance <= CreditLimit
- Customer.Balance = Sum(Order.AmountTotal where unshipped)
- Order.AmountTotal = Sum(OrderDetail.Amount)
- OrderDetail.Amount = Quantity * UnitPrice
- OrderDetail.UnitPrice = copy from Product
We place an Order with an Order Detail. It's one transaction.
Note how the Order.AmountTotal
and Customer.Balance
are adjusted as Order Details are processed.
Similarly, the Product.UnitsShipped
is adjusted, and used to recompute UnitsInStock
Key Takeaway: sum/count aggregates (e.g.,
Customer.Balance
) automate chain up multi-table transactions.
Events - Extensible Logic
Inspect the log for Hi, Andrew - Congratulate Nancy on their new order.
The congratulate_sales_rep
event illustrates logic
Extensibility
- using Python to provide logic not covered by rules,
like non-database operations such as sending email or messages.
There are actually multiple kinds of events:
- Before row logic
- After row logic
- On commit, after all row logic has completed (as here), so that your code "sees" the full logic results
Events are passed the row
and old_row
, as well as logic_row
which enables you to test the actual operation, chaining nest level, etc.
You can set breakpoints in events, and inspect these.
Rules Used in Scenario: Good Order Custom Service
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
3. RowEvent Customer.customer_defaults()
4. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
5. RowEvent Order.order_defaults()
6. RowEvent Order.do_not_ship_empty_orders()
7. Derive Order.OrderDate as Formula (1): as_expression=lambda row: datetime.datetime.now())
8. Derive Order.AmountTotal as Sum(OrderDetail.Amount Where None)
9. RowEvent Order.clone_order()
10. RowEvent Order.congratulate_sales_rep()
11. Derive Order.OrderDetailCount as Count(<class 'database.models.OrderDetail'> Where None)
12. RowEvent Order.send_order_to_shipping()
OrderDetail
13. RowEvent OrderDetail.order_detail_defaults()
14. Derive OrderDetail.UnitPrice as Copy(Product.UnitPrice)
15. Derive OrderDetail.Amount as Formula (1): as_expression=lambda row: row.UnitPrice * row.Qua [...]
16. Derive OrderDetail.ShippedDate as Formula (2): row.Order.ShippedDate
Product
17. Derive Product.UnitsShipped as Sum(OrderDetail.Quantity Where <function declare_logic.<locals>.<lambda> at 0x105744d60>)
18. Derive Product.UnitsInStock as Formula (1): <function>
Logic Phase: ROW LOGIC (session=0x1207d6f30) (sqlalchemy before_flush) - 2024-08-31 21:14:54,620 - logic_logger - INF
..OrderDetail[None] {Insert - client} Id: None, OrderId: None, ProductId: 1, UnitPrice: None, Quantity: 1, Discount: 0, Amount: None, ShippedDate: None row: 0x1207d6f00 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,621 - logic_logger - INF
..OrderDetail[None] {copy_rules for role: Product - UnitPrice} Id: None, OrderId: None, ProductId: 1, UnitPrice: 18.0000000000, Quantity: 1, Discount: 0, Amount: None, ShippedDate: None row: 0x1207d6f00 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,625 - logic_logger - INF
..OrderDetail[None] {Formula Amount} Id: None, OrderId: None, ProductId: 1, UnitPrice: 18.0000000000, Quantity: 1, Discount: 0, Amount: 18.0000000000, ShippedDate: None row: 0x1207d6f00 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,625 - logic_logger - INF
....Order[None] {Adjustment logic chaining deferred for this parent parent do_defer_adjustment: True, is_parent_submitted: True, is_parent_row_processed: False, Order} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: [None-->] 18.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: [None-->] 1, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: * - 2024-08-31 21:14:54,627 - logic_logger - INF
....Product[1] {Update - Adjusting Product: UnitsShipped} Id: 1, ProductName: Chai, SupplierId: 1, CategoryId: 1, QuantityPerUnit: 10 boxes x 20 bags, UnitPrice: 18.0000000000, UnitsInStock: 39, UnitsOnOrder: 0, ReorderLevel: 10, Discontinued: 0, UnitsShipped: [0-->] 1 row: 0x1207d60c0 session: 0x1207d6f30 ins_upd_dlt: upd - 2024-08-31 21:14:54,627 - logic_logger - INF
....Product[1] {Formula UnitsInStock} Id: 1, ProductName: Chai, SupplierId: 1, CategoryId: 1, QuantityPerUnit: 10 boxes x 20 bags, UnitPrice: 18.0000000000, UnitsInStock: [39-->] 38, UnitsOnOrder: 0, ReorderLevel: 10, Discontinued: 0, UnitsShipped: [0-->] 1 row: 0x1207d60c0 session: 0x1207d6f30 ins_upd_dlt: upd - 2024-08-31 21:14:54,628 - logic_logger - INF
..Order[None] {Insert - client} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 18.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 1, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,629 - logic_logger - INF
..Order[None] {server_defaults: -- skipped: Ready[BOOLEAN (not handled)] } Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 18.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 1, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,630 - logic_logger - INF
..Order[None] {Formula OrderDate} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 18.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 1, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,637 - logic_logger - INF
....Customer[ALFKI] {Update - Adjusting Customer: Balance, UnpaidOrderCount, OrderCount} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 2120.0000000000, CreditLimit: 2300.0000000000, OrderCount: [15-->] 16, UnpaidOrderCount: [10-->] 11, Client_id: 1 row: 0x1207d44a0 session: 0x1207d6f30 ins_upd_dlt: upd - 2024-08-31 21:14:54,638 - logic_logger - INF
..OrderDetail[None] {Insert - client} Id: None, OrderId: None, ProductId: 2, UnitPrice: None, Quantity: 2, Discount: 0, Amount: None, ShippedDate: None row: 0x1207d70e0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,641 - logic_logger - INF
..OrderDetail[None] {copy_rules for role: Product - UnitPrice} Id: None, OrderId: None, ProductId: 2, UnitPrice: 19.0000000000, Quantity: 2, Discount: 0, Amount: None, ShippedDate: None row: 0x1207d70e0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,643 - logic_logger - INF
..OrderDetail[None] {Formula Amount} Id: None, OrderId: None, ProductId: 2, UnitPrice: 19.0000000000, Quantity: 2, Discount: 0, Amount: 38.0000000000, ShippedDate: None row: 0x1207d70e0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,644 - logic_logger - INF
....Order[None] {Update - Adjusting Order: AmountTotal, OrderDetailCount} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: [18.0000000000-->] 56.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: [1-->] 2, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: upd - 2024-08-31 21:14:54,645 - logic_logger - INF
....Order[None] {Prune Formula: OrderDate [[]]} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: [18.0000000000-->] 56.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: [1-->] 2, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: upd - 2024-08-31 21:14:54,646 - logic_logger - INF
......Customer[ALFKI] {Update - Adjusting Customer: Balance} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2120.0000000000-->] 2158.0000000000, CreditLimit: 2300.0000000000, OrderCount: 16, UnpaidOrderCount: 11, Client_id: 1 row: 0x1207d44a0 session: 0x1207d6f30 ins_upd_dlt: upd - 2024-08-31 21:14:54,647 - logic_logger - INF
....Product[2] {Update - Adjusting Product: UnitsShipped} Id: 2, ProductName: Chang, SupplierId: 1, CategoryId: 1, QuantityPerUnit: 24 - 12 oz bottles, UnitPrice: 19.0000000000, UnitsInStock: 17, UnitsOnOrder: 40, ReorderLevel: 25, Discontinued: 0, UnitsShipped: [0-->] 2 row: 0x1207d64e0 session: 0x1207d6f30 ins_upd_dlt: upd - 2024-08-31 21:14:54,650 - logic_logger - INF
....Product[2] {Formula UnitsInStock} Id: 2, ProductName: Chang, SupplierId: 1, CategoryId: 1, QuantityPerUnit: 24 - 12 oz bottles, UnitPrice: 19.0000000000, UnitsInStock: [17-->] 15, UnitsOnOrder: 40, ReorderLevel: 25, Discontinued: 0, UnitsShipped: [0-->] 2 row: 0x1207d64e0 session: 0x1207d6f30 ins_upd_dlt: upd - 2024-08-31 21:14:54,651 - logic_logger - INF
Logic Phase: COMMIT LOGIC (session=0x1207d6f30) - 2024-08-31 21:14:54,651 - logic_logger - INF
..Order[None] {Commit Event} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 56.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 2, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,652 - logic_logger - INF
..Order[None] {Hi, Andrew - Congratulate Nancy on their new order} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 56.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 2, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,656 - logic_logger - INF
..Order[None] {Illustrate database access} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 56.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 2, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,657 - logic_logger - INF
..Order[None] {Commit Event} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 56.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 2, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,658 - logic_logger - INF
Logic Phase: AFTER_FLUSH LOGIC (session=0x1207d6f30) - 2024-08-31 21:14:54,670 - logic_logger - INF
..Order[11078] {AfterFlush Event} Id: 11078, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 56.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 2, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,670 - logic_logger - INF
..Order[11078] {Sending Order to Shipping << not activated >>} Id: 11078, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:54.637311, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 56.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 2, CloneFromOrder: None row: 0x1207d7ce0 session: 0x1207d6f30 ins_upd_dlt: ins - 2024-08-31 21:14:54,674 - logic_logger - INF
Scenario: Bad Ship of Empty Order
Scenario: Bad Ship of Empty Order
Given Customer Account: ALFKI
When Order Shipped with no Items
Then Rejected per Do Not Ship Empty Orders
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Bad Ship of Empty Order
Reuse the rules for Good Order...
Familiar logic patterns:
- Constrain a derived result
- Counts as existence checks
Logic Design ("Cocktail Napkin Design")
- Constraint: do_not_ship_empty_orders()
- Order.OrderDetailCount = count(OrderDetail)
Rules Used in Scenario: Bad Ship of Empty Order
Logic Log in Scenario: Bad Ship of Empty OrderLogic Phase: ROW LOGIC (session=0x121069d60) (sqlalchemy before_flush) - 2024-08-31 21:14:55,264 - logic_logger - INF
..Order[None] {Insert - client} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: 2013-10-13, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: None, Country: None, City: None, Ready: True, OrderDetailCount: None, CloneFromOrder: None row: 0x12106a090 session: 0x121069d60 ins_upd_dlt: ins - 2024-08-31 21:14:55,265 - logic_logger - INF
..Order[None] {server_defaults: OrderDetailCount -- skipped: Ready[BOOLEAN (not handled)] } Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: 2013-10-13, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: None, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: None row: 0x12106a090 session: 0x121069d60 ins_upd_dlt: ins - 2024-08-31 21:14:55,266 - logic_logger - INF
..Order[None] {Formula OrderDate} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:55.270516, RequiredDate: None, ShippedDate: 2013-10-13, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 0, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: None row: 0x12106a090 session: 0x121069d60 ins_upd_dlt: ins - 2024-08-31 21:14:55,270 - logic_logger - INF
....Customer[ALFKI] {Update - Adjusting Customer: OrderCount} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: 2102.0000000000, CreditLimit: 2300.0000000000, OrderCount: [15-->] 16, UnpaidOrderCount: 10, Client_id: 1 row: 0x12106b260 session: 0x121069d60 ins_upd_dlt: upd - 2024-08-31 21:14:55,271 - logic_logger - INF
Logic Phase: COMMIT LOGIC (session=0x121069d60) - 2024-08-31 21:14:55,274 - logic_logger - INF
..Order[None] {Commit Event} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:55.270516, RequiredDate: None, ShippedDate: 2013-10-13, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 0, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: None row: 0x12106a090 session: 0x121069d60 ins_upd_dlt: ins - 2024-08-31 21:14:55,274 - logic_logger - INF
..Order[None] {Hi, Andrew - Congratulate Nancy on their new order} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:55.270516, RequiredDate: None, ShippedDate: 2013-10-13, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 0, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: None row: 0x12106a090 session: 0x121069d60 ins_upd_dlt: ins - 2024-08-31 21:14:55,276 - logic_logger - INF
..Order[None] {Illustrate database access} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:55.270516, RequiredDate: None, ShippedDate: 2013-10-13, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 0, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: None row: 0x12106a090 session: 0x121069d60 ins_upd_dlt: ins - 2024-08-31 21:14:55,277 - logic_logger - INF
..Order[None] {Commit Event} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:55.270516, RequiredDate: None, ShippedDate: 2013-10-13, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 0, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: None row: 0x12106a090 session: 0x121069d60 ins_upd_dlt: ins - 2024-08-31 21:14:55,277 - logic_logger - INF
Scenario: Bad Order Custom Service
Scenario: Bad Order Custom Service
Given Customer Account: ALFKI
When Order Placed with excessive quantity
Then Rejected per Check Credit
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Bad Order Custom Service
Reuse the rules for Good Order...
Familiar logic patterns:
- Constrain a derived result
- Chain up, to adjust parent sum/count aggregates
Logic Design ("Cocktail Napkin Design")
- Customer.Balance <= CreditLimit
- Customer.Balance = Sum(Order.AmountTotal where unshipped)
- Order.AmountTotal = Sum(OrderDetail.Amount)
- OrderDetail.Amount = Quantity * UnitPrice
- OrderDetail.UnitPrice = copy from Product
Rules Used in Scenario: Bad Order Custom Service
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Constraint Function: None
3. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
4. RowEvent Customer.customer_defaults()
5. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
6. RowEvent Order.order_defaults()
7. Derive Order.OrderDate as Formula (1): as_expression=lambda row: datetime.datetime.now())
8. Derive Order.AmountTotal as Sum(OrderDetail.Amount Where None)
9. Derive Order.OrderDetailCount as Count(<class 'database.models.OrderDetail'> Where None)
OrderDetail
10. RowEvent OrderDetail.order_detail_defaults()
11. Derive OrderDetail.UnitPrice as Copy(Product.UnitPrice)
12. Derive OrderDetail.Amount as Formula (1): as_expression=lambda row: row.UnitPrice * row.Qua [...]
13. Derive OrderDetail.ShippedDate as Formula (2): row.Order.ShippedDate
Product
14. Derive Product.UnitsShipped as Sum(OrderDetail.Quantity Where <function declare_logic.<locals>.<lambda> at 0x105744d60>)
15. Derive Product.UnitsInStock as Formula (1): <function>
Logic Phase: ROW LOGIC (session=0x121069880) (sqlalchemy before_flush) - 2024-08-31 21:14:55,444 - logic_logger - INF
..OrderDetail[None] {Insert - client} Id: None, OrderId: None, ProductId: 1, UnitPrice: None, Quantity: 1111, Discount: 0, Amount: None, ShippedDate: None row: 0x121069250 session: 0x121069880 ins_upd_dlt: ins - 2024-08-31 21:14:55,445 - logic_logger - INF
..OrderDetail[None] {copy_rules for role: Product - UnitPrice} Id: None, OrderId: None, ProductId: 1, UnitPrice: 18.0000000000, Quantity: 1111, Discount: 0, Amount: None, ShippedDate: None row: 0x121069250 session: 0x121069880 ins_upd_dlt: ins - 2024-08-31 21:14:55,457 - logic_logger - INF
..OrderDetail[None] {Formula Amount} Id: None, OrderId: None, ProductId: 1, UnitPrice: 18.0000000000, Quantity: 1111, Discount: 0, Amount: 19998.0000000000, ShippedDate: None row: 0x121069250 session: 0x121069880 ins_upd_dlt: ins - 2024-08-31 21:14:55,460 - logic_logger - INF
....Order[None] {Adjustment logic chaining deferred for this parent parent do_defer_adjustment: True, is_parent_submitted: True, is_parent_row_processed: False, Order} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: [None-->] 19998.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: [None-->] 1, CloneFromOrder: None row: 0x1210695e0 session: 0x121069880 ins_upd_dlt: * - 2024-08-31 21:14:55,465 - logic_logger - INF
....Product[1] {Update - Adjusting Product: UnitsShipped} Id: 1, ProductName: Chai, SupplierId: 1, CategoryId: 1, QuantityPerUnit: 10 boxes x 20 bags, UnitPrice: 18.0000000000, UnitsInStock: 39, UnitsOnOrder: 0, ReorderLevel: 10, Discontinued: 0, UnitsShipped: [0-->] 1111 row: 0x121069670 session: 0x121069880 ins_upd_dlt: upd - 2024-08-31 21:14:55,466 - logic_logger - INF
....Product[1] {Formula UnitsInStock} Id: 1, ProductName: Chai, SupplierId: 1, CategoryId: 1, QuantityPerUnit: 10 boxes x 20 bags, UnitPrice: 18.0000000000, UnitsInStock: [39-->] -1072, UnitsOnOrder: 0, ReorderLevel: 10, Discontinued: 0, UnitsShipped: [0-->] 1111 row: 0x121069670 session: 0x121069880 ins_upd_dlt: upd - 2024-08-31 21:14:55,466 - logic_logger - INF
..Order[None] {Insert - client} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 19998.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 1, CloneFromOrder: None row: 0x1210695e0 session: 0x121069880 ins_upd_dlt: ins - 2024-08-31 21:14:55,468 - logic_logger - INF
..Order[None] {server_defaults: -- skipped: Ready[BOOLEAN (not handled)] } Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 19998.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 1, CloneFromOrder: None row: 0x1210695e0 session: 0x121069880 ins_upd_dlt: ins - 2024-08-31 21:14:55,468 - logic_logger - INF
..Order[None] {Formula OrderDate} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:55.472505, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 10, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 19998.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 1, CloneFromOrder: None row: 0x1210695e0 session: 0x121069880 ins_upd_dlt: ins - 2024-08-31 21:14:55,472 - logic_logger - INF
....Customer[ALFKI] {Update - Adjusting Customer: Balance, UnpaidOrderCount, OrderCount} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 22100.0000000000, CreditLimit: 2300.0000000000, OrderCount: [15-->] 16, UnpaidOrderCount: [10-->] 11, Client_id: 1 row: 0x12106a540 session: 0x121069880 ins_upd_dlt: upd - 2024-08-31 21:14:55,473 - logic_logger - INF
....Customer[ALFKI] {Constraint Failure: balance (22100.00) exceeds credit (2300.00)} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 22100.0000000000, CreditLimit: 2300.0000000000, OrderCount: [15-->] 16, UnpaidOrderCount: [10-->] 11, Client_id: 1 row: 0x12106a540 session: 0x121069880 ins_upd_dlt: upd - 2024-08-31 21:14:55,474 - logic_logger - INF
Scenario: Alter Item Qty to exceed credit
Scenario: Alter Item Qty to exceed credit
Given Customer Account: ALFKI
When Order Detail Quantity altered very high
Then Rejected per Check Credit
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Alter Item Qty to exceed credit
Same constraint as above.
Key Takeaway: Automatic Reuse (design one, solve many)
Rules Used in Scenario: Alter Item Qty to exceed credit
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Constraint Function: None
3. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
4. RowEvent Customer.customer_defaults()
5. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
6. RowEvent Order.order_defaults()
7. Derive Order.AmountTotal as Sum(OrderDetail.Amount Where None)
8. Derive Order.OrderDetailCount as Count(<class 'database.models.OrderDetail'> Where None)
OrderDetail
9. RowEvent OrderDetail.order_detail_defaults()
10. Derive OrderDetail.Amount as Formula (1): as_expression=lambda row: row.UnitPrice * row.Qua [...]
Logic Phase: ROW LOGIC (session=0x1207568a0) (sqlalchemy before_flush) - 2024-08-31 21:14:55,656 - logic_logger - INF
..OrderDetail[1040] {Update - client} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: [15-->] 1110, Discount: 0.25, Amount: 684.0000000000, ShippedDate: None row: 0x121069130 session: 0x1207568a0 ins_upd_dlt: upd - 2024-08-31 21:14:55,657 - logic_logger - INF
..OrderDetail[1040] {Formula Amount} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: [15-->] 1110, Discount: 0.25, Amount: [684.0000000000-->] 50616.0000000000, ShippedDate: None row: 0x121069130 session: 0x1207568a0 ins_upd_dlt: upd - 2024-08-31 21:14:55,658 - logic_logger - INF
..OrderDetail[1040] {Prune Formula: ShippedDate [['Order.ShippedDate']]} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: [15-->] 1110, Discount: 0.25, Amount: [684.0000000000-->] 50616.0000000000, ShippedDate: None row: 0x121069130 session: 0x1207568a0 ins_upd_dlt: upd - 2024-08-31 21:14:55,659 - logic_logger - INF
....Order[10643] {Update - Adjusting Order: AmountTotal} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-09-22, ShippedDate: None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: [1086.00-->] 51018.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1207d7020 session: 0x1207568a0 ins_upd_dlt: upd - 2024-08-31 21:14:55,662 - logic_logger - INF
....Order[10643] {Prune Formula: OrderDate [[]]} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-09-22, ShippedDate: None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: [1086.00-->] 51018.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1207d7020 session: 0x1207568a0 ins_upd_dlt: upd - 2024-08-31 21:14:55,663 - logic_logger - INF
......Customer[ALFKI] {Update - Adjusting Customer: Balance} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 52034.0000000000, CreditLimit: 2300.0000000000, OrderCount: 15, UnpaidOrderCount: 10, Client_id: 1 row: 0x12106acf0 session: 0x1207568a0 ins_upd_dlt: upd - 2024-08-31 21:14:55,665 - logic_logger - INF
......Customer[ALFKI] {Constraint Failure: balance (52034.00) exceeds credit (2300.00)} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 52034.0000000000, CreditLimit: 2300.0000000000, OrderCount: 15, UnpaidOrderCount: 10, Client_id: 1 row: 0x12106acf0 session: 0x1207568a0 ins_upd_dlt: upd - 2024-08-31 21:14:55,666 - logic_logger - INF
Scenario: Alter Required Date - adjust logic pruned
Scenario: Alter Required Date - adjust logic pruned
Given Customer Account: ALFKI
When Order RequiredDate altered (2013-10-13)
Then Balance not adjusted
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Alter Required Date - adjust logic pruned
We set Order.RequiredDate
.
This is a normal update. Nothing depends on the columns altered, so this has no effect on the related Customer, Order Details or Products. Contrast this to the Cascade Update Test and the Custom Service test, where logic chaining affects related rows. Only the commit event fires.
Key Takeaway: rule pruning automatically avoids unnecessary SQL overhead.
Rules Used in Scenario: Alter Required Date - adjust logic pruned
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
3. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
4. RowEvent Order.order_defaults()
5. RowEvent Order.clone_order()
6. RowEvent Order.congratulate_sales_rep()
7. RowEvent Order.do_not_ship_empty_orders()
8. RowEvent Order.send_order_to_shipping()
Logic Phase: ROW LOGIC (session=0x1210402c0) (sqlalchemy before_flush) - 2024-08-31 21:14:55,849 - logic_logger - INF
..Order[10643] {Update - client} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: [2013-09-22-->] 2013-10-13 00:00:00, ShippedDate: None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x12106bb30 session: 0x1210402c0 ins_upd_dlt: upd - 2024-08-31 21:14:55,850 - logic_logger - INF
..Order[10643] {Prune Formula: OrderDate [[]]} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: [2013-09-22-->] 2013-10-13 00:00:00, ShippedDate: None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x12106bb30 session: 0x1210402c0 ins_upd_dlt: upd - 2024-08-31 21:14:55,852 - logic_logger - INF
Logic Phase: COMMIT LOGIC (session=0x1210402c0) - 2024-08-31 21:14:55,854 - logic_logger - INF
..Order[10643] {Commit Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: [2013-09-22-->] 2013-10-13 00:00:00, ShippedDate: None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x12106bb30 session: 0x1210402c0 ins_upd_dlt: upd - 2024-08-31 21:14:55,854 - logic_logger - INF
..Order[10643] {Commit Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: [2013-09-22-->] 2013-10-13 00:00:00, ShippedDate: None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x12106bb30 session: 0x1210402c0 ins_upd_dlt: upd - 2024-08-31 21:14:55,854 - logic_logger - INF
Logic Phase: AFTER_FLUSH LOGIC (session=0x1210402c0) - 2024-08-31 21:14:55,857 - logic_logger - INF
..Order[10643] {AfterFlush Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: [2013-09-22-->] 2013-10-13 00:00:00, ShippedDate: None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x12106bb30 session: 0x1210402c0 ins_upd_dlt: upd - 2024-08-31 21:14:55,857 - logic_logger - INF
Scenario: Set Shipped - adjust logic reuse
Scenario: Set Shipped - adjust logic reuse
Given Customer Account: ALFKI
When Order ShippedDate altered (2013-10-13)
Then Balance reduced 1086
Then Product[46] UnitsInStock adjusted
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Set Shipped - adjust logic reuse
Logic Patterns:
- Chain Down
Logic Design ("Cocktail Napkin Design")
- Formula: OrderDetail.ShippedDate = Order.ShippedDate
We set Order.ShippedDate
.
This cascades to the Order Details, per the derive=models.OrderDetail.ShippedDate
rule.
This chains to adjust the Product.UnitsShipped
and recomputes UnitsInStock
, as above
Key Takeaway: parent references (e.g.,
OrderDetail.ShippedDate
) automate chain-down multi-table transactions.Key Takeaway: Automatic Reuse (design one, solve many)
Rules Used in Scenario: Set Shipped - adjust logic reuse
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
3. RowEvent Customer.customer_defaults()
4. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
5. RowEvent Order.order_defaults()
6. RowEvent Order.do_not_ship_empty_orders()
7. Derive Order.AmountTotal as Sum(OrderDetail.Amount Where None)
8. RowEvent Order.clone_order()
9. RowEvent Order.congratulate_sales_rep()
10. Derive Order.OrderDetailCount as Count(<class 'database.models.OrderDetail'> Where None)
11. RowEvent Order.send_order_to_shipping()
OrderDetail
12. RowEvent OrderDetail.order_detail_defaults()
13. Derive OrderDetail.ShippedDate as Formula (2): row.Order.ShippedDate
Product
14. Derive Product.UnitsShipped as Sum(OrderDetail.Quantity Where <function declare_logic.<locals>.<lambda> at 0x105744d60>)
15. Derive Product.UnitsInStock as Formula (1): <function>
Logic Phase: ROW LOGIC (session=0x121109430) (sqlalchemy before_flush) - 2024-08-31 21:14:56,232 - logic_logger - INF
..Order[10643] {Update - client} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [None-->] 2013-10-13, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210f98b0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,233 - logic_logger - INF
..Order[10643] {Prune Formula: OrderDate [[]]} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [None-->] 2013-10-13, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210f98b0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,234 - logic_logger - INF
....Customer[ALFKI] {Update - Adjusting Customer: Balance, UnpaidOrderCount} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 1016.0000000000, CreditLimit: 2300.0000000000, OrderCount: 15, UnpaidOrderCount: [10-->] 9, Client_id: 1 row: 0x12110a3c0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,236 - logic_logger - INF
....OrderDetail[1040] {Update - Cascading Order.ShippedDate (,...)} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: 15, Discount: 0.25, Amount: 684.0000000000, ShippedDate: None row: 0x1211094c0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,241 - logic_logger - INF
....OrderDetail[1040] {Prune Formula: Amount [['UnitPrice', 'Quantity']]} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: 15, Discount: 0.25, Amount: 684.0000000000, ShippedDate: None row: 0x1211094c0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,241 - logic_logger - INF
....OrderDetail[1040] {Formula ShippedDate} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: 15, Discount: 0.25, Amount: 684.0000000000, ShippedDate: [None-->] 2013-10-13 row: 0x1211094c0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,242 - logic_logger - INF
......Product[28] {Update - Adjusting Product: UnitsShipped} Id: 28, ProductName: Rössle Sauerkraut, SupplierId: 12, CategoryId: 7, QuantityPerUnit: 25 - 825 g cans, UnitPrice: 45.6000000000, UnitsInStock: 26, UnitsOnOrder: 0, ReorderLevel: 0, Discontinued: 1, UnitsShipped: [0-->] -15 row: 0x12110b1a0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,244 - logic_logger - INF
......Product[28] {Formula UnitsInStock} Id: 28, ProductName: Rössle Sauerkraut, SupplierId: 12, CategoryId: 7, QuantityPerUnit: 25 - 825 g cans, UnitPrice: 45.6000000000, UnitsInStock: [26-->] 41, UnitsOnOrder: 0, ReorderLevel: 0, Discontinued: 1, UnitsShipped: [0-->] -15 row: 0x12110b1a0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,244 - logic_logger - INF
....OrderDetail[1041] {Update - Cascading Order.ShippedDate (,...)} Id: 1041, OrderId: 10643, ProductId: 39, UnitPrice: 18.0000000000, Quantity: 21, Discount: 0.25, Amount: 378.0000000000, ShippedDate: None row: 0x12110a330 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,246 - logic_logger - INF
....OrderDetail[1041] {Prune Formula: Amount [['UnitPrice', 'Quantity']]} Id: 1041, OrderId: 10643, ProductId: 39, UnitPrice: 18.0000000000, Quantity: 21, Discount: 0.25, Amount: 378.0000000000, ShippedDate: None row: 0x12110a330 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,246 - logic_logger - INF
....OrderDetail[1041] {Formula ShippedDate} Id: 1041, OrderId: 10643, ProductId: 39, UnitPrice: 18.0000000000, Quantity: 21, Discount: 0.25, Amount: 378.0000000000, ShippedDate: [None-->] 2013-10-13 row: 0x12110a330 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,247 - logic_logger - INF
......Product[39] {Update - Adjusting Product: UnitsShipped} Id: 39, ProductName: Chartreuse verte, SupplierId: 18, CategoryId: 1, QuantityPerUnit: 750 cc per bottle, UnitPrice: 18.0000000000, UnitsInStock: 69, UnitsOnOrder: 0, ReorderLevel: 5, Discontinued: 0, UnitsShipped: [0-->] -21 row: 0x121108fb0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,248 - logic_logger - INF
......Product[39] {Formula UnitsInStock} Id: 39, ProductName: Chartreuse verte, SupplierId: 18, CategoryId: 1, QuantityPerUnit: 750 cc per bottle, UnitPrice: 18.0000000000, UnitsInStock: [69-->] 90, UnitsOnOrder: 0, ReorderLevel: 5, Discontinued: 0, UnitsShipped: [0-->] -21 row: 0x121108fb0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,249 - logic_logger - INF
....OrderDetail[1042] {Update - Cascading Order.ShippedDate (,...)} Id: 1042, OrderId: 10643, ProductId: 46, UnitPrice: 12.0000000000, Quantity: 2, Discount: 0.25, Amount: 24.0000000000, ShippedDate: None row: 0x121109fd0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,250 - logic_logger - INF
....OrderDetail[1042] {Prune Formula: Amount [['UnitPrice', 'Quantity']]} Id: 1042, OrderId: 10643, ProductId: 46, UnitPrice: 12.0000000000, Quantity: 2, Discount: 0.25, Amount: 24.0000000000, ShippedDate: None row: 0x121109fd0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,251 - logic_logger - INF
....OrderDetail[1042] {Formula ShippedDate} Id: 1042, OrderId: 10643, ProductId: 46, UnitPrice: 12.0000000000, Quantity: 2, Discount: 0.25, Amount: 24.0000000000, ShippedDate: [None-->] 2013-10-13 row: 0x121109fd0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,251 - logic_logger - INF
......Product[46] {Update - Adjusting Product: UnitsShipped} Id: 46, ProductName: Spegesild, SupplierId: 21, CategoryId: 8, QuantityPerUnit: 4 - 450 g glasses, UnitPrice: 12.0000000000, UnitsInStock: 95, UnitsOnOrder: 0, ReorderLevel: 0, Discontinued: 0, UnitsShipped: [0-->] -2 row: 0x12110ad20 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,253 - logic_logger - INF
......Product[46] {Formula UnitsInStock} Id: 46, ProductName: Spegesild, SupplierId: 21, CategoryId: 8, QuantityPerUnit: 4 - 450 g glasses, UnitPrice: 12.0000000000, UnitsInStock: [95-->] 97, UnitsOnOrder: 0, ReorderLevel: 0, Discontinued: 0, UnitsShipped: [0-->] -2 row: 0x12110ad20 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,253 - logic_logger - INF
Logic Phase: COMMIT LOGIC (session=0x121109430) - 2024-08-31 21:14:56,254 - logic_logger - INF
..Order[10643] {Commit Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [None-->] 2013-10-13, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210f98b0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,256 - logic_logger - INF
..Order[10643] {Commit Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [None-->] 2013-10-13, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210f98b0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,256 - logic_logger - INF
Logic Phase: AFTER_FLUSH LOGIC (session=0x121109430) - 2024-08-31 21:14:56,262 - logic_logger - INF
..Order[10643] {AfterFlush Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [None-->] 2013-10-13, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210f98b0 session: 0x121109430 ins_upd_dlt: upd - 2024-08-31 21:14:56,263 - logic_logger - INF
Scenario: Reset Shipped - adjust logic reuse
Scenario: Reset Shipped - adjust logic reuse
Given Shipped Order
When Order ShippedDate set to None
Then Logic adjusts Balance by -1086
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Reset Shipped - adjust logic reuse
Same logic as above.
Key Takeaway: Automatic Reuse (design one, solve many)
Rules Used in Scenario: Reset Shipped - adjust logic reuse
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
3. RowEvent Customer.customer_defaults()
4. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
5. RowEvent Order.order_defaults()
6. RowEvent Order.do_not_ship_empty_orders()
7. Derive Order.AmountTotal as Sum(OrderDetail.Amount Where None)
8. RowEvent Order.clone_order()
9. RowEvent Order.congratulate_sales_rep()
10. Derive Order.OrderDetailCount as Count(<class 'database.models.OrderDetail'> Where None)
11. RowEvent Order.send_order_to_shipping()
OrderDetail
12. RowEvent OrderDetail.order_detail_defaults()
13. Derive OrderDetail.ShippedDate as Formula (2): row.Order.ShippedDate
Product
14. Derive Product.UnitsShipped as Sum(OrderDetail.Quantity Where <function declare_logic.<locals>.<lambda> at 0x105744d60>)
15. Derive Product.UnitsInStock as Formula (1): <function>
Logic Phase: ROW LOGIC (session=0x121042900) (sqlalchemy before_flush) - 2024-08-31 21:14:56,658 - logic_logger - INF
..Order[10643] {Update - client} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [2013-10-13-->] None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210fa4e0 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,659 - logic_logger - INF
..Order[10643] {Prune Formula: OrderDate [[]]} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [2013-10-13-->] None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210fa4e0 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,660 - logic_logger - INF
....Customer[ALFKI] {Update - Adjusting Customer: Balance, UnpaidOrderCount} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [1016.0000000000-->] 2102.0000000000, CreditLimit: 2300.0000000000, OrderCount: 15, UnpaidOrderCount: [9-->] 10, Client_id: 1 row: 0x12106a570 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,662 - logic_logger - INF
....OrderDetail[1040] {Update - Cascading Order.ShippedDate (,...)} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: 15, Discount: 0.25, Amount: 684.0000000000, ShippedDate: 2013-10-13 row: 0x12106ac60 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,666 - logic_logger - INF
....OrderDetail[1040] {Prune Formula: Amount [['UnitPrice', 'Quantity']]} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: 15, Discount: 0.25, Amount: 684.0000000000, ShippedDate: 2013-10-13 row: 0x12106ac60 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,667 - logic_logger - INF
....OrderDetail[1040] {Formula ShippedDate} Id: 1040, OrderId: 10643, ProductId: 28, UnitPrice: 45.6000000000, Quantity: 15, Discount: 0.25, Amount: 684.0000000000, ShippedDate: [2013-10-13-->] None row: 0x12106ac60 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,667 - logic_logger - INF
......Product[28] {Update - Adjusting Product: UnitsShipped} Id: 28, ProductName: Rössle Sauerkraut, SupplierId: 12, CategoryId: 7, QuantityPerUnit: 25 - 825 g cans, UnitPrice: 45.6000000000, UnitsInStock: 41, UnitsOnOrder: 0, ReorderLevel: 0, Discontinued: 1, UnitsShipped: [-15-->] 0 row: 0x1211097c0 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,669 - logic_logger - INF
......Product[28] {Formula UnitsInStock} Id: 28, ProductName: Rössle Sauerkraut, SupplierId: 12, CategoryId: 7, QuantityPerUnit: 25 - 825 g cans, UnitPrice: 45.6000000000, UnitsInStock: [41-->] 26, UnitsOnOrder: 0, ReorderLevel: 0, Discontinued: 1, UnitsShipped: [-15-->] 0 row: 0x1211097c0 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,670 - logic_logger - INF
....OrderDetail[1041] {Update - Cascading Order.ShippedDate (,...)} Id: 1041, OrderId: 10643, ProductId: 39, UnitPrice: 18.0000000000, Quantity: 21, Discount: 0.25, Amount: 378.0000000000, ShippedDate: 2013-10-13 row: 0x12106a960 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,671 - logic_logger - INF
....OrderDetail[1041] {Prune Formula: Amount [['UnitPrice', 'Quantity']]} Id: 1041, OrderId: 10643, ProductId: 39, UnitPrice: 18.0000000000, Quantity: 21, Discount: 0.25, Amount: 378.0000000000, ShippedDate: 2013-10-13 row: 0x12106a960 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,672 - logic_logger - INF
....OrderDetail[1041] {Formula ShippedDate} Id: 1041, OrderId: 10643, ProductId: 39, UnitPrice: 18.0000000000, Quantity: 21, Discount: 0.25, Amount: 378.0000000000, ShippedDate: [2013-10-13-->] None row: 0x12106a960 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,672 - logic_logger - INF
......Product[39] {Update - Adjusting Product: UnitsShipped} Id: 39, ProductName: Chartreuse verte, SupplierId: 18, CategoryId: 1, QuantityPerUnit: 750 cc per bottle, UnitPrice: 18.0000000000, UnitsInStock: 90, UnitsOnOrder: 0, ReorderLevel: 5, Discontinued: 0, UnitsShipped: [-21-->] 0 row: 0x12110aa50 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,673 - logic_logger - INF
......Product[39] {Formula UnitsInStock} Id: 39, ProductName: Chartreuse verte, SupplierId: 18, CategoryId: 1, QuantityPerUnit: 750 cc per bottle, UnitPrice: 18.0000000000, UnitsInStock: [90-->] 69, UnitsOnOrder: 0, ReorderLevel: 5, Discontinued: 0, UnitsShipped: [-21-->] 0 row: 0x12110aa50 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,674 - logic_logger - INF
....OrderDetail[1042] {Update - Cascading Order.ShippedDate (,...)} Id: 1042, OrderId: 10643, ProductId: 46, UnitPrice: 12.0000000000, Quantity: 2, Discount: 0.25, Amount: 24.0000000000, ShippedDate: 2013-10-13 row: 0x12106ba40 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,675 - logic_logger - INF
....OrderDetail[1042] {Prune Formula: Amount [['UnitPrice', 'Quantity']]} Id: 1042, OrderId: 10643, ProductId: 46, UnitPrice: 12.0000000000, Quantity: 2, Discount: 0.25, Amount: 24.0000000000, ShippedDate: 2013-10-13 row: 0x12106ba40 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,675 - logic_logger - INF
....OrderDetail[1042] {Formula ShippedDate} Id: 1042, OrderId: 10643, ProductId: 46, UnitPrice: 12.0000000000, Quantity: 2, Discount: 0.25, Amount: 24.0000000000, ShippedDate: [2013-10-13-->] None row: 0x12106ba40 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,676 - logic_logger - INF
......Product[46] {Update - Adjusting Product: UnitsShipped} Id: 46, ProductName: Spegesild, SupplierId: 21, CategoryId: 8, QuantityPerUnit: 4 - 450 g glasses, UnitPrice: 12.0000000000, UnitsInStock: 97, UnitsOnOrder: 0, ReorderLevel: 0, Discontinued: 0, UnitsShipped: [-2-->] 0 row: 0x12110ae70 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,677 - logic_logger - INF
......Product[46] {Formula UnitsInStock} Id: 46, ProductName: Spegesild, SupplierId: 21, CategoryId: 8, QuantityPerUnit: 4 - 450 g glasses, UnitPrice: 12.0000000000, UnitsInStock: [97-->] 95, UnitsOnOrder: 0, ReorderLevel: 0, Discontinued: 0, UnitsShipped: [-2-->] 0 row: 0x12110ae70 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,678 - logic_logger - INF
Logic Phase: COMMIT LOGIC (session=0x121042900) - 2024-08-31 21:14:56,679 - logic_logger - INF
..Order[10643] {Commit Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [2013-10-13-->] None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210fa4e0 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,680 - logic_logger - INF
..Order[10643] {Commit Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [2013-10-13-->] None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210fa4e0 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,681 - logic_logger - INF
Logic Phase: AFTER_FLUSH LOGIC (session=0x121042900) - 2024-08-31 21:14:56,685 - logic_logger - INF
..Order[10643] {AfterFlush Event} Id: 10643, CustomerId: ALFKI, EmployeeId: 6, OrderDate: 2013-08-25, RequiredDate: 2013-10-13, ShippedDate: [2013-10-13-->] None, ShipVia: 1, Freight: 29.4600000000, ShipName: Alfreds Futterkiste, ShipAddress: Obere Str. 57, ShipCity: Berlin, ShipRegion: Western Europe, ShipZip: 12209, ShipCountry: Germany, AmountTotal: 1086.00, Country: None, City: None, Ready: True, OrderDetailCount: 3, CloneFromOrder: None row: 0x1210fa4e0 session: 0x121042900 ins_upd_dlt: upd - 2024-08-31 21:14:56,687 - logic_logger - INF
Scenario: Clone Existing Order
Scenario: Clone Existing Order
Given Shipped Order
When Cloning Existing Order
Then Logic Copies ClonedFrom OrderDetails
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Clone Existing Order
We create an order, setting CloneFromOrder.
This copies the CloneFromOrder OrderDetails to our new Order.
The copy operation is automated using logic_row.copy_children()
:
-
place_order.feature
defines the test -
place_order.py
implements the test. It uses the API to Post an Order, settingCloneFromOrder
to trigger the copy logic -
declare_logic.py
implements the logic, by invokinglogic_row.copy_children()
.which
defines which children to copy, here justOrderDetailList
CopyChildren
For more information, see here
Useful in row event handlers to copy multiple children types to self from copy_from children.
child-spec := < ‘child-list-name’ | < ‘child-list-name = parent-list-name’ >
child-list-spec := [child-spec | (child-spec, child-list-spec)]
Eg. RowEvent on Order
which = ["OrderDetailList"]
logic_row.copy_children(copy_from=row.parent, which_children=which)
Eg, test/copy_children:
child_list_spec = [
("MileStoneList",
["DeliverableList"] # for each Milestone, get the Deliverables
),
"StaffList"
]
Key Takeaway: copy_children provides a deep-copy service.
Rules Used in Scenario: Clone Existing Order
Customer
1. Derive Customer.UnpaidOrderCount as Count(<class 'database.models.Order'> Where <function declare_logic.<locals>.<lambda> at 0x105744ea0>)
2. Constraint Function: None
3. Derive Customer.Balance as Sum(Order.AmountTotal Where <function declare_logic.<locals>.<lambda> at 0x105623740>)
4. RowEvent Customer.customer_defaults()
5. Derive Customer.OrderCount as Count(<class 'database.models.Order'> Where None)
Order
6. RowEvent Order.order_defaults()
7. Derive Order.OrderDate as Formula (1): as_expression=lambda row: datetime.datetime.now())
8. RowEvent Order.clone_order()
9. Derive Order.AmountTotal as Sum(OrderDetail.Amount Where None)
10. Derive Order.OrderDetailCount as Count(<class 'database.models.OrderDetail'> Where None)
OrderDetail
11. RowEvent OrderDetail.order_detail_defaults()
12. Derive OrderDetail.UnitPrice as Copy(Product.UnitPrice)
13. Derive OrderDetail.Amount as Formula (1): as_expression=lambda row: row.UnitPrice * row.Qua [...]
14. Derive OrderDetail.ShippedDate as Formula (2): row.Order.ShippedDate
Logic Phase: ROW LOGIC (session=0x121146a50) (sqlalchemy before_flush) - 2024-08-31 21:14:57,021 - logic_logger - INF
..Order[None] {Insert - client} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: None, Country: None, City: None, Ready: True, OrderDetailCount: None, CloneFromOrder: 10643 row: 0x121146ed0 session: 0x121146a50 ins_upd_dlt: ins - 2024-08-31 21:14:57,022 - logic_logger - INF
..Order[None] {server_defaults: OrderDetailCount -- skipped: Ready[BOOLEAN (not handled)] } Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: None, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: None, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: 10643 row: 0x121146ed0 session: 0x121146a50 ins_upd_dlt: ins - 2024-08-31 21:14:57,022 - logic_logger - INF
..Order[None] {Formula OrderDate} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:57.029015, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 0, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: 10643 row: 0x121146ed0 session: 0x121146a50 ins_upd_dlt: ins - 2024-08-31 21:14:57,029 - logic_logger - INF
....Customer[ALFKI] {Update - Adjusting Customer: UnpaidOrderCount, OrderCount} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: 2102.0000000000, CreditLimit: 2300.0000000000, OrderCount: [15-->] 16, UnpaidOrderCount: [10-->] 11, Client_id: 1 row: 0x1210fa6f0 session: 0x121146a50 ins_upd_dlt: upd - 2024-08-31 21:14:57,030 - logic_logger - INF
..Order[None] {Begin copy_children} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:57.029015, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: 0, Country: None, City: None, Ready: True, OrderDetailCount: 0, CloneFromOrder: 10643 row: 0x121146ed0 session: 0x121146a50 ins_upd_dlt: ins - 2024-08-31 21:14:57,033 - logic_logger - INF
....OrderDetail[None] {Insert - Copy Children OrderDetailList} Id: None, OrderId: None, ProductId: [None-->] 28, UnitPrice: None, Quantity: [None-->] 15, Discount: [None-->] 0.25, Amount: None, ShippedDate: None row: 0x12110af90 session: 0x121146a50 ins_upd_dlt: ins - 2024-08-31 21:14:57,035 - logic_logger - INF
....OrderDetail[None] {copy_rules for role: Product - UnitPrice} Id: None, OrderId: None, ProductId: [None-->] 28, UnitPrice: [None-->] 45.6000000000, Quantity: [None-->] 15, Discount: [None-->] 0.25, Amount: None, ShippedDate: None row: 0x12110af90 session: 0x121146a50 ins_upd_dlt: ins - 2024-08-31 21:14:57,037 - logic_logger - INF
....OrderDetail[None] {Formula Amount} Id: None, OrderId: None, ProductId: [None-->] 28, UnitPrice: [None-->] 45.6000000000, Quantity: [None-->] 15, Discount: [None-->] 0.25, Amount: [None-->] 684.0000000000, ShippedDate: None row: 0x12110af90 session: 0x121146a50 ins_upd_dlt: ins - 2024-08-31 21:14:57,037 - logic_logger - INF
......Order[None] {Update - Adjusting Order: AmountTotal, OrderDetailCount} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:57.029015, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: [0-->] 684.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: [0-->] 1, CloneFromOrder: 10643 row: 0x121146ed0 session: 0x121146a50 ins_upd_dlt: upd - 2024-08-31 21:14:57,038 - logic_logger - INF
......Order[None] {Prune Formula: OrderDate [[]]} Id: None, CustomerId: ALFKI, EmployeeId: 1, OrderDate: 2024-08-31 21:14:57.029015, RequiredDate: None, ShippedDate: None, ShipVia: None, Freight: 11, ShipName: None, ShipAddress: None, ShipCity: None, ShipRegion: None, ShipZip: None, ShipCountry: None, AmountTotal: [0-->] 684.0000000000, Country: None, City: None, Ready: True, OrderDetailCount: [0-->] 1, CloneFromOrder: 10643 row: 0x121146ed0 session: 0x121146a50 ins_upd_dlt: upd - 2024-08-31 21:14:57,039 - logic_logger - INF
........Customer[ALFKI] {Update - Adjusting Customer: Balance} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 2786.0000000000, CreditLimit: 2300.0000000000, OrderCount: 16, UnpaidOrderCount: 11, Client_id: 1 row: 0x1210fa6f0 session: 0x121146a50 ins_upd_dlt: upd - 2024-08-31 21:14:57,040 - logic_logger - INF
........Customer[ALFKI] {Constraint Failure: balance (2786.00) exceeds credit (2300.00)} Id: ALFKI, CompanyName: Alfreds Futterkiste, ContactName: Maria Anders, ContactTitle: Sales Representative, Address: Obere Str. 57A, City: Berlin, Region: Western Europe, PostalCode: 12209, Country: Germany, Phone: 030-0074321, Fax: 030-0076545, Balance: [2102.0000000000-->] 2786.0000000000, CreditLimit: 2300.0000000000, OrderCount: 16, UnpaidOrderCount: 11, Client_id: 1 row: 0x1210fa6f0 session: 0x121146a50 ins_upd_dlt: upd - 2024-08-31 21:14:57,041 - logic_logger - INF
Feature: Salary Change
Scenario: Audit Salary Change
Scenario: Audit Salary Change
Given Employee 5 (Buchanan) - Salary 95k
When Patch Salary to 200k
Then Salary_audit row created
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Audit Salary Change
Logic Patterns:
- Auditing
Logic Design ("Cocktail Napkin Design")
- copy_row(copy_to=models.EmployeeAudit...)
Observe the logic log to see that it creates audit rows:
- Discouraged: you can implement auditing with events. But auditing is a common pattern, and this can lead to repetitive, tedious code
- Preferred: approaches use extensible rules.
Generic event handlers can also reduce redundant code, illustrated in the time/date stamping handle_all
logic.
This is due to the copy_row
rule. Contrast this to the tedious audit_by_event
alternative:
Key Takeaway: use extensible own rule types to automate pattern you identify; events can result in tedious amounts of code.
Rules Used in Scenario: Audit Salary Change
Logic Log in Scenario: Audit Salary ChangeLogic Phase: ROW LOGIC (session=0x10dfbbdd0) (sqlalchemy before_flush) - 2024-07-12 14:57:09,599 - logic_logger - INF
..Employee[5] {Update - client} Id: 5, LastName: Buchanan, FirstName: Steven, Title: Sales Manager, TitleOfCourtesy: Mr., BirthDate: 1987-03-04, HireDate: 2025-10-17, Address: 14 Garrett Hill, City: London, Region: British Isles, PostalCode: SW1 8JR, Country: UK, HomePhone: (71) 555-4848, Extension: 3453, Notes: Steven Buchanan graduated from St. Andrews University, Scotland, with a BSC degree in 1976. Upon joining the company as a sales representative in 1992, he spent 6 months in an orientation program at the Seattle office and then returned to his permanent post in London. He was promoted to sales manager in March 1993. Mr. Buchanan has completed the courses 'Successful Telemarketing' and 'International Sales Management.' He is fluent in French., ReportsTo: 2, PhotoPath: http://localhost:5656/ui/images/Employee/buchanan.jpg, EmployeeType: Commissioned, Salary: [95000.0000000000-->] 200000, WorksForDepartmentId: 3, OnLoanDepartmentId: None, UnionId: None, Dues: None row: 0x10dfbb710 session: 0x10dfbbdd0 ins_upd_dlt: upd - 2024-07-12 14:57:09,600 - logic_logger - INF
..Employee[5] {BEGIN Copy to: EmployeeAudit} Id: 5, LastName: Buchanan, FirstName: Steven, Title: Sales Manager, TitleOfCourtesy: Mr., BirthDate: 1987-03-04, HireDate: 2025-10-17, Address: 14 Garrett Hill, City: London, Region: British Isles, PostalCode: SW1 8JR, Country: UK, HomePhone: (71) 555-4848, Extension: 3453, Notes: Steven Buchanan graduated from St. Andrews University, Scotland, with a BSC degree in 1976. Upon joining the company as a sales representative in 1992, he spent 6 months in an orientation program at the Seattle office and then returned to his permanent post in London. He was promoted to sales manager in March 1993. Mr. Buchanan has completed the courses 'Successful Telemarketing' and 'International Sales Management.' He is fluent in French., ReportsTo: 2, PhotoPath: http://localhost:5656/ui/images/Employee/buchanan.jpg, EmployeeType: Commissioned, Salary: [95000.0000000000-->] 200000, WorksForDepartmentId: 3, OnLoanDepartmentId: None, UnionId: None, Dues: None row: 0x10dfbb710 session: 0x10dfbbdd0 ins_upd_dlt: upd - 2024-07-12 14:57:09,604 - logic_logger - INF
....EmployeeAudit[None] {Insert - Copy EmployeeAudit} Id: None, Title: Sales Manager, Salary: 200000, LastName: Buchanan, FirstName: Steven, EmployeeId: None, CreatedOn: None, UpdatedOn: None, CreatedBy: None, UpdatedBy: None row: 0x10dfba1b0 session: 0x10dfbbdd0 ins_upd_dlt: ins - 2024-07-12 14:57:09,605 - logic_logger - INF
....EmployeeAudit[None] {early_row_event_all_classes - handle_all did stamping} Id: None, Title: Sales Manager, Salary: 200000, LastName: Buchanan, FirstName: Steven, EmployeeId: None, CreatedOn: 2024-07-12 14:57:09.606065, UpdatedOn: None, CreatedBy: aneu, UpdatedBy: None row: 0x10dfba1b0 session: 0x10dfbbdd0 ins_upd_dlt: ins - 2024-07-12 14:57:09,606 - logic_logger - INF
Logic Phase: COMMIT LOGIC (session=0x10dfbbdd0) - 2024-07-12 14:57:09,607 - logic_logger - INF
Logic Phase: AFTER_FLUSH LOGIC (session=0x10dfbbdd0) - 2024-07-12 14:57:09,612 - logic_logger - INF
Scenario: Manage ProperSalary
Scenario: Manage ProperSalary
Given Employee 5 (Buchanan) - Salary 95k
When Retrieve Employee Row
Then Verify Contains ProperSalary
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Manage ProperSalary
Observe the use of old_row
Key Takeaway: State Transition Logic enabled per
old_row
Rules Used in Scenario: Manage ProperSalary
Logic Log in Scenario: Manage ProperSalary
Scenario: Raise Must be Meaningful
Scenario: Raise Must be Meaningful
Given Employee 5 (Buchanan) - Salary 95k
When Patch Salary to 96k
Then Reject - Raise too small
Tests - and their logic - are transparent.. click to see Logic
Logic Doc for scenario: Raise Must be Meaningful
Logic Patterns:
- State Transition Logic
Logic Design ("Cocktail Napkin Design")
- Rule.constraint(validate=Employee, calling=raise_over_20_percent...)
Observe the use of old_row
Key Takeaway: State Transition Logic enabled per
old_row
Rules Used in Scenario: Raise Must be Meaningful
Employee
1. Constraint Function: <function declare_logic.<locals>.raise_over_20_percent at 0x10b959940>
Logic Phase: ROW LOGIC (session=0x10dfd4b60) (sqlalchemy before_flush) - 2024-07-12 14:57:09,907 - logic_logger - INF
..Employee[5] {Update - client} Id: 5, LastName: Buchanan, FirstName: Steven, Title: Sales Manager, TitleOfCourtesy: Mr., BirthDate: 1987-03-04, HireDate: 2025-10-17, Address: 14 Garrett Hill, City: London, Region: British Isles, PostalCode: SW1 8JR, Country: UK, HomePhone: (71) 555-4848, Extension: 3453, Notes: Steven Buchanan graduated from St. Andrews University, Scotland, with a BSC degree in 1976. Upon joining the company as a sales representative in 1992, he spent 6 months in an orientation program at the Seattle office and then returned to his permanent post in London. He was promoted to sales manager in March 1993. Mr. Buchanan has completed the courses 'Successful Telemarketing' and 'International Sales Management.' He is fluent in French., ReportsTo: 2, PhotoPath: http://localhost:5656/ui/images/Employee/buchanan.jpg, EmployeeType: Commissioned, Salary: [95000.0000000000-->] 96000, WorksForDepartmentId: 3, OnLoanDepartmentId: None, UnionId: None, Dues: None row: 0x10dd75790 session: 0x10dfd4b60 ins_upd_dlt: upd - 2024-07-12 14:57:09,908 - logic_logger - INF
..Employee[5] {Constraint Failure: Buchanan needs a more meaningful raise} Id: 5, LastName: Buchanan, FirstName: Steven, Title: Sales Manager, TitleOfCourtesy: Mr., BirthDate: 1987-03-04, HireDate: 2025-10-17, Address: 14 Garrett Hill, City: London, Region: British Isles, PostalCode: SW1 8JR, Country: UK, HomePhone: (71) 555-4848, Extension: 3453, Notes: Steven Buchanan graduated from St. Andrews University, Scotland, with a BSC degree in 1976. Upon joining the company as a sales representative in 1992, he spent 6 months in an orientation program at the Seattle office and then returned to his permanent post in London. He was promoted to sales manager in March 1993. Mr. Buchanan has completed the courses 'Successful Telemarketing' and 'International Sales Management.' He is fluent in French., ReportsTo: 2, PhotoPath: http://localhost:5656/ui/images/Employee/buchanan.jpg, EmployeeType: Commissioned, Salary: [95000.0000000000-->] 96000, WorksForDepartmentId: 3, OnLoanDepartmentId: None, UnionId: None, Dues: None row: 0x10dd75790 session: 0x10dfd4b60 ins_upd_dlt: upd - 2024-07-12 14:57:09,909 - logic_logger - INF
Feature: Tests Successful
Scenario: Run Tests
Scenario: Run Tests
Given Database and Set of Tests
When Run Configuration: Behave Tests
Then No Errors
Completed at July 12, 2024 14:57:0