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()