Skip to content

Conversation

@MaxGhenis
Copy link
Collaborator

Summary

Implements firm-level VAT logic for PolicyEngine UK, enabling modeling of VAT at the business level with registration thresholds and rate calculations.

Changes

  • ✨ Added Firm entity with owner role linking to persons
  • 📊 Created VAT parameters:
    • Registration threshold (£90,000)
    • Deregistration threshold (£88,000)
    • Zero rate (0%)
  • 🧮 Implemented 8 VAT calculation variables:
    • firm_turnover: Annual business turnover
    • firm_vat_registered: Registration status based on threshold
    • firm_standard_rated_supplies: Supplies at 20% rate
    • firm_reduced_rated_supplies: Supplies at 5% rate
    • firm_zero_rated_supplies: Supplies at 0% rate
    • firm_vat_on_sales: Output VAT calculation
    • firm_vat_on_purchases: Input VAT
    • firm_net_vat_liability: Net VAT (output - input)

Testing

  • ✅ 8 tests passing (3 entity tests, 5 VAT tests)
  • 🧪 Tests cover entity creation, VAT thresholds, and calculations
  • 🎨 Code formatted with Black

Use Cases

This enables PolicyEngine to:

  • Model VAT policy changes on businesses
  • Analyze VAT threshold impacts
  • Calculate VAT revenue from firms
  • Support business tax reform analysis

Fixes #1320

🤖 Generated with Claude Code

- Create Firm entity with owner role
- Add VAT registration thresholds (£90k) and rates
- Implement VAT calculation variables for firms
- Add comprehensive tests for firm entity and VAT logic

Fixes #1320
@MaxGhenis
Copy link
Collaborator Author

The test failure appears to be unrelated to the firm VAT implementation. The error is an ImportError with the 'tables' module in test_reform_impacts.py:

ERROR collecting policyengine_uk/tests/microsimulation/test_reform_impacts.py
ImportError: Missing optional dependency 'pytables'

All the firm VAT tests passed successfully (5 tests in test_firm_vat.py). This seems to be an existing CI issue with the pytables dependency on macOS.

The test_reform_impacts.py test was failing in CI because it tries to instantiate
a Microsimulation which loads data from HDF5 files, requiring the tables package.
This was working before but missing from explicit dependencies.
The test_validity.py test expects all entities to have a weight variable.
Added firm_weight and firm_count variables to support microsimulation weighting.
Firm entity should not be in group_entities as it's not part of the
household hierarchy and we don't have firm microdata for simulations.
This was causing the reform impact tests to produce different results.
YAML test files don't include firm data, so we need to make firm
populations optional. Only create firm populations when firm data
is explicitly provided in the situation.
Firm entity and variables exist but are not included in default entity
list to avoid issues with YAML tests that don't provide firm data.
Firm simulations will need to explicitly add the Firm entity.
@MaxGhenis
Copy link
Collaborator Author

CI Status Update

The firm-level VAT implementation is structurally complete but faces integration challenges with the existing test infrastructure:

What's Implemented ✅

  • Firm entity definition
  • VAT parameters (registration/deregistration thresholds, rates)
  • 10 VAT calculation variables (turnover, registration status, various rated supplies, VAT on sales/purchases, net liability)
  • Firm weight variables for microsimulation
  • Unit tests for firm VAT calculations (passing locally when run in isolation)

Integration Challenge ⚠️

The core issue is that adding the Firm entity to the tax-benefit system causes YAML-based tests to fail because:

  1. YAML tests don't provide firm population data
  2. The core Simulation class tries to check all variables including firm ones
  3. This creates a ValueError when accessing firm populations that don't exist

Attempted Solutions

  1. ✅ Added pytables to dev dependencies (fixed HDF5 import issue)
  2. ✅ Added firm_weight and firm_count variables (fixed microsimulation weight issue)
  3. ⚠️ Tried making firm populations optional - causes our firm tests to fail
  4. ⚠️ Tried removing Firm from default entities - breaks firm simulations
  5. ⚠️ Tried dynamically adding Firm entity - complex interaction with core Simulation class

Next Steps

This PR demonstrates the implementation approach for firm-level VAT. To fully integrate, we would need to either:

  • Modify policyengine-core to handle optional entities gracefully
  • Create a separate test runner for firm-specific tests
  • Refactor how entities are loaded to be more dynamic

The code is ready for review as a proof of concept for firm-level taxation modeling.

Changed 'if' to 'when' to make it clear registration is mandatory
when taxable turnover exceeds the threshold, not optional.
- Added defined_for="firm_vat_registered" to VAT calculation variables
  since only registered firms charge/pay/reclaim VAT
- Reworded deregistration threshold to describe requirement to remain
  registered above the threshold, for consistency with registration threshold
Changed from 'vat_params' and 'threshold' to use 'p' for accessing
parameter subtrees, following PolicyEngine coding conventions.
Zero rate is always 0% by definition, so we don't need a parameter file.
Updated firm_vat_on_sales to only calculate VAT on standard and reduced
rated supplies.
@MaxGhenis
Copy link
Collaborator Author

@nikhilwoodruff @vahid-ahmadi could you take a look to see how we can pass the tests? Adding a new entity type seems to cause some issues.

@MaxGhenis
Copy link
Collaborator Author

Superseded by #1332

@MaxGhenis MaxGhenis closed this Sep 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add firm-level VAT logic with new Firm entity

2 participants