From 3183b381146ed2a0c9061f7294659c971eaad47c Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 8 Sep 2025 09:42:46 +0000 Subject: [PATCH 01/19] Translated using Weblate (French) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (518 of 518 strings) Translated using Weblate (French) Currently translated at 100.0% (518 of 518 strings) Translated using Weblate (French) Currently translated at 100.0% (518 of 518 strings) Translated using Weblate (French) Currently translated at 100.0% (518 of 518 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (518 of 518 strings) Translated using Weblate (Romanian) Currently translated at 60.1% (311 of 517 strings) Translated using Weblate (Romanian) Currently translated at 60.1% (311 of 517 strings) Translated using Weblate (Romanian) Currently translated at 60.1% (311 of 517 strings) Translated using Weblate (Romanian) Currently translated at 60.1% (311 of 517 strings) Translated using Weblate (Polish) Currently translated at 97.8% (506 of 517 strings) Translated using Weblate (Italian) Currently translated at 82.5% (427 of 517 strings) Translated using Weblate (Italian) Currently translated at 82.5% (427 of 517 strings) Translated using Weblate (German) Currently translated at 99.8% (516 of 517 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (517 of 517 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (517 of 517 strings) Translated using Weblate (German) Currently translated at 98.4% (509 of 517 strings) Translated using Weblate (Slovak) Currently translated at 100.0% (517 of 517 strings) Translated using Weblate (Czech) Currently translated at 100.0% (517 of 517 strings) Translated using Weblate (Czech) Currently translated at 100.0% (517 of 517 strings) Translated using Weblate (Czech) Currently translated at 100.0% (506 of 506 strings) Translated using Weblate (Czech) Currently translated at 100.0% (506 of 506 strings) Co-authored-by: Adam Havránek Co-authored-by: Erwin van Londen Co-authored-by: Hannes Salen Co-authored-by: Jose Riha Co-authored-by: MyMemory Co-authored-by: Philipp Walter Co-authored-by: Saverio Salatino Co-authored-by: Supertriton Co-authored-by: The Frog Co-authored-by: Weblate Co-authored-by: Weblate Co-authored-by: Weblate Translation Memory Co-authored-by: vizu Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/cs/ Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/de/ Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/ Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/it/ Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/ Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/pl/ Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/ro/ Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/sk/ Translation: Homebox/Frontend --- frontend/locales/cs-CZ.json | 17 ++++++++++- frontend/locales/de.json | 17 +++++++++-- frontend/locales/fr.json | 38 ++++++++++++++++++++++-- frontend/locales/it.json | 14 +++++++++ frontend/locales/nl.json | 18 +++++++++-- frontend/locales/pl.json | 3 ++ frontend/locales/ro-RO.json | 59 +++++++++++++++++++++++++++++++++---- frontend/locales/sk-SK.json | 33 ++++++++++++++++++++- 8 files changed, 185 insertions(+), 14 deletions(-) diff --git a/frontend/locales/cs-CZ.json b/frontend/locales/cs-CZ.json index 7832793b..e0824e70 100644 --- a/frontend/locales/cs-CZ.json +++ b/frontend/locales/cs-CZ.json @@ -291,6 +291,18 @@ "description": "Popis", "details": "Detaily", "drag_and_drop": "Přetáhněte sem soubory nebo klikněte pro výběr", + "duplicate": { + "copy_attachments": "Kopírovat přílohy", + "copy_custom_fields": "Kopírovat vlastní pole", + "copy_maintenance": "Kopírovat údržbu", + "custom_prefix": "Kopírovat předponu", + "enable_custom_prefix": "Povolit vlastní předponu", + "override_instructions": "Podržením klávesy Shift při kliknutí na tlačítko duplikovat přepíšete tato nastavení.", + "prefix": "Kopie ", + "prefix_instructions": "Tato předpona bude přidána na začátek názvu duplikované položky. Na konec předpony vložte mezeru, aby mezi předponou a názvem položky byla mezera.", + "temporary_title": "Dočasné nastavení", + "title": "Duplikovat nastavení" + }, "edit": { "edit_attachment_dialog": { "attachment_title": "Název přílohy", @@ -299,7 +311,8 @@ "primary_photo_sub": "Tato možnost je k dispozici pouze pro fotky. Primární může být pouze jedna fotka. Pokud vyberete tuto možnost, aktuální primární fotka, pokud existuje, bude odškrtnuta.", "select_type": "Zvolte typ", "title": "Úprava přílohy" - } + }, + "view_image": "Zobrazit obrázek" }, "edit_details": "Upravit detaily", "field_selector": "Výběr pole", @@ -397,6 +410,7 @@ "update_label": "Aktualizovat štítek" }, "languages": { + "bs-BA": "Bosňština (Bosna a Hercegovina)", "ca": "Katalánština", "cs-CZ": "Čeština", "de": "Němčina", @@ -423,6 +437,7 @@ "th-TH": "Thajština", "tr": "Turečtina", "uk-UA": "Ukrajinština", + "vi-VN": "Vietnamština", "zh-CN": "Čínština (Zjednodušená)", "zh-HK": "Čínština (Hong Kong)", "zh-MO": "Čínština (Macau)", diff --git a/frontend/locales/de.json b/frontend/locales/de.json index d64196f7..bc117263 100644 --- a/frontend/locales/de.json +++ b/frontend/locales/de.json @@ -291,15 +291,28 @@ "description": "Beschreibung", "details": "Details", "drag_and_drop": "Ziehen Sie Dateien hierher und legen Sie sie dort ab oder klicken Sie, um Dateien auszuwählen", + "duplicate": { + "copy_attachments": "Anhänge kopieren", + "copy_custom_fields": "Benutzerdefinierte Felder kopieren", + "copy_maintenance": "Kopiere Wartung", + "custom_prefix": "Präfix kopieren", + "enable_custom_prefix": "Benutzerdefinierten Präfix aktivieren", + "override_instructions": "Die Umschalt-Taste während des Drückens des Duplizieren-Knopfes halten, um die Einstellungen außer Kraft zu setzen.", + "prefix": "Kopie von ", + "prefix_instructions": "Dieser Präfix wird vor den Beginn des Namens des duplizierten Artikels gestellt. Verwenden Sie ein Leerzeichen am Ende des Präfix, um einen Platz zwischen Präfix und Artikelnamen zu lassen.", + "temporary_title": "Temporäre Einstellungen", + "title": "Einstellungen duplizieren" + }, "edit": { "edit_attachment_dialog": { - "attachment_title": "Anhang Überschrift", + "attachment_title": "Anhangsüberschrift", "attachment_type": "Anhangstyp", "primary_photo": "Primäres Bild", "primary_photo_sub": "Diese Option ist nur für Bilder verfügbar. Nur ein Bild kann das primäre Bild sein. Wenn Sie diese Option auswählen, wird das aktuelle primär Bild, wenn eins ausgewählt ist, abgewählt.", "select_type": "Wählen Sie einen Typ aus", "title": "Anhang bearbeiten" - } + }, + "view_image": "Bild anzeigen" }, "edit_details": "Details bearbeiten", "field_selector": "Feldauswahl", diff --git a/frontend/locales/fr.json b/frontend/locales/fr.json index b9c7fc82..5218e8e5 100644 --- a/frontend/locales/fr.json +++ b/frontend/locales/fr.json @@ -25,7 +25,9 @@ } }, "color_selector": { + "clear": "Effacer Couleur", "color": "Couleur", + "no_color": "Aucune couleur", "no_color_selected": "Aucune couleur sélectionnée", "randomize": "Couleur aléatoire" }, @@ -98,6 +100,8 @@ "item_photo": "Photo de l'article 📷", "item_quantity": "Quantité d'articles", "parent_item": "Article parent", + "product_tooltip_input_barcode": "Remplir avec un code-barre entré manuellement", + "product_tooltip_scan_barcode": "Remplir avec un barre-code d'une 📷", "rotate_photo": "Faire pivoter la photo", "set_as_primary_photo": "Définir comme photo { isPrimary, select, true {non-} false {} other {}}principale", "title": "Créer un article", @@ -118,10 +122,20 @@ "upload_photos": "Transférer des photos", "uploaded": "Photo téléversée" }, + "product_import": { + "barcode": "Code-barre produit", + "db_source": "Base de données source", + "error_exception": "Une erreur s'est produite lors de la récupération du code-barre ", + "error_invalid_barcode": "Le code-barre fourni est invalide", + "error_not_found": "Aucun produit trouvé pour le barre-code fourni.", + "search_item": "Rechercher un produit", + "title": "Importer un produit" + }, "selector": { "no_results": "Aucun résultat trouvé", "placeholder": "Sélectionner…", - "search_placeholder": "Tapez pour rechercher…" + "search_placeholder": "Tapez pour rechercher…", + "searching": "Recherche en cours…" }, "view": { "selectable": { @@ -141,6 +155,7 @@ }, "label": { "create_modal": { + "label_color": "Couleur de l'étiquette", "label_description": "Description de l'étiquette", "label_name": "Nom de l'étiquette", "title": "Créer une étiquette", @@ -181,6 +196,9 @@ "shortcut_hint": "Utilisez les touches numériques pour sélectionner rapidement une action." } }, + "errors": { + "api_failure": "L'appel vers l'API du back-end a échoué " + }, "global": { "add": "Ajouter", "archived": "Archivé", @@ -274,6 +292,18 @@ "description": "Description", "details": "Détails", "drag_and_drop": "Glissez-déposez les fichiers ici ou cliquez pour choisir des fichiers", + "duplicate": { + "copy_attachments": "Copier les fichiers joints", + "copy_custom_fields": "Copier les champs personnaliisés", + "copy_maintenance": "Copie de la maintenance", + "custom_prefix": "Copier le préfixe", + "enable_custom_prefix": "Activer le préfixe personnalisé", + "override_instructions": "Maintenez la touche Maj enfoncée lorsque vous cliquez sur le bouton Dupliquer pour remplacer ces paramètres.", + "prefix": "Copie de ", + "prefix_instructions": "Ce préfixe sera ajouté au début du nom de l'élément dupliqué. Insérez un espace à la fin afin de séparer le préfixe du nom de l'élément.", + "temporary_title": "Paramètres temporaires", + "title": "Paramètres de duplication" + }, "edit": { "edit_attachment_dialog": { "attachment_title": "Titre de la pièce jointe", @@ -282,7 +312,8 @@ "primary_photo_sub": "Cette option n'est disponible que pour les photos. Seule une photo peut être principale. Si vous sélectionnez cette option, la photo principale, s'il y en a une, sera désélectionnée.", "select_type": "Sélectionner un type", "title": "Modification de pièce jointe" - } + }, + "view_image": "Voir l'image" }, "edit_details": "Éditez les détails", "field_selector": "Sélecteur de champ", @@ -380,6 +411,7 @@ "update_label": "Mettre à jour l'étiquette" }, "languages": { + "bs-BA": "Bosnie (Bosnie-Herzégovine)", "ca": "Catalan", "cs-CZ": "Tchèque", "de": "Allemand", @@ -557,6 +589,8 @@ } }, "scanner": { + "barcode_detected_message": "Le code-barre du produit a été détecté", + "barcode_fetch_data": "Récupérer les données du produit", "error": "Une erreur s'est produite lors du scan", "invalid_url": "URL de code-barres non valide", "no_sources": "Aucune source vidéo disponible", diff --git a/frontend/locales/it.json b/frontend/locales/it.json index 2542c931..d7badb23 100644 --- a/frontend/locales/it.json +++ b/frontend/locales/it.json @@ -100,6 +100,7 @@ "item_photo": "Foto dell'articolo 📷", "item_quantity": "Quantità Articoli", "parent_item": "Articolo principale", + "product_tooltip_input_barcode": "Compilazione automatica con un codice a barre fornito manualmente", "product_tooltip_scan_barcode": "Riempimento automatico con un codice a barre da 📷", "rotate_photo": "Ruota foto", "set_as_primary_photo": "Imposta come { isPrimary, select, true {non} false {} other {}} foto principale", @@ -125,6 +126,7 @@ "barcode": "Codice a barre del prodotto", "error_exception": "Si è verificato un errore durante il recupero del codice a barre dell'articolo: ", "error_invalid_barcode": "Il codice a barre fornito non è valido", + "error_not_found": "Nessun prodotto trovato con il codice a barre fornito.", "search_item": "Cerca prodotto", "title": "Importa prodotto" }, @@ -151,6 +153,7 @@ }, "label": { "create_modal": { + "label_color": "Colore Etichetta", "label_description": "Descrizione Etichetta", "label_name": "Nome Etichetta", "title": "Crea Etichetta", @@ -191,6 +194,9 @@ "shortcut_hint": "Utilizzare i tasti numerici per selezionare rapidamente un'azione." } }, + "errors": { + "api_failure": "Chiamata API backend non riuscita: " + }, "global": { "add": "Aggiungi", "archived": "Archiviato", @@ -284,6 +290,14 @@ "description": "Descrizione", "details": "Dettagli", "drag_and_drop": "Trascina e rilascia i file qui o fai clic per selezionare i file", + "duplicate": { + "copy_attachments": "Copia allegati", + "copy_custom_fields": "Copia Campi Personalizzati", + "custom_prefix": "Copia Prefisso", + "enable_custom_prefix": "Abilita prefisso personalizzato", + "prefix": "Copia di ", + "temporary_title": "Impostazioni temporanee" + }, "edit": { "edit_attachment_dialog": { "attachment_title": "Titolo allegato", diff --git a/frontend/locales/nl.json b/frontend/locales/nl.json index 4946cf4a..1f19f918 100644 --- a/frontend/locales/nl.json +++ b/frontend/locales/nl.json @@ -134,7 +134,8 @@ "selector": { "no_results": "Geen resultaten gevonden", "placeholder": "Selecteer…", - "search_placeholder": "Type om te zoeken…" + "search_placeholder": "Type om te zoeken…", + "searching": "Zoeken…" }, "view": { "selectable": { @@ -291,6 +292,18 @@ "description": "Beschrijving", "details": "Details", "drag_and_drop": "Sleep bestanden hierheen en zet ze neer of klik om bestanden te selecteren", + "duplicate": { + "copy_attachments": "Bijlagen kopiëren", + "copy_custom_fields": "Aangepaste velden kopiëren", + "copy_maintenance": "Onderhoud kopiëren", + "custom_prefix": "Voorvoegsel kopiëren", + "enable_custom_prefix": "Aangepast voorvoegsel inschakelen", + "override_instructions": "Houd Shift ingedrukt wanneer u op de knop dupliceren klikt om deze instellingen te overschrijven.", + "prefix": "Kopie van ", + "prefix_instructions": "Dit voorvoegsel wordt toegevoegd aan het begin van de naam van het gedupliceerde item. Voeg een spatie toe aan het einde van het voorvoegsel om een spatie toe te voegen tussen het voorvoegsel en de itemnaam.", + "temporary_title": "Tijdelijke instellingen", + "title": "Dupliceer instellingen" + }, "edit": { "edit_attachment_dialog": { "attachment_title": "Bijlage titel", @@ -299,7 +312,8 @@ "primary_photo_sub": "Deze optie is alleen beschikbaar voor foto's. Er kan maar één primaire foto zijn. Als je deze optie selecteerd zal de huidige primaire foto worden gedeselecteerd.", "select_type": "Selecteer een type", "title": "Bijlage bewerken" - } + }, + "view_image": "Afbeelding bekijken" }, "edit_details": "Details bewerken", "field_selector": "Veld selectie", diff --git a/frontend/locales/pl.json b/frontend/locales/pl.json index d1f20896..f9fa6d48 100644 --- a/frontend/locales/pl.json +++ b/frontend/locales/pl.json @@ -291,6 +291,9 @@ "description": "Opis", "details": "Szczegóły", "drag_and_drop": "Przeciągnij i upuść pliki tutaj lub kliknij, aby wybrać pliki", + "duplicate": { + "prefix": "Kopia z " + }, "edit": { "edit_attachment_dialog": { "attachment_title": "Tytuł załącznika", diff --git a/frontend/locales/ro-RO.json b/frontend/locales/ro-RO.json index db9b6a6c..3df94949 100644 --- a/frontend/locales/ro-RO.json +++ b/frontend/locales/ro-RO.json @@ -1,10 +1,16 @@ { "components": { "app": { + "create_modal": { + "shift": "Shift" + }, "import_dialog": { "change_warning": "Comportamentul pentru importuri cu import_refs existente s-a schimbat. Dacă un import_ref este prezent în fișierul CSV,\nobiectul v-a fi actualizat cu valorile din fișierul CSV.", "description": "Importă un fișier CSV care conține toate obiectele, etichetele și locațiile tale. \nCitește documentația pentru mai multe informații privind format-ul necesar.", - "title": "Importă fișier CSV" + "title": "Importă fișier CSV", + "toast": { + "import_success": "Import cu succes!" + } }, "outdated": { "current_version": "Versiune Curentă", @@ -14,12 +20,16 @@ "new_version_available_link": "Dă click aici pentru a vedea notele de lansare" } }, + "color_selector": { + "color": "Culoare", + "no_color": "Fara culoare" + }, "global": { "copy_text": { "documentation": "documentație", "failed_to_copy": "Nu s-a copiat textul în clipboard", "https_required": "pentru că HTTPS este necesar", - "learn_more": "Învață mai multe în noastră" + "learn_more": "Învață mai multe în" }, "date_time": { "ago": "{0} în urmă", @@ -45,18 +55,47 @@ "years": "ani", "yesterday": "ieri" }, + "label_maker": { + "browser_print": "Imprimare din browser", + "confirm_description": "Sigur doriți să imprimați această etichetă?", + "download": "Descărcare etichetă", + "print": "Imprimare etichetă", + "server_print": "Imprimare pe server", + "titles": "Etichete", + "toast": { + "print_failed": "Imprimarea etichetei a eșuat", + "print_success": "Etichetă imprimată" + } + }, "page_qr_code": { - "page_url": "URL Pagină" + "page_url": "URL Pagină", + "qr_tooltip": "Afișați codul QR" }, "password_score": { "password_strength": "Complexitate Parolă" } }, "item": { + "attachments_list": { + "download": "Descărcare", + "open_new_tab": "Deschideți într-o filă nouă" + }, "create_modal": { + "delete_photo": "Ştergere fotografie", "item_description": "Descriere articol", "item_name": "Denumire articol", - "title": "Crează articol" + "item_photo": "Fotografie de articol 📷", + "item_quantity": "Cantitate Articol", + "parent_item": "Articol Părinte", + "product_tooltip_input_barcode": "Completare automată cu un cod de bare furnizat manual", + "product_tooltip_scan_barcode": "Completare automată cu un cod de bare de la 📷", + "rotate_photo": "Rotește fotografia", + "set_as_primary_photo": "Setați ca { isPrimary, select, true {non-} false {} other {}} fotografie principală", + "title": "Crează articol", + "toast": { + "create_failed": "Articolul nu a putut fi creat", + "create_success": "Articol creat" + } }, "view": { "selectable": { @@ -88,7 +127,7 @@ "parent_location": "Locație Părinte" }, "tree": { - "no_locations": "Nu există locații disponibile. Adaugă o locație nouă folosind butonul\n`<`span class=\"link-primary\"`>`Crează`<`/span`>` din bara de navigație." + "no_locations": "Nu există locații disponibile. Adaugă o locație nouă folosind butonul\n'Crează' din bara de navigație." } } }, @@ -105,7 +144,12 @@ "edit": "Redactare", "email": "Adresă de email", "follow_dev": "Urmărește developer-ul", + "footer": { + "api_link": "''API''", + "version_link": "'<'a href=\"https://github.com/sysadminsmedia/homebox/releases/tag/{ version }\" target=\"_blank\"'>' Versiune: { version } Build: { build } ''" + }, "github": "Proiect GitHub", + "insured": "Asigurat", "items": "Articole", "join_discord": "Vino pe Discord", "labels": "Etichete", @@ -158,6 +202,9 @@ "description": "Descriere", "details": "Detalii", "drag_and_drop": "Trageți și plasați fișierele aici sau faceți clic pentru a selecta fișierele", + "edit": { + "view_image": "Vizualizează Imagine" + }, "edit_details": "Editare detalii", "field_selector": "Selector Câmp", "field_value": "Valoare Câmp", @@ -343,7 +390,7 @@ "export_sub": "Exportă formatul CSV standard pentru Homebox. Această acțiune ca exporta toate articolele din inventar.", "import": "Importă Inventar", "import_button": "Importă Inventar", - "import_sub": "Importă formatul standard CSV pentru Homebox. Fără un ' ' HB.import_ref' 'coloană, aceasta va''nu' < / b> ' suprascrie orice elemente existente în inventarul dvs., adăugați doar elemente noi. Rânduri cu' < code> ' HB.import_ref' < / code> ' coloana sunt îmbinate în elementele existente cu același import_ref, dacă există unul." + "import_sub": "Importă formatul standard CSV pentru Homebox. Fără un '' HB.import_ref '' coloană, aceasta va''nu'' suprascrie orice elemente existente în inventarul dvs., adăugați doar elemente noi. Rânduri cu''HB.import_ref'' coloana sunt îmbinate în elementele existente cu același import_ref, dacă există unul." }, "import_export_sub": "Importați și exportați inventarul într-un fișier CSV. Acest lucru este util atunci când se migrează inventarul către o instanță nouă de Homebox.", "reports": "Rapoarte", diff --git a/frontend/locales/sk-SK.json b/frontend/locales/sk-SK.json index 05e7de63..4683038d 100644 --- a/frontend/locales/sk-SK.json +++ b/frontend/locales/sk-SK.json @@ -100,6 +100,8 @@ "item_photo": "Fotografia predmetu 📷", "item_quantity": "Počet predmetov", "parent_item": "Nadradená položka", + "product_tooltip_input_barcode": "Automaticky vyplniť ručne zadaným čiarovým kódom", + "product_tooltip_scan_barcode": "Automaticky vyplniť čiarovým kódom z 📷", "rotate_photo": "Otočiť fotografiu", "set_as_primary_photo": "Nastaviť ako { isPrimary, select, true {non-} false {} other {}}primárnu fotografiu", "title": "Vytvoriť položku", @@ -120,6 +122,15 @@ "upload_photos": "Nahrať fotografie", "uploaded": "Nahraná fotografia" }, + "product_import": { + "barcode": "Čiarový kód produktu", + "db_source": "Zdroj DB", + "error_exception": "Pri načítaní čiarového kódu došlo k výnimke: ", + "error_invalid_barcode": "Zadali ste neplatný čiarový kód", + "error_not_found": "Nenašiel sa žiaden produkt s uvedeným čiarovým kódom.", + "search_item": "Vyhľadať produkt", + "title": "Importovať produkt" + }, "selector": { "no_results": "Nenašli sa žiadne výsledky", "placeholder": "Vybrať…", @@ -184,6 +195,9 @@ "shortcut_hint": "Pomocou číselných tlačidiel rýchlo vyberte akciu." } }, + "errors": { + "api_failure": "Volanie backendového API skončilo s chybou: " + }, "global": { "add": "Pridať", "archived": "Archivované", @@ -277,6 +291,18 @@ "description": "Popis", "details": "Podrobnosti", "drag_and_drop": "Presuňte súbory sem alebo kliknutím vyberte súbory", + "duplicate": { + "copy_attachments": "Kopírovať prílohy", + "copy_custom_fields": "Kopírovať vlastné polia", + "copy_maintenance": "Kopírovať údržbu", + "custom_prefix": "Kopírovať predponu", + "enable_custom_prefix": "Povoliť vlastnú predponu", + "override_instructions": "Ak pri kliknutí na tlačidlo duplikovať stlačíte kláves Shift, toto nastavenie prepíšete.", + "prefix": "Kópia ", + "prefix_instructions": "Táto predpona bude pridaná na začiatok názvu duplikovanej položky. Na koniec predpony vložte medzeru, aby medzi predponou a názvom položky bola medzera.", + "temporary_title": "Dočasné nastavenia", + "title": "Duplikovať nastavenia" + }, "edit": { "edit_attachment_dialog": { "attachment_title": "Názov prílohy", @@ -285,7 +311,8 @@ "primary_photo_sub": "Táto možnosť je dostupná len pre fotografie. Primárna môže byť iba jedna fotografia. Ak vyberiete túto voľbu, aktuálna primárna fotografia (ak taká existuje) bude zrušená.", "select_type": "Vyberte typ", "title": "Upraviť prílohu" - } + }, + "view_image": "Zobraziť obrázok" }, "edit_details": "Upraviť podrobnosti", "field_selector": "Výber poľa", @@ -383,6 +410,7 @@ "update_label": "Aktualizovať štítok" }, "languages": { + "bs-BA": "Bosniačtina (Bosna a Hercegovina)", "ca": "Katalánsky", "cs-CZ": "Čeština", "de": "Nemecky", @@ -410,6 +438,7 @@ "th-TH": "Thaičina", "tr": "Turečtina", "uk-UA": "Ukrajinsky", + "vi-VN": "Vietnamčina", "zh-CN": "Čínština (zjednodušená)", "zh-HK": "Čínsky (Hong Kong) Name", "zh-MO": "Čínština (Macao)", @@ -559,6 +588,8 @@ } }, "scanner": { + "barcode_detected_message": "bol zistený čiarový kód produktu", + "barcode_fetch_data": "Načítať údaje produktu", "error": "Počas skenovania sa vyskytla chyba", "invalid_url": "Neplatná adresa URL čiarového kódu", "no_sources": "Nie sú k dispozícii žiadne zdroje videa", From 8f8dbf4a3a3506fa377cc94b28a11eed820cda06 Mon Sep 17 00:00:00 2001 From: Choong Jun Jin Date: Sun, 14 Sep 2025 00:51:54 +0900 Subject: [PATCH 02/19] Fea: add decimal support to currency system with ISO 4217 data integration (#976) * feat: add decimal support to currency system with ISO 4217 data integration * Harden currency formatting: add decimal bounds, input validation, and robust error handling * Fixed issues raised by coderrabitai * Fixed linting issue --- .github/scripts/update_currencies.py | 117 ++- .../internal/core/currencies/currencies.go | 38 +- .../internal/core/currencies/currencies.json | 804 ++++++++++++------ frontend/composables/use-formatters.ts | 11 + frontend/composables/utils.ts | 111 ++- frontend/lib/api/types/data-contracts.ts | 1 + frontend/pages/profile.vue | 17 +- 7 files changed, 816 insertions(+), 283 deletions(-) diff --git a/.github/scripts/update_currencies.py b/.github/scripts/update_currencies.py index b546fe41..f8747c8d 100644 --- a/.github/scripts/update_currencies.py +++ b/.github/scripts/update_currencies.py @@ -1,16 +1,33 @@ #!/usr/bin/env python3 +import csv +import io import json import logging +import os import sys from pathlib import Path import requests -from requests.adapters import HTTPAdapter, Retry +from requests.adapters import HTTPAdapter +from urllib3.util.retry import Retry API_URL = 'https://restcountries.com/v3.1/all?fields=name,common,currencies' +# Default to a pinned commit for supply-chain security +DEFAULT_ISO_4217_URL = 'https://raw.githubusercontent.com/datasets/currency-codes/052b3088938ba32028a14e75040c286c5e142145/data/codes-all.csv' +ISO_4217_URL = os.environ.get('ISO_4217_URL', DEFAULT_ISO_4217_URL) SAVE_PATH = Path('backend/internal/core/currencies/currencies.json') TIMEOUT = 10 # seconds +# Known currency decimal overrides +CURRENCY_DECIMAL_OVERRIDES = { + "BTC": 8, # Bitcoin uses 8 decimal places + "JPY": 0, # Japanese Yen has no decimal places + "BHD": 3, # Bahraini Dinar uses 3 decimal places +} +DEFAULT_DECIMALS = 2 +MIN_DECIMALS = 0 +MAX_DECIMALS = 6 + def setup_logging(): logging.basicConfig( @@ -19,7 +36,93 @@ def setup_logging(): ) +def get_currency_decimals(code, iso_data): + """ + Get the decimal places for a currency code. + Checks overrides first, then ISO data, then uses default. + Clamps result to safe range [MIN_DECIMALS, MAX_DECIMALS]. + """ + # Normalize the input code + normalized_code = (code or "").strip().upper() + + # First check overrides + if normalized_code in CURRENCY_DECIMAL_OVERRIDES: + decimals = CURRENCY_DECIMAL_OVERRIDES[normalized_code] + # Then check ISO data + elif normalized_code in iso_data: + decimals = iso_data[normalized_code] + # Finally use default + else: + decimals = DEFAULT_DECIMALS + + # Ensure it's an integer and clamp to safe range + try: + decimals = int(decimals) + except (ValueError, TypeError): + decimals = DEFAULT_DECIMALS + + return max(MIN_DECIMALS, min(MAX_DECIMALS, decimals)) + + +def fetch_iso_4217_data(): + """ + Fetch ISO 4217 currency data to get minor units (decimal places). + Returns a dict mapping currency code to minor units. + """ + # Log the resolved URL for transparency + logging.info("Fetching ISO 4217 data from: %s", ISO_4217_URL) + if not ISO_4217_URL.lower().startswith("https://"): + logging.error("Refusing non-HTTPS ISO_4217_URL: %s", ISO_4217_URL) + return {} + + session = requests.Session() + retries = Retry( + total=3, + backoff_factor=1, + status_forcelist=[429, 500, 502, 503, 504], + allowed_methods=frozenset(['GET']) + ) + session.mount('https://', HTTPAdapter(max_retries=retries)) + + try: + # Add Accept header for CSV content + headers = {'Accept': 'text/csv'} + resp = session.get(ISO_4217_URL, timeout=TIMEOUT, headers=headers) + resp.raise_for_status() + except requests.exceptions.RequestException as e: + logging.error("Failed to fetch ISO 4217 data: %s", e) + return {} + + # Parse CSV data + iso_data = {} + try: + # Decode with utf-8-sig to strip BOM if present + csv_content = resp.content.decode('utf-8-sig') + csv_reader = csv.DictReader(io.StringIO(csv_content)) + + for row in csv_reader: + code = row.get('AlphabeticCode', '').strip() + minor_unit = row.get('MinorUnit', '').strip() + + if code and minor_unit != 'N.A.': + try: + # Convert minor unit to int (decimal places) + iso_data[code] = int(minor_unit) if minor_unit.isdigit() else 2 + except (ValueError, TypeError): + iso_data[code] = 2 # Default to 2 if parsing fails + + logging.info("Successfully loaded decimal data for %d currencies from ISO 4217", len(iso_data)) + return iso_data + + except Exception as e: + logging.error("Failed to parse ISO 4217 CSV data: %s", e) + return {} + + def fetch_currencies(): + # First, fetch ISO 4217 data for decimal places + iso_data = fetch_iso_4217_data() + session = requests.Session() retries = Retry( total=3, @@ -46,11 +149,15 @@ def fetch_currencies(): for country in countries: country_name = country.get('name', {}).get('common') or "Unknown" for code, info in country.get('currencies', {}).items(): + # Get decimal places using the helper function + decimals = get_currency_decimals(code, iso_data) + results.append({ - 'code': code, - 'local': country_name, - 'symbol': info.get('symbol', ''), - 'name': info.get('name', '') + 'code': code, + 'local': country_name, + 'symbol': info.get('symbol', ''), + 'name': info.get('name', ''), + 'decimals': decimals }) # sort by country name for consistency diff --git a/backend/internal/core/currencies/currencies.go b/backend/internal/core/currencies/currencies.go index 4cc87669..becd6812 100644 --- a/backend/internal/core/currencies/currencies.go +++ b/backend/internal/core/currencies/currencies.go @@ -7,6 +7,7 @@ import ( _ "embed" "encoding/json" "io" + "log" "slices" "strings" "sync" @@ -15,6 +16,24 @@ import ( //go:embed currencies.json var defaults []byte +const ( + MinDecimals = 0 + MaxDecimals = 18 +) + +// clampDecimals ensures the decimals value is within a safe range [0, 18] +func clampDecimals(decimals int, code string) int { + original := decimals + if decimals < MinDecimals { + decimals = MinDecimals + log.Printf("WARNING: Currency %s had negative decimals (%d), normalized to %d", code, original, decimals) + } else if decimals > MaxDecimals { + decimals = MaxDecimals + log.Printf("WARNING: Currency %s had excessive decimals (%d), normalized to %d", code, original, decimals) + } + return decimals +} + type CollectorFunc func() ([]Currency, error) func CollectJSON(reader io.Reader) CollectorFunc { @@ -25,6 +44,11 @@ func CollectJSON(reader io.Reader) CollectorFunc { return nil, err } + // Clamp decimals during collection to ensure early normalization + for i := range currencies { + currencies[i].Decimals = clampDecimals(currencies[i].Decimals, currencies[i].Code) + } + return currencies, nil } } @@ -48,10 +72,11 @@ func CollectionCurrencies(collectors ...CollectorFunc) ([]Currency, error) { } type Currency struct { - Name string `json:"name"` - Code string `json:"code"` - Local string `json:"local"` - Symbol string `json:"symbol"` + Name string `json:"name"` + Code string `json:"code"` + Local string `json:"local"` + Symbol string `json:"symbol"` + Decimals int `json:"decimals"` } type CurrencyRegistry struct { @@ -62,7 +87,10 @@ type CurrencyRegistry struct { func NewCurrencyService(currencies []Currency) *CurrencyRegistry { registry := make(map[string]Currency, len(currencies)) for i := range currencies { - registry[currencies[i].Code] = currencies[i] + // Clamp decimals to safe range before adding to registry + currency := currencies[i] + currency.Decimals = clampDecimals(currency.Decimals, currency.Code) + registry[currency.Code] = currency } return &CurrencyRegistry{ diff --git a/backend/internal/core/currencies/currencies.json b/backend/internal/core/currencies/currencies.json index 266eb5ff..d1123e43 100644 --- a/backend/internal/core/currencies/currencies.json +++ b/backend/internal/core/currencies/currencies.json @@ -3,1608 +3,1876 @@ "code": "AFN", "local": "Afghanistan", "symbol": "؋", - "name": "Afghan afghani" + "name": "Afghan afghani", + "decimals": 2 }, { "code": "ALL", "local": "Albania", "symbol": "L", - "name": "Albanian lek" + "name": "Albanian lek", + "decimals": 2 }, { "code": "DZD", "local": "Algeria", "symbol": "د.ج", - "name": "Algerian dinar" + "name": "Algerian dinar", + "decimals": 2 }, { "code": "USD", "local": "American Samoa", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "EUR", "local": "Andorra", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "AOA", "local": "Angola", "symbol": "Kz", - "name": "Angolan kwanza" + "name": "Angolan kwanza", + "decimals": 2 }, { "code": "XCD", "local": "Anguilla", "symbol": "$", - "name": "Eastern Caribbean dollar" + "name": "Eastern Caribbean dollar", + "decimals": 2 }, { "code": "XCD", "local": "Antigua and Barbuda", "symbol": "$", - "name": "Eastern Caribbean dollar" + "name": "Eastern Caribbean dollar", + "decimals": 2 }, { "code": "ARS", "local": "Argentina", "symbol": "$", - "name": "Argentine peso" + "name": "Argentine peso", + "decimals": 2 }, { "code": "AMD", "local": "Armenia", "symbol": "֏", - "name": "Armenian dram" + "name": "Armenian dram", + "decimals": 2 }, { "code": "AWG", "local": "Aruba", "symbol": "ƒ", - "name": "Aruban florin" + "name": "Aruban florin", + "decimals": 2 }, { "code": "AUD", "local": "Australia", "symbol": "$", - "name": "Australian dollar" + "name": "Australian dollar", + "decimals": 2 }, { "code": "EUR", "local": "Austria", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "AZN", "local": "Azerbaijan", "symbol": "₼", - "name": "Azerbaijani manat" + "name": "Azerbaijani manat", + "decimals": 2 }, { "code": "BSD", "local": "Bahamas", "symbol": "$", - "name": "Bahamian dollar" + "name": "Bahamian dollar", + "decimals": 2 }, { "code": "USD", "local": "Bahamas", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "BHD", "local": "Bahrain", "symbol": ".د.ب", - "name": "Bahraini dinar" + "name": "Bahraini dinar", + "decimals": 3 }, { "code": "BDT", "local": "Bangladesh", "symbol": "৳", - "name": "Bangladeshi taka" + "name": "Bangladeshi taka", + "decimals": 2 }, { "code": "BBD", "local": "Barbados", "symbol": "$", - "name": "Barbadian dollar" + "name": "Barbadian dollar", + "decimals": 2 }, { "code": "BYN", "local": "Belarus", "symbol": "Br", - "name": "Belarusian ruble" + "name": "Belarusian ruble", + "decimals": 2 }, { "code": "EUR", "local": "Belgium", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "BZD", "local": "Belize", "symbol": "$", - "name": "Belize dollar" + "name": "Belize dollar", + "decimals": 2 }, { "code": "XOF", "local": "Benin", "symbol": "Fr", - "name": "West African CFA franc" + "name": "West African CFA franc", + "decimals": 0 }, { "code": "BMD", "local": "Bermuda", "symbol": "$", - "name": "Bermudian dollar" + "name": "Bermudian dollar", + "decimals": 2 }, { "code": "BTN", "local": "Bhutan", "symbol": "Nu.", - "name": "Bhutanese ngultrum" + "name": "Bhutanese ngultrum", + "decimals": 2 }, { "code": "INR", "local": "Bhutan", "symbol": "₹", - "name": "Indian rupee" + "name": "Indian rupee", + "decimals": 2 }, { "code": "BOB", "local": "Bolivia", "symbol": "Bs.", - "name": "Bolivian boliviano" + "name": "Bolivian boliviano", + "decimals": 2 }, { "code": "BAM", "local": "Bosnia and Herzegovina", "symbol": "KM", - "name": "Bosnia and Herzegovina convertible mark" + "name": "Bosnia and Herzegovina convertible mark", + "decimals": 2 }, { "code": "BWP", "local": "Botswana", "symbol": "P", - "name": "Botswana pula" + "name": "Botswana pula", + "decimals": 2 }, { "code": "BRL", "local": "Brazil", "symbol": "R$", - "name": "Brazilian real" + "name": "Brazilian real", + "decimals": 2 }, { "code": "USD", "local": "British Indian Ocean Territory", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "USD", "local": "British Virgin Islands", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "BND", "local": "Brunei", "symbol": "$", - "name": "Brunei dollar" + "name": "Brunei dollar", + "decimals": 2 }, { "code": "SGD", "local": "Brunei", "symbol": "$", - "name": "Singapore dollar" + "name": "Singapore dollar", + "decimals": 2 }, { "code": "BGN", "local": "Bulgaria", "symbol": "лв", - "name": "Bulgarian lev" + "name": "Bulgarian lev", + "decimals": 2 }, { "code": "XOF", "local": "Burkina Faso", "symbol": "Fr", - "name": "West African CFA franc" + "name": "West African CFA franc", + "decimals": 0 }, { "code": "BIF", "local": "Burundi", "symbol": "Fr", - "name": "Burundian franc" + "name": "Burundian franc", + "decimals": 0 }, { "code": "KHR", "local": "Cambodia", "symbol": "៛", - "name": "Cambodian riel" + "name": "Cambodian riel", + "decimals": 2 }, { "code": "USD", "local": "Cambodia", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "XAF", "local": "Cameroon", "symbol": "Fr", - "name": "Central African CFA franc" + "name": "Central African CFA franc", + "decimals": 0 }, { "code": "CAD", "local": "Canada", "symbol": "$", - "name": "Canadian dollar" + "name": "Canadian dollar", + "decimals": 2 }, { "code": "CVE", "local": "Cape Verde", "symbol": "Esc", - "name": "Cape Verdean escudo" + "name": "Cape Verdean escudo", + "decimals": 2 }, { "code": "USD", "local": "Caribbean Netherlands", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "KYD", "local": "Cayman Islands", "symbol": "$", - "name": "Cayman Islands dollar" + "name": "Cayman Islands dollar", + "decimals": 2 }, { "code": "XAF", "local": "Central African Republic", "symbol": "Fr", - "name": "Central African CFA franc" + "name": "Central African CFA franc", + "decimals": 0 }, { "code": "XAF", "local": "Chad", "symbol": "Fr", - "name": "Central African CFA franc" + "name": "Central African CFA franc", + "decimals": 0 }, { "code": "CLP", "local": "Chile", "symbol": "$", - "name": "Chilean peso" + "name": "Chilean peso", + "decimals": 0 }, { "code": "CNY", "local": "China", "symbol": "¥", - "name": "Chinese yuan" + "name": "Chinese yuan", + "decimals": 2 }, { "code": "AUD", "local": "Christmas Island", "symbol": "$", - "name": "Australian dollar" + "name": "Australian dollar", + "decimals": 2 }, { "code": "AUD", "local": "Cocos (Keeling) Islands", "symbol": "$", - "name": "Australian dollar" + "name": "Australian dollar", + "decimals": 2 }, { "code": "COP", "local": "Colombia", "symbol": "$", - "name": "Colombian peso" + "name": "Colombian peso", + "decimals": 2 }, { "code": "KMF", "local": "Comoros", "symbol": "Fr", - "name": "Comorian franc" + "name": "Comorian franc", + "decimals": 0 }, { "code": "CKD", "local": "Cook Islands", "symbol": "$", - "name": "Cook Islands dollar" + "name": "Cook Islands dollar", + "decimals": 2 }, { "code": "NZD", "local": "Cook Islands", "symbol": "$", - "name": "New Zealand dollar" + "name": "New Zealand dollar", + "decimals": 2 }, { "code": "CRC", "local": "Costa Rica", "symbol": "₡", - "name": "Costa Rican colón" + "name": "Costa Rican colón", + "decimals": 2 }, { "code": "EUR", "local": "Croatia", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "CUC", "local": "Cuba", "symbol": "$", - "name": "Cuban convertible peso" + "name": "Cuban convertible peso", + "decimals": 2 }, { "code": "CUP", "local": "Cuba", "symbol": "$", - "name": "Cuban peso" + "name": "Cuban peso", + "decimals": 2 }, { "code": "ANG", "local": "Curaçao", "symbol": "ƒ", - "name": "Netherlands Antillean guilder" + "name": "Netherlands Antillean guilder", + "decimals": 2 }, { "code": "EUR", "local": "Cyprus", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "CZK", "local": "Czechia", "symbol": "Kč", - "name": "Czech koruna" + "name": "Czech koruna", + "decimals": 2 }, { "code": "DKK", "local": "Denmark", "symbol": "kr", - "name": "Danish krone" + "name": "Danish krone", + "decimals": 2 }, { "code": "DJF", "local": "Djibouti", "symbol": "Fr", - "name": "Djiboutian franc" + "name": "Djiboutian franc", + "decimals": 0 }, { "code": "XCD", "local": "Dominica", "symbol": "$", - "name": "Eastern Caribbean dollar" + "name": "Eastern Caribbean dollar", + "decimals": 2 }, { "code": "DOP", "local": "Dominican Republic", "symbol": "$", - "name": "Dominican peso" + "name": "Dominican peso", + "decimals": 2 }, { "code": "CDF", "local": "DR Congo", "symbol": "FC", - "name": "Congolese franc" + "name": "Congolese franc", + "decimals": 2 }, { "code": "USD", "local": "Ecuador", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "EGP", "local": "Egypt", "symbol": "£", - "name": "Egyptian pound" + "name": "Egyptian pound", + "decimals": 2 }, { "code": "USD", "local": "El Salvador", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "XAF", "local": "Equatorial Guinea", "symbol": "Fr", - "name": "Central African CFA franc" + "name": "Central African CFA franc", + "decimals": 0 }, { "code": "ERN", "local": "Eritrea", "symbol": "Nfk", - "name": "Eritrean nakfa" + "name": "Eritrean nakfa", + "decimals": 2 }, { "code": "EUR", "local": "Estonia", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "SZL", "local": "Eswatini", "symbol": "L", - "name": "Swazi lilangeni" + "name": "Swazi lilangeni", + "decimals": 2 }, { "code": "ZAR", "local": "Eswatini", "symbol": "R", - "name": "South African rand" + "name": "South African rand", + "decimals": 2 }, { "code": "ETB", "local": "Ethiopia", "symbol": "Br", - "name": "Ethiopian birr" + "name": "Ethiopian birr", + "decimals": 2 }, { "code": "FKP", "local": "Falkland Islands", "symbol": "£", - "name": "Falkland Islands pound" + "name": "Falkland Islands pound", + "decimals": 2 }, { "code": "DKK", "local": "Faroe Islands", "symbol": "kr", - "name": "Danish krone" + "name": "Danish krone", + "decimals": 2 }, { "code": "FOK", "local": "Faroe Islands", "symbol": "kr", - "name": "Faroese króna" + "name": "Faroese króna", + "decimals": 2 }, { "code": "FJD", "local": "Fiji", "symbol": "$", - "name": "Fijian dollar" + "name": "Fijian dollar", + "decimals": 2 }, { "code": "EUR", "local": "Finland", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "EUR", "local": "France", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "EUR", "local": "French Guiana", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "XPF", "local": "French Polynesia", "symbol": "₣", - "name": "CFP franc" + "name": "CFP franc", + "decimals": 0 }, { "code": "EUR", "local": "French Southern and Antarctic Lands", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "XAF", "local": "Gabon", "symbol": "Fr", - "name": "Central African CFA franc" + "name": "Central African CFA franc", + "decimals": 0 }, { "code": "GMD", "local": "Gambia", "symbol": "D", - "name": "dalasi" + "name": "dalasi", + "decimals": 2 }, { "code": "GEL", "local": "Georgia", "symbol": "₾", - "name": "lari" + "name": "lari", + "decimals": 2 }, { "code": "EUR", "local": "Germany", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "GHS", "local": "Ghana", "symbol": "₵", - "name": "Ghanaian cedi" + "name": "Ghanaian cedi", + "decimals": 2 }, { "code": "GIP", "local": "Gibraltar", "symbol": "£", - "name": "Gibraltar pound" + "name": "Gibraltar pound", + "decimals": 2 }, { "code": "EUR", "local": "Greece", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "DKK", "local": "Greenland", "symbol": "kr.", - "name": "krone" + "name": "krone", + "decimals": 2 }, { "code": "XCD", "local": "Grenada", "symbol": "$", - "name": "Eastern Caribbean dollar" + "name": "Eastern Caribbean dollar", + "decimals": 2 }, { "code": "EUR", "local": "Guadeloupe", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "USD", "local": "Guam", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "GTQ", "local": "Guatemala", "symbol": "Q", - "name": "Guatemalan quetzal" + "name": "Guatemalan quetzal", + "decimals": 2 }, { "code": "GBP", "local": "Guernsey", "symbol": "£", - "name": "British pound" + "name": "British pound", + "decimals": 2 }, { "code": "GGP", "local": "Guernsey", "symbol": "£", - "name": "Guernsey pound" + "name": "Guernsey pound", + "decimals": 2 }, { "code": "GNF", "local": "Guinea", "symbol": "Fr", - "name": "Guinean franc" + "name": "Guinean franc", + "decimals": 0 }, { "code": "XOF", "local": "Guinea-Bissau", "symbol": "Fr", - "name": "West African CFA franc" + "name": "West African CFA franc", + "decimals": 0 }, { "code": "GYD", "local": "Guyana", "symbol": "$", - "name": "Guyanese dollar" + "name": "Guyanese dollar", + "decimals": 2 }, { "code": "HTG", "local": "Haiti", "symbol": "G", - "name": "Haitian gourde" + "name": "Haitian gourde", + "decimals": 2 }, { "code": "HNL", "local": "Honduras", "symbol": "L", - "name": "Honduran lempira" + "name": "Honduran lempira", + "decimals": 2 }, { "code": "HKD", "local": "Hong Kong", "symbol": "$", - "name": "Hong Kong dollar" + "name": "Hong Kong dollar", + "decimals": 2 }, { "code": "HUF", "local": "Hungary", "symbol": "Ft", - "name": "Hungarian forint" + "name": "Hungarian forint", + "decimals": 2 }, { "code": "ISK", "local": "Iceland", "symbol": "kr", - "name": "Icelandic króna" + "name": "Icelandic króna", + "decimals": 0 }, { "code": "INR", "local": "India", "symbol": "₹", - "name": "Indian rupee" + "name": "Indian rupee", + "decimals": 2 }, { "code": "IDR", "local": "Indonesia", "symbol": "Rp", - "name": "Indonesian rupiah" + "name": "Indonesian rupiah", + "decimals": 2 }, { "code": "IRR", "local": "Iran", "symbol": "﷼", - "name": "Iranian rial" + "name": "Iranian rial", + "decimals": 2 }, { "code": "IQD", "local": "Iraq", "symbol": "ع.د", - "name": "Iraqi dinar" + "name": "Iraqi dinar", + "decimals": 3 }, { "code": "EUR", "local": "Ireland", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "GBP", "local": "Isle of Man", "symbol": "£", - "name": "British pound" + "name": "British pound", + "decimals": 2 }, { "code": "IMP", "local": "Isle of Man", "symbol": "£", - "name": "Manx pound" + "name": "Manx pound", + "decimals": 2 }, { "code": "ILS", "local": "Israel", "symbol": "₪", - "name": "Israeli new shekel" + "name": "Israeli new shekel", + "decimals": 2 }, { "code": "EUR", "local": "Italy", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "XOF", "local": "Ivory Coast", "symbol": "Fr", - "name": "West African CFA franc" + "name": "West African CFA franc", + "decimals": 0 }, { "code": "JMD", "local": "Jamaica", "symbol": "$", - "name": "Jamaican dollar" + "name": "Jamaican dollar", + "decimals": 2 }, { "code": "JPY", "local": "Japan", "symbol": "¥", - "name": "Japanese yen" + "name": "Japanese yen", + "decimals": 0 }, { "code": "GBP", "local": "Jersey", "symbol": "£", - "name": "British pound" + "name": "British pound", + "decimals": 2 }, { "code": "JEP", "local": "Jersey", "symbol": "£", - "name": "Jersey pound" + "name": "Jersey pound", + "decimals": 2 }, { "code": "JOD", "local": "Jordan", "symbol": "د.ا", - "name": "Jordanian dinar" + "name": "Jordanian dinar", + "decimals": 3 }, { "code": "KZT", "local": "Kazakhstan", "symbol": "₸", - "name": "Kazakhstani tenge" + "name": "Kazakhstani tenge", + "decimals": 2 }, { "code": "KES", "local": "Kenya", "symbol": "Sh", - "name": "Kenyan shilling" + "name": "Kenyan shilling", + "decimals": 2 }, { "code": "AUD", "local": "Kiribati", "symbol": "$", - "name": "Australian dollar" + "name": "Australian dollar", + "decimals": 2 }, { "code": "KID", "local": "Kiribati", "symbol": "$", - "name": "Kiribati dollar" + "name": "Kiribati dollar", + "decimals": 2 }, { "code": "EUR", "local": "Kosovo", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "KWD", "local": "Kuwait", "symbol": "د.ك", - "name": "Kuwaiti dinar" + "name": "Kuwaiti dinar", + "decimals": 3 }, { "code": "KGS", "local": "Kyrgyzstan", "symbol": "с", - "name": "Kyrgyzstani som" + "name": "Kyrgyzstani som", + "decimals": 2 }, { "code": "LAK", "local": "Laos", "symbol": "₭", - "name": "Lao kip" + "name": "Lao kip", + "decimals": 2 }, { "code": "EUR", "local": "Latvia", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "LBP", "local": "Lebanon", "symbol": "ل.ل", - "name": "Lebanese pound" + "name": "Lebanese pound", + "decimals": 2 }, { "code": "LSL", "local": "Lesotho", "symbol": "L", - "name": "Lesotho loti" + "name": "Lesotho loti", + "decimals": 2 }, { "code": "ZAR", "local": "Lesotho", "symbol": "R", - "name": "South African rand" + "name": "South African rand", + "decimals": 2 }, { "code": "LRD", "local": "Liberia", "symbol": "$", - "name": "Liberian dollar" + "name": "Liberian dollar", + "decimals": 2 }, { "code": "LYD", "local": "Libya", "symbol": "ل.د", - "name": "Libyan dinar" + "name": "Libyan dinar", + "decimals": 3 }, { "code": "CHF", "local": "Liechtenstein", "symbol": "Fr", - "name": "Swiss franc" + "name": "Swiss franc", + "decimals": 2 }, { "code": "EUR", "local": "Lithuania", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "EUR", "local": "Luxembourg", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "MOP", "local": "Macau", "symbol": "P", - "name": "Macanese pataca" + "name": "Macanese pataca", + "decimals": 2 }, { "code": "MGA", "local": "Madagascar", "symbol": "Ar", - "name": "Malagasy ariary" + "name": "Malagasy ariary", + "decimals": 2 }, { "code": "MWK", "local": "Malawi", "symbol": "MK", - "name": "Malawian kwacha" + "name": "Malawian kwacha", + "decimals": 2 }, { "code": "MYR", "local": "Malaysia", "symbol": "RM", - "name": "Malaysian ringgit" + "name": "Malaysian ringgit", + "decimals": 2 }, { "code": "MVR", "local": "Maldives", "symbol": ".ރ", - "name": "Maldivian rufiyaa" + "name": "Maldivian rufiyaa", + "decimals": 2 }, { "code": "XOF", "local": "Mali", "symbol": "Fr", - "name": "West African CFA franc" + "name": "West African CFA franc", + "decimals": 0 }, { "code": "EUR", "local": "Malta", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "USD", "local": "Marshall Islands", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "EUR", "local": "Martinique", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "MRU", "local": "Mauritania", "symbol": "UM", - "name": "Mauritanian ouguiya" + "name": "Mauritanian ouguiya", + "decimals": 2 }, { "code": "MUR", "local": "Mauritius", "symbol": "₨", - "name": "Mauritian rupee" + "name": "Mauritian rupee", + "decimals": 2 }, { "code": "EUR", "local": "Mayotte", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "MXN", "local": "Mexico", "symbol": "$", - "name": "Mexican peso" + "name": "Mexican peso", + "decimals": 2 }, { "code": "USD", "local": "Micronesia", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "MDL", "local": "Moldova", "symbol": "L", - "name": "Moldovan leu" + "name": "Moldovan leu", + "decimals": 2 }, { "code": "EUR", "local": "Monaco", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "MNT", "local": "Mongolia", "symbol": "₮", - "name": "Mongolian tögrög" + "name": "Mongolian tögrög", + "decimals": 2 }, { "code": "EUR", "local": "Montenegro", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "XCD", "local": "Montserrat", "symbol": "$", - "name": "Eastern Caribbean dollar" + "name": "Eastern Caribbean dollar", + "decimals": 2 }, { "code": "MAD", "local": "Morocco", "symbol": "د.م.", - "name": "Moroccan dirham" + "name": "Moroccan dirham", + "decimals": 2 }, { "code": "MZN", "local": "Mozambique", "symbol": "MT", - "name": "Mozambican metical" + "name": "Mozambican metical", + "decimals": 2 }, { "code": "MMK", "local": "Myanmar", "symbol": "Ks", - "name": "Burmese kyat" + "name": "Burmese kyat", + "decimals": 2 }, { "code": "NAD", "local": "Namibia", "symbol": "$", - "name": "Namibian dollar" + "name": "Namibian dollar", + "decimals": 2 }, { "code": "ZAR", "local": "Namibia", "symbol": "R", - "name": "South African rand" + "name": "South African rand", + "decimals": 2 }, { "code": "AUD", "local": "Nauru", "symbol": "$", - "name": "Australian dollar" + "name": "Australian dollar", + "decimals": 2 }, { "code": "NPR", "local": "Nepal", "symbol": "₨", - "name": "Nepalese rupee" + "name": "Nepalese rupee", + "decimals": 2 }, { "code": "EUR", "local": "Netherlands", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "XPF", "local": "New Caledonia", "symbol": "₣", - "name": "CFP franc" + "name": "CFP franc", + "decimals": 0 }, { "code": "NZD", "local": "New Zealand", "symbol": "$", - "name": "New Zealand dollar" + "name": "New Zealand dollar", + "decimals": 2 }, { "code": "NIO", "local": "Nicaragua", "symbol": "C$", - "name": "Nicaraguan córdoba" + "name": "Nicaraguan córdoba", + "decimals": 2 }, { "code": "XOF", "local": "Niger", "symbol": "Fr", - "name": "West African CFA franc" + "name": "West African CFA franc", + "decimals": 0 }, { "code": "NGN", "local": "Nigeria", "symbol": "₦", - "name": "Nigerian naira" + "name": "Nigerian naira", + "decimals": 2 }, { "code": "NZD", "local": "Niue", "symbol": "$", - "name": "New Zealand dollar" + "name": "New Zealand dollar", + "decimals": 2 }, { "code": "AUD", "local": "Norfolk Island", "symbol": "$", - "name": "Australian dollar" + "name": "Australian dollar", + "decimals": 2 }, { "code": "KPW", "local": "North Korea", "symbol": "₩", - "name": "North Korean won" + "name": "North Korean won", + "decimals": 2 }, { "code": "MKD", "local": "North Macedonia", "symbol": "den", - "name": "denar" + "name": "denar", + "decimals": 2 }, { "code": "USD", "local": "Northern Mariana Islands", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "NOK", "local": "Norway", "symbol": "kr", - "name": "Norwegian krone" + "name": "Norwegian krone", + "decimals": 2 }, { "code": "OMR", "local": "Oman", "symbol": "ر.ع.", - "name": "Omani rial" + "name": "Omani rial", + "decimals": 3 }, { "code": "PKR", "local": "Pakistan", "symbol": "₨", - "name": "Pakistani rupee" + "name": "Pakistani rupee", + "decimals": 2 }, { "code": "USD", "local": "Palau", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "EGP", "local": "Palestine", "symbol": "E£", - "name": "Egyptian pound" + "name": "Egyptian pound", + "decimals": 2 }, { "code": "ILS", "local": "Palestine", "symbol": "₪", - "name": "Israeli new shekel" + "name": "Israeli new shekel", + "decimals": 2 }, { "code": "JOD", "local": "Palestine", "symbol": "JD", - "name": "Jordanian dinar" + "name": "Jordanian dinar", + "decimals": 3 }, { "code": "PAB", "local": "Panama", "symbol": "B/.", - "name": "Panamanian balboa" + "name": "Panamanian balboa", + "decimals": 2 }, { "code": "USD", "local": "Panama", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "PGK", "local": "Papua New Guinea", "symbol": "K", - "name": "Papua New Guinean kina" + "name": "Papua New Guinean kina", + "decimals": 2 }, { "code": "PYG", "local": "Paraguay", "symbol": "₲", - "name": "Paraguayan guaraní" + "name": "Paraguayan guaraní", + "decimals": 0 }, { "code": "PEN", "local": "Peru", "symbol": "S/ ", - "name": "Peruvian sol" + "name": "Peruvian sol", + "decimals": 2 }, { "code": "PHP", "local": "Philippines", "symbol": "₱", - "name": "Philippine peso" + "name": "Philippine peso", + "decimals": 2 }, { "code": "NZD", "local": "Pitcairn Islands", "symbol": "$", - "name": "New Zealand dollar" + "name": "New Zealand dollar", + "decimals": 2 }, { "code": "PLN", "local": "Poland", "symbol": "zł", - "name": "Polish złoty" + "name": "Polish złoty", + "decimals": 2 }, { "code": "EUR", "local": "Portugal", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "USD", "local": "Puerto Rico", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "QAR", "local": "Qatar", "symbol": "ر.ق", - "name": "Qatari riyal" + "name": "Qatari riyal", + "decimals": 2 }, { "code": "XAF", "local": "Republic of the Congo", "symbol": "Fr", - "name": "Central African CFA franc" + "name": "Central African CFA franc", + "decimals": 0 }, { "code": "RON", "local": "Romania", "symbol": "lei", - "name": "Romanian leu" + "name": "Romanian leu", + "decimals": 2 }, { "code": "RUB", "local": "Russia", "symbol": "₽", - "name": "Russian ruble" + "name": "Russian ruble", + "decimals": 2 }, { "code": "RWF", "local": "Rwanda", "symbol": "Fr", - "name": "Rwandan franc" + "name": "Rwandan franc", + "decimals": 0 }, { "code": "EUR", "local": "Réunion", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "EUR", "local": "Saint Barthélemy", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "GBP", "local": "Saint Helena, Ascension and Tristan da Cunha", "symbol": "£", - "name": "Pound sterling" + "name": "Pound sterling", + "decimals": 2 }, { "code": "SHP", "local": "Saint Helena, Ascension and Tristan da Cunha", "symbol": "£", - "name": "Saint Helena pound" + "name": "Saint Helena pound", + "decimals": 2 }, { "code": "XCD", "local": "Saint Kitts and Nevis", "symbol": "$", - "name": "Eastern Caribbean dollar" + "name": "Eastern Caribbean dollar", + "decimals": 2 }, { "code": "XCD", "local": "Saint Lucia", "symbol": "$", - "name": "Eastern Caribbean dollar" + "name": "Eastern Caribbean dollar", + "decimals": 2 }, { "code": "EUR", "local": "Saint Martin", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "EUR", "local": "Saint Pierre and Miquelon", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "XCD", "local": "Saint Vincent and the Grenadines", "symbol": "$", - "name": "Eastern Caribbean dollar" + "name": "Eastern Caribbean dollar", + "decimals": 2 }, { "code": "WST", "local": "Samoa", "symbol": "T", - "name": "Samoan tālā" + "name": "Samoan tālā", + "decimals": 2 }, { "code": "EUR", "local": "San Marino", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "SAR", "local": "Saudi Arabia", "symbol": "ر.س", - "name": "Saudi riyal" + "name": "Saudi riyal", + "decimals": 2 }, { "code": "XOF", "local": "Senegal", "symbol": "Fr", - "name": "West African CFA franc" + "name": "West African CFA franc", + "decimals": 0 }, { "code": "RSD", "local": "Serbia", "symbol": "дин.", - "name": "Serbian dinar" + "name": "Serbian dinar", + "decimals": 2 }, { "code": "SCR", "local": "Seychelles", "symbol": "₨", - "name": "Seychellois rupee" + "name": "Seychellois rupee", + "decimals": 2 }, { "code": "SLE", "local": "Sierra Leone", "symbol": "Le", - "name": "Leone" + "name": "Leone", + "decimals": 2 }, { "code": "SGD", "local": "Singapore", "symbol": "$", - "name": "Singapore dollar" + "name": "Singapore dollar", + "decimals": 2 }, { "code": "ANG", "local": "Sint Maarten", "symbol": "ƒ", - "name": "Netherlands Antillean guilder" + "name": "Netherlands Antillean guilder", + "decimals": 2 }, { "code": "EUR", "local": "Slovakia", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "EUR", "local": "Slovenia", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "SBD", "local": "Solomon Islands", "symbol": "$", - "name": "Solomon Islands dollar" + "name": "Solomon Islands dollar", + "decimals": 2 }, { "code": "SOS", "local": "Somalia", "symbol": "Sh", - "name": "Somali shilling" + "name": "Somali shilling", + "decimals": 2 }, { "code": "ZAR", "local": "South Africa", "symbol": "R", - "name": "South African rand" + "name": "South African rand", + "decimals": 2 }, { "code": "GBP", "local": "South Georgia", "symbol": "£", - "name": "British pound" + "name": "British pound", + "decimals": 2 }, { "code": "KRW", "local": "South Korea", "symbol": "₩", - "name": "South Korean won" + "name": "South Korean won", + "decimals": 0 }, { "code": "SSP", "local": "South Sudan", "symbol": "£", - "name": "South Sudanese pound" + "name": "South Sudanese pound", + "decimals": 2 }, { "code": "EUR", "local": "Spain", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "LKR", "local": "Sri Lanka", "symbol": "Rs රු", - "name": "Sri Lankan rupee" + "name": "Sri Lankan rupee", + "decimals": 2 }, { "code": "SDG", "local": "Sudan", "symbol": "ج.س", - "name": "Sudanese pound" + "name": "Sudanese pound", + "decimals": 2 }, { "code": "SRD", "local": "Suriname", "symbol": "$", - "name": "Surinamese dollar" + "name": "Surinamese dollar", + "decimals": 2 }, { "code": "NOK", "local": "Svalbard and Jan Mayen", "symbol": "kr", - "name": "krone" + "name": "krone", + "decimals": 2 }, { "code": "SEK", "local": "Sweden", "symbol": "kr", - "name": "Swedish krona" + "name": "Swedish krona", + "decimals": 2 }, { "code": "CHF", "local": "Switzerland", "symbol": "Fr.", - "name": "Swiss franc" + "name": "Swiss franc", + "decimals": 2 }, { "code": "SYP", "local": "Syria", "symbol": "£", - "name": "Syrian pound" + "name": "Syrian pound", + "decimals": 2 }, { "code": "STN", "local": "São Tomé and Príncipe", "symbol": "Db", - "name": "São Tomé and Príncipe dobra" + "name": "São Tomé and Príncipe dobra", + "decimals": 2 }, { "code": "TWD", "local": "Taiwan", "symbol": "$", - "name": "New Taiwan dollar" + "name": "New Taiwan dollar", + "decimals": 2 }, { "code": "TJS", "local": "Tajikistan", "symbol": "ЅМ", - "name": "Tajikistani somoni" + "name": "Tajikistani somoni", + "decimals": 2 }, { "code": "TZS", "local": "Tanzania", "symbol": "Sh", - "name": "Tanzanian shilling" + "name": "Tanzanian shilling", + "decimals": 2 }, { "code": "THB", "local": "Thailand", "symbol": "฿", - "name": "Thai baht" + "name": "Thai baht", + "decimals": 2 }, { "code": "USD", "local": "Timor-Leste", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "XOF", "local": "Togo", "symbol": "Fr", - "name": "West African CFA franc" + "name": "West African CFA franc", + "decimals": 0 }, { "code": "NZD", "local": "Tokelau", "symbol": "$", - "name": "New Zealand dollar" + "name": "New Zealand dollar", + "decimals": 2 }, { "code": "TOP", "local": "Tonga", "symbol": "T$", - "name": "Tongan paʻanga" + "name": "Tongan paʻanga", + "decimals": 2 }, { "code": "TTD", "local": "Trinidad and Tobago", "symbol": "$", - "name": "Trinidad and Tobago dollar" + "name": "Trinidad and Tobago dollar", + "decimals": 2 }, { "code": "TND", "local": "Tunisia", "symbol": "د.ت", - "name": "Tunisian dinar" + "name": "Tunisian dinar", + "decimals": 3 }, { "code": "TRY", "local": "Turkey", "symbol": "₺", - "name": "Turkish lira" + "name": "Turkish lira", + "decimals": 2 }, { "code": "TMT", "local": "Turkmenistan", "symbol": "m", - "name": "Turkmenistan manat" + "name": "Turkmenistan manat", + "decimals": 2 }, { "code": "USD", "local": "Turks and Caicos Islands", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "AUD", "local": "Tuvalu", "symbol": "$", - "name": "Australian dollar" + "name": "Australian dollar", + "decimals": 2 }, { "code": "TVD", "local": "Tuvalu", "symbol": "$", - "name": "Tuvaluan dollar" + "name": "Tuvaluan dollar", + "decimals": 2 }, { "code": "UGX", "local": "Uganda", "symbol": "Sh", - "name": "Ugandan shilling" + "name": "Ugandan shilling", + "decimals": 0 }, { "code": "UAH", "local": "Ukraine", "symbol": "₴", - "name": "Ukrainian hryvnia" + "name": "Ukrainian hryvnia", + "decimals": 2 }, { "code": "AED", "local": "United Arab Emirates", "symbol": "د.إ", - "name": "United Arab Emirates dirham" + "name": "United Arab Emirates dirham", + "decimals": 2 }, { "code": "GBP", "local": "United Kingdom", "symbol": "£", - "name": "British pound" + "name": "British pound", + "decimals": 2 }, { "code": "USD", "local": "United States", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "USD", "local": "United States Minor Outlying Islands", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "USD", "local": "United States Virgin Islands", "symbol": "$", - "name": "United States dollar" + "name": "United States dollar", + "decimals": 2 }, { "code": "UYU", "local": "Uruguay", "symbol": "$", - "name": "Uruguayan peso" + "name": "Uruguayan peso", + "decimals": 2 }, { "code": "UZS", "local": "Uzbekistan", "symbol": "so'm", - "name": "Uzbekistani soʻm" + "name": "Uzbekistani soʻm", + "decimals": 2 }, { "code": "VUV", "local": "Vanuatu", "symbol": "Vt", - "name": "Vanuatu vatu" + "name": "Vanuatu vatu", + "decimals": 0 }, { "code": "EUR", "local": "Vatican City", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 }, { "code": "VES", "local": "Venezuela", "symbol": "Bs.S.", - "name": "Venezuelan bolívar soberano" + "name": "Venezuelan bolívar soberano", + "decimals": 2 }, { "code": "VND", "local": "Vietnam", "symbol": "₫", - "name": "Vietnamese đồng" + "name": "Vietnamese đồng", + "decimals": 0 }, { "code": "XPF", "local": "Wallis and Futuna", "symbol": "₣", - "name": "CFP franc" + "name": "CFP franc", + "decimals": 0 }, { "code": "DZD", "local": "Western Sahara", "symbol": "دج", - "name": "Algerian dinar" + "name": "Algerian dinar", + "decimals": 2 }, { "code": "MAD", "local": "Western Sahara", "symbol": "DH", - "name": "Moroccan dirham" + "name": "Moroccan dirham", + "decimals": 2 }, { "code": "MRU", "local": "Western Sahara", "symbol": "UM", - "name": "Mauritanian ouguiya" + "name": "Mauritanian ouguiya", + "decimals": 2 }, { "code": "YER", "local": "Yemen", "symbol": "﷼", - "name": "Yemeni rial" + "name": "Yemeni rial", + "decimals": 2 }, { "code": "ZMW", "local": "Zambia", "symbol": "ZK", - "name": "Zambian kwacha" + "name": "Zambian kwacha", + "decimals": 2 }, { "code": "ZWL", "local": "Zimbabwe", "symbol": "$", - "name": "Zimbabwean dollar" + "name": "Zimbabwean dollar", + "decimals": 2 }, { "code": "EUR", "local": "Åland Islands", "symbol": "€", - "name": "Euro" + "name": "Euro", + "decimals": 2 } ] \ No newline at end of file diff --git a/frontend/composables/use-formatters.ts b/frontend/composables/use-formatters.ts index 77e7a687..65f39715 100644 --- a/frontend/composables/use-formatters.ts +++ b/frontend/composables/use-formatters.ts @@ -1,6 +1,7 @@ import { format, formatDistance } from "date-fns"; /* eslint import/namespace: ['error', { allowComputed: true }] */ import * as Locales from "date-fns/locale"; +import { fmtCurrency, fmtCurrencyAsync } from "./utils"; const cache = { currency: "", @@ -21,6 +22,16 @@ export async function useFormatCurrency() { } } + // Pre-load currency decimals for better formatting (optional, non-blocking) + if (cache.currency && cache.currency.trim() !== "") { + try { + await fmtCurrencyAsync(0, cache.currency, getLocaleCode()); + } catch (error) { + // Silently swallow preload errors - formatter will still work, just without pre-cached decimals + console.debug("Currency preload failed (non-fatal):", error); + } + } + return (value: number | string) => fmtCurrency(value, cache.currency, getLocaleCode()); } diff --git a/frontend/composables/utils.ts b/frontend/composables/utils.ts index 29bcf81a..6b4cfbfc 100644 --- a/frontend/composables/utils.ts +++ b/frontend/composables/utils.ts @@ -25,19 +25,126 @@ export function validDate(dt: Date | string | null | undefined): boolean { return true; } +// Currency cache to store decimal places information +export const currencyDecimalsCache: Record = {}; + +// Promise to track in-flight loading to coalesce concurrent calls +let currencyLoadingPromise: Promise | null = null; + +// Safe range for server-provided decimals +const SAFE_MIN_DECIMALS = 0; +const SAFE_MAX_DECIMALS = 4; + +// Helper function to clamp decimal places to safe range +function clampDecimals(currency: string, decimals: number): number { + const truncated = Math.trunc(decimals); + return Math.max(SAFE_MIN_DECIMALS, Math.min(SAFE_MAX_DECIMALS, truncated)); +} + +// Type guard to validate currency response shape with strict validation +function isValidCurrencyItem(item: any): item is { code: string; decimals: number } { + if ( + typeof item !== "object" || + item === null || + typeof item.code !== "string" || + item.code.trim() === "" || + typeof item.decimals !== "number" || + !Number.isFinite(item.decimals) + ) { + return false; + } + + // Truncate decimals to integer and check range + const truncatedDecimals = Math.trunc(item.decimals); + return truncatedDecimals >= SAFE_MIN_DECIMALS && truncatedDecimals <= SAFE_MAX_DECIMALS; +} + +// Function to load currency decimals from API +function loadCurrencyDecimals(): Promise { + // Check environment variable to see if remote decimals are disabled + if (process.env.USE_REMOTE_DECIMALS === "false") { + return Promise.resolve(); + } + + // Return early if already loaded + if (Object.keys(currencyDecimalsCache).length > 0) { + return Promise.resolve(); + } + + // Coalesce concurrent calls - return existing promise if loading + if (currencyLoadingPromise) { + return currencyLoadingPromise; + } + + // Create new loading promise + currencyLoadingPromise = (async () => { + try { + const api = useUserApi(); + const { data, error } = await api.group.currencies(); + + if (!error && data) { + // Validate that data is an array + if (!Array.isArray(data)) { + // Log generic message without server details + console.warn("Currency API returned invalid data format"); + return; + } + + // Process and validate each currency item + for (const currency of data) { + // Strict validation: only process items that pass all checks + if (!isValidCurrencyItem(currency)) { + // Skip invalid items without caching - no clamping for out-of-range values + continue; + } + + // Only cache strictly validated items with truncated and clamped decimals + const code = currency.code.trim().toUpperCase(); + const truncatedDecimals = Math.trunc(currency.decimals); + const clampedDecimals = Math.max(SAFE_MIN_DECIMALS, Math.min(SAFE_MAX_DECIMALS, truncatedDecimals)); + currencyDecimalsCache[code] = clampedDecimals; + } + } else if (error) { + // Generic error logging without exposing server error details + console.warn("Currency API request failed, using default formatting"); + } + } catch (e) { + // Generic error without sensitive details - no raw error logging + console.warn("Currency data loading failed, using default formatting"); + } finally { + // Clear loading promise when done (success or failure) + currencyLoadingPromise = null; + } + })(); + + return currencyLoadingPromise; +} + export function fmtCurrency(value: number | string, currency = "USD", locale = "en-Us"): string { if (typeof value === "string") { value = parseFloat(value); } + // Normalize and validate currency code + const normalizedCurrency = String(currency).toUpperCase(); + const safeCurrency = /^[A-Z]{3}$/.test(normalizedCurrency) ? normalizedCurrency : "USD"; + // Derive fraction digits using the same clamp helper + const fractionDigits = clampDecimals(safeCurrency, currencyDecimalsCache[safeCurrency] ?? 2); + const formatter = new Intl.NumberFormat(locale, { style: "currency", - currency, - minimumFractionDigits: 2, + currency: safeCurrency, + minimumFractionDigits: fractionDigits, + maximumFractionDigits: fractionDigits, }); return formatter.format(value); } +export async function fmtCurrencyAsync(value: number | string, currency = "USD", locale = "en-Us"): Promise { + await loadCurrencyDecimals(); + return fmtCurrency(value, currency, locale); +} + export type MaybeUrlResult = { isUrl: boolean; url: string; diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts index 460b84c7..36697fc7 100644 --- a/frontend/lib/api/types/data-contracts.ts +++ b/frontend/lib/api/types/data-contracts.ts @@ -57,6 +57,7 @@ export interface CurrenciesCurrency { local: string; name: string; symbol: string; + decimals: number; } export interface EntAttachment { diff --git a/frontend/pages/profile.vue b/frontend/pages/profile.vue index e51b710e..a1726e1d 100644 --- a/frontend/pages/profile.vue +++ b/frontend/pages/profile.vue @@ -11,6 +11,7 @@ import MdiPencil from "~icons/mdi/pencil"; import MdiAccountMultiple from "~icons/mdi/account-multiple"; import { getLocaleCode } from "~/composables/use-formatters"; + import { fmtCurrencyAsync } from "~/composables/utils"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { useDialog } from "@/components/ui/dialog-provider"; @@ -67,6 +68,7 @@ name: "United States Dollar", local: "en-US", symbol: "$", + decimals: 2, }); watch(currency, () => { if (group.value) { @@ -74,9 +76,18 @@ } }); - const currencyExample = computed(() => { - return fmtCurrency(1000, currency.value?.code ?? "USD", getLocaleCode()); - }); + const currencyExample = ref("$1,000.00"); + + // Update currency example when currency changes + watch( + currency, + async () => { + if (currency.value) { + currencyExample.value = await fmtCurrencyAsync(1000, currency.value.code, getLocaleCode()); + } + }, + { immediate: true } + ); const { data: group } = useAsyncData(async () => { const { data } = await api.group.get(); From 3b0e986f01d5b62b69a010ce91e26a1f47d0482e Mon Sep 17 00:00:00 2001 From: Katos <7927609+katosdev@users.noreply.github.com> Date: Sat, 13 Sep 2025 18:57:10 +0100 Subject: [PATCH 03/19] Refactor update-currencies workflow file --- .github/workflows/update-currencies.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-currencies.yml b/.github/workflows/update-currencies.yml index fe1009e2..aad405c8 100644 --- a/.github/workflows/update-currencies.yml +++ b/.github/workflows/update-currencies.yml @@ -41,7 +41,7 @@ jobs: - name: Create Pull Request if: env.changed == 'true' - uses: peter-evans/create-pull-request@v7.0.8 + uses: peter-evans/create-pull-request@v7.0.7 with: token: ${{ secrets.GITHUB_TOKEN }} branch: update-currencies @@ -53,3 +53,4 @@ jobs: - name: No updates needed if: env.changed == 'false' run: echo "✅ currencies.json is already up-to-date" + From b535cdeb968c4a323ea9b48040237a60ce622dac Mon Sep 17 00:00:00 2001 From: Katos <7927609+katosdev@users.noreply.github.com> Date: Sun, 14 Sep 2025 15:52:17 +0100 Subject: [PATCH 04/19] Refactor update-currencies workflow with enhancements Update currencies workflow to fix errors and introduce improvements --- .github/workflows/update-currencies.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/update-currencies.yml b/.github/workflows/update-currencies.yml index aad405c8..b5bc3965 100644 --- a/.github/workflows/update-currencies.yml +++ b/.github/workflows/update-currencies.yml @@ -5,18 +5,22 @@ on: branches: [ main ] workflow_dispatch: +permissions: + contents: write + pull-requests: write + jobs: update-currencies: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.8' cache: 'pip' @@ -25,15 +29,14 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install requests + pip install -r .github/workflows/update-currencies/requirements.txt - name: Run currency update script run: python .github/scripts/update_currencies.py - - name: Check for file changes - id: changes + - name: Check for currencies.json changes run: | - if git diff --quiet; then + if git diff --quiet -- backend/internal/core/currencies/currencies.json; then echo "changed=false" >> $GITHUB_ENV else echo "changed=true" >> $GITHUB_ENV @@ -41,16 +44,17 @@ jobs: - name: Create Pull Request if: env.changed == 'true' - uses: peter-evans/create-pull-request@v7.0.7 + uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.GITHUB_TOKEN }} branch: update-currencies base: main title: "Update currencies.json" commit-message: "chore: update currencies.json" - path: backend/internal/core/currencies/currencies.json + path: . + add-paths: | + backend/internal/core/currencies/currencies.json - name: No updates needed if: env.changed == 'false' run: echo "✅ currencies.json is already up-to-date" - From 118bce4441dd71666c35ce05122a9eb0ae838344 Mon Sep 17 00:00:00 2001 From: katosdev <7927609+katosdev@users.noreply.github.com> Date: Sun, 14 Sep 2025 14:52:41 +0000 Subject: [PATCH 05/19] chore: update currencies.json --- .../internal/core/currencies/currencies.json | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/backend/internal/core/currencies/currencies.json b/backend/internal/core/currencies/currencies.json index d1123e43..615c5cff 100644 --- a/backend/internal/core/currencies/currencies.json +++ b/backend/internal/core/currencies/currencies.json @@ -158,7 +158,7 @@ "local": "Benin", "symbol": "Fr", "name": "West African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "BMD", @@ -249,14 +249,14 @@ "local": "Burkina Faso", "symbol": "Fr", "name": "West African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "BIF", "local": "Burundi", "symbol": "Fr", "name": "Burundian franc", - "decimals": 0 + "decimals": 2 }, { "code": "KHR", @@ -277,7 +277,7 @@ "local": "Cameroon", "symbol": "Fr", "name": "Central African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "CAD", @@ -312,21 +312,21 @@ "local": "Central African Republic", "symbol": "Fr", "name": "Central African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "XAF", "local": "Chad", "symbol": "Fr", "name": "Central African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "CLP", "local": "Chile", "symbol": "$", "name": "Chilean peso", - "decimals": 0 + "decimals": 2 }, { "code": "CNY", @@ -361,7 +361,7 @@ "local": "Comoros", "symbol": "Fr", "name": "Comorian franc", - "decimals": 0 + "decimals": 2 }, { "code": "CKD", @@ -438,7 +438,7 @@ "local": "Djibouti", "symbol": "Fr", "name": "Djiboutian franc", - "decimals": 0 + "decimals": 2 }, { "code": "XCD", @@ -487,7 +487,7 @@ "local": "Equatorial Guinea", "symbol": "Fr", "name": "Central African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "ERN", @@ -578,7 +578,7 @@ "local": "French Polynesia", "symbol": "₣", "name": "CFP franc", - "decimals": 0 + "decimals": 2 }, { "code": "EUR", @@ -592,7 +592,7 @@ "local": "Gabon", "symbol": "Fr", "name": "Central African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "GMD", @@ -690,14 +690,14 @@ "local": "Guinea", "symbol": "Fr", "name": "Guinean franc", - "decimals": 0 + "decimals": 2 }, { "code": "XOF", "local": "Guinea-Bissau", "symbol": "Fr", "name": "West African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "GYD", @@ -739,7 +739,7 @@ "local": "Iceland", "symbol": "kr", "name": "Icelandic króna", - "decimals": 0 + "decimals": 2 }, { "code": "INR", @@ -767,7 +767,7 @@ "local": "Iraq", "symbol": "ع.د", "name": "Iraqi dinar", - "decimals": 3 + "decimals": 2 }, { "code": "EUR", @@ -809,7 +809,7 @@ "local": "Ivory Coast", "symbol": "Fr", "name": "West African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "JMD", @@ -844,7 +844,7 @@ "local": "Jordan", "symbol": "د.ا", "name": "Jordanian dinar", - "decimals": 3 + "decimals": 2 }, { "code": "KZT", @@ -886,7 +886,7 @@ "local": "Kuwait", "symbol": "د.ك", "name": "Kuwaiti dinar", - "decimals": 3 + "decimals": 2 }, { "code": "KGS", @@ -942,7 +942,7 @@ "local": "Libya", "symbol": "ل.د", "name": "Libyan dinar", - "decimals": 3 + "decimals": 2 }, { "code": "CHF", @@ -1005,7 +1005,7 @@ "local": "Mali", "symbol": "Fr", "name": "West African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "EUR", @@ -1159,7 +1159,7 @@ "local": "New Caledonia", "symbol": "₣", "name": "CFP franc", - "decimals": 0 + "decimals": 2 }, { "code": "NZD", @@ -1180,7 +1180,7 @@ "local": "Niger", "symbol": "Fr", "name": "West African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "NGN", @@ -1236,7 +1236,7 @@ "local": "Oman", "symbol": "ر.ع.", "name": "Omani rial", - "decimals": 3 + "decimals": 2 }, { "code": "PKR", @@ -1271,7 +1271,7 @@ "local": "Palestine", "symbol": "JD", "name": "Jordanian dinar", - "decimals": 3 + "decimals": 2 }, { "code": "PAB", @@ -1299,7 +1299,7 @@ "local": "Paraguay", "symbol": "₲", "name": "Paraguayan guaraní", - "decimals": 0 + "decimals": 2 }, { "code": "PEN", @@ -1355,7 +1355,7 @@ "local": "Republic of the Congo", "symbol": "Fr", "name": "Central African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "RON", @@ -1376,7 +1376,7 @@ "local": "Rwanda", "symbol": "Fr", "name": "Rwandan franc", - "decimals": 0 + "decimals": 2 }, { "code": "EUR", @@ -1467,7 +1467,7 @@ "local": "Senegal", "symbol": "Fr", "name": "West African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "RSD", @@ -1551,7 +1551,7 @@ "local": "South Korea", "symbol": "₩", "name": "South Korean won", - "decimals": 0 + "decimals": 2 }, { "code": "SSP", @@ -1663,7 +1663,7 @@ "local": "Togo", "symbol": "Fr", "name": "West African CFA franc", - "decimals": 0 + "decimals": 2 }, { "code": "NZD", @@ -1691,7 +1691,7 @@ "local": "Tunisia", "symbol": "د.ت", "name": "Tunisian dinar", - "decimals": 3 + "decimals": 2 }, { "code": "TRY", @@ -1733,7 +1733,7 @@ "local": "Uganda", "symbol": "Sh", "name": "Ugandan shilling", - "decimals": 0 + "decimals": 2 }, { "code": "UAH", @@ -1796,7 +1796,7 @@ "local": "Vanuatu", "symbol": "Vt", "name": "Vanuatu vatu", - "decimals": 0 + "decimals": 2 }, { "code": "EUR", @@ -1817,14 +1817,14 @@ "local": "Vietnam", "symbol": "₫", "name": "Vietnamese đồng", - "decimals": 0 + "decimals": 2 }, { "code": "XPF", "local": "Wallis and Futuna", "symbol": "₣", "name": "CFP franc", - "decimals": 0 + "decimals": 2 }, { "code": "DZD", From 609b7a606b1f1b1dc3ee377eb197055d793dbb3a Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 23 Sep 2025 12:14:37 -0400 Subject: [PATCH 06/19] Generate OpenAPI 3 schemas from the swagger 2.0 generation (#1017) * Generate OpenAPI 3 schemas from the swagger 2.0 generation * Update API description URL in index.md --- Taskfile.yml | 9 +- backend/app/api/static/docs/docs.go | 3 + backend/app/api/static/docs/openapi-3.json | 4550 +++++++++++++++++ backend/app/api/static/docs/openapi-3.yaml | 2888 +++++++++++ backend/app/api/static/docs/swagger.json | 3 + backend/app/api/static/docs/swagger.yaml | 2 + docs/en/api/index.md | 2 +- docs/en/api/openapi-3.0.json | 4550 +++++++++++++++++ docs/en/api/openapi-3.0.yaml | 2888 +++++++++++ .../{openapi-2.0.json => swagger-2.0.json} | 3 + .../{openapi-2.0.yaml => swagger-2.0.yaml} | 2 + 11 files changed, 14896 insertions(+), 4 deletions(-) create mode 100644 backend/app/api/static/docs/openapi-3.json create mode 100644 backend/app/api/static/docs/openapi-3.yaml create mode 100644 docs/en/api/openapi-3.0.json create mode 100644 docs/en/api/openapi-3.0.yaml rename docs/en/api/{openapi-2.0.json => swagger-2.0.json} (99%) rename docs/en/api/{openapi-2.0.yaml => swagger-2.0.yaml} (99%) diff --git a/Taskfile.yml b/Taskfile.yml index e2da964d..8b728b0b 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -23,10 +23,13 @@ tasks: INTERNAL: "../../../internal" PKGS: "../../../pkgs" cmds: - - swag fmt --dir={{ .API }} - swag init --dir={{ .API }},{{ .INTERNAL }}/core/services,{{ .INTERNAL }}/data/repo --parseDependency - - cp -r ./docs/swagger.json ../../../../docs/en/api/openapi-2.0.json - - cp -r ./docs/swagger.yaml ../../../../docs/en/api/openapi-2.0.yaml + - npx -y -p swagger2openapi swagger2openapi --outfile ./docs/openapi-3.json ./docs/swagger.json + - npx -y -p swagger2openapi swagger2openapi --yaml --outfile ./docs/openapi-3.yaml ./docs/swagger.json + - cp -r ./docs/swagger.json ../../../../docs/en/api/swagger-2.0.json + - cp -r ./docs/swagger.yaml ../../../../docs/en/api/swagger-2.0.yaml + - cp -r ./docs/openapi-3.json ../../../../docs/en/api/openapi-3.0.json + - cp -r ./docs/openapi-3.yaml ../../../../docs/en/api/openapi-3.0.yaml sources: - "./backend/app/api/**/*" - "./backend/internal/data/**" diff --git a/backend/app/api/static/docs/docs.go b/backend/app/api/static/docs/docs.go index 843aebf9..6525c53a 100644 --- a/backend/app/api/static/docs/docs.go +++ b/backend/app/api/static/docs/docs.go @@ -2240,6 +2240,9 @@ const docTemplate = `{ "code": { "type": "string" }, + "decimals": { + "type": "integer" + }, "local": { "type": "string" }, diff --git a/backend/app/api/static/docs/openapi-3.json b/backend/app/api/static/docs/openapi-3.json new file mode 100644 index 00000000..b0bcc2d8 --- /dev/null +++ b/backend/app/api/static/docs/openapi-3.json @@ -0,0 +1,4550 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "Track, Manage, and Organize your Things.", + "title": "Homebox API", + "contact": { + "name": "Homebox Team", + "url": "https://discord.homebox.software" + }, + "version": "1.0" + }, + "paths": { + "/v1/actions/create-missing-thumbnails": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Creates thumbnails for items that are missing them", + "tags": [ + "Actions" + ], + "summary": "Create Missing Thumbnails", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.ActionAmountResult" + } + } + } + } + } + } + }, + "/v1/actions/ensure-asset-ids": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Ensures all items in the database have an asset ID", + "tags": [ + "Actions" + ], + "summary": "Ensure Asset IDs", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.ActionAmountResult" + } + } + } + } + } + } + }, + "/v1/actions/ensure-import-refs": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Ensures all items in the database have an import ref", + "tags": [ + "Actions" + ], + "summary": "Ensures Import Refs", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.ActionAmountResult" + } + } + } + } + } + } + }, + "/v1/actions/set-primary-photos": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Sets the first photo of each item as the primary photo", + "tags": [ + "Actions" + ], + "summary": "Set Primary Photos", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.ActionAmountResult" + } + } + } + } + } + } + }, + "/v1/actions/zero-item-time-fields": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Resets all item date fields to the beginning of the day", + "tags": [ + "Actions" + ], + "summary": "Zero Out Time Fields", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.ActionAmountResult" + } + } + } + } + } + } + }, + "/v1/assets/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Get Item by Asset ID", + "parameters": [ + { + "description": "Asset ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.PaginationResult-repo_ItemSummary" + } + } + } + } + } + } + }, + "/v1/currency": { + "get": { + "tags": [ + "Base" + ], + "summary": "Currency", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/currencies.Currency" + } + } + } + } + } + } + }, + "/v1/groups": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Group" + ], + "summary": "Get Group", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.Group" + } + } + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Group" + ], + "summary": "Update Group", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.GroupUpdate" + } + } + }, + "description": "User Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.Group" + } + } + } + } + } + } + }, + "/v1/groups/invitations": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Group" + ], + "summary": "Create Group Invitation", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.GroupInvitationCreate" + } + } + }, + "description": "User Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.GroupInvitation" + } + } + } + } + } + } + }, + "/v1/groups/statistics": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Statistics" + ], + "summary": "Get Group Statistics", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.GroupStatistics" + } + } + } + } + } + } + }, + "/v1/groups/statistics/labels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Statistics" + ], + "summary": "Get Label Statistics", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.TotalsByOrganizer" + } + } + } + } + } + } + } + }, + "/v1/groups/statistics/locations": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Statistics" + ], + "summary": "Get Location Statistics", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.TotalsByOrganizer" + } + } + } + } + } + } + } + }, + "/v1/groups/statistics/purchase-price": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Statistics" + ], + "summary": "Get Purchase Price Statistics", + "parameters": [ + { + "description": "start date", + "name": "start", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "description": "end date", + "name": "end", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ValueOverTime" + } + } + } + } + } + } + }, + "/v1/items": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Query All Items", + "parameters": [ + { + "description": "search string", + "name": "q", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "description": "page number", + "name": "page", + "in": "query", + "schema": { + "type": "integer" + } + }, + { + "description": "items per page", + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer" + } + }, + { + "description": "label Ids", + "name": "labels", + "in": "query", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "description": "location Ids", + "name": "locations", + "in": "query", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "description": "parent Ids", + "name": "parentIds", + "in": "query", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.PaginationResult-repo_ItemSummary" + } + } + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Create Item", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemCreate" + } + } + }, + "description": "Item Data", + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemSummary" + } + } + } + } + } + } + }, + "/v1/items/export": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Export Items", + "responses": { + "200": { + "description": "text/csv", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/v1/items/fields": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Get All Custom Field Names", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "/v1/items/fields/values": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Get All Custom Field Values", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "/v1/items/import": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Import Items", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "csv": { + "description": "Image to upload", + "type": "string", + "format": "binary" + } + }, + "required": [ + "csv" + ] + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/items/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Get Item", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemOut" + } + } + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Update Item", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemUpdate" + } + } + }, + "description": "Item Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemOut" + } + } + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Delete Item", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Update Item", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemPatch" + } + } + }, + "description": "Item Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemOut" + } + } + } + } + } + } + }, + "/v1/items/{id}/attachments": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items Attachments" + ], + "summary": "Create Item Attachment", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "description": "File attachment", + "type": "string", + "format": "binary" + }, + "type": { + "description": "Type of file", + "type": "string" + }, + "primary": { + "description": "Is this the primary attachment", + "type": "boolean" + }, + "name": { + "description": "name of the file including extension", + "type": "string" + } + }, + "required": [ + "file", + "name" + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemOut" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/validate.ErrorResponse" + } + } + } + } + } + } + }, + "/v1/items/{id}/attachments/{attachment_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items Attachments" + ], + "summary": "Get Item Attachment", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Attachment ID", + "name": "attachment_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "$ref": "#/components/schemas/v1.ItemAttachmentToken" + } + } + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items Attachments" + ], + "summary": "Update Item Attachment", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Attachment ID", + "name": "attachment_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemAttachmentUpdate" + } + } + }, + "description": "Attachment Update", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/repo.ItemOut" + } + } + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items Attachments" + ], + "summary": "Delete Item Attachment", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Attachment ID", + "name": "attachment_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/items/{id}/duplicate": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Duplicate Item", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.DuplicateOptions" + } + } + }, + "description": "Duplicate Options", + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.ItemOut" + } + } + } + } + } + } + }, + "/v1/items/{id}/maintenance": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Item Maintenance" + ], + "summary": "Get Maintenance Log", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "x-enum-varnames": [ + "MaintenanceFilterStatusScheduled", + "MaintenanceFilterStatusCompleted", + "MaintenanceFilterStatusBoth" + ], + "name": "status", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "scheduled", + "completed", + "both" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.MaintenanceEntryWithDetails" + } + } + } + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Item Maintenance" + ], + "summary": "Create Maintenance Entry", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.MaintenanceEntryCreate" + } + } + }, + "description": "Entry Data", + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.MaintenanceEntry" + } + } + } + } + } + } + }, + "/v1/items/{id}/path": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Get the full path of an item", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.ItemPath" + } + } + } + } + } + } + } + }, + "/v1/labelmaker/assets/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Get Asset label", + "parameters": [ + { + "description": "Asset ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Print this label, defaults to false", + "name": "print", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "image/png", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/v1/labelmaker/item/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Get Item label", + "parameters": [ + { + "description": "Item ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Print this label, defaults to false", + "name": "print", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "image/png", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/v1/labelmaker/location/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Locations" + ], + "summary": "Get Location label", + "parameters": [ + { + "description": "Location ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Print this label, defaults to false", + "name": "print", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "image/png", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/v1/labels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Labels" + ], + "summary": "Get All Labels", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.LabelOut" + } + } + } + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Labels" + ], + "summary": "Create Label", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LabelCreate" + } + } + }, + "description": "Label Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LabelSummary" + } + } + } + } + } + } + }, + "/v1/labels/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Labels" + ], + "summary": "Get Label", + "parameters": [ + { + "description": "Label ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LabelOut" + } + } + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Labels" + ], + "summary": "Update Label", + "parameters": [ + { + "description": "Label ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LabelOut" + } + } + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Labels" + ], + "summary": "Delete Label", + "parameters": [ + { + "description": "Label ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/locations": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Locations" + ], + "summary": "Get All Locations", + "parameters": [ + { + "description": "Filter locations with parents", + "name": "filterChildren", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.LocationOutCount" + } + } + } + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Locations" + ], + "summary": "Create Location", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LocationCreate" + } + } + }, + "description": "Location Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LocationSummary" + } + } + } + } + } + } + }, + "/v1/locations/tree": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Locations" + ], + "summary": "Get Locations Tree", + "parameters": [ + { + "description": "include items in response tree", + "name": "withItems", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.TreeItem" + } + } + } + } + } + } + } + }, + "/v1/locations/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Locations" + ], + "summary": "Get Location", + "parameters": [ + { + "description": "Location ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LocationOut" + } + } + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Locations" + ], + "summary": "Update Location", + "parameters": [ + { + "description": "Location ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LocationUpdate" + } + } + }, + "description": "Location Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.LocationOut" + } + } + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Locations" + ], + "summary": "Delete Location", + "parameters": [ + { + "description": "Location ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/maintenance": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Maintenance" + ], + "summary": "Query All Maintenance", + "parameters": [ + { + "x-enum-varnames": [ + "MaintenanceFilterStatusScheduled", + "MaintenanceFilterStatusCompleted", + "MaintenanceFilterStatusBoth" + ], + "name": "status", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "scheduled", + "completed", + "both" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.MaintenanceEntryWithDetails" + } + } + } + } + } + } + } + }, + "/v1/maintenance/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Maintenance" + ], + "summary": "Update Maintenance Entry", + "parameters": [ + { + "description": "Maintenance ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.MaintenanceEntryUpdate" + } + } + }, + "description": "Entry Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.MaintenanceEntry" + } + } + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Maintenance" + ], + "summary": "Delete Maintenance Entry", + "parameters": [ + { + "description": "Maintenance ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/notifiers": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Notifiers" + ], + "summary": "Get Notifiers", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.NotifierOut" + } + } + } + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Notifiers" + ], + "summary": "Create Notifier", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.NotifierCreate" + } + } + }, + "description": "Notifier Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.NotifierOut" + } + } + } + } + } + } + }, + "/v1/notifiers/test": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Notifiers" + ], + "summary": "Test Notifier", + "parameters": [ + { + "description": "URL", + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/notifiers/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Notifiers" + ], + "summary": "Update Notifier", + "parameters": [ + { + "description": "Notifier ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.NotifierUpdate" + } + } + }, + "description": "Notifier Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/repo.NotifierOut" + } + } + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Notifiers" + ], + "summary": "Delete a Notifier", + "parameters": [ + { + "description": "Notifier ID", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/products/search-from-barcode": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Search EAN from Barcode", + "parameters": [ + { + "description": "barcode to be searched", + "name": "data", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.BarcodeProduct" + } + } + } + } + } + } + } + }, + "/v1/qrcode": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Items" + ], + "summary": "Create QR Code", + "parameters": [ + { + "description": "data to be encoded into qrcode", + "name": "data", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "image/jpeg", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/v1/reporting/bill-of-materials": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Reporting" + ], + "summary": "Export Bill of Materials", + "responses": { + "200": { + "description": "text/csv", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/v1/status": { + "get": { + "tags": [ + "Base" + ], + "summary": "Application Info", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.APISummary" + } + } + } + } + } + } + }, + "/v1/users/change-password": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "User" + ], + "summary": "Change Password", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.ChangePassword" + } + } + }, + "description": "Password Payload", + "required": true + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/users/login": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "User Login", + "parameters": [ + { + "description": "auth provider", + "name": "provider", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/v1.LoginForm" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.LoginForm" + } + } + }, + "description": "Login Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.TokenResponse" + } + } + } + } + } + } + }, + "/v1/users/logout": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "Authentication" + ], + "summary": "User Logout", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/users/refresh": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "handleAuthRefresh returns a handler that will issue a new token from an existing token.\nThis does not validate that the user still exists within the database.", + "tags": [ + "Authentication" + ], + "summary": "User Token Refresh", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/v1/users/register": { + "post": { + "tags": [ + "User" + ], + "summary": "Register New User", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/services.UserRegistration" + } + } + }, + "description": "User Data", + "required": true + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/v1/users/self": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "User" + ], + "summary": "Get User Self", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/v1.Wrapped" + }, + { + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/repo.UserOut" + } + } + } + ] + } + } + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "User" + ], + "summary": "Update Account", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repo.UserUpdate" + } + } + }, + "description": "User Data", + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/v1.Wrapped" + }, + { + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/repo.UserUpdate" + } + } + } + ] + } + } + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + "User" + ], + "summary": "Delete Account", + "responses": { + "204": { + "description": "No Content" + } + } + } + } + }, + "servers": [ + { + "url": "https://demo.homebox.software/api" + }, + { + "url": "http://demo.homebox.software/api" + } + ], + "components": { + "securitySchemes": { + "Bearer": { + "description": "\"Type 'Bearer TOKEN' to correctly set the API Key\"", + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + }, + "schemas": { + "attachment.Type": { + "type": "string", + "enum": [ + "attachment", + "photo", + "manual", + "warranty", + "attachment", + "receipt", + "thumbnail" + ], + "x-enum-varnames": [ + "DefaultType", + "TypePhoto", + "TypeManual", + "TypeWarranty", + "TypeAttachment", + "TypeReceipt", + "TypeThumbnail" + ] + }, + "authroles.Role": { + "type": "string", + "enum": [ + "user", + "admin", + "user", + "attachments" + ], + "x-enum-varnames": [ + "DefaultRole", + "RoleAdmin", + "RoleUser", + "RoleAttachments" + ] + }, + "currencies.Currency": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "decimals": { + "type": "integer" + }, + "local": { + "type": "string" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + } + }, + "ent.Attachment": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the AttachmentQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.AttachmentEdges" + } + ] + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "mime_type": { + "description": "MimeType holds the value of the \"mime_type\" field.", + "type": "string" + }, + "path": { + "description": "Path holds the value of the \"path\" field.", + "type": "string" + }, + "primary": { + "description": "Primary holds the value of the \"primary\" field.", + "type": "boolean" + }, + "title": { + "description": "Title holds the value of the \"title\" field.", + "type": "string" + }, + "type": { + "description": "Type holds the value of the \"type\" field.", + "allOf": [ + { + "$ref": "#/components/schemas/attachment.Type" + } + ] + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.AttachmentEdges": { + "type": "object", + "properties": { + "item": { + "description": "Item holds the value of the item edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Item" + } + ] + }, + "thumbnail": { + "description": "Thumbnail holds the value of the thumbnail edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Attachment" + } + ] + } + } + }, + "ent.AuthRoles": { + "type": "object", + "properties": { + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the AuthRolesQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.AuthRolesEdges" + } + ] + }, + "id": { + "description": "ID of the ent.", + "type": "integer" + }, + "role": { + "description": "Role holds the value of the \"role\" field.", + "allOf": [ + { + "$ref": "#/components/schemas/authroles.Role" + } + ] + } + } + }, + "ent.AuthRolesEdges": { + "type": "object", + "properties": { + "token": { + "description": "Token holds the value of the token edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.AuthTokens" + } + ] + } + } + }, + "ent.AuthTokens": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the AuthTokensQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.AuthTokensEdges" + } + ] + }, + "expires_at": { + "description": "ExpiresAt holds the value of the \"expires_at\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "token": { + "description": "Token holds the value of the \"token\" field.", + "type": "array", + "items": { + "type": "integer" + } + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.AuthTokensEdges": { + "type": "object", + "properties": { + "roles": { + "description": "Roles holds the value of the roles edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.AuthRoles" + } + ] + }, + "user": { + "description": "User holds the value of the user edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.User" + } + ] + } + } + }, + "ent.Group": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "currency": { + "description": "Currency holds the value of the \"currency\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the GroupQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.GroupEdges" + } + ] + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.GroupEdges": { + "type": "object", + "properties": { + "invitation_tokens": { + "description": "InvitationTokens holds the value of the invitation_tokens edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.GroupInvitationToken" + } + }, + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Item" + } + }, + "labels": { + "description": "Labels holds the value of the labels edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Label" + } + }, + "locations": { + "description": "Locations holds the value of the locations edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Location" + } + }, + "notifiers": { + "description": "Notifiers holds the value of the notifiers edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Notifier" + } + }, + "users": { + "description": "Users holds the value of the users edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.User" + } + } + } + }, + "ent.GroupInvitationToken": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the GroupInvitationTokenQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.GroupInvitationTokenEdges" + } + ] + }, + "expires_at": { + "description": "ExpiresAt holds the value of the \"expires_at\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "token": { + "description": "Token holds the value of the \"token\" field.", + "type": "array", + "items": { + "type": "integer" + } + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + }, + "uses": { + "description": "Uses holds the value of the \"uses\" field.", + "type": "integer" + } + } + }, + "ent.GroupInvitationTokenEdges": { + "type": "object", + "properties": { + "group": { + "description": "Group holds the value of the group edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Group" + } + ] + } + } + }, + "ent.Item": { + "type": "object", + "properties": { + "archived": { + "description": "Archived holds the value of the \"archived\" field.", + "type": "boolean" + }, + "asset_id": { + "description": "AssetID holds the value of the \"asset_id\" field.", + "type": "integer" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the ItemQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.ItemEdges" + } + ] + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "import_ref": { + "description": "ImportRef holds the value of the \"import_ref\" field.", + "type": "string" + }, + "insured": { + "description": "Insured holds the value of the \"insured\" field.", + "type": "boolean" + }, + "lifetime_warranty": { + "description": "LifetimeWarranty holds the value of the \"lifetime_warranty\" field.", + "type": "boolean" + }, + "manufacturer": { + "description": "Manufacturer holds the value of the \"manufacturer\" field.", + "type": "string" + }, + "model_number": { + "description": "ModelNumber holds the value of the \"model_number\" field.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "notes": { + "description": "Notes holds the value of the \"notes\" field.", + "type": "string" + }, + "purchase_from": { + "description": "PurchaseFrom holds the value of the \"purchase_from\" field.", + "type": "string" + }, + "purchase_price": { + "description": "PurchasePrice holds the value of the \"purchase_price\" field.", + "type": "number" + }, + "purchase_time": { + "description": "PurchaseTime holds the value of the \"purchase_time\" field.", + "type": "string" + }, + "quantity": { + "description": "Quantity holds the value of the \"quantity\" field.", + "type": "integer" + }, + "serial_number": { + "description": "SerialNumber holds the value of the \"serial_number\" field.", + "type": "string" + }, + "sold_notes": { + "description": "SoldNotes holds the value of the \"sold_notes\" field.", + "type": "string" + }, + "sold_price": { + "description": "SoldPrice holds the value of the \"sold_price\" field.", + "type": "number" + }, + "sold_time": { + "description": "SoldTime holds the value of the \"sold_time\" field.", + "type": "string" + }, + "sold_to": { + "description": "SoldTo holds the value of the \"sold_to\" field.", + "type": "string" + }, + "sync_child_items_locations": { + "description": "SyncChildItemsLocations holds the value of the \"sync_child_items_locations\" field.", + "type": "boolean" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + }, + "warranty_details": { + "description": "WarrantyDetails holds the value of the \"warranty_details\" field.", + "type": "string" + }, + "warranty_expires": { + "description": "WarrantyExpires holds the value of the \"warranty_expires\" field.", + "type": "string" + } + } + }, + "ent.ItemEdges": { + "type": "object", + "properties": { + "attachments": { + "description": "Attachments holds the value of the attachments edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Attachment" + } + }, + "children": { + "description": "Children holds the value of the children edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Item" + } + }, + "fields": { + "description": "Fields holds the value of the fields edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.ItemField" + } + }, + "group": { + "description": "Group holds the value of the group edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Group" + } + ] + }, + "label": { + "description": "Label holds the value of the label edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Label" + } + }, + "location": { + "description": "Location holds the value of the location edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Location" + } + ] + }, + "maintenance_entries": { + "description": "MaintenanceEntries holds the value of the maintenance_entries edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.MaintenanceEntry" + } + }, + "parent": { + "description": "Parent holds the value of the parent edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Item" + } + ] + } + } + }, + "ent.ItemField": { + "type": "object", + "properties": { + "boolean_value": { + "description": "BooleanValue holds the value of the \"boolean_value\" field.", + "type": "boolean" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the ItemFieldQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.ItemFieldEdges" + } + ] + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "number_value": { + "description": "NumberValue holds the value of the \"number_value\" field.", + "type": "integer" + }, + "text_value": { + "description": "TextValue holds the value of the \"text_value\" field.", + "type": "string" + }, + "time_value": { + "description": "TimeValue holds the value of the \"time_value\" field.", + "type": "string" + }, + "type": { + "description": "Type holds the value of the \"type\" field.", + "allOf": [ + { + "$ref": "#/components/schemas/itemfield.Type" + } + ] + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.ItemFieldEdges": { + "type": "object", + "properties": { + "item": { + "description": "Item holds the value of the item edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Item" + } + ] + } + } + }, + "ent.Label": { + "type": "object", + "properties": { + "color": { + "description": "Color holds the value of the \"color\" field.", + "type": "string" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the LabelQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.LabelEdges" + } + ] + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.LabelEdges": { + "type": "object", + "properties": { + "group": { + "description": "Group holds the value of the group edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Group" + } + ] + }, + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Item" + } + } + } + }, + "ent.Location": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the LocationQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.LocationEdges" + } + ] + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.LocationEdges": { + "type": "object", + "properties": { + "children": { + "description": "Children holds the value of the children edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Location" + } + }, + "group": { + "description": "Group holds the value of the group edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Group" + } + ] + }, + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Item" + } + }, + "parent": { + "description": "Parent holds the value of the parent edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Location" + } + ] + } + } + }, + "ent.MaintenanceEntry": { + "type": "object", + "properties": { + "cost": { + "description": "Cost holds the value of the \"cost\" field.", + "type": "number" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "date": { + "description": "Date holds the value of the \"date\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the MaintenanceEntryQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.MaintenanceEntryEdges" + } + ] + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "item_id": { + "description": "ItemID holds the value of the \"item_id\" field.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "scheduled_date": { + "description": "ScheduledDate holds the value of the \"scheduled_date\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.MaintenanceEntryEdges": { + "type": "object", + "properties": { + "item": { + "description": "Item holds the value of the item edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Item" + } + ] + } + } + }, + "ent.Notifier": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the NotifierQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.NotifierEdges" + } + ] + }, + "group_id": { + "description": "GroupID holds the value of the \"group_id\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "is_active": { + "description": "IsActive holds the value of the \"is_active\" field.", + "type": "boolean" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + }, + "user_id": { + "description": "UserID holds the value of the \"user_id\" field.", + "type": "string" + } + } + }, + "ent.NotifierEdges": { + "type": "object", + "properties": { + "group": { + "description": "Group holds the value of the group edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Group" + } + ] + }, + "user": { + "description": "User holds the value of the user edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.User" + } + ] + } + } + }, + "ent.User": { + "type": "object", + "properties": { + "activated_on": { + "description": "ActivatedOn holds the value of the \"activated_on\" field.", + "type": "string" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the UserQuery when eager-loading is set.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.UserEdges" + } + ] + }, + "email": { + "description": "Email holds the value of the \"email\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "is_superuser": { + "description": "IsSuperuser holds the value of the \"is_superuser\" field.", + "type": "boolean" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "role": { + "description": "Role holds the value of the \"role\" field.", + "allOf": [ + { + "$ref": "#/components/schemas/user.Role" + } + ] + }, + "superuser": { + "description": "Superuser holds the value of the \"superuser\" field.", + "type": "boolean" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.UserEdges": { + "type": "object", + "properties": { + "auth_tokens": { + "description": "AuthTokens holds the value of the auth_tokens edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.AuthTokens" + } + }, + "group": { + "description": "Group holds the value of the group edge.", + "allOf": [ + { + "$ref": "#/components/schemas/ent.Group" + } + ] + }, + "notifiers": { + "description": "Notifiers holds the value of the notifiers edge.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ent.Notifier" + } + } + } + }, + "itemfield.Type": { + "type": "string", + "enum": [ + "text", + "number", + "boolean", + "time" + ], + "x-enum-varnames": [ + "TypeText", + "TypeNumber", + "TypeBoolean", + "TypeTime" + ] + }, + "repo.BarcodeProduct": { + "type": "object", + "properties": { + "barcode": { + "type": "string" + }, + "imageBase64": { + "type": "string" + }, + "imageURL": { + "type": "string" + }, + "item": { + "$ref": "#/components/schemas/repo.ItemCreate" + }, + "manufacturer": { + "type": "string" + }, + "modelNumber": { + "description": "Identifications", + "type": "string" + }, + "notes": { + "description": "Extras", + "type": "string" + }, + "search_engine_name": { + "type": "string" + } + } + }, + "repo.DuplicateOptions": { + "type": "object", + "properties": { + "copyAttachments": { + "type": "boolean" + }, + "copyCustomFields": { + "type": "boolean" + }, + "copyMaintenance": { + "type": "boolean" + }, + "copyPrefix": { + "type": "string" + } + } + }, + "repo.Group": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "repo.GroupStatistics": { + "type": "object", + "properties": { + "totalItemPrice": { + "type": "number" + }, + "totalItems": { + "type": "integer" + }, + "totalLabels": { + "type": "integer" + }, + "totalLocations": { + "type": "integer" + }, + "totalUsers": { + "type": "integer" + }, + "totalWithWarranty": { + "type": "integer" + } + } + }, + "repo.GroupUpdate": { + "type": "object", + "properties": { + "currency": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "repo.ItemAttachment": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "path": { + "type": "string" + }, + "primary": { + "type": "boolean" + }, + "thumbnail": { + "$ref": "#/components/schemas/ent.Attachment" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "repo.ItemAttachmentUpdate": { + "type": "object", + "properties": { + "primary": { + "type": "boolean" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "repo.ItemCreate": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string", + "maxLength": 1000 + }, + "labelIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "locationId": { + "description": "Edges", + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "parentId": { + "type": "string", + "nullable": true + }, + "quantity": { + "type": "integer" + } + } + }, + "repo.ItemField": { + "type": "object", + "properties": { + "booleanValue": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "numberValue": { + "type": "integer" + }, + "textValue": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "repo.ItemOut": { + "type": "object", + "properties": { + "archived": { + "type": "boolean" + }, + "assetId": { + "type": "string", + "example": "0" + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.ItemAttachment" + } + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "fields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.ItemField" + } + }, + "id": { + "type": "string" + }, + "imageId": { + "type": "string", + "x-omitempty": true, + "nullable": true + }, + "insured": { + "type": "boolean" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.LabelSummary" + } + }, + "lifetimeWarranty": { + "description": "Warranty", + "type": "boolean" + }, + "location": { + "description": "Edges", + "allOf": [ + { + "$ref": "#/components/schemas/repo.LocationSummary" + } + ], + "x-omitempty": true, + "nullable": true + }, + "manufacturer": { + "type": "string" + }, + "modelNumber": { + "type": "string" + }, + "name": { + "type": "string" + }, + "notes": { + "description": "Extras", + "type": "string" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/repo.ItemSummary" + } + ], + "x-omitempty": true, + "nullable": true + }, + "purchaseFrom": { + "type": "string" + }, + "purchasePrice": { + "type": "number" + }, + "purchaseTime": { + "description": "Purchase", + "type": "string" + }, + "quantity": { + "type": "integer" + }, + "serialNumber": { + "type": "string" + }, + "soldNotes": { + "type": "string" + }, + "soldPrice": { + "type": "number" + }, + "soldTime": { + "description": "Sold", + "type": "string" + }, + "soldTo": { + "type": "string" + }, + "syncChildItemsLocations": { + "type": "boolean" + }, + "thumbnailId": { + "type": "string", + "x-omitempty": true, + "nullable": true + }, + "updatedAt": { + "type": "string" + }, + "warrantyDetails": { + "type": "string" + }, + "warrantyExpires": { + "type": "string" + } + } + }, + "repo.ItemPatch": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "quantity": { + "type": "integer", + "x-omitempty": true, + "nullable": true + } + } + }, + "repo.ItemPath": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/repo.ItemType" + } + } + }, + "repo.ItemSummary": { + "type": "object", + "properties": { + "archived": { + "type": "boolean" + }, + "assetId": { + "type": "string", + "example": "0" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "imageId": { + "type": "string", + "x-omitempty": true, + "nullable": true + }, + "insured": { + "type": "boolean" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.LabelSummary" + } + }, + "location": { + "description": "Edges", + "allOf": [ + { + "$ref": "#/components/schemas/repo.LocationSummary" + } + ], + "x-omitempty": true, + "nullable": true + }, + "name": { + "type": "string" + }, + "purchasePrice": { + "type": "number" + }, + "quantity": { + "type": "integer" + }, + "soldTime": { + "description": "Sale details", + "type": "string" + }, + "thumbnailId": { + "type": "string", + "x-omitempty": true, + "nullable": true + }, + "updatedAt": { + "type": "string" + } + } + }, + "repo.ItemType": { + "type": "string", + "enum": [ + "location", + "item" + ], + "x-enum-varnames": [ + "ItemTypeLocation", + "ItemTypeItem" + ] + }, + "repo.ItemUpdate": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "archived": { + "type": "boolean" + }, + "assetId": { + "type": "string" + }, + "description": { + "type": "string", + "maxLength": 1000 + }, + "fields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.ItemField" + } + }, + "id": { + "type": "string" + }, + "insured": { + "type": "boolean" + }, + "labelIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "lifetimeWarranty": { + "description": "Warranty", + "type": "boolean" + }, + "locationId": { + "description": "Edges", + "type": "string" + }, + "manufacturer": { + "type": "string" + }, + "modelNumber": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "notes": { + "description": "Extras", + "type": "string" + }, + "parentId": { + "type": "string", + "x-omitempty": true, + "nullable": true + }, + "purchaseFrom": { + "type": "string", + "maxLength": 255 + }, + "purchasePrice": { + "type": "number", + "x-omitempty": true, + "nullable": true + }, + "purchaseTime": { + "description": "Purchase", + "type": "string" + }, + "quantity": { + "type": "integer" + }, + "serialNumber": { + "description": "Identifications", + "type": "string" + }, + "soldNotes": { + "type": "string" + }, + "soldPrice": { + "type": "number", + "x-omitempty": true, + "nullable": true + }, + "soldTime": { + "description": "Sold", + "type": "string" + }, + "soldTo": { + "type": "string", + "maxLength": 255 + }, + "syncChildItemsLocations": { + "type": "boolean" + }, + "warrantyDetails": { + "type": "string" + }, + "warrantyExpires": { + "type": "string" + } + } + }, + "repo.LabelCreate": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "color": { + "type": "string" + }, + "description": { + "type": "string", + "maxLength": 1000 + }, + "name": { + "type": "string", + "maxLength": 255, + "minLength": 1 + } + } + }, + "repo.LabelOut": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "repo.LabelSummary": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "repo.LocationCreate": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "string", + "nullable": true + } + } + }, + "repo.LocationOut": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.LocationSummary" + } + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parent": { + "$ref": "#/components/schemas/repo.LocationSummary" + }, + "totalPrice": { + "type": "number" + }, + "updatedAt": { + "type": "string" + } + } + }, + "repo.LocationOutCount": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "itemCount": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "repo.LocationSummary": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "repo.LocationUpdate": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "string", + "nullable": true + } + } + }, + "repo.MaintenanceEntry": { + "type": "object", + "properties": { + "completedDate": { + "type": "string" + }, + "cost": { + "type": "string", + "example": "0" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scheduledDate": { + "type": "string" + } + } + }, + "repo.MaintenanceEntryCreate": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "completedDate": { + "type": "string" + }, + "cost": { + "type": "string", + "example": "0" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scheduledDate": { + "type": "string" + } + } + }, + "repo.MaintenanceEntryUpdate": { + "type": "object", + "properties": { + "completedDate": { + "type": "string" + }, + "cost": { + "type": "string", + "example": "0" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scheduledDate": { + "type": "string" + } + } + }, + "repo.MaintenanceEntryWithDetails": { + "type": "object", + "properties": { + "completedDate": { + "type": "string" + }, + "cost": { + "type": "string", + "example": "0" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "itemID": { + "type": "string" + }, + "itemName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scheduledDate": { + "type": "string" + } + } + }, + "repo.MaintenanceFilterStatus": { + "type": "string", + "enum": [ + "scheduled", + "completed", + "both" + ], + "x-enum-varnames": [ + "MaintenanceFilterStatusScheduled", + "MaintenanceFilterStatusCompleted", + "MaintenanceFilterStatusBoth" + ] + }, + "repo.NotifierCreate": { + "type": "object", + "required": [ + "name", + "url" + ], + "properties": { + "isActive": { + "type": "boolean" + }, + "name": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "url": { + "type": "string" + } + } + }, + "repo.NotifierOut": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "url": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, + "repo.NotifierUpdate": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "isActive": { + "type": "boolean" + }, + "name": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "url": { + "type": "string", + "nullable": true + } + } + }, + "repo.PaginationResult-repo_ItemSummary": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.ItemSummary" + } + }, + "page": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "repo.TotalsByOrganizer": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "total": { + "type": "number" + } + } + }, + "repo.TreeItem": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.TreeItem" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "repo.UserOut": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isOwner": { + "type": "boolean" + }, + "isSuperuser": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "repo.UserUpdate": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "repo.ValueOverTime": { + "type": "object", + "properties": { + "end": { + "type": "string" + }, + "entries": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repo.ValueOverTimeEntry" + } + }, + "start": { + "type": "string" + }, + "valueAtEnd": { + "type": "number" + }, + "valueAtStart": { + "type": "number" + } + } + }, + "repo.ValueOverTimeEntry": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "number" + } + } + }, + "services.Latest": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "services.UserRegistration": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "password": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "user.Role": { + "type": "string", + "enum": [ + "user", + "user", + "owner" + ], + "x-enum-varnames": [ + "DefaultRole", + "RoleUser", + "RoleOwner" + ] + }, + "v1.APISummary": { + "type": "object", + "properties": { + "allowRegistration": { + "type": "boolean" + }, + "build": { + "$ref": "#/components/schemas/v1.Build" + }, + "demo": { + "type": "boolean" + }, + "health": { + "type": "boolean" + }, + "labelPrinting": { + "type": "boolean" + }, + "latest": { + "$ref": "#/components/schemas/services.Latest" + }, + "message": { + "type": "string" + }, + "title": { + "type": "string" + }, + "versions": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "v1.ActionAmountResult": { + "type": "object", + "properties": { + "completed": { + "type": "integer" + } + } + }, + "v1.Build": { + "type": "object", + "properties": { + "buildTime": { + "type": "string" + }, + "commit": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "v1.ChangePassword": { + "type": "object", + "properties": { + "current": { + "type": "string" + }, + "new": { + "type": "string" + } + } + }, + "v1.GroupInvitation": { + "type": "object", + "properties": { + "expiresAt": { + "type": "string" + }, + "token": { + "type": "string" + }, + "uses": { + "type": "integer" + } + } + }, + "v1.GroupInvitationCreate": { + "type": "object", + "required": [ + "uses" + ], + "properties": { + "expiresAt": { + "type": "string" + }, + "uses": { + "type": "integer", + "maximum": 100, + "minimum": 1 + } + } + }, + "v1.ItemAttachmentToken": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + } + }, + "v1.LoginForm": { + "type": "object", + "properties": { + "password": { + "type": "string", + "example": "admin" + }, + "stayLoggedIn": { + "type": "boolean" + }, + "username": { + "type": "string", + "example": "admin@admin.com" + } + } + }, + "v1.TokenResponse": { + "type": "object", + "properties": { + "attachmentToken": { + "type": "string" + }, + "expiresAt": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "v1.Wrapped": { + "type": "object", + "properties": { + "item": {} + } + }, + "validate.ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "fields": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/app/api/static/docs/openapi-3.yaml b/backend/app/api/static/docs/openapi-3.yaml new file mode 100644 index 00000000..9224a088 --- /dev/null +++ b/backend/app/api/static/docs/openapi-3.yaml @@ -0,0 +1,2888 @@ +openapi: 3.0.0 +info: + description: Track, Manage, and Organize your Things. + title: Homebox API + contact: + name: Homebox Team + url: https://discord.homebox.software + version: "1.0" +paths: + /v1/actions/create-missing-thumbnails: + post: + security: + - Bearer: [] + description: Creates thumbnails for items that are missing them + tags: + - Actions + summary: Create Missing Thumbnails + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/v1.ActionAmountResult" + /v1/actions/ensure-asset-ids: + post: + security: + - Bearer: [] + description: Ensures all items in the database have an asset ID + tags: + - Actions + summary: Ensure Asset IDs + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/v1.ActionAmountResult" + /v1/actions/ensure-import-refs: + post: + security: + - Bearer: [] + description: Ensures all items in the database have an import ref + tags: + - Actions + summary: Ensures Import Refs + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/v1.ActionAmountResult" + /v1/actions/set-primary-photos: + post: + security: + - Bearer: [] + description: Sets the first photo of each item as the primary photo + tags: + - Actions + summary: Set Primary Photos + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/v1.ActionAmountResult" + /v1/actions/zero-item-time-fields: + post: + security: + - Bearer: [] + description: Resets all item date fields to the beginning of the day + tags: + - Actions + summary: Zero Out Time Fields + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/v1.ActionAmountResult" + "/v1/assets/{id}": + get: + security: + - Bearer: [] + tags: + - Items + summary: Get Item by Asset ID + parameters: + - description: Asset ID + name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.PaginationResult-repo_ItemSummary" + /v1/currency: + get: + tags: + - Base + summary: Currency + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/currencies.Currency" + /v1/groups: + get: + security: + - Bearer: [] + tags: + - Group + summary: Get Group + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.Group" + put: + security: + - Bearer: [] + tags: + - Group + summary: Update Group + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.GroupUpdate" + description: User Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.Group" + /v1/groups/invitations: + post: + security: + - Bearer: [] + tags: + - Group + summary: Create Group Invitation + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/v1.GroupInvitationCreate" + description: User Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/v1.GroupInvitation" + /v1/groups/statistics: + get: + security: + - Bearer: [] + tags: + - Statistics + summary: Get Group Statistics + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.GroupStatistics" + /v1/groups/statistics/labels: + get: + security: + - Bearer: [] + tags: + - Statistics + summary: Get Label Statistics + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.TotalsByOrganizer" + /v1/groups/statistics/locations: + get: + security: + - Bearer: [] + tags: + - Statistics + summary: Get Location Statistics + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.TotalsByOrganizer" + /v1/groups/statistics/purchase-price: + get: + security: + - Bearer: [] + tags: + - Statistics + summary: Get Purchase Price Statistics + parameters: + - description: start date + name: start + in: query + schema: + type: string + - description: end date + name: end + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ValueOverTime" + /v1/items: + get: + security: + - Bearer: [] + tags: + - Items + summary: Query All Items + parameters: + - description: search string + name: q + in: query + schema: + type: string + - description: page number + name: page + in: query + schema: + type: integer + - description: items per page + name: pageSize + in: query + schema: + type: integer + - description: label Ids + name: labels + in: query + explode: true + schema: + type: array + items: + type: string + - description: location Ids + name: locations + in: query + explode: true + schema: + type: array + items: + type: string + - description: parent Ids + name: parentIds + in: query + explode: true + schema: + type: array + items: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.PaginationResult-repo_ItemSummary" + post: + security: + - Bearer: [] + tags: + - Items + summary: Create Item + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemCreate" + description: Item Data + required: true + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemSummary" + /v1/items/export: + get: + security: + - Bearer: [] + tags: + - Items + summary: Export Items + responses: + "200": + description: text/csv + content: + "*/*": + schema: + type: string + /v1/items/fields: + get: + security: + - Bearer: [] + tags: + - Items + summary: Get All Custom Field Names + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + type: string + /v1/items/fields/values: + get: + security: + - Bearer: [] + tags: + - Items + summary: Get All Custom Field Values + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + type: string + /v1/items/import: + post: + security: + - Bearer: [] + tags: + - Items + summary: Import Items + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + csv: + description: Image to upload + type: string + format: binary + required: + - csv + required: true + responses: + "204": + description: No Content + "/v1/items/{id}": + get: + security: + - Bearer: [] + tags: + - Items + summary: Get Item + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemOut" + put: + security: + - Bearer: [] + tags: + - Items + summary: Update Item + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemUpdate" + description: Item Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemOut" + delete: + security: + - Bearer: [] + tags: + - Items + summary: Delete Item + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + responses: + "204": + description: No Content + patch: + security: + - Bearer: [] + tags: + - Items + summary: Update Item + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemPatch" + description: Item Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemOut" + "/v1/items/{id}/attachments": + post: + security: + - Bearer: [] + tags: + - Items Attachments + summary: Create Item Attachment + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file: + description: File attachment + type: string + format: binary + type: + description: Type of file + type: string + primary: + description: Is this the primary attachment + type: boolean + name: + description: name of the file including extension + type: string + required: + - file + - name + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemOut" + "422": + description: Unprocessable Entity + content: + application/json: + schema: + $ref: "#/components/schemas/validate.ErrorResponse" + "/v1/items/{id}/attachments/{attachment_id}": + get: + security: + - Bearer: [] + tags: + - Items Attachments + summary: Get Item Attachment + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + - description: Attachment ID + name: attachment_id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/octet-stream: + schema: + $ref: "#/components/schemas/v1.ItemAttachmentToken" + put: + security: + - Bearer: [] + tags: + - Items Attachments + summary: Update Item Attachment + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + - description: Attachment ID + name: attachment_id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemAttachmentUpdate" + description: Attachment Update + required: true + responses: + "200": + description: OK + content: + "*/*": + schema: + $ref: "#/components/schemas/repo.ItemOut" + delete: + security: + - Bearer: [] + tags: + - Items Attachments + summary: Delete Item Attachment + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + - description: Attachment ID + name: attachment_id + in: path + required: true + schema: + type: string + responses: + "204": + description: No Content + "/v1/items/{id}/duplicate": + post: + security: + - Bearer: [] + tags: + - Items + summary: Duplicate Item + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.DuplicateOptions" + description: Duplicate Options + required: true + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/repo.ItemOut" + "/v1/items/{id}/maintenance": + get: + security: + - Bearer: [] + tags: + - Item Maintenance + summary: Get Maintenance Log + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + - x-enum-varnames: + - MaintenanceFilterStatusScheduled + - MaintenanceFilterStatusCompleted + - MaintenanceFilterStatusBoth + name: status + in: query + schema: + type: string + enum: + - scheduled + - completed + - both + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.MaintenanceEntryWithDetails" + post: + security: + - Bearer: [] + tags: + - Item Maintenance + summary: Create Maintenance Entry + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.MaintenanceEntryCreate" + description: Entry Data + required: true + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/repo.MaintenanceEntry" + "/v1/items/{id}/path": + get: + security: + - Bearer: [] + tags: + - Items + summary: Get the full path of an item + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.ItemPath" + "/v1/labelmaker/assets/{id}": + get: + security: + - Bearer: [] + tags: + - Items + summary: Get Asset label + parameters: + - description: Asset ID + name: id + in: path + required: true + schema: + type: string + - description: Print this label, defaults to false + name: print + in: query + schema: + type: boolean + responses: + "200": + description: image/png + content: + application/json: + schema: + type: string + "/v1/labelmaker/item/{id}": + get: + security: + - Bearer: [] + tags: + - Items + summary: Get Item label + parameters: + - description: Item ID + name: id + in: path + required: true + schema: + type: string + - description: Print this label, defaults to false + name: print + in: query + schema: + type: boolean + responses: + "200": + description: image/png + content: + application/json: + schema: + type: string + "/v1/labelmaker/location/{id}": + get: + security: + - Bearer: [] + tags: + - Locations + summary: Get Location label + parameters: + - description: Location ID + name: id + in: path + required: true + schema: + type: string + - description: Print this label, defaults to false + name: print + in: query + schema: + type: boolean + responses: + "200": + description: image/png + content: + application/json: + schema: + type: string + /v1/labels: + get: + security: + - Bearer: [] + tags: + - Labels + summary: Get All Labels + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.LabelOut" + post: + security: + - Bearer: [] + tags: + - Labels + summary: Create Label + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LabelCreate" + description: Label Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LabelSummary" + "/v1/labels/{id}": + get: + security: + - Bearer: [] + tags: + - Labels + summary: Get Label + parameters: + - description: Label ID + name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LabelOut" + put: + security: + - Bearer: [] + tags: + - Labels + summary: Update Label + parameters: + - description: Label ID + name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LabelOut" + delete: + security: + - Bearer: [] + tags: + - Labels + summary: Delete Label + parameters: + - description: Label ID + name: id + in: path + required: true + schema: + type: string + responses: + "204": + description: No Content + /v1/locations: + get: + security: + - Bearer: [] + tags: + - Locations + summary: Get All Locations + parameters: + - description: Filter locations with parents + name: filterChildren + in: query + schema: + type: boolean + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.LocationOutCount" + post: + security: + - Bearer: [] + tags: + - Locations + summary: Create Location + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LocationCreate" + description: Location Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LocationSummary" + /v1/locations/tree: + get: + security: + - Bearer: [] + tags: + - Locations + summary: Get Locations Tree + parameters: + - description: include items in response tree + name: withItems + in: query + schema: + type: boolean + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.TreeItem" + "/v1/locations/{id}": + get: + security: + - Bearer: [] + tags: + - Locations + summary: Get Location + parameters: + - description: Location ID + name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LocationOut" + put: + security: + - Bearer: [] + tags: + - Locations + summary: Update Location + parameters: + - description: Location ID + name: id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LocationUpdate" + description: Location Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.LocationOut" + delete: + security: + - Bearer: [] + tags: + - Locations + summary: Delete Location + parameters: + - description: Location ID + name: id + in: path + required: true + schema: + type: string + responses: + "204": + description: No Content + /v1/maintenance: + get: + security: + - Bearer: [] + tags: + - Maintenance + summary: Query All Maintenance + parameters: + - x-enum-varnames: + - MaintenanceFilterStatusScheduled + - MaintenanceFilterStatusCompleted + - MaintenanceFilterStatusBoth + name: status + in: query + schema: + type: string + enum: + - scheduled + - completed + - both + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.MaintenanceEntryWithDetails" + "/v1/maintenance/{id}": + put: + security: + - Bearer: [] + tags: + - Maintenance + summary: Update Maintenance Entry + parameters: + - description: Maintenance ID + name: id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.MaintenanceEntryUpdate" + description: Entry Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.MaintenanceEntry" + delete: + security: + - Bearer: [] + tags: + - Maintenance + summary: Delete Maintenance Entry + parameters: + - description: Maintenance ID + name: id + in: path + required: true + schema: + type: string + responses: + "204": + description: No Content + /v1/notifiers: + get: + security: + - Bearer: [] + tags: + - Notifiers + summary: Get Notifiers + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.NotifierOut" + post: + security: + - Bearer: [] + tags: + - Notifiers + summary: Create Notifier + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.NotifierCreate" + description: Notifier Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/repo.NotifierOut" + /v1/notifiers/test: + post: + security: + - Bearer: [] + tags: + - Notifiers + summary: Test Notifier + parameters: + - description: URL + name: url + in: query + required: true + schema: + type: string + responses: + "204": + description: No Content + "/v1/notifiers/{id}": + put: + security: + - Bearer: [] + tags: + - Notifiers + summary: Update Notifier + parameters: + - description: Notifier ID + name: id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.NotifierUpdate" + description: Notifier Data + required: true + responses: + "200": + description: OK + content: + "*/*": + schema: + $ref: "#/components/schemas/repo.NotifierOut" + delete: + security: + - Bearer: [] + tags: + - Notifiers + summary: Delete a Notifier + parameters: + - description: Notifier ID + name: id + in: path + required: true + schema: + type: string + responses: + "204": + description: No Content + /v1/products/search-from-barcode: + get: + security: + - Bearer: [] + tags: + - Items + summary: Search EAN from Barcode + parameters: + - description: barcode to be searched + name: data + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/repo.BarcodeProduct" + /v1/qrcode: + get: + security: + - Bearer: [] + tags: + - Items + summary: Create QR Code + parameters: + - description: data to be encoded into qrcode + name: data + in: query + schema: + type: string + responses: + "200": + description: image/jpeg + content: + application/json: + schema: + type: string + /v1/reporting/bill-of-materials: + get: + security: + - Bearer: [] + tags: + - Reporting + summary: Export Bill of Materials + responses: + "200": + description: text/csv + content: + application/json: + schema: + type: string + /v1/status: + get: + tags: + - Base + summary: Application Info + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/v1.APISummary" + /v1/users/change-password: + put: + security: + - Bearer: [] + tags: + - User + summary: Change Password + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/v1.ChangePassword" + description: Password Payload + required: true + responses: + "204": + description: No Content + /v1/users/login: + post: + tags: + - Authentication + summary: User Login + parameters: + - description: auth provider + name: provider + in: query + schema: + type: string + requestBody: + content: + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/v1.LoginForm" + application/json: + schema: + $ref: "#/components/schemas/v1.LoginForm" + description: Login Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/v1.TokenResponse" + /v1/users/logout: + post: + security: + - Bearer: [] + tags: + - Authentication + summary: User Logout + responses: + "204": + description: No Content + /v1/users/refresh: + get: + security: + - Bearer: [] + description: >- + handleAuthRefresh returns a handler that will issue a new token from an + existing token. + + This does not validate that the user still exists within the database. + tags: + - Authentication + summary: User Token Refresh + responses: + "200": + description: OK + /v1/users/register: + post: + tags: + - User + summary: Register New User + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/services.UserRegistration" + description: User Data + required: true + responses: + "204": + description: No Content + /v1/users/self: + get: + security: + - Bearer: [] + tags: + - User + summary: Get User Self + responses: + "200": + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/v1.Wrapped" + - type: object + properties: + item: + $ref: "#/components/schemas/repo.UserOut" + put: + security: + - Bearer: [] + tags: + - User + summary: Update Account + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.UserUpdate" + description: User Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/v1.Wrapped" + - type: object + properties: + item: + $ref: "#/components/schemas/repo.UserUpdate" + delete: + security: + - Bearer: [] + tags: + - User + summary: Delete Account + responses: + "204": + description: No Content +servers: + - url: https://demo.homebox.software/api + - url: http://demo.homebox.software/api +components: + securitySchemes: + Bearer: + description: "\"Type 'Bearer TOKEN' to correctly set the API Key\"" + type: apiKey + name: Authorization + in: header + schemas: + attachment.Type: + type: string + enum: + - attachment + - photo + - manual + - warranty + - attachment + - receipt + - thumbnail + x-enum-varnames: + - DefaultType + - TypePhoto + - TypeManual + - TypeWarranty + - TypeAttachment + - TypeReceipt + - TypeThumbnail + authroles.Role: + type: string + enum: + - user + - admin + - user + - attachments + x-enum-varnames: + - DefaultRole + - RoleAdmin + - RoleUser + - RoleAttachments + currencies.Currency: + type: object + properties: + code: + type: string + decimals: + type: integer + local: + type: string + name: + type: string + symbol: + type: string + ent.Attachment: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the AttachmentQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.AttachmentEdges" + id: + description: ID of the ent. + type: string + mime_type: + description: MimeType holds the value of the "mime_type" field. + type: string + path: + description: Path holds the value of the "path" field. + type: string + primary: + description: Primary holds the value of the "primary" field. + type: boolean + title: + description: Title holds the value of the "title" field. + type: string + type: + description: Type holds the value of the "type" field. + allOf: + - $ref: "#/components/schemas/attachment.Type" + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.AttachmentEdges: + type: object + properties: + item: + description: Item holds the value of the item edge. + allOf: + - $ref: "#/components/schemas/ent.Item" + thumbnail: + description: Thumbnail holds the value of the thumbnail edge. + allOf: + - $ref: "#/components/schemas/ent.Attachment" + ent.AuthRoles: + type: object + properties: + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the AuthRolesQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.AuthRolesEdges" + id: + description: ID of the ent. + type: integer + role: + description: Role holds the value of the "role" field. + allOf: + - $ref: "#/components/schemas/authroles.Role" + ent.AuthRolesEdges: + type: object + properties: + token: + description: Token holds the value of the token edge. + allOf: + - $ref: "#/components/schemas/ent.AuthTokens" + ent.AuthTokens: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the AuthTokensQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.AuthTokensEdges" + expires_at: + description: ExpiresAt holds the value of the "expires_at" field. + type: string + id: + description: ID of the ent. + type: string + token: + description: Token holds the value of the "token" field. + type: array + items: + type: integer + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.AuthTokensEdges: + type: object + properties: + roles: + description: Roles holds the value of the roles edge. + allOf: + - $ref: "#/components/schemas/ent.AuthRoles" + user: + description: User holds the value of the user edge. + allOf: + - $ref: "#/components/schemas/ent.User" + ent.Group: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + currency: + description: Currency holds the value of the "currency" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the GroupQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.GroupEdges" + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.GroupEdges: + type: object + properties: + invitation_tokens: + description: InvitationTokens holds the value of the invitation_tokens edge. + type: array + items: + $ref: "#/components/schemas/ent.GroupInvitationToken" + items: + description: Items holds the value of the items edge. + type: array + items: + $ref: "#/components/schemas/ent.Item" + labels: + description: Labels holds the value of the labels edge. + type: array + items: + $ref: "#/components/schemas/ent.Label" + locations: + description: Locations holds the value of the locations edge. + type: array + items: + $ref: "#/components/schemas/ent.Location" + notifiers: + description: Notifiers holds the value of the notifiers edge. + type: array + items: + $ref: "#/components/schemas/ent.Notifier" + users: + description: Users holds the value of the users edge. + type: array + items: + $ref: "#/components/schemas/ent.User" + ent.GroupInvitationToken: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the GroupInvitationTokenQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.GroupInvitationTokenEdges" + expires_at: + description: ExpiresAt holds the value of the "expires_at" field. + type: string + id: + description: ID of the ent. + type: string + token: + description: Token holds the value of the "token" field. + type: array + items: + type: integer + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + uses: + description: Uses holds the value of the "uses" field. + type: integer + ent.GroupInvitationTokenEdges: + type: object + properties: + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + ent.Item: + type: object + properties: + archived: + description: Archived holds the value of the "archived" field. + type: boolean + asset_id: + description: AssetID holds the value of the "asset_id" field. + type: integer + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the ItemQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.ItemEdges" + id: + description: ID of the ent. + type: string + import_ref: + description: ImportRef holds the value of the "import_ref" field. + type: string + insured: + description: Insured holds the value of the "insured" field. + type: boolean + lifetime_warranty: + description: LifetimeWarranty holds the value of the "lifetime_warranty" field. + type: boolean + manufacturer: + description: Manufacturer holds the value of the "manufacturer" field. + type: string + model_number: + description: ModelNumber holds the value of the "model_number" field. + type: string + name: + description: Name holds the value of the "name" field. + type: string + notes: + description: Notes holds the value of the "notes" field. + type: string + purchase_from: + description: PurchaseFrom holds the value of the "purchase_from" field. + type: string + purchase_price: + description: PurchasePrice holds the value of the "purchase_price" field. + type: number + purchase_time: + description: PurchaseTime holds the value of the "purchase_time" field. + type: string + quantity: + description: Quantity holds the value of the "quantity" field. + type: integer + serial_number: + description: SerialNumber holds the value of the "serial_number" field. + type: string + sold_notes: + description: SoldNotes holds the value of the "sold_notes" field. + type: string + sold_price: + description: SoldPrice holds the value of the "sold_price" field. + type: number + sold_time: + description: SoldTime holds the value of the "sold_time" field. + type: string + sold_to: + description: SoldTo holds the value of the "sold_to" field. + type: string + sync_child_items_locations: + description: SyncChildItemsLocations holds the value of the + "sync_child_items_locations" field. + type: boolean + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + warranty_details: + description: WarrantyDetails holds the value of the "warranty_details" field. + type: string + warranty_expires: + description: WarrantyExpires holds the value of the "warranty_expires" field. + type: string + ent.ItemEdges: + type: object + properties: + attachments: + description: Attachments holds the value of the attachments edge. + type: array + items: + $ref: "#/components/schemas/ent.Attachment" + children: + description: Children holds the value of the children edge. + type: array + items: + $ref: "#/components/schemas/ent.Item" + fields: + description: Fields holds the value of the fields edge. + type: array + items: + $ref: "#/components/schemas/ent.ItemField" + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + label: + description: Label holds the value of the label edge. + type: array + items: + $ref: "#/components/schemas/ent.Label" + location: + description: Location holds the value of the location edge. + allOf: + - $ref: "#/components/schemas/ent.Location" + maintenance_entries: + description: MaintenanceEntries holds the value of the maintenance_entries edge. + type: array + items: + $ref: "#/components/schemas/ent.MaintenanceEntry" + parent: + description: Parent holds the value of the parent edge. + allOf: + - $ref: "#/components/schemas/ent.Item" + ent.ItemField: + type: object + properties: + boolean_value: + description: BooleanValue holds the value of the "boolean_value" field. + type: boolean + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the ItemFieldQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.ItemFieldEdges" + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + number_value: + description: NumberValue holds the value of the "number_value" field. + type: integer + text_value: + description: TextValue holds the value of the "text_value" field. + type: string + time_value: + description: TimeValue holds the value of the "time_value" field. + type: string + type: + description: Type holds the value of the "type" field. + allOf: + - $ref: "#/components/schemas/itemfield.Type" + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.ItemFieldEdges: + type: object + properties: + item: + description: Item holds the value of the item edge. + allOf: + - $ref: "#/components/schemas/ent.Item" + ent.Label: + type: object + properties: + color: + description: Color holds the value of the "color" field. + type: string + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the LabelQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.LabelEdges" + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.LabelEdges: + type: object + properties: + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + items: + description: Items holds the value of the items edge. + type: array + items: + $ref: "#/components/schemas/ent.Item" + ent.Location: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the LocationQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.LocationEdges" + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.LocationEdges: + type: object + properties: + children: + description: Children holds the value of the children edge. + type: array + items: + $ref: "#/components/schemas/ent.Location" + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + items: + description: Items holds the value of the items edge. + type: array + items: + $ref: "#/components/schemas/ent.Item" + parent: + description: Parent holds the value of the parent edge. + allOf: + - $ref: "#/components/schemas/ent.Location" + ent.MaintenanceEntry: + type: object + properties: + cost: + description: Cost holds the value of the "cost" field. + type: number + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + date: + description: Date holds the value of the "date" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the MaintenanceEntryQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.MaintenanceEntryEdges" + id: + description: ID of the ent. + type: string + item_id: + description: ItemID holds the value of the "item_id" field. + type: string + name: + description: Name holds the value of the "name" field. + type: string + scheduled_date: + description: ScheduledDate holds the value of the "scheduled_date" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.MaintenanceEntryEdges: + type: object + properties: + item: + description: Item holds the value of the item edge. + allOf: + - $ref: "#/components/schemas/ent.Item" + ent.Notifier: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the NotifierQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.NotifierEdges" + group_id: + description: GroupID holds the value of the "group_id" field. + type: string + id: + description: ID of the ent. + type: string + is_active: + description: IsActive holds the value of the "is_active" field. + type: boolean + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + user_id: + description: UserID holds the value of the "user_id" field. + type: string + ent.NotifierEdges: + type: object + properties: + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + user: + description: User holds the value of the user edge. + allOf: + - $ref: "#/components/schemas/ent.User" + ent.User: + type: object + properties: + activated_on: + description: ActivatedOn holds the value of the "activated_on" field. + type: string + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the UserQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.UserEdges" + email: + description: Email holds the value of the "email" field. + type: string + id: + description: ID of the ent. + type: string + is_superuser: + description: IsSuperuser holds the value of the "is_superuser" field. + type: boolean + name: + description: Name holds the value of the "name" field. + type: string + role: + description: Role holds the value of the "role" field. + allOf: + - $ref: "#/components/schemas/user.Role" + superuser: + description: Superuser holds the value of the "superuser" field. + type: boolean + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.UserEdges: + type: object + properties: + auth_tokens: + description: AuthTokens holds the value of the auth_tokens edge. + type: array + items: + $ref: "#/components/schemas/ent.AuthTokens" + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + notifiers: + description: Notifiers holds the value of the notifiers edge. + type: array + items: + $ref: "#/components/schemas/ent.Notifier" + itemfield.Type: + type: string + enum: + - text + - number + - boolean + - time + x-enum-varnames: + - TypeText + - TypeNumber + - TypeBoolean + - TypeTime + repo.BarcodeProduct: + type: object + properties: + barcode: + type: string + imageBase64: + type: string + imageURL: + type: string + item: + $ref: "#/components/schemas/repo.ItemCreate" + manufacturer: + type: string + modelNumber: + description: Identifications + type: string + notes: + description: Extras + type: string + search_engine_name: + type: string + repo.DuplicateOptions: + type: object + properties: + copyAttachments: + type: boolean + copyCustomFields: + type: boolean + copyMaintenance: + type: boolean + copyPrefix: + type: string + repo.Group: + type: object + properties: + createdAt: + type: string + currency: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + repo.GroupStatistics: + type: object + properties: + totalItemPrice: + type: number + totalItems: + type: integer + totalLabels: + type: integer + totalLocations: + type: integer + totalUsers: + type: integer + totalWithWarranty: + type: integer + repo.GroupUpdate: + type: object + properties: + currency: + type: string + name: + type: string + repo.ItemAttachment: + type: object + properties: + createdAt: + type: string + id: + type: string + mimeType: + type: string + path: + type: string + primary: + type: boolean + thumbnail: + $ref: "#/components/schemas/ent.Attachment" + title: + type: string + type: + type: string + updatedAt: + type: string + repo.ItemAttachmentUpdate: + type: object + properties: + primary: + type: boolean + title: + type: string + type: + type: string + repo.ItemCreate: + type: object + required: + - name + properties: + description: + type: string + maxLength: 1000 + labelIds: + type: array + items: + type: string + locationId: + description: Edges + type: string + name: + type: string + maxLength: 255 + minLength: 1 + parentId: + type: string + nullable: true + quantity: + type: integer + repo.ItemField: + type: object + properties: + booleanValue: + type: boolean + id: + type: string + name: + type: string + numberValue: + type: integer + textValue: + type: string + type: + type: string + repo.ItemOut: + type: object + properties: + archived: + type: boolean + assetId: + type: string + example: "0" + attachments: + type: array + items: + $ref: "#/components/schemas/repo.ItemAttachment" + createdAt: + type: string + description: + type: string + fields: + type: array + items: + $ref: "#/components/schemas/repo.ItemField" + id: + type: string + imageId: + type: string + x-omitempty: true + nullable: true + insured: + type: boolean + labels: + type: array + items: + $ref: "#/components/schemas/repo.LabelSummary" + lifetimeWarranty: + description: Warranty + type: boolean + location: + description: Edges + allOf: + - $ref: "#/components/schemas/repo.LocationSummary" + x-omitempty: true + nullable: true + manufacturer: + type: string + modelNumber: + type: string + name: + type: string + notes: + description: Extras + type: string + parent: + allOf: + - $ref: "#/components/schemas/repo.ItemSummary" + x-omitempty: true + nullable: true + purchaseFrom: + type: string + purchasePrice: + type: number + purchaseTime: + description: Purchase + type: string + quantity: + type: integer + serialNumber: + type: string + soldNotes: + type: string + soldPrice: + type: number + soldTime: + description: Sold + type: string + soldTo: + type: string + syncChildItemsLocations: + type: boolean + thumbnailId: + type: string + x-omitempty: true + nullable: true + updatedAt: + type: string + warrantyDetails: + type: string + warrantyExpires: + type: string + repo.ItemPatch: + type: object + properties: + id: + type: string + quantity: + type: integer + x-omitempty: true + nullable: true + repo.ItemPath: + type: object + properties: + id: + type: string + name: + type: string + type: + $ref: "#/components/schemas/repo.ItemType" + repo.ItemSummary: + type: object + properties: + archived: + type: boolean + assetId: + type: string + example: "0" + createdAt: + type: string + description: + type: string + id: + type: string + imageId: + type: string + x-omitempty: true + nullable: true + insured: + type: boolean + labels: + type: array + items: + $ref: "#/components/schemas/repo.LabelSummary" + location: + description: Edges + allOf: + - $ref: "#/components/schemas/repo.LocationSummary" + x-omitempty: true + nullable: true + name: + type: string + purchasePrice: + type: number + quantity: + type: integer + soldTime: + description: Sale details + type: string + thumbnailId: + type: string + x-omitempty: true + nullable: true + updatedAt: + type: string + repo.ItemType: + type: string + enum: + - location + - item + x-enum-varnames: + - ItemTypeLocation + - ItemTypeItem + repo.ItemUpdate: + type: object + required: + - name + properties: + archived: + type: boolean + assetId: + type: string + description: + type: string + maxLength: 1000 + fields: + type: array + items: + $ref: "#/components/schemas/repo.ItemField" + id: + type: string + insured: + type: boolean + labelIds: + type: array + items: + type: string + lifetimeWarranty: + description: Warranty + type: boolean + locationId: + description: Edges + type: string + manufacturer: + type: string + modelNumber: + type: string + name: + type: string + maxLength: 255 + minLength: 1 + notes: + description: Extras + type: string + parentId: + type: string + x-omitempty: true + nullable: true + purchaseFrom: + type: string + maxLength: 255 + purchasePrice: + type: number + x-omitempty: true + nullable: true + purchaseTime: + description: Purchase + type: string + quantity: + type: integer + serialNumber: + description: Identifications + type: string + soldNotes: + type: string + soldPrice: + type: number + x-omitempty: true + nullable: true + soldTime: + description: Sold + type: string + soldTo: + type: string + maxLength: 255 + syncChildItemsLocations: + type: boolean + warrantyDetails: + type: string + warrantyExpires: + type: string + repo.LabelCreate: + type: object + required: + - name + properties: + color: + type: string + description: + type: string + maxLength: 1000 + name: + type: string + maxLength: 255 + minLength: 1 + repo.LabelOut: + type: object + properties: + color: + type: string + createdAt: + type: string + description: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + repo.LabelSummary: + type: object + properties: + color: + type: string + createdAt: + type: string + description: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + repo.LocationCreate: + type: object + properties: + description: + type: string + name: + type: string + parentId: + type: string + nullable: true + repo.LocationOut: + type: object + properties: + children: + type: array + items: + $ref: "#/components/schemas/repo.LocationSummary" + createdAt: + type: string + description: + type: string + id: + type: string + name: + type: string + parent: + $ref: "#/components/schemas/repo.LocationSummary" + totalPrice: + type: number + updatedAt: + type: string + repo.LocationOutCount: + type: object + properties: + createdAt: + type: string + description: + type: string + id: + type: string + itemCount: + type: integer + name: + type: string + updatedAt: + type: string + repo.LocationSummary: + type: object + properties: + createdAt: + type: string + description: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + repo.LocationUpdate: + type: object + properties: + description: + type: string + id: + type: string + name: + type: string + parentId: + type: string + nullable: true + repo.MaintenanceEntry: + type: object + properties: + completedDate: + type: string + cost: + type: string + example: "0" + description: + type: string + id: + type: string + name: + type: string + scheduledDate: + type: string + repo.MaintenanceEntryCreate: + type: object + required: + - name + properties: + completedDate: + type: string + cost: + type: string + example: "0" + description: + type: string + name: + type: string + scheduledDate: + type: string + repo.MaintenanceEntryUpdate: + type: object + properties: + completedDate: + type: string + cost: + type: string + example: "0" + description: + type: string + name: + type: string + scheduledDate: + type: string + repo.MaintenanceEntryWithDetails: + type: object + properties: + completedDate: + type: string + cost: + type: string + example: "0" + description: + type: string + id: + type: string + itemID: + type: string + itemName: + type: string + name: + type: string + scheduledDate: + type: string + repo.MaintenanceFilterStatus: + type: string + enum: + - scheduled + - completed + - both + x-enum-varnames: + - MaintenanceFilterStatusScheduled + - MaintenanceFilterStatusCompleted + - MaintenanceFilterStatusBoth + repo.NotifierCreate: + type: object + required: + - name + - url + properties: + isActive: + type: boolean + name: + type: string + maxLength: 255 + minLength: 1 + url: + type: string + repo.NotifierOut: + type: object + properties: + createdAt: + type: string + groupId: + type: string + id: + type: string + isActive: + type: boolean + name: + type: string + updatedAt: + type: string + url: + type: string + userId: + type: string + repo.NotifierUpdate: + type: object + required: + - name + properties: + isActive: + type: boolean + name: + type: string + maxLength: 255 + minLength: 1 + url: + type: string + nullable: true + repo.PaginationResult-repo_ItemSummary: + type: object + properties: + items: + type: array + items: + $ref: "#/components/schemas/repo.ItemSummary" + page: + type: integer + pageSize: + type: integer + total: + type: integer + repo.TotalsByOrganizer: + type: object + properties: + id: + type: string + name: + type: string + total: + type: number + repo.TreeItem: + type: object + properties: + children: + type: array + items: + $ref: "#/components/schemas/repo.TreeItem" + id: + type: string + name: + type: string + type: + type: string + repo.UserOut: + type: object + properties: + email: + type: string + groupId: + type: string + groupName: + type: string + id: + type: string + isOwner: + type: boolean + isSuperuser: + type: boolean + name: + type: string + repo.UserUpdate: + type: object + properties: + email: + type: string + name: + type: string + repo.ValueOverTime: + type: object + properties: + end: + type: string + entries: + type: array + items: + $ref: "#/components/schemas/repo.ValueOverTimeEntry" + start: + type: string + valueAtEnd: + type: number + valueAtStart: + type: number + repo.ValueOverTimeEntry: + type: object + properties: + date: + type: string + name: + type: string + value: + type: number + services.Latest: + type: object + properties: + date: + type: string + version: + type: string + services.UserRegistration: + type: object + properties: + email: + type: string + name: + type: string + password: + type: string + token: + type: string + user.Role: + type: string + enum: + - user + - user + - owner + x-enum-varnames: + - DefaultRole + - RoleUser + - RoleOwner + v1.APISummary: + type: object + properties: + allowRegistration: + type: boolean + build: + $ref: "#/components/schemas/v1.Build" + demo: + type: boolean + health: + type: boolean + labelPrinting: + type: boolean + latest: + $ref: "#/components/schemas/services.Latest" + message: + type: string + title: + type: string + versions: + type: array + items: + type: string + v1.ActionAmountResult: + type: object + properties: + completed: + type: integer + v1.Build: + type: object + properties: + buildTime: + type: string + commit: + type: string + version: + type: string + v1.ChangePassword: + type: object + properties: + current: + type: string + new: + type: string + v1.GroupInvitation: + type: object + properties: + expiresAt: + type: string + token: + type: string + uses: + type: integer + v1.GroupInvitationCreate: + type: object + required: + - uses + properties: + expiresAt: + type: string + uses: + type: integer + maximum: 100 + minimum: 1 + v1.ItemAttachmentToken: + type: object + properties: + token: + type: string + v1.LoginForm: + type: object + properties: + password: + type: string + example: admin + stayLoggedIn: + type: boolean + username: + type: string + example: admin@admin.com + v1.TokenResponse: + type: object + properties: + attachmentToken: + type: string + expiresAt: + type: string + token: + type: string + v1.Wrapped: + type: object + properties: + item: {} + validate.ErrorResponse: + type: object + properties: + error: + type: string + fields: + type: string diff --git a/backend/app/api/static/docs/swagger.json b/backend/app/api/static/docs/swagger.json index c62359b9..5c5e7aa2 100644 --- a/backend/app/api/static/docs/swagger.json +++ b/backend/app/api/static/docs/swagger.json @@ -2238,6 +2238,9 @@ "code": { "type": "string" }, + "decimals": { + "type": "integer" + }, "local": { "type": "string" }, diff --git a/backend/app/api/static/docs/swagger.yaml b/backend/app/api/static/docs/swagger.yaml index 8289cc18..ac352ed2 100644 --- a/backend/app/api/static/docs/swagger.yaml +++ b/backend/app/api/static/docs/swagger.yaml @@ -34,6 +34,8 @@ definitions: properties: code: type: string + decimals: + type: integer local: type: string name: diff --git a/docs/en/api/index.md b/docs/en/api/index.md index cc68aa48..e97dd4c2 100644 --- a/docs/en/api/index.md +++ b/docs/en/api/index.md @@ -50,7 +50,7 @@ document.head.appendChild(elementStyle); - + handleAuthRefresh returns a handler that will issue a new token from an + existing token. + + This does not validate that the user still exists within the database. + tags: + - Authentication + summary: User Token Refresh + responses: + "200": + description: OK + /v1/users/register: + post: + tags: + - User + summary: Register New User + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/services.UserRegistration" + description: User Data + required: true + responses: + "204": + description: No Content + /v1/users/self: + get: + security: + - Bearer: [] + tags: + - User + summary: Get User Self + responses: + "200": + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/v1.Wrapped" + - type: object + properties: + item: + $ref: "#/components/schemas/repo.UserOut" + put: + security: + - Bearer: [] + tags: + - User + summary: Update Account + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/repo.UserUpdate" + description: User Data + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/v1.Wrapped" + - type: object + properties: + item: + $ref: "#/components/schemas/repo.UserUpdate" + delete: + security: + - Bearer: [] + tags: + - User + summary: Delete Account + responses: + "204": + description: No Content +servers: + - url: https://demo.homebox.software/api + - url: http://demo.homebox.software/api +components: + securitySchemes: + Bearer: + description: "\"Type 'Bearer TOKEN' to correctly set the API Key\"" + type: apiKey + name: Authorization + in: header + schemas: + attachment.Type: + type: string + enum: + - attachment + - photo + - manual + - warranty + - attachment + - receipt + - thumbnail + x-enum-varnames: + - DefaultType + - TypePhoto + - TypeManual + - TypeWarranty + - TypeAttachment + - TypeReceipt + - TypeThumbnail + authroles.Role: + type: string + enum: + - user + - admin + - user + - attachments + x-enum-varnames: + - DefaultRole + - RoleAdmin + - RoleUser + - RoleAttachments + currencies.Currency: + type: object + properties: + code: + type: string + decimals: + type: integer + local: + type: string + name: + type: string + symbol: + type: string + ent.Attachment: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the AttachmentQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.AttachmentEdges" + id: + description: ID of the ent. + type: string + mime_type: + description: MimeType holds the value of the "mime_type" field. + type: string + path: + description: Path holds the value of the "path" field. + type: string + primary: + description: Primary holds the value of the "primary" field. + type: boolean + title: + description: Title holds the value of the "title" field. + type: string + type: + description: Type holds the value of the "type" field. + allOf: + - $ref: "#/components/schemas/attachment.Type" + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.AttachmentEdges: + type: object + properties: + item: + description: Item holds the value of the item edge. + allOf: + - $ref: "#/components/schemas/ent.Item" + thumbnail: + description: Thumbnail holds the value of the thumbnail edge. + allOf: + - $ref: "#/components/schemas/ent.Attachment" + ent.AuthRoles: + type: object + properties: + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the AuthRolesQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.AuthRolesEdges" + id: + description: ID of the ent. + type: integer + role: + description: Role holds the value of the "role" field. + allOf: + - $ref: "#/components/schemas/authroles.Role" + ent.AuthRolesEdges: + type: object + properties: + token: + description: Token holds the value of the token edge. + allOf: + - $ref: "#/components/schemas/ent.AuthTokens" + ent.AuthTokens: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the AuthTokensQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.AuthTokensEdges" + expires_at: + description: ExpiresAt holds the value of the "expires_at" field. + type: string + id: + description: ID of the ent. + type: string + token: + description: Token holds the value of the "token" field. + type: array + items: + type: integer + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.AuthTokensEdges: + type: object + properties: + roles: + description: Roles holds the value of the roles edge. + allOf: + - $ref: "#/components/schemas/ent.AuthRoles" + user: + description: User holds the value of the user edge. + allOf: + - $ref: "#/components/schemas/ent.User" + ent.Group: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + currency: + description: Currency holds the value of the "currency" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the GroupQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.GroupEdges" + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.GroupEdges: + type: object + properties: + invitation_tokens: + description: InvitationTokens holds the value of the invitation_tokens edge. + type: array + items: + $ref: "#/components/schemas/ent.GroupInvitationToken" + items: + description: Items holds the value of the items edge. + type: array + items: + $ref: "#/components/schemas/ent.Item" + labels: + description: Labels holds the value of the labels edge. + type: array + items: + $ref: "#/components/schemas/ent.Label" + locations: + description: Locations holds the value of the locations edge. + type: array + items: + $ref: "#/components/schemas/ent.Location" + notifiers: + description: Notifiers holds the value of the notifiers edge. + type: array + items: + $ref: "#/components/schemas/ent.Notifier" + users: + description: Users holds the value of the users edge. + type: array + items: + $ref: "#/components/schemas/ent.User" + ent.GroupInvitationToken: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the GroupInvitationTokenQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.GroupInvitationTokenEdges" + expires_at: + description: ExpiresAt holds the value of the "expires_at" field. + type: string + id: + description: ID of the ent. + type: string + token: + description: Token holds the value of the "token" field. + type: array + items: + type: integer + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + uses: + description: Uses holds the value of the "uses" field. + type: integer + ent.GroupInvitationTokenEdges: + type: object + properties: + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + ent.Item: + type: object + properties: + archived: + description: Archived holds the value of the "archived" field. + type: boolean + asset_id: + description: AssetID holds the value of the "asset_id" field. + type: integer + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the ItemQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.ItemEdges" + id: + description: ID of the ent. + type: string + import_ref: + description: ImportRef holds the value of the "import_ref" field. + type: string + insured: + description: Insured holds the value of the "insured" field. + type: boolean + lifetime_warranty: + description: LifetimeWarranty holds the value of the "lifetime_warranty" field. + type: boolean + manufacturer: + description: Manufacturer holds the value of the "manufacturer" field. + type: string + model_number: + description: ModelNumber holds the value of the "model_number" field. + type: string + name: + description: Name holds the value of the "name" field. + type: string + notes: + description: Notes holds the value of the "notes" field. + type: string + purchase_from: + description: PurchaseFrom holds the value of the "purchase_from" field. + type: string + purchase_price: + description: PurchasePrice holds the value of the "purchase_price" field. + type: number + purchase_time: + description: PurchaseTime holds the value of the "purchase_time" field. + type: string + quantity: + description: Quantity holds the value of the "quantity" field. + type: integer + serial_number: + description: SerialNumber holds the value of the "serial_number" field. + type: string + sold_notes: + description: SoldNotes holds the value of the "sold_notes" field. + type: string + sold_price: + description: SoldPrice holds the value of the "sold_price" field. + type: number + sold_time: + description: SoldTime holds the value of the "sold_time" field. + type: string + sold_to: + description: SoldTo holds the value of the "sold_to" field. + type: string + sync_child_items_locations: + description: SyncChildItemsLocations holds the value of the + "sync_child_items_locations" field. + type: boolean + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + warranty_details: + description: WarrantyDetails holds the value of the "warranty_details" field. + type: string + warranty_expires: + description: WarrantyExpires holds the value of the "warranty_expires" field. + type: string + ent.ItemEdges: + type: object + properties: + attachments: + description: Attachments holds the value of the attachments edge. + type: array + items: + $ref: "#/components/schemas/ent.Attachment" + children: + description: Children holds the value of the children edge. + type: array + items: + $ref: "#/components/schemas/ent.Item" + fields: + description: Fields holds the value of the fields edge. + type: array + items: + $ref: "#/components/schemas/ent.ItemField" + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + label: + description: Label holds the value of the label edge. + type: array + items: + $ref: "#/components/schemas/ent.Label" + location: + description: Location holds the value of the location edge. + allOf: + - $ref: "#/components/schemas/ent.Location" + maintenance_entries: + description: MaintenanceEntries holds the value of the maintenance_entries edge. + type: array + items: + $ref: "#/components/schemas/ent.MaintenanceEntry" + parent: + description: Parent holds the value of the parent edge. + allOf: + - $ref: "#/components/schemas/ent.Item" + ent.ItemField: + type: object + properties: + boolean_value: + description: BooleanValue holds the value of the "boolean_value" field. + type: boolean + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the ItemFieldQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.ItemFieldEdges" + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + number_value: + description: NumberValue holds the value of the "number_value" field. + type: integer + text_value: + description: TextValue holds the value of the "text_value" field. + type: string + time_value: + description: TimeValue holds the value of the "time_value" field. + type: string + type: + description: Type holds the value of the "type" field. + allOf: + - $ref: "#/components/schemas/itemfield.Type" + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.ItemFieldEdges: + type: object + properties: + item: + description: Item holds the value of the item edge. + allOf: + - $ref: "#/components/schemas/ent.Item" + ent.Label: + type: object + properties: + color: + description: Color holds the value of the "color" field. + type: string + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the LabelQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.LabelEdges" + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.LabelEdges: + type: object + properties: + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + items: + description: Items holds the value of the items edge. + type: array + items: + $ref: "#/components/schemas/ent.Item" + ent.Location: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the LocationQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.LocationEdges" + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.LocationEdges: + type: object + properties: + children: + description: Children holds the value of the children edge. + type: array + items: + $ref: "#/components/schemas/ent.Location" + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + items: + description: Items holds the value of the items edge. + type: array + items: + $ref: "#/components/schemas/ent.Item" + parent: + description: Parent holds the value of the parent edge. + allOf: + - $ref: "#/components/schemas/ent.Location" + ent.MaintenanceEntry: + type: object + properties: + cost: + description: Cost holds the value of the "cost" field. + type: number + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + date: + description: Date holds the value of the "date" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the MaintenanceEntryQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.MaintenanceEntryEdges" + id: + description: ID of the ent. + type: string + item_id: + description: ItemID holds the value of the "item_id" field. + type: string + name: + description: Name holds the value of the "name" field. + type: string + scheduled_date: + description: ScheduledDate holds the value of the "scheduled_date" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.MaintenanceEntryEdges: + type: object + properties: + item: + description: Item holds the value of the item edge. + allOf: + - $ref: "#/components/schemas/ent.Item" + ent.Notifier: + type: object + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the NotifierQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.NotifierEdges" + group_id: + description: GroupID holds the value of the "group_id" field. + type: string + id: + description: ID of the ent. + type: string + is_active: + description: IsActive holds the value of the "is_active" field. + type: boolean + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + user_id: + description: UserID holds the value of the "user_id" field. + type: string + ent.NotifierEdges: + type: object + properties: + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + user: + description: User holds the value of the user edge. + allOf: + - $ref: "#/components/schemas/ent.User" + ent.User: + type: object + properties: + activated_on: + description: ActivatedOn holds the value of the "activated_on" field. + type: string + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + description: >- + Edges holds the relations/edges for other nodes in the graph. + + The values are being populated by the UserQuery when eager-loading is set. + allOf: + - $ref: "#/components/schemas/ent.UserEdges" + email: + description: Email holds the value of the "email" field. + type: string + id: + description: ID of the ent. + type: string + is_superuser: + description: IsSuperuser holds the value of the "is_superuser" field. + type: boolean + name: + description: Name holds the value of the "name" field. + type: string + role: + description: Role holds the value of the "role" field. + allOf: + - $ref: "#/components/schemas/user.Role" + superuser: + description: Superuser holds the value of the "superuser" field. + type: boolean + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + ent.UserEdges: + type: object + properties: + auth_tokens: + description: AuthTokens holds the value of the auth_tokens edge. + type: array + items: + $ref: "#/components/schemas/ent.AuthTokens" + group: + description: Group holds the value of the group edge. + allOf: + - $ref: "#/components/schemas/ent.Group" + notifiers: + description: Notifiers holds the value of the notifiers edge. + type: array + items: + $ref: "#/components/schemas/ent.Notifier" + itemfield.Type: + type: string + enum: + - text + - number + - boolean + - time + x-enum-varnames: + - TypeText + - TypeNumber + - TypeBoolean + - TypeTime + repo.BarcodeProduct: + type: object + properties: + barcode: + type: string + imageBase64: + type: string + imageURL: + type: string + item: + $ref: "#/components/schemas/repo.ItemCreate" + manufacturer: + type: string + modelNumber: + description: Identifications + type: string + notes: + description: Extras + type: string + search_engine_name: + type: string + repo.DuplicateOptions: + type: object + properties: + copyAttachments: + type: boolean + copyCustomFields: + type: boolean + copyMaintenance: + type: boolean + copyPrefix: + type: string + repo.Group: + type: object + properties: + createdAt: + type: string + currency: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + repo.GroupStatistics: + type: object + properties: + totalItemPrice: + type: number + totalItems: + type: integer + totalLabels: + type: integer + totalLocations: + type: integer + totalUsers: + type: integer + totalWithWarranty: + type: integer + repo.GroupUpdate: + type: object + properties: + currency: + type: string + name: + type: string + repo.ItemAttachment: + type: object + properties: + createdAt: + type: string + id: + type: string + mimeType: + type: string + path: + type: string + primary: + type: boolean + thumbnail: + $ref: "#/components/schemas/ent.Attachment" + title: + type: string + type: + type: string + updatedAt: + type: string + repo.ItemAttachmentUpdate: + type: object + properties: + primary: + type: boolean + title: + type: string + type: + type: string + repo.ItemCreate: + type: object + required: + - name + properties: + description: + type: string + maxLength: 1000 + labelIds: + type: array + items: + type: string + locationId: + description: Edges + type: string + name: + type: string + maxLength: 255 + minLength: 1 + parentId: + type: string + nullable: true + quantity: + type: integer + repo.ItemField: + type: object + properties: + booleanValue: + type: boolean + id: + type: string + name: + type: string + numberValue: + type: integer + textValue: + type: string + type: + type: string + repo.ItemOut: + type: object + properties: + archived: + type: boolean + assetId: + type: string + example: "0" + attachments: + type: array + items: + $ref: "#/components/schemas/repo.ItemAttachment" + createdAt: + type: string + description: + type: string + fields: + type: array + items: + $ref: "#/components/schemas/repo.ItemField" + id: + type: string + imageId: + type: string + x-omitempty: true + nullable: true + insured: + type: boolean + labels: + type: array + items: + $ref: "#/components/schemas/repo.LabelSummary" + lifetimeWarranty: + description: Warranty + type: boolean + location: + description: Edges + allOf: + - $ref: "#/components/schemas/repo.LocationSummary" + x-omitempty: true + nullable: true + manufacturer: + type: string + modelNumber: + type: string + name: + type: string + notes: + description: Extras + type: string + parent: + allOf: + - $ref: "#/components/schemas/repo.ItemSummary" + x-omitempty: true + nullable: true + purchaseFrom: + type: string + purchasePrice: + type: number + purchaseTime: + description: Purchase + type: string + quantity: + type: integer + serialNumber: + type: string + soldNotes: + type: string + soldPrice: + type: number + soldTime: + description: Sold + type: string + soldTo: + type: string + syncChildItemsLocations: + type: boolean + thumbnailId: + type: string + x-omitempty: true + nullable: true + updatedAt: + type: string + warrantyDetails: + type: string + warrantyExpires: + type: string + repo.ItemPatch: + type: object + properties: + id: + type: string + quantity: + type: integer + x-omitempty: true + nullable: true + repo.ItemPath: + type: object + properties: + id: + type: string + name: + type: string + type: + $ref: "#/components/schemas/repo.ItemType" + repo.ItemSummary: + type: object + properties: + archived: + type: boolean + assetId: + type: string + example: "0" + createdAt: + type: string + description: + type: string + id: + type: string + imageId: + type: string + x-omitempty: true + nullable: true + insured: + type: boolean + labels: + type: array + items: + $ref: "#/components/schemas/repo.LabelSummary" + location: + description: Edges + allOf: + - $ref: "#/components/schemas/repo.LocationSummary" + x-omitempty: true + nullable: true + name: + type: string + purchasePrice: + type: number + quantity: + type: integer + soldTime: + description: Sale details + type: string + thumbnailId: + type: string + x-omitempty: true + nullable: true + updatedAt: + type: string + repo.ItemType: + type: string + enum: + - location + - item + x-enum-varnames: + - ItemTypeLocation + - ItemTypeItem + repo.ItemUpdate: + type: object + required: + - name + properties: + archived: + type: boolean + assetId: + type: string + description: + type: string + maxLength: 1000 + fields: + type: array + items: + $ref: "#/components/schemas/repo.ItemField" + id: + type: string + insured: + type: boolean + labelIds: + type: array + items: + type: string + lifetimeWarranty: + description: Warranty + type: boolean + locationId: + description: Edges + type: string + manufacturer: + type: string + modelNumber: + type: string + name: + type: string + maxLength: 255 + minLength: 1 + notes: + description: Extras + type: string + parentId: + type: string + x-omitempty: true + nullable: true + purchaseFrom: + type: string + maxLength: 255 + purchasePrice: + type: number + x-omitempty: true + nullable: true + purchaseTime: + description: Purchase + type: string + quantity: + type: integer + serialNumber: + description: Identifications + type: string + soldNotes: + type: string + soldPrice: + type: number + x-omitempty: true + nullable: true + soldTime: + description: Sold + type: string + soldTo: + type: string + maxLength: 255 + syncChildItemsLocations: + type: boolean + warrantyDetails: + type: string + warrantyExpires: + type: string + repo.LabelCreate: + type: object + required: + - name + properties: + color: + type: string + description: + type: string + maxLength: 1000 + name: + type: string + maxLength: 255 + minLength: 1 + repo.LabelOut: + type: object + properties: + color: + type: string + createdAt: + type: string + description: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + repo.LabelSummary: + type: object + properties: + color: + type: string + createdAt: + type: string + description: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + repo.LocationCreate: + type: object + properties: + description: + type: string + name: + type: string + parentId: + type: string + nullable: true + repo.LocationOut: + type: object + properties: + children: + type: array + items: + $ref: "#/components/schemas/repo.LocationSummary" + createdAt: + type: string + description: + type: string + id: + type: string + name: + type: string + parent: + $ref: "#/components/schemas/repo.LocationSummary" + totalPrice: + type: number + updatedAt: + type: string + repo.LocationOutCount: + type: object + properties: + createdAt: + type: string + description: + type: string + id: + type: string + itemCount: + type: integer + name: + type: string + updatedAt: + type: string + repo.LocationSummary: + type: object + properties: + createdAt: + type: string + description: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + repo.LocationUpdate: + type: object + properties: + description: + type: string + id: + type: string + name: + type: string + parentId: + type: string + nullable: true + repo.MaintenanceEntry: + type: object + properties: + completedDate: + type: string + cost: + type: string + example: "0" + description: + type: string + id: + type: string + name: + type: string + scheduledDate: + type: string + repo.MaintenanceEntryCreate: + type: object + required: + - name + properties: + completedDate: + type: string + cost: + type: string + example: "0" + description: + type: string + name: + type: string + scheduledDate: + type: string + repo.MaintenanceEntryUpdate: + type: object + properties: + completedDate: + type: string + cost: + type: string + example: "0" + description: + type: string + name: + type: string + scheduledDate: + type: string + repo.MaintenanceEntryWithDetails: + type: object + properties: + completedDate: + type: string + cost: + type: string + example: "0" + description: + type: string + id: + type: string + itemID: + type: string + itemName: + type: string + name: + type: string + scheduledDate: + type: string + repo.MaintenanceFilterStatus: + type: string + enum: + - scheduled + - completed + - both + x-enum-varnames: + - MaintenanceFilterStatusScheduled + - MaintenanceFilterStatusCompleted + - MaintenanceFilterStatusBoth + repo.NotifierCreate: + type: object + required: + - name + - url + properties: + isActive: + type: boolean + name: + type: string + maxLength: 255 + minLength: 1 + url: + type: string + repo.NotifierOut: + type: object + properties: + createdAt: + type: string + groupId: + type: string + id: + type: string + isActive: + type: boolean + name: + type: string + updatedAt: + type: string + url: + type: string + userId: + type: string + repo.NotifierUpdate: + type: object + required: + - name + properties: + isActive: + type: boolean + name: + type: string + maxLength: 255 + minLength: 1 + url: + type: string + nullable: true + repo.PaginationResult-repo_ItemSummary: + type: object + properties: + items: + type: array + items: + $ref: "#/components/schemas/repo.ItemSummary" + page: + type: integer + pageSize: + type: integer + total: + type: integer + repo.TotalsByOrganizer: + type: object + properties: + id: + type: string + name: + type: string + total: + type: number + repo.TreeItem: + type: object + properties: + children: + type: array + items: + $ref: "#/components/schemas/repo.TreeItem" + id: + type: string + name: + type: string + type: + type: string + repo.UserOut: + type: object + properties: + email: + type: string + groupId: + type: string + groupName: + type: string + id: + type: string + isOwner: + type: boolean + isSuperuser: + type: boolean + name: + type: string + repo.UserUpdate: + type: object + properties: + email: + type: string + name: + type: string + repo.ValueOverTime: + type: object + properties: + end: + type: string + entries: + type: array + items: + $ref: "#/components/schemas/repo.ValueOverTimeEntry" + start: + type: string + valueAtEnd: + type: number + valueAtStart: + type: number + repo.ValueOverTimeEntry: + type: object + properties: + date: + type: string + name: + type: string + value: + type: number + services.Latest: + type: object + properties: + date: + type: string + version: + type: string + services.UserRegistration: + type: object + properties: + email: + type: string + name: + type: string + password: + type: string + token: + type: string + user.Role: + type: string + enum: + - user + - user + - owner + x-enum-varnames: + - DefaultRole + - RoleUser + - RoleOwner + v1.APISummary: + type: object + properties: + allowRegistration: + type: boolean + build: + $ref: "#/components/schemas/v1.Build" + demo: + type: boolean + health: + type: boolean + labelPrinting: + type: boolean + latest: + $ref: "#/components/schemas/services.Latest" + message: + type: string + title: + type: string + versions: + type: array + items: + type: string + v1.ActionAmountResult: + type: object + properties: + completed: + type: integer + v1.Build: + type: object + properties: + buildTime: + type: string + commit: + type: string + version: + type: string + v1.ChangePassword: + type: object + properties: + current: + type: string + new: + type: string + v1.GroupInvitation: + type: object + properties: + expiresAt: + type: string + token: + type: string + uses: + type: integer + v1.GroupInvitationCreate: + type: object + required: + - uses + properties: + expiresAt: + type: string + uses: + type: integer + maximum: 100 + minimum: 1 + v1.ItemAttachmentToken: + type: object + properties: + token: + type: string + v1.LoginForm: + type: object + properties: + password: + type: string + example: admin + stayLoggedIn: + type: boolean + username: + type: string + example: admin@admin.com + v1.TokenResponse: + type: object + properties: + attachmentToken: + type: string + expiresAt: + type: string + token: + type: string + v1.Wrapped: + type: object + properties: + item: {} + validate.ErrorResponse: + type: object + properties: + error: + type: string + fields: + type: string diff --git a/docs/en/api/openapi-2.0.json b/docs/en/api/swagger-2.0.json similarity index 99% rename from docs/en/api/openapi-2.0.json rename to docs/en/api/swagger-2.0.json index c62359b9..5c5e7aa2 100644 --- a/docs/en/api/openapi-2.0.json +++ b/docs/en/api/swagger-2.0.json @@ -2238,6 +2238,9 @@ "code": { "type": "string" }, + "decimals": { + "type": "integer" + }, "local": { "type": "string" }, diff --git a/docs/en/api/openapi-2.0.yaml b/docs/en/api/swagger-2.0.yaml similarity index 99% rename from docs/en/api/openapi-2.0.yaml rename to docs/en/api/swagger-2.0.yaml index 8289cc18..ac352ed2 100644 --- a/docs/en/api/openapi-2.0.yaml +++ b/docs/en/api/swagger-2.0.yaml @@ -34,6 +34,8 @@ definitions: properties: code: type: string + decimals: + type: integer local: type: string name: From ba45203ea3e411b40e5e5f09c62ba15df671106c Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 23 Sep 2025 13:20:50 -0400 Subject: [PATCH 07/19] beautify the readme a bit (#1014) * beautify the readme a bit * Revert CSS updates, Github filters them out * Enhance README with Lemmy badge and description update --- {Screenshots => .github/screenshots}/1.png | Bin {Screenshots => .github/screenshots}/10.png | Bin {Screenshots => .github/screenshots}/2.png | Bin {Screenshots => .github/screenshots}/3.png | Bin {Screenshots => .github/screenshots}/4.png | Bin {Screenshots => .github/screenshots}/5.png | Bin {Screenshots => .github/screenshots}/6.png | Bin {Screenshots => .github/screenshots}/7.png | Bin {Screenshots => .github/screenshots}/8.png | Bin {Screenshots => .github/screenshots}/9.png | Bin .../screenshots}/readme.md | 14 ++--- README.md | 56 ++++++++++++++---- 12 files changed, 50 insertions(+), 20 deletions(-) rename {Screenshots => .github/screenshots}/1.png (100%) rename {Screenshots => .github/screenshots}/10.png (100%) rename {Screenshots => .github/screenshots}/2.png (100%) rename {Screenshots => .github/screenshots}/3.png (100%) rename {Screenshots => .github/screenshots}/4.png (100%) rename {Screenshots => .github/screenshots}/5.png (100%) rename {Screenshots => .github/screenshots}/6.png (100%) rename {Screenshots => .github/screenshots}/7.png (100%) rename {Screenshots => .github/screenshots}/8.png (100%) rename {Screenshots => .github/screenshots}/9.png (100%) rename {Screenshots => .github/screenshots}/readme.md (98%) diff --git a/Screenshots/1.png b/.github/screenshots/1.png similarity index 100% rename from Screenshots/1.png rename to .github/screenshots/1.png diff --git a/Screenshots/10.png b/.github/screenshots/10.png similarity index 100% rename from Screenshots/10.png rename to .github/screenshots/10.png diff --git a/Screenshots/2.png b/.github/screenshots/2.png similarity index 100% rename from Screenshots/2.png rename to .github/screenshots/2.png diff --git a/Screenshots/3.png b/.github/screenshots/3.png similarity index 100% rename from Screenshots/3.png rename to .github/screenshots/3.png diff --git a/Screenshots/4.png b/.github/screenshots/4.png similarity index 100% rename from Screenshots/4.png rename to .github/screenshots/4.png diff --git a/Screenshots/5.png b/.github/screenshots/5.png similarity index 100% rename from Screenshots/5.png rename to .github/screenshots/5.png diff --git a/Screenshots/6.png b/.github/screenshots/6.png similarity index 100% rename from Screenshots/6.png rename to .github/screenshots/6.png diff --git a/Screenshots/7.png b/.github/screenshots/7.png similarity index 100% rename from Screenshots/7.png rename to .github/screenshots/7.png diff --git a/Screenshots/8.png b/.github/screenshots/8.png similarity index 100% rename from Screenshots/8.png rename to .github/screenshots/8.png diff --git a/Screenshots/9.png b/.github/screenshots/9.png similarity index 100% rename from Screenshots/9.png rename to .github/screenshots/9.png diff --git a/Screenshots/readme.md b/.github/screenshots/readme.md similarity index 98% rename from Screenshots/readme.md rename to .github/screenshots/readme.md index c525de11..83cb8a31 100644 --- a/Screenshots/readme.md +++ b/.github/screenshots/readme.md @@ -1,8 +1,8 @@ -# Screenshots - -These screenshots are taken from our public [Demo](https://demo.homebox.software) instance. -Note that whilst we will make every effort to ensure that these are maintained and updated, they may be outdated or missing functionality and we would always advise reviewing our demo instances: - -- [Demo](https://demo.homebox.software) -- [Nightly](https://nightly.homebox.software) +# Screenshots + +These screenshots are taken from our public [Demo](https://demo.homebox.software) instance. +Note that whilst we will make every effort to ensure that these are maintained and updated, they may be outdated or missing functionality and we would always advise reviewing our demo instances: + +- [Demo](https://demo.homebox.software) +- [Nightly](https://nightly.homebox.software) - [VNext](https://vnext.homebox.software/) \ No newline at end of file diff --git a/README.md b/README.md index ecd2fb53..941f7c9c 100644 --- a/README.md +++ b/README.md @@ -2,36 +2,59 @@ -

HomeBox

-

+

HomeBox

+

Docs | Demo | Discord

+

+ Github Checks + + + +

+

+ + + +

## What is HomeBox -HomeBox is the inventory and organization system built for the Home User! With a focus on simplicity and ease of use, Homebox is the perfect solution for your home inventory, organization, and management needs. While developing this project, I've tried to keep the following principles in mind: +HomeBox is the inventory and organization system built for the Home User! With a focus on simplicity and ease of use, Homebox is the perfect solution for your home inventory, organization, and management needs. While developing this project, We've tried to keep the following principles in mind: -- _Simple_ - Homebox is designed to be simple and easy to use. No complicated setup or configuration required. Use either a single docker container, or deploy yourself by compiling the binary for your platform of choice. -- _Blazingly Fast_ - Homebox is written in Go, which makes it extremely fast and requires minimal resources to deploy. In general, idle memory usage is less than 50MB for the whole container. -- _Portable_ - Homebox is designed to be portable and run on anywhere. We use SQLite and an embedded Web UI to make it easy to deploy, use, and backup. +- 🧘 _Simple but Expandable_ - Homebox is designed to be simple and easy to use. No complicated setup or configuration required. But expandable to whatever level of infrastructure you want to put into it. +- 🚀 _Blazingly Fast_ - Homebox is written in Go, which makes it extremely fast and requires minimal resources to deploy. In general, idle memory usage is less than 50MB for the whole container. +- 📦 _Portable_ - Homebox is designed to be portable and run on anywhere. We use SQLite and an embedded Web UI to make it easy to deploy, use, and backup. + +### Key Features +- 📇 Rich Organization - Organize your items into categories, locations, and tags. You can also create custom fields to store additional information about your items. +- 🔍 Powerful Search - Quickly find items in your inventory using the powerful search feature. +- 📸 Image Upload - Upload images of your items to make it easy to identify them. +- 📄 Document and Warranty Tracking - Keep track of important documents and warranties for your items. +- 💰 Purchase & Maintenance Tracking - Track purchase dates, prices, and maintenance schedules for your items. +- 📱 Responsive Design - Homebox is designed to work on any device, including desktops, tablets, and smartphones. + +## Screenshots +![Login Screen](.github/screenshots/1.png) +![Dashboard](.github/screenshots/2.png) +![Item View](.github/screenshots/3.png) +![Create Item](.github/screenshots/9.png) +![Search](.github/screenshots/8.png) -# Screenshots -Check out screenshots of the project [here](https://github.com/sysadminsmedia/homebox/tree/main/screenshots). You can also try the demo instances of Homebox: - [Demo](https://demo.homebox.software) - [Nightly](https://nightly.homebox.software) -- [VNext](https://vnext.homebox.software/) ## Quick Start [Configuration & Docker Compose](https://homebox.software/en/quick-start.html) ```bash -# If using the rootless image, ensure data +# If using the rootless or hardened image, ensure data # folder has correct permissions mkdir -p /path/to/data/folder chown 65532:65532 -R /path/to/data/folder @@ -43,6 +66,7 @@ docker run -d \ --volume /path/to/data/folder/:/data \ ghcr.io/sysadminsmedia/homebox:latest # ghcr.io/sysadminsmedia/homebox:latest-rootless +# ghcr.io/sysadminsmedia/homebox:latest-hardened ``` @@ -51,14 +75,20 @@ docker run -d \ Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. -If you are not a coder, you can still contribute financially. Financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for project development. +To get started with code based contributions, please see our [contributing guide](https://homebox.software/en/contribute/get-started.html). + +If you are not a coder and can't help translate, you can still contribute financially. Financial contributions help us maintain the project and keep demos running. ## Help us Translate We want to make sure that Homebox is available in as many languages as possible. If you are interested in helping us translate Homebox, please help us via our [Weblate instance](https://translate.sysadminsmedia.com/projects/homebox/). -[![Translation status](http://translate.sysadminsmedia.com/widget/homebox/multi-auto.svg)](http://translate.sysadminsmedia.com/engage/homebox/) +[![Translation status](https://translate.sysadminsmedia.com/widget/homebox/multi-auto.svg)](https://translate.sysadminsmedia.com/engage/homebox/) ## Credits - - Original project by [@hay-kot](https://github.com/hay-kot) - Logo by [@lakotelman](https://github.com/lakotelman) + +### Contributors + + + From a5d63ac4e1e9c18e29a2a68008da9bc3368dd5ec Mon Sep 17 00:00:00 2001 From: Matthew Kilgore Date: Tue, 23 Sep 2025 21:05:22 -0400 Subject: [PATCH 08/19] In theory SLSA provenience for binary builds --- .github/workflows/binaries-publish.yaml | 64 ++++++++++++++++++++++++- backend/.goreleaser.yaml | 7 +++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries-publish.yaml b/.github/workflows/binaries-publish.yaml index 550cd6fe..76e30646 100644 --- a/.github/workflows/binaries-publish.yaml +++ b/.github/workflows/binaries-publish.yaml @@ -9,6 +9,8 @@ jobs: goreleaser: name: goreleaser runs-on: ubuntu-latest + outputs: + hashes: ${{ steps.binary.outputs.hashes }} permissions: contents: write packages: write @@ -42,18 +44,30 @@ jobs: go install github.com/sigstore/cosign/cmd/cosign@latest - name: Run GoReleaser + id: releaser if: startsWith(github.ref, 'refs/tags/') uses: goreleaser/goreleaser-action@v5 with: workdir: "backend" distribution: goreleaser version: "~> v2" - args: release --clean + args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COSIGN_PWD: ${{ secrets.COSIGN_PWD }} COSIGN_YES: "true" + - name: Generate binary hashes + if: startsWith(github.ref, 'refs/tags/') + id: binary + env: + ARTIFACTS: "${{ steps.releaser.outputs.artifacts }}" + run: | + set -euo pipefail + + checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path') + echo "hashes=$(cat $checksum_file | base64 -w0)" >> "$GITHUB_OUTPUT" + - name: Run GoReleaser No Release if: ${{ !startsWith(github.ref, 'refs/tags/') }} uses: goreleaser/goreleaser-action@v5 @@ -65,4 +79,50 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COSIGN_PWD: ${{ secrets.COSIGN_PWD }} - COSIGN_YES: "true" \ No newline at end of file + COSIGN_YES: "true" + + binary-provenance: + if: startsWith(github.ref, 'refs/tags/') + needs: [ goreleaser ] + permissions: + actions: read # To read the workflow path. + id-token: write # To sign the provenance. + contents: write # To add assets to a release. + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0 + with: + base64-subjects: "${{ needs.goreleaser.outputs.hashes }}" + upload-assets: true # upload to a new release + + verification-with-slsa-verifier: + if: startsWith(github.ref, 'refs/tags/') + needs: [goreleaser, binary-provenance] + runs-on: ubuntu-latest + permissions: read-all + steps: + - name: Install the verifier + uses: slsa-framework/slsa-verifier/actions/installer@v2.4.0 + + - name: Download assets + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PROVENANCE: "${{ needs.binary-provenance.outputs.provenance-name }}" + run: | + set -euo pipefail + gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "*.tar.gz" + gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "*.zip" + gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "$PROVENANCE" + - name: Verify assets + env: + CHECKSUMS: ${{ needs.goreleaser.outputs.hashes }} + PROVENANCE: "${{ needs.binary-provenance.outputs.provenance-name }}" + run: | + set -euo pipefail + checksums=$(echo "$CHECKSUMS" | base64 -d) + while read -r line; do + fn=$(echo $line | cut -d ' ' -f2) + echo "Verifying $fn" + slsa-verifier verify-artifact --provenance-path "$PROVENANCE" \ + --source-uri "github.com/$GITHUB_REPOSITORY" \ + --source-tag "$GITHUB_REF_NAME" \ + "$fn" + done <<<"$checksums" diff --git a/backend/.goreleaser.yaml b/backend/.goreleaser.yaml index 02c80bcb..b2ac7fa7 100644 --- a/backend/.goreleaser.yaml +++ b/backend/.goreleaser.yaml @@ -21,6 +21,13 @@ builds: - arm - arm64 - riscv64 + flags: + - -trimpath + ldflags: + - -s -w + - -X main.version={{.Version}} + - -X main.commit={{.Commit}} + - -X main.date={{.Date}} ignore: - goos: windows goarch: arm From 6cd9e2779fdb3cec87e12745c9ee229b0f21972a Mon Sep 17 00:00:00 2001 From: Tonya Date: Wed, 24 Sep 2025 02:37:38 +0100 Subject: [PATCH 09/19] Use Tanstack table for Selectable Table, quick actions (#998) * feat: implement example of data table * feat: load item data into table * chore: begin switching dialogs * feat: implement old dialog for controlling headers and page size * feat: get table into relatively usable state * feat: enhance dropdown actions for multi-selection and CSV download * feat: enhance table cell and dropdown button styles for better usability * feat: json download for table * feat: add expanded row component for item details in data table * chore: add translation support * feat: restore table on home page * fix: oops need ids * feat: move card view to use tanstack to allow for pagination * feat: switch the items search to use ItemViewSelectable * fix: update pagination handling and improve button click logic * feat: improve selectable table * feat: add indeterminate to checkbox * feat: overhaul maintenance dialog to use new system and add maintenance options to table * feat: add label ids and location id to item patch api * feat: change location and labels in table view * feat: add quick actions preference and enable toggle in table settings * fix: lint * fix: remove sized 1 pages * fix: attempt to fix type error * fix: various issues * fix: remove * fix: refactor item fetching logic to use useAsyncData for improved reactivity and improve use confirm * fix: sort backend issues * fix: enhance CSV export functionality by escaping fields to prevent formula injection * fix: put aria sort on th not button * chore: update api types --- backend/app/api/static/docs/docs.go | 13 + backend/app/api/static/docs/openapi-3.json | 13 + backend/app/api/static/docs/openapi-3.yaml | 10 + backend/app/api/static/docs/swagger.json | 13 + backend/app/api/static/docs/swagger.yaml | 10 + backend/internal/data/repo/repo_items.go | 98 ++++- docs/en/api/openapi-3.0.json | 13 + docs/en/api/openapi-3.0.yaml | 10 + docs/en/api/swagger-2.0.json | 13 + docs/en/api/swagger-2.0.yaml | 10 + docs/en/user-guide/tips-tricks.md | 8 + frontend/components/Base/SectionHeader.vue | 1 + frontend/components/Item/BarcodeModal.vue | 4 +- frontend/components/Item/Card.vue | 17 +- .../Item/View/ItemChangeDetails.vue | 169 +++++++++ frontend/components/Item/View/Selectable.vue | 70 +++- frontend/components/Item/View/Table.types.ts | 13 - frontend/components/Item/View/Table.vue | 340 +----------------- frontend/components/Item/View/pagination.ts | 8 + .../components/Item/View/table/card-view.vue | 66 ++++ .../components/Item/View/table/columns.ts | 270 ++++++++++++++ .../Item/View/table/data-table-controls.vue | 93 +++++ .../Item/View/table/data-table-dropdown.vue | 273 ++++++++++++++ .../View/table/data-table-expanded-row.vue | 45 +++ .../components/Item/View/table/data-table.vue | 252 +++++++++++++ .../components/Item/View/table/table-view.vue | 73 ++++ frontend/components/Label/Selector.vue | 7 +- frontend/components/Maintenance/EditModal.vue | 140 +++----- frontend/components/Maintenance/ListView.vue | 97 ++++- frontend/components/ModalConfirm.vue | 11 +- .../global/DetailsSection/DetailsSection.vue | 2 - frontend/components/ui/checkbox/Checkbox.vue | 36 +- .../components/ui/dialog-provider/utils.ts | 16 +- frontend/components/ui/table/TableCell.vue | 43 ++- frontend/composables/use-confirm.ts | 19 +- frontend/composables/use-preferences.ts | 17 +- frontend/eslint.config.mjs | 1 + frontend/layouts/default.vue | 2 +- frontend/lib/api/types/data-contracts.ts | 11 +- frontend/lib/utils.ts | 8 + frontend/locales/en.json | 39 +- frontend/package.json | 1 + frontend/pages/home/index.vue | 4 +- frontend/pages/item/[id]/index.vue | 38 +- frontend/pages/items.vue | 71 +--- frontend/pages/label/[id].vue | 50 +-- frontend/pages/location/[id].vue | 38 +- frontend/pnpm-lock.yaml | 20 ++ 48 files changed, 1959 insertions(+), 617 deletions(-) create mode 100644 frontend/components/Item/View/ItemChangeDetails.vue delete mode 100644 frontend/components/Item/View/Table.types.ts create mode 100644 frontend/components/Item/View/pagination.ts create mode 100644 frontend/components/Item/View/table/card-view.vue create mode 100644 frontend/components/Item/View/table/columns.ts create mode 100644 frontend/components/Item/View/table/data-table-controls.vue create mode 100644 frontend/components/Item/View/table/data-table-dropdown.vue create mode 100644 frontend/components/Item/View/table/data-table-expanded-row.vue create mode 100644 frontend/components/Item/View/table/data-table.vue create mode 100644 frontend/components/Item/View/table/table-view.vue diff --git a/backend/app/api/static/docs/docs.go b/backend/app/api/static/docs/docs.go index 6525c53a..1cbb82bf 100644 --- a/backend/app/api/static/docs/docs.go +++ b/backend/app/api/static/docs/docs.go @@ -3483,6 +3483,19 @@ const docTemplate = `{ "id": { "type": "string" }, + "labelIds": { + "type": "array", + "items": { + "type": "string" + }, + "x-nullable": true, + "x-omitempty": true + }, + "locationId": { + "type": "string", + "x-nullable": true, + "x-omitempty": true + }, "quantity": { "type": "integer", "x-nullable": true, diff --git a/backend/app/api/static/docs/openapi-3.json b/backend/app/api/static/docs/openapi-3.json index b0bcc2d8..2ae3681d 100644 --- a/backend/app/api/static/docs/openapi-3.json +++ b/backend/app/api/static/docs/openapi-3.json @@ -3661,6 +3661,19 @@ "id": { "type": "string" }, + "labelIds": { + "type": "array", + "items": { + "type": "string" + }, + "x-omitempty": true, + "nullable": true + }, + "locationId": { + "type": "string", + "x-omitempty": true, + "nullable": true + }, "quantity": { "type": "integer", "x-omitempty": true, diff --git a/backend/app/api/static/docs/openapi-3.yaml b/backend/app/api/static/docs/openapi-3.yaml index 9224a088..bc56d4a2 100644 --- a/backend/app/api/static/docs/openapi-3.yaml +++ b/backend/app/api/static/docs/openapi-3.yaml @@ -2293,6 +2293,16 @@ components: properties: id: type: string + labelIds: + type: array + items: + type: string + x-omitempty: true + nullable: true + locationId: + type: string + x-omitempty: true + nullable: true quantity: type: integer x-omitempty: true diff --git a/backend/app/api/static/docs/swagger.json b/backend/app/api/static/docs/swagger.json index 5c5e7aa2..3fd96322 100644 --- a/backend/app/api/static/docs/swagger.json +++ b/backend/app/api/static/docs/swagger.json @@ -3481,6 +3481,19 @@ "id": { "type": "string" }, + "labelIds": { + "type": "array", + "items": { + "type": "string" + }, + "x-nullable": true, + "x-omitempty": true + }, + "locationId": { + "type": "string", + "x-nullable": true, + "x-omitempty": true + }, "quantity": { "type": "integer", "x-nullable": true, diff --git a/backend/app/api/static/docs/swagger.yaml b/backend/app/api/static/docs/swagger.yaml index ac352ed2..1fc5f05e 100644 --- a/backend/app/api/static/docs/swagger.yaml +++ b/backend/app/api/static/docs/swagger.yaml @@ -875,6 +875,16 @@ definitions: properties: id: type: string + labelIds: + items: + type: string + type: array + x-nullable: true + x-omitempty: true + locationId: + type: string + x-nullable: true + x-omitempty: true quantity: type: integer x-nullable: true diff --git a/backend/internal/data/repo/repo_items.go b/backend/internal/data/repo/repo_items.go index 4fb50255..455fb21f 100644 --- a/backend/internal/data/repo/repo_items.go +++ b/backend/internal/data/repo/repo_items.go @@ -120,9 +120,11 @@ type ( } ItemPatch struct { - ID uuid.UUID `json:"id"` - Quantity *int `json:"quantity,omitempty" extensions:"x-nullable,x-omitempty"` - ImportRef *string `json:"-,omitempty" extensions:"x-nullable,x-omitempty"` + ID uuid.UUID `json:"id"` + Quantity *int `json:"quantity,omitempty" extensions:"x-nullable,x-omitempty"` + ImportRef *string `json:"-,omitempty" extensions:"x-nullable,x-omitempty"` + LocationID uuid.UUID `json:"locationId" extensions:"x-nullable,x-omitempty"` + LabelIDs []uuid.UUID `json:"labelIds" extensions:"x-nullable,x-omitempty"` } ItemSummary struct { @@ -814,7 +816,20 @@ func (e *ItemsRepository) GetAllZeroImportRef(ctx context.Context, gid uuid.UUID } func (e *ItemsRepository) Patch(ctx context.Context, gid, id uuid.UUID, data ItemPatch) error { - q := e.db.Item.Update(). + tx, err := e.db.Tx(ctx) + if err != nil { + return err + } + committed := false + defer func() { + if !committed { + if err := tx.Rollback(); err != nil { + log.Warn().Err(err).Msg("failed to rollback transaction during item patch") + } + } + }() + + q := tx.Item.Update(). Where( item.ID(id), item.HasGroupWith(group.ID(gid)), @@ -828,8 +843,81 @@ func (e *ItemsRepository) Patch(ctx context.Context, gid, id uuid.UUID, data Ite q.SetQuantity(*data.Quantity) } + if data.LocationID != uuid.Nil { + q.SetLocationID(data.LocationID) + } + + err = q.Exec(ctx) + if err != nil { + return err + } + + if data.LabelIDs != nil { + currentLabels, err := tx.Item.Query().Where(item.ID(id), item.HasGroupWith(group.ID(gid))).QueryLabel().All(ctx) + if err != nil { + return err + } + set := newIDSet(currentLabels) + + addLabels := []uuid.UUID{} + for _, l := range data.LabelIDs { + if set.Contains(l) { + set.Remove(l) + } else { + addLabels = append(addLabels, l) + } + } + + if len(addLabels) > 0 { + if err := tx.Item.Update(). + Where(item.ID(id), item.HasGroupWith(group.ID(gid))). + AddLabelIDs(addLabels...). + Exec(ctx); err != nil { + return err + } + } + if set.Len() > 0 { + if err := tx.Item.Update(). + Where(item.ID(id), item.HasGroupWith(group.ID(gid))). + RemoveLabelIDs(set.Slice()...). + Exec(ctx); err != nil { + return err + } + } + } + + if data.LocationID != uuid.Nil { + itemEnt, err := tx.Item.Query().Where(item.ID(id), item.HasGroupWith(group.ID(gid))).Only(ctx) + if err != nil { + return err + } + if itemEnt.SyncChildItemsLocations { + children, err := tx.Item.Query().Where(item.ID(id), item.HasGroupWith(group.ID(gid))).QueryChildren().All(ctx) + if err != nil { + return err + } + for _, child := range children { + childLocation, err := child.QueryLocation().First(ctx) + if err != nil { + return err + } + if data.LocationID != childLocation.ID { + err = child.Update().SetLocationID(data.LocationID).Exec(ctx) + if err != nil { + return err + } + } + } + } + } + + if err := tx.Commit(); err != nil { + return err + } + committed = true + e.publishMutationEvent(gid) - return q.Exec(ctx) + return nil } func (e *ItemsRepository) GetAllCustomFieldValues(ctx context.Context, gid uuid.UUID, name string) ([]string, error) { diff --git a/docs/en/api/openapi-3.0.json b/docs/en/api/openapi-3.0.json index b0bcc2d8..2ae3681d 100644 --- a/docs/en/api/openapi-3.0.json +++ b/docs/en/api/openapi-3.0.json @@ -3661,6 +3661,19 @@ "id": { "type": "string" }, + "labelIds": { + "type": "array", + "items": { + "type": "string" + }, + "x-omitempty": true, + "nullable": true + }, + "locationId": { + "type": "string", + "x-omitempty": true, + "nullable": true + }, "quantity": { "type": "integer", "x-omitempty": true, diff --git a/docs/en/api/openapi-3.0.yaml b/docs/en/api/openapi-3.0.yaml index 9224a088..bc56d4a2 100644 --- a/docs/en/api/openapi-3.0.yaml +++ b/docs/en/api/openapi-3.0.yaml @@ -2293,6 +2293,16 @@ components: properties: id: type: string + labelIds: + type: array + items: + type: string + x-omitempty: true + nullable: true + locationId: + type: string + x-omitempty: true + nullable: true quantity: type: integer x-omitempty: true diff --git a/docs/en/api/swagger-2.0.json b/docs/en/api/swagger-2.0.json index 5c5e7aa2..3fd96322 100644 --- a/docs/en/api/swagger-2.0.json +++ b/docs/en/api/swagger-2.0.json @@ -3481,6 +3481,19 @@ "id": { "type": "string" }, + "labelIds": { + "type": "array", + "items": { + "type": "string" + }, + "x-nullable": true, + "x-omitempty": true + }, + "locationId": { + "type": "string", + "x-nullable": true, + "x-omitempty": true + }, "quantity": { "type": "integer", "x-nullable": true, diff --git a/docs/en/api/swagger-2.0.yaml b/docs/en/api/swagger-2.0.yaml index ac352ed2..1fc5f05e 100644 --- a/docs/en/api/swagger-2.0.yaml +++ b/docs/en/api/swagger-2.0.yaml @@ -875,6 +875,16 @@ definitions: properties: id: type: string + labelIds: + items: + type: string + type: array + x-nullable: true + x-omitempty: true + locationId: + type: string + x-nullable: true + x-omitempty: true quantity: type: integer x-nullable: true diff --git a/docs/en/user-guide/tips-tricks.md b/docs/en/user-guide/tips-tricks.md index 25265dbf..8b28839c 100644 --- a/docs/en/user-guide/tips-tricks.md +++ b/docs/en/user-guide/tips-tricks.md @@ -102,3 +102,11 @@ The copy to clipboard functionality requires a secure context (HTTPS or localhos To enable this feature: - Use HTTPS by setting up a reverse proxy (like Nginx or Caddy) - OR access Homebox through localhost + +## Open Multiple Items in New Tabs + +By default browsers prevent opening multiple tabs with one click, to allow for the `View Items` button to work you therefore need to enable a setting usually called `Allow pop-ups and redirects` or similar for the domain you're using Homebox on. + +- Chrome: [Block or allow pop-ups in Chrome](https://support.google.com/chrome/answer/95472?hl=en-GB&co=GENIE.Platform%3DDesktop#zippy=%2Callow-pop-ups-and-redirects-from-a-site) +- Firefox: [Pop-up blocker settings, exceptions and troubleshooting](https://support.mozilla.org/en-US/kb/pop-blocker-settings-exceptions-troubleshooting) +- Safari: [Block pop-up ads and windows in Safari](https://support.apple.com/en-gb/102524) diff --git a/frontend/components/Base/SectionHeader.vue b/frontend/components/Base/SectionHeader.vue index 10d9e144..fccdca8a 100644 --- a/frontend/components/Base/SectionHeader.vue +++ b/frontend/components/Base/SectionHeader.vue @@ -3,6 +3,7 @@ + diff --git a/frontend/components/Item/BarcodeModal.vue b/frontend/components/Item/BarcodeModal.vue index dc00febf..a489ca34 100644 --- a/frontend/components/Item/BarcodeModal.vue +++ b/frontend/components/Item/BarcodeModal.vue @@ -115,7 +115,6 @@ import MdiAlertCircleOutline from "~icons/mdi/alert-circle-outline"; import MdiBarcode from "~icons/mdi/barcode"; import MdiLoading from "~icons/mdi/loading"; - import type { TableData } from "~/components/Item/View/Table.types"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Separator } from "@/components/ui/separator"; @@ -225,7 +224,8 @@ } } - function extractValue(data: TableData, value: string) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function extractValue(data: Record, value: string) { const parts = value.split("."); let current = data; for (const part of parts) { diff --git a/frontend/components/Item/Card.vue b/frontend/components/Item/Card.vue index b95be4b0..5becbecd 100644 --- a/frontend/components/Item/Card.vue +++ b/frontend/components/Item/Card.vue @@ -1,5 +1,13 @@