[MIGR] lunch: migration to the new API, cleaning and new

widget for displaying previous orders.

Detailed changes :
- previous order is not a computed field, with its own custom widget that has been designed
according the new spec for widgets;
- useless wizards have been removed, and replaced by some server actions that perform the
same action with less code and views;
- removed deprecated or incomplete reporting;
- some renaming to follow the new specifications of module coding
This commit is contained in:
Nicolas Martinelli
2015-04-23 10:27:08 +02:00
committed by Thibault Delavallée
parent 058d403309
commit 3d63c15a56
31 changed files with 680 additions and 1160 deletions
+4 -6
View File
@@ -1,8 +1,8 @@
# -*- encoding: utf-8 -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2012 Tiny SPRL (<http://tiny.be>).
# Odoo, Open Source Management Solution
# Copyright (C) 2004-Today Odoo SA (<https://www.odoo.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -15,10 +15,8 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import models
import report
import wizard
+13 -15
View File
@@ -1,21 +1,21 @@
# -*- encoding: utf-8 -*-
# -*- coding: utf-8 -*-
{
'author': 'Odoo SA',
'name': 'Lunch',
'author': 'OpenERP SA',
'version': '0.2',
'depends': ['base', 'report'],
'version': '1.0',
'depends': ['base', 'web', 'decimal_precision'],
'category': 'Tools',
'summary': 'Lunch Order, Meal, Food',
'description': """
The base module to manage lunch.
================================
Many companies order sandwiches, pizzas and other, from usual suppliers, for their employees to offer them more facilities.
Many companies order sandwiches, pizzas and other, from usual suppliers, for their employees to offer them more facilities.
However lunches management within the company requires proper administration especially when the number of employees or suppliers is important.
However lunches management within the company requires proper administration especially when the number of employees or suppliers is important.
The “Lunch Order” module has been developed to make this management easier but also to offer employees more tools and usability.
The “Lunch Order” module has been developed to make this management easier but also to offer employees more tools and usability.
In addition to a full meal and supplier management, this module offers the possibility to display warning and provides quick order selection based on employee’s preferences.
@@ -23,17 +23,15 @@ If you want to save your employees' time and avoid them to always have coins in
""",
'data': [
'security/lunch_security.xml',
'views/lunch_views.xml',
'views/lunch_templates.xml',
'wizard/lunch_order_view.xml',
'wizard/lunch_validation_view.xml',
'wizard/lunch_cancel_view.xml',
'report/lunch_report_views.xml',
'report/report_lunch_order_view.xml',
'report/lunch_order_templates.xml',
'security/ir.model.access.csv',
'report/lunch_order_views.xml',
'report/lunch_order_templates.xml',
'views/lunch_templates.xml',
'views/lunch_views.xml',
'data/lunch_data.xml',
],
'demo': ['data/lunch_demo.xml'],
'qweb': ['static/src/xml/lunch.xml', ],
'installable': True,
'application': True,
'certificate': '001292377792581874189',
+60
View File
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="lunch_order_line_action_order" model="ir.actions.server">
<field name="name">Order meals</field>
<field name="model_id" ref="model_lunch_order_line"/>
<field name="state">code</field>
<field name="code">
if context.get('active_ids'):
self.order(cr, uid, context['active_ids'], context=context)
</field>
</record>
<record id="ir_lunch_order_line_action_order" model="ir.values">
<field name="model_id" ref="model_lunch_order_line" />
<field name="name">Order meals</field>
<field name="key2">client_action_multi</field>
<field eval="'ir.actions.server,%d'%lunch_order_line_action_order" name="value"/>
<field name="key">action</field>
<field name="model">lunch.order.line</field>
</record>
<record id="lunch_order_line_action_confirm" model="ir.actions.server">
<field name="name">Receive meals</field>
<field name="model_id" ref="model_lunch_order_line"/>
<field name="state">code</field>
<field name="code">
if context.get('active_ids'):
self.confirm(cr, uid, context['active_ids'], context=context)
</field>
</record>
<record id="ir_lunch_order_line_action_confirm" model="ir.values">
<field name="model_id" ref="model_lunch_order_line" />
<field name="name">Receive meals</field>
<field name="key2">client_action_multi</field>
<field eval="'ir.actions.server,%d'%lunch_order_line_action_confirm" name="value"/>
<field name="key">action</field>
<field name="model">lunch.order.line</field>
</record>
<record id="lunch_order_line_action_cancel" model="ir.actions.server">
<field name="name">Cancel meals</field>
<field name="model_id" ref="model_lunch_order_line"/>
<field name="state">code</field>
<field name="code">
if context.get('active_ids'):
self.cancel(cr, uid, context['active_ids'], context=context)
</field>
</record>
<record id="ir_lunch_order_line_action_cancel" model="ir.values">
<field name="model_id" ref="model_lunch_order_line" />
<field name="name">Cancel meals</field>
<field name="key2">client_action_multi</field>
<field eval="'ir.actions.server,%d'%lunch_order_line_action_cancel" name="value"/>
<field name="key">action</field>
<field name="model">lunch.order.line</field>
</record>
</odoo>
+31 -18
View File
@@ -1,9 +1,9 @@
<?xml version="1.0"?>
<openerp>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="base.user_demo" model="res.users">
<field name="groups_id" eval="[(4,ref('lunch.group_lunch_user'))]"/>
<field name="groups_id" eval="[(4, ref('lunch.group_lunch_user'))]"/>
</record>
<record model="lunch.product.category" id="categ_sandwich">
@@ -109,29 +109,30 @@
<record model="lunch.order" id="order_1">
<field name="user_id" ref="base.user_demo"/>
<field name="date" eval="time.strftime('2012-10-23')"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d')"/>
<field name="order_line_ids" eval="[]"/>
<field name="state">new</field>
</record>
<record model="lunch.order" id="order_2">
<field name="user_id" ref="base.user_demo"/>
<field name="date" eval="time.strftime('2012-10-22')"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d')"/>
<field name="order_line_ids" eval="[]"/>
<field name="state">confirmed</field>
</record>
<record model="lunch.order" id="order_3">
<field name="user_id" ref="base.user_demo"/>
<field name="date" eval="time.strftime('2012-10-24')"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d')"/>
<field name="order_line_ids" eval="[]"/>
<field name="state">partially</field>
<field name="state">confirmed</field>
</record>
<record model="lunch.order.line" id="order_line_1">
<field name="user_id" ref="base.user_demo"/>
<field name="product_id" ref="product_Bolognese"/>
<field name="date" eval="time.strftime('2012-10-23')"/>
<field name="price">7.70</field>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d')"/>
<field name="state">new</field>
<field name="supplier" ref="partner_pizza_inn"/>
<field name="note">+Emmental</field>
@@ -141,7 +142,8 @@
<record model="lunch.order.line" id="order_line_2">
<field name="user_id" ref="base.user_demo"/>
<field name="product_id" ref="product_italiana"/>
<field name="date" eval="time.strftime('2012-10-22')"/>
<field name="price">7.40</field>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d')"/>
<field name="state">confirmed</field>
<field name="supplier" ref="partner_pizza_inn"/>
<field name="note">+Mushrooms</field>
@@ -151,7 +153,8 @@
<record model="lunch.order.line" id="order_line_3">
<field name="user_id" ref="base.user_demo"/>
<field name="product_id" ref="product_gouda"/>
<field name="date" eval="time.strftime('2012-10-24')"/>
<field name="price">2.50</field>
<field name="date" eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d')"/>
<field name="state">cancelled</field>
<field name="supplier" ref="partner_coin_gourmand"/>
<field name="note">+Salad +Tomatoes +Cucumbers</field>
@@ -161,16 +164,17 @@
<record model="lunch.order.line" id="order_line_4">
<field name="user_id" ref="base.user_demo"/>
<field name="product_id" ref="product_chicken_curry"/>
<field name="date" eval="time.strftime('2012-10-24')"/>
<field name="price">2.60</field>
<field name="date" eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d')"/>
<field name="state">confirmed</field>
<field name="supplier" ref="partner_coin_gourmand"/>
<field name="note">+Salad +Tomatoes +Eggs</field>
<field name="order_id" ref="order_3"/>
</record>
<record model="lunch.cashmove" id="cashmove_1">
<field name="user_id" ref="base.user_demo"/>
<field name="date" eval="time.strftime('2012-10-23')"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d')"/>
<field name="description">Pizza Italiana</field>
<field name="amount">-7.40</field>
<field name="order_id" ref="order_2"/>
@@ -179,7 +183,7 @@
<record model="lunch.cashmove" id="cashmove_2">
<field name="user_id" ref="base.user_demo"/>
<field name="date" eval="time.strftime('2012-10-24')"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d')"/>
<field name="description">Chicken curry</field>
<field name="amount">-2.60</field>
<field name="order_id" ref="order_3"/>
@@ -188,15 +192,24 @@
<record model="lunch.cashmove" id="cashmove_3">
<field name="user_id" ref="base.user_demo"/>
<field name="date" eval="time.strftime('2012-10-24')"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d')"/>
<field name="description">Payment: 5 lunch tickets (6€)</field>
<field name="amount">30</field>
<field name="state">payment</field>
</record>
<record model="lunch.alert" id="alert_1">
<field name="message">Lunch must be ordered before 10h30 am</field>
<field name="alert_type">week</field>
<field name="monday" eval="True"/>
<field name="tuesday" eval="True"/>
<field name="wednesday" eval="True"/>
<field name="thursday" eval="True"/>
<field name="friday" eval="True"/>
<field name="saturday" eval="False"/>
<field name="sunday" eval="False"/>
<field name="start_hour">7</field>
<field name="end_hour">23</field>
<field name="message">Lunch must be ordered before 10:30 AM</field>
</record>
</data>
</openerp>
</odoo>
+1 -1
View File
@@ -1,3 +1,3 @@
# -*- encoding: utf-8 -*-
# -*- coding: utf-8 -*-
import lunch
+232 -455
View File
@@ -1,501 +1,278 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# -*- coding: utf-8 -*-
from xml.sax.saxutils import escape
import time
from openerp.osv import fields, osv
from datetime import datetime
from lxml import etree
from openerp import tools
from openerp.tools.translate import _
import datetime
class lunch_order(osv.Model):
"""
lunch order (contains one or more lunch order line(s))
from openerp import models, fields, api, _
import openerp.addons.decimal_precision as dp
from openerp.exceptions import UserError
class LunchOrder(models.Model):
"""
A lunch order contains one or more lunch order line(s). It is associated to a user for a given
date. When creating a lunch order, applicable lunch alerts are displayed.
"""
_name = 'lunch.order'
_description = 'Lunch Order'
_order = 'date desc'
def name_get(self, cr, uid, ids, context=None):
if not ids:
return []
res = []
for elmt in self.browse(cr, uid, ids, context=context):
name = _("Lunch Order")
name = name + ' ' + str(elmt.id)
res.append((elmt.id, name))
return res
def _price_get(self, cr, uid, ids, name, arg, context=None):
"""
user_id = fields.Many2one('res.users', 'User', required=True, readonly=True,
states={'new': [('readonly', False)]},
default=lambda self: self.env.uid)
date = fields.Date('Date', required=True, readonly=True,
states={'new': [('readonly', False)]},
default=fields.Date.context_today)
order_line_ids = fields.One2many('lunch.order.line', 'order_id', 'Products',
ondelete="cascade", readonly=True, copy=True,
states={'new': [('readonly', False)], False: [('readonly', False)]})
total = fields.Float(compute='_compute_total', string="Total", store=True)
state = fields.Selection([('new', 'New'),
('confirmed', 'Received'),
('cancelled', 'Cancelled')],
'Status', readonly=True, index=True, copy=False, default='new',
compute='_compute_order_state')
alerts = fields.Text(compute='_compute_alerts_get', string="Alerts")
previous_order_ids = fields.One2many(comodel_name='lunch.order.line',
compute='_compute_get_previous_order_ids')
company_id = fields.Many2one('res.company', related='user_id.company_id', store=True)
currency_id = fields.Many2one('res.currency', related='company_id.currency_id', readonly=True, store=True)
@api.one
@api.depends('order_line_ids')
def _compute_total(self):
"""
get and sum the order lines' price
"""
result = dict.fromkeys(ids, 0)
for order in self.browse(cr, uid, ids, context=context):
result[order.id] = sum(order_line.product_id.price
for order_line in order.order_line_ids)
return result
self.total = sum(
orderline.price for orderline in self.order_line_ids)
def _fetch_orders_from_lines(self, cr, uid, ids, name, context=None):
"""
return the list of lunch orders to which belong the order lines `ids´
@api.multi
def name_get(self):
return [(order.id, '%s %s' % (_('Lunch Order'), '#%d' % order.id)) for order in self]
@api.depends('state')
def _compute_alerts_get(self):
"""
result = set()
for order_line in self.browse(cr, uid, ids, context=context):
if order_line.order_id:
result.add(order_line.order_id.id)
return list(result)
def add_preference(self, cr, uid, ids, pref_id, context=None):
"""
create a new order line based on the preference selected (pref_id)
"""
assert len(ids) == 1
orderline_ref = self.pool.get('lunch.order.line')
prod_ref = self.pool.get('lunch.product')
order = self.browse(cr, uid, ids[0], context=context)
pref = orderline_ref.browse(cr, uid, pref_id, context=context)
new_order_line = {
'date': order.date,
'user_id': uid,
'product_id': pref.product_id.id,
'note': pref.note,
'order_id': order.id,
'price': pref.product_id.price,
'supplier': pref.product_id.supplier.id
}
return orderline_ref.create(cr, uid, new_order_line, context=context)
def _alerts_get(self, cr, uid, ids, name, arg, context=None):
"""
get the alerts to display on the order form
"""
result = {}
alert_msg = self._default_alerts_get(cr, uid, context=context)
for order in self.browse(cr, uid, ids, context=context):
if order.state == 'new':
result[order.id] = alert_msg
return result
def check_day(self, alert):
"""
This method is used by can_display_alert
to check if the alert day corresponds
to the current day
"""
today = datetime.now().isoweekday()
assert 1 <= today <= 7, "Should be between 1 and 7"
mapping = dict((idx, name) for idx, name in enumerate('days monday tuesday wednesday thursday friday saturday sunday'.split()))
return alert[mapping[today]]
def can_display_alert(self, alert):
"""
This method check if the alert can be displayed today
"""
if alert.alter_type == 'specific':
#the alert is only activated on a specific day
return alert.specific_day == time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
elif alert.alter_type == 'week':
#the alert is activated during some days of the week
return self.check_day(alert)
return True # alter_type == 'days' (every day)
def _default_alerts_get(self, cr, uid, context=None):
"""
get the alerts to display on the order form
"""
alert_ref = self.pool.get('lunch.alert')
alert_ids = alert_ref.search(cr, uid, [], context=context)
alert_msg = []
for alert in alert_ref.browse(cr, uid, alert_ids, context=context):
#check if the address must be displayed today
if self.can_display_alert(alert):
#display the address only during its active time
mynow = fields.datetime.context_timestamp(cr, uid, datetime.now(), context=context)
hour_to = int(alert.active_to)
min_to = int((alert.active_to - hour_to) * 60)
to_alert = datetime.strptime(str(hour_to) + ":" + str(min_to), "%H:%M")
hour_from = int(alert.active_from)
min_from = int((alert.active_from - hour_from) * 60)
from_alert = datetime.strptime(str(hour_from) + ":" + str(min_from), "%H:%M")
if mynow.time() >= from_alert.time() and mynow.time() <= to_alert.time():
alert_msg.append(alert.message)
return '\n'.join(alert_msg)
alert_msg = [alert.message
for alert in self.env['lunch.alert'].search([])
if alert.display]
def onchange_price(self, cr, uid, ids, order_line_ids, context=None):
if self.state == 'new':
self.alerts = '\n'.join(alert_msg)
@api.depends('state')
def _compute_get_previous_order_ids(self):
prev_order = self.env['lunch.order.line'].search([('user_id', '=', self.env.uid)], limit=20, order='id desc')
# If we return return prev_order.ids, we will have duplicates (identical orders).
# Therefore, this following part removes duplicates based on product_id and note.
self.previous_order_ids = {
(order.product_id, order.note): order.id
for order in prev_order
}.values()
@api.onchange('total')
def _onchange_total(self):
"""
Onchange methode that refresh the total price of order
Calculates the forecasted balance of the user, taking into account the current order
"""
res = {'value': {'total': 0.0}}
order_line_ids = self.resolve_o2m_commands_to_record_dicts(cr, uid, "order_line_ids", order_line_ids, ["price"], context=context)
if order_line_ids:
tot = 0.0
product_ref = self.pool.get("lunch.product")
for prod in order_line_ids:
if 'product_id' in prod:
tot += product_ref.browse(cr, uid, prod['product_id'], context=context).price
prev_cashmove = self.env['lunch.cashmove'].search([('user_id', '=', self.user_id.id)])
balance = sum(cashmove.amount for cashmove in prev_cashmove)
if self.total > balance:
raise UserError('%s (%s %s)' % (_('Insufficient balance. The total amount of the order is larger than the available balance'), balance, self.currency_id.name))
@api.one
@api.constrains('date')
def _check_date(self):
"""
Prevents the user to create an order in the past
"""
date_order = datetime.datetime.strptime(self.date, '%Y-%m-%d')
date_today = datetime.datetime.strptime(fields.Date.context_today(self), '%Y-%m-%d')
if (date_order < date_today):
raise UserError(_('The date of your order is in the past.'))
@api.one
@api.depends('order_line_ids.state')
def _compute_order_state(self):
"""
Update the state of lunch.order based on its orderlines. Here is the logic:
- if at least one order line is cancelled, the order is set as cancelled
- if no line is cancelled but at least one line is not confirmed, the order is set as new
- if all lines are confirmed, the order is set as confirmed
"""
if not self.order_line_ids:
self.state = 'new'
else:
isConfirmed = True
for orderline in self.order_line_ids:
if orderline.state == 'cancelled':
self.state = 'cancelled'
return
elif orderline.state == 'confirmed':
continue
else:
tot += prod['price']
res = {'value': {'total': tot}}
return res
isConfirmed = False
def __getattr__(self, attr):
"""
this method catch unexisting method call and if it starts with
add_preference_'n' we execute the add_preference method with
'n' as parameter
"""
if attr.startswith('add_preference_'):
pref_id = int(attr[15:])
def specific_function(cr, uid, ids, context=None):
return self.add_preference(cr, uid, ids, pref_id, context=context)
return specific_function
return super(lunch_order, self).__getattr__(attr)
def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
"""
Add preferences in the form view of order.line
"""
res = super(lunch_order,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
line_ref = self.pool.get("lunch.order.line")
if view_type == 'form':
doc = etree.XML(res['arch'])
pref_ids = line_ref.search(cr, uid, [('user_id', '=', uid)], order='id desc', context=context)
xml_start = etree.Element("div")
#If there are no preference (it's the first time for the user)
if len(pref_ids)==0:
#create Elements
xml_no_pref_1 = etree.Element("div")
xml_no_pref_1.set('class','oe_inline oe_lunch_intro')
xml_no_pref_2 = etree.Element("h3")
xml_no_pref_2.text = _("This is the first time you order a meal")
xml_no_pref_3 = etree.Element("p")
xml_no_pref_3.set('class','oe_grey')
xml_no_pref_3.text = _("Select a product and put your order comments on the note.")
xml_no_pref_4 = etree.Element("p")
xml_no_pref_4.set('class','oe_grey')
xml_no_pref_4.text = _("Your favorite meals will be created based on your last orders.")
xml_no_pref_5 = etree.Element("p")
xml_no_pref_5.set('class','oe_grey')
xml_no_pref_5.text = _("Don't forget the alerts displayed in the reddish area")
#structure Elements
xml_start.append(xml_no_pref_1)
xml_no_pref_1.append(xml_no_pref_2)
xml_no_pref_1.append(xml_no_pref_3)
xml_no_pref_1.append(xml_no_pref_4)
xml_no_pref_1.append(xml_no_pref_5)
#Else: the user already have preferences so we display them
if isConfirmed:
self.state = 'confirmed'
else:
preferences = line_ref.browse(cr, uid, pref_ids, context=context)
categories = {} #store the different categories of products in preference
count = 0
for pref in preferences:
#For each preference
categories.setdefault(pref.product_id.category_id.name, {})
#if this product has already been added to the categories dictionnary
if pref.product_id.id in categories[pref.product_id.category_id.name]:
#we check if for the same product the note has already been added
if pref.note not in categories[pref.product_id.category_id.name][pref.product_id.id]:
#if it's not the case then we add this to preferences
categories[pref.product_id.category_id.name][pref.product_id.id][pref.note] = pref
#if this product is not in the dictionnay, we add it
else:
categories[pref.product_id.category_id.name][pref.product_id.id] = {}
categories[pref.product_id.category_id.name][pref.product_id.id][pref.note] = pref
currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id
#For each preferences that we get, we will create the XML structure
for key, value in categories.items():
xml_pref_1 = etree.Element("div")
xml_pref_1.set('class', 'oe_lunch_30pc')
xml_pref_2 = etree.Element("h2")
xml_pref_2.text = key
xml_pref_1.append(xml_pref_2)
i = 0
value = value.values()
#TODO: sorted_values is used for a quick and dirty hack in order to display the 5 last orders of each categories.
#It would be better to fetch only the 5 items to display instead of fetching everything then sorting them in order to keep only the 5 last.
#NB: The note could also be ignored + we could fetch the preferences on the most ordered products instead of the last ones...
sorted_values = {}
for val in value:
for elmt in val.values():
sorted_values[elmt.id] = elmt
for key, pref in sorted(sorted_values.iteritems(), key=lambda (k, v): (k, v), reverse=True):
#We only show 5 preferences per category (or it will be too long)
if i == 5:
break
i += 1
xml_pref_3 = etree.Element("div")
xml_pref_3.set('class','oe_lunch_vignette')
xml_pref_1.append(xml_pref_3)
xml_pref_4 = etree.Element("span")
xml_pref_4.set('class','oe_lunch_button')
xml_pref_3.append(xml_pref_4)
xml_pref_5 = etree.Element("button")
xml_pref_5.set('name',"add_preference_"+str(pref.id))
xml_pref_5.set('class','oe_link oe_i oe_button_plus')
xml_pref_5.set('type','object')
xml_pref_5.set('string','+')
xml_pref_4.append(xml_pref_5)
xml_pref_6 = etree.Element("button")
xml_pref_6.set('name',"add_preference_"+str(pref.id))
xml_pref_6.set('class','oe_link oe_button_add')
xml_pref_6.set('type','object')
xml_pref_6.set('string',_("Add"))
xml_pref_4.append(xml_pref_6)
xml_pref_7 = etree.Element("div")
xml_pref_7.set('class','oe_group_text_button')
xml_pref_3.append(xml_pref_7)
xml_pref_8 = etree.Element("div")
xml_pref_8.set('class','oe_lunch_text')
xml_pref_8.text = escape(pref.product_id.name)+str(" ")
xml_pref_7.append(xml_pref_8)
price = pref.product_id.price or 0.0
cur = currency.name or ''
xml_pref_9 = etree.Element("span")
xml_pref_9.set('class','oe_tag')
xml_pref_9.text = str(price)+str(" ")+cur
xml_pref_8.append(xml_pref_9)
xml_pref_10 = etree.Element("div")
xml_pref_10.set('class','oe_grey')
xml_pref_10.text = escape(pref.note or '')
xml_pref_3.append(xml_pref_10)
xml_start.append(xml_pref_1)
first_node = doc.xpath("//div[@name='preferences']")
if first_node and len(first_node)>0:
first_node[0].append(xml_start)
res['arch'] = etree.tostring(doc)
return res
_columns = {
'user_id': fields.many2one('res.users', 'User Name', required=True, readonly=True, states={'new':[('readonly', False)]}),
'date': fields.date('Date', required=True, readonly=True, states={'new':[('readonly', False)]}),
'order_line_ids': fields.one2many('lunch.order.line', 'order_id', 'Products',
ondelete="cascade", readonly=True, states={'new':[('readonly', False)]},
copy=True),
'total': fields.function(_price_get, string="Total", store={
'lunch.order.line': (_fetch_orders_from_lines, ['product_id','order_id'], 20),
}),
'state': fields.selection([('new', 'New'), \
('confirmed','Confirmed'), \
('cancelled','Cancelled'), \
('partially','Partially Confirmed')] \
,'Status', readonly=True, select=True, copy=False),
'alerts': fields.function(_alerts_get, string="Alerts", type='text'),
}
_defaults = {
'user_id': lambda self, cr, uid, context: uid,
'date': fields.date.context_today,
'state': 'new',
'alerts': _default_alerts_get,
}
self.state = 'new'
return
class lunch_order_line(osv.Model):
"""
lunch order line: one lunch order can have many order lines
"""
class LunchOrderLine(models.Model):
_name = 'lunch.order.line'
_description = 'lunch order line'
def onchange_price(self, cr, uid, ids, product_id, context=None):
if product_id:
price = self.pool.get('lunch.product').browse(cr, uid, product_id, context=context).price
return {'value': {'price': price}}
return {'value': {'price': 0.0}}
name = fields.Char(related='product_id.name', readonly=True)
order_id = fields.Many2one('lunch.order', 'Order', ondelete='cascade', required=True)
product_id = fields.Many2one('lunch.product', 'Product', required=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)
supplier = fields.Many2one('res.partner', string='Supplier', related='product_id.supplier',
readonly=True, store=True)
user_id = fields.Many2one('res.users', string='User', related='order_id.user_id',
readonly=True, store=True)
note = fields.Text('Note')
price = fields.Float(related='product_id.price', readonly=True, store=True,
digits_compute=dp.get_precision('Account'))
state = fields.Selection([('new', 'New'),
('confirmed', 'Received'),
('ordered', 'Ordered'),
('cancelled', 'Cancelled')],
'Status', readonly=True, select=True, default='new')
cashmove = fields.One2many('lunch.cashmove', 'order_id', 'Cash Move')
currency_id = fields.Many2one('res.currency', related='order_id.currency_id')
def order(self, cr, uid, ids, context=None):
"""
@api.one
def order(self):
"""
The order_line is ordered to the supplier but isn't received yet
"""
self.write(cr, uid, ids, {'state': 'ordered'}, context=context)
return self._update_order_lines(cr, uid, ids, context=context)
self.state = 'ordered'
def confirm(self, cr, uid, ids, context=None):
"""
confirm one or more order line, update order status and create new cashmove
@api.one
def confirm(self):
"""
cashmove_ref = self.pool.get('lunch.cashmove')
for order_line in self.browse(cr, uid, ids, context=context):
if order_line.state != 'confirmed':
values = {
'user_id': order_line.user_id.id,
'amount': -order_line.price,
'description': order_line.product_id.name,
'order_id': order_line.id,
'state': 'order',
'date': order_line.date,
}
cashmove_ref.create(cr, uid, values, context=context)
order_line.write({'state': 'confirmed'})
return self._update_order_lines(cr, uid, ids, context=context)
confirm one or more order line, update order status and create new cashmove
"""
if self.state != 'confirmed':
values = {
'user_id': self.user_id.id,
'amount': -self.price,
'description': self.product_id.name,
'order_id': self.id,
'state': 'order',
'date': self.date,
}
self.env['lunch.cashmove'].create(values)
self.state = 'confirmed'
def _update_order_lines(self, cr, uid, ids, context=None):
"""
Update the state of lunch.order based on its orderlines
"""
orders_ref = self.pool.get('lunch.order')
orders = []
for order_line in self.browse(cr, uid, ids, context=context):
orders.append(order_line.order_id)
for order in set(orders):
isconfirmed = True
for orderline in order.order_line_ids:
if orderline.state == 'new':
isconfirmed = False
if orderline.state == 'cancelled':
isconfirmed = False
orders_ref.write(cr, uid, [order.id], {'state': 'partially'}, context=context)
if isconfirmed:
orders_ref.write(cr, uid, [order.id], {'state': 'confirmed'}, context=context)
return {}
def cancel(self, cr, uid, ids, context=None):
@api.one
def cancel(self):
"""
cancel one or more order.line, update order status and unlink existing cashmoves
"""
cashmove_ref = self.pool.get('lunch.cashmove')
self.write(cr, uid, ids, {'state':'cancelled'}, context=context)
for order_line in self.browse(cr, uid, ids, context=context):
cash_ids = [cash.id for cash in order_line.cashmove]
cashmove_ref.unlink(cr, uid, cash_ids, context=context)
return self._update_order_lines(cr, uid, ids, context=context)
def _get_line_order_ids(self, cr, uid, ids, context=None):
"""
return the list of lunch.order.lines ids to which belong the lunch.order 'ids'
"""
result = set()
for lunch_order in self.browse(cr, uid, ids, context=context):
for lines in lunch_order.order_line_ids:
result.add(lines.id)
return list(result)
_columns = {
'name': fields.related('product_id', 'name', readonly=True),
'order_id': fields.many2one('lunch.order', 'Order', ondelete='cascade'),
'product_id': fields.many2one('lunch.product', 'Product', required=True),
'date': fields.related('order_id', 'date', type='date', string="Date", readonly=True, store={
'lunch.order': (_get_line_order_ids, ['date'], 10),
'lunch.order.line': (lambda self, cr, uid, ids, ctx: ids, [], 10),
}),
'supplier': fields.related('product_id', 'supplier', type='many2one', relation='res.partner', string="Supplier", readonly=True, store=True),
'user_id': fields.related('order_id', 'user_id', type='many2one', relation='res.users', string='User', readonly=True, store=True),
'note': fields.text('Note'),
'price': fields.float("Price"),
'state': fields.selection([('new', 'New'), \
('confirmed', 'Received'), \
('ordered', 'Ordered'), \
('cancelled', 'Cancelled')], \
'Status', readonly=True, select=True),
'cashmove': fields.one2many('lunch.cashmove', 'order_id', 'Cash Move', ondelete='cascade'),
}
_defaults = {
'state': 'new',
}
self.state = 'cancelled'
self.cashmove.unlink()
class lunch_product(osv.Model):
"""
lunch product
"""
class LunchProduct(models.Model):
""" Products available to order. A product is linked to a specific supplier. """
_name = 'lunch.product'
_description = 'lunch product'
_columns = {
'name': fields.char('Product', required=True),
'category_id': fields.many2one('lunch.product.category', 'Category', required=True),
'description': fields.text('Description', size=256),
'price': fields.float('Price', digits=(16,2)), #TODO: use decimal precision of 'Account', move it from product to decimal_precision
'supplier': fields.many2one('res.partner', 'Supplier'),
}
class lunch_product_category(osv.Model):
"""
lunch product category
"""
name = fields.Char('Product', required=True)
category_id = fields.Many2one('lunch.product.category', 'Category', required=True)
description = fields.Text('Description')
price = fields.Float('Price', digits_compute=dp.get_precision('Account'))
supplier = fields.Many2one('res.partner', 'Supplier')
class LunchProductCategory(models.Model):
""" Category of the product such as pizza, sandwich, pasta, chinese, burger... """
_name = 'lunch.product.category'
_description = 'lunch product category'
_columns = {
'name': fields.char('Category', required=True), #such as PIZZA, SANDWICH, PASTA, CHINESE, BURGER, ...
}
class lunch_cashmove(osv.Model):
"""
lunch cashmove => order or payment
"""
name = fields.Char('Category', required=True)
class LunchCashMove(models.Model):
""" Two types of cashmoves: payment (credit) or order (debit) """
_name = 'lunch.cashmove'
_description = 'lunch cashmove'
_columns = {
'user_id': fields.many2one('res.users', 'User Name', required=True),
'date': fields.date('Date', required=True),
'amount': fields.float('Amount', required=True), #depending on the kind of cashmove, the amount will be positive or negative
'description': fields.text('Description'), #the description can be an order or a payment
'order_id': fields.many2one('lunch.order.line', 'Order', ondelete='cascade'),
'state': fields.selection([('order','Order'), ('payment','Payment')], 'Is an order or a Payment'),
}
_defaults = {
'user_id': lambda self, cr, uid, context: uid,
'date': fields.date.context_today,
'state': 'payment',
}
class lunch_alert(osv.Model):
"""
lunch alert
"""
user_id = fields.Many2one('res.users', 'User', required=True,
default=lambda self: self.env.uid)
date = fields.Date('Date', required=True, default=fields.Date.context_today)
amount = fields.Float('Amount', required=True, help='Can be positive (payment) or negative (order or payment if user wants to get his money back)')
description = fields.Text('Description', help='Can be an order or a payment')
order_id = fields.Many2one('lunch.order.line', 'Order', ondelete='cascade')
state = fields.Selection([('order', 'Order'), ('payment', 'Payment')],
'Is an order or a payment', default='payment')
@api.multi
def name_get(self):
return [(cashmove.id, '%s %s' % (_('Lunch Cashmove'), '#%d' % cashmove.id)) for cashmove in self]
class LunchAlert(models.Model):
""" Alerts to display during a lunch order. An alert can be specific to a
given day, weekly or daily. The alert is displayed from start to end hour. """
_name = 'lunch.alert'
_description = 'Lunch Alert'
_columns = {
'message': fields.text('Message', size=256, required=True),
'alter_type': fields.selection([('specific', 'Specific Day'), \
('week', 'Every Week'), \
('days', 'Every Day')], \
string='Recurrency', required=True, select=True),
'specific_day': fields.date('Day'),
'monday': fields.boolean('Monday'),
'tuesday': fields.boolean('Tuesday'),
'wednesday': fields.boolean('Wednesday'),
'thursday': fields.boolean('Thursday'),
'friday': fields.boolean('Friday'),
'saturday': fields.boolean('Saturday'),
'sunday': fields.boolean('Sunday'),
'active_from': fields.float('Between', required=True),
'active_to': fields.float('And', required=True),
}
_defaults = {
'alter_type': 'specific',
'specific_day': fields.date.context_today,
'active_from': 7,
'active_to': 23,
}
display = fields.Boolean(compute='_compute_display_get')
message = fields.Text('Message', required=True)
alert_type = fields.Selection([('specific', 'Specific Day'),
('week', 'Every Week'),
('days', 'Every Day')],
string='Recurrency', required=True, select=True, default='specific')
specific_day = fields.Date('Day', default=fields.Date.context_today)
monday = fields.Boolean('Monday')
tuesday = fields.Boolean('Tuesday')
wednesday = fields.Boolean('Wednesday')
thursday = fields.Boolean('Thursday')
friday = fields.Boolean('Friday')
saturday = fields.Boolean('Saturday')
sunday = fields.Boolean('Sunday')
start_hour = fields.Float('Between', oldname='active_from', required=True, default=7)
end_hour = fields.Float('And', oldname='active_to', required=True, default=23)
@api.multi
def name_get(self):
return [(alert.id, '%s %s' % (_('Alert'), '#%d' % alert.id)) for alert in self]
@api.one
def _compute_display_get(self):
"""
This method check if the alert can be displayed today
if alert type is specific : compare specific_day(date) with today's date
if alert type is week : check today is set as alert (checkbox true) eg. self['monday']
if alert type is day : True
return : Message if can_display_alert is True else False
"""
can_display_alert = {
'specific': (self.specific_day == fields.Date.context_today(self)),
'week': self[datetime.datetime.now().strftime('%A').lower()],
'days': True
}
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)
if from_alert <= mynow.time() <= to_alert:
self.display = True
else:
self.display = False
-4
View File
@@ -1,4 +0,0 @@
# -*- encoding: utf-8 -*-
import order
import report_lunch_order
+70 -69
View File
@@ -1,79 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="report_lunchorder">
<t t-call="report.html_container">
<t t-call="report.external_layout">
<div class="page">
<div class="oe_structure"/>
<odoo>
<template id="report_lunch_order">
<t t-call="report.html_container">
<t t-call="report.external_layout">
<div class="page">
<div class="oe_structure"/>
<div class="row">
<div class="col-xs-5 col-xs-offset-7">
<div t-field="user.partner_id"
t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' />
<div class="row">
<div class="col-xs-5 col-xs-offset-7">
<div t-field="user.partner_id"
t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' />
</div>
</div>
</div>
<h2>Lunch Order</h2>
<h2>Lunch Order</h2>
<table class="table table-condensed">
<thead>
<tr>
<th>Name/Date</th>
<th>Order</th>
<th>Description</th>
<th class="text-right">Unit Price</th>
</tr>
</thead>
<tbody>
<t t-foreach="get_users(docs)" t-as="o">
<table class="table table-condensed">
<thead>
<tr>
<td colspan="2">
<strong t-field="o.name"/>
</td>
<td class="text-right" colspan="2">
<strong>
<span t-esc="get_total(o, docs)"/>
<span t-field="o.company_id.currency_id.symbol"/>
</strong>
</td>
<th>Name/Date</th>
<th>Order</th>
<th>Description</th>
<th class="text-right">Unit Price</th>
</tr>
<tr t-foreach="get_lines(o, docs)" t-as="lines">
<td>
<span t-field="lines.date"></span>
</td>
<td>
<span t-field="lines.product_id.name"/>
</td>
<td>
<span t-field="lines.note"/>
</td>
<td class="text-right">
<span t-field="lines.price"
t-field-options='{"widget": "monetary", "display_currency": "o.company_id.currency_id"}'/>
</td>
</tr>
</t>
</tbody>
</table>
</thead>
<tbody>
<t t-foreach="docs.read_group([('id', 'in', docs.ids)],['user_id'],['user_id'])" t-as="o">
<t t-set="user" t-value="user.browse(o['user_id'][0])"/>
<t t-set="lines" t-value="docs.search([('user_id', '=', user.id), ('id', 'in', docs.ids)])"/>
<tr>
<td colspan="2">
<strong t-field="user.name"/>
</td>
<td class="text-right" colspan="2">
<strong>
<span t-esc="sum(line.price for line in lines)"/>
<span t-field="user.company_id.currency_id.symbol"/>
</strong>
</td>
</tr>
<tr t-foreach="lines" t-as="line">
<td>
<span t-field="line.date"></span>
</td>
<td>
<span t-field="line.product_id.name"/>
</td>
<td>
<span t-field="line.note"/>
</td>
<td class="text-right">
<span t-field="line.price"
t-field-options='{"widget": "monetary", "display_currency": "user.company_id.currency_id"}'/>
</td>
</tr>
</t>
</tbody>
</table>
<div class="row">
<div class="col-xs-4 pull-right">
<table class="table table-condensed">
<tr class="border-black">
<td><strong>Total</strong></td>
<td class="text-right">
<span t-esc="formatLang(get_nettotal(), currency_obj=res_company.currency_id)"/>
</td>
</tr>
</table>
<div class="row">
<div class="col-xs-4 pull-right">
<table class="table table-condensed">
<tr class="border-black">
<td><strong>Total</strong></td>
<td class="text-right">
<span t-esc="sum([line.price for line in docs]) or 0.0"/>
<span t-field="user.company_id.currency_id.symbol"/>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="oe_structure"/>
</div>
</t>
</t>
</template>
</data>
</openerp>
<div class="oe_structure"/>
</div>
</t>
</t>
</template>
</odoo>
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<report
id="action_report_lunch_order"
string="Lunch Order"
model="lunch.order.line"
report_type="qweb-pdf"
name="lunch.report_lunch_order"
file="lunch.report_lunch_order"
/>
</odoo>
@@ -1,13 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
<report
id="action_report_lunch_order"
string="Lunch Order"
model="lunch.order.line"
report_type="qweb-pdf"
name="lunch.report_lunchorder"
file="lunch.report_lunchorder"
/>
</data>
</openerp>
-80
View File
@@ -1,80 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
from openerp.report import report_sxw
from openerp.osv import osv
class order(report_sxw.rml_parse):
def get_lines(self, user,objects):
lines=[]
for obj in objects:
if user.id==obj.user_id.id:
lines.append(obj)
return lines
def get_total(self, user,objects):
lines=[]
for obj in objects:
if user.id==obj.user_id.id:
lines.append(obj)
total=0.0
for line in lines:
total+=line.price
self.net_total+=total
return total
def get_nettotal(self):
return self.net_total
def get_users(self, objects):
users=[]
for obj in objects:
if obj.user_id not in users:
users.append(obj.user_id)
return users
def get_note(self,objects):
notes=[]
for obj in objects:
notes.append(obj.note)
return notes
def __init__(self, cr, uid, name, context):
super(order, self).__init__(cr, uid, name, context)
self.net_total=0.0
self.localcontext.update({
'time': time,
'get_lines': self.get_lines,
'get_users': self.get_users,
'get_total': self.get_total,
'get_nettotal': self.get_nettotal,
'get_note': self.get_note,
})
class report_lunchorder(osv.AbstractModel):
_name = 'report.lunch.report_lunchorder'
_inherit = 'report.abstract_report'
_template = 'lunch.report_lunchorder'
_wrapped_report_class = order
-62
View File
@@ -1,62 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import tools
from openerp.osv import fields,osv
class report_lunch_order(osv.osv):
_name = "report.lunch.order.line"
_description = "Lunch Orders Statistics"
_auto = False
_rec_name = 'date'
_columns = {
'date': fields.date('Date Order', readonly=True, select=True),
'year': fields.char('Year', size=4, readonly=True),
'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'),
('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'),
('10','October'), ('11','November'), ('12','December')], 'Month', readonly=True),
'day': fields.char('Day', size=128, readonly=True),
'user_id': fields.many2one('res.users', 'User Name'),
'price_total':fields.float('Total Price', readonly=True),
'note' : fields.text('Note', readonly=True),
}
_order = 'date desc'
def init(self, cr):
tools.drop_view_if_exists(cr, 'report_lunch_order_line')
cr.execute("""
create or replace view report_lunch_order_line as (
select
min(lo.id) as id,
lo.user_id as user_id,
lo.date as date,
to_char(lo.date, 'YYYY') as year,
to_char(lo.date, 'MM') as month,
to_char(lo.date, 'YYYY-MM-DD') as day,
lo.note as note,
sum(lp.price) as price_total
from
lunch_order_line as lo
left join lunch_product as lp on (lo.product_id = lp.id)
group by
lo.date,lo.user_id,lo.note
)
""")
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
</data>
</openerp>
@@ -11,6 +11,3 @@ cashmove_user,"Cashmove user",model_lunch_cashmove,group_lunch_user,1,0,0,0
product_user,"Product user",model_lunch_product,group_lunch_user,1,0,0,0
product_category_user,"Product category user",model_lunch_product_category,group_lunch_user,1,0,0,0
alert_user,"Alert user",model_lunch_alert,group_lunch_user,1,0,0,0
access_report_order_line,access_report_order_line,model_report_lunch_order_line,,1,1,1,1
access_lunch_validation,access_lunch_validation,model_lunch_validation,,1,1,1,1
access_lunch_cancel,access_lunch_cancel,model_lunch_cancel,,1,1,1,1
1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
11 product_user Product user model_lunch_product group_lunch_user 1 0 0 0
12 product_category_user Product category user model_lunch_product_category group_lunch_user 1 0 0 0
13 alert_user Alert user model_lunch_alert group_lunch_user 1 0 0 0
access_report_order_line access_report_order_line model_report_lunch_order_line 1 1 1 1
access_lunch_validation access_lunch_validation model_lunch_validation 1 1 1 1
access_lunch_cancel access_lunch_cancel model_lunch_cancel 1 1 1 1
+2 -5
View File
@@ -1,6 +1,5 @@
<?xml version="1.0" ?>
<openerp>
<data>
<odoo>
<record model="ir.module.category" id="module_lunch_category">
<field name="name">Lunch</field>
<field name="description">Helps you handle your lunch needs, if you are a manager you will be able to create new products, cashmoves and to confirm or cancel orders.</field>
@@ -29,6 +28,4 @@
<field name="groups" eval="[(4, ref('group_lunch_manager'))]"/>
<field name="domain_force">[(1, '=', 1)]</field>
</record>
</data>
</openerp>
</odoo>
+9 -24
View File
@@ -1,43 +1,28 @@
@charset "utf-8";
.openerp .oe_lunch .oe_lunch_alert textarea {
background-color: #ffc7c7;
padding: 10px;
height: 1em;
margin-bottom: 20px;
.openerp .o_lunch .o_lunch_alert {
height: 22px;
}
.openerp .oe_lunch button.oe_button_add {
position: relative;
top: -2px;
left: 3px;
}
.openerp .oe_lunch button.oe_button_plus {
margin: -2px;
}
.openerp .oe_lunch .oe_lunch_30pc {
.openerp .o_lunch .o_lunch_30pc {
width: 30%;
display: inline-block;
vertical-align: top;
}
.openerp .oe_lunch .oe_lunch_30pc:nth-child(3) {
.openerp .o_lunch .o_lunch_30pc:nth-child(3) {
padding-right: 0;
}
.openerp .oe_lunch .oe_lunch_30pc {
.openerp .o_lunch .o_lunch_30pc {
padding-right: 5%;
}
.openerp .oe_lunch h2 {
.openerp .o_lunch h2 {
color: #7c7bad;
}
.openerp .oe_lunch .oe_lunch_button {
float: right;
}
.openerp .oe_lunch .oe_lunch_vignette {
.openerp .o_lunch .o_lunch_vignette {
border-bottom: 1px solid #dddddd;
padding-top: 5px;
padding-bottom: 5px;
}
.openerp .oe_lunch .oe_lunch_vignette:last-child {
.openerp .o_lunch .o_lunch_vignette:last-child {
border: none;
}
.openerp .oe_lunch .oe_group_text_button {
.openerp .o_lunch .oe_group_text_button {
margin-bottom: 3px;
}
+8 -20
View File
@@ -1,36 +1,24 @@
@charset "utf-8"
.openerp
.oe_lunch
.oe_lunch_alert
textarea
background-color: #ffc7c7
padding: 10px
height: 1em
margin-bottom: 20px
button.oe_button_add
position: relative
top: -2px
left: 3px
button.oe_button_plus
margin: -2px
.oe_lunch_30pc
.o_lunch
.o_lunch_alert
height: 22px
.o_lunch_30pc
width: 30%
display: inline-block
vertical-align: top
.oe_lunch_30pc:nth-child(3)
.o_lunch_30pc:nth-child(3)
padding-right: 0
.oe_lunch_30pc
.o_lunch_30pc
padding-right: 5%
h2
color: #7C7BAD
.oe_lunch_button
float: right
.oe_lunch_vignette
.o_lunch_vignette
border-bottom: 1px solid #dddddd
padding-top: 5px
padding-bottom: 5px
.oe_lunch_vignette:last-child
.o_lunch_vignette:last-child
border: none
.oe_group_text_button
margin-bottom: 3px
+73
View File
@@ -0,0 +1,73 @@
odoo.define('lunch.form_widgets', function (require) {
"use strict";
var core = require('web.core');
var Model = require('web.Model');
var form_common = require('web.form_common');
var form_widgets = require('web.form_widgets');
var form_relational = require('web.form_relational');
var _t = core._t;
var QWeb = core.qweb;
var lunch = {};
lunch.lunch_order_widget_previous_orders = form_relational.AbstractManyField.extend(form_common.ReinitializeWidgetMixin, {
template : 'lunch_order_widget_previous_orders',
events: {
'click span.add_button': 'set_order_line',
},
init: function(field_manager, node){
this._super.apply(this, arguments);
this.lunch_data = {};
this.fields_to_read = ['product_id', 'supplier', 'note', 'price', 'category_id', 'currency_id'];
this.monetary = new form_widgets.FieldMonetary(field_manager, node); // create instance to use format_value
this.monetary.__edispatcherRegisteredEvents = []; // remove all bind events
},
fetch_value: function(){
var self = this;
return this.dataset.read_ids(this.get('value'), this.fields_to_read)
.done(function(data){
_.each(data, function(order){
self.lunch_data[order['id']] = order;
});
});
},
get_line_value: function (id) {
var data = _.clone(this.lunch_data[id]);
if (typeof this.lunch_data[id]['product_id'][0] != 'undefined'){
data['product_id'] = this.lunch_data[id]['product_id'][0];
}
if (typeof this.lunch_data[id]['supplier'][0] != 'undefined'){
data['supplier'] = this.lunch_data[id]['supplier'][0];
}
if (typeof this.lunch_data[id]['category_id'][0] != 'undefined'){
data['category_id'] = this.lunch_data[id]['category_id'][0];
}
return data;
},
set_order_line: function(event){
var data = this.get_line_value( parseInt($(event.currentTarget).attr('id')) );
var order_line_ids = this.field_manager.fields.order_line_ids;
order_line_ids.data_create(data);
order_line_ids.reload_current_view();
},
render_value: function() {
var self = this;
return this.fetch_value().done(function(data) {
if (_.isEmpty(data)) {
return self.$el.html(QWeb.render("lunch_order_widget_no_previous_order"));
}
var categories = _.groupBy(data,function(data1){return data1['supplier'][1];});
return self.$el.html(QWeb.render("lunch_order_widget_previous_orders_list", {'widget': self, 'categories': categories}
));
});
},
destroy: function () {
this.monetary.destroy();
this._super();
}
});
core.form_widget_registry.add('previous_order', lunch.lunch_order_widget_previous_orders);
return lunch;
});
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="lunch_order_widget_previous_orders">
<div>
<div class="previous_order_lines"></div>
</div>
</t>
<t t-name="lunch_order_widget_no_previous_order">
<div class="oe_inline o_lunch_intro">
<h3>This is the first time you order a meal</h3>
<p class="text-muted">Select a product and put your order comments on the note.</p>
<p class="text-muted">Your favorite meals will be created based on your last orders.</p>
<p class="text-muted">Don't forget the alerts displayed in the reddish area</p>
</div>
</t>
<t t-name="lunch_order_widget_previous_orders_list">
<t t-foreach="categories" t-as="product_category">
<div class="o_lunch_30pc">
<h2><t t-esc="product_category"/></h2>
<t t-foreach='categories[product_category]' t-as='product'>
<div class="o_lunch_vignette">
<span class="pull-right add_button oe_edit_only" t-att-id="product.id">
<button type="button" class="oe_button oe_link">
<span class="fa fa-plus-square"></span>
<span>Add</span>
</button>
</span>
<div class="oe_group_text_button">
<div class="o_lunch_text">
<t t-esc="product.product_id[1]"/>
<span class="oe_tag">
<t t-esc="widget.monetary.format_value(product.price)"/> <t t-esc="product.currency_id[1]"/>
</span>
</div>
</div>
<div class="text-muted"><t t-if="product.note != false" t-esc="product.note"/></div>
</div>
</t>
</div>
</t>
</t>
</templates>
+1 -20
View File
@@ -1,22 +1,3 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_lunch
import test_lunch
+21 -52
View File
@@ -1,92 +1,61 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import tools
from openerp.tests import common
class Test_Lunch(common.TransactionCase):
def setUp(self):
"""*****setUp*****"""
super(Test_Lunch, self).setUp()
cr, uid = self.cr, self.uid
self.res_users = self.registry('res.users')
self.lunch_order = self.registry('lunch.order')
self.lunch_order_line = self.registry('lunch.order.line')
self.lunch_cashmove = self.registry('lunch.cashmove')
self.lunch_product = self.registry('lunch.product')
self.lunch_alert = self.registry('lunch.alert')
self.lunch_product_category = self.registry('lunch.product.category')
self.demo_id = self.res_users.search(cr, uid, [('name', '=', 'Demo User')])
self.product_bolognese_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'lunch', 'product_Bolognese')
self.demo_user = self.env['res.users'].search([('name', '=', 'Demo User')])
self.product_bolognese_ref = self.env['ir.model.data'].get_object_reference('lunch', 'product_Bolognese')
self.product_Bolognese_id = self.product_bolognese_ref and self.product_bolognese_ref[1] or False
self.new_id_order = self.lunch_order.create(cr,uid,{
'user_id': self.demo_id[0],
'order_line_ids':'[]',
},context=None)
self.new_id_order_line = self.lunch_order_line.create(cr,uid,{
'order_id':self.new_id_order,
'product_id':self.product_Bolognese_id,
self.new_id_order = self.env['lunch.order'].create({
'user_id': self.demo_user.id,
'order_line_ids': '[]',
})
self.new_id_order_line = self.env['lunch.order.line'].create({
'order_id': self.new_id_order.id,
'product_id': self.product_Bolognese_id,
'note': '+Emmental',
'cashmove': [],
'price': self.lunch_product.browse(cr,uid,self.product_Bolognese_id,context=None).price,
'price': self.env['lunch.product'].browse(self.product_Bolognese_id).price,
})
def test_00_lunch_order(self):
"""Change the state of an order line from 'new' to 'ordered'. Check that there are no cashmove linked to that order line"""
cr, uid = self.cr, self.uid
self.order_one = self.lunch_order_line.browse(cr,uid,self.new_id_order_line,context=None)
self.order_one = self.new_id_order_line
#we check that our order_line is a 'new' one and that there are no cashmove linked to that order_line:
self.assertEqual(self.order_one.state,'new')
self.assertEqual(self.order_one.state, 'new')
self.assertEqual(list(self.order_one.cashmove), [])
#we order that orderline so it's state will be 'ordered'
self.order_one.order()
self.order_one = self.lunch_order_line.browse(cr,uid,self.new_id_order_line,context=None)
self.order_one = self.new_id_order_line
#we check that our order_line is a 'ordered' one and that there are no cashmove linked to that order_line:
self.assertEqual(self.order_one.state,'ordered')
self.assertEqual(self.order_one.state, 'ordered')
self.assertEqual(list(self.order_one.cashmove), [])
def test_01_lunch_order(self):
"""Change the state of an order line from 'new' to 'ordered' then to 'confirmed'. Check that there is a cashmove linked to the order line"""
cr, uid = self.cr, self.uid
self.test_00_lunch_order()
#We receive the order so we confirm the order line so it's state will be 'confirmed'
#A cashmove will be created and we will test that the cashmove amount equals the order line price
self.order_one.confirm()
self.order_one = self.lunch_order_line.browse(cr,uid,self.new_id_order_line,context=None)
self.order_one = self.new_id_order_line
#we check that our order_line is a 'confirmed' one and that there are a cashmove linked to that order_line with an amount equals to the order line price:
self.assertEqual(self.order_one.state,'confirmed')
self.assertEqual(self.order_one.state, 'confirmed')
self.assertTrue(self.order_one.cashmove)
self.assertTrue(self.order_one.cashmove[0].amount==-self.order_one.price)
self.assertTrue(self.order_one.cashmove[0].amount == -self.order_one.price)
def test_02_lunch_order(self):
"""Change the state of an order line from 'confirmed' to 'cancelled' and check that the cashmove linked to that order line will be deleted"""
cr, uid = self.cr, self.uid
self.test_01_lunch_order()
#We have a confirmed order with its associate cashmove
#We execute the cancel function
self.order_one.cancel()
self.order_one = self.lunch_order_line.browse(cr,uid,self.new_id_order_line,context=None)
self.order_one = self.new_id_order_line
#We check that the state is cancelled and that the cashmove has been deleted
self.assertEqual(self.order_one.state,'cancelled')
self.assertFalse(self.order_one.cashmove)
self.assertEqual(self.order_one.state, 'cancelled')
self.assertFalse(self.order_one.cashmove)
+8 -9
View File
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="assets_backend" name="lunch assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/lunch/static/src/css/lunch.css"/>
</xpath>
</template>
</data>
</openerp>
<odoo>
<template id="assets_backend" name="lunch assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/lunch/static/src/css/lunch.css"/>
<script type="text/javascript" src="/lunch/static/src/js/lunch.js"></script>
</xpath>
</template>
</odoo>
+93 -96
View File
@@ -1,6 +1,5 @@
<?xml version="1.0"?>
<openerp>
<data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Top menu item -->
<menuitem id='menu_lunch' name='Lunch' sequence="100" groups="group_lunch_user" icon="fa-cutlery"/>
<menuitem name="Lunch" parent="menu_lunch" id="menu_lunch_title" sequence="50" />
@@ -9,14 +8,14 @@
<menuitem name="Configuration" parent="menu_lunch" id="menu_lunch_config" sequence="53" groups="group_lunch_manager"/>
<!--View Search to group/filter by Supplier and time-->
<record model="ir.ui.view" id="lunch_order_line_search_view">
<field name="name">Search</field>
<record id="lunch_order_line_view_search" model="ir.ui.view">
<field name="name">lunch.order.line.search</field>
<field name="model">lunch.order.line</field>
<field name="arch" type="xml">
<search string="Search">
<field name="name" filter_domain="['|', ('name', 'ilike', self), ('note', 'ilike', self)]"/>
<filter name="not_confirmed" string="Not Received" domain="[('state','!=',('confirmed'))]"/>
<filter name="comfirmed" string="Received" domain="[('state','=','confirmed')]"/>
<filter name="confirmed" string="Received" domain="[('state','=','confirmed')]"/>
<filter name="cancelled" string="Cancelled" domain="[('state','=','cancelled')]"/>
<separator/>
<filter name="today" string="Today" domain="[('date','=',time.strftime('%%Y-%%m-%%d'))]"/>
@@ -30,8 +29,8 @@
</record>
<!--View Search to group by employee and input/output (cashmoves)-->
<record id="view_lunch_employee_payment_filter" model="ir.ui.view">
<field name='name'>lunch employee payment</field>
<record id="lunch_cashmove_view_search" model="ir.ui.view">
<field name='name'>lunch.cashmove.search</field>
<field name='model'>lunch.cashmove</field>
<field name='arch' type='xml'>
<search string="lunch employee payment">
@@ -45,8 +44,8 @@
</field>
</record>
<record id="view_lunch_cashmove_filter" model="ir.ui.view">
<field name='name'>lunch cashmove</field>
<record id="lunch_cashmove_view_search_2" model="ir.ui.view">
<field name='name'>lunch.cashmove.search</field>
<field name='model'>lunch.cashmove</field>
<field name='arch' type='xml'>
<search string="lunch cashmove">
@@ -60,8 +59,8 @@
</record>
<!--View search for order-->
<record id="view_search_my_order" model="ir.ui.view">
<field name='name'>lunch orders</field>
<record id="lunch_order_view_search" model="ir.ui.view">
<field name='name'>lunch.order.search</field>
<field name='model'>lunch.order</field>
<field name='arch' type='xml'>
<search string="lunch orders">
@@ -75,8 +74,8 @@
</record>
<record model="ir.ui.view" id="alert_search_view">
<field name="name">Search</field>
<record id="lunch_alert_view_search" model="ir.ui.view">
<field name="name">lunch.alert.search</field>
<field name="model">lunch.alert</field>
<field name="arch" type="xml">
<search string="Search">
@@ -86,8 +85,8 @@
</record>
<!--view for cashmove-->
<record model="ir.ui.view" id="casmove_tree_view">
<field name="name">cashmove tree</field>
<record id="lunch_cashmove_view_tree" model="ir.ui.view">
<field name="name">lunch.cashmove.tree</field>
<field name="model">lunch.cashmove</field>
<field name="arch" type="xml">
<tree string="cashmove tree">
@@ -99,8 +98,8 @@
</field>
</record>
<record model="ir.ui.view" id="casmove_form_view">
<field name="name">cashmove form</field>
<record id="lunch_cashmove_view_form" model="ir.ui.view">
<field name="name">lunch.cashmove.form</field>
<field name="model">lunch.cashmove</field>
<field name="arch" type="xml">
<form string="cashmove form">
@@ -120,22 +119,22 @@
<!--Action for Your Orders-->
<record model="ir.actions.act_window" id="action_lunch_order_form">
<record id="lunch_order_action_form" model="ir.actions.act_window">
<field name="name">New Order</field>
<field name="res_model">lunch.order</field>
<field name="view_mode">form</field>
</record>
<menuitem name="New Order" parent="menu_lunch_title" id="menu_lunch_order_form" action="action_lunch_order_form" sequence="1"/>
<menuitem name="New Order" parent="menu_lunch_title" id="lunch_order_menu_form" action="lunch_order_action_form" sequence="1"/>
<record model="ir.actions.act_window" id="action_lunch_order_tree">
<record id="lunch_order_action_tree" model="ir.actions.act_window">
<field name="name">Your Orders</field>
<field name="res_model">lunch.order</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_search_my_order"/>
<field name="search_view_id" ref="lunch_order_view_search"/>
<field name="context">{"search_default_is_mine":1}</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a lunch order.
Click to create a lunch order.
</p>
<p>
A lunch order is defined by its user, date and order lines.
@@ -144,10 +143,10 @@
</p>
</field>
</record>
<menuitem name="Previous Orders" parent="menu_lunch_title" id="menu_lunch_order_tree" action="action_lunch_order_tree" sequence="2"/>
<record model="ir.ui.view" id="casmove_tree">
<field name="name">cashmove tree</field>
<menuitem name="Previous Orders" parent="menu_lunch_title" id="lunch_order_menu_tree" action="lunch_order_action_tree" sequence="2"/>
<record id="lunch_cashmove_view_tree_2" model="ir.ui.view">
<field name="name">lunch.cashmove.tree</field>
<field name="model">lunch.cashmove</field>
<field name="arch" type="xml">
<tree string="cashmove tree" create='false'>
@@ -160,13 +159,13 @@
</record>
<!--Action for Lunch cashmoves-->
<record model="ir.actions.act_window" id="action_lunch_cashmove_form">
<record id="lunch_cashmove_action_account" model="ir.actions.act_window">
<field name="name">Your Account</field>
<field name="res_model">lunch.cashmove</field>
<field name="view_mode">tree</field>
<field name="search_view_id" ref="view_lunch_employee_payment_filter"/>
<field name="search_view_id" ref="lunch_cashmove_view_search"/>
<field name="context">{"search_default_is_mine_group":1}</field>
<field name="view_id" ref="casmove_tree"/>
<field name="view_id" ref="lunch_cashmove_view_tree_2"/>
<field name="help" type="html">
<p>
Here you can see your cash moves.<br/>A cash moves can be either an expense or a payment.
@@ -174,14 +173,14 @@
</p>
</field>
</record>
<menuitem name="Your Lunch Account" parent="menu_lunch_title" id="menu_lunch_cashmove_form" action="action_lunch_cashmove_form" sequence="3"/>
<menuitem name="Your Lunch Account" parent="menu_lunch_title" id="lunch_cashmove_menu_form" action="lunch_cashmove_action_account" sequence="3"/>
<!--Action for Administrate Orders group by supplier-->
<record model="ir.actions.act_window" id="action_lunch_order_by_supplier_form">
<record id="lunch_order_line_action_by_supplier" model="ir.actions.act_window">
<field name="name">Orders by Supplier</field>
<field name="res_model">lunch.order.line</field>
<field name="view_mode">tree</field>
<field name="search_view_id" ref="lunch_order_line_search_view"/>
<field name="search_view_id" ref="lunch_order_line_view_search"/>
<field name="context">{"search_default_group_by_supplier":1, "search_default_today":1}</field>
<field name="help" type="html">
<p>
@@ -194,14 +193,14 @@
</p>
</field>
</record>
<menuitem name="Today's Orders by Supplier" parent="menu_lunch_admin" id="menu_lunch_order_by_supplier_form" action="action_lunch_order_by_supplier_form" />
<menuitem name="Today's Orders by Supplier" parent="menu_lunch_admin" id="lunch_order_line_menu_by_supplier" action="lunch_order_line_action_by_supplier" />
<!--Action for control Supplier-->
<record model="ir.actions.act_window" id="action_lunch_control_suppliers">
<record id="lunch_order_line_action_control_suppliers" model="ir.actions.act_window">
<field name="name">Control Suppliers</field>
<field name="res_model">lunch.order.line</field>
<field name="view_mode">tree</field>
<field name="search_view_id" ref="lunch_order_line_search_view"/>
<field name="search_view_id" ref="lunch_order_line_view_search"/>
<field name="context">{"search_default_group_by_date":1, "search_default_group_by_supplier":1}</field>
<field name="help" type="html">
<p>
@@ -214,19 +213,19 @@
</p>
</field>
</record>
<menuitem name="Orders by Supplier" parent="menu_lunch_admin" id="menu_lunch_control_suppliers" action="action_lunch_control_suppliers" />
<menuitem name="Orders by Supplier" parent="menu_lunch_admin" id="lunch_order_line_menu_control_suppliers" action="lunch_order_line_action_control_suppliers" />
<!--Action for Control Accounts-->
<record model="ir.actions.act_window" id="action_lunch_control_accounts">
<record id="lunch_cashmove_action_control_accounts" model="ir.actions.act_window">
<field name="name">Control Accounts</field>
<field name="res_model">lunch.cashmove</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_lunch_cashmove_filter"/>
<field name="search_view_id" ref="lunch_cashmove_view_search_2"/>
<field name="context">{"search_default_group_by_user":1}</field>
<field name="view_id" ref="casmove_tree_view"/>
<field name="view_id" ref="lunch_cashmove_view_tree"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new payment.
Click to create a new payment.
</p>
<p>
A cashmove can either be an expense or a payment.<br/>
@@ -235,52 +234,52 @@
</p>
</field>
</record>
<menuitem name="Control Accounts" parent="menu_lunch_cash" id="menu_lunch_control_accounts" action="action_lunch_control_accounts" />
<menuitem name="Control Accounts" parent="menu_lunch_cash" id="lunch_cashmove_menu_control_accounts" action="lunch_cashmove_action_control_accounts" />
<!--Action for Payment cashmove-->
<record model="ir.actions.act_window" id="action_lunch_cashmove">
<record id="lunch_cashmove_action_payment" model="ir.actions.act_window">
<field name="name">Register Cash Moves</field>
<field name="res_model">lunch.cashmove</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_lunch_employee_payment_filter"/>
<field name="search_view_id" ref="lunch_cashmove_view_search"/>
<field name="context">{"search_default_is_payment":1}</field>
<field name="view_id" ref="casmove_tree_view"/>
<field name="view_id" ref="lunch_cashmove_view_tree"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a payment.
Click to create a payment.
</p>
<p>
Here you can see the employees' payment. A payment is a cash move from the employee to the company.
</p>
</field>
</record>
<menuitem name="Employee Payments" parent="menu_lunch_cash" id="menu_lunch_cashmove" action="action_lunch_cashmove" />
<menuitem name="Employee Payments" parent="menu_lunch_cash" id="lunch_cashmove_menu_payment" action="lunch_cashmove_action_payment" />
<!--Action for Products-->
<record model="ir.actions.act_window" id="action_lunch_products">
<record id="lunch_product_action" model="ir.actions.act_window">
<field name="name">Products</field>
<field name="res_model">lunch.product</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a product for lunch.
Click to create a product for lunch.
</p>
<p>
A product is defined by its name, category, price and supplier.
</p>
</field>
</record>
<menuitem name="Products" parent="menu_lunch_config" id="menu_lunch_products" action="action_lunch_products" />
<menuitem name="Products" parent="menu_lunch_config" id="lunch_product_menu" action="lunch_product_action" />
<!--Action for Product categories-->
<record model="ir.actions.act_window" id="action_lunch_product_categories">
<record id="lunch_product_category_action" model="ir.actions.act_window">
<field name="name">Product Categories</field>
<field name="res_model">lunch.product.category</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a lunch category.
Click to create a lunch category.
</p>
<p>
Here you can find every lunch categories for products.
@@ -288,7 +287,7 @@
</field>
</record>
<record model="ir.ui.view" id="product_category_form_view">
<record id="lunch_product_category_view_form" model="ir.ui.view">
<field name="name">Product category Form</field>
<field name="model">lunch.product.category</field>
<field name="arch" type="xml">
@@ -300,17 +299,17 @@
</field>
</record>
<menuitem name="Product Categories" parent="menu_lunch_config" id="menu_lunch_product_categories" action="action_lunch_product_categories" />
<menuitem name="Product Categories" parent="menu_lunch_config" id="lunch_product_category_menu" action="lunch_product_category_action" />
<!--Action for Alert-->
<record model="ir.actions.act_window" id="action_lunch_alert">
<record id="lunch_alert_action" model="ir.actions.act_window">
<field name="name">Alerts</field>
<field name="res_model">lunch.alert</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="alert_search_view"/>
<field name="search_view_id" ref="lunch_alert_view_search"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a lunch alert.
Click to create a lunch alert.
</p>
<p>
Alerts are used to warn employee from possible issues concerning the lunch orders.
@@ -324,31 +323,31 @@
</p>
</field>
</record>
<menuitem name="Alerts" parent="menu_lunch_config" id="menu_lunch_alert" action="action_lunch_alert" />
<menuitem name="Alerts" parent="menu_lunch_config" id="lunch_alert_menu" action="lunch_alert_action" />
<!--View for Order lines-->
<record model="ir.ui.view" id="orders_order_lines_tree_view">
<field name="name">Order lines Tree</field>
<record id="lunch_order_line_view_tree" model="ir.ui.view">
<field name="name">lunch.order.line.tree</field>
<field name="model">lunch.order.line</field>
<field name="arch" type="xml">
<tree string="Order lines Tree" create="false" edit="false">
<field name='date'/>
<field name='user_id'/>
<field name='supplier' invisible='1'/>
<field name='product_id'/>
<field name='supplier' invisible='1'/>
<field name='product_id'/>
<field name='note'/>
<field name='state'/>
<field name='price' sum="Total"/>
<button name="order" string="Order" type="object" icon="terp-call-start" attrs="{'invisible': ['|',('state','=','confirmed'),('state','=','ordered')]}"/>
<button name="confirm" string="Confirm" type="object" icon="gtk-apply" attrs="{'invisible': [('state','!=','ordered')]}"/>
<button name="confirm" string="Receive" type="object" icon="gtk-apply" attrs="{'invisible': [('state','!=','ordered')]}"/>
<button name="cancel" string="Cancel" type="object" icon="gtk-cancel" attrs="{'invisible': [('state','=','cancelled')]}"/>
</tree>
</field>
</record>
<!--View for Your orders-->
<record model="ir.ui.view" id="orders_tree_view">
<field name="name">Orders Tree View</field>
<record id="lunch_order_view_tree" model="ir.ui.view">
<field name="name">lunch.order.tree</field>
<field name="model">lunch.order</field>
<field name="arch" type="xml">
<tree string="Orders Tree">
@@ -360,12 +359,12 @@
</tree>
</field>
</record>
<record model="ir.ui.view" id="orders_form_view">
<field name="name">Lunch Order</field>
<record id="lunch_order_view_form" model="ir.ui.view">
<field name="name">lunch.order.form</field>
<field name="model">lunch.order</field>
<field name="arch" type="xml">
<form string='Orders Form' class="oe_lunch">
<form string='Orders Form' class="o_lunch">
<header>
<field name='state' widget='statusbar' statusbar_visible='new,confirmed'/>
</header>
@@ -375,25 +374,24 @@
<field name='user_id'
context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'lunch.group_lunch_user']}"/>
</group>
<group>
<group>
<field name='date'/>
</group>
</group>
<field name='alerts' attrs="{'invisible': ['|',('state','!=','new'),('alerts','=',False)]}" class="oe_inline oe_lunch_alert"/>
<div name="preferences">
</div>
<field name='alerts' attrs="{'invisible': ['|',('state','!=','new'),('alerts','=',False)]}" class="oe_inline alert alert-danger"/>
<field name='previous_order_ids' widget='previous_order'/>
<separator string='Select your order'/>
<field name='order_line_ids' nolabel='1' on_change='onchange_price(order_line_ids)'>
<field name='order_line_ids' nolabel='1'>
<tree string='List' editable='bottom'>
<field name='product_id' on_change='onchange_price(product_id)'/>
<field name='product_id'/>
<field name='note' />
<field name='price' on_change='onchange_price(product_id)'/>
<field name='price'/>
<field name='supplier' invisible="1"/>
<field name="state" invisible="1"/>
</tree>
</field>
</field>
<group class='oe_subtotal_footer oe_right'>
<field name='total'/>
<field name='total' widget='monetary' options="{'currency_field': 'currency_id'}"/>
</group>
<br/><br/>
</sheet>
@@ -402,8 +400,8 @@
</record>
<!--View for Products-->
<record model="ir.ui.view" id="products_tree_view">
<field name="name">Products Tree</field>
<record id="lunch_product_view_tree" model="ir.ui.view">
<field name="name">lunch.product.tree</field>
<field name="model">lunch.product</field>
<field name="arch" type="xml">
<tree string="Products Tree">
@@ -416,8 +414,8 @@
</field>
</record>
<record model="ir.ui.view" id="products_form_view">
<field name="name">Products Form</field>
<record id="lunch_product_view_form" model="ir.ui.view">
<field name="name">lunch.product.form</field>
<field name="model">lunch.product</field>
<field name="arch" type="xml">
<form string="Products Form">
@@ -440,31 +438,31 @@
</record>
<!--view for alerts-->
<record model="ir.ui.view" id="alert_tree_view">
<field name="name">alert tree</field>
<record id="lunch_alert_view_tree" model="ir.ui.view">
<field name="name">lunch.alert.tree</field>
<field name="model">lunch.alert</field>
<field name="arch" type="xml">
<tree string="alert tree">
<field name="message"/>
<field name="alter_type"/>
<field name='active_from' widget='float_time'/>
<field name='active_to' widget='float_time'/>
<field name="alert_type"/>
<field name='start_hour' widget='float_time'/>
<field name='end_hour' widget='float_time'/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="alert_form_view">
<field name="name">alert form</field>
<record id="lunch_alert_view_form" model="ir.ui.view">
<field name="name">lunch.alert.form</field>
<field name="model">lunch.alert</field>
<field name="arch" type="xml">
<form string="alert tree">
<group string="Schedule Date">
<group>
<field name="alter_type" class="oe_inline"/>
<field name="specific_day" attrs="{'invisible': [('alter_type','!=','specific')], 'required':[('alter_type','=','specific')]}"/>
<field name="alert_type" class="oe_inline"/>
<field name="specific_day" attrs="{'invisible': [('alert_type','!=','specific')], 'required':[('alert_type','=','specific')]}"/>
</group>
</group>
<group attrs="{'invisible': [('alter_type','!=','week')]}">
<group attrs="{'invisible': [('alert_type','!=','week')]}">
<group>
<field name="monday"/>
<field name="tuesday"/>
@@ -478,8 +476,8 @@
</group>
</group>
<group string="Schedule Hour">
<field name='active_from' widget='float_time'/>
<field name='active_to' widget='float_time'/>
<field name='start_hour' widget='float_time'/>
<field name='end_hour' widget='float_time'/>
</group>
<group string='Message'>
<field name='message' nolabel='1' placeholder="Write the message you want to display during the defined period..."/>
@@ -488,5 +486,4 @@
</field>
</record>
</data>
</openerp>
</odoo>
-24
View File
@@ -1,24 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2012 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import lunch_validation
import lunch_cancel
import lunch_order
-29
View File
@@ -1,29 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2012 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, osv
class lunch_cancel(osv.Model):
""" lunch cancel """
_name = 'lunch.cancel'
_description = 'cancel lunch order'
def cancel(self,cr,uid,ids,context=None):
return self.pool.get('lunch.order.line').cancel(cr, uid, ids, context=context)
-31
View File
@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.ui.view" id="cancel_order_lines_view">
<field name="name">cancel order lines</field>
<field name="model">lunch.cancel</field>
<field name="arch" type="xml">
<form string="cancel order lines">
<separator string="Are you sure you want to cancel these meals?"/>
<p class="oe_grey">
Cancel a meal means that we didn't receive it from the supplier.
<br/>
A cancelled meal should not be paid by employees.
</p>
<footer>
<button name="cancel" string="Cancel Orders" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<act_window id="cancel_order_lines"
multi="True"
key2="client_action_multi" name="Cancel meals"
res_model="lunch.cancel" src_model="lunch.order.line"
view_mode="form" target="new" view_type="form" view_id="cancel_order_lines_view"/>
</data>
</openerp>
-29
View File
@@ -1,29 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2012 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, osv
class lunch_order_order(osv.TransientModel):
""" lunch order meal """
_name = 'lunch.order.order'
_description = 'Wizard to order a meal'
def order(self,cr,uid,ids,context=None):
return self.pool.get('lunch.order.line').order(cr, uid, ids, context=context)
-30
View File
@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.ui.view" id="order_order_lines_view">
<field name="name">Order meal</field>
<field name="model">lunch.order.order</field>
<field name="arch" type="xml">
<form string="Order meal">
<separator string="Are you sure you want to order these meals?"/>
<p class="oe_grey">
Order a meal doesn't mean that we have to pay it.
A meal should be paid when it is received.
</p>
<footer>
<button name="order" string="Order Meals" type="object" class="oe_highlight"/>
or
<button name="cancel" string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<act_window id="order_order_lines"
multi="True"
key2="client_action_multi" name="Order meals"
res_model="lunch.order.order" src_model="lunch.order.line"
view_mode="form" target="new" view_type="form" view_id="order_order_lines_view"/>
</data>
</openerp>
-29
View File
@@ -1,29 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2012 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, osv
class lunch_validation(osv.Model):
""" lunch validation """
_name = 'lunch.validation'
_description = 'lunch validation for order'
def confirm(self,cr,uid,ids,context=None):
return self.pool.get('lunch.order.line').confirm(cr, uid, ids, context=context)
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.ui.view" id="validate_order_lines_view">
<field name="name">validate order lines</field>
<field name="model">lunch.validation</field>
<field name="arch" type="xml">
<form string="validate order lines">
<separator string="Did your received these meals?"/>
<p class="oe_grey">
Once a meal is received a new cash move is created for the employee.
</p>
<footer>
<button name="confirm" string="Receive Meals" type="object" class="oe_highlight"/>
or
<button name="cancel" string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<act_window id="validate_order_lines"
multi="True"
key2="client_action_multi" name="Receive meals"
res_model="lunch.validation" src_model="lunch.order.line"
view_mode="form" target="new" view_type="form" view_id="validate_order_lines_view"/>
</data>
</openerp>
@@ -1592,6 +1592,7 @@ core.form_tag_registry.add('button', WidgetButton);
return {
FieldChar: FieldChar,
FieldMonetary: FieldMonetary
};
});