diff --git a/addons/crm/models/crm_team.py b/addons/crm/models/crm_team.py
index f24f253d230ad..107c2676aea8f 100644
--- a/addons/crm/models/crm_team.py
+++ b/addons/crm/models/crm_team.py
@@ -50,13 +50,18 @@ def _compute_unassigned_leads_count(self):
team.unassigned_leads_count = counts.get(team.id, 0)
def _compute_opportunities(self):
- opportunity_data = self.env['crm.lead'].read_group([
+ opportunity_data = self.env['crm.lead'].search([
('team_id', 'in', self.ids),
('probability', '<', 100),
('type', '=', 'opportunity'),
- ], ['planned_revenue', 'probability', 'team_id'], ['team_id'])
- counts = {datum['team_id'][0]: datum['team_id_count'] for datum in opportunity_data}
- amounts = {datum['team_id'][0]: (datum['planned_revenue'] * datum['probability'] / 100) for datum in opportunity_data}
+ ]).read(['planned_revenue', 'probability', 'team_id'])
+ counts = {}
+ amounts = {}
+ for datum in opportunity_data:
+ counts.setdefault(datum['team_id'][0], 0)
+ amounts.setdefault(datum['team_id'][0], 0)
+ counts[datum['team_id'][0]] += 1
+ amounts[datum['team_id'][0]] += (datum.get('planned_revenue', 0) * datum.get('probability', 0) / 100.0)
for team in self:
team.opportunities_count = counts.get(team.id, 0)
team.opportunities_amount = amounts.get(team.id, 0)
diff --git a/addons/fleet/models/fleet_vehicle_cost.py b/addons/fleet/models/fleet_vehicle_cost.py
index b8b3602f85118..d42d0b94c72f3 100644
--- a/addons/fleet/models/fleet_vehicle_cost.py
+++ b/addons/fleet/models/fleet_vehicle_cost.py
@@ -270,7 +270,7 @@ def scheduler_manage_contract_expiration(self):
'fleet.mail_act_fleet_contract_to_renew', contract.expiration_date,
user_id=contract.user_id.id)
- expired_contracts = self.search([('state', '!=', 'expired'), ('expiration_date', '<',fields.Date.today() )])
+ expired_contracts = self.search([('state', 'not in', ['expired', 'closed']), ('expiration_date', '<',fields.Date.today() )])
expired_contracts.write({'state': 'expired'})
futur_contracts = self.search([('state', 'not in', ['futur', 'closed']), ('start_date', '>', fields.Date.today())])
diff --git a/addons/l10n_tr/__manifest__.py b/addons/l10n_tr/__manifest__.py
index 6cd7f6b7d96d3..d02f945b72f1b 100644
--- a/addons/l10n_tr/__manifest__.py
+++ b/addons/l10n_tr/__manifest__.py
@@ -13,9 +13,9 @@
* Sihirbaz sizden hesap planı şablonu, planın kurulacağı şirket, banka hesap
bilgileriniz, ilgili para birimi gibi bilgiler isteyecek.
""",
- 'author': 'Ahmet Altınışık',
+ 'author': 'Ahmet Altınışık, Can Tecim',
'maintainer':'https://launchpad.net/~openerp-turkey',
- 'website':'https://launchpad.net/openerp-turkey',
+ 'website':'https://launchpad.net/openerp-turkey, http://www.cantecim.com',
'depends': [
'account',
],
@@ -24,5 +24,6 @@
'data/account_data.xml',
'data/account_tax_template_data.xml',
'data/account_chart_template_data.xml',
+ 'data/res.country.state.csv'
],
}
diff --git a/addons/l10n_tr/data/res.country.state.csv b/addons/l10n_tr/data/res.country.state.csv
new file mode 100644
index 0000000000000..7a749f056b1ed
--- /dev/null
+++ b/addons/l10n_tr/data/res.country.state.csv
@@ -0,0 +1,82 @@
+"id","country_id:id","name","code"
+state_tr_01,base.tr,"Adana","01"
+state_tr_02,base.tr,"Adıyaman","02"
+state_tr_03,base.tr,"Afyon","03"
+state_tr_04,base.tr,"Ağrı","04"
+state_tr_05,base.tr,"Amasya","05"
+state_tr_06,base.tr,"Ankara","06"
+state_tr_07,base.tr,"Antalya","07"
+state_tr_08,base.tr,"Artvin","08"
+state_tr_09,base.tr,"Aydın","09"
+state_tr_10,base.tr,"Balıkesir","10"
+state_tr_11,base.tr,"Bilecik","11"
+state_tr_12,base.tr,"Bingöl","12"
+state_tr_13,base.tr,"Bitlis","13"
+state_tr_14,base.tr,"Bolu","14"
+state_tr_15,base.tr,"Burdur","15"
+state_tr_16,base.tr,"Bursa","16"
+state_tr_17,base.tr,"Çanakkale","17"
+state_tr_18,base.tr,"Çankırı","18"
+state_tr_19,base.tr,"Çorum","19"
+state_tr_20,base.tr,"Denizli","20"
+state_tr_21,base.tr,"Diyarbakır","21"
+state_tr_22,base.tr,"Edirne","22"
+state_tr_23,base.tr,"Elazığ","23"
+state_tr_24,base.tr,"Erzincan","24"
+state_tr_25,base.tr,"Erzurum","25"
+state_tr_26,base.tr,"Eskişehir","26"
+state_tr_27,base.tr,"Gaziantep","27"
+state_tr_28,base.tr,"Giresun","28"
+state_tr_29,base.tr,"Gümüşhane","29"
+state_tr_30,base.tr,"Hakkari","30"
+state_tr_31,base.tr,"Hatay","31"
+state_tr_32,base.tr,"Isparta","32"
+state_tr_33,base.tr,"İçel","33"
+state_tr_34,base.tr,"İstanbul","34"
+state_tr_35,base.tr,"İzmir","35"
+state_tr_36,base.tr,"Kars","36"
+state_tr_37,base.tr,"Kastamonu","37"
+state_tr_38,base.tr,"Kayseri","38"
+state_tr_39,base.tr,"Kırklareli","39"
+state_tr_40,base.tr,"Kırşehir","40"
+state_tr_41,base.tr,"Kocaeli","41"
+state_tr_42,base.tr,"Konya","42"
+state_tr_43,base.tr,"Kütahya","43"
+state_tr_44,base.tr,"Malatya","44"
+state_tr_45,base.tr,"Manisa","45"
+state_tr_46,base.tr,"K.maraş","46"
+state_tr_47,base.tr,"Mardin","47"
+state_tr_48,base.tr,"Muğla","48"
+state_tr_49,base.tr,"Muş","49"
+state_tr_50,base.tr,"Nevşehir","50"
+state_tr_51,base.tr,"Niğde","51"
+state_tr_52,base.tr,"Ordu","52"
+state_tr_53,base.tr,"Rize","53"
+state_tr_54,base.tr,"Sakarya","54"
+state_tr_55,base.tr,"Samsun","55"
+state_tr_56,base.tr,"Siirt","56"
+state_tr_57,base.tr,"Sinop","57"
+state_tr_58,base.tr,"Sivas","58"
+state_tr_59,base.tr,"Tekirdağ","59"
+state_tr_60,base.tr,"Tokat","60"
+state_tr_61,base.tr,"Trabzon","61"
+state_tr_62,base.tr,"Tunceli","62"
+state_tr_63,base.tr,"Şanlıurfa","63"
+state_tr_64,base.tr,"Uşak","64"
+state_tr_65,base.tr,"Van","65"
+state_tr_66,base.tr,"Yozgat","66"
+state_tr_67,base.tr,"Zonguldak","67"
+state_tr_68,base.tr,"Aksaray","68"
+state_tr_69,base.tr,"Bayburt","69"
+state_tr_70,base.tr,"Karaman","70"
+state_tr_71,base.tr,"Kırıkkale","71"
+state_tr_72,base.tr,"Batman","72"
+state_tr_73,base.tr,"Şırnak","73"
+state_tr_74,base.tr,"Bartın","74"
+state_tr_75,base.tr,"Ardahan","75"
+state_tr_76,base.tr,"Iğdır","76"
+state_tr_77,base.tr,"Yalova","77"
+state_tr_78,base.tr,"Karabük","78"
+state_tr_79,base.tr,"Kilis","79"
+state_tr_80,base.tr,"Osmaniye","80"
+state_tr_81,base.tr,"Düzce","81"
\ No newline at end of file
diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py
index 87fdba30447be..d40c1842db03a 100644
--- a/addons/mass_mailing/models/mass_mailing.py
+++ b/addons/mass_mailing/models/mass_mailing.py
@@ -668,15 +668,28 @@ def _get_seen_list(self):
"""Returns a set of emails already targeted by current mailing/campaign (no duplicates)"""
self.ensure_one()
target = self.env[self.mailing_model_real]
- mail_field = 'email' if 'email' in target._fields else 'email_from'
- # avoid loading a large number of records in memory
- # + use a basic heuristic for extracting emails
- query = """
- SELECT lower(substring(%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)'))
- FROM mail_mail_statistics s
- JOIN %(target)s t ON (s.res_id = t.id)
- WHERE substring(%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)') IS NOT NULL
- """
+ if set(['email', 'email_from']) & set(target._fields):
+ mail_field = 'email' if 'email' in target._fields else 'email_from'
+ # avoid loading a large number of records in memory
+ # + use a basic heuristic for extracting emails
+ query = """
+ SELECT lower(substring(%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)'))
+ FROM mail_mail_statistics s
+ JOIN %(target)s t ON (s.res_id = t.id)
+ WHERE substring(%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)') IS NOT NULL
+ """
+ elif 'partner_id' in target._fields:
+ mail_field = 'email'
+ query = """
+ SELECT lower(substring(p.%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)'))
+ FROM mail_mail_statistics s
+ JOIN %(target)s t ON (s.res_id = t.id)
+ JOIN res_partner p ON (t.partner_id = p.id)
+ WHERE substring(p.%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)') IS NOT NULL
+ """
+ else:
+ raise UserError(_("Unsupported mass mailing model %s") % self.mailing_model_id.name)
+
if self.mass_mailing_campaign_id.unique_ab_testing:
query +="""
AND s.mass_mailing_campaign_id = %%(mailing_campaign_id)s;
diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py
index 3459fbfced36f..9935b06336693 100644
--- a/addons/mrp/models/mrp_workorder.py
+++ b/addons/mrp/models/mrp_workorder.py
@@ -246,7 +246,7 @@ def _onchange_qty_producing(self):
@api.multi
def write(self, values):
- if ('date_planned_start' in values or 'date_planned_finished' in values) and any(workorder.state == 'done' for workorder in self):
+ if list(values.keys()) != ['time_ids'] and any(workorder.state == 'done' for workorder in self):
raise UserError(_('You can not change the finished work order.'))
return super(MrpWorkorder, self).write(values)
diff --git a/addons/payment_adyen/models/payment.py b/addons/payment_adyen/models/payment.py
index c28093f826f81..03787a73233db 100644
--- a/addons/payment_adyen/models/payment.py
+++ b/addons/payment_adyen/models/payment.py
@@ -18,6 +18,31 @@
_logger = logging.getLogger(__name__)
+# https://docs.adyen.com/developers/development-resources/currency-codes
+CURRENCY_CODE_MAPS = {
+ "BHD": 3,
+ "CVE": 0,
+ "DJF": 0,
+ "GNF": 0,
+ "IDR": 0,
+ "JOD": 3,
+ "JPY": 0,
+ "KMF": 0,
+ "KRW": 0,
+ "KWD": 3,
+ "LYD": 3,
+ "OMR": 3,
+ "PYG": 0,
+ "RWF": 0,
+ "TND": 3,
+ "UGX": 0,
+ "VND": 0,
+ "VUV": 0,
+ "XAF": 0,
+ "XOF": 0,
+ "XPF": 0,
+}
+
class AcquirerAdyen(models.Model):
_inherit = 'payment.acquirer'
@@ -27,6 +52,16 @@ class AcquirerAdyen(models.Model):
adyen_skin_code = fields.Char('Skin Code', required_if_provider='adyen', groups='base.group_user')
adyen_skin_hmac_key = fields.Char('Skin HMAC Key', required_if_provider='adyen', groups='base.group_user')
+ @api.model
+ def _adyen_convert_amount(self, amount, currency):
+ """
+ Adyen requires the amount to be multiplied by 10^k,
+ where k depends on the currency code.
+ """
+ k = CURRENCY_CODE_MAPS.get(currency.name, 2)
+ paymentAmount = int(tools.float_round(amount, k) * (10**k))
+ return paymentAmount
+
@api.model
def _get_adyen_urls(self, environment):
""" Adyen URLs: yhpp: hosted payment page: pay.shtml for single, select.shtml for multiple """
@@ -120,12 +155,13 @@ def adyen_form_generate_values(self, values):
import datetime
from dateutil import relativedelta
+ paymentAmount = self._adyen_convert_amount(values['amount'], values['currency'])
if self.provider == 'adyen' and len(self.adyen_skin_hmac_key) == 64:
tmp_date = datetime.datetime.today() + relativedelta.relativedelta(days=1)
values.update({
'merchantReference': values['reference'],
- 'paymentAmount': '%d' % int(tools.float_round(values['amount'], 2) * 100),
+ 'paymentAmount': '%d' % paymentAmount,
'currencyCode': values['currency'] and values['currency'].name or '',
'shipBeforeDate': tmp_date.strftime('%Y-%m-%d'),
'skinCode': self.adyen_skin_code,
@@ -143,7 +179,7 @@ def adyen_form_generate_values(self, values):
values.update({
'merchantReference': values['reference'],
- 'paymentAmount': '%d' % int(tools.float_round(values['amount'], 2) * 100),
+ 'paymentAmount': '%d' % paymentAmount,
'currencyCode': values['currency'] and values['currency'].name or '',
'shipBeforeDate': tmp_date,
'skinCode': self.adyen_skin_code,
diff --git a/addons/payment_stripe/static/src/js/stripe.js b/addons/payment_stripe/static/src/js/stripe.js
index 262068fd12dec..7d8d20a05906c 100644
--- a/addons/payment_stripe/static/src/js/stripe.js
+++ b/addons/payment_stripe/static/src/js/stripe.js
@@ -21,9 +21,13 @@ odoo.define('payment_stripe.stripe', function(require) {
$.blockUI.defaults.css["background-color"] = '';
$.blockUI.defaults.overlayCSS["opacity"] = '0.9';
}
+ var stripeHandler;
function getStripeHandler()
{
- var handler = StripeCheckout.configure({
+ if (stripeHandler) {
+ return stripeHandler;
+ }
+ var handler = stripeHandler = StripeCheckout.configure({
key: $("input[name='stripe_key']").val(),
image: $("input[name='stripe_image']").val(),
locale: 'auto',
diff --git a/addons/payment_stripe/views/payment_stripe_templates.xml b/addons/payment_stripe/views/payment_stripe_templates.xml
index 2fc054f97ae14..059bf6fede425 100644
--- a/addons/payment_stripe/views/payment_stripe_templates.xml
+++ b/addons/payment_stripe/views/payment_stripe_templates.xml
@@ -21,7 +21,12 @@
-
+
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index 80b23f8635e69..bd72567d2e831 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -147,7 +147,7 @@ def _default_group_id(self):
restrict_partner_id = fields.Many2one('res.partner', 'Owner ', help="Technical field used to depict a restriction on the ownership of quants to consider when marking this move as 'done'")
route_ids = fields.Many2many('stock.location.route', 'stock_location_route_move', 'move_id', 'route_id', 'Destination route', help="Preferred route")
warehouse_id = fields.Many2one('stock.warehouse', 'Warehouse', help="Technical field depicting the warehouse to consider for the route selection on the next procurement (if any).")
- has_tracking = fields.Selection(related='product_id.tracking', string='Product with Tracking')
+ has_tracking = fields.Selection(related='product_id.tracking', string='Product with Tracking', readonly=True)
quantity_done = fields.Float('Quantity Done', compute='_quantity_done_compute', digits=dp.get_precision('Product Unit of Measure'), inverse='_quantity_done_set')
show_operations = fields.Boolean(related='picking_id.picking_type_id.show_operations')
show_details_visible = fields.Boolean('Details Visible', compute='_compute_show_details_visible')
diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml
index 640c95d358056..211d2afba9538 100644
--- a/addons/stock/views/stock_picking_views.xml
+++ b/addons/stock/views/stock_picking_views.xml
@@ -353,7 +353,7 @@
-
+
diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py
index 327a0c7f007c1..b659bda25fb3e 100644
--- a/addons/test_mail/tests/test_mail_gateway.py
+++ b/addons/test_mail/tests/test_mail_gateway.py
@@ -499,7 +499,7 @@ def test_private_discussion(self):
self.assertEqual(msg.model, False,
'message_post: private discussion: context key "thread_model" not correctly ignored when having no res_id')
# Test: message-id
- self.assertIn('openerp-private', msg.message_id, 'message_post: private discussion: message-id should contain the private keyword')
+ self.assertIn('openerp-private', msg.message_id.split('@')[0], 'message_post: private discussion: message-id should contain the private keyword')
# Do: Bert replies through mailgateway (is a customer)
self.format_and_process(
@@ -527,9 +527,9 @@ def test_private_discussion(self):
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models', 'odoo.addons.mail.models.mail_mail')
def test_forward_parent_id(self):
msg = self.test_record.sudo(self.user_employee).message_post(no_auto_thread=True, subtype='mail.mt_comment')
- self.assertNotIn(msg.model, msg.message_id)
- self.assertNotIn('-%d-' % msg.res_id, msg.message_id)
- self.assertIn('reply_to', msg.message_id)
+ self.assertNotIn(msg.model, msg.message_id.split('@')[0])
+ self.assertNotIn('-%d-' % msg.res_id, msg.message_id.split('@')[0])
+ self.assertIn('reply_to', msg.message_id.split('@')[0])
# forward it to a new thread AND an existing thread
fw_msg_id = ''
diff --git a/addons/test_mail/tests/test_mail_message.py b/addons/test_mail/tests/test_mail_message.py
index 5d747e0a207f0..79454dc65e4f0 100644
--- a/addons/test_mail/tests/test_mail_message.py
+++ b/addons/test_mail/tests/test_mail_message.py
@@ -28,7 +28,7 @@ def test_mail_message_values_basic(self):
'reply_to': 'test.reply@example.com',
'email_from': 'test.from@example.com',
})
- self.assertIn('-private', msg.message_id, 'mail_message: message_id for a void message should be a "private" one')
+ self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
self.assertEqual(msg.reply_to, 'test.reply@example.com')
self.assertEqual(msg.email_from, 'test.from@example.com')
@@ -36,7 +36,7 @@ def test_mail_message_values_default(self):
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.domain')]).unlink()
msg = self.Message.create({})
- self.assertIn('-private', msg.message_id, 'mail_message: message_id for a void message should be a "private" one')
+ self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
self.assertEqual(msg.reply_to, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
@@ -47,7 +47,7 @@ def test_mail_message_values_alias(self):
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.alias')]).unlink()
msg = self.Message.create({})
- self.assertIn('-private', msg.message_id, 'mail_message: message_id for a void message should be a "private" one')
+ self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
self.assertEqual(msg.reply_to, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
@@ -58,7 +58,7 @@ def test_mail_message_values_alias_catchall(self):
self.env['ir.config_parameter'].set_param('mail.catchall.alias', alias_catchall)
msg = self.Message.create({})
- self.assertIn('-private', msg.message_id, 'mail_message: message_id for a void message should be a "private" one')
+ self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
self.assertEqual(msg.reply_to, '%s <%s@%s>' % (self.env.user.company_id.name, alias_catchall, alias_domain))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
@@ -69,7 +69,7 @@ def test_mail_message_values_document_no_alias(self):
'model': 'mail.test',
'res_id': self.alias_record.id
})
- self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id)
+ self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0])
self.assertEqual(msg.reply_to, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
@@ -83,7 +83,7 @@ def test_mail_message_values_document_alias(self):
'model': 'mail.test',
'res_id': self.alias_record.id
})
- self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id)
+ self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0])
self.assertEqual(msg.reply_to, '%s %s <%s@%s>' % (self.env.user.company_id.name, self.alias_record.name, self.alias_record.alias_name, alias_domain))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
@@ -97,7 +97,7 @@ def test_mail_message_values_document_alias_catchall(self):
'model': 'mail.test',
'res_id': self.alias_record.id
})
- self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id)
+ self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0])
self.assertEqual(msg.reply_to, '%s %s <%s@%s>' % (self.env.user.company_id.name, self.alias_record.name, self.alias_record.alias_name, alias_domain))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
@@ -107,9 +107,9 @@ def test_mail_message_values_no_auto_thread(self):
'res_id': self.alias_record.id,
'no_auto_thread': True,
})
- self.assertIn('reply_to', msg.message_id)
- self.assertNotIn('mail.test', msg.message_id)
- self.assertNotIn('-%d-' % self.alias_record.id, msg.message_id)
+ self.assertIn('reply_to', msg.message_id.split('@')[0])
+ self.assertNotIn('mail.test', msg.message_id.split('@')[0])
+ self.assertNotIn('-%d-' % self.alias_record.id, msg.message_id.split('@')[0])
def test_mail_message_base64_image(self):
msg = self.env['mail.message'].sudo(self.user_employee).create({
diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index e731ec0fd992d..9a486d958c010 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -374,6 +374,7 @@ var SnippetEditor = Widget.extend({
*/
_onDragAndDropStart: function () {
var self = this;
+ this.dropped = false;
self.size = {
width: self.$target.width(),
height: self.$target.height()
@@ -419,10 +420,21 @@ var SnippetEditor = Widget.extend({
* 'move' button.
*
* @private
+ * @param {Event} ev
+ * @param {Object} ui
*/
- _onDragAndDropStop: function () {
+ _onDragAndDropStop: function (ev, ui) {
var self = this;
+ // TODO lot of this is duplicated code of the d&d feature of snippets
+ if (!this.dropped) {
+ var $el = $.nearest({x: ui.position.left, y: ui.position.top}, '.oe_drop_zone').first();
+ if ($el.length) {
+ $el.after(this.$target);
+ this.dropped = true;
+ }
+ }
+
$('.oe_drop_zone').droppable('destroy').remove();
var prev = this.$target.first()[0].previousSibling;
diff --git a/addons/website_quote/models/sale_order.py b/addons/website_quote/models/sale_order.py
index 294cce55d265a..40add3901c73c 100644
--- a/addons/website_quote/models/sale_order.py
+++ b/addons/website_quote/models/sale_order.py
@@ -272,7 +272,7 @@ def button_add_to_order(self):
order_line = order.order_line.filtered(lambda line: line.product_id == self.product_id)
if order_line:
order_line = order_line[0]
- order_line.product_uom_qty += 1
+ order_line.product_uom_qty += self.quantity
else:
vals = {
'price_unit': self.price_unit,
diff --git a/addons/website_quote/views/website_quote_templates.xml b/addons/website_quote/views/website_quote_templates.xml
index b649f501370a5..f2724642d1316 100644
--- a/addons/website_quote/views/website_quote_templates.xml
+++ b/addons/website_quote/views/website_quote_templates.xml
@@ -243,7 +243,7 @@
-