From 186249ba66732e2e8380e9494bfede8fdee34a9b Mon Sep 17 00:00:00 2001 From: "jim.hoefnagels" Date: Wed, 5 Feb 2020 11:03:56 +0100 Subject: [PATCH] [IMP] stock_cycle_count: black, isort --- stock_cycle_count/__manifest__.py | 5 +- stock_cycle_count/models/stock_cycle_count.py | 108 +++--- .../models/stock_cycle_count_rule.py | 236 +++++++----- stock_cycle_count/models/stock_inventory.py | 49 ++- stock_cycle_count/models/stock_location.py | 112 +++--- stock_cycle_count/models/stock_move.py | 2 +- stock_cycle_count/models/stock_warehouse.py | 144 +++---- .../reports/report_stock_location_accuracy.py | 17 +- .../tests/test_stock_cycle_count.py | 355 +++++++++--------- 9 files changed, 563 insertions(+), 465 deletions(-) diff --git a/stock_cycle_count/__manifest__.py b/stock_cycle_count/__manifest__.py index de84db8e4..f04d6ee4b 100644 --- a/stock_cycle_count/__manifest__.py +++ b/stock_cycle_count/__manifest__.py @@ -4,12 +4,11 @@ { "name": "Stock Cycle Count", "summary": "Adds the capability to schedule cycle counts in a " - "warehouse through different rules defined by the user.", + "warehouse through different rules defined by the user.", "version": "12.0.1.0.1", "development_status": "Mature", "maintainers": ["lreficent"], - "author": "Eficent, " - "Odoo Community Association (OCA)", + "author": "Eficent, " "Odoo Community Association (OCA)", "website": "https://github.com/OCA/stock-logistics-warehouse", "category": "Warehouse Management", "depends": [ diff --git a/stock_cycle_count/models/stock_cycle_count.py b/stock_cycle_count/models/stock_cycle_count.py index ea0faf792..f03efc249 100644 --- a/stock_cycle_count/models/stock_cycle_count.py +++ b/stock_cycle_count/models/stock_cycle_count.py @@ -2,66 +2,77 @@ # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError class StockCycleCount(models.Model): - _name = 'stock.cycle.count' + _name = "stock.cycle.count" _description = "Stock Cycle Counts" - _inherit = 'mail.thread' + _inherit = "mail.thread" _order = "id desc" @api.model def _default_company(self): - company_id = self.env['res.company']._company_default_get(self._name) + company_id = self.env["res.company"]._company_default_get(self._name) return company_id - name = fields.Char(string='Name', readonly=True) + name = fields.Char(string="Name", readonly=True) location_id = fields.Many2one( - comodel_name='stock.location', string='Location', + comodel_name="stock.location", + string="Location", required=True, - readonly=True, states={'draft': [('readonly', False)]}, + readonly=True, + states={"draft": [("readonly", False)]}, ) responsible_id = fields.Many2one( - comodel_name='res.users', string='Assigned to', - readonly=True, states={'draft': [('readonly', False)]}, - track_visibility='onchange', + comodel_name="res.users", + string="Assigned to", + readonly=True, + states={"draft": [("readonly", False)]}, + track_visibility="onchange", ) date_deadline = fields.Date( - string='Required Date', - readonly=True, states={'draft': [('readonly', False)]}, - track_visibility='onchange', + string="Required Date", + readonly=True, + states={"draft": [("readonly", False)]}, + track_visibility="onchange", ) cycle_count_rule_id = fields.Many2one( - comodel_name='stock.cycle.count.rule', string='Cycle count rule', + comodel_name="stock.cycle.count.rule", + string="Cycle count rule", required=True, - readonly=True, states={'draft': [('readonly', False)]}, - track_visibility='onchange', + readonly=True, + states={"draft": [("readonly", False)]}, + track_visibility="onchange", ) - state = fields.Selection(selection=[ - ('draft', 'Planned'), - ('open', 'Execution'), - ('cancelled', 'Cancelled'), - ('done', 'Done') - ], string='State', default='draft', track_visibility='onchange', + state = fields.Selection( + selection=[ + ("draft", "Planned"), + ("open", "Execution"), + ("cancelled", "Cancelled"), + ("done", "Done"), + ], + string="State", + default="draft", + track_visibility="onchange", ) stock_adjustment_ids = fields.One2many( - comodel_name='stock.inventory', inverse_name='cycle_count_id', - string='Inventory Adjustment', - track_visibility='onchange', - ) - inventory_adj_count = fields.Integer( - compute='_compute_inventory_adj_count', + comodel_name="stock.inventory", + inverse_name="cycle_count_id", + string="Inventory Adjustment", + track_visibility="onchange", ) + inventory_adj_count = fields.Integer(compute="_compute_inventory_adj_count") company_id = fields.Many2one( - comodel_name='res.company', string='Company', + comodel_name="res.company", + string="Company", required=True, default=_default_company, readonly=True, ) - @api.depends('stock_adjustment_ids') + @api.depends("stock_adjustment_ids") @api.multi def _compute_inventory_adj_count(self): for rec in self: @@ -69,46 +80,43 @@ class StockCycleCount(models.Model): @api.multi def do_cancel(self): - self.write({'state': 'cancelled'}) + self.write({"state": "cancelled"}) @api.multi def _prepare_inventory_adjustment(self): self.ensure_one() return { - 'name': 'INV/{}'.format(self.name), - 'cycle_count_id': self.id, - 'location_id': self.location_id.id, - 'exclude_sublocation': True + "name": "INV/{}".format(self.name), + "cycle_count_id": self.id, + "location_id": self.location_id.id, + "exclude_sublocation": True, } @api.model def create(self, vals): - vals['name'] = self.env['ir.sequence'].next_by_code( - 'stock.cycle.count') or '' + vals["name"] = self.env["ir.sequence"].next_by_code("stock.cycle.count") or "" return super(StockCycleCount, self).create(vals) @api.multi def action_create_inventory_adjustment(self): - if any([s != 'draft' for s in self.mapped('state')]): - raise UserError(_( - "You can only confirm cycle counts in state 'Planned'." - )) + if any([s != "draft" for s in self.mapped("state")]): + raise UserError(_("You can only confirm cycle counts in state 'Planned'.")) for rec in self: data = rec._prepare_inventory_adjustment() - self.env['stock.inventory'].create(data) - self.write({'state': 'open'}) + self.env["stock.inventory"].create(data) + self.write({"state": "open"}) return True @api.multi def action_view_inventory(self): - action = self.env.ref('stock.action_inventory_form') + action = self.env.ref("stock.action_inventory_form") result = action.read()[0] - result['context'] = {} - adjustment_ids = self.mapped('stock_adjustment_ids').ids + result["context"] = {} + adjustment_ids = self.mapped("stock_adjustment_ids").ids if len(adjustment_ids) > 1: - result['domain'] = [('id', 'in', adjustment_ids)] + result["domain"] = [("id", "in", adjustment_ids)] elif len(adjustment_ids) == 1: - res = self.env.ref('stock.view_inventory_form', False) - result['views'] = [(res and res.id or False, 'form')] - result['res_id'] = adjustment_ids and adjustment_ids[0] or False + res = self.env.ref("stock.view_inventory_form", False) + result["views"] = [(res and res.id or False, "form")] + result["res_id"] = adjustment_ids and adjustment_ids[0] or False return result diff --git a/stock_cycle_count/models/stock_cycle_count_rule.py b/stock_cycle_count/models/stock_cycle_count_rule.py index 59e80a6a3..2cb71963c 100644 --- a/stock_cycle_count/models/stock_cycle_count_rule.py +++ b/stock_cycle_count/models/stock_cycle_count_rule.py @@ -2,13 +2,14 @@ # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models, _ +from datetime import datetime, timedelta + +from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError -from datetime import timedelta, datetime class StockCycleCountRule(models.Model): - _name = 'stock.cycle.count.rule' + _name = "stock.cycle.count.rule" _description = "Stock Cycle Counts Rules" @api.multi @@ -19,131 +20,149 @@ class StockCycleCountRule(models.Model): @api.model def _selection_rule_types(self): return [ - ('periodic', _('Periodic')), - ('turnover', _('Value Turnover')), - ('accuracy', _('Minimum Accuracy')), - ('zero', _('Zero Confirmation'))] + ("periodic", _("Periodic")), + ("turnover", _("Value Turnover")), + ("accuracy", _("Minimum Accuracy")), + ("zero", _("Zero Confirmation")), + ] @api.multi - @api.constrains('rule_type', 'warehouse_ids') + @api.constrains("rule_type", "warehouse_ids") def _check_zero_rule(self): for rec in self: - if rec.rule_type == 'zero' and len(rec.warehouse_ids) > 1: + if rec.rule_type == "zero" and len(rec.warehouse_ids) > 1: raise ValidationError( - _('Zero confirmation rules can only have one warehouse ' - 'assigned.') + _( + "Zero confirmation rules can only have one warehouse " + "assigned." + ) + ) + if rec.rule_type == "zero": + zero_rule = self.search( + [ + ("rule_type", "=", "zero"), + ("warehouse_ids", "=", rec.warehouse_ids.id), + ] ) - if rec.rule_type == 'zero': - zero_rule = self.search([ - ('rule_type', '=', 'zero'), - ('warehouse_ids', '=', rec.warehouse_ids.id)]) if len(zero_rule) > 1: raise ValidationError( - _('You can only have one zero confirmation rule per ' - 'warehouse.') + _( + "You can only have one zero confirmation rule per " + "warehouse." + ) ) - @api.depends('rule_type') + @api.depends("rule_type") def _compute_rule_description(self): - if self.rule_type == 'periodic': - self.rule_description = _('Ensures that at least a defined number ' - 'of counts in a given period will ' - 'be run.') - elif self.rule_type == 'turnover': - self.rule_description = _('Schedules a count every time the total ' - 'turnover of a location exceeds the ' - 'threshold. This considers every ' - 'product going into/out of the location') - elif self.rule_type == 'accuracy': - self.rule_description = _('Schedules a count every time the ' - 'accuracy of a location goes under a ' - 'given threshold.') - elif self.rule_type == 'zero': - self.rule_description = _('Perform an Inventory Adjustment every ' - 'time a location in the warehouse runs ' - 'out of stock in order to confirm it is ' - 'truly empty.') + if self.rule_type == "periodic": + self.rule_description = _( + "Ensures that at least a defined number " + "of counts in a given period will " + "be run." + ) + elif self.rule_type == "turnover": + self.rule_description = _( + "Schedules a count every time the total " + "turnover of a location exceeds the " + "threshold. This considers every " + "product going into/out of the location" + ) + elif self.rule_type == "accuracy": + self.rule_description = _( + "Schedules a count every time the " + "accuracy of a location goes under a " + "given threshold." + ) + elif self.rule_type == "zero": + self.rule_description = _( + "Perform an Inventory Adjustment every " + "time a location in the warehouse runs " + "out of stock in order to confirm it is " + "truly empty." + ) else: - self.rule_description = _('(No description provided.)') + self.rule_description = _("(No description provided.)") @api.multi - @api.constrains('periodic_qty_per_period', 'periodic_count_period') + @api.constrains("periodic_qty_per_period", "periodic_count_period") def _check_negative_periodic(self): for rec in self: if rec.periodic_qty_per_period < 1: raise ValidationError( - _('You cannot define a negative or null number of counts ' - 'per period.') + _( + "You cannot define a negative or null number of counts " + "per period." + ) ) if rec.periodic_count_period < 0: - raise ValidationError( - _('You cannot define a negative period.') - ) + raise ValidationError(_("You cannot define a negative period.")) - @api.onchange('location_ids') + @api.onchange("location_ids") def _onchange_locaton_ids(self): """Get the warehouses for the selected locations.""" wh_ids = [] for loc in self.location_ids: wh_ids.append(loc.get_warehouse().id) wh_ids = list(set(wh_ids)) - self.warehouse_ids = self.env['stock.warehouse'].browse(wh_ids) + self.warehouse_ids = self.env["stock.warehouse"].browse(wh_ids) name = fields.Char(required=True) rule_type = fields.Selection( - selection="_selection_rule_types", - string='Type of rule', required=True, + selection="_selection_rule_types", string="Type of rule", required=True ) rule_description = fields.Char( - string='Rule Description', compute='_compute_rule_description', + string="Rule Description", compute="_compute_rule_description" ) active = fields.Boolean(default=True) - periodic_qty_per_period = fields.Integer( - string='Counts per period', default=1, - ) - periodic_count_period = fields.Integer(string='Period in days') + periodic_qty_per_period = fields.Integer(string="Counts per period", default=1) + periodic_count_period = fields.Integer(string="Period in days") turnover_inventory_value_threshold = fields.Float( - string='Turnover Inventory Value Threshold', + string="Turnover Inventory Value Threshold" ) currency_id = fields.Many2one( - comodel_name='res.currency', string='Currency', - compute='_compute_currency_id', + comodel_name="res.currency", string="Currency", compute="_compute_currency_id" ) accuracy_threshold = fields.Float( - string='Minimum Accuracy Threshold', digits=(3, 2), + string="Minimum Accuracy Threshold", digits=(3, 2) ) apply_in = fields.Selection( - string='Apply this rule in:', - selection=[('warehouse', 'Selected warehouses'), - ('location', 'Selected Location Zones.')], - default='warehouse', + string="Apply this rule in:", + selection=[ + ("warehouse", "Selected warehouses"), + ("location", "Selected Location Zones."), + ], + default="warehouse", ) warehouse_ids = fields.Many2many( - comodel_name='stock.warehouse', - relation='warehouse_cycle_count_rule_rel', column1='rule_id', - column2='warehouse_id', string='Warehouses where applied', + comodel_name="stock.warehouse", + relation="warehouse_cycle_count_rule_rel", + column1="rule_id", + column2="warehouse_id", + string="Warehouses where applied", ) location_ids = fields.Many2many( - comodel_name='stock.location', - relation='location_cycle_count_rule_rel', column1='rule_id', - column2='location_id', string='Zones where applied', + comodel_name="stock.location", + relation="location_cycle_count_rule_rel", + column1="rule_id", + column2="location_id", + string="Zones where applied", ) def compute_rule(self, locs): - if self.rule_type == 'periodic': + if self.rule_type == "periodic": proposed_cycle_counts = self._compute_rule_periodic(locs) - elif self.rule_type == 'turnover': + elif self.rule_type == "turnover": proposed_cycle_counts = self._compute_rule_turnover(locs) - elif self.rule_type == 'accuracy': + elif self.rule_type == "accuracy": proposed_cycle_counts = self._compute_rule_accuracy(locs) return proposed_cycle_counts @api.model def _propose_cycle_count(self, date, location): cycle_count = { - 'date': fields.Datetime.from_string(date), - 'location': location, - 'rule_type': self + "date": fields.Datetime.from_string(date), + "location": location, + "rule_type": self, } return cycle_count @@ -151,22 +170,34 @@ class StockCycleCountRule(models.Model): def _compute_rule_periodic(self, locs): cycle_counts = [] for loc in locs: - latest_inventory_date = self.env['stock.inventory'].search([ - ('location_id', '=', loc.id), - ('state', 'in', ['confirm', 'done', 'draft'])], - order="date desc", limit=1).date + latest_inventory_date = ( + self.env["stock.inventory"] + .search( + [ + ("location_id", "=", loc.id), + ("state", "in", ["confirm", "done", "draft"]), + ], + order="date desc", + limit=1, + ) + .date + ) if latest_inventory_date: try: - period = self.periodic_count_period / \ - self.periodic_qty_per_period + period = self.periodic_count_period / self.periodic_qty_per_period next_date = fields.Datetime.from_string( - latest_inventory_date) + timedelta(days=period) + latest_inventory_date + ) + timedelta(days=period) if next_date < datetime.today(): next_date = datetime.today() except Exception as e: raise UserError( - _('Error found determining the frequency of periodic ' - 'cycle count rule. %s') % str(e)) + _( + "Error found determining the frequency of periodic " + "cycle count rule. %s" + ) + % str(e) + ) else: next_date = datetime.today() cycle_count = self._propose_cycle_count(next_date, loc) @@ -175,11 +206,15 @@ class StockCycleCountRule(models.Model): @api.model def _get_turnover_moves(self, location, date): - moves = self.env['stock.move'].search([ - '|', ('location_id', '=', location.id), - ('location_dest_id', '=', location.id), - ('date', '>', date), - ('state', '=', 'done')]) + moves = self.env["stock.move"].search( + [ + "|", + ("location_id", "=", location.id), + ("location_dest_id", "=", location.id), + ("date", ">", date), + ("state", "=", "done"), + ] + ) return moves @api.model @@ -192,9 +227,16 @@ class StockCycleCountRule(models.Model): def _compute_rule_turnover(self, locs): cycle_counts = [] for loc in locs: - last_inventories = self.env['stock.inventory'].search([ - ('location_id', '=', loc.id), - ('state', 'in', ['confirm', 'done', 'draft'])]).mapped('date') + last_inventories = ( + self.env["stock.inventory"] + .search( + [ + ("location_id", "=", loc.id), + ("state", "in", ["confirm", "done", "draft"]), + ] + ) + .mapped("date") + ) if last_inventories: latest_inventory = sorted(last_inventories, reverse=True)[0] moves = self._get_turnover_moves(loc, latest_inventory) @@ -204,16 +246,18 @@ class StockCycleCountRule(models.Model): turnover = self._compute_turnover(m) total_turnover += turnover try: - if total_turnover > \ - self.turnover_inventory_value_threshold: + if total_turnover > self.turnover_inventory_value_threshold: next_date = datetime.today() - cycle_count = self._propose_cycle_count(next_date, - loc) + cycle_count = self._propose_cycle_count(next_date, loc) cycle_counts.append(cycle_count) except Exception as e: - raise UserError(_( - 'Error found when comparing turnover with the ' - 'rule threshold. %s') % str(e)) + raise UserError( + _( + "Error found when comparing turnover with the " + "rule threshold. %s" + ) + % str(e) + ) else: next_date = datetime.today() cycle_count = self._propose_cycle_count(next_date, loc) diff --git a/stock_cycle_count/models/stock_inventory.py b/stock_cycle_count/models/stock_inventory.py index de84d1907..aeb3e7a98 100644 --- a/stock_cycle_count/models/stock_inventory.py +++ b/stock_cycle_count/models/stock_inventory.py @@ -2,41 +2,46 @@ # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError PERCENT = 100.0 class StockInventory(models.Model): - _inherit = 'stock.inventory' + _inherit = "stock.inventory" @api.multi @api.depends("state", "line_ids") def _compute_inventory_accuracy(self): for inv in self: - theoretical = sum(inv.line_ids.mapped( - lambda x: abs(x.theoretical_qty))) - abs_discrepancy = sum(inv.line_ids.mapped( - lambda x: abs(x.discrepancy_qty))) + theoretical = sum(inv.line_ids.mapped(lambda x: abs(x.theoretical_qty))) + abs_discrepancy = sum(inv.line_ids.mapped(lambda x: abs(x.discrepancy_qty))) if theoretical: inv.inventory_accuracy = max( - PERCENT * (theoretical - abs_discrepancy) / theoretical, - 0.0) - if not inv.line_ids and inv.state == 'done': + PERCENT * (theoretical - abs_discrepancy) / theoretical, 0.0 + ) + if not inv.line_ids and inv.state == "done": inv.inventory_accuracy = PERCENT cycle_count_id = fields.Many2one( - comodel_name='stock.cycle.count', string='Stock Cycle Count', - ondelete='restrict', readonly=True) + comodel_name="stock.cycle.count", + string="Stock Cycle Count", + ondelete="restrict", + readonly=True, + ) inventory_accuracy = fields.Float( - string='Accuracy', compute='_compute_inventory_accuracy', - digits=(3, 2), store=True, group_operator="avg") + string="Accuracy", + compute="_compute_inventory_accuracy", + digits=(3, 2), + store=True, + group_operator="avg", + ) def _update_cycle_state(self): for inv in self: - if inv.cycle_count_id and inv.state == 'done': - inv.cycle_count_id.state = 'done' + if inv.cycle_count_id and inv.state == "done": + inv.cycle_count_id.state = "done" return True @api.multi @@ -54,9 +59,15 @@ class StockInventory(models.Model): @api.multi def write(self, vals): for inventory in self: - if (inventory.cycle_count_id and 'state' not in vals.keys() and - inventory.state == 'draft'): + if ( + inventory.cycle_count_id + and "state" not in vals.keys() + and inventory.state == "draft" + ): raise UserError( - _('You cannot modify the configuration of an Inventory ' - 'Adjustment related to a Cycle Count.')) + _( + "You cannot modify the configuration of an Inventory " + "Adjustment related to a Cycle Count." + ) + ) return super(StockInventory, self).write(vals) diff --git a/stock_cycle_count/models/stock_location.py b/stock_cycle_count/models/stock_location.py index 1b6fd58d0..979f7211b 100644 --- a/stock_cycle_count/models/stock_location.py +++ b/stock_cycle_count/models/stock_location.py @@ -3,64 +3,66 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). import logging +from datetime import datetime from odoo import api, fields, models, tools from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT -from datetime import datetime + _logger = logging.getLogger(__name__) try: from statistics import mean - STATS_PATH = tools.find_in_path('statistics') + + STATS_PATH = tools.find_in_path("statistics") except (ImportError, IOError) as err: _logger.debug(err) class StockLocation(models.Model): - _inherit = 'stock.location' + _inherit = "stock.location" @api.multi def _compute_loc_accuracy(self): for rec in self: - history = self.env['stock.inventory'].search([ - ('location_id', '=', rec.id), ('state', '=', 'done')]) + history = self.env["stock.inventory"].search( + [("location_id", "=", rec.id), ("state", "=", "done")] + ) history = history.sorted(key=lambda r: r.write_date, reverse=True) if history: wh = rec.get_warehouse() - if wh.counts_for_accuracy_qty and \ - len(history) > wh.counts_for_accuracy_qty: + if ( + wh.counts_for_accuracy_qty + and len(history) > wh.counts_for_accuracy_qty + ): rec.loc_accuracy = mean( - history[:wh.counts_for_accuracy_qty].mapped( - 'inventory_accuracy')) + history[: wh.counts_for_accuracy_qty].mapped( + "inventory_accuracy" + ) + ) else: - rec.loc_accuracy = mean( - history.mapped('inventory_accuracy')) + rec.loc_accuracy = mean(history.mapped("inventory_accuracy")) zero_confirmation_disabled = fields.Boolean( - string='Disable Zero Confirmations', - help='Define whether this location will trigger a zero-confirmation ' - 'validation when a rule for its warehouse is defined to perform ' - 'zero-confirmations.', + string="Disable Zero Confirmations", + help="Define whether this location will trigger a zero-confirmation " + "validation when a rule for its warehouse is defined to perform " + "zero-confirmations.", ) cycle_count_disabled = fields.Boolean( - string='Exclude from Cycle Count', - help='Define whether the location is going to be cycle counted.', + string="Exclude from Cycle Count", + help="Define whether the location is going to be cycle counted.", ) qty_variance_inventory_threshold = fields.Float( - string='Acceptable Inventory Quantity Variance Threshold', + string="Acceptable Inventory Quantity Variance Threshold" ) loc_accuracy = fields.Float( - string='Inventory Accuracy', compute='_compute_loc_accuracy', - digits=(3, 2), + string="Inventory Accuracy", compute="_compute_loc_accuracy", digits=(3, 2) ) @api.multi def _get_zero_confirmation_domain(self): self.ensure_one() - domain = [ - ('location_id', '=', self.id), - ('quantity', '>', 0.0), - ] + domain = [("location_id", "=", self.id), ("quantity", ">", 0.0)] return domain @api.multi @@ -68,13 +70,14 @@ class StockLocation(models.Model): for rec in self: if not rec.zero_confirmation_disabled: wh = rec.get_warehouse() - rule_model = self.env['stock.cycle.count.rule'] - zero_rule = rule_model.search([ - ('rule_type', '=', 'zero'), - ('warehouse_ids', '=', wh.id)]) + rule_model = self.env["stock.cycle.count.rule"] + zero_rule = rule_model.search( + [("rule_type", "=", "zero"), ("warehouse_ids", "=", wh.id)] + ) if zero_rule: - quants = self.env['stock.quant'].search( - rec._get_zero_confirmation_domain()) + quants = self.env["stock.quant"].search( + rec._get_zero_confirmation_domain() + ) if not quants: rec.create_zero_confirmation_cycle_count() @@ -83,30 +86,41 @@ class StockLocation(models.Model): self.ensure_one() date = datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT) wh_id = self.get_warehouse().id - date_horizon = self.get_warehouse().get_horizon_date().strftime( - DEFAULT_SERVER_DATETIME_FORMAT) - counts_planned = self.env['stock.cycle.count'].search([ - ('date_deadline', '<', date_horizon), ('state', '=', 'draft'), - ('location_id', '=', self.id)]) + date_horizon = ( + self.get_warehouse() + .get_horizon_date() + .strftime(DEFAULT_SERVER_DATETIME_FORMAT) + ) + counts_planned = self.env["stock.cycle.count"].search( + [ + ("date_deadline", "<", date_horizon), + ("state", "=", "draft"), + ("location_id", "=", self.id), + ] + ) if counts_planned: - counts_planned.write({'state': 'cancelled'}) - rule = self.env['stock.cycle.count.rule'].search([ - ('rule_type', '=', 'zero'), ('warehouse_ids', '=', wh_id)]) - self.env['stock.cycle.count'].create({ - 'date_deadline': date, - 'location_id': self.id, - 'cycle_count_rule_id': rule.id, - 'state': 'draft' - }) + counts_planned.write({"state": "cancelled"}) + rule = self.env["stock.cycle.count.rule"].search( + [("rule_type", "=", "zero"), ("warehouse_ids", "=", wh_id)] + ) + self.env["stock.cycle.count"].create( + { + "date_deadline": date, + "location_id": self.id, + "cycle_count_rule_id": rule.id, + "state": "draft", + } + ) return True @api.multi def action_accuracy_stats(self): self.ensure_one() - action = self.env.ref('stock_cycle_count.act_accuracy_stats') + action = self.env.ref("stock_cycle_count.act_accuracy_stats") result = action.read()[0] - result['context'] = {"search_default_location_id": self.id} - new_domain = result['domain'][:-1] + \ - ", ('location_id', 'child_of', active_ids)]" - result['domain'] = new_domain + result["context"] = {"search_default_location_id": self.id} + new_domain = ( + result["domain"][:-1] + ", ('location_id', 'child_of', active_ids)]" + ) + result["domain"] = new_domain return result diff --git a/stock_cycle_count/models/stock_move.py b/stock_cycle_count/models/stock_move.py index 18026cbbb..05caeae95 100644 --- a/stock_cycle_count/models/stock_move.py +++ b/stock_cycle_count/models/stock_move.py @@ -7,7 +7,7 @@ from odoo import api, models class StockMove(models.Model): - _inherit = 'stock.move' + _inherit = "stock.move" @api.multi def _action_done(self): diff --git a/stock_cycle_count/models/stock_warehouse.py b/stock_cycle_count/models/stock_warehouse.py index ef1a5c23d..485943761 100644 --- a/stock_cycle_count/models/stock_warehouse.py +++ b/stock_cycle_count/models/stock_warehouse.py @@ -2,31 +2,34 @@ # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models -from datetime import datetime, timedelta import logging +from datetime import datetime, timedelta + +from odoo import api, fields, models _logger = logging.getLogger(__name__) class StockWarehouse(models.Model): - _inherit = 'stock.warehouse' + _inherit = "stock.warehouse" cycle_count_rule_ids = fields.Many2many( - comodel_name='stock.cycle.count.rule', - relation='warehouse_cycle_count_rule_rel', - column1='warehouse_id', - column2='rule_id', - string='Cycle Count Rules') + comodel_name="stock.cycle.count.rule", + relation="warehouse_cycle_count_rule_rel", + column1="warehouse_id", + column2="rule_id", + string="Cycle Count Rules", + ) cycle_count_planning_horizon = fields.Integer( - string='Cycle Count Planning Horizon (in days)', - help='Cycle Count planning horizon in days. Only the counts inside ' - 'the horizon will be created.') + string="Cycle Count Planning Horizon (in days)", + help="Cycle Count planning horizon in days. Only the counts inside " + "the horizon will be created.", + ) counts_for_accuracy_qty = fields.Integer( - string='Inventories for location accuracy calculation', + string="Inventories for location accuracy calculation", default=1, - help='Number of latest inventories used to calculate location ' - 'accuracy') + help="Number of latest inventories used to calculate location " "accuracy", + ) @api.multi def get_horizon_date(self): @@ -37,40 +40,42 @@ class StockWarehouse(models.Model): return date_horizon @api.model - def _get_cycle_count_locations_search_domain( - self, parent): - domain = [('parent_path', '=like', parent.parent_path + '%'), - ('cycle_count_disabled', '=', False)] + def _get_cycle_count_locations_search_domain(self, parent): + domain = [ + ("parent_path", "=like", parent.parent_path + "%"), + ("cycle_count_disabled", "=", False), + ] return domain @api.model def _search_cycle_count_locations(self, rule): - locations = self.env['stock.location'] - if rule.apply_in == 'warehouse': - locations = self.env['stock.location'].search( - self._get_cycle_count_locations_search_domain( - self.view_location_id)) - elif rule.apply_in == 'location': + locations = self.env["stock.location"] + if rule.apply_in == "warehouse": + locations = self.env["stock.location"].search( + self._get_cycle_count_locations_search_domain(self.view_location_id) + ) + elif rule.apply_in == "location": for loc in rule.location_ids: - locations += self.env['stock.location'].search( - self._get_cycle_count_locations_search_domain(loc)) + locations += self.env["stock.location"].search( + self._get_cycle_count_locations_search_domain(loc) + ) return locations @api.multi def _cycle_count_rules_to_compute(self): self.ensure_one() - rules = self.env['stock.cycle.count.rule'].search([ - ('rule_type', '!=', 'zero'), ('warehouse_ids', 'in', self.ids)]) + rules = self.env["stock.cycle.count.rule"].search( + [("rule_type", "!=", "zero"), ("warehouse_ids", "in", self.ids)] + ) return rules @api.model def _prepare_cycle_count(self, cycle_count_proposed): return { - 'date_deadline': cycle_count_proposed['date'], - 'location_id': cycle_count_proposed['location'].id, - 'cycle_count_rule_id': cycle_count_proposed[ - 'rule_type'].id, - 'state': 'draft' + "date_deadline": cycle_count_proposed["date"], + "location_id": cycle_count_proposed["location"].id, + "cycle_count_rule_id": cycle_count_proposed["rule_type"].id, + "state": "draft", } @api.multi @@ -86,43 +91,49 @@ class StockWarehouse(models.Model): if locations: proposed_cycle_counts.extend(rule.compute_rule(locations)) if proposed_cycle_counts: - locations = list(set([d['location'] for d in - proposed_cycle_counts])) + locations = list({d["location"] for d in proposed_cycle_counts}) for loc in locations: - proposed_for_loc = list(filter( - lambda x: x['location'] == loc, proposed_cycle_counts)) - earliest_date = min([d['date'] for d in proposed_for_loc]) - cycle_count_proposed = list(filter( - lambda x: x['date'] == earliest_date, - proposed_for_loc))[0] - domain = [('location_id', '=', loc.id), - ('state', 'in', ['draft'])] - existing_cycle_counts = self.env[ - 'stock.cycle.count'].search(domain) + proposed_for_loc = list( + filter(lambda x: x["location"] == loc, proposed_cycle_counts) + ) + earliest_date = min([d["date"] for d in proposed_for_loc]) + cycle_count_proposed = list( + filter(lambda x: x["date"] == earliest_date, proposed_for_loc) + )[0] + domain = [("location_id", "=", loc.id), ("state", "in", ["draft"])] + existing_cycle_counts = self.env["stock.cycle.count"].search(domain) if existing_cycle_counts: existing_earliest_date = sorted( - existing_cycle_counts.mapped('date_deadline'))[0] + existing_cycle_counts.mapped("date_deadline") + )[0] existing_earliest_date = fields.Date.from_string( - existing_earliest_date) + existing_earliest_date + ) cycle_count_proposed_date = fields.Date.from_string( - cycle_count_proposed['date']) - if (cycle_count_proposed_date < - existing_earliest_date): - cc_to_update = existing_cycle_counts.search([ - ('date_deadline', '=', existing_earliest_date) - ]) - cc_to_update.write({ - 'date_deadline': cycle_count_proposed_date, - 'cycle_count_rule_id': cycle_count_proposed[ - 'rule_type'].id, - }) - delta = (fields.Datetime.from_string( - cycle_count_proposed['date']) - datetime.today()) - if not existing_cycle_counts and \ - delta.days < rec.cycle_count_planning_horizon: - cc_vals = self._prepare_cycle_count( - cycle_count_proposed) - self.env['stock.cycle.count'].create(cc_vals) + cycle_count_proposed["date"] + ) + if cycle_count_proposed_date < existing_earliest_date: + cc_to_update = existing_cycle_counts.search( + [("date_deadline", "=", existing_earliest_date)] + ) + cc_to_update.write( + { + "date_deadline": cycle_count_proposed_date, + "cycle_count_rule_id": cycle_count_proposed[ + "rule_type" + ].id, + } + ) + delta = ( + fields.Datetime.from_string(cycle_count_proposed["date"]) + - datetime.today() + ) + if ( + not existing_cycle_counts + and delta.days < rec.cycle_count_planning_horizon + ): + cc_vals = self._prepare_cycle_count(cycle_count_proposed) + self.env["stock.cycle.count"].create(cc_vals) @api.model def cron_cycle_count(self): @@ -131,8 +142,7 @@ class StockWarehouse(models.Model): whs = self.search([]) whs.action_compute_cycle_count_rules() except Exception as e: - _logger.info( - "Error while running stock_cycle_count cron job: %s", str(e)) + _logger.info("Error while running stock_cycle_count cron job: %s", str(e)) raise _logger.info("stock_cycle_count cron job ended.") return True diff --git a/stock_cycle_count/reports/report_stock_location_accuracy.py b/stock_cycle_count/reports/report_stock_location_accuracy.py index 3d9e38738..d539d20da 100644 --- a/stock_cycle_count/reports/report_stock_location_accuracy.py +++ b/stock_cycle_count/reports/report_stock_location_accuracy.py @@ -11,9 +11,11 @@ class LocationAccuracyReport(models.AbstractModel): @api.model def _get_inventory_domain(self, loc_id, exclude_sublocation=True): - return [('location_id', '=', loc_id), - ('exclude_sublocation', '=', exclude_sublocation), - ('state', '=', 'done')] + return [ + ("location_id", "=", loc_id), + ("exclude_sublocation", "=", exclude_sublocation), + ("state", "=", "done"), + ] @api.model def _get_location_data(self, locations): @@ -29,10 +31,5 @@ class LocationAccuracyReport(models.AbstractModel): report_obj = self.env["report"] locs = self.env["stock.location"].browse(self._ids) data = self._get_location_data(locs) - docargs = { - "doc_ids": locs._ids, - "docs": locs, - "data": data, - } - return report_obj.render( - "stock_cycle_count.stock_location_accuracy", docargs) + docargs = {"doc_ids": locs._ids, "docs": locs, "data": data} + return report_obj.render("stock_cycle_count.stock_location_accuracy", docargs) diff --git a/stock_cycle_count/tests/test_stock_cycle_count.py b/stock_cycle_count/tests/test_stock_cycle_count.py index 5cf2fc723..80bfef2d4 100644 --- a/stock_cycle_count/tests/test_stock_cycle_count.py +++ b/stock_cycle_count/tests/test_stock_cycle_count.py @@ -1,136 +1,139 @@ # Copyright 2017 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo.tests import common -from odoo.exceptions import ValidationError -from odoo.exceptions import AccessError - from datetime import datetime, timedelta +from odoo.exceptions import AccessError, ValidationError +from odoo.tests import common + class TestStockCycleCount(common.TransactionCase): - def setUp(self): super(TestStockCycleCount, self).setUp() - self.res_users_model = self.env['res.users'] - self.cycle_count_model = self.env['stock.cycle.count'] - self.stock_cycle_count_rule_model = self.env['stock.cycle.count.rule'] - self.inventory_model = self.env['stock.inventory'] - self.stock_location_model = self.env['stock.location'] - self.stock_move_model = self.env['stock.move'] - self.stock_warehouse_model = self.env['stock.warehouse'] - self.product_model = self.env['product.product'] - self.quant_model = self.env['stock.quant'] - self.move_model = self.env['stock.move'] + self.res_users_model = self.env["res.users"] + self.cycle_count_model = self.env["stock.cycle.count"] + self.stock_cycle_count_rule_model = self.env["stock.cycle.count.rule"] + self.inventory_model = self.env["stock.inventory"] + self.stock_location_model = self.env["stock.location"] + self.stock_move_model = self.env["stock.move"] + self.stock_warehouse_model = self.env["stock.warehouse"] + self.product_model = self.env["product.product"] + self.quant_model = self.env["stock.quant"] + self.move_model = self.env["stock.move"] - self.company = self.env.ref('base.main_company') - self.partner = self.env.ref('base.res_partner_1') - self.g_stock_manager = self.env.ref('stock.group_stock_manager') - self.g_stock_user = self.env.ref('stock.group_stock_user') + self.company = self.env.ref("base.main_company") + self.partner = self.env.ref("base.res_partner_1") + self.g_stock_manager = self.env.ref("stock.group_stock_manager") + self.g_stock_user = self.env.ref("stock.group_stock_user") # Create users: self.manager = self._create_user( - 'user_1', [self.g_stock_manager], self.company).id - self.user = self._create_user( - 'user_2', [self.g_stock_user], self.company).id + "user_1", [self.g_stock_manager], self.company + ).id + self.user = self._create_user("user_2", [self.g_stock_user], self.company).id # Create warehouses: - self.big_wh = self.stock_warehouse_model.create({ - 'name': 'BIG', - 'code': 'B', - 'cycle_count_planning_horizon': 30}) - self.small_wh = self.stock_warehouse_model.create({ - 'name': 'SMALL', 'code': 'S'}) + self.big_wh = self.stock_warehouse_model.create( + {"name": "BIG", "code": "B", "cycle_count_planning_horizon": 30} + ) + self.small_wh = self.stock_warehouse_model.create( + {"name": "SMALL", "code": "S"} + ) # Create rules: - self.rule_periodic = \ - self._create_stock_cycle_count_rule_periodic( - self.manager, 'rule_1', [2, 7]) - self.rule_turnover = \ - self._create_stock_cycle_count_rule_turnover( - self.manager, 'rule_2', [100]) - self.rule_accuracy = \ - self._create_stock_cycle_count_rule_accuracy( - self.manager, 'rule_3', [5], self.big_wh.view_location_id.ids) + self.rule_periodic = self._create_stock_cycle_count_rule_periodic( + self.manager, "rule_1", [2, 7] + ) + self.rule_turnover = self._create_stock_cycle_count_rule_turnover( + self.manager, "rule_2", [100] + ) + self.rule_accuracy = self._create_stock_cycle_count_rule_accuracy( + self.manager, "rule_3", [5], self.big_wh.view_location_id.ids + ) self.zero_rule = self._create_stock_cycle_count_rule_zero( - self.manager, 'rule_4') + self.manager, "rule_4" + ) # Configure warehouses: self.rule_ids = [ self.rule_periodic.id, self.rule_turnover.id, self.rule_accuracy.id, - self.zero_rule.id] - self.big_wh.write({ - 'cycle_count_rule_ids': [(6, 0, self.rule_ids)] - }) + self.zero_rule.id, + ] + self.big_wh.write({"cycle_count_rule_ids": [(6, 0, self.rule_ids)]}) # Create a location: - self.count_loc = self.stock_location_model.create({ - 'name': 'Place', - 'usage': 'production' - }) + self.count_loc = self.stock_location_model.create( + {"name": "Place", "usage": "production"} + ) self.stock_location_model._parent_store_compute() # Create a cycle count: - self.cycle_count_1 = self.cycle_count_model.sudo(self.manager).create({ - 'name': 'Test cycle count', - 'cycle_count_rule_id': self.rule_periodic.id, - 'location_id': self.count_loc.id, - }) + self.cycle_count_1 = self.cycle_count_model.sudo(self.manager).create( + { + "name": "Test cycle count", + "cycle_count_rule_id": self.rule_periodic.id, + "location_id": self.count_loc.id, + } + ) # Create a product: - self.product1 = self.product_model.create({ - 'name': 'Test Product 1', - 'type': 'product', - 'default_code': 'PROD1', - }) + self.product1 = self.product_model.create( + {"name": "Test Product 1", "type": "product", "default_code": "PROD1"} + ) def _create_user(self, login, groups, company): group_ids = [group.id for group in groups] - user = self.res_users_model.create({ - 'name': login, - 'login': login, - 'email': 'example@yourcompany.com', - 'company_id': company.id, - 'company_ids': [(4, company.id)], - 'groups_id': [(6, 0, group_ids)] - }) + user = self.res_users_model.create( + { + "name": login, + "login": login, + "email": "example@yourcompany.com", + "company_id": company.id, + "company_ids": [(4, company.id)], + "groups_id": [(6, 0, group_ids)], + } + ) return user def _create_stock_cycle_count_rule_periodic(self, uid, name, values): - rule = self.stock_cycle_count_rule_model.sudo(uid).create({ - 'name': name, - 'rule_type': 'periodic', - 'periodic_qty_per_period': values[0], - 'periodic_count_period': values[1], - }) + rule = self.stock_cycle_count_rule_model.sudo(uid).create( + { + "name": name, + "rule_type": "periodic", + "periodic_qty_per_period": values[0], + "periodic_count_period": values[1], + } + ) return rule def _create_stock_cycle_count_rule_turnover(self, uid, name, values): - rule = self.stock_cycle_count_rule_model.sudo(uid).create({ - 'name': name, - 'rule_type': 'turnover', - 'turnover_inventory_value_threshold': values[0], - }) + rule = self.stock_cycle_count_rule_model.sudo(uid).create( + { + "name": name, + "rule_type": "turnover", + "turnover_inventory_value_threshold": values[0], + } + ) return rule - def _create_stock_cycle_count_rule_accuracy( - self, uid, name, values, zone_ids): - rule = self.stock_cycle_count_rule_model.sudo(uid).create({ - 'name': name, - 'rule_type': 'accuracy', - 'accuracy_threshold': values[0], - 'apply_in': 'location', - 'location_ids': [(6, 0, zone_ids)], - }) + def _create_stock_cycle_count_rule_accuracy(self, uid, name, values, zone_ids): + rule = self.stock_cycle_count_rule_model.sudo(uid).create( + { + "name": name, + "rule_type": "accuracy", + "accuracy_threshold": values[0], + "apply_in": "location", + "location_ids": [(6, 0, zone_ids)], + } + ) return rule def _create_stock_cycle_count_rule_zero(self, uid, name): - rule = self.stock_cycle_count_rule_model.sudo(uid).create({ - 'name': name, - 'rule_type': 'zero', - }) + rule = self.stock_cycle_count_rule_model.sudo(uid).create( + {"name": name, "rule_type": "zero"} + ) return rule def test_cycle_count_planner(self): @@ -141,113 +144,127 @@ class TestStockCycleCount(common.TransactionCase): for rule in self.big_wh.cycle_count_rule_ids: locs += wh._search_cycle_count_locations(rule) locs = locs.exists() # remove duplicated locations. - counts = self.cycle_count_model.search([ - ('location_id', 'in', locs.ids)]) - self.assertFalse( - counts, 'Existing cycle counts before execute planner.') + counts = self.cycle_count_model.search([("location_id", "in", locs.ids)]) + self.assertFalse(counts, "Existing cycle counts before execute planner.") date_pre_existing_cc = datetime.today() + timedelta(days=30) - loc = locs.filtered(lambda l: l.usage != 'view')[0] - pre_existing_count = self.cycle_count_model.create({ - 'name': 'To be cancelled when running cron job.', - 'cycle_count_rule_id': self.rule_periodic.id, - 'location_id': loc.id, - 'date_deadline': date_pre_existing_cc - }) - self.assertEqual(pre_existing_count.state, 'draft', - 'Testing data not generated properly.') + loc = locs.filtered(lambda l: l.usage != "view")[0] + pre_existing_count = self.cycle_count_model.create( + { + "name": "To be cancelled when running cron job.", + "cycle_count_rule_id": self.rule_periodic.id, + "location_id": loc.id, + "date_deadline": date_pre_existing_cc, + } + ) + self.assertEqual( + pre_existing_count.state, "draft", "Testing data not generated properly." + ) date = datetime.today() - timedelta(days=1) - self.inventory_model.create({ - 'name': 'Pre-existing inventory', - 'location_id': loc.id, - 'date': date - }) - self.quant_model.create({ - 'product_id': self.product1.id, - 'location_id': self.count_loc.id, - 'quantity': 1.0, - }) - move1 = self.stock_move_model.create({ - 'name': 'Pre-existing move', - 'product_id': self.product1.id, - 'product_uom_qty': 1.0, - 'product_uom': self.product1.uom_id.id, - 'location_id': self.count_loc.id, - 'location_dest_id': loc.id - }) + self.inventory_model.create( + {"name": "Pre-existing inventory", "location_id": loc.id, "date": date} + ) + self.quant_model.create( + { + "product_id": self.product1.id, + "location_id": self.count_loc.id, + "quantity": 1.0, + } + ) + move1 = self.stock_move_model.create( + { + "name": "Pre-existing move", + "product_id": self.product1.id, + "product_uom_qty": 1.0, + "product_uom": self.product1.uom_id.id, + "location_id": self.count_loc.id, + "location_dest_id": loc.id, + } + ) move1._action_confirm() move1._action_assign() move1.move_line_ids[0].qty_done = 1.0 move1._action_done() wh.cron_cycle_count() - self.assertNotEqual(pre_existing_count.date_deadline, - date_pre_existing_cc, - 'Date of pre-existing cycle counts has not been ' - 'updated.') - counts = self.cycle_count_model.search([ - ('location_id', 'in', locs.ids)]) - self.assertTrue(counts, 'Cycle counts not planned') + self.assertNotEqual( + pre_existing_count.date_deadline, + date_pre_existing_cc, + "Date of pre-existing cycle counts has not been " "updated.", + ) + counts = self.cycle_count_model.search([("location_id", "in", locs.ids)]) + self.assertTrue(counts, "Cycle counts not planned") # Zero-confirmations: - count = self.cycle_count_model.search([ - ('location_id', '=', loc.id), - ('cycle_count_rule_id', '=', self.zero_rule.id)]) - self.assertFalse( - count, 'Unexpected zero confirmation.') - move2 = self.move_model.create({ - 'name': 'make the locations to run out of stock.', - 'product_id': self.product1.id, - 'product_uom_qty': 1.0, - 'product_uom': self.product1.uom_id.id, - 'location_id': loc.id, - 'location_dest_id': self.count_loc.id - }) + count = self.cycle_count_model.search( + [ + ("location_id", "=", loc.id), + ("cycle_count_rule_id", "=", self.zero_rule.id), + ] + ) + self.assertFalse(count, "Unexpected zero confirmation.") + move2 = self.move_model.create( + { + "name": "make the locations to run out of stock.", + "product_id": self.product1.id, + "product_uom_qty": 1.0, + "product_uom": self.product1.uom_id.id, + "location_id": loc.id, + "location_dest_id": self.count_loc.id, + } + ) move2._action_confirm() move2._action_assign() move2.move_line_ids[0].qty_done = 1.0 move2._action_done() - count = self.cycle_count_model.search([ - ('location_id', '=', loc.id), - ('cycle_count_rule_id', '=', self.zero_rule.id)]) - self.assertTrue( - count, 'Zero confirmation not being created.') + count = self.cycle_count_model.search( + [ + ("location_id", "=", loc.id), + ("cycle_count_rule_id", "=", self.zero_rule.id), + ] + ) + self.assertTrue(count, "Zero confirmation not being created.") def test_cycle_count_workflow(self): """Tests workflow.""" self.cycle_count_1.action_create_inventory_adjustment() - inventory = self.inventory_model.search([ - ('cycle_count_id', '=', self.cycle_count_1.id)]) - self.assertTrue(inventory, 'Inventory not created.') + inventory = self.inventory_model.search( + [("cycle_count_id", "=", self.cycle_count_1.id)] + ) + self.assertTrue(inventory, "Inventory not created.") inventory.action_start() inventory.action_validate() - self.assertEqual(self.cycle_count_1.state, 'done', - 'Cycle count not set as done.') + self.assertEqual( + self.cycle_count_1.state, "done", "Cycle count not set as done." + ) self.cycle_count_1.do_cancel() - self.assertEqual(self.cycle_count_1.state, 'cancelled', - 'Cycle count not set as cancelled.') + self.assertEqual( + self.cycle_count_1.state, "cancelled", "Cycle count not set as cancelled." + ) def test_view_methods(self): """Tests the methods used to handle views.""" self.cycle_count_1.action_create_inventory_adjustment() self.cycle_count_1.action_view_inventory() inv_count = self.cycle_count_1.inventory_adj_count - self.assertEqual(inv_count, 1, - 'View method failing.') - rules = [self.rule_periodic, - self.rule_turnover, - self.rule_accuracy, - self.zero_rule] + self.assertEqual(inv_count, 1, "View method failing.") + rules = [ + self.rule_periodic, + self.rule_turnover, + self.rule_accuracy, + self.zero_rule, + ] for r in rules: r._compute_rule_description() - self.assertTrue(r.rule_description, 'No description provided') + self.assertTrue(r.rule_description, "No description provided") self.rule_accuracy._onchange_locaton_ids() - self.assertEqual(self.rule_accuracy.warehouse_ids.ids, self.big_wh.ids, - 'Rules defined for zones are not getting the right ' - 'warehouse.') + self.assertEqual( + self.rule_accuracy.warehouse_ids.ids, + self.big_wh.ids, + "Rules defined for zones are not getting the right " "warehouse.", + ) def test_user_security(self): """Tests user rights.""" with self.assertRaises(AccessError): - self._create_stock_cycle_count_rule_periodic( - self.user, 'rule_1b', [2, 7]) + self._create_stock_cycle_count_rule_periodic(self.user, "rule_1b", [2, 7]) with self.assertRaises(AccessError): self.cycle_count_1.sudo(self.user).unlink() @@ -255,22 +272,20 @@ class TestStockCycleCount(common.TransactionCase): """Tests the constrains for the periodic rules.""" # constrain: periodic_qty_per_period < 1 with self.assertRaises(ValidationError): - self._create_stock_cycle_count_rule_periodic( - self.manager, 'rule_0', [0, 0]) + self._create_stock_cycle_count_rule_periodic(self.manager, "rule_0", [0, 0]) # constrain: periodic_count_period < 0 with self.assertRaises(ValidationError): self._create_stock_cycle_count_rule_periodic( - self.manager, 'rule_0', [1, -1]) + self.manager, "rule_0", [1, -1] + ) def test_rule_zero_constrains(self): """Tests the constrains for the zero-confirmation rule: it might only exist one zero confirmation rule per warehouse and have just one warehouse assigned. """ - zero2 = self._create_stock_cycle_count_rule_zero( - self.manager, 'zero_rule_2') + zero2 = self._create_stock_cycle_count_rule_zero(self.manager, "zero_rule_2") with self.assertRaises(ValidationError): zero2.warehouse_ids = [(4, self.big_wh.id)] with self.assertRaises(ValidationError): - self.zero_rule.warehouse_ids = [ - (4, self.small_wh.id)] + self.zero_rule.warehouse_ids = [(4, self.small_wh.id)]