Lesson 10 — Python triggers

Continue in the SAME Python console after lesson 9 and run console.py in order.
Starting fresh? Open Python in this extracted folder and run:
    exec(open("start-here.py").read())
    exec(open("console.py").read())
    exec(open("checks.py").read())
Requires Python 3.11+ and asqueel[migration]==0.6.0.

The bootstrap rebuilds the course database and replays lesson 9, which restores
all original rows. Optional lesson 8 is not required. Four CSV inputs are included.
No model or database files are created.

These are Python hooks in Asqueel, not SQLite CREATE TRIGGER objects.
The example expects quantity and unit_price in records passed to normal writes.
Table update does not fetch missing fields or the old record: we read physical
fields, copy them and pass old_record explicitly. Query update prepares records
and old_record itself, then runs the write cycle for each selected row.
After hooks run after the SQL statement, before commit.
Direct sql_insert (including CSV import) and query.sql_update skip these hooks.
The stored total is separate from the calculated_total formula on reads.
An exception in a write cycle requires rollback before further work.
Rollback undoes all pending database writes, but not Python side effects.
All example changes are rolled back; the original 36 invoice rows remain.
