forked from coracle/caravel
Use invoice items instead of amount
This commit is contained in:
+4
-4
@@ -696,7 +696,7 @@ async fn list_invoices(
|
||||
let pubkey = state.api.extract_auth_pubkey(&headers)?;
|
||||
state.api.require_admin(&pubkey)?;
|
||||
|
||||
match state.api.repo.list_invoices().await {
|
||||
match state.api.repo.list_invoices_with_items().await {
|
||||
Ok(invoices) => Ok(ok(StatusCode::OK, invoices)),
|
||||
Err(e) => Ok(err(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
@@ -714,7 +714,7 @@ async fn list_tenant_invoices(
|
||||
let auth = state.api.extract_auth_pubkey(&headers)?;
|
||||
state.api.require_admin_or_tenant(&auth, &pubkey)?;
|
||||
|
||||
match state.api.repo.list_invoices_for_tenant(&pubkey).await {
|
||||
match state.api.repo.list_invoices_for_tenant_with_items(&pubkey).await {
|
||||
Ok(invoices) => Ok(ok(StatusCode::OK, invoices)),
|
||||
Err(e) => Ok(err(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
@@ -731,7 +731,7 @@ async fn get_invoice(
|
||||
) -> std::result::Result<Response, ApiError> {
|
||||
let auth = state.api.extract_auth_pubkey(&headers)?;
|
||||
|
||||
let invoice = match state.api.repo.get_invoice(&id).await {
|
||||
let invoice = match state.api.repo.get_invoice_with_items(&id).await {
|
||||
Ok(Some(i)) => i,
|
||||
Ok(None) => return Ok(err(StatusCode::NOT_FOUND, "not-found", "invoice not found")),
|
||||
Err(e) => {
|
||||
@@ -743,7 +743,7 @@ async fn get_invoice(
|
||||
}
|
||||
};
|
||||
|
||||
state.api.require_admin_or_tenant(&auth, &invoice.tenant)?;
|
||||
state.api.require_admin_or_tenant(&auth, &invoice.invoice.tenant)?;
|
||||
|
||||
Ok(ok(StatusCode::OK, invoice))
|
||||
}
|
||||
|
||||
@@ -75,3 +75,10 @@ pub struct InvoiceItem {
|
||||
pub relay: String,
|
||||
pub sats: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct InvoiceWithItems {
|
||||
#[serde(flatten)]
|
||||
pub invoice: Invoice,
|
||||
pub items: Vec<InvoiceItem>,
|
||||
}
|
||||
|
||||
+34
-1
@@ -7,7 +7,7 @@ use sqlx::{
|
||||
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
|
||||
};
|
||||
|
||||
use crate::models::{Activity, Invoice, InvoiceItem, Plan, Relay, Tenant};
|
||||
use crate::models::{Activity, Invoice, InvoiceItem, InvoiceWithItems, Plan, Relay, Tenant};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Repo {
|
||||
@@ -529,6 +529,39 @@ impl Repo {
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
pub async fn list_invoices_with_items(&self) -> Result<Vec<InvoiceWithItems>> {
|
||||
let invoices = self.list_invoices().await?;
|
||||
let mut result = Vec::with_capacity(invoices.len());
|
||||
for invoice in invoices {
|
||||
let items = self.get_invoice_items(&invoice.id).await?;
|
||||
result.push(InvoiceWithItems { invoice, items });
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn list_invoices_for_tenant_with_items(
|
||||
&self,
|
||||
tenant_id: &str,
|
||||
) -> Result<Vec<InvoiceWithItems>> {
|
||||
let invoices = self.list_invoices_for_tenant(tenant_id).await?;
|
||||
let mut result = Vec::with_capacity(invoices.len());
|
||||
for invoice in invoices {
|
||||
let items = self.get_invoice_items(&invoice.id).await?;
|
||||
result.push(InvoiceWithItems { invoice, items });
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn get_invoice_with_items(&self, id: &str) -> Result<Option<InvoiceWithItems>> {
|
||||
match self.get_invoice(id).await? {
|
||||
Some(invoice) => {
|
||||
let items = self.get_invoice_items(&invoice.id).await?;
|
||||
Ok(Some(InvoiceWithItems { invoice, items }))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_invoice_items(&self, invoice_id: &str) -> Result<Vec<InvoiceItem>> {
|
||||
let rows = sqlx::query_as::<_, InvoiceItem>(
|
||||
"SELECT id, invoice, relay, sats
|
||||
|
||||
@@ -51,6 +51,8 @@ export default function PaymentDialog(props: PaymentDialogProps) {
|
||||
props.onClose()
|
||||
}
|
||||
|
||||
const totalSats = () => props.invoice.items.reduce((sum, item) => sum + item.sats, 0)
|
||||
|
||||
const periodLabel = () => {
|
||||
const start = new Date(props.invoice.period_start * 1000)
|
||||
const end = new Date(props.invoice.period_end * 1000)
|
||||
@@ -69,9 +71,9 @@ export default function PaymentDialog(props: PaymentDialogProps) {
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-900">Pay Invoice</h2>
|
||||
<Show when={props.invoice.amount}>
|
||||
<Show when={totalSats() > 0}>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-1">
|
||||
{props.invoice.amount.toLocaleString()} <span class="text-base font-normal text-gray-500">sats</span>
|
||||
{totalSats().toLocaleString()} <span class="text-base font-normal text-gray-500">sats</span>
|
||||
</p>
|
||||
</Show>
|
||||
<Show when={props.invoice.period_start && props.invoice.period_end}>
|
||||
|
||||
@@ -100,11 +100,18 @@ export type Tenant = {
|
||||
billing_anchor: number
|
||||
}
|
||||
|
||||
export type InvoiceItem = {
|
||||
id: string
|
||||
invoice: string
|
||||
relay: string
|
||||
sats: number
|
||||
}
|
||||
|
||||
export type Invoice = {
|
||||
id: string
|
||||
tenant: string
|
||||
status: string
|
||||
amount: number
|
||||
items: InvoiceItem[]
|
||||
created_at: number
|
||||
attempted_at: number
|
||||
error: string
|
||||
|
||||
@@ -133,7 +133,9 @@ export default function Account() {
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<span class="font-medium text-gray-900">
|
||||
{invoice.amount ? `${invoice.amount.toLocaleString()} sats` : "—"}
|
||||
{invoice.items.length > 0
|
||||
? `${invoice.items.reduce((sum, item) => sum + item.sats, 0).toLocaleString()} sats`
|
||||
: "—"}
|
||||
</span>
|
||||
<Show when={invoice.period_start && invoice.period_end}>
|
||||
<p class="text-xs text-gray-500 mt-0.5">{periodLabel()}</p>
|
||||
|
||||
Reference in New Issue
Block a user