async function memberCreditPreview(context,input,payload){ requireRole(context,['ops-manager']);const target=await memberTarget(context,input,text(input.objectId||input.targetId)); return(await enrollmentSql('SELECT xs_credit_plan($1,$2,$3::jsonb) result',[pointerId(target.get('company')),target.id,JSON.stringify(payload)])).result; } async function memberLedger(context,input,store=false){ const target=store?await storeCreditTarget(context,input):await memberTarget(context,input,text(input.objectId||input.targetId));const co=pointerId(target.get('company')),uid=number(target.get('legacyUserId')); const page=Math.max(1,Math.floor(number(input.page,1))),size=[20,50,100].includes(number(input.pageSize))?number(input.pageSize):20; const from=text(input.dateFrom),to=text(input.dateTo);for(const d of [from,to])if(d&&!/^\d{4}-\d{2}-\d{2}$/.test(d))fail(400,'日期格式无效');if(from&&to&&from>to)fail(400,'开始日期不能晚于结束日期'); const result=await Psql.one(`WITH grouped AS ( SELECT "groupId",max("happenedAt") at,jsonb_agg(to_jsonb(l) ORDER BY account,id) lines FROM xs_member_ledger l WHERE company=$1 AND "studentId"=$2 GROUP BY "groupId" ), filtered AS (SELECT * FROM grouped WHERE ($3='' OR EXISTS(SELECT 1 FROM jsonb_array_elements(lines) v WHERE v->>'account'=$3)) AND ($4='' OR EXISTS(SELECT 1 FROM jsonb_array_elements(lines) v WHERE v->>'kind'=$4)) AND ($5='' OR at>=NULLIF($5,'')::date::timestamp AT TIME ZONE 'Asia/Shanghai') AND ($6='' OR at<(NULLIF($6,'')::date+1)::timestamp AT TIME ZONE 'Asia/Shanghai')) SELECT (SELECT md5(COALESCE(string_agg(lines::text,'' ORDER BY "groupId"),'')) FROM filtered) version,(SELECT count(*)::int FROM filtered) total,COALESCE((SELECT jsonb_agg(to_jsonb(p)) FROM (SELECT * FROM filtered ORDER BY at DESC NULLS LAST,"groupId" DESC LIMIT $7 OFFSET $8) p),'[]') items`,[co,uid,text(input.account),text(input.kind),from,to,size,(page-1)*size]); return{...result,page,pageSize:size}; }