Files
aggios.app/backend/internal/repository/erp_repository.go
2025-12-29 17:23:59 -03:00

494 lines
15 KiB
Go

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()
}