157 lines
7.9 KiB
Python
157 lines
7.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import babel.dates
|
|
from json import dumps
|
|
from datetime import datetime, time
|
|
|
|
|
|
from odoo import api, fields, models, SUPERUSER_ID, _
|
|
from odoo.osv.expression import AND
|
|
from odoo.tools import get_month, subtract, format_date
|
|
from odoo.tools.misc import get_lang
|
|
|
|
|
|
class StockReplenishmentInfo(models.TransientModel):
|
|
_name = 'stock.replenishment.info'
|
|
_description = 'Stock supplier replenishment information'
|
|
_rec_name = 'orderpoint_id'
|
|
|
|
orderpoint_id = fields.Many2one('stock.warehouse.orderpoint')
|
|
product_id = fields.Many2one('product.product', related='orderpoint_id.product_id')
|
|
qty_to_order = fields.Float(related='orderpoint_id.qty_to_order')
|
|
json_lead_days = fields.Char(compute='_compute_json_lead_days')
|
|
json_replenishment_history = fields.Char(compute='_compute_json_replenishment_history')
|
|
|
|
warehouseinfo_ids = fields.One2many(related='orderpoint_id.warehouse_id.resupply_route_ids')
|
|
wh_replenishment_option_ids = fields.One2many('stock.replenishment.option', 'replenishment_info_id', compute='_compute_wh_replenishment_options')
|
|
|
|
@api.depends('orderpoint_id')
|
|
def _compute_wh_replenishment_options(self):
|
|
for replenishment_info in self:
|
|
replenishment_info.wh_replenishment_option_ids = self.env['stock.replenishment.option'].create([
|
|
{'product_id': replenishment_info.product_id.id, 'route_id': route_id.id, 'replenishment_info_id': replenishment_info.id}
|
|
for route_id in replenishment_info.warehouseinfo_ids
|
|
]).sorted(lambda o: o.free_qty, reverse=True)
|
|
|
|
@api.depends('orderpoint_id')
|
|
def _compute_json_lead_days(self):
|
|
self.json_lead_days = False
|
|
for replenishment_report in self:
|
|
if not replenishment_report.orderpoint_id.product_id or not replenishment_report.orderpoint_id.location_id:
|
|
continue
|
|
orderpoint = replenishment_report.orderpoint_id
|
|
orderpoints_values = orderpoint._get_lead_days_values()
|
|
dummy, lead_days_description = orderpoint.rule_ids._get_lead_days(
|
|
orderpoint.product_id, **orderpoints_values)
|
|
replenishment_report.json_lead_days = dumps({
|
|
'lead_days_date': format_date(self.env, replenishment_report.orderpoint_id.lead_days_date),
|
|
'lead_days_description': lead_days_description,
|
|
'today': format_date(self.env, fields.Date.today()),
|
|
'trigger': orderpoint.trigger,
|
|
'qty_forecast': self.env['ir.qweb.field.float'].value_to_html(orderpoint.qty_forecast, {'decimal_precision': 'Product Unit of Measure'}),
|
|
'qty_to_order': self.env['ir.qweb.field.float'].value_to_html(orderpoint.qty_to_order, {'decimal_precision': 'Product Unit of Measure'}),
|
|
'product_min_qty': self.env['ir.qweb.field.float'].value_to_html(orderpoint.product_min_qty, {'decimal_precision': 'Product Unit of Measure'}),
|
|
'product_max_qty': self.env['ir.qweb.field.float'].value_to_html(orderpoint.product_max_qty, {'decimal_precision': 'Product Unit of Measure'}),
|
|
'product_uom_name': orderpoint.product_uom_name,
|
|
'virtual': orderpoint.trigger == 'manual' and orderpoint.create_uid.id == SUPERUSER_ID,
|
|
})
|
|
|
|
@api.depends('orderpoint_id')
|
|
def _compute_json_replenishment_history(self):
|
|
for replenishment_report in self:
|
|
replenishment_history = []
|
|
today = fields.Datetime.now()
|
|
first_month = subtract(today, months=2)
|
|
date_from, dummy = get_month(first_month)
|
|
dummy, date_to = get_month(today)
|
|
domain = [
|
|
('product_id', '=', replenishment_report.product_id.id),
|
|
('date', '>=', date_from),
|
|
('date', '<=', datetime.combine(date_to, time.max)),
|
|
('state', '=', 'done'),
|
|
('company_id', '=', replenishment_report.orderpoint_id.company_id.id)
|
|
]
|
|
quantity_by_month_out = self.env['stock.move']._read_group(
|
|
AND([domain, [('location_dest_id.usage', '=', 'customer')]]),
|
|
['date:month'], ['product_qty:sum'])
|
|
quantity_by_month_returned = dict(self.env['stock.move']._read_group(
|
|
AND([domain, [('location_id.usage', '=', 'customer')]]),
|
|
['date:month'], ['product_qty:sum']))
|
|
locale = get_lang(self.env).code
|
|
fmt = models.READ_GROUP_DISPLAY_FORMAT['month']
|
|
for month, product_qty_sum in quantity_by_month_out:
|
|
replenishment_history.append({
|
|
'name': babel.dates.format_datetime(month, format=fmt, locale=locale),
|
|
'quantity': product_qty_sum - quantity_by_month_returned.get(month, 0),
|
|
'uom_name': replenishment_report.product_id.uom_id.display_name,
|
|
})
|
|
replenishment_report.json_replenishment_history = dumps({
|
|
'template': 'stock.replenishmentHistory',
|
|
'replenishment_history': replenishment_history
|
|
})
|
|
|
|
|
|
class StockReplenishmentOption(models.TransientModel):
|
|
_name = 'stock.replenishment.option'
|
|
_description = 'Stock warehouse replenishment option'
|
|
|
|
route_id = fields.Many2one('stock.route')
|
|
product_id = fields.Many2one('product.product')
|
|
replenishment_info_id = fields.Many2one('stock.replenishment.info')
|
|
|
|
location_id = fields.Many2one('stock.location', related='warehouse_id.lot_stock_id')
|
|
warehouse_id = fields.Many2one('stock.warehouse', related='route_id.supplier_wh_id')
|
|
uom = fields.Char(related='product_id.uom_name')
|
|
qty_to_order = fields.Float(related='replenishment_info_id.qty_to_order')
|
|
|
|
free_qty = fields.Float(compute='_compute_free_qty')
|
|
lead_time = fields.Char(compute='_compute_lead_time')
|
|
|
|
warning_message = fields.Char(compute='_compute_warning_message')
|
|
|
|
@api.depends('product_id', 'route_id')
|
|
def _compute_free_qty(self):
|
|
for record in self:
|
|
record.free_qty = record.product_id.with_context(location=record.location_id.id).free_qty
|
|
|
|
@api.depends('replenishment_info_id')
|
|
def _compute_lead_time(self):
|
|
for record in self:
|
|
lead_time = record.route_id.rule_ids._get_lead_days(record.product_id)[0]['total_delay'] #TO FIX: use _get_rule to avoid singleton issue
|
|
record.lead_time = str(lead_time) + " days"
|
|
|
|
@api.depends('warehouse_id', 'free_qty', 'uom', 'qty_to_order')
|
|
def _compute_warning_message(self):
|
|
self.warning_message = ''
|
|
for record in self:
|
|
if record.free_qty < record.qty_to_order:
|
|
record.warning_message = _(
|
|
'%(warehouse)s can only provide %(free_qty)s %(uom)s, while the quantity to order is %(qty_to_order)s %(uom)s.',
|
|
warehouse=record.warehouse_id.name,
|
|
free_qty=record.free_qty,
|
|
uom=record.uom,
|
|
qty_to_order=record.qty_to_order
|
|
)
|
|
|
|
def select_route(self):
|
|
if self.free_qty < self.qty_to_order:
|
|
return {
|
|
"type": "ir.actions.act_window",
|
|
"res_model": "stock.replenishment.option",
|
|
"res_id": self.id,
|
|
"views": [[self.env.ref('stock.replenishment_option_warning_view').id, "form"]],
|
|
"target": "new",
|
|
"name": _("Quantity available too low")
|
|
}
|
|
return self.order_all()
|
|
|
|
def order_avbl(self):
|
|
self.replenishment_info_id.orderpoint_id.route_id = self.route_id
|
|
self.replenishment_info_id.orderpoint_id.qty_to_order = self.free_qty
|
|
return {'type': 'ir.actions.act_window_close'}
|
|
|
|
def order_all(self):
|
|
self.replenishment_info_id.orderpoint_id.route_id = self.route_id
|
|
return {'type': 'ir.actions.act_window_close'}
|