mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'fix/kosong' into 'development'
[FIX][BE]: fix laporan daily checklist kandang kosong See merge request mbugroup/lti-api!506
This commit is contained in:
@@ -1459,11 +1459,12 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
|||||||
Group("a.id, a.name, loc.id, loc.name, k.id, k.name, e.id, e.name, p.id, p.name")
|
Group("a.id, a.name, loc.id, loc.name, k.id, k.name, e.id, e.name, p.id, p.name")
|
||||||
}
|
}
|
||||||
|
|
||||||
var total int64
|
// --- Count approved rows ---
|
||||||
|
var approvedTotal int64
|
||||||
groupedForCount := buildGroupedQuery()
|
groupedForCount := buildGroupedQuery()
|
||||||
if err := s.Repository.DB().WithContext(c.Context()).
|
if err := s.Repository.DB().WithContext(c.Context()).
|
||||||
Table("(?) AS grouped", groupedForCount).
|
Table("(?) AS grouped", groupedForCount).
|
||||||
Count(&total).Error; err != nil {
|
Count(&approvedTotal).Error; err != nil {
|
||||||
s.Log.Errorf("Failed to count report data: %+v", err)
|
s.Log.Errorf("Failed to count report data: %+v", err)
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -1483,19 +1484,246 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
|||||||
TotalAssignments int64
|
TotalAssignments int64
|
||||||
}
|
}
|
||||||
|
|
||||||
rows := make([]reportRow, 0)
|
type fallbackRowType struct {
|
||||||
if err := buildGroupedQuery().
|
AreaID uint
|
||||||
Order("a.name, loc.name, k.name, e.name").
|
AreaName string
|
||||||
Offset(offset).
|
LocationID uint
|
||||||
Limit(params.Limit).
|
LocationName string
|
||||||
Scan(&rows).Error; err != nil {
|
KandangID uint
|
||||||
s.Log.Errorf("Failed to fetch report data: %+v", err)
|
KandangName string
|
||||||
|
EmployeeID uint
|
||||||
|
EmployeeName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildFallbackQ returns employees in kandangs that have NO approved checklist data
|
||||||
|
// for the filtered period. Applies the same scope/area/location/kandang/employee filters.
|
||||||
|
buildFallbackQ := func() *gorm.DB {
|
||||||
|
approvedKandangSubQ := buildBase().Select("DISTINCT dc.kandang_id")
|
||||||
|
q := s.Repository.DB().WithContext(c.Context()).
|
||||||
|
Table("employee_kandangs ek").
|
||||||
|
Joins("JOIN employees e ON e.id = ek.employee_id AND e.deleted_at IS NULL").
|
||||||
|
Joins("JOIN kandang_groups k ON k.id = ek.kandang_id AND k.deleted_at IS NULL").
|
||||||
|
Joins("JOIN locations loc ON loc.id = k.location_id AND loc.deleted_at IS NULL").
|
||||||
|
Joins("JOIN areas a ON a.id = loc.area_id AND a.deleted_at IS NULL").
|
||||||
|
Where("ek.kandang_id NOT IN (?)", approvedKandangSubQ).
|
||||||
|
Select("e.id AS employee_id, e.name AS employee_name, k.id AS kandang_id, k.name AS kandang_name, loc.id AS location_id, loc.name AS location_name, a.id AS area_id, a.name AS area_name")
|
||||||
|
q = m.ApplyScopeFilter(q, locationScope, "loc.id")
|
||||||
|
q = m.ApplyScopeFilter(q, areaScope, "a.id")
|
||||||
|
if params.AreaID != nil {
|
||||||
|
q = q.Where("a.id = ?", *params.AreaID)
|
||||||
|
}
|
||||||
|
if params.LocationID != nil {
|
||||||
|
q = q.Where("loc.id = ?", *params.LocationID)
|
||||||
|
}
|
||||||
|
if params.KandangID != nil {
|
||||||
|
q = q.Where("ek.kandang_id = ?", *params.KandangID)
|
||||||
|
}
|
||||||
|
if params.EmployeeID != nil {
|
||||||
|
q = q.Where("ek.employee_id = ?", *params.EmployeeID)
|
||||||
|
}
|
||||||
|
// PhaseID not applied: fallback rows have no phase data
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Count fallback rows ---
|
||||||
|
var fallbackTotal int64
|
||||||
|
if err := s.Repository.DB().WithContext(c.Context()).
|
||||||
|
Table("(?) AS fb", buildFallbackQ()).
|
||||||
|
Count(&fallbackTotal).Error; err != nil {
|
||||||
|
s.Log.Errorf("Failed to count fallback report data: %+v", err)
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rows) == 0 {
|
total := approvedTotal + fallbackTotal
|
||||||
|
|
||||||
|
// --- Fetch ALL approved rows (pagination done in Go after merging with fallback) ---
|
||||||
|
allApprovedRows := make([]reportRow, 0)
|
||||||
|
if approvedTotal > 0 {
|
||||||
|
if err := buildGroupedQuery().
|
||||||
|
Order("a.name, loc.name, k.name, e.name").
|
||||||
|
Scan(&allApprovedRows).Error; err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch report data: %+v", err)
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Fetch ALL fallback rows ---
|
||||||
|
allFallbackRows := make([]fallbackRowType, 0)
|
||||||
|
if fallbackTotal > 0 {
|
||||||
|
if err := buildFallbackQ().
|
||||||
|
Order("a.name, loc.name, k.name, e.name").
|
||||||
|
Scan(&allFallbackRows).Error; err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch fallback report data: %+v", err)
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Merge approved + fallback and sort consistently ---
|
||||||
|
type mergedEntry struct {
|
||||||
|
AreaName string
|
||||||
|
LocationName string
|
||||||
|
KandangName string
|
||||||
|
EmployeeName string
|
||||||
|
IsApproved bool
|
||||||
|
Idx int
|
||||||
|
}
|
||||||
|
|
||||||
|
merged := make([]mergedEntry, 0, len(allApprovedRows)+len(allFallbackRows))
|
||||||
|
for i, r := range allApprovedRows {
|
||||||
|
merged = append(merged, mergedEntry{
|
||||||
|
AreaName: r.AreaName, LocationName: r.LocationName,
|
||||||
|
KandangName: r.KandangName, EmployeeName: r.EmployeeName,
|
||||||
|
IsApproved: true, Idx: i,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for i, r := range allFallbackRows {
|
||||||
|
merged = append(merged, mergedEntry{
|
||||||
|
AreaName: r.AreaName, LocationName: r.LocationName,
|
||||||
|
KandangName: r.KandangName, EmployeeName: r.EmployeeName,
|
||||||
|
IsApproved: false, Idx: i,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(merged, func(i, j int) bool {
|
||||||
|
a, b := merged[i], merged[j]
|
||||||
|
if a.AreaName != b.AreaName {
|
||||||
|
return a.AreaName < b.AreaName
|
||||||
|
}
|
||||||
|
if a.LocationName != b.LocationName {
|
||||||
|
return a.LocationName < b.LocationName
|
||||||
|
}
|
||||||
|
if a.KandangName != b.KandangName {
|
||||||
|
return a.KandangName < b.KandangName
|
||||||
|
}
|
||||||
|
return a.EmployeeName < b.EmployeeName
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- Apply Go-level pagination ---
|
||||||
|
end := offset + params.Limit
|
||||||
|
if end > len(merged) {
|
||||||
|
end = len(merged)
|
||||||
|
}
|
||||||
|
if offset >= len(merged) {
|
||||||
return []DailyChecklistReportItem{}, total, nil
|
return []DailyChecklistReportItem{}, total, nil
|
||||||
}
|
}
|
||||||
|
pageData := merged[offset:end]
|
||||||
|
|
||||||
|
// --- Split page into approved vs fallback rows ---
|
||||||
|
pageApproved := make([]reportRow, 0)
|
||||||
|
pageFallback := make([]fallbackRowType, 0)
|
||||||
|
for _, entry := range pageData {
|
||||||
|
if entry.IsApproved {
|
||||||
|
pageApproved = append(pageApproved, allApprovedRows[entry.Idx])
|
||||||
|
} else {
|
||||||
|
pageFallback = append(pageFallback, allFallbackRows[entry.Idx])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyEmptyKandangFlags := func(items []DailyChecklistReportItem, kandangIDs []uint) error {
|
||||||
|
if len(kandangIDs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
firstDay := time.Date(params.Year, time.Month(params.Month), 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
lastDay := firstDay.AddDate(0, 1, 0).AddDate(0, 0, -1)
|
||||||
|
today := time.Now().UTC().Truncate(24 * time.Hour)
|
||||||
|
|
||||||
|
type emptyKandangRec struct {
|
||||||
|
KandangID uint
|
||||||
|
Date time.Time
|
||||||
|
}
|
||||||
|
var emptyRecs []emptyKandangRec
|
||||||
|
if err := s.Repository.DB().WithContext(c.Context()).
|
||||||
|
Model(&entity.DailyChecklist{}).
|
||||||
|
Where("kandang_id IN ? AND category = ? AND date <= ? AND deleted_at IS NULL",
|
||||||
|
kandangIDs, dailyChecklistCategoryEmptyKandang, lastDay).
|
||||||
|
Select("kandang_id, date").
|
||||||
|
Scan(&emptyRecs).Error; err != nil {
|
||||||
|
s.Log.Errorf("Failed to get empty kandang records for report: %+v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyDaysByKandang := make(map[uint]map[int]struct{})
|
||||||
|
|
||||||
|
if len(emptyRecs) > 0 {
|
||||||
|
minEmptyDate := emptyRecs[0].Date
|
||||||
|
for _, rec := range emptyRecs[1:] {
|
||||||
|
if rec.Date.Before(minEmptyDate) {
|
||||||
|
minEmptyDate = rec.Date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type checklistDateRec struct {
|
||||||
|
KandangID uint
|
||||||
|
Date time.Time
|
||||||
|
}
|
||||||
|
var nextDates []checklistDateRec
|
||||||
|
if err := s.Repository.DB().WithContext(c.Context()).
|
||||||
|
Model(&entity.DailyChecklist{}).
|
||||||
|
Where("kandang_id IN ? AND category != ? AND date > ? AND (status IS NULL OR status != ?) AND deleted_at IS NULL",
|
||||||
|
kandangIDs, dailyChecklistCategoryEmptyKandang, minEmptyDate, dailyChecklistStatusRejected).
|
||||||
|
Select("kandang_id, date").
|
||||||
|
Order("kandang_id ASC, date ASC").
|
||||||
|
Scan(&nextDates).Error; err != nil {
|
||||||
|
s.Log.Errorf("Failed to get next checklist dates for empty kandang: %+v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nextDatesByKandang := make(map[uint][]time.Time)
|
||||||
|
for _, row := range nextDates {
|
||||||
|
nextDatesByKandang[row.KandangID] = append(nextDatesByKandang[row.KandangID], row.Date)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rec := range emptyRecs {
|
||||||
|
var nextDate time.Time
|
||||||
|
for _, d := range nextDatesByKandang[rec.KandangID] {
|
||||||
|
if d.After(rec.Date) {
|
||||||
|
nextDate = d
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no next checklist, cap empty period at today (not end of month)
|
||||||
|
ceiling := lastDay
|
||||||
|
if today.Before(lastDay) {
|
||||||
|
ceiling = today
|
||||||
|
}
|
||||||
|
periodEnd := ceiling
|
||||||
|
if !nextDate.IsZero() {
|
||||||
|
periodEnd = nextDate.AddDate(0, 0, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveStart := rec.Date
|
||||||
|
if effectiveStart.Before(firstDay) {
|
||||||
|
effectiveStart = firstDay
|
||||||
|
}
|
||||||
|
effectiveEnd := periodEnd
|
||||||
|
if effectiveEnd.After(lastDay) {
|
||||||
|
effectiveEnd = lastDay
|
||||||
|
}
|
||||||
|
|
||||||
|
if effectiveStart.After(effectiveEnd) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := emptyDaysByKandang[rec.KandangID]; !ok {
|
||||||
|
emptyDaysByKandang[rec.KandangID] = make(map[int]struct{})
|
||||||
|
}
|
||||||
|
for d := effectiveStart; !d.After(effectiveEnd); d = d.AddDate(0, 0, 1) {
|
||||||
|
emptyDaysByKandang[rec.KandangID][d.Day()] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range items {
|
||||||
|
daySet := emptyDaysByKandang[item.KandangID]
|
||||||
|
for day := range daySet {
|
||||||
|
key := strconv.Itoa(day)
|
||||||
|
if _, exists := items[i].DailyActivities[key]; !exists {
|
||||||
|
items[i].DailyActivities[key] = "Kandang kosong"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type comboKey struct {
|
type comboKey struct {
|
||||||
EmployeeID uint
|
EmployeeID uint
|
||||||
@@ -1517,7 +1745,7 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
|||||||
kandangSet := make(map[uint]struct{})
|
kandangSet := make(map[uint]struct{})
|
||||||
phaseSet := make(map[uint]struct{})
|
phaseSet := make(map[uint]struct{})
|
||||||
|
|
||||||
for _, row := range rows {
|
for _, row := range pageApproved {
|
||||||
key := comboKey{EmployeeID: row.EmployeeID, KandangID: row.KandangID, PhaseID: row.PhaseID}
|
key := comboKey{EmployeeID: row.EmployeeID, KandangID: row.KandangID, PhaseID: row.PhaseID}
|
||||||
comboSet[key] = struct{}{}
|
comboSet[key] = struct{}{}
|
||||||
if _, ok := employeeSet[row.EmployeeID]; !ok {
|
if _, ok := employeeSet[row.EmployeeID]; !ok {
|
||||||
@@ -1658,8 +1886,9 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
|||||||
return selected
|
return selected
|
||||||
}
|
}
|
||||||
|
|
||||||
items := make([]DailyChecklistReportItem, len(rows))
|
// --- Build approved items (existing logic) ---
|
||||||
for i, row := range rows {
|
approvedItems := make([]DailyChecklistReportItem, len(pageApproved))
|
||||||
|
for i, row := range pageApproved {
|
||||||
key := comboKey{EmployeeID: row.EmployeeID, KandangID: row.KandangID, PhaseID: row.PhaseID}
|
key := comboKey{EmployeeID: row.EmployeeID, KandangID: row.KandangID, PhaseID: row.PhaseID}
|
||||||
|
|
||||||
activities := dailyActivityMap[key]
|
activities := dailyActivityMap[key]
|
||||||
@@ -1706,7 +1935,7 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
|||||||
kandangPercentage = int(math.Round(float64(kandangStat.Completed) / float64(kandangStat.Total) * 100))
|
kandangPercentage = int(math.Round(float64(kandangStat.Completed) / float64(kandangStat.Total) * 100))
|
||||||
}
|
}
|
||||||
|
|
||||||
items[i] = DailyChecklistReportItem{
|
approvedItems[i] = DailyChecklistReportItem{
|
||||||
AreaID: row.AreaID,
|
AreaID: row.AreaID,
|
||||||
AreaName: row.AreaName,
|
AreaName: row.AreaName,
|
||||||
LocationID: row.LocationID,
|
LocationID: row.LocationID,
|
||||||
@@ -1727,109 +1956,55 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flag empty kandang days within this report month
|
// --- Build fallback items (kandangs with no approved data) ---
|
||||||
if len(kandangIDs) > 0 {
|
fallbackItems := make([]DailyChecklistReportItem, len(pageFallback))
|
||||||
firstDay := time.Date(params.Year, time.Month(params.Month), 1, 0, 0, 0, 0, time.UTC)
|
for i, fb := range pageFallback {
|
||||||
lastDay := firstDay.AddDate(0, 1, 0).AddDate(0, 0, -1)
|
fallbackItems[i] = DailyChecklistReportItem{
|
||||||
today := time.Now().UTC().Truncate(24 * time.Hour)
|
AreaID: fb.AreaID,
|
||||||
|
AreaName: fb.AreaName,
|
||||||
type emptyKandangRec struct {
|
LocationID: fb.LocationID,
|
||||||
KandangID uint
|
LocationName: fb.LocationName,
|
||||||
Date time.Time
|
KandangID: fb.KandangID,
|
||||||
|
KandangName: fb.KandangName,
|
||||||
|
EmployeeID: fb.EmployeeID,
|
||||||
|
EmployeeName: fb.EmployeeName,
|
||||||
|
PhaseName: "",
|
||||||
|
DailyActivities: map[string]any{},
|
||||||
|
Summary: DailyChecklistReportSummary{},
|
||||||
}
|
}
|
||||||
var emptyRecs []emptyKandangRec
|
}
|
||||||
if err := s.Repository.DB().WithContext(c.Context()).
|
|
||||||
Model(&entity.DailyChecklist{}).
|
// --- Reconstruct allItems in the sorted pageData order ---
|
||||||
Where("kandang_id IN ? AND category = ? AND date <= ? AND deleted_at IS NULL",
|
allItems := make([]DailyChecklistReportItem, len(pageData))
|
||||||
kandangIDs, dailyChecklistCategoryEmptyKandang, lastDay).
|
approvedIdx := 0
|
||||||
Select("kandang_id, date").
|
fallbackIdx := 0
|
||||||
Scan(&emptyRecs).Error; err != nil {
|
for i, entry := range pageData {
|
||||||
s.Log.Errorf("Failed to get empty kandang records for report: %+v", err)
|
if entry.IsApproved {
|
||||||
|
allItems[i] = approvedItems[approvedIdx]
|
||||||
|
approvedIdx++
|
||||||
|
} else {
|
||||||
|
allItems[i] = fallbackItems[fallbackIdx]
|
||||||
|
fallbackIdx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Collect all kandangIDs on this page (approved + fallback) for empty_kandang flags ---
|
||||||
|
allKandangSet := make(map[uint]struct{})
|
||||||
|
for _, id := range kandangIDs {
|
||||||
|
allKandangSet[id] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, fb := range pageFallback {
|
||||||
|
allKandangSet[fb.KandangID] = struct{}{}
|
||||||
|
}
|
||||||
|
allKandangIDs := make([]uint, 0, len(allKandangSet))
|
||||||
|
for id := range allKandangSet {
|
||||||
|
allKandangIDs = append(allKandangIDs, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Flag empty kandang days within this report month ---
|
||||||
|
if err := applyEmptyKandangFlags(allItems, allKandangIDs); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
emptyDaysByKandang := make(map[uint]map[int]struct{})
|
return allItems, total, nil
|
||||||
|
|
||||||
if len(emptyRecs) > 0 {
|
|
||||||
minEmptyDate := emptyRecs[0].Date
|
|
||||||
for _, rec := range emptyRecs[1:] {
|
|
||||||
if rec.Date.Before(minEmptyDate) {
|
|
||||||
minEmptyDate = rec.Date
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type checklistDateRec struct {
|
|
||||||
KandangID uint
|
|
||||||
Date time.Time
|
|
||||||
}
|
|
||||||
var nextDates []checklistDateRec
|
|
||||||
if err := s.Repository.DB().WithContext(c.Context()).
|
|
||||||
Model(&entity.DailyChecklist{}).
|
|
||||||
Where("kandang_id IN ? AND category != ? AND date > ? AND (status IS NULL OR status != ?) AND deleted_at IS NULL",
|
|
||||||
kandangIDs, dailyChecklistCategoryEmptyKandang, minEmptyDate, dailyChecklistStatusRejected).
|
|
||||||
Select("kandang_id, date").
|
|
||||||
Order("kandang_id ASC, date ASC").
|
|
||||||
Scan(&nextDates).Error; err != nil {
|
|
||||||
s.Log.Errorf("Failed to get next checklist dates for empty kandang: %+v", err)
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nextDatesByKandang := make(map[uint][]time.Time)
|
|
||||||
for _, row := range nextDates {
|
|
||||||
nextDatesByKandang[row.KandangID] = append(nextDatesByKandang[row.KandangID], row.Date)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rec := range emptyRecs {
|
|
||||||
var nextDate time.Time
|
|
||||||
for _, d := range nextDatesByKandang[rec.KandangID] {
|
|
||||||
if d.After(rec.Date) {
|
|
||||||
nextDate = d
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no next checklist, cap empty period at today (not end of month)
|
|
||||||
ceiling := lastDay
|
|
||||||
if today.Before(lastDay) {
|
|
||||||
ceiling = today
|
|
||||||
}
|
|
||||||
periodEnd := ceiling
|
|
||||||
if !nextDate.IsZero() {
|
|
||||||
periodEnd = nextDate.AddDate(0, 0, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
effectiveStart := rec.Date
|
|
||||||
if effectiveStart.Before(firstDay) {
|
|
||||||
effectiveStart = firstDay
|
|
||||||
}
|
|
||||||
effectiveEnd := periodEnd
|
|
||||||
if effectiveEnd.After(lastDay) {
|
|
||||||
effectiveEnd = lastDay
|
|
||||||
}
|
|
||||||
|
|
||||||
if effectiveStart.After(effectiveEnd) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := emptyDaysByKandang[rec.KandangID]; !ok {
|
|
||||||
emptyDaysByKandang[rec.KandangID] = make(map[int]struct{})
|
|
||||||
}
|
|
||||||
for d := effectiveStart; !d.After(effectiveEnd); d = d.AddDate(0, 0, 1) {
|
|
||||||
emptyDaysByKandang[rec.KandangID][d.Day()] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, item := range items {
|
|
||||||
daySet := emptyDaysByKandang[item.KandangID]
|
|
||||||
for day := range daySet {
|
|
||||||
key := strconv.Itoa(day)
|
|
||||||
if _, exists := items[i].DailyActivities[key]; !exists {
|
|
||||||
items[i].DailyActivities[key] = "Kandang kosong"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return items, total, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user