diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py index fc2a4bd0c36..71ed7b11a7e 100644 --- a/addons/account/models/account_bank_statement.py +++ b/addons/account/models/account_bank_statement.py @@ -426,7 +426,7 @@ class AccountBankStatementLine(models.Model): payment_to_unreconcile |= line.payment_id if st_line.move_name and line.payment_id.payment_reference == st_line.move_name: #there can be several moves linked to a statement line but maximum one created by the line itself - aml_to_cancel |= st_line.journal_entry_ids + aml_to_cancel |= line payment_to_cancel |= line.payment_id aml_to_unbind = aml_to_unbind - aml_to_cancel @@ -887,6 +887,8 @@ class AccountBankStatementLine(models.Model): if aml_dict.get('tax_ids') and isinstance(aml_dict['tax_ids'][0], pycompat.integer_types): # Transform the value in the format required for One2many and Many2many fields aml_dict['tax_ids'] = map(lambda id: (4, id, None), aml_dict['tax_ids']) + if any(line.journal_entry_ids for line in self): + raise UserError(_('A selected statement line was already reconciled with an account move.')) # Fully reconciled moves are just linked to the bank statement total = self.amount diff --git a/addons/account/models/company.py b/addons/account/models/company.py index b15cb4afe0b..44eee5432ae 100644 --- a/addons/account/models/company.py +++ b/addons/account/models/company.py @@ -79,8 +79,7 @@ Best Regards,''') account.write({'code': account.code.rstrip('0').ljust(digits, '0')}) @api.multi - def write(self, values): - #restrict the closing of FY if there are still unposted entries + def _validate_fiscalyear_lock(self, values): if values.get('fiscalyear_lock_date'): nb_draft_entries = self.env['account.move'].search([ ('company_id', 'in', [c.id for c in self]), @@ -88,6 +87,12 @@ Best Regards,''') ('date', '<=', values['fiscalyear_lock_date'])]) if nb_draft_entries: raise ValidationError(_('There are still unposted entries in the period you want to lock. You should either post or delete them.')) + + @api.multi + def write(self, values): + #restrict the closing of FY if there are still unposted entries + self._validate_fiscalyear_lock(values) + # Reflect the change on accounts for company in self: digits = values.get('accounts_code_digits') or company.accounts_code_digits diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index 042455837ce..b028301af6f 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -108,7 +108,7 @@ class AccountInvoiceReport(models.Model): 1 AS nbr, ai.type, ai.state, pt.categ_id, ai.date_due, ai.account_id, ail.account_id AS account_line_id, ai.partner_bank_id, - SUM ((invoice_type.sign * ail.quantity) / (u.factor * u2.factor)) AS product_qty, + SUM ((invoice_type.sign * ail.quantity) / u.factor * u2.factor) AS product_qty, SUM(ail.price_subtotal_signed) AS price_total, SUM(ABS(ail.price_subtotal_signed)) / CASE WHEN SUM(ail.quantity / u.factor * u2.factor) <> 0::numeric diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 6c1b0facad3..dcaba89bc61 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -57,6 +57,9 @@ class AuthSignupHome(Home): else: login = qcontext.get('login') assert login, "No login provided." + _logger.info( + "Password reset attempt for <%s> by user <%s> from %s", + login, request.env.user.login, request.httprequest.remote_addr) request.env['res.users'].sudo().reset_password(login) qcontext['message'] = _("An email has been sent with credentials to reset your password") except SignupError: diff --git a/addons/auth_signup/models/res_users.py b/addons/auth_signup/models/res_users.py index d99961076be..05d52596c85 100644 --- a/addons/auth_signup/models/res_users.py +++ b/addons/auth_signup/models/res_users.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import logging from ast import literal_eval @@ -11,6 +12,7 @@ from odoo.tools.misc import ustr from odoo.addons.base.ir.ir_mail_server import MailDeliveryException from odoo.addons.auth_signup.models.res_partner import SignupError, now +_logger = logging.getLogger(__name__) class ResUsers(models.Model): _inherit = 'res.users' @@ -134,6 +136,7 @@ class ResUsers(models.Model): if not user.email: raise UserError(_("Cannot send email: user %s has no email address.") % user.name) template.with_context(lang=user.lang).send_mail(user.id, force_send=True, raise_exception=True) + _logger.info("Password reset email sent for user <%s> to <%s>", user.login, user.email) @api.model def create(self, values): diff --git a/addons/base_automation/views/base_automation_view.xml b/addons/base_automation/views/base_automation_view.xml index fa8c4b5eab2..a81dd04d75b 100644 --- a/addons/base_automation/views/base_automation_view.xml +++ b/addons/base_automation/views/base_automation_view.xml @@ -15,6 +15,13 @@ attrs="{'invisible':[('menu_ir_values_id','=',False)]}" help="Remove the contextual action related to this server action"/> +
+
+ +
+
diff --git a/addons/google_spreadsheet/views/base_config_settings_views.xml b/addons/google_spreadsheet/views/base_config_settings_views.xml index 14bda91e373..6afd8ddacda 100644 --- a/addons/google_spreadsheet/views/base_config_settings_views.xml +++ b/addons/google_spreadsheet/views/base_config_settings_views.xml @@ -8,13 +8,10 @@
-
-
- - - +
+ Please use the settings of Google Drive + on the left or above. +
diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py index c2f0e8cb3c2..20dc2939d00 100644 --- a/addons/im_livechat/models/im_livechat_channel.py +++ b/addons/im_livechat/models/im_livechat_channel.py @@ -166,7 +166,7 @@ class ImLivechatChannel(models.Model): operator_partner_id = user.partner_id.id # partner to add to the mail.channel channel_partner_to_add = [(4, operator_partner_id)] - if self.env.uid: # if the user if logged (portal user), he can be identify + if self.env.user and self.env.user.active: # valid session user (not public) channel_partner_to_add.append((4, self.env.user.partner_id.id)) # create the session, and add the link with the given channel mail_channel = self.env["mail.channel"].with_context(mail_create_nosubscribe=False).sudo().create({ diff --git a/addons/l10n_fr_hr_payroll/report/report_l10n_fr_fiche_paye.xml b/addons/l10n_fr_hr_payroll/report/report_l10n_fr_fiche_paye.xml index 883a7c75f1e..85ae97c2248 100644 --- a/addons/l10n_fr_hr_payroll/report/report_l10n_fr_fiche_paye.xml +++ b/addons/l10n_fr_hr_payroll/report/report_l10n_fr_fiche_paye.xml @@ -48,7 +48,7 @@
-
+



diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index cc3519ef737..4c3bab77de1 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -507,7 +507,7 @@ class MassMailing(models.Model): return super(MassMailing, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby) def update_opt_out(self, email, res_ids, value): - model = self.env[self.mailing_model_real] + model = self.env[self.mailing_model_real].with_context(active_test=False) if 'opt_out' in model._fields: email_fname = 'email_from' if 'email' in model._fields: diff --git a/addons/mass_mailing/static/src/js/mass_mailing_editor.js b/addons/mass_mailing/static/src/js/mass_mailing_editor.js index 4475d001e5d..5d475808589 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_editor.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_editor.js @@ -273,9 +273,13 @@ snippets_editor.Class.include({ function editable_area_is_empty($layout) { $layout = $layout || $editable_area.find(".o_layout"); var $mail_wrapper = $layout.children(".o_mail_wrapper"); + var $mail_wrapper_content = $mail_wrapper.find('.o_mail_wrapper_td'); + if (!$mail_wrapper_content.length) { // compatibility + $mail_wrapper_content = $mail_wrapper; + } return ( $editable_area.html().trim() === "" - || ($layout.length > 0 && ($layout.html().trim() === "" || $mail_wrapper.length > 0 && $mail_wrapper.html().trim() === "")) + || ($layout.length > 0 && ($layout.html().trim() === "" || $mail_wrapper_content.length > 0 && $mail_wrapper_content.html().trim() === "")) ); } @@ -325,7 +329,15 @@ snippets_editor.Class.include({ switch_images(theme_params, $editable_area); var $old_layout = $editable_area.find(".o_layout"); - var $new_wrapper = $("
", {"class": "o_mail_wrapper oe_structure"}); + // This wrapper structure is the only way to have a responsive and + // centered fixed-width content column on all mail clients + var $new_wrapper = $('', {class: 'o_mail_wrapper'}); + var $new_wrapper_content = $("').append( + $("
", {class: 'o_mail_no_resize o_mail_wrapper_td oe_structure'}); + $new_wrapper.append($('
", {class: 'o_mail_no_resize'}), + $new_wrapper_content, + $("", {class: 'o_mail_no_resize'}) + )); var $new_layout = $("
", {"class": "o_layout " + theme_params.className}).append($new_wrapper); var $contents; @@ -338,11 +350,11 @@ snippets_editor.Class.include({ } $editable_area.empty().append($new_layout); - $new_wrapper.append($contents); + $new_wrapper_content.append($contents); $old_layout.remove(); if (first_choice) { - self.add_default_snippet_text_classes($new_wrapper); + self.add_default_snippet_text_classes($new_wrapper_content); } self.show_blocks(); } diff --git a/addons/mass_mailing/static/src/less/themes/theme_default.less b/addons/mass_mailing/static/src/less/themes/theme_default.less index 98f86421000..74f71191db2 100644 --- a/addons/mass_mailing/static/src/less/themes/theme_default.less +++ b/addons/mass_mailing/static/src/less/themes/theme_default.less @@ -49,27 +49,36 @@ // ===== Layout ===== .o_layout { - font-family: @o_mm_def_font; - color: lighten(@o_mm_def_text_color, 20%); overflow: hidden; - width: 100%; + width: 94%; min-height: 100%; background-color: @o_mm_def_color_alpha; - padding: 24px 0; + padding: 24px 3%; + + // Forces on

elements as several mail clients does not correctly + // inherit font properties + &, p { + font-family: @o_mm_def_font; + color: lighten(@o_mm_def_text_color, 20%); + } > .o_mail_wrapper { width: 100%; - max-width: @o_mm_def_body_width; - margin: auto; - border: 1px solid darken(@o_mm_def_color_alpha, 2%); - background-color: @o_mm_def_color_beta; + border-collapse: separate; // Allow the first and last td to have the + // same width + + .o_mail_wrapper_td { + width: @o_mm_def_body_width; + border: 1px solid darken(@o_mm_def_color_alpha, 2%); + background-color: @o_mm_def_color_beta; + } } } // ===== Snippets (general) ===== .o_mail_snippet_general { width: 100%; - max-width: @o_mm_def_body_width; + max-width: @o_mm_def_body_width; // should not be necessary thanks to mail wrapper margin: 0 auto; h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { @@ -157,7 +166,11 @@ } .o_mail_block_two_cols, .o_mail_block_three_cols { - font-size: small; + // Forces on

elements as several mail clients does not correctly + // inherit font properties + &, p { + font-size: small; + } .o_mail_col_container { padding: 10px 20px; @@ -169,7 +182,11 @@ } .o_mail_block_image_text, .o_mail_block_text_image { - font-size: small; + // Forces on

elements as several mail clients does not correctly + // inherit font properties + &, p { + font-size: small; + } } .o_mail_block_event { diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 5fd40f455c7..ac799bd0538 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -499,11 +499,14 @@ class MrpProduction(models.Model): @api.multi def post_inventory(self): for order in self: + moves_not_to_do = order.move_raw_ids.filtered(lambda x: x.state == 'done') moves_to_do = order.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) moves_to_do.action_done() + moves_to_do = order.move_raw_ids.filtered(lambda x: x.state == 'done') - moves_not_to_do order._cal_price(moves_to_do) moves_to_finish = order.move_finished_ids.filtered(lambda x: x.state not in ('done','cancel')) moves_to_finish.action_done() + for move in moves_to_finish: #Group quants by lots lot_quants = {} diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index bb98284f9f2..b2fd5ea0ab6 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -507,7 +507,7 @@ class PaymentTransaction(models.Model): tx.write({'reference': str(tx.id)}) # Generate callback hash if it is configured on the tx; avoid generating unnecessary stuff - if tx.callback_model_id and tx.callback_res_id and tx.callback_method: + if tx.callback_model_id and tx.callback_res_id and tx.sudo().callback_method: tx.write({'callback_hash': tx._generate_callback_hash()}) return tx @@ -547,7 +547,9 @@ class PaymentTransaction(models.Model): def _generate_callback_hash(self): self.ensure_one() secret = self.env['ir.config_parameter'].sudo().get_param('database.secret') - token = '%s%s%s' % (self.callback_model_id.model, self.callback_res_id, self.callback_method) + token = '%s%s%s' % (self.callback_model_id.model, + self.callback_res_id, + self.sudo().callback_method) return hmac.new(str(secret), token, hashlib.sha256).hexdigest() # -------------------------------------------------- @@ -635,7 +637,7 @@ class PaymentTransaction(models.Model): @api.multi def execute_callback(self): res = None - for transaction in self.filtered(lambda tx: tx.callback_model_id and tx.callback_res_id and tx.callback_method): + for transaction in self.filtered(lambda tx: tx.callback_model_id and tx.callback_res_id and tx.sudo().callback_method): valid_token = transaction._generate_callback_hash() if not consteq(ustr(valid_token), transaction.callback_hash): _logger.warning("Invalid callback signature for transaction %d" % (transaction.id)) @@ -643,7 +645,7 @@ class PaymentTransaction(models.Model): record = self.env[transaction.callback_model_id.model].browse(transaction.callback_res_id).exists() if record: - res = getattr(record, transaction.callback_method)(transaction) + res = getattr(record, transaction.sudo().callback_method)(transaction) else: _logger.warning("Did not found record %s.%s for callback of transaction %d" % (transaction.callback_model_id.model, transaction.callback_res_id, transaction.id)) return res diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index ed5335c1c46..2bb4fb23dd9 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -677,7 +677,7 @@ class PosOrder(models.Model): qty = 0 qty_done = 0 pack_lots = [] - pos_pack_lots = PosPackOperationLot.search([('order_id', '=', order.id), ('product_id', '=', pack_operation.product_id.id)]) + pos_pack_lots = PosPackOperationLot.search([('order_id', '=', order.id), ('product_id', '=', pack_operation.product_id.id)]) pack_lot_names = [pos_pack.lot_name for pos_pack in pos_pack_lots] if pack_lot_names and lots_necessary: diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 04c59ac13fb..71e4ca2c1c8 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -753,7 +753,7 @@ - + > @@ -765,19 +765,19 @@ - + x - + - + @@ -789,11 +789,11 @@ -------- - Subtotal + Subtotal - + @@ -803,7 +803,7 @@ --------

        TOTAL
- +

@@ -812,14 +812,14 @@ - +
        CHANGE
- +

@@ -828,19 +828,19 @@ Discounts - + - + Total Taxes - + diff --git a/addons/pos_mercury/models/pos_mercury_transaction.py b/addons/pos_mercury/models/pos_mercury_transaction.py index 36748c5ea0c..ca392c4d895 100644 --- a/addons/pos_mercury/models/pos_mercury_transaction.py +++ b/addons/pos_mercury/models/pos_mercury_transaction.py @@ -17,7 +17,7 @@ class MercuryTransaction(models.Model): _name = 'pos_mercury.mercury_transaction' def _get_pos_session(self): - pos_session = self.env['pos.session'].search([('state', '=', 'opened'), ('user_id', '=', self.env.uid)]) + pos_session = self.env['pos.session'].search([('state', '=', 'opened'), ('user_id', '=', self.env.uid)], limit=1) if not pos_session: raise UserError("No POS session") @@ -34,6 +34,7 @@ class MercuryTransaction(models.Model): raise UserError("No Mercury configuration associated with the journal.") def _setup_request(self, data): + # todo: in master make the client include the pos.session id and use that pos_session = self._get_pos_session() config = pos_session.config_id diff --git a/addons/pos_restaurant/static/src/js/floors.js b/addons/pos_restaurant/static/src/js/floors.js index 2494401ff00..67307794fbc 100644 --- a/addons/pos_restaurant/static/src/js/floors.js +++ b/addons/pos_restaurant/static/src/js/floors.js @@ -979,4 +979,11 @@ screens.define_action_button({ }, }); +return { + TableGuestsButton: TableGuestsButton, + TransferOrderButton:TransferOrderButton, + TableWidget: TableWidget, + FloorScreenWidget: FloorScreenWidget, +}; + }); diff --git a/addons/product/models/res_company.py b/addons/product/models/res_company.py index 3d8d0dcfc70..d94e8d2cda2 100644 --- a/addons/product/models/res_company.py +++ b/addons/product/models/res_company.py @@ -18,12 +18,13 @@ class ResCompany(models.Model): 'currency_id': new_company.currency_id.id, }) field = self.env['ir.model.fields']._get('res.partner', 'property_product_pricelist') - self.env['ir.property'].create({ + product_property = self.env['ir.property'].create({ 'name': 'property_product_pricelist', - 'company_id': new_company.id, 'value_reference': 'product.pricelist,%s' % pricelist.id, 'fields_id': field.id }) + # multi-company security rules prevents access + product_property.sudo().write({'company_id': new_company.id}) return new_company @api.multi diff --git a/addons/report/models/report.py b/addons/report/models/report.py index 55abb28a638..3ff68a9052f 100644 --- a/addons/report/models/report.py +++ b/addons/report/models/report.py @@ -576,19 +576,23 @@ class Report(models.Model): """ writer = PdfFileWriter() streams = [] # We have to close the streams *after* PdfFilWriter's call to write() - for document in documents: - pdfreport = open(document, 'rb') - streams.append(pdfreport) - reader = PdfFileReader(pdfreport) - for page in range(0, reader.getNumPages()): - writer.addPage(reader.getPage(page)) + try: + for document in documents: + pdfreport = open(document, 'rb') + streams.append(pdfreport) + reader = PdfFileReader(pdfreport) + for page in range(0, reader.getNumPages()): + writer.addPage(reader.getPage(page)) - merged_file_fd, merged_file_path = tempfile.mkstemp(suffix='.html', prefix='report.merged.tmp.') - with closing(os.fdopen(merged_file_fd, 'w')) as merged_file: - writer.write(merged_file) - - for stream in streams: - stream.close() + merged_file_fd, merged_file_path = tempfile.mkstemp(suffix='.pdf', prefix='report.merged.tmp.') + with closing(os.fdopen(merged_file_fd, 'w')) as merged_file: + writer.write(merged_file) + finally: + for stream in streams: + try: + stream.close() + except Exception: + pass return merged_file_path diff --git a/addons/sale/report/sale_report.xml b/addons/sale/report/sale_report.xml index 3e4996dffe2..e0bad531aea 100644 --- a/addons/sale/report/sale_report.xml +++ b/addons/sale/report/sale_report.xml @@ -12,17 +12,5 @@ (object.state in ('draft', 'sent') and 'Quotation'+'-'+(object.name) or 'Order'+'-'+(object.name)) - - - - 'Pro-Forma'+'-'+(object.name) - diff --git a/addons/sale/report/sale_report_templates.xml b/addons/sale/report/sale_report_templates.xml index b16c4d98192..71a6bbd144d 100644 --- a/addons/sale/report/sale_report_templates.xml +++ b/addons/sale/report/sale_report_templates.xml @@ -29,11 +29,11 @@

- + Order # Quotation # - + Pro-Forma # diff --git a/addons/sale/security/sale_security.xml b/addons/sale/security/sale_security.xml index 32c1d36917e..0a269ab44bf 100644 --- a/addons/sale/security/sale_security.xml +++ b/addons/sale/security/sale_security.xml @@ -4,7 +4,6 @@ Personalize sales order and invoice report - diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py index 3e28cbb9575..886d80d19bd 100644 --- a/addons/sale/wizard/sale_make_invoice_advance.py +++ b/addons/sale/wizard/sale_make_invoice_advance.py @@ -82,10 +82,11 @@ class SaleAdvancePaymentInv(models.TransientModel): else: amount = self.amount name = _('Down Payment') - if order.fiscal_position_id and self.product_id.taxes_id: - tax_ids = order.fiscal_position_id.map_tax(self.product_id.taxes_id).ids + taxes = self.product_id.taxes_id.filtered(lambda r: not order.company_id or r.company_id == order.company_id) + if order.fiscal_position_id and taxes: + tax_ids = order.fiscal_position_id.map_tax(taxes).ids else: - tax_ids = self.product_id.taxes_id.ids + tax_ids = taxes.ids invoice = inv_obj.create({ 'name': order.client_order_ref or order.name, @@ -145,10 +146,11 @@ class SaleAdvancePaymentInv(models.TransientModel): raise UserError(_('The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.')) if self.product_id.type != 'service': raise UserError(_("The product used to invoice a down payment should be of type 'Service'. Please use another product or update this product.")) - if order.fiscal_position_id and self.product_id.taxes_id: - tax_ids = order.fiscal_position_id.map_tax(self.product_id.taxes_id).ids + taxes = self.product_id.taxes_id.filtered(lambda r: not order.company_id or r.company_id == order.company_id) + if order.fiscal_position_id and taxes: + tax_ids = order.fiscal_position_id.map_tax(taxes).ids else: - tax_ids = self.product_id.taxes_id.ids + tax_ids = taxes.ids so_line = sale_line_obj.create({ 'name': _('Advance: %s') % (time.strftime('%m %Y'),), 'price_unit': amount, diff --git a/addons/sale_mrp/sale_mrp.py b/addons/sale_mrp/sale_mrp.py index 8aa4074c8bb..03aec004f03 100644 --- a/addons/sale_mrp/sale_mrp.py +++ b/addons/sale_mrp/sale_mrp.py @@ -42,6 +42,30 @@ class SaleOrderLine(models.Model): return 0.0 return super(SaleOrderLine, self)._get_delivered_qty() + @api.multi + def _get_bom_component_qty(self, bom): + bom_quantity = self.product_uom._compute_quantity(self.product_uom_qty, bom.product_uom_id) + boms, lines = bom.explode(self.product_id, bom_quantity) + components = {} + for line, line_data in lines: + product = line.product_id.id + uom = line.product_uom_id + qty = line.product_qty + if components.get(product, False): + if uom.id != components[product]['uom']: + from_uom = uom + to_uom = self.env['product.uom'].browse(components[product]['uom']) + qty = from_uom._compute_quantity(qty, to_uom_id=to_uom) + components[product]['qty'] += qty + else: + # To be in the uom reference of the product + to_uom = self.env['product.product'].browse(product).uom_id + if uom.id != to_uom.id: + from_uom = uom + qty = from_uom._compute_quantity(qty, to_uom_id=to_uom) + components[product] = {'qty': qty, 'uom': to_uom.id} + return components + class AccountInvoiceLine(models.Model): # TDE FIXME: what is this code ?? diff --git a/addons/sale_timesheet/models/procurement.py b/addons/sale_timesheet/models/procurement.py index bd7e4c85329..437a6f7398c 100644 --- a/addons/sale_timesheet/models/procurement.py +++ b/addons/sale_timesheet/models/procurement.py @@ -71,7 +71,7 @@ class ProcurementOrder(models.Model): 'partner_id': self.sale_line_id.order_id.partner_id.id or self.partner_dest_id.id, 'user_id': self.env.uid, 'procurement_id': self.id, - 'description': self.name + '\n', + 'description': self.name + '
', 'project_id': project.id, 'company_id': self.company_id.id, } diff --git a/addons/stock/models/procurement.py b/addons/stock/models/procurement.py index 7ce3bb2abed..902e0517ef5 100644 --- a/addons/stock/models/procurement.py +++ b/addons/stock/models/procurement.py @@ -273,6 +273,11 @@ class ProcurementOrder(models.Model): def _procurement_from_orderpoint_post_process(self, orderpoint_ids): return True + def _get_orderpoint_domain(self, company_id=False): + domain = [('company_id', '=', company_id)] if company_id else [] + domain += [('product_id.active', '=', True)] + return domain + @api.model def _procure_orderpoint_confirm(self, use_new_cursor=False, company_id=False): """ Create procurements based on orderpoints. @@ -282,9 +287,7 @@ class ProcurementOrder(models.Model): """ OrderPoint = self.env['stock.warehouse.orderpoint'] - - domain = [('company_id', '=', company_id)] if company_id else [] - domain += [('product_id.active', '=', True)] + domain = self._get_orderpoint_domain(company_id=company_id) orderpoints_noprefetch = OrderPoint.with_context(prefetch_fields=False).search(domain, order=self._procurement_from_orderpoint_get_order()).ids while orderpoints_noprefetch: diff --git a/addons/stock/models/res_company.py b/addons/stock/models/res_company.py index c1fd7aff521..6860ce7496b 100644 --- a/addons/stock/models/res_company.py +++ b/addons/stock/models/res_company.py @@ -23,14 +23,17 @@ class Company(models.Model): location = self.env['stock.location'].create({ 'name': _('%s: Transit Location') % self.name, 'usage': 'transit', - 'company_id': self.id, 'location_id': parent_location and parent_location.id or False, }) + location.sudo().write({'company_id': self.id}) self.write({'internal_transit_location_id': location.id}) @api.model def create(self, vals): company = super(Company, self).create(vals) - self.env['stock.warehouse'].create({'name': company.name, 'code': company.name[:5], 'company_id': company.id}) + + # multi-company rules prevents creating warehouse and sub-locations + self.env['stock.warehouse'].check_access_rights('create') + self.env['stock.warehouse'].sudo().create({'name': company.name, 'code': company.name[:5], 'company_id': company.id}) company.create_transit_location() return company diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 591842e4225..b7ec53666ae 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -495,9 +495,12 @@ class StockMove(models.Model): self._push_apply() return self + def _set_default_price_moves(self): + return self.filtered(lambda move: not move.price_unit) + def set_default_price_unit_from_product(self): """ Set price to move, important in inter-company moves or receipts with only one partner """ - for move in self.filtered(lambda move: not move.price_unit): + for move in self._set_default_price_moves(): move.write({'price_unit': move.product_id.standard_price}) attribute_price = set_default_price_unit_from_product diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml index 5d5f2b2b4e6..ff42bf3095a 100644 --- a/addons/stock/views/stock_picking_views.xml +++ b/addons/stock/views/stock_picking_views.xml @@ -226,7 +226,7 @@

-