diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index cffc8f00fd5..dbf6c2fec53 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -44,7 +44,7 @@ class AuthSignupHome(Home): lang=user_sudo.lang, auth_login=werkzeug.url_encode({'auth_login': user_sudo.email}), ).send_mail(user_sudo.id, force_send=True) - return super(AuthSignupHome, self).web_login(*args, **kw) + return self.web_login(*args, **kw) except UserError as e: qcontext['error'] = e.name or e.value except (SignupError, AssertionError) as e: @@ -69,7 +69,7 @@ class AuthSignupHome(Home): try: if qcontext.get('token'): self.do_signup(qcontext) - return super(AuthSignupHome, self).web_login(*args, **kw) + return self.web_login(*args, **kw) else: login = qcontext.get('login') assert login, _("No login provided.") diff --git a/addons/base_import_module/models/ir_module.py b/addons/base_import_module/models/ir_module.py index 128594cc824..9d650a4b6fb 100644 --- a/addons/base_import_module/models/ir_module.py +++ b/addons/base_import_module/models/ir_module.py @@ -38,6 +38,8 @@ class IrModule(models.Model): installed_mods = [m.name for m in known_mods if m.state == 'installed'] terp = load_information_from_description_file(module, mod_path=path) + if not terp: + return False values = self.get_values_from_terp(terp) if 'version' in terp: values['latest_version'] = terp['version'] @@ -133,8 +135,8 @@ class IrModule(models.Model): try: # assert mod_name.startswith('theme_') path = opj(module_dir, mod_name) - self._import_module(mod_name, path, force=force) - success.append(mod_name) + if self._import_module(mod_name, path, force=force): + success.append(mod_name) except Exception as e: _logger.exception('Error while importing module') errors[mod_name] = exception_to_unicode(e) diff --git a/addons/base_setup/data/base_setup_data.xml b/addons/base_setup/data/base_setup_data.xml index 6499d00add4..e0d02590384 100644 --- a/addons/base_setup/data/base_setup_data.xml +++ b/addons/base_setup/data/base_setup_data.xml @@ -1,7 +1,7 @@ - + base_setup.show_effect True diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py index fac1730bead..52017208adc 100644 --- a/addons/event_sale/models/sale_order.py +++ b/addons/event_sale/models/sale_order.py @@ -42,11 +42,11 @@ class SaleOrderLine(models.Model): registrations linked to this line. This method update existing registrations and create new one for missing one. """ Registration = self.env['event.registration'].sudo() - registrations = Registration.search([('sale_order_line_id', 'in', self.ids), ('state', '!=', 'cancel')]) + registrations = Registration.search([('sale_order_line_id', 'in', self.ids)]) for so_line in self.filtered('event_id'): existing_registrations = registrations.filtered(lambda self: self.sale_order_line_id.id == so_line.id) if confirm: - existing_registrations.filtered(lambda self: self.state != 'open').confirm_registration() + existing_registrations.filtered(lambda self: self.state not in ['open', 'cancel']).confirm_registration() if cancel_to_draft: existing_registrations.filtered(lambda self: self.state == 'cancel').do_draft() diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index a3fff911d50..41305ff81bb 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -365,7 +365,7 @@ class HrExpense(models.Model): # get move line values move_line_values = move_line_values_by_expense.get(expense.id) move_line_dst = move_line_values[-1] - total_amount = abs(move_line_dst['debit']) + total_amount = move_line_dst['debit'] or -move_line_dst['credit'] total_amount_currency = move_line_dst['amount_currency'] # create one more move line, a counterline for the total on payable account diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index cea08636258..7aaf0a7097c 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -323,19 +323,22 @@ class HolidaysRequest(models.Model): @api.onchange('holiday_type') def _onchange_type(self): - if self.holiday_type == 'employee' and not self.employee_id: - self.employee_id = self.env.user.employee_ids[:1].id + if self.holiday_type == 'employee': + if not self.employee_id: + self.employee_id = self.env.user.employee_ids[:1].id self.mode_company_id = False self.category_id = False - elif self.holiday_type == 'company' and not self.mode_company_id: + elif self.holiday_type == 'company': self.employee_id = False - self.mode_company_id = self.env.user.company_id.id + if not self.mode_company_id: + self.mode_company_id = self.env.user.company_id.id self.category_id = False - elif self.holiday_type == 'department' and not self.department_id: + elif self.holiday_type == 'department': self.employee_id = False self.mode_company_id = False - self.department_id = self.env.user.employee_ids[:1].department_id.id self.category_id = False + if not self.department_id: + self.department_id = self.env.user.employee_ids[:1].department_id.id elif self.holiday_type == 'category': self.employee_id = False self.mode_company_id = False @@ -785,8 +788,9 @@ class HolidaysRequest(models.Model): responsible = self.env.user if self.validation_type == 'hr' or (self.validation_type == 'both' and self.state == 'validate1'): + company = self.department_id.company_id if self.holiday_type == 'department' else self.employee_id.company_id responsible = self.env['res.users'].search([ - ('company_id', '=', self.employee_id.company_id.id), + ('company_id', '=', company.id), ('groups_id', 'in', self.env.ref('hr_holidays.group_hr_holidays_user').id) ], limit=1) elif self.state == 'confirm' or (self.state == 'validate' and self.validation_type == 'no_validation'): diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index 023b3aebeaf..2b9e27aba96 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -244,16 +244,26 @@ class HolidaysAllocation(models.Model): @api.onchange('holiday_type') def _onchange_type(self): - if self.holiday_type == 'employee' and not self.employee_id: - if self.env.user.employee_ids: - self.employee_id = self.env.user.employee_ids[0] + if self.holiday_type == 'employee': + if not self.employee_id: + self.employee_id = self.env.user.employee_ids[:1].id + self.mode_company_id = False + self.category_id = False + elif self.holiday_type == 'company': + self.employee_id = False + if not self.mode_company_id: + self.mode_company_id = self.env.user.company_id.id + self.category_id = False elif self.holiday_type == 'department': - if self.env.user.employee_ids: - self.department_id = self.department_id or self.env.user.employee_ids[0].department_id - self.employee_id = None - else: - self.employee_id = None - self.department_id = None + self.employee_id = False + self.mode_company_id = False + self.category_id = False + if not self.department_id: + self.department_id = self.env.user.employee_ids[:1].department_id.id + elif self.holiday_type == 'category': + self.employee_id = False + self.mode_company_id = False + self.department_id = False @api.onchange('employee_id') def _onchange_employee(self): diff --git a/addons/hr_holidays/tests/test_leave_requests.py b/addons/hr_holidays/tests/test_leave_requests.py index 8dad86eae39..8f437333033 100644 --- a/addons/hr_holidays/tests/test_leave_requests.py +++ b/addons/hr_holidays/tests/test_leave_requests.py @@ -6,6 +6,7 @@ from dateutil.relativedelta import relativedelta from odoo import fields from odoo.exceptions import ValidationError from odoo.tools import mute_logger +from odoo.tests.common import Form from odoo.addons.hr_holidays.tests.common import TestHrHolidaysBase @@ -150,3 +151,25 @@ class TestLeaveRequests(TestHrHolidaysBase): 'date_to': fields.Datetime.from_string('2017-07-11 19:00:00'), 'number_of_days': 1, }) + + @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') + def test_department_leave(self): + """ Create a department leave """ + self.employee_hrmanager.write({'department_id': self.hr_dept.id}) + self.assertFalse(self.env['hr.leave'].search([('employee_id', 'in', self.hr_dept.member_ids.ids)])) + leave_form = Form(self.env['hr.leave'].sudo(self.user_hrmanager)) + leave_form.holiday_status_id = self.holidays_type_1 + leave_form.holiday_type = 'department' + leave_form.department_id = self.hr_dept + leave = leave_form.save() + leave.action_approve() + member_ids = self.hr_dept.member_ids.ids + self.assertEqual(self.env['hr.leave'].search_count([('employee_id', 'in', member_ids)]), len(member_ids), "Leave should be created for members of department") + + @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') + def test_allocation_request(self): + """ Create an allocation request """ + # employee should be set to current user + allocation_form = Form(self.env['hr.leave.allocation'].sudo(self.user_employee)) + allocation_form.holiday_status_id = self.holidays_type_1 + allocation = allocation_form.save() diff --git a/addons/l10n_fr_pos_cert/models/__init__.py b/addons/l10n_fr_pos_cert/models/__init__.py index 76de40a9e62..33657992e1c 100644 --- a/addons/l10n_fr_pos_cert/models/__init__.py +++ b/addons/l10n_fr_pos_cert/models/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import account_bank_statement +from . import account_fiscal_position from . import res_company from . import pos diff --git a/addons/l10n_fr_pos_cert/models/account_fiscal_position.py b/addons/l10n_fr_pos_cert/models/account_fiscal_position.py new file mode 100644 index 00000000000..71b4b12155a --- /dev/null +++ b/addons/l10n_fr_pos_cert/models/account_fiscal_position.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from odoo import _, models +from odoo.exceptions import UserError + + +class AccountFiscalPosition(models.Model): + _inherit = "account.fiscal.position" + + def write(self, vals): + if "tax_ids" in vals: + if self.env["pos.order"].sudo().search_count([("fiscal_position_id", "in", self.ids)]): + raise UserError( + _( + "You cannot modify a fiscal position used in a POS order. " + + "You should archive it and create a new one." + ) + ) + return super(AccountFiscalPosition, self).write(vals) diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index 3b8732d86ac..44cea49b102 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -476,9 +476,13 @@ class MailTemplate(models.Model): report = template.report_template report_service = report.report_name - if report.report_type not in ['qweb-html', 'qweb-pdf']: - raise UserError(_('Unsupported report type %s found.') % report.report_type) - result, format = report.render_qweb_pdf([res_id]) + if report.report_type in ['qweb-html', 'qweb-pdf']: + result, format = report.render_qweb_pdf([res_id]) + else: + res = report.render([res_id]) + if not res: + raise UserError(_('Unsupported report type %s found.') % report.report_type) + result, format = res # TODO in trunk, change return format to binary to match message_post expected format result = base64.b64encode(result) diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index d688b10d1e3..599f1c72594 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -376,15 +376,15 @@ class MrpWorkorder(models.Model): production_move.quantity_done += self.qty_producing if not self.next_work_order_id: - for by_product_move in self.production_id.move_finished_ids.filtered(lambda x: (x.product_id.id != self.production_id.product_id.id) and (x.state not in ('done', 'cancel'))): - if by_product_move.has_tracking != 'serial': - values = self._get_byproduct_move_line(by_product_move, self.qty_producing * by_product_move.unit_factor) - self.env['stock.move.line'].create(values) - elif by_product_move.has_tracking == 'serial': - qty_todo = by_product_move.product_uom._compute_quantity(self.qty_producing * by_product_move.unit_factor, by_product_move.product_id.uom_id) - for i in range(0, int(float_round(qty_todo, precision_digits=0))): - values = self._get_byproduct_move_line(by_product_move, 1) + for by_product_move in self._get_byproduct_move_to_update(): + if by_product_move.has_tracking != 'serial': + values = self._get_byproduct_move_line(by_product_move, self.qty_producing * by_product_move.unit_factor) self.env['stock.move.line'].create(values) + elif by_product_move.has_tracking == 'serial': + qty_todo = by_product_move.product_uom._compute_quantity(self.qty_producing * by_product_move.unit_factor, by_product_move.product_id.uom_id) + for i in range(0, int(float_round(qty_todo, precision_digits=0))): + values = self._get_byproduct_move_line(by_product_move, 1) + self.env['stock.move.line'].create(values) # Update workorder quantity produced self.qty_produced += self.qty_producing @@ -415,6 +415,9 @@ class MrpWorkorder(models.Model): self.button_finish() return True + def _get_byproduct_move_to_update(self): + return self.production_id.move_finished_ids.filtered(lambda x: (x.product_id.id != self.production_id.product_id.id) and (x.state not in ('done', 'cancel'))) + @api.multi def _start_nextworkorder(self): rounding = self.product_id.uom_id.rounding diff --git a/addons/mrp/models/res_config_settings.py b/addons/mrp/models/res_config_settings.py index 5e47515b1f3..dcda06cb4c2 100644 --- a/addons/mrp/models/res_config_settings.py +++ b/addons/mrp/models/res_config_settings.py @@ -24,4 +24,13 @@ class ResConfigSettings(models.TransientModel): @api.onchange('group_mrp_routings') def _onchange_group_mrp_routings(self): - self.module_mrp_workorder = self.group_mrp_routings + # If we activate 'MRP Work Orders', it means that we need to install 'mrp_workorder'. + # The opposite is not always true: other modules (such as 'quality_mrp_workorder') may + # depend on 'mrp_workorder', so we should not automatically uninstall the module if 'MRP + # Work Orders' is deactivated. + # Long story short: if 'mrp_workorder' is already installed, we don't uninstall it based on + # group_mrp_routings + if self.group_mrp_routings: + self.module_mrp_workorder = True + elif not self.env['ir.module.module'].search([('name', '=', 'mrp_workorder'), ('state', '=', 'installed')]): + self.module_mrp_workorder = False diff --git a/addons/mrp/models/stock_warehouse.py b/addons/mrp/models/stock_warehouse.py index 59fa4f39e36..bd4de62bdb4 100644 --- a/addons/mrp/models/stock_warehouse.py +++ b/addons/mrp/models/stock_warehouse.py @@ -170,18 +170,19 @@ class StockWarehouse(models.Model): manufacture_steps = vals.get('manufacture_steps', def_values['manufacture_steps']) code = vals.get('code') or self.code code = code.replace(' ', '').upper() + company_id = vals.get('company_id', self.company_id.id) values.update({ 'pbm_loc_id': { 'name': _('Pre-Production'), 'active': manufacture_steps in ('pbm', 'pbm_sam'), 'usage': 'internal', - 'barcode': code + '-PREPRODUCTION' + 'barcode': self._valid_barcode(code + '-PREPRODUCTION', company_id) }, 'sam_loc_id': { 'name': _('Post-Production'), 'active': manufacture_steps == 'pbm_sam', 'usage': 'internal', - 'barcode': code + '-POSTPRODUCTION' + 'barcode': self._valid_barcode(code + '-POSTPRODUCTION', company_id) }, }) return values diff --git a/addons/point_of_sale/data/point_of_sale_data.xml b/addons/point_of_sale/data/point_of_sale_data.xml index e35a32fc706..34b75548760 100644 --- a/addons/point_of_sale/data/point_of_sale_data.xml +++ b/addons/point_of_sale/data/point_of_sale_data.xml @@ -1,13 +1,14 @@ - + Picking POS POS 5 - + + PoS Orders diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 663d037ee21..329b72c1789 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -360,7 +360,7 @@ class PosOrder(models.Model): account_analytic=account_analytic) if res: line1, line2 = res - line1 = Product._convert_prepared_anglosaxon_line(line1, order.partner_id) + line1 = Product._convert_prepared_anglosaxon_line(line1, line['partner_id']) insert_data('counter_part', { 'name': line1['name'], 'account_id': line1['account_id'], @@ -370,7 +370,7 @@ class PosOrder(models.Model): }) - line2 = Product._convert_prepared_anglosaxon_line(line2, order.partner_id) + line2 = Product._convert_prepared_anglosaxon_line(line2, line['partner_id']) insert_data('counter_part', { 'name': line2['name'], 'account_id': line2['account_id'], @@ -392,7 +392,6 @@ class PosOrder(models.Model): def insert_data(data_type, values): # if have_to_group_by: values.update({ - 'partner_id': partner_id, 'move_id': move.id, }) diff --git a/addons/point_of_sale/views/account_journal_view.xml b/addons/point_of_sale/views/account_journal_view.xml index 94c80cbacef..99e0403306e 100644 --- a/addons/point_of_sale/views/account_journal_view.xml +++ b/addons/point_of_sale/views/account_journal_view.xml @@ -5,8 +5,7 @@ account.journal form tree,kanban,form - - [('journal_user','=', 1)] +

Add a new payment method diff --git a/addons/purchase/report/purchase_bill.py b/addons/purchase/report/purchase_bill.py index 036309e8e29..009ce1f6ac8 100644 --- a/addons/purchase/report/purchase_bill.py +++ b/addons/purchase/report/purchase_bill.py @@ -37,7 +37,7 @@ class PurchaseBillUnion(models.Model): NULL as vendor_bill_id, id as purchase_order_id FROM purchase_order WHERE - state = 'purchase' AND + state in ('purchase', 'done') AND invoice_status in ('to invoice', 'no') )""") diff --git a/addons/purchase/views/purchase_views.xml b/addons/purchase/views/purchase_views.xml index 9218fd5bc91..f31568500ab 100644 --- a/addons/purchase/views/purchase_views.xml +++ b/addons/purchase/views/purchase_views.xml @@ -135,12 +135,12 @@ +