153 lines
7.1 KiB
Python
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)
|