summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2017-03-17 17:57:52 +0100
committerChristophe Simonis <chs@odoo.com>2017-03-17 17:57:52 +0100
commitb2fa9f2ad1dac059c365e854cc1078b45eccdd85 (patch)
treed92287b0652b68b46882b6acd14be0232a4efaf4 /addons
parentcee15f7d90956b3e004f27f1e91e02ab59c450d1 (diff)
parent31ebb077734a4fda87df8eb19a88c0591f7e79e2 (diff)
[MERGE] forward port branch 10.0 up to 31ebb077734
Diffstat (limited to 'addons')
-rw-r--r--addons/crm/models/crm_lead.py3
-rw-r--r--addons/crm/report/crm_opportunity_report_views.xml4
-rw-r--r--addons/mass_mailing/static/src/css/email_template.css1
-rw-r--r--addons/mrp/i18n/mrp.pot6
-rw-r--r--addons/mrp/models/stock_move.py10
-rw-r--r--addons/mrp/wizard/mrp_product_produce.py4
-rw-r--r--addons/mrp_repair/models/mrp_repair.py4
-rw-r--r--addons/sale/models/sale.py4
-rw-r--r--addons/sale_stock/models/sale_order.py3
-rw-r--r--addons/stock/models/stock_inventory.py58
-rw-r--r--addons/stock/models/stock_picking.py6
-rw-r--r--addons/stock/models/stock_warehouse.py2
-rw-r--r--addons/stock/tests/test_warehouse.py128
-rw-r--r--addons/web/static/src/less/form_view.less2
-rw-r--r--addons/web_calendar/static/src/js/web_calendar.js4
-rw-r--r--addons/web_editor/static/src/js/summernote.js18
-rw-r--r--addons/web_editor/static/src/js/transcoder.js36
-rw-r--r--addons/web_editor/static/src/xml/backend.xml8
-rw-r--r--addons/web_kanban/static/src/js/kanban_view.js12
19 files changed, 251 insertions, 62 deletions
diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py
index c0614c034e0..4943c934902 100644
--- a/addons/crm/models/crm_lead.py
+++ b/addons/crm/models/crm_lead.py
@@ -30,7 +30,6 @@ CRM_LEAD_FIELDS_TO_MERGE = [
'city',
'contact_name',
'description',
- 'email',
'fax',
'mobile',
'partner_name',
@@ -468,7 +467,7 @@ class Lead(models.Model):
body = [title]
fields = self.env['ir.model.fields'].search([('name', 'in', fields or []), ('model_id.model', '=', self._name)])
for field in fields:
- value = self[field.name]
+ value = getattr(self, field.name, False)
if field.ttype == 'selection':
value = dict(field.get_values(self.env)).get(value, value)
elif field.ttype == 'many2one':
diff --git a/addons/crm/report/crm_opportunity_report_views.xml b/addons/crm/report/crm_opportunity_report_views.xml
index 9a4a199f9b4..b4a7534c520 100644
--- a/addons/crm/report/crm_opportunity_report_views.xml
+++ b/addons/crm/report/crm_opportunity_report_views.xml
@@ -8,7 +8,7 @@
<field name="arch" type="xml">
<pivot string="Pipeline Analysis" disable_linking="True">
<field name="create_date" interval="month" type="col"/>
- <field name="stage_name" type="row"/>
+ <field name="stage_id" type="row"/>
<field name="expected_revenue" type="measure"/>
</pivot>
</field>
@@ -31,7 +31,7 @@
<field name="model">crm.opportunity.report</field>
<field name="arch" type="xml">
<graph string="Pipeline Analysis" stacked="True">
- <field name="stage_name" type="row"/>
+ <field name="stage_id" type="row"/>
<field name="date_deadline" type="row" interval="month"/>
<field name="expected_revenue" type="measure"/>
</graph>
diff --git a/addons/mass_mailing/static/src/css/email_template.css b/addons/mass_mailing/static/src/css/email_template.css
index f0b25ba612d..edd9c0c3f0d 100644
--- a/addons/mass_mailing/static/src/css/email_template.css
+++ b/addons/mass_mailing/static/src/css/email_template.css
@@ -9,6 +9,7 @@
}
.kanban_html_preview {
+ pointer-events: none;
width: 600px;
-webkit-transform: scale(.50);
-ms-transform: scale(.50);
diff --git a/addons/mrp/i18n/mrp.pot b/addons/mrp/i18n/mrp.pot
index 7ed0640b829..be8d2e5b1f0 100644
--- a/addons/mrp/i18n/mrp.pot
+++ b/addons/mrp/i18n/mrp.pot
@@ -3169,6 +3169,12 @@ msgid "You cannot cancel a stock move having already consumed material"
msgstr ""
#. module: mrp
+#: code:addons/mrp/models/stock_move.py:40
+#, python-format
+msgid "You cannot use the same serial number in two different lines."
+msgstr ""
+
+#. module: mrp
#: code:addons/mrp/wizard/change_production_qty.py:44
#, python-format
msgid "You have already processed %d. Please input a quantity higher than %d "
diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py
index 42df10850ca..9b2ff511a33 100644
--- a/addons/mrp/models/stock_move.py
+++ b/addons/mrp/models/stock_move.py
@@ -28,6 +28,16 @@ class StockMoveLots(models.Model):
done_move = fields.Boolean('Move Done', related='move_id.is_done', store=True) # TDE FIXME: naming
plus_visible = fields.Boolean("Plus Visible", compute='_compute_plus')
+ @api.one
+ @api.constrains('lot_id')
+ def _check_lot_id(self):
+ if self.move_id.product_id.tracking == 'serial':
+ lots = set([])
+ for move_lot in self.move_id.move_lot_ids.filtered(lambda r: not r.lot_produced_id):
+ if move_lot.lot_id in lots:
+ raise exceptions.UserError(_('You cannot use the same serial number in two different lines.'))
+ lots.add(move_lot.lot_id)
+
def _compute_plus(self):
for movelot in self:
if movelot.move_id.product_id.tracking == 'serial':
diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py
index f6a70c4a1f6..b7cd863a5fc 100644
--- a/addons/mrp/wizard/mrp_product_produce.py
+++ b/addons/mrp/wizard/mrp_product_produce.py
@@ -125,8 +125,8 @@ class MrpProductProduce(models.TransientModel):
#Possibly the entire move is selected
remaining_qty = movelots.quantity - movelots.quantity_done
if remaining_qty > 0:
- new_move_lot = movelots.copy()
- new_move_lot.write({'quantity':movelots.quantity_done, 'lot_produced_id': self.lot_id.id})
+ default = {'quantity': movelots.quantity_done, 'lot_produced_id': self.lot_id.id}
+ new_move_lot = movelots.copy(default=default)
movelots.write({'quantity': remaining_qty, 'quantity_done': 0})
else:
movelots.write({'lot_produced_id': self.lot_id.id})
diff --git a/addons/mrp_repair/models/mrp_repair.py b/addons/mrp_repair/models/mrp_repair.py
index cdbca9811a3..c23e810fd1f 100644
--- a/addons/mrp_repair/models/mrp_repair.py
+++ b/addons/mrp_repair/models/mrp_repair.py
@@ -453,7 +453,7 @@ class RepairLine(models.Model):
if not self.to_invoice:
self.price_subtotal = 0.0
else:
- taxes = self.env['account.tax'].compute_all(self.price_unit, self.repair_id.pricelist_id.currency_id, self.product_uom_qty, self.product_id, self.repair_id.partner_id)
+ taxes = self.tax_id.compute_all(self.price_unit, self.repair_id.pricelist_id.currency_id, self.product_uom_qty, self.product_id, self.repair_id.partner_id)
self.price_subtotal = taxes['total_excluded']
@api.onchange('type', 'repair_id')
@@ -536,7 +536,7 @@ class RepairFee(models.Model):
if not self.to_invoice:
self.price_subtotal = 0.0
else:
- taxes = self.env['account.tax'].compute_all(self.price_unit, self.repair_id.pricelist_id.currency_id, self.product_uom_qty, self.product_id, self.repair_id.partner_id)
+ taxes = self.tax_id.compute_all(self.price_unit, self.repair_id.pricelist_id.currency_id, self.product_uom_qty, self.product_id, self.repair_id.partner_id)
self.price_subtotal = taxes['total_excluded']
@api.onchange('repair_id', 'product_id', 'product_uom_qty')
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index 5f3fef9b915..2c2f7048d8d 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -250,8 +250,8 @@ class SaleOrder(models.Model):
@api.model
def create(self, vals):
- if vals.get('name', 'New') == 'New':
- vals['name'] = self.env['ir.sequence'].next_by_code('sale.order') or 'New'
+ if vals.get('name', _('New')) == _('New'):
+ vals['name'] = self.env['ir.sequence'].next_by_code('sale.order') or _('New')
# Makes sure partner_invoice_id', 'partner_shipping_id' and 'pricelist_id' are defined
if any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id']):
diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py
index 65bbdf51a07..bad030ec77f 100644
--- a/addons/sale_stock/models/sale_order.py
+++ b/addons/sale_stock/models/sale_order.py
@@ -183,7 +183,8 @@ class SaleOrderLine(models.Model):
qty = 0.0
for move in self.procurement_ids.mapped('move_ids').filtered(lambda r: r.state == 'done' and not r.scrapped):
if move.location_dest_id.usage == "customer":
- qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom)
+ if not move.origin_returned_move_id:
+ qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom)
elif move.location_dest_id.usage == "internal" and move.to_refund_so:
qty -= move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom)
return qty
diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py
index 5cc88ee7237..a95d394ce55 100644
--- a/addons/stock/models/stock_inventory.py
+++ b/addons/stock/models/stock_inventory.py
@@ -385,31 +385,57 @@ class InventoryLine(models.Model):
('owner_id', '=', self.partner_id.id),
('package_id', '=', self.package_id.id)])
+ def _get_move_values(self, qty, location_id, location_dest_id):
+ self.ensure_one()
+ return {
+ 'name': _('INV:') + (self.inventory_id.name or ''),
+ 'product_id': self.product_id.id,
+ 'product_uom': self.product_uom_id.id,
+ 'product_uom_qty': qty,
+ 'date': self.inventory_id.date,
+ 'company_id': self.inventory_id.company_id.id,
+ 'inventory_id': self.inventory_id.id,
+ 'state': 'confirmed',
+ 'restrict_lot_id': self.prod_lot_id.id,
+ 'restrict_partner_id': self.partner_id.id,
+ 'location_id': location_id,
+ 'location_dest_id': location_dest_id,
+ }
+
+ def _fixup_negative_quants(self):
+ """ This will handle the irreconciable quants created by a force availability followed by a
+ return. When generating the moves of an inventory line, we look for quants of this line's
+ product created to compensate a force availability. If there are some and if the quant
+ which it is propagated from is still in the same location, we move it to the inventory
+ adjustment location before getting it back. Getting the quantity from the inventory
+ location will allow the negative quant to be compensated.
+ """
+ self.ensure_one()
+ for quant in self._get_quants().filtered(lambda q: q.propagated_from_id.location_id.id == self.location_id.id):
+ # send the quantity to the inventory adjustment location
+ move_out_vals = self._get_move_values(quant.qty, self.location_id.id, self.product_id.property_stock_inventory.id)
+ move_out = self.env['stock.move'].create(move_out_vals)
+ self.env['stock.quant'].quants_reserve([(quant, quant.qty)], move_out)
+ move_out.action_done()
+
+ # get back the quantity from the inventory adjustment location
+ move_in_vals = self._get_move_values(quant.qty, self.product_id.property_stock_inventory.id, self.location_id.id)
+ move_in = self.env['stock.move'].create(move_in_vals)
+ move_in.action_done()
+
def _generate_moves(self):
moves = self.env['stock.move']
Quant = self.env['stock.quant']
for line in self:
+ line._fixup_negative_quants()
+
if float_utils.float_compare(line.theoretical_qty, line.product_qty, precision_rounding=line.product_id.uom_id.rounding) == 0:
continue
diff = line.theoretical_qty - line.product_qty
- vals = {
- 'name': _('INV:') + (line.inventory_id.name or ''),
- 'product_id': line.product_id.id,
- 'product_uom': line.product_uom_id.id,
- 'date': line.inventory_id.date,
- 'company_id': line.inventory_id.company_id.id,
- 'inventory_id': line.inventory_id.id,
- 'state': 'confirmed',
- 'restrict_lot_id': line.prod_lot_id.id,
- 'restrict_partner_id': line.partner_id.id}
if diff < 0: # found more than expected
- vals['location_id'] = line.product_id.property_stock_inventory.id
- vals['location_dest_id'] = line.location_id.id
- vals['product_uom_qty'] = abs(diff)
+ vals = self._get_move_values(abs(diff), line.product_id.property_stock_inventory.id, line.location_id.id)
else:
- vals['location_id'] = line.location_id.id
- vals['location_dest_id'] = line.product_id.property_stock_inventory.id
- vals['product_uom_qty'] = diff
+ vals = self._get_move_values(abs(diff), line.location_id.id, line.product_id.property_stock_inventory.id)
move = moves.create(vals)
if diff > 0:
diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py
index 635e3b3ad53..cfc8a3bd38c 100644
--- a/addons/stock/models/stock_picking.py
+++ b/addons/stock/models/stock_picking.py
@@ -245,8 +245,10 @@ class Picking(models.Model):
picking_type_code = fields.Selection([
('incoming', 'Vendors'),
('outgoing', 'Customers'),
- ('internal', 'Internal')], related='picking_type_id.code')
- picking_type_entire_packs = fields.Boolean(related='picking_type_id.show_entire_packs')
+ ('internal', 'Internal')], related='picking_type_id.code',
+ readonly=True)
+ picking_type_entire_packs = fields.Boolean(related='picking_type_id.show_entire_packs',
+ readonly=True)
quant_reserved_exist = fields.Boolean(
'Has quants already reserved', compute='_compute_quant_reserved_exist',
diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py
index 8baec91e569..4ab96f82ddc 100644
--- a/addons/stock/models/stock_warehouse.py
+++ b/addons/stock/models/stock_warehouse.py
@@ -548,7 +548,7 @@ class Warehouse(models.Model):
routes = self.env['stock.location.route'].search([('supplied_wh_id', 'in', self.ids)])
self.env['procurement.rule'].search([
'&', ('route_id', 'in', routes.ids),
- ('location_src_id.usage', '=', 'transit')]).write({'location_id': new_location})
+ ('location_src_id.usage', '=', 'transit')]).write({'location_id': new_location.id})
@api.multi
def _update_routes(self):
diff --git a/addons/stock/tests/test_warehouse.py b/addons/stock/tests/test_warehouse.py
index 6e71ec74276..ef18411a79e 100644
--- a/addons/stock/tests/test_warehouse.py
+++ b/addons/stock/tests/test_warehouse.py
@@ -103,6 +103,134 @@ class TestInventory(TestStockCommon):
self.assertEqual(product.qty_available, 8.0)
self.assertEqual(product.with_context(location=self.env.ref('stock.stock_location_customers').id).qty_available, 7.0)
+ def test_inventory_adjustment_and_negative_quants_1(self):
+ """Make sure negative quants from returns get wiped out with an inventory adjustment"""
+ productA = self.env['product.product'].create({'name': 'Product A', 'type': 'product'})
+ stock_location = self.env.ref('stock.stock_location_stock')
+ customer_location = self.env.ref('stock.stock_location_customers')
+ location_loss = self.env.ref('stock.location_inventory')
+
+ # Create a picking out and force availability
+ picking_out = self.env['stock.picking'].create({
+ 'partner_id': self.env.ref('base.res_partner_2').id,
+ 'picking_type_id': self.env.ref('stock.picking_type_out').id,
+ 'location_id': stock_location.id,
+ 'location_dest_id': customer_location.id,
+ })
+ self.env['stock.move'].create({
+ 'name': productA.name,
+ 'product_id': productA.id,
+ 'product_uom_qty': 1,
+ 'product_uom': productA.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': stock_location.id,
+ 'location_dest_id': customer_location.id,
+ })
+ picking_out.action_confirm()
+ picking_out.force_assign()
+ picking_out.do_transfer()
+
+ # Create return picking for all goods
+ default_data = self.env['stock.return.picking']\
+ .with_context(active_ids=picking_out.ids, active_id=picking_out.ids[0])\
+ .default_get([
+ 'move_dest_exists',
+ 'original_location_id',
+ 'product_return_moves',
+ 'parent_location_id',
+ 'location_id',
+ ])
+ return_wiz = self.env['stock.return.picking']\
+ .with_context(active_ids=picking_out.ids, active_id=picking_out.ids[0])\
+ .create(default_data)
+ res = return_wiz.create_returns()
+ return_pick = self.env['stock.picking'].browse(res['res_id'])
+ return_pick.action_assign()
+ return_pick.do_transfer()
+
+ # Make an inventory adjustment to set the quantity to 0
+ inventory = self.env['stock.inventory'].create({
+ 'name': 'Starting for product_1',
+ 'filter': 'product',
+ 'location_id': stock_location.id,
+ 'product_id': productA.id,
+ })
+ inventory.prepare_inventory()
+ self.assertEqual(len(inventory.line_ids), 1, "Wrong inventory lines generated.")
+ self.assertEqual(inventory.line_ids.theoretical_qty, 0, "Theoretical quantity should be zero.")
+ inventory.action_done()
+
+ # The inventory adjustment should have created two moves
+ self.assertEqual(len(inventory.move_ids), 2)
+ quantity = inventory.move_ids.mapped('product_qty')
+ self.assertEqual(quantity, [1, 1], "Moves created with wrong quantity.")
+ location_ids = inventory.move_ids.mapped('location_id').ids
+ self.assertEqual(set(location_ids), {stock_location.id, location_loss.id})
+
+ # There should be no quant in the stock location
+ quants = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', stock_location.id)])
+ self.assertEqual(len(quants), 0)
+
+ # There should be one quant in the inventory loss location
+ quant = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', location_loss.id)])
+ self.assertEqual(len(quant), 1)
+ self.assertEqual(quant.qty, 1)
+
+ def test_inventory_adjustment_and_negative_quants_2(self):
+ """Make sure negative quants get wiped out with an inventory adjustment"""
+ productA = self.env['product.product'].create({'name': 'Product A', 'type': 'product'})
+ stock_location = self.env.ref('stock.stock_location_stock')
+ customer_location = self.env.ref('stock.stock_location_customers')
+ location_loss = self.env.ref('stock.location_inventory')
+
+ # Create a picking out and force availability
+ picking_out = self.env['stock.picking'].create({
+ 'partner_id': self.env.ref('base.res_partner_2').id,
+ 'picking_type_id': self.env.ref('stock.picking_type_out').id,
+ 'location_id': stock_location.id,
+ 'location_dest_id': customer_location.id,
+ })
+ self.env['stock.move'].create({
+ 'name': productA.name,
+ 'product_id': productA.id,
+ 'product_uom_qty': 1,
+ 'product_uom': productA.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': stock_location.id,
+ 'location_dest_id': customer_location.id,
+ })
+ picking_out.action_confirm()
+ picking_out.force_assign()
+ picking_out.do_transfer()
+
+ # Make an inventory adjustment to set the quantity to 0
+ inventory = self.env['stock.inventory'].create({
+ 'name': 'Starting for product_1',
+ 'filter': 'product',
+ 'location_id': stock_location.id,
+ 'product_id': productA.id,
+ })
+ inventory.prepare_inventory()
+ self.assertEqual(len(inventory.line_ids), 1, "Wrong inventory lines generated.")
+ self.assertEqual(inventory.line_ids.theoretical_qty, -1, "Theoretical quantity should be -1.")
+ inventory.line_ids.product_qty = 0 # Put the quantity back to 0
+ inventory.action_done()
+
+ # The inventory adjustment should have created one
+ self.assertEqual(len(inventory.move_ids), 1)
+ quantity = inventory.move_ids.mapped('product_qty')
+ self.assertEqual(quantity, [1], "Moves created with wrong quantity.")
+ location_ids = inventory.move_ids.mapped('location_id').ids
+ self.assertEqual(set(location_ids), {location_loss.id})
+
+ # There should be no quant in the stock location
+ quants = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', stock_location.id)])
+ self.assertEqual(len(quants), 0)
+
+ # There should be no quant in the inventory loss location
+ quant = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', location_loss.id)])
+ self.assertEqual(len(quant), 0)
+
class TestResupply(TestStockCommon):
def setUp(self):
diff --git a/addons/web/static/src/less/form_view.less b/addons/web/static/src/less/form_view.less
index a8422938b2e..7a804d311cc 100644
--- a/addons/web/static/src/less/form_view.less
+++ b/addons/web/static/src/less/form_view.less
@@ -179,7 +179,7 @@
.o_form_invalid {
&.o_form_input, .o_form_input, &.o_form_textarea, .o_form_textarea {
- border-color: @brand-danger;
+ border-color: @brand-danger!important; // enterprise o_form_required rule overrides this without !important
}
}
diff --git a/addons/web_calendar/static/src/js/web_calendar.js b/addons/web_calendar/static/src/js/web_calendar.js
index 97e9055919c..799bf2f567a 100644
--- a/addons/web_calendar/static/src/js/web_calendar.js
+++ b/addons/web_calendar/static/src/js/web_calendar.js
@@ -642,8 +642,8 @@ var CalendarView = View.extend({
date_stop = m_start.toDate();
}
var r = {
- 'start': moment(date_start).format('YYYY-MM-DD HH:mm:ss'),
- 'end': moment(date_stop).format('YYYY-MM-DD HH:mm:ss'),
+ 'start': moment(date_start).toISOString(),
+ 'end': moment(date_stop).toISOString(),
'title': the_title,
'allDay': (this.fields[this.date_start].type == 'date' || (this.all_day && evt[this.all_day]) || false),
'id': evt.id,
diff --git a/addons/web_editor/static/src/js/summernote.js b/addons/web_editor/static/src/js/summernote.js
index 9d7304caf75..71647828cc6 100644
--- a/addons/web_editor/static/src/js/summernote.js
+++ b/addons/web_editor/static/src/js/summernote.js
@@ -2313,11 +2313,19 @@ eventHandler.modules.popover.update = function ($popover, oStyle, isAirMode) {
eventHandler.modules.clipboard.attach = function(layoutInfo) {
var $editable = layoutInfo.editable();
$editable.on('paste', function(e) {
- e.preventDefault();
- $editable.data('NoteHistory').recordUndo($editable);
- var pastedText = ((e.originalEvent || e).clipboardData || window.clipboardData).getData('Text');
- var formattedText = pastedText.replace(/([^.!?:;])\r?\n/g, "$1").trim(); // Remove linebreaks which are not at the end of a sentence
- document.execCommand("insertText", false, formattedText);
+ var clipboardData = ((e.originalEvent || e).clipboardData || window.clipboardData);
+ // Change nothing if pasting html (copy from text editor / web / ...) or
+ // if clipboardData is not available (IE / ...)
+ if (clipboardData && clipboardData.types && clipboardData.types.length === 1 && clipboardData.types[0] === "text/plain") {
+ e.preventDefault();
+ $editable.data('NoteHistory').recordUndo($editable); // FIXME
+ var pastedText = clipboardData.getData("text/plain");
+ // Try removing linebreaks which are not really linebreaks (in a PDF,
+ // when a sentence goes over the next line, copying it considers it
+ // a linebreak for example).
+ var formattedText = pastedText.replace(/([\w-])\r?\n([\w-])/g, "$1 $2").trim();
+ document.execCommand("insertText", false, formattedText);
+ }
});
};
diff --git a/addons/web_editor/static/src/js/transcoder.js b/addons/web_editor/static/src/js/transcoder.js
index 20d4f5844af..fa7b4023377 100644
--- a/addons/web_editor/static/src/js/transcoder.js
+++ b/addons/web_editor/static/src/js/transcoder.js
@@ -98,20 +98,28 @@ var getMatchedCSSRules = function (a) {
if (style.display === 'block') {
delete style.display;
}
- if (style['margin-top']) {
- style.margin = (style['margin-top'] || 0) + ' ' + (style['margin-right'] || 0) + ' ' + (style['margin-bottom'] || 0) + ' ' + (style['margin-left'] || 0);
- delete style['margin-top'];
- delete style['margin-right'];
- delete style['margin-bottom'];
- delete style['margin-left'];
- }
- if (style['padding-top']) {
- style.padding = (style['padding-top'] || 0) + ' ' + (style['padding-right'] || 0) + ' ' + (style['padding-bottom'] || 0) + ' ' + (style['padding-left'] || 0);
- delete style['padding-top'];
- delete style['padding-right'];
- delete style['padding-bottom'];
- delete style['padding-left'];
- }
+
+ _.each(['margin', 'padding'], function(p) {
+ if (style[p+'-top'] || style[p+'-right'] || style[p+'-bottom'] || style[p+'-left']) {
+ if (style[p+'-top'] === style[p+'-right'] && style[p+'-top'] === style[p+'-bottom'] && style[p+'-top'] === style[p+'-left']) {
+ // keep => property: [top/right/bottom/left value];
+ style[p] = style[p+'-top'];
+ }
+ else {
+ // keep => property: [top value] [right value] [bottom value] [left value];
+ style[p] = (style[p+'-top'] || 0) + ' ' + (style[p+'-right'] || 0) + ' ' + (style[p+'-bottom'] || 0) + ' ' + (style[p+'-left'] || 0);
+ if (style[p].indexOf('inherit') !== -1 || style[p].indexOf('initial') !== -1) {
+ // keep => property-top: [top value]; property-right: [right value]; property-bottom: [bottom value]; property-left: [left value];
+ delete style[p];
+ return;
+ }
+ }
+ delete style[p+'-top'];
+ delete style[p+'-right'];
+ delete style[p+'-bottom'];
+ delete style[p+'-left'];
+ }
+ });
return style;
};
diff --git a/addons/web_editor/static/src/xml/backend.xml b/addons/web_editor/static/src/xml/backend.xml
index 4035752cea5..5c85996e890 100644
--- a/addons/web_editor/static/src/xml/backend.xml
+++ b/addons/web_editor/static/src/xml/backend.xml
@@ -38,11 +38,11 @@
-webkit-border-radius: 3px;
border-radius: 3px;
background: white;">
+ <option value="en_US" t-att-selected="'en_US' == widget.lang ? 'selected' : null">
+ <t t-if="!widget.get('effective_readonly')">English (edit mode)</t>
+ <t t-if="widget.get('effective_readonly')">English</t>
+ </option>
<t t-foreach="widget.languages" t-as="lang">
- <option value="en_US" t-att-selected="'en_US' == widget.lang ? 'selected' : null">
- <t t-if="!widget.get('effective_readonly')">English (edit mode)</t>
- <t t-if="widget.get('effective_readonly')">English</t>
- </option>
<option t-att-value="lang.code" t-att-selected="lang.code == widget.lang ? 'selected' : null"><t t-esc="lang.name"/></option>
</t>
</select>
diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js
index 1df6c56e139..8c382caacf4 100644
--- a/addons/web_kanban/static/src/js/kanban_view.js
+++ b/addons/web_kanban/static/src/js/kanban_view.js
@@ -63,7 +63,7 @@ var KanbanView = View.extend({
this.qweb = new QWeb(session.debug, {_s: session.origin});
this.limit = this.options.limit || parseInt(this.fields_view.arch.attrs.limit, 10) || 40;
- this.fields = {};
+ this.fields = this.fields_view.fields;
this.fields_keys = _.keys(this.fields_view.fields);
this.grouped = undefined;
this.group_by_field = undefined;
@@ -105,7 +105,7 @@ var KanbanView = View.extend({
this.qweb.add_template(utils.json_node_to_xml(child));
break;
} else if (child.tag === 'field') {
- var ftype = child.attrs.widget || this.fields_view.fields[child.attrs.name].type;
+ var ftype = child.attrs.widget || this.fields[child.attrs.name].type;
if(ftype === "many2many" && "context" in child.attrs) {
this.m2m_context[child.attrs.name] = child.attrs.context;
}
@@ -122,7 +122,7 @@ var KanbanView = View.extend({
do_search: function(domain, context, group_by) {
var self = this;
var group_by_field = group_by[0] || this.default_group_by;
- var field = this.fields_view.fields[group_by_field];
+ var field = this.fields[group_by_field];
var options = {};
var fields_def;
if (field === undefined) {
@@ -248,7 +248,7 @@ var KanbanView = View.extend({
_.each(groups, function (group) {
var value = group.attributes.value;
group.id = value instanceof Array ? value[0] : value;
- var field = self.fields_view.fields[options.group_by_field];
+ var field = self.fields[options.group_by_field];
if (field && field.type === "selection") {
value= _.find(field.selection, function (s) { return s[0] === group.id; });
}
@@ -304,7 +304,7 @@ var KanbanView = View.extend({
return this._super(action);
},
has_active_field: function() {
- return this.fields_view.fields.active;
+ return this.fields.active;
},
_is_quick_create_enabled: function() {
if (!this.quick_creatable || !this.is_action_enabled('create'))
@@ -465,7 +465,7 @@ var KanbanView = View.extend({
var self = this;
// Drag'n'drop activation/deactivation
- var group_by_field_attrs = this.fields_view.fields[this.group_by_field] || this.fields[this.group_by_field];
+ var group_by_field_attrs = this.fields[this.group_by_field];
// Deactivate the drag'n'drop if:
// - field is a date or datetime since we group by month