diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index c41db44bf0..3ba19d482f 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -143,7 +143,7 @@ def update_taxes_from_templates(cr, chart_template_xmlid): outdated_taxes = [] for company in companies: template_to_tax = _get_template_to_tax_xmlid_mapping(company) - templates = env['account.tax.template'].search([("chart_template_id", "=", chart_template_id)]) + templates = env['account.tax.template'].with_context(active_test=False).search([("chart_template_id", "=", chart_template_id)]) for template in templates: tax = env["account.tax"].browse(template_to_tax.get(template.id)) if not tax or not _is_tax_and_template_same(template, tax): diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py index 3e700a192e..5d43efe551 100644 --- a/addons/l10n_fr_fec/wizard/account_fr_fec.py +++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py @@ -231,7 +231,8 @@ class AccountFrFec(models.TransientModel): and (unaffected_earnings_results[11] != '0,00' or unaffected_earnings_results[12] != '0,00')): #search an unaffected earnings account - unaffected_earnings_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_unaffected_earnings').id)], limit=1) + unaffected_earnings_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_unaffected_earnings').id), + ('company_id', '=', company.id)], limit=1) if unaffected_earnings_account: unaffected_earnings_results[4] = unaffected_earnings_account.code unaffected_earnings_results[5] = unaffected_earnings_account.name diff --git a/addons/l10n_sg/__manifest__.py b/addons/l10n_sg/__manifest__.py index 02e9b566e0..bb6df94903 100644 --- a/addons/l10n_sg/__manifest__.py +++ b/addons/l10n_sg/__manifest__.py @@ -6,7 +6,7 @@ { 'name': 'Singapore - Accounting', 'author': 'Tech Receptives', - 'version': '2.0', + 'version': '2.1', 'category': 'Accounting/Localizations/Account Charts', 'description': """ Singapore accounting chart and localization. diff --git a/addons/l10n_sg/data/account_data.xml b/addons/l10n_sg/data/account_data.xml index b7f9af41d1..99b2e54688 100644 --- a/addons/l10n_sg/data/account_data.xml +++ b/addons/l10n_sg/data/account_data.xml @@ -9,6 +9,9 @@ TAX 7% + + TAX 8% + diff --git a/addons/l10n_sg/data/account_tax_data.xml b/addons/l10n_sg/data/account_tax_data.xml index 649eebe054..ae8b80a3e0 100644 --- a/addons/l10n_sg/data/account_tax_data.xml +++ b/addons/l10n_sg/data/account_tax_data.xml @@ -72,6 +72,43 @@ ]"/> + + Sales Tax 8% DS + 8% DS + + 8 + percent + sale + + + + + + Sales Tax 0% ESN33 0% ESN33 @@ -206,6 +243,43 @@ ]"/> + + Sales Tax 8% SR + 8% SR + + 8 + percent + sale + + + + + + Sales Tax SRCA-S SRCA-S @@ -276,6 +350,43 @@ ]"/> + + Sales Tax 8% SRCA-C + 8% SRCA-C + + 8 + percent + sale + + + + + + @@ -315,6 +426,43 @@ ]"/> + + Purchase Tax 8% TX-N33 + 8% TX-N33 + + 8 + percent + purchase + + + + + + Purchase Tax 7% TX-E33 7% TX-E33 @@ -352,6 +500,43 @@ ]"/> + + Purchase Tax 8% TX-E33 + 8% TX-E33 + + 8 + percent + purchase + + + + + + Purchase Tax 7% BL 7% BL @@ -385,6 +570,39 @@ ]"/> + + Purchase Tax 8% BL + 8% BL + + 8 + percent + purchase + + + + + + Purchase Tax 7% IM 7% IM @@ -422,6 +640,43 @@ ]"/> + + Purchase Tax 8% IM + 8% IM + + 8 + percent + purchase + + + + + + Purchase Tax 7% TX-RE 7% TX-RE @@ -459,6 +714,43 @@ ]"/> + + Purchase Tax 8% TX-RE + 8% TX-RE + + 8 + percent + purchase + + + + + + Purchase Tax 7% IGDS 7% IGDS @@ -496,6 +788,43 @@ ]"/> + + Purchase Tax 8% IGDS + 8% IGDS + + 8 + percent + purchase + + + + + + Purchase Tax 0% NR 0% NR @@ -692,6 +1021,43 @@ ]"/> + + Purchase Tax 8% TX8 + 8% TX8 + + 8 + percent + purchase + + + + + + Purchase Tax 7% TXCA 7% TXCA @@ -728,5 +1094,42 @@ }), ]"/> + + + Purchase Tax 8% TXCA + 8% TXCA + + 8 + percent + purchase + + + + + diff --git a/addons/l10n_sg/data/l10n_sg_chart_data.xml b/addons/l10n_sg/data/l10n_sg_chart_data.xml index 3e36763777..eed72f4a6b 100644 --- a/addons/l10n_sg/data/l10n_sg_chart_data.xml +++ b/addons/l10n_sg/data/l10n_sg_chart_data.xml @@ -316,7 +316,7 @@ - Purchase Tax Account 7% TX-N33 + Purchase Tax Account 8% TX-N33 101260 @@ -324,7 +324,7 @@ - Purchase Tax Account 7% TX-E33 + Purchase Tax Account 8% TX-E33 101270 @@ -332,7 +332,7 @@ - Purchase Tax Account 7% BL + Purchase Tax Account 8% BL 101280 @@ -340,7 +340,7 @@ - Purchase Tax Account 7% IM + Purchase Tax Account 8% IM 101290 @@ -348,7 +348,7 @@ - Purchase Tax Account 7% TX-RE + Purchase Tax Account 8% TX-RE 101300 @@ -356,7 +356,7 @@ - Purchase Tax Account 7% IGDS + Purchase Tax Account 8% IGDS 101310 @@ -404,7 +404,7 @@ - Purchase Tax Account 7% TX7 + Purchase Tax Account 8% TX8 101370 @@ -412,7 +412,7 @@ - Purchase Tax Account 7% TXCA + Purchase Tax Account 8% TXCA 101380 @@ -708,7 +708,7 @@ - Sales Tax Account 7% DS + Sales Tax Account 8% DS 201180 @@ -740,7 +740,7 @@ - Sales Tax Account 7% SR + Sales Tax Account 8% SR 201220 @@ -756,7 +756,7 @@ - Sales Tax Account 7% SRCA-C + Sales Tax Account 8% SRCA-C 201240 diff --git a/addons/l10n_sg/migrations/2.1/post-migrate_update_tax.py b/addons/l10n_sg/migrations/2.1/post-migrate_update_tax.py new file mode 100644 index 0000000000..6e9b576d51 --- /dev/null +++ b/addons/l10n_sg/migrations/2.1/post-migrate_update_tax.py @@ -0,0 +1,6 @@ +# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details. +from flectra.addons.account.models.chart_template import update_taxes_from_templates + + +def migrate(cr, version): + update_taxes_from_templates(cr, 'l10n_sg.sg_chart_template') diff --git a/addons/l10n_za/data/account.account.template.csv b/addons/l10n_za/data/account.account.template.csv index 17fbef2b79..25fbcc5b77 100644 --- a/addons/l10n_za/data/account.account.template.csv +++ b/addons/l10n_za/data/account.account.template.csv @@ -109,4 +109,4 @@ id,code,name,user_type_id/id,reconcile,chart_template_id/id 620040,620040,Land and buildings Depreciation,account.data_account_type_depreciation,False,default_chart_template 620050,620050,Motor vehicles Depreciation,account.data_account_type_depreciation,False,default_chart_template 620060,620060,Office equipment (inc computer equipment) Depreciation,account.data_account_type_depreciation,False,default_chart_template -620070,620070,Plant and machinery Depreciation,account.data_account_type_depreciation,False,default_chart_template \ No newline at end of file +620070,620070,Plant and machinery Depreciation,account.data_account_type_depreciation,False,default_chart_template \ No newline at end of file diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 60a0537b72..42ce2424da 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -238,6 +238,7 @@ class StockMove(models.Model): def _action_confirm(self, merge=True, merge_into=False): moves = self.action_explode() + merge_into = merge_into and merge_into.action_explode() # we go further with the list of ids potentially changed by action_explode return super(StockMove, moves)._action_confirm(merge=merge, merge_into=merge_into) @@ -257,20 +258,23 @@ class StockMove(models.Model): if not bom: moves_ids_to_return.add(move.id) continue - if move.picking_id.immediate_transfer: + if move.picking_id.immediate_transfer or float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding): factor = move.product_uom._compute_quantity(move.quantity_done, bom.product_uom_id) / bom.product_qty else: factor = move.product_uom._compute_quantity(move.product_uom_qty, bom.product_uom_id) / bom.product_qty boms, lines = bom.sudo().explode(move.product_id, factor, picking_type=bom.picking_type_id) for bom_line, line_data in lines: - if move.picking_id.immediate_transfer: + if move.picking_id.immediate_transfer or float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding): phantom_moves_vals_list += move._generate_move_phantom(bom_line, 0, line_data['qty']) else: phantom_moves_vals_list += move._generate_move_phantom(bom_line, line_data['qty'], 0) # delete the move with original product which is not relevant anymore moves_ids_to_unlink.add(move.id) - self.env['stock.move'].browse(moves_ids_to_unlink).sudo().unlink() + move_to_unlink = self.env['stock.move'].browse(moves_ids_to_unlink).sudo() + move_to_unlink.quantity_done = 0 + move_to_unlink._action_cancel() + move_to_unlink.unlink() if phantom_moves_vals_list: phantom_moves = self.env['stock.move'].create(phantom_moves_vals_list) phantom_moves._adjust_procure_method() @@ -319,7 +323,8 @@ class StockMove(models.Model): if bom_line.product_id.type in ['product', 'consu']: vals = self.copy_data(default=self._prepare_phantom_move_values(bom_line, product_qty, quantity_done)) if self.state == 'assigned': - vals['state'] = 'assigned' + for v in vals: + v['state'] = 'assigned' return vals @api.model diff --git a/addons/mrp/tests/test_stock.py b/addons/mrp/tests/test_stock.py index 3486e17368..1bcb41a415 100644 --- a/addons/mrp/tests/test_stock.py +++ b/addons/mrp/tests/test_stock.py @@ -348,3 +348,46 @@ class TestKitPicking(common.TestMrpCommon): self.assertEqual(len(picking.move_lines), 7) for move_line in picking.move_lines: self.assertEqual(move_line.product_qty, self.expected_quantities[move_line.product_id]) + + def test_add_sml_with_kit_to_confirmed_picking(self): + warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + customer_location = self.env.ref('stock.stock_location_customers') + stock_location = warehouse.lot_stock_id + in_type = warehouse.in_type_id + + self.bom_4.type = 'phantom' + kit = self.bom_4.product_id + compo = self.bom_4.bom_line_ids.product_id + product = self.env['product.product'].create({'name': 'Super Product', 'type': 'product'}) + + receipt = self.env['stock.picking'].create({ + 'picking_type_id': in_type.id, + 'location_id': customer_location.id, + 'location_dest_id': stock_location.id, + 'move_lines': [(0, 0, { + 'name': product.name, + 'product_id': product.id, + 'product_uom_qty': 1, + 'product_uom': product.uom_id.id, + 'location_id': customer_location.id, + 'location_dest_id': stock_location.id, + })] + }) + receipt.action_confirm() + + receipt.move_line_ids.qty_done = 1 + receipt.move_line_ids = [(0, 0, { + 'product_id': kit.id, + 'qty_done': 1, + 'product_uom_id': kit.uom_id.id, + 'location_id': customer_location.id, + 'location_dest_id': stock_location.id, + })] + + receipt.button_validate() + + self.assertEqual(receipt.state, 'done') + self.assertRecordValues(receipt.move_lines, [ + {'product_id': product.id, 'quantity_done': 1, 'state': 'done'}, + {'product_id': compo.id, 'quantity_done': 1, 'state': 'done'}, + ]) diff --git a/addons/mrp_account/tests/test_valuation_layers.py b/addons/mrp_account/tests/test_valuation_layers.py index 4c6c7f71c5..03b3500bc7 100644 --- a/addons/mrp_account/tests/test_valuation_layers.py +++ b/addons/mrp_account/tests/test_valuation_layers.py @@ -276,3 +276,35 @@ class TestMrpValuationStandard(TestMrpValuationCommon): self.assertEqual(self.product1.quantity_svl, 2) self._make_out_move(self.product1, 1) self.assertEqual(self.product1.value_svl, 15) + + def test_validate_draft_kit(self): + """ + Create a draft receipt, add a kit to its move lines and directly + validate it. From client side, such a behaviour is possible with + the Barcode app. + """ + self.component.product_tmpl_id.categ_id.property_cost_method = 'average' + self.product1.type = 'consu' + self.bom.type = 'phantom' + self.component.standard_price = 1424 + + receipt = self.env['stock.picking'].create({ + 'location_id': self.customer_location.id, + 'location_dest_id': self.stock_location.id, + 'picking_type_id': self.picking_type_in.id, + 'move_line_ids': [(0, 0, { + 'product_id': self.product1.id, + 'qty_done': 1, + 'product_uom_id': self.product1.uom_id.id, + 'location_id': self.customer_location.id, + 'location_dest_id': self.stock_location.id, + })] + }) + receipt.button_validate() + + self.assertEqual(receipt.state, 'done') + self.assertRecordValues(receipt.move_lines, [ + {'product_id': self.component.id, 'quantity_done': 1, 'state': 'done'}, + ]) + self.assertEqual(self.component.qty_available, 1) + self.assertEqual(self.component.value_svl, 1424) diff --git a/addons/snailmail/static/src/scss/snailmail_external_layout_asset.scss b/addons/snailmail/static/src/scss/snailmail_external_layout_asset.scss index 2bff2d495c..8af3492646 100644 --- a/addons/snailmail/static/src/scss/snailmail_external_layout_asset.scss +++ b/addons/snailmail/static/src/scss/snailmail_external_layout_asset.scss @@ -1,14 +1,12 @@ /* Modifications for the Light and Boxed document layouts */ .article.o_report_layout_standard.o_company_1_layout, .article.o_report_layout_boxed.o_company_1_layout { > .pt-5 { + padding-top: 0 !important; > .address.row { - width: 100% !important; - height: 62mm !important; + width: 117% !important; + height: 68mm !important; + margin-top: -4mm !important; line-height: 1.1em; - margin-top: -13mm; - > div[name="address"] { - padding-top: 5mm; - } } } } @@ -16,39 +14,36 @@ /* Modifications for Clean document layouts */ .article.o_report_layout_clean.o_company_1_layout { > .address.row { - width: 100% !important; - height: 62mm !important; + width: 117% !important; + height: 68mm !important; + margin-top: -4mm !important; line-height: 1.1em; - > div[name="address"] { - padding-top: 15mm !important; - } } } /* Modification for background document layouts */ .o_company_1_layout.article.o_report_layout_background { > .address.row { - background-color: #ffffff !important; - width: 103% !important; - height: 62mm !important; + width: 117% !important; + height: 68mm !important; + margin-top: -4mm !important; line-height: 1.1em; - > div[name="address"] { - padding-top: 8mm !important; - } } } div .address.row > div[name="address"] { position: relative !important; - width: 103mm !important; + margin-left: 48.5% !important; background-color: #ffffff; - > address { - width: 100% !important; - position: absolute !important; - bottom: 0 !important; - padding-left: 3mm; - padding-top: 3mm; - height: 33mm; - max-height: 33mm; + > address, div { + > address { + width: 100% !important; + position: absolute !important; + bottom: 0 !important; + padding-left: 3mm; + padding-top: 3mm; + height: 33mm; + max-height: 33mm; + } } } diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 7412297498..064dba504e 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -1515,8 +1515,8 @@ class StockMove(models.Model): return extra_move | self def _action_done(self, cancel_backorder=False): - self.filtered(lambda move: move.state == 'draft')._action_confirm() # MRP allows scrapping draft moves - moves = self.exists().filtered(lambda x: x.state not in ('done', 'cancel')) + moves = self.filtered(lambda move: move.state == 'draft')._action_confirm() # MRP allows scrapping draft moves + moves = (self | moves).exists().filtered(lambda x: x.state not in ('done', 'cancel')) moves_todo = self.env['stock.move'] # Cancel moves where necessary ; we should do it before creating the extra moves because diff --git a/addons/stock_account/models/stock_move.py b/addons/stock_account/models/stock_move.py index b4d8c9b77e..d223927d93 100644 --- a/addons/stock_account/models/stock_move.py +++ b/addons/stock_account/models/stock_move.py @@ -256,6 +256,9 @@ class StockMove(models.Model): res = super(StockMove, self)._action_done(cancel_backorder=cancel_backorder) + # '_action_done' might have deleted some exploded stock moves + valued_moves = {value_type: moves.exists() for value_type, moves in valued_moves.items()} + # '_action_done' might have created an extra move to be valued for move in res - self: for valued_type in self._get_valued_types(): diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index 052afa111d..a0a12bf612 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -1585,17 +1585,6 @@ options.registry.sizing_x = options.registry.sizing.extend({ }); options.registry.layout_column = options.Class.extend({ - /** - * @override - */ - start: function () { - // Needs to be done manually for now because _computeWidgetVisibility - // doesn't go through this option for buttons inside of a select. - // TODO: improve this. - this.$el.find('we-button[data-name="zero_cols_opt"]') - .toggleClass('d-none', !this.$target.is('.s_allow_columns')); - return this._super(...arguments); - }, //-------------------------------------------------------------------------- // Options @@ -1640,6 +1629,20 @@ options.registry.layout_column = options.Class.extend({ } return this._super(...arguments); }, + /** + * @override + */ + _computeWidgetVisibility(widgetName, params) { + if (widgetName === 'zero_cols_opt') { + // Note: "s_allow_columns" indicates containers which may have + // bare content (without columns) and are allowed to have columns. + // By extension, we only show the "None" option on elements that + // were marked as such as they were allowed to have bare content in + // the first place. + return this.$target.is('.s_allow_columns'); + } + return this._super(...arguments); + }, /** * Adds new columns which are clones of the last column or removes the * last x columns.