diff --git a/auth_quick_master/__manifest__.py b/auth_quick_master/__manifest__.py
index 69beba2bc..cfaf04f64 100644
--- a/auth_quick_master/__manifest__.py
+++ b/auth_quick_master/__manifest__.py
@@ -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",
diff --git a/saas/__manifest__.py b/saas/__manifest__.py
index ca8bd8c5d..01c4dac4c 100644
--- a/saas/__manifest__.py
+++ b/saas/__manifest__.py
@@ -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",
@@ -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",
diff --git a/saas/__pycache__/__init__.cpython-311.pyc b/saas/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 000000000..2fb26012e
Binary files /dev/null and b/saas/__pycache__/__init__.cpython-311.pyc differ
diff --git a/saas/controllers/__pycache__/__init__.cpython-311.pyc b/saas/controllers/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 000000000..b352b6460
Binary files /dev/null and b/saas/controllers/__pycache__/__init__.cpython-311.pyc differ
diff --git a/saas/controllers/__pycache__/main.cpython-311.pyc b/saas/controllers/__pycache__/main.cpython-311.pyc
new file mode 100644
index 000000000..d380b68ee
Binary files /dev/null and b/saas/controllers/__pycache__/main.cpython-311.pyc differ
diff --git a/saas/data/saas_operator_data.xml b/saas/data/saas_operator_data.xml
index 99287487d..0f2b0f3e7 100644
--- a/saas/data/saas_operator_data.xml
+++ b/saas/data/saas_operator_data.xml
@@ -7,5 +7,6 @@
http://{db_name}.127.0.0.1.nip.io
+
diff --git a/saas/models/__pycache__/__init__.cpython-311.pyc b/saas/models/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 000000000..62ef53d27
Binary files /dev/null and b/saas/models/__pycache__/__init__.cpython-311.pyc differ
diff --git a/saas/models/__pycache__/auth_quick_master_token.cpython-311.pyc b/saas/models/__pycache__/auth_quick_master_token.cpython-311.pyc
new file mode 100644
index 000000000..545342595
Binary files /dev/null and b/saas/models/__pycache__/auth_quick_master_token.cpython-311.pyc differ
diff --git a/saas/models/__pycache__/queue_job.cpython-311.pyc b/saas/models/__pycache__/queue_job.cpython-311.pyc
new file mode 100644
index 000000000..3a143f679
Binary files /dev/null and b/saas/models/__pycache__/queue_job.cpython-311.pyc differ
diff --git a/saas/models/__pycache__/saas_db.cpython-311.pyc b/saas/models/__pycache__/saas_db.cpython-311.pyc
new file mode 100644
index 000000000..a27e2cde8
Binary files /dev/null and b/saas/models/__pycache__/saas_db.cpython-311.pyc differ
diff --git a/saas/models/__pycache__/saas_log.cpython-311.pyc b/saas/models/__pycache__/saas_log.cpython-311.pyc
new file mode 100644
index 000000000..a8d353e55
Binary files /dev/null and b/saas/models/__pycache__/saas_log.cpython-311.pyc differ
diff --git a/saas/models/__pycache__/saas_operator.cpython-311.pyc b/saas/models/__pycache__/saas_operator.cpython-311.pyc
new file mode 100644
index 000000000..82dda45d5
Binary files /dev/null and b/saas/models/__pycache__/saas_operator.cpython-311.pyc differ
diff --git a/saas/models/__pycache__/saas_template.cpython-311.pyc b/saas/models/__pycache__/saas_template.cpython-311.pyc
new file mode 100644
index 000000000..a87fcd356
Binary files /dev/null and b/saas/models/__pycache__/saas_template.cpython-311.pyc differ
diff --git a/saas/models/queue_job.py b/saas/models/queue_job.py
index e758de104..f11e87629 100644
--- a/saas/models/queue_job.py
+++ b/saas/models/queue_job.py
@@ -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
diff --git a/saas/models/saas_db.py b/saas/models/saas_db.py
index d0ff2cf46..4b8cc8ca6 100644
--- a/saas/models/saas_db.py
+++ b/saas/models/saas_db.py
@@ -2,8 +2,11 @@
# Copyright 2019 Denis Mudarisov
# Copyright 2020 Eugene Molotov
# 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'
@@ -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:
@@ -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()
@@ -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"):
diff --git a/saas/models/saas_db_old.py b/saas/models/saas_db_old.py
new file mode 100644
index 000000000..4f6cda439
--- /dev/null
+++ b/saas/models/saas_db_old.py
@@ -0,0 +1,105 @@
+# Copyright 2018 Ivan Yelizariev
+# Copyright 2019 Denis Mudarisov
+# Copyright 2020 Eugene Molotov
+# 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())])
diff --git a/saas/models/saas_operator.py b/saas/models/saas_operator.py
index 58a9fe770..11d3bfa48 100644
--- a/saas/models/saas_operator.py
+++ b/saas/models/saas_operator.py
@@ -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'),
@@ -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:
@@ -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):
@@ -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):
@@ -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()
diff --git a/saas/models/saas_template.py b/saas/models/saas_template.py
index 867ce9433..cf1494fcb 100644
--- a/saas/models/saas_template.py
+++ b/saas/models/saas_template.py
@@ -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,
@@ -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()
\ No newline at end of file
diff --git a/saas/views/saas_db_views.xml b/saas/views/saas_db_views.xml
index 685c299c0..5edb95e47 100644
--- a/saas/views/saas_db_views.xml
+++ b/saas/views/saas_db_views.xml
@@ -20,9 +20,9 @@
@@ -38,7 +66,13 @@
-
+
+
+
+
+
+
+
diff --git a/saas/views/saas_template_operator_views.xml b/saas/views/saas_template_operator_views.xml
index e6663938f..a0a05640e 100644
--- a/saas/views/saas_template_operator_views.xml
+++ b/saas/views/saas_template_operator_views.xml
@@ -8,7 +8,7 @@