package models import ( "database/sql" "encoding/json" "time" ) // Retribusi represents the data structure for the retribusi table // with proper null handling and optimized JSON marshaling type Retribusi struct { ID string `json:"id" db:"id"` Status string `json:"status" db:"status"` Sort sql.NullInt32 `json:"sort,omitempty" db:"sort"` UserCreated sql.NullString `json:"user_created,omitempty" db:"user_created"` DateCreated sql.NullTime `json:"date_created,omitempty" db:"date_created"` UserUpdated sql.NullString `json:"user_updated,omitempty" db:"user_updated"` DateUpdated sql.NullTime `json:"date_updated,omitempty" db:"date_updated"` Jenis sql.NullString `json:"jenis,omitempty" db:"Jenis"` Pelayanan sql.NullString `json:"pelayanan,omitempty" db:"Pelayanan"` Dinas sql.NullString `json:"dinas,omitempty" db:"Dinas"` KelompokObyek sql.NullString `json:"kelompok_obyek,omitempty" db:"Kelompok_obyek"` KodeTarif sql.NullString `json:"kode_tarif,omitempty" db:"Kode_tarif"` Tarif sql.NullString `json:"tarif,omitempty" db:"Tarif"` Satuan sql.NullString `json:"satuan,omitempty" db:"Satuan"` TarifOvertime sql.NullString `json:"tarif_overtime,omitempty" db:"Tarif_overtime"` SatuanOvertime sql.NullString `json:"satuan_overtime,omitempty" db:"Satuan_overtime"` RekeningPokok sql.NullString `json:"rekening_pokok,omitempty" db:"Rekening_pokok"` RekeningDenda sql.NullString `json:"rekening_denda,omitempty" db:"Rekening_denda"` Uraian1 sql.NullString `json:"uraian_1,omitempty" db:"Uraian_1"` Uraian2 sql.NullString `json:"uraian_2,omitempty" db:"Uraian_2"` Uraian3 sql.NullString `json:"uraian_3,omitempty" db:"Uraian_3"` } // Custom JSON marshaling untuk Retribusi agar NULL values tidak muncul di response func (r Retribusi) MarshalJSON() ([]byte, error) { type Alias Retribusi aux := &struct { Sort *int `json:"sort,omitempty"` UserCreated *string `json:"user_created,omitempty"` DateCreated *time.Time `json:"date_created,omitempty"` UserUpdated *string `json:"user_updated,omitempty"` DateUpdated *time.Time `json:"date_updated,omitempty"` Jenis *string `json:"jenis,omitempty"` Pelayanan *string `json:"pelayanan,omitempty"` Dinas *string `json:"dinas,omitempty"` KelompokObyek *string `json:"kelompok_obyek,omitempty"` KodeTarif *string `json:"kode_tarif,omitempty"` Tarif *string `json:"tarif,omitempty"` Satuan *string `json:"satuan,omitempty"` TarifOvertime *string `json:"tarif_overtime,omitempty"` SatuanOvertime *string `json:"satuan_overtime,omitempty"` RekeningPokok *string `json:"rekening_pokok,omitempty"` RekeningDenda *string `json:"rekening_denda,omitempty"` Uraian1 *string `json:"uraian_1,omitempty"` Uraian2 *string `json:"uraian_2,omitempty"` Uraian3 *string `json:"uraian_3,omitempty"` *Alias }{ Alias: (*Alias)(&r), } // Convert sql.Null* to pointers if r.Sort.Valid { sort := int(r.Sort.Int32) aux.Sort = &sort } if r.UserCreated.Valid { aux.UserCreated = &r.UserCreated.String } if r.DateCreated.Valid { aux.DateCreated = &r.DateCreated.Time } if r.UserUpdated.Valid { aux.UserUpdated = &r.UserUpdated.String } if r.DateUpdated.Valid { aux.DateUpdated = &r.DateUpdated.Time } if r.Jenis.Valid { aux.Jenis = &r.Jenis.String } if r.Pelayanan.Valid { aux.Pelayanan = &r.Pelayanan.String } if r.Dinas.Valid { aux.Dinas = &r.Dinas.String } if r.KelompokObyek.Valid { aux.KelompokObyek = &r.KelompokObyek.String } if r.KodeTarif.Valid { aux.KodeTarif = &r.KodeTarif.String } if r.Tarif.Valid { aux.Tarif = &r.Tarif.String } if r.Satuan.Valid { aux.Satuan = &r.Satuan.String } if r.TarifOvertime.Valid { aux.TarifOvertime = &r.TarifOvertime.String } if r.SatuanOvertime.Valid { aux.SatuanOvertime = &r.SatuanOvertime.String } if r.RekeningPokok.Valid { aux.RekeningPokok = &r.RekeningPokok.String } if r.RekeningDenda.Valid { aux.RekeningDenda = &r.RekeningDenda.String } if r.Uraian1.Valid { aux.Uraian1 = &r.Uraian1.String } if r.Uraian2.Valid { aux.Uraian2 = &r.Uraian2.String } if r.Uraian3.Valid { aux.Uraian3 = &r.Uraian3.String } return json.Marshal(aux) } // Helper methods untuk mendapatkan nilai yang aman func (r *Retribusi) GetJenis() string { if r.Jenis.Valid { return r.Jenis.String } return "" } func (r *Retribusi) GetDinas() string { if r.Dinas.Valid { return r.Dinas.String } return "" } func (r *Retribusi) GetTarif() string { if r.Tarif.Valid { return r.Tarif.String } return "" } // Response struct untuk GET by ID - diperbaiki struktur type RetribusiGetByIDResponse struct { Message string `json:"message"` Data *Retribusi `json:"data"` } // Request struct untuk create - dioptimalkan dengan validasi type RetribusiCreateRequest struct { Status string `json:"status" validate:"required,oneof=draft active inactive"` Jenis *string `json:"jenis,omitempty" validate:"omitempty,min=1,max=255"` Pelayanan *string `json:"pelayanan,omitempty" validate:"omitempty,min=1,max=255"` Dinas *string `json:"dinas,omitempty" validate:"omitempty,min=1,max=255"` KelompokObyek *string `json:"kelompok_obyek,omitempty" validate:"omitempty,min=1,max=255"` KodeTarif *string `json:"kode_tarif,omitempty" validate:"omitempty,min=1,max=255"` Uraian1 *string `json:"uraian_1,omitempty"` Uraian2 *string `json:"uraian_2,omitempty"` Uraian3 *string `json:"uraian_3,omitempty"` Tarif *string `json:"tarif,omitempty" validate:"omitempty,numeric"` Satuan *string `json:"satuan,omitempty" validate:"omitempty,min=1,max=255"` TarifOvertime *string `json:"tarif_overtime,omitempty" validate:"omitempty,numeric"` SatuanOvertime *string `json:"satuan_overtime,omitempty" validate:"omitempty,min=1,max=255"` RekeningPokok *string `json:"rekening_pokok,omitempty" validate:"omitempty,min=1,max=255"` RekeningDenda *string `json:"rekening_denda,omitempty" validate:"omitempty,min=1,max=255"` } // Response struct untuk create type RetribusiCreateResponse struct { Message string `json:"message"` Data *Retribusi `json:"data"` } // Update request - sama seperti create tapi dengan ID type RetribusiUpdateRequest struct { ID string `json:"-" validate:"required,uuid4"` // ID dari URL path Status string `json:"status" validate:"required,oneof=draft active inactive"` Jenis *string `json:"jenis,omitempty" validate:"omitempty,min=1,max=255"` Pelayanan *string `json:"pelayanan,omitempty" validate:"omitempty,min=1,max=255"` Dinas *string `json:"dinas,omitempty" validate:"omitempty,min=1,max=255"` KelompokObyek *string `json:"kelompok_obyek,omitempty" validate:"omitempty,min=1,max=255"` KodeTarif *string `json:"kode_tarif,omitempty" validate:"omitempty,min=1,max=255"` Uraian1 *string `json:"uraian_1,omitempty"` Uraian2 *string `json:"uraian_2,omitempty"` Uraian3 *string `json:"uraian_3,omitempty"` Tarif *string `json:"tarif,omitempty" validate:"omitempty,numeric"` Satuan *string `json:"satuan,omitempty" validate:"omitempty,min=1,max=255"` TarifOvertime *string `json:"tarif_overtime,omitempty" validate:"omitempty,numeric"` SatuanOvertime *string `json:"satuan_overtime,omitempty" validate:"omitempty,min=1,max=255"` RekeningPokok *string `json:"rekening_pokok,omitempty" validate:"omitempty,min=1,max=255"` RekeningDenda *string `json:"rekening_denda,omitempty" validate:"omitempty,min=1,max=255"` } // Response struct untuk update type RetribusiUpdateResponse struct { Message string `json:"message"` Data *Retribusi `json:"data"` } // Response struct untuk delete type RetribusiDeleteResponse struct { Message string `json:"message"` ID string `json:"id"` } // Enhanced GET response dengan pagination dan aggregation type RetribusiGetResponse struct { Message string `json:"message"` Data []Retribusi `json:"data"` Meta MetaResponse `json:"meta"` Summary *AggregateData `json:"summary,omitempty"` } // Metadata untuk pagination - dioptimalkan type MetaResponse struct { Limit int `json:"limit"` Offset int `json:"offset"` Total int `json:"total"` TotalPages int `json:"total_pages"` CurrentPage int `json:"current_page"` HasNext bool `json:"has_next"` HasPrev bool `json:"has_prev"` } // Aggregate data untuk summary type AggregateData struct { TotalActive int `json:"total_active"` TotalDraft int `json:"total_draft"` TotalInactive int `json:"total_inactive"` ByStatus map[string]int `json:"by_status"` ByDinas map[string]int `json:"by_dinas,omitempty"` ByJenis map[string]int `json:"by_jenis,omitempty"` LastUpdated *time.Time `json:"last_updated,omitempty"` CreatedToday int `json:"created_today"` UpdatedToday int `json:"updated_today"` } // Error response yang konsisten type ErrorResponse struct { Error string `json:"error"` Code int `json:"code"` Message string `json:"message"` Timestamp time.Time `json:"timestamp"` } // Filter struct untuk query parameters type RetribusiFilter struct { Status *string `json:"status,omitempty" form:"status"` Jenis *string `json:"jenis,omitempty" form:"jenis"` Dinas *string `json:"dinas,omitempty" form:"dinas"` KelompokObyek *string `json:"kelompok_obyek,omitempty" form:"kelompok_obyek"` Search *string `json:"search,omitempty" form:"search"` DateFrom *time.Time `json:"date_from,omitempty" form:"date_from"` DateTo *time.Time `json:"date_to,omitempty" form:"date_to"` } // Validation constants const ( StatusDraft = "draft" StatusActive = "active" StatusInactive = "inactive" StatusDeleted = "deleted" ) // ValidStatuses untuk validasi var ValidStatuses = []string{StatusDraft, StatusActive, StatusInactive} // IsValidStatus helper function func IsValidStatus(status string) bool { for _, validStatus := range ValidStatuses { if status == validStatus { return true } } return false }