@@ -1274,3 +1274,214 @@ def test_proportional_scaling_uses_all_employees_not_filtered(self, client, team
12741274 assert abs (result ['total_allocated' ] - expected_pool ) < 1 , (
12751275 f"Total allocated should be ${ expected_pool } , got ${ result ['total_allocated' ]} "
12761276 )
1277+
1278+
1279+ class TestFilterWithOverrideAndBudgetScaling :
1280+ """Test interaction between global filters, bonus overrides, and budget scaling.
1281+
1282+ Regression tests ensuring that:
1283+ 1. Budget override scales proportionally when employees are filtered
1284+ 2. Override employees (pro-rata leave) get their fixed % correctly
1285+ 3. Remaining pool is correctly reduced by override amounts
1286+ 4. All three features work together without double-counting or miscalculation
1287+ """
1288+
1289+ @pytest .fixture
1290+ def team_with_override_employee (self , db_session ):
1291+ """Create a team with a mix of normal and override employees.
1292+
1293+ Team composition (all ICs to avoid manager filter complexity):
1294+ - 3 Normal employees: $10,000 each in bonus targets ($30,000 total)
1295+ - 1 Override employee: $10,000 target, 50% override (pro-rata leave)
1296+ - Total team: $40,000 in bonus targets
1297+ """
1298+ employees = [
1299+ # Normal employees
1300+ Employee (
1301+ associate_id = 'NORM001' ,
1302+ associate = 'Normal One' ,
1303+ supervisory_organization = 'Team Alpha (Manager)' ,
1304+ current_job_profile = 'Software Engineer' ,
1305+ management_level = 'Individual Contributor (Prof Level 3)' ,
1306+ current_base_pay_manager_currency = 100000.0 ,
1307+ currency = 'USD' ,
1308+ bonus_target_manager_currency = 10000.0 ,
1309+ performance_rating_percent = 100.0 ,
1310+ ),
1311+ Employee (
1312+ associate_id = 'NORM002' ,
1313+ associate = 'Normal Two' ,
1314+ supervisory_organization = 'Team Alpha (Manager)' ,
1315+ current_job_profile = 'Software Engineer' ,
1316+ management_level = 'Individual Contributor (Prof Level 3)' ,
1317+ current_base_pay_manager_currency = 100000.0 ,
1318+ currency = 'USD' ,
1319+ bonus_target_manager_currency = 10000.0 ,
1320+ performance_rating_percent = 110.0 , # Slightly above average
1321+ ),
1322+ Employee (
1323+ associate_id = 'NORM003' ,
1324+ associate = 'Normal Three' ,
1325+ supervisory_organization = 'Team Alpha (Manager)' ,
1326+ current_job_profile = 'Senior Software Engineer' ,
1327+ management_level = 'Individual Contributor (Prof Level 4)' ,
1328+ current_base_pay_manager_currency = 120000.0 ,
1329+ currency = 'USD' ,
1330+ bonus_target_manager_currency = 10000.0 ,
1331+ performance_rating_percent = 90.0 , # Slightly below average
1332+ ),
1333+ # Override employee (pro-rata leave)
1334+ Employee (
1335+ associate_id = 'LEAVE001' ,
1336+ associate = 'Leave Employee' ,
1337+ supervisory_organization = 'Team Alpha (Manager)' ,
1338+ current_job_profile = 'Software Engineer' ,
1339+ management_level = 'Individual Contributor (Prof Level 3)' ,
1340+ current_base_pay_manager_currency = 100000.0 ,
1341+ currency = 'USD' ,
1342+ bonus_target_manager_currency = 10000.0 ,
1343+ performance_rating_percent = None , # No rating needed for override
1344+ bonus_override_percent = 50.0 , # 50% pro-rata
1345+ special_case_notes = 'Paternity leave Apr-Sep' ,
1346+ ),
1347+ ]
1348+
1349+ for emp in employees :
1350+ db_session .add (emp )
1351+ db_session .commit ()
1352+ return employees
1353+
1354+ def test_budget_scaling_with_override_employee (self , client , team_with_override_employee ):
1355+ """Budget override should scale, then override bonus deducted from scaled pool.
1356+
1357+ With $40,000 total targets and a $40,000 budget override:
1358+ - Full pool = $40,000
1359+ - Override employee gets $5,000 (50% of $10,000 target)
1360+ - Remaining pool for normal employees = $35,000
1361+ - Normal employees compete for $35,000 based on their ratings
1362+ """
1363+ from app import calculate_bonus_for_employees , get_all_employees
1364+ from models import BonusSettings , get_db
1365+
1366+ # Set budget override
1367+ db = get_db ()
1368+ settings = db .query (BonusSettings ).first ()
1369+ if not settings :
1370+ settings = BonusSettings ()
1371+ db .add (settings )
1372+ settings .budget_override = 40000.0
1373+ settings .workday_pool = 40000.0
1374+ db .commit ()
1375+
1376+ all_employees = get_all_employees ()
1377+ assert len (all_employees ) == 4 , "Should have 4 employees total"
1378+
1379+ # Verify override employee is present
1380+ override_emp = next ((e for e in all_employees if e ['Associate ID' ] == 'LEAVE001' ), None )
1381+ assert override_emp is not None , "Override employee should exist"
1382+ assert override_emp .get ('bonus_override_percent' ) == 50.0
1383+
1384+ # Calculate bonuses
1385+ params = {'upside_exponent' : 1.35 , 'downside_exponent' : 1.9 }
1386+ all_targets = sum (
1387+ emp .get ('Bonus Target Manager Currency' ) or 0
1388+ for emp in all_employees
1389+ )
1390+ assert all_targets == 40000 , f"Total targets should be $40,000, got ${ all_targets } "
1391+
1392+ result = calculate_bonus_for_employees (
1393+ all_employees ,
1394+ params ,
1395+ budget_override = 40000.0 ,
1396+ workday_pool = 40000.0 ,
1397+ all_targets_sum = all_targets
1398+ )
1399+
1400+ # Override employee should get exactly $5,000 (50% of $10,000)
1401+ override_result = next (
1402+ (r for r in result ['results' ] if r ['employee' ]['Associate ID' ] == 'LEAVE001' ),
1403+ None
1404+ )
1405+ assert override_result is not None , "Override employee should have a result"
1406+ assert override_result ['is_override' ] is True
1407+ assert abs (override_result ['final_bonus' ] - 5000 ) < 1 , (
1408+ f"Override employee should get $5,000, got ${ override_result ['final_bonus' ]} "
1409+ )
1410+
1411+ # Total allocated should equal budget override ($40,000)
1412+ assert abs (result ['total_allocated' ] - 40000 ) < 100 , (
1413+ f"Total allocated should be ~$40,000, got ${ result ['total_allocated' ]} "
1414+ )
1415+
1416+ def test_filter_plus_override_plus_budget_scaling (self , client , team_with_override_employee ):
1417+ """Three-way interaction: filter excludes some, override uses fixed %, budget scales.
1418+
1419+ Scenario: Exclude NORM003 (Senior Engineer) via employee ID filter
1420+ - Filtered targets: $30,000 (3 remaining employees)
1421+ - Budget $40,000 scales to $30,000 (75% of full pool)
1422+ - Override employee still gets $5,000 (from scaled pool)
1423+ - Remaining pool for NORM001, NORM002: $25,000
1424+ """
1425+ from app import calculate_bonus_for_employees , get_all_employees , apply_employee_filters
1426+ from models import BonusSettings , get_db
1427+
1428+ db = get_db ()
1429+ settings = db .query (BonusSettings ).first ()
1430+ if not settings :
1431+ settings = BonusSettings ()
1432+ db .add (settings )
1433+ settings .budget_override = 40000.0
1434+ settings .workday_pool = 40000.0
1435+ db .commit ()
1436+
1437+ all_employees = get_all_employees ()
1438+ all_targets = sum (
1439+ emp .get ('Bonus Target Manager Currency' ) or 0
1440+ for emp in all_employees
1441+ )
1442+
1443+ # Apply filter to exclude NORM003
1444+ filter_params = {'exclude_ids' : ['NORM003' ]}
1445+ filtered_team , _ = apply_employee_filters (all_employees , filter_params )
1446+ assert len (filtered_team ) == 3 , "Should have 3 employees after filtering"
1447+
1448+ # Verify NORM003 is excluded
1449+ assert not any (e ['Associate ID' ] == 'NORM003' for e in filtered_team )
1450+
1451+ # Calculate filtered targets
1452+ filtered_targets = sum (
1453+ emp .get ('Bonus Target Manager Currency' ) or 0
1454+ for emp in filtered_team
1455+ )
1456+ assert filtered_targets == 30000 , f"Filtered targets should be $30,000, got ${ filtered_targets } "
1457+
1458+ # Calculate bonuses with proportional scaling
1459+ params = {'upside_exponent' : 1.35 , 'downside_exponent' : 1.9 }
1460+ result = calculate_bonus_for_employees (
1461+ filtered_team ,
1462+ params ,
1463+ budget_override = 40000.0 ,
1464+ workday_pool = 40000.0 ,
1465+ all_targets_sum = all_targets # Use ALL employees' targets for proportion
1466+ )
1467+
1468+ # Scaled pool should be 75% of $40,000 = $30,000
1469+ expected_pool = 40000 * (filtered_targets / all_targets ) # 40000 * 0.75 = 30000
1470+ assert abs (result ['total_pool' ] - expected_pool ) < 1 , (
1471+ f"Scaled pool should be ${ expected_pool } , got ${ result ['total_pool' ]} "
1472+ )
1473+
1474+ # Override employee should still get $5,000 (50% of their $10,000 target)
1475+ override_result = next (
1476+ (r for r in result ['results' ] if r ['employee' ]['Associate ID' ] == 'LEAVE001' ),
1477+ None
1478+ )
1479+ assert override_result is not None
1480+ assert abs (override_result ['final_bonus' ] - 5000 ) < 1 , (
1481+ f"Override employee should get $5,000 even when filtered, got ${ override_result ['final_bonus' ]} "
1482+ )
1483+
1484+ # Total allocated should be close to scaled pool ($30,000)
1485+ assert abs (result ['total_allocated' ] - 30000 ) < 100 , (
1486+ f"Total allocated should be ~$30,000, got ${ result ['total_allocated' ]} "
1487+ )
0 commit comments