[MERGE] Forward-port of latest saas-3 bugfixes, up to rev. 9439 revid:dle@openerp.com-20140429112147-hqfqd0y487s817n7

bzr revid: dle@openerp.com-20140429112738-y2el246dmd2o2zy9
This commit is contained in:
Denis Ledoux 2014-04-29 13:27:38 +02:00
commit 79d4c57877
17 changed files with 101 additions and 89 deletions

View File

@ -65,7 +65,8 @@
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button type="action" string="Invoices"
name="%(account.action_invoice_tree)d"
name="%(account.action_invoice_tree1)d"
attrs="{'invisible': [('customer', '=', False)]}"
context="{'search_default_partner_id': active_id,'default_partner_id': active_id}" groups="account.group_account_invoice"/>
<button type="action" string="Journal Items" name="%(account.action_account_moves_all_tree)d" groups="account.group_account_user"/>
<button type="action" string="Contracts" name="%(account.action_open_partner_analytic_accounts)d"

View File

@ -73,9 +73,7 @@ class account_analytic_invoice_line(osv.osv):
result = {}
res = self.pool.get('product.product').browse(cr, uid, product, context=context)
result.update({'name':res.partner_ref or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': res.list_price or 0.0})
if res.description:
result['name'] += '\n'+res.description
result.update({'name': name or res.description or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': price_unit or res.list_price or 0.0})
res_final = {'value':result}
if result['uom_id'] != res.uom_id.id:

View File

@ -194,9 +194,9 @@ class delivery_grid(osv.osv):
for line in order.order_line:
if not line.product_id or line.is_delivery:
continue
total += line.price_subtotal or 0.0
weight += (line.product_id.weight or 0.0) * line.product_uom_qty
volume += (line.product_id.volume or 0.0) * line.product_uom_qty
total = order.amount_total or 0.0
return self.get_price_from_picking(cr, uid, id, total,weight, volume, context=context)

View File

@ -118,7 +118,7 @@ openerp.gamification = function(instance) {
}
});
instance.mail.Widget.include({
instance.mail.Wall.include({
start: function() {
this._super();
var sidebar = new instance.gamification.Sidebar(this);

View File

@ -4,18 +4,6 @@
<!-- challenges -->
<record model="gamification.challenge" id="challenge_crm_sale">
<field name="user_ids" eval="[(4,ref('base.user_demo'))]" />
<field name="state">inprogress</field>
</record>
<!-- goals -->
<record model="gamification.goal" id="goal_crm_sale1">
<field name="definition_id" eval="ref('definition_crm_tot_invoices')" />
<field name="user_id" eval="ref('base.user_demo')" />
<field name="line_id" eval="ref('line_crm_sale1')" />
<field name="start_date" eval="time.strftime('%Y-%m-01')" />
<field name="end_date" eval="(DateTime.today().replace(day=1)+relativedelta(months=1, days=-1)).strftime('%Y-%m-%d')" />
<field name="target_goal">2000</field>
<field name="state">inprogress</field>
</record>

View File

@ -20,10 +20,11 @@
##############################################################################
import time
from datetime import datetime, timedelta
from datetime import datetime
from dateutil.relativedelta import relativedelta
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.translate import _
class hr_timesheet_sheet(osv.osv):
@ -396,18 +397,22 @@ 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):
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)
return sheet_ids and sheet_ids[0] or False
def _sheet(self, cursor, user, ids, name, args, context=None):
sheet_obj = self.pool.get('hr_timesheet_sheet.sheet')
res = {}.fromkeys(ids, False)
for attendance in self.browse(cursor, user, ids, context=context):
date_to = datetime.strftime(datetime.strptime(attendance.name[0:10], '%Y-%m-%d'), '%Y-%m-%d %H:%M:%S')
sheet_ids = sheet_obj.search(cursor, user,
[('date_to', '>=', date_to), ('date_from', '<=', attendance.name),
('employee_id', '=', attendance.employee_id.id)],
context=context)
if sheet_ids:
# [0] because only one sheet possible for an employee between 2 dates
res[attendance.id] = sheet_obj.name_get(cursor, user, sheet_ids, context=context)[0]
res[attendance.id] = self._get_current_sheet(cursor, user, attendance.employee_id.id, attendance.name, context=context)
return res
_columns = {
@ -426,16 +431,15 @@ class hr_attendance(osv.osv):
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if 'sheet_id' in context:
ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, context['sheet_id'], context=context)
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:
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 cannot modify an entry in a confirmed timesheet.'))
res = super(hr_attendance,self).create(cr, uid, vals, context=context)
if 'sheet_id' in context:
if context['sheet_id'] != self.browse(cr, uid, res, context=context).sheet_id.id:
raise osv.except_osv(_('User Error!'), _('You cannot enter an attendance ' \
'date outside the current timesheet dates.'))
return res
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'):
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)
def unlink(self, cr, uid, ids, *args, **kwargs):
if isinstance(ids, (int, long)):

View File

@ -805,9 +805,17 @@ class product_product(osv.osv):
price_type_currency_id = pricetype_obj.browse(cr,uid,price_type_id).currency_id.id
res = {}
# standard_price field can only be seen by users in base.group_user
# Thus, in order to compute the sale price from the cost price for users not in this group
# We fetch the standard price as the superuser
for product in products:
if ptype != 'standard_price':
res[product.id] = product[ptype] or 0.0
else:
res[product.id] = self.read(cr, SUPERUSER_ID, product.id, [ptype], context=context)[ptype] or 0.0
product_uom_obj = self.pool.get('product.uom')
for product in products:
res[product.id] = product[ptype] or 0.0
if ptype == 'list_price':
res[product.id] = (res[product.id] * (product.price_margin or 1.0)) + \
product.price_extra

View File

@ -356,7 +356,6 @@ class sale_order(osv.osv):
}
def ship_recreate(self, cr, uid, order, line, move_id, proc_id):
# FIXME: deals with potentially cancelled shipments, seems broken (specially if shipment has production lot)
"""
Define ship_recreate for process after shipping exception
param order: sales order to which the order lines belong
@ -365,16 +364,25 @@ class sale_order(osv.osv):
param proc_id: the ID of procurement
"""
move_obj = self.pool.get('stock.move')
if order.state == 'shipping_except':
for pick in order.picking_ids:
for move in pick.move_lines:
if move.state == 'cancel':
mov_ids = move_obj.search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)])
if mov_ids:
for mov in move_obj.browse(cr, uid, mov_ids):
# FIXME: the following seems broken: what if move_id doesn't exist? What if there are several mov_ids? Shouldn't that be a sum?
move_obj.write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
self.pool.get('procurement.order').write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
proc_obj = self.pool.get('procurement.order')
if move_id and order.state == 'shipping_except':
current_move = move_obj.browse(cr, uid, move_id)
moves = []
for picking in order.picking_ids:
if picking.id != current_move.picking_id.id and picking.state != 'cancel':
moves.extend(move for move in picking.move_lines if move.state != 'cancel' and move.sale_line_id.id == line.id)
if moves:
product_qty = current_move.product_qty
product_uos_qty = current_move.product_uos_qty
for move in moves:
product_qty -= move.product_qty
product_uos_qty -= move.product_uos_qty
if product_qty > 0 or product_uos_qty > 0:
move_obj.write(cr, uid, [move_id], {'product_qty': product_qty, 'product_uos_qty': product_uos_qty})
proc_obj.write(cr, uid, [proc_id], {'product_qty': product_qty, 'product_uos_qty': product_uos_qty})
else:
current_move.unlink()
proc_obj.unlink(cr, uid, [proc_id])
return True
def _get_date_planned(self, cr, uid, order, line, start_date, context=None):

View File

@ -1271,7 +1271,7 @@
<field name="date" attrs="{'invisible': [('state', '!=', 'done')]}"/>
</group>
<group string="Traceability"
groups="stock.group_tracking_lot">
groups="stock.group_tracking_lot,stock.group_production_lot">
<label for="tracking_id" groups="stock.group_tracking_lot"/>
<div groups="stock.group_tracking_lot">
<field name="tracking_id" class="oe_inline"/>

View File

@ -51,7 +51,7 @@
<section class="oe_container">
<div class="oe_row">
<h2 class="oe_slogan">Automated Translation by Professionals</h2>
<h3 class="oe_slogan">Reach multi-languages audiences without Pain</h3>
<h3 class="oe_slogan">Reach multi-language audiences without pain</h3>
<div class="oe_span6">
<div class="oe_bg_img">
<img class="oe_picture" src="openerp_translation_tool.png">
@ -75,7 +75,7 @@
<h3 class="oe_slogan">Live chat with your visitors in one click</h3>
<div class="oe_span6">
<p class='oe_mt32'>
The integrated live chat features allows you to start chatting in
The integrated live chat feature allows you to start chatting in
real time with your visitors to get feedback on your recent posts
or get ideas to write new posts.
</p><p>
@ -93,7 +93,7 @@
<section class="oe_container">
<div class="oe_row">
<h2 class="oe_slogan">Build Visitors Loyalty</h2>
<h2 class="oe_slogan">Build Visitor Loyalty</h2>
<h3 class="oe_slogan">Social Media Integration and Easy Following</h3>
<div class="oe_span6">
<img class="oe_picture oe_screenshot" src="social.png">
@ -102,7 +102,7 @@
<p class='oe_mt32'>
The one click <em>follow</em> button will allow visitors
to receive your blog posts by email with no effort, without
having to register. Social media icons allows visitors to share
having to register. Social media icons allow visitors to share
your best blog posts easily.
</p>
</div>
@ -116,7 +116,7 @@
<div class="oe_span6">
<p class='oe_mt32'>
Get a clear visibility of your sales funnel. OpenERP's Google
Analytics trackers are configured by default to track all kind of
Analytics trackers are configured by default to track all kinds of
events related to shopping carts, call-to-actions, etc.
</p><p>
As OpenERP marketing tools (mass mailing, campaigns, etc) are also
@ -141,13 +141,13 @@
<div class="oe_span6">
<p class='oe_mt32'>
SEO tools are ready to use, with no configuration required.
OpenERP suggests keywords for your titles according to Google
most searched terms, Google Analytics tracks interrests of your
visitors, sitemap are created automatically for Google
indexation, etc.
OpenERP suggests keywords for your titles according to Google's
most searched terms, Google Analytics tracks interests of your
visitors, sitemaps are created automatically for quick Google
indexing, etc.
</p><p>
We even do structured content automatically to promote your
product and events efficiently in Google.
The system even creates structured content automatically to promote your
products and events effectively in Google.
</p>
</div>
</div>
@ -162,10 +162,10 @@
Themes are awesome and easy to design. You don't need to develop
to create new pages, themes or building blocks. We use a clean
HTML structure, a <a href="http://getbootstrap.com/">bootstrap</a>
CSS and our modularity allows to distribute your themes easily.
CSS and our modularity allows you to distribute your themes easily.
</p><p>
The building block approach allows the website to remain clean
after the end-users start creating new contents.
after end-users start creating new contents.
</p>
</div>
<div class="oe_span6">
@ -178,7 +178,7 @@
<section class="oe_container">
<div class="oe_row">
<h2 class="oe_slogan">Easy Access Rights</h2>
<h3 class="oe_slogan">Adapt your publishing process to your own need</h3>
<h3 class="oe_slogan">A publishing process that meets your own needs</h3>
<div class="oe_span6">
<div class="oe_bg_img">
<img class="oe_picture oe_screenshot" src="blog_create.png">
@ -187,13 +187,13 @@
<div class="oe_span6">
<p class='oe_mt32'>
Not everyone requires the same access to your website.
Designers manage the layout of the site, editors approves content and
Designers manage the layout of the site, editors approve content and
authors write that content. This lets you organize your publishing
process according to your needs.
</p><p>
Others access rights related to business objects (products, people,
events, etc) directly follows OpenERP's standard access right allowing
you to not have to configure them twice.
Other access rights are related to business objects (products, people,
events, etc) and directly following OpenERP's standard access rights
management, so you do not have to configure things twice.
</p>
</div>
</div>

View File

@ -7,15 +7,15 @@ from openerp.addons.web.http import request
class sale_order_line(osv.osv):
_inherit = "sale.order.line"
def _recalculate_product_values(self, cr, uid, ids, product_id=0, context=None):
def _recalculate_product_values(self, cr, uid, ids, product_id=0, fiscal_position=False, context=None):
if not ids:
return super(sale_order_line, self)._recalculate_product_values(cr, uid, ids, product_id, context=context)
return super(sale_order_line, self)._recalculate_product_values(cr, uid, ids, product_id, fiscal_position=fiscal_position, context=context)
order_line = self.browse(cr, SUPERUSER_ID, ids[0], context=context)
assert order_line.order_id.website_session_id == request.session['website_session_id']
product = product_id and self.pool.get('product.product').browse(cr, uid, product_id, context=context) or order_line.product_id
res = super(sale_order_line, self)._recalculate_product_values(cr, uid, ids, product.id, context=context)
res = super(sale_order_line, self)._recalculate_product_values(cr, uid, ids, product.id, fiscal_position=fiscal_position, context=context)
if product.event_type_id and order_line.event_ticket_id and order_line.event_ticket_id.price != product.lst_price:
res.update({'price_unit': order_line.event_ticket_id.price})

View File

@ -406,14 +406,16 @@ class Ecommerce(http.Controller):
order.amount_total,
request.website._render("website_sale.total", {'website_sale_order': order})]
@http.route(['/shop/set_cart_json'], type='json', auth="public")
@http.route(['/shop/set_cart_json'], type='json', auth="public", website=True, multilang=True)
def set_cart_json(self, path=None, product_id=None, order_line_id=None, set_number=0, json=None):
quantity = request.registry['website']._ecommerce_add_product_to_cart(request.cr, request.uid,
product_id=product_id, order_line_id=order_line_id, set_number=set_number,
context=request.context)
order = self.get_order()
return [quantity,
order.get_number_of_products()]
order.get_number_of_products(),
order.amount_total,
request.website._render("website_sale.total", {'website_sale_order': order})]
@http.route(['/shop/checkout'], type='http', auth="public", website=True, multilang=True)
def checkout(self, **post):

View File

@ -33,7 +33,7 @@ class SaleOrder(osv.Model):
class SaleOrderLine(osv.Model):
_inherit = "sale.order.line"
def _recalculate_product_values(self, cr, uid, ids, product_id=0, context=None):
def _recalculate_product_values(self, cr, uid, ids, product_id=0, fiscal_position=False, context=None):
# TDE FIXME: seems to be defined several times -> fix me ?
if context is None:
context = {}
@ -49,5 +49,6 @@ class SaleOrderLine(osv.Model):
pricelist=context.pop('pricelist'),
product=product_id,
partner_id=user_obj.browse(cr, SUPERUSER_ID, uid).partner_id.id,
fiscal_position=fiscal_position,
context=context
)['value']

View File

@ -111,7 +111,7 @@ class Website(orm.Model):
# change and record value
if quantity:
vals = order_line_obj._recalculate_product_values(cr, uid, order_line_ids, product_id, context=context)
vals = order_line_obj._recalculate_product_values(cr, uid, order_line_ids, product_id, fiscal_position=order.fiscal_position.id, context=context)
values.update(vals)
values['product_uom_qty'] = quantity
values['product_id'] = product_id
@ -119,12 +119,11 @@ class Website(orm.Model):
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
values['name'] = "%s: %s" % (product.name, product.variants) if product.variants else product.name
values['tax_id'] = [(6, 0, [tax.id for tax in product.taxes_id])]
if order_line_id:
order_line_obj.write(cr, SUPERUSER_ID, order_line_ids, values, context=context)
else:
order_line_id = order_line_obj.create(cr, SUPERUSER_ID, values, context=context)
order_obj.write(cr, SUPERUSER_ID, [order.id], {'order_line': [(4, order_line_id)]}, context=context)
if values.get('tax_id'):
values['tax_id'] = [(6, 0, values['tax_id'])]
order_obj.write(cr, SUPERUSER_ID, [order.id], {'order_line': [(1, order_line_id, values) if order_line_id else (0, 0, values)]}, context=context)
elif order_line_ids:
order_line_obj.unlink(cr, SUPERUSER_ID, order_line_ids, context=context)

View File

@ -20,18 +20,20 @@ $(document).ready(function () {
.fadeIn(600);
}
$(".oe_website_sale .oe_mycart input.js_quantity").change(function () {
$(".oe_website_sale .oe_mycart input.js_quantity").change(function (ev) {
var $input = $(this);
var $link = $(ev.currentTarget);
var value = parseInt($input.val(), 10);
if (isNaN(value)) value = 0;
openerp.jsonRpc("/shop/set_cart_json", 'call', {'order_line_id': $input.data('id'), 'set_number': value})
.then(function (data) {
if (!data) {
if (!data[0]) {
location.reload();
return;
}
set_my_cart_quantity(data[1]);
$input.val(data[0]);
$link.parents(".input-group:first").find(".js_quantity").val(data[0]);
$('#mycart_total').replaceWith(data[3]);
});
});

View File

@ -82,10 +82,10 @@ class SaleOrder(orm.Model):
carrier_id = delivery_id
break
order.write({'carrier_id': carrier_id}, context=context)
if carrier_id:
order.delivery_set(context=context)
else:
order._delivery_unset(context=context)
if carrier_id:
order.delivery_set(context=context)
else:
order._delivery_unset(context=context)
return bool(carrier_id)

View File

@ -17,4 +17,5 @@ class Website(orm.Model):
product_id=product_id, order_line_id=order_line_id, number=number, set_number=set_number,
context=context)
order = self.ecommerce_get_current_order(cr, uid, context=context)
return self.pool['sale.order']._check_carrier_quotation(cr, uid, order, force_carrier_id=None, context=context) and quantity or None
self.pool['sale.order']._check_carrier_quotation(cr, uid, order, force_carrier_id=None, context=context) and quantity or None
return quantity