billing frontend for mariadb. setup as otb_billing for outsidethebox.top accounting. also involved with outsidethedb
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

117 lines
2.3 KiB

#!/usr/bin/env python3
import sys
import os
from datetime import datetime, timedelta
from dotenv import load_dotenv
# load same environment config as Flask
load_dotenv("/home/def/otb_billing/.env")
sys.path.append("/home/def/otb_billing/backend")
from app import get_db_connection, send_configured_email
REMINDER_DAYS = 7
OVERDUE_DAYS = 14
def main():
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
now = datetime.utcnow()
cursor.execute("""
SELECT
i.id,
i.invoice_number,
i.created_at,
i.total,
i.client_id,
c.email,
c.company_name,
c.contact_name
FROM invoices i
JOIN clients c ON c.id = i.client_id
WHERE i.status IN ('pending','sent')
""")
invoices = cursor.fetchall()
for inv in invoices:
age = (now - inv["created_at"]).days
email = inv["email"]
if not email:
continue
name = inv.get("contact_name") or inv.get("company_name") or "Client"
portal_url = f"https://portal.outsidethebox.top/portal/invoice/{inv['id']}"
if age >= OVERDUE_DAYS:
subject = f"Invoice {inv['invoice_number']} is overdue"
body = f"""
Hello {name},
Invoice {inv['invoice_number']} is now overdue.
Amount Due:
{inv['total']}
View invoice:
{portal_url}
Please arrange payment at your earliest convenience.
OutsideTheBox
"""
send_configured_email(
to_email=email,
subject=subject,
body=body,
attachments=None,
email_type="invoice_overdue",
invoice_id=inv["id"]
)
elif age >= REMINDER_DAYS:
subject = f"Invoice {inv['invoice_number']} reminder"
body = f"""
Hello {name},
This is a reminder that invoice {inv['invoice_number']} is still outstanding.
Amount Due:
{inv['total']}
View invoice:
{portal_url}
Thank you.
OutsideTheBox
"""
send_configured_email(
to_email=email,
subject=subject,
body=body,
attachments=None,
email_type="invoice_reminder",
invoice_id=inv["id"]
)
conn.close()
if __name__ == "__main__":
main()