payday tests

This commit is contained in:
JCEEE
2026-09-20 16:40:09 +01:00
parent 3a6ba556e6
commit 6d05721629
6 changed files with 82 additions and 24 deletions
+20 -9
View File
@@ -1,4 +1,4 @@
import { todayInTz, resolveTz } from '@shared/timezone';
import { todayInTz, resolveTz, wallClockToUtc } from '@shared/timezone';
import { famMeta } from './fam';
import { pbAdmin } from '$lib/server/pocketbase';
@@ -26,24 +26,35 @@ async function completeOnceConfig(famId: string, reward: any) {
} catch {}
}
export function assertPaydayUnlocked(reward: any, tz?: string) {
// Payday-gated rewards (claimable 'payday', e.g. pocket money) unlock on the
// settle DATE at the fam's configured payday TIME — not midnight. Anything
// else (claimable 'immediate'/unset, e.g. manual triggers) is always claimable.
export function assertPaydayUnlocked(reward: any, tz?: string, paydayTime?: string) {
if (!reward || reward.claimable !== 'payday' || !reward.settleDate) return;
const today = todayInTz(resolveServerTz(tz));
if (today < reward.settleDate) {
throw new Error(`This bonus pays out on payday (${reward.settleDate}) — hang tight!`);
const resolvedTz = resolveServerTz(tz);
const settle = (reward.settleDate as string).slice(0, 10);
const today = todayInTz(resolvedTz);
if (today < settle) {
throw new Error(`This bonus pays out on payday (${settle}) — hang tight!`);
}
if (today === settle && paydayTime && /^\d{2}:\d{2}$/.test(paydayTime)) {
const unlock = wallClockToUtc(settle, paydayTime, resolvedTz);
if (Date.now() < unlock) {
throw new Error(`This bonus unlocks at ${paydayTime} on payday — hang tight!`);
}
}
}
// Member claim → status 'requested' (pending parent approval).
export async function claim(pb: any, famId: string, id: string) {
const now = new Date().toISOString();
const { tz } = await famMeta(pb, famId);
const { tz, paydayTime } = await famMeta(pb, famId);
const found = await pb
.collection('rewards')
.getFullList({ filter: `famId = '${famId}' && id = '${id}'` });
const reward = found?.[0];
if (!reward) throw new Error('Reward not found');
assertPaydayUnlocked(reward, tz);
assertPaydayUnlocked(reward, tz, paydayTime);
return pb.collection('rewards').update(id, { status: 'requested', requestedAt: now });
}
@@ -59,14 +70,14 @@ export async function approve(pb: any, famId: string, id: string) {
export async function requestAll(pb: any, famId: string, memberId: string) {
const now = new Date().toISOString();
const { tz } = await famMeta(pb, famId);
const { tz, paydayTime } = await famMeta(pb, famId);
const rewards = await pb.collection('rewards').getFullList({
filter: `famId = '${famId}' && memberId = '${memberId}' && status = 'unclaimed'`
});
let count = 0;
for (const r of rewards) {
try {
assertPaydayUnlocked(r, tz);
assertPaydayUnlocked(r, tz, paydayTime);
} catch {
continue;
}