# Copyright 2025 Criptomart # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) """ Test suite for validations and constraints in website_sale_aplicoop. Coverage: - group.order constraint: same company for all groups - group.order constraint: start_date < end_date - group.order computed field: image_1920 fallback logic - group.order computed field: product count - res.partner validation: user without partner_id - group.order state transitions: illegal transitions """ from datetime import datetime, timedelta from odoo.tests.common import TransactionCase from odoo.exceptions import ValidationError, UserError class TestGroupOrderValidations(TransactionCase): """Test constraints and validations for group.order model.""" def setUp(self): super().setUp() self.company1 = self.env.company self.company2 = self.env['res.company'].create({ 'name': 'Company 2', }) self.group_c1 = self.env['res.partner'].create({ 'name': 'Group Company 1', 'is_company': True, 'company_id': self.company1.id, }) self.group_c2 = self.env['res.partner'].create({ 'name': 'Group Company 2', 'is_company': True, 'company_id': self.company2.id, }) def test_group_order_same_company_constraint(self): """Test that all groups in an order must be from same company.""" start_date = datetime.now().date() # Creating order with groups from different companies should fail with self.assertRaises(ValidationError): self.env['group.order'].create({ 'name': 'Multi-Company Order', 'group_ids': [(6, 0, [self.group_c1.id, self.group_c2.id])], 'type': 'regular', 'start_date': start_date, 'end_date': start_date + timedelta(days=7), 'period': 'weekly', 'pickup_day': '3', 'cutoff_day': '0', }) def test_group_order_same_company_mixed_single(self): """Test that single company group is valid.""" start_date = datetime.now().date() # Single company should pass order = self.env['group.order'].create({ 'name': 'Single Company Order', 'group_ids': [(6, 0, [self.group_c1.id])], 'type': 'regular', 'start_date': start_date, 'end_date': start_date + timedelta(days=7), 'period': 'weekly', 'pickup_day': '3', 'cutoff_day': '0', }) self.assertTrue(order.exists()) def test_group_order_date_validation_start_after_end(self): """Test that start_date must be before end_date.""" start_date = datetime.now().date() end_date = start_date - timedelta(days=1) # End before start with self.assertRaises(ValidationError): self.env['group.order'].create({ 'name': 'Bad Dates Order', 'group_ids': [(6, 0, [self.group_c1.id])], 'type': 'regular', 'start_date': start_date, 'end_date': end_date, 'period': 'weekly', 'pickup_day': '3', 'cutoff_day': '0', }) def test_group_order_date_validation_same_date(self): """Test that start_date == end_date is allowed (single-day order).""" same_date = datetime.now().date() order = self.env['group.order'].create({ 'name': 'Same Day Order', 'group_ids': [(6, 0, [self.group_c1.id])], 'type': 'regular', 'start_date': same_date, 'end_date': same_date, 'period': 'once', 'pickup_day': '0', 'cutoff_day': '0', }) self.assertTrue(order.exists()) class TestGroupOrderImageFallback(TransactionCase): """Test image_1920 computed field fallback logic.""" def setUp(self): super().setUp() self.group = self.env['res.partner'].create({ 'name': 'Test Group', 'is_company': True, }) start_date = datetime.now().date() self.group_order = self.env['group.order'].create({ 'name': 'Test Order', 'group_ids': [(6, 0, [self.group.id])], 'type': 'regular', 'start_date': start_date, 'end_date': start_date + timedelta(days=7), 'period': 'weekly', 'pickup_day': '3', 'cutoff_day': '0', }) def test_image_fallback_order_image_first(self): """Test that order image takes priority over group image.""" # Set both order and group image test_image = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==' self.group_order.image_1920 = test_image self.group.image_1920 = test_image # Order image should be returned computed_image = self.group_order.image_1920 self.assertEqual(computed_image, test_image) def test_image_fallback_group_image_when_no_order_image(self): """Test fallback to group image when order has no image.""" test_image = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==' # Only set group image self.group_order.image_1920 = False self.group.image_1920 = test_image # Group image should be returned as fallback # Note: This requires the computed field logic to be tested # after field recalculation def test_image_fallback_none_when_no_images(self): """Test that None is returned when no image available.""" # No images set self.group_order.image_1920 = False self.group.image_1920 = False # Should be empty/False computed_image = self.group_order.image_1920 self.assertFalse(computed_image) class TestGroupOrderProductCount(TransactionCase): """Test product_count computed field.""" def setUp(self): super().setUp() self.group = self.env['res.partner'].create({ 'name': 'Test Group', 'is_company': True, }) start_date = datetime.now().date() self.group_order = self.env['group.order'].create({ 'name': 'Test Order', 'group_ids': [(6, 0, [self.group.id])], 'type': 'regular', 'start_date': start_date, 'end_date': start_date + timedelta(days=7), 'period': 'weekly', 'pickup_day': '3', 'cutoff_day': '0', }) self.product1 = self.env['product.product'].create({ 'name': 'Product 1', 'type': 'consu', 'list_price': 10.0, }) self.product2 = self.env['product.product'].create({ 'name': 'Product 2', 'type': 'consu', 'list_price': 20.0, }) def test_product_count_initial_zero(self): """Test that new order has zero products.""" self.assertEqual(self.group_order.product_count, 0) def test_product_count_increments_on_add(self): """Test that product_count increases when adding products.""" self.group_order.product_ids = [(4, self.product1.id)] self.assertEqual(self.group_order.product_count, 1) self.group_order.product_ids = [(4, self.product2.id)] self.assertEqual(self.group_order.product_count, 2) def test_product_count_decrements_on_remove(self): """Test that product_count decreases when removing products.""" self.group_order.product_ids = [(6, 0, [self.product1.id, self.product2.id])] self.assertEqual(self.group_order.product_count, 2) self.group_order.product_ids = [(3, self.product1.id)] self.assertEqual(self.group_order.product_count, 1) def test_product_count_all_removed(self): """Test that product_count is zero when all removed.""" self.group_order.product_ids = [(6, 0, [self.product1.id, self.product2.id])] self.group_order.product_ids = [(6, 0, [])] self.assertEqual(self.group_order.product_count, 0) class TestStateTransitions(TransactionCase): """Test group.order state transition validation.""" def setUp(self): super().setUp() self.group = self.env['res.partner'].create({ 'name': 'Test Group', 'is_company': True, }) start_date = datetime.now().date() self.order = self.env['group.order'].create({ 'name': 'Test Order', 'group_ids': [(6, 0, [self.group.id])], 'type': 'regular', 'start_date': start_date, 'end_date': start_date + timedelta(days=7), 'period': 'weekly', 'pickup_day': '3', 'cutoff_day': '0', }) def test_illegal_transition_draft_to_closed(self): """Test that Draft -> Closed transition is not allowed.""" # Should not allow skipping Open state self.assertEqual(self.order.state, 'draft') # Calling action_close() without action_open() should fail with self.assertRaises((ValidationError, UserError)): self.order.action_close() def test_illegal_transition_cancelled_to_open(self): """Test that Cancelled -> Open transition is not allowed.""" self.order.action_cancel() self.assertEqual(self.order.state, 'cancelled') # Should not allow re-opening cancelled order with self.assertRaises((ValidationError, UserError)): self.order.action_open() def test_legal_transition_draft_open_closed(self): """Test that Draft -> Open -> Closed is allowed.""" self.assertEqual(self.order.state, 'draft') self.order.action_open() self.assertEqual(self.order.state, 'open') self.order.action_close() self.assertEqual(self.order.state, 'closed') def test_transition_draft_to_cancelled(self): """Test that Draft -> Cancelled is allowed.""" self.assertEqual(self.order.state, 'draft') self.order.action_cancel() self.assertEqual(self.order.state, 'cancelled') def test_transition_open_to_cancelled(self): """Test that Open -> Cancelled is allowed (emergency stop).""" self.order.action_open() self.assertEqual(self.order.state, 'open') self.order.action_cancel() self.assertEqual(self.order.state, 'cancelled') class TestUserPartnerValidation(TransactionCase): """Test validation when user has no partner_id.""" def setUp(self): super().setUp() self.group = self.env['res.partner'].create({ 'name': 'Test Group', 'is_company': True, }) # Create user without partner (edge case) self.user_no_partner = self.env['res.users'].create({ 'name': 'User No Partner', 'login': 'noparnter@test.com', 'partner_id': False, # Explicitly no partner }) def test_user_without_partner_cannot_access_order(self): """Test that user without partner_id has no access to orders.""" start_date = datetime.now().date() order = self.env['group.order'].create({ 'name': 'Test Order', 'group_ids': [(6, 0, [self.group.id])], 'type': 'regular', 'start_date': start_date, 'end_date': start_date + timedelta(days=7), 'period': 'weekly', 'pickup_day': '3', 'cutoff_day': '0', }) # User without partner should not have access # This should be validated in controller self.assertFalse(self.user_no_partner.partner_id)