Asset Management

The Asset Management API provides offline-first access to equipment and device data with comprehensive filtering, sorting, and pagination capabilities.

Table of contents

  1. Fetch Assets (Paginated)
    1. Parameters
    2. ZyncAssetSortAndFilter Options
    3. Return Value: GetAssetsResult
  2. Get Asset Detail
    1. Parameters
    2. Return Value: GetAssetResult
  3. Asset Data Models
    1. ZyncAsset (List Item)
    2. ZyncAssetDetail (Full Detail)
    3. ZyncAssetStatus Enum
  4. Best Practices
    1. Pagination
    2. Filtering & Sorting
    3. Offline-First Behavior
    4. Error Handling
  5. Common Use Cases
    1. Find Assets with Expiring Warranties
    2. Filter by Status and Customer
    3. Get Asset with Parts and Inspection Form
    4. Search by Serial Number

Fetch Assets (Paginated)

Retrieves a paginated list of assets with advanced filtering and sorting options.

import zync.api.asset.models.GetAssetsResult
import zync.api.asset.models.ZyncAssetSortAndFilter
import zync.api.asset.models.ZyncAssetSortBy
import zync.api.common.filter.ZyncSortType

val sortAndFilter = ZyncAssetSortAndFilter(
    sortType = ZyncSortType.Descending,
    sortBy = ZyncAssetSortBy.AssetName,
    keyword = "equipment",
    isActive = true
)

when (val result = zync.asset.fetchAssets(
    sortAndFilter = sortAndFilter,
    page = 1,
    pageSize = 20
)) {
    is GetAssetsResult.Success -> {
        val assets = result.data
        val currentPage = result.currentPage
        val totalPages = result.totalPages
        val totalRecords = result.totalRecords
        val isPartialData = result.isPartialData

        println("Fetched ${assets.size} assets (page $currentPage of $totalPages)")
        if (isPartialData) {
            println("Data from cache - may be incomplete")
        }
    }
    is GetAssetsResult.Failure -> {
        println("Error: ${result.error.message}")
    }
}
import ZuperSync

let sortAndFilter = ZyncAssetSortAndFilter(
    sortType: .descending,
    sortBy: .assetName,
    keyword: "equipment",
    isActive: true
)

switch onEnum(of: await zync.asset.fetchAssets(
    sortAndFilter: sortAndFilter,
    page: 1,
    pageSize: 20
)) {
case .success(let success):
    let assets = success.data
    let currentPage = success.currentPage
    let totalPages = success.totalPages
    let isPartialData = success.isPartialData

    print("Fetched \(assets.count) assets (page \(currentPage) of \(totalPages))")
    if isPartialData {
        print("Data from cache - may be incomplete")
    }

case .failure(let failure):
    print("Error: \(failure.error.message)")
}

Parameters

ParameterTypeDescription
sortAndFilterZyncAssetSortAndFilterComprehensive filter and sort options
pageIntPage number (1-based)
pageSizeIntNumber of assets per page

ZyncAssetSortAndFilter Options

PropertyTypeDescription
sortTypeZyncSortTypeSort direction (Ascending/Descending/DEFAULT)
sortByZyncAssetSortByField to sort by (AssetCode, AssetName, WarrantyExpiryDate, CreatedDate)
keywordString?Search keyword for asset name or code
categoryString?Filter by asset category UID
assetStatusString?Filter by asset status
assetCodeString?Filter by asset code
createdDateString?Filter by creation date
customFieldZyncFilterByCustomField?Filter by custom field values
customerZyncFilterModule?Filter by customer UID
organizationZyncFilterModule?Filter by organization UID
propertyZyncFilterModule?Filter by property UID
serialNumbersList<String>?Filter by serial numbers
warrantyExpiryDateRangeZyncFilterDateRange?Filter by warranty expiry date range
filterOrphanedAssetsBoolean?Filter orphaned assets (not linked to customer/property)
populateInspectionFormBoolean?Include inspection form details
uidList<String>?Filter by specific asset UIDs
isActiveBoolean?Filter by active status

Return Value: GetAssetsResult

Success Case:

  • data: List of ZyncAsset objects
  • currentPage: Current page number
  • totalPages: Total number of pages available
  • totalRecords: Total number of assets across all pages
  • isPartialData: true if data is from cache (may be incomplete), false if from API

Failure Case:

  • error: ZyncError with error details

Get Asset Detail

Retrieves comprehensive details for a specific asset, including purchase information, warranty details, parts, inspection forms, custom fields, and attachments.

import zync.api.asset.models.GetAssetResult

when (val result = zync.asset.getAssetDetail(
    assetUid = "550e8400-e29b-41d4-a716-446655440000"
)) {
    is GetAssetResult.Success -> {
        val asset = result.data
        val lastSynced = result.lastSyncedAt
        val syncStatus = result.syncStatus

        println("Asset: ${asset.assetName}")
        println("Code: ${asset.assetCode}")
        println("Status: ${asset.assetStatus}")
        println("Serial Number: ${asset.serialNumber}")
        println("Category: ${asset.assetCategory?.categoryName}")
        println("Customer: ${asset.customer?.fullName}")
        println("Property: ${asset.property?.propertyName}")
        println("Warranty Expiry: ${asset.warrantyExpiryDate}")
        println("Parts: ${asset.parts.size}")
        println("Custom Fields: ${asset.customFields.size}")
        println("Attachments: ${asset.attachments.size}")

        if (syncStatus == ZyncDataSyncStatus.AGED) {
            println("Note: Data is older than 5 minutes. Last synced: $lastSynced")
        }
    }
    is GetAssetResult.Failure -> {
        println("Error: ${result.error.message}")
    }
}
import ZuperSync

switch onEnum(of: await zync.asset.getAssetDetail(
    assetUid: "550e8400-e29b-41d4-a716-446655440000"
)) {
case .success(let success):
    let asset = success.data
    let lastSynced = success.lastSyncedAt
    let syncStatus = success.syncStatus

    print("Asset: \(asset.assetName)")
    print("Code: \(asset.assetCode)")
    print("Status: \(asset.assetStatus.rawValue)")
    print("Serial Number: \(asset.serialNumber ?? "N/A")")
    print("Category: \(asset.assetCategory?.categoryName ?? "N/A")")
    print("Customer: \(asset.customer?.fullName ?? "N/A")")
    print("Property: \(asset.property?.propertyName ?? "N/A")")
    print("Warranty Expiry: \(asset.warrantyExpiryDate ?? "N/A")")
    print("Parts: \(asset.parts.count)")
    print("Custom Fields: \(asset.customFields.count)")
    print("Attachments: \(asset.attachments.count)")

    if syncStatus == .aged {
        print("Note: Data is older than 5 minutes. Last synced: \(lastSynced)")
    }

case .failure(let failure):
    print("Error: \(failure.error.message)")
}

Parameters

ParameterTypeDescription
assetUidStringUnique identifier of the asset

Return Value: GetAssetResult

Success Case:

  • data: ZyncAssetDetail object with comprehensive asset information
  • syncStatus: ZyncDataSyncStatus - Indicates data freshness (NONE for fresh data, AGED for data older than 5 minutes, OUTDATED_RECORD for stale data)
  • lastSyncedAt: String - ISO-8601 formatted timestamp of when this record was last synced from the server

Failure Case:

  • error: ZyncError with error details

Asset Data Models

ZyncAsset (List Item)

Basic asset information returned in paginated lists:

PropertyTypeDescription
assetUidStringUnique identifier
assetCodeStringAsset code
assetNameStringAsset name
assetImageString?Asset image URL
assetStatusZyncAssetStatusAsset status enum
isActiveBooleanActive status
assetCategoryZyncAssetCategory?Asset category
assetLocationZyncAddress?Asset location address
serialNumberString?Serial number
warrantyExpiryDateString?Warranty expiry date
customerZyncAssetCustomer?Associated customer info
propertyZyncAssetProperty?Associated property info
organizationZyncAssetOrganization?Associated organization info
inspectionFormZyncAssetInspectionForm?Inspection form details

ZyncAssetDetail (Full Detail)

Comprehensive asset information including all related data:

PropertyTypeDescription
assetUidStringUnique identifier
assetCodeStringAsset code
assetNameStringAsset name
assetImageString?Asset image URL
assetStatusZyncAssetStatusAsset status enum
assetDescriptionString?Asset description
isActiveBooleanActive status
assetCategoryZyncAssetCategory?Asset category
assetLocationZyncAddress?Asset location address
billingAddressZyncAddress?Billing address
ownedByCustomerBooleanOwnership flag
serialNumberString?Serial number
purchaseDateString?Purchase date
purchasePriceDouble?Purchase price
residualPriceDouble?Residual value
warrantyExpiryDateString?Warranty expiry date
placedInServiceString?Service placement date
usefulLifeZyncAssetUsefulLife?Useful life configuration
customerUidString?Customer UID
customerZyncCustomer?Full customer details
propertyUidString?Property UID
propertyZyncProperty?Full property details
organizationUidString?Organization UID
organizationZyncOrganization?Full organization details
parentAssetUidString?Parent asset UID
parentAssetZyncParentAsset?Parent asset details
createdByUidString?Creator UID
createdByUser?User who created the asset
partsList<ZyncAssetPart>Asset parts/components
inspectionFormZyncAssetInspectionForm?Inspection form details
productZyncAssetProduct?Associated product
customFieldsList<ZyncFormField>Custom field values
attachmentsList<ZyncAttachment>Asset attachments
createdAtStringCreation timestamp
updatedAtString?Last update timestamp
syncedAtString?Last sync timestamp

ZyncAssetStatus Enum

Asset status values:

ValueDescription
READY_TO_INSTALLReady for installation
INSTALLEDInstalled
ONLINEOnline/operational
OFFLINEOffline
NEEDS_REPAIRNeeds repair
UNDER_SERVICEUnder service
REMOVEDRemoved
OBSOLETEObsolete

Best Practices

Pagination

Start with page 1 and use consistent page sizes across requests. Monitor the isPartialData flag to determine if data is from cache.

suspend fun loadAllAssets() {
    val filter = ZyncAssetSortAndFilter(isActive = true)
    var currentPage = 1
    val pageSize = 50

    do {
        when (val result = zync.asset.fetchAssets(filter, currentPage, pageSize)) {
            is GetAssetsResult.Success -> {
                processAssets(result.data)
                currentPage++

                if (result.isPartialData) {
                    println("Warning: Showing cached data")
                }

                if (currentPage > result.totalPages) break
            }
            is GetAssetsResult.Failure -> {
                println("Error loading page $currentPage: ${result.error.message}")
                break
            }
        }
    } while (true)
}
func loadAllAssets() async {
    let filter = ZyncAssetSortAndFilter(isActive: true)
    var currentPage = 1
    let pageSize = 50

    while true {
        switch onEnum(of: await zync.asset.fetchAssets(
            sortAndFilter: filter,
            page: currentPage,
            pageSize: pageSize
        )) {
        case .success(let success):
            processAssets(success.data)
            currentPage += 1

            if success.isPartialData {
                print("Warning: Showing cached data")
            }

            if currentPage > success.totalPages { break }

        case .failure(let failure):
            print("Error loading page \(currentPage): \(failure.error.message)")
            break
        }
    }
}

Filtering & Sorting

Combine filters to find assets by customer, property, category, status, or warranty expiry. Use keyword search for name or code.

val filter = ZyncAssetSortAndFilter(
    sortType = ZyncSortType.Ascending,
    sortBy = ZyncAssetSortBy.WarrantyExpiryDate,
    customer = ZyncFilterModule(uid = "customer-uid-123"),
    assetStatus = "INSTALLED",
    isActive = true,
    warrantyExpiryDateRange = ZyncFilterDateRange(
        fromDate = "2025-01-01",
        toDate = "2025-12-31"
    )
)

val result = zync.asset.fetchAssets(filter, 1, 50)
let filter = ZyncAssetSortAndFilter(
    sortType: .ascending,
    sortBy: .warrantyExpiryDate,
    customer: ZyncFilterModule(uid: "customer-uid-123"),
    assetStatus: "INSTALLED",
    isActive: true,
    warrantyExpiryDateRange: ZyncFilterDateRange(
        fromDate: "2025-01-01",
        toDate: "2025-12-31"
    )
)

let result = await zync.asset.fetchAssets(
    sortAndFilter: filter,
    page: 1,
    pageSize: 50
)

Offline-First Behavior

The Asset Management API follows an offline-first approach. Data is immediately available from cache, with background synchronization when online. Always check the isPartialData flag to determine data freshness.

The SDK returns cached data when offline and syncs in the background when online.

Error Handling

Always handle both Success and Failure cases. Use error.message for user-friendly messages and error.httpStatusCode for specific error handling.

when (val result = zync.asset.getAssetDetail(assetUid)) {
    is GetAssetResult.Success -> {
        // Handle success
    }
    is GetAssetResult.Failure -> {
        when (result.error.httpStatusCode) {
            404 -> println("Asset not found")
            401 -> println("Authentication required")
            else -> println("Error: ${result.error.message}")
        }
    }
}
switch onEnum(of: await zync.asset.getAssetDetail(assetUid: assetUid)) {
case .success(let success):
    // Handle success

case .failure(let failure):
    switch failure.error.httpStatusCode {
    case 404:
        print("Asset not found")
    case 401:
        print("Authentication required")
    default:
        print("Error: \(failure.error.message)")
    }
}

Common Use Cases

Find Assets with Expiring Warranties

val expiringWarrantyFilter = ZyncAssetSortAndFilter(
    sortBy = ZyncAssetSortBy.WarrantyExpiryDate,
    isActive = true,
    warrantyExpiryDateRange = ZyncFilterDateRange(
        fromDate = "2025-01-01",
        toDate = "2025-03-31"
    )
)

when (val result = zync.asset.fetchAssets(expiringWarrantyFilter, 1, 50)) {
    is GetAssetsResult.Success -> {
        println("Assets with warranties expiring in Q1 2025: ${result.totalRecords}")
        result.data.forEach { asset ->
            println("${asset.assetName} (${asset.assetCode}) - Expires: ${asset.warrantyExpiryDate}")
        }
    }
    is GetAssetsResult.Failure -> {
        println("Error: ${result.error.message}")
    }
}
let expiringWarrantyFilter = ZyncAssetSortAndFilter(
    sortBy: .warrantyExpiryDate,
    isActive: true,
    warrantyExpiryDateRange: ZyncFilterDateRange(
        fromDate: "2025-01-01",
        toDate: "2025-03-31"
    )
)

switch onEnum(of: await zync.asset.fetchAssets(
    sortAndFilter: expiringWarrantyFilter,
    page: 1,
    pageSize: 50
)) {
case .success(let success):
    print("Assets with warranties expiring in Q1 2025: \(success.totalRecords)")
    success.data.forEach { asset in
        print("\(asset.assetName) (\(asset.assetCode)) - Expires: \(asset.warrantyExpiryDate ?? "N/A")")
    }

case .failure(let failure):
    print("Error: \(failure.error.message)")
}

Filter by Status and Customer

val customerAssetsFilter = ZyncAssetSortAndFilter(
    customer = ZyncFilterModule(uid = "customer-uid-123"),
    assetStatus = "INSTALLED",
    isActive = true
)

val result = zync.asset.fetchAssets(customerAssetsFilter, 1, 50)
let customerAssetsFilter = ZyncAssetSortAndFilter(
    customer: ZyncFilterModule(uid: "customer-uid-123"),
    assetStatus: "INSTALLED",
    isActive: true
)

let result = await zync.asset.fetchAssets(
    sortAndFilter: customerAssetsFilter,
    page: 1,
    pageSize: 50
)

Get Asset with Parts and Inspection Form

when (val result = zync.asset.getAssetDetail(assetUid)) {
    is GetAssetResult.Success -> {
        val asset = result.data

        println("Asset: ${asset.assetName} (${asset.assetCode})")
        println("Status: ${asset.assetStatus}")
        println("Customer: ${asset.customer?.fullName}")
        println("Property: ${asset.property?.propertyName}")

        // Access purchase information
        println("\nPurchase Info:")
        println("  Date: ${asset.purchaseDate}")
        println("  Price: ${asset.purchasePrice}")
        println("  Warranty Expires: ${asset.warrantyExpiryDate}")

        // Access parts
        println("\nParts (${asset.parts.size}):")
        asset.parts.forEach { part ->
            println("  - ${part.productName}: ${part.quantity} units")
            println("    Serial Numbers: ${part.serialNumbers.joinToString()}")
        }

        // Access inspection form
        asset.inspectionForm?.let { form ->
            println("\nInspection Form: ${form.formName}")
            println("  Fields: ${form.formFields.size}")
        }

        // Access custom fields
        println("\nCustom Fields (${asset.customFields.size}):")
        asset.customFields.forEach { field ->
            println("  ${field.fieldName}: ${field.fieldValue}")
        }

        // Access attachments
        println("\nAttachments (${asset.attachments.size}):")
        asset.attachments.forEach { attachment ->
            println("  - ${attachment.fileName}")
        }
    }
    is GetAssetResult.Failure -> {
        println("Error: ${result.error.message}")
    }
}
switch onEnum(of: await zync.asset.getAssetDetail(assetUid: assetUid)) {
case .success(let success):
    let asset = success.data

    print("Asset: \(asset.assetName) (\(asset.assetCode))")
    print("Status: \(asset.assetStatus.rawValue)")
    print("Customer: \(asset.customer?.fullName ?? "N/A")")
    print("Property: \(asset.property?.propertyName ?? "N/A")")

    // Access purchase information
    print("\nPurchase Info:")
    print("  Date: \(asset.purchaseDate ?? "N/A")")
    print("  Price: \(asset.purchasePrice ?? 0)")
    print("  Warranty Expires: \(asset.warrantyExpiryDate ?? "N/A")")

    // Access parts
    print("\nParts (\(asset.parts.count)):")
    asset.parts.forEach { part in
        print("  - \(part.productName ?? "N/A"): \(part.quantity) units")
        print("    Serial Numbers: \(part.serialNumbers.joined(separator: ", "))")
    }

    // Access inspection form
    if let form = asset.inspectionForm {
        print("\nInspection Form: \(form.formName ?? "N/A")")
        print("  Fields: \(form.formFields.count)")
    }

    // Access custom fields
    print("\nCustom Fields (\(asset.customFields.count)):")
    asset.customFields.forEach { field in
        print("  \(field.fieldName): \(field.fieldValue ?? "N/A")")
    }

    // Access attachments
    print("\nAttachments (\(asset.attachments.count)):")
    asset.attachments.forEach { attachment in
        print("  - \(attachment.fileName)")
    }

case .failure(let failure):
    print("Error: \(failure.error.message)")
}

Search by Serial Number

val serialNumberFilter = ZyncAssetSortAndFilter(
    serialNumbers = listOf("SN-12345", "SN-67890"),
    isActive = true
)

when (val result = zync.asset.fetchAssets(serialNumberFilter, 1, 50)) {
    is GetAssetsResult.Success -> {
        println("Found ${result.totalRecords} assets with specified serial numbers")
        result.data.forEach { asset ->
            println("${asset.assetName}: ${asset.serialNumber}")
        }
    }
    is GetAssetsResult.Failure -> {
        println("Error: ${result.error.message}")
    }
}
let serialNumberFilter = ZyncAssetSortAndFilter(
    serialNumbers: ["SN-12345", "SN-67890"],
    isActive: true
)

switch onEnum(of: await zync.asset.fetchAssets(
    sortAndFilter: serialNumberFilter,
    page: 1,
    pageSize: 50
)) {
case .success(let success):
    print("Found \(success.totalRecords) assets with specified serial numbers")
    success.data.forEach { asset in
        print("\(asset.assetName): \(asset.serialNumber ?? "N/A")")
    }

case .failure(let failure):
    print("Error: \(failure.error.message)")
}

Copyright © 2025 Zuper Inc. All rights reserved. This software is proprietary and confidential.