275 lines
9.8 KiB
Python
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"
|
|
)
|
|
|