[ADD] lunch: prevent ordering unavailable products

Add a field partner_id on a lunch.alert
If the field is set, the alert becomes blocking and will prevent people from
ordering a lunch.

Add a available field on the lunch.product that will check if any alert forbids
ordering that product at the time of the order.

Pass the lunch.order date in the context to be able to plan a lunch order
another day if the provider is forbidden today.
This commit is contained in:
Martin Trigaux
2018-08-10 15:32:04 +02:00
parent 1de9496f14
commit 8f22f23e61
2 changed files with 106 additions and 22 deletions
+92 -20
View File
@@ -9,6 +9,7 @@ import datetime
from odoo import api, fields, models, _
from odoo.exceptions import AccessError, ValidationError
from odoo.addons import decimal_precision as dp
from odoo.osv import expression
class LunchOrder(models.Model):
@@ -164,7 +165,8 @@ class LunchOrderLine(models.Model):
name = fields.Char(related='product_id.name', string="Product Name", readonly=True)
order_id = fields.Many2one('lunch.order', 'Order', ondelete='cascade', required=True)
product_id = fields.Many2one('lunch.product', 'Product', required=True)
product_id = fields.Many2one('lunch.product', 'Product', required=True,
domain=[('available', '=', True)])
category_id = fields.Many2one('lunch.product.category', string='Product Category',
related='product_id.category_id', readonly=True, store=True)
date = fields.Date(string='Date', related='order_id.date', readonly=True, store=True)
@@ -183,6 +185,28 @@ class LunchOrderLine(models.Model):
cashmove = fields.One2many('lunch.cashmove', 'order_id', 'Cash Move')
currency_id = fields.Many2one('res.currency', related='order_id.currency_id')
def _check_supplier_availibility(self):
products = self.mapped('product_id')
if not all(product.available for product in products):
supplier_name = ", ".join(product.supplier.display_name for product in products if not product.available)
raise ValidationError(_("Vendor(s) '%s' is not available today") % supplier_name)
@api.model
def create(self, vals):
""" Override as an onchange would not apply if using the history buttons """
res = super(LunchOrderLine, self).create(vals)
res.with_context(lunch_date=res.order_id.date)._check_supplier_availibility()
return res
@api.model
def write(self, vals):
""" Override as an onchange would not apply if using the history buttons """
res = super(LunchOrderLine, self).write(vals)
if vals.get('product_id'):
for line in self:
line.with_context(lunch_date=line.order_id.date)._check_supplier_availibility()
return res
@api.one
def order(self):
"""
@@ -236,6 +260,41 @@ class LunchProduct(models.Model):
price = fields.Float('Price', digits=dp.get_precision('Account'))
supplier = fields.Many2one('res.partner', 'Vendor')
active = fields.Boolean(default=True)
available = fields.Boolean(compute='_get_available_product', search='_search_available_products')
@api.depends('supplier')
def _get_available_product(self):
for product in self:
if not product.supplier:
product.available = True
else:
alerts = self.env['lunch.alert'].search([
('partner_id', '=', self.supplier.id)
])
if alerts and not any(alert.display for alert in alerts):
# every alert is not available
product.available = False
else:
# no alert for the supplier or at least one is not available
product.available = True
def _search_available_products(self, operator, value):
alerts = self.env['lunch.alert'].search([])
supplier_w_alerts = alerts.mapped('partner_id')
available_suppliers = alerts.filtered(lambda a: a.display).mapped('partner_id')
available_products = self.search([
'|',
('supplier', 'not in', supplier_w_alerts.ids),
('supplier', 'in', available_suppliers.ids)
])
if (operator in expression.NEGATIVE_TERM_OPERATORS and value) or \
(operator not in expression.NEGATIVE_TERM_OPERATORS and not value):
# e.g. (available = False) or (available != True)
return [('id', 'not in', available_products.ids)]
else:
# e.g. (available = True) or (available != False)
return [('id', 'in', available_products.ids)]
class LunchProductCategory(models.Model):
@@ -270,6 +329,7 @@ class LunchAlert(models.Model):
given day, weekly or daily. The alert is displayed from start to end hour. """
_name = 'lunch.alert'
_description = 'Lunch Alert'
_rec_name = 'message'
display = fields.Boolean(compute='_compute_display_get')
message = fields.Text('Message', required=True)
@@ -277,6 +337,8 @@ class LunchAlert(models.Model):
('week', 'Every Week'),
('days', 'Every Day')],
string='Recurrence', required=True, index=True, default='specific')
partner_id = fields.Many2one('res.partner', string="Vendor",
help="If specified, the selected vendor can be ordered only on selected days")
specific_day = fields.Date('Day', default=fields.Date.context_today)
monday = fields.Boolean('Monday')
tuesday = fields.Boolean('Tuesday')
@@ -293,7 +355,8 @@ class LunchAlert(models.Model):
def name_get(self):
return [(alert.id, '%s %s' % (_('Alert'), '#%d' % alert.id)) for alert in self]
@api.one
@api.depends('alert_type', 'specific_day', 'monday', 'tuesday', 'thursday',
'friday', 'saturday', 'sunday', 'start_hour', 'end_hour')
def _compute_display_get(self):
"""
This method check if the alert can be displayed today
@@ -302,7 +365,6 @@ class LunchAlert(models.Model):
if alert type is day : True
return : Message if can_display_alert is True else False
"""
days_codes = {'0': 'sunday',
'1': 'monday',
'2': 'tuesday',
@@ -310,22 +372,32 @@ class LunchAlert(models.Model):
'4': 'thursday',
'5': 'friday',
'6': 'saturday'}
can_display_alert = {
'specific': (str(self.specific_day) == fields.Date.context_today(self)),
'week': self[days_codes[datetime.datetime.now().strftime('%w')]],
'days': True
}
fullday = False
now = datetime.datetime.now()
if self.env.context.get('lunch_date'):
# lunch_date is a fields.Date -> 00:00:00
lunch_date = fields.Datetime.from_string(self.env.context['lunch_date'])
# if lunch_date is in the future, planned lunch, ignore hours
fullday = lunch_date > now
now = max(lunch_date, now)
mynow = fields.Datetime.context_timestamp(self, now)
if can_display_alert[self.alert_type]:
mynow = fields.Datetime.context_timestamp(self, datetime.datetime.now())
hour_to = int(self.end_hour)
min_to = int((self.end_hour - hour_to) * 60)
to_alert = datetime.time(hour_to, min_to)
hour_from = int(self.start_hour)
min_from = int((self.start_hour - hour_from) * 60)
from_alert = datetime.time(hour_from, min_from)
for alert in self:
can_display_alert = {
'specific': (str(alert.specific_day) == fields.Date.to_string(mynow)),
'week': alert[days_codes[mynow.strftime('%w')]],
'days': True
}
if from_alert <= mynow.time() <= to_alert:
self.display = True
else:
self.display = False
if can_display_alert[alert.alert_type]:
hour_to = int(alert.end_hour)
min_to = int((alert.end_hour - hour_to) * 60)
to_alert = datetime.time(hour_to, min_to)
hour_from = int(alert.start_hour)
min_from = int((alert.start_hour - hour_from) * 60)
from_alert = datetime.time(hour_from, min_from)
if fullday or (from_alert <= mynow.time() <= to_alert):
alert.display = True
else:
alert.display = False
+14 -2
View File
@@ -477,7 +477,7 @@
<separator string='Select your order'/>
<field name='order_line_ids'>
<tree string='List' editable='bottom'>
<field name='product_id'/>
<field name='product_id' context="{'lunch_date': parent.date}"/>
<field name='note' />
<field name='price'/>
<field name='supplier' invisible="1"/>
@@ -485,7 +485,7 @@
</tree>
<form string='Lunch Order Form'>
<group>
<field name='product_id'/>
<field name='product_id' context="{'lunch_date': parent.date}"/>
<field name='note' />
<field name='price'/>
</group>
@@ -688,6 +688,18 @@
</group>
</group>
</group>
<group>
<group>
<field name="partner_id"/>
<field name="display" invisible="True"/>
</group>
<div class="oe_grey" attrs="{'invisible': ['|', ('partner_id', '=', False), ('display', '=', True)]}">
Products from this vendor can not be order at the moment.
</div>
<div class="oe_grey" attrs="{'invisible': ['|', ('partner_id', '=', False), ('display', '=', False)]}">
Products from this vendor can be order at the moment.
</div>
</group>
<group string='Message'>
<field name='message' nolabel='1' placeholder="Write the message you want to display during the defined period..."/>
</group>