diff --git a/product_expiry_configurable/README.rst b/product_expiry_configurable/README.rst index e777f6e1499..394fe428074 100644 --- a/product_expiry_configurable/README.rst +++ b/product_expiry_configurable/README.rst @@ -14,13 +14,13 @@ Product Expiry Configurable :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproduct--attribute-lightgray.png?logo=github - :target: https://github.com/OCA/product-attribute/tree/14.0/product_expiry_configurable + :target: https://github.com/OCA/product-attribute/tree/16.0/product_expiry_configurable :alt: OCA/product-attribute .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/product-attribute-14-0/product-attribute-14-0-product_expiry_configurable + :target: https://translation.odoo-community.org/projects/product-attribute-16-0/product-attribute-16-0-product_expiry_configurable :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/135/14.0 + :target: https://runbot.odoo-community.org/runbot/135/16.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -43,7 +43,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -56,6 +56,7 @@ Contributors * `CreuBlanca `_: * Alba Riera +* Denis Roussel Maintainers ~~~~~~~~~~~ @@ -70,6 +71,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/product-attribute `_ project on GitHub. +This module is part of the `OCA/product-attribute `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/product_expiry_configurable/__manifest__.py b/product_expiry_configurable/__manifest__.py index dc244d584d8..50f9d86efac 100644 --- a/product_expiry_configurable/__manifest__.py +++ b/product_expiry_configurable/__manifest__.py @@ -6,7 +6,7 @@ "summary": """ This model allows setting expiry times on category and to use the 'end_of_life' date for the computation of lot dates""", - "version": "14.0.1.0.0", + "version": "16.0.1.0.0", "license": "AGPL-3", "author": "Odoo Community Association (OCA)", "website": "https://github.com/OCA/product-attribute", @@ -14,8 +14,6 @@ "data": [ "data/product_expiry_data.xml", "views/product_category.xml", - "views/product_template.xml", - "views/stock_production_lot.xml", + "views/stock_lot.xml", ], - "demo": [], } diff --git a/product_expiry_configurable/data/product_expiry_data.xml b/product_expiry_configurable/data/product_expiry_data.xml index 0c68c92827f..af325c2a901 100644 --- a/product_expiry_configurable/data/product_expiry_data.xml +++ b/product_expiry_configurable/data/product_expiry_data.xml @@ -1,79 +1,62 @@ - - + - - Expiry Date Reached - default - - fa-tasks - 0 - + + Expiry Date Reached + default + stock.lot + fa-tasks + 0 + - - Product Removal Date Reached - - - - code - 1 - days - -1 - - model._expiry_date_exceeded(date_field='removal_date') - - + + Product Removal Date Reached + + + + code + 1 + days + -1 + + model._expiry_date_exceeded(date_field='removal_date') + + - - Product Use Date Reached - - - - code - 1 - days - -1 - - model._expiry_date_exceeded(date_field='use_date') - - + + Product Use Date Reached + + + + code + 1 + days + -1 + + model._expiry_date_exceeded(date_field='use_date') + + - - Product Expiration Date Reached - - - - code - 1 - days - -1 - - model._expiry_date_exceeded(date_field='expiration_date') - - + + Product Expiration Date Reached + + + + code + 1 + days + -1 + + model._expiry_date_exceeded(date_field='expiration_date') + + - diff --git a/product_expiry_configurable/migrations/14.0.1.0.0/noupdate_changes.xml b/product_expiry_configurable/migrations/14.0.1.0.0/noupdate_changes.xml deleted file mode 100644 index 7e27e7a5e17..00000000000 --- a/product_expiry_configurable/migrations/14.0.1.0.0/noupdate_changes.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - Product Expiration Date Reached - model._expiry_date_exceeded(date_field='expiration_date') - - diff --git a/product_expiry_configurable/migrations/14.0.1.0.0/post-migration.py b/product_expiry_configurable/migrations/14.0.1.0.0/post-migration.py deleted file mode 100644 index 26b93a1b7a4..00000000000 --- a/product_expiry_configurable/migrations/14.0.1.0.0/post-migration.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2022 CreuBlanca -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openupgradelib import openupgrade - - -@openupgrade.migrate() -def migrate(env, version): - openupgrade.load_data( - env.cr, - "product_expiry_configurable", - "migrations/14.0.1.0.0/noupdate_changes.xml", - ) diff --git a/product_expiry_configurable/migrations/14.0.1.0.0/pre-migration.py b/product_expiry_configurable/migrations/14.0.1.0.0/pre-migration.py deleted file mode 100644 index 2157eda119e..00000000000 --- a/product_expiry_configurable/migrations/14.0.1.0.0/pre-migration.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2022 CreuBlanca -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openupgradelib import openupgrade - -_xmlid_renames = [ - # rename suffix: - ( - "product_expiry_configurable.ir_cron_product_expiry_life_date", - "product_expiry_configurable.ir_cron_product_expiry_expiration_date", - ), -] -_column_renames = { - "product_category": [ - ("specific_life_time", "specific_expiration_time"), - ], - "product_template": [ - ("specific_life_time", "specific_expiration_time"), - ], - "stock_production_lot": [ - ("life_date_reminded", "expiration_date_reminded"), - ], -} - - -@openupgrade.migrate() -def migrate(env, version): - openupgrade.rename_xmlids(env.cr, _xmlid_renames) - openupgrade.rename_columns(env.cr, _column_renames) - openupgrade.logged_query( - env.cr, - """ - UPDATE product_category - SET specific_compute_dates_from = 'expiration_date' - WHERE specific_compute_dates_from = 'life_date' - """, - ) - openupgrade.logged_query( - env.cr, - """ - UPDATE product_template - SET specific_compute_dates_from = 'expiration_date' - WHERE specific_compute_dates_from = 'life_date' - """, - ) diff --git a/product_expiry_configurable/models/__init__.py b/product_expiry_configurable/models/__init__.py index 2f12940ee5c..10bdded4812 100644 --- a/product_expiry_configurable/models/__init__.py +++ b/product_expiry_configurable/models/__init__.py @@ -1,3 +1,3 @@ -from . import stock_production_lot +from . import stock_lot from . import product_template from . import product_category diff --git a/product_expiry_configurable/models/product_category.py b/product_expiry_configurable/models/product_category.py index bfe94000d78..053a2c5d24f 100644 --- a/product_expiry_configurable/models/product_category.py +++ b/product_expiry_configurable/models/product_category.py @@ -1,6 +1,5 @@ # Copyright 2022 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - from odoo import api, fields, models @@ -8,37 +7,31 @@ class ProductCategory(models.Model): _inherit = "product.category" - compute_dates_from = fields.Selection( - selection=[ - ("current_date", "Current Date"), - ("expiration_date", "Expiration Time Date"), - ], - help="If current_date is selected, the dates will be computed taking " - "as reference the current date when the lot is created." - "Whereas if expiration_date is selected, " - "the dates will be computed taking as reference the lot's expiration_date.", - compute="_compute_compute_dates_from", - ) - specific_compute_dates_from = fields.Selection( - selection=[ - ("current_date", "Current Date"), - ("expiration_date", "Expiration Time Date"), - ], - help="If not provided, the one defined on the parent is used.", - ) - parent_compute_dates_from = fields.Selection( - selection=[ - ("current_date", "Current Date"), - ("expiration_date", "Expiration Time Date"), - ], - compute="_compute_parent_compute_dates_from", + # This is the // as Odoo core field in product model + use_expiration_date = fields.Boolean( + compute="_compute_use_expiration_date", + store=True, + readonly=False, ) + + @api.depends("parent_id") + def _compute_use_expiration_date(self): + """ + Change the value of use_expiration_date field + when parent + """ + for category in self: + category.use_expiration_date = category.parent_id.use_expiration_date + expiration_time = fields.Integer( string="Product Expiration Time", help="Number of days before the goods" " may become dangerous and must not be consumed. " "It will be computed on the lot/serial number.", compute="_compute_date_fields", + store=True, + readonly=False, + recursive=True, ) use_time = fields.Integer( string="Product Use Time", @@ -46,118 +39,41 @@ class ProductCategory(models.Model): "without being dangerous yet." " It will be computed using the lot/serial number.", compute="_compute_date_fields", - ) - removal_time = fields.Integer( - string="Product Removal Time", - help="Number of days before the goods should be removed from the stock. " - "It will be computed on the lot/serial number.", - compute="_compute_date_fields", + store=True, + readonly=False, + recursive=True, ) alert_time = fields.Integer( string="Product Alert Time", help="Number of days before an alert should be raised " "on the lot/serial number.", compute="_compute_date_fields", + store=True, + readonly=False, + recursive=True, ) - specific_expiration_time = fields.Integer( - string="Specific Product Expiration Time", - help="Number of days before the goods may become dangerous " - "and must not be consumed. " - "It will be computed on the lot/serial number." - " If not provided, the one defined on the parent is used.", - ) - specific_use_time = fields.Integer( - string="Specific Product Use Time", - help="Number of days before the goods starts deteriorating, " - "without being dangerous yet. " - "It will be computed using the lot/serial number." - " If not provided, the one defined on the parent is used.", - ) - specific_removal_time = fields.Integer( - string="Specific Product Removal Time", - help="Number of days before the goods should be removed from the stock." - " It will be computed on the lot/serial number." - " If not provided, the one defined on the parent is used.", - ) - specific_alert_time = fields.Integer( - string="Specific Product Alert Time", - help="Number of days before an alert should be raised on the lot/serial number." - " If not provided, the one defined on the parent is used.", - ) - - parent_expiration_time = fields.Integer( - string="Parent Product Expiration Time", - help="Number of days before the goods may become dangerous and must not be consumed. " - "It will be computed on the lot/serial number.", - compute="_compute_parent_date_fields", - ) - parent_use_time = fields.Integer( - string="Parent Product Use Time", - help="Number of days before the goods starts deteriorating," - " without being dangerous yet. " - "It will be computed using the lot/serial number.", - compute="_compute_parent_date_fields", - ) - parent_removal_time = fields.Integer( - string="Parent Product Removal Time", - help="Number of days before the goods should be removed from the stock. " - "It will be computed on the lot/serial number.", - compute="_compute_parent_date_fields", - ) - parent_alert_time = fields.Integer( - string="Parent Product Alert Time", - help="Number of days before an alert should be raised on the lot/serial number.", - compute="_compute_parent_date_fields", - ) - - @api.depends("specific_compute_dates_from", "parent_compute_dates_from") - def _compute_compute_dates_from(self): - for rec in self: - rec.compute_dates_from = ( - rec.specific_compute_dates_from - or rec.parent_compute_dates_from - or "current_date" - ) - - @api.depends( - "parent_id.specific_compute_dates_from", "parent_id.parent_compute_dates_from" + removal_time = fields.Integer( + string="Product Removal Time", + help="Number of days before an alert should be raised " + "on the lot/serial number.", + compute="_compute_date_fields", + store=True, + readonly=False, + recursive=True, ) - def _compute_parent_compute_dates_from(self): - for rec in self: - parent_id = rec.parent_id - rec.parent_compute_dates_from = ( - parent_id.specific_compute_dates_from - or parent_id.parent_compute_dates_from - ) + @api.model def _get_date_fields(self): return ["expiration_time", "use_time", "removal_time", "alert_time"] - def _get_specific_and_parent_date_fields(self): - specific_dates = ["specific_%s" % date for date in self._get_date_fields()] - parent_dates = ["parent_%s" % date for date in self._get_date_fields()] - return specific_dates + parent_dates - - @api.depends(lambda r: r._get_specific_and_parent_date_fields()) + @api.depends(lambda r: r._get_parent_specific_and_parent_date_fields()) def _compute_date_fields(self): for rec in self: for date in rec._get_date_fields(): - specific_value = getattr(rec, "specific_%s" % date) - parent_value = getattr(rec, "parent_%s" % date) - setattr(rec, date, specific_value or parent_value) + parent_value = getattr(rec.parent_id, "%s" % date) + value = getattr(rec, "%s" % date) + setattr(rec, date, value or parent_value) def _get_parent_specific_and_parent_date_fields(self): - return [ - "parent_id.%s" % date_field - for date_field in self._get_specific_and_parent_date_fields() - ] - - @api.depends(lambda r: r._get_parent_specific_and_parent_date_fields()) - def _compute_parent_date_fields(self): - for rec in self: - for date in self._get_date_fields(): - parent_id = rec.parent_id - specific_value = getattr(parent_id, "specific_%s" % date) - parent_value = getattr(parent_id, "parent_%s" % date) - setattr(rec, "parent_%s" % date, specific_value or parent_value) + return ["parent_id.%s" % date_field for date_field in self._get_date_fields()] diff --git a/product_expiry_configurable/models/product_template.py b/product_expiry_configurable/models/product_template.py index 4c22c6183a3..5bc2ec0edc8 100644 --- a/product_expiry_configurable/models/product_template.py +++ b/product_expiry_configurable/models/product_template.py @@ -8,96 +8,35 @@ class ProductTemplate(models.Model): _inherit = "product.template" - compute_dates_from = fields.Selection( - selection=[ - ("current_date", "Current Date"), - ("expiration_date", "Expiration Date"), - ], - help="If current_date is selected, " - "the dates will be computed taking as reference " - "the current date when the lot is created." - "Whereas if expiration_time is selected," - " the dates will be computed taking as reference the lot's expiration_time.", - compute="_compute_compute_dates_from", - ) - specific_compute_dates_from = fields.Selection( - selection=[ - ("current_date", "Current Date"), - ("expiration_date", "Expiration Date"), - ], - help="If current_date is selected, " - "the dates will be computed taking as reference " - "the current date when the lot is created." - "Whereas if expiration_time is selected," - " the dates will be computed taking as reference the lot's expiration_date.", - ) - category_compute_dates_from = fields.Selection( - string="Category compute dates from", - help="If current_date is selected, " - "the dates will be computed taking as reference " - "the current date when the lot is created." - "Whereas if expiration_date is selected, " - "the dates will be computed taking as reference the lot's expiration_date.", - related="categ_id.compute_dates_from", + use_expiration_date = fields.Boolean( + compute="_compute_use_expiration_date", readonly=False, store=True ) - expiration_time = fields.Integer(compute="_compute_date_fields") - use_time = fields.Integer(compute="_compute_date_fields") - removal_time = fields.Integer(compute="_compute_date_fields") - alert_time = fields.Integer(compute="_compute_date_fields") - - specific_expiration_time = fields.Integer( - string="Specific Product Expiration Time", - help="Number of days before the goods may " - "become dangerous and must not be consumed. " - "It will be computed on the lot/serial number." - "If not provided, the one defined on the category is used.", + expiration_time = fields.Integer( + compute="_compute_date_fields", + store=True, + readonly=False, ) - specific_use_time = fields.Integer( - string="Specific Product Use Time", - help="Number of days before the goods starts deteriorating," - " without being dangerous yet. " - "It will be computed using the lot/serial number." - "If not provided, the one defined on the category is used.", + use_time = fields.Integer( + compute="_compute_date_fields", + store=True, + readonly=False, ) - specific_removal_time = fields.Integer( - string="Specific Product Removal Time", - help="Number of days before the goods should be removed from the stock. " - "It will be computed on the lot/serial number." - "If not provided, the one defined on the category is used.", + removal_time = fields.Integer( + compute="_compute_date_fields", + store=True, + readonly=False, ) - specific_alert_time = fields.Integer( - string="Specific Product Alert Time", - help="Number of days before an alert should be raised on the lot/serial number." - "If not provided, the one defined on the category is used.", + alert_time = fields.Integer( + compute="_compute_date_fields", + store=True, + readonly=False, ) - category_expiration_time = fields.Integer( - string="Category Product Expiration Time", - help="Number of days before the goods may become " - "dangerous and must not be consumed. " - "It will be computed on the lot/serial number.", - related="categ_id.expiration_time", - ) - category_use_time = fields.Integer( - string="Category Product Use Time", - help="Number of days before the goods starts deteriorating," - " without being dangerous yet." - " It will be computed using the lot/serial number.", - related="categ_id.use_time", - ) - category_removal_time = fields.Integer( - string="Category Product Removal Time", - help="Number of days before the goods should be removed from the stock. " - "It will be computed on the lot/serial number.", - related="categ_id.removal_time", - ) - category_alert_time = fields.Integer( - string="Category Product Alert Time", - help="Number of days before an alert should be raised " - "on the lot/serial number.", - related="categ_id.alert_time", - ) + @api.depends("categ_id.use_expiration_date") + def _compute_use_expiration_date(self): + for template in self: + template.use_expiration_date = template.categ_id.use_expiration_date def _get_date_fields(self): return ["expiration_time", "use_time", "removal_time", "alert_time"] @@ -111,15 +50,13 @@ def _compute_compute_dates_from(self): or "current_date" ) - def _get_specific_and_category_date_fields(self): - specific_dates = ["specific_%s" % date for date in self._get_date_fields()] - category_dates = ["category_%s" % date for date in self._get_date_fields()] - return specific_dates + category_dates + def _get_category_date_fields_depends(self): + return ["categ_id.%s" % date_field for date_field in self._get_date_fields()] - @api.depends(lambda r: r._get_specific_and_category_date_fields()) + @api.depends(lambda self: self._get_category_date_fields_depends()) def _compute_date_fields(self): for rec in self: for date in rec._get_date_fields(): - specific_value = getattr(rec, "specific_%s" % date) - category_value = getattr(rec, "category_%s" % date) - setattr(rec, date, specific_value or category_value) + # Set the field value from its category one + value = getattr(rec.categ_id, "%s" % date) + setattr(rec, date, value) diff --git a/product_expiry_configurable/models/stock_production_lot.py b/product_expiry_configurable/models/stock_lot.py similarity index 52% rename from product_expiry_configurable/models/stock_production_lot.py rename to product_expiry_configurable/models/stock_lot.py index db856adcdcb..8e8d0a0b673 100644 --- a/product_expiry_configurable/models/stock_production_lot.py +++ b/product_expiry_configurable/models/stock_lot.py @@ -6,9 +6,9 @@ from odoo import SUPERUSER_ID, _, api, fields, models -class StockProductionLot(models.Model): +class StockLot(models.Model): - _inherit = "stock.production.lot" + _inherit = "stock.lot" use_date_reminded = fields.Boolean(default=False) @@ -28,47 +28,6 @@ def _get_dates_from_expiration_date( res[field] = fields.Datetime.to_string(date) return res - def _get_dates(self, product_id=None): - """Returns dates based on number of days configured in current lot's product. - The date will be computed depending on the field 'compute_dates_from'""" - mapped_fields = { - "use_date": "use_time", - "removal_date": "removal_time", - "alert_date": "alert_time", - } - product = self.env["product.product"].browse(product_id) or self.product_id - res = dict.fromkeys(mapped_fields, False) - expiration_date = self.expiration_date or self.env.context.get( - "expiration_date" - ) - if product and product.tracking != "none": - if product.compute_dates_from == "expiration_date" and expiration_date: - res = self._get_dates_from_expiration_date( - product, mapped_fields, expiration_date - ) - elif product.compute_dates_from == "current_date": - res = super()._get_dates(product_id=product_id) - return res - - @api.onchange("expiration_date") - def _onchange_expiration_date(self): - if self.product_id.compute_dates_from == "expiration_date": - dates_dict = self._get_dates() - for field, value in dates_dict.items(): - setattr(self, field, value) - - @api.model - def create(self, vals): - dates = self.with_context( - expiration_date=vals.get("expiration_date") - )._get_dates( - vals.get("product_id") or self.env.context.get("default_product_id") - ) - for d in dates: - if not vals.get(d): - vals[d] = dates[d] - return super(StockProductionLot, self).create(vals) - def _search_quants_domain(self, expiry_lots): return [ ("lot_id", "in", expiry_lots.ids), @@ -86,7 +45,7 @@ def _expiry_date_exceeded(self, date_field=False): date_reminded_field = "%s_reminded" % date_field - expiry_lots = self.env["stock.production.lot"].search( + expiry_lots = self.env["stock.lot"].search( [(date_field, "<=", fields.Date.today()), (date_reminded_field, "=", False)] ) @@ -103,8 +62,8 @@ def _expiry_date_exceeded(self, date_field=False): "product_expiry_configurable.mail_activity_type_expiry_date_reached", user_id=lot.product_id.responsible_id.id or SUPERUSER_ID, note=_( - "The %s has been reached for this lot/serial number" % date_name - ), + "The {date_name} has been reached for this lot/serial number" + ).format(date_name=date_name), ) expiry_lots.write({date_reminded_field: True}) diff --git a/product_expiry_configurable/readme/CONTRIBUTORS.rst b/product_expiry_configurable/readme/CONTRIBUTORS.rst index a83040109c5..635c6d47495 100644 --- a/product_expiry_configurable/readme/CONTRIBUTORS.rst +++ b/product_expiry_configurable/readme/CONTRIBUTORS.rst @@ -1,3 +1,4 @@ * `CreuBlanca `_: * Alba Riera +* Denis Roussel diff --git a/product_expiry_configurable/static/description/index.html b/product_expiry_configurable/static/description/index.html index 277709d0508..01c411c0ce9 100644 --- a/product_expiry_configurable/static/description/index.html +++ b/product_expiry_configurable/static/description/index.html @@ -3,7 +3,7 @@ - + Product Expiry Configurable