Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
239 lines
6.5 KiB
Go
239 lines
6.5 KiB
Go
package models
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/timetracker/backend/internal/db"
|
|
"github.com/timetracker/backend/internal/types"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Project represents a project in the system
|
|
type Project struct {
|
|
EntityBase
|
|
Name string `gorm:"column:name;not null"`
|
|
CustomerID *types.ULID `gorm:"column:customer_id;type:bytea;index"`
|
|
|
|
// Relationships (for Eager Loading)
|
|
Customer *Customer `gorm:"foreignKey:CustomerID"`
|
|
}
|
|
|
|
// TableName specifies the table name for GORM
|
|
func (Project) TableName() string {
|
|
return "projects"
|
|
}
|
|
|
|
// ProjectCreate contains the fields for creating a new project
|
|
type ProjectCreate struct {
|
|
Name string
|
|
CustomerID *types.ULID
|
|
}
|
|
|
|
// ProjectUpdate contains the updatable fields of a project
|
|
type ProjectUpdate struct {
|
|
ID types.ULID `gorm:"-"` // Exclude from updates
|
|
Name *string `gorm:"column:name"`
|
|
CustomerID types.Nullable[types.ULID] `gorm:"column:customer_id"`
|
|
}
|
|
|
|
// Validate checks if the Create struct contains valid data
|
|
func (pc *ProjectCreate) Validate() error {
|
|
if pc.Name == "" {
|
|
return errors.New("project name cannot be empty")
|
|
}
|
|
// Check for valid CustomerID
|
|
if pc.CustomerID != nil && pc.CustomerID.Compare(types.ULID{}) == 0 {
|
|
return errors.New("customerID cannot be empty")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Validate checks if the Update struct contains valid data
|
|
func (pu *ProjectUpdate) Validate() error {
|
|
if pu.Name != nil && *pu.Name == "" {
|
|
return errors.New("project name cannot be empty")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetProjectByID finds a project by its ID
|
|
func GetProjectByID(ctx context.Context, id types.ULID) (*Project, error) {
|
|
var project Project
|
|
result := db.GetEngine(ctx).Where("id = ?", id).First(&project)
|
|
if result.Error != nil {
|
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
|
return nil, nil
|
|
}
|
|
return nil, result.Error
|
|
}
|
|
return &project, nil
|
|
}
|
|
|
|
// GetProjectWithCustomer loads a project with the associated customer information
|
|
func GetProjectWithCustomer(ctx context.Context, id types.ULID) (*Project, error) {
|
|
var project Project
|
|
result := db.GetEngine(ctx).Preload("Customer").Where("id = ?", id).First(&project)
|
|
if result.Error != nil {
|
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
|
return nil, nil
|
|
}
|
|
return nil, result.Error
|
|
}
|
|
return &project, nil
|
|
}
|
|
|
|
// GetAllProjects returns all projects
|
|
func GetAllProjects(ctx context.Context) ([]Project, error) {
|
|
var projects []Project
|
|
result := db.GetEngine(ctx).Find(&projects)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
return projects, nil
|
|
}
|
|
|
|
// GetAllProjectsWithCustomers returns all projects with customer information
|
|
func GetAllProjectsWithCustomers(ctx context.Context) ([]Project, error) {
|
|
var projects []Project
|
|
result := db.GetEngine(ctx).Preload("Customer").Find(&projects)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
return projects, nil
|
|
}
|
|
|
|
// GetProjectsByCustomerID returns all projects of a specific customer
|
|
func GetProjectsByCustomerID(ctx context.Context, customerId types.ULID) ([]Project, error) {
|
|
var projects []Project
|
|
result := db.GetEngine(ctx).Where("customer_id = ?", customerId.ULID).Find(&projects)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
return projects, nil
|
|
}
|
|
|
|
// CreateProject creates a new project with validation
|
|
func CreateProject(ctx context.Context, create ProjectCreate) (*Project, error) {
|
|
// Validation
|
|
if err := create.Validate(); err != nil {
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
}
|
|
|
|
// Check if the customer exists
|
|
if create.CustomerID != nil {
|
|
customer, err := GetCustomerByID(ctx, *create.CustomerID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error checking the customer: %w", err)
|
|
}
|
|
if customer == nil {
|
|
return nil, errors.New("the specified customer does not exist")
|
|
}
|
|
}
|
|
|
|
project := Project{
|
|
Name: create.Name,
|
|
CustomerID: create.CustomerID,
|
|
}
|
|
|
|
result := db.GetEngine(ctx).Create(&project)
|
|
if result.Error != nil {
|
|
return nil, fmt.Errorf("error creating the project: %w", result.Error)
|
|
}
|
|
return &project, nil
|
|
}
|
|
|
|
// UpdateProject updates an existing project with validation
|
|
func UpdateProject(ctx context.Context, update ProjectUpdate) (*Project, error) {
|
|
// Validation
|
|
if err := update.Validate(); err != nil {
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
}
|
|
|
|
project, err := GetProjectByID(ctx, update.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if project == nil {
|
|
return nil, errors.New("project not found")
|
|
}
|
|
|
|
// If CustomerID is updated, check if the customer exists
|
|
if update.CustomerID.Valid {
|
|
if update.CustomerID.Value != nil {
|
|
customer, err := GetCustomerByID(ctx, *update.CustomerID.Value)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error checking the customer: %w", err)
|
|
}
|
|
if customer == nil {
|
|
return nil, errors.New("the specified customer does not exist")
|
|
}
|
|
} else {
|
|
// If CustomerID is nil, set it to nil in the project
|
|
project.CustomerID = nil
|
|
}
|
|
}
|
|
|
|
// Use generic update function
|
|
if err := UpdateModel(ctx, project, update); err != nil {
|
|
return nil, fmt.Errorf("error updating the project: %w", err)
|
|
}
|
|
|
|
// Load updated data from the database
|
|
return GetProjectByID(ctx, update.ID)
|
|
}
|
|
|
|
// DeleteProject deletes a project by its ID
|
|
func DeleteProject(ctx context.Context, id types.ULID) error {
|
|
// Here you could check if dependent entities exist
|
|
result := db.GetEngine(ctx).Delete(&Project{}, id)
|
|
if result.Error != nil {
|
|
return fmt.Errorf("error deleting the project: %w", result.Error)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CreateProjectWithTransaction creates a project within a transaction
|
|
func CreateProjectWithTransaction(ctx context.Context, create ProjectCreate) (*Project, error) {
|
|
// Validation
|
|
if err := create.Validate(); err != nil {
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
}
|
|
|
|
var project *Project
|
|
|
|
// Start transaction
|
|
err := db.GetEngine(ctx).Transaction(func(tx *gorm.DB) error {
|
|
// Customer check within the transaction
|
|
var customer Customer
|
|
if err := tx.Where("id = ?", create.CustomerID).First(&customer).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errors.New("the specified customer does not exist")
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Create project
|
|
newProject := Project{
|
|
Name: create.Name,
|
|
CustomerID: create.CustomerID,
|
|
}
|
|
|
|
if err := tx.Create(&newProject).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Save project for return
|
|
project = &newProject
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("transaction error: %w", err)
|
|
}
|
|
|
|
return project, nil
|
|
}
|