diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py index aa4f96244d8..2fe1ea6ae78 100644 --- a/addons/delivery/models/delivery_carrier.py +++ b/addons/delivery/models/delivery_carrier.py @@ -308,7 +308,7 @@ class DeliveryCarrier(models.Model): for line in order.order_line.filtered(lambda line: not line.is_delivery and not line.display_type): total_cost += self._product_price_to_company_currency(line.product_qty, line.product_id, order.company_id) - total_weight = order._get_estimated_weight() + total_weight = order._get_estimated_weight() + default_package_type.base_weight # If max weight == 0 => division by 0. If this happens, we want to have # more in the max weight than in the total weight, so that it only # creates ONE package with everything. @@ -327,7 +327,7 @@ class DeliveryCarrier(models.Model): if picking.is_return_picking: commodities = self._get_commodities_from_stock_move_lines(picking.move_line_ids) - weight = picking._get_estimated_weight() + weight = picking._get_estimated_weight() + default_package_type.base_weight packages.append(DeliveryPackage(commodities, weight, default_package_type, currency=picking.company_id.currency_id, picking=picking)) return packages diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py index aefad9130b3..48f1bedba5e 100644 --- a/addons/delivery/models/stock_picking.py +++ b/addons/delivery/models/stock_picking.py @@ -11,10 +11,11 @@ from odoo.exceptions import UserError class StockQuantPackage(models.Model): _inherit = "stock.quant.package" - @api.depends('quant_ids') + @api.depends('quant_ids', 'package_type_id') def _compute_weight(self): for package in self: - weight = 0.0 + # set initial weight to package type base weight + weight = package.package_type_id.base_weight or 0.0 if self.env.context.get('picking_id'): # TODO: potential bottleneck: N packages = N queries, use groupby ? current_picking_move_line_ids = self.env['stock.move.line'].search([ diff --git a/addons/delivery/wizard/choose_delivery_package.py b/addons/delivery/wizard/choose_delivery_package.py index 65a9bbaa49c..7d713522931 100644 --- a/addons/delivery/wizard/choose_delivery_package.py +++ b/addons/delivery/wizard/choose_delivery_package.py @@ -9,25 +9,9 @@ class ChooseDeliveryPackage(models.TransientModel): _name = 'choose.delivery.package' _description = 'Delivery Package Selection Wizard' - @api.model - def default_get(self, fields_list): - defaults = super().default_get(fields_list) - if 'shipping_weight' in fields_list: - picking = self.env['stock.picking'].browse(defaults.get('picking_id')) - move_line_ids = picking.move_line_ids.filtered(lambda m: - float_compare(m.qty_done, 0.0, precision_rounding=m.product_uom_id.rounding) > 0 - and not m.result_package_id - ) - total_weight = 0.0 - for ml in move_line_ids: - qty = ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id) - total_weight += qty * ml.product_id.weight - defaults['shipping_weight'] = total_weight - return defaults - picking_id = fields.Many2one('stock.picking', 'Picking') delivery_package_type_id = fields.Many2one('stock.package.type', 'Delivery Package Type', check_company=True) - shipping_weight = fields.Float('Shipping Weight') + shipping_weight = fields.Float('Shipping Weight', compute='_compute_shipping_weight', store=True, readonly=False) weight_uom_name = fields.Char(string='Weight unit of measure label', compute='_compute_weight_uom_name') company_id = fields.Many2one(related='picking_id.company_id') @@ -37,6 +21,20 @@ class ChooseDeliveryPackage(models.TransientModel): for package in self: package.weight_uom_name = weight_uom_id.name + @api.depends('delivery_package_type_id') + def _compute_shipping_weight(self): + for rec in self: + move_line_ids = rec.picking_id.move_line_ids.filtered(lambda m: + float_compare(m.qty_done, 0.0, precision_rounding=m.product_uom_id.rounding) > 0 + and not m.result_package_id + ) + # Add package weights to shipping weight, package base weight is defined in package.type + total_weight = rec.delivery_package_type_id.base_weight or 0.0 + for ml in move_line_ids: + qty = ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id) + total_weight += qty * ml.product_id.weight + rec.shipping_weight = total_weight + @api.onchange('delivery_package_type_id', 'shipping_weight') def _onchange_package_type_weight(self): if self.delivery_package_type_id.max_weight and self.shipping_weight > self.delivery_package_type_id.max_weight: diff --git a/addons/mrp/models/product.py b/addons/mrp/models/product.py index eeaf8deba20..48e6ac068b5 100644 --- a/addons/mrp/models/product.py +++ b/addons/mrp/models/product.py @@ -27,7 +27,7 @@ class ProductTemplate(models.Model): compute='_compute_bom_count', compute_sudo=False) used_in_bom_count = fields.Integer('# of BoM Where is Used', compute='_compute_used_in_bom_count', compute_sudo=False) - mrp_product_qty = fields.Float('Manufactured', + mrp_product_qty = fields.Float('Manufactured', digits='Product Unit of Measure', compute='_compute_mrp_product_qty', compute_sudo=False) produce_delay = fields.Float( 'Manufacturing Lead Time', default=0.0, @@ -97,7 +97,7 @@ class ProductProduct(models.Model): compute='_compute_bom_count', compute_sudo=False) used_in_bom_count = fields.Integer('# BoM Where Used', compute='_compute_used_in_bom_count', compute_sudo=False) - mrp_product_qty = fields.Float('Manufactured', + mrp_product_qty = fields.Float('Manufactured', digits='Product Unit of Measure', compute='_compute_mrp_product_qty', compute_sudo=False) is_kits = fields.Boolean(compute="_compute_is_kits", compute_sudo=False) diff --git a/addons/product/report/product_label_report.py b/addons/product/report/product_label_report.py index 35c9eb5b4e7..f2c0b3bcb61 100644 --- a/addons/product/report/product_label_report.py +++ b/addons/product/report/product_label_report.py @@ -17,9 +17,13 @@ def _prepare_data(env, data): raise UserError(_('Product model not defined, Please contact your administrator.')) total = 0 + qty_by_product_in = data.get('quantity_by_product') + # search for products all at once, ordered by name desc since popitem() used in xml to print the labels + # is LIFO, which results in ordering by product name in the report + products = Product.search([('id', 'in', [int(p) for p in qty_by_product_in.keys()])], order='name desc') quantity_by_product = defaultdict(list) - for p, q in data.get('quantity_by_product').items(): - product = Product.browse(int(p)) + for product in products: + q = qty_by_product_in[str(product.id)] quantity_by_product[product].append((product.barcode, q)) total += q if data.get('custom_barcodes'): diff --git a/addons/product/views/product_supplierinfo_views.xml b/addons/product/views/product_supplierinfo_views.xml index 9825d85f281..d5ee3ba59b8 100644 --- a/addons/product/views/product_supplierinfo_views.xml +++ b/addons/product/views/product_supplierinfo_views.xml @@ -47,6 +47,8 @@ + + @@ -122,7 +124,7 @@ ir.actions.act_window product.supplierinfo tree,form,kanban - {'visible_product_tmpl_id':False} + {'visible_product_tmpl_id': False, 'search_default_active_products': True}

No vendor pricelist found diff --git a/addons/purchase/models/product.py b/addons/purchase/models/product.py index 60409ec2a3c..dbc1431b4ea 100644 --- a/addons/purchase/models/product.py +++ b/addons/purchase/models/product.py @@ -15,7 +15,7 @@ class ProductTemplate(models.Model): 'account.account', string="Price Difference Account", company_dependent=True, help="This account is used in automated inventory valuation to "\ "record the price difference between a purchase order and its related vendor bill when validating this vendor bill.") - purchased_product_qty = fields.Float(compute='_compute_purchased_product_qty', string='Purchased') + purchased_product_qty = fields.Float(compute='_compute_purchased_product_qty', string='Purchased', digits='Product Unit of Measure') purchase_method = fields.Selection([ ('purchase', 'On ordered quantities'), ('receive', 'On received quantities'), @@ -52,7 +52,8 @@ class ProductProduct(models.Model): _name = 'product.product' _inherit = 'product.product' - purchased_product_qty = fields.Float(compute='_compute_purchased_product_qty', string='Purchased') + purchased_product_qty = fields.Float(compute='_compute_purchased_product_qty', string='Purchased', + digits='Product Unit of Measure') def _compute_purchased_product_qty(self): date_from = fields.Datetime.to_string(fields.Date.context_today(self) - relativedelta(years=1)) diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index ed511de6d56..f29e7913489 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -12,7 +12,7 @@ from odoo import api, fields, models, _ from odoo.osv import expression from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, format_amount, format_date, formatLang, get_lang, groupby from odoo.tools.float_utils import float_compare, float_is_zero, float_round -from odoo.exceptions import AccessError, UserError, ValidationError +from odoo.exceptions import UserError, ValidationError class PurchaseOrder(models.Model): @@ -492,7 +492,7 @@ class PurchaseOrder(models.Model): return { 'partner_id': partner.id, 'sequence': max(line.product_id.seller_ids.mapped('sequence')) + 1 if line.product_id.seller_ids else 1, - 'min_qty': 0.0, + 'min_qty': 1.0, 'price': price, 'currency_id': currency.id, 'delay': 0, @@ -528,10 +528,8 @@ class PurchaseOrder(models.Model): vals = { 'seller_ids': [(0, 0, supplierinfo)], } - try: - line.product_id.write(vals) - except AccessError: # no write access rights -> just ignore - break + # supplier info should be added regardless of the user access rights + line.product_id.sudo().write(vals) def action_create_invoice(self): """Create the invoice associated to the PO. diff --git a/addons/purchase/tests/test_purchase_invoice.py b/addons/purchase/tests/test_purchase_invoice.py index eabfdbdf23f..e3fbb8ccd39 100644 --- a/addons/purchase/tests/test_purchase_invoice.py +++ b/addons/purchase/tests/test_purchase_invoice.py @@ -327,71 +327,3 @@ class TestPurchaseToInvoice(AccountTestInvoicingCommon): aml = self.env['account.move.line'].search([('purchase_line_id', '=', purchase_order.order_line.id)]) self.assertRecordValues(aml, [{'analytic_account_id': analytic_account_manual.id}]) - - def test_multicompany_partner_bank(self): - """ Test that in a multiple company environment, the bank account of the invoice - is the one corresponding to the active company. """ - company_a = self.env.user.company_id - company_b = self.env['res.company'].create({ - 'name': 'Company B', - }) - self.env.ref('l10n_generic_coa.configurable_chart_template')._load(company_b) - - partner = self.env['res.partner'].create({ - 'name': 'AAAAA', - }) - partner_bank_a = self.env['res.partner.bank'].create({ - 'acc_number': "BE01 23456789 10", - 'partner_id': partner.id, - 'acc_type': 'bank' - }) - partner_bank_b = self.env['res.partner.bank'].create({ - 'acc_number': "BE10 98765432 10", - 'partner_id': partner.id, - 'acc_type': 'bank' - }) - partner.bank_ids = [partner_bank_a.id, partner_bank_b.id] - partner_bank_a.company_id = company_a - partner_bank_b.company_id = company_b - - PurchaseOrder = self.env['purchase.order'].with_context(tracking_disable=True) - po_a = PurchaseOrder.with_company(company_a).create({ - 'partner_id': partner.id, - 'company_id': company_a.id, - 'currency_id': company_a.currency_id.id, - 'date_order': '2019-01-01', - }) - po_b = PurchaseOrder.with_company(company_b).create({ - 'partner_id': partner.id, - 'company_id': company_b.id, - 'currency_id': company_b.currency_id.id, - 'date_order': '2019-01-01', - }) - - vals_pol = { - 'name': self.product_deliver.name, - 'product_id': self.product_deliver.id, - 'product_qty': 10.0, - 'product_uom': self.product_deliver.uom_id.id, - 'price_unit': self.product_deliver.list_price, - 'order_id': po_a.id, - 'taxes_id': False, - } - PurchaseOrderLine = self.env['purchase.order.line'].with_context(tracking_disable=True) - PurchaseOrderLine.create(vals_pol) - vals_pol['order_id'] = po_b.id - PurchaseOrderLine.create(vals_pol) - - po_a.button_confirm() - po_b.button_confirm() - - po_a.order_line.qty_received = po_b.order_line.qty_received = 10 - - po_a.with_company(company_a).action_create_invoice() - po_b.with_company(company_b).action_create_invoice() - - self.assertEqual(po_a.invoice_ids.company_id, company_a) - self.assertEqual(po_a.invoice_ids.partner_bank_id, partner_bank_a) - - self.assertEqual(po_b.invoice_ids.company_id, company_b) - self.assertEqual(po_b.invoice_ids.partner_bank_id, partner_bank_b) diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py index 454053441b1..09b54dd6214 100644 --- a/addons/purchase_stock/models/purchase.py +++ b/addons/purchase_stock/models/purchase.py @@ -18,7 +18,7 @@ class PurchaseOrder(models.Model): return self._get_picking_type(self.env.context.get('company_id') or self.env.company.id) incoterm_id = fields.Many2one('account.incoterms', 'Incoterm', states={'done': [('readonly', True)]}, help="International Commercial Terms are a series of predefined commercial terms used in international transactions.") - + incoterm_location = fields.Char(string='Incoterm Location', states={'done': [('readonly', True)]}) incoming_picking_count = fields.Integer("Incoming Shipment count", compute='_compute_incoming_picking_count') picking_ids = fields.Many2many('stock.picking', compute='_compute_picking_ids', string='Receptions', copy=False, store=True) diff --git a/addons/purchase_stock/report/purchase_report_templates.xml b/addons/purchase_stock/report/purchase_report_templates.xml index dabcaaf4854..d0cafb0d891 100644 --- a/addons/purchase_stock/report/purchase_report_templates.xml +++ b/addons/purchase_stock/report/purchase_report_templates.xml @@ -16,7 +16,8 @@

Incoterm: -

+

+

@@ -37,7 +38,8 @@
Incoterm: -

+

+

diff --git a/addons/purchase_stock/tests/test_purchase_order.py b/addons/purchase_stock/tests/test_purchase_order.py index cfa518d5d59..51003ef98b7 100644 --- a/addons/purchase_stock/tests/test_purchase_order.py +++ b/addons/purchase_stock/tests/test_purchase_order.py @@ -370,19 +370,14 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon): self.assertEqual(po.order_line.mapped('qty_received'), [4.0], 'Purchase: no conversion error on receipt in different uom"') def test_message_qty_already_received(self): - _product = self.env['product.product'].create({ - 'name': 'TempProduct', - 'type': 'consu', - 'company_id': self.env.user.company_id.id, - }) + self.env.user.write({'company_id': self.company_data['company'].id}) _purchase_order = self.env['purchase.order'].create({ - 'company_id': self.env.user.company_id.id, 'partner_id': self.partner_a.id, 'order_line': [ (0, 0, { - 'name': _product.name, - 'product_id': _product.id, + 'name': self.product_id_2.name, + 'product_id': self.product_id_2.id, 'product_qty': 25.0, 'price_unit': 250.0, })], diff --git a/addons/purchase_stock/views/purchase_views.xml b/addons/purchase_stock/views/purchase_views.xml index adefc43921f..63573286768 100644 --- a/addons/purchase_stock/views/purchase_views.xml +++ b/addons/purchase_stock/views/purchase_views.xml @@ -61,6 +61,7 @@ + diff --git a/addons/sale/models/product_product.py b/addons/sale/models/product_product.py index eb2c9083448..2d73da588a7 100644 --- a/addons/sale/models/product_product.py +++ b/addons/sale/models/product_product.py @@ -8,7 +8,7 @@ from odoo.tools.float_utils import float_round class ProductProduct(models.Model): _inherit = 'product.product' - sales_count = fields.Float(compute='_compute_sales_count', string='Sold') + sales_count = fields.Float(compute='_compute_sales_count', string='Sold', digits='Product Unit of Measure') def _compute_sales_count(self): r = {} diff --git a/addons/sale/models/product_template.py b/addons/sale/models/product_template.py index 0386eab81ba..7f4b49b6f5d 100644 --- a/addons/sale/models/product_template.py +++ b/addons/sale/models/product_template.py @@ -32,7 +32,7 @@ class ProductTemplate(models.Model): help="Expenses and vendor bills can be re-invoiced to a customer." "With this option, a validated expense can be re-invoice to a customer at its cost or sales price.") visible_expense_policy = fields.Boolean("Re-Invoice Policy visible", compute='_compute_visible_expense_policy') - sales_count = fields.Float(compute='_compute_sales_count', string='Sold') + sales_count = fields.Float(compute='_compute_sales_count', string='Sold', digits='Product Unit of Measure') visible_qty_configurator = fields.Boolean("Quantity visible in configurator", compute='_compute_visible_qty_configurator') invoice_policy = fields.Selection( [('order', 'Ordered quantities'), diff --git a/addons/sale_purchase/__init__.py b/addons/sale_purchase/__init__.py index dc5e6b693d1..3d0197513d5 100644 --- a/addons/sale_purchase/__init__.py +++ b/addons/sale_purchase/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import models +from . import wizards diff --git a/addons/sale_purchase/__manifest__.py b/addons/sale_purchase/__manifest__.py index 192ad7e4410..a776c7e0993 100644 --- a/addons/sale_purchase/__manifest__.py +++ b/addons/sale_purchase/__manifest__.py @@ -20,6 +20,7 @@ by external providers and will automatically generate purchase orders directed t 'views/product_views.xml', 'views/sale_order_views.xml', 'views/purchase_order_views.xml', + 'wizards/sale_order_cancel_views.xml', ], 'auto_install': True, 'license': 'LGPL-3', diff --git a/addons/sale_purchase/wizards/__init__.py b/addons/sale_purchase/wizards/__init__.py new file mode 100644 index 00000000000..a6dec7feede --- /dev/null +++ b/addons/sale_purchase/wizards/__init__.py @@ -0,0 +1 @@ +from . import sale_order_cancel diff --git a/addons/sale_purchase/wizards/sale_order_cancel.py b/addons/sale_purchase/wizards/sale_order_cancel.py new file mode 100644 index 00000000000..db9220f630b --- /dev/null +++ b/addons/sale_purchase/wizards/sale_order_cancel.py @@ -0,0 +1,17 @@ +from odoo import api, fields, models + + +class SaleOrderCancel(models.TransientModel): + _inherit = "sale.order.cancel" + + display_purchase_orders_alert = fields.Boolean( + string="Purchase Order Alert", + compute='_compute_display_purchase_orders_alert' + ) + + @api.depends('order_id') + def _compute_display_purchase_orders_alert(self): + for wizard in self: + wizard.display_purchase_orders_alert = bool( + wizard.order_id.purchase_order_count + ) diff --git a/addons/sale_purchase/wizards/sale_order_cancel_views.xml b/addons/sale_purchase/wizards/sale_order_cancel_views.xml new file mode 100644 index 00000000000..081daba1884 --- /dev/null +++ b/addons/sale_purchase/wizards/sale_order_cancel_views.xml @@ -0,0 +1,18 @@ + + + sale.order.cancel.form.inherit + sale.order.cancel + + + + + + + + There are active purchase orders linked to this sale order that are not cancelled automatically!
+
+
+
+
+
diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py index 5feaa25a4ad..3a5016901a8 100644 --- a/addons/sale_stock/models/sale_order.py +++ b/addons/sale_stock/models/sale_order.py @@ -16,6 +16,7 @@ class SaleOrder(models.Model): incoterm = fields.Many2one( 'account.incoterms', 'Incoterm', help="International Commercial Terms are a series of predefined commercial terms used in international transactions.") + incoterm_location = fields.Char(string='Incoterm Location') picking_policy = fields.Selection([ ('direct', 'As soon as possible'), ('one', 'When all products are ready')], diff --git a/addons/sale_stock/report/sale_order_report_templates.xml b/addons/sale_stock/report/sale_order_report_templates.xml index 8eab11e22cc..5278aa93da8 100644 --- a/addons/sale_stock/report/sale_order_report_templates.xml +++ b/addons/sale_stock/report/sale_order_report_templates.xml @@ -4,7 +4,8 @@
Incoterm: -

+

+

diff --git a/addons/sale_stock/views/sale_order_views.xml b/addons/sale_stock/views/sale_order_views.xml index 9dff9c800db..89376c720df 100644 --- a/addons/sale_stock/views/sale_order_views.xml +++ b/addons/sale_stock/views/sale_order_views.xml @@ -22,6 +22,7 @@ + diff --git a/addons/stock/models/stock_package_type.py b/addons/stock/models/stock_package_type.py index da6ff321a46..0585c972e4a 100644 --- a/addons/stock/models/stock_package_type.py +++ b/addons/stock/models/stock_package_type.py @@ -19,6 +19,7 @@ class PackageType(models.Model): height = fields.Integer('Height', help="Packaging Height") width = fields.Integer('Width', help="Packaging Width") packaging_length = fields.Integer('Length', help="Packaging Length") + base_weight = fields.Float(string='Weight', help='Weight of the package type') max_weight = fields.Float('Max Weight', help='Maximum weight shippable in this packaging') barcode = fields.Char('Barcode', copy=False) weight_uom_name = fields.Char(string='Weight unit of measure label', compute='_compute_weight_uom_name', default=_get_default_weight_uom) diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index 5d0b2f29d65..6731c0fe05d 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -318,13 +318,13 @@ class Picking(models.Model): location_id = fields.Many2one( 'stock.location', "Source Location", default=lambda self: self.env['stock.picking.type'].browse(self._context.get('default_picking_type_id')).default_location_src_id, - check_company=True, readonly=True, required=True, - states={'draft': [('readonly', False)]}) + check_company=True, required=True, + states={'done': [('readonly', True)]}) location_dest_id = fields.Many2one( 'stock.location', "Destination Location", default=lambda self: self.env['stock.picking.type'].browse(self._context.get('default_picking_type_id')).default_location_dest_id, - check_company=True, readonly=True, required=True, - states={'draft': [('readonly', False)]}) + check_company=True, required=True, + states={'done': [('readonly', True)]}) move_ids = fields.One2many('stock.move', 'picking_id', string="Stock Moves", copy=True) move_ids_without_package = fields.One2many( 'stock.move', 'picking_id', string="Stock moves not in package", compute='_compute_move_without_package', diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index f389f25ae75..2a62632b91e 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -104,7 +104,8 @@ class StockQuant(models.Model): 'Counted Quantity', digits='Product Unit of Measure', help="The product's counted quantity.") inventory_quantity_auto_apply = fields.Float( - 'Inventoried Quantity', compute='_compute_inventory_quantity_auto_apply', + 'Inventoried Quantity', digits='Product Unit of Measure', + compute='_compute_inventory_quantity_auto_apply', inverse='_set_inventory_quantity', groups='stock.group_stock_manager' ) inventory_diff_quantity = fields.Float( @@ -303,6 +304,13 @@ class StockQuant(models.Model): group['inventory_quantity_auto_apply'] = group['quantity'] return result + @api.model + def get_import_templates(self): + return [{ + 'label': _('Import Template for Inventory Adjustments'), + 'template': '/stock/static/xlsx/stock_quant.xlsx' + }] + def write(self, vals): """ Override to handle the "inventory mode" and create the inventory move. """ allowed_fields = self._get_inventory_fields_write() @@ -877,6 +885,10 @@ class StockQuant(models.Model): name = _('Product Quantity Confirmed') else: name = _('Product Quantity Updated') + + if self.inventory_date: + name += _(' [Scheduled on %s]', self.inventory_date) + return { 'name': self.env.context.get('inventory_name') or name, 'product_id': self.product_id.id, diff --git a/addons/stock/static/xlsx/stock_quant.xlsx b/addons/stock/static/xlsx/stock_quant.xlsx new file mode 100644 index 00000000000..ead9d452f5c Binary files /dev/null and b/addons/stock/static/xlsx/stock_quant.xlsx differ diff --git a/addons/stock/views/product_views.xml b/addons/stock/views/product_views.xml index f1557646819..5ba0dddc3d5 100644 --- a/addons/stock/views/product_views.xml +++ b/addons/stock/views/product_views.xml @@ -220,7 +220,7 @@ name="%(action_product_replenish)d" context="{'default_product_id': id}" groups="stock.group_stock_user" - attrs="{'invisible': [('type', '!=', 'product')]}"/> + attrs="{'invisible': [('type', 'not in', ['consu', 'product'])]}"/>