diff --git a/addons/account/models/account_reconcile_model.py b/addons/account/models/account_reconcile_model.py index 1c2a11373f7..1788306b57c 100644 --- a/addons/account/models/account_reconcile_model.py +++ b/addons/account/models/account_reconcile_model.py @@ -591,8 +591,10 @@ class AccountReconcileModel(models.Model): if line_residual > total_residual: amount_percentage = (total_residual / line_residual) * 100 - else: + elif total_residual: amount_percentage = (line_residual / total_residual) * 100 if total_residual else 0 + else: + return False return amount_percentage >= self.match_total_amount_param @api.multi diff --git a/addons/hr_expense/tests/test_expenses.py b/addons/hr_expense/tests/test_expenses.py index 66a56b2f557..d8f909a533a 100644 --- a/addons/hr_expense/tests/test_expenses.py +++ b/addons/hr_expense/tests/test_expenses.py @@ -187,6 +187,70 @@ class TestAccountEntry(TestExpenseCommon): self.assertEquals(expense.total_amount, 9876.0) self.assertTrue(expense.employee_id in user_demo.employee_ids) + def test_partial_payment_multiexpense(self): + bank_journal = self.env['account.journal'].create({ + 'name': 'Payment Journal', + 'code': 'PAY', + 'type': 'bank', + 'company_id': self.env.user.company_id.id, + }) + + outbound_pay_method = self.env['account.payment.method'].create({ + 'name': 'outbound', + 'code': 'out', + 'payment_type': 'outbound', + }) + + expense = self.env['hr.expense.sheet'].create({ + 'name': 'Expense for John Smith', + 'employee_id': self.employee.id, + }) + expense_line = self.env['hr.expense'].create({ + 'name': 'Car Travel Expenses', + 'employee_id': self.employee.id, + 'product_id': self.product_expense.id, + 'unit_amount': 200.00, + 'tax_ids': [(6, 0, [self.tax.id])], + 'sheet_id': expense.id, + 'analytic_account_id': self.analytic_account.id, + }) + expense_line.copy({ + 'sheet_id': expense.id + }) + expense.approve_expense_sheets() + expense.action_sheet_move_create() + + exp_move_lines = expense.account_move_id.line_ids + payable_move_lines = exp_move_lines.filtered(lambda l: l.account_id.internal_type == 'payable') + self.assertEquals(len(payable_move_lines), 2) + + WizardRegister = self.env['hr.expense.sheet.register.payment.wizard'].with_context(active_ids=expense.ids) + + register_pay1 = WizardRegister.create({ + 'journal_id': bank_journal.id, + 'payment_method_id': outbound_pay_method.id, + 'amount': 300, + }) + register_pay1.expense_post_payment() + + exp_move_lines = expense.account_move_id.line_ids + payable_move_lines = exp_move_lines.filtered(lambda l: l.account_id.internal_type == 'payable') + self.assertEquals(len(payable_move_lines.filtered(lambda l: l.reconciled)), 1) + + register_pay2 = WizardRegister.create({ + 'journal_id': bank_journal.id, + 'payment_method_id': outbound_pay_method.id, + 'amount': 100, + }) + register_pay2.expense_post_payment() + exp_move_lines = expense.account_move_id.line_ids + payable_move_lines = exp_move_lines.filtered(lambda l: l.account_id.internal_type == 'payable') + self.assertEquals(len(payable_move_lines.filtered(lambda l: l.reconciled)), 2) + + full_reconcile = payable_move_lines.mapped('full_reconcile_id') + self.assertEquals(len(full_reconcile), 1) + + class TestExpenseRights(TestExpenseCommon): @classmethod diff --git a/addons/hr_expense/wizard/hr_expense_sheet_register_payment.py b/addons/hr_expense/wizard/hr_expense_sheet_register_payment.py index 76a97f6a561..b97a4493904 100644 --- a/addons/hr_expense/wizard/hr_expense_sheet_register_payment.py +++ b/addons/hr_expense/wizard/hr_expense_sheet_register_payment.py @@ -109,7 +109,7 @@ class HrExpenseSheetRegisterPaymentWizard(models.TransientModel): # Reconcile the payment and the expense, i.e. lookup on the payable account move lines account_move_lines_to_reconcile = self.env['account.move.line'] for line in payment.move_line_ids + expense_sheet.account_move_id.line_ids: - if line.account_id.internal_type == 'payable': + if line.account_id.internal_type == 'payable' and not line.reconciled: account_move_lines_to_reconcile |= line account_move_lines_to_reconcile.reconcile() diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index a9bc53d1bae..0505d54b505 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -6,6 +6,8 @@ import logging import math +from collections import namedtuple + from datetime import datetime, time from pytz import timezone, UTC @@ -18,6 +20,9 @@ from odoo.tools.translate import _ _logger = logging.getLogger(__name__) +# Used to agglomerate the attendances in order to find the hour_from and hour_to +# See _onchange_request_parameters +DummyAttendance = namedtuple('DummyAttendance', 'hour_from, hour_to, dayofweek, day_period') class HolidaysRequest(models.Model): """ Leave Requests Access specifications @@ -287,12 +292,17 @@ class HolidaysRequest(models.Model): return domain = [('calendar_id', '=', self.employee_id.resource_calendar_id.id or self.env.user.company_id.resource_calendar_id.id)] - attendances = self.env['resource.calendar.attendance'].search(domain, order='dayofweek, day_period DESC') + attendances = self.env['resource.calendar.attendance'].read_group(domain, ['ids:array_agg(id)', 'hour_from:min(hour_from)', 'hour_to:max(hour_to)', 'dayofweek', 'day_period'], ['dayofweek', 'day_period'], lazy=False) + + # Must be sorted by dayofweek ASC and day_period DESC + attendances = sorted([DummyAttendance(group['hour_from'], group['hour_to'], group['dayofweek'], group['day_period']) for group in attendances], key=lambda att: (att.dayofweek, att.day_period != 'morning')) + + default_value = DummyAttendance(0, 0, 0, 'morning') # find first attendance coming after first_day - attendance_from = next((att for att in attendances if int(att.dayofweek) >= self.request_date_from.weekday()), attendances[0]) + attendance_from = next((att for att in attendances if int(att.dayofweek) >= self.request_date_from.weekday()), attendances[0] if attendances else default_value) # find last attendance coming before last_day - attendance_to = next((att for att in reversed(attendances) if int(att.dayofweek) <= self.request_date_to.weekday()), attendances[-1]) + attendance_to = next((att for att in reversed(attendances) if int(att.dayofweek) <= self.request_date_to.weekday()), attendances[-1] if attendances else default_value) if self.request_unit_half: if self.request_date_from_period == 'am': diff --git a/addons/hr_holidays/tests/__init__.py b/addons/hr_holidays/tests/__init__.py index 67c8327e999..3c15cd779a1 100644 --- a/addons/hr_holidays/tests/__init__.py +++ b/addons/hr_holidays/tests/__init__.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import test_access_rights +from . import test_automatic_leave_dates from . import test_holidays_flow from . import test_hr_leave_type from . import test_accrual_allocations diff --git a/addons/hr_holidays/tests/test_automatic_leave_dates.py b/addons/hr_holidays/tests/test_automatic_leave_dates.py new file mode 100644 index 00000000000..5b39259d053 --- /dev/null +++ b/addons/hr_holidays/tests/test_automatic_leave_dates.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +from datetime import date, datetime + +from odoo.tests.common import Form, tagged + +from odoo.addons.hr_holidays.tests.common import TestHrHolidaysBase + + +@tagged('prout') +class TestAutomaticLeaveDates(TestHrHolidaysBase): + def setUp(self): + super(TestAutomaticLeaveDates, self).setUp() + + self.leave_type = self.env['hr.leave.type'].create({ + 'name': 'Automatic Test', + 'time_type': 'leave', + 'allocation_type': 'no', + 'validity_start': False, + }) + + def test_no_attendances(self): + calendar = self.env['resource.calendar'].create({ + 'name': 'No Attendances', + 'attendance_ids': [(5, 0, 0)], + }) + employee = self.employee_emp + employee.resource_calendar_id = calendar + + with Form(self.env['hr.leave']) as leave_form: + leave_form.employee_id = employee + leave_form.holiday_status_id = self.leave_type + leave_form.request_date_from = date(2019, 9, 2) + leave_form.request_date_to = date(2019, 9, 2) + leave_form.request_unit_half = True + leave_form.request_date_from_period = 'am' + + self.assertEqual(leave_form.number_of_days_display, 0) + self.assertEqual(leave_form.number_of_hours_display, 0) + + def test_single_attendance_on_morning_and_afternoon(self): + calendar = self.env['resource.calendar'].create({ + 'name': 'simple morning + afternoon', + 'attendance_ids': [(5, 0, 0), + (0, 0, { + 'name': 'monday morning', + 'hour_from': 8, + 'hour_to': 12, + 'day_period': 'morning', + 'dayofweek': '0', + }), + (0, 0, { + 'name': 'monday afternoon', + 'hour_from': 13, + 'hour_to': 17, + 'day_period': 'afternoon', + 'dayofweek': '0', + })] + }) + + employee = self.employee_emp + employee.resource_calendar_id = calendar + + with Form(self.env['hr.leave']) as leave_form: + leave_form.employee_id = employee + leave_form.holiday_status_id = self.leave_type + leave_form.request_date_from = date(2019, 9, 2) + leave_form.request_date_to = date(2019, 9, 2) + leave_form.request_unit_half = True + leave_form.request_date_from_period = 'am' + + self.assertEqual(leave_form.number_of_days_display, .5) + self.assertEqual(leave_form.number_of_hours_display, 4) + + leave_form.request_date_from_period = 'pm' + + self.assertEqual(leave_form.number_of_days_display, .5) + self.assertEqual(leave_form.number_of_hours_display, 4) + + def test_multiple_attendance_on_morning(self): + calendar = self.env['resource.calendar'].create({ + 'name': 'multi morning', + 'attendance_ids': [(5, 0, 0), + (0, 0, { + 'name': 'monday morning 1', + 'hour_from': 8, + 'hour_to': 10, + 'day_period': 'morning', + 'dayofweek': '0', + }), + (0, 0, { + 'name': 'monday morning 2', + 'hour_from': 10.25, + 'hour_to': 12.25, + 'day_period': 'morning', + 'dayofweek': '0', + }), + (0, 0, { + 'name': 'monday afternoon', + 'hour_from': 13, + 'hour_to': 17, + 'day_period': 'afternoon', + 'dayofweek': '0', + })] + }) + employee = self.employee_emp + employee.resource_calendar_id = calendar + + with Form(self.env['hr.leave']) as leave_form: + leave_form.employee_id = employee + leave_form.holiday_status_id = self.leave_type + leave_form.request_date_from = date(2019, 9, 2) + leave_form.request_date_to = date(2019, 9, 2) + leave_form.request_unit_half = True + leave_form.request_date_from_period = 'am' + + self.assertEqual(leave_form.number_of_days_display, .5) + self.assertEqual(leave_form.number_of_hours_display, 4) + + leave_form.request_date_from_period = 'pm' + + self.assertEqual(leave_form.number_of_days_display, .5) + self.assertEqual(leave_form.number_of_hours_display, 4) + + def test_attendance_on_morning(self): + calendar = self.env['resource.calendar'].create({ + 'name': 'Morning only', + 'attendance_ids': [(5, 0, 0), + (0, 0, { + 'name': 'Monday All day', + 'hour_from': 8, + 'hour_to': 16, + 'day_period': 'morning', + 'dayofweek': '0', + })], + }) + employee = self.employee_emp + employee.resource_calendar_id = calendar + with Form(self.env['hr.leave']) as leave_form: + leave_form.employee_id = employee + leave_form.holiday_status_id = self.leave_type + leave_form.request_date_from = date(2019, 9, 2) + leave_form.request_date_to = date(2019, 9, 2) + leave_form.request_unit_half = True + # Ask for morning + leave_form.request_date_from_period = 'am' + + self.assertEqual(leave_form.number_of_days_display, 1) + self.assertEqual(leave_form.number_of_hours_display, 8) + + # Ask for afternoon + leave_form.request_date_from_period = 'pm' + + self.assertEqual(leave_form.number_of_days_display, 1) + self.assertEqual(leave_form.number_of_hours_display, 8) + + def test_attendance_next_day(self): + calendar = self.env['resource.calendar'].create({ + 'name': 'auto next day', + 'attendance_ids': [(5, 0, 0), + (0, 0, { + 'name': 'tuesday morning', + 'hour_from': 8, + 'hour_to': 12, + 'day_period': 'morning', + 'dayofweek': '1', + })] + }) + employee = self.employee_emp + employee.resource_calendar_id = calendar + + with Form(self.env['hr.leave']) as leave_form: + leave_form.employee_id = employee + leave_form.holiday_status_id = self.leave_type + leave_form.request_date_from = date(2019, 9, 2) + leave_form.request_date_to = date(2019, 9, 2) + leave_form.request_unit_half = True + leave_form.request_date_from_period = 'am' + + + self.assertEqual(leave_form.number_of_days_display, 0) + self.assertEqual(leave_form.number_of_hours_display, 0) + self.assertEqual(leave_form.date_from, datetime(2019, 9, 2, 6, 0, 0)) + self.assertEqual(leave_form.date_to, datetime(2019, 9, 2, 10, 0, 0)) + + def test_attendance_previous_day(self): + calendar = self.env['resource.calendar'].create({ + 'name': 'auto next day', + 'attendance_ids': [(5, 0, 0), + (0, 0, { + 'name': 'monday morning', + 'hour_from': 8, + 'hour_to': 12, + 'day_period': 'morning', + 'dayofweek': '0', + })] + }) + employee = self.employee_emp + employee.resource_calendar_id = calendar + + with Form(self.env['hr.leave']) as leave_form: + leave_form.employee_id = employee + leave_form.holiday_status_id = self.leave_type + leave_form.request_date_from = date(2019, 9, 3) + leave_form.request_date_to = date(2019, 9, 3) + leave_form.request_unit_half = True + leave_form.request_date_from_period = 'am' + + + self.assertEqual(leave_form.number_of_days_display, 0) + self.assertEqual(leave_form.number_of_hours_display, 0) + self.assertEqual(leave_form.date_from, datetime(2019, 9, 3, 6, 0, 0)) + self.assertEqual(leave_form.date_to, datetime(2019, 9, 3, 10, 0, 0)) diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 8d8dcc3de26..5f5175c7409 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -237,10 +237,6 @@ class StockMove(models.Model): keys = super(StockMove, self)._key_assign_picking() return keys + (self.created_production_id,) - def _search_picking_for_assignation_domain(self): - res = super(StockMove, self)._search_picking_for_assignation_domain() - res.append(('move_lines.created_production_id', '=', self.created_production_id.id)) - return res def _compute_kit_quantities(self, product_id, kit_qty, kit_bom, filters): """ Computes the quantity delivered or received when a kit is sold or purchased. @@ -275,4 +271,4 @@ class StockMove(models.Model): # doesn't make sense. return min(qty_ratios) // 1 else: - return 0.0 + return 0.0 \ No newline at end of file diff --git a/addons/mrp/report/mrp_production_templates.xml b/addons/mrp/report/mrp_production_templates.xml index 531b516f0da..a4df1ef7ec5 100644 --- a/addons/mrp/report/mrp_production_templates.xml +++ b/addons/mrp/report/mrp_production_templates.xml @@ -136,7 +136,8 @@ - + +

All components could not be reserved. Click on the "Check Availability button" to try to reserve components.

diff --git a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py index 37bf6ee7f7e..a0d78d3cc78 100644 --- a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py +++ b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py @@ -122,109 +122,6 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon): self.assertEqual(sam_move.picking_type_id, self.warehouse.sam_type_id) self.assertFalse(sam_move.move_dest_ids) - def test_manufacturing_complex_product_3_steps(self): - """ Test MO/picking after manufacturing a complex product which uses - manufactured components. Ensure that everything is created and picked - correctly. - """ - # Create manifactured product which uses another manifactured - - product_form = Form(self.env['product.product']) - product_form.name = 'Arrow' - product_form.type = 'product' - product_form.route_ids.clear() - product_form.route_ids.add(self.warehouse.manufacture_pull_id.route_id) - product_form.route_ids.add(self.warehouse.mto_pull_id.route_id) - self.complex_product = product_form.save() - - ## Create raw product for manufactured product - product_form = Form(self.env['product.product']) - product_form.name = 'Raw Iron' - product_form.type = 'product' - product_form.uom_id = self.uom_unit - product_form.uom_po_id = self.uom_unit - self.raw_product_2 = product_form.save() - - ## Create bom for manufactured product - bom_product_form = Form(self.env['mrp.bom']) - bom_product_form.product_id = self.complex_product - bom_product_form.product_tmpl_id = self.complex_product.product_tmpl_id - with bom_product_form.bom_line_ids.new() as line: - line.product_id = self.finished_product - line.product_qty = 1.0 - with bom_product_form.bom_line_ids.new() as line: - line.product_id = self.raw_product_2 - line.product_qty = 1.0 - - self.complex_bom = bom_product_form.save() - - with Form(self.warehouse) as warehouse: - warehouse.manufacture_steps = 'pbm_sam' - - production_form = Form(self.env['mrp.production']) - production_form.product_id = self.complex_product - production_form.picking_type_id = self.warehouse.manu_type_id - production = production_form.save() - production.action_confirm() - - move_raw_ids = production.move_raw_ids - self.assertEqual(len(move_raw_ids), 2) - sfp_move_raw_id, raw_move_raw_id = move_raw_ids - self.assertEqual(sfp_move_raw_id.product_id, self.finished_product) - self.assertEqual(raw_move_raw_id.product_id, self.raw_product_2) - - for move_raw_id in move_raw_ids: - self.assertEqual(move_raw_id.picking_type_id, self.warehouse.manu_type_id) - - pbm_move = move_raw_id.move_orig_ids - self.assertEqual(len(pbm_move), 1) - self.assertEqual(pbm_move.location_id, self.warehouse.lot_stock_id) - self.assertEqual(pbm_move.location_dest_id, self.warehouse.pbm_loc_id) - self.assertEqual(pbm_move.picking_type_id, self.warehouse.pbm_type_id) - - move_finished_ids = production.move_finished_ids - self.assertEqual(len(move_finished_ids), 1) - self.assertEqual(move_finished_ids.product_id, self.complex_product) - self.assertEqual(move_finished_ids.picking_type_id, self.warehouse.manu_type_id) - sam_move = move_finished_ids.move_dest_ids - self.assertEqual(len(sam_move), 1) - self.assertEqual(sam_move.location_id, self.warehouse.sam_loc_id) - self.assertEqual(sam_move.location_dest_id, self.warehouse.lot_stock_id) - self.assertEqual(sam_move.picking_type_id, self.warehouse.sam_type_id) - self.assertFalse(sam_move.move_dest_ids) - - pickings = production.picking_ids.sorted('id', reverse=True) - self.assertEqual(len(pickings), 3) - - # Picking: SFP PostProcessing -> Stock - ## Stick from Subprocess - picking = self.env['stock.picking'].search([ - ('move_lines.product_id', '=', self.finished_product.id), - ('location_dest_id', '=', self.warehouse.lot_stock_id.id), - ('group_id', '=', production.procurement_group_id.id) - ]) - self.assertEqual(len(picking.move_lines), 1) - - # Picking: PC Stock -> Preprocessing - ## Stick - ## Raw Iron - picking = self.env['stock.picking'].search([ - ('move_lines.product_id', 'in', [self.finished_product.id, self.raw_product_2.id]), - ('location_id', '=', self.warehouse.lot_stock_id.id), - ('group_id', '=', production.procurement_group_id.id) - ]) - self.assertEqual(len(picking.move_lines), 2) - - # Picking: SFP PostProcessing -> Stock - ## Arrow from this process - picking = picking = self.env['stock.picking'].search([ - ('move_lines.product_id', '=', self.complex_product.id), - ('location_dest_id', '=', self.warehouse.lot_stock_id.id), - ('group_id', '=', production.procurement_group_id.id) - ]) - self.assertEqual(len(picking.move_lines), 1) - - def test_manufacturing_flow(self): """ Simulate a pick pack ship delivery combined with a picking before manufacturing and store after manufacturing. Also ensure that the MO and diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py index de1fd231b8f..fab35f27692 100644 --- a/addons/product/models/product_pricelist.py +++ b/addons/product/models/product_pricelist.py @@ -112,6 +112,7 @@ class Pricelist(models.Model): self.ensure_one() if not date: date = self._context.get('date') or fields.Date.context_today(self) + date = fields.Date.to_date(date) # handwritten query below does not work with non-dates (e.g. datetimes) if not uom_id and self._context.get('uom'): uom_id = self._context['uom'] if uom_id: @@ -319,10 +320,10 @@ class Pricelist(models.Model): return res[partner_id].id def _get_partner_pricelist_multi_search_domain_hook(self): - return [] + return [('active', '=', True)] def _get_partner_pricelist_multi_filter_hook(self): - return self + return self.filtered('active') def _get_partner_pricelist_multi(self, partner_ids, company_id=None): """ Retrieve the applicable pricelist for given partners in a given company. diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 651ab3be0c0..bd9cf07f558 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -404,8 +404,6 @@ class PurchaseOrder(models.Model): 'default_company_id': self.company_id.id, 'company_id': self.company_id.id } - if self.user_id: - result['context']['default_user_id'] = self.user_id.id # choose the view_mode accordingly if len(self.invoice_ids) > 1 and not create_bill: result['domain'] = "[('id', 'in', " + str(self.invoice_ids.ids) + ")]" diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 3ebc3dbc864..dff2791677c 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -682,20 +682,15 @@ class StockMove(models.Model): self.ensure_one() return self.group_id, self.location_id, self.location_dest_id, self.picking_type_id - def _search_picking_for_assignation_domain(self): - return [ + def _search_picking_for_assignation(self): + self.ensure_one() + picking = self.env['stock.picking'].search([ ('group_id', '=', self.group_id.id), ('location_id', '=', self.location_id.id), ('location_dest_id', '=', self.location_dest_id.id), ('picking_type_id', '=', self.picking_type_id.id), ('printed', '=', False), - ('state', 'in', ['draft', 'confirmed', 'waiting', - 'partially_available', 'assigned']) - ] - - def _search_picking_for_assignation(self): - self.ensure_one() - picking = self.env['stock.picking'].search(self._search_picking_for_assignation_domain(), limit=1) + ('state', 'in', ['draft', 'confirmed', 'waiting', 'partially_available', 'assigned'])], limit=1) return picking def _assign_picking(self): diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py index e47e00a9ed1..36e32b67ab4 100644 --- a/addons/stock_landed_costs/models/stock_landed_cost.py +++ b/addons/stock_landed_costs/models/stock_landed_cost.py @@ -131,13 +131,13 @@ class LandedCost(models.Model): prec_digits = self.env.user.company_id.currency_id.decimal_places for landed_cost in self: total_amount = sum(landed_cost.valuation_adjustment_lines.mapped('additional_landed_cost')) - if not tools.float_compare(total_amount, landed_cost.amount_total, precision_digits=prec_digits) == 0: + if not tools.float_is_zero(total_amount - landed_cost.amount_total, precision_digits=prec_digits): return False val_to_cost_lines = defaultdict(lambda: 0.0) for val_line in landed_cost.valuation_adjustment_lines: val_to_cost_lines[val_line.cost_line_id] += val_line.additional_landed_cost - if any(tools.float_compare(cost_line.price_unit, val_amount, precision_digits=prec_digits) != 0 + if any(not tools.float_is_zero(cost_line.price_unit - val_amount, precision_digits=prec_digits) for cost_line, val_amount in val_to_cost_lines.items()): return False return True diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index e81b6ebaf35..4b5b3a3fa3a 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -330,18 +330,19 @@ class View(models.Model): qcontext['main_object'] = self cur = Website.get_current_website() - qcontext['multi_website_websites_current'] = {'website_id': cur.id, 'name': cur.name, 'domain': cur.domain} - qcontext['multi_website_websites'] = [ - {'website_id': website.id, 'name': website.name, 'domain': website.domain} - for website in Website.search([]) if website != cur - ] + if self.env.user.has_group('website.group_website_publisher') and self.env.user.has_group('website.group_multi_website'): + qcontext['multi_website_websites_current'] = {'website_id': cur.id, 'name': cur.name, 'domain': cur.domain} + qcontext['multi_website_websites'] = [ + {'website_id': website.id, 'name': website.name, 'domain': website.domain} + for website in Website.search([]) if website != cur + ] - cur_company = self.env.user.company_id - qcontext['multi_website_companies_current'] = {'company_id': cur_company.id, 'name': cur_company.name} - qcontext['multi_website_companies'] = [ - {'company_id': comp.id, 'name': comp.name} - for comp in self.env.user.company_ids if comp != cur_company - ] + cur_company = self.env.user.company_id + qcontext['multi_website_companies_current'] = {'company_id': cur_company.id, 'name': cur_company.name} + qcontext['multi_website_companies'] = [ + {'company_id': comp.id, 'name': comp.name} + for comp in self.env.user.company_ids if comp != cur_company + ] qcontext.update(dict( self._context.copy(), diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py index 6f02268afc0..947ae83b06d 100644 --- a/addons/website_blog/controllers/main.py +++ b/addons/website_blog/controllers/main.py @@ -238,9 +238,11 @@ class WebsiteBlog(http.Controller): tags = request.env['blog.tag'].search([]) # Find next Post - all_post = BlogPost.search([('blog_id', '=', blog.id)]) + blog_post_domain = [('blog_id', '=', blog.id)] if not request.env.user.has_group('website.group_website_designer'): - all_post = all_post.filtered(lambda r: r.post_date <= fields.Datetime.now()) + blog_post_domain += [('post_date', '<=', fields.Datetime.now())] + + all_post = BlogPost.search(blog_post_domain) if blog_post not in all_post: return request.redirect("/blog/%s" % (slug(blog_post.blog_id))) diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index 562949215f8..668391636cf 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -76,6 +76,13 @@ class TestFields(common.TransactionCase): field = self.env['test_new_api.message']._fields['x_bool_false_computed'] self.assertFalse(field.depends) + def test_10_display_name(self): + """ test definition of automatic field 'display_name' """ + field = type(self.env['test_new_api.discussion']).display_name + self.assertTrue(field.automatic) + self.assertTrue(field.compute) + self.assertEqual(field.depends, ('name',)) + def test_10_non_stored(self): """ test non-stored fields """ # a field declared with store=False should not have a column diff --git a/odoo/fields.py b/odoo/fields.py index 5529288d177..1ba30e17f3f 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -490,6 +490,10 @@ class Field(MetaField('DummyField', (object,), {})): def _setup_regular_base(self, model): """ Setup the attributes of a non-related field. """ + pass + + def _setup_regular_full(self, model): + """ Determine the dependencies and inverse field(s) of ``self``. """ if self.depends is not None: return @@ -507,10 +511,6 @@ class Field(MetaField('DummyField', (object,), {})): else: self.depends = tuple(get_depends(self.compute)) - def _setup_regular_full(self, model): - """ Setup the inverse field(s) of ``self``. """ - pass - # # Setup of related fields # @@ -2364,8 +2364,8 @@ class _RelationalMulti(_Relational): for record in records: record[self.name] = record[self.name].filtered(accessible) - def _setup_regular_base(self, model): - super(_RelationalMulti, self)._setup_regular_base(model) + def _setup_regular_full(self, model): + super(_RelationalMulti, self)._setup_regular_full(model) if isinstance(self.domain, list): self.depends += tuple( self.name + '.' + arg[0] diff --git a/odoo/models.py b/odoo/models.py index 6693dac4c55..d7abce147d7 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -2609,6 +2609,15 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): cls._setup_done = True + # 5. determine and validate rec_name + if cls._rec_name: + assert cls._rec_name in cls._fields, \ + "Invalid rec_name %s for model %s" % (cls._rec_name, cls._name) + elif 'name' in cls._fields: + cls._rec_name = 'name' + elif 'x_name' in cls._fields: + cls._rec_name = 'x_name' + @api.model def _setup_fields(self): """ Setup the fields, except for recomputation triggers. """ @@ -2661,15 +2670,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # register constraints and onchange methods cls._init_constraints_onchanges() - # validate rec_name - if cls._rec_name: - assert cls._rec_name in cls._fields, \ - "Invalid rec_name %s for model %s" % (cls._rec_name, cls._name) - elif 'name' in cls._fields: - cls._rec_name = 'name' - elif 'x_name' in cls._fields: - cls._rec_name = 'x_name' - @api.model def fields_get(self, allfields=None, attributes=None): """ fields_get([fields][, attributes])