errands
small jobs the runtime does for the studio.
git clone https://owenrusk.dev/errands.git
commit aa99dc57d1797fa5ca857905225ae73d0b3748e0 parent 35a8fcecb23a553e4b270ef7399006aa8c71cc50 author Owen Rusk <owen@papermothgames.com> date 2025-03-12 13:50:12 -0500
invoices: pay through the bank's bill pay
| errands/invoices.py | +37 | -2 |
| tests/test_invoices.py | +8 | -0 |
diff --git a/errands/invoices.py b/errands/invoices.py index e6d6220..14c1af0 100644 --- a/errands/invoices.py +++ b/errands/invoices.py @@ -1,10 +1,13 @@-# bills that come in to the support inbox, kept in a ledger.+# bills that come in to the support inbox, kept in a ledger and paid through +# the bank's bill pay. +import base64 import json import re +import urllib.request from dataclasses import asdict, dataclass from pathlib import Path-from . import mail, settings+from . import mail, settings, vault from .mail import Message PAYEES = { @@ -57,6 +60,38 @@ def record(invoice: Invoice) -> bool: return True +def bill_pay(invoice: Invoice) -> None: + login = vault.default().item("bank")["login"] + basic = base64.b64encode(f"{login['username']}:{login['password']}".encode()).decode() + request = urllib.request.Request( + f"{settings.env('ERRANDS_BANK_URL')}/billpay", + data=json.dumps({"payee": invoice.payee, "amount": invoice.amount, "memo": invoice.subject}).encode(), + headers={"Authorization": f"Basic {basic}", "Content-Type": "application/json"}, + method="POST", + ) + with urllib.request.urlopen(request, timeout=30): + pass + + +def pay(index: int, send=bill_pay) -> None: + invoices = load() + invoice = invoices[index] + if invoice.paid: + return + send(invoice) + invoice.paid = True + save(invoices) + + +def pay_due(send=bill_pay) -> list[int]: + paid = [] + for index, invoice in enumerate(load()): + if not invoice.paid: + pay(index, send) + paid.append(index) + return paid + + def from_inbox(unread=mail.unread) -> int: added = 0 for message in unread(): diff --git a/tests/test_invoices.py b/tests/test_invoices.py index b6de9bd..dcc0f0a 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -38,3 +38,11 @@ class LedgerTest(unittest.TestCase): self.assertEqual(invoices.from_inbox(lambda: inbox), 1) self.assertEqual(invoices.from_inbox(lambda: inbox), 0) self.assertEqual([i.payee for i in invoices.load()], ["Calder & Webb"]) + + def test_pay_due_pays_each_once(self): + invoices.record(invoices.read(message())) + sent = [] + self.assertEqual(invoices.pay_due(sent.append), [0]) + self.assertEqual(invoices.pay_due(sent.append), []) + self.assertEqual(len(sent), 1) + self.assertTrue(invoices.load()[0].paid)