diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py
index 33a48756a33..f63fc8a5972 100644
--- a/addons/account/report/account_aged_partner_balance.py
+++ b/addons/account/report/account_aged_partner_balance.py
@@ -194,7 +194,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
values['name'] = _('Unknown Partner')
values['trust'] = False
- if at_least_one_amount or self._context.get('include_nullified_amount'):
+ if at_least_one_amount or (self._context.get('include_nullified_amount') and lines[partner['partner_id']]):
res.append(values)
return res, total, lines
diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py
index 99069c6383e..deeba898ce9 100644
--- a/addons/account/tests/test_reconciliation.py
+++ b/addons/account/tests/test_reconciliation.py
@@ -753,3 +753,61 @@ class TestReconciliation(AccountingTestCase):
self.assertEqual(reversed_bank_line.full_reconcile_id.id, bank_line.full_reconcile_id.id)
self.assertEqual(reversed_customer_line.full_reconcile_id.id, customer_line.full_reconcile_id.id)
+
+ def create_invoice_partner(self, type='out_invoice', invoice_amount=50, currency_id=None, partner_id=False):
+ #we create an invoice in given currency
+ invoice = self.account_invoice_model.create({'partner_id': partner_id,
+ 'reference_type': 'none',
+ 'currency_id': currency_id,
+ 'name': type == 'out_invoice' and 'invoice to client' or 'invoice to vendor',
+ 'account_id': self.account_rcv.id,
+ 'type': type,
+ 'date_invoice': time.strftime('%Y') + '-07-01',
+ })
+ self.account_invoice_line_model.create({'product_id': self.product.id,
+ 'quantity': 1,
+ 'price_unit': invoice_amount,
+ 'invoice_id': invoice.id,
+ 'name': 'product that cost ' + str(invoice_amount),
+ 'account_id': self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_revenue').id)], limit=1).id,
+ })
+
+ #validate invoice
+ invoice.action_invoice_open()
+ return invoice
+
+ def test_aged_report(self):
+ AgedReport = self.env['report.account.report_agedpartnerbalance'].with_context(include_nullified_amount=True)
+ account_type = ['receivable']
+ report_date_to = time.strftime('%Y') + '-07-15'
+ partner = self.env['res.partner'].create({'name': 'AgedPartner'})
+ currency = self.env.user.company_id.currency_id
+
+ invoice = self.create_invoice_partner(currency_id=currency.id, partner_id=partner.id)
+ # Don't forward port in >= 11.0
+ journal = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'THE'})
+
+ statement = self.make_payment(invoice, journal, 50)
+
+ # Case 1: The invoice and payment are reconciled: Nothing should appear
+ report_lines, total, amls = AgedReport._get_partner_move_lines(account_type, report_date_to, 'posted', 30)
+
+ partner_lines = [line for line in report_lines if line['partner_id'] == partner.id]
+ self.assertEqual(partner_lines, [], 'The aged receivable shouldn\'t have lines at this point')
+ self.assertFalse(partner.id in amls, 'The aged receivable should not have amls either')
+
+ # Case 2: The invoice and payment are not reconciled: we should have one line on the report
+ # and 2 amls
+ invoice.move_id.line_ids.with_context(invoice_id=invoice.id).remove_move_reconcile()
+ report_lines, total, amls = AgedReport._get_partner_move_lines(account_type, report_date_to, 'posted', 30)
+
+ partner_lines = [line for line in report_lines if line['partner_id'] == partner.id]
+ self.assertEqual(partner_lines, [{'trust': 'normal', '1': 0.0, '0': 0.0, 'direction': 0.0, 'partner_id': partner.id, '3': 0.0, 'total': 0.0, 'name': 'AgedPartner', '4': 0.0, '2': 0.0}],
+ 'We should have a line in the report for the partner')
+ self.assertEqual(len(amls[partner.id]), 2, 'We should have 2 account move lines for the partner')
+
+ positive_line = [line for line in amls[partner.id] if line['line'].balance > 0]
+ negative_line = [line for line in amls[partner.id] if line['line'].balance < 0]
+
+ self.assertEqual(positive_line[0]['amount'], 50.0, 'The amount of the amls should be 50')
+ self.assertEqual(negative_line[0]['amount'], -50.0, 'The amount of the amls should be -50')
diff --git a/addons/l10n_multilang/models/account.py b/addons/l10n_multilang/models/account.py
index 2e5e0ffe973..be7c6b36dab 100644
--- a/addons/l10n_multilang/models/account.py
+++ b/addons/l10n_multilang/models/account.py
@@ -69,3 +69,9 @@ class AccountAnalyticAccount(models.Model):
_inherit = 'account.analytic.account'
name = fields.Char(translate=True)
+
+
+class ResCountryState(models.Model):
+ _inherit = 'res.country.state'
+
+ name = fields.Char(translate=True)
diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py
index f16421a02ae..0d08b8b388a 100644
--- a/addons/point_of_sale/models/pos_config.py
+++ b/addons/point_of_sale/models/pos_config.py
@@ -206,10 +206,11 @@ class PosConfig(models.Model):
def name_get(self):
result = []
for config in self:
- if (not config.session_ids) or (config.session_ids[0].state == 'closed'):
+ last_session = self.env['pos.session'].search([('config_id', '=', config.id)], limit=1)
+ if (not last_session) or (last_session.state == 'closed'):
result.append((config.id, config.name + ' (' + _('not used') + ')'))
continue
- result.append((config.id, config.name + ' (' + config.session_ids[0].user_id.name + ')'))
+ result.append((config.id, config.name + ' (' + last_session.user_id.name + ')'))
return result
@api.model
diff --git a/addons/pos_restaurant/static/src/js/multiprint.js b/addons/pos_restaurant/static/src/js/multiprint.js
index 65956ffa66d..5270f103c48 100644
--- a/addons/pos_restaurant/static/src/js/multiprint.js
+++ b/addons/pos_restaurant/static/src/js/multiprint.js
@@ -29,8 +29,10 @@ var Printer = core.Class.extend(mixins.PropertiesMixin,{
self.connection.rpc('/hw_proxy/print_xml_receipt',{receipt: r},{timeout: 5000})
.then(function(){
send_printing_job();
- },function(){
+ },function(error, event){
self.receipt_queue.unshift(r);
+ console.log('There was an error while trying to print the order:');
+ console.log(error);
});
}
}
@@ -54,7 +56,7 @@ models.load_models({
for(var i = 0; i < printers.length; i++){
if(active_printers[printers[i].id]){
- var url = printers[i].proxy_ip;
+ var url = printers[i].proxy_ip || '';
if(url.indexOf('//') < 0){
url = 'http://'+url;
}
diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py
index 66aad43df8e..fedfa9196c1 100644
--- a/addons/purchase/models/purchase.py
+++ b/addons/purchase/models/purchase.py
@@ -1065,7 +1065,7 @@ class ProcurementOrder(models.Model):
if not po:
vals = procurement._prepare_purchase_order(partner)
po = self.env['purchase.order'].create(vals)
- name = (procurement.group_id and (procurement.group_id.name + ":") or "") + (procurement.name != "/" and procurement.name or procurement.move_dest_id.raw_material_production_id and procurement.move_dest_id.raw_material_production_id.name or "")
+ name = (procurement.group_id and (procurement.group_id.name + ":") or "") + (procurement.name != "/" and procurement.name or "")
message = _("This purchase order has been created from: %s") % (procurement.id, name)
po.message_post(body=message)
cache[domain] = po
@@ -1078,7 +1078,7 @@ class ProcurementOrder(models.Model):
po.write({'origin': po.origin})
else:
po.write({'origin': procurement.origin})
- name = (self.group_id and (self.group_id.name + ":") or "") + (self.name != "/" and self.name or self.move_dest_id.raw_material_production_id and self.move_dest_id.raw_material_production_id.name or "")
+ name = (self.group_id and (self.group_id.name + ":") or "") + (self.name != "/" and self.name or "")
message = _("This purchase order has been modified from: %s") % (procurement.id, name)
po.message_post(body=message)
if po:
diff --git a/addons/sale/static/src/js/tour.js b/addons/sale/static/src/js/tour.js
index d25068cc40c..092fb00e6f4 100644
--- a/addons/sale/static/src/js/tour.js
+++ b/addons/sale/static/src/js/tour.js
@@ -50,7 +50,11 @@ tour.register('sale_tour', {
in_modal: false,
run: function (actions) {
actions.auto();
- actions.auto(".modal-footer .btn-primary");
+ // There might be a modal because of the view:
+ // sale.order.form.editable.list, enabled by some groups
+ if ($(".modal-footer .btn-primary").length) {
+ actions.auto(".modal-footer .btn-primary");
+ }
},
id: "quotation_product_selected",
}, {
diff --git a/addons/sale_mrp/sale_mrp.py b/addons/sale_mrp/sale_mrp.py
index 5b411c02ba2..3ac4d375487 100644
--- a/addons/sale_mrp/sale_mrp.py
+++ b/addons/sale_mrp/sale_mrp.py
@@ -81,10 +81,7 @@ class AccountInvoiceLine(models.Model):
qty_done = sum([x.uom_id._compute_quantity(x.quantity, x.product_id.uom_id) for x in s_line.invoice_lines if x.invoice_id.state in ('open', 'paid')])
quantity = self.uom_id._compute_quantity(self.quantity, self.product_id.uom_id)
# Put moves in fixed order by date executed
- moves = self.env['stock.move']
- for procurement in s_line.procurement_ids:
- moves |= procurement.move_ids
- moves.sorted(lambda x: x.date)
+ moves = s_line.mapped('procurement_ids.move_ids').sorted(lambda x: x.date)
# Go through all the moves and do nothing until you get to qty_done
# Beyond qty_done we need to calculate the average of the price_unit
# on the moves we encounter.
diff --git a/addons/sale_stock/models/account_invoice.py b/addons/sale_stock/models/account_invoice.py
index 96f35cf5424..084ffbd3706 100644
--- a/addons/sale_stock/models/account_invoice.py
+++ b/addons/sale_stock/models/account_invoice.py
@@ -25,10 +25,7 @@ class AccountInvoiceLine(models.Model):
qty_done = sum([x.uom_id._compute_quantity(x.quantity, x.product_id.uom_id) for x in s_line.invoice_lines if x.invoice_id.state in ('open', 'paid')])
quantity = self.uom_id._compute_quantity(self.quantity, self.product_id.uom_id)
# Put moves in fixed order by date executed
- moves = self.env['stock.move']
- for procurement in s_line.procurement_ids:
- moves |= procurement.move_ids
- moves.sorted(lambda x: x.date)
+ moves = s_line.mapped('procurement_ids.move_ids').sorted(lambda x: x.date)
# Go through all the moves and do nothing until you get to qty_done
# Beyond qty_done we need to calculate the average of the price_unit
# on the moves we encounter.
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index 36e82df38e6..a83833f76dd 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -529,7 +529,7 @@ class StockMove(models.Model):
'name': self.rule_id and self.rule_id.name or "/",
'origin': origin,
'company_id': self.company_id.id,
- 'date_planned': self.date,
+ 'date_planned': self.date_expected,
'product_id': self.product_id.id,
'product_qty': self.product_uom_qty,
'product_uom': self.product_uom.id,
diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py
index 90287337c01..949098ce5cd 100644
--- a/addons/stock_account/models/product.py
+++ b/addons/stock_account/models/product.py
@@ -128,11 +128,13 @@ class ProductProduct(models.Model):
'account_id': debit_account_id,
'debit': abs(diff * qty_available),
'credit': 0,
+ 'product_id': product.id,
}), (0, 0, {
'name': _('Standard Price changed'),
'account_id': credit_account_id,
'debit': 0,
'credit': abs(diff * qty_available),
+ 'product_id': product.id,
})],
}
move = AccountMove.create(move_vals)
diff --git a/addons/web_calendar/static/src/js/web_calendar.js b/addons/web_calendar/static/src/js/web_calendar.js
index 60f1e09c3b1..f622a3a1318 100644
--- a/addons/web_calendar/static/src/js/web_calendar.js
+++ b/addons/web_calendar/static/src/js/web_calendar.js
@@ -33,7 +33,7 @@ function get_fc_defaultOptions() {
// adapt format for fullcalendar v1.
// see http://fullcalendar.io/docs1/utilities/formatDate/
- var conversions = [['YYYY', 'yyyy'], ['YY', 'y'], ['DDDD', 'dddd'], ['DD', 'dd']];
+ var conversions = [['YYYY', 'yyyy'], ['YY', 'yy'], ['DDDD', 'dddd'], ['DD', 'dd']];
_.each(conversions, function(conv) {
dateFormat = dateFormat.replace(conv[0], conv[1]);
});
diff --git a/addons/website_quote/models/sale_order.py b/addons/website_quote/models/sale_order.py
index 618dd5999b3..7902c525d72 100644
--- a/addons/website_quote/models/sale_order.py
+++ b/addons/website_quote/models/sale_order.py
@@ -64,6 +64,17 @@ class SaleOrder(models.Model):
else:
return 0
+ @api.model_cr_context
+ def _init_column(self, column_name):
+ if column_name != 'access_token':
+ super(SaleOrder, self)._init_column(column_name)
+ else:
+ query = """UPDATE %(table_name)s
+ SET %(column_name)s = md5(random()::text || clock_timestamp()::text)::uuid
+ WHERE %(column_name)s IS NULL
+ """ % {'table_name': self._table, 'column_name': column_name}
+ self.env.cr.execute(query)
+
access_token = fields.Char(
'Security Token', copy=False, default=lambda self: str(uuid.uuid4()),
required=True)
diff --git a/addons/website_sale_options/static/src/js/website_sale.js b/addons/website_sale_options/static/src/js/website_sale.js
index da7cd9db5ff..90ede4791a8 100644
--- a/addons/website_sale_options/static/src/js/website_sale.js
+++ b/addons/website_sale_options/static/src/js/website_sale.js
@@ -9,7 +9,7 @@ require('website_sale.website_sale');
$('.oe_website_sale #add_to_cart, .oe_website_sale #products_grid .a-submit')
.off('click')
.removeClass('a-submit')
- .click(function (event) {
+ .click(_.debounce(function (event) {
var $form = $(this).closest('form');
var quantity = parseFloat($form.find('input[name="add_qty"]').val() || 1);
var product_id = parseInt($form.find('input[type="hidden"][name="product_id"], input[type="radio"][name="product_id"]:checked').first().val(),10);
@@ -95,6 +95,6 @@ $('.oe_website_sale #add_to_cart, .oe_website_sale #products_grid .a-submit')
});
});
return false;
- });
+ }, 200, true));
});
diff --git a/odoo/addons/base/res/ir_property.py b/odoo/addons/base/res/ir_property.py
index 4dcc33209f2..c8aa6bddf26 100644
--- a/odoo/addons/base/res/ir_property.py
+++ b/odoo/addons/base/res/ir_property.py
@@ -188,7 +188,7 @@ class Property(models.Model):
if not values:
return
- if not default_value:
+ if default_value is None:
domain = self._get_domain(name, model)
if domain is None:
raise Exception()