@@ -11,13 +11,19 @@ export interface AlignmentConfirmation {
1111 appliedAt : Date ;
1212}
1313
14+ export interface GroupAlignmentConfirmation {
15+ previews : AlignmentPlanPreview [ ] ;
16+ appliedAt : Date ;
17+ }
18+
1419/**
1520 * Server-side counterpart to the mobile billing-alignment store: tracks the
16- * 90-day re-alignment lockout per merchant/subscriber and produces alignment
17- * previews/confirmations from the same pure domain logic.
21+ * 90-day re-alignment lockout per merchant/subscriber/group and produces
22+ * alignment previews/confirmations from the same pure domain logic.
1823 */
1924export class AlignmentService {
2025 private lastAlignedAt = new Map < string , Date > ( ) ;
26+ private lastGroupAlignedAt = new Map < string , Date > ( ) ;
2127
2228 previewAlignment (
2329 userId : string ,
@@ -48,6 +54,36 @@ export class AlignmentService {
4854 this . lastAlignedAt . set ( userId , now ) ;
4955 return { preview, appliedAt : now } ;
5056 }
57+
58+ previewGroupAlignment (
59+ groupId : string ,
60+ memberSubscriptions : Subscription [ ] [ ] ,
61+ targetDay : AlignmentTargetDay
62+ ) : AlignmentPlanPreview [ ] {
63+ return memberSubscriptions . map ( subs => buildAlignmentPlanPreview ( subs , targetDay ) ) ;
64+ }
65+
66+ canRealignGroup ( groupId : string , now : Date = new Date ( ) ) : boolean {
67+ return canRealign ( this . lastGroupAlignedAt . get ( groupId ) ?? null , now ) ;
68+ }
69+
70+ daysUntilNextGroupRealignment ( groupId : string , now : Date = new Date ( ) ) : number {
71+ return daysUntilNextRealignment ( this . lastGroupAlignedAt . get ( groupId ) ?? null , now ) ;
72+ }
73+
74+ confirmGroupAlignment (
75+ groupId : string ,
76+ memberSubscriptions : Subscription [ ] [ ] ,
77+ targetDay : AlignmentTargetDay ,
78+ now : Date = new Date ( )
79+ ) : GroupAlignmentConfirmation {
80+ if ( ! this . canRealignGroup ( groupId , now ) ) {
81+ throw new Error ( `Re-alignment for group ${ groupId } is locked until the 90-day cooldown elapses` ) ;
82+ }
83+ const previews = memberSubscriptions . map ( subs => buildAlignmentPlanPreview ( subs , targetDay ) ) ;
84+ this . lastGroupAlignedAt . set ( groupId , now ) ;
85+ return { previews, appliedAt : now } ;
86+ }
5187}
5288
5389export const alignmentService = new AlignmentService ( ) ;
0 commit comments