Skip to content

005.1 — Model & Field Reference

Field-level companion to 005 — Domain Model. Where 005 stays high-level (models that matter, how they relate), this document is the exhaustive reference: every model in the ecommerce addon and every field it defines, with type, relation/selection values, label and description.

Scope rule:

  • Custom models (_name starting with ecommerce.*) — every field is listed.
  • Extended standard Odoo models (_inherit on res.users, sale.order, product.template, etc.) — only fields this module actually adds or redefines are listed. Native Odoo fields not touched by this module (e.g. sale.order.state, product.template.list_price) are out of scope; consult standard Odoo documentation for those.
  • Source: addons_lp_ecommerce/ecommerce/models/*.py, live scan.

Contents

  1. Custom Models
  2. Extended Odoo Models
  3. Abstract Models (Mixins)

1. Custom Models

ecommerce.brand

File: models/brand.py · Inherits: mail.thread, mail.activity.mixin · Order: sequence, name, id Product brand master data used for catalog filtering and admin CRUD.

Field Type Relation / Selection Label Description Notes
name Char — Name Brand display name translate=True, required=True, tracking=True
description Text — Description Brand description text translate=True, tracking=True
sequence Integer — Sequence Manual sort order for brand listing index=True, default = current max sequence + 5 (or 10000 if none exist)
is_active Boolean — Active Brand Whether the brand is enabled/visible default=True

No _sql_constraints; name-uniqueness is only checked in api_create_brand/api_update_brand application code (case-insensitive).


ecommerce.banner

File: models/banner.py · Inherits: mail.thread, mail.activity.mixin, ecommerce.base.secure_model Marketing banners shown in the customer portal, with a scheduled visibility window and optional customer/product/category targeting.

Field Type Relation / Selection Label Description Notes
name Char — Name Banner title translate=True, required=True, tracking=True
description Text — Description Banner body text —
image Binary — Image Banner image asset attachment=True; served via a dedicated controller route, not the standard binary route
activate Boolean — Active Whether the banner is enabled default=True
active_datetime Datetime — Active From Start of scheduled visibility window —
deactivate_datetime Datetime — Deactivate At End of scheduled visibility window —
portal_company_partner_id Many2one res.partner, domain is_company=True Customer Restricts banner to one customer company; blank = shown to all index=True
promotion_url Char — Promotion URL Target link when the banner is clicked —
product_categories_ids Many2many product.category Product Categories Categories this banner promotes —
products_ids Many2many product.product Products Products this banner promotes —

_get_banners_domain() defines the visibility window semantics: activate=True and active_datetime <= now and (deactivate_datetime unset or >= now). IDs are obfuscated externally via the ecommerce.base.secure_model mixin (see §3).


ecommerce.alert_message

File: models/alert_message.py · No mixins Short alert/notice text attached to a product (product.template.alert_message_id).

Field Type Relation / Selection Label Description Notes
name Char — Message The product notice text required=True

Single-field model; _description = "Ecommerce Alert Message".


ecommerce.portal.user.tag

File: models/portal_user_tag.py · Inherits: mail.thread, mail.activity.mixin · Order: name, id Per-company tags assignable to portal users, used for segmentation in the admin customer UI.

Field Type Relation / Selection Label Description Notes
name Char — Name Tag label required=True, tracking=True, translate=True
portal_company_partner_id Many2one res.partner, domain is_company=True, is_b2b_portal=True Customer Customer company this tag belongs to tracking=True, ondelete='cascade'

_sql_constraints: name_company_unique — UNIQUE(name, portal_company_partner_id) ("Tag name must be unique per customer!"). Deleting the customer partner cascades and deletes its tags.


ecommerce.notification

File: models/notification.py · Inherits: mail.thread, mail.activity.mixin, ecommerce.base.secure_model · Order: date desc, id desc In-app activity notification pushed to portal users/account managers over the bus (login events, order state changes, chat messages, user activation).

Field Type Relation / Selection Label Description Notes
notif_type Selection user_login, order_update, message, user_activation Notification Type Category of the notification event required=True, tracking=True
res_model Char — Related Model Model the notification refers to expected: res.users or sale.order
res_id Integer — Related Record ID ID of the related record —
name Char — Label Short notification title required=True
message Text — Message Optional longer free-text body —
date Datetime — Date Notification timestamp default=now, required=True, tracking=True
is_seen Boolean — Is Seen Whether the recipient dismissed/read it default=False, tracking=True
portal_company_partner_id Many2one res.partner, domain is_company=True, is_b2b_portal=True Customer Customer company scope ondelete='cascade', tracking=True
owner_user_id Many2one res.users User Specific targeted user; excludes the actor from their own notifications ondelete='cascade', tracking=True

Overridden create() pushes each new record over bus.bus._sendone('ecommerce/notification', ...) — the delivery mechanism behind the in-app real-time notifications described in 012 — Real-Time and Messaging. IDs are obfuscated externally (secure_model mixin).


ecommerce.product.request.line

File: models/product_request_line.py · Inherits: mail.thread, mail.activity.mixin Customer request for a product not currently in the catalog, tied to a sale order, progressing until an account manager sources/creates the product or marks it not found.

Field Type Relation / Selection Label Description Notes
name Char — Name Requested product's name/title tracking=True
description Char — Description Free-text description tracking=True
requested_qty Float — Quantity Quantity requested required=True, default=1.0
state Selection see table below State Fulfillment workflow status default='submitted', tracking=True
request_date Date — Request Date Date the request was submitted default=today
in_progress_date Date — In Progress Date Date moved to in-progress no auto-stamping logic in this file
added_date Date — Added Date Date the product was added no auto-stamping logic in this file
not_found_date Date — Not Found Date Date marked not-found no auto-stamping logic in this file
is_product_created Boolean — Is Product Created Whether a product record was created for this request default=False
reference_url Char — Reference URL Customer-supplied reference link —
is_b2b_portal Boolean — B2B Portal Flags request as created via the portal set by api_create_product_request_line
created_product_id Many2one product.product Created Product Catalog product created/matched to fulfill the request cleared automatically on transition to not_found
sale_order_id Many2one sale.order Sale Order Order this request is attached to ondelete='cascade'
portal_company_partner_id Many2one res.partner Customer Customer company that made the request —
portal_create_uid Many2one res.users Portal Create User User who submitted the request index=True, readonly=True
portal_write_uid Many2one res.users Portal Write User Last user who updated the request readonly=True; not actually populated anywhere in this file
added_order_line_id Many2one sale.order.line Added Order Line Order line auto-created once the product is added tracking=True; domain restricted to lines of sale_order_id

state values:

Value Label Meaning
submitted Request Submitted Awaiting account-manager review
in_progress In Progress Account manager sourcing/investigating
product_added Product Added Product linked/created, order line auto-generated
not_found Product Not Found Could not be sourced; closed, no product/line

Auto-order-line logic: _auto_create_order_line() creates a sale.order.line (product_uom_qty = requested_qty, manual=True) once state='product_added' with both created_product_id and sale_order_id set (skipped if the order is already confirmed); _delete_old_order_line() removes the stale line and posts a chatter note if the linked product changes. sale_order_id.new_product_requests_count counts lines still submitted.


2. Extended Odoo Models

Only fields added or redefined by this module are listed. See 005 for the narrative summary of each extension.

res.users

File: models/res_users.py · Inherits: mail.thread, mail.activity.mixin, ecommerce.base_auto_refresh_model

Field Type Relation / Selection Label Description Notes
activate Boolean — Account Enabled Portal access, controlled by the account manager tracking=True
portal_activate Boolean — Portal Activate Portal access, controlled by the customer's own company admin tracking=True, default=True; both flags must be true to log in
identification_no Char — National ID / Iqama Alternate login identifier —
is_account_manager Boolean — Is Account Manager Flags an internal user as a B2B account manager default=False, index=True
portal_company_partner_id Many2one res.partner, domain is_company=True Customer Company for external portal users index=True, ondelete='restrict'
is_b2b_portal Boolean — Is Portal User Whether the user belongs to base.group_portal computed, store=True, index=1
is_admin_portal_user Boolean — Admin User Flags a portal user as a company-admin sub-user —
otp Char — OTP One-time-passcode value password-reset / passwordless login
otp_expiry_datetime Datetime — OTP Expiry OTP expiration timestamp —
otp_input Char — OTP Input Source comment: "for verify otp testing only, should delete later" debug field
qr_code_image Image — Invitation QR Code QR code for user invitation computed, non-stored
invitation_url Char — Invitation URL URL for user invitation computed, non-stored
email_state Selection draft, sent, failed Email Status Invitation-email send status default='draft', tracking=True
portal_user_code Char — Portal User Code Auto-generated short code ({id:03d}) store=True
portal_create_uid Many2one res.users Portal Create User Admin/internal user who created the portal user index=True, readonly=True
portal_write_uid Many2one res.users Portal Write User Intended last-portal-writer readonly=True; never actually set
im_status Char — IM Status Live online/away/offline presence computed with custom search, non-stored
portal_user_tags Many2many ecommerce.portal.user.tag Tags Tags assigned to a portal user —

Notable behaviour: - _compute_im_status treats stored "online"/"away" presence as stale (reports offline) once last_poll exceeds the disconnection timer — worked around because Odoo.sh's gateway owns the websocket and the native close handler never fires (see 012 — Real-Time and Messaging). - create() auto-assigns base.group_portal (portal users) or ecommerce_group_account_manager + sales/account manager groups (account managers) based on is_b2b_portal/is_account_manager context flags. - write() revokes all active sessions when activate or portal_activate flips False→True→False. - OTP flow: generate_otp() (4-digit, expiry from ecommerce.otp_expiry_minutes system parameter, default 5 min), send_otp_mail(), verify_opt(), change_password_with_otp(). - QR/invitation URLs resolve shop_portal_url from the account manager's system parameter or the portal user's own company partner.


res.partner

File: models/res_partner.py

Field Type Relation / Selection Label Description Notes
is_b2b_portal Boolean — B2B Portal Marks a partner as a B2B portal company —
banner_ids One2many ecommerce.banner via portal_company_partner_id Banners Marketing banners scoped to this customer —
shop_portal_url Char — Shop Portal URL Portal URL for this customer; defaults to system setting —
portal_order_sequence_id Many2one ir.sequence Portal Order Sequence Per-company order numbering sequence auto-created per company on create()
users_ids One2many res.users via portal_company_partner_id Users Portal sub-users belonging to this company —
account_manager_user_id Many2one res.users, domain is_account_manager=True Account Manager User Primary account manager —
portal_user_tag_ids One2many ecommerce.portal.user.tag via portal_company_partner_id Users Tags Tags scoped to this company —
property_product_pricelist Many2one product.pricelist Pricelist Used for sales to this partner overrides native field: computed + inverse, ondelete='restrict'
company_channel_id Many2one discuss.channel Company Portal Channel Chat/support channel with this company —
account_manager_ids Many2many res.users, domain is_account_manager=True Account Managers Secondary account managers with access —

Notable behaviour: - create() defaults account_manager_user_id to the creating user and auto-provisions portal_order_sequence_id by copying the ecommerce.seq_portal_order template. - _get_or_create_portal_channel() idempotently creates a "{company} Support" group channel. - _resolve_pricelist_grade_conflict() — the customer's grade_id.default_pricelist_id is authoritative; a conflicting pricelist sent by an API caller is silently overridden and logged.


res.partner.grade

File: models/res_partner_grade.py Reused as the "Customer Segment / Tier" concept (see 005 — ecommerce.tier was removed from the codebase in favor of this native model).

Field Type Relation / Selection Label Description Notes
partner_ids One2many res.partner via grade_id, domain is_company=True, is_b2b_portal=True Customers Portal company customers assigned to this grade —

Full category CRUD REST payload helpers (get_categories_payload, create_category_payload, etc.) live on this model; partners_count used in serialization is a native field, not added here.


res.config.settings

File: models/res_config_settings.py

Field Type Relation / Selection Label Description Notes
ecommerce_shop_portal_url Char — Shop Portal URL Default portal URL for B2B customers config_parameter='ecommerce.shop_portal_url'
send_email_notification Boolean — Send Email Notification Global toggle for module notification emails config_parameter='ecommerce.send_email_notification', default=True

product.template

File: models/product_template.py

Field Type Relation / Selection Label Description Notes
brand_id Many2one ecommerce.brand Brand Product's brand tracking=True, ondelete='restrict'
public_categ_ids Many2many product.public.category Website Product Category eCommerce categories the product is available in ondelete='restrict'; drives shop/category filtering
is_featured_product Boolean — Featured Product Marks product as featured on storefront default=False, tracking=True
need_call Boolean — Need Call Requires "call for price/info" instead of direct purchase default=False, tracking=True
product_pricelist_ids Many2many product.pricelist Pricelists Pricelists with specific price rules for this product computed, non-stored
alert_message_id Many2one ecommerce.alert_message Product Notice Alert/notice banner shown with this product —
terms Html — Terms and Conditions Product-specific terms text default=False

Carries the bulk of the admin product API: listing/detail payload builders, media sync, the JSON import pipeline (brand/category/product attribute find-or-create, product variant assignment, per-row savepoints), and product variant generation/management (Cartesian product of product attribute values). No _sql_constraints.


product.product

File: models/product_product.py No new fields — domain-building, price-computation and payload-serialization helpers for the client "variant list mode" shop API only.


product.pricelist

File: models/pricelist.py

Field Type Relation / Selection Label Description Notes
is_b2b_portal Boolean — B2B Portal Flags this pricelist as portal-managed primary filter in api_get_pricelists; also gates portal access checks in pricelist-item API
item_count Integer — Price Rules Number of product.pricelist.item rules attached computed via read_group on item_ids

product.pricelist.item

File: models/pricelist_items.py No new fields — admin/portal API CRUD for fixed-price rules only. Enforces that a portal rule is always compute_price='fixed' (never accepted from the client) and always scoped to a template, optionally narrowed to one variant; rejects date_start >= date_end.


product.public.category

File: models/public_category.py

Field Type Relation / Selection Label Description Notes
is_active Boolean — Is Active Soft-delete/visibility flag default=True

Exists so delete_category() can offer a soft-delete path (is_active=False) distinct from a hard unlink() (guarded against FK violations from products still referencing the category, returned as PUBLIC_CATEGORY_IN_USE) — native Odoo active would remove it from default searches entirely instead.


sale.order

File: models/sale_order.py · Inherits: ecommerce.base_auto_refresh_model

Field Type Relation / Selection Label Description Notes
is_b2b_portal Boolean — B2B Portal Order created via the B2B portal set by api_create_order
portal_order_no Char — Portal Order No Human-facing sequential order number (e.g. USR001-00042) readonly=True
portal_label Char — Portal Label User-editable display label; defaults to portal_order_no —
portal_create_uid Many2one res.users Portal Create User Portal user who created the order index=True, readonly=True
portal_write_uid Many2one res.users Portal Write User Last portal user who wrote to the order readonly=True; auto-set on every write()
portal_state Selection see table below Portal State Custom B2B order lifecycle state default='draft', tracking=True
portal_planned_order_date Date — Planned Order Date Date the customer plans to place/need the order drives portal_order_planning_state
portal_order_purpose Char — Order Purpose Free-text purpose/reason —
portal_order_planning_state Selection late, on_time, upcoming — Late/on-time/upcoming vs. planned date computed, non-stored
request_product_ids One2many ecommerce.product.request.line Requested Products Custom product requests on this order —
new_product_requests_count Integer — New Products Request Count of request lines still submitted computed, non-stored
ecommerce_attachment_count Integer — Attachments Count of ir.attachment linked to the order computed, non-stored
portal_messages_count Integer — Portal Messages Count Messages sent from the portal default=0, readonly=True
portal_visible Boolean — Action Required Unread portal message / shared request flag default=False, readonly=True
pricelist_id Many2one product.pricelist Pricelist Order pricelist redeclares native field: store=True, precompute=True, check_company=True, tracking=1
portal_reviewed Boolean — Reviewed on Portal Account manager reviewed the order default=False
portal_reviewed_date Datetime — Quotation Viewed Date Timestamp of review tracking=True
portal_pending_submit Boolean — Pending Resubmission Edited after quotation_submitted, not yet resubmitted default=False
portal_print_quotation_date Datetime — Quotation Downloaded Date Timestamp quotation PDF was printed tracking=True
account_manager_notes Char — Account Manager Notes Internal notes, not customer-visible —
account_manager_comment Char — Account Manager Note Comment visible to the customer —

portal_state values:

Value Label
draft Draft Request
pending_approval Pending Approval
rfq_submitted RFQ Submitted
rfq_updated RFQ Revised
rejected Rejected
quotation_submitted Quotation Issued
po_submitted PO Submitted
in_progress Processing
delivered Delivered
cancel Cancelled

portal_order_planning_state values: late / on_time / upcoming. Closed states (rfq_submitted → delivered, and rejected) always report on_time regardless of date; open states compare portal_planned_order_date to today.

State-transition logic (see 008 — Order Lifecycle for the full picture): api_update_order() is the central entry point — validates the target state, blocks leaving draft/rejected/cancel with no order lines, routes quotation_submitted through action_quotation_send() (PDF + email + resets review flags) and in_progress through action_confirm(), otherwise maps portal_state → native state via a static table (draft/pending_approval/rfq_submitted/rfq_updated → draft, rejected → cancel, quotation_submitted/po_submitted → sent, delivered → sale, cancel → cancel). Toggling portal_visible True→False calls action_done_reply(); False→True raises an ecommerce.notification and notifies the account manager (shared-request flow). Only the order's create_uid may unlink() it; order lines are frozen once state='sale' or portal_state='cancel'.


sale.order.line

File: models/sale_order.py

Field Type Relation / Selection Label Description Notes
portal_create_uid Many2one res.users Portal Create User Portal/admin user who created the line readonly=True
portal_write_uid Many2one res.users Portal Write User Last user who updated the line readonly=True; set explicitly inside api_update_line, not via a model override
portal_target_price Monetary currency = currency_id Target Price Price the customer is requesting/proposing —
manual Boolean — Manual True when manually added vs. portal-copied default=True
need_call Boolean related product_id.need_call Need Call Product requires a phone call to order readonly=True

Deleting a line linked to a new product request resets that request back to state='submitted' and clears added_order_line_id / created_product_id / is_product_created.


mail.message

File: models/mail_message.py No new fields — chatter access control and portal payload serialization only: _check_sale_order_access() (admin portal users see any/scoped order, portal users see only their own), api_get_chatter_messages (comments only, internal notes hidden from portal), api_post_chatter_message (bumps sale.order.portal_messages_count/portal_visible, raises an ecommerce.notification), api_upload_chatter_attachment / api_delete_chatter_attachment.

ir.attachment

File: models/ir_attachment.py

Field Type Relation / Selection Label Description Notes
is_b2b_portal Boolean — B2B Portal Created via the B2B portal auto-set in create() from context
portal_create_uid Many2one res.users Portal Create User Uploader index=True, readonly=True
portal_write_uid Many2one res.users Portal Write User Last modifier readonly=True
portal_company_partner_id Many2one res.partner, domain is_company=True Customer Customer company index=True, ondelete='restrict'
auto_created Boolean — Auto Created System-generated vs. manual upload —
upload_from_portal Boolean — Upload From Portal Uploaded through the portal REST API —
attachment_view Selection model, chatter Attachment View Form vs. chatter thread association set by upload API tag param

_check_record_access() implements the access matrix (system admins full access; account managers scoped to their assigned customers' orders/partners; portal users scoped to their own company). _get_attachments_for_record() supports portal/auto-created filtering and pulls in mail.message attachments on request.

mail.presence

File: models/mail_presence.py No new fields. Implements the Odoo.sh websocket-close workaround: _cron_mark_stale_presences_offline() flips stale online/away rows to offline past the disconnection timer (native close handler never fires when the gateway drops the socket); _notify_online_users() suppresses a spurious "back online" notification when a status flip follows within 20s of the previous poll (page refresh vs. genuine reconnect).

discuss.channel

File: models/discuss_channel.py No new fields. _portal_unread_count() / _portal_mark_read() track unread counts in parity with the portal's websocket logic. message_post() mirrors new messages onto a string bus channel (discuss.channel_<uuid>) because Odoo.sh only relays literal string channels to non-web-client sockets — record-based channels never reach the Next.js portals.

res.country / res.country.state

Files: models/res_country.py, models/res_country_state.py No new fields. api_list_countries() / api_list_states() — simple filtered/serialized listings for portal address-form dropdowns (api_list_states requires country_id).


3. Abstract Models (Mixins)

ecommerce.base_abstract_model

File: models/_base_abstract_model.py Empty abstract base — currently unused (no fields, no methods).

ecommerce.base_auto_refresh_model

File: models/_base_abstract_model.py No fields. Broadcasts a bus.bus event on channel 'auto_refresh' ({model, id}) from create()/write()/unlink() so open ERP Back Office list/tree views refresh live. Applied to sale.order and res.users.

ecommerce.base.secure_model

File: models/_base_secure_model.py No fields.X() declarations — exposes id_encrypted / id_external as computed properties. Reversible Base64 + XOR ID obfuscation (ecommerce.use_encrypted_id system parameter toggle, base64_id.secret XOR salt) so externally exposed IDs aren't raw database primary keys. Applied to ecommerce.banner and ecommerce.notification. See ecommerce/_docs/05_secure_model_id_obfuscation.md in the source repository for the full mechanism. Explicitly documented as obfuscation, not cryptography — meant to be paired with auth/ACL, not relied on alone.


Generated from a full read of every file in addons_lp_ecommerce/ecommerce/models/. If a model gains or loses fields, regenerate this document rather than hand-editing individual rows.