mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 17:14:03 +00:00
77 lines
2.7 KiB
TypeScript
77 lines
2.7 KiB
TypeScript
import { BillingPeriod, Money, Temporal } from '@household/domain'
|
|
import type {
|
|
HouseholdBillingSettingsRecord,
|
|
HouseholdPaymentBalanceAdjustmentPolicy
|
|
} from '@household/ports'
|
|
|
|
import type { FinanceDashboardMemberLine } from './finance-command-service'
|
|
|
|
export interface MemberPaymentGuidance {
|
|
kind: 'rent' | 'utilities'
|
|
adjustmentPolicy: HouseholdPaymentBalanceAdjustmentPolicy
|
|
baseAmount: Money
|
|
purchaseOffset: Money
|
|
proposalAmount: Money
|
|
totalRemaining: Money
|
|
reminderDate: string
|
|
dueDate: string
|
|
paymentWindowOpen: boolean
|
|
paymentDue: boolean
|
|
}
|
|
|
|
function cycleDate(period: string, day: number): Temporal.PlainDate {
|
|
const billingPeriod = BillingPeriod.fromString(period)
|
|
const [yearRaw, monthRaw] = billingPeriod.toString().split('-')
|
|
const year = Number(yearRaw)
|
|
const month = Number(monthRaw)
|
|
const yearMonth = new Temporal.PlainYearMonth(year, month)
|
|
const boundedDay = Math.min(Math.max(day, 1), yearMonth.daysInMonth)
|
|
|
|
return new Temporal.PlainDate(year, month, boundedDay)
|
|
}
|
|
|
|
function adjustmentApplies(
|
|
policy: HouseholdPaymentBalanceAdjustmentPolicy,
|
|
kind: 'rent' | 'utilities'
|
|
): boolean {
|
|
return (policy === 'utilities' && kind === 'utilities') || (policy === 'rent' && kind === 'rent')
|
|
}
|
|
|
|
export function buildMemberPaymentGuidance(input: {
|
|
kind: 'rent' | 'utilities'
|
|
period: string
|
|
memberLine: FinanceDashboardMemberLine
|
|
settings: HouseholdBillingSettingsRecord
|
|
referenceInstant?: Temporal.Instant
|
|
}): MemberPaymentGuidance {
|
|
const policy = input.settings.paymentBalanceAdjustmentPolicy ?? 'utilities'
|
|
const baseAmount =
|
|
input.kind === 'rent' ? input.memberLine.rentShare : input.memberLine.utilityShare
|
|
const purchaseOffset = input.memberLine.purchaseOffset
|
|
const proposalAmount = adjustmentApplies(policy, input.kind)
|
|
? baseAmount.add(purchaseOffset)
|
|
: baseAmount
|
|
|
|
const reminderDay =
|
|
input.kind === 'rent' ? input.settings.rentWarningDay : input.settings.utilitiesReminderDay
|
|
const dueDay = input.kind === 'rent' ? input.settings.rentDueDay : input.settings.utilitiesDueDay
|
|
const reminderDate = cycleDate(input.period, reminderDay)
|
|
const dueDate = cycleDate(input.period, dueDay)
|
|
const localDate = (input.referenceInstant ?? Temporal.Now.instant())
|
|
.toZonedDateTimeISO(input.settings.timezone)
|
|
.toPlainDate()
|
|
|
|
return {
|
|
kind: input.kind,
|
|
adjustmentPolicy: policy,
|
|
baseAmount,
|
|
purchaseOffset,
|
|
proposalAmount,
|
|
totalRemaining: input.memberLine.remaining,
|
|
reminderDate: reminderDate.toString(),
|
|
dueDate: dueDate.toString(),
|
|
paymentWindowOpen: Temporal.PlainDate.compare(localDate, reminderDate) >= 0,
|
|
paymentDue: Temporal.PlainDate.compare(localDate, dueDate) >= 0
|
|
}
|
|
}
|