@@ -236,8 +236,6 @@ func (db *DB) CreateOverrideEvents(ctx *models.QueryContext, overrideEvents []*m
236236 return nil
237237}
238238
239-
240- // calculateClassBoundariesWithContext computes boundaries with context for restore scenarios.
241239func (db * DB ) calculateClassBoundariesWithContext (trans * gorm.DB , classID uint , isRestore bool ) (time.Time , time.Time , error ) {
242240 var events []models.ProgramClassEvent
243241 if err := trans .Preload ("Overrides" ).Where ("class_id = ?" , classID ).Find (& events ).Error ; err != nil {
@@ -262,24 +260,18 @@ func (db *DB) calculateClassBoundariesWithContext(trans *gorm.DB, classID uint,
262260 continue
263261 }
264262
265- // For boundary calculation, use different logic based on context
266263 startBoundary := rRule .OrigOptions .Dtstart
267264 endBoundary := time .Now ().UTC ().AddDate (MaxYearsForInfiniteRRule , 0 , 0 )
268265
269266 if isRestore {
270- // During restore, calculate boundaries based on the original RRULE pattern
271- // without considering the current extended UNTIL date
272267 var originalEndBoundary time.Time
273268 if ! rRule .OrigOptions .Until .IsZero () {
274- // Use the original RRULE UNTIL if it exists, but limit it to reasonable range
275269 originalEndBoundary = rRule .OrigOptions .Until
276270 } else {
277- // If no UNTIL, use a reasonable window based on start date
278- originalEndBoundary = rRule .OrigOptions .Dtstart .AddDate (0 , 3 , 0 ) // 3 months from start
271+ originalEndBoundary = rRule .OrigOptions .Dtstart .AddDate (0 , 3 , 0 )
279272 }
280273 endBoundary = originalEndBoundary .AddDate (0 , ClassEndDateBufferMonths , 0 )
281274 } else {
282- // During normal operations, use current RRULE UNTIL or class end date
283275 if ! rRule .OrigOptions .Until .IsZero () {
284276 endBoundary = rRule .OrigOptions .Until .AddDate (0 , ClassEndDateBufferMonths , 0 )
285277 } else if class .EndDt != nil {
@@ -334,13 +326,6 @@ func (db *DB) calculateClassBoundariesWithContext(trans *gorm.DB, classID uint,
334326 return computedStart , computedEnd , nil
335327}
336328
337- // calculateOriginalEndDate calculates what the end date should be based on the base RRULE pattern
338- // without considering any overrides or current extended UNTIL dates.
339- //
340- // This function dynamically determines the actual last occurrence of the class by:
341- // 1. Using the RRULE pattern to calculate occurrences within the original date range
342- // 2. Finding the last actual occurrence (not just the UNTIL parameter)
343- // 3. Returning the real last class date
344329func (db * DB ) calculateOriginalEndDate (trans * gorm.DB , classID uint ) (time.Time , error ) {
345330 var events []models.ProgramClassEvent
346331 if err := trans .Where ("class_id = ?" , classID ).Find (& events ).Error ; err != nil {
@@ -356,22 +341,16 @@ func (db *DB) calculateOriginalEndDate(trans *gorm.DB, classID uint) (time.Time,
356341 return time.Time {}, newGetRecordsDBError (err , "program_classes" )
357342 }
358343
359- // Determine the original date range (before any rescheduling extensions)
360- originalEndRange := class .StartDt .AddDate (0 , 3 , 0 ) // Default: 3 months from start
344+ originalEndRange := class .StartDt .AddDate (0 , 3 , 0 )
361345 if class .EndDt != nil {
362- // If current end_dt suggests this was extended, use a reasonable original range
363- // Check if current end date is significantly beyond what it would normally be
364346 monthsDiff := int (class .EndDt .Sub (class .StartDt ).Hours () / (24 * 30 ))
365347 if monthsDiff > 3 {
366- // Likely extended, use 3 months from start as original range
367348 originalEndRange = class .StartDt .AddDate (0 , 3 , 0 )
368349 } else {
369- // Use current end date as it seems reasonable
370350 originalEndRange = * class .EndDt
371351 }
372352 }
373353
374- // Calculate the actual last occurrence within the original date range
375354 var lastOccurrence time.Time
376355 for _ , event := range events {
377356 rRule , err := event .GetRRule ()
@@ -380,7 +359,6 @@ func (db *DB) calculateOriginalEndDate(trans *gorm.DB, classID uint) (time.Time,
380359 continue
381360 }
382361
383- // Get all occurrences within the original date range
384362 occurrences := rRule .Between (class .StartDt , originalEndRange , true )
385363 if len (occurrences ) > 0 {
386364 eventLastOccurrence := occurrences [len (occurrences )- 1 ]
@@ -394,11 +372,9 @@ func (db *DB) calculateOriginalEndDate(trans *gorm.DB, classID uint) (time.Time,
394372 return lastOccurrence , nil
395373 }
396374
397- // Fallback: if no occurrences found, use the original end range
398375 return originalEndRange .Truncate (24 * time .Hour ), nil
399376}
400377
401- // updateClassDateBoundaries updates only the class start_dt and end_dt fields.
402378func (db * DB ) updateClassDateBoundaries (trans * gorm.DB , classID uint , newStart , newEnd time.Time ) error {
403379 updates := make (map [string ]interface {})
404380
@@ -407,7 +383,6 @@ func (db *DB) updateClassDateBoundaries(trans *gorm.DB, classID uint, newStart,
407383 return newGetRecordsDBError (err , "program_classes" )
408384 }
409385
410- // Truncate times to start of day since StartDt and EndDt are date-only fields
411386 truncatedStart := newStart .Truncate (24 * time .Hour )
412387 truncatedEnd := newEnd .Truncate (24 * time .Hour )
413388
@@ -428,8 +403,6 @@ func (db *DB) updateClassDateBoundaries(trans *gorm.DB, classID uint, newStart,
428403 return nil
429404}
430405
431- // updateRRuleUntilDates updates the UNTIL parameter for all event RRULEs in a class.
432- // This should be called separately from class boundary updates to avoid circular dependencies.
433406func (db * DB ) updateRRuleUntilDates (trans * gorm.DB , classID uint , newUntil time.Time ) error {
434407 var events []models.ProgramClassEvent
435408 if err := trans .Where ("class_id = ?" , classID ).Find (& events ).Error ; err != nil {
@@ -443,7 +416,6 @@ func (db *DB) updateRRuleUntilDates(trans *gorm.DB, classID uint, newUntil time.
443416 continue
444417 }
445418
446- // Update UNTIL parameter - add it if it doesn't exist, or update if it does
447419 newOptions := rRule .OrigOptions
448420 newOptions .Until = newUntil
449421
@@ -460,24 +432,17 @@ func (db *DB) updateRRuleUntilDates(trans *gorm.DB, classID uint, newUntil time.
460432 return nil
461433}
462434
463- // syncClassDateBoundaries synchronizes class date boundaries with actual event occurrences.
464- // This refactored version separates calculation from updates to avoid circular dependencies.
465435func (db * DB ) syncClassDateBoundaries (trans * gorm.DB , classID uint ) error {
466436 return db .syncClassDateBoundariesWithContext (trans , classID , false )
467437}
468438
469- // syncClassDateBoundariesWithContext synchronizes class date boundaries with context.
470- // isRestore indicates whether this is being called during an override restore operation.
471439func (db * DB ) syncClassDateBoundariesWithContext (trans * gorm.DB , classID uint , isRestore bool ) error {
472- // Step 1: Get current class state for comparison
473440 var class models.ProgramClass
474441 if err := trans .First (& class , "id = ?" , classID ).Error ; err != nil {
475442 return newGetRecordsDBError (err , "program_classes" )
476443 }
477444
478- // Step 2: For restore scenarios, calculate the correct end date based on base pattern
479445 if isRestore {
480- // Calculate what the end date should be based on the original RRULE pattern
481446 originalEndDate , err := db .calculateOriginalEndDate (trans , classID )
482447 if err != nil {
483448 return err
@@ -489,23 +454,19 @@ func (db *DB) syncClassDateBoundariesWithContext(trans *gorm.DB, classID uint, i
489454 }
490455 }
491456
492- // Step 3: Calculate the correct boundaries
493457 computedStart , computedEnd , err := db .calculateClassBoundariesWithContext (trans , classID , isRestore )
494458 if err != nil {
495459 return err
496460 }
497461
498- // If no dates found, nothing to sync
499462 if computedStart .IsZero () || computedEnd .IsZero () {
500463 return nil
501464 }
502465
503- // Step 4: Update class dates only
504466 if err := db .updateClassDateBoundaries (trans , classID , computedStart , computedEnd ); err != nil {
505467 return err
506468 }
507469
508- // Step 5: Update RRULE UNTIL dates separately (only if not restore, since we already did it)
509470 if ! isRestore {
510471 if err := db .updateRRuleUntilDates (trans , classID , computedEnd ); err != nil {
511472 return err
0 commit comments