From b25c054c93e7e5123fecf3e0e1165238c423e705 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 23 Mar 2016 14:50:46 +0100 Subject: [PATCH 1/4] [FIX] web: prevent crash in rare case with status field Current behavior before PR: if you create a new record within a one2many field and the model's form has a clickable status bar defined, clicking this status bar will raise an exception because the virtual id (one2many_v_XXXX) will be passed to the model's write method Desired behavior after PR is merged: clicking just changes the cached value --- addons/web/static/src/js/view_form.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 9f89f729a58..72744618fa4 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -6145,13 +6145,20 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ val = $li.data("id"); } if (val != self.get('value')) { - this.view.recursive_save().done(function() { - var change = {}; - change[self.name] = val; - self.view.dataset.write(self.view.datarecord.id, change).done(function() { - self.view.reload(); + if(!this.view.datarecord.id || + this.view.datarecord.id.toString().match(instance.web.BufferedDataSet.virtual_id_regex)) { + // don't save, only set value for not-yet-saved many2ones + self.set_value(val); + } + else { + this.view.recursive_save().done(function() { + var change = {}; + change[self.name] = val; + self.view.dataset.write(self.view.datarecord.id, change).done(function() { + self.view.reload(); + }); }); - }); + } } }, }); From 880e951f344cc1a4f96040b5e9e7d07936ac0d81 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Mon, 11 Apr 2016 17:10:55 +0200 Subject: [PATCH 2/4] [FIX] website_event_sale: get_unit_price with event ticket When the line is linked to an event ticket, the price must be taken on the ticket. Fixes #11521 opw:673176 --- addons/website_event_sale/controllers/main.py | 18 +++++++++++++++++- .../website_sale/static/src/js/website_sale.js | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index f57f0c8e175..1d7f4c65ddf 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -23,7 +23,7 @@ from openerp import SUPERUSER_ID from openerp.addons.web import http from openerp.addons.web.http import request from openerp.addons.website_event.controllers.main import website_event -from openerp.addons.website_sale.controllers.main import get_pricelist +from openerp.addons.website_sale.controllers.main import get_pricelist, website_sale from openerp.tools.translate import _ @@ -74,4 +74,20 @@ class website_event(website_event): return super(website_event, self)._add_event(event_name, context, **kwargs) +class website_sale(website_sale): + @http.route(['/shop/get_unit_price'], type='json', auth="public", methods=['POST'], website=True) + def get_unit_price(self, product_ids, add_qty, use_order_pricelist=False, **kw): + cr, uid, context, pool = request.cr, request.uid, request.context, request.registry + res_ticket = {} + if 'line_id' in kw: + line = pool['sale.order.line'].browse(cr, SUPERUSER_ID, kw['line_id']) + if line.event_ticket_id: + if line.order_id.pricelist_id: + ticket = pool['event.event.ticket'].browse(cr, SUPERUSER_ID, line.event_ticket_id.id, context=dict(context, pricelist=line.order_id.pricelist_id.id)) + else: + ticket = line.event_ticket_id + res_ticket = {ticket.product_id.id: ticket.price_reduce or ticket.price} + product_ids.remove(ticket.product_id.id) + res_options = super(website_sale, self).get_unit_price(product_ids, add_qty, use_order_pricelist, **kw) + return dict(res_ticket.items() + res_options.items()) diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index b72df30c67f..565e82b458b 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -69,7 +69,8 @@ $('.oe_website_sale').each(function () { openerp.jsonRpc("/shop/get_unit_price", 'call', { 'product_ids': product_ids, 'add_qty': value, - 'use_order_pricelist': true}) + 'use_order_pricelist': true, + 'line_id': line_id}) .then(function (res) { //basic case $dom.find('span.oe_currency_value').last().text(res[product_id].toFixed(2)); From ffdfadfc9148c679c9f82393703e4a3877edbf08 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 13 Apr 2016 14:34:45 +0200 Subject: [PATCH 3/4] [FIX] paypal: no spurious warning about IPN version In addition to being mostly useless because Paypal's API changes are supposed to be backwards-compatible, this warning was using inconsistent version numbers. Switched to a simple INFO line with IPN version. --- addons/payment_paypal/models/paypal.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/addons/payment_paypal/models/paypal.py b/addons/payment_paypal/models/paypal.py index 52c3ec768d4..fa9c4e7a660 100644 --- a/addons/payment_paypal/models/paypal.py +++ b/addons/payment_paypal/models/paypal.py @@ -203,11 +203,7 @@ class TxPaypal(osv.Model): def _paypal_form_get_invalid_parameters(self, cr, uid, tx, data, context=None): invalid_parameters = [] - if data.get('notify_version')[0] != '3.4': - _logger.warning( - 'Received a notification from Paypal with version %s instead of 2.6. This could lead to issues when managing it.' % - data.get('notify_version') - ) + _logger.info('Received a notification from Paypal with IPN version %s', data.get('notify_version')) if data.get('test_ipn'): _logger.warning( 'Received a notification from Paypal using sandbox' From 46c5f93b6cfa2b348078e674cd9e271ef5f81292 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 13 Apr 2016 12:51:20 +0200 Subject: [PATCH 4/4] [FIX] website_sale: verify match between TX and SO amount When processing a payment transaction, double-check the match between the amount of the transaction and the amount of the SO, to be sure that we won't be validating a SO that has been modified since the payment. Such cases have to be double-checked manually. Also add a bit of extra logging to make auditing ecommerce transactions easier. --- addons/website_sale/models/payment.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/addons/website_sale/models/payment.py b/addons/website_sale/models/payment.py index 6129de092aa..f38b9463d05 100644 --- a/addons/website_sale/models/payment.py +++ b/addons/website_sale/models/payment.py @@ -3,6 +3,7 @@ import logging from openerp import SUPERUSER_ID from openerp.osv import orm, fields +from openerp.tools import float_compare _logger = logging.getLogger(__name__) @@ -26,10 +27,20 @@ class PaymentTransaction(orm.Model): tx_find_method_name = '_%s_form_get_tx_from_data' % acquirer_name if hasattr(self, tx_find_method_name): tx = getattr(self, tx_find_method_name)(cr, uid, data, context=context) - if tx and tx.state == 'done' and tx.sale_order_id and tx.sale_order_id.state in ['draft', 'sent']: - self.pool['sale.order'].action_button_confirm(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=dict(context, send_email=True)) - elif tx and tx.state not in ['cancel'] and tx.sale_order_id and tx.sale_order_id.state in ['draft']: - self.pool['sale.order'].force_quotation_send(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=context) + _logger.info('<%s> transaction processed: tx ref:%s, tx amount: %s', acquirer_name, tx.reference if tx else 'n/a', tx.amount if tx else 'n/a') + + if tx and tx.sale_order_id: + # verify SO/TX match, excluding tx.fees which are currently not included in SO + amount_matches = (tx.sale_order_id.state in ['draft', 'sent'] and float_compare(tx.amount, tx.sale_order_id.amount_total, 2) == 0) + if amount_matches: + if tx.state == 'done': + _logger.info('<%s> transaction completed, confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) + self.pool['sale.order'].action_button_confirm(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=dict(context, send_email=True)) + elif tx.state != 'cancel' and tx.sale_order_id.state == 'draft': + _logger.info('<%s> transaction pending, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) + self.pool['sale.order'].force_quotation_send(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=context) + else: + _logger.warning('<%s> transaction MISMATCH for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) except Exception: _logger.exception('Fail to confirm the order or send the confirmation email%s', tx and ' for the transaction %s' % tx.reference or '')