package handlers import ( "database/sql" "encoding/json" "net/http" "time" ) type SupplierHandler struct { DB *sql.DB } func NewSupplierHandler(db *sql.DB) *SupplierHandler { return &SupplierHandler{DB: db} } type Supplier struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` Phone string `json:"phone"` OrgNumber string `json:"org_number"` Address map[string]interface{} `json:"address"` PaymentTerms string `json:"payment_terms"` BankAccount string `json:"bank_account"` Bankgiro string `json:"bankgiro"` Postgiro string `json:"postgiro"` Currency string `json:"currency"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` } type PurchaseOrder struct { ID string `json:"id"` SupplierID string `json:"supplier_id"` PONumber string `json:"po_number"` Status string `json:"status"` Amount float64 `json:"amount"` TaxAmount float64 `json:"tax_amount"` Currency string `json:"currency"` ExpectedDelivery *time.Time `json:"expected_delivery"` ReceivedAt *time.Time `json:"received_at"` Notes string `json:"notes"` CreatedAt time.Time `json:"created_at"` } type SupplierInvoice struct { ID string `json:"id"` SupplierID string `json:"supplier_id"` POID *string `json:"po_id"` InvoiceNumber string `json:"invoice_number"` Amount float64 `json:"amount"` TaxAmount float64 `json:"tax_amount"` Currency string `json:"currency"` Status string `json:"status"` DueDate *time.Time `json:"due_date"` PaidAt *time.Time `json:"paid_at"` OCRNumber string `json:"ocr_number"` Notes string `json:"notes"` CreatedAt time.Time `json:"created_at"` } func (h *SupplierHandler) ListSuppliers(w http.ResponseWriter, r *http.Request) { rows, err := h.DB.Query(` SELECT id, name, email, phone, org_number, address, payment_terms, bank_account, bankgiro, postgiro, currency, status, created_at FROM boc_suppliers WHERE status = 'active' ORDER BY name `) if err != nil { writeError(w, http.StatusInternalServerError, "database error") return } defer rows.Close() suppliers := []Supplier{} for rows.Next() { var s Supplier var addr []byte if err := rows.Scan(&s.ID, &s.Name, &s.Email, &s.Phone, &s.OrgNumber, &addr, &s.PaymentTerms, &s.BankAccount, &s.Bankgiro, &s.Postgiro, &s.Currency, &s.Status, &s.CreatedAt); err != nil { continue } json.Unmarshal(addr, &s.Address) suppliers = append(suppliers, s) } writeJSON(w, http.StatusOK, map[string]interface{}{ "suppliers": suppliers, "total": len(suppliers), }) } func (h *SupplierHandler) CreateSupplier(w http.ResponseWriter, r *http.Request) { var req Supplier if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request") return } addr, _ := json.Marshal(req.Address) var id string err := h.DB.QueryRow(` INSERT INTO boc_suppliers (name, email, phone, org_number, address, payment_terms, bank_account, bankgiro, postgiro, currency) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id `, req.Name, req.Email, req.Phone, req.OrgNumber, addr, req.PaymentTerms, req.BankAccount, req.Bankgiro, req.Postgiro, req.Currency).Scan(&id) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create supplier") return } writeJSON(w, http.StatusCreated, map[string]interface{}{ "id": id, "message": "Supplier created", }) } func (h *SupplierHandler) ListPurchaseOrders(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") if status == "" { status = "all" } var query string var args []interface{} if status == "all" { query = `SELECT id, supplier_id, po_number, status, amount, currency, expected_delivery, received_at, created_at FROM boc_purchase_orders ORDER BY created_at DESC LIMIT 100` } else { query = `SELECT id, supplier_id, po_number, status, amount, currency, expected_delivery, received_at, created_at FROM boc_purchase_orders WHERE status = $1 ORDER BY created_at DESC LIMIT 100` args = append(args, status) } rows, err := h.DB.Query(query, args...) if err != nil { writeError(w, http.StatusInternalServerError, "database error") return } defer rows.Close() pos := []PurchaseOrder{} for rows.Next() { var p PurchaseOrder if err := rows.Scan(&p.ID, &p.SupplierID, &p.PONumber, &p.Status, &p.Amount, &p.Currency, &p.ExpectedDelivery, &p.ReceivedAt, &p.CreatedAt); err != nil { continue } pos = append(pos, p) } writeJSON(w, http.StatusOK, map[string]interface{}{ "purchase_orders": pos, "total": len(pos), }) } func (h *SupplierHandler) CreatePurchaseOrder(w http.ResponseWriter, r *http.Request) { var req struct { SupplierID string `json:"supplier_id"` ExpectedDelivery *time.Time `json:"expected_delivery"` Notes string `json:"notes"` Items []struct { ProductID string `json:"product_id"` Description string `json:"description"` Quantity float64 `json:"quantity"` UnitPrice float64 `json:"unit_price"` TaxRate float64 `json:"tax_rate"` } `json:"items"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request") return } poNumber := "PO-" + time.Now().Format("20060102-150405") var totalAmount, totalTax float64 for _, item := range req.Items { itemTotal := item.Quantity * item.UnitPrice itemTax := itemTotal * (item.TaxRate / 100) totalAmount += itemTotal totalTax += itemTax } tx, err := h.DB.Begin() if err != nil { writeError(w, http.StatusInternalServerError, "transaction error") return } defer tx.Rollback() var id string err = tx.QueryRow(` INSERT INTO boc_purchase_orders (supplier_id, po_number, amount, tax_amount, currency, expected_delivery, notes) VALUES ($1, $2, $3, $4, 'USD', $5, $6) RETURNING id `, req.SupplierID, poNumber, totalAmount, totalTax, req.ExpectedDelivery, req.Notes).Scan(&id) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create PO") return } for _, item := range req.Items { itemTotal := item.Quantity * item.UnitPrice _, err = tx.Exec(` INSERT INTO boc_purchase_order_items (po_id, product_id, description, quantity, unit_price, tax_rate, total) VALUES ($1, $2, $3, $4, $5, $6, $7) `, id, item.ProductID, item.Description, item.Quantity, item.UnitPrice, item.TaxRate, itemTotal) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create PO items") return } } if err := tx.Commit(); err != nil { writeError(w, http.StatusInternalServerError, "commit failed") return } writeJSON(w, http.StatusCreated, map[string]interface{}{ "id": id, "number": poNumber, "message": "Purchase order created", }) } func (h *SupplierHandler) ListSupplierInvoices(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") if status == "" { status = "all" } var query string var args []interface{} if status == "all" { query = `SELECT id, supplier_id, po_id, invoice_number, amount, currency, status, due_date, paid_at, ocr_number, created_at FROM boc_supplier_invoices ORDER BY created_at DESC LIMIT 100` } else { query = `SELECT id, supplier_id, po_id, invoice_number, amount, currency, status, due_date, paid_at, ocr_number, created_at FROM boc_supplier_invoices WHERE status = $1 ORDER BY created_at DESC LIMIT 100` args = append(args, status) } rows, err := h.DB.Query(query, args...) if err != nil { writeError(w, http.StatusInternalServerError, "database error") return } defer rows.Close() invoices := []SupplierInvoice{} for rows.Next() { var i SupplierInvoice if err := rows.Scan(&i.ID, &i.SupplierID, &i.POID, &i.InvoiceNumber, &i.Amount, &i.Currency, &i.Status, &i.DueDate, &i.PaidAt, &i.OCRNumber, &i.CreatedAt); err != nil { continue } invoices = append(invoices, i) } writeJSON(w, http.StatusOK, map[string]interface{}{ "invoices": invoices, "total": len(invoices), }) } func (h *SupplierHandler) CreateSupplierInvoice(w http.ResponseWriter, r *http.Request) { var req SupplierInvoice if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request") return } var id string err := h.DB.QueryRow(` INSERT INTO boc_supplier_invoices (supplier_id, po_id, invoice_number, amount, tax_amount, currency, due_date, ocr_number, notes) VALUES ($1, $2, $3, $4, $5, 'USD', $6, $7, $8) RETURNING id `, req.SupplierID, req.POID, req.InvoiceNumber, req.Amount, req.TaxAmount, req.DueDate, req.OCRNumber, req.Notes).Scan(&id) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create supplier invoice") return } writeJSON(w, http.StatusCreated, map[string]interface{}{ "id": id, "message": "Supplier invoice created", }) }