"""Response formatting utilities."""
from datetime import datetime
from typing import Optional
from .models import InvoiceSummary, OracleApiResponse, OracleInvoice
def format_currency(amount: float, currency: str = "USD") -> str:
"""Format currency values."""
return f"${amount:,.2f}"
def format_date(date_string: str) -> str:
"""Format date strings."""
try:
dt = datetime.fromisoformat(date_string.replace("Z", "+00:00"))
return dt.strftime("%b %d, %Y")
except Exception:
return date_string
def get_days_overdue(due_date: str) -> int:
"""Calculate days overdue."""
try:
due = datetime.fromisoformat(due_date.replace("Z", "+00:00"))
today = datetime.now(due.tzinfo)
diff = (today - due).days
return max(0, diff)
except Exception:
return 0
def create_invoice_summary(api_response: OracleApiResponse) -> InvoiceSummary:
"""Create invoice summary with calculated totals."""
total_amount = sum(inv.InvoiceAmount for inv in api_response.items)
total_outstanding = sum(inv.OutstandingAmount for inv in api_response.items)
return InvoiceSummary(
items=api_response.items,
count=api_response.count,
hasMore=api_response.hasMore,
totalCount=api_response.totalResults or api_response.count,
offset=api_response.offset,
limit=api_response.limit,
totalAmount=total_amount,
totalOutstanding=total_outstanding,
)
def format_invoice_list_markdown(summary: InvoiceSummary) -> str:
"""Format invoice list as Markdown."""
lines = ["# Invoice Summary\n"]
# Summary statistics
lines.append("## Statistics\n")
lines.append(f"**Total Invoices Found**: {summary.totalCount}")
lines.append(
f"**Showing**: {summary.count} invoice(s) (offset: {summary.offset}, "
f"limit: {summary.limit})\n"
)
if summary.totalAmount:
lines.append(f"**Total Invoice Amount**: {format_currency(summary.totalAmount)}")
if summary.totalOutstanding:
lines.append(f"**Total Outstanding**: {format_currency(summary.totalOutstanding)}")
if summary.hasMore:
next_offset = summary.offset + summary.limit
lines.append(
f"\n*Note: More results available. Use offset {next_offset} to retrieve "
"next page.*\n"
)
if not summary.items:
lines.append("\n## No Invoices Found\n")
lines.append("No invoices match your criteria. Try:")
lines.append("- Broadening the date range")
lines.append("- Removing specific filters")
lines.append("- Checking spelling of customer names\n")
return "\n".join(lines)
# Invoice list
lines.append("\n## Invoices\n")
for index, invoice in enumerate(summary.items, 1):
lines.append(f"### {index}. {invoice.InvoiceNumber}\n")
lines.append(f"- **Customer**: {invoice.CustomerName}")
lines.append(
f"- **Amount**: {format_currency(invoice.InvoiceAmount, invoice.InvoiceCurrencyCode)}"
)
lines.append(f"- **Status**: {invoice.InvoiceStatus}")
lines.append(f"- **Invoice Date**: {format_date(invoice.InvoiceDate)}")
lines.append(f"- **Due Date**: {format_date(invoice.DueDate)}")
if invoice.OutstandingAmount > 0:
lines.append(
f"- **Outstanding**: {format_currency(invoice.OutstandingAmount, invoice.InvoiceCurrencyCode)}"
)
if invoice.InvoiceStatus == "Open":
days_overdue = get_days_overdue(invoice.DueDate)
if days_overdue > 0:
lines.append(f"- **⚠️ Overdue**: {days_overdue} day(s)")
if invoice.PaidAmount > 0:
lines.append(
f"- **Paid**: {format_currency(invoice.PaidAmount, invoice.InvoiceCurrencyCode)}"
)
if invoice.PaymentTerms:
lines.append(f"- **Payment Terms**: {invoice.PaymentTerms}")
lines.append(f"- **Invoice ID**: {invoice.InvoiceId}\n")
return "\n".join(lines)
def format_invoice_details_markdown(invoice: OracleInvoice) -> str:
"""Format single invoice details as Markdown."""
lines = [f"# Invoice Details: {invoice.InvoiceNumber}\n"]
# Header information
lines.append("## Summary\n")
lines.append(f"- **Invoice Number**: {invoice.InvoiceNumber}")
lines.append(f"- **Invoice ID**: {invoice.InvoiceId}")
lines.append(f"- **Status**: {invoice.InvoiceStatus}")
lines.append(
f"- **Amount**: {format_currency(invoice.InvoiceAmount, invoice.InvoiceCurrencyCode)}"
)
lines.append(f"- **Currency**: {invoice.InvoiceCurrencyCode}")
# Dates
lines.append("\n## Dates\n")
lines.append(f"- **Invoice Date**: {format_date(invoice.InvoiceDate)}")
lines.append(f"- **Due Date**: {format_date(invoice.DueDate)}")
lines.append(f"- **Created**: {format_date(invoice.CreationDate)}")
lines.append(f"- **Last Updated**: {format_date(invoice.LastUpdateDate)}")
# Payment information
lines.append("\n## Payment Information\n")
lines.append(
f"- **Total Amount**: {format_currency(invoice.InvoiceAmount, invoice.InvoiceCurrencyCode)}"
)
lines.append(
f"- **Paid Amount**: {format_currency(invoice.PaidAmount, invoice.InvoiceCurrencyCode)}"
)
lines.append(
f"- **Outstanding**: {format_currency(invoice.OutstandingAmount, invoice.InvoiceCurrencyCode)}"
)
if invoice.PaymentTerms:
lines.append(f"- **Payment Terms**: {invoice.PaymentTerms}")
if invoice.InvoiceStatus == "Open" and invoice.OutstandingAmount > 0:
days_overdue = get_days_overdue(invoice.DueDate)
if days_overdue > 0:
lines.append(f"- **⚠️ Overdue**: {days_overdue} day(s)")
else:
days_until_due = -days_overdue
lines.append(f"- **Due in**: {days_until_due} day(s)")
# Customer information
lines.append("\n## Customer Information\n")
lines.append(f"- **Customer Name**: {invoice.CustomerName}")
lines.append(f"- **Customer ID**: {invoice.CustomerId}")
if invoice.BillToCustomerName:
lines.append(f"- **Bill To**: {invoice.BillToCustomerName}")
if invoice.BillToAddress:
lines.append(f"- **Billing Address**: {invoice.BillToAddress}")
return "\n".join(lines)
def format_search_results_markdown(
summary: InvoiceSummary, search_query: Optional[str] = None
) -> str:
"""Format search results with statistics."""
lines = ["# Invoice Search Results\n"]
if search_query:
lines.append(f"**Search Query**: {search_query}\n")
lines.append(format_invoice_list_markdown(summary))
return "\n".join(lines)
def format_error_message(error: Exception) -> str:
"""Format error message for user."""
return f"# Error\n\n{str(error)}"