Files
honeyDueKMP/composeApp/src/commonMain/kotlin/com/tt/honeyDue/models/Document.kt
Trey T 4d363ca44e Fix API contract mismatches with Go backend
- Document.purchasePrice: String? → Double? (matches Go decimal.Decimal)
- TaskTemplate: add regionId/regionName (Go returns these, KMM was ignoring)
- TaskResponse.completions: add comment explaining separate fetch pattern
- Document: add comments clarifying fileUrl vs mediaUrl usage
2026-03-26 17:06:36 -05:00

160 lines
6.3 KiB
Kotlin

package com.tt.honeyDue.models
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class WarrantyStatus(
@SerialName("status_text") val statusText: String,
@SerialName("status_color") val statusColor: String,
@SerialName("is_expiring_soon") val isExpiringSoon: Boolean
)
@Serializable
data class DocumentUser(
val id: Int,
val username: String = "",
@SerialName("first_name") val firstName: String = "",
@SerialName("last_name") val lastName: String = ""
)
@Serializable
data class DocumentImage(
val id: Int? = null,
@SerialName("image_url") val imageUrl: String,
@SerialName("media_url") val mediaUrl: String? = null, // Authenticated endpoint: /api/media/document-image/{id}
val caption: String? = null,
@SerialName("uploaded_at") val uploadedAt: String? = null
)
@Serializable
data class Document(
val id: Int? = null,
val title: String,
@SerialName("document_type") val documentType: String,
val description: String? = null,
// fileUrl: raw storage path (internal). Not included in Go DocumentResponse DTO —
// will always be null from the API. Kept for backward compatibility; prefer mediaUrl.
@SerialName("file_url") val fileUrl: String? = null,
// mediaUrl: authenticated endpoint clients should use (e.g. /api/media/document/{id}).
// This is the URL the Go API actually returns for document access.
@SerialName("media_url") val mediaUrl: String? = null,
@SerialName("file_name") val fileName: String? = null,
@SerialName("file_size") val fileSize: Int? = null,
@SerialName("mime_type") val mimeType: String? = null,
// Warranty-specific fields
@SerialName("model_number") val modelNumber: String? = null,
@SerialName("serial_number") val serialNumber: String? = null,
val vendor: String? = null,
@SerialName("purchase_date") val purchaseDate: String? = null,
@SerialName("purchase_price") val purchasePrice: Double? = null,
@SerialName("expiry_date") val expiryDate: String? = null,
// Relationships
@SerialName("residence_id") val residenceId: Int? = null,
val residence: Int,
@SerialName("created_by_id") val createdById: Int? = null,
@SerialName("created_by") val createdBy: DocumentUser? = null,
@SerialName("task_id") val taskId: Int? = null,
// Images
val images: List<DocumentImage> = emptyList(),
// Status
@SerialName("is_active") val isActive: Boolean = true,
@SerialName("created_at") val createdAt: String? = null,
@SerialName("updated_at") val updatedAt: String? = null,
// Client-side convenience fields (not from backend, kept for UI compatibility)
// These fields are populated client-side or kept optional so deserialization doesn't fail
val category: String? = null,
val tags: String? = null,
val notes: String? = null,
@SerialName("item_name") val itemName: String? = null,
val provider: String? = null,
@SerialName("provider_contact") val providerContact: String? = null,
@SerialName("claim_phone") val claimPhone: String? = null,
@SerialName("claim_email") val claimEmail: String? = null,
@SerialName("claim_website") val claimWebsite: String? = null,
@SerialName("start_date") val startDate: String? = null,
@SerialName("days_until_expiration") val daysUntilExpiration: Int? = null,
@SerialName("warranty_status") val warrantyStatus: WarrantyStatus? = null
) {
// Backward-compatible alias: endDate maps to expiryDate
val endDate: String? get() = expiryDate
}
@Serializable
data class DocumentCreateRequest(
val title: String,
@SerialName("document_type") val documentType: String,
val description: String? = null,
// Note: file will be handled separately as multipart/form-data
// Warranty-specific fields
@SerialName("model_number") val modelNumber: String? = null,
@SerialName("serial_number") val serialNumber: String? = null,
val vendor: String? = null,
@SerialName("purchase_date") val purchaseDate: String? = null,
@SerialName("purchase_price") val purchasePrice: Double? = null,
@SerialName("expiry_date") val expiryDate: String? = null,
// Relationships
@SerialName("residence_id") val residenceId: Int,
@SerialName("task_id") val taskId: Int? = null,
// Images
@SerialName("image_urls") val imageUrls: List<String>? = null
)
@Serializable
data class DocumentUpdateRequest(
val title: String? = null,
@SerialName("document_type") val documentType: String? = null,
val description: String? = null,
// Warranty-specific fields
@SerialName("model_number") val modelNumber: String? = null,
@SerialName("serial_number") val serialNumber: String? = null,
val vendor: String? = null,
@SerialName("purchase_date") val purchaseDate: String? = null,
@SerialName("purchase_price") val purchasePrice: Double? = null,
@SerialName("expiry_date") val expiryDate: String? = null,
// Relationships
@SerialName("task_id") val taskId: Int? = null
)
// Removed: DocumentListResponse - no longer using paginated responses
// API now returns List<Document> directly
// Document type choices
enum class DocumentType(val value: String, val displayName: String) {
WARRANTY("warranty", "Warranty"),
MANUAL("manual", "User Manual"),
RECEIPT("receipt", "Receipt/Invoice"),
INSPECTION("inspection", "Inspection Report"),
PERMIT("permit", "Permit"),
DEED("deed", "Deed/Title"),
INSURANCE("insurance", "Insurance"),
CONTRACT("contract", "Contract"),
PHOTO("photo", "Photo"),
OTHER("other", "Other");
companion object {
fun fromValue(value: String): DocumentType {
return values().find { it.value == value } ?: OTHER
}
}
}
// Document/Warranty category choices
enum class DocumentCategory(val value: String, val displayName: String) {
APPLIANCE("appliance", "Appliance"),
HVAC("hvac", "HVAC"),
PLUMBING("plumbing", "Plumbing"),
ELECTRICAL("electrical", "Electrical"),
ROOFING("roofing", "Roofing"),
STRUCTURAL("structural", "Structural"),
LANDSCAPING("landscaping", "Landscaping"),
GENERAL("general", "General"),
OTHER("other", "Other");
companion object {
fun fromValue(value: String): DocumentCategory {
return values().find { it.value == value } ?: OTHER
}
}
}