fix(miniapp): show original ledger currencies

This commit is contained in:
2026-03-10 15:58:33 +04:00
parent d55bf42c7b
commit 4c0508f618
9 changed files with 35 additions and 12 deletions

View File

@@ -63,6 +63,7 @@ function repository(
id: 'purchase-1', id: 'purchase-1',
payerMemberId: member?.id ?? 'member-1', payerMemberId: member?.id ?? 'member-1',
amountMinor: 3000n, amountMinor: 3000n,
currency: 'GEL',
description: 'Soap', description: 'Soap',
occurredAt: instantFromIso('2026-03-12T11:00:00.000Z') occurredAt: instantFromIso('2026-03-12T11:00:00.000Z')
} }
@@ -244,10 +245,12 @@ describe('createMiniAppDashboardHandler', () => {
], ],
ledger: [ ledger: [
{ {
title: 'Soap' title: 'Soap',
currency: 'GEL'
}, },
{ {
title: 'Electricity' title: 'Electricity',
currency: 'USD'
} }
] ]
} }

View File

@@ -103,6 +103,7 @@ export function createMiniAppDashboardHandler(options: {
kind: entry.kind, kind: entry.kind,
title: entry.title, title: entry.title,
amountMajor: entry.amount.toMajorString(), amountMajor: entry.amount.toMajorString(),
currency: entry.currency,
actorDisplayName: entry.actorDisplayName, actorDisplayName: entry.actorDisplayName,
occurredAt: entry.occurredAt occurredAt: entry.occurredAt
})) }))

View File

@@ -413,6 +413,7 @@ function App() {
kind: 'purchase', kind: 'purchase',
title: 'Soap', title: 'Soap',
amountMajor: '30.00', amountMajor: '30.00',
currency: 'GEL',
actorDisplayName: 'Alice', actorDisplayName: 'Alice',
occurredAt: '2026-03-12T11:00:00.000Z' occurredAt: '2026-03-12T11:00:00.000Z'
}, },
@@ -421,6 +422,7 @@ function App() {
kind: 'utility', kind: 'utility',
title: 'Electricity', title: 'Electricity',
amountMajor: '120.00', amountMajor: '120.00',
currency: 'GEL',
actorDisplayName: 'Alice', actorDisplayName: 'Alice',
occurredAt: '2026-03-12T12:00:00.000Z' occurredAt: '2026-03-12T12:00:00.000Z'
} }
@@ -898,7 +900,7 @@ function App() {
<ShowDashboard <ShowDashboard
dashboard={dashboard()} dashboard={dashboard()}
fallback={<p>{copy().emptyDashboard}</p>} fallback={<p>{copy().emptyDashboard}</p>}
render={(data) => ( render={() => (
<> <>
<article class="balance-item"> <article class="balance-item">
<header> <header>
@@ -913,7 +915,7 @@ function App() {
<header> <header>
<strong>{entry.title}</strong> <strong>{entry.title}</strong>
<span> <span>
{entry.amountMajor} {data.currency} {entry.amountMajor} {entry.currency}
</span> </span>
</header> </header>
<p>{entry.actorDisplayName ?? copy().ledgerActorFallback}</p> <p>{entry.actorDisplayName ?? copy().ledgerActorFallback}</p>
@@ -935,7 +937,7 @@ function App() {
<header> <header>
<strong>{entry.title}</strong> <strong>{entry.title}</strong>
<span> <span>
{entry.amountMajor} {data.currency} {entry.amountMajor} {entry.currency}
</span> </span>
</header> </header>
<p>{entry.actorDisplayName ?? copy().ledgerActorFallback}</p> <p>{entry.actorDisplayName ?? copy().ledgerActorFallback}</p>
@@ -1564,7 +1566,7 @@ function App() {
<header> <header>
<strong>{entry.title}</strong> <strong>{entry.title}</strong>
<span> <span>
{entry.amountMajor} {data.currency} {entry.amountMajor} {entry.currency}
</span> </span>
</header> </header>
<p>{entry.actorDisplayName ?? copy().ledgerActorFallback}</p> <p>{entry.actorDisplayName ?? copy().ledgerActorFallback}</p>

View File

@@ -252,9 +252,9 @@ button {
.balance-item header, .balance-item header,
.ledger-item header { .ledger-item header {
display: flex; display: grid;
align-items: baseline; grid-template-columns: minmax(0, 1fr) auto;
justify-content: space-between; align-items: start;
gap: 12px; gap: 12px;
margin-bottom: 10px; margin-bottom: 10px;
} }
@@ -262,6 +262,7 @@ button {
.balance-item strong, .balance-item strong,
.ledger-item strong { .ledger-item strong {
font-size: 1rem; font-size: 1rem;
overflow-wrap: anywhere;
} }
.balance-item p, .balance-item p,
@@ -298,7 +299,7 @@ button {
.settings-grid { .settings-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: minmax(0, 1fr);
gap: 12px; gap: 12px;
margin-top: 12px; margin-top: 12px;
} }
@@ -362,10 +363,16 @@ button {
} }
.settings-grid { .settings-grid {
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
.panel--wide { .panel--wide {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
} }
@media (min-width: 980px) {
.settings-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}

View File

@@ -81,6 +81,7 @@ export interface MiniAppDashboard {
kind: 'purchase' | 'utility' kind: 'purchase' | 'utility'
title: string title: string
amountMajor: string amountMajor: string
currency: 'USD' | 'GEL'
actorDisplayName: string | null actorDisplayName: string | null
occurredAt: string | null occurredAt: string | null
}[] }[]

View File

@@ -283,6 +283,7 @@ export function createDbFinanceRepository(
id: schema.purchaseMessages.id, id: schema.purchaseMessages.id,
payerMemberId: schema.purchaseMessages.senderMemberId, payerMemberId: schema.purchaseMessages.senderMemberId,
amountMinor: schema.purchaseMessages.parsedAmountMinor, amountMinor: schema.purchaseMessages.parsedAmountMinor,
currency: schema.purchaseMessages.parsedCurrency,
description: schema.purchaseMessages.parsedItemDescription, description: schema.purchaseMessages.parsedItemDescription,
occurredAt: schema.purchaseMessages.messageSentAt occurredAt: schema.purchaseMessages.messageSentAt
}) })
@@ -292,6 +293,7 @@ export function createDbFinanceRepository(
eq(schema.purchaseMessages.householdId, householdId), eq(schema.purchaseMessages.householdId, householdId),
isNotNull(schema.purchaseMessages.senderMemberId), isNotNull(schema.purchaseMessages.senderMemberId),
isNotNull(schema.purchaseMessages.parsedAmountMinor), isNotNull(schema.purchaseMessages.parsedAmountMinor),
isNotNull(schema.purchaseMessages.parsedCurrency),
gte(schema.purchaseMessages.messageSentAt, instantToDate(start)), gte(schema.purchaseMessages.messageSentAt, instantToDate(start)),
lt(schema.purchaseMessages.messageSentAt, instantToDate(end)) lt(schema.purchaseMessages.messageSentAt, instantToDate(end))
) )
@@ -301,6 +303,7 @@ export function createDbFinanceRepository(
id: row.id, id: row.id,
payerMemberId: row.payerMemberId!, payerMemberId: row.payerMemberId!,
amountMinor: row.amountMinor!, amountMinor: row.amountMinor!,
currency: toCurrencyCode(row.currency!),
description: row.description, description: row.description,
occurredAt: instantFromDatabaseValue(row.occurredAt) occurredAt: instantFromDatabaseValue(row.occurredAt)
})) }))

View File

@@ -247,6 +247,7 @@ describe('createFinanceCommandService', () => {
id: 'purchase-1', id: 'purchase-1',
payerMemberId: 'alice', payerMemberId: 'alice',
amountMinor: 3000n, amountMinor: 3000n,
currency: 'GEL',
description: 'Soap', description: 'Soap',
occurredAt: instantFromIso('2026-03-12T11:00:00.000Z') occurredAt: instantFromIso('2026-03-12T11:00:00.000Z')
} }
@@ -259,6 +260,7 @@ describe('createFinanceCommandService', () => {
expect(dashboard).not.toBeNull() expect(dashboard).not.toBeNull()
expect(dashboard?.members.map((line) => line.netDue.amountMinor)).toEqual([39500n, 42500n]) expect(dashboard?.members.map((line) => line.netDue.amountMinor)).toEqual([39500n, 42500n])
expect(dashboard?.ledger.map((entry) => entry.title)).toEqual(['Soap', 'Electricity']) expect(dashboard?.ledger.map((entry) => entry.title)).toEqual(['Soap', 'Electricity'])
expect(dashboard?.ledger.map((entry) => entry.currency)).toEqual(['GEL', 'USD'])
expect(statement).toBe( expect(statement).toBe(
[ [
'Statement for 2026-03', 'Statement for 2026-03',

View File

@@ -72,6 +72,7 @@ export interface FinanceDashboardLedgerEntry {
kind: 'purchase' | 'utility' kind: 'purchase' | 'utility'
title: string title: string
amount: Money amount: Money
currency: CurrencyCode
actorDisplayName: string | null actorDisplayName: string | null
occurredAt: string | null occurredAt: string | null
} }
@@ -182,6 +183,7 @@ async function buildFinanceDashboard(
kind: 'utility' as const, kind: 'utility' as const,
title: bill.billName, title: bill.billName,
amount: Money.fromMinor(bill.amountMinor, bill.currency), amount: Money.fromMinor(bill.amountMinor, bill.currency),
currency: bill.currency,
actorDisplayName: bill.createdByMemberId actorDisplayName: bill.createdByMemberId
? (memberNameById.get(bill.createdByMemberId) ?? null) ? (memberNameById.get(bill.createdByMemberId) ?? null)
: null, : null,
@@ -191,7 +193,8 @@ async function buildFinanceDashboard(
id: purchase.id, id: purchase.id,
kind: 'purchase' as const, kind: 'purchase' as const,
title: purchase.description ?? 'Shared purchase', title: purchase.description ?? 'Shared purchase',
amount: Money.fromMinor(purchase.amountMinor, rentRule.currency), amount: Money.fromMinor(purchase.amountMinor, purchase.currency),
currency: purchase.currency,
actorDisplayName: memberNameById.get(purchase.payerMemberId) ?? null, actorDisplayName: memberNameById.get(purchase.payerMemberId) ?? null,
occurredAt: purchase.occurredAt?.toString() ?? null occurredAt: purchase.occurredAt?.toString() ?? null
})) }))

View File

@@ -23,6 +23,7 @@ export interface FinanceParsedPurchaseRecord {
id: string id: string
payerMemberId: string payerMemberId: string
amountMinor: bigint amountMinor: bigint
currency: CurrencyCode
description: string | null description: string | null
occurredAt: Instant | null occurredAt: Instant | null
} }