mirror of
https://github.com/Sea-Haven-Industries/afterhours-shift-manager.git
synced 2026-10-05 07:12:05 +00:00
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:
commit
7718feb17e
4 changed files with 23 additions and 13 deletions
|
|
@ -171,7 +171,7 @@ def create_app(bot_token: str, signing_secret: str) -> App:
|
||||||
prev_sunday = prev_monday + timedelta(days=6)
|
prev_sunday = prev_monday + timedelta(days=6)
|
||||||
week_label = f"{prev_monday.strftime('%b %-d')} to {prev_sunday.strftime('%b %-d')}"
|
week_label = f"{prev_monday.strftime('%b %-d')} to {prev_sunday.strftime('%b %-d')}"
|
||||||
blocks = build_pay_summary_blocks(
|
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)
|
respond(blocks=blocks)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -120,22 +120,22 @@ def build_help_blocks() -> list[dict]:
|
||||||
return [{"type": "section", "text": {"type": "mrkdwn", "text": text}}]
|
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.
|
"""Build Block Kit blocks for the weekly pay summary.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
week_label: e.g. "Mar 30 to Apr 5"
|
week_label: e.g. "Mar 30 to Apr 5"
|
||||||
pay_breakdown: list of {date, day, name, extension, rate} per shift
|
pay_breakdown: list of {date, day, name, extension, rate} per shift
|
||||||
total_by_person: {name: {shifts: int, total: float, extension: str}}
|
total_by_person: {name: {shifts: int, total: float, extension: str, rate: float}}
|
||||||
rate: flat rate per shift
|
|
||||||
"""
|
"""
|
||||||
lines = [f"_Rate: ${rate:.2f} per shift_\n"]
|
lines = []
|
||||||
for entry in pay_breakdown:
|
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:*")
|
lines.append("\n*Totals:*")
|
||||||
for name, info in sorted(total_by_person.items()):
|
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 = [
|
blocks = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,15 @@ class ShiftSchedule:
|
||||||
resp = self.table.get_item(Key={"PK": "CONFIG", "SK": "CONFIG"})
|
resp = self.table.get_item(Key={"PK": "CONFIG", "SK": "CONFIG"})
|
||||||
return resp.get("Item", {})
|
return resp.get("Item", {})
|
||||||
|
|
||||||
def get_shift_rate(self) -> float:
|
def get_shift_rate(self, extension: str | None = None) -> float:
|
||||||
"""Get the flat per-shift pay rate from config."""
|
"""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()
|
config = self.get_config()
|
||||||
return float(config.get("shift_rate", 0))
|
return float(config.get("shift_rate", 0))
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ DAY_ORDER = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
|
||||||
|
|
||||||
def _calculate_weekly_pay(schedule: ShiftSchedule, week_start: datetime) -> dict:
|
def _calculate_weekly_pay(schedule: ShiftSchedule, week_start: datetime) -> dict:
|
||||||
"""Calculate pay for a Mon–Sun week. Returns pay record dict."""
|
"""Calculate pay for a Mon–Sun week. Returns pay record dict."""
|
||||||
rate = schedule.get_shift_rate()
|
default_rate = schedule.get_shift_rate()
|
||||||
breakdown = []
|
breakdown = []
|
||||||
totals = {}
|
totals = {}
|
||||||
|
|
||||||
|
|
@ -35,6 +35,8 @@ def _calculate_weekly_pay(schedule: ShiftSchedule, week_start: datetime) -> dict
|
||||||
if source == "available":
|
if source == "available":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
rate = schedule.get_shift_rate(ext)
|
||||||
|
|
||||||
breakdown.append({
|
breakdown.append({
|
||||||
"date": date_str,
|
"date": date_str,
|
||||||
"date_label": date.strftime("%b %-d"),
|
"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:
|
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]["shifts"] += 1
|
||||||
totals[name]["total"] += rate
|
totals[name]["total"] += rate
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"week_start": week_start.strftime("%Y-%m-%d"),
|
"week_start": week_start.strftime("%Y-%m-%d"),
|
||||||
"rate": str(rate),
|
"default_rate": str(default_rate),
|
||||||
"breakdown": breakdown,
|
"breakdown": breakdown,
|
||||||
"totals": totals,
|
"totals": totals,
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +90,7 @@ def handler(event, context):
|
||||||
prev_sunday = prev_monday + timedelta(days=6)
|
prev_sunday = prev_monday + timedelta(days=6)
|
||||||
week_label = f"{prev_monday.strftime('%b %-d')} to {prev_sunday.strftime('%b %-d')}"
|
week_label = f"{prev_monday.strftime('%b %-d')} to {prev_sunday.strftime('%b %-d')}"
|
||||||
pay_blocks = build_pay_summary_blocks(
|
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(
|
slack.chat_postMessage(
|
||||||
channel=channel_id,
|
channel=channel_id,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue