Files

77 lines
3.2 KiB
Python
Raw Permalink Normal View History

2026-07-01 14:41:49 +07:00
# -*- coding: utf-8 -*-
from odoo import api, fields, models
class SaasApp(models.Model):
"""Mirrors REAL installed Odoo "apps" (not technical dependency modules)
so the public /trial "Choose your Apps" page only shows what is
actually installed on this internal instance - exactly like the
odoo.com/trial reference page.
Odoo's ir.module.module already flags genuine apps via the
`application = True` field (set by each module's __manifest__.py
with 'application': True). Pure dependency/technical modules
(e.g. 'mail', 'web', 'base_setup') have application = False and are
therefore excluded automatically - no manual curation needed.
"""
_name = 'saas.app'
_description = 'SaaS Sellable App (synced from installed Odoo apps)'
_order = 'category_sequence, sequence, name'
name = fields.Char(required=True)
technical_module_name = fields.Char(
required=True,
help="Technical name of the Odoo module to install, e.g. 'website', 'crm', 'account'."
)
module_id = fields.Many2one('ir.module.module', string='Source Module', ondelete='cascade')
icon = fields.Image('Icon', max_width=128, max_height=128)
icon_url = fields.Char() # fallback path to module's static icon if no binary icon
description = fields.Text()
category_id = fields.Many2one('ir.module.category', string='App Category')
category_sequence = fields.Integer(related='category_id.sequence', store=True)
sequence = fields.Integer(default=10)
active = fields.Boolean(default=True)
_sql_constraints = [
('module_unique', 'unique(module_id)', 'Each installed app can only be listed once.'),
]
@api.model
def _sync_from_installed_modules(self):
"""Step: Internal Apps Mirror
Reads every module on THIS Odoo instance that is:
- state = 'installed' (actually installed internally)
- application = True (a real "app", not a dependency module)
and upserts a saas.app record for each, so the public /trial page
always reflects exactly what is installed - nothing more.
"""
Module = self.env['ir.module.module'].sudo()
installed_apps = Module.search([
('state', '=', 'installed'),
('application', '=', True),
])
existing = self.sudo().search([])
existing_by_module = {a.module_id.id: a for a in existing}
for module in installed_apps:
vals = {
'name': module.shortdesc or module.name,
'technical_module_name': module.name,
'module_id': module.id,
'description': module.summary or module.description,
'category_id': module.category_id.id if module.category_id else False,
'icon_url': f'/{module.name}/static/description/icon.png',
'active': True,
}
if module.id in existing_by_module:
existing_by_module[module.id].sudo().write(vals)
else:
self.sudo().create(vals)
# Deactivate saas.app entries whose module got uninstalled meanwhile
stale = existing.filtered(lambda a: a.module_id.id not in installed_apps.ids)
stale.sudo().write({'active': False})
return True