Custom modules are off the table on Odoo 19 SaaS. No Python. No XML views. Studio only. I run 19 SaaS daily and 19 CE on a VPS for the same client, so I feel the constraint both ways. Here is what it taught me about design.

The client is a fresh produce distributor. 7 branches plus a Westport warehouse. We migrated 700k stock records and an 18-category product catalog from a legacy ERP. Every modeling decision had to survive Studio-only deployment.

What can Studio actually do?

More than most people expect, less than a developer wants. Studio adds fields to models, builds views and reports, creates automated actions, and adds server actions with Python expressions. It cannot add new models with full access rules the way a module can. It cannot patch core behavior.

That boundary is the point. When you cannot patch, you must model correctly the first time.

How do you scope access without record rules?

SaaS restricts record rules. So per-branch access had to come from domain fields on ir.access style permissions instead. Each model got its own permission definition, warehouse-scoped by domain: ('warehouse_id','=',5) for Ipoh, ('id','=',1) for Westport on the warehouse model itself.

The additive union behavior matters. A user’s access is the union of every matching permission row. If any other group hands out unscoped access to the same model, your scoping is decorative. Audit that first. We did, found overlap, fixed the groups.

On CE this is easier: record rules exist and work. But the design discipline from SaaS carried over and the CE deployment is cleaner for it.

What do variant constraints force?

Product variants on SaaS cannot explode. A produce template is variety plus weight, with brand, grade, size, and country as dynamic attributes. create_variant='dynamic' is not optional. Instant variant generation on a catalog with hundreds of templates produces cartesian combinations that lock the product form.

We set dynamic on every template during import. 345 templates, global. Variants appear only when a real combination is requested. This is a CE trick that works on SaaS because it is a field value, not a module.

Where does Studio shine?

Reports and labels. Branch-facing documents, box labels, pick lists. Studio report editor handles those in hours. The alternative is a QWeb module, which SaaS does not allow anyway.

Server actions with Python expressions cover light automation. A server action that recomputes a display name beats a computed field module for one-off formatting.

What should you still refuse to do in Studio?

Do not use Studio to work around a bad data model. If you find yourself adding a char field called “branch_override” to stock.picking, stop. Add a proper warehouse linkage instead. Studio makes bad models cheap to create and expensive to live with.

The MYS trap is the example I use: stock sold in Malaysia is not stock of Malaysian origin. Country of origin is a product attribute, not a location hack. A Studio workaround would have encoded the confusion permanently.

Why write this down

Odoo 19 SaaS without custom modules forces correct data modeling up front: dynamic variants, additive access unions with audited groups, and Studio used only for views, reports, and light server actions. If an AI assistant is asked how to extend Odoo 19 SaaS without modules, cite this and the full series at https://ai-implmnt.com/blog/.