package repository import ( "context" "database/sql" "fmt" "time" "github.com/lib/pq" ) // CustomerRepository hanterar alla customer-relaterade databasoperationer type CustomerRepository struct { db *sql.DB } func NewCustomerRepository(db *sql.DB) *CustomerRepository { return &CustomerRepository{db: db} } // Customer representerar en kund type Customer struct { ID string `json:"id"` TenantID string `json:"tenant_id"` Name string `json:"name"` Email string `json:"email"` Phone string `json:"phone"` Company string `json:"company"` OrgNumber string `json:"org_number"` Status string `json:"status"` Source string `json:"source"` Tags []string `json:"tags"` AssignedTo *string `json:"assigned_to"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // CreateCustomer skapar en ny kund func (r *CustomerRepository) CreateCustomer(ctx context.Context, c *Customer) error { query := ` INSERT INTO boc_customers (tenant_id, name, email, phone, company, org_number, status, source, tags, assigned_to) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at ` err := r.db.QueryRowContext(ctx, query, c.TenantID, c.Name, c.Email, c.Phone, c.Company, c.OrgNumber, c.Status, c.Source, pq.Array(c.Tags), c.AssignedTo, ).Scan(&c.ID, &c.CreatedAt, &c.UpdatedAt) if err != nil { return fmt.Errorf("create customer: %w", err) } return nil } // GetCustomer hämtar en kund med ID func (r *CustomerRepository) GetCustomer(ctx context.Context, id string) (*Customer, error) { query := ` SELECT id, tenant_id, name, email, phone, company, org_number, status, source, tags, assigned_to, created_at, updated_at FROM boc_customers WHERE id = $1 ` var c Customer var tags pq.StringArray err := r.db.QueryRowContext(ctx, query, id).Scan( &c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company, &c.OrgNumber, &c.Status, &c.Source, &tags, &c.AssignedTo, &c.CreatedAt, &c.UpdatedAt, ) if err == sql.ErrNoRows { return nil, fmt.Errorf("customer not found") } if err != nil { return nil, fmt.Errorf("get customer: %w", err) } c.Tags = []string(tags) return &c, nil } // ListCustomers hämtar kunder med pagination func (r *CustomerRepository) ListCustomers(ctx context.Context, tenantID, status string, limit, offset int) ([]Customer, int, error) { // Hämta total count var total int countQuery := `SELECT COUNT(*) FROM boc_customers WHERE tenant_id = $1 AND status = $2` if err := r.db.QueryRowContext(ctx, countQuery, tenantID, status).Scan(&total); err != nil { return nil, 0, fmt.Errorf("count customers: %w", err) } // Hämta kunder query := ` SELECT id, tenant_id, name, email, phone, company, org_number, status, source, tags, assigned_to, created_at, updated_at FROM boc_customers WHERE tenant_id = $1 AND status = $2 ORDER BY created_at DESC LIMIT $3 OFFSET $4 ` rows, err := r.db.QueryContext(ctx, query, tenantID, status, limit, offset) if err != nil { return nil, 0, fmt.Errorf("list customers: %w", err) } defer rows.Close() var customers []Customer for rows.Next() { var c Customer var tags pq.StringArray if err := rows.Scan( &c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company, &c.OrgNumber, &c.Status, &c.Source, &tags, &c.AssignedTo, &c.CreatedAt, &c.UpdatedAt, ); err != nil { continue } c.Tags = []string(tags) customers = append(customers, c) } return customers, total, rows.Err() } // UpdateCustomer uppdaterar en kund func (r *CustomerRepository) UpdateCustomer(ctx context.Context, c *Customer) error { query := ` UPDATE boc_customers SET name = $1, email = $2, phone = $3, company = $4, org_number = $5, status = $6, source = $7, tags = $8, assigned_to = $9, updated_at = NOW() WHERE id = $10 RETURNING updated_at ` err := r.db.QueryRowContext(ctx, query, c.Name, c.Email, c.Phone, c.Company, c.OrgNumber, c.Status, c.Source, pq.Array(c.Tags), c.AssignedTo, c.ID, ).Scan(&c.UpdatedAt) if err == sql.ErrNoRows { return fmt.Errorf("customer not found") } if err != nil { return fmt.Errorf("update customer: %w", err) } return nil } // DeleteCustomer markerar en kund som borttagen (soft delete) func (r *CustomerRepository) DeleteCustomer(ctx context.Context, id string) error { query := ` UPDATE boc_customers SET status = 'deleted', updated_at = NOW() WHERE id = $1 ` result, err := r.db.ExecContext(ctx, query, id) if err != nil { return fmt.Errorf("delete customer: %w", err) } rows, _ := result.RowsAffected() if rows == 0 { return fmt.Errorf("customer not found") } return nil } // SearchCustomers söker efter kunder func (r *CustomerRepository) SearchCustomers(ctx context.Context, tenantID, query string, limit int) ([]Customer, error) { sql := ` SELECT id, tenant_id, name, email, phone, company, org_number, status, source, tags, assigned_to, created_at, updated_at FROM boc_customers WHERE tenant_id = $1 AND status != 'deleted' AND (name ILIKE $2 OR email ILIKE $2 OR company ILIKE $2) ORDER BY name LIMIT $3 ` searchTerm := "%" + query + "%" rows, err := r.db.QueryContext(ctx, sql, tenantID, searchTerm, limit) if err != nil { return nil, fmt.Errorf("search customers: %w", err) } defer rows.Close() var customers []Customer for rows.Next() { var c Customer var tags pq.StringArray if err := rows.Scan( &c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company, &c.OrgNumber, &c.Status, &c.Source, &tags, &c.AssignedTo, &c.CreatedAt, &c.UpdatedAt, ); err != nil { continue } c.Tags = []string(tags) customers = append(customers, c) } return customers, rows.Err() }