"""
Integration tests for journal entry views.
Tests task 11.6 requirements.
"""

from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth import get_user_model
from decimal import Decimal
from datetime import date, timedelta

from accounting.models import Account, JournalEntry, JournalEntryLine, FiscalPeriod
from users.models import Branch

User = get_user_model()


class JournalEntryViewsTestCase(TestCase):
    """Test case for journal entry views integration tests."""
    
    def setUp(self):
        """Set up test data."""
        # Create test user with staff permissions
        self.user = User.objects.create_user(
            username='teststaff',
            password='testpass123',
            email='staff@test.com',
            is_staff=True,
            role='admin'  # Add role for staff_required decorator
        )
        
        # Create branch
        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TB001',
            address='Test Location'
        )
        
        # Create test accounts
        self.cash_account = Account.objects.create(
            code='202001',
            name='Cash',
            account_type='asset',
            description='Cash account',
            created_by=self.user,
            is_active=True
        )
        
        self.loan_portfolio_account = Account.objects.create(
            code='202002',
            name='Loan Portfolio',
            account_type='asset',
            description='Loan portfolio account',
            created_by=self.user,
            is_active=True
        )
        
        self.interest_income_account = Account.objects.create(
            code='401001',
            name='Interest Income',
            account_type='income',
            description='Interest income account',
            created_by=self.user,
            is_active=True
        )
        
        self.expense_account = Account.objects.create(
            code='501001',
            name='Operating Expenses',
            account_type='expense',
            description='Operating expenses account',
            created_by=self.user,
            is_active=True
        )
        
        # Create an open fiscal period
        today = date.today()
        self.fiscal_period = FiscalPeriod.objects.create(
            name=f'Test Period {today.year}',
            period_type='monthly',
            start_date=date(today.year, today.month, 1),
            end_date=date(today.year, today.month, 28) if today.month != 12 
                    else date(today.year, 12, 31),
            status='open'
        )
        
        # Create client
        self.client = Client()
        self.client.login(username='teststaff', password='testpass123')
    
    def test_journal_entry_list_renders(self):
        """Test that journal entry list view renders correctly."""
        url = reverse('accounting:journal_entry_list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'accounting/journal/list.html')
        self.assertIn('entries', response.context)
        self.assertIn('filter_form', response.context)
    
    def test_journal_entry_list_filtering_by_status(self):
        """Test journal entry list filtering by status."""
        # Create draft and posted entries
        draft_entry = JournalEntry.objects.create(
            reference_number='JE-TEST-001',
            transaction_date=date.today(),
            description='Draft entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        posted_entry = JournalEntry.objects.create(
            reference_number='JE-TEST-002',
            transaction_date=date.today(),
            description='Posted entry',
            status='posted',
            branch=self.branch,
            created_by=self.user,
            posted_by=self.user,
            posted_at=date.today()
        )
        
        # Test filtering by draft status
        url = reverse('accounting:journal_entry_list')
        response = self.client.get(url, {'status': 'draft'})
        
        self.assertEqual(response.status_code, 200)
        entries = [item['entry'] for item in response.context['entries_with_totals']]
        self.assertIn(draft_entry, entries)
        self.assertNotIn(posted_entry, entries)
        
        # Test filtering by posted status
        response = self.client.get(url, {'status': 'posted'})
        entries = [item['entry'] for item in response.context['entries_with_totals']]
        self.assertNotIn(draft_entry, entries)
        self.assertIn(posted_entry, entries)
    
    def test_journal_entry_list_filtering_by_date_range(self):
        """Test journal entry list filtering by date range."""
        today = date.today()
        yesterday = today - timedelta(days=1)
        
        # Create entries with different dates
        recent_entry = JournalEntry.objects.create(
            reference_number='JE-TEST-003',
            transaction_date=today,
            description='Recent entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        old_entry = JournalEntry.objects.create(
            reference_number='JE-TEST-004',
            transaction_date=yesterday,
            description='Old entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        # Test filtering by start date
        url = reverse('accounting:journal_entry_list')
        response = self.client.get(url, {'start_date': today})
        
        self.assertEqual(response.status_code, 200)
        entries = [item['entry'] for item in response.context['entries_with_totals']]
        self.assertIn(recent_entry, entries)
        self.assertNotIn(old_entry, entries)
    
    def test_journal_entry_list_filtering_by_branch(self):
        """Test journal entry list filtering by branch."""
        # Create another branch
        other_branch = Branch.objects.create(
            name='Other Branch',
            code='OB001',
            address='Other Location'
        )
        
        # Create entries for different branches
        branch1_entry = JournalEntry.objects.create(
            reference_number='JE-TEST-005',
            transaction_date=date.today(),
            description='Branch 1 entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        branch2_entry = JournalEntry.objects.create(
            reference_number='JE-TEST-006',
            transaction_date=date.today(),
            description='Branch 2 entry',
            status='draft',
            branch=other_branch,
            created_by=self.user
        )
        
        # Test filtering by branch
        url = reverse('accounting:journal_entry_list')
        response = self.client.get(url, {'branch': self.branch.id})
        
        self.assertEqual(response.status_code, 200)
        entries = [item['entry'] for item in response.context['entries_with_totals']]
        self.assertIn(branch1_entry, entries)
        self.assertNotIn(branch2_entry, entries)
    
    def test_journal_entry_detail_displays_correctly(self):
        """Test that journal entry detail view shows correct entry and lines."""
        # Create journal entry with lines
        entry = JournalEntry.objects.create(
            reference_number='JE-TEST-007',
            transaction_date=date.today(),
            description='Test entry with lines',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        # Add lines
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.cash_account,
            description='Debit cash',
            debit_amount=Decimal('1000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )
        
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.interest_income_account,
            description='Credit interest income',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1000.00'),
            line_number=2
        )
        
        # Test detail view
        url = reverse('accounting:journal_entry_detail', 
                     kwargs={'reference_number': entry.reference_number})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'accounting/journal/detail.html')
        self.assertEqual(response.context['entry'], entry)
        self.assertEqual(response.context['total_debits'], Decimal('1000.00'))
        self.assertEqual(response.context['total_credits'], Decimal('1000.00'))
        
        # Check that lines are displayed
        self.assertContains(response, 'Debit cash')
        self.assertContains(response, 'Credit interest income')
        self.assertContains(response, self.cash_account.code)
        self.assertContains(response, self.interest_income_account.code)
    
    def test_journal_entry_create_view_renders(self):
        """Test that journal entry create view renders with form and formset."""
        url = reverse('accounting:journal_entry_create')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'accounting/journal/create.html')
        self.assertIn('form', response.context)
        self.assertIn('formset', response.context)
    
    def test_journal_entry_create_draft_entry(self):
        """Test creating a journal entry as draft."""
        url = reverse('accounting:journal_entry_create')
        
        data = {
            'transaction_date': date.today(),
            'description': 'Test journal entry creation',
            'branch': self.branch.id,
            
            # Formset management form
            'lines-TOTAL_FORMS': '2',
            'lines-INITIAL_FORMS': '0',
            'lines-MIN_NUM_FORMS': '2',
            'lines-MAX_NUM_FORMS': '1000',
            
            # Line 1
            'lines-0-account': self.cash_account.id,
            'lines-0-description': 'Debit cash account',
            'lines-0-debit_amount': '5000.00',
            'lines-0-credit_amount': '0.00',
            
            # Line 2
            'lines-1-account': self.interest_income_account.id,
            'lines-1-description': 'Credit interest income',
            'lines-1-debit_amount': '0.00',
            'lines-1-credit_amount': '5000.00',
        }
        
        response = self.client.post(url, data)
        
        # Should redirect to detail page on success
        self.assertEqual(response.status_code, 302)
        
        # Verify entry was created
        entry = JournalEntry.objects.filter(description='Test journal entry creation').first()
        self.assertIsNotNone(entry)
        self.assertEqual(entry.status, 'draft')
        self.assertEqual(entry.branch, self.branch)
        self.assertEqual(entry.created_by, self.user)
        
        # Verify lines were created
        lines = entry.lines.all()
        self.assertEqual(lines.count(), 2)
        
        # Verify line details
        debit_line = lines.get(line_number=1)
        self.assertEqual(debit_line.account, self.cash_account)
        self.assertEqual(debit_line.debit_amount, Decimal('5000.00'))
        self.assertEqual(debit_line.credit_amount, Decimal('0.00'))
        
        credit_line = lines.get(line_number=2)
        self.assertEqual(credit_line.account, self.interest_income_account)
        self.assertEqual(credit_line.debit_amount, Decimal('0.00'))
        self.assertEqual(credit_line.credit_amount, Decimal('5000.00'))
    
    def test_formset_validation_rejects_unbalanced_entries(self):
        """Test that formset validation rejects entries where debits != credits."""
        url = reverse('accounting:journal_entry_create')
        
        data = {
            'transaction_date': date.today(),
            'description': 'Unbalanced entry',
            'branch': self.branch.id,
            
            # Formset management form
            'lines-TOTAL_FORMS': '2',
            'lines-INITIAL_FORMS': '0',
            'lines-MIN_NUM_FORMS': '2',
            'lines-MAX_NUM_FORMS': '1000',
            
            # Line 1 - Debit 5000
            'lines-0-account': self.cash_account.id,
            'lines-0-description': 'Debit cash account',
            'lines-0-debit_amount': '5000.00',
            'lines-0-credit_amount': '0.00',
            
            # Line 2 - Credit 3000 (unbalanced!)
            'lines-1-account': self.interest_income_account.id,
            'lines-1-description': 'Credit interest income',
            'lines-1-debit_amount': '0.00',
            'lines-1-credit_amount': '3000.00',
        }
        
        response = self.client.post(url, data)
        
        # Should stay on same page with errors
        self.assertEqual(response.status_code, 200)
        self.assertFormsetError(
            response, 'formset', None, None,
            'Total debits (5000.00) must equal total credits (3000.00). Difference: 2000.00'
        )
        
        # Verify no entry was created
        entry = JournalEntry.objects.filter(description='Unbalanced entry').first()
        self.assertIsNone(entry)
    
    def test_formset_validation_requires_minimum_two_lines(self):
        """Test that formset validation requires at least 2 line items."""
        url = reverse('accounting:journal_entry_create')
        
        data = {
            'transaction_date': date.today(),
            'description': 'Single line entry',
            'branch': self.branch.id,
            
            # Formset management form
            'lines-TOTAL_FORMS': '1',
            'lines-INITIAL_FORMS': '0',
            'lines-MIN_NUM_FORMS': '2',
            'lines-MAX_NUM_FORMS': '1000',
            
            # Only one line
            'lines-0-account': self.cash_account.id,
            'lines-0-description': 'Single line',
            'lines-0-debit_amount': '1000.00',
            'lines-0-credit_amount': '0.00',
        }
        
        response = self.client.post(url, data)
        
        # Should stay on same page with errors
        self.assertEqual(response.status_code, 200)
        
        # Verify no entry was created
        entry = JournalEntry.objects.filter(description='Single line entry').first()
        self.assertIsNone(entry)
    
    def test_journal_entry_post_view_posts_valid_entry(self):
        """Test posting a valid draft journal entry."""
        # Create draft entry
        entry = JournalEntry.objects.create(
            reference_number='JE-TEST-008',
            transaction_date=date.today(),
            description='Entry to be posted',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        # Add balanced lines
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.cash_account,
            description='Debit cash',
            debit_amount=Decimal('2000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )
        
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.loan_portfolio_account,
            description='Credit loan portfolio',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('2000.00'),
            line_number=2
        )
        
        # Post the entry
        url = reverse('accounting:journal_entry_post', 
                     kwargs={'reference_number': entry.reference_number})
        response = self.client.post(url)
        
        # Should redirect back to detail page
        self.assertEqual(response.status_code, 302)
        
        # Verify entry was posted
        entry.refresh_from_db()
        self.assertEqual(entry.status, 'posted')
        self.assertEqual(entry.posted_by, self.user)
        self.assertIsNotNone(entry.posted_at)
        
        # Verify general ledger entries were created
        from accounting.models import GeneralLedger
        ledger_entries = GeneralLedger.objects.filter(journal_entry=entry)
        self.assertEqual(ledger_entries.count(), 2)
    
    def test_journal_entry_post_view_rejects_unbalanced_entry(self):
        """Test that posting rejects an unbalanced entry."""
        # Create draft entry with unbalanced lines
        entry = JournalEntry.objects.create(
            reference_number='JE-TEST-009',
            transaction_date=date.today(),
            description='Unbalanced entry to be rejected',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        # Add unbalanced lines
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.cash_account,
            description='Debit cash',
            debit_amount=Decimal('3000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )
        
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.interest_income_account,
            description='Credit interest',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('2000.00'),  # Unbalanced!
            line_number=2
        )
        
        # Try to post the entry
        url = reverse('accounting:journal_entry_post', 
                     kwargs={'reference_number': entry.reference_number})
        response = self.client.post(url)
        
        # Should redirect but with error message
        self.assertEqual(response.status_code, 302)
        
        # Verify entry was NOT posted
        entry.refresh_from_db()
        self.assertEqual(entry.status, 'draft')
        self.assertIsNone(entry.posted_by)
        
        # Verify no ledger entries were created
        from accounting.models import GeneralLedger
        ledger_entries = GeneralLedger.objects.filter(journal_entry=entry)
        self.assertEqual(ledger_entries.count(), 0)
    
    def test_journal_entry_reverse_view_creates_reversal(self):
        """Test that reversing a posted entry creates a reversal entry."""
        # Create and post an entry
        entry = JournalEntry.objects.create(
            reference_number='JE-TEST-010',
            transaction_date=date.today(),
            description='Entry to be reversed',
            status='posted',
            branch=self.branch,
            created_by=self.user,
            posted_by=self.user,
            posted_at=date.today()
        )
        
        # Add lines
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.expense_account,
            description='Debit expense',
            debit_amount=Decimal('1500.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )
        
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.cash_account,
            description='Credit cash',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1500.00'),
            line_number=2
        )
        
        # Create ledger entries for posted entry
        from accounting.models import GeneralLedger
        from django.utils import timezone
        
        GeneralLedger.objects.create(
            account=self.expense_account,
            journal_entry=entry,
            journal_entry_line=entry.lines.get(line_number=1),
            transaction_date=entry.transaction_date,
            description='Debit expense',
            reference_number=entry.reference_number,
            debit_amount=Decimal('1500.00'),
            credit_amount=Decimal('0.00'),
            balance=Decimal('1500.00'),
            branch=self.branch,
            posted_at=timezone.now(),
            posted_by=self.user
        )
        
        GeneralLedger.objects.create(
            account=self.cash_account,
            journal_entry=entry,
            journal_entry_line=entry.lines.get(line_number=2),
            transaction_date=entry.transaction_date,
            description='Credit cash',
            reference_number=entry.reference_number,
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1500.00'),
            balance=Decimal('-1500.00'),
            branch=self.branch,
            posted_at=timezone.now(),
            posted_by=self.user
        )
        
        # Reverse the entry
        url = reverse('accounting:journal_entry_reverse', 
                     kwargs={'reference_number': entry.reference_number})
        response = self.client.post(url, {'reason': 'Test reversal reason'})
        
        # Should redirect
        self.assertEqual(response.status_code, 302)
        
        # Verify original entry was marked as reversed
        entry.refresh_from_db()
        self.assertEqual(entry.status, 'reversed')
        
        # Verify reversal entry was created
        reversal = JournalEntry.objects.filter(reverses=entry).first()
        self.assertIsNotNone(reversal)
        self.assertEqual(reversal.status, 'posted')
        self.assertIn('Test reversal reason', reversal.description)
        
        # Verify reversal has opposite lines
        reversal_lines = reversal.lines.all()
        self.assertEqual(reversal_lines.count(), 2)
        
        # Original debit should become credit in reversal
        original_debit_line = entry.lines.get(debit_amount=Decimal('1500.00'))
        reversal_credit_line = reversal.lines.filter(
            account=original_debit_line.account
        ).first()
        self.assertIsNotNone(reversal_credit_line)
        self.assertEqual(reversal_credit_line.credit_amount, Decimal('1500.00'))
        self.assertEqual(reversal_credit_line.debit_amount, Decimal('0.00'))
        
        # Original credit should become debit in reversal
        original_credit_line = entry.lines.get(credit_amount=Decimal('1500.00'))
        reversal_debit_line = reversal.lines.filter(
            account=original_credit_line.account
        ).first()
        self.assertIsNotNone(reversal_debit_line)
        self.assertEqual(reversal_debit_line.debit_amount, Decimal('1500.00'))
        self.assertEqual(reversal_debit_line.credit_amount, Decimal('0.00'))
    
    def test_formset_validation_mutual_exclusivity(self):
        """Test that debit and credit are mutually exclusive per line."""
        url = reverse('accounting:journal_entry_create')
        
        data = {
            'transaction_date': date.today(),
            'description': 'Entry with both debit and credit on same line',
            'branch': self.branch.id,
            
            # Formset management form
            'lines-TOTAL_FORMS': '2',
            'lines-INITIAL_FORMS': '0',
            'lines-MIN_NUM_FORMS': '2',
            'lines-MAX_NUM_FORMS': '1000',
            
            # Line 1 - Both debit and credit (invalid!)
            'lines-0-account': self.cash_account.id,
            'lines-0-description': 'Both debit and credit',
            'lines-0-debit_amount': '1000.00',
            'lines-0-credit_amount': '500.00',  # Should not be allowed
            
            # Line 2
            'lines-1-account': self.interest_income_account.id,
            'lines-1-description': 'Credit interest',
            'lines-1-debit_amount': '0.00',
            'lines-1-credit_amount': '1500.00',
        }
        
        response = self.client.post(url, data)
        
        # Should stay on same page with errors
        self.assertEqual(response.status_code, 200)
        self.assertFormsetError(
            response, 'formset', None, None,
            'Each line must have either a debit OR a credit, not both.'
        )
        
        # Verify no entry was created
        entry = JournalEntry.objects.filter(
            description='Entry with both debit and credit on same line'
        ).first()
        self.assertIsNone(entry)


# Task 23.3: Unit tests for view-level validation
class JournalEntryViewValidationTest(TestCase):
    """
    Test view-level validation for journal entries.
    Tests for task 23.3: Write unit tests for validation
    Requirements: 16.4, 16.5, 16.6, 16.8, 16.9
    """
    
    def setUp(self):
        """Set up test data."""
        # Create test user with staff permissions
        self.user = User.objects.create_user(
            username='teststaff',
            password='testpass123',
            email='staff@test.com',
            is_staff=True,
            role='admin'
        )
        
        # Create branch
        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TB001',
            address='Test Location'
        )
        
        # Create test accounts
        self.cash_account = Account.objects.create(
            code='202001',
            name='Cash',
            account_type='asset',
            description='Cash account',
            created_by=self.user,
            is_active=True
        )
        
        self.loan_portfolio_account = Account.objects.create(
            code='202002',
            name='Loan Portfolio',
            account_type='asset',
            description='Loan portfolio account',
            created_by=self.user,
            is_active=True
        )
        
        self.inactive_account = Account.objects.create(
            code='202099',
            name='Inactive Account',
            account_type='asset',
            description='Inactive test account',
            created_by=self.user,
            is_active=False  # Inactive!
        )
        
        # Create client
        self.client = Client()
        self.client.login(username='teststaff', password='testpass123')
    
    def test_view_prevents_entry_creation_in_closed_period(self):
        """
        Test that view validation prevents journal entry creation when fiscal period is closed.
        Requirement: 16.4
        """
        # Create a closed fiscal period for today
        today = date.today()
        closed_period = FiscalPeriod.objects.create(
            name=f'Closed Period {today.year}',
            period_type='monthly',
            start_date=date(today.year, today.month, 1),
            end_date=date(today.year, today.month, 28) if today.month != 12 
                    else date(today.year, 12, 31),
            status='closed'  # CLOSED!
        )
        
        url = reverse('accounting:journal_entry_create')
        data = {
            'transaction_date': today,
            'description': 'Entry in closed period',
            'branch': self.branch.id,
            
            # Formset management form
            'lines-TOTAL_FORMS': '2',
            'lines-INITIAL_FORMS': '0',
            'lines-MIN_NUM_FORMS': '2',
            'lines-MAX_NUM_FORMS': '1000',
            
            # Line 1 - Debit
            'lines-0-account': self.cash_account.id,
            'lines-0-description': 'Debit cash',
            'lines-0-debit_amount': '1000.00',
            'lines-0-credit_amount': '0.00',
            
            # Line 2 - Credit
            'lines-1-account': self.loan_portfolio_account.id,
            'lines-1-description': 'Credit loan',
            'lines-1-debit_amount': '0.00',
            'lines-1-credit_amount': '1000.00',
        }
        
        response = self.client.post(url, data, follow=True)
        
        # Should stay on same page (status 200 after follow)
        self.assertEqual(response.status_code, 200)
        
        # Verify error message was shown
        messages_list = list(response.context.get('messages', []))
        has_closed_period_message = any('closed' in str(m).lower() for m in messages_list)
        
        # If not in messages, might be an error in form or formset
        if not has_closed_period_message:
            # Check that entry was not created
            entry = JournalEntry.objects.filter(description='Entry in closed period').first()
            self.assertIsNone(entry, "Entry should not be created when period is closed")
    
    def test_view_prevents_entry_creation_with_inactive_account(self):
        """
        Test that view validation prevents using inactive accounts in journal entries.
        Requirement: 16.6
        """
        # Create an open fiscal period
        today = date.today()
        FiscalPeriod.objects.create(
            name=f'Open Period {today.year}',
            period_type='monthly',
            start_date=date(today.year, today.month, 1),
            end_date=date(today.year, today.month, 28) if today.month != 12 
                    else date(today.year, 12, 31),
            status='open'
        )
        
        url = reverse('accounting:journal_entry_create')
        data = {
            'transaction_date': today,
            'description': 'Entry with inactive account',
            'branch': self.branch.id,
            
            # Formset management form
            'lines-TOTAL_FORMS': '2',
            'lines-INITIAL_FORMS': '0',
            'lines-MIN_NUM_FORMS': '2',
            'lines-MAX_NUM_FORMS': '1000',
            
            # Line 1 - Using INACTIVE account
            'lines-0-account': self.inactive_account.id,
            'lines-0-description': 'Debit inactive account',
            'lines-0-debit_amount': '1000.00',
            'lines-0-credit_amount': '0.00',
            
            # Line 2 - Credit active account
            'lines-1-account': self.cash_account.id,
            'lines-1-description': 'Credit cash',
            'lines-1-debit_amount': '0.00',
            'lines-1-credit_amount': '1000.00',
        }
        
        response = self.client.post(url, data, follow=True)
        
        # Should stay on same page
        self.assertEqual(response.status_code, 200)
        
        # Verify error message was shown
        messages_list = list(response.context.get('messages', []))
        has_inactive_account_message = any('inactive' in str(m).lower() for m in messages_list)
        
        # If not in messages, verify entry was not created
        if not has_inactive_account_message:
            entry = JournalEntry.objects.filter(description='Entry with inactive account').first()
            self.assertIsNone(entry, "Entry should not be created with inactive account")
    
    def test_view_prevents_posting_non_draft_entry(self):
        """
        Test that view validation prevents posting entries that are not in draft status.
        Requirement: 16.8
        """
        # Create a posted entry
        entry = JournalEntry.objects.create(
            reference_number='JE-POSTED-001',
            transaction_date=date.today(),
            description='Already posted entry',
            status='posted',  # Already posted!
            branch=self.branch,
            created_by=self.user,
            posted_by=self.user,
            posted_at=date.today()
        )
        
        # Try to post it again
        url = reverse('accounting:journal_entry_post', 
                     kwargs={'reference_number': entry.reference_number})
        response = self.client.post(url)
        
        # Should redirect with error
        self.assertEqual(response.status_code, 302)
        
        # Check for error message
        response = self.client.get(reverse('accounting:journal_entry_detail',
                                          kwargs={'reference_number': entry.reference_number}))
        messages = list(response.context['messages'])
        self.assertTrue(
            any('draft' in str(m).lower() or 'posted' in str(m).lower() for m in messages),
            "Should show error about entry not being draft"
        )
    
    def test_view_prevents_posting_entry_in_closed_period(self):
        """
        Test that view validation prevents posting journal entries when fiscal period is closed.
        Requirement: 16.4
        """
        # Create an open period first, then close it
        today = date.today()
        period = FiscalPeriod.objects.create(
            name=f'Period {today.year}',
            period_type='monthly',
            start_date=date(today.year, today.month, 1),
            end_date=date(today.year, today.month, 28) if today.month != 12 
                    else date(today.year, 12, 31),
            status='closed'  # Closed period
        )
        
        # Create a draft entry in that period
        entry = JournalEntry.objects.create(
            reference_number='JE-CLOSED-001',
            transaction_date=today,
            description='Entry in closed period',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        # Add balanced lines
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.cash_account,
            description='Debit',
            debit_amount=Decimal('1000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )
        
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.loan_portfolio_account,
            description='Credit',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1000.00'),
            line_number=2
        )
        
        # Try to post
        url = reverse('accounting:journal_entry_post',
                     kwargs={'reference_number': entry.reference_number})
        response = self.client.post(url)
        
        # Should redirect with error
        self.assertEqual(response.status_code, 302)
        
        # Verify entry is still draft
        entry.refresh_from_db()
        self.assertEqual(entry.status, 'draft')
        
        # Check for error message
        response = self.client.get(reverse('accounting:journal_entry_detail',
                                          kwargs={'reference_number': entry.reference_number}))
        messages = list(response.context['messages'])
        self.assertTrue(
            any('closed' in str(m).lower() for m in messages),
            "Should show error about closed fiscal period"
        )
    
    def test_view_prevents_posting_entry_with_inactive_accounts(self):
        """
        Test that view validation prevents posting entries that use inactive accounts.
        Requirement: 16.6
        """
        # Create an open period
        today = date.today()
        FiscalPeriod.objects.create(
            name=f'Open Period {today.year}',
            period_type='monthly',
            start_date=date(today.year, today.month, 1),
            end_date=date(today.year, today.month, 28) if today.month != 12 
                    else date(today.year, 12, 31),
            status='open'
        )
        
        # Create a draft entry
        entry = JournalEntry.objects.create(
            reference_number='JE-INACTIVE-001',
            transaction_date=today,
            description='Entry with inactive account',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )
        
        # Add lines with inactive account
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.inactive_account,  # Inactive!
            description='Debit inactive',
            debit_amount=Decimal('1000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )
        
        JournalEntryLine.objects.create(
            journal_entry=entry,
            account=self.cash_account,
            description='Credit cash',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1000.00'),
            line_number=2
        )
        
        # Try to post
        url = reverse('accounting:journal_entry_post',
                     kwargs={'reference_number': entry.reference_number})
        response = self.client.post(url)
        
        # Should redirect with error
        self.assertEqual(response.status_code, 302)
        
        # Verify entry is still draft
        entry.refresh_from_db()
        self.assertEqual(entry.status, 'draft')
        
        # Check for error message
        response = self.client.get(reverse('accounting:journal_entry_detail',
                                          kwargs={'reference_number': entry.reference_number}))
        messages = list(response.context['messages'])
        self.assertTrue(
            any('inactive' in str(m).lower() for m in messages),
            "Should show error about inactive accounts"
        )
    
    def test_view_prevents_reversing_non_posted_entry(self):
        """
        Test that view validation prevents reversing entries that are not posted.
        Requirement: 16.9
        """
        # Create a draft entry
        entry = JournalEntry.objects.create(
            reference_number='JE-DRAFT-REV-001',
            transaction_date=date.today(),
            description='Draft entry to reverse',
            status='draft',  # Not posted!
            branch=self.branch,
            created_by=self.user
        )
        
        # Try to reverse it
        url = reverse('accounting:journal_entry_reverse',
                     kwargs={'reference_number': entry.reference_number})
        response = self.client.post(url, {'reason': 'Test reversal'})
        
        # Should redirect with error
        self.assertEqual(response.status_code, 302)
        
        # Verify entry is still draft (not reversed)
        entry.refresh_from_db()
        self.assertEqual(entry.status, 'draft')
        
        # Check for error message
        response = self.client.get(reverse('accounting:journal_entry_detail',
                                          kwargs={'reference_number': entry.reference_number}))
        messages = list(response.context['messages'])
        self.assertTrue(
            any('posted' in str(m).lower() for m in messages),
            "Should show error about entry needing to be posted"
        )
