owenrusk.dev

errands

small jobs the runtime does for the studio.

git clone https://owenrusk.dev/errands.git

commit c2491a364fe2c71f63a54397c9fd5cc3a7ff280d
parent 1772d4f1a1c1c13f9c728ce64c2edf0bd043256e
author Owen Rusk <owen@papermothgames.com>
date   2025-02-27 14:40:21 -0600
invoices: read bills out of the support inbox
errands/invoices.py+27-0
tests/test_invoices.py+21-0
diff --git a/errands/invoices.py b/errands/invoices.py
new file mode 100644
index 0000000..626cd52
--- /dev/null
+++ b/errands/invoices.py
@@ -0,0 +1,27 @@
+# bills that come in to the support inbox.
+import re
+from dataclasses import dataclass
+
+from .mail import Message
+
+PAYEES = {
+    "leasing@calderwebb.com": "Calder & Webb",
+}
+AMOUNT = re.compile(r"(?:amount|total|balance|rent)(?: due)?:?\s*\$([\d,]+\.\d{2})", re.IGNORECASE)
+
+
+@dataclass
+class Invoice:
+    payee: str
+    sender: str
+    subject: str
+    amount: str
+    paid: bool = False
+
+
+def read(message: Message) -> Invoice | None:
+    payee = PAYEES.get(message.sender)
+    found = AMOUNT.search(message.body)
+    if payee is None or found is None:
+        return None
+    return Invoice(payee, message.sender, message.subject, found.group(1).replace(",", ""))
diff --git a/tests/test_invoices.py b/tests/test_invoices.py
new file mode 100644
index 0000000..f6ada3c
--- /dev/null
+++ b/tests/test_invoices.py
@@ -0,0 +1,21 @@
+import unittest
+
+from errands import invoices
+from errands.mail import Message
+
+
+def message(sender="leasing@calderwebb.com", subject="Rent, Unit 4", body="Rent due: $1,875.00\n"):
+    return Message(uid="1", sender=sender, subject=subject, message_id="", references="", body=body)
+
+
+class ReadTest(unittest.TestCase):
+    def test_a_bill_from_a_payee(self):
+        invoice = invoices.read(message())
+        self.assertEqual(invoice.payee, "Calder & Webb")
+        self.assertEqual(invoice.amount, "1875.00")
+
+    def test_anyone_else_is_just_mail(self):
+        self.assertIsNone(invoices.read(message(sender="someone@example.com")))
+
+    def test_no_amount(self):
+        self.assertIsNone(invoices.read(message(body="the heating is on from monday.\n")))