summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/account/models/account_analytic_line.py2
-rw-r--r--addons/account/models/account_payment.py38
-rw-r--r--addons/fleet/models/fleet_vehicle.py2
-rw-r--r--addons/l10n_fr_fec/wizard/account_fr_fec.py2
-rw-r--r--addons/mrp/models/mrp_production.py2
-rw-r--r--addons/payment_authorize/controllers/main.py13
-rw-r--r--addons/purchase/data/mail_template_data.xml15
-rw-r--r--addons/purchase/i18n/purchase.pot11
-rw-r--r--addons/purchase/models/purchase.py10
-rw-r--r--addons/sale_mrp/models/sale_mrp.py2
-rw-r--r--addons/stock/models/stock_location.py5
-rw-r--r--addons/stock/models/stock_move_line.py2
-rw-r--r--addons/stock/models/stock_picking.py3
-rw-r--r--addons/stock/models/stock_warehouse.py1
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_renderer.js6
-rw-r--r--addons/web/static/tests/views/calendar_tests.js48
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.editor.js24
-rw-r--r--addons/web_editor/static/src/less/web_editor.ui.less4
-rw-r--r--doc/cla/individual/carelvd.md11
19 files changed, 156 insertions, 45 deletions
diff --git a/addons/account/models/account_analytic_line.py b/addons/account/models/account_analytic_line.py
index 4e610fd6dcf..2904565f173 100644
--- a/addons/account/models/account_analytic_line.py
+++ b/addons/account/models/account_analytic_line.py
@@ -34,7 +34,7 @@ class AccountAnalyticLine(models.Model):
# Compute based on pricetype
amount_unit = self.product_id.price_compute('standard_price', uom=unit)[self.product_id.id]
amount = amount_unit * self.unit_amount or 0.0
- result = self.currency_id.round(amount) * -1
+ result = (self.currency_id.round(amount) if self.currency_id else round(amount, 2)) * -1
self.amount = result
self.general_account_id = account
self.product_uom_id = unit
diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py
index 121b71ca79a..7f09875044e 100644
--- a/addons/account/models/account_payment.py
+++ b/addons/account/models/account_payment.py
@@ -335,7 +335,7 @@ class account_payment(models.Model):
payment.move_reconciled = rec
company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', readonly=True)
- name = fields.Char(readonly=True, copy=False, default="Draft Payment") # The name is attributed upon post()
+ name = fields.Char(readonly=True, copy=False) # The name is attributed upon post()
state = fields.Selection([('draft', 'Draft'), ('posted', 'Posted'), ('sent', 'Sent'), ('reconciled', 'Reconciled'), ('cancelled', 'Cancelled')], readonly=True, default='draft', copy=False, string="Status")
payment_type = fields.Selection(selection_add=[('transfer', 'Internal Transfer')])
@@ -515,23 +515,25 @@ class account_payment(models.Model):
if any(inv.state != 'open' for inv in rec.invoice_ids):
raise ValidationError(_("The payment cannot be processed because the invoice is not open!"))
- # Use the right sequence to set the name
- if rec.payment_type == 'transfer':
- sequence_code = 'account.payment.transfer'
- else:
- if rec.partner_type == 'customer':
- if rec.payment_type == 'inbound':
- sequence_code = 'account.payment.customer.invoice'
- if rec.payment_type == 'outbound':
- sequence_code = 'account.payment.customer.refund'
- if rec.partner_type == 'supplier':
- if rec.payment_type == 'inbound':
- sequence_code = 'account.payment.supplier.refund'
- if rec.payment_type == 'outbound':
- sequence_code = 'account.payment.supplier.invoice'
- rec.name = self.env['ir.sequence'].with_context(ir_sequence_date=rec.payment_date).next_by_code(sequence_code)
- if not rec.name and rec.payment_type != 'transfer':
- raise UserError(_("You have to define a sequence for %s in your company.") % (sequence_code,))
+ # keep the name in case of a payment reset to draft
+ if not rec.name:
+ # Use the right sequence to set the name
+ if rec.payment_type == 'transfer':
+ sequence_code = 'account.payment.transfer'
+ else:
+ if rec.partner_type == 'customer':
+ if rec.payment_type == 'inbound':
+ sequence_code = 'account.payment.customer.invoice'
+ if rec.payment_type == 'outbound':
+ sequence_code = 'account.payment.customer.refund'
+ if rec.partner_type == 'supplier':
+ if rec.payment_type == 'inbound':
+ sequence_code = 'account.payment.supplier.refund'
+ if rec.payment_type == 'outbound':
+ sequence_code = 'account.payment.supplier.invoice'
+ rec.name = self.env['ir.sequence'].with_context(ir_sequence_date=rec.payment_date).next_by_code(sequence_code)
+ if not rec.name and rec.payment_type != 'transfer':
+ raise UserError(_("You have to define a sequence for %s in your company.") % (sequence_code,))
# Create the journal entry
amount = rec.amount * (rec.payment_type in ('outbound', 'transfer') and 1 or -1)
diff --git a/addons/fleet/models/fleet_vehicle.py b/addons/fleet/models/fleet_vehicle.py
index 79a67f544c9..ae8fa16f858 100644
--- a/addons/fleet/models/fleet_vehicle.py
+++ b/addons/fleet/models/fleet_vehicle.py
@@ -14,7 +14,7 @@ class FleetVehicle(models.Model):
def _get_default_state(self):
state = self.env.ref('fleet.vehicle_state_active', raise_if_not_found=False)
- return state and state.id or False
+ return state if state and state.id else False
name = fields.Char(compute="_compute_vehicle_name", store=True)
active = fields.Boolean('Active', default=True, track_visibility="onchange")
diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py
index bd8e4cc44c0..032baf2d971 100644
--- a/addons/l10n_fr_fec/wizard/account_fr_fec.py
+++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py
@@ -265,7 +265,7 @@ class AccountFrFec(models.TransientModel):
for row in self._cr.fetchall():
listrow = list(row)
account_id = listrow.pop()
- w.writerow([s.encode("utf-8") for s in listrow])
+ w.writerow(listrow)
# LINES
sql_query = '''
diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index 177324431f6..31c6af180ab 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -454,7 +454,7 @@ class MrpProduction(models.Model):
move[0].with_context(do_not_unreserve=True).write({'product_uom_qty': quantity})
move[0]._recompute_state()
move[0]._action_assign()
- move.unit_factor = quantity / move.raw_material_production_id.product_qty
+ move[0].unit_factor = quantity / move[0].raw_material_production_id.product_qty
elif quantity < 0: # Do not remove 0 lines
if move[0].quantity_done > 0:
raise UserError(_('Lines need to be deleted, but can not as you still have some quantities to consume in them. '))
diff --git a/addons/payment_authorize/controllers/main.py b/addons/payment_authorize/controllers/main.py
index 9614327401a..a5f55f07afe 100644
--- a/addons/payment_authorize/controllers/main.py
+++ b/addons/payment_authorize/controllers/main.py
@@ -77,13 +77,14 @@ class AuthorizeController(http.Controller):
'id': token.id,
'short_name': token.short_name,
'3d_secure': False,
- 'verified': False,
+ 'verified': True, #Authorize.net does a transaction type of Authorization Only
+ #As Authorize.net already verify this card, we do not verify this card again.
}
-
- if verify_validity != False:
- token.validate()
- res['verified'] = token.verified
-
+ #token.validate() don't work with Authorize.net.
+ #Payments made via Authorize.net are settled and allowed to be refunded only on the next day.
+ #https://account.authorize.net/help/Miscellaneous/FAQ/Frequently_Asked_Questions.htm#Refund
+ #<quote>The original transaction that you wish to refund must have a status of Settled Successfully.
+ #You cannot issue refunds against unsettled, voided, declined or errored transactions.</quote>
return res
@http.route(['/payment/authorize/s2s/create'], type='http', auth='public')
diff --git a/addons/purchase/data/mail_template_data.xml b/addons/purchase/data/mail_template_data.xml
index 072342afc87..338fa149f0d 100644
--- a/addons/purchase/data/mail_template_data.xml
+++ b/addons/purchase/data/mail_template_data.xml
@@ -5,7 +5,7 @@
<!--Email template -->
<record id="email_template_edi_purchase" model="mail.template">
<field name="name">RFQ - Send by Email</field>
- <field name="email_from">${(object.create_uid.email and '&quot;%s&quot; &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
+ <field name="email_from">${(object.sudo().create_uid.email and '&quot;%s&quot; &lt;%s&gt;' % (object.sudo().create_uid.name, object.sudo().create_uid.email) or '')|safe}</field>
<field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
@@ -32,11 +32,11 @@ amounting in <strong>${format_amount(object.amount_total, object.currency_id)}</
from ${object.company_id.name}.
</p>
-<p>Do not hesitate to contact us, further you have any question.</p>
+<p>Do not hesitate to contact us if you have any further question.</p>
<p>Best regards,</p>
<p style="color:#888888;">
-% if object.user_id and object.user_id.signature:
- ${object.user_id.signature | safe}
+% if object.sudo().create_uid and object.sudo().create_uid.signature:
+ ${object.sudo().create_uid.signature | safe}
% endif
</p>
]]></field>
@@ -46,7 +46,7 @@ from ${object.company_id.name}.
<!--Email template -->
<record id="email_template_edi_purchase_done" model="mail.template">
<field name="name">Purchase Order - Send by Email</field>
- <field name="email_from">${(object.create_uid.email and '&quot;%s&quot; &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
+ <field name="email_from">${(object.sudo().create_uid.email and '&quot;%s&quot; &lt;%s&gt;' % (object.sudo().create_uid.name, object.sudo().create_uid.email) or '')|safe}</field>
<field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
@@ -74,6 +74,11 @@ from ${object.company_id.name}.
<p>You can reply to this email if you have any questions.</p>
<p>Thank you,</p>
+<p style="color:#888888;">
+% if object.sudo().create_uid and object.sudo().create_uid.signature:
+ ${object.sudo().create_uid.signature | safe}
+% endif
+</p>
]]></field>
</record>
</data>
diff --git a/addons/purchase/i18n/purchase.pot b/addons/purchase/i18n/purchase.pot
index be6ed395606..5e3bae24754 100644
--- a/addons/purchase/i18n/purchase.pot
+++ b/addons/purchase/i18n/purchase.pot
@@ -36,11 +36,11 @@ msgid "\n"
"from ${object.company_id.name}.\n"
"</p>\n"
"\n"
-"<p>Do not hesitate to contact us, further you have any question.</p>\n"
+"<p>Do not hesitate to contact us if you have any further question.</p>\n"
"<p>Best regards,</p>\n"
"<p style=\"color:#888888;\">\n"
-"% if object.user_id and object.user_id.signature:\n"
-" ${object.user_id.signature | safe}\n"
+"% if object.sudo().create_uid and object.sudo().create_uid.signature:\n"
+" ${object.sudo().create_uid.signature | safe}\n"
"% endif\n"
"</p>\n"
""
@@ -68,6 +68,11 @@ msgid "\n"
"\n"
"<p>You can reply to this email if you have any questions.</p>\n"
"<p>Thank you,</p>\n"
+"<p style=\"color:#888888;\">\n"
+"% if object.sudo().create_uid and object.sudo().create_uid.signature:\n"
+" ${object.sudo().create_uid.signature | safe}\n"
+"% endif\n"
+"</p>\n"
""
msgstr ""
diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py
index d6916d05af4..a3c5dea9e18 100644
--- a/addons/purchase/models/purchase.py
+++ b/addons/purchase/models/purchase.py
@@ -484,6 +484,16 @@ class PurchaseOrder(models.Model):
'currency_id': currency.id,
'delay': 0,
}
+ # In case the order partner is a contact address, a new supplierinfo is created on
+ # the parent company. In this case, we keep the product name and code.
+ seller = line.product_id._select_seller(
+ partner_id=line.partner_id,
+ quantity=line.product_qty,
+ date=line.order_id.date_order and line.order_id.date_order[:10],
+ uom_id=line.product_uom)
+ if seller:
+ supplierinfo['product_name'] = seller.product_name
+ supplierinfo['product_code'] = seller.product_code
vals = {
'seller_ids': [(0, 0, supplierinfo)],
}
diff --git a/addons/sale_mrp/models/sale_mrp.py b/addons/sale_mrp/models/sale_mrp.py
index 38a1e36b9c3..9a5ba682392 100644
--- a/addons/sale_mrp/models/sale_mrp.py
+++ b/addons/sale_mrp/models/sale_mrp.py
@@ -16,7 +16,7 @@ class SaleOrderLine(models.Model):
if line.qty_delivered_method == 'stock_move':
# In the case of a kit, we need to check if all components are shipped. Since the BOM might
# have changed, we don't compute the quantities but verify the move state.
- bom = self.env['mrp.bom']._bom_find(product=line.product_id)
+ bom = self.env['mrp.bom']._bom_find(product=line.product_id, company_id=line.company_id.id)
if bom and bom.type == 'phantom':
moves = line.move_ids.filtered(lambda m: m.picking_id and m.picking_id.state != 'cancel')
bom_delivered = all([move.state == 'done' for move in moves])
diff --git a/addons/stock/models/stock_location.py b/addons/stock/models/stock_location.py
index 38d4203c391..471fcc48da2 100644
--- a/addons/stock/models/stock_location.py
+++ b/addons/stock/models/stock_location.py
@@ -167,6 +167,11 @@ class Route(models.Model):
categ_ids = fields.Many2many('product.category', 'stock_location_route_categ', 'route_id', 'categ_id', 'Product Categories')
warehouse_ids = fields.Many2many('stock.warehouse', 'stock_route_warehouse', 'route_id', 'warehouse_id', 'Warehouses')
+ @api.onchange('warehouse_selectable')
+ def _onchange_warehouse_selectable(self):
+ if not self.warehouse_selectable:
+ self.warehouse_ids = []
+
def write(self, values):
'''when a route is deactivated, deactivate also its pull and push rules'''
res = super(Route, self).write(values)
diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py
index 43a010e3604..2b440cd716c 100644
--- a/addons/stock/models/stock_move_line.py
+++ b/addons/stock/models/stock_move_line.py
@@ -141,7 +141,7 @@ class StockMoveLine(models.Model):
"""
res = {}
if self.qty_done and self.product_id.tracking == 'serial':
- if float_compare(self.qty_done, 1.0, precision_rounding=self.move_id.product_id.uom_id.rounding) != 0:
+ if float_compare(self.qty_done, 1.0, precision_rounding=self.product_id.uom_id.rounding) != 0:
message = _('You can only process 1.0 %s of products with unique serial number.') % self.product_id.uom_id.name
res['warning'] = {'title': _('Warning'), 'message': message}
return res
diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py
index c8341ee3102..5481939602f 100644
--- a/addons/stock/models/stock_picking.py
+++ b/addons/stock/models/stock_picking.py
@@ -247,7 +247,8 @@ class Picking(models.Model):
picking_type_id = fields.Many2one(
'stock.picking.type', 'Operation Type',
required=True,
- states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
+ readonly=True,
+ states={'draft': [('readonly', False)]})
picking_type_code = fields.Selection([
('incoming', 'Vendors'),
('outgoing', 'Customers'),
diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py
index 88c21b7772a..ef116e1424b 100644
--- a/addons/stock/models/stock_warehouse.py
+++ b/addons/stock/models/stock_warehouse.py
@@ -467,6 +467,7 @@ class Warehouse(models.Model):
'name': self._format_routename(route_type=route_type),
'product_categ_selectable': True,
'product_selectable': False,
+ 'warehouse_selectable': True,
'company_id': self.company_id.id,
'sequence': 10,
}
diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js
index a92951dce9b..aeada42de55 100644
--- a/addons/web/static/src/js/views/calendar/calendar_renderer.js
+++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js
@@ -359,6 +359,11 @@ return AbstractRenderer.extend({
this.$calendar = this.$(".o_calendar_widget");
+ // This seems like a workaround but apparently passing the locale
+ // in the options is not enough. We should initialize it beforehand
+ var locale = moment.locale();
+ $.fullCalendar.locale(locale);
+
//Documentation here : http://arshaw.com/fullcalendar/docs/
var fc_options = $.extend({}, this.state.fc_options, {
eventDrop: function (event) {
@@ -410,6 +415,7 @@ return AbstractRenderer.extend({
},
height: 'parent',
unselectAuto: false,
+ locale: locale, // reset locale when fullcalendar has already been instanciated before now
});
this.$calendar.fullCalendar(fc_options);
diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js
index 5e1f07dfd53..cf717027ebe 100644
--- a/addons/web/static/tests/views/calendar_tests.js
+++ b/addons/web/static/tests/views/calendar_tests.js
@@ -7,6 +7,7 @@ var Dialog = require('web.Dialog');
var fieldUtils = require('web.field_utils');
var mixins = require('web.mixins');
var testUtils = require('web.test_utils');
+var time = require('web.time');
var session = require('web.session');
var createActionManager = testUtils.createActionManager;
@@ -2275,6 +2276,53 @@ QUnit.module('Views', {
calendar.destroy();
});
+ QUnit.test('fullcalendar initializes with right locale', function (assert) {
+ assert.expect(1);
+
+ // Do not forward port this part in versions >= saas11.3
+ testUtils.patch(time, {
+ getLangDateFormat: function () {
+ return 'DD/MM';
+ },
+ });
+
+ var initialLocale = moment.locale();
+ // This will set the locale to zz
+ moment.defineLocale('zz', {
+ longDateFormat: {
+ L: 'DD/MM/YYYY'
+ },
+ weekdaysShort: ["zz1.", "zz2.", "zz3.", "zz4.", "zz5.", "zz6.", "zz7."],
+ });
+
+ var calendar = createView({
+ View: CalendarView,
+ model: 'event',
+ data: this.data,
+ arch: '<calendar class="o_calendar_test" '+
+ 'date_start="start" '+
+ 'date_stop="stop" '+
+ 'mode="week"> '+
+ '<field name="name"/>'+
+ '</calendar>',
+ archs: archs,
+ viewOptions: {
+ initialDate: initialDate,
+ action: {views: [{viewID: 1, type: 'kanban'}, {viewID: 43, type: 'form'}]}
+ },
+
+ });
+
+ assert.strictEqual(calendar.$('.fc-day-header:first').text(), "zz1. 11/12",
+ 'The day should be in the given locale specific format');
+
+ moment.locale(initialLocale);
+ // Do not forward port this part in versions >= saas11.3
+ testUtils.unpatch(time);
+
+ calendar.destroy();
+ });
+
});
});
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 2ec8506321c..6599000d245 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -663,6 +663,10 @@ var SnippetsMenu = Widget.extend({
class: 'oe_drop_zone oe_insert',
});
+ function isFullWidth($elem) {
+ return $elem.parent().width() === $elem.outerWidth(true);
+ }
+
if ($selectorChildren) {
$selectorChildren.each(function () {
var $zone = $(this);
@@ -680,7 +684,10 @@ var SnippetsMenu = Widget.extend({
display: 'inline-block',
});
} else if (float === 'left' || float === 'right') {
- $drop.addClass('oe_vertical').css('height', Math.max(Math.min($zone.outerHeight(), $zone.children().last().outerHeight()), 30));
+ $drop.css('float', float);
+ if (!isFullWidth($zone)) {
+ $drop.addClass('oe_vertical').css('height', Math.max(Math.min($zone.outerHeight(), $zone.children().last().outerHeight()), 30));
+ }
}
$drop = $drop.clone();
@@ -695,7 +702,10 @@ var SnippetsMenu = Widget.extend({
display: 'inline-block'
});
} else if (float === 'left' || float === 'right') {
- $drop.addClass('oe_vertical').css('height', Math.max(Math.min($zone.outerHeight(), $zone.children().first().outerHeight()), 30));
+ $drop.css('float', float);
+ if (!isFullWidth($zone)) {
+ $drop.addClass('oe_vertical').css('height', Math.max(Math.min($zone.outerHeight(), $zone.children().first().outerHeight()), 30));
+ }
}
if (test) {
$drop.css({'float': 'none', 'display': 'inline-block'});
@@ -716,14 +726,20 @@ var SnippetsMenu = Widget.extend({
if ($zone.prev('.oe_drop_zone:visible').length === 0) {
$drop = zone_template.clone();
if (float === 'left' || float === 'right') {
- $drop.addClass('oe_vertical').css('height', Math.max(Math.min($zone.outerHeight(), $zone.prev().outerHeight() || Infinity), 30));
+ $drop.css('float', float);
+ if (!isFullWidth($zone)) {
+ $drop.addClass('oe_vertical').css('height', Math.max(Math.min($zone.outerHeight(), $zone.prev().outerHeight() || Infinity), 30));
+ }
}
$zone.before($drop);
}
if ($zone.next('.oe_drop_zone:visible').length === 0) {
$drop = zone_template.clone();
if (float === 'left' || float === 'right') {
- $drop.addClass('oe_vertical').css('height', Math.max(Math.min($zone.outerHeight(), $zone.next().outerHeight() || Infinity), 30));
+ $drop.css('float', float);
+ if (!isFullWidth($zone)) {
+ $drop.addClass('oe_vertical').css('height', Math.max(Math.min($zone.outerHeight(), $zone.next().outerHeight() || Infinity), 30));
+ }
}
$zone.after($drop);
}
diff --git a/addons/web_editor/static/src/less/web_editor.ui.less b/addons/web_editor/static/src/less/web_editor.ui.less
index 43f218a4294..a7fb8b68c00 100644
--- a/addons/web_editor/static/src/less/web_editor.ui.less
+++ b/addons/web_editor/static/src/less/web_editor.ui.less
@@ -274,12 +274,12 @@ html[lang] > body.editor_enable [data-oe-translation-state] {
}
&.oe_vertical {
- border: 2px dashed @odoo-brand-primary;
+ border: @odoo-dropzone-border;
border-left: none;
border-right: none;
width: @odoo-dropzone-size;
- float: left;
+ height: 100%;
margin: 0 -@odoo-dropzone-size/2;
&:after {
diff --git a/doc/cla/individual/carelvd.md b/doc/cla/individual/carelvd.md
new file mode 100644
index 00000000000..e4570a18845
--- /dev/null
+++ b/doc/cla/individual/carelvd.md
@@ -0,0 +1,11 @@
+South Africa, 05/02/2019
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Carel van Dam carelvdam@gmail.com https://github.com/carelvd