diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index d0eb8c4765d..6015f7f4e05 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -700,7 +700,9 @@ class account_analytic_account(osv.osv): def _prepare_invoice_lines(self, cr, uid, contract, fiscal_position_id, context=None): fpos_obj = self.pool.get('account.fiscal.position') - fiscal_position = fpos_obj.browse(cr, uid, fiscal_position_id, context=context) + fiscal_position = None + if fiscal_position_id: + fiscal_position = fpos_obj.browse(cr, uid, fiscal_position_id, context=context) invoice_lines = [] for line in contract.recurring_invoice_line_ids: diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 128e5928f70..f43beef566e 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -222,9 +222,9 @@ class email_template(osv.osv): help="Optional translation language (ISO code) to select when sending out an email. " "If not set, the english version will be used. " "This should usually be a placeholder expression " - "that provides the appropriate language code, e.g. " - "${object.partner_id.lang.code}.", - placeholder="${object.partner_id.lang.code}"), + "that provides the appropriate language, e.g. " + "${object.partner_id.lang}.", + placeholder="${object.partner_id.lang}"), 'user_signature': fields.boolean('Add Signature', help="If checked, the user's signature will be appended to the text version " "of the message"), diff --git a/addons/event/event.py b/addons/event/event.py index 2fb831b66f3..e5a12fb02d2 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -55,7 +55,7 @@ class event_event(models.Model): readonly=False, states={'done': [('readonly', True)]}) type = fields.Many2one('event.type', string='Type of Event', readonly=False, states={'done': [('readonly', True)]}) - seats_max = fields.Integer(string='Maximum Avalaible Seats', oldname='register_max', + seats_max = fields.Integer(string='Maximum Available Seats', oldname='register_max', readonly=True, states={'draft': [('readonly', False)]}, help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )") seats_min = fields.Integer(string='Minimum Reserved Seats', oldname='register_min', diff --git a/addons/event_sale/event_sale.py b/addons/event_sale/event_sale.py index a0940b5af2d..2e2313f12a2 100644 --- a/addons/event_sale/event_sale.py +++ b/addons/event_sale/event_sale.py @@ -186,7 +186,7 @@ class event_ticket(osv.osv): 'deadline': fields.date("Sales End"), 'is_expired': fields.function(_is_expired, type='boolean', string='Is Expired'), 'price': fields.float('Price'), - 'seats_max': fields.integer('Maximum Avalaible Seats', oldname='register_max', help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )"), + 'seats_max': fields.integer('Maximum Available Seats', oldname='register_max', help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )"), 'seats_reserved': fields.function(_get_seats, string='Reserved Seats', type='integer', multi='seats_reserved'), 'seats_available': fields.function(_get_seats, string='Available Seats', type='integer', multi='seats_reserved'), 'seats_unconfirmed': fields.function(_get_seats, string='Unconfirmed Seat Reservations', type='integer', multi='seats_reserved'), diff --git a/addons/hr_holidays/report/holidays_summary_report.py b/addons/hr_holidays/report/holidays_summary_report.py index a5889360774..d197ea238dc 100644 --- a/addons/hr_holidays/report/holidays_summary_report.py +++ b/addons/hr_holidays/report/holidays_summary_report.py @@ -220,8 +220,7 @@ class report_custom(report_rml): elif data['model']=='ir.ui.menu': for dept in obj_dept.browse(cr, uid, data['form']['depts'], context=context): - cr.execute("SELECT id FROM hr_employee WHERE department_id = %s", (dept.id,)) - emp_ids = [x[0] for x in cr.fetchall()] + emp_ids = obj_emp.search(cr, uid, [('department_id', '=', dept.id)], context=context) if emp_ids==[]: continue dept_done=0 diff --git a/addons/mass_mailing/models/mail_mail.py b/addons/mass_mailing/models/mail_mail.py index 0e44399a256..41be9c21cbf 100644 --- a/addons/mass_mailing/models/mail_mail.py +++ b/addons/mass_mailing/models/mail_mail.py @@ -74,6 +74,11 @@ class MailMail(osv.Model): """ Override to add the tracking URL to the body. """ body = super(MailMail, self).send_get_mail_body(cr, uid, mail, partner=partner, context=context) + # prepend tag for images using absolute urls + domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "web.base.url", context=context) + base = "" % domain + body = tools.append_content_to_html(base, body, plaintext=False, container_tag='div') + # generate tracking URL if mail.statistics_ids: tracking_url = self._get_tracking_url(cr, uid, mail, partner, context=context) diff --git a/addons/product/product_report.xml b/addons/product/product_report.xml index 5d2c3b99f62..9c7ca7e390c 100644 --- a/addons/product/product_report.xml +++ b/addons/product/product_report.xml @@ -10,6 +10,6 @@ report_type="qweb-pdf" name="product.report_pricelist" file="product.report_pricelist" - /> + menu="False"/> diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index 2e72d1aebf0..510c21ed88d 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -69,7 +69,7 @@ - + @@ -269,6 +269,9 @@
Product Variant
+ + lst_price + diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv index 8a94f027408..b05ab25d3cf 100644 --- a/addons/stock/security/ir.model.access.csv +++ b/addons/stock/security/ir.model.access.csv @@ -3,7 +3,7 @@ access_stock_incoterms_all,stock.incoterms all,model_stock_incoterms,,1,0,0,0 access_stock_incoterms_manager,stock.incoterms manager,model_stock_incoterms,stock.group_stock_manager,1,1,1,1 access_stock_warehouse_manager,stock.warehouse.manager,model_stock_warehouse,stock.group_stock_manager,1,1,1,1 access_stock_warehouse_user,stock.warehouse.user,model_stock_warehouse,base.group_user,1,0,0,0 -access_stock_location__partner_manager,stock.location.partner.manager,model_stock_location,base.group_partner_manager,1,1,1,1 +access_stock_location__partner_manager,stock.location.partner.manager,model_stock_location,base.group_partner_manager,1,0,0,0 access_stock_location_manager,stock.location.manager,model_stock_location,stock.group_stock_manager,1,1,1,1 access_stock_location_user,stock.location.user,model_stock_location,base.group_user,1,0,0,0 access_stock_picking_user,stock.picking user,model_stock_picking,stock.group_stock_user,1,1,1,1 diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index fe86088fad0..5d1dbe04bec 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -40,8 +40,7 @@ class Website(openerp.addons.web.controllers.main.Home): if not (first_menu.url.startswith(('/page/', '/?', '/#')) or (first_menu.url=='/')): return request.redirect(first_menu.url) if first_menu.url.startswith('/page/'): - page = first_menu.url[6:] - + return request.registry['ir.http'].reroute(first_menu.url) return self.page(page) @http.route(website=True, auth="public") diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index c6d3c62f61d..dab436390a0 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -94,6 +94,17 @@ + + +
@@ -150,17 +161,6 @@
- - - diff --git a/addons/website_crm/controllers/main.py b/addons/website_crm/controllers/main.py index ce9ea9bb675..b209c70d962 100644 --- a/addons/website_crm/controllers/main.py +++ b/addons/website_crm/controllers/main.py @@ -39,7 +39,7 @@ class contactus(http.Controller): post_file = [] # List of file to add to ir_attachment once we have the ID post_description = [] # Info to add after the message - values = {'user_id': False} + values = {} lead_model = request.registry['crm.lead'] @@ -80,7 +80,7 @@ class contactus(http.Controller): post_description.append("%s: %s" % ("REFERER", environ.get("HTTP_REFERER"))) values['description'] += dict_to_str(_("Environ Fields: "), post_description) - lead_id = lead_model.create(request.cr, SUPERUSER_ID, values, request.context) + lead_id = lead_model.create(request.cr, SUPERUSER_ID, dict(values, user_id=False), request.context) if lead_id: for field_value in post_file: attachment_value = { diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py index c104b26ce27..a48ce49af11 100644 --- a/addons/website_crm_partner_assign/controllers/main.py +++ b/addons/website_crm_partner_assign/controllers/main.py @@ -109,7 +109,7 @@ class WebsiteCrmPartnerAssign(http.Controller): context=request.context) # todo in trunk: order="grade_id DESC, implemented_count DESC", offset=pager['offset'], limit=self._references_per_page partners = partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context) # remove me in trunk - partners = sorted(partners, key=lambda x: (-1 * (x.grade_id and x.grade_id.id or 0), len(x.implemented_partner_ids)), reverse=True) + partners = sorted(partners, key=lambda x: (x.grade_id.sequence if x.grade_id else 0, len([i for i in x.implemented_partner_ids if i.website_published])), reverse=True) partners = partners[pager['offset']:pager['offset'] + self._references_per_page] google_map_partner_ids = ','.join(map(str, [p.id for p in partners])) diff --git a/addons/website_forum/models/forum.py b/addons/website_forum/models/forum.py index a34eb101352..891ce3b982d 100644 --- a/addons/website_forum/models/forum.py +++ b/addons/website_forum/models/forum.py @@ -112,7 +112,7 @@ class Post(osv.Model): _name = 'forum.post' _description = 'Forum Post' _inherit = ['mail.thread', 'website.seo.metadata'] - _order = "is_correct DESC, vote_count DESC" + _order = "is_correct DESC, vote_count DESC, write_date DESC" def _get_user_vote(self, cr, uid, ids, field_name, arg, context): res = dict.fromkeys(ids, 0) diff --git a/addons/website_partner/models/res_partner.py b/addons/website_partner/models/res_partner.py index 08fc29ce75f..2162e6be91d 100644 --- a/addons/website_partner/models/res_partner.py +++ b/addons/website_partner/models/res_partner.py @@ -17,7 +17,7 @@ class WebsiteResPartner(osv.Model): 'Website Partner Full Description' ), 'website_short_description': fields.text( - 'Website artner Short Description' + 'Website Partner Short Description' ), # hack to allow using plain browse record in qweb views 'self': fields.function(_get_ids, type='many2one', relation=_name), diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 2c52e21e281..5c8bf288f16 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -820,5 +820,30 @@ class website_sale(http.Controller): product = product_obj.browse(request.cr, request.uid, id, context=request.context) return product.write({'website_size_x': x, 'website_size_y': y}) + @http.route(['/shop/tracking_last_order'], type='json', auth="public") + def tracking_cart(self, **post): + """ return JS code for google analytics""" + cr, uid, context = request.cr, request.uid, request.context + ret = {} + sale_order_id = request.session.get('sale_last_order_id') + if sale_order_id: + order = request.registry['sale.order'].browse(cr, SUPERUSER_ID, sale_order_id, context=context) + ret['transaction'] = { + 'id': sale_order_id, + 'affiliation': order.company_id.name, + 'revenue': order.amount_total, + 'currency': order.currency_id.name + } + ret['lines'] = [] + for line in order.order_line: + if not line.is_delivery: + ret['lines'].append({ + 'id': line.order_id and line.order_id.id, + 'name': line.product_id.categ_id and line.product_id.categ_id.name or '-', + 'sku': line.product_id.id, + 'quantity': line.product_uom_qty, + 'price': line.price_unit, + }) + return ret # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/website_sale/static/src/js/website_sale_tracking.js b/addons/website_sale/static/src/js/website_sale_tracking.js new file mode 100644 index 00000000000..8ff5a5ac789 --- /dev/null +++ b/addons/website_sale/static/src/js/website_sale_tracking.js @@ -0,0 +1,67 @@ +$(document).ready(function () { + + // Watching a product + if ($("#product_detail.oe_website_sale").length) { + prod_id = $("input[name='product_id']").attr('value'); + vpv("/stats/ecom/product_view/" + prod_id); + } + + // Add a product into the cart + $(".oe_website_sale form[action='/shop/cart/update'] a.a-submit").on('click', function(o) { + prod_id = $("input[name='product_id']").attr('value'); + vpv("/stats/ecom/product_add_to_cart/" + prod_id); + }); + + // Start checkout + $(".oe_website_sale a[href='/shop/checkout']").on('click', function(o) { + vpv("/stats/ecom/customer_checkout"); + }); + + $(".oe_website_sale div.oe_cart a[href^='/web?redirect'][href$='/shop/checkout']").on('click', function(o) { + vpv("/stats/ecom/customer_signin"); + }); + + $(".oe_website_sale form[action='/shop/confirm_order'] a.a-submit").on('click', function(o) { + if ($("#top_menu > li > a[href='/web/login']").length){ + vpv("/stats/ecom/customer_signup"); + } + vpv("/stats/ecom/order_checkout"); + }); + + $(".oe_website_sale form[target='_self'] button[type=submit]").on('click', function(o) { + var method = $("#payment_method input[name=acquirer]:checked").nextAll("span:first").text(); + vpv("/stats/ecom/order_payment/" + method); + }); + + if ($(".oe_website_sale div.oe_cart div.oe_website_sale_tx_status").length) { + track_ga('require', 'ecommerce'); + + order_id = $(".oe_website_sale div.oe_cart div.oe_website_sale_tx_status").data("order-id"); + vpv("/stats/ecom/order_confirmed/" + order_id); + + openerp.jsonRpc("/shop/tracking_last_order/").then(function(o) { + track_ga('ecommerce:clear'); + + if (o.transaction && o.lines) { + track_ga('ecommerce:addTransaction', o.transaction); + _.forEach(o.lines, function(line) { + track_ga('ecommerce:addItem', line); + }); + } + track_ga('ecommerce:send'); + }); + } + + function vpv(page){ //virtual page view + track_ga('send', 'pageview', { + 'page': page, + 'title': document.title, + }); + } + + function track_ga() { + website_ga = this._gaw || function(){}; + website_ga.apply(this, arguments); + } + +}); diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index 4a6539a5007..1e65c91a5a0 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -344,6 +344,7 @@