2025-12-20 00:01:08 +07:00

214 lines
7.5 KiB
Go

// internal/models/item.go - FIXED VERSION dengan SecretDetails
package models
import (
"time"
"gorm.io/gorm"
)
// Item represents a found item
type Item struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"type:varchar(100);not null" json:"name"`
CategoryID uint `gorm:"not null" json:"category_id"`
Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
PhotoURL string `gorm:"type:varchar(255)" json:"photo_url"`
Location string `gorm:"type:varchar(200);not null" json:"location"`
Description string `gorm:"type:text;not null" json:"description"`
SecretDetails string `gorm:"type:text;column:secret_details" json:"secret_details,omitempty"`
DateFound time.Time `gorm:"not null" json:"date_found"`
Status string `gorm:"type:varchar(50);default:'unclaimed'" json:"status"`
ReporterID uint `gorm:"not null" json:"reporter_id"`
Reporter User `gorm:"foreignKey:ReporterID" json:"reporter,omitempty"`
ReporterName string `gorm:"type:varchar(100);not null" json:"reporter_name"`
ReporterContact string `gorm:"type:varchar(50);not null" json:"reporter_contact"`
ExpiresAt *time.Time `json:"expires_at"`
// ✅ NEW FIELDS FOR CASE CLOSE
BeritaAcaraNo string `gorm:"type:varchar(100)" json:"berita_acara_no"`
BuktiSerahTerima string `gorm:"type:varchar(255)" json:"bukti_serah_terima"`
CaseClosedAt *time.Time `json:"case_closed_at"`
CaseClosedBy *uint `json:"case_closed_by"`
CaseClosedBy_User *User `gorm:"foreignKey:CaseClosedBy" json:"case_closed_by_user,omitempty"`
CaseClosedNotes string `gorm:"type:text" json:"case_closed_notes"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
// Relationships
Claims []Claim `gorm:"foreignKey:ItemID" json:"claims,omitempty"`
MatchResults []MatchResult `gorm:"foreignKey:ItemID" json:"match_results,omitempty"`
RevisionLogs []RevisionLog `gorm:"foreignKey:ItemID" json:"revision_logs,omitempty"`
}
// TableName specifies the table name for Item model
func (Item) TableName() string {
return "items"
}
// Status constants
const (
ItemStatusUnclaimed = "unclaimed"
ItemStatusPendingClaim = "pending_claim"
ItemStatusVerified = "verified"
ItemStatusCaseClosed = "case_closed"
ItemStatusExpired = "expired"
)
// BeforeCreate hook to set expiration date
func (i *Item) BeforeCreate(tx *gorm.DB) error {
if i.Status == "" {
i.Status = ItemStatusUnclaimed
}
if i.ExpiresAt == nil {
expiresAt := i.DateFound.AddDate(0, 0, 90)
i.ExpiresAt = &expiresAt
}
return nil
}
// IsExpired checks if item has expired
func (i *Item) IsExpired() bool {
if i.ExpiresAt == nil {
return false
}
return time.Now().After(*i.ExpiresAt)
}
// CanBeClaimed checks if item can be claimed
func (i *Item) CanBeClaimed() bool {
return i.Status == ItemStatusUnclaimed && !i.IsExpired()
}
// CanBeEdited checks if item can be edited
func (i *Item) CanBeEdited() bool {
return i.Status != ItemStatusCaseClosed && i.Status != ItemStatusExpired
}
// ItemPublicResponse represents item data for public view (without sensitive info)
type ItemPublicResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Category string `json:"category"`
PhotoURL string `json:"photo_url"`
Location string `json:"location"`
DateFound time.Time `json:"date_found"`
Status string `json:"status"`
ReporterID uint `json:"reporter_id"`
CreatedAt time.Time `json:"created_at"`
}
func (i *Item) GetDisplayStatus() string {
// Jika case closed atau expired, return as-is
if i.Status == ItemStatusCaseClosed || i.Status == ItemStatusExpired {
return i.Status
}
// ✅ LOGIKA BARU: Check claims
if len(i.Claims) > 0 {
// Check apakah ada claim yang approved/completed
for _, claim := range i.Claims {
if claim.Status == ClaimStatusApproved || claim.Status == ClaimStatusVerified {
return ItemStatusVerified // atau "completed"
}
if claim.Status == ClaimStatusPending || claim.Status == ClaimStatusWaitingOwner {
return ItemStatusPendingClaim // "Sedang Diklaim"
}
}
}
// Default: return status dari database
return i.Status
}
// ToPublicResponse converts Item to ItemPublicResponse (hides description, reporter details)
func (i *Item) ToPublicResponse() ItemPublicResponse {
categoryName := ""
if i.Category.ID != 0 {
categoryName = i.Category.Name
}
return ItemPublicResponse{
ID: i.ID,
Name: i.Name,
Category: categoryName,
PhotoURL: i.PhotoURL,
Location: i.Location,
DateFound: i.DateFound,
Status: i.GetDisplayStatus(), // ✅ GANTI JADI GetDisplayStatus()
ReporterID: i.ReporterID,
CreatedAt: i.CreatedAt,
}
}
type ItemDetailResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Category string `json:"category"`
PhotoURL string `json:"photo_url"`
Location string `json:"location"`
Description string `json:"description"`
SecretDetails string `json:"secret_details"`
DateFound time.Time `json:"date_found"`
Status string `json:"status"`
ReporterName string `json:"reporter_name"`
ReporterContact string `json:"reporter_contact"`
ExpiresAt *time.Time `json:"expires_at"`
// ✅ NEW FIELDS
BeritaAcaraNo string `json:"berita_acara_no,omitempty"`
BuktiSerahTerima string `json:"bukti_serah_terima,omitempty"`
CaseClosedAt *time.Time `json:"case_closed_at,omitempty"`
CaseClosedByName string `json:"case_closed_by_name,omitempty"`
CaseClosedNotes string `json:"case_closed_notes,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
// ✅ FIXED ToDetailResponse - MAPPING LENGKAP
func (i *Item) ToDetailResponse() ItemDetailResponse {
categoryName := ""
if i.Category.ID != 0 {
categoryName = i.Category.Name
}
reporterName := i.ReporterName
if reporterName == "" && i.Reporter.ID != 0 {
reporterName = i.Reporter.Name
}
reporterContact := i.ReporterContact
if reporterContact == "" && i.Reporter.ID != 0 {
reporterContact = i.Reporter.Phone
}
caseClosedByName := ""
if i.CaseClosedBy_User != nil && i.CaseClosedBy_User.ID != 0 {
caseClosedByName = i.CaseClosedBy_User.Name
}
return ItemDetailResponse{
ID: i.ID,
Name: i.Name,
Category: categoryName,
PhotoURL: i.PhotoURL,
Location: i.Location,
Description: i.Description,
SecretDetails: i.SecretDetails,
DateFound: i.DateFound,
Status: i.GetDisplayStatus(), // ✅ GANTI JADI GetDisplayStatus()
ReporterName: reporterName,
ReporterContact: reporterContact,
ExpiresAt: i.ExpiresAt,
BeritaAcaraNo: i.BeritaAcaraNo,
BuktiSerahTerima: i.BuktiSerahTerima,
CaseClosedAt: i.CaseClosedAt,
CaseClosedByName: caseClosedByName,
CaseClosedNotes: i.CaseClosedNotes,
CreatedAt: i.CreatedAt,
}
}