Client models represent tenants in the greenhouse management system. Each client can have multiple greenhouses, users, and associated resources.
Client
Main data class representing a client/tenant in the system. Matches the TenantResponse structure from the API.
@Serializable
data class Client(
val id: Long,
val code: String,
val name: String,
val email: String,
val phone: String = "",
val province: String = "",
val country: String = "",
val location: Location? = null,
val isActive: Boolean? = true,
val status: ClientStatus = ClientStatus.ACTIVE
)
Fields
Unique identifier for the client
Unique code assigned to the client
Client’s contact email address
Province where the client is located
Country where the client is located
Geographic coordinates of the client’s location
Whether the client account is active
status
ClientStatus
default:"ClientStatus.ACTIVE"
Current status of the client account
Computed Properties
The Client class provides computed properties for display purposes:
Returns the first letter of each word in the client’s name (up to 2 letters) in uppercase.val initials: String
get() = name.split(" ")
.take(2)
.mapNotNull { it.firstOrNull()?.uppercaseChar() }
.joinToString("")
Example: “Acme Corporation” → “AC”
Returns a formatted location string combining province and country, or ”-” if both are blank.val fullLocation: String
get() = buildString {
if (province.isNotBlank()) append(province)
if (province.isNotBlank() && country.isNotBlank()) append(", ")
if (country.isNotBlank()) append(country)
}.ifBlank { "-" }
Example: “Almería, España”
Location
Represents geographic coordinates. Matches the JSONB location field in the database.
@Serializable
data class Location(
val lat: Double? = null,
val lon: Double? = null
)
Fields
Computed Properties
Returns true if both latitude and longitude are set.val isValid: Boolean
get() = lat != null && lon != null
Returns a formatted string of coordinates or empty string if not valid.val displayString: String
get() = if (isValid) "$lat, $lon" else ""
Example: “36.8381, -2.4597”
ClientStatus
Enum representing the status of a client account.
@Serializable
enum class ClientStatus {
ACTIVE,
PENDING,
INACTIVE
}
Values
- ACTIVE: Client account is active and operational
- PENDING: Client account is pending activation or approval
- INACTIVE: Client account is inactive or disabled
Extension Functions
Converts ClientStatus to nullable Boolean for API compatibility.fun ClientStatus.toIsActive(): Boolean? = when (this) {
ClientStatus.ACTIVE -> true
ClientStatus.INACTIVE -> false
ClientStatus.PENDING -> null
}
Maps: ACTIVE → true, INACTIVE → false, PENDING → null
Converts ClientStatus to API status string (Spanish).fun ClientStatus.toApiStatus(): String = when (this) {
ClientStatus.ACTIVE -> "Activo"
ClientStatus.INACTIVE -> "Inactivo"
ClientStatus.PENDING -> "Pendiente"
}
ClientStatusFilter
Enum for filtering clients by status.
enum class ClientStatusFilter {
ALL,
ACTIVE,
PENDING,
INACTIVE
}
Values
- ALL: Show all clients regardless of status
- ACTIVE: Show only active clients
- PENDING: Show only pending clients
- INACTIVE: Show only inactive clients
Methods
matches
(ClientStatus) -> Boolean
Determines if a given ClientStatus matches this filter.fun matches(status: ClientStatus): Boolean = when (this) {
ALL -> true
ACTIVE -> status == ClientStatus.ACTIVE
PENDING -> status == ClientStatus.PENDING
INACTIVE -> status == ClientStatus.INACTIVE
}
Data class for managing pagination state in client lists.
data class PaginationInfo(
val currentPage: Int = 0,
val pageSize: Int = 10,
val totalItems: Int = 0
)
Fields
Current page number (zero-indexed)
Total number of items across all pages
Computed Properties
Total number of pages based on items and page size.val totalPages: Int
get() = if (totalItems == 0) 0 else (totalItems + pageSize - 1) / pageSize
Starting index for the current page.val startIndex: Int
get() = currentPage * pageSize
Ending index for the current page (inclusive).val endIndex: Int
get() = minOf(startIndex + pageSize, totalItems)
Whether there is a next page available.val hasNextPage: Boolean
get() = currentPage < totalPages - 1
Whether there is a previous page available.val hasPreviousPage: Boolean
get() = currentPage > 0
Human-readable range display (e.g., “1-10” or “11-20”).val displayRange: String
get() = if (totalItems == 0) "0-0" else "${startIndex + 1}-$endIndex"
API DTOs
TenantResponse
Response DTO from the API representing a tenant. Maps to Client domain model.
@Serializable
data class TenantResponse(
val id: Long,
val code: String,
val name: String,
val email: String,
val phone: String? = null,
val province: String? = null,
val country: String? = null,
val location: Location? = null,
val isActive: Boolean? = true,
val status: String = "Activo"
)
Converts TenantResponse to Client domain model.fun TenantResponse.toClient(): Client = Client(
id = id,
code = code,
name = name,
email = email,
phone = phone ?: "",
province = province ?: "",
country = country ?: "",
location = location,
isActive = isActive,
status = when (status) {
"Activo" -> ClientStatus.ACTIVE
"Inactivo" -> ClientStatus.INACTIVE
"Pendiente" -> ClientStatus.PENDING
else -> ClientStatus.ACTIVE
}
)
CreateTenantRequest
Request DTO for creating a new tenant.
@Serializable
data class CreateTenantRequest(
val name: String,
val email: String,
val phone: String? = null,
val province: String? = null,
val country: String? = "España",
val location: Location? = null,
val status: String? = "Activo"
)
UpdateTenantRequest
Request DTO for updating an existing tenant. All fields are optional for partial updates.
@Serializable
data class UpdateTenantRequest(
val name: String? = null,
val email: String? = null,
val phone: String? = null,
val province: String? = null,
val country: String? = null,
val location: Location? = null,
val status: String? = null
)
Usage Examples
Creating a Client
val client = Client(
id = 1L,
code = "CLT001",
name = "Acme Corporation",
email = "[email protected]",
phone = "+34 950 123 456",
province = "Almería",
country = "España",
location = Location(lat = 36.8381, lon = -2.4597),
isActive = true,
status = ClientStatus.ACTIVE
)
println(client.initials) // "AC"
println(client.fullLocation) // "Almería, España"
Filtering Clients
val filter = ClientStatusFilter.ACTIVE
val clients = listOf(client1, client2, client3)
val activeClients = clients.filter { filter.matches(it.status) }
val pagination = PaginationInfo(
currentPage = 0,
pageSize = 10,
totalItems = 45
)
println(pagination.displayRange) // "1-10"
println(pagination.totalPages) // 5
println(pagination.hasNextPage) // true
Converting API Response
val response: TenantResponse = apiClient.getTenant(1L)
val client: Client = response.toClient()
val createRequest: CreateTenantRequest = client.toCreateRequest()
val updateRequest: UpdateTenantRequest = client.toUpdateRequest()