diff --git a/My_Auto_Backup/__init__.py b/My_Auto_Backup/__init__.py
new file mode 100644
index 000000000..74d37b7be
--- /dev/null
+++ b/My_Auto_Backup/__init__.py
@@ -0,0 +1,8 @@
+
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Eng Mohamed Saied
+#
+#############################################################################
+from . import models
diff --git a/My_Auto_Backup/__manifest__.py b/My_Auto_Backup/__manifest__.py
new file mode 100644
index 000000000..727e36bd8
--- /dev/null
+++ b/My_Auto_Backup/__manifest__.py
@@ -0,0 +1,34 @@
+
+{
+ 'name': 'My Auto Backup Database',
+ 'version': '1.0',
+ 'category': 'Tools',
+ 'summary': 'Manage and schedule database backups for Odoo instances',
+ 'description': """
+ This module helps you to configure, schedule, and manage database backups for Odoo.
+ It includes features for backup generation, retention management, and report generation.
+ The module supports local storage and can be extended to cloud storage.
+ Features include:
+ - Configure backups for databases.
+ - Automate backups with a scheduler.
+ - Retain backups based on a defined retention policy.
+ - Generate detailed reports about backup status.
+ - Track backup progress in real-time.
+ """,
+ 'depends':['base'],
+ 'author': 'Mohamed Saied',
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'data/ir_cron_data.xml',
+ 'views/db_backup.xml',
+ ],
+ 'images': [
+ 'static/description/banner.gif',
+ 'static/description/icon.png',
+
+ ],
+ 'license': 'LGPL-3',
+ 'installable': True,
+ 'auto_install': False,
+ 'application': True,
+}
diff --git a/My_Auto_Backup/data/ir_cron_data.xml b/My_Auto_Backup/data/ir_cron_data.xml
new file mode 100644
index 000000000..939888146
--- /dev/null
+++ b/My_Auto_Backup/data/ir_cron_data.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Backup: Automatic Database Backup
+
+ code
+ model._schedule_auto_backup()
+ 1
+ days
+ -1
+ True
+
+
+
diff --git a/My_Auto_Backup/models/__init__.py b/My_Auto_Backup/models/__init__.py
new file mode 100644
index 000000000..91fc05b10
--- /dev/null
+++ b/My_Auto_Backup/models/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Eng Mohamed Saied
+#
+#############################################################################
+from . import db_backup
\ No newline at end of file
diff --git a/My_Auto_Backup/models/db_backup.py b/My_Auto_Backup/models/db_backup.py
new file mode 100644
index 000000000..484635734
--- /dev/null
+++ b/My_Auto_Backup/models/db_backup.py
@@ -0,0 +1,158 @@
+#############################################################################
+#
+# Eng Mohamed Saied
+#
+#############################################################################
+
+
+from odoo import api, fields, models, _
+from odoo.service import db
+from odoo.exceptions import ValidationError, UserError
+import os
+import datetime
+import logging
+import time
+
+_logger = logging.getLogger(__name__)
+
+class DbBackupConfigure(models.Model):
+ _name = 'db.backup.configure'
+ _description = 'Database Backup Configuration'
+
+ name = fields.Char(string='Name', required=True)
+ db_name = fields.Char(string='Database Name', required=True)
+ master_pwd = fields.Char(string='Master Password', required=True)
+ backup_format = fields.Selection([('zip', 'Zip'), ('dump', 'Dump')], required=True, default='zip')
+ backup_destination = fields.Selection([('local', 'Local Storage')], required=True)
+ backup_path = fields.Char(string='Backup Path')
+ active = fields.Boolean(default=True)
+ auto_remove = fields.Boolean(string='Auto Remove Old Backups')
+ days_to_remove = fields.Integer(string='Days to Retain Backups')
+ backup_filename = fields.Char(string='Generated Filename')
+ generated_exception = fields.Text(string='Exception Details')
+ last_backup_status = fields.Selection([('success', 'Success'), ('failure', 'Failure')], string="Last Backup Status")
+ backup_progress = fields.Integer(string="Backup Progress (%)", default=0)
+
+ @api.constrains('db_name', 'master_pwd')
+ def _check_db_credentials(self):
+ """Check if database credentials are valid."""
+ if self.db_name not in db.list_dbs():
+ raise ValidationError(_("Database %s does not exist.") % self.db_name)
+ try:
+ db.check_super(self.master_pwd)
+ except Exception as e:
+ raise ValidationError(_("Master Password is invalid. Error: %s") % e)
+
+ @api.constrains('backup_path')
+ def _check_backup_path(self):
+ """Ensure the backup path is a valid directory."""
+ if self.backup_path and not os.path.isdir(self.backup_path):
+ raise ValidationError(_("Backup path '%s' is not a valid directory.") % self.backup_path)
+
+ def _generate_backup(self):
+ """Generate a backup for this record with real-time progress."""
+ if self.backup_destination == 'local' and not self.backup_path:
+ raise UserError(_("Backup path is required for local storage."))
+
+ backup_time = datetime.datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S")
+ backup_filename = f"{self.db_name}_{backup_time}.{self.backup_format}"
+ self.backup_filename = backup_filename
+ backup_file = os.path.join(self.backup_path, backup_filename)
+ self.backup_progress = 0
+
+ try:
+ os.makedirs(self.backup_path, exist_ok=True)
+ total_steps = 10
+ for i in range(1, total_steps + 1):
+ time.sleep(1) # Simulate progress
+ self.backup_progress = int((i / total_steps) * 100)
+ self.backup_progress = 100
+ with open(backup_file, "wb") as f:
+ db.dump_db(self.db_name, f, self.backup_format)
+
+ if self.auto_remove:
+ self._remove_old_backups()
+
+ self.env['db.backup.log'].create({
+ 'db_config_id': self.id,
+ 'status': 'success',
+ 'details': "Backup completed successfully.",
+ 'file_path': backup_file,
+ })
+ self.last_backup_status = 'success'
+ except Exception as e:
+ self.backup_progress = 0
+ _logger.error("Backup failed for database %s: %s", self.db_name, e)
+ self.generated_exception = str(e)
+ self.env['db.backup.log'].create({
+ 'db_config_id': self.id,
+ 'status': 'failure',
+ 'details': str(e),
+ })
+ self.last_backup_status = 'failure'
+
+ def _remove_old_backups(self):
+ """Remove old backups based on the retention policy."""
+ if not self.auto_remove or not self.days_to_remove:
+ return
+ try:
+ now = datetime.datetime.utcnow()
+ for filename in os.listdir(self.backup_path):
+ file_path = os.path.join(self.backup_path, filename)
+ if os.path.isfile(file_path):
+ if filename.endswith(f".{self.backup_format}") and filename.startswith(self.db_name):
+ creation_time = datetime.datetime.fromtimestamp(os.path.getctime(file_path))
+ if (now - creation_time).days > self.days_to_remove:
+ os.remove(file_path)
+ except Exception as e:
+ _logger.error("Error removing old backups: %s", e)
+
+ def _schedule_auto_backup(self):
+ """Method to trigger automatic database backup for active records."""
+ active_records = self.search([('active', '=', True)])
+ for record in active_records:
+ try:
+ record._generate_backup()
+ _logger.info("Backup successfully created for database: %s", record.db_name)
+ except Exception as e:
+ _logger.error("Failed to create backup for database %s: %s", record.db_name, e)
+
+
+class DbBackupLog(models.Model):
+ _name = 'db.backup.log'
+ _description = 'Database Backup Log'
+
+ db_config_id = fields.Many2one('db.backup.configure', string="Configuration")
+ backup_date = fields.Datetime(string="Backup Date", default=fields.Datetime.now)
+ status = fields.Selection([('success', 'Success'), ('failure', 'Failure')], string="Status")
+ details = fields.Text(string="Details")
+ file_path = fields.Char(string="File Path")
+ file_size = fields.Float(string="File Size (MB)", compute="_compute_file_size")
+
+ def _compute_file_size(self):
+ for record in self:
+ if record.file_path and os.path.isfile(record.file_path):
+ record.file_size = os.path.getsize(record.file_path) / (1024 * 1024)
+ else:
+ record.file_size = 0
+
+ def download_backup(self):
+ """Download the backup file."""
+ self.ensure_one()
+ if self.file_path and os.path.isfile(self.file_path):
+ return {
+ 'type': 'ir.actions.act_url',
+ 'url': f'/web/content?model=db.backup.log&id={self.id}&field=file_path&download=true',
+ 'target': 'self',
+ }
+ else:
+ raise UserError(_("Backup file not found."))
+
+ def delete_backup(self):
+ """Delete the backup file."""
+ self.ensure_one()
+ if self.file_path and os.path.isfile(self.file_path):
+ os.remove(self.file_path)
+ self.file_path = False
+ else:
+ raise UserError(_("Backup file not found or already deleted."))
diff --git a/My_Auto_Backup/security/ir.model.access.csv b/My_Auto_Backup/security/ir.model.access.csv
new file mode 100644
index 000000000..2eb9976b0
--- /dev/null
+++ b/My_Auto_Backup/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_db_backup_configure,db.backup.configure,model_db_backup_configure,,1,1,1,1
+access_db_backup_log,db.backup.log,model_db_backup_log,,1,1,1,1
diff --git a/My_Auto_Backup/static/description/assets/icons/check.png b/My_Auto_Backup/static/description/assets/icons/check.png
new file mode 100644
index 000000000..c8e85f51d
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/check.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/chevron.png b/My_Auto_Backup/static/description/assets/icons/chevron.png
new file mode 100644
index 000000000..2089293d6
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/chevron.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/cogs.png b/My_Auto_Backup/static/description/assets/icons/cogs.png
new file mode 100644
index 000000000..95d0bad62
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/cogs.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/consultation.png b/My_Auto_Backup/static/description/assets/icons/consultation.png
new file mode 100644
index 000000000..8319d4baa
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/consultation.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/ecom-black.png b/My_Auto_Backup/static/description/assets/icons/ecom-black.png
new file mode 100644
index 000000000..a9385ff13
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/ecom-black.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/education-black.png b/My_Auto_Backup/static/description/assets/icons/education-black.png
new file mode 100644
index 000000000..3eb09b27b
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/education-black.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/hotel-black.png b/My_Auto_Backup/static/description/assets/icons/hotel-black.png
new file mode 100644
index 000000000..130f613be
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/hotel-black.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/license.png b/My_Auto_Backup/static/description/assets/icons/license.png
new file mode 100644
index 000000000..a5869797e
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/license.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/lifebuoy.png b/My_Auto_Backup/static/description/assets/icons/lifebuoy.png
new file mode 100644
index 000000000..658d56ccc
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/lifebuoy.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/manufacturing-black.png b/My_Auto_Backup/static/description/assets/icons/manufacturing-black.png
new file mode 100644
index 000000000..697eb0e9f
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/manufacturing-black.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/pos-black.png b/My_Auto_Backup/static/description/assets/icons/pos-black.png
new file mode 100644
index 000000000..97c0f90c1
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/pos-black.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/puzzle.png b/My_Auto_Backup/static/description/assets/icons/puzzle.png
new file mode 100644
index 000000000..65cf854e7
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/puzzle.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/restaurant-black.png b/My_Auto_Backup/static/description/assets/icons/restaurant-black.png
new file mode 100644
index 000000000..4a35eb939
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/restaurant-black.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/service-black.png b/My_Auto_Backup/static/description/assets/icons/service-black.png
new file mode 100644
index 000000000..301ab51cb
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/service-black.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/trading-black.png b/My_Auto_Backup/static/description/assets/icons/trading-black.png
new file mode 100644
index 000000000..9398ba2f1
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/trading-black.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/training.png b/My_Auto_Backup/static/description/assets/icons/training.png
new file mode 100644
index 000000000..884ca024d
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/training.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/update.png b/My_Auto_Backup/static/description/assets/icons/update.png
new file mode 100644
index 000000000..ecbc5a01a
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/update.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/user.png b/My_Auto_Backup/static/description/assets/icons/user.png
new file mode 100644
index 000000000..6ffb23d9f
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/user.png differ
diff --git a/My_Auto_Backup/static/description/assets/icons/wrench.png b/My_Auto_Backup/static/description/assets/icons/wrench.png
new file mode 100644
index 000000000..6c04dea0f
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/icons/wrench.png differ
diff --git a/My_Auto_Backup/static/description/assets/screenshots/Capture.JPG b/My_Auto_Backup/static/description/assets/screenshots/Capture.JPG
new file mode 100644
index 000000000..3d8adb2b8
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/screenshots/Capture.JPG differ
diff --git a/My_Auto_Backup/static/description/assets/screenshots/Capture1.JPG b/My_Auto_Backup/static/description/assets/screenshots/Capture1.JPG
new file mode 100644
index 000000000..a7bffb32e
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/screenshots/Capture1.JPG differ
diff --git a/My_Auto_Backup/static/description/assets/screenshots/asdad.JPG b/My_Auto_Backup/static/description/assets/screenshots/asdad.JPG
new file mode 100644
index 000000000..40315a96f
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/screenshots/asdad.JPG differ
diff --git a/My_Auto_Backup/static/description/assets/screenshots/asdasd.JPG b/My_Auto_Backup/static/description/assets/screenshots/asdasd.JPG
new file mode 100644
index 000000000..3452bdbfc
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/screenshots/asdasd.JPG differ
diff --git a/My_Auto_Backup/static/description/assets/screenshots/asdasdasd.JPG b/My_Auto_Backup/static/description/assets/screenshots/asdasdasd.JPG
new file mode 100644
index 000000000..daa328e74
Binary files /dev/null and b/My_Auto_Backup/static/description/assets/screenshots/asdasdasd.JPG differ
diff --git a/My_Auto_Backup/static/description/banner.gif b/My_Auto_Backup/static/description/banner.gif
new file mode 100644
index 000000000..4a1575578
Binary files /dev/null and b/My_Auto_Backup/static/description/banner.gif differ
diff --git a/My_Auto_Backup/static/description/banner.jpg b/My_Auto_Backup/static/description/banner.jpg
new file mode 100644
index 000000000..4f8a76451
Binary files /dev/null and b/My_Auto_Backup/static/description/banner.jpg differ
diff --git a/My_Auto_Backup/static/description/icon.png b/My_Auto_Backup/static/description/icon.png
new file mode 100644
index 000000000..bd3548231
Binary files /dev/null and b/My_Auto_Backup/static/description/icon.png differ
diff --git a/My_Auto_Backup/static/description/index.html b/My_Auto_Backup/static/description/index.html
new file mode 100644
index 000000000..430976e8a
--- /dev/null
+++ b/My_Auto_Backup/static/description/index.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+ Community
+
+
+ Enterprise
+
+
+ Odoo.sh
+
+
+
+
+
+
+
+
+
+
+ Screenshots
+
+
+
+
+
+ Database Backup Configuration Menu
+
+ Got Setting --> Technical --> Backup Configuration to configure backups
+

+
+
+
+
+ Steps to Configure Database Backup
+
+ - Enable Developer Mode in your Odoo instance.
+ - Navigate to
Settings --> Technical --> Database Backup under Configurations.
+ - Click on Create to add a new database backup configuration.
+ - Fill in the required fields:
+
+ - Host: Enter the host of your database.
+ - Database Name: Specify the actual database you want to back up.
+ - Backup Type: Choose the type of backup (e.g., full, incremental).
+ - Destination: Enter the directory path for the backup. Ensure the folder permissions are set correctly using
chown odoo:odoo.
+
+
+ - Save the configuration.
+ - Go to
Scheduled Actions and locate the cron job named Backup: Automatic Database Backup.
+ - Adjust the cron job settings to define how often backups should occur (e.g., daily, weekly).
+ - Test the backup configuration to ensure it works as expected.
+
+
+
+
diff --git a/My_Auto_Backup/views/db_backup.xml b/My_Auto_Backup/views/db_backup.xml
new file mode 100644
index 000000000..56a4c4208
--- /dev/null
+++ b/My_Auto_Backup/views/db_backup.xml
@@ -0,0 +1,125 @@
+
+
+
+
+ db.backup.configure.tree
+ db.backup.configure
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ db.backup.configure.form
+ db.backup.configure
+
+
+
+
+
+
+
+ db.backup.log.tree
+ db.backup.log
+
+
+
+
+
+
+
+
+
+
+
+
+
+ db.backup.log.form
+ db.backup.log
+
+
+
+
+
+
+
+ Database Backup Configurations
+ db.backup.configure
+ tree,form
+
+
+ Click to create a new database backup configuration.
+
+
+
+
+
+
+ Backup Logs
+ db.backup.log
+ tree,form
+
+
+ No backup logs yet. Configure backups and start the process.
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yuthon_hr_attendance/views/resource_calendar_leaves_views.xml b/yuthon_hr_attendance/views/resource_calendar_leaves_views.xml
index d6e75540c..54b6e0d04 100644
--- a/yuthon_hr_attendance/views/resource_calendar_leaves_views.xml
+++ b/yuthon_hr_attendance/views/resource_calendar_leaves_views.xml
@@ -90,10 +90,16 @@
-
+
+
+ date
+
+
+ date
+
diff --git a/yuthon_property/wizard/meter_price_wizard.py b/yuthon_property/wizard/meter_price_wizard.py
index 1bcc1eea5..92d40ca78 100644
--- a/yuthon_property/wizard/meter_price_wizard.py
+++ b/yuthon_property/wizard/meter_price_wizard.py
@@ -73,6 +73,7 @@ class MeterPriceWizard(models.TransientModel):
valid.write({
'unit': unit,
})
+ valid.onchange_receivables()
self.env['hydropower.logs'].create({
'meter_type': 'electric',
diff --git a/yuthon_property/wizard/task_push_wizard.py b/yuthon_property/wizard/task_push_wizard.py
index 740cdf276..5eb23e856 100644
--- a/yuthon_property/wizard/task_push_wizard.py
+++ b/yuthon_property/wizard/task_push_wizard.py
@@ -53,19 +53,19 @@ class TaskPushWizard(models.TransientModel):
original_amount = meter.archives_id.fixed_receivables
else:
original_amount = meter.receivables
-
- self.env['property.accounts.receivable'].create({
- 'contract_id': meter.contract_id.id,
- 'lessee_id': meter.lessee_id.id if meter.lessee_id else None,
- 'original_amount': original_amount,
- 'due_date': due_date,
- 'due_date_end': due_date_end,
- 'charging_range': f"{start_date.strftime('%Y-%m-%d')} 到 {(end_date).strftime('%Y-%m-%d')}",
- 'create_time': datetime.now(),
- 'remark': remark,
- 'management_type_id': management_type_id.id,
- })
- meter.is_push = True
+ if original_amount > 0:
+ self.env['property.accounts.receivable'].create({
+ 'contract_id': meter.contract_id.id,
+ 'lessee_id': meter.lessee_id.id if meter.lessee_id else None,
+ 'original_amount': original_amount,
+ 'due_date': due_date,
+ 'due_date_end': due_date_end,
+ 'charging_range': f"{start_date.strftime('%Y-%m-%d')} 到 {(end_date).strftime('%Y-%m-%d')}",
+ 'create_time': datetime.now(),
+ 'remark': remark,
+ 'management_type_id': management_type_id.id,
+ })
+ meter.is_push = True
if self.water_ids:
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '水费')], limit=1)
for water in self.water_ids:
@@ -78,19 +78,19 @@ class TaskPushWizard(models.TransientModel):
remark = '新合同,暂无需抄表'
else:
remark = str(start_date) + '到' + str(end_date)
-
- self.env['property.accounts.receivable'].create({
- 'contract_id': water.contract_id.id,
- 'lessee_id': water.lessee_id.id if water.lessee_id else None,
- 'original_amount': water.water_id.fixed_receivables if water.water_id.fixed_receivables else water.receivables,
- 'due_date': due_date,
- 'due_date_end': due_date_end,
- 'charging_range': f"{start_date.strftime('%Y-%m-%d')} 到 {(end_date).strftime('%Y-%m-%d')}",
- 'create_time': datetime.now(),
- 'remark': remark,
- 'management_type_id': management_type_id.id,
- })
- water.is_push = True
+ if water.water_id.fixed_receivables > 0 or water.receivables > 0:
+ self.env['property.accounts.receivable'].create({
+ 'contract_id': water.contract_id.id,
+ 'lessee_id': water.lessee_id.id if water.lessee_id else None,
+ 'original_amount': water.water_id.fixed_receivables if water.water_id.fixed_receivables else water.receivables,
+ 'due_date': due_date,
+ 'due_date_end': due_date_end,
+ 'charging_range': f"{start_date.strftime('%Y-%m-%d')} 到 {(end_date).strftime('%Y-%m-%d')}",
+ 'create_time': datetime.now(),
+ 'remark': remark,
+ 'management_type_id': management_type_id.id,
+ })
+ water.is_push = True
def _get_date_range_remake(self, meter_date, contract_start):
meter_year = meter_date.year
diff --git a/yuthon_property/wizard/water_price_wizard.py b/yuthon_property/wizard/water_price_wizard.py
index ab86a4cb3..be3b8b71b 100644
--- a/yuthon_property/wizard/water_price_wizard.py
+++ b/yuthon_property/wizard/water_price_wizard.py
@@ -72,6 +72,7 @@ class WaterPriceWizard(models.TransientModel):
valid.write({
'unit': unit,
})
+ valid.onchange_receivables()
self.env['hydropower.logs'].create({
'meter_type': 'water',