494 lines
15 KiB
Go
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()
|
|
}
|