fix(erp): enable erp pages and menu items
This commit is contained in:
144
backend/internal/api/handlers/document_handler.go
Normal file
144
backend/internal/api/handlers/document_handler.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"aggios-app/backend/internal/api/middleware"
|
||||
"aggios-app/backend/internal/domain"
|
||||
"aggios-app/backend/internal/repository"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type DocumentHandler struct {
|
||||
repo *repository.DocumentRepository
|
||||
}
|
||||
|
||||
func NewDocumentHandler(repo *repository.DocumentRepository) *DocumentHandler {
|
||||
return &DocumentHandler{repo: repo}
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) Create(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
userID, _ := r.Context().Value(middleware.UserIDKey).(string)
|
||||
|
||||
var doc domain.Document
|
||||
if err := json.NewDecoder(r.Body).Decode(&doc); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
doc.ID = uuid.New()
|
||||
doc.TenantID, _ = uuid.Parse(tenantID)
|
||||
doc.CreatedBy, _ = uuid.Parse(userID)
|
||||
doc.LastUpdatedBy, _ = uuid.Parse(userID)
|
||||
if doc.Status == "" {
|
||||
doc.Status = "draft"
|
||||
}
|
||||
|
||||
if err := h.repo.Create(&doc); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(doc)
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) List(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
|
||||
docs, err := h.repo.GetByTenant(tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(docs)
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
id := mux.Vars(r)["id"]
|
||||
|
||||
doc, err := h.repo.GetByID(id, tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if doc == nil {
|
||||
http.Error(w, "document not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(doc)
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
userID, _ := r.Context().Value(middleware.UserIDKey).(string)
|
||||
id := mux.Vars(r)["id"]
|
||||
|
||||
var doc domain.Document
|
||||
if err := json.NewDecoder(r.Body).Decode(&doc); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
doc.ID, _ = uuid.Parse(id)
|
||||
doc.TenantID, _ = uuid.Parse(tenantID)
|
||||
doc.LastUpdatedBy, _ = uuid.Parse(userID)
|
||||
|
||||
if err := h.repo.Update(&doc); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(doc)
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
id := mux.Vars(r)["id"]
|
||||
|
||||
if err := h.repo.Delete(id, tenantID); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) GetSubpages(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
parentID := mux.Vars(r)["id"]
|
||||
|
||||
docs, err := h.repo.GetSubpages(parentID, tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(docs)
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) GetActivities(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
id := mux.Vars(r)["id"]
|
||||
|
||||
activities, err := h.repo.GetActivities(id, tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(activities)
|
||||
}
|
||||
399
backend/internal/api/handlers/erp_handler.go
Normal file
399
backend/internal/api/handlers/erp_handler.go
Normal file
@@ -0,0 +1,399 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"aggios-app/backend/internal/api/middleware"
|
||||
"aggios-app/backend/internal/domain"
|
||||
"aggios-app/backend/internal/repository"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type ERPHandler struct {
|
||||
repo *repository.ERPRepository
|
||||
}
|
||||
|
||||
func NewERPHandler(repo *repository.ERPRepository) *ERPHandler {
|
||||
return &ERPHandler{repo: repo}
|
||||
}
|
||||
|
||||
// ==================== FINANCE ====================
|
||||
|
||||
func (h *ERPHandler) CreateFinancialCategory(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
var cat domain.FinancialCategory
|
||||
if err := json.NewDecoder(r.Body).Decode(&cat); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
cat.ID = uuid.New()
|
||||
cat.TenantID, _ = uuid.Parse(tenantID)
|
||||
cat.IsActive = true
|
||||
|
||||
if err := h.repo.CreateFinancialCategory(&cat); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(cat)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) GetFinancialCategories(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
cats, err := h.repo.GetFinancialCategoriesByTenant(tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(cats)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) CreateBankAccount(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
var acc domain.BankAccount
|
||||
if err := json.NewDecoder(r.Body).Decode(&acc); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
acc.ID = uuid.New()
|
||||
acc.TenantID, _ = uuid.Parse(tenantID)
|
||||
acc.IsActive = true
|
||||
|
||||
if err := h.repo.CreateBankAccount(&acc); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(acc)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) GetBankAccounts(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
accs, err := h.repo.GetBankAccountsByTenant(tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(accs)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) CreateTransaction(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
userID, _ := r.Context().Value(middleware.UserIDKey).(string)
|
||||
var t domain.FinancialTransaction
|
||||
if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
t.ID = uuid.New()
|
||||
t.TenantID, _ = uuid.Parse(tenantID)
|
||||
t.CreatedBy, _ = uuid.Parse(userID)
|
||||
|
||||
if err := h.repo.CreateTransaction(&t); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(t)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) GetTransactions(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
txs, err := h.repo.GetTransactionsByTenant(tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(txs)
|
||||
}
|
||||
|
||||
// ==================== PRODUCTS ====================
|
||||
|
||||
func (h *ERPHandler) CreateProduct(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
var p domain.Product
|
||||
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
p.ID = uuid.New()
|
||||
p.TenantID, _ = uuid.Parse(tenantID)
|
||||
p.IsActive = true
|
||||
|
||||
if err := h.repo.CreateProduct(&p); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(p)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) GetProducts(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
products, err := h.repo.GetProductsByTenant(tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(products)
|
||||
}
|
||||
|
||||
// ==================== ORDERS ====================
|
||||
|
||||
type createOrderRequest struct {
|
||||
Order domain.Order `json:"order"`
|
||||
Items []domain.OrderItem `json:"items"`
|
||||
}
|
||||
|
||||
func (h *ERPHandler) CreateOrder(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
userID, _ := r.Context().Value(middleware.UserIDKey).(string)
|
||||
var req createOrderRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
req.Order.ID = uuid.New()
|
||||
req.Order.TenantID, _ = uuid.Parse(tenantID)
|
||||
req.Order.CreatedBy, _ = uuid.Parse(userID)
|
||||
if req.Order.Status == "" {
|
||||
req.Order.Status = "draft"
|
||||
}
|
||||
|
||||
for i := range req.Items {
|
||||
req.Items[i].ID = uuid.New()
|
||||
req.Items[i].OrderID = req.Order.ID
|
||||
req.Items[i].CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
if err := h.repo.CreateOrder(&req.Order, req.Items); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(req.Order)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) GetOrders(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
orders, err := h.repo.GetOrdersByTenant(tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(orders)
|
||||
}
|
||||
|
||||
// ==================== ENTITIES ====================
|
||||
|
||||
func (h *ERPHandler) CreateEntity(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
var e domain.Entity
|
||||
if err := json.NewDecoder(r.Body).Decode(&e); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
e.ID = uuid.New()
|
||||
e.TenantID, _ = uuid.Parse(tenantID)
|
||||
if e.Status == "" {
|
||||
e.Status = "active"
|
||||
}
|
||||
|
||||
if err := h.repo.CreateEntity(&e); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(e)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) GetEntities(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
entityType := r.URL.Query().Get("type") // customer or supplier
|
||||
|
||||
entities, err := h.repo.GetEntitiesByTenant(tenantID, entityType)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(entities)
|
||||
}
|
||||
func (h *ERPHandler) UpdateTransaction(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var t domain.FinancialTransaction
|
||||
if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
t.ID = id
|
||||
t.TenantID, _ = uuid.Parse(tenantID)
|
||||
|
||||
if err := h.repo.UpdateTransaction(&t); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) DeleteTransaction(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
if err := h.repo.DeleteTransaction(idStr, tenantID); err != nil {
|
||||
log.Printf("❌ Error deleting transaction: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) UpdateEntity(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var e domain.Entity
|
||||
if err := json.NewDecoder(r.Body).Decode(&e); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
e.ID = id
|
||||
e.TenantID, _ = uuid.Parse(tenantID)
|
||||
|
||||
if err := h.repo.UpdateEntity(&e); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) DeleteEntity(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
if err := h.repo.DeleteEntity(idStr, tenantID); err != nil {
|
||||
log.Printf("❌ Error deleting entity: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) UpdateProduct(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var p domain.Product
|
||||
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
p.ID = id
|
||||
p.TenantID, _ = uuid.Parse(tenantID)
|
||||
|
||||
if err := h.repo.UpdateProduct(&p); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) DeleteProduct(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
if err := h.repo.DeleteProduct(idStr, tenantID); err != nil {
|
||||
log.Printf("❌ Error deleting product: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) UpdateBankAccount(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var a domain.BankAccount
|
||||
if err := json.NewDecoder(r.Body).Decode(&a); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
a.ID = id
|
||||
a.TenantID, _ = uuid.Parse(tenantID)
|
||||
|
||||
if err := h.repo.UpdateBankAccount(&a); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) DeleteBankAccount(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
if err := h.repo.DeleteBankAccount(idStr, tenantID); err != nil {
|
||||
log.Printf("❌ Error deleting bank account: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *ERPHandler) DeleteOrder(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
idStr := mux.Vars(r)["id"]
|
||||
if err := h.repo.DeleteOrder(idStr, tenantID); err != nil {
|
||||
log.Printf("❌ Error deleting order: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
-- Migration: 025_create_erp_tables.sql
|
||||
-- Description: Create tables for Finance, Inventory, and Order management
|
||||
|
||||
-- Financial Categories
|
||||
CREATE TABLE IF NOT EXISTS erp_financial_categories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('income', 'expense')),
|
||||
color VARCHAR(20),
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Bank Accounts
|
||||
CREATE TABLE IF NOT EXISTS erp_bank_accounts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
bank_name VARCHAR(255),
|
||||
initial_balance DECIMAL(15,2) DEFAULT 0.00,
|
||||
current_balance DECIMAL(15,2) DEFAULT 0.00,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Financial Transactions
|
||||
CREATE TABLE IF NOT EXISTS erp_financial_transactions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
account_id UUID REFERENCES erp_bank_accounts(id),
|
||||
category_id UUID REFERENCES erp_financial_categories(id),
|
||||
description TEXT,
|
||||
amount DECIMAL(15,2) NOT NULL,
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('income', 'expense')),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'paid', 'cancelled')),
|
||||
due_date DATE,
|
||||
payment_date TIMESTAMP WITH TIME ZONE,
|
||||
attachments TEXT[], -- URLs for proofs
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Products & Services
|
||||
CREATE TABLE IF NOT EXISTS erp_products (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
sku VARCHAR(100),
|
||||
description TEXT,
|
||||
price DECIMAL(15,2) NOT NULL,
|
||||
cost_price DECIMAL(15,2),
|
||||
type VARCHAR(20) DEFAULT 'product' CHECK (type IN ('product', 'service')),
|
||||
stock_quantity INT DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Orders
|
||||
CREATE TABLE IF NOT EXISTS erp_orders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
customer_id UUID REFERENCES companies(id), -- Linked to CRM (companies)
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'confirmed', 'completed', 'cancelled')),
|
||||
total_amount DECIMAL(15,2) DEFAULT 0.00,
|
||||
notes TEXT,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Order Items
|
||||
CREATE TABLE IF NOT EXISTS erp_order_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
order_id UUID NOT NULL REFERENCES erp_orders(id) ON DELETE CASCADE,
|
||||
product_id UUID NOT NULL REFERENCES erp_products(id),
|
||||
quantity INT NOT NULL DEFAULT 1,
|
||||
unit_price DECIMAL(15,2) NOT NULL,
|
||||
total_price DECIMAL(15,2) NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Indexes for performance and multi-tenancy
|
||||
CREATE INDEX idx_erp_fin_cat_tenant ON erp_financial_categories(tenant_id);
|
||||
CREATE INDEX idx_erp_bank_acc_tenant ON erp_bank_accounts(tenant_id);
|
||||
CREATE INDEX idx_erp_fin_trans_tenant ON erp_financial_transactions(tenant_id);
|
||||
CREATE INDEX idx_erp_products_tenant ON erp_products(tenant_id);
|
||||
CREATE INDEX idx_erp_orders_tenant ON erp_orders(tenant_id);
|
||||
CREATE INDEX idx_erp_order_items_order ON erp_order_items(order_id);
|
||||
@@ -0,0 +1,32 @@
|
||||
-- Migration: 026_create_erp_entities.sql
|
||||
-- Description: Create tables for Customers and Suppliers in ERP
|
||||
|
||||
-- ERP Entities (Customers and Suppliers)
|
||||
CREATE TABLE IF NOT EXISTS erp_entities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
document VARCHAR(20), -- CPF/CNPJ
|
||||
email VARCHAR(255),
|
||||
phone VARCHAR(20),
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('customer', 'supplier', 'both')),
|
||||
status VARCHAR(20) DEFAULT 'active',
|
||||
address TEXT,
|
||||
city VARCHAR(100),
|
||||
state VARCHAR(2),
|
||||
zip VARCHAR(10),
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Update Financial Transactions to link with Entities
|
||||
ALTER TABLE erp_financial_transactions ADD COLUMN IF NOT EXISTS entity_id UUID REFERENCES erp_entities(id);
|
||||
|
||||
-- Update Orders to link with Entities instead of companies (optional but more consistent for ERP)
|
||||
-- Keep customer_id for now to avoid breaking existing logic, but allow entity_id
|
||||
ALTER TABLE erp_orders ADD COLUMN IF NOT EXISTS entity_id UUID REFERENCES erp_entities(id);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_erp_entities_tenant ON erp_entities(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_erp_entities_type ON erp_entities(type);
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Migration: 027_add_payment_method_to_transactions.sql
|
||||
-- Description: Add payment_method field to financial transactions
|
||||
|
||||
ALTER TABLE erp_financial_transactions ADD COLUMN IF NOT EXISTS payment_method VARCHAR(50);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Migration: 028_add_crm_links_to_transactions.sql
|
||||
-- Description: Add fields to link financial transactions to CRM Customers and Companies
|
||||
|
||||
ALTER TABLE erp_financial_transactions ADD COLUMN IF NOT EXISTS crm_customer_id UUID REFERENCES crm_customers(id) ON DELETE SET NULL;
|
||||
ALTER TABLE erp_financial_transactions ADD COLUMN IF NOT EXISTS company_id UUID REFERENCES companies(id) ON DELETE SET NULL;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Migration: 029_create_documents_table.sql
|
||||
-- Description: Create table for text documents (Google Docs style)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS documents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT,
|
||||
status VARCHAR(50) DEFAULT 'draft',
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_tenant_id ON documents(tenant_id);
|
||||
@@ -0,0 +1,22 @@
|
||||
-- Migration: 030_add_subpages_and_activities_to_documents.sql
|
||||
-- Description: Add parent_id for subpages and tracking columns (Fixed)
|
||||
|
||||
ALTER TABLE documents
|
||||
ADD COLUMN IF NOT EXISTS parent_id UUID REFERENCES documents(id) ON DELETE CASCADE,
|
||||
ADD COLUMN IF NOT EXISTS last_updated_by UUID REFERENCES users(id),
|
||||
ADD COLUMN IF NOT EXISTS version INTEGER DEFAULT 1;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_parent_id ON documents(parent_id);
|
||||
|
||||
-- Simple activity log table
|
||||
CREATE TABLE IF NOT EXISTS document_activities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
action VARCHAR(50) NOT NULL, -- 'created', 'updated', 'deleted', 'status_change'
|
||||
description TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_doc_activities_doc_id ON document_activities(document_id);
|
||||
32
backend/internal/domain/document.go
Normal file
32
backend/internal/domain/document.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Document struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
|
||||
ParentID *uuid.UUID `json:"parent_id" db:"parent_id"`
|
||||
Title string `json:"title" db:"title"`
|
||||
Content string `json:"content" db:"content"` // JSON for blocks
|
||||
Status string `json:"status" db:"status"` // draft, published
|
||||
CreatedBy uuid.UUID `json:"created_by" db:"created_by"`
|
||||
LastUpdatedBy uuid.UUID `json:"last_updated_by" db:"last_updated_by"`
|
||||
Version int `json:"version" db:"version"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
type DocumentActivity struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
DocumentID uuid.UUID `json:"document_id" db:"document_id"`
|
||||
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
|
||||
UserID uuid.UUID `json:"user_id" db:"user_id"`
|
||||
UserName string `json:"user_name" db:"user_name"` // For join
|
||||
Action string `json:"action" db:"action"`
|
||||
Description string `json:"description" db:"description"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
}
|
||||
115
backend/internal/domain/erp.go
Normal file
115
backend/internal/domain/erp.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// FinancialCategory represents a category for income or expenses
|
||||
type FinancialCategory struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Type string `json:"type" db:"type"` // income, expense
|
||||
Color string `json:"color" db:"color"`
|
||||
IsActive bool `json:"is_active" db:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
// BankAccount represents a financial account in the agency
|
||||
type BankAccount struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
BankName string `json:"bank_name" db:"bank_name"`
|
||||
InitialBalance decimal.Decimal `json:"initial_balance" db:"initial_balance"`
|
||||
CurrentBalance decimal.Decimal `json:"current_balance" db:"current_balance"`
|
||||
IsActive bool `json:"is_active" db:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
// Entity represents a customer or supplier in the ERP
|
||||
type Entity struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Document string `json:"document" db:"document"`
|
||||
Email string `json:"email" db:"email"`
|
||||
Phone string `json:"phone" db:"phone"`
|
||||
Type string `json:"type" db:"type"` // customer, supplier, both
|
||||
Status string `json:"status" db:"status"`
|
||||
Address string `json:"address" db:"address"`
|
||||
City string `json:"city" db:"city"`
|
||||
State string `json:"state" db:"state"`
|
||||
Zip string `json:"zip" db:"zip"`
|
||||
Notes string `json:"notes" db:"notes"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
// FinancialTransaction represents a single financial movement
|
||||
type FinancialTransaction struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
|
||||
AccountID *uuid.UUID `json:"account_id" db:"account_id"`
|
||||
CategoryID *uuid.UUID `json:"category_id" db:"category_id"`
|
||||
EntityID *uuid.UUID `json:"entity_id" db:"entity_id"`
|
||||
CRMCustomerID *uuid.UUID `json:"crm_customer_id" db:"crm_customer_id"`
|
||||
CompanyID *uuid.UUID `json:"company_id" db:"company_id"`
|
||||
Description string `json:"description" db:"description"`
|
||||
Amount decimal.Decimal `json:"amount" db:"amount"`
|
||||
Type string `json:"type" db:"type"` // income, expense
|
||||
Status string `json:"status" db:"status"` // pending, paid, cancelled
|
||||
DueDate *time.Time `json:"due_date" db:"due_date"`
|
||||
PaymentDate *time.Time `json:"payment_date" db:"payment_date"`
|
||||
PaymentMethod string `json:"payment_method" db:"payment_method"`
|
||||
Attachments []string `json:"attachments" db:"attachments"`
|
||||
CreatedBy uuid.UUID `json:"created_by" db:"created_by"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
// Product represents a product or service in the catalog
|
||||
type Product struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
SKU string `json:"sku" db:"sku"`
|
||||
Description string `json:"description" db:"description"`
|
||||
Price decimal.Decimal `json:"price" db:"price"`
|
||||
CostPrice decimal.Decimal `json:"cost_price" db:"cost_price"`
|
||||
Type string `json:"type" db:"type"` // product, service
|
||||
StockQuantity int `json:"stock_quantity" db:"stock_quantity"`
|
||||
IsActive bool `json:"is_active" db:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
// Order represents a sales or service order
|
||||
type Order struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
|
||||
CustomerID *uuid.UUID `json:"customer_id" db:"customer_id"`
|
||||
EntityID *uuid.UUID `json:"entity_id" db:"entity_id"`
|
||||
Status string `json:"status" db:"status"` // draft, confirmed, completed, cancelled
|
||||
TotalAmount decimal.Decimal `json:"total_amount" db:"total_amount"`
|
||||
Notes string `json:"notes" db:"notes"`
|
||||
CreatedBy uuid.UUID `json:"created_by" db:"created_by"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
// OrderItem represents an item within an order
|
||||
type OrderItem struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
OrderID uuid.UUID `json:"order_id" db:"order_id"`
|
||||
ProductID uuid.UUID `json:"product_id" db:"product_id"`
|
||||
Quantity int `json:"quantity" db:"quantity"`
|
||||
UnitPrice decimal.Decimal `json:"unit_price" db:"unit_price"`
|
||||
TotalPrice decimal.Decimal `json:"total_price" db:"total_price"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
}
|
||||
156
backend/internal/repository/document_repository.go
Normal file
156
backend/internal/repository/document_repository.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"aggios-app/backend/internal/domain"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type DocumentRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewDocumentRepository(db *sql.DB) *DocumentRepository {
|
||||
return &DocumentRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *DocumentRepository) Create(doc *domain.Document) error {
|
||||
query := `
|
||||
INSERT INTO documents (id, tenant_id, parent_id, title, content, status, created_by, last_updated_by, version, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $7, 1, NOW(), NOW())
|
||||
`
|
||||
_, err := r.db.Exec(query, doc.ID, doc.TenantID, doc.ParentID, doc.Title, doc.Content, doc.Status, doc.CreatedBy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.logActivity(doc.ID.String(), doc.TenantID.String(), doc.CreatedBy.String(), "created", "Criou o documento")
|
||||
}
|
||||
|
||||
func (r *DocumentRepository) GetByTenant(tenantID string) ([]domain.Document, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, parent_id, title, content, status, created_by, last_updated_by, version, created_at, updated_at
|
||||
FROM documents
|
||||
WHERE tenant_id = $1 AND parent_id IS NULL
|
||||
ORDER BY updated_at DESC
|
||||
`
|
||||
rows, err := r.db.Query(query, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var docs []domain.Document
|
||||
for rows.Next() {
|
||||
var doc domain.Document
|
||||
if err := rows.Scan(&doc.ID, &doc.TenantID, &doc.ParentID, &doc.Title, &doc.Content, &doc.Status, &doc.CreatedBy, &doc.LastUpdatedBy, &doc.Version, &doc.CreatedAt, &doc.UpdatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
docs = append(docs, doc)
|
||||
}
|
||||
return docs, nil
|
||||
}
|
||||
|
||||
func (r *DocumentRepository) GetSubpages(parentID, tenantID string) ([]domain.Document, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, parent_id, title, content, status, created_by, last_updated_by, version, created_at, updated_at
|
||||
FROM documents
|
||||
WHERE parent_id = $1 AND tenant_id = $2
|
||||
ORDER BY created_at ASC
|
||||
`
|
||||
rows, err := r.db.Query(query, parentID, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var docs []domain.Document
|
||||
for rows.Next() {
|
||||
var doc domain.Document
|
||||
if err := rows.Scan(&doc.ID, &doc.TenantID, &doc.ParentID, &doc.Title, &doc.Content, &doc.Status, &doc.CreatedBy, &doc.LastUpdatedBy, &doc.Version, &doc.CreatedAt, &doc.UpdatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
docs = append(docs, doc)
|
||||
}
|
||||
return docs, nil
|
||||
}
|
||||
|
||||
func (r *DocumentRepository) GetByID(id, tenantID string) (*domain.Document, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, parent_id, title, content, status, created_by, last_updated_by, version, created_at, updated_at
|
||||
FROM documents
|
||||
WHERE id = $1 AND tenant_id = $2
|
||||
`
|
||||
var doc domain.Document
|
||||
err := r.db.QueryRow(query, id, tenantID).Scan(
|
||||
&doc.ID, &doc.TenantID, &doc.ParentID, &doc.Title, &doc.Content, &doc.Status, &doc.CreatedBy, &doc.LastUpdatedBy, &doc.Version, &doc.CreatedAt, &doc.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &doc, nil
|
||||
}
|
||||
|
||||
func (r *DocumentRepository) Update(doc *domain.Document) error {
|
||||
query := `
|
||||
UPDATE documents
|
||||
SET title = $1, content = $2, status = $3, last_updated_by = $4, version = version + 1, updated_at = NOW()
|
||||
WHERE id = $5 AND tenant_id = $6
|
||||
`
|
||||
_, err := r.db.Exec(query, doc.Title, doc.Content, doc.Status, doc.LastUpdatedBy, doc.ID, doc.TenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.logActivity(doc.ID.String(), doc.TenantID.String(), doc.LastUpdatedBy.String(), "updated", "Atualizou o conteúdo")
|
||||
}
|
||||
|
||||
func (r *DocumentRepository) Delete(id, tenantID string) error {
|
||||
query := "DELETE FROM documents WHERE id = $1 AND tenant_id = $2"
|
||||
res, err := r.db.Exec(query, id, tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, _ := res.RowsAffected()
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("document not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DocumentRepository) logActivity(docID, tenantID, userID, action, description string) error {
|
||||
query := `
|
||||
INSERT INTO document_activities (document_id, tenant_id, user_id, action, description)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
`
|
||||
_, err := r.db.Exec(query, docID, tenantID, userID, action, description)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *DocumentRepository) GetActivities(docID, tenantID string) ([]domain.DocumentActivity, error) {
|
||||
query := `
|
||||
SELECT a.id, a.document_id, a.tenant_id, a.user_id, COALESCE(u.first_name, 'Usuário Removido') as user_name, a.action, a.description, a.created_at
|
||||
FROM document_activities a
|
||||
LEFT JOIN users u ON a.user_id = u.id
|
||||
WHERE a.document_id = $1 AND a.tenant_id = $2
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT 20
|
||||
`
|
||||
rows, err := r.db.Query(query, docID, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var activities []domain.DocumentActivity
|
||||
for rows.Next() {
|
||||
var a domain.DocumentActivity
|
||||
err := rows.Scan(&a.ID, &a.DocumentID, &a.TenantID, &a.UserID, &a.UserName, &a.Action, &a.Description, &a.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
activities = append(activities, a)
|
||||
}
|
||||
return activities, nil
|
||||
}
|
||||
493
backend/internal/repository/erp_repository.go
Normal file
493
backend/internal/repository/erp_repository.go
Normal file
@@ -0,0 +1,493 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"aggios-app/backend/internal/domain"
|
||||
"database/sql"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
type ERPRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewERPRepository(db *sql.DB) *ERPRepository {
|
||||
return &ERPRepository{db: db}
|
||||
}
|
||||
|
||||
// ==================== FINANCE: CATEGORIES ====================
|
||||
|
||||
func (r *ERPRepository) CreateFinancialCategory(cat *domain.FinancialCategory) error {
|
||||
query := `
|
||||
INSERT INTO erp_financial_categories (id, tenant_id, name, type, color, is_active)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING created_at, updated_at
|
||||
`
|
||||
return r.db.QueryRow(
|
||||
query,
|
||||
cat.ID, cat.TenantID, cat.Name, cat.Type, cat.Color, cat.IsActive,
|
||||
).Scan(&cat.CreatedAt, &cat.UpdatedAt)
|
||||
}
|
||||
|
||||
func (r *ERPRepository) GetFinancialCategoriesByTenant(tenantID string) ([]domain.FinancialCategory, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, name, type, color, is_active, created_at, updated_at
|
||||
FROM erp_financial_categories
|
||||
WHERE tenant_id = $1
|
||||
ORDER BY name ASC
|
||||
`
|
||||
rows, err := r.db.Query(query, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var categories []domain.FinancialCategory
|
||||
for rows.Next() {
|
||||
var c domain.FinancialCategory
|
||||
err := rows.Scan(&c.ID, &c.TenantID, &c.Name, &c.Type, &c.Color, &c.IsActive, &c.CreatedAt, &c.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
categories = append(categories, c)
|
||||
}
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
// ==================== FINANCE: BANK ACCOUNTS ====================
|
||||
|
||||
func (r *ERPRepository) CreateBankAccount(acc *domain.BankAccount) error {
|
||||
query := `
|
||||
INSERT INTO erp_bank_accounts (id, tenant_id, name, bank_name, initial_balance, current_balance, is_active)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING created_at, updated_at
|
||||
`
|
||||
return r.db.QueryRow(
|
||||
query,
|
||||
acc.ID, acc.TenantID, acc.Name, acc.BankName, acc.InitialBalance, acc.InitialBalance, acc.IsActive,
|
||||
).Scan(&acc.CreatedAt, &acc.UpdatedAt)
|
||||
}
|
||||
|
||||
func (r *ERPRepository) GetBankAccountsByTenant(tenantID string) ([]domain.BankAccount, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, name, bank_name, initial_balance, current_balance, is_active, created_at, updated_at
|
||||
FROM erp_bank_accounts
|
||||
WHERE tenant_id = $1
|
||||
ORDER BY name ASC
|
||||
`
|
||||
rows, err := r.db.Query(query, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var accounts []domain.BankAccount
|
||||
for rows.Next() {
|
||||
var a domain.BankAccount
|
||||
err := rows.Scan(&a.ID, &a.TenantID, &a.Name, &a.BankName, &a.InitialBalance, &a.CurrentBalance, &a.IsActive, &a.CreatedAt, &a.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accounts = append(accounts, a)
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// ==================== ENTITIES: CUSTOMERS & SUPPLIERS ====================
|
||||
|
||||
func (r *ERPRepository) CreateEntity(e *domain.Entity) error {
|
||||
query := `
|
||||
INSERT INTO erp_entities (id, tenant_id, name, document, email, phone, type, status, address, city, state, zip, notes)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||
RETURNING created_at, updated_at
|
||||
`
|
||||
return r.db.QueryRow(
|
||||
query,
|
||||
e.ID, e.TenantID, e.Name, e.Document, e.Email, e.Phone, e.Type, e.Status, e.Address, e.City, e.State, e.Zip, e.Notes,
|
||||
).Scan(&e.CreatedAt, &e.UpdatedAt)
|
||||
}
|
||||
|
||||
func (r *ERPRepository) GetEntitiesByTenant(tenantID string, entityType string) ([]domain.Entity, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, name, document, email, phone, type, status, address, city, state, zip, notes, created_at, updated_at
|
||||
FROM erp_entities
|
||||
WHERE tenant_id = $1
|
||||
`
|
||||
var args []interface{}
|
||||
args = append(args, tenantID)
|
||||
|
||||
if entityType != "" {
|
||||
query += " AND (type = $2 OR type = 'both')"
|
||||
args = append(args, entityType)
|
||||
}
|
||||
|
||||
query += " ORDER BY name ASC"
|
||||
|
||||
rows, err := r.db.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var entities []domain.Entity
|
||||
for rows.Next() {
|
||||
var e domain.Entity
|
||||
err := rows.Scan(
|
||||
&e.ID, &e.TenantID, &e.Name, &e.Document, &e.Email, &e.Phone, &e.Type, &e.Status, &e.Address, &e.City, &e.State, &e.Zip, &e.Notes, &e.CreatedAt, &e.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entities = append(entities, e)
|
||||
}
|
||||
return entities, nil
|
||||
}
|
||||
|
||||
// ==================== FINANCE: TRANSACTIONS ====================
|
||||
|
||||
func (r *ERPRepository) CreateTransaction(t *domain.FinancialTransaction) error {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `
|
||||
INSERT INTO erp_financial_transactions (
|
||||
id, tenant_id, account_id, category_id, entity_id, crm_customer_id, company_id, description, amount, type, status, due_date, payment_date, payment_method, attachments, created_by
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
||||
RETURNING created_at, updated_at
|
||||
`
|
||||
err = tx.QueryRow(
|
||||
query,
|
||||
t.ID, t.TenantID, t.AccountID, t.CategoryID, t.EntityID, t.CRMCustomerID, t.CompanyID, t.Description, t.Amount, t.Type, t.Status, t.DueDate, t.PaymentDate, t.PaymentMethod, pq.Array(t.Attachments), t.CreatedBy,
|
||||
).Scan(&t.CreatedAt, &t.UpdatedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update balance if paid
|
||||
if t.Status == "paid" && t.AccountID != nil {
|
||||
balanceQuery := ""
|
||||
if t.Type == "income" {
|
||||
balanceQuery = "UPDATE erp_bank_accounts SET current_balance = current_balance + $1 WHERE id = $2"
|
||||
} else {
|
||||
balanceQuery = "UPDATE erp_bank_accounts SET current_balance = current_balance - $1 WHERE id = $2"
|
||||
}
|
||||
_, err = tx.Exec(balanceQuery, t.Amount, t.AccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *ERPRepository) GetTransactionsByTenant(tenantID string) ([]domain.FinancialTransaction, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, account_id, category_id, entity_id, crm_customer_id, company_id, description, amount, type, status, due_date, payment_date, payment_method, attachments, created_by, created_at, updated_at
|
||||
FROM erp_financial_transactions
|
||||
WHERE tenant_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
rows, err := r.db.Query(query, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var transactions []domain.FinancialTransaction
|
||||
for rows.Next() {
|
||||
var t domain.FinancialTransaction
|
||||
err := rows.Scan(
|
||||
&t.ID, &t.TenantID, &t.AccountID, &t.CategoryID, &t.EntityID, &t.CRMCustomerID, &t.CompanyID, &t.Description, &t.Amount, &t.Type, &t.Status, &t.DueDate, &t.PaymentDate, &t.PaymentMethod, pq.Array(&t.Attachments), &t.CreatedBy, &t.CreatedAt, &t.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions = append(transactions, t)
|
||||
}
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
// ==================== PRODUCTS ====================
|
||||
|
||||
func (r *ERPRepository) CreateProduct(p *domain.Product) error {
|
||||
query := `
|
||||
INSERT INTO erp_products (id, tenant_id, name, sku, description, price, cost_price, type, stock_quantity, is_active)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
RETURNING created_at, updated_at
|
||||
`
|
||||
return r.db.QueryRow(
|
||||
query,
|
||||
p.ID, p.TenantID, p.Name, p.SKU, p.Description, p.Price, p.CostPrice, p.Type, p.StockQuantity, p.IsActive,
|
||||
).Scan(&p.CreatedAt, &p.UpdatedAt)
|
||||
}
|
||||
|
||||
func (r *ERPRepository) GetProductsByTenant(tenantID string) ([]domain.Product, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, name, sku, description, price, cost_price, type, stock_quantity, is_active, created_at, updated_at
|
||||
FROM erp_products
|
||||
WHERE tenant_id = $1
|
||||
ORDER BY name ASC
|
||||
`
|
||||
rows, err := r.db.Query(query, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var products []domain.Product
|
||||
for rows.Next() {
|
||||
var p domain.Product
|
||||
err := rows.Scan(&p.ID, &p.TenantID, &p.Name, &p.SKU, &p.Description, &p.Price, &p.CostPrice, &p.Type, &p.StockQuantity, &p.IsActive, &p.CreatedAt, &p.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
products = append(products, p)
|
||||
}
|
||||
return products, nil
|
||||
}
|
||||
|
||||
// ==================== ORDERS ====================
|
||||
|
||||
func (r *ERPRepository) CreateOrder(o *domain.Order, items []domain.OrderItem) error {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
orderQuery := `
|
||||
INSERT INTO erp_orders (id, tenant_id, customer_id, entity_id, status, total_amount, notes, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING created_at, updated_at
|
||||
`
|
||||
err = tx.QueryRow(
|
||||
orderQuery,
|
||||
o.ID, o.TenantID, o.CustomerID, o.EntityID, o.Status, o.TotalAmount, o.Notes, o.CreatedBy,
|
||||
).Scan(&o.CreatedAt, &o.UpdatedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
itemQuery := `
|
||||
INSERT INTO erp_order_items (id, order_id, product_id, quantity, unit_price, total_price)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
`
|
||||
for _, item := range items {
|
||||
_, err = tx.Exec(itemQuery, item.ID, o.ID, item.ProductID, item.Quantity, item.UnitPrice, item.TotalPrice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update stock if product
|
||||
stockQuery := "UPDATE erp_products SET stock_quantity = stock_quantity - $1 WHERE id = $2 AND type = 'product'"
|
||||
_, err = tx.Exec(stockQuery, item.Quantity, item.ProductID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *ERPRepository) GetOrdersByTenant(tenantID string) ([]domain.Order, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, customer_id, status, total_amount, notes, created_by, created_at, updated_at
|
||||
FROM erp_orders
|
||||
WHERE tenant_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
rows, err := r.db.Query(query, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var orders []domain.Order
|
||||
for rows.Next() {
|
||||
var o domain.Order
|
||||
err := rows.Scan(&o.ID, &o.TenantID, &o.CustomerID, &o.Status, &o.TotalAmount, &o.Notes, &o.CreatedBy, &o.CreatedAt, &o.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orders = append(orders, o)
|
||||
}
|
||||
return orders, nil
|
||||
}
|
||||
func (r *ERPRepository) UpdateTransaction(t *domain.FinancialTransaction) error {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// Get old transaction to adjust balance
|
||||
var oldT domain.FinancialTransaction
|
||||
err = tx.QueryRow(`
|
||||
SELECT amount, type, status, account_id
|
||||
FROM erp_financial_transactions
|
||||
WHERE id = $1 AND tenant_id = $2`, t.ID, t.TenantID).
|
||||
Scan(&oldT.Amount, &oldT.Type, &oldT.Status, &oldT.AccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Falls back to old type if not provided in request
|
||||
if t.Type == "" {
|
||||
t.Type = oldT.Type
|
||||
}
|
||||
|
||||
// Reverse old balance impact
|
||||
if oldT.Status == "paid" && oldT.AccountID != nil {
|
||||
balanceQuery := ""
|
||||
if oldT.Type == "income" {
|
||||
balanceQuery = "UPDATE erp_bank_accounts SET current_balance = current_balance - $1 WHERE id = $2"
|
||||
} else {
|
||||
balanceQuery = "UPDATE erp_bank_accounts SET current_balance = current_balance + $1 WHERE id = $2"
|
||||
}
|
||||
_, err = tx.Exec(balanceQuery, oldT.Amount, oldT.AccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
query := `
|
||||
UPDATE erp_financial_transactions
|
||||
SET description = $1, amount = $2, type = $3, status = $4, due_date = $5, payment_date = $6,
|
||||
category_id = $7, entity_id = $8, crm_customer_id = $9, company_id = $10, account_id = $11, payment_method = $12, updated_at = NOW()
|
||||
WHERE id = $13 AND tenant_id = $14
|
||||
`
|
||||
_, err = tx.Exec(query,
|
||||
t.Description, t.Amount, t.Type, t.Status, t.DueDate, t.PaymentDate,
|
||||
t.CategoryID, t.EntityID, t.CRMCustomerID, t.CompanyID, t.AccountID, t.PaymentMethod,
|
||||
t.ID, t.TenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Apply new balance impact
|
||||
if t.Status == "paid" && t.AccountID != nil {
|
||||
balanceQuery := ""
|
||||
if t.Type == "income" {
|
||||
balanceQuery = "UPDATE erp_bank_accounts SET current_balance = current_balance + $1 WHERE id = $2"
|
||||
} else {
|
||||
balanceQuery = "UPDATE erp_bank_accounts SET current_balance = current_balance - $1 WHERE id = $2"
|
||||
}
|
||||
_, err = tx.Exec(balanceQuery, t.Amount, t.AccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *ERPRepository) DeleteTransaction(id, tenantID string) error {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// Adjust balance before delete
|
||||
var t domain.FinancialTransaction
|
||||
err = tx.QueryRow(`
|
||||
SELECT amount, type, status, account_id
|
||||
FROM erp_financial_transactions
|
||||
WHERE id = $1 AND tenant_id = $2`, id, tenantID).
|
||||
Scan(&t.Amount, &t.Type, &t.Status, &t.AccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.Status == "paid" && t.AccountID != nil {
|
||||
balanceQuery := ""
|
||||
if t.Type == "income" {
|
||||
balanceQuery = "UPDATE erp_bank_accounts SET current_balance = current_balance - $1 WHERE id = $2"
|
||||
} else {
|
||||
balanceQuery = "UPDATE erp_bank_accounts SET current_balance = current_balance + $1 WHERE id = $2"
|
||||
}
|
||||
_, err = tx.Exec(balanceQuery, t.Amount, t.AccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = tx.Exec("DELETE FROM erp_financial_transactions WHERE id = $1 AND tenant_id = $2", id, tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
|
||||
func (r *ERPRepository) UpdateEntity(e *domain.Entity) error {
|
||||
query := `
|
||||
UPDATE erp_entities
|
||||
SET name = $1, document = $2, email = $3, phone = $4, type = $5, status = $6,
|
||||
address = $7, city = $8, state = $9, zip = $10, notes = $11, updated_at = NOW()
|
||||
WHERE id = $12 AND tenant_id = $13
|
||||
`
|
||||
_, err := r.db.Exec(query, e.Name, e.Document, e.Email, e.Phone, e.Type, e.Status, e.Address, e.City, e.State, e.Zip, e.Notes, e.ID, e.TenantID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ERPRepository) DeleteEntity(id, tenantID string) error {
|
||||
_, err := r.db.Exec("DELETE FROM erp_entities WHERE id = $1 AND tenant_id = $2", id, tenantID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ERPRepository) UpdateProduct(p *domain.Product) error {
|
||||
query := `
|
||||
UPDATE erp_products
|
||||
SET name = $1, sku = $2, description = $3, price = $4, cost_price = $5,
|
||||
type = $6, stock_quantity = $7, is_active = $8, updated_at = NOW()
|
||||
WHERE id = $9 AND tenant_id = $10
|
||||
`
|
||||
_, err := r.db.Exec(query, p.Name, p.SKU, p.Description, p.Price, p.CostPrice, p.Type, p.StockQuantity, p.IsActive, p.ID, p.TenantID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ERPRepository) DeleteProduct(id, tenantID string) error {
|
||||
_, err := r.db.Exec("DELETE FROM erp_products WHERE id = $1 AND tenant_id = $2", id, tenantID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ERPRepository) UpdateBankAccount(a *domain.BankAccount) error {
|
||||
query := `
|
||||
UPDATE erp_bank_accounts
|
||||
SET name = $1, bank_name = $2, initial_balance = $3, is_active = $4, updated_at = NOW()
|
||||
WHERE id = $5 AND tenant_id = $6
|
||||
`
|
||||
_, err := r.db.Exec(query, a.Name, a.BankName, a.InitialBalance, a.IsActive, a.ID, a.TenantID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ERPRepository) DeleteBankAccount(id, tenantID string) error {
|
||||
_, err := r.db.Exec("DELETE FROM erp_bank_accounts WHERE id = $1 AND tenant_id = $2", id, tenantID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ERPRepository) DeleteOrder(id, tenantID string) error {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// Deleta os itens do pedido primeiro
|
||||
_, err = tx.Exec("DELETE FROM erp_order_items WHERE order_id = $1", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deleta o pedido
|
||||
_, err = tx.Exec("DELETE FROM erp_orders WHERE id = $1 AND tenant_id = $2", id, tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
Reference in New Issue
Block a user