vendor_bill_editable_totals/tests/test_view_configuration.py
2025-11-21 18:02:20 +07:00

275 lines
9.8 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml import etree
from odoo.tests import TransactionCase
class TestViewConfiguration(TransactionCase):
"""
Unit tests for view configuration in vendor bill editable totals.
Tests cover:
- Verify price_subtotal field is editable in vendor bill form (Requirement 1.1)
- Verify price_total field is editable in vendor bill form (Requirement 2.1)
- Verify fields are readonly in non-vendor-bill contexts (Requirement 5.2)
"""
def setUp(self):
super().setUp()
# Get the view record
self.view = self.env.ref('vendor_bill_editable_totals.view_move_form_editable_totals')
# Get the base account.move form view
self.base_view = self.env.ref('account.view_move_form')
def test_view_exists(self):
"""
Test that the custom view exists and inherits from the correct base view.
Requirement 5.2: Use proper view inheritance
"""
self.assertTrue(self.view, "Custom view should exist")
self.assertEqual(
self.view.model,
'account.move',
"View should be for account.move model"
)
self.assertEqual(
self.view.inherit_id.id,
self.base_view.id,
"View should inherit from account.view_move_form"
)
def test_price_subtotal_field_editable(self):
"""
Test that price_subtotal field is made editable in the view.
Requirement 1.1: price_subtotal field should be editable in vendor bill form
"""
# Parse the view architecture
arch = etree.fromstring(self.view.arch)
# Find the xpath that modifies price_subtotal
xpath_elements = arch.xpath("//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_subtotal']\"]")
self.assertTrue(
len(xpath_elements) > 0,
"XPath for price_subtotal field should exist in view"
)
# Check that readonly attribute is set to 0
xpath_element = xpath_elements[0]
attributes = xpath_element.xpath(".//attribute[@name='readonly']")
self.assertTrue(
len(attributes) > 0,
"Readonly attribute should be set for price_subtotal"
)
self.assertEqual(
attributes[0].text,
'0',
"price_subtotal readonly should be set to 0 (editable)"
)
def test_price_total_field_editable(self):
"""
Test that price_total field is made editable in the view.
Requirement 2.1: price_total field should be editable in vendor bill form
"""
# Parse the view architecture
arch = etree.fromstring(self.view.arch)
# Find the xpath that modifies price_total
xpath_elements = arch.xpath("//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_total']\"]")
self.assertTrue(
len(xpath_elements) > 0,
"XPath for price_total field should exist in view"
)
# Check that readonly attribute is set to 0
xpath_element = xpath_elements[0]
attributes = xpath_element.xpath(".//attribute[@name='readonly']")
self.assertTrue(
len(attributes) > 0,
"Readonly attribute should be set for price_total"
)
self.assertEqual(
attributes[0].text,
'0',
"price_total readonly should be set to 0 (editable)"
)
def test_fields_readonly_when_not_draft(self):
"""
Test that fields have attrs to make them readonly when state is not draft.
Requirement 5.2: Fields should be readonly in non-draft states
"""
# Parse the view architecture
arch = etree.fromstring(self.view.arch)
# Check price_subtotal attrs
xpath_subtotal = arch.xpath("//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_subtotal']\"]")
self.assertTrue(len(xpath_subtotal) > 0, "price_subtotal xpath should exist")
attrs_subtotal = xpath_subtotal[0].xpath(".//attribute[@name='attrs']")
self.assertTrue(
len(attrs_subtotal) > 0,
"Attrs attribute should be set for price_subtotal to control readonly state"
)
# Verify the attrs contain readonly condition
attrs_text = attrs_subtotal[0].text
self.assertIn(
'readonly',
attrs_text,
"Attrs should contain readonly condition for price_subtotal"
)
self.assertIn(
'draft',
attrs_text,
"Attrs should reference draft state for price_subtotal"
)
# Check price_total attrs
xpath_total = arch.xpath("//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_total']\"]")
self.assertTrue(len(xpath_total) > 0, "price_total xpath should exist")
attrs_total = xpath_total[0].xpath(".//attribute[@name='attrs']")
self.assertTrue(
len(attrs_total) > 0,
"Attrs attribute should be set for price_total to control readonly state"
)
# Verify the attrs contain readonly condition
attrs_text = attrs_total[0].text
self.assertIn(
'readonly',
attrs_text,
"Attrs should contain readonly condition for price_total"
)
self.assertIn(
'draft',
attrs_text,
"Attrs should reference draft state for price_total"
)
def test_view_applies_to_vendor_bills(self):
"""
Test that the view modifications apply to vendor bill context.
Requirement 1.1, 2.1: Fields should be editable in vendor bill form
"""
# Create a vendor partner
vendor = self.env['res.partner'].create({
'name': 'Test Vendor',
'supplier_rank': 1,
})
# Create a vendor bill
vendor_bill = self.env['account.move'].create({
'move_type': 'in_invoice',
'partner_id': vendor.id,
'invoice_date': '2024-01-01',
})
# Get the view for this record
view_id = self.env['ir.ui.view'].search([
('model', '=', 'account.move'),
('type', '=', 'form'),
('inherit_id', '=', self.base_view.id),
], limit=1)
self.assertTrue(
view_id,
"View should be available for vendor bills"
)
def test_fields_editable_in_draft_state(self):
"""
Test that fields are editable when vendor bill is in draft state.
Requirement 1.1, 2.1: Fields should be editable in draft vendor bills
"""
# Create a vendor partner
vendor = self.env['res.partner'].create({
'name': 'Test Vendor',
'supplier_rank': 1,
})
# Create a product
product = self.env['res.product'].create({
'name': 'Test Product',
'type': 'service',
'list_price': 100.0,
})
# Create a vendor bill in draft state
vendor_bill = self.env['account.move'].create({
'move_type': 'in_invoice',
'partner_id': vendor.id,
'invoice_date': '2024-01-01',
'state': 'draft',
})
# Create a line
line = self.env['account.move.line'].create({
'move_id': vendor_bill.id,
'product_id': product.id,
'name': 'Test Line',
'quantity': 10.0,
'price_unit': 100.0,
})
# Verify the bill is in draft state
self.assertEqual(
vendor_bill.state,
'draft',
"Vendor bill should be in draft state"
)
# Test that we can modify price_subtotal (this would fail if readonly)
line.price_subtotal = 1200.0
line._onchange_price_subtotal()
# If we got here without error, the field is editable
self.assertAlmostEqual(
line.price_unit,
120.0,
places=2,
msg="price_unit should be recalculated when price_subtotal is modified"
)
def test_fields_readonly_in_posted_state(self):
"""
Test that fields behavior is appropriate when vendor bill is posted.
Requirement 5.2: Fields should respect state-based readonly conditions
Note: This test verifies the attrs configuration exists. The actual readonly
enforcement happens at the UI level based on the attrs domain.
"""
# Parse the view architecture
arch = etree.fromstring(self.view.arch)
# Verify both fields have attrs that reference parent.state
xpath_subtotal = arch.xpath("//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_subtotal']\"]")
attrs_subtotal = xpath_subtotal[0].xpath(".//attribute[@name='attrs']")
attrs_text_subtotal = attrs_subtotal[0].text
# The attrs should make the field readonly when state != 'draft'
self.assertIn(
'parent.state',
attrs_text_subtotal,
"Attrs should reference parent.state for price_subtotal"
)
xpath_total = arch.xpath("//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_total']\"]")
attrs_total = xpath_total[0].xpath(".//attribute[@name='attrs']")
attrs_text_total = attrs_total[0].text
self.assertIn(
'parent.state',
attrs_text_total,
"Attrs should reference parent.state for price_total"
)