"""Tests for weekly-post pay calculation helpers.""" from datetime import datetime from decimal import Decimal # 2026-06-01 is a Monday. Day names come from the date, so a week may start any day. WEEK_START = datetime(2026, 6, 1) # Sunday that opens the week containing Monday 2026-06-01. SUNDAY_WEEK = datetime(2026, 5, 31) class TestAddShiftToPay: def test_assigned_shift_is_added(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") seed.weekly("Monday", "114", "Alice") breakdown, totals = [], {} weeklypost_app._add_shift_to_pay( schedule, breakdown, totals, datetime(2026, 6, 1), "Monday" ) assert len(breakdown) == 1 assert breakdown[0]["name"] == "Alice" assert breakdown[0]["rate"] == Decimal("50") assert totals["Alice"]["shifts"] == 1 assert totals["Alice"]["total"] == Decimal("50") def test_available_shift_is_skipped(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") breakdown, totals = [], {} weeklypost_app._add_shift_to_pay( schedule, breakdown, totals, datetime(2026, 6, 1), "Monday" ) assert breakdown == [] and totals == {} def test_weekend_day_label(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") seed.weekly("Saturday", "200", "Alice", shift_type="day") breakdown, totals = [], {} weeklypost_app._add_shift_to_pay( schedule, breakdown, totals, datetime(2026, 6, 6), "Saturday", "day" ) assert breakdown[0]["day"] == "Sat (Day)" class TestCalculateWeeklyPay: def test_single_weekday_shift(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") seed.weekly("Monday", "114", "Alice") record = weeklypost_app._calculate_weekly_pay(schedule, WEEK_START) assert record["week_start"] == "2026-06-01" assert len(record["breakdown"]) == 1 assert record["totals"]["Alice"]["total"] == Decimal("50") def test_per_employee_rate_applied(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") seed.roster("114", "Alice", shift_rate="75") seed.weekly("Monday", "114", "Alice") record = weeklypost_app._calculate_weekly_pay(schedule, WEEK_START) assert record["totals"]["Alice"]["total"] == Decimal("75.0") def test_weekend_counts_day_and_night(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") seed.weekly("Saturday", "200", "Alice", shift_type="day") seed.weekly("Saturday", "200", "Alice", shift_type="night") record = weeklypost_app._calculate_weekly_pay(schedule, WEEK_START) # Alice worked both Saturday shifts → 2 shifts, $100. assert record["totals"]["Alice"]["shifts"] == 2 assert record["totals"]["Alice"]["total"] == Decimal("100") def test_saturday_night_stays_in_week_ending_saturday( self, weeklypost_app, schedule, seed ): # Sat 2026-06-06 night runs into Sunday morning and stays on Saturday. # Sun 2026-06-07 day opens the next week. Sun 2026-05-31 day is this week. seed.config(shift_rate="50") seed.weekly("Saturday", "200", "Alice", shift_type="night") seed.weekly("Sunday", "201", "Bob", shift_type="day") record = weeklypost_app._calculate_weekly_pay(schedule, SUNDAY_WEEK) by_date = {line["date"]: line for line in record["breakdown"]} assert by_date["2026-06-06"]["name"] == "Alice" assert by_date["2026-06-06"]["day"].startswith("Sat") assert by_date["2026-05-31"]["name"] == "Bob" assert "2026-06-07" not in by_date def test_sunday_day_opens_the_next_week(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") seed.weekly("Saturday", "200", "Alice", shift_type="night") seed.weekly("Sunday", "201", "Bob", shift_type="day") record = weeklypost_app._calculate_weekly_pay(schedule, datetime(2026, 6, 7)) by_date = {line["date"]: line for line in record["breakdown"]} assert by_date["2026-06-07"]["name"] == "Bob" assert by_date["2026-06-13"]["name"] == "Alice" assert "2026-06-06" not in by_date def test_empty_week_has_no_breakdown(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") record = weeklypost_app._calculate_weekly_pay(schedule, WEEK_START) assert record["breakdown"] == [] and record["totals"] == {} class TestHolidayPay: def test_single_holiday_assignee(self, weeklypost_app, schedule, seed): # Wed 2026-06-03 holiday; one assignee at base $50 * 1.5 = $75. seed.config(shift_rate="50") seed.holiday( "2026-06-03", slots=2, label="Founders Day", assignees={"114": {"name": "Alice", "claimed_at": "x"}}, ) breakdown, totals = [], {} weeklypost_app._add_shift_to_pay( schedule, breakdown, totals, datetime(2026, 6, 3), "Wednesday", "day" ) assert len(breakdown) == 1 line = breakdown[0] assert line["name"] == "Alice" assert line["is_holiday"] is True assert line["holiday_label"] == "Founders Day" assert line["multiplier"] == Decimal("1.5") assert line["base_rate"] == Decimal("50") # rate is the effective (multiplied) pay for Slack-block compatibility. assert line["rate"] == Decimal("75.0") assert line["amount"] == Decimal("75.0") assert isinstance(line["multiplier"], Decimal) assert isinstance(line["amount"], Decimal) assert totals["Alice"]["shifts"] == 1 assert totals["Alice"]["holiday_shifts"] == 1 assert totals["Alice"]["total"] == Decimal("75.0") def test_two_assignees_same_holiday(self, weeklypost_app, schedule, seed): # Two slots filled on the same holiday; each paid base * 1.5. seed.config(shift_rate="40") seed.roster("114", "Alice", shift_rate="40") seed.roster("200", "Bob", shift_rate="60") seed.holiday( "2026-06-03", slots=2, label="Founders Day", assignees={ "114": {"name": "Alice", "claimed_at": "x"}, "200": {"name": "Bob", "claimed_at": "y"}, }, ) breakdown, totals = [], {} weeklypost_app._add_shift_to_pay( schedule, breakdown, totals, datetime(2026, 6, 3), "Wednesday", "day" ) assert len(breakdown) == 2 assert all(line["is_holiday"] for line in breakdown) by_name = {line["name"]: line for line in breakdown} assert by_name["Alice"]["amount"] == Decimal("60.0") # 40 * 1.5 assert by_name["Bob"]["amount"] == Decimal("90.0") # 60 * 1.5 assert totals["Alice"]["total"] == Decimal("60.0") assert totals["Bob"]["total"] == Decimal("90.0") assert totals["Alice"]["holiday_shifts"] == 1 assert totals["Bob"]["holiday_shifts"] == 1 def test_custom_multiplier(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") seed.holiday( "2026-06-03", slots=1, label="Double Day", assignees={"114": {"name": "Alice", "claimed_at": "x"}}, multiplier=Decimal("2"), ) breakdown, totals = [], {} weeklypost_app._add_shift_to_pay( schedule, breakdown, totals, datetime(2026, 6, 3), "Wednesday", "day" ) assert breakdown[0]["multiplier"] == Decimal("2") assert breakdown[0]["amount"] == Decimal("100") assert totals["Alice"]["total"] == Decimal("100") def test_empty_holiday_adds_nothing(self, weeklypost_app, schedule, seed): seed.config(shift_rate="50") seed.holiday("2026-06-03", slots=2, label="Empty", assignees={}) breakdown, totals = [], {} weeklypost_app._add_shift_to_pay( schedule, breakdown, totals, datetime(2026, 6, 3), "Wednesday", "day" ) assert breakdown == [] and totals == {} def test_mixed_week(self, weeklypost_app, schedule, seed): # A week with a weekday night shift, a weekend day+night, and a holiday. seed.config(shift_rate="50") # Regular Monday night for Alice. seed.weekly("Monday", "114", "Alice") # Holiday on Wednesday (day shift) with Alice + Bob. seed.roster("200", "Bob", shift_rate="50") seed.holiday( "2026-06-03", slots=2, label="Founders Day", assignees={ "114": {"name": "Alice", "claimed_at": "x"}, "200": {"name": "Bob", "claimed_at": "y"}, }, ) # Saturday day + night for Bob (regular weekend). seed.weekly("Saturday", "200", "Bob", shift_type="day") seed.weekly("Saturday", "200", "Bob", shift_type="night") record = weeklypost_app._calculate_weekly_pay(schedule, WEEK_START) # Alice: Mon night $50 + Wed holiday $75 = $125 over 2 shifts. assert record["totals"]["Alice"]["shifts"] == 2 assert record["totals"]["Alice"]["total"] == Decimal("125.0") assert record["totals"]["Alice"]["holiday_shifts"] == 1 # Bob: Wed holiday $75 + Sat day $50 + Sat night $50 = $175 over 3 shifts. assert record["totals"]["Bob"]["shifts"] == 3 assert record["totals"]["Bob"]["total"] == Decimal("175.0") assert record["totals"]["Bob"]["holiday_shifts"] == 1 # Exactly two holiday breakdown lines (Alice + Bob on Wed). holiday_lines = [b for b in record["breakdown"] if b.get("is_holiday")] assert len(holiday_lines) == 2 assert {line["name"] for line in holiday_lines} == {"Alice", "Bob"} def test_night_on_holiday_date_is_regular(self, weeklypost_app, schedule, seed): # A holiday only applies to the day shift; night falls through to weekly. seed.config(shift_rate="50") seed.weekly("Wednesday", "114", "Alice") seed.holiday( "2026-06-03", slots=1, label="Founders Day", assignees={"200": {"name": "Bob", "claimed_at": "y"}}, ) breakdown, totals = [], {} weeklypost_app._add_shift_to_pay( schedule, breakdown, totals, datetime(2026, 6, 3), "Wednesday", "night" ) assert len(breakdown) == 1 assert breakdown[0]["name"] == "Alice" assert breakdown[0].get("is_holiday") is None assert breakdown[0]["amount"] == Decimal("50")