diff --git a/frontend/assets/js/api.js b/frontend/assets/js/api.js index 289ae55..9ef391c 100644 --- a/frontend/assets/js/api.js +++ b/frontend/assets/js/api.js @@ -96,7 +96,7 @@ function editItem(item) { document.getElementById('item-name').value = item.name; document.getElementById('item-category').value = item.category; document.getElementById('item-desc').value = item.description; - document.getElementById('item-qty').value = item.total_quantity; + //document.getElementById('item-qty').value = item.total_quantity; document.getElementById('item-modal-title').innerText = 'Edit Item'; document.getElementById('item-modal').classList.add('show'); } @@ -108,7 +108,7 @@ async function saveItem(event) { name: document.getElementById('item-name').value, category: document.getElementById('item-category').value, description: document.getElementById('item-desc').value, - total_quantity: parseInt(document.getElementById('item-qty').value, 10) + //total_quantity: parseInt(document.getElementById('item-qty').value, 10) }; const method = id ? 'PUT' : 'POST'; diff --git a/handlers/api.go b/handlers/api.go index 6169ba2..4a516a4 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -18,6 +18,32 @@ func Location(w http.ResponseWriter, r *http.Request) { case http.MethodGet: idStr := r.URL.Query().Get("id") + contentMode := r.URL.Query().Get("content") + + if idStr != "" && contentMode == "true" { + _, _ = strconv.Atoi(idStr) + query := ` + SELECT s.item_id, i.name, s.quantity + FROM stock s + JOIN items i ON s.item_id = i.id + WHERE s.location_id = ? AND s.quantity > 0 + ` + rows, err := storage.DB.Query(query) + if err != nil { + http.Error(w, "Database error", http.StatusInternalServerError) + return + } + defer rows.Close() + + var contents []models.LocationContent + for rows.Next() { + var c models.LocationContent + rows.Scan(&c.ItemID, &c.ItemName, &c.Quantity) + contents = append(contents, c) + } + json.NewEncoder(w).Encode(map[string]interface{}{"contents": contents}) + return + } if idStr != "" { id, err := strconv.Atoi(idStr) @@ -188,46 +214,33 @@ func Item(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - idStr := r.URL.Query().Get("id") - - // Read One - if idStr != "" { - id, err := strconv.Atoi(idStr) - if err != nil { - http.Error(w, "Invalid ID parameter", http.StatusBadRequest) - return - } - - var item models.Item - err = storage.DB.QueryRow("SELECT id, name, category, description, total_quantity FROM items WHERE id = ?", id). - Scan(&item.ID, &item.Name, &item.Category, &item.Description, &item.TotalQuantity) - if err != nil { - if err == sql.ErrNoRows { - http.Error(w, "Item not found", http.StatusNotFound) - return - } - http.Error(w, "Internal server error", http.StatusInternalServerError) - return - } - json.NewEncoder(w).Encode(item) - return - } - - // Read All - rows, err := storage.DB.Query("SELECT id, name, category, description, total_quantity FROM items ORDER BY name ASC") + query := ` + SELECT + i.id, i.name, i.category, i.description, + COALESCE((SELECT SUM(quantity) FROM stock WHERE item_id = i.id), 0) as total_qty, + COALESCE((SELECT SUM(quantity) FROM project_items WHERE item_id = i.id), 0) as allocated_qty + FROM items i + ORDER BY i.name ASC + ` + rows, err := storage.DB.Query(query) if err != nil { - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(w, "Database error", http.StatusInternalServerError) return } defer rows.Close() - items := []models.Item{} + var itemsExtended []models.ItemExtended for rows.Next() { - var item models.Item - rows.Scan(&item.ID, &item.Name, &item.Category, &item.Description, &item.TotalQuantity) - items = append(items, item) + var ie models.ItemExtended + err := rows.Scan(&ie.ID, &ie.Name, &ie.Category, &ie.Description, &ie.TotalQuantity, &ie.AllocatedQty) + if err != nil { + continue + } + ie.AvailableQty = ie.TotalQuantity - ie.AllocatedQty + itemsExtended = append(itemsExtended, ie) } - json.NewEncoder(w).Encode(map[string]interface{}{"items": items}) + json.NewEncoder(w).Encode(map[string]interface{}{"items": itemsExtended}) + return case http.MethodPost: var body models.Item @@ -304,6 +317,33 @@ func Project(w http.ResponseWriter, r *http.Request) { case http.MethodGet: idStr := r.URL.Query().Get("id") + detailsMode := r.URL.Query().Get("details") + + if idStr != "" && detailsMode == "true" { + _, _ = strconv.Atoi(idStr) + query := ` + SELECT pi.item_id, i.name, pi.quantity + FROM project_items pi + JOIN items i ON pi.item_id = i.id + WHERE pi.project_id = ? + ` + rows, err := storage.DB.Query(query) + if err != nil { + http.Error(w, "Database error", http.StatusInternalServerError) + return + } + defer rows.Close() + + var details []models.ProjectDetailItem + for rows.Next() { + var d models.ProjectDetailItem + rows.Scan(&d.ItemID, &d.ItemName, &d.Quantity) + details = append(details, d) + } + json.NewEncoder(w).Encode(map[string]interface{}{"items": details}) + return + } + if idStr != "" { id, _ := strconv.Atoi(idStr) var p models.Project diff --git a/models/inventory.go b/models/inventory.go index becb799..1ab2258 100644 --- a/models/inventory.go +++ b/models/inventory.go @@ -42,3 +42,25 @@ type ProjectItem struct { ProjectID int `json:"project_id"` Quantity int `json:"quantity"` } + +type ItemExtended struct { + ID int `json:"id"` + Name string `json:"name"` + Category string `json:"category"` + Description string `json:"description"` + TotalQuantity int `json:"total_quantity"` // Summe aus allen Stock-Einträgen + AllocatedQty int `json:"allocated_quantity"` // Summe aus allen Projekt-Zuweisungen + AvailableQty int `json:"available_quantity"` // Total - Allocated +} + +type LocationContent struct { + ItemID int `json:"item_id"` + ItemName string `json:"item_name"` + Quantity int `json:"quantity"` +} + +type ProjectDetailItem struct { + ItemID int `json:"item_id"` + ItemName string `json:"item_name"` + Quantity int `json:"quantity"` +}