From 8323fdd73272a2882781aba3c499ba0be3dff2a6 Mon Sep 17 00:00:00 2001 From: UMTS at Teleco Date: Sat, 13 Dec 2025 02:51:15 +0100 Subject: committing to insanity --- src/core/data/asset_fields.rs | 1008 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1008 insertions(+) create mode 100644 src/core/data/asset_fields.rs (limited to 'src/core/data/asset_fields.rs') diff --git a/src/core/data/asset_fields.rs b/src/core/data/asset_fields.rs new file mode 100644 index 0000000..c9b6a78 --- /dev/null +++ b/src/core/data/asset_fields.rs @@ -0,0 +1,1008 @@ +use crate::api::ApiClient; +use crate::core::components::form_builder::FormBuilder; +use crate::core::{EditorField, FieldType}; +use serde_json::Value; + +/// Struct to hold commonly used dropdown options for assets +pub struct AssetDropdownOptions { + pub asset_types: Vec<(String, String)>, + pub status_options: Vec<(String, String)>, + pub lending_status_options: Vec<(String, String)>, + pub no_scan_options: Vec<(String, String)>, + pub zone_plus_options: Vec<(String, String)>, + pub category_options: Vec<(String, String)>, + pub zone_options: Vec<(String, String)>, + pub supplier_options: Vec<(String, String)>, + pub label_template_options: Vec<(String, String)>, + pub audit_task_options: Vec<(String, String)>, +} + +impl AssetDropdownOptions { + /// Create dropdown options by fetching from API + pub fn new(api_client: &ApiClient) -> Self { + // Static options + let asset_types = vec![ + ("N".to_string(), "Normal".to_string()), + ("B".to_string(), "Basic".to_string()), + ("L".to_string(), "License".to_string()), + ("C".to_string(), "Consumable".to_string()), + ]; + + // Status options: include full set supported by schema. Some installations may use "Retired" while others use "Scrapped". + // We include both to allow selection wherever the backend enum allows it. + let status_options = vec![ + ("Good".to_string(), "Good".to_string()), + ("Attention".to_string(), "Attention".to_string()), + ("Faulty".to_string(), "Faulty".to_string()), + ("Missing".to_string(), "Missing".to_string()), + ("In Repair".to_string(), "In Repair".to_string()), + ("In Transit".to_string(), "In Transit".to_string()), + ("Expired".to_string(), "Expired".to_string()), + ("Unmanaged".to_string(), "Unmanaged".to_string()), + ("Retired".to_string(), "Retired".to_string()), + ("Scrapped".to_string(), "Scrapped".to_string()), + ]; + + let lending_status_options = vec![ + ("Available".to_string(), "Available".to_string()), + ("Borrowed".to_string(), "Borrowed".to_string()), + ("Overdue".to_string(), "Overdue".to_string()), + ("Deployed".to_string(), "Deployed".to_string()), + ( + "Illegally Handed Out".to_string(), + "Illegally Handed Out".to_string(), + ), + ("Stolen".to_string(), "Stolen".to_string()), + ]; + + let no_scan_options = vec![ + ("No".to_string(), "No".to_string()), + ("Ask".to_string(), "Ask".to_string()), + ("Yes".to_string(), "Yes".to_string()), + ]; + + let zone_plus_options = vec![ + ("".into(), "".into()), + ("Floating Local".into(), "Floating Local".into()), + ("Floating Global".into(), "Floating Global".into()), + ("Clarify".into(), "Clarify".into()), + ]; + + // Fetch categories from API + let mut category_options: Vec<(String, String)> = Vec::new(); + if let Ok(resp) = api_client.select( + "categories", + Some(vec!["id".into(), "category_name".into()]), + None, + Some(vec![crate::models::OrderBy { + column: "category_name".into(), + direction: "ASC".into(), + }]), + None, + ) { + if resp.success { + if let Some(data) = resp.data { + for row in data { + let id = row + .get("id") + .and_then(|v| v.as_i64()) + .map(|n| n.to_string()) + .unwrap_or_default(); + let name = row + .get("category_name") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + category_options.push((id, name)); + } + } + } + } + + // Fetch zones from API + let mut zone_options: Vec<(String, String)> = Vec::new(); + if let Ok(resp) = api_client.select( + "zones", + Some(vec!["id".into(), "zone_code".into(), "zone_name".into()]), + None, + Some(vec![crate::models::OrderBy { + column: "zone_code".into(), + direction: "ASC".into(), + }]), + None, + ) { + if resp.success { + if let Some(data) = resp.data { + for row in data { + let id = row + .get("id") + .and_then(|v| v.as_i64()) + .map(|n| n.to_string()) + .unwrap_or_default(); + let code = row + .get("zone_code") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + let name = row + .get("zone_name") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + zone_options.push((id, format!("{} - {}", code, name))); + } + } + } + } + + // Fetch suppliers from API + let mut supplier_options: Vec<(String, String)> = Vec::new(); + if let Ok(resp) = api_client.select( + "suppliers", + Some(vec!["id".into(), "name".into()]), + None, + Some(vec![crate::models::OrderBy { + column: "name".into(), + direction: "ASC".into(), + }]), + None, + ) { + if resp.success { + if let Some(data) = resp.data { + for row in data { + let id = row + .get("id") + .and_then(|v| v.as_i64()) + .map(|n| n.to_string()) + .unwrap_or_default(); + let name = row + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + supplier_options.push((id, name)); + } + } + } + } + + // Fetch label templates for dropdown + let mut label_template_options: Vec<(String, String)> = Vec::new(); + if let Ok(resp) = api_client.select( + "label_templates", + Some(vec!["id".into(), "template_name".into()]), + None, + Some(vec![crate::models::OrderBy { + column: "template_name".into(), + direction: "ASC".into(), + }]), + None, + ) { + if resp.success { + if let Some(data) = resp.data { + for row in data { + let id = row + .get("id") + .and_then(|v| v.as_i64()) + .map(|n| n.to_string()) + .unwrap_or_default(); + let name = row + .get("template_name") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + label_template_options.push((id, name)); + } + } + } + } + + // Fetch audit tasks and add default "None" option + let mut audit_task_options: Vec<(String, String)> = + vec![(String::new(), "-- None --".to_string())]; + if let Ok(resp) = api_client.select( + "audit_tasks", + Some(vec!["id".into(), "task_name".into()]), + None, + Some(vec![crate::models::OrderBy { + column: "task_name".into(), + direction: "ASC".into(), + }]), + None, + ) { + if resp.success { + if let Some(data) = resp.data { + for row in data { + if let Some(id) = row.get("id").and_then(|v| v.as_i64()) { + let name = row + .get("task_name") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + audit_task_options.push((id.to_string(), name)); + } + } + } + } + } + + Self { + asset_types, + status_options, + lending_status_options, + no_scan_options, + zone_plus_options, + category_options, + zone_options, + supplier_options, + label_template_options, + audit_task_options, + } + } +} + +/// Asset field configuration builder - provides standardized field definitions for asset forms +pub struct AssetFieldBuilder; + +impl AssetFieldBuilder { + /// Create a Full Add dialog that shows (nearly) all asset fields similar to Advanced Edit, + /// but configured for inserting a new asset (no ID fields, audit fields stay read-only). + pub fn create_full_add_dialog(api_client: &ApiClient) -> FormBuilder { + let options = AssetDropdownOptions::new(api_client); + + let fields: Vec = vec![ + // Core identifiers for new record + EditorField { + name: "asset_tag".into(), + label: "Asset Tag".into(), + field_type: FieldType::Text, + required: true, + read_only: false, + }, + EditorField { + name: "asset_type".into(), + label: "Type".into(), + field_type: FieldType::Dropdown(options.asset_types.clone()), + required: true, + read_only: false, + }, + EditorField { + name: "name".into(), + label: "Name".into(), + field_type: FieldType::Text, + required: true, + read_only: false, + }, + // Classification + EditorField { + name: "category_id".into(), + label: "Category".into(), + field_type: FieldType::Dropdown(options.category_options.clone()), + required: false, + read_only: false, + }, + // Quick add category + EditorField { + name: "new_category_name".into(), + label: "Add Category - Name".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "new_category_code".into(), + label: "Add Category - Code".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + // Location + EditorField { + name: "zone_id".into(), + label: "Zone".into(), + field_type: FieldType::Dropdown(options.zone_options.clone()), + required: false, + read_only: false, + }, + // Quick add zone + EditorField { + name: "new_zone_parent_id".into(), + label: "Add Zone - Parent".into(), + field_type: FieldType::Dropdown(options.zone_options.clone()), + required: false, + read_only: false, + }, + EditorField { + name: "new_zone_mini_code".into(), + label: "Add Zone - Mini Code".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "new_zone_name".into(), + label: "Add Zone - Name".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "zone_plus".into(), + label: "Zone Plus".into(), + field_type: FieldType::Dropdown(options.zone_plus_options.clone()), + required: false, + read_only: false, + }, + EditorField { + name: "no_scan".into(), + label: "No Scan".into(), + field_type: FieldType::Dropdown(options.no_scan_options.clone()), + required: false, + read_only: false, + }, + // Make/model + EditorField { + name: "manufacturer".into(), + label: "Manufacturer".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "model".into(), + label: "Model".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "serial_number".into(), + label: "Serial Number".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + // Status + EditorField { + name: "status".into(), + label: "Status".into(), + field_type: FieldType::Dropdown(options.status_options.clone()), + required: true, + read_only: false, + }, + // Financial / dates + EditorField { + name: "price".into(), + label: "Price".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "purchase_date".into(), + label: "Purchase Date".into(), + field_type: FieldType::Date, + required: false, + read_only: false, + }, + EditorField { + name: "warranty_until".into(), + label: "Warranty Until".into(), + field_type: FieldType::Date, + required: false, + read_only: false, + }, + EditorField { + name: "expiry_date".into(), + label: "Expiry Date".into(), + field_type: FieldType::Date, + required: false, + read_only: false, + }, + // Lendable + EditorField { + name: "lendable".into(), + label: "Lendable".into(), + field_type: FieldType::Checkbox, + required: false, + read_only: false, + }, + EditorField { + name: "lending_status".into(), + label: "Lending Status".into(), + field_type: FieldType::Dropdown(options.lending_status_options.clone()), + required: false, + read_only: false, + }, + EditorField { + name: "due_date".into(), + label: "Due Date".into(), + field_type: FieldType::Date, + required: false, + read_only: false, + }, + // Supplier + EditorField { + name: "supplier_id".into(), + label: "Supplier".into(), + field_type: FieldType::Dropdown(options.supplier_options.clone()), + required: false, + read_only: false, + }, + // Quick add supplier + EditorField { + name: "new_supplier_name".into(), + label: "Add Supplier - Name".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + // Label template + EditorField { + name: "label_template_id".into(), + label: "Label Template".into(), + field_type: FieldType::Dropdown(options.label_template_options.clone()), + required: false, + read_only: false, + }, + // Notes + EditorField { + name: "notes".into(), + label: "Notes".into(), + field_type: FieldType::MultilineText, + required: false, + read_only: false, + }, + // Optional: print on add toggle + EditorField { + name: "print_label".into(), + label: "Print Label".into(), + field_type: FieldType::Checkbox, + required: false, + read_only: false, + }, + // Audit/meta (read-only informational) + EditorField { + name: "created_date".into(), + label: "Created Date".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "created_by_username".into(), + label: "Created By".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "last_modified_date".into(), + label: "Last Modified".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "last_modified_by_username".into(), + label: "Modified By".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + ]; + + let mut dialog = FormBuilder::new("Add Asset (Full)", fields); + + // Prefill sensible defaults + let mut preset = serde_json::Map::new(); + preset.insert("asset_type".to_string(), Value::String("N".to_string())); + preset.insert("status".to_string(), Value::String("Good".to_string())); + preset.insert("lendable".to_string(), Value::Bool(true)); + preset.insert("print_label".to_string(), Value::Bool(true)); + dialog.open_new(Some(&preset)); + + dialog + } + /// Create a comprehensive Advanced Edit dialog with all asset fields + pub fn create_advanced_edit_dialog(api_client: &ApiClient) -> FormBuilder { + let options = AssetDropdownOptions::new(api_client); + + let fields: Vec = vec![ + // Identifiers (read-only) + EditorField { + name: "id".into(), + label: "ID".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "asset_numeric_id".into(), + label: "Numeric ID".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "asset_tag".into(), + label: "Asset Tag".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + // Core fields + EditorField { + name: "asset_type".into(), + label: "Type".into(), + field_type: FieldType::Dropdown(options.asset_types), + required: true, + read_only: false, + }, + EditorField { + name: "name".into(), + label: "Name".into(), + field_type: FieldType::Text, + required: true, + read_only: false, + }, + EditorField { + name: "description".into(), + label: "Description".into(), + field_type: FieldType::MultilineText, + required: false, + read_only: false, + }, + // Classification + EditorField { + name: "category_id".into(), + label: "Category".into(), + field_type: FieldType::Dropdown(options.category_options.clone()), + required: false, + read_only: false, + }, + // Quick add category + EditorField { + name: "new_category_name".into(), + label: "Add Category - Name".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "new_category_code".into(), + label: "Add Category - Code".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + // Location + EditorField { + name: "zone_id".into(), + label: "Zone".into(), + field_type: FieldType::Dropdown(options.zone_options.clone()), + required: false, + read_only: false, + }, + // Quick add zone + EditorField { + name: "new_zone_parent_id".into(), + label: "Add Zone - Parent".into(), + field_type: FieldType::Dropdown(options.zone_options.clone()), + required: false, + read_only: false, + }, + EditorField { + name: "new_zone_mini_code".into(), + label: "Add Zone - Mini Code".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "new_zone_name".into(), + label: "Add Zone - Name".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "zone_plus".into(), + label: "Zone Plus".into(), + field_type: FieldType::Dropdown(options.zone_plus_options), + required: false, + read_only: false, + }, + EditorField { + name: "no_scan".into(), + label: "No Scan".into(), + field_type: FieldType::Dropdown(options.no_scan_options), + required: false, + read_only: false, + }, + EditorField { + name: "audit_task_id".into(), + label: "Audit Task".into(), + field_type: FieldType::Dropdown(options.audit_task_options.clone()), + required: false, + read_only: false, + }, + // Make/model + EditorField { + name: "manufacturer".into(), + label: "Manufacturer".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "model".into(), + label: "Model".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "serial_number".into(), + label: "Serial Number".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + // Status + EditorField { + name: "status".into(), + label: "Status".into(), + field_type: FieldType::Dropdown(options.status_options), + required: true, + read_only: false, + }, + // Financial / dates + EditorField { + name: "price".into(), + label: "Price".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "purchase_date".into(), + label: "Purchase Date".into(), + field_type: FieldType::Date, + required: false, + read_only: false, + }, + EditorField { + name: "warranty_until".into(), + label: "Warranty Until".into(), + field_type: FieldType::Date, + required: false, + read_only: false, + }, + EditorField { + name: "expiry_date".into(), + label: "Expiry Date".into(), + field_type: FieldType::Date, + required: false, + read_only: false, + }, + // Lendable + EditorField { + name: "lendable".into(), + label: "Lendable".into(), + field_type: FieldType::Checkbox, + required: false, + read_only: false, + }, + EditorField { + name: "lending_status".into(), + label: "Lending Status".into(), + field_type: FieldType::Dropdown(options.lending_status_options), + required: false, + read_only: false, + }, + EditorField { + name: "due_date".into(), + label: "Due Date".into(), + field_type: FieldType::Date, + required: false, + read_only: false, + }, + EditorField { + name: "supplier_id".into(), + label: "Supplier".into(), + field_type: FieldType::Dropdown(options.supplier_options), + required: false, + read_only: false, + }, + // Quick add supplier + EditorField { + name: "new_supplier_name".into(), + label: "Add Supplier - Name".into(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + // Borrowers (read-only historical) + EditorField { + name: "previous_borrower_id".into(), + label: "Prev Borrower".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "current_borrower_id".into(), + label: "Current Borrower".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + // Label template selection + EditorField { + name: "label_template_id".into(), + label: "Label Template".into(), + field_type: FieldType::Dropdown(options.label_template_options.clone()), + required: false, + read_only: false, + }, + // Notes / images + EditorField { + name: "notes".into(), + label: "Notes".into(), + field_type: FieldType::MultilineText, + required: false, + read_only: false, + }, + // Audit/meta (read-only) + EditorField { + name: "created_date".into(), + label: "Created Date".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "created_by_username".into(), + label: "Created By".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "last_modified_date".into(), + label: "Last Modified".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + EditorField { + name: "last_modified_by_username".into(), + label: "Modified By".into(), + field_type: FieldType::Text, + required: false, + read_only: true, + }, + ]; + + FormBuilder::new("Advanced Edit Asset", fields) + } + + /// Create an Easy Edit dialog with essential asset fields only + pub fn create_easy_edit_dialog(api_client: &ApiClient) -> FormBuilder { + let options = AssetDropdownOptions::new(api_client); + + let fields = vec![ + EditorField { + name: "asset_tag".to_string(), + label: "Asset Tag".to_string(), + field_type: FieldType::Text, + required: true, + read_only: false, + }, + EditorField { + name: "asset_type".to_string(), + label: "Type".to_string(), + field_type: FieldType::Dropdown(options.asset_types), + required: true, + read_only: false, + }, + EditorField { + name: "name".to_string(), + label: "Name".to_string(), + field_type: FieldType::Text, + required: true, + read_only: false, + }, + EditorField { + name: "category_id".to_string(), + label: "Category".to_string(), + field_type: FieldType::Dropdown(options.category_options), + required: false, + read_only: false, + }, + EditorField { + name: "manufacturer".to_string(), + label: "Manufacturer".to_string(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "model".to_string(), + label: "Model".to_string(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "zone_id".to_string(), + label: "Zone".to_string(), + field_type: FieldType::Dropdown(options.zone_options), + required: false, + read_only: false, + }, + EditorField { + name: "status".to_string(), + label: "Status".to_string(), + field_type: FieldType::Dropdown(options.status_options), + required: true, + read_only: false, + }, + EditorField { + name: "lendable".to_string(), + label: "Lendable".to_string(), + field_type: FieldType::Checkbox, + required: false, + read_only: false, + }, + ]; + + FormBuilder::new("Easy Edit Asset", fields) + } + + /// Create an Add Asset dialog with quick-add functionality for categories/zones/suppliers + pub fn create_add_dialog_with_preset(api_client: &ApiClient) -> FormBuilder { + let options = AssetDropdownOptions::new(api_client); + + let fields = vec![ + EditorField { + name: "asset_tag".to_string(), + label: "Asset Tag".to_string(), + field_type: FieldType::Text, + required: true, + read_only: false, + }, + EditorField { + name: "asset_type".to_string(), + label: "Type".to_string(), + field_type: FieldType::Dropdown(options.asset_types), + required: true, + read_only: false, + }, + EditorField { + name: "name".to_string(), + label: "Name".to_string(), + field_type: FieldType::Text, + required: true, + read_only: false, + }, + // Category dropdown + add-new placeholders + EditorField { + name: "category_id".to_string(), + label: "Category".to_string(), + field_type: FieldType::Dropdown(options.category_options), + required: false, + read_only: false, + }, + // Label template selection + EditorField { + name: "label_template_id".to_string(), + label: "Label Template".to_string(), + field_type: FieldType::Dropdown(options.label_template_options.clone()), + required: false, + read_only: false, + }, + // Print label option + EditorField { + name: "print_label".to_string(), + label: "Print Label".to_string(), + field_type: FieldType::Checkbox, + required: false, + read_only: false, + }, + // Add new category name/code as text fields + EditorField { + name: "new_category_name".to_string(), + label: "Add New Category - Name".to_string(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "new_category_code".to_string(), + label: "Add New Category - Code".to_string(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "manufacturer".to_string(), + label: "Manufacturer".to_string(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "model".to_string(), + label: "Model".to_string(), + field_type: FieldType::Text, + required: false, + read_only: false, + }, + EditorField { + name: "zone_id".to_string(), + label: "Zone".to_string(), + field_type: FieldType::Dropdown(options.zone_options), + required: false, + read_only: false, + }, + EditorField { + name: "status".to_string(), + label: "Status".to_string(), + field_type: FieldType::Dropdown(options.status_options), + required: true, + read_only: false, + }, + EditorField { + name: "lendable".to_string(), + label: "Lendable".to_string(), + field_type: FieldType::Checkbox, + required: false, + read_only: false, + }, + ]; + + let mut dialog = FormBuilder::new("Add Asset", fields); + + // Open with sensible defaults + let mut preset = serde_json::Map::new(); + preset.insert("asset_type".to_string(), Value::String("N".to_string())); + preset.insert("status".to_string(), Value::String("Good".to_string())); + preset.insert("lendable".to_string(), Value::Bool(true)); + // Default to printing a label after adding when possible + preset.insert("print_label".to_string(), Value::Bool(true)); + + dialog.open_new(Some(&preset)); + dialog + } + + /// Get the list of fields that are allowed to be updated via API + #[allow(dead_code)] + pub fn get_allowed_update_fields() -> Vec { + vec![ + "asset_tag".to_string(), + "asset_type".to_string(), + "name".to_string(), + "description".to_string(), + "category_id".to_string(), + "zone_id".to_string(), + "zone_plus".to_string(), + "no_scan".to_string(), + "manufacturer".to_string(), + "model".to_string(), + "serial_number".to_string(), + "status".to_string(), + "price".to_string(), + "purchase_date".to_string(), + "warranty_until".to_string(), + "expiry_date".to_string(), + "lendable".to_string(), + "lending_status".to_string(), + "due_date".to_string(), + "supplier_id".to_string(), + "notes".to_string(), + "label_template_id".to_string(), + ] + } +} -- cgit v1.2.3-70-g09d2