[MERGE] Forward-port of latest 7.0 bugfixes, up to rev. 10025 rev-id odo@openerp.com-20140430102552-qkwv20b20nve64th

bzr revid: odo@openerp.com-20140430110048-6971xplq8k073tto
This commit is contained in:
Olivier Dony 2014-04-30 13:00:48 +02:00
commit c3e812f34a
6 changed files with 63 additions and 20 deletions

View File

@ -22,9 +22,11 @@
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
from pytz import timezone
import pytz
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.translate import _
class hr_timesheet_sheet(osv.osv):
@ -386,22 +388,53 @@ class hr_attendance(osv.osv):
attendance_ids.extend([row[0] for row in cr.fetchall()])
return attendance_ids
def _get_attendance_employee_tz(self, cr, uid, employee_id, date, context=None):
""" Simulate timesheet in employee timezone
Return the attendance date in string format in the employee
tz converted from utc timezone as we consider date of employee
timesheet is in employee timezone
"""
employee_obj = self.pool['hr.employee']
tz = False
if employee_id:
employee = employee_obj.browse(cr, uid, employee_id, context=context)
tz = employee.user_id.partner_id.tz
att_tz = timezone(tz or 'utc')
attendance_dt = datetime.strptime(date, DEFAULT_SERVER_DATETIME_FORMAT)
att_tz_dt = pytz.utc.localize(attendance_dt)
att_tz_dt = att_tz_dt.astimezone(att_tz)
# We take only the date omiting the hours as we compare with timesheet
# date_from which is a date format thus using hours would lead to
# be out of scope of timesheet
att_tz_date_str = datetime.strftime(att_tz_dt, DEFAULT_SERVER_DATE_FORMAT)
return att_tz_date_str
def _get_current_sheet(self, cr, uid, employee_id, date=False, context=None):
sheet_obj = self.pool['hr_timesheet_sheet.sheet']
if not date:
date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
# ending date with no time to avoid timesheet with early date_to
date_to = date[0:10]+' 00:00:00'
# limit=1 because only one sheet possible for an employee between 2 dates
sheet_ids = self.pool.get('hr_timesheet_sheet.sheet').search(cr, uid, [
('date_to', '>=', date_to), ('date_from', '<=', date),
('employee_id', '=', employee_id)
], limit=1, context=context)
att_tz_date_str = self._get_attendance_employee_tz(
cr, uid, employee_id,
date=date, context=context)
sheet_ids = sheet_obj.search(cr, uid,
[('date_from', '<=', att_tz_date_str),
('date_to', '>=', att_tz_date_str),
('employee_id', '=', employee_id)],
limit=1, context=context)
return sheet_ids and sheet_ids[0] or False
def _sheet(self, cursor, user, ids, name, args, context=None):
res = {}.fromkeys(ids, False)
for attendance in self.browse(cursor, user, ids, context=context):
res[attendance.id] = self._get_current_sheet(cursor, user, attendance.employee_id.id, attendance.name, context=context)
res[attendance.id] = self._get_current_sheet(
cursor, user, attendance.employee_id.id, attendance.name,
context=context)
return res
_columns = {
@ -423,10 +456,13 @@ class hr_attendance(osv.osv):
sheet_id = context.get('sheet_id') or self._get_current_sheet(cr, uid, vals.get('employee_id'), vals.get('name'), context=context)
if sheet_id:
att_tz_date_str = self._get_attendance_employee_tz(
cr, uid, vals.get('employee_id'),
date=vals.get('name'), context=context)
ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, sheet_id, context=context)
if ts.state not in ('draft', 'new'):
raise osv.except_osv(_('Error!'), _('You can not enter an attendance in a submitted timesheet. Ask your manager to reset it before adding attendance.'))
elif ts.date_from > vals.get('name') or ts.date_to < vals.get('name'):
elif ts.date_from > att_tz_date_str or ts.date_to < att_tz_date_str:
raise osv.except_osv(_('User Error!'), _('You can not enter an attendance date outside the current timesheet dates.'))
return super(hr_attendance,self).create(cr, uid, vals, context=context)
@ -578,4 +614,3 @@ class res_company(osv.osv):
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -6,6 +6,7 @@ from openerp import SUPERUSER_ID
from openerp import http
from openerp.http import request
from openerp.addons.web.controllers.main import content_disposition
import mimetypes
class MailController(http.Controller):
@ -19,10 +20,11 @@ class MailController(http.Controller):
if res:
filecontent = base64.b64decode(res.get('base64'))
filename = res.get('filename')
content_type = mimetypes.guess_type(filename)
if filecontent and filename:
return request.make_response(
filecontent,
headers=[('Content-Type', 'application/octet-stream'),
headers=[('Content-Type', content_type[0] or 'application/octet-stream'),
('Content-Disposition', content_disposition(filename))])
return request.not_found()

View File

@ -284,7 +284,7 @@ class mail_mail(osv.Model):
res = ir_mail_server.send_email(cr, uid, msg,
mail_server_id=mail.mail_server_id.id,
context=context)
if res:
mail.write({'state': 'sent', 'message_id': res})
mail_sent = True
@ -296,6 +296,10 @@ class mail_mail(osv.Model):
# see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1
if mail_sent:
self._postprocess_sent_message(cr, uid, mail, context=context)
except MemoryError:
# prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
# instead of marking the mail as failed
raise
except Exception as e:
_logger.exception('failed sending mail.mail %s', mail.id)
mail.write({'state': 'exception'})

View File

@ -59,7 +59,7 @@
<record id="res_company_oerp_us" model="res.company">
<field name="partner_id" ref="res_partner_oerp_us"/>
<field name="parent_id" ref="res_company_oerp_editor"/>
<field name="currency_id" ref="base.EUR"/>
<field name="currency_id" ref="base.USD"/>
<field name="name">OpenERP US</field>
</record>
<record id="res_company_oerp_be" model="res.company">
@ -400,7 +400,7 @@
</record>
<record id="project.project_project_3" model="project.project">
<field name="company_id" ref="res_company_oerp_us"/>
<field name="currency_id" ref="base.EUR"/>
<field name="currency_id" ref="base.USD"/>
</record>
<record id="project.project_project_4" model="project.project">
<field name="company_id" ref="res_company_oerp_be"/>

View File

@ -105,7 +105,9 @@ class product_product(osv.osv):
'compute_child': False
})
qty = product.qty_available
# qty_available depends of the location in the context
qty = self.read(cr, uid, [product.id], ['qty_available'], context=c)[0]['qty_available']
diff = product.standard_price - new_price
if not diff: raise osv.except_osv(_('Error!'), _("No difference between standard price and new price!"))
if qty:

View File

@ -792,7 +792,7 @@
</group>
<notebook>
<page string="Products">
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'picking_type': 'internal'}" options='{"reload_on_button": true}'/>
<field name="move_lines" string="Stock Move" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'picking_type': 'internal'}" options='{"reload_on_button": true}'/>
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
</page>
<page string="Additional Info">
@ -926,7 +926,7 @@
<field name="partner_id" on_change="onchange_partner_in(partner_id)" string="Customer" domain="[('customer','=',True)]" />
</xpath>
<xpath expr="//field[@name='move_lines']" position="replace">
<field name="move_lines" context="{'address_out_id': partner_id, 'picking_type': 'out', 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree'}" options='{"reload_on_button": true}'/>
<field name="move_lines" string="Stock Move" context="{'address_out_id': partner_id, 'picking_type': 'out', 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree'}" options='{"reload_on_button": true}'/>
</xpath>
<xpath expr="/form/sheet" position="after">
<div class="oe_chatter">
@ -1053,7 +1053,7 @@
<field name="partner_id" on_change="onchange_partner_in(partner_id)" string="Supplier" domain="[('supplier','=',True)]" context="{'default_supplier':1,'default_customer':0}"/>
</xpath>
<xpath expr="//field[@name='move_lines']" position="replace">
<field name="move_lines" context="{'address_in_id': partner_id, 'picking_type': 'in', 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree'}" options='{"reload_on_button": true}'/>
<field name="move_lines" string="Stock Move" context="{'address_in_id': partner_id, 'picking_type': 'in', 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree'}" options='{"reload_on_button": true}'/>
</xpath>
<xpath expr="/form/sheet" position="after">
<div class="oe_chatter">
@ -1340,7 +1340,7 @@
<group string="Locations" groups="stock.group_locations">
<field name="location_id" domain="[('usage','&lt;&gt;','view')]"/>
<field name="location_dest_id" domain="[('usage','=','internal')]" groups="stock.group_locations"/>
<field name="location_dest_id" domain="[('usage','in', ['internal', 'supplier', 'customer'])]" groups="stock.group_locations"/>
</group>
<group groups="stock.group_tracking_lot" string="Traceability">