package api import ( "errors" "time" "gardomatic.kleiax.de/internal/storage" ) type seasonTemplateDefinition struct { origin storage.TaskTemplateOrigin title string trigger storage.TaskTriggerType monthFrom, dayFrom *int monthTo, dayTo *int } func (app *application) syncSeasonTaskTemplates(gardenID int, species storage.Species) error { if app.models.SpeciesTaskTemplates == nil { return nil } existing, err := app.models.SpeciesTaskTemplates.GetAllForSpecies(gardenID, species.ID) if err != nil { return err } byOrigin := make(map[storage.TaskTemplateOrigin]storage.SpeciesTaskTemplate, len(existing)) for _, template := range existing { if template.Origin != storage.TaskTemplateOriginManual { byOrigin[template.Origin] = template } } definitions := []seasonTemplateDefinition{ {storage.TaskTemplateOriginSeasonSowing, "Aussaat", storage.TaskTriggerRelativeToSowing, species.SowMonthFrom, species.SowDayFrom, species.SowMonthTo, species.SowDayTo}, {storage.TaskTemplateOriginSeasonPlanting, "Pflanzen", storage.TaskTriggerRelativeToSpeciesPlanting, species.PlantingMonthFrom, species.PlantingDayFrom, species.PlantingMonthTo, species.PlantingDayTo}, {storage.TaskTemplateOriginSeasonHarvest, "Ernten", storage.TaskTriggerRelativeToHarvest, species.HarvestMonthFrom, species.HarvestDayFrom, species.HarvestMonthTo, species.HarvestDayTo}, } writeScope := gardenID if species.GardenID == nil { writeScope = 0 } for _, definition := range definitions { template, found := byOrigin[definition.origin] if definition.monthFrom == nil { if found && template.Active { template.Active = false if _, err = app.models.SpeciesTaskTemplates.Update(writeScope, template); err != nil { return err } } continue } if found { // Preserve all user-editable fields. Only repair legacy generated templates. if template.TriggerType != definition.trigger { template.TriggerType = definition.trigger if _, err = app.models.SpeciesTaskTemplates.Update(writeScope, template); err != nil { return err } } continue } duration := seasonDurationDays(definition.monthFrom, definition.dayFrom, definition.monthTo, definition.dayTo) template = storage.SpeciesTaskTemplate{ SpeciesID: species.ID, Origin: definition.origin, Title: definition.title, Description: definition.title + " im vorgesehenen Zeitraum", TriggerType: definition.trigger, TriggerOffsetUnit: storage.TaskDurationDay, Duration: duration, DurationUnit: storage.TaskDurationDay, Recurrence: storage.TaskRecurrenceNone, RecurrenceInterval: 1, Active: true, } if _, err = app.models.SpeciesTaskTemplates.Insert(template); err != nil && !errors.Is(err, storage.ErrConflict) { return err } } return nil } func seasonDurationDays(fromMonth, fromDay, toMonth, toDay *int) int { if fromMonth == nil || toMonth == nil { return 0 } startDay, endDay := 1, 1 if fromDay != nil { startDay = *fromDay } if toDay != nil { endDay = *toDay } start := time.Date(2024, time.Month(*fromMonth), startDay, 0, 0, 0, 0, time.UTC) end := time.Date(2024, time.Month(*toMonth), endDay, 0, 0, 0, 0, time.UTC) if end.Before(start) { end = end.AddDate(1, 0, 0) } return int(end.Sub(start).Hours() / 24) }