package repository import ( "aggios-app/backend/internal/domain" "database/sql" "fmt" "github.com/lib/pq" ) type CRMRepository struct { db *sql.DB } func NewCRMRepository(db *sql.DB) *CRMRepository { return &CRMRepository{db: db} } // ==================== CUSTOMERS ==================== func (r *CRMRepository) CreateCustomer(customer *domain.CRMCustomer) error { query := ` INSERT INTO crm_customers ( id, tenant_id, name, email, phone, company, position, address, city, state, zip_code, country, notes, tags, is_active, created_by ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING created_at, updated_at ` return r.db.QueryRow( query, customer.ID, customer.TenantID, customer.Name, customer.Email, customer.Phone, customer.Company, customer.Position, customer.Address, customer.City, customer.State, customer.ZipCode, customer.Country, customer.Notes, pq.Array(customer.Tags), customer.IsActive, customer.CreatedBy, ).Scan(&customer.CreatedAt, &customer.UpdatedAt) } func (r *CRMRepository) GetCustomersByTenant(tenantID string) ([]domain.CRMCustomer, error) { query := ` SELECT id, tenant_id, name, email, phone, company, position, address, city, state, zip_code, country, notes, tags, is_active, created_by, created_at, updated_at FROM crm_customers WHERE tenant_id = $1 AND is_active = true ORDER BY created_at DESC ` rows, err := r.db.Query(query, tenantID) if err != nil { return nil, err } defer rows.Close() var customers []domain.CRMCustomer for rows.Next() { var c domain.CRMCustomer err := rows.Scan( &c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company, &c.Position, &c.Address, &c.City, &c.State, &c.ZipCode, &c.Country, &c.Notes, pq.Array(&c.Tags), &c.IsActive, &c.CreatedBy, &c.CreatedAt, &c.UpdatedAt, ) if err != nil { return nil, err } customers = append(customers, c) } return customers, nil } func (r *CRMRepository) GetCustomerByID(id string, tenantID string) (*domain.CRMCustomer, error) { query := ` SELECT id, tenant_id, name, email, phone, company, position, address, city, state, zip_code, country, notes, tags, is_active, created_by, created_at, updated_at FROM crm_customers WHERE id = $1 AND tenant_id = $2 ` var c domain.CRMCustomer err := r.db.QueryRow(query, id, tenantID).Scan( &c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company, &c.Position, &c.Address, &c.City, &c.State, &c.ZipCode, &c.Country, &c.Notes, pq.Array(&c.Tags), &c.IsActive, &c.CreatedBy, &c.CreatedAt, &c.UpdatedAt, ) if err != nil { return nil, err } return &c, nil } func (r *CRMRepository) UpdateCustomer(customer *domain.CRMCustomer) error { query := ` UPDATE crm_customers SET name = $1, email = $2, phone = $3, company = $4, position = $5, address = $6, city = $7, state = $8, zip_code = $9, country = $10, notes = $11, tags = $12, is_active = $13 WHERE id = $14 AND tenant_id = $15 ` result, err := r.db.Exec( query, customer.Name, customer.Email, customer.Phone, customer.Company, customer.Position, customer.Address, customer.City, customer.State, customer.ZipCode, customer.Country, customer.Notes, pq.Array(customer.Tags), customer.IsActive, customer.ID, customer.TenantID, ) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("customer not found") } return nil } func (r *CRMRepository) DeleteCustomer(id string, tenantID string) error { query := `DELETE FROM crm_customers WHERE id = $1 AND tenant_id = $2` result, err := r.db.Exec(query, id, tenantID) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("customer not found") } return nil } // ==================== LISTS ==================== func (r *CRMRepository) CreateList(list *domain.CRMList) error { query := ` INSERT INTO crm_lists (id, tenant_id, name, description, color, created_by) VALUES ($1, $2, $3, $4, $5, $6) RETURNING created_at, updated_at ` return r.db.QueryRow( query, list.ID, list.TenantID, list.Name, list.Description, list.Color, list.CreatedBy, ).Scan(&list.CreatedAt, &list.UpdatedAt) } func (r *CRMRepository) GetListsByTenant(tenantID string) ([]domain.CRMListWithCustomers, error) { query := ` SELECT l.id, l.tenant_id, l.name, l.description, l.color, l.created_by, l.created_at, l.updated_at, COUNT(cl.customer_id) as customer_count FROM crm_lists l LEFT JOIN crm_customer_lists cl ON l.id = cl.list_id WHERE l.tenant_id = $1 GROUP BY l.id ORDER BY l.created_at DESC ` rows, err := r.db.Query(query, tenantID) if err != nil { return nil, err } defer rows.Close() var lists []domain.CRMListWithCustomers for rows.Next() { var l domain.CRMListWithCustomers err := rows.Scan( &l.ID, &l.TenantID, &l.Name, &l.Description, &l.Color, &l.CreatedBy, &l.CreatedAt, &l.UpdatedAt, &l.CustomerCount, ) if err != nil { return nil, err } lists = append(lists, l) } return lists, nil } func (r *CRMRepository) GetListByID(id string, tenantID string) (*domain.CRMList, error) { query := ` SELECT id, tenant_id, name, description, color, created_by, created_at, updated_at FROM crm_lists WHERE id = $1 AND tenant_id = $2 ` var l domain.CRMList err := r.db.QueryRow(query, id, tenantID).Scan( &l.ID, &l.TenantID, &l.Name, &l.Description, &l.Color, &l.CreatedBy, &l.CreatedAt, &l.UpdatedAt, ) if err != nil { return nil, err } return &l, nil } func (r *CRMRepository) UpdateList(list *domain.CRMList) error { query := ` UPDATE crm_lists SET name = $1, description = $2, color = $3 WHERE id = $4 AND tenant_id = $5 ` result, err := r.db.Exec(query, list.Name, list.Description, list.Color, list.ID, list.TenantID) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("list not found") } return nil } func (r *CRMRepository) DeleteList(id string, tenantID string) error { query := `DELETE FROM crm_lists WHERE id = $1 AND tenant_id = $2` result, err := r.db.Exec(query, id, tenantID) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("list not found") } return nil } // ==================== CUSTOMER <-> LIST ==================== func (r *CRMRepository) AddCustomerToList(customerID, listID, addedBy string) error { query := ` INSERT INTO crm_customer_lists (customer_id, list_id, added_by) VALUES ($1, $2, $3) ON CONFLICT (customer_id, list_id) DO NOTHING ` _, err := r.db.Exec(query, customerID, listID, addedBy) return err } func (r *CRMRepository) RemoveCustomerFromList(customerID, listID string) error { query := `DELETE FROM crm_customer_lists WHERE customer_id = $1 AND list_id = $2` _, err := r.db.Exec(query, customerID, listID) return err } func (r *CRMRepository) GetCustomerLists(customerID string) ([]domain.CRMList, error) { query := ` SELECT l.id, l.tenant_id, l.name, l.description, l.color, l.created_by, l.created_at, l.updated_at FROM crm_lists l INNER JOIN crm_customer_lists cl ON l.id = cl.list_id WHERE cl.customer_id = $1 ORDER BY l.name ` rows, err := r.db.Query(query, customerID) if err != nil { return nil, err } defer rows.Close() var lists []domain.CRMList for rows.Next() { var l domain.CRMList err := rows.Scan( &l.ID, &l.TenantID, &l.Name, &l.Description, &l.Color, &l.CreatedBy, &l.CreatedAt, &l.UpdatedAt, ) if err != nil { return nil, err } lists = append(lists, l) } return lists, nil } func (r *CRMRepository) GetListCustomers(listID string, tenantID string) ([]domain.CRMCustomer, error) { query := ` SELECT c.id, c.tenant_id, c.name, c.email, c.phone, c.company, c.position, c.address, c.city, c.state, c.zip_code, c.country, c.notes, c.tags, c.is_active, c.created_by, c.created_at, c.updated_at FROM crm_customers c INNER JOIN crm_customer_lists cl ON c.id = cl.customer_id WHERE cl.list_id = $1 AND c.tenant_id = $2 AND c.is_active = true ORDER BY c.name ` rows, err := r.db.Query(query, listID, tenantID) if err != nil { return nil, err } defer rows.Close() var customers []domain.CRMCustomer for rows.Next() { var c domain.CRMCustomer err := rows.Scan( &c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company, &c.Position, &c.Address, &c.City, &c.State, &c.ZipCode, &c.Country, &c.Notes, pq.Array(&c.Tags), &c.IsActive, &c.CreatedBy, &c.CreatedAt, &c.UpdatedAt, ) if err != nil { return nil, err } customers = append(customers, c) } return customers, nil }