# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from psycopg2.sql import SQL, Literal, Identifier from itertools import zip_longest from odoo import api, models, _ from odoo.tools import get_lang, OrderedSet from odoo.tools.float_utils import float_repr, float_round from odoo.tools.misc import format_date _merchandise_export_code = { 'BE': '29', 'FR': '21', 'NL': '7', } _merchandise_import_code = { 'BE': '19', 'FR': '11', 'NL': '6', } _unknown_country_code = { 'BE': 'QU', 'NL': 'QV', } _qn_unknown_individual_vat_country_codes = ('FI', 'SE', 'SK', 'DE', 'AT') errors = ('expired_trans', 'premature_trans', 'missing_trans', 'expired_comm', 'premature_comm', 'missing_comm', 'missing_unit', 'missing_weight') REPORT_LINE_ID_KEYS = ['type', 'transaction_code', 'commodity_code', 'intrastat_product_origin_country_code', 'partner_vat', 'country_code', 'incoterm_code', 'transport_code', 'invoice_currency_id', 'region_code'] class IntrastatReportCustomHandler(models.AbstractModel): _name = 'account.intrastat.report.handler' _inherit = 'account.report.custom.handler' _description = 'Intrastat Report Custom Handler' def _get_custom_display_config(self): return { 'components': { 'AccountReportFilters': 'account_intrastat.IntrastatReportFilters', }, } def _dynamic_lines_generator(self, report, options, all_column_groups_expression_totals=None, warnings=None): if options.get('intrastat_grouped'): # dict of the form {move_id: {column_group_key: {expression_label: value}}} move_info_dict = {} # dict of the form {column_group_key: {expression_label: value}} total_values_dict = {} # Build query query_list = [] full_query_params = [] for column_group_key, column_group_options in report._split_options_per_column_group(options).items(): query, params = self._build_query_group(column_group_options, column_group_key) query_list.append(query) full_query_params += params full_query = SQL(" UNION ALL ").join(query_list) self._cr.execute(full_query, full_query_params) results = self._cr.dictfetchall() # Fill dictionaries for res in results: line_id = self._get_report_line_id(report, res) column_group_key = res['column_group_key'] current_move_info = move_info_dict.setdefault(line_id, {'name': self._get_move_info_name(res)}) current_move_info[column_group_key] = res # We add the value to the total (for total line) total_values_dict.setdefault(column_group_key, {'value': 0}) total_values_dict[column_group_key]['value'] += res['value'] # Create lines lines = [] for move_id, move_info in move_info_dict.items(): line = self._create_report_line(options, move_info, move_id, ['value'], warnings=warnings) lines.append((0, line)) # Create total line if only one type of invoice is selected if options.get('intrastat_total_line'): total_line = self._create_report_total_line(options, total_values_dict) lines.append((0, total_line)) else: lines = [(0, line) for line in self._get_lines(options)] return lines def _get_move_info_name(self, move_info): # Some values are necessary for the id generation but not in the name, we removed region_code because a condition handles it after keys = OrderedSet(REPORT_LINE_ID_KEYS) - OrderedSet(['incoterm_code', 'transport_code', 'invoice_currency_id', 'region_code']) name = " - ".join(str(move_info[key]) for key in keys) if self._show_region_code(): name += f" - {move_info['region_code']}" return name def _get_report_line_id(self, report, move_info): move_values = [] for key in REPORT_LINE_ID_KEYS: if key == 'intrastat_product_origin_country_code' and move_info.get(key) == 'XU': # Special case for the United Kingdom where the code is XU instead of GB, # to avoid issue when we fetch children lines, we set to GB in the line id. move_values.append('GB') else: move_values.append(str(move_info.get(key))) return report._get_generic_line_id('account.move', None, markup=",".join(move_values)) def _custom_options_initializer(self, report, options, previous_options=None): super()._custom_options_initializer(report, options, previous_options=previous_options) previous_options = previous_options or {} # Filter only partners with VAT options['intrastat_with_vat'] = previous_options.get('intrastat_with_vat', False) options['intrastat_grouped'] = previous_options.get('intrastat_grouped', False) # Filter types of invoices default_type = [ {'name': _('Arrival'), 'selected': False, 'id': 'arrival'}, {'name': _('Dispatch'), 'selected': False, 'id': 'dispatch'}, ] options['intrastat_type'] = previous_options.get('intrastat_type', default_type) options['country_format'] = previous_options.get('country_format') options['commodity_flow'] = previous_options.get('commodity_flow') # Filter the domain based on the types of invoice selected include_arrivals, include_dispatches = self._determine_inclusion(options) invoice_types = [] if include_arrivals: invoice_types += ['in_invoice', 'out_refund'] if include_dispatches: invoice_types += ['out_invoice', 'in_refund'] # When only one type is selected, we can display a total line options['intrastat_total_line'] = include_arrivals != include_dispatches options.setdefault('forced_domain', []).append(('move_id.move_type', 'in', invoice_types)) # Filter report type (extended form) options['intrastat_extended'] = previous_options.get('intrastat_extended', True) # 2 columns are conditional and should only appear when rendering the extended intrastat report # Some countries don't use the region code column; we hide it for them. excluded_columns = set() if not options['intrastat_extended']: excluded_columns |= {'transport_code', 'incoterm_code'} if not self._show_region_code(): excluded_columns.add('region_code') new_columns = [] for col in options['columns']: if col['expression_label'] not in excluded_columns: new_columns.append(col) # Replace country names by codes if necessary (for file exports) if options.get('country_format') == 'code': if col['expression_label'] == 'country_name': col['expression_label'] = 'country_code' elif col['expression_label'] == 'intrastat_product_origin_country_name': col['expression_label'] = 'intrastat_product_origin_country_code' options['columns'] = new_columns # Only pick Sale/Purchase journals (+ divider) report._init_options_journals(options, previous_options=previous_options, additional_journals_domain=[('type', 'in', ('sale', 'purchase'))]) # When printing the report to xlsx, we want to use country codes instead of names xlsx_button_option = next(button_opt for button_opt in options['buttons'] if button_opt.get('action_param') == 'export_to_xlsx') xlsx_button_option['action_param'] = 'export_to_xlsx' options['ignore_totals_below_sections'] = True @api.model def _determine_inclusion(self, options): include_arrivals = options['intrastat_type'][0]['selected'] include_dispatches = options['intrastat_type'][1]['selected'] if not include_arrivals and not include_dispatches: include_arrivals = include_dispatches = True return include_arrivals, include_dispatches #################################################### # OVERRIDES #################################################### def _show_region_code(self): """Return a bool indicating if the region code is to be displayed for the country concerned in this localisation.""" # TO OVERRIDE return True #################################################### # OPTIONS: INIT #################################################### def export_to_xlsx(self, options, response=None): # We need to regenerate the options to make sure we hide the country name columns as expected. report = self.env['account.report'].browse(options['report_id']) new_options = report.get_options(previous_options={**options, 'country_format': 'code', 'commodity_flow': 'code'}) return report.export_to_xlsx(new_options, response=response) #################################################### # REPORT LINES: CORE #################################################### @api.model def _create_report_line(self, options, line_vals, line_id, number_values, warnings=None): """ Create a standard (non-total) line for the report :param options: report options :param line_vals: values necessary for the line :param line_id: id of the line :param number_values: list of expression labels that need to have the 'number' class """ report = self.env['account.report'].browse(options['report_id']) columns = [] uom_precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') for column in options['columns']: expression_label = column['expression_label'] value = line_vals.get(column['column_group_key'], {}).get(expression_label, None) if value: if options.get('commodity_flow') != 'code' and column['expression_label'] == 'system': value = f"{value} ({line_vals.get(column['column_group_key'], {}).get('type', False)})" elif column['expression_label'] == 'supplementary_units': value = float_repr(float_round(value, precision_digits=uom_precision), precision_digits=uom_precision) columns.append(report._build_column_dict(value, column, options=options)) if warnings is not None: for column_group in options['column_groups']: for warning_code in errors: if line_vals.get(column_group) and any(line_vals[column_group].get(warning_code)): warning_params = warnings.setdefault( f'account_intrastat.intrastat_warning_{warning_code}', {'ids': [], 'alert_type': 'warning'} ) warning_params['ids'].extend(aml_id for aml_id in line_vals[column_group][warning_code] if aml_id is not None) unfold_all = self._context.get('print_mode') or options.get('unfold_all') return { 'id': line_id, 'name': line_vals['name'], 'columns': columns, 'unfoldable': True, 'unfolded': unfold_all or line_id in options.get('unfolded_lines'), 'expand_function': '_report_expand_unfoldable_line_intrastat_line', 'level': 1, 'class': 'account_intrastat_line_name', } def _report_expand_unfoldable_line_intrastat_line(self, line_dict_id, groupby, options, progress, offset, unfold_all_batch_data=None): options['account_report'] = self.env['account.report'].browse(options['report_id']) lines = self._get_lines(options, line_dict_id) return { 'lines': lines, 'offset_increment': len(lines), 'has_more': self._has_more_lines(options['account_report'], len(lines)), } def _get_lines(self, options, parent_line=None): """ This functions gets every line (account.move.line) that matches the selected options. """ report = self.env['account.report'].browse(options['report_id']) expanded_line_options = None if parent_line: expanded_line_options = self._get_markup_info_from_intrastat_id(parent_line) queries = [] full_query_params = [] for column_group_key, column_group_options in report._split_options_per_column_group(options).items(): query, params, = self._prepare_query(column_group_options, column_group_key, expanded_line_options) queries.append(query) full_query_params += params full_query = SQL(" UNION ALL ").join(queries) self._cr.execute(full_query, full_query_params) raw_intrastat_lines = self._cr.dictfetchall() raw_intrastat_lines = self._fill_missing_values(raw_intrastat_lines) lines = [] for raw_intrastat_line in raw_intrastat_lines: if self._has_more_lines(report, len(lines)): # Enough elements loaded. Only the one due to the +1 in the limit passed when computing aml_results is left. # This element won't generate a line now, but we use it to know that we'll need to add a load_more line. break lines.append(self._get_aml_line(report, parent_line, options, raw_intrastat_line)) return lines def _has_more_lines(self, report, treated_results_count): limit_to_load = report.load_more_limit + 1 if report.load_more_limit and not self._context.get('print_mode') else None return limit_to_load and treated_results_count == report.load_more_limit def _get_aml_line(self, report, parent_line_id, options, aml_data): line_columns = [] for column in options['columns']: col_expr_label = column['expression_label'] col_value = aml_data.get(col_expr_label) if col_expr_label == 'system' and options.get('commodity_flow') != 'code': col_value = f"{col_value} ({aml_data['type']})" # if several columns with same name but different group key (i.e. with period comparison), # then ensure that None is set as value for the non-corresponding columns. col_value = col_value if column.get('column_group_key') == aml_data.get('column_group_key') else None new_column = report._build_column_dict(col_value, column, options=options) line_columns.append(new_column) return { 'id': report._get_generic_line_id('account.move.line', aml_data['id'], parent_line_id=parent_line_id), 'caret_options': 'account.move.line', 'parent_id': parent_line_id, 'name': aml_data['name'], 'columns': line_columns, 'level': 3, } @api.model def _create_report_total_line(self, options, total_vals): """ Create a total line for the report :param options: report options :param total_vals: total values dict """ report = self.env['account.report'].browse(options['report_id']) columns = [] for column in options['columns']: value = total_vals.get(column['column_group_key'], {}).get(column['expression_label']) columns.append(report._build_column_dict(value, column, options=options)) return { 'id': report._get_generic_line_id(None, None, markup='total'), 'name': _('Total'), 'level': 1, 'columns': columns, } #################################################### # REPORT LINES: QUERY #################################################### @api.model def _prepare_query(self, options, column_group_key=None, expanded_line_options=None): query_blocks, where_params = self._build_query(options, column_group_key, expanded_line_options) # pylint: disable=sql-injection query = SQL("({select} {from} {where} {order})").format(**query_blocks) return query, where_params @api.model def _build_query(self, options, column_group_key=None, expanded_line_options=None): # pylint: disable=sql-injection def format_where_params(option_key, comparison_value=('None',)): if expanded_line_options[option_key] not in comparison_value: return expanded_line_options[option_key] return None domain = None if expanded_line_options: Move = self.env['account.move'] move_types = Move.get_outbound_types(include_receipts=False) if expanded_line_options['type'] == 'Arrival' else Move.get_inbound_types(include_receipts=False) domain = [('move_id.move_type', 'in', move_types)] # triangular use cases are handled by letting the intrastat_country_id editable on # invoices. Modifying or emptying it allow to alter the intrastat declaration # accordingly to specs (https://www.nbb.be/doc/dq/f_pdf_ex/intra2017fr.pdf (ยง 4.x)) report = self.env['account.report'].browse(options['report_id']) tables, where_clause, where_params = report._query_get(options, 'strict_range', domain=domain) tables = SQL(tables) where_clause = SQL(where_clause) import_merchandise_code = _merchandise_import_code.get(self.env.company.country_id.code, '29') export_merchandise_code = _merchandise_export_code.get(self.env.company.country_id.code, '19') unknown_individual_vat = 'QN999999999999' if self.env.company.country_id.code in _qn_unknown_individual_vat_country_codes else 'QV999999999999' unknown_country_code = _unknown_country_code.get(self.env.company.country_id.code, 'QV') weight_category_id = self.env['ir.model.data']._xmlid_to_res_id('uom.product_uom_categ_kgm') currency_table_query = SQL(report._get_query_currency_table(options)) select = SQL(""" SELECT %s AS column_group_key, row_number() over () AS sequence, CASE WHEN account_move.move_type IN ('in_invoice', 'out_refund') THEN %s ELSE %s END AS system, country.code AS country_code, COALESCE(country.name->>{user_lang}, country.name->>'en_US') AS country_name, company_country.code AS comp_country_code, transaction.code AS transaction_code, company_region.code AS region_code, comp_partner.vat as company_vat, code.code AS commodity_code, account_move_line.id AS id, prodt.id AS template_id, prodt.categ_id AS category_id, account_move_line.product_uom_id AS uom_id, inv_line_uom.category_id AS uom_category_id, account_move.id AS invoice_id, account_move_line.id as move_line_id, account_move.currency_id AS invoice_currency_id, account_move.name, COALESCE(account_move.date, account_move.invoice_date) AS invoice_date, account_move.move_type AS invoice_type, COALESCE(inv_incoterm.code, comp_incoterm.code) AS incoterm_code, COALESCE(inv_transport.code, comp_transport.code) AS transport_code, CASE WHEN account_move.move_type IN ('in_invoice', 'out_refund') THEN 'Arrival' ELSE 'Dispatch' END AS type, ROUND( COALESCE(prod.weight, 0) * account_move_line.quantity / ( CASE WHEN inv_line_uom.category_id IS NULL OR inv_line_uom.category_id = prod_uom.category_id THEN inv_line_uom.factor ELSE 1 END ) * ( CASE WHEN prod_uom.uom_type <> 'reference' THEN prod_uom.factor ELSE 1 END ), SCALE(ref_weight_uom.rounding) ) AS weight, account_move_line.quantity / ( CASE WHEN inv_line_uom.category_id IS NULL OR inv_line_uom.category_id = prod_uom.category_id THEN inv_line_uom.factor ELSE 1 END ) AS quantity, CASE WHEN code.supplementary_unit IS NOT NULL and prod.intrastat_supplementary_unit_amount != 0 THEN ROUND(CAST( prod.intrastat_supplementary_unit_amount * ( account_move_line.quantity / ( CASE WHEN inv_line_uom.category_id IS NULL OR inv_line_uom.category_id = prod_uom.category_id THEN inv_line_uom.factor ELSE 1 END )) AS numeric), 2) ELSE NULL END AS supplementary_units, account_move_line.quantity AS line_quantity, -- We double sign the balance to make sure that we keep consistency between invoice/bill and the intrastat report -- Example: An invoice selling 10 items (but one is free 10 - 1), in the intrastat report we'll have 2 lines -- One for 10 items minus one for the free item SIGN(account_move_line.quantity) * SIGN(account_move_line.price_unit) * ABS(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS value, CASE WHEN product_country.code = 'GB' THEN 'XU' ELSE COALESCE(product_country.code, %s) END AS intrastat_product_origin_country_code, COALESCE(product_country.name->>{user_lang}, product_country.name->>'en_US') AS intrastat_product_origin_country_name, CASE WHEN partner.vat IS NOT NULL THEN partner.vat WHEN partner.vat IS NULL AND partner.is_company IS FALSE THEN %s ELSE 'QV999999999999' END AS partner_vat, transaction.expiry_date <= account_move.invoice_date AS expired_trans, transaction.start_date > account_move.invoice_date AS premature_trans, transaction.id IS NULL AS missing_trans, code.expiry_date <= account_move.invoice_date AS expired_comm, code.start_date > account_move.invoice_date AS premature_comm, code.id IS NULL as missing_comm, COALESCE(prod.intrastat_supplementary_unit_amount, 0) = 0 AND code.supplementary_unit IS NOT NULL as missing_unit, COALESCE(prod.weight, 0) = 0 AND code.supplementary_unit IS NULL AS missing_weight, prod.id AS product_id, prodt.categ_id AS template_categ, prodt.description as goods_description """).format(user_lang=Literal(self.env.user.lang or get_lang(self.env).code)) from_ = SQL(""" FROM {tables} JOIN {currency_table_query} ON currency_table.company_id = account_move_line.company_id JOIN account_move ON account_move.id = account_move_line.move_id LEFT JOIN account_intrastat_code transaction ON account_move_line.intrastat_transaction_id = transaction.id LEFT JOIN res_company company ON account_move.company_id = company.id LEFT JOIN account_intrastat_code company_region ON company.intrastat_region_id = company_region.id LEFT JOIN res_partner partner ON account_move_line.partner_id = partner.id LEFT JOIN res_partner comp_partner ON company.partner_id = comp_partner.id LEFT JOIN res_country country ON account_move.intrastat_country_id = country.id LEFT JOIN res_country company_country ON comp_partner.country_id = company_country.id INNER JOIN product_product prod ON account_move_line.product_id = prod.id LEFT JOIN product_template prodt ON prod.product_tmpl_id = prodt.id LEFT JOIN account_intrastat_code code ON code.id = prod.intrastat_code_id LEFT JOIN uom_uom inv_line_uom ON account_move_line.product_uom_id = inv_line_uom.id LEFT JOIN uom_uom prod_uom ON prodt.uom_id = prod_uom.id LEFT JOIN account_incoterms inv_incoterm ON account_move.invoice_incoterm_id = inv_incoterm.id LEFT JOIN account_incoterms comp_incoterm ON company.incoterm_id = comp_incoterm.id LEFT JOIN account_intrastat_code inv_transport ON account_move.intrastat_transport_mode_id = inv_transport.id LEFT JOIN account_intrastat_code comp_transport ON company.intrastat_transport_mode_id = comp_transport.id LEFT JOIN res_country product_country ON product_country.id = account_move_line.intrastat_product_origin_country_id LEFT JOIN res_country partner_country ON partner.country_id = partner_country.id AND partner_country.intrastat IS TRUE LEFT JOIN uom_uom ref_weight_uom on ref_weight_uom.category_id = %s and ref_weight_uom.uom_type = 'reference' """).format(tables=tables, currency_table_query=currency_table_query) where = SQL(""" WHERE {where_clause} AND account_move_line.display_type = 'product' AND (account_move_line.price_subtotal != 0 OR account_move_line.price_unit * account_move_line.quantity != 0) AND company_country.id != country.id AND country.intrastat = TRUE AND (country.code != 'GB' OR account_move.date < '2021-01-01') AND prodt.type != 'service' AND ref_weight_uom.active """).format(where_clause=where_clause) if expanded_line_options: # When expanding the grouped lines, we only want to add the ones that matches the parent country code, currency and commodity code where_values = { 'country.code': format_where_params('country_code'), 'code.code': format_where_params('commodity_code'), 'product_country.code': format_where_params('intrastat_product_origin_country_code', comparison_value=('QV', 'QU')), 'transaction.code': format_where_params('transaction_code'), 'company_region.code': format_where_params('region_code'), 'partner.vat': format_where_params('partner_vat', comparison_value=('QV999999999999', 'QN999999999999')), 'account_move.currency_id': format_where_params('invoice_currency_id'), } for key, value in where_values.items(): where += SQL(" AND {key} IS NOT DISTINCT FROM %s").format(key=Identifier(*key.split('.'))) where_params.append(value) where += SQL(" AND COALESCE(inv_incoterm.code, comp_incoterm.code) IS NOT DISTINCT FROM %s") where_params.append(format_where_params('incoterm_code')) where += SQL(" AND COALESCE(inv_transport.code, comp_transport.code) IS NOT DISTINCT FROM %s") where_params.append(format_where_params('transport_code')) if options['intrastat_with_vat']: where += SQL(" AND partner.vat IS NOT NULL ") order = SQL("ORDER BY account_move.invoice_date DESC, account_move_line.id") query = { 'select': select, 'from': from_, 'where': where, 'order': order, } query_params = [ column_group_key, import_merchandise_code, export_merchandise_code, unknown_country_code, unknown_individual_vat, weight_category_id, *where_params, ] return query, query_params @api.model def _build_query_group(self, options, column_group_key=None): """ This is the query to have the line grouped by country, currency and commodity code. """ inner_query, params = self._prepare_query(options, column_group_key) query = SQL(""" SELECT %s AS column_group_key, intrastat_lines.system as system, intrastat_lines.type as type, intrastat_lines.country_code AS country_code, intrastat_lines.transaction_code as transaction_code, intrastat_lines.transport_code as transport_code, intrastat_lines.region_code as region_code, intrastat_lines.commodity_code AS commodity_code, intrastat_lines.country_name as country_name, intrastat_lines.partner_vat as partner_vat, intrastat_lines.incoterm_code as incoterm_code, intrastat_lines.intrastat_product_origin_country_name as intrastat_product_origin_country_name, intrastat_lines.intrastat_product_origin_country_code as intrastat_product_origin_country_code, intrastat_lines.invoice_currency_id as invoice_currency_id, ARRAY_AGG(CASE WHEN intrastat_lines.expired_trans IS TRUE THEN intrastat_lines.move_line_id END) as expired_trans, ARRAY_AGG(CASE WHEN intrastat_lines.premature_trans IS TRUE THEN intrastat_lines.move_line_id END) as premature_trans, ARRAY_AGG(CASE WHEN intrastat_lines.missing_trans IS TRUE THEN intrastat_lines.move_line_id END) as missing_trans, ARRAY_AGG(CASE WHEN intrastat_lines.expired_comm IS TRUE THEN intrastat_lines.product_id END) as expired_comm, ARRAY_AGG(CASE WHEN intrastat_lines.premature_comm IS TRUE THEN intrastat_lines.product_id END) as premature_comm, ARRAY_AGG(CASE WHEN intrastat_lines.missing_comm IS TRUE THEN intrastat_lines.product_id END) as missing_comm, ARRAY_AGG(CASE WHEN intrastat_lines.missing_unit IS TRUE THEN intrastat_lines.product_id END) as missing_unit, ARRAY_AGG(CASE WHEN intrastat_lines.missing_weight IS TRUE THEN intrastat_lines.product_id END) as missing_weight, SUM(intrastat_lines.value) as value, SUM(intrastat_lines.weight) as weight, SUM(intrastat_lines.supplementary_units) as supplementary_units FROM ({}) intrastat_lines INNER JOIN account_move ON account_move.id = intrastat_lines.invoice_id GROUP BY system, type, country_code, transaction_code, transport_code, region_code, commodity_code, country_name, partner_vat, incoterm_code,intrastat_product_origin_country_code, intrastat_product_origin_country_name, invoice_currency_id """).format(inner_query) params = [ column_group_key, *params, ] return query, params #################################################### # REPORT LINES: HELPERS #################################################### @api.model def _fill_missing_values(self, vals_list): """ Template method to be overidden to retrieve complex data :param vals_list: A dictionary created by the dictfetchall method. """ return vals_list def _get_markup_info_from_intrastat_id(self, line_id): """ This function gets necessary info present in the generic report line id. This information are related to REPORT_LINE_ID_KEYS. :param line_id: A generic report line id :return: A dictionary containing as key all the values in REPORT_LINE_ID_KEYS and as value what we found in the generic report line id. If we don't have a related value, we fill it with 'None'. """ markup = self.env['account.report']._get_markup(line_id) line = markup.split(',') return { key: value for key, value in zip_longest(REPORT_LINE_ID_KEYS, line, fillvalue='None') } #################################################### # ACTIONS #################################################### def action_invalid_code_moves(self, options, params): return { 'type': 'ir.actions.act_window', 'name': _('Invalid transaction intrastat code entries.'), 'res_model': 'account.move.line', 'views': [( self.env.ref('account_intrastat.account_move_line_tree_view_account_intrastat_transaction_codes').id, 'list', ), (False, 'form')], 'domain': [('id', 'in', params['ids'])], 'context': { 'create': False, 'delete': False, 'expand': True, }, } def action_invalid_code_products(self, options, params): return { 'type': 'ir.actions.act_window', 'name': _('Invalid commodity intrastat code products.'), 'res_model': 'product.product', 'views': [( self.env.ref('account_intrastat.product_product_tree_view_account_intrastat').id, 'list', ), (False, 'form')], 'domain': [('id', 'in', params['ids'])], 'context': { 'create': False, 'delete': False, 'expand': True, }, } def action_undefined_units_products(self, options, params): return { 'type': 'ir.actions.act_window', 'name': _('Undefined supplementary unit products.'), 'res_model': 'product.product', 'views': [( self.env.ref('account_intrastat.product_product_tree_view_account_intrastat_supplementary_unit').id, 'list', ), (False, 'form')], 'domain': [('id', 'in', params['ids'])], 'context': { 'create': False, 'delete': False, 'expand': True, }, } def action_undefined_weight_products(self, options, params): return { 'type': 'ir.actions.act_window', 'name': _('Undefined weight products.'), 'res_model': 'product.product', 'views': [( self.env.ref('account_intrastat.product_product_tree_view_account_intrastat_weight').id, 'list', ), (False, 'form')], 'domain': [('id', 'in', params['ids'])], 'context': { 'create': False, 'delete': False, 'expand': True, }, }