Files
enterprise-addons/mrp_mps/wizard/mrp_mps_forecast_suggestion.py

153 lines
7.1 KiB
Python

from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools.misc import format_date
from odoo.tools.float_utils import float_round
class MrpMpsForecastSuggestion(models.TransientModel):
_name = 'mrp.mps.forecast.suggestion'
_description = "Forecast Demand Suggestion"
mrp_mps_id = fields.Many2one('mrp.production.schedule')
product_id = fields.Many2one('product.product', related='mrp_mps_id.product_id', readonly=True)
period = fields.Integer(string='Period')
based_on = fields.Selection(
[('actual_demand', 'Actual Demand'),
('last_year', 'Previous Year'),
('30_days', 'Last 30 Days'),
('three_months', 'Last 3 Months'),
('one_year', 'Last 12 Months'),
]
, required=True, default='last_year', string='Based on', readonly=False)
based_on_readonly = fields.Char(compute='_compute_suggestion_fields')
percent_factor = fields.Integer(default=100, required=True)
quantity = fields.Float(compute='_compute_suggestion_fields', digits='Product Unit')
quantity_before_scale = fields.Float(compute='_compute_suggestion_fields', digits='Product Unit')
def action_open_suggest_forecasted_form_view(self, mps_id):
context = dict(self.env.context)
context['default_mrp_mps_id'] = mps_id
return {
'type': 'ir.actions.act_window',
'name': _("Suggest Forecasted Demand"),
'target': 'new',
'view_mode': 'form',
'views': [[False, 'form']],
'res_id': False,
'view_id': self.env.ref('mrp_mps.mrp_mps_forecast_suggestion_form_view').id,
'res_model': 'mrp.mps.forecast.suggestion',
'context': context,
}
@api.constrains('percent_factor')
def _check_percent_factor_gte_0(self):
if self.percent_factor < 0:
raise ValidationError(_("Percent factor cannot be less than zero."))
@api.depends('period', 'based_on', 'percent_factor')
def _compute_suggestion_fields(self):
period_index = 0
period_scale = self.env.context.get('period_scale')
based_on_date = ''
qty_before_scale = 0
rounding = self.mrp_mps_id.product_uom_id.rounding
if self.period:
period_index = self.period - 1
date_range = self.mrp_mps_id.company_id._get_date_range(years=1, force_period=period_scale)
start_chosen_date, end_chosen_date = date_range[period_index]
match period_scale:
case 'year':
based_on_date = _("%(year)s", year=start_chosen_date.year)
case 'month':
based_on_date = _("%(month)s %(year)s",
month=start_chosen_date.strftime("%b"), year=start_chosen_date.year)
case 'week':
based_on_date = _("Week %(week)s (%(start_day)s-%(end_day)s/%(month)s) %(year)s",
week=format_date(self.env, start_chosen_date, date_format='w'),
start_day=start_chosen_date.day,
end_day=end_chosen_date.day,
month=end_chosen_date.strftime("%b"),
year=end_chosen_date.year,
)
case 'day':
based_on_date = _("%(month)s %(day)s %(year)s",
day=start_chosen_date.day,
month=start_chosen_date.strftime("%b"),
year=start_chosen_date.year,
)
if self.period or self.based_on in ['30_days', 'three_months', 'one_year']:
suggestion_quantities = self._get_suggestion_quantities(period_scale=period_scale)
qty_before_scale = suggestion_quantities[period_index]
self.quantity_before_scale = float_round(qty_before_scale, precision_rounding=rounding, rounding_method='UP')
self.quantity = float_round(qty_before_scale * (self.percent_factor / 100), precision_rounding=rounding, rounding_method='UP')
self.based_on_readonly = based_on_date
def _get_suggestion_quantities(self, period_scale=False):
if self.based_on in ['last_year', 'actual_demand'] or self.period:
return self._get_suggestion_quantities_for_period_type(period_scale=period_scale)
else:
return self._get_suggestion_quantities_for_period_length(period_scale=period_scale)
def _get_suggestion_quantities_for_period_type(self, period_scale=False):
"""
Return a list of quantities, each is a suggestion demand for the matched period of the selected type.
"""
suggestion_quantities = []
years = 0 if self.based_on == 'actual_demand' else 1
date_range = self.mrp_mps_id.company_id._get_date_range(years=years, force_period=period_scale)
outgoing_qty, outgoing_qty_done, __, __ = self.mrp_mps_id._get_outgoing_qty(date_range)
for date in date_range:
period_qty = 0
key = (date, self.product_id, self.mrp_mps_id.warehouse_id)
period_qty += outgoing_qty_done.get(key, 0.0)
if self.based_on == 'actual_demand':
period_qty += outgoing_qty.get(key, 0.0)
suggestion_quantities.append(period_qty)
return suggestion_quantities
def _get_suggestion_quantities_for_period_length(self, period_scale=False):
"""
Return a list of quantities, each is a suggestion demand representing a ratio between
the length of the period of the selected type and the length of the demand period.
"""
if period_scale == 'year':
multiplier_monthly_demand = 12
elif period_scale == 'month':
multiplier_monthly_demand = 1
elif period_scale == 'week':
multiplier_monthly_demand = 7 / (365.25 / 12)
else:
multiplier_monthly_demand = 1 / (365.25 / 12)
context = {
'suggest_based_on': self.based_on,
'warehouse_id': self.mrp_mps_id.warehouse_id.id,
}
product = self.product_id.with_context(context)
qty = product.monthly_demand * multiplier_monthly_demand
return ([qty] * self.mrp_mps_id.company_id['manufacturing_period_to_display_%s' % period_scale])
def apply_forecast_quantity_suggestion(self):
self.ensure_one()
period_scale = self.env.context.get('period_scale')
rounding = self.mrp_mps_id.product_uom_id.rounding
if self.period:
period_index = self.period - 1
self.mrp_mps_id.set_forecast_qty(period_index, self.quantity, period_scale=period_scale)
else:
suggestion_quantities = self._get_suggestion_quantities(period_scale=period_scale)
for i in range(self.mrp_mps_id.company_id['manufacturing_period_to_display_%s' % period_scale]):
quantity_to_suggest = suggestion_quantities[i]
quantity_to_suggest = float_round(quantity_to_suggest * (self.percent_factor / 100), precision_rounding=rounding, rounding_method='UP')
self.mrp_mps_id.set_forecast_qty(i, quantity_to_suggest, period_scale=period_scale)