mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 12:04:02 +00:00
feat(bot): redesign household status output
This commit is contained in:
@@ -125,8 +125,8 @@ function createDashboard(): NonNullable<
|
|||||||
period: '2026-03',
|
period: '2026-03',
|
||||||
currency: 'GEL',
|
currency: 'GEL',
|
||||||
totalDue: Money.fromMajor('400', 'GEL'),
|
totalDue: Money.fromMajor('400', 'GEL'),
|
||||||
totalPaid: Money.fromMajor('150', 'GEL'),
|
totalPaid: Money.fromMajor('100', 'GEL'),
|
||||||
totalRemaining: Money.fromMajor('250', 'GEL'),
|
totalRemaining: Money.fromMajor('300', 'GEL'),
|
||||||
rentSourceAmount: Money.fromMajor('700', 'USD'),
|
rentSourceAmount: Money.fromMajor('700', 'USD'),
|
||||||
rentDisplayAmount: Money.fromMajor('1890', 'GEL'),
|
rentDisplayAmount: Money.fromMajor('1890', 'GEL'),
|
||||||
rentFxRateMicros: 2_700_000n,
|
rentFxRateMicros: 2_700_000n,
|
||||||
@@ -150,8 +150,8 @@ function createDashboard(): NonNullable<
|
|||||||
utilityShare: Money.fromMajor('20', 'GEL'),
|
utilityShare: Money.fromMajor('20', 'GEL'),
|
||||||
purchaseOffset: Money.fromMajor('10', 'GEL'),
|
purchaseOffset: Money.fromMajor('10', 'GEL'),
|
||||||
netDue: Money.fromMajor('190', 'GEL'),
|
netDue: Money.fromMajor('190', 'GEL'),
|
||||||
paid: Money.fromMajor('50', 'GEL'),
|
paid: Money.zero('GEL'),
|
||||||
remaining: Money.fromMajor('140', 'GEL'),
|
remaining: Money.fromMajor('190', 'GEL'),
|
||||||
explanations: []
|
explanations: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -238,7 +238,7 @@ function createFinanceService(): FinanceCommandService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('createFinanceCommandsService', () => {
|
describe('createFinanceCommandsService', () => {
|
||||||
test('replies with a compact localized household status summary', async () => {
|
test('replies with a clearer localized household status summary', async () => {
|
||||||
const repository = createRepository()
|
const repository = createRepository()
|
||||||
const financeService = createFinanceService()
|
const financeService = createFinanceService()
|
||||||
const bot = createTelegramBot('000000:test-token', undefined, repository)
|
const bot = createTelegramBot('000000:test-token', undefined, repository)
|
||||||
@@ -282,12 +282,18 @@ describe('createFinanceCommandsService', () => {
|
|||||||
|
|
||||||
const payload = calls[0]?.payload as { text?: string } | undefined
|
const payload = calls[0]?.payload as { text?: string } | undefined
|
||||||
expect(payload?.text).toContain('Статус на март 2026')
|
expect(payload?.text).toContain('Статус на март 2026')
|
||||||
|
expect(payload?.text).toContain('\n\nНачисления\n')
|
||||||
expect(payload?.text).toContain('Аренда: 700.00 USD (~1890.00 GEL)')
|
expect(payload?.text).toContain('Аренда: 700.00 USD (~1890.00 GEL)')
|
||||||
expect(payload?.text).toContain('Коммуналка: 82.00 GEL')
|
expect(payload?.text).toContain('Коммуналка: 82.00 GEL')
|
||||||
expect(payload?.text).toContain('Общие покупки: 30.00 GEL')
|
expect(payload?.text).toContain('Общие покупки: 30.00 GEL')
|
||||||
expect(payload?.text).toContain('Срок оплаты аренды: до 20 марта')
|
expect(payload?.text).toContain('Срок оплаты аренды: до 20 марта')
|
||||||
expect(payload?.text).toContain(
|
expect(payload?.text).toContain('Расчёты')
|
||||||
'- Стас: баланс 210.00 GEL, оплачено 100.00 GEL, остаток 110.00 GEL'
|
expect(payload?.text).toContain('Общий баланс: 400.00 GEL')
|
||||||
)
|
expect(payload?.text).toContain('Уже оплачено: 100.00 GEL')
|
||||||
|
expect(payload?.text).toContain('Осталось оплатить: 300.00 GEL')
|
||||||
|
expect(payload?.text).toContain('Участники')
|
||||||
|
expect(payload?.text).toContain('- Ион: остаток 190.00 GEL')
|
||||||
|
expect(payload?.text).toContain('- Стас: остаток 110.00 GEL (210.00 баланс, 100.00 оплачено)')
|
||||||
|
expect(payload?.text).not.toContain('- Ион: остаток 190.00 GEL (')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -111,27 +111,45 @@ export function createFinanceCommandsService(options: {
|
|||||||
dashboard.currency
|
dashboard.currency
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const memberLines = [...dashboard.members]
|
||||||
|
.sort((left, right) => right.remaining.compare(left.remaining))
|
||||||
|
.map((member) =>
|
||||||
|
member.paid.isZero()
|
||||||
|
? t.householdStatusMemberCompact(
|
||||||
|
member.displayName,
|
||||||
|
member.remaining.toMajorString(),
|
||||||
|
dashboard.currency
|
||||||
|
)
|
||||||
|
: t.householdStatusMemberDetailed(
|
||||||
|
member.displayName,
|
||||||
|
member.remaining.toMajorString(),
|
||||||
|
member.netDue.toMajorString(),
|
||||||
|
member.paid.toMajorString(),
|
||||||
|
dashboard.currency
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
t.householdStatusTitle(formatBillingPeriodLabel(locale, dashboard.period)),
|
t.householdStatusTitle(formatBillingPeriodLabel(locale, dashboard.period)),
|
||||||
t.householdStatusDueDate(formatCycleDueDate(locale, dashboard.period, dueDay)),
|
t.householdStatusDueDate(formatCycleDueDate(locale, dashboard.period, dueDay)),
|
||||||
|
'',
|
||||||
|
t.householdStatusChargesHeading,
|
||||||
rentLine,
|
rentLine,
|
||||||
t.householdStatusUtilities(utilityTotal.toMajorString(), dashboard.currency),
|
t.householdStatusUtilities(utilityTotal.toMajorString(), dashboard.currency),
|
||||||
t.householdStatusPurchases(purchaseTotal.toMajorString(), dashboard.currency),
|
t.householdStatusPurchases(purchaseTotal.toMajorString(), dashboard.currency),
|
||||||
...dashboard.members.map((member) =>
|
'',
|
||||||
t.householdStatusMember(
|
t.householdStatusSettlementHeading,
|
||||||
member.displayName,
|
t.householdStatusSettlementBalance(dashboard.totalDue.toMajorString(), dashboard.currency),
|
||||||
member.netDue.toMajorString(),
|
...(!dashboard.totalPaid.isZero()
|
||||||
member.paid.toMajorString(),
|
? [t.householdStatusSettlementPaid(dashboard.totalPaid.toMajorString(), dashboard.currency)]
|
||||||
member.remaining.toMajorString(),
|
: []),
|
||||||
dashboard.currency
|
t.householdStatusSettlementRemaining(
|
||||||
)
|
|
||||||
),
|
|
||||||
t.householdStatusTotals(
|
|
||||||
dashboard.totalDue.toMajorString(),
|
|
||||||
dashboard.totalPaid.toMajorString(),
|
|
||||||
dashboard.totalRemaining.toMajorString(),
|
dashboard.totalRemaining.toMajorString(),
|
||||||
dashboard.currency
|
dashboard.currency
|
||||||
)
|
),
|
||||||
|
'',
|
||||||
|
t.householdStatusMembersHeading,
|
||||||
|
...memberLines
|
||||||
].join('\n')
|
].join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -231,15 +231,21 @@ export const enBotTranslations: BotTranslationCatalog = {
|
|||||||
noStatementCycle: 'No cycle found for statement.',
|
noStatementCycle: 'No cycle found for statement.',
|
||||||
householdStatusTitle: (period) => `Household status for ${period}`,
|
householdStatusTitle: (period) => `Household status for ${period}`,
|
||||||
householdStatusDueDate: (dueDate) => `Rent due by ${dueDate}`,
|
householdStatusDueDate: (dueDate) => `Rent due by ${dueDate}`,
|
||||||
|
householdStatusChargesHeading: 'Charges',
|
||||||
householdStatusRentDirect: (amount, currency) => `Rent: ${amount} ${currency}`,
|
householdStatusRentDirect: (amount, currency) => `Rent: ${amount} ${currency}`,
|
||||||
householdStatusRentConverted: (sourceAmount, sourceCurrency, displayAmount, displayCurrency) =>
|
householdStatusRentConverted: (sourceAmount, sourceCurrency, displayAmount, displayCurrency) =>
|
||||||
`Rent: ${sourceAmount} ${sourceCurrency} (~${displayAmount} ${displayCurrency})`,
|
`Rent: ${sourceAmount} ${sourceCurrency} (~${displayAmount} ${displayCurrency})`,
|
||||||
householdStatusUtilities: (amount, currency) => `Utilities: ${amount} ${currency}`,
|
householdStatusUtilities: (amount, currency) => `Utilities: ${amount} ${currency}`,
|
||||||
householdStatusPurchases: (amount, currency) => `Shared purchases: ${amount} ${currency}`,
|
householdStatusPurchases: (amount, currency) => `Shared purchases: ${amount} ${currency}`,
|
||||||
householdStatusMember: (displayName, balance, paid, remaining, currency) =>
|
householdStatusSettlementHeading: 'Settlement',
|
||||||
`- ${displayName}: balance ${balance} ${currency}, paid ${paid} ${currency}, remaining ${remaining} ${currency}`,
|
householdStatusSettlementBalance: (amount, currency) => `Gross balance: ${amount} ${currency}`,
|
||||||
householdStatusTotals: (balance, paid, remaining, currency) =>
|
householdStatusSettlementPaid: (amount, currency) => `Paid so far: ${amount} ${currency}`,
|
||||||
`Household total: balance ${balance} ${currency}, paid ${paid} ${currency}, remaining ${remaining} ${currency}`,
|
householdStatusSettlementRemaining: (amount, currency) => `Remaining: ${amount} ${currency}`,
|
||||||
|
householdStatusMembersHeading: 'Members',
|
||||||
|
householdStatusMemberCompact: (displayName, remaining, currency) =>
|
||||||
|
`- ${displayName}: remaining ${remaining} ${currency}`,
|
||||||
|
householdStatusMemberDetailed: (displayName, remaining, balance, paid, currency) =>
|
||||||
|
`- ${displayName}: remaining ${remaining} ${currency} (${balance} balance, ${paid} paid)`,
|
||||||
statementTitle: (period) => `Statement for ${period}`,
|
statementTitle: (period) => `Statement for ${period}`,
|
||||||
statementLine: (displayName, amount, currency) => `- ${displayName}: ${amount} ${currency}`,
|
statementLine: (displayName, amount, currency) => `- ${displayName}: ${amount} ${currency}`,
|
||||||
statementTotal: (amount, currency) => `Total: ${amount} ${currency}`,
|
statementTotal: (amount, currency) => `Total: ${amount} ${currency}`,
|
||||||
|
|||||||
@@ -234,15 +234,22 @@ export const ruBotTranslations: BotTranslationCatalog = {
|
|||||||
noStatementCycle: 'Для выписки период не найден.',
|
noStatementCycle: 'Для выписки период не найден.',
|
||||||
householdStatusTitle: (period) => `Статус на ${period}`,
|
householdStatusTitle: (period) => `Статус на ${period}`,
|
||||||
householdStatusDueDate: (dueDate) => `Срок оплаты аренды: до ${dueDate}`,
|
householdStatusDueDate: (dueDate) => `Срок оплаты аренды: до ${dueDate}`,
|
||||||
|
householdStatusChargesHeading: 'Начисления',
|
||||||
householdStatusRentDirect: (amount, currency) => `Аренда: ${amount} ${currency}`,
|
householdStatusRentDirect: (amount, currency) => `Аренда: ${amount} ${currency}`,
|
||||||
householdStatusRentConverted: (sourceAmount, sourceCurrency, displayAmount, displayCurrency) =>
|
householdStatusRentConverted: (sourceAmount, sourceCurrency, displayAmount, displayCurrency) =>
|
||||||
`Аренда: ${sourceAmount} ${sourceCurrency} (~${displayAmount} ${displayCurrency})`,
|
`Аренда: ${sourceAmount} ${sourceCurrency} (~${displayAmount} ${displayCurrency})`,
|
||||||
householdStatusUtilities: (amount, currency) => `Коммуналка: ${amount} ${currency}`,
|
householdStatusUtilities: (amount, currency) => `Коммуналка: ${amount} ${currency}`,
|
||||||
householdStatusPurchases: (amount, currency) => `Общие покупки: ${amount} ${currency}`,
|
householdStatusPurchases: (amount, currency) => `Общие покупки: ${amount} ${currency}`,
|
||||||
householdStatusMember: (displayName, balance, paid, remaining, currency) =>
|
householdStatusSettlementHeading: 'Расчёты',
|
||||||
`- ${displayName}: баланс ${balance} ${currency}, оплачено ${paid} ${currency}, остаток ${remaining} ${currency}`,
|
householdStatusSettlementBalance: (amount, currency) => `Общий баланс: ${amount} ${currency}`,
|
||||||
householdStatusTotals: (balance, paid, remaining, currency) =>
|
householdStatusSettlementPaid: (amount, currency) => `Уже оплачено: ${amount} ${currency}`,
|
||||||
`Итого по дому: баланс ${balance} ${currency}, оплачено ${paid} ${currency}, остаток ${remaining} ${currency}`,
|
householdStatusSettlementRemaining: (amount, currency) =>
|
||||||
|
`Осталось оплатить: ${amount} ${currency}`,
|
||||||
|
householdStatusMembersHeading: 'Участники',
|
||||||
|
householdStatusMemberCompact: (displayName, remaining, currency) =>
|
||||||
|
`- ${displayName}: остаток ${remaining} ${currency}`,
|
||||||
|
householdStatusMemberDetailed: (displayName, remaining, balance, paid, currency) =>
|
||||||
|
`- ${displayName}: остаток ${remaining} ${currency} (${balance} баланс, ${paid} оплачено)`,
|
||||||
statementTitle: (period) => `Выписка за ${period}`,
|
statementTitle: (period) => `Выписка за ${period}`,
|
||||||
statementLine: (displayName, amount, currency) => `- ${displayName}: ${amount} ${currency}`,
|
statementLine: (displayName, amount, currency) => `- ${displayName}: ${amount} ${currency}`,
|
||||||
statementTotal: (amount, currency) => `Итого: ${amount} ${currency}`,
|
statementTotal: (amount, currency) => `Итого: ${amount} ${currency}`,
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ export interface BotTranslationCatalog {
|
|||||||
noStatementCycle: string
|
noStatementCycle: string
|
||||||
householdStatusTitle: (period: string) => string
|
householdStatusTitle: (period: string) => string
|
||||||
householdStatusDueDate: (dueDate: string) => string
|
householdStatusDueDate: (dueDate: string) => string
|
||||||
|
householdStatusChargesHeading: string
|
||||||
householdStatusRentDirect: (amount: string, currency: string) => string
|
householdStatusRentDirect: (amount: string, currency: string) => string
|
||||||
householdStatusRentConverted: (
|
householdStatusRentConverted: (
|
||||||
sourceAmount: string,
|
sourceAmount: string,
|
||||||
@@ -215,17 +216,21 @@ export interface BotTranslationCatalog {
|
|||||||
) => string
|
) => string
|
||||||
householdStatusUtilities: (amount: string, currency: string) => string
|
householdStatusUtilities: (amount: string, currency: string) => string
|
||||||
householdStatusPurchases: (amount: string, currency: string) => string
|
householdStatusPurchases: (amount: string, currency: string) => string
|
||||||
householdStatusMember: (
|
householdStatusSettlementHeading: string
|
||||||
|
householdStatusSettlementBalance: (amount: string, currency: string) => string
|
||||||
|
householdStatusSettlementPaid: (amount: string, currency: string) => string
|
||||||
|
householdStatusSettlementRemaining: (amount: string, currency: string) => string
|
||||||
|
householdStatusMembersHeading: string
|
||||||
|
householdStatusMemberCompact: (
|
||||||
displayName: string,
|
displayName: string,
|
||||||
balance: string,
|
|
||||||
paid: string,
|
|
||||||
remaining: string,
|
remaining: string,
|
||||||
currency: string
|
currency: string
|
||||||
) => string
|
) => string
|
||||||
householdStatusTotals: (
|
householdStatusMemberDetailed: (
|
||||||
|
displayName: string,
|
||||||
|
remaining: string,
|
||||||
balance: string,
|
balance: string,
|
||||||
paid: string,
|
paid: string,
|
||||||
remaining: string,
|
|
||||||
currency: string
|
currency: string
|
||||||
) => string
|
) => string
|
||||||
statementTitle: (period: string) => string
|
statementTitle: (period: string) => string
|
||||||
|
|||||||
Reference in New Issue
Block a user