diff --git a/addons/account/tests/common.py b/addons/account/tests/common.py index 7b0515bf16c..6ae2a249630 100644 --- a/addons/account/tests/common.py +++ b/addons/account/tests/common.py @@ -385,7 +385,14 @@ class AccountTestInvoicingCommon(TransactionCase): .with_company(company or cls.env.company) \ .with_context(default_move_type=move_type, account_predictive_bills_disable_prediction=True)) move_form.invoice_date = invoice_date or fields.Date.from_string('2019-01-01') - move_form.date = move_form.invoice_date + # According to the state or type of the invoice, the date field is sometimes visible or not + # Besides, the date field can be put multiple times in the view + # "invisible": "['|', ('state', '!=', 'draft'), ('auto_post', '!=', 'at_date')]" + # "invisible": ['|', '|', ('state', '!=', 'draft'), ('auto_post', '=', 'no'), ('auto_post', '=', 'at_date')] + # "invisible": "['&', ('move_type', 'in', ['out_invoice', 'out_refund', 'out_receipt']), ('quick_edit_mode', '=', False)]" + # :TestAccountMoveOutInvoiceOnchanges, :TestAccountMoveOutRefundOnchanges, .test_00_debit_note_out_invoice, :TestAccountEdi + if not move_form._get_modifier('date', 'invisible'): + move_form.date = move_form.invoice_date move_form.partner_id = partner or cls.partner_a for product in (products or []): diff --git a/addons/account/tests/test_account_move_in_invoice.py b/addons/account/tests/test_account_move_in_invoice.py index 8060e11f694..897e7635c46 100644 --- a/addons/account/tests/test_account_move_in_invoice.py +++ b/addons/account/tests/test_account_move_in_invoice.py @@ -849,6 +849,8 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): }) def test_in_invoice_line_onchange_cash_rounding_1(self): + # Required for `invoice_cash_rounding_id` to be visible in the view + self.env.user.groups_id += self.env.ref('account.group_cash_rounding') # Test 'add_invoice_line' rounding move_form = Form(self.invoice) # Add a cash rounding having 'add_invoice_line'. @@ -1193,6 +1195,12 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): }) def test_in_invoice_onchange_past_invoice_1(self): + if self.env.ref('purchase.group_purchase_manager', raise_if_not_found=False): + # `purchase` adds a view which makes `invoice_vendor_bill_id` invisible + # for purchase users + # https://github.com/odoo/odoo/blob/385884afd31f25d61e99d139ecd4c574d99a1863/addons/purchase/views/account_move_views.xml#L26 + self.env.user.groups_id -= self.env.ref('purchase.group_purchase_manager') + self.env.user.groups_id -= self.env.ref('purchase.group_purchase_user') copy_invoice = self.invoice.copy() move_form = Form(self.invoice) diff --git a/addons/account/tests/test_account_move_in_refund.py b/addons/account/tests/test_account_move_in_refund.py index c44b32a3f7a..50bd082f28a 100644 --- a/addons/account/tests/test_account_move_in_refund.py +++ b/addons/account/tests/test_account_move_in_refund.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- # pylint: disable=bad-whitespace +from lxml import etree + from odoo.addons.account.tests.common import AccountTestInvoicingCommon from odoo.tests.common import Form from odoo.tests import tagged @@ -533,6 +535,8 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon): }) def test_in_refund_line_onchange_cash_rounding_1(self): + # Required for `invoice_cash_rounding_id` to be visible in the view + self.env.user.groups_id += self.env.ref('account.group_cash_rounding') # Test 'add_invoice_line' rounding move_form = Form(self.invoice) # Add a cash rounding having 'add_invoice_line'. @@ -878,6 +882,20 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon): def test_in_refund_onchange_past_invoice_1(self): copy_invoice = self.invoice.copy() + if self.env.ref('purchase.group_purchase_manager', raise_if_not_found=False): + # `purchase` adds a view which makes `invoice_vendor_bill_id` invisible + # for purchase users + # https://github.com/odoo/odoo/blob/385884afd31f25d61e99d139ecd4c574d99a1863/addons/purchase/views/account_move_views.xml#L26 + self.env.user.groups_id -= self.env.ref('purchase.group_purchase_manager') + self.env.user.groups_id -= self.env.ref('purchase.group_purchase_user') + # 'invisible': ['|', ('state', '!=', 'draft'), ('move_type', '!=', 'in_invoice')] + # This is an in_refund invoice, `invoice_vendor_bill_id` is not supposed to be visible + # and therefore not supposed to be changed. + view = self.env.ref('account.view_move_form') + tree = etree.fromstring(view.arch) + for node in tree.xpath('//field[@name="invoice_vendor_bill_id"]'): + del node.attrib['attrs'] + view.arch = etree.tostring(tree) move_form = Form(self.invoice) move_form.invoice_line_ids.remove(0) diff --git a/addons/account/tests/test_account_move_out_invoice.py b/addons/account/tests/test_account_move_out_invoice.py index 7cb6bb9b560..497b3860abd 100644 --- a/addons/account/tests/test_account_move_out_invoice.py +++ b/addons/account/tests/test_account_move_out_invoice.py @@ -1453,6 +1453,8 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): ]) def test_out_invoice_line_onchange_cash_rounding_1(self): + # Required for `invoice_cash_rounding_id` to be visible in the view + self.env.user.groups_id += self.env.ref('account.group_cash_rounding') # Test 'add_invoice_line' rounding move_form = Form(self.invoice) # Add a cash rounding having 'add_invoice_line'. @@ -1977,7 +1979,8 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): a custom reversal date. ''' move_form = Form(self.invoice) - move_form.date = '2016-01-01' + # `date` is invisible in the view, the date of the invoice should be set using `invoice_date` instead + move_form.invoice_date = '2016-01-01' move_form.currency_id = self.currency_data['currency'] move_form.save() @@ -3459,6 +3462,8 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): move_form.partner_id = self.partner_a # Quick edit total amount not activated yet + # As quick edit total is not yet activated, it's invisible by default in the view + move_form._view['modifiers']['quick_edit_total_amount']['invisible'] = False move_form.quick_edit_total_amount = 100.0 invoice = move_form.save() self.assertEqual(invoice.amount_total, 0.0) diff --git a/addons/account/tests/test_account_move_out_refund.py b/addons/account/tests/test_account_move_out_refund.py index 9cdd3a6b81a..95735bc92b2 100644 --- a/addons/account/tests/test_account_move_out_refund.py +++ b/addons/account/tests/test_account_move_out_refund.py @@ -532,6 +532,8 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon): }) def test_out_refund_line_onchange_cash_rounding_1(self): + # Required for `invoice_cash_rounding_id` to be visible in the view + self.env.user.groups_id += self.env.ref('account.group_cash_rounding') # Test 'add_invoice_line' rounding move_form = Form(self.invoice) # Add a cash rounding having 'add_invoice_line'. diff --git a/addons/account/tests/test_account_move_reconcile.py b/addons/account/tests/test_account_move_reconcile.py index 9209a44a8f8..20a82816ba2 100644 --- a/addons/account/tests/test_account_move_reconcile.py +++ b/addons/account/tests/test_account_move_reconcile.py @@ -3536,6 +3536,8 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): """ Test the CABA entries generated from an invoice with almost equal lines, different only on analytic accounting """ + # Required for `analytic_account_id` to be visible in the view + self.env.user.groups_id += self.env.ref('analytic.group_analytic_accounting') # Make the tax account reconcilable self.tax_account_1.reconcile = True diff --git a/addons/account/tests/test_account_payment.py b/addons/account/tests/test_account_payment.py index bd5bebe0b99..115627f4277 100644 --- a/addons/account/tests/test_account_payment.py +++ b/addons/account/tests/test_account_payment.py @@ -200,10 +200,14 @@ class TestAccountPayment(AccountTestInvoicingCommon): def test_payment_move_sync_onchange(self): - pay_form = Form(self.env['account.payment'].with_context(default_journal_id=self.company_data['default_journal_bank'].id)) + pay_form = Form(self.env['account.payment'].with_context( + default_journal_id=self.company_data['default_journal_bank'].id, + # The `partner_type` is set through the window action context in the web client + # the field is otherwise invisible in the form. + default_partner_type='customer', + )) pay_form.amount = 50.0 pay_form.payment_type = 'inbound' - pay_form.partner_type = 'customer' pay_form.partner_id = self.partner_a payment = pay_form.save() @@ -246,8 +250,12 @@ class TestAccountPayment(AccountTestInvoicingCommon): # ==== Check editing the account.payment ==== + # `partner_type` on payment is always invisible. It's supposed to be set through a context `default_` key + # In this case the goal of the test is to take an existing customer payment and change it to a supplier payment, + # which is not supposed to be possible through the web interface. + # So, change the payment partner_type beforehand rather than in the form view. + payment.partner_type = 'supplier' pay_form = Form(payment) - pay_form.partner_type = 'supplier' pay_form.currency_id = self.currency_data['currency'] pay_form.partner_id = self.partner_a payment = pay_form.save() diff --git a/addons/account/tests/test_payment_term.py b/addons/account/tests/test_payment_term.py index fb40d38c126..a3133247f6c 100644 --- a/addons/account/tests/test_payment_term.py +++ b/addons/account/tests/test_payment_term.py @@ -73,7 +73,6 @@ class TestAccountInvoiceRounding(AccountTestInvoicingCommon): with Form(self.invoice) as move_form: move_form.invoice_payment_term_id = pay_term move_form.invoice_date = invoice_date - move_form.date = invoice_date self.assertEqual( self.invoice.line_ids.filtered( lambda l: l.account_id == self.company_data['default_account_receivable'] diff --git a/addons/crm/tests/test_crm_lead.py b/addons/crm/tests/test_crm_lead.py index e1bd9854275..34acfcad160 100644 --- a/addons/crm/tests/test_crm_lead.py +++ b/addons/crm/tests/test_crm_lead.py @@ -287,105 +287,110 @@ class TestCRMLead(TestCrmCommon): phone and email fields. Phone especially has some corner cases due to automatic formatting (notably with onchange in form view). """ lead, partner = self.lead_1.with_user(self.env.user), self.contact_2 - lead_form = Form(lead) + # This is a type == 'lead', not a type == 'opportunity' + # {'invisible': ['|', ('type', '=', 'opportunity'), ('is_partner_visible', '=', False)]} + # lead.is_partner_visible = bool(lead.type == 'opportunity' or lead.partner_id or is_debug_mode) + # Hence, debug mode required for `partner_id` to be visible + with self.debug_mode(): + lead_form = Form(lead) - # reset partner phone to a local number and prepare formatted / sanitized values - partner_phone, partner_mobile = self.test_phone_data[2], self.test_phone_data[1] - partner_phone_formatted = phone_format(partner_phone, 'US', '1') - partner_phone_sanitized = phone_format(partner_phone, 'US', '1', force_format='E164') - partner_mobile_formatted = phone_format(partner_mobile, 'US', '1') - partner_mobile_sanitized = phone_format(partner_mobile, 'US', '1', force_format='E164') - partner_email, partner_email_normalized = self.test_email_data[2], self.test_email_data_normalized[2] - self.assertEqual(partner_phone_formatted, '+1 202-555-0888') - self.assertEqual(partner_phone_sanitized, self.test_phone_data_sanitized[2]) - self.assertEqual(partner_mobile_formatted, '+1 202-555-0999') - self.assertEqual(partner_mobile_sanitized, self.test_phone_data_sanitized[1]) - # ensure initial data - self.assertEqual(partner.phone, partner_phone) - self.assertEqual(partner.mobile, partner_mobile) - self.assertEqual(partner.email, partner_email) + # reset partner phone to a local number and prepare formatted / sanitized values + partner_phone, partner_mobile = self.test_phone_data[2], self.test_phone_data[1] + partner_phone_formatted = phone_format(partner_phone, 'US', '1') + partner_phone_sanitized = phone_format(partner_phone, 'US', '1', force_format='E164') + partner_mobile_formatted = phone_format(partner_mobile, 'US', '1') + partner_mobile_sanitized = phone_format(partner_mobile, 'US', '1', force_format='E164') + partner_email, partner_email_normalized = self.test_email_data[2], self.test_email_data_normalized[2] + self.assertEqual(partner_phone_formatted, '+1 202-555-0888') + self.assertEqual(partner_phone_sanitized, self.test_phone_data_sanitized[2]) + self.assertEqual(partner_mobile_formatted, '+1 202-555-0999') + self.assertEqual(partner_mobile_sanitized, self.test_phone_data_sanitized[1]) + # ensure initial data + self.assertEqual(partner.phone, partner_phone) + self.assertEqual(partner.mobile, partner_mobile) + self.assertEqual(partner.email, partner_email) - # LEAD/PARTNER SYNC: email and phone are propagated to lead - # as well as mobile (who does not trigger the reverse sync) - lead_form.partner_id = partner - self.assertEqual(lead_form.email_from, partner_email) - self.assertEqual(lead_form.phone, partner_phone_formatted, - 'Lead: form automatically formats numbers') - self.assertEqual(lead_form.mobile, partner_mobile_formatted, - 'Lead: form automatically formats numbers') - self.assertFalse(lead_form.partner_email_update) - self.assertFalse(lead_form.partner_phone_update) + # LEAD/PARTNER SYNC: email and phone are propagated to lead + # as well as mobile (who does not trigger the reverse sync) + lead_form.partner_id = partner + self.assertEqual(lead_form.email_from, partner_email) + self.assertEqual(lead_form.phone, partner_phone_formatted, + 'Lead: form automatically formats numbers') + self.assertEqual(lead_form.mobile, partner_mobile_formatted, + 'Lead: form automatically formats numbers') + self.assertFalse(lead_form.partner_email_update) + self.assertFalse(lead_form.partner_phone_update) - lead_form.save() - self.assertEqual(partner.phone, partner_phone, - 'Lead / Partner: partner values sent to lead') - self.assertEqual(lead.email_from, partner_email, - 'Lead / Partner: partner values sent to lead') - self.assertEqual(lead.email_normalized, partner_email_normalized, - 'Lead / Partner: equal emails should lead to equal normalized emails') - self.assertEqual(lead.phone, partner_phone_formatted, - 'Lead / Partner: partner values (formatted) sent to lead') - self.assertEqual(lead.mobile, partner_mobile_formatted, - 'Lead / Partner: partner values (formatted) sent to lead') - self.assertEqual(lead.phone_sanitized, partner_mobile_sanitized, - 'Lead: phone_sanitized computed field on mobile') + lead_form.save() + self.assertEqual(partner.phone, partner_phone, + 'Lead / Partner: partner values sent to lead') + self.assertEqual(lead.email_from, partner_email, + 'Lead / Partner: partner values sent to lead') + self.assertEqual(lead.email_normalized, partner_email_normalized, + 'Lead / Partner: equal emails should lead to equal normalized emails') + self.assertEqual(lead.phone, partner_phone_formatted, + 'Lead / Partner: partner values (formatted) sent to lead') + self.assertEqual(lead.mobile, partner_mobile_formatted, + 'Lead / Partner: partner values (formatted) sent to lead') + self.assertEqual(lead.phone_sanitized, partner_mobile_sanitized, + 'Lead: phone_sanitized computed field on mobile') - # for email_from, if only formatting differs, warning should not appear and - # email on partner should not be updated - lead_form.email_from = '"Hermes Conrad" <%s>' % partner_email_normalized - self.assertFalse(lead_form.partner_email_update) - lead_form.save() - self.assertEqual(partner.email, partner_email) + # for email_from, if only formatting differs, warning should not appear and + # email on partner should not be updated + lead_form.email_from = '"Hermes Conrad" <%s>' % partner_email_normalized + self.assertFalse(lead_form.partner_email_update) + lead_form.save() + self.assertEqual(partner.email, partner_email) - # for phone, if only formatting differs, warning should not appear and - # phone on partner should not be updated - lead_form.phone = partner_phone_sanitized - self.assertFalse(lead_form.partner_phone_update) - lead_form.save() - self.assertEqual(partner.phone, partner_phone) + # for phone, if only formatting differs, warning should not appear and + # phone on partner should not be updated + lead_form.phone = partner_phone_sanitized + self.assertFalse(lead_form.partner_phone_update) + lead_form.save() + self.assertEqual(partner.phone, partner_phone) - # LEAD/PARTNER SYNC: lead updates partner - new_email = '"John Zoidberg" ' - new_email_normalized = 'john.zoidberg@test.example.com' - lead_form.email_from = new_email - self.assertTrue(lead_form.partner_email_update) - new_phone = '+1 202 555 7799' - new_phone_formatted = phone_format(new_phone, 'US', '1') - lead_form.phone = new_phone - self.assertEqual(lead_form.phone, new_phone_formatted) - self.assertTrue(lead_form.partner_email_update) - self.assertTrue(lead_form.partner_phone_update) + # LEAD/PARTNER SYNC: lead updates partner + new_email = '"John Zoidberg" ' + new_email_normalized = 'john.zoidberg@test.example.com' + lead_form.email_from = new_email + self.assertTrue(lead_form.partner_email_update) + new_phone = '+1 202 555 7799' + new_phone_formatted = phone_format(new_phone, 'US', '1') + lead_form.phone = new_phone + self.assertEqual(lead_form.phone, new_phone_formatted) + self.assertTrue(lead_form.partner_email_update) + self.assertTrue(lead_form.partner_phone_update) - lead_form.save() - self.assertEqual(partner.email, new_email) - self.assertEqual(partner.email_normalized, new_email_normalized) - self.assertEqual(partner.phone, new_phone_formatted) + lead_form.save() + self.assertEqual(partner.email, new_email) + self.assertEqual(partner.email_normalized, new_email_normalized) + self.assertEqual(partner.phone, new_phone_formatted) - # LEAD/PARTNER SYNC: mobile does not update partner - new_mobile = '+1 202 555 6543' - new_mobile_formatted = phone_format(new_mobile, 'US', '1') - lead_form.mobile = new_mobile - lead_form.save() - self.assertEqual(lead.mobile, new_mobile_formatted) - self.assertEqual(partner.mobile, partner_mobile) + # LEAD/PARTNER SYNC: mobile does not update partner + new_mobile = '+1 202 555 6543' + new_mobile_formatted = phone_format(new_mobile, 'US', '1') + lead_form.mobile = new_mobile + lead_form.save() + self.assertEqual(lead.mobile, new_mobile_formatted) + self.assertEqual(partner.mobile, partner_mobile) - # LEAD/PARTNER SYNC: reseting lead values also resets partner for email - # and phone, but not for mobile - lead_form.email_from, lead_form.phone, lead.mobile = False, False, False - self.assertTrue(lead_form.partner_email_update) - self.assertTrue(lead_form.partner_phone_update) - lead_form.save() - self.assertFalse(partner.email) - self.assertFalse(partner.email_normalized) - self.assertFalse(partner.phone) - self.assertFalse(lead.phone) - self.assertFalse(lead.mobile) - self.assertFalse(lead.phone_sanitized) - self.assertEqual(partner.mobile, partner_mobile) - # if SMS is uninstalled, phone_sanitized is not available on partner - if 'phone_sanitized' in partner: - self.assertEqual(partner.phone_sanitized, partner_mobile_sanitized, - 'Partner sanitized should be computed on mobile') + # LEAD/PARTNER SYNC: reseting lead values also resets partner for email + # and phone, but not for mobile + lead_form.email_from, lead_form.phone, lead.mobile = False, False, False + self.assertTrue(lead_form.partner_email_update) + self.assertTrue(lead_form.partner_phone_update) + lead_form.save() + self.assertFalse(partner.email) + self.assertFalse(partner.email_normalized) + self.assertFalse(partner.phone) + self.assertFalse(lead.phone) + self.assertFalse(lead.mobile) + self.assertFalse(lead.phone_sanitized) + self.assertEqual(partner.mobile, partner_mobile) + # if SMS is uninstalled, phone_sanitized is not available on partner + if 'phone_sanitized' in partner: + self.assertEqual(partner.phone_sanitized, partner_mobile_sanitized, + 'Partner sanitized should be computed on mobile') @users('user_sales_manager') def test_crm_lead_partner_sync_email_phone_corner_cases(self): @@ -401,69 +406,74 @@ class TestCRMLead(TestCrmCommon): 'mobile': '', }) - lead_form = Form(lead) - self.assertEqual(lead_form.email_from, test_email) - self.assertFalse(lead_form.partner_email_update) - self.assertFalse(lead_form.partner_phone_update) + # This is a type == 'lead', not a type == 'opportunity' + # {'invisible': ['|', ('type', '=', 'opportunity'), ('is_partner_visible', '=', False)]} + # lead.is_partner_visible = bool(lead.type == 'opportunity' or lead.partner_id or is_debug_mode) + # Hence, debug mode required for `partner_id` to be visible + with self.debug_mode(): + lead_form = Form(lead) + self.assertEqual(lead_form.email_from, test_email) + self.assertFalse(lead_form.partner_email_update) + self.assertFalse(lead_form.partner_phone_update) - # email: False versus empty string - lead_form.partner_id = contact - self.assertTrue(lead_form.partner_email_update) - self.assertFalse(lead_form.partner_phone_update) - lead_form.email_from = '' - self.assertFalse(lead_form.partner_email_update) - lead_form.email_from = False - self.assertFalse(lead_form.partner_email_update) + # email: False versus empty string + lead_form.partner_id = contact + self.assertTrue(lead_form.partner_email_update) + self.assertFalse(lead_form.partner_phone_update) + lead_form.email_from = '' + self.assertFalse(lead_form.partner_email_update) + lead_form.email_from = False + self.assertFalse(lead_form.partner_email_update) - # phone: False versus empty string - lead_form.phone = '+1 202-555-0888' - self.assertFalse(lead_form.partner_email_update) - self.assertTrue(lead_form.partner_phone_update) - lead_form.phone = '' - self.assertFalse(lead_form.partner_phone_update) - lead_form.phone = False - self.assertFalse(lead_form.partner_phone_update) + # phone: False versus empty string + lead_form.phone = '+1 202-555-0888' + self.assertFalse(lead_form.partner_email_update) + self.assertTrue(lead_form.partner_phone_update) + lead_form.phone = '' + self.assertFalse(lead_form.partner_phone_update) + lead_form.phone = False + self.assertFalse(lead_form.partner_phone_update) - # email/phone: formatting should not trigger ribbon - lead.write({ - 'email_from': '"My Name" <%s>' % test_email, - 'phone': '+1 202-555-0888', - }) - contact.write({ - 'email': '"My Name" <%s>' % test_email, - 'phone': '+1 202-555-0888', - }) + # email/phone: formatting should not trigger ribbon + lead.write({ + 'email_from': '"My Name" <%s>' % test_email, + 'phone': '+1 202-555-0888', + }) + contact.write({ + 'email': '"My Name" <%s>' % test_email, + 'phone': '+1 202-555-0888', + }) - lead_form = Form(lead) - self.assertFalse(lead_form.partner_email_update) - self.assertFalse(lead_form.partner_phone_update) - lead_form.partner_id = contact - self.assertFalse(lead_form.partner_email_update) - self.assertFalse(lead_form.partner_phone_update) - lead_form.email_from = '"Another Name" <%s>' % test_email # same email normalized - self.assertFalse(lead_form.partner_email_update, 'Formatting-only change should not trigger write') - self.assertFalse(lead_form.partner_phone_update, 'Formatting-only change should not trigger write') - lead_form.phone = '2025550888' # same number but another format - self.assertFalse(lead_form.partner_email_update, 'Formatting-only change should not trigger write') - self.assertFalse(lead_form.partner_phone_update, 'Formatting-only change should not trigger write') + lead_form = Form(lead) + self.assertFalse(lead_form.partner_email_update) + self.assertFalse(lead_form.partner_phone_update) + lead_form.partner_id = contact + self.assertFalse(lead_form.partner_email_update) + self.assertFalse(lead_form.partner_phone_update) + lead_form.email_from = '"Another Name" <%s>' % test_email # same email normalized + self.assertFalse(lead_form.partner_email_update, 'Formatting-only change should not trigger write') + self.assertFalse(lead_form.partner_phone_update, 'Formatting-only change should not trigger write') + lead_form.phone = '2025550888' # same number but another format + self.assertFalse(lead_form.partner_email_update, 'Formatting-only change should not trigger write') + self.assertFalse(lead_form.partner_phone_update, 'Formatting-only change should not trigger write') - # wrong value are also propagated - lead_form.phone = '666 789456789456789456' - self.assertTrue(lead_form.partner_phone_update) + # wrong value are also propagated + lead_form.phone = '666 789456789456789456' + self.assertTrue(lead_form.partner_phone_update) - # test country propagation allowing to correctly compute sanitized numbers - # by adding missing relevant information from contact - be_country = self.env.ref('base.be') - contact.write({ - 'country_id': be_country.id, - 'phone': '+32456001122', - }) - lead.write({'country_id': False}) - lead_form = Form(lead) - lead_form.partner_id = contact - lead_form.phone = '0456 00 11 22' - self.assertFalse(lead_form.partner_phone_update) - self.assertEqual(lead_form.country_id, be_country) + # test country propagation allowing to correctly compute sanitized numbers + # by adding missing relevant information from contact + be_country = self.env.ref('base.be') + contact.write({ + 'country_id': be_country.id, + 'phone': '+32456001122', + }) + lead.write({'country_id': False}) + lead_form = Form(lead) + lead_form.partner_id = contact + lead_form.phone = '0456 00 11 22' + self.assertFalse(lead_form.partner_phone_update) + self.assertEqual(lead_form.country_id, be_country) @users('user_sales_manager') diff --git a/addons/crm/tests/test_res_partner.py b/addons/crm/tests/test_res_partner.py index e8b5c295496..3525801f5a6 100644 --- a/addons/crm/tests/test_res_partner.py +++ b/addons/crm/tests/test_res_partner.py @@ -46,6 +46,20 @@ class TestPartner(TestCrmCommon): # test form tool partner_form = Form(self.env['res.partner'], 'base.view_partner_form') + # `parent_id` is invisible when `is_company` is True (`company_type == 'company'`) + # and parent_id is not set + # So, set a temporary `parent_id` before setting the contact as company + # to make `parent_id` visible in the interface while being a company + # + partner_form.parent_id = contact_company_1 partner_form.company_type = 'company' partner_form.parent_id = contact_company partner_form.name = 'Mom Corp' diff --git a/addons/hr_holidays/tests/test_automatic_leave_dates.py b/addons/hr_holidays/tests/test_automatic_leave_dates.py index 87f554b7b3a..2c47d3d95a6 100644 --- a/addons/hr_holidays/tests/test_automatic_leave_dates.py +++ b/addons/hr_holidays/tests/test_automatic_leave_dates.py @@ -16,6 +16,8 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon): 'name': 'Automatic Test', 'time_type': 'leave', 'requires_allocation': 'no', + # Required for `request_unit_half` to be visible in the view + 'request_unit': 'half_day', }) def test_no_attendances(self): diff --git a/addons/hr_holidays/tests/test_leave_requests.py b/addons/hr_holidays/tests/test_leave_requests.py index fdc161cc131..c70efec9464 100644 --- a/addons/hr_holidays/tests/test_leave_requests.py +++ b/addons/hr_holidays/tests/test_leave_requests.py @@ -271,6 +271,18 @@ class TestLeaveRequests(TestHrHolidaysCommon): leave_form.holiday_status_id = self.holidays_type_1 leave_form.request_date_from = date(2019, 5, 6) leave_form.request_date_to = date(2019, 5, 6) + # TODO: The test is wrong by modifying `date_from` and `date_to`, which are invisible + # It should edit only `request_date_from` and `request_date_to` instead + # And there is really a bug. Using the web client, when you put your PC in Auckland timezone, + # and the admin preferences in Auckland Timezone + # and create a time off for the current day, the computation is completely wrong + # and compute the date to before the date from *-) + # For instance, for a time-off from 06/16/2022 to 06/16/2022 (1 day) it computes + # 06/16/2022 08:00:00 as date_from and 06/15/2022 17:00:00 as date_to + # Bug reported to the rd-fun-vidange channel to the dev who introduced the bug + # https://discord.com/channels/678381219515465750/687337760452902925/986918361768263710 + leave_form._view['modifiers']['date_from']['invisible'] = False + leave_form._view['modifiers']['date_to']['invisible'] = False leave_form.date_from = datetime(2019, 5, 6, 0, 0, 0) leave_form.date_to = datetime(2019, 5, 6, 23, 59, 59) leave = leave_form.save() diff --git a/addons/hr_holidays/tests/test_stress_days.py b/addons/hr_holidays/tests/test_stress_days.py index 4408d83d9b4..1b09f824d4a 100644 --- a/addons/hr_holidays/tests/test_stress_days.py +++ b/addons/hr_holidays/tests/test_stress_days.py @@ -111,9 +111,9 @@ class TestHrLeaveStressDays(TransactionCase): with self.assertRaises(ValidationError), Form(self.env['hr.leave'].with_user(self.employee_user.id).with_context(default_employee_id=self.employee_emp.id)) as leave_form: leave_form.holiday_status_id = self.leave_type - leave_form.date_from = datetime(2021, 11, 1) - leave_form.date_to = datetime(2021, 11, 1) + leave_form.request_date_from = datetime(2021, 11, 1) + leave_form.request_date_to = datetime(2021, 11, 1) self.assertFalse(leave_form.has_stress_day) - leave_form.date_to = datetime(2021, 11, 5) + leave_form.request_date_to = datetime(2021, 11, 5) self.assertTrue(leave_form.has_stress_day) diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py index 311cf1819f1..bca15a54a45 100644 --- a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py @@ -17,6 +17,9 @@ class TestUBLCommon(AccountEdiTestCommon): def setUpClass(cls, chart_template_ref=None, edi_format_ref=None): super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref) + # Required for `product_uom_id` to be visible in the form views + cls.env.user.groups_id += cls.env.ref('uom.group_uom') + # Ensure the testing currency is using a valid ISO code. real_usd = cls.env.ref('base.USD') real_usd.name = 'FUSD' diff --git a/addons/mass_mailing/tests/test_mailing_list.py b/addons/mass_mailing/tests/test_mailing_list.py index 38aaba6b1f1..6fa18139357 100644 --- a/addons/mass_mailing/tests/test_mailing_list.py +++ b/addons/mass_mailing/tests/test_mailing_list.py @@ -122,8 +122,10 @@ class TestMailingListMerge(MassMailCommon): active_model='mailing.list' )) merge_form.new_list_name = False - merge_form.dest_list_id = self.mailing_list_3 merge_form.merge_options = 'existing' + # Need to set `merge_options` before `dest_lid_id` so `dest_list_id` is visible + # `'invisible': [('merge_options', '=', 'new')]` + merge_form.dest_list_id = self.mailing_list_3 merge_form.archive_src_lists = False result_list = merge_form.save().action_mailing_lists_merge() diff --git a/addons/mrp/tests/common.py b/addons/mrp/tests/common.py index 3a837202bca..0237d71022c 100644 --- a/addons/mrp/tests/common.py +++ b/addons/mrp/tests/common.py @@ -66,7 +66,7 @@ class TestMrpCommon(common2.TestStockCommon): login='hilda', email='h.h@example.com', notification_type='inbox', - groups='mrp.group_mrp_user, stock.group_stock_user, mrp.group_mrp_byproducts', + groups='mrp.group_mrp_user, stock.group_stock_user, mrp.group_mrp_byproducts, uom.group_uom', ) cls.user_mrp_manager = mail_new_test_user( cls.env, @@ -74,8 +74,11 @@ class TestMrpCommon(common2.TestStockCommon): login='gary', email='g.g@example.com', notification_type='inbox', - groups='mrp.group_mrp_manager, stock.group_stock_user, mrp.group_mrp_byproducts', + groups='mrp.group_mrp_manager, stock.group_stock_user, mrp.group_mrp_byproducts, uom.group_uom', ) + # Required for `product_uom_id` to be visible in the view + # This class is used by a lot of tests which sets `product_uom_id` on `mrp.production` + cls.env.user.groups_id += cls.env.ref('uom.group_uom') cls.workcenter_1 = cls.env['mrp.workcenter'].create({ 'name': 'Nuclear Workcenter', diff --git a/addons/mrp/tests/test_backorder.py b/addons/mrp/tests/test_backorder.py index 6dd33195823..543aafdc67e 100644 --- a/addons/mrp/tests/test_backorder.py +++ b/addons/mrp/tests/test_backorder.py @@ -85,6 +85,8 @@ class TestMrpProductionBackorder(TestMrpCommon): should be MO/001-02. Check that all MO are reachable through the procurement group. """ + # Required for `manufacture_steps` to be visible in the view + self.env.user.groups_id += self.env.ref("stock.group_adv_location") with Form(self.warehouse) as warehouse: warehouse.manufacture_steps = 'pbm' @@ -128,6 +130,8 @@ class TestMrpProductionBackorder(TestMrpCommon): should be MO/001-02. Check that all MO are reachable through the procurement group. """ + # Required for `manufacture_steps` to be visible in the view + self.env.user.groups_id += self.env.ref("stock.group_adv_location") with Form(self.warehouse) as warehouse: warehouse.manufacture_steps = 'pbm_sam' production, _, product_to_build, product_to_use_1, product_to_use_2 = self.generate_mo(qty_base_1=4, qty_final=4, picking_type_id=self.warehouse.manu_type_id) diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py index 5afb46081c5..2fac4c75609 100644 --- a/addons/mrp/tests/test_bom.py +++ b/addons/mrp/tests/test_bom.py @@ -496,6 +496,8 @@ class TestBoM(TestMrpCommon): 'name': 'Deserts Table' }) + # Required to display `operation_ids` in the form view + self.env.user.groups_id += self.env.ref("mrp.group_mrp_routings") with Form(bom_crumble) as bom: with bom.bom_line_ids.new() as line: line.product_id = butter @@ -660,6 +662,8 @@ class TestBoM(TestMrpCommon): 'name': 'Deserts Table' }) + # Required to display `operation_ids` in the form view + self.env.user.groups_id += self.env.ref("mrp.group_mrp_routings") with Form(bom_drawer) as bom: with bom.bom_line_ids.new() as line: line.product_id = screw diff --git a/addons/mrp/tests/test_multicompany.py b/addons/mrp/tests/test_multicompany.py index 83e4d3fe051..aeca55a0560 100644 --- a/addons/mrp/tests/test_multicompany.py +++ b/addons/mrp/tests/test_multicompany.py @@ -134,6 +134,11 @@ class TestMrpMulticompany(common.TransactionCase): }) mo_form = Form(self.env['mrp.production'].with_user(self.user_a)) mo_form.product_id = product + # The mo must be confirmed, no longer in draft, in order for `lot_producing_id` to be visible in the view + #
+ mo = mo_form.save() + mo.action_confirm() + mo_form = Form(mo) mo_form.lot_producing_id = lot_b mo = mo_form.save() with self.assertRaises(UserError): diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index c71dc488935..56746fd6b1f 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -339,6 +339,8 @@ class TestMrpOrder(TestMrpCommon): def test_update_quantity_4(self): """ Workcenter 1 has 10' start time and 5' stop time """ + # Required for `workerorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') bom = self.env['mrp.bom'].create({ 'product_id': self.product_6.id, 'product_tmpl_id': self.product_6.product_tmpl_id.id, @@ -517,6 +519,11 @@ class TestMrpOrder(TestMrpCommon): self.assertEqual(production.move_raw_ids.mapped('manual_consumption'), [False, True, True]) + # + production.action_confirm() + production.action_assign() + production.is_locked = False + # test no updating production_form = Form(production) production_form.qty_producing = 5 @@ -689,6 +696,11 @@ class TestMrpOrder(TestMrpCommon): self.stock_shelf_2 = self.stock_location_14 mo, _, p_final, p1, p2 = self.generate_mo(tracking_base_1='lot', qty_base_1=10, qty_final=1) + + # Required for `lot_producing_id` to be visible in the view + # + p_final.tracking = 'lot' + self.assertEqual(len(mo), 1, 'MO should have been created') first_lot_for_p1 = self.env['stock.lot'].create({ @@ -999,6 +1011,8 @@ class TestMrpOrder(TestMrpCommon): byproduct3 none 1.0 dozen Check qty producing update and moves finished values. """ + # Required for `byproduct_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_byproducts') dozen = self.env.ref('uom.product_uom_dozen') self.byproduct1 = self.env['product.product'].create({ 'name': 'Byproduct 1', @@ -2368,6 +2382,8 @@ class TestMrpOrder(TestMrpCommon): Create a second one in 10 minutes (expected should NOT go from 15 to 12.5, it should go from 15 to 10) """ # First production, the default is 60 and there is 0 productions of that operation + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') production_form = Form(self.env['mrp.production']) production_form.bom_id = self.bom_4 production = production_form.save() @@ -2375,6 +2391,8 @@ class TestMrpOrder(TestMrpCommon): production.action_confirm() production.button_plan() # Production planned, time to start, I produce all the 1 product + # 'invisible': [('state', '=', 'draft')] + production_form = Form(production) production_form.qty_producing = 1 with production_form.workorder_ids.edit(0) as wo: wo.duration = 15 # in 15 minutes @@ -2390,6 +2408,8 @@ class TestMrpOrder(TestMrpCommon): production.action_confirm() production.button_plan() # Production planned, time to start, I produce all the 1 product + # 'invisible': [('state', '=', 'draft')] + production_form = Form(production) production_form.qty_producing = 1 with production_form.workorder_ids.edit(0) as wo: wo.duration = 10 # In 10 minutes this time @@ -2410,6 +2430,8 @@ class TestMrpOrder(TestMrpCommon): Test that when tracking the 2 last production, if we make one with under capacity, and one with normal capacity, the two are equivalent (1 done with capacity 2 in 10mn = 2 done with capacity 2 in 10mn) """ + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') production_form = Form(self.env['mrp.production']) production_form.bom_id = self.bom_5 production = production_form.save() @@ -2417,6 +2439,8 @@ class TestMrpOrder(TestMrpCommon): production.button_plan() # Production planned, time to start, I produce all the 1 product + # 'invisible': [('state', '=', 'draft')] + production_form = Form(production) production_form.qty_producing = 1 with production_form.workorder_ids.edit(0) as wo: wo.duration = 10 # in 10 minutes @@ -2434,6 +2458,8 @@ class TestMrpOrder(TestMrpCommon): production.action_confirm() production.button_plan() # Production planned, time to start, I produce all the 2 product + # 'invisible': [('state', '=', 'draft')] + production_form = Form(production) production_form.qty_producing = 2 with production_form.workorder_ids.edit(0) as wo: wo.duration = 10 # In 10 minutes this time @@ -2457,6 +2483,8 @@ class TestMrpOrder(TestMrpCommon): 5 -> 30mn ... """ + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') production_form = Form(self.env['mrp.production']) production_form.bom_id = self.bom_6 production = production_form.save() @@ -2464,6 +2492,8 @@ class TestMrpOrder(TestMrpCommon): production.button_plan() # Production planned, time to start, I produce all the 1 product + # 'invisible': [('state', '=', 'draft')] + production_form = Form(production) production_form.qty_producing = 1 with production_form.workorder_ids.edit(0) as wo: wo.duration = 10 # in 10 minutes @@ -2760,6 +2790,8 @@ class TestMrpOrder(TestMrpCommon): """ Check that the work order is started only once when clicking the start button several times. """ + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') production_form = Form(self.env['mrp.production']) production_form.bom_id = self.bom_2 production_form.product_qty = 1 @@ -2869,6 +2901,8 @@ class TestMrpOrder(TestMrpCommon): -> The user replans one of the WO: the warnings should disappear and the WO should be postponed. """ + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') mos = self.env['mrp.production'] for _ in range(2): mo_form = Form(self.env['mrp.production']) @@ -2899,6 +2933,8 @@ class TestMrpOrder(TestMrpCommon): -> The user replans one of the WO: the warnings should disappear and the WO should be postponed. """ + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') mos = self.env['mrp.production'] for _ in range(2): mo_form = Form(self.env['mrp.production']) diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py index d2f4743f2b5..e37580d67d9 100644 --- a/addons/mrp/tests/test_procurement.py +++ b/addons/mrp/tests/test_procurement.py @@ -681,6 +681,8 @@ class TestProcurement(TestMrpCommon): This test ensures that, when running the scheduler, the generated MOs are based on the correct BoMs """ + # Required for `picking_type_id` to be visible in the view + self.env.user.groups_id += self.env.ref('stock.group_adv_location') warehouse = self.env.ref('stock.warehouse0') stock_location01 = warehouse.lot_stock_id diff --git a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py index 9bce94adc2f..952510439da 100644 --- a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py +++ b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py @@ -11,6 +11,10 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon): @classmethod def setUpClass(cls): super().setUpClass() + # Required for `uom_id` to be visible in the view + cls.env.user.groups_id += cls.env.ref('uom.group_uom') + # Required for `manufacture_steps` to be visible in the view + cls.env.user.groups_id += cls.env.ref('stock.group_adv_location') # Create warehouse cls.customer_location = cls.env['ir.model.data']._xmlid_to_res_id('stock.stock_location_customers') warehouse_form = Form(cls.env['stock.warehouse']) diff --git a/addons/mrp_account/tests/test_analytic_account.py b/addons/mrp_account/tests/test_analytic_account.py index 6f4c25ae654..becba02cb9d 100644 --- a/addons/mrp_account/tests/test_analytic_account.py +++ b/addons/mrp_account/tests/test_analytic_account.py @@ -107,6 +107,8 @@ class TestAnalyticAccount(TransactionCase): """Test when workcenter and MO are using the same analytic account, no duplicated lines will be post. """ + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') # set wc analytic account to be the same of the one on the bom self.workcenter.costs_hour_account_id = self.analytic_account @@ -147,6 +149,8 @@ class TestAnalyticAccount(TransactionCase): """Test when workcenter and MO are using the same analytic account, no duplicated lines will be post. """ + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') # set wc analytic account to be different from the one on the bom wc_analytic_account = self.env['account.analytic.account'].create({'name': 'wc_analytic_account'}) self.workcenter.costs_hour_account_id = wc_analytic_account diff --git a/addons/mrp_account/tests/test_bom_price.py b/addons/mrp_account/tests/test_bom_price.py index 355b39496ce..dff1f023b75 100644 --- a/addons/mrp_account/tests/test_bom_price.py +++ b/addons/mrp_account/tests/test_bom_price.py @@ -19,6 +19,8 @@ class TestBomPrice(common.TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() + # Required for `product_uom_id ` to be visible in the view + cls.env.user.groups_id += cls.env.ref('uom.group_uom') cls.Product = cls.env['product.product'] cls.Bom = cls.env['mrp.bom'] diff --git a/addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py b/addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py index 62c3fcf0ef1..e2b75cd779c 100644 --- a/addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py +++ b/addons/mrp_landed_costs/tests/test_stock_landed_costs_mrp.py @@ -160,6 +160,9 @@ class TestStockLandedCostsMrp(ValuationReconciliationTestCommon): man_order = man_order_form.save() man_order.action_confirm() # produce product + # To edit `qty_producing`, the mo must no be draft. It's not thanks to the above `action_confirm()` + # but the values of the form do not update automatically, it must be reloaded. + man_order_form = Form(man_order) man_order_form.qty_producing = 1 man_order_form.save() man_order.button_mark_done() diff --git a/addons/mrp_subcontracting/tests/test_subcontracting.py b/addons/mrp_subcontracting/tests/test_subcontracting.py index f4d43680e02..99b369fde7a 100644 --- a/addons/mrp_subcontracting/tests/test_subcontracting.py +++ b/addons/mrp_subcontracting/tests/test_subcontracting.py @@ -237,6 +237,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon): picking. Checks that the delivery and MO for its components are automatically created. """ + # Required for `location_id` to be visible in the view + self.env.user.groups_id += self.env.ref('stock.group_stock_multi_locations') # Tick "manufacture" and MTO on self.comp2 mto_route = self.env.ref('stock.route_warehouse0_mto') mto_route.active = True diff --git a/addons/product/tests/test_variants.py b/addons/product/tests/test_variants.py index 4176aea3487..fe6272c04f3 100644 --- a/addons/product/tests/test_variants.py +++ b/addons/product/tests/test_variants.py @@ -1008,6 +1008,8 @@ class TestVariantsArchive(common.TestProductCommon): def test_uom_update_variant(self): """ Changing the uom on the template do not behave the same as changing on the product product.""" + # Required for `uom_id` to be visible in the view + self.env.user.groups_id += self.env.ref("uom.group_uom") units = self.env.ref('uom.product_uom_unit') cm = self.env.ref('uom.product_uom_cm') template = self.env['product.template'].create({ diff --git a/addons/project/tests/test_multicompany.py b/addons/project/tests/test_multicompany.py index 85703c442e7..0236d01e79e 100644 --- a/addons/project/tests/test_multicompany.py +++ b/addons/project/tests/test_multicompany.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from contextlib import contextmanager +from lxml import etree from odoo.tests.common import TransactionCase, Form from odoo.exceptions import AccessError, UserError @@ -257,10 +258,24 @@ class TestMultiCompanyProject(TestMultiCompanyCommon): # set parent on existing orphan task; the onchange will set the correct company and subtask project self.task_2.write({'project_id': False}) - with Form(self.task_2) as task_form: - task_form.name = 'Test Task 2 becomes child of Task 1 (other company)' - task_form.parent_id = self.task_1 - task = task_form.save() + # For `parent_id` to be visible in the view, you need + # 1. The debug mode + # 2. `allow_subtasks` to be true + # + # `allow_subtasks` is a related to `allow_subtasks` on the project + # as the point of the test is to test the behavior of the task `_compute_project_id` when there is no project, + # `allow_subtasks` is by default invisible, and you shouldn't therefore be able to change it. + # So, to make it visible, temporary modify the view to make it visible even when `allow_subtasks` is `False`. + view = self.env.ref('project.view_task_form2').sudo() + tree = etree.fromstring(view.arch) + for node in tree.xpath('//field[@name="parent_id"][@attrs]'): + node.attrib.pop('attrs') + view.arch = etree.tostring(tree) + with self.debug_mode(): + with Form(self.task_2) as task_form: + task_form.name = 'Test Task 2 becomes child of Task 1 (other company)' + task_form.parent_id = self.task_1 + task = task_form.save() self.assertEqual(task.company_id, task.project_id.company_id, "The company of the orphan subtask should be the one from its project.") @@ -268,13 +283,28 @@ class TestMultiCompanyProject(TestMultiCompanyCommon): # set up default subtask project self.project_company_a.write({'allow_subtasks': True}) + # For `parent_id` to be visible in the view, you need + # 1. The debug mode + # 2. `allow_subtasks` to be true + # + # `allow_subtasks` is a related to `allow_subtasks` on the project + # as the point of the test is to test the behavior of the task `_compute_project_id` when there is no project, + # `allow_subtasks` is by default invisible, and you shouldn't therefore be able to change it. + # So, to make it visible, temporary modify the view to make it visible even when `allow_subtasks` is `False`. + view = self.env.ref('project.view_task_form2').sudo() + tree = etree.fromstring(view.arch) + for node in tree.xpath('//field[@name="parent_id"][@attrs]'): + node.attrib.pop('attrs') + view.arch = etree.tostring(tree) + with self.sudo('employee-a'): with self.allow_companies([self.company_a.id, self.company_b.id]): - with Form(self.env['project.task'].with_context({'tracking_disable': True})) as task_form: - task_form.name = 'Test Subtask in company B' - task_form.parent_id = self.task_1 + with self.debug_mode(): + with Form(self.env['project.task'].with_context({'tracking_disable': True})) as task_form: + task_form.name = 'Test Subtask in company B' + task_form.parent_id = self.task_1 - task = task_form.save() + task = task_form.save() self.assertEqual(task.project_id, self.task_1.project_id, "The default project of a subtask should be the default subtask project of the project from the mother task") self.assertEqual(task.company_id, task.project_id.company_id, "The company of the orphan subtask should be the one from its project.") diff --git a/addons/project/tests/test_project_recurrence.py b/addons/project/tests/test_project_recurrence.py index f68aa81a5c1..7f3d68e86ae 100644 --- a/addons/project/tests/test_project_recurrence.py +++ b/addons/project/tests/test_project_recurrence.py @@ -465,6 +465,9 @@ class TestProjectrecurrence(TransactionCase): tasks = self.env['project.task'].search(domain) return tasks, len(tasks), len(tasks.filtered('parent_id')) + # Required for `child_ids` to be visible in the view + # {'invisible': [('allow_subtasks', '=', False)]} + self.project_recurring.allow_subtasks = True parent_task = self.env['project.task'].create({ 'name': 'Parent Task', 'project_id': self.project_recurring.id @@ -558,6 +561,9 @@ class TestProjectrecurrence(TransactionCase): return len(tasks), len(tasks.filtered('parent_id')) # Phase 0 : Initialize test case + # Required for `child_ids` to be visible in the view + # {'invisible': [('allow_subtasks', '=', False)]} + self.project_recurring.allow_subtasks = True parent_task = self.env['project.task'].create({ 'name': 'Parent Task', 'project_id': self.project_recurring.id diff --git a/addons/project/tests/test_project_sharing.py b/addons/project/tests/test_project_sharing.py index a88756641db..7829069233f 100644 --- a/addons/project/tests/test_project_sharing.py +++ b/addons/project/tests/test_project_sharing.py @@ -144,6 +144,10 @@ class TestProjectSharing(TestProjectSharingCommon): 3.2) Create a sub-task 3.3) Create a second sub-task """ + # 0) Allow to create subtasks in the project tasks + # Required for `child_ids` to be visible in the view + # {'invisible': [('allow_subtasks', '=', False)]} + self.project_cows.allow_subtasks = True # 1) Give the 'read' access mode to a portal user in a project and try to create task with this user. with self.assertRaises(AccessError, msg="Should not accept the portal user create a task in the project when he has not the edit access right."): with self.get_project_sharing_form_view(self.task_cow.with_context({'tracking_disable': True, 'default_project_id': self.project_cows.id}), self.user_portal) as form: diff --git a/addons/project/tests/test_project_subtasks.py b/addons/project/tests/test_project_subtasks.py index 5e1bf1aeaa3..66f134417fa 100644 --- a/addons/project/tests/test_project_subtasks.py +++ b/addons/project/tests/test_project_subtasks.py @@ -72,8 +72,10 @@ class TestProjectSubtasks(TestProjectCommon): """ form_views = self.env['ir.ui.view'].search([('model', '=', 'project.task'), ('type', '=', 'form')]) for form_view in form_views: - with Form(self.env['project.task'].with_context({'tracking_disable': True, 'default_project_id': self.project_pigs.id}), view=form_view) as task_form: - task_form.name = 'Test Task 1' + task_form = Form(self.env['project.task'].with_context({'tracking_disable': True, 'default_project_id': self.project_pigs.id, 'default_name': 'Test Task 1'}), view=form_view) + # Some views have the `name` field invisible + # As the goal is simply to test the default project field and not the name, we can skip setting the name + # in the view and set it using `default_name` instead task = task_form.save() self.assertEqual(task.project_id, self.project_pigs, "The project should be assigned from the default project, form_view name : %s." % form_view.name) @@ -148,27 +150,29 @@ class TestProjectSubtasks(TestProjectCommon): self.assertEqual(self.task_1.child_ids.display_project_id, self.project_goats, "Display Project of the task should be well assigned") self.assertEqual(self.task_1.child_ids.project_id, self.project_goats, "Changing display project id on a subtask should change project id") - # 6) - with Form(self.task_1.child_ids.with_context({'tracking_disable': True})) as subtask_form: - subtask_form.parent_id = self.env['project.task'] - orphan_subtask = subtask_form.save() + # Debug mode required for `parent_id` to be visible in the view + with self.debug_mode(): + # 6) + with Form(self.task_1.child_ids.with_context({'tracking_disable': True})) as subtask_form: + subtask_form.parent_id = self.env['project.task'] + orphan_subtask = subtask_form.save() - self.assertEqual(orphan_subtask.display_project_id, self.project_goats, "Display Project of the task should be well assigned") - self.assertEqual(orphan_subtask.project_id, self.project_goats, "Changing display project id on a subtask should change project id") - self.assertFalse(orphan_subtask.parent_id, "Parent should be false") + self.assertEqual(orphan_subtask.display_project_id, self.project_goats, "Display Project of the task should be well assigned") + self.assertEqual(orphan_subtask.project_id, self.project_goats, "Changing display project id on a subtask should change project id") + self.assertFalse(orphan_subtask.parent_id, "Parent should be false") - # 7) - with Form(self.task_1.with_context({'tracking_disable': True})) as task_form: - with task_form.child_ids.new() as subtask_form: - subtask_form.name = 'Test Subtask 1' - subtask_form.display_project_id = self.project_goats - with Form(self.task_1.child_ids.with_context({'tracking_disable': True})) as subtask_form: - subtask_form.display_project_id = self.env['project.project'] - subtask_form.parent_id = self.env['project.task'] - orphan_subtask = subtask_form.save() + # 7) + with Form(self.task_1.with_context({'tracking_disable': True})) as task_form: + with task_form.child_ids.new() as subtask_form: + subtask_form.name = 'Test Subtask 1' + subtask_form.display_project_id = self.project_goats + with Form(self.task_1.child_ids.with_context({'tracking_disable': True})) as subtask_form: + subtask_form.display_project_id = self.env['project.project'] + subtask_form.parent_id = self.env['project.task'] + orphan_subtask = subtask_form.save() - self.assertEqual(orphan_subtask.project_id, self.project_pigs, "Removing parent should not change project") - self.assertEqual(orphan_subtask.display_project_id, self.project_pigs, "Removing parent should make the display project set as project.") + self.assertEqual(orphan_subtask.project_id, self.project_pigs, "Removing parent should not change project") + self.assertEqual(orphan_subtask.display_project_id, self.project_pigs, "Removing parent should make the display project set as project.") def test_subtask_stage(self): """ diff --git a/addons/purchase/tests/test_purchase.py b/addons/purchase/tests/test_purchase.py index 3df561ddd1d..c585a61e0c4 100644 --- a/addons/purchase/tests/test_purchase.py +++ b/addons/purchase/tests/test_purchase.py @@ -179,6 +179,8 @@ class TestPurchase(AccountTestInvoicingCommon): according to the product_qty. Also check product_qty or product_packaging are correctly calculated when one of them changed. """ + # Required for `product_packaging_qty` to be visible in the view + self.env.user.groups_id += self.env.ref('product.group_stock_packaging') packaging_single = self.env['product.packaging'].create({ 'name': "I'm a packaging", 'product_id': self.product_a.id, diff --git a/addons/purchase/tests/test_purchase_invoice.py b/addons/purchase/tests/test_purchase_invoice.py index 630397b7d7a..4c91da3ddb9 100644 --- a/addons/purchase/tests/test_purchase_invoice.py +++ b/addons/purchase/tests/test_purchase_invoice.py @@ -304,6 +304,8 @@ class TestPurchaseToInvoice(AccountTestInvoicingCommon): """ Tests whether, when an analytic account rule is set, and user changes manually the analytic account on the po, it is the same that is mentioned in the bill. """ + # Required for `analytic.group_analytic_accounting` to be visible in the view + self.env.user.groups_id += self.env.ref('analytic.group_analytic_accounting') analytic_account_default = self.env['account.analytic.account'].create({'name': 'default'}) analytic_account_manual = self.env['account.analytic.account'].create({'name': 'manual'}) diff --git a/addons/purchase/tests/test_purchase_order_report.py b/addons/purchase/tests/test_purchase_order_report.py index 0df4857a6c4..53e0ce954f3 100644 --- a/addons/purchase/tests/test_purchase_order_report.py +++ b/addons/purchase/tests/test_purchase_order_report.py @@ -41,7 +41,39 @@ class TestPurchaseOrderReport(AccountTestInvoicingCommon): f = Form(self.env['account.move'].with_context(default_move_type='in_invoice')) f.invoice_date = f.date f.partner_id = po.partner_id - f.purchase_id = po + # + # + #