From 7b635c000556bafdc76152ab281ea484d90d7394 Mon Sep 17 00:00:00 2001
From: Preksha Chouhan <prec@odoo.com>
Date: Tue, 25 Jul 2023 11:42:48 +0000
Subject: [PATCH] [FIX] web: return updated list of fields in export template

When user access template at the time of export with deleted field.
The traceback will be generated.

To reproduce the issue(any model, here- 'account.move.line'):

- Install 'account_accountant' module
- Go to Settings > Technical > Database Structure > Fields
- Create a new field with model as 'Journal Item' and save
- Go to Accounting > Miscellaneous > Journal Items
- Select any record in list view and click on export and generate a new export
template with newly created field
- Go to 'ir.model.fields' and delete that field
- Go to 'Journal Items' and select that template while export

Error: A traceback appears: KeyError: 'tax_audit'

When a field gets deleted from 'ir.model.fields' but it does not get deleted
from export template. And selecting that template to export the records will
lead to traceback.

See -
https://github.com/odoo/odoo/blob/59669e9943158e51dcbb9ae69ad758df8f7c7976/addons/web/controllers/main.py#L1815-L1818

sentry-4331986723

closes odoo/odoo#134275

X-original-commit: 814fb43e717932b2ffd16d3fd91af4074a2ab6b1
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
---
 addons/test_xlsx_export/tests/test_export.py | 36 ++++++++++++++++++++
 addons/web/controllers/main.py               | 10 +++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/addons/test_xlsx_export/tests/test_export.py b/addons/test_xlsx_export/tests/test_export.py
index c676dca3ca8a..4d253b332d6d 100644
--- a/addons/test_xlsx_export/tests/test_export.py
+++ b/addons/test_xlsx_export/tests/test_export.py
@@ -7,6 +7,7 @@ from unittest.mock import patch
 
 from odoo import http
 from odoo.tests import common, tagged
+from odoo.tools import mute_logger
 from odoo.tools.misc import get_lang
 from odoo.addons.web.controllers.main import ExportXlsxWriter
 from odoo.addons.mail.tests.common import mail_new_test_user
@@ -393,3 +394,38 @@ class TestGroupedExport(XlsxCreatorCase):
             ['    86420.864 (1)','86420.86'],
             ['1'                ,'86420.86'],
         ])
+
+    @mute_logger('odoo.addons.web.controllers.main')
+    def test_export_with_deleted_field(self):
+        model = self.env['ir.model']._get('res.partner')
+        url = '/web/export/namelist'
+        header = {"Content-Type": "application/json"}
+
+        custom_field = self.env['ir.model.fields'].create([{
+            'name': 'x_test',
+            'ttype': 'char',
+            'field_description': 'test field',
+            'model_id': model.id,
+        }])
+
+        export_template = self.env['ir.exports'].create({
+            'name': custom_field.name,
+            'export_fields': [(0, 0, {'name': custom_field.name})],
+        })
+
+        data = json.dumps({
+                'params': {
+                    'model': model.model,
+                    'export_id': export_template.id,
+                    },
+                })
+
+        export_line_field = self.env['ir.exports.line'].search([('name', '=', export_template.name)])
+
+        self.url_open(url, data, headers=header).json()
+        self.assertTrue(export_line_field.exists())
+
+        custom_field.unlink()
+
+        self.url_open(url, data, headers=header).json()
+        self.assertFalse(export_line_field.exists())
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index bfeb39ddf620..93d6e5209e83 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -1690,7 +1690,15 @@ class Export(http.Controller):
     def namelist(self, model, export_id):
         # TODO: namelist really has no reason to be in Python (although itertools.groupby helps)
         export = request.env['ir.exports'].browse([export_id]).read()[0]
-        export_fields_list = request.env['ir.exports.line'].browse(export['export_fields']).read()
+        exported_fields = request.env['ir.exports.line'].browse(export['export_fields'])
+        fields = self.fields_get(model)
+
+        for invalid_fields in exported_fields.filtered(lambda f: f.name not in fields):
+            # ir.exports.line lack a ondelete=cascade foreign key on ir.model.fields
+            _logger.warning("Field %r not found for saved ir.exports of model %s, deleting", invalid_fields.name, model)
+            invalid_fields.unlink()
+
+        export_fields_list = exported_fields.read()
 
         fields_data = self.fields_info(
             model, [f['name'] for f in export_fields_list])
-- 
GitLab