diff --git a/addons/account/tests/test_account_account.py b/addons/account/tests/test_account_account.py index 5b88a14a82f..3b83e8c20d3 100644 --- a/addons/account/tests/test_account_account.py +++ b/addons/account/tests/test_account_account.py @@ -61,7 +61,7 @@ class TestAccountAccount(AccountTestInvoicingCommon): # Set the account as reconcile and fully reconcile something. account.reconcile = True - self.env['account.move.line'].invalidate_model() + self.env.invalidate_all() self.assertRecordValues(move.line_ids, [ {'reconciled': False, 'amount_residual': 100.0, 'amount_residual_currency': 200.0}, @@ -77,7 +77,7 @@ class TestAccountAccount(AccountTestInvoicingCommon): # Set back to a not reconcile account and check the journal items. move.line_ids.remove_move_reconcile() account.reconcile = False - self.env['account.move.line'].invalidate_model() + self.env.invalidate_all() self.assertRecordValues(move.line_ids, [ {'reconciled': False, 'amount_residual': 0.0, 'amount_residual_currency': 0.0}, @@ -119,7 +119,7 @@ class TestAccountAccount(AccountTestInvoicingCommon): # Set the account as reconcile and partially reconcile something. account.reconcile = True - self.env['account.move.line'].invalidate_model() + self.env.invalidate_all() move.line_ids.filtered(lambda line: line.account_id == account).reconcile() diff --git a/addons/calendar/models/calendar_alarm_manager.py b/addons/calendar/models/calendar_alarm_manager.py index 40d1318c16e..f67a32d5d5e 100644 --- a/addons/calendar/models/calendar_alarm_manager.py +++ b/addons/calendar/models/calendar_alarm_manager.py @@ -15,6 +15,10 @@ class AlarmManager(models.AbstractModel): _description = 'Event Alarm Manager' def _get_next_potential_limit_alarm(self, alarm_type, seconds=None, partners=None): + # flush models before making queries + for model_name in ('calendar.alarm', 'calendar.event', 'calendar.recurrence'): + self.env[model_name].flush_model() + result = {} delta_request = """ SELECT diff --git a/addons/calendar/tests/test_calendar.py b/addons/calendar/tests/test_calendar.py index a26aecbbc1a..473db2dc702 100644 --- a/addons/calendar/tests/test_calendar.py +++ b/addons/calendar/tests/test_calendar.py @@ -161,6 +161,7 @@ class TestCalendar(SavepointCaseWithUserDemo): 'stop_date': "2018-10-18", 'allday': True, }) + self.env.invalidate_all() self.assertEqual(str(event.start), '2018-10-16 08:00:00') self.assertEqual(str(event.stop), '2018-10-18 18:00:00') diff --git a/addons/crm/tests/test_crm_pls.py b/addons/crm/tests/test_crm_pls.py index 98e54f11e33..19d30718317 100644 --- a/addons/crm/tests/test_crm_pls.py +++ b/addons/crm/tests/test_crm_pls.py @@ -191,6 +191,9 @@ class TestCRMPLS(TransactionCase): # rebuild frequencies table and recompute automated_probability for all leads. Lead._cron_update_automated_probabilities() + # As the cron is computing and writing in SQL queries, we need to invalidate the cache + self.env.invalidate_all() + self.assertEqual(tools.float_compare(leads[3].automated_probability, 33.49, 2), 0) self.assertEqual(tools.float_compare(leads[8].automated_probability, 7.74, 2), 0) lead_13_team_3_proba = leads[13].automated_probability @@ -403,6 +406,7 @@ class TestCRMPLS(TransactionCase): # Force recompute - A priori, no need to do this as, for each won / lost, we increment tag frequency. Lead._cron_update_automated_probabilities() + self.env.invalidate_all() lead_tag_1 = leads_with_tags[30] lead_tag_2 = leads_with_tags[90] @@ -440,6 +444,7 @@ class TestCRMPLS(TransactionCase): leads.filtered(lambda lead: lead.id % 2 == 0).email_state = 'correct' leads.filtered(lambda lead: lead.id % 2 == 1).email_state = 'incorrect' Lead._cron_update_automated_probabilities() + self.env.invalidate_all() self.assertEqual(tools.float_compare(leads[3].automated_probability, 4.21, 2), 0) self.assertEqual(tools.float_compare(leads[8].automated_probability, 0.23, 2), 0) @@ -447,6 +452,7 @@ class TestCRMPLS(TransactionCase): # remove all pls fields self.env['ir.config_parameter'].sudo().set_param("crm.pls_fields", False) Lead._cron_update_automated_probabilities() + self.env.invalidate_all() self.assertEqual(tools.float_compare(leads[3].automated_probability, 34.38, 2), 0) self.assertEqual(tools.float_compare(leads[8].automated_probability, 50.0, 2), 0) @@ -454,6 +460,7 @@ class TestCRMPLS(TransactionCase): # check if the probabilities are the same with the old param self.env['ir.config_parameter'].sudo().set_param("crm.pls_fields", "country_id,state_id,email_state,phone_state,source_id") Lead._cron_update_automated_probabilities() + self.env.invalidate_all() self.assertEqual(tools.float_compare(leads[3].automated_probability, 4.21, 2), 0) self.assertEqual(tools.float_compare(leads[8].automated_probability, 0.23, 2), 0) diff --git a/addons/delivery/tests/test_delivery_cost.py b/addons/delivery/tests/test_delivery_cost.py index 262edd2a70c..1da41ea4440 100644 --- a/addons/delivery/tests/test_delivery_cost.py +++ b/addons/delivery/tests/test_delivery_cost.py @@ -45,10 +45,10 @@ class TestDeliveryCost(common.TransactionCase): # as the tests hereunder assume all the prices in USD, we must ensure # that the company actually uses USD # We do an invalidation so the cache is aware of it too. + self.env.company.invalidate_recordset() self.env.cr.execute( "UPDATE res_company SET currency_id = %s WHERE id = %s", [self.env.ref('base.USD').id, self.env.company.id]) - self.env.company.invalidate_recordset() self.pricelist.currency_id = self.env.ref('base.USD').id def test_00_delivery_cost(self): diff --git a/addons/hr/models/hr_employee.py b/addons/hr/models/hr_employee.py index dd0fba67fba..5978a2ac7de 100644 --- a/addons/hr/models/hr_employee.py +++ b/addons/hr/models/hr_employee.py @@ -181,10 +181,15 @@ class HrEmployeePrivate(models.Model): if self.check_access_rights('read', raise_exception=False): return super(HrEmployeePrivate, self)._read(fields) - res = self.env['hr.employee.public'].browse(self.ids).read(fields) - for r in res: - record = self.browse(r['id']) - record._update_cache({k:v for k,v in r.items() if k in fields}, validate=False) + # HACK: retrieve publicly available values from hr.employee.public and + # copy them to the cache of self; non-public data will be missing from + # cache, and interpreted as an access error + self.flush_recordset(fields) + public = self.env['hr.employee.public'].browse(self._ids) + public._read(fields) + for fname in fields: + values = self.env.cache.get_values(public, public._fields[fname]) + self.env.cache.update(self, self._fields[fname], values) @api.model def _cron_check_work_permit_validity(self): diff --git a/addons/hr_work_entry_holidays/tests/test_leave.py b/addons/hr_work_entry_holidays/tests/test_leave.py index 1985e6faae2..7a3eba9967e 100644 --- a/addons/hr_work_entry_holidays/tests/test_leave.py +++ b/addons/hr_work_entry_holidays/tests/test_leave.py @@ -158,17 +158,16 @@ class TestWorkEntryLeave(TestWorkEntryHolidaysBase): 'date_to': datetime(2022, 3, 25, 20), 'number_of_days': 4, }) - can_cancel_field = leave._fields['can_cancel'] leave.with_user(SUPERUSER_ID).action_validate() # No work entries exist yet self.assertTrue(leave.can_cancel, "The leave should still be cancellable") # can not create in the future self.richard_emp.contract_ids._generate_work_entries(datetime(2022, 3, 21, 6), datetime(2022, 3, 25, 20)) work_entries = self.env['hr.work.entry'].search([('employee_id', '=', self.richard_emp.id)]) - self.env.cache.invalidate([(can_cancel_field, leave.ids)]) + leave.invalidate_recordset(['can_cancel']) # Work entries exist but are not locked yet self.assertTrue(leave.can_cancel, "The leave should still be cancellable") work_entries.action_validate() - self.env.cache.invalidate([(can_cancel_field, leave.ids)]) + leave.invalidate_recordset(['can_cancel']) # Work entries locked self.assertFalse(leave.can_cancel, "The leave should not be cancellable") diff --git a/addons/l10n_eg_edi_eta/tests/common.py b/addons/l10n_eg_edi_eta/tests/common.py index d949353dd32..1a7a0d72b15 100644 --- a/addons/l10n_eg_edi_eta/tests/common.py +++ b/addons/l10n_eg_edi_eta/tests/common.py @@ -89,13 +89,18 @@ class TestEGEdiCommon(AccountEdiTestCommon): @classmethod def create_invoice(cls, **kwargs): - return (cls.env['account.move'] - .with_context(edi_test_mode=True) - .create({ - 'move_type': 'out_invoice', - 'partner_id': cls.partner_a.id, - 'invoice_date': '2022-03-15', - 'date': '2022-03-15', - **kwargs, - 'invoice_line_ids': [Command.create({**line_vals, }) for line_vals in kwargs.get('invoice_line_ids', [])] - })) + invoice = ( + cls.env['account.move'] + .with_context(edi_test_mode=True) + .create({ + 'move_type': 'out_invoice', + 'partner_id': cls.partner_a.id, + 'invoice_date': '2022-03-15', + 'date': '2022-03-15', + **kwargs, + 'invoice_line_ids': [Command.create({**line_vals, }) for line_vals in kwargs.get('invoice_line_ids', [])] + }) + ) + # this fixes rounding issues in cache + cls.env.invalidate_all() + return invoice diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index b1aa97f0b21..7bf5af2a2b1 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -1093,6 +1093,7 @@ class Message(models.Model): def _invalidate_documents(self, model=None, res_id=None): """ Invalidate the cache of the documents followed by ``self``. """ fnames = ['message_ids', 'message_needaction', 'message_needaction_counter'] + self.flush_recordset(['model', 'res_id']) for record in self: model = model or record.model res_id = res_id or record.res_id diff --git a/addons/mrp_subcontracting/tests/test_subcontracting.py b/addons/mrp_subcontracting/tests/test_subcontracting.py index 3f90c5114a8..f4d43680e02 100644 --- a/addons/mrp_subcontracting/tests/test_subcontracting.py +++ b/addons/mrp_subcontracting/tests/test_subcontracting.py @@ -357,7 +357,7 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon): 'parent_id': main_partner_2.id, 'company_id': self.env.ref('base.main_company').id, }) - self.env.cache.invalidate() + self.env.invalidate_all() # We create a different BoM for the same product comp3 = self.env['product.product'].create({ diff --git a/addons/project/tests/test_project_report.py b/addons/project/tests/test_project_report.py index 8fd2006d44c..2e6f3fe594e 100644 --- a/addons/project/tests/test_project_report.py +++ b/addons/project/tests/test_project_report.py @@ -33,6 +33,9 @@ class TestProjectReport(TestProjectCommon): self.assertEqual(task_3.rating_avg, 0) self.assertEqual(task_3.rating_last_value, 0) + # fix cache consistency + self.env['project.task'].invalidate_model(['rating_avg', 'rating_last_value']) + tasks = [self.task_1, self.task_2, task_3] for task in tasks: rating_values = task.read(['rating_avg', 'rating_last_value'])[0] diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index dd03d670d18..3a03207fff5 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -638,6 +638,7 @@ class PurchaseOrder(models.Model): # Invoice_ids may be filtered depending on the user. To ensure we get all # invoices related to the purchase order, we read them in sudo to fill the # cache. + self.invalidate_model(['invoice_ids']) self.sudo()._read(['invoice_ids']) invoices = self.invoice_ids diff --git a/addons/purchase_stock/tests/test_reordering_rule.py b/addons/purchase_stock/tests/test_reordering_rule.py index 3f93e1c30fc..d0295b9cdb9 100644 --- a/addons/purchase_stock/tests/test_reordering_rule.py +++ b/addons/purchase_stock/tests/test_reordering_rule.py @@ -602,7 +602,7 @@ class TestReorderingRule(TransactionCase): "route_ids": [], } ) - self.env.cache.invalidate() + self.env.invalidate_all() self.env["procurement.group"].run([procurement]) @@ -707,10 +707,11 @@ class TestReorderingRule(TransactionCase): {'qty_forecast': -1, 'qty_to_order': 1}, ]) - delivery.scheduled_date += td(days=7) + # invalidate the fields that will eventually be inconsistent orderpoint.invalidate_model(fnames=['qty_forecast', 'qty_to_order']) orderpoint.product_id.invalidate_model(fnames=['virtual_available']) + delivery.scheduled_date += td(days=7) self.assertRecordValues(orderpoint, [ {'qty_forecast': 0, 'qty_to_order': 0}, ]) diff --git a/addons/sale/tests/test_sale_order.py b/addons/sale/tests/test_sale_order.py index a1a0c6bde60..22664c079ea 100644 --- a/addons/sale/tests/test_sale_order.py +++ b/addons/sale/tests/test_sale_order.py @@ -176,6 +176,7 @@ class TestSaleOrder(TestSaleCommon): # upsell and invoice self.sol_serv_order.write({'product_uom_qty': 10}) + # There is a bug with `new` and `_origin` # If you create a first new from a record, then change a value on the origin record, than create another new, # this other new wont have the updated value of the origin record, but the one from the previous new @@ -184,10 +185,7 @@ class TestSaleOrder(TestSaleCommon): # Here, we update `qty_delivered` on the origin record, but the `new` records which are in cache with this order line # as origin are not updated, nor the fields that depends on it. self.env.flush_all() - for field in self.env['sale.order.line']._fields.values(): - for res_id in list(self.env.cache._data[field]): - if not res_id: - self.env.cache._data[field].pop(res_id) + self.env.invalidate_all() invoice3 = self.sale_order._create_invoices() self.assertEqual(len(invoice3.invoice_line_ids), 1, 'Sale: third invoice is missing lines') diff --git a/addons/sale_loyalty/tests/test_program_rules.py b/addons/sale_loyalty/tests/test_program_rules.py index 0e4d924ca2d..4cd92a42c20 100644 --- a/addons/sale_loyalty/tests/test_program_rules.py +++ b/addons/sale_loyalty/tests/test_program_rules.py @@ -269,8 +269,6 @@ class TestProgramRules(TestSaleCouponCommon): }) ]}) # Invalidate total_order_count - field_order_count = self.env['loyalty.program']._fields['order_count'] - field_total_order_count = self.env['loyalty.program']._fields['total_order_count'] - self.env.cache.invalidate([(field_order_count, self.immediate_promotion_program.ids), (field_total_order_count, self.immediate_promotion_program.ids)]) + self.immediate_promotion_program.invalidate_recordset(['order_count', 'total_order_count']) self._auto_rewards(order, self.immediate_promotion_program) self.assertEqual(len(order.order_line.ids), 2, "The promo offer shouldn't have been applied as the number of uses is exceeded") diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py index 896b996ed51..482fcd1074c 100644 --- a/addons/sale_stock/tests/test_sale_stock.py +++ b/addons/sale_stock/tests/test_sale_stock.py @@ -93,6 +93,7 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon): self.assertEqual(del_qties, del_qties_truth, 'Sale Stock: delivered quantities are wrong after complete delivery') # Without timesheet, we manually set the delivered qty for the product serv_del self.so.order_line.sorted()[1]['qty_delivered'] = 2.0 + # There is a bug with `new` and `_origin` # If you create a first new from a record, then change a value on the origin record, than create another new, # this other new wont have the updated value of the origin record, but the one from the previous new @@ -101,10 +102,8 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon): # Here, we update `qty_delivered` on the origin record, but the `new` records which are in cache with this order line # as origin are not updated, nor the fields that depends on it. self.env.flush_all() - for field in self.env['sale.order.line']._fields.values(): - for res_id in list(self.env.cache._data[field]): - if not res_id: - self.env.cache._data[field].pop(res_id) + self.env.invalidate_all() + inv_id = self.so._create_invoices() self.assertEqual(self.so.invoice_status, 'invoiced', 'Sale Stock: so invoice_status should be "fully invoiced" after complete delivery and invoicing') diff --git a/addons/stock/report/report_stock_quantity.py b/addons/stock/report/report_stock_quantity.py index 6592303f5cf..4b3314f97eb 100644 --- a/addons/stock/report/report_stock_quantity.py +++ b/addons/stock/report/report_stock_quantity.py @@ -9,6 +9,15 @@ class ReportStockQuantity(models.Model): _auto = False _description = 'Stock Quantity Report' + _depends = { + 'product.product': ['product_tmpl_id'], + 'product.template': ['type'], + 'stock.location': ['parent_path'], + 'stock.move': ['company_id', 'date', 'location_dest_id', 'location_id', 'product_id', 'product_qty', 'state'], + 'stock.quant': ['company_id', 'location_id', 'product_id', 'quantity'], + 'stock.warehouse': ['view_location_id'], + } + date = fields.Date(string='Date', readonly=True) product_tmpl_id = fields.Many2one('product.template', readonly=True) product_id = fields.Many2one('product.product', string='Product', readonly=True) diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py index a332e99ae02..43525222629 100644 --- a/addons/website/tests/test_views.py +++ b/addons/website/tests/test_views.py @@ -774,7 +774,7 @@ class TestCowViewSaving(TestViewSavingCommon): 'arch': '
COMPARE