[MERGE] forward port of branch saas-3 up to revid 9450 chs@openerp.com-20140507153348-g2qw61dkxvps13br

bzr revid: chs@openerp.com-20140507160023-8wwqlbf3bv7yqe67
This commit is contained in:
Christophe Simonis 2014-05-07 18:00:23 +02:00
commit 1091b24a0c
19 changed files with 132 additions and 57 deletions

View File

@ -1026,7 +1026,7 @@ class account_move_line(osv.osv):
if opening_reconciliation:
obj_move_rec.write(cr, uid, unlink_ids, {'opening_reconciliation': False})
obj_move_rec.unlink(cr, uid, unlink_ids)
if all_moves:
if len(all_moves) >= 2:
obj_move_line.reconcile_partial(cr, uid, all_moves, 'auto',context=context)
return True

View File

@ -11,6 +11,7 @@
<field name="name" />
<field name="client_id" />
<field name="enabled" />
<field name="body" />
</group>
<group>
<field name="auth_endpoint" />

View File

@ -14,6 +14,7 @@
<record model="crm.case.stage" id="stage_lead2">
<field name="name">Dead</field>
<field name="case_default">1</field>
<field name="fold">1</field>
<field name="probability">0</field>
<field name="on_change">1</field>
<field name="sequence">30</field>

View File

@ -88,7 +88,7 @@ Find duplicates, merge leads and assign them to the right salesperson in one ope
<p class="oe_mt32">
Get your opportunities organized to stay focused on the best deals. Manage all your customer interactions from the opportunity like emails, phone calls, internal notes, meetings and quotations.
</p><p>
Follow opportunities that interrests you to get notified upon specific events: deal won or lost, stage changed, new customer demand, etc.
Follow opportunities that interest you to get notified upon specific events: deal won or lost, stage changed, new customer demand, etc.
</p>
</div>
</div>

View File

@ -248,7 +248,13 @@ class hr_employee(osv.osv):
'image': _get_default_image,
'color': 0,
}
def copy_data(self, cr, uid, ids, default=None, context=None):
if default is None:
default = {}
default.update({'child_ids': False})
return super(hr_employee, self).copy_data(cr, uid, ids, default, context=context)
def _broadcast_welcome(self, cr, uid, employee_id, context=None):
""" Broadcast the welcome message to all users in the employee company. """
employee = self.browse(cr, uid, employee_id, context=context)
@ -421,18 +427,23 @@ class hr_department(osv.osv):
res.append((record['id'], name))
return res
def copy(self, cr, uid, ids, default=None, context=None):
def copy_data(self, cr, uid, ids, default=None, context=None):
if default is None:
default = {}
default = default.copy()
default['member_ids'] = []
return super(hr_department, self).copy(cr, uid, ids, default, context=context)
return super(hr_department, self).copy_data(cr, uid, ids, default, context=context)
class res_users(osv.osv):
_name = 'res.users'
_inherit = 'res.users'
def copy_data(self, cr, uid, ids, default=None, context=None):
if default is None:
default = {}
default.update({'employee_ids': False})
return super(res_users, self).copy_data(cr, uid, ids, default, context=context)
_columns = {
'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'),
}

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):
@ -397,22 +399,56 @@ class hr_attendance(osv.osv):
attendance_ids.extend([row[0] for row in cr.fetchall()])
return attendance_ids
def _get_current_sheet(self, cr, uid, employee_id, date=False, context=None):
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
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 = 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)
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 = {
@ -434,10 +470,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)
@ -589,4 +628,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

@ -234,13 +234,17 @@ class mail_mail(osv.Model):
:return: True
"""
ir_mail_server = self.pool.get('ir.mail_server')
ir_attachment = self.pool['ir.attachment']
for mail in self.browse(cr, SUPERUSER_ID, ids, context=context):
try:
# handle attachments
attachments = []
for attach in mail.attachment_ids:
attachments.append((attach.datas_fname, base64.b64decode(attach.datas)))
# load attachment binary data with a separate read(), as prefetching all
# `datas` (binary field) could bloat the browse cache, triggerring
# soft/hard mem limits with temporary data.
attachment_ids = [a.id for a in mail.attachment_ids]
attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
for a in ir_attachment.read(cr, SUPERUSER_ID, attachment_ids,
['datas_fname', 'datas'])]
# specific behavior to customize the send email for notified partners
email_list = []
if mail.email_to:
@ -289,6 +293,14 @@ class mail_mail(osv.Model):
# /!\ can't use mail.state here, as mail.refresh() will cause an error
# see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1
self._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=mail_sent)
_logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id)
except MemoryError:
# prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
# instead of marking the mail as failed
_logger.exception('MemoryError while processing mail with ID %r and Msg-Id %r. '\
'Consider raising the --limit-memory-hard startup option',
mail.id, mail.message_id)
raise
except Exception as e:
_logger.exception('failed sending mail.mail %s', mail.id)
mail.write({'state': 'exception'})

View File

@ -434,14 +434,14 @@ class mail_thread(osv.AbstractModel):
fol_obj.unlink(cr, SUPERUSER_ID, fol_ids, context=context)
return res
def copy(self, cr, uid, id, default=None, context=None):
def copy_data(self, cr, uid, id, default=None, context=None):
# avoid tracking multiple temporary changes during copy
context = dict(context or {}, mail_notrack=True)
default = default or {}
default['message_ids'] = []
default['message_follower_ids'] = []
return super(mail_thread, self).copy(cr, uid, id, default=default, context=context)
return super(mail_thread, self).copy_data(cr, uid, id, default=default, context=context)
#------------------------------------------------------
# Automatically log tracked fields

View File

@ -34,14 +34,14 @@ class project_configuration(osv.TransientModel):
}
def get_default_alias_domain(self, cr, uid, ids, context=None):
alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context)
if not alias_domain:
alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", default=None, context=context)
if alias_domain is None:
domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "web.base.url", context=context)
try:
alias_domain = urlparse.urlsplit(domain).netloc.split(':')[0]
except Exception:
pass
return {'alias_domain': alias_domain}
return {'alias_domain': alias_domain or False}
def set_alias_domain(self, cr, uid, ids, context=None):
config_parameters = self.pool.get("ir.config_parameter")

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

@ -1167,8 +1167,8 @@ class pos_order(osv.osv):
return self.write(cr, uid, ids, {'state': 'payment'}, context=context)
def action_paid(self, cr, uid, ids, context=None):
self.create_picking(cr, uid, ids, context=context)
self.write(cr, uid, ids, {'state': 'paid'}, context=context)
self.create_picking(cr, uid, ids, context=context)
return True
def action_cancel(self, cr, uid, ids, context=None):

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

@ -2307,7 +2307,9 @@ class stock_move(osv.osv):
# if product is set to average price and a specific value was entered in the picking wizard,
# we use it
if move.product_id.cost_method == 'average' and move.price_unit:
if move.location_dest_id.usage != 'internal' and move.product_id.cost_method == 'average':
reference_amount = qty * move.product_id.standard_price
elif move.product_id.cost_method == 'average' and move.price_unit:
reference_amount = qty * move.price_unit
reference_currency_id = move.price_currency_id.id or reference_currency_id

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">

View File

@ -62,14 +62,14 @@ class sale_order(osv.osv):
message = False
partner = self.pool.get('res.partner').browse(cr, uid, part, context=context)
if partner.sale_warn != 'no-message':
if partner.sale_warn == 'block':
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.sale_warn_msg)
title = _("Warning for %s") % partner.name
message = partner.sale_warn_msg
warning = {
'title': title,
'message': message,
}
if partner.sale_warn == 'block':
return {'value': {'partner_id': False}, 'warning': warning}
result = super(sale_order, self).onchange_partner_id(cr, uid, ids, part, context=context)
@ -90,14 +90,15 @@ class purchase_order(osv.osv):
message = False
partner = self.pool.get('res.partner').browse(cr, uid, part)
if partner.purchase_warn != 'no-message':
if partner.purchase_warn == 'block':
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.purchase_warn_msg)
title = _("Warning for %s") % partner.name
message = partner.purchase_warn_msg
warning = {
'title': title,
'message': message
}
if partner.purchase_warn == 'block':
return {'value': {'partner_id': False}, 'warning': warning}
result = super(purchase_order, self).onchange_partner_id(cr, uid, ids, part)
if result.get('warning',False):
@ -123,15 +124,16 @@ class account_invoice(osv.osv):
message = False
partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
if partner.invoice_warn != 'no-message':
if partner.invoice_warn == 'block':
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.invoice_warn_msg)
title = _("Warning for %s") % partner.name
message = partner.invoice_warn_msg
warning = {
'title': title,
'message': message
}
if partner.invoice_warn == 'block':
return {'value': {'partner_id': False}, 'warning': warning}
result = super(account_invoice, self).onchange_partner_id(cr, uid, ids, type, partner_id,
date_invoice=date_invoice, payment_term=payment_term,
partner_bank_id=partner_bank_id, company_id=company_id)
@ -154,14 +156,15 @@ class stock_picking(osv.osv):
title = False
message = False
if partner.picking_warn != 'no-message':
if partner.picking_warn == 'block':
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
title = _("Warning for %s") % partner.name
message = partner.picking_warn_msg
warning = {
'title': title,
'message': message
}
if partner.picking_warn == 'block':
return {'value': {'partner_id': False}, 'warning': warning}
result = super(stock_picking, self).onchange_partner_in(cr, uid, ids, partner_id, context)
if result.get('warning',False):
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
@ -183,14 +186,15 @@ class stock_picking_in(osv.osv):
title = False
message = False
if partner.picking_warn != 'no-message':
if partner.picking_warn == 'block':
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
title = _("Warning for %s") % partner.name
message = partner.picking_warn_msg
warning = {
'title': title,
'message': message
}
if partner.picking_warn == 'block':
return {'value': {'partner_id': False}, 'warning': warning}
result = super(stock_picking_in, self).onchange_partner_in(cr, uid, ids, partner_id, context)
if result.get('warning',False):
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
@ -209,14 +213,15 @@ class stock_picking_out(osv.osv):
title = False
message = False
if partner.picking_warn != 'no-message':
if partner.picking_warn == 'block':
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
title = _("Warning for %s") % partner.name
message = partner.picking_warn_msg
warning = {
'title': title,
'message': message
}
if partner.picking_warn == 'block':
return {'value': {'partner_id': False}, 'warning': warning}
result = super(stock_picking_out, self).onchange_partner_in(cr, uid, ids, partner_id, context)
if result.get('warning',False):
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
@ -256,12 +261,12 @@ class sale_order_line(osv.osv):
message = False
if product_info.sale_line_warn != 'no-message':
if product_info.sale_line_warn == 'block':
raise osv.except_osv(_('Alert for %s!') % (product_info.name), product_info.sale_line_warn_msg)
title = _("Warning for %s") % product_info.name
message = product_info.sale_line_warn_msg
warning['title'] = title
warning['message'] = message
if product_info.sale_line_warn == 'block':
return {'value': {'product_id': False}, 'warning': warning}
result = super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty,
uom, qty_uos, uos, name, partner_id,
@ -288,12 +293,12 @@ class purchase_order_line(osv.osv):
message = False
if product_info.purchase_line_warn != 'no-message':
if product_info.purchase_line_warn == 'block':
raise osv.except_osv(_('Alert for %s!') % (product_info.name), product_info.purchase_line_warn_msg)
title = _("Warning for %s") % product_info.name
message = product_info.purchase_line_warn_msg
warning['title'] = title
warning['message'] = message
if product_info.purchase_line_warn == 'block':
return {'value': {'product_id': False}, 'warning': warning}
result = super(purchase_order_line, self).onchange_product_id(cr, uid, ids, pricelist, product, qty, uom,
partner_id, date_order, fiscal_position_id)

View File

@ -2,6 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_website_public,website,website.model_website,,1,0,0,0
access_website,website,website.model_website,base.group_website_designer,1,1,1,1
access_website_menu,access_website_menu,model_website_menu,,1,0,0,0
access_website_menu_designer,Web Menu Manager,model_website_menu,base.group_website_designer,1,1,1,1
access_website,web menu manager,website.model_website,base.group_website_designer,1,1,1,1
access_website_converter_test,access_website_converter_test,model_website_converter_test,,1,1,1,1
access_website_converter_test_sub,access_website_converter_test_sub,model_website_converter_test_sub,,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_website_public website website.model_website 1 0 0 0
3 access_website website website.model_website base.group_website_designer 1 1 1 1
4 access_website_menu access_website_menu model_website_menu 1 0 0 0
5 access_website_menu_designer Web Menu Manager model_website_menu base.group_website_designer 1 1 1 1
6 access_website web menu manager website.model_website base.group_website_designer 1 1 1 1
7 access_website_converter_test access_website_converter_test model_website_converter_test 1 1 1 1
8 access_website_converter_test_sub access_website_converter_test_sub model_website_converter_test_sub 1 1 1 1

View File

@ -26,10 +26,10 @@
}
</style>
<script>var partner_url = '<t t-raw="partner_url or ''"/>';</script>
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script src="//maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" t-attf-src="/google_map/partners.json?partner_ids=#{ partner_ids }"></script>
<script type="text/javascript" src="/website_google_map/static/src/js/markerclusterer_compiled.js"></script>
<script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script src="//code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" src="/website_google_map/static/src/js/google_map.js"></script>
</head>
<body>

View File

@ -122,7 +122,7 @@
<section class="oe_container oe_dark">
<div class="oe_row">
<h2 class="oe_slogan">Fine Tune Your Catalog</h2>
<h3 class="oe_slogan">Boost Sales by Emphasazing Best Products</h3>
<h3 class="oe_slogan">Boost Sales by Emphasizing Best Products</h3>
<div class="oe_span6">
<p class='oe_mt32'>
Get a full control on how you display your products in