diff --git a/addons/l10n_fr_fec/tests/test_wizard.py b/addons/l10n_fr_fec/tests/test_wizard.py index f87f37ed89b..db1e00de86e 100644 --- a/addons/l10n_fr_fec/tests/test_wizard.py +++ b/addons/l10n_fr_fec/tests/test_wizard.py @@ -23,7 +23,7 @@ class TestAccountFrFec(AccountTestInvoicingCommon): lines_data = [(1437.12, 'Hello\tDarkness'), (1676.64, 'my\rold\nfriend'), (3353.28, '\t\t\r')] with freeze_time('2021-05-02'): - today = fields.Date.today().strftime('%Y-%m-%d') + cls.today = fields.Date.today().strftime('%Y-%m-%d') cls.wizard = cls.env['account.fr.fec'].create({ 'date_from': fields.Date.today() - timedelta(days=1), @@ -53,8 +53,8 @@ class TestAccountFrFec(AccountTestInvoicingCommon): cls.invoice_a = cls.env['account.move'].create({ 'move_type': 'out_invoice', 'partner_id': cls.partner_a.id, - 'date': today, - 'invoice_date': today, + 'date': cls.today, + 'invoice_date': cls.today, 'currency_id': company.currency_id.id, 'invoice_line_ids': [(0, None, { 'name': name, @@ -66,15 +66,50 @@ class TestAccountFrFec(AccountTestInvoicingCommon): }) cls.invoice_a.action_post() - def test_generate_fec_sanitize_pieceref(self): - self.wizard.generate_fec() - expected_content = ( + cls.expected_report = ( "JournalCode|JournalLib|EcritureNum|EcritureDate|CompteNum|CompteLib|CompAuxNum|CompAuxLib|PieceRef|PieceDate|EcritureLib|Debit|Credit|EcritureLet|DateLet|ValidDate|Montantdevise|Idevise\r\n" "INV|Customer Invoices|INV/2021/00001|20210502|701100|Finished products (or group) A|||-|20210502|Hello Darkness|0,00| 000000000001437,12|||20210502|-000000000001437,12|EUR\r\n" "INV|Customer Invoices|INV/2021/00001|20210502|701100|Finished products (or group) A|||-|20210502|my old friend|0,00| 000000000001676,64|||20210502|-000000000001676,64|EUR\r\n" "INV|Customer Invoices|INV/2021/00001|20210502|701100|Finished products (or group) A|||-|20210502|/|0,00| 000000000003353,28|||20210502|-000000000003353,28|EUR\r\n" "INV|Customer Invoices|INV/2021/00001|20210502|445710|VAT collected|||-|20210502|TVA 20,0%|0,00| 000000000001293,41|||20210502|-000000000001293,41|EUR\r\n" - f"INV|Customer Invoices|INV/2021/00001|20210502|411100|Customers - Sales of goods or services|{self.partner_a.id}|partner_a|-|20210502|INV/2021/00001| 000000000007760,45|0,00|||20210502| 000000000007760,45|EUR" + f"INV|Customer Invoices|INV/2021/00001|20210502|411100|Customers - Sales of goods or services|{cls.partner_a.id}|partner_a|-|20210502|INV/2021/00001| 000000000007760,45|0,00|||20210502| 000000000007760,45|EUR" + ) + + def test_generate_fec_sanitize_pieceref(self): + self.wizard.generate_fec() + content = base64.b64decode(self.wizard.fec_data).decode() + self.assertEqual(self.expected_report, content) + + def test_generate_fec_exclude_journals(self): + journal = self.company_data['default_journal_misc'] + self.env['account.move'].create({ + 'journal_id': journal.id, + 'date': self.today, + 'line_ids': [ + Command.create({ + 'account_id': self.company_data['default_account_payable'].id, + 'debit': 500, + 'company_id': self.company_data['company'].id + }), + Command.create({ + 'account_id': self.company_data['default_account_receivable'].id, + 'credit': 500, + 'company_id': self.company_data['company'].id + }) + ] + }).action_post() + self.env.flush_all() + + self.wizard.generate_fec() + expected_content = self.expected_report + ( + "\r\n" + "MISC|Miscellaneous Operations|MISC/2021/05/0001|20210502|400000|Suppliers and related accounts|||-|20210502|/| 000000000000500,00|0,00|||20210502| 000000000000500,00|EUR\r\n" + "MISC|Miscellaneous Operations|MISC/2021/05/0001|20210502|411100|Customers - Sales of goods or services|||-|20210502|/|0,00| 000000000000500,00|||20210502|-000000000000500,00|EUR" ) content = base64.b64decode(self.wizard.fec_data).decode() self.assertEqual(expected_content, content) + + self.wizard.excluded_journal_ids = journal + self.wizard.generate_fec() + content = base64.b64decode(self.wizard.fec_data).decode() + self.assertEqual(self.expected_report, content) diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py index f7d0481f73b..af2becc3d68 100644 --- a/addons/l10n_fr_fec/wizard/account_fr_fec.py +++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py @@ -25,36 +25,48 @@ class AccountFrFec(models.TransientModel): export_type = fields.Selection([ ('official', 'Official FEC report (posted entries only)'), ('nonofficial', 'Non-official FEC report (posted and unposted entries)'), - ], string='Export Type', required=True, default='official') + ], string='Export Type', required=True, default='official') + excluded_journal_ids = fields.Many2many('account.journal', string="Excluded Journals", domain="[('company_id', '=', current_company_id)]") @api.onchange('test_file') def _onchange_export_file(self): if not self.test_file: self.export_type = 'official' + def _get_where_query(self): + where_params = {'company_id': self.env.company.id} + where_query = "am.company_id = %(company_id)s\n" + # For official report: only use posted entries + if self.export_type == "official": + where_query += "AND am.state = 'posted'\n" + if self.excluded_journal_ids: + where_params['excluded_journal_ids'] = tuple(self.excluded_journal_ids.ids) + where_query += "AND am.journal_id NOT IN %(excluded_journal_ids)s\n" + return where_query, where_params + def _do_query_unaffected_earnings(self): ''' Compute the sum of ending balances for all accounts that are of a type that does not bring forward the balance in new fiscal years. This is needed because we have to display only one line for the initial balance of all expense/revenue accounts in the FEC. ''' - - sql_query = ''' + where_query, where_params = self._get_where_query() + sql_query = f''' SELECT 'OUV' AS JournalCode, 'Balance initiale' AS JournalLib, - 'OUVERTURE/' || %s AS EcritureNum, - %s AS EcritureDate, + 'OUVERTURE/' || %(formatted_date_year)s AS EcritureNum, + %(formatted_date_from)s AS EcritureDate, '120/129' AS CompteNum, 'Benefice (perte) reporte(e)' AS CompteLib, '' AS CompAuxNum, '' AS CompAuxLib, '-' AS PieceRef, - %s AS PieceDate, + %(formatted_date_from)s AS PieceDate, '/' AS EcritureLib, replace(CASE WHEN COALESCE(sum(aml.balance), 0) <= 0 THEN '0,00' ELSE to_char(SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Debit, replace(CASE WHEN COALESCE(sum(aml.balance), 0) >= 0 THEN '0,00' ELSE to_char(-SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Credit, '' AS EcritureLet, '' AS DateLet, - %s AS ValidDate, + %(formatted_date_from)s AS ValidDate, '' AS Montantdevise, '' AS Idevise FROM @@ -62,25 +74,17 @@ class AccountFrFec(models.TransientModel): LEFT JOIN account_move am ON am.id=aml.move_id JOIN account_account aa ON aa.id = aml.account_id WHERE - am.date < %s - AND am.company_id = %s + {where_query} + AND am.date < %(date_from)s AND aa.include_initial_balance IS NOT TRUE ''' - # For official report: only use posted entries - if self.export_type == "official": - sql_query += ''' - AND am.state = 'posted' - ''' - company = self.env.company - formatted_date_from = fields.Date.to_string(self.date_from).replace('-', '') - date_from = self.date_from - formatted_date_year = date_from.year - self._cr.execute( - sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) - listrow = [] - row = self._cr.fetchone() - listrow = list(row) - return listrow + self._cr.execute(sql_query, { + **where_params, + 'formatted_date_year': self.date_from.year, + 'formatted_date_from': fields.Date.to_string(self.date_from).replace('-', ''), + 'date_from': self.date_from, + }) + return list(self._cr.fetchone()) def _get_company_legal_data(self, company): """ @@ -162,24 +166,26 @@ class AccountFrFec(models.TransientModel): aa_name = f"COALESCE(aa.name->>'{lang}', aa.name->>'en_US')" else: aa_name = "aa.name" + + where_query, where_params = self._get_where_query() sql_query = f''' SELECT 'OUV' AS JournalCode, 'Balance initiale' AS JournalLib, - 'OUVERTURE/' || %s AS EcritureNum, - %s AS EcritureDate, + 'OUVERTURE/' || %(formatted_date_year)s AS EcritureNum, + %(formatted_date_from)s AS EcritureDate, MIN(aa.code) AS CompteNum, replace(replace(MIN({aa_name}), '|', '/'), '\t', '') AS CompteLib, '' AS CompAuxNum, '' AS CompAuxLib, '-' AS PieceRef, - %s AS PieceDate, + %(formatted_date_from)s AS PieceDate, '/' AS EcritureLib, replace(CASE WHEN sum(aml.balance) <= 0 THEN '0,00' ELSE to_char(SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Debit, replace(CASE WHEN sum(aml.balance) >= 0 THEN '0,00' ELSE to_char(-SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Credit, '' AS EcritureLet, '' AS DateLet, - %s AS ValidDate, + %(formatted_date_from)s AS ValidDate, '' AS Montantdevise, '' AS Idevise, MIN(aa.id) AS CompteID @@ -188,29 +194,21 @@ class AccountFrFec(models.TransientModel): LEFT JOIN account_move am ON am.id=aml.move_id JOIN account_account aa ON aa.id = aml.account_id WHERE - am.date < %s - AND am.company_id = %s + {where_query} + AND am.date < %(date_from)s AND aa.include_initial_balance = 't' - ''' - - # For official report: only use posted entries - if self.export_type == "official": - sql_query += ''' - AND am.state = 'posted' - ''' - - sql_query += ''' GROUP BY aml.account_id, aa.account_type HAVING aa.account_type not in ('asset_receivable', 'liability_payable') ''' - formatted_date_from = fields.Date.to_string(self.date_from).replace('-', '') - date_from = self.date_from - formatted_date_year = date_from.year + params = { + **where_params, + 'formatted_date_year': self.date_from.year, + 'formatted_date_from': fields.Date.to_string(self.date_from).replace('-', ''), + 'date_from': self.date_from, + } + self._cr.execute(sql_query, params) + currency_digits = 2 - - self._cr.execute( - sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) - for row in self._cr.fetchall(): listrow = list(row) account_id = listrow.pop() @@ -250,8 +248,8 @@ class AccountFrFec(models.TransientModel): SELECT 'OUV' AS JournalCode, 'Balance initiale' AS JournalLib, - 'OUVERTURE/' || %s AS EcritureNum, - %s AS EcritureDate, + 'OUVERTURE/' || %(formatted_date_year)s AS EcritureNum, + %(formatted_date_from)s AS EcritureDate, MIN(aa.code) AS CompteNum, replace(MIN({aa_name}), '|', '/') AS CompteLib, CASE WHEN MIN(aa.account_type) IN ('asset_receivable', 'liability_payable') @@ -268,13 +266,13 @@ class AccountFrFec(models.TransientModel): ELSE '' END AS CompAuxLib, '-' AS PieceRef, - %s AS PieceDate, + %(formatted_date_from)s AS PieceDate, '/' AS EcritureLib, replace(CASE WHEN sum(aml.balance) <= 0 THEN '0,00' ELSE to_char(SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Debit, replace(CASE WHEN sum(aml.balance) >= 0 THEN '0,00' ELSE to_char(-SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Credit, '' AS EcritureLet, '' AS DateLet, - %s AS ValidDate, + %(formatted_date_from)s AS ValidDate, '' AS Montantdevise, '' AS Idevise, MIN(aa.id) AS CompteID @@ -284,23 +282,13 @@ class AccountFrFec(models.TransientModel): LEFT JOIN res_partner rp ON rp.id=aml.partner_id JOIN account_account aa ON aa.id = aml.account_id WHERE - am.date < %s - AND am.company_id = %s + {where_query} + AND am.date < %(date_from)s AND aa.include_initial_balance = 't' - ''' - - # For official report: only use posted entries - if self.export_type == "official": - sql_query += ''' - AND am.state = 'posted' - ''' - - sql_query += ''' GROUP BY aml.account_id, aa.account_type, rp.ref, rp.id HAVING aa.account_type in ('asset_receivable', 'liability_payable') ''' - self._cr.execute( - sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) + self._cr.execute(sql_query, params) for row in self._cr.fetchall(): listrow = list(row) @@ -362,25 +350,15 @@ class AccountFrFec(models.TransientModel): LEFT JOIN res_currency rc ON rc.id = aml.currency_id LEFT JOIN account_full_reconcile rec ON rec.id = aml.full_reconcile_id WHERE - am.date >= %s - AND am.date <= %s - AND am.company_id = %s - ''' - - # For official report: only use posted entries - if self.export_type == "official": - sql_query += ''' - AND am.state = 'posted' - ''' - - sql_query += ''' + {where_query} + AND am.date >= %(date_from)s + AND am.date <= %(date_to)s ORDER BY am.date, am.name, aml.id ''' - self._cr.execute( - sql_query, (self.date_from, self.date_to, company.id)) + self._cr.execute(sql_query, {**params, 'date_to': self.date_to}) for row in self._cr.fetchall(): rows_to_write.append(list(row)) @@ -395,7 +373,7 @@ class AccountFrFec(models.TransientModel): 'fec_data': base64.encodebytes(fecvalue), # Filename = FECYYYYMMDD where YYYMMDD is the closing date 'filename': '%sFEC%s%s.csv' % (company_legal_data, end_date, suffix), - }) + }) # Set fiscal year lock date to the end date (not in test) fiscalyear_lock_date = self.env.company.fiscalyear_lock_date diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec_view.xml b/addons/l10n_fr_fec/wizard/account_fr_fec_view.xml index 0d6c85c52ca..525a106aac3 100644 --- a/addons/l10n_fr_fec/wizard/account_fr_fec_view.xml +++ b/addons/l10n_fr_fec/wizard/account_fr_fec_view.xml @@ -19,6 +19,7 @@ +