[10.0][IMP] fix api issues and clean up code

This commit is contained in:
lreficent
2018-01-03 12:06:56 +01:00
committed by Lois Rilo
parent b4e25ccf73
commit 10beda575e
5 changed files with 163 additions and 134 deletions

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# Copyright 2017-18 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).
@@ -13,7 +13,7 @@ class StockCycleCount(models.Model):
_inherit = 'mail.thread'
@api.multi
def _count_inventory_adj(self):
def _compute_inventory_adj_count(self):
for rec in self:
rec.inventory_adj_count = len(rec.stock_adjustment_ids)
@@ -53,7 +53,8 @@ class StockCycleCount(models.Model):
inverse_name='cycle_count_id',
string='Inventory Adjustment',
track_visibility='onchange')
inventory_adj_count = fields.Integer(compute='_count_inventory_adj')
inventory_adj_count = fields.Integer(
compute='_compute_inventory_adj_count')
company_id = fields.Many2one(
comodel_name='res.company', string='Company', required=True,
default=_company_get, readonly=True)
@@ -62,8 +63,9 @@ class StockCycleCount(models.Model):
def do_cancel(self):
self.write({'state': 'cancelled'})
@api.model
@api.multi
def _prepare_inventory_adjustment(self):
self.ensure_one()
return {
'name': 'INV/{}'.format(self.name),
'cycle_count_id': self.id,

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# Copyright 2017-18 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 import api, fields, models, _
from odoo.exceptions import UserError
from odoo.exceptions import UserError, ValidationError
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from datetime import timedelta, datetime
@@ -13,9 +13,10 @@ class StockCycleCountRule(models.Model):
_name = 'stock.cycle.count.rule'
_description = "Stock Cycle Counts Rules"
@api.one
def _compute_currency(self):
self.currency_id = self.env.user.company_id.currency_id
@api.multi
def _compute_currency_id(self):
for rec in self:
rec.currency_id = self.env.user.company_id.currency_id
@api.model
def _selection_rule_types(self):
@@ -25,26 +26,27 @@ class StockCycleCountRule(models.Model):
('accuracy', _('Minimum Accuracy')),
('zero', _('Zero Confirmation'))]
@api.one
@api.multi
@api.constrains('rule_type', 'warehouse_ids')
def _check_zero_rule(self):
if self.rule_type == 'zero' and len(self.warehouse_ids) > 1:
raise UserError(
_('Zero confirmation rules can only have one warehouse '
'assigned.')
)
if self.rule_type == 'zero':
zero_rule = self.search([
('rule_type', '=', 'zero'),
('warehouse_ids', '=', self.warehouse_ids.id)])
if len(zero_rule) > 1:
raise UserError(
_('You can only have one zero confirmation rule per '
'warehouse.')
for rec in self:
if rec.rule_type == 'zero' and len(rec.warehouse_ids) > 1:
raise ValidationError(
_('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 len(zero_rule) > 1:
raise ValidationError(
_('You can only have one zero confirmation rule per '
'warehouse.')
)
@api.onchange('rule_type')
def _get_rule_description(self):
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 '
@@ -66,17 +68,19 @@ class StockCycleCountRule(models.Model):
else:
self.rule_description = _('(No description provided.)')
@api.multi
@api.constrains('periodic_qty_per_period', 'periodic_count_period')
def _check_negative_periodic(self):
if self.periodic_qty_per_period < 1:
raise UserError(
_('You cannot define a negative or null number of counts per '
'period.')
)
if self.periodic_count_period < 0:
raise UserError(
_('You cannot define a negative period.')
)
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.')
)
if rec.periodic_count_period < 0:
raise ValidationError(
_('You cannot define a negative period.')
)
@api.onchange('location_ids')
def _get_warehouses(self):
@@ -87,36 +91,45 @@ class StockCycleCountRule(models.Model):
wh_ids = list(set(wh_ids))
self.warehouse_ids = self.env['stock.warehouse'].browse(wh_ids)
name = fields.Char('Name', required=True)
rule_type = fields.Selection(selection="_selection_rule_types",
string='Type of rule',
required=True)
rule_description = fields.Char(string='Rule Description',
compute='_get_rule_description')
active = fields.Boolean(string='Active', default=True)
periodic_qty_per_period = fields.Integer(string='Counts per period',
default=1)
name = fields.Char(required=True)
rule_type = fields.Selection(
selection="_selection_rule_types",
string='Type of rule', required=True,
)
rule_description = fields.Char(
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')
turnover_inventory_value_threshold = fields.Float(
string='Turnover Inventory Value Threshold')
currency_id = fields.Many2one(comodel_name='res.currency',
string='Currency',
compute='_compute_currency')
accuracy_threshold = fields.Float(string='Minimum Accuracy Threshold',
digits=(3, 2))
string='Turnover Inventory Value Threshold',
)
currency_id = fields.Many2one(
comodel_name='res.currency', string='Currency',
compute='_compute_currency_id',
)
accuracy_threshold = fields.Float(
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')
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')
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')
column2='location_id', string='Zones where applied',
)
def compute_rule(self, locs):
if self.rule_type == 'periodic':
@@ -211,8 +224,9 @@ class StockCycleCountRule(models.Model):
cycle_counts.append(cycle_count)
return cycle_counts
@api.model
@api.multi
def _compute_rule_accuracy(self, locs):
self.ensure_one()
cycle_counts = []
for loc in locs:
if loc.loc_accuracy < self.accuracy_threshold:

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# Copyright 2017-18 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).
@@ -20,59 +20,67 @@ except (ImportError, IOError) as err:
class StockLocation(models.Model):
_inherit = 'stock.location'
@api.one
@api.multi
def _compute_loc_accuracy(self):
history = self.env['stock.inventory'].search([
('location_id', '=', self.id), ('state', '=', 'done')])
history = history.sorted(key=lambda r: r.write_date, reverse=True)
if history:
wh = self.get_warehouse()
if len(history) > wh.counts_for_accuracy_qty:
self.loc_accuracy = mean(history[:wh.counts_for_accuracy_qty].
mapped('inventory_accuracy'))
else:
self.loc_accuracy = mean(history.mapped('inventory_accuracy'))
for rec in self:
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 len(history) > wh.counts_for_accuracy_qty:
rec.loc_accuracy = mean(
history[:wh.counts_for_accuracy_qty].mapped(
'inventory_accuracy'))
else:
rec.loc_accuracy = mean(
history.mapped('inventory_accuracy'))
zero_confirmation_disabled = fields.Boolean(
string='Disable Zero Confirmations',
default=False,
help='Define whether this location will trigger a zero-confirmation '
'validation when a rule for its warehouse is defined to perform '
'zero-confirmations.')
'zero-confirmations.',
)
cycle_count_disabled = fields.Boolean(
string='Exclude from Cycle Count',
default=False,
help='Define whether the location is going to be cycle counted.')
qty_variance_inventory_threshold = fields.Float('Acceptable Inventory '
'Quantity Variance '
'Threshold')
help='Define whether the location is going to be cycle counted.',
)
qty_variance_inventory_threshold = fields.Float(
string='Acceptable Inventory Quantity Variance Threshold',
)
loc_accuracy = fields.Float(
string='Inventory Accuracy', compute='_compute_loc_accuracy',
digits=(3, 2))
digits=(3, 2),
)
@api.model
@api.multi
def _get_zero_confirmation_domain(self):
self.ensure_one()
domain = [('location_id', '=', self.id)]
return domain
@api.one
@api.multi
def check_zero_confirmation(self):
if not self.zero_confirmation_disabled:
wh = self.get_warehouse()
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(
self._get_zero_confirmation_domain())
if not quants:
self.create_zero_confirmation_cycle_count()
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)])
if zero_rule:
quants = self.env['stock.quant'].search(
rec._get_zero_confirmation_domain())
if not quants:
rec.create_zero_confirmation_cycle_count()
@api.multi
def create_zero_confirmation_cycle_count(self):
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()[0].strftime(
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'),

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# Copyright 2017-18 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).
@@ -30,8 +30,9 @@ class StockWarehouse(models.Model):
help='Number of latest inventories used to calculate location '
'accuracy')
@api.one
@api.multi
def get_horizon_date(self):
self.ensure_one()
date = datetime.today()
delta = timedelta(self.cycle_count_planning_horizon)
date_horizon = date + delta
@@ -58,60 +59,64 @@ class StockWarehouse(models.Model):
self._get_cycle_count_locations_search_domain(loc))
return locations
@api.model
@api.multi
def _cycle_count_rules_to_compute(self):
rules = self.cycle_count_rule_ids.search([
('rule_type', '!=', 'zero'), ('warehouse_ids', '=', self.id)])
self.ensure_one()
rules = self.env['stock.cycle.count.rule'].search([
('rule_type', '!=', 'zero'), ('warehouse_ids', 'in', self.ids)])
return rules
@api.one
@api.multi
def action_compute_cycle_count_rules(self):
''' Apply the rule in all the sublocations of a given warehouse(s) and
""" Apply the rule in all the sublocations of a given warehouse(s) and
returns a list with required dates for the cycle count of each
location '''
proposed_cycle_counts = []
rules = self._cycle_count_rules_to_compute()
for rule in rules:
locations = self._search_cycle_count_locations(rule)
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]))
for loc in locations:
proposed_for_loc = filter(lambda x: x['location'] == loc,
proposed_cycle_counts)
earliest_date = min([d['date'] for d in proposed_for_loc])
cycle_count_proposed = 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]
if cycle_count_proposed['date'] < existing_earliest_date:
cc_to_update = existing_cycle_counts.search([
('date_deadline', '=', existing_earliest_date)])
cc_to_update.write({
location """
for rec in self:
proposed_cycle_counts = []
rules = rec._cycle_count_rules_to_compute()
for rule in rules:
locations = rec._search_cycle_count_locations(rule)
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]))
for loc in locations:
proposed_for_loc = filter(lambda x: x['location'] == loc,
proposed_cycle_counts)
earliest_date = min([d['date'] for d in proposed_for_loc])
cycle_count_proposed = 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]
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 = datetime.strptime(
cycle_count_proposed['date'],
DEFAULT_SERVER_DATETIME_FORMAT) - datetime.today()
if not existing_cycle_counts and \
delta.days < rec.cycle_count_planning_horizon:
self.env['stock.cycle.count'].create({
'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'
})
delta = datetime.strptime(
cycle_count_proposed['date'],
DEFAULT_SERVER_DATETIME_FORMAT) - datetime.today()
if not existing_cycle_counts and \
delta.days < self.cycle_count_planning_horizon:
self.env['stock.cycle.count'].create({
'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.model
def cron_cycle_count(self):

View File

@@ -233,7 +233,7 @@ class TestStockCycleCount(common.TransactionCase):
self.rule_accuracy,
self.zero_rule]
for r in rules:
r._get_rule_description()
r._compute_rule_description()
self.assertTrue(r.rule_description, 'No description provided')
self.rule_accuracy._get_warehouses()
self.assertEqual(self.rule_accuracy.warehouse_ids.ids, self.big_wh.ids,