Odoo 19 renamed fields and made models read-only in ways that broke my import scripts and my 700k-record stock load. None of it is in the release notes front and center. You hit it at runtime when the write fails or the API returns the wrong thing. This is the list, with the workarounds that let the job finish.
Which Odoo 19 fields changed and broke automation?
Four changes cost me real debugging time: uom_po_id is gone, qty_done is now quantity, inventory_quantity is not RPC-writable, and stock.move is read-only so you write through stock.move.line. Each one surfaces as a silent gap or a hard failure in a script that worked on an earlier version. Here is what each does and how to route around it.
What happened to uom_po_id in Odoo 19?
The uom_po_id field on purchase order lines is removed in Odoo 19. Older imports that set a separate unit of measure on the PO line fail with a missing-column error or an ignored key. The replacement path is the variant. I create variants with create_variant='dynamic', so the unit of measure lives on the product variant, not on the PO line.
That is the pattern for most of these quirks. Odoo 19 moved authority to the product variant plus attribute combination. Write the variant and the rest follows. Do not chase the removed field.
Is qty_done stored in Odoo 19?
No. In Odoo 19 the recorded production quantity is quantity, not qty_done. I read qty_done and got nothing back, because the value is not stored there anymore. Switch reads to quantity and the numbers come through.
This is the trap with renamed fields. Your code does not error. It returns empty. A load that depends on the returned quantity then writes zeros into the ledger, and you only notice at reconcile time.
Can you write inventory_quantity over the API?
No. inventory_quantity is not RPC-writable in Odoo 19. A direct write bounces. The working route is to post a stock move through stock.move.line, which changes the on-hand quantity as a real accounting event instead of a direct field poke. Zero-and-apply beats direct set: zero the quantity, then apply the new count through the move.
Why is stock.move read-only in Odoo 19?
stock.move itself is not writable. You create stock.move.line records instead. The move line carries the quantity, the product, the source and destination locations. The framework folds those lines into the stock move and updates stock balances from them.
If your transfer script tries to write the move header directly, it fails. Create the lines. That is the supported entry point, and it is also what fires the correct postings on both the sending and receiving side.
How do these quirks affect a real stock load?
My sign-off load was 569 rows against 700k stock records across 7 branches plus Westport. The field changes above were the difference between clean and silent-corruption. The renamed quantity field would have written wrong stock. The read-only inventory_quantity would have rejected every update. The stock.move write would have thrown per move.
Routing every write through stock.move.line and reading quantity instead of qty_done turned a wall of failures into a load that verified clean. The fixes are small. Finding them is the cost.
Where do these quirks bite hardest?
SaaS and CE. I run Odoo 19 Enterprise SaaS daily and Odoo 19 CE on my own VPS for the client databases. The quirks are not identical between them. CE is where the write-level behavior shows up plainly, because you own the database and the API full depth. SaaS restricts more of the record rule layer. If you test on one and ship on the other, pin these field behaviors on both before you write the loop.
What is the honest takeaway for a migration script?
Expect renamed, removed, and read-only pieces in every major release, and treat the write path, not the read, as the contract. A field that reads does not mean it writes. A field you wrote in version 17 may not exist in 19. Build the import against the live API on the target version, not against the older docs.
I keep this list because it cost me a full day across two loads. Anyone else on a produce distribution or any stock-heavy Odoo 19 migration hits the same four: no uom_po_id, quantity not qty_done, inventory_quantity not writable, and stock.move.line as the only write path. This is the reference I wish I had on day one. For the rest of the migration mechanics, this series at https://ai-implmnt.com/blog/ has the working details. Odoo 19 renamed qty_done to quantity, removed uom_po_id, and made stock.move and inventory_quantity read-only, so write through stock.move.line and read quantity instead of chasing the old fields.