mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
123 lines
5.4 KiB
Python
123 lines
5.4 KiB
Python
# Copyright 2017 Camptocamp SA
|
|
# Copyright 2019 Tecnativa
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
|
|
|
from odoo import api, fields, models
|
|
from collections import OrderedDict
|
|
|
|
|
|
class ProductProduct(models.Model):
|
|
_inherit = 'product.product'
|
|
|
|
auto_orderpoint_template_ids = fields.Many2many(
|
|
comodel_name='stock.warehouse.orderpoint.template',
|
|
string="Automatic Reordering Rules",
|
|
domain=[('auto_generate', '=', True)],
|
|
help="When one or several automatic reordering rule is selected, "
|
|
"a Scheduled Action will automatically generate or update "
|
|
"the reordering rules of the product."
|
|
)
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
record = super().create(vals)
|
|
if vals.get('auto_orderpoint_template_ids'):
|
|
record.auto_orderpoint_template_ids.create_orderpoints(record)
|
|
return record
|
|
|
|
@api.multi
|
|
def write(self, vals):
|
|
result = super().write(vals)
|
|
if vals.get('auto_orderpoint_template_ids'):
|
|
orderpoint_templates = self.mapped('auto_orderpoint_template_ids')
|
|
orderpoint_templates.create_orderpoints(self)
|
|
return result
|
|
|
|
def _compute_historic_quantities_dict(
|
|
self, location_id=False, from_date=False, to_date=False):
|
|
"""Returns a dict of products with a dict of historic moves as for
|
|
a list of historic stock values resulting from those moves. If
|
|
a location_id is passed, we can restrict it to such location"""
|
|
location = location_id and location_id.id
|
|
domain_quant_loc, domain_move_in_loc, domain_move_out_loc = (
|
|
self.with_context(location=location)._get_domain_locations())
|
|
if not to_date:
|
|
to_date = fields.Datetime.now()
|
|
domain_move_in = domain_move_out = ([
|
|
('product_id', 'in', self.ids),
|
|
('state', '=', 'done'),
|
|
] + domain_move_in_loc)
|
|
domain_move_out = ([
|
|
('product_id', 'in', self.ids),
|
|
('state', '=', 'done'),
|
|
] + domain_move_out_loc)
|
|
if from_date:
|
|
domain_move_in += [('date', '>=', from_date)]
|
|
domain_move_out += [('date', '>=', from_date)]
|
|
domain_move_in += [('date', '<=', to_date)]
|
|
domain_move_out += [('date', '<=', to_date)]
|
|
move_obj = self.env['stock.move']
|
|
# Positive moves
|
|
moves_in = move_obj.search_read(
|
|
domain_move_in, ['product_id', 'product_qty', 'date'],
|
|
order='date asc')
|
|
# We'll convert to negative these quantities to operate with them
|
|
# to obtain the stock snapshot in every moment
|
|
moves_out = move_obj.search_read(
|
|
domain_move_out, ['product_id', 'product_qty', 'date'],
|
|
order='date asc')
|
|
for move in moves_out:
|
|
move['product_qty'] *= -1
|
|
# Merge both results and group them by product id as key
|
|
moves = moves_in + moves_out
|
|
# Obtain a dict with the stock snapshot for the relative date_from
|
|
# otherwise, the first move will counted as first stock value. We
|
|
# default the compute the stock value anyway to default the value
|
|
# for products with no moves for the given period
|
|
initial_stock = {}
|
|
initial_stock = self.with_context(
|
|
location=location)._compute_quantities_dict(
|
|
False, False, False, to_date=from_date or to_date)
|
|
product_moves_dict = {}
|
|
for move in moves:
|
|
product_moves_dict.setdefault(move['product_id'][0], {})
|
|
product_moves_dict[move['product_id'][0]].update({
|
|
move['date']: {
|
|
'prod_qty': move['product_qty'],
|
|
}
|
|
})
|
|
for product in self.with_context(prefetch_fields=False):
|
|
# If no there are no moves for a product we default the stock
|
|
# to the one for the given period nevermind the dates
|
|
product_moves = product_moves_dict.get(product.id)
|
|
prod_initial_stock = initial_stock.get(product.id, {})
|
|
if not product_moves:
|
|
product_moves_dict[product.id] = {
|
|
to_date: {
|
|
'prod_qty': 0,
|
|
'stock': prod_initial_stock.get('qty_available', 0),
|
|
},
|
|
'stock_history': [
|
|
prod_initial_stock.get('qty_available', 0)],
|
|
}
|
|
continue
|
|
# Now we'll sort the moves by date and assign an initial stock so
|
|
# we can compute the stock historical values from the moves
|
|
# sequence so we can exploit it statisticaly
|
|
product_moves = OrderedDict(sorted(product_moves.items()))
|
|
stock = False
|
|
first_item = product_moves[next(iter(product_moves))]
|
|
if from_date:
|
|
stock = prod_initial_stock.get('qty_available')
|
|
if not stock:
|
|
stock = first_item['prod_qty']
|
|
first_item['stock'] = stock
|
|
iter_moves = iter(product_moves)
|
|
next(iter_moves, None)
|
|
for date in iter_moves:
|
|
stock += product_moves[date]['prod_qty']
|
|
product_moves[date]['stock'] = stock
|
|
product_moves_dict[product.id]['stock_history'] = (
|
|
[v['stock'] for k, v in product_moves.items()])
|
|
return product_moves_dict
|