2026-04-18 19:42:04 +08:00
|
|
|
// src/api/ledgers.ts
|
|
|
|
|
import { pb } from './pocketbase'
|
|
|
|
|
import type { Ledger } from '@/types'
|
|
|
|
|
|
|
|
|
|
interface CreateLedgerData {
|
|
|
|
|
group: string
|
|
|
|
|
type: string
|
|
|
|
|
amount: number
|
|
|
|
|
category: string
|
2026-04-18 20:32:06 +08:00
|
|
|
description?: string
|
2026-04-18 19:42:04 +08:00
|
|
|
relatedMembers?: string[]
|
|
|
|
|
occurredAt: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ListLedgersOptions {
|
|
|
|
|
page?: number
|
|
|
|
|
limit?: number
|
|
|
|
|
type?: string
|
|
|
|
|
category?: string
|
|
|
|
|
month?: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface LedgerSummary {
|
|
|
|
|
totalIncome: number
|
|
|
|
|
totalExpense: number
|
|
|
|
|
balance: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function createLedger(data: CreateLedgerData): Promise<Ledger> {
|
|
|
|
|
const user = pb.authStore.model
|
|
|
|
|
const record = await pb.collection('ledgers').create({
|
|
|
|
|
...data,
|
|
|
|
|
creator: user?.id || '',
|
|
|
|
|
relatedMembers: data.relatedMembers || []
|
2026-04-18 20:05:27 +08:00
|
|
|
}, { $autoCancel: false })
|
2026-04-18 19:42:04 +08:00
|
|
|
return record as unknown as Ledger
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function listLedgers(
|
|
|
|
|
groupId: string,
|
|
|
|
|
options?: ListLedgersOptions
|
|
|
|
|
): Promise<{ items: Ledger[]; total: number }> {
|
|
|
|
|
const { page = 1, limit = 30, type, category, month } = options || {}
|
|
|
|
|
let filter = `group="${groupId}"`
|
|
|
|
|
if (type) filter += ` && type="${type}"`
|
|
|
|
|
if (category) filter += ` && category="${category}"`
|
|
|
|
|
if (month) {
|
|
|
|
|
const start = `${month}-01 00:00:00`
|
|
|
|
|
const year = parseInt(month.slice(0, 4))
|
|
|
|
|
const mon = parseInt(month.slice(5, 7))
|
|
|
|
|
const nextMonth = mon === 12 ? `${year + 1}-01` : `${year}-${String(mon + 1).padStart(2, '0')}`
|
|
|
|
|
const end = `${nextMonth}-01 00:00:00`
|
|
|
|
|
filter += ` && occurredAt>="${start}" && occurredAt<"${end}"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await pb.collection('ledgers').getList(page, limit, {
|
|
|
|
|
filter,
|
|
|
|
|
sort: '-occurredAt',
|
2026-04-18 20:05:27 +08:00
|
|
|
expand: 'creator,relatedMembers',
|
|
|
|
|
$autoCancel: false
|
2026-04-18 19:42:04 +08:00
|
|
|
})
|
|
|
|
|
return { items: result.items as unknown as Ledger[], total: result.totalItems }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function updateLedger(
|
|
|
|
|
ledgerId: string,
|
|
|
|
|
data: Partial<CreateLedgerData>
|
|
|
|
|
): Promise<Ledger> {
|
2026-04-18 20:05:27 +08:00
|
|
|
const record = await pb.collection('ledgers').update(ledgerId, data, { $autoCancel: false })
|
2026-04-18 19:42:04 +08:00
|
|
|
return record as unknown as Ledger
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function deleteLedger(ledgerId: string): Promise<void> {
|
2026-04-18 20:05:27 +08:00
|
|
|
await pb.collection('ledgers').delete(ledgerId, { $autoCancel: false })
|
2026-04-18 19:42:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getLedgerSummary(
|
|
|
|
|
groupId: string,
|
|
|
|
|
month?: string
|
|
|
|
|
): Promise<LedgerSummary> {
|
|
|
|
|
let totalIncome = 0
|
|
|
|
|
let totalExpense = 0
|
|
|
|
|
let page = 1
|
|
|
|
|
const batchSize = 500
|
|
|
|
|
let hasMore = true
|
|
|
|
|
|
2026-04-18 20:05:27 +08:00
|
|
|
let filter = `group="${groupId}"`
|
|
|
|
|
if (month) {
|
|
|
|
|
const start = `${month}-01 00:00:00`
|
|
|
|
|
const year = parseInt(month.slice(0, 4))
|
|
|
|
|
const mon = parseInt(month.slice(5, 7))
|
|
|
|
|
const nextMonth = mon === 12 ? `${year + 1}-01` : `${year}-${String(mon + 1).padStart(2, '0')}`
|
|
|
|
|
const end = `${nextMonth}-01 00:00:00`
|
|
|
|
|
filter += ` && occurredAt>="${start}" && occurredAt<"${end}"`
|
|
|
|
|
}
|
2026-04-18 19:42:04 +08:00
|
|
|
|
2026-04-18 20:05:27 +08:00
|
|
|
while (hasMore) {
|
2026-04-18 19:42:04 +08:00
|
|
|
const result = await pb.collection('ledgers').getList(page, batchSize, {
|
|
|
|
|
filter,
|
2026-04-18 20:05:27 +08:00
|
|
|
fields: 'type,amount',
|
|
|
|
|
$autoCancel: false
|
2026-04-18 19:42:04 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
for (const item of result.items as any[]) {
|
|
|
|
|
if (item.type === 'income') {
|
|
|
|
|
totalIncome += item.amount || 0
|
|
|
|
|
} else if (item.type === 'expense') {
|
|
|
|
|
totalExpense += item.amount || 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hasMore = result.items.length === batchSize && page * batchSize < result.totalItems
|
|
|
|
|
page++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
totalIncome,
|
|
|
|
|
totalExpense,
|
|
|
|
|
balance: totalIncome - totalExpense
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function subscribeLedgers(
|
|
|
|
|
groupId: string,
|
|
|
|
|
callback: (data: any) => void
|
|
|
|
|
) {
|
|
|
|
|
return pb.collection('ledgers').subscribe('*', (data) => {
|
|
|
|
|
if (data.record?.group === groupId) {
|
|
|
|
|
callback(data)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|