Add _inherit (ExtendModel) + Inverse fields + sale extends partner

ORM:
- ExtendModel(name) retrieves existing model for extension (mirrors
  Python _inherit without _name). Panics on missing model.
- RegisterInverse(fieldName, fn) convenience for computed write-back
- Inverse field handling in Write(): caches new value, calls inverse
  method so computed fields can be written back

Sale module:
- Extends res.partner with sale_order_ids (O2M) and sale_order_count
  (computed) via ExtendModel — demonstrates real _inherit pattern

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc
2026-04-03 13:23:40 +02:00
parent 912abc4b97
commit 2e5a550069
3 changed files with 93 additions and 0 deletions

View File

@@ -393,6 +393,31 @@ func (rs *Recordset) Write(vals Values) error {
}
}
// Handle inverse fields (write-back for computed fields).
// Mirrors: odoo/orm/fields.py Field.inverse
//
// When a computed field with an Inverse method is written to, the inverse
// method is called so it can propagate the value to the underlying fields.
// In Python Odoo, the inverse method reads self.field_name from cache (which
// has the new value). We replicate this by caching the new value before calling.
for fieldName := range vals {
f := m.GetField(fieldName)
if f == nil || f.Inverse == "" {
continue
}
inverseMethod, ok := m.Methods[f.Inverse]
if !ok {
continue
}
// Cache the new value so the inverse method can read it via Get()
for _, id := range rs.ids {
rs.env.cache.Set(m.Name(), id, fieldName, vals[fieldName])
}
if _, err := inverseMethod(rs); err != nil {
return fmt.Errorf("orm: inverse %s.%s: %w", m.name, f.Inverse, err)
}
}
// Trigger recompute for stored computed fields that depend on written fields
if err := TriggerRecompute(rs, vals); err != nil {
return err