first push message
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# -*- 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
|
||||
Reference in New Issue
Block a user