package web import ( "net/http" "strconv" "inventree/internal/models" ) type assetRequest struct { AssetTag string `json:"asset_id"` Name string `json:"name"` ItemTypeID int64 `json:"item_type_id"` FolderID *int64 `json:"folder_id"` StatusID int64 `json:"status_id"` AssignedUserID *int64 `json:"assigned_user_id"` Notes string `json:"notes"` Quantity int `json:"quantity"` Fields map[string]string `json:"fields"` } func (req assetRequest) fieldValues() map[int64]string { out := make(map[int64]string, len(req.Fields)) for k, v := range req.Fields { id, err := strconv.ParseInt(k, 10, 64) if err != nil { continue } out[id] = v } return out } func (a *App) handleAssetCreate(w http.ResponseWriter, r *http.Request) { var req assetRequest if err := readJSON(r, &req); err != nil { jsonErr(w, http.StatusBadRequest, err) return } asset := &models.Asset{ AssetTag: req.AssetTag, Name: req.Name, ItemTypeID: req.ItemTypeID, FolderID: req.FolderID, StatusID: req.StatusID, AssignedUserID: req.AssignedUserID, Notes: req.Notes, Quantity: req.Quantity, } if err := a.Assets.Create(r.Context(), asset, req.fieldValues()); err != nil { jsonErr(w, http.StatusBadRequest, err) return } jsonOK(w, map[string]any{"id": asset.ID, "asset_id": asset.AssetTag, "folder_id": asset.FolderID}) } func (a *App) handleAssetUpdate(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } var req assetRequest if err := readJSON(r, &req); err != nil { jsonErr(w, http.StatusBadRequest, err) return } existing, err := a.Assets.Get(r.Context(), id) if err != nil { jsonErr(w, http.StatusNotFound, err) return } asset := &models.Asset{ ID: id, AssetTag: req.AssetTag, Name: req.Name, ItemTypeID: existing.ItemTypeID, FolderID: req.FolderID, StatusID: req.StatusID, AssignedUserID: req.AssignedUserID, Notes: req.Notes, Quantity: req.Quantity, } if err := a.Assets.Update(r.Context(), asset, req.fieldValues()); err != nil { jsonErr(w, http.StatusBadRequest, err) return } jsonOK(w, map[string]any{"id": asset.ID, "asset_id": asset.AssetTag, "folder_id": asset.FolderID}) } func (a *App) handleAssetDelete(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } asset, err := a.Assets.Get(r.Context(), id) if err != nil { jsonErr(w, http.StatusNotFound, err) return } if err := a.Assets.Delete(r.Context(), id); err != nil { jsonErr(w, http.StatusInternalServerError, err) return } jsonOK(w, map[string]any{"id": id, "folder_id": asset.FolderID}) } func (a *App) handleAssetDuplicate(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } copyAsset, err := a.Assets.Duplicate(r.Context(), id) if err != nil { jsonErr(w, http.StatusBadRequest, err) return } jsonOK(w, map[string]any{"id": copyAsset.ID, "asset_id": copyAsset.AssetTag, "folder_id": copyAsset.FolderID}) } type assetMoveRequest struct { FolderID *int64 `json:"folder_id"` } func (a *App) handleAssetMove(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } var req assetMoveRequest if err := readJSON(r, &req); err != nil { jsonErr(w, http.StatusBadRequest, err) return } if err := a.Assets.Move(r.Context(), id, req.FolderID); err != nil { jsonErr(w, http.StatusInternalServerError, err) return } jsonOK(w, map[string]any{"id": id, "folder_id": req.FolderID}) } // handleAssetGet returns a single asset as JSON, for lightweight lookups // (e.g. the checkout modal) that don't need the full HTML detail panel. func (a *App) handleAssetGet(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } asset, err := a.Assets.Get(r.Context(), id) if err != nil { jsonErr(w, http.StatusNotFound, err) return } jsonOK(w, asset) } type assetCheckoutRequest struct { AssignedUserID *int64 `json:"assigned_user_id"` } // handleAssetCheckout assigns (checks out) or unassigns (checks in) an // asset's user without touching any other field. func (a *App) handleAssetCheckout(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } var req assetCheckoutRequest if err := readJSON(r, &req); err != nil { jsonErr(w, http.StatusBadRequest, err) return } if err := a.Assets.AssignUser(r.Context(), id, req.AssignedUserID); err != nil { jsonErr(w, http.StatusInternalServerError, err) return } jsonOK(w, map[string]any{"id": id, "assigned_user_id": req.AssignedUserID}) } type assetQuantityRequest struct { Delta int `json:"delta"` } // handleAssetQuantity increments or decrements a non-unique asset's // quantity in place, for the +/- steppers on the asset panel and folder // tables. func (a *App) handleAssetQuantity(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } var req assetQuantityRequest if err := readJSON(r, &req); err != nil { jsonErr(w, http.StatusBadRequest, err) return } asset, err := a.Assets.AdjustQuantity(r.Context(), id, req.Delta) if err != nil { jsonErr(w, http.StatusBadRequest, err) return } jsonOK(w, map[string]any{"id": id, "quantity": asset.Quantity}) }