@@ -45,53 +45,113 @@ export const filterOutFutureReduceToLatestUpdates = (
4545 items : Array < itemType > ,
4646 currentTime : number = Date . now ( ) // injectable for testing
4747) : outputPrescriptionType => {
48+ // Track whether prescription was onboarded (had any items)
49+ const onboarded = items . length > 0
4850
49- // filter out items with future lastUpdateDateTime
50- const validTimeUpdates = items . filter ( item => {
51- const updateTime = Date . parse ( item . lastUpdateDateTime )
52- return updateTime <= currentTime
51+ // 1. filter out PostDatedLastModifiedSetAt where LastModified > currentTime
52+ const maturedItems = items . filter ( ( item ) => {
53+ if ( item . postDatedLastModifiedSetAt ) {
54+ const lastModifiedTime = new Date ( item . lastUpdateDateTime ) . getTime ( )
55+ return lastModifiedTime <= currentTime
56+ }
57+ return true
5358 } )
5459
55- // group by itemId and separate post-dated from regular updates
56- const itemGroups : Record < string , { regular : itemType | null , postDated : itemType | null } > = { }
57-
58- validTimeUpdates . forEach ( item => {
59- if ( ! itemGroups [ item . itemId ] ) {
60- itemGroups [ item . itemId ] = { regular : null , postDated : null }
60+ // 2. Filter out post-dated that have been revoked
61+ const filteredItems = maturedItems . filter ( ( item ) => {
62+ if ( ! item . postDatedLastModifiedSetAt ) {
63+ return true // Non-post-dated items are always included
6164 }
62- const group = itemGroups [ item . itemId ]
63-
64- if ( item . postDatedLastModifiedSetAt && ! group . postDated ) { // this is a post-dated update
65- group . postDated = item
66- } else if ( item . postDatedLastModifiedSetAt && group . postDated ) { // also a post-dated update
67- const existingTime = Date . parse ( group . postDated . postDatedLastModifiedSetAt )
68- const newTime = Date . parse ( item . postDatedLastModifiedSetAt )
69- if ( newTime > existingTime ) {
70- group . postDated = item
65+
66+ // This is a post-dated item. Check if it's been revoked.
67+ // A post-dated item is revoked if there's a non-post-dated update for the same itemId
68+ // that came after the PostDatedLastModifiedSetAt but before this item's lastUpdateDateTime
69+ const postDatedSetTime = new Date ( item . postDatedLastModifiedSetAt ) . getTime ( )
70+ const itemLastModified = new Date ( item . lastUpdateDateTime ) . getTime ( )
71+
72+ const isRevoked = maturedItems . some ( ( otherItem ) => {
73+ if ( otherItem . itemId !== item . itemId ) {
74+ return false // Different item
75+ }
76+ if ( otherItem . postDatedLastModifiedSetAt ) {
77+ return false // Another post-dated item doesn't revoke this one
7178 }
72- } else if ( ! group . regular ) { // this is a regular update
73- group . regular = item
74- } else if ( group . regular ) { // also a regular update
75- const existingTime = Date . parse ( group . regular . lastUpdateDateTime )
76- const newTime = Date . parse ( item . lastUpdateDateTime )
77- if ( newTime > existingTime ) {
78- group . regular = item
79+ const otherLastModified = new Date ( otherItem . lastUpdateDateTime ) . getTime ( )
80+ // Revoked if the other item came after the post-dated was set but before it matured
81+ return otherLastModified > postDatedSetTime && otherLastModified < itemLastModified
82+ } )
83+
84+ return ! isRevoked
85+ } )
86+
87+ // 3: Reduce to latest update for each itemId + status combination
88+ const latestByItemIdAndStatus = filteredItems . reduce ( ( acc , item ) => {
89+ const key = `${ item . itemId } :${ item . latestStatus } `
90+ const existingItem = acc . get ( key )
91+
92+ if ( ! existingItem ) {
93+ acc . set ( key , item )
94+ } else {
95+ const existingTime = new Date ( existingItem . lastUpdateDateTime ) . getTime ( )
96+ const itemTime = new Date ( item . lastUpdateDateTime ) . getTime ( )
97+
98+ if ( itemTime > existingTime ) {
99+ acc . set ( key , item )
79100 }
80101 }
102+
103+ return acc
104+ } , new Map < string , itemType > ( ) )
105+
106+ // Convert map to array
107+ let reducedItems = Array . from ( latestByItemIdAndStatus . values ( ) )
108+
109+ // Step 2b: For each itemId, if there are multiple non-post-dated items, keep only the latest one
110+ const itemsByItemId = reducedItems . reduce ( ( acc , item ) => {
111+ if ( ! acc . has ( item . itemId ) ) {
112+ acc . set ( item . itemId , [ ] )
113+ }
114+ acc . get ( item . itemId ) ! . push ( item )
115+ return acc
116+ } , new Map < string , Array < itemType > > ( ) )
117+
118+ const finalItems : Array < itemType > = [ ]
119+ itemsByItemId . forEach ( ( items ) => {
120+ const nonPostDatedItems = items . filter ( ( item ) => ! item . postDatedLastModifiedSetAt )
121+ const postDatedItems = items . filter ( ( item ) => item . postDatedLastModifiedSetAt )
122+
123+ // Keep only the latest non-post-dated item
124+ if ( nonPostDatedItems . length > 0 ) {
125+ const latestNonPostDated = nonPostDatedItems . reduce ( ( latest , item ) => {
126+ const latestTime = new Date ( latest . lastUpdateDateTime ) . getTime ( )
127+ const itemTime = new Date ( item . lastUpdateDateTime ) . getTime ( )
128+ return itemTime > latestTime ? item : latest
129+ } )
130+ finalItems . push ( latestNonPostDated )
131+ }
132+
133+ // Keep all post-dated items (already reduced to latest of each status)
134+ finalItems . push ( ...postDatedItems )
81135 } )
82136
83- // flatten both regular and post-dated updates into single array
84- const uniqueItems : Array < itemType > = [ ]
85- Object . values ( itemGroups ) . forEach ( group => {
86- if ( group . regular ) uniqueItems . push ( group . regular )
87- if ( group . postDated ) uniqueItems . push ( group . postDated )
137+ // Step 3: Sort by itemId first, then by lastUpdateDateTime within each itemId
138+ const sortedItems = finalItems . sort ( ( a , b ) => {
139+ // First sort by itemId
140+ if ( a . itemId !== b . itemId ) {
141+ return a . itemId . localeCompare ( b . itemId )
142+ }
143+ // Then sort by lastUpdateDateTime
144+ const timeA = new Date ( a . lastUpdateDateTime ) . getTime ( )
145+ const timeB = new Date ( b . lastUpdateDateTime ) . getTime ( )
146+ return timeA - timeB
88147 } )
89148
90149 const result : outputPrescriptionType = {
91150 prescriptionID : inputPrescription . prescriptionID ,
92- onboarded : items . length > 0 , // consider onboarded even if all updates were post-dated
93- items : uniqueItems
151+ onboarded : onboarded ,
152+ items : sortedItems
94153 }
154+
95155 return result
96156}
97157
0 commit comments