Merge pull request #8 from Sea-Haven-Industries/feature/oncall-rate-command

feat: Add /oncall rate command for managing pay rates
This commit is contained in:
Adam Moussa 2026-04-07 18:16:08 -04:00 • committed by GitHub
commit 9782cd98ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 72 additions and 0 deletions

View file

@ -105,6 +105,8 @@ def create_app(bot_token: str, signing_secret: str) -> App:
respond(blocks=build_roster_blocks(schedule.get_roster()))
elif text == "pay":
_show_pay(respond, schedule)
elif text.startswith("rate"):
_handle_rate(respond, schedule, text)
elif text.startswith("register"):
_handle_register(respond, schedule, user_id, text)
elif text.startswith("pick"):
@ -177,6 +179,57 @@ def create_app(bot_token: str, signing_secret: str) -> App:
else:
respond(text=f"No pay record found for the week of {prev_monday.strftime('%b %-d')}.")
def _handle_rate(respond, schedule, text):
parts = text.split()
# /oncall rate — show current rates
if len(parts) == 1:
default_rate = schedule.get_shift_rate()
roster = schedule.get_roster()
lines = [f"*Default rate:* ${default_rate:.2f}/shift\n"]
custom = [(e["SK"], e.get("name", "Unknown"), float(e["shift_rate"])) for e in roster if e.get("shift_rate")]
if custom:
lines.append("*Per-person rates:*")
for ext, name, rate in sorted(custom, key=lambda x: x[0]):
lines.append(f"• {name} (Ext {ext}) — ${rate:.2f}/shift")
else:
lines.append("_No per-person rates set — everyone uses the default._")
respond(text="\n".join(lines))
return
# /oncall rate default <amount>
if parts[1] == "default":
if len(parts) < 3:
respond(text="Usage: `/oncall rate default <amount>` (e.g. `/oncall rate default 50`)")
return
try:
amount = float(parts[2].replace("$", ""))
except ValueError:
respond(text=f"Invalid amount: `{parts[2]}`")
return
schedule.set_default_shift_rate(amount)
respond(text=f"Default shift rate set to *${amount:.2f}*.")
return
# /oncall rate <extension> <amount>
if len(parts) < 3:
respond(text="Usage: `/oncall rate <extension> <amount>` (e.g. `/oncall rate 114 75`)")
return
ext = parts[1]
employee = schedule.get_employee_by_extension(ext)
if not employee:
respond(text=f"Extension `{ext}` not found in the roster.")
return
try:
amount = float(parts[2].replace("$", ""))
except ValueError:
respond(text=f"Invalid amount: `{parts[2]}`")
return
schedule.set_employee_shift_rate(ext, amount)
respond(text=f"Shift rate for *{employee['name']}* (Ext {ext}) set to *${amount:.2f}*.")
def _handle_register(respond, schedule, user_id, text):
parts = text.split()
if len(parts) < 2:

View file

@ -113,6 +113,9 @@ def build_help_blocks() -> list[dict]:
"`/oncall swap <date> @person` — Swap your shift with someone\n"
"`/oncall register <extension>` — Link your Slack account to your extension\n"
"`/oncall pay` — Show last week's pay summary\n"
"`/oncall rate` — Show current shift rates\n"
"`/oncall rate default <amount>` — Set the default rate\n"
"`/oncall rate <ext> <amount>` — Set a person's rate\n"
"`/oncall roster` — Show all registered employees\n"
"`/oncall help` — Show this help message\n\n"
"_Dates can be: today, tomorrow, monday, friday, 4/5, 2026-04-05_"

View file

@ -127,6 +127,22 @@ class ShiftSchedule:
resp = self.table.get_item(Key={"PK": "CONFIG", "SK": "CONFIG"})
return resp.get("Item", {})
def set_default_shift_rate(self, rate: float) -> None:
"""Set the global default shift rate in config."""
self.table.update_item(
Key={"PK": "CONFIG", "SK": "CONFIG"},
UpdateExpression="SET shift_rate = :r",
ExpressionAttributeValues={":r": str(rate)},
)
def set_employee_shift_rate(self, extension: str, rate: float) -> None:
"""Set a per-person shift rate on a roster entry."""
self.table.update_item(
Key={"PK": "ROSTER", "SK": extension},
UpdateExpression="SET shift_rate = :r",
ExpressionAttributeValues={":r": str(rate)},
)
def get_shift_rate(self, extension: str | None = None) -> float:
"""Get the per-shift pay rate.