Merge pull request #7 from Sea-Haven-Industries/feature/per-person-pay-rate

feat: Support per-person shift pay rates
This commit is contained in:
Adam Moussa 2026-04-07 18:12:48 -04:00 • committed by GitHub
commit 7718feb17e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 23 additions and 13 deletions

View file

@ -171,7 +171,7 @@ def create_app(bot_token: str, signing_secret: str) -> App:
prev_sunday = prev_monday + timedelta(days=6)
week_label = f"{prev_monday.strftime('%b %-d')} to {prev_sunday.strftime('%b %-d')}"
blocks = build_pay_summary_blocks(
week_label, pay_record["breakdown"], pay_record["totals"], float(pay_record["rate"])
week_label, pay_record["breakdown"], pay_record["totals"]
)
respond(blocks=blocks)
else:

View file

@ -120,22 +120,22 @@ def build_help_blocks() -> list[dict]:
return [{"type": "section", "text": {"type": "mrkdwn", "text": text}}]
def build_pay_summary_blocks(week_label: str, pay_breakdown: list[dict], total_by_person: dict[str, dict], rate: float) -> list[dict]:
def build_pay_summary_blocks(week_label: str, pay_breakdown: list[dict], total_by_person: dict[str, dict]) -> list[dict]:
"""Build Block Kit blocks for the weekly pay summary.
Args:
week_label: e.g. "Mar 30 to Apr 5"
pay_breakdown: list of {date, day, name, extension, rate} per shift
total_by_person: {name: {shifts: int, total: float, extension: str}}
rate: flat rate per shift
total_by_person: {name: {shifts: int, total: float, extension: str, rate: float}}
"""
lines = [f"_Rate: ${rate:.2f} per shift_\n"]
lines = []
for entry in pay_breakdown:
lines.append(f"{entry['day']} {entry['date_label']} — {entry['name']} (Ext {entry['extension']})")
lines.append(f"{entry['day']} {entry['date_label']} — {entry['name']} (Ext {entry['extension']}) — ${entry['rate']:.2f}")
lines.append("\n*Totals:*")
for name, info in sorted(total_by_person.items()):
lines.append(f"• {name} — {info['shifts']} shift{'s' if info['shifts'] != 1 else ''} — *${info['total']:.2f}*")
rate_note = f" @ ${info['rate']:.2f}/shift" if info.get("rate") else ""
lines.append(f"• {name} — {info['shifts']} shift{'s' if info['shifts'] != 1 else ''}{rate_note} — *${info['total']:.2f}*")
blocks = [
{

View file

@ -127,7 +127,15 @@ class ShiftSchedule:
resp = self.table.get_item(Key={"PK": "CONFIG", "SK": "CONFIG"})
return resp.get("Item", {})
def get_shift_rate(self) -> float:
"""Get the flat per-shift pay rate from config."""
def get_shift_rate(self, extension: str | None = None) -> float:
"""Get the per-shift pay rate.
Checks the roster entry for a person-specific rate first,
then falls back to the global config rate.
"""
if extension:
employee = self.get_employee_by_extension(extension)
if employee and employee.get("shift_rate"):
return float(employee["shift_rate"])
config = self.get_config()
return float(config.get("shift_rate", 0))

View file

@ -22,7 +22,7 @@ DAY_ORDER = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
def _calculate_weekly_pay(schedule: ShiftSchedule, week_start: datetime) -> dict:
"""Calculate pay for a Mon–Sun week. Returns pay record dict."""
rate = schedule.get_shift_rate()
default_rate = schedule.get_shift_rate()
breakdown = []
totals = {}
@ -35,6 +35,8 @@ def _calculate_weekly_pay(schedule: ShiftSchedule, week_start: datetime) -> dict
if source == "available":
continue
rate = schedule.get_shift_rate(ext)
breakdown.append({
"date": date_str,
"date_label": date.strftime("%b %-d"),
@ -45,13 +47,13 @@ def _calculate_weekly_pay(schedule: ShiftSchedule, week_start: datetime) -> dict
})
if name not in totals:
totals[name] = {"shifts": 0, "total": 0.0, "extension": ext}
totals[name] = {"shifts": 0, "total": 0.0, "extension": ext, "rate": rate}
totals[name]["shifts"] += 1
totals[name]["total"] += rate
return {
"week_start": week_start.strftime("%Y-%m-%d"),
"rate": str(rate),
"default_rate": str(default_rate),
"breakdown": breakdown,
"totals": totals,
}
@ -88,7 +90,7 @@ def handler(event, context):
prev_sunday = prev_monday + timedelta(days=6)
week_label = f"{prev_monday.strftime('%b %-d')} to {prev_sunday.strftime('%b %-d')}"
pay_blocks = build_pay_summary_blocks(
week_label, pay_record["breakdown"], pay_record["totals"], float(pay_record["rate"])
week_label, pay_record["breakdown"], pay_record["totals"]
)
slack.chat_postMessage(
channel=channel_id,