Skip to content
Open

17.0 #155

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
cde1626
:sos: Make the modules as uninstallable
ilmir-k Mar 30, 2023
6d30822
github: change funding link
em230418 Apr 20, 2023
4f9f239
Update __manifest__.py
deepcodesystem Nov 28, 2023
fc2bdb5
Update __manifest__.py
deepcodesystem Nov 28, 2023
4dc031e
Update __manifest__.py
deepcodesystem Nov 28, 2023
be95e56
Update __manifest__.py
deepcodesystem Nov 28, 2023
bfe4564
Update __manifest__.py
deepcodesystem Nov 28, 2023
e508949
Update __manifest__.py
deepcodesystem Nov 28, 2023
9f9a8c5
Update __manifest__.py
deepcodesystem Nov 28, 2023
d57b57d
Update __manifest__.py
deepcodesystem Nov 28, 2023
51f01a4
Update __manifest__.py
deepcodesystem Nov 28, 2023
1524ceb
Update __manifest__.py
deepcodesystem Nov 28, 2023
73479f0
Update __manifest__.py
deepcodesystem Nov 28, 2023
6b67bfc
Update __manifest__.py
deepcodesystem Nov 28, 2023
538d687
Update __manifest__.py
deepcodesystem Nov 28, 2023
27420f2
Update __manifest__.py
deepcodesystem Nov 28, 2023
86f8760
Update __manifest__.py
deepcodesystem Nov 28, 2023
afccffc
Update __manifest__.py
deepcodesystem Nov 28, 2023
88f8037
Update main.py
deepcodesystem Nov 29, 2023
bcf4a6f
Update queue_job.py
deepcodesystem Nov 29, 2023
0b0e15c
Update saas_db.py
deepcodesystem Nov 29, 2023
00b111e
Update saas_template.py
deepcodesystem Nov 29, 2023
f810af2
Update queue_job.py
deepcodesystem Nov 29, 2023
38b396a
Update saas_db.py
deepcodesystem Nov 29, 2023
3318448
Update saas_template.py
deepcodesystem Nov 29, 2023
b56e77f
Update saas_db.py
deepcodesystem Nov 29, 2023
72bb9f8
Update saas_db.py
deepcodesystem Nov 29, 2023
0476391
Update saas_db.py
deepcodesystem Nov 29, 2023
c364f1d
Update saas_db.py
deepcodesystem Nov 29, 2023
06c3258
Create __manifest__.py
deepcodesystem Aug 28, 2024
3761a7e
Create __init__.py
deepcodesystem Aug 28, 2024
4dd62e2
Create __init__.py
deepcodesystem Aug 28, 2024
889a86f
Create subscription_package.xml
deepcodesystem Aug 28, 2024
ab2a213
Add files via upload
deepcodesystem Aug 30, 2024
91843a5
Add files via upload
deepcodesystem Sep 2, 2024
1924689
Add files via upload
deepcodesystem Sep 5, 2024
b6eff1b
Add files via upload
deepcodesystem Sep 5, 2024
0e424ac
Add files via upload
deepcodesystem Oct 12, 2024
e56efa5
Add files via upload
deepcodesystem Oct 12, 2024
f3dc18a
Add files via upload
deepcodesystem Nov 3, 2024
971ac60
Add files via upload
deepcodesystem Nov 3, 2024
66f72ac
Add files via upload
deepcodesystem Nov 3, 2024
8a952ca
Merge branch 'it-projects-llc:16.0' into 16.0
deepcodesystem Jan 22, 2025
4dff97d
update 17.0
deepcodesystem Apr 2, 2025
9830fb5
update 17.0
deepcodesystem Apr 2, 2025
5796fe4
update 17.0
deepcodesystem Apr 3, 2025
385a1a4
Modifications 19
deepcodesystem Jul 14, 2025
25c5093
Modifications 19
deepcodesystem Jul 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auth_quick_master/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"category": "Extra Tools",
# "live_test_url": "http://apps.it-projects.info/shop/product/DEMO-URL?version=12.0",
"images": ['images/quick_auth_master.jpg'],
"version": "15.0.1.1.0",
"version": "17.0.1.1.0",
"application": False,

"author": "IT-Projects LLC, Ivan Yelizariev",
Expand Down
4 changes: 2 additions & 2 deletions saas/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"category": "SaaS",
# "live_test_url": "http://apps.it-projects.info/shop/product/DEMO-URL?version=14.0",
"images": [],
"version": "15.0.3.1.0",
"version": "17.0.21.3.0",
"application": False,

"author": "IT-Projects LLC, Ivan Yelizariev",
Expand Down Expand Up @@ -38,7 +38,7 @@
"views/saas_operator_views.xml",
"views/saas_module_views.xml",
"views/saas_db_views.xml",
"views/res_config_settings_views.xml",
#"views/res_config_settings_views.xml",
"wizard/saas_template_create_build_view.xml",
"data/ir_cron_data.xml",
"data/saas_operator_data.xml",
Expand Down
Binary file added saas/__pycache__/__init__.cpython-311.pyc
Binary file not shown.
Binary file not shown.
Binary file added saas/controllers/__pycache__/main.cpython-311.pyc
Binary file not shown.
1 change: 1 addition & 0 deletions saas/data/saas_operator_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
<field name="db_url_template">http://{db_name}.127.0.0.1.nip.io</field>
<!-- It works when db-filter=^%d$ -->
<field name="global_url" eval="'http://' + obj().env.cr.dbname + '.127.0.0.1.nip.io:8069'" model="saas.operator"/>
<field name="domain_build"></field>
</record>
</odoo>
Binary file added saas/models/__pycache__/__init__.cpython-311.pyc
Binary file not shown.
Binary file not shown.
Binary file added saas/models/__pycache__/queue_job.cpython-311.pyc
Binary file not shown.
Binary file added saas/models/__pycache__/saas_db.cpython-311.pyc
Binary file not shown.
Binary file added saas/models/__pycache__/saas_log.cpython-311.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion saas/models/queue_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ class QueueJob(models.Model):

def write(self, vals):
res = super(QueueJob, self).write(vals)
self.flush()
self.env.cr.commit()
return res
35 changes: 31 additions & 4 deletions saas/models/saas_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
# Copyright 2019 Denis Mudarisov <https://it-projects.info/team/trojikman>
# Copyright 2020 Eugene Molotov <https://it-projects.info/team/em230418>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import models, fields
from odoo import models, fields, api
from odoo.exceptions import ValidationError
import logging

_logger = logging.getLogger(__name__)

class SAASDB(models.Model):
_name = 'saas.db'
Expand All @@ -25,14 +28,30 @@ def unlink(self):
self.drop_db()
return super(SAASDB, self).unlink()

@api.model
def create_db(self, template_db, demo, lang='en_US', callback_obj=None, callback_method=None):
self.ensure_one()
db_name = self.name
self.operator_id._create_db(template_db, db_name, demo, lang)
_logger.info(f"Creating savepoint for DB: {db_name}")
try:
_logger.info(f"Creating DB: {db_name} using template: {template_db}")
self.operator_id._create_db(template_db, db_name, demo, lang)
except Exception as e:
_logger.error(f"Error during DB creation: {str(e)}")
raise ValidationError(f"Erreur lors de la création de la base de données: {str(e)}")
self.name = db_name
self.state = 'done'
self.env['saas.log'].log_db_created(self)
if callback_obj and callback_method:
_logger.info(f"Calling callback method: {callback_method}")
getattr(callback_obj, callback_method)()
_logger.info(f"DB {db_name} created successfully")

def _on_template_created(self):
self.ensure_one()
self.to_rebuild = False
self.state = 'installing_modules'
self.operator_id.with_delay().install_modules(self.template_id, self)

def drop_db(self):
for r in self:
Expand Down Expand Up @@ -60,7 +79,7 @@ def write(self, vals):
return res

def refresh_data(self, should_read_from_build=True, should_write_to_build=True):
self.flush()
self.env.cr.commit()
for record in self.filtered(lambda record: (record.type, record.state) == ("build", "done")).with_context(writing_from_refresh_data=True):
if should_read_from_build:
vals = record.read_values_from_build()
Expand All @@ -84,7 +103,15 @@ def xmlid_lookup(self, xmlid):
return self.execute_kw("ir.model.data", "_xmlid_lookup", xmlid)

def xmlid_to_res_model_res_id(self, xmlid, raise_if_not_found=False):
return self.execute_kw("ir.model.data", "_xmlid_to_res_model_res_id", xmlid, raise_if_not_found=raise_if_not_found)
# Utilise xmlid_lookup qui est une méthode publique et gère l'extraction des informations
# au lieu d'appeler une méthode privée directement.
res = self.xmlid_lookup(xmlid)
if res:
# xmlid_lookup retourne (external_id, model, res_id)
return res[1], res[2]
elif raise_if_not_found:
raise ValueError(f"XML ID '{xmlid}' not found.")
return None, None

def action_install_missing_mandatory_modules(self):
for build in self.filtered(lambda x: x.state == "done"):
Expand Down
105 changes: 105 additions & 0 deletions saas/models/saas_db_old.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2019 Denis Mudarisov <https://it-projects.info/team/trojikman>
# Copyright 2020 Eugene Molotov <https://it-projects.info/team/em230418>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import models, fields
from odoo.exceptions import ValidationError


class SAASDB(models.Model):
_name = 'saas.db'
_inherit = 'mail.thread'
_description = 'Build'

name = fields.Char('Name', help='Technical Database name', readonly=True)
operator_id = fields.Many2one('saas.operator', required=True)
type = fields.Selection([
('template', 'Template DB'),
('build', 'Normal Build'),
], string='DB Type', default='build')
state = fields.Selection([
('draft', 'Draft'),
('done', 'Ready'),
], default='draft')

def unlink(self):
self.drop_db()
return super(SAASDB, self).unlink()

def create_db(self, template_db, demo, lang='en_US', callback_obj=None, callback_method=None):
self.ensure_one()
db_name = self.name + ".getaperp.com"
existing_db = [db[0] for db in self.list_databases()]
if db_name in existing_db:
raise ValidationError("Une base de données avec ce nom existe déjà.")
else:
self.operator_id._create_db(template_db, db_name, demo, lang)
self.name = db_name
self.state = 'done'
self.env['saas.log'].log_db_created(self)
if callback_obj and callback_method:
getattr(callback_obj, callback_method)()


def list_databases(self):
"""Liste toutes les bases de données disponibles."""
# Remplacer 'your_postgresql_connection_string' par votre chaîne de connexion PostgreSQL
self.env.cr.execute("SELECT datname FROM pg_catalog.pg_database")
return self.env.cr.fetchall()

def drop_db(self):
for r in self:
r.operator_id._drop_db(r.name)
r.state = 'draft'
self.env['saas.log'].log_db_dropped(r)

def get_url(self):
# TODO: need possibility to use custom domain
self.ensure_one()
return self.sudo().operator_id.get_db_url(self)

def action_get_build_access(self):
auth_url = '/saas/auth-to-build/' + str(self.id)
return {
'type': 'ir.actions.act_url',
'target': 'new',
'url': auth_url,
}

def write(self, vals):
res = super(SAASDB, self).write(vals)
if not self.env.context.get("writing_from_refresh_data"): # Do not run "refresh_data", if already running it
self.refresh_data()
return res

def refresh_data(self, should_read_from_build=True, should_write_to_build=True):
self.env.cr.commit()
for record in self.filtered(lambda record: (record.type, record.state) == ("build", "done")).with_context(writing_from_refresh_data=True):
if should_read_from_build:
vals = record.read_values_from_build()
if vals:
record.write(vals)

if should_write_to_build:
record.write_values_to_build()
record.operator_id._signal_changes(record.name)

def write_values_to_build(self):
pass

def read_values_from_build(self):
return {}

def execute_kw(self, model, method, *args, **kwargs):
return self.operator_id.build_execute_kw(self, model, method, args, kwargs)

def xmlid_lookup(self, xmlid):
return self.execute_kw("ir.model.data", "_xmlid_lookup", xmlid)

def xmlid_to_res_model_res_id(self, xmlid, raise_if_not_found=False):
return self.execute_kw("ir.model.data", "_xmlid_to_res_model_res_id", xmlid, raise_if_not_found=raise_if_not_found)

def action_install_missing_mandatory_modules(self):
for build in self.filtered(lambda x: x.state == "done"):
operator = build.operator_id
operator._install_modules(build.name, [('name', 'in', operator.get_mandatory_modules())])
65 changes: 60 additions & 5 deletions saas/models/saas_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
class SAASOperator(models.Model):
_name = 'saas.operator'
_description = 'Database Operator'
_inherit = ['mail.thread', 'mail.activity.mixin']

name = fields.Char(required=True)
name = fields.Char(string='Name', required=True)
# list of types can be extended via selection_add
type = fields.Selection([
('local', 'Same Instance'),
Expand All @@ -27,6 +28,25 @@ class SAASOperator(models.Model):
template_operator_ids = fields.One2many('saas.template.operator', 'operator_id')

build_count = fields.Integer(compute="_compute_build_count")
domain_build = fields.Char('Build Domain', required=True)

# Server capacity
max_databases = fields.Integer(string='Max Databases', default=50)
cpu_cores = fields.Integer(string='CPU Cores', default=4)
ram_size = fields.Integer(string='RAM (GB)', default=16)
storage_size = fields.Integer(string='Storage (GB)', default=500)

# Server status
active = fields.Boolean(default=True)
state = fields.Selection([
('online', 'Online'),
('offline', 'Offline'),
('maintenance', 'Maintenance'),
], string='Status', default='offline', tracking=True)

# Usage
used_databases = fields.Integer(string='Used Databases')
load_percentage = fields.Float(string='Load (%)', default=0)

def _compute_build_count(self):
for record in self:
Expand All @@ -49,11 +69,11 @@ def get_mandatory_modules(self):

def _create_db(self, template_db, db_name, demo, lang='en_US'):
"""Synchronous db creation"""
db_name = db_name
if not self:
return
elif self.type != 'local':
raise NotImplementedError()

return cluster.create_db(template_db, db_name, demo, lang)

def _drop_db(self, db_name):
Expand All @@ -65,9 +85,43 @@ def _drop_db(self, db_name):
return cluster.drop_db(db_name)

def _install_modules(self, db_name, modules):
if self.type != 'local':
raise NotImplementedError()

"""
Installe les modules sur une base de données donnée.
Cette méthode est appelée par la tâche en file d'attente.
"""
# cluster = self.env['saas.cluster']._get_current_cluster()

# Étape 1 : S'assurer que le module 'mail' est installé en premier s'il ne l'est pas déjà.
# C'est une étape critique pour éviter les erreurs UndefinedColumn pour res_users.notification_type.
# Le module 'mail' ajoute cette colonne.
# Vous devrez peut-être ajuster cela en fonction de la façon dont votre cluster gère les installations initiales de modules.
# Une approche courante consiste à s'assurer que 'mail' fait partie des modules de base installés sur une nouvelle base de données.

# Exemple : Si votre cluster a une méthode pour installer des modules de base ou s'assurer que certains modules existent
# cluster.ensure_base_modules(db_name, ['mail'])

# Sinon, vous devrez peut-être l'installer explicitement avant d'autres modules.
# Ceci est un exemple simplifié et pourrait nécessiter une gestion des erreurs/vérifications plus robuste.
try:
# Tenter d'installer 'mail' s'il n'est pas déjà dans la liste des modules à installer
# et s'il n'est pas déjà installé dans la base de données cible.
# Cette partie dépend fortement de votre implémentation de 'saas_cluster_simple'.
# Un meilleur endroit pourrait être dans la logique de création/initialisation de la base de données.
if 'mail' not in modules:
# Ceci est un espace réservé. Vous avez besoin d'un mécanisme dans votre cluster pour installer un seul module.
# Par exemple, en appelant une commande Odoo sur la base de données cible.
# Pour la démonstration, supposons que cluster.install_modules peut gérer un seul module.
# Dans un scénario réel, vous auriez probablement une méthode dédiée pour la configuration des modules de base.
self.env.cr.commit() # Committer la transaction actuelle avant l'appel Odoo externe
cluster.install_modules(db_name, ['mail'])
self.env.cr.commit() # Committer après l'appel Odoo externe
except Exception as e:
# Journaliser l'erreur mais essayer de continuer si 'mail' est peut-être déjà là ou géré différemment
self.env.cr.rollback() # Annuler si l'installation de 'mail' a échoué
# self.env.logger.warning(f"Impossible de s'assurer que le module 'mail' est installé sur {db_name}: {e}")
# Relancer l'exception si c'est une défaillance critique, ou gérer gracieusement.

# Étape 2 : Procéder à l'installation des modules d'origine
return cluster.install_modules(db_name, modules)

def install_modules(self, template_id, template_operator_id):
Expand All @@ -79,6 +133,7 @@ def install_modules(self, template_id, template_operator_id):
self.with_delay().post_init(template_id, template_operator_id)

def _post_init(self, db_name, template_post_init):
db_name = db_name
if self.type != 'local':
raise NotImplementedError()

Expand Down
4 changes: 2 additions & 2 deletions saas/models/saas_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def _prepare_template(self):
r.write({
'state': 'creating',
})
r.flush()
r.env.cr.commit()
r.operator_db_id.with_delay().create_db(
None,
r.template_id.template_demo,
Expand Down Expand Up @@ -253,4 +253,4 @@ def random_ready_operator(self):

def action_install_missing_mandatory_modules(self):
for record in self:
record.operator_db_id.action_install_missing_mandatory_modules()
record.operator_db_id.action_install_missing_mandatory_modules()
6 changes: 3 additions & 3 deletions saas/views/saas_db_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
<field name="arch" type="xml">
<form>
<header>
<button name="action_get_build_access" type="object" string="Connect to the Build" attrs="{'invisible':[('state', '!=', 'done')]}" class="oe_read_only" />
<button name="refresh_data" type="object" string="Refresh" attrs="{'invisible':[('state', '!=', 'done')]}" class="oe_read_only" />
<button name="action_install_missing_mandatory_modules" type="object" string="Install missing mandatory modules" attrs="{'invisible':[('state', '!=', 'done')]}" class="oe_read_only" groups="base.group_no_one" />
<button name="action_get_build_access" type="object" string="Connect to the Build" invisible="state != 'done'" />
<button name="refresh_data" type="object" string="Refresh" invisible="state != 'done'" />
<button name="action_install_missing_mandatory_modules" type="object" string="Install missing mandatory modules" invisible="state != 'done'" groups="base.group_no_one" />
<field name="state" widget="statusbar" nolabel="1"/>
</header>
<sheet>
Expand Down
Loading