432 lines
10 KiB
Go
432 lines
10 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"forge.cadoles.com/Cadoles/guesstimate/internal/orm"
|
|
"github.com/jinzhu/gorm"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type ProjectRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func (r *ProjectRepository) Create(ctx context.Context, title string, ownerID int64) (*Project, error) {
|
|
project := &Project{
|
|
Title: &title,
|
|
Tasks: make([]*Task, 0),
|
|
TaskCategories: NewDefaultTaskCategories(),
|
|
Params: NewDefaultProjectParams(),
|
|
}
|
|
|
|
err := orm.WithTx(ctx, r.db, func(ctx context.Context, tx *gorm.DB) error {
|
|
if err := tx.Save(project).Error; err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
err := tx.Model(project).Association("ACL").Append(&Access{
|
|
Level: LevelOwner,
|
|
UserID: ownerID,
|
|
}).Error
|
|
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
err = tx.Model(project).
|
|
Preload("ACL").
|
|
Preload("ACL.User").
|
|
Preload("Tasks").
|
|
Preload("Tasks.Category").
|
|
Preload("TaskCategories").
|
|
Find(project).
|
|
Error
|
|
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not create user")
|
|
}
|
|
|
|
return project, nil
|
|
}
|
|
|
|
func (r *ProjectRepository) UpdateTitle(ctx context.Context, projectID int64, title string) (*Project, error) {
|
|
project := &Project{}
|
|
project.ID = projectID
|
|
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Model(project).Update("title", title).Error; err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
err := tx.Model(project).
|
|
Preload("ACL").
|
|
Preload("ACL.User").
|
|
Preload("Tasks").
|
|
Preload("Tasks.Category").
|
|
Preload("TaskCategories").
|
|
First(project, "id = ?", projectID).
|
|
Error
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
return project, nil
|
|
}
|
|
|
|
func (r *ProjectRepository) Search(ctx context.Context, filter *ProjectsFilter) ([]*Project, error) {
|
|
projects := make([]*Project, 0)
|
|
|
|
projectTableName := r.db.NewScope(&Project{}).TableName()
|
|
|
|
query := r.db.Table(projectTableName).
|
|
Preload("ACL").
|
|
Preload("ACL.User").
|
|
Preload("Tasks").
|
|
Preload("Tasks.Category").
|
|
Preload("TaskCategories")
|
|
|
|
if filter != nil {
|
|
if len(filter.Ids) > 0 {
|
|
query = query.Where(fmt.Sprintf("%s.id in (?)", projectTableName), filter.Ids)
|
|
}
|
|
|
|
if len(filter.OwnerIds) > 0 {
|
|
accessTableName := r.db.NewScope(&Access{}).TableName()
|
|
|
|
query = query.
|
|
Joins(
|
|
fmt.Sprintf(
|
|
"left join %s on %s.project_id = %s.id",
|
|
accessTableName, accessTableName, projectTableName,
|
|
),
|
|
).
|
|
Where(fmt.Sprintf("%s.level = ?", accessTableName), LevelOwner).
|
|
Where(fmt.Sprintf("%s.user_id IN (?)", accessTableName), filter.OwnerIds)
|
|
}
|
|
|
|
if filter.Limit != nil {
|
|
query = query.Limit(*filter.Limit)
|
|
}
|
|
|
|
if filter.Offset != nil {
|
|
query = query.Offset(*filter.Offset)
|
|
}
|
|
}
|
|
|
|
if err := query.Find(&projects).Error; err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
return projects, nil
|
|
}
|
|
|
|
func (r *ProjectRepository) AddTask(ctx context.Context, projectID int64, changes ProjectTaskChanges) (*Task, error) {
|
|
project := &Project{}
|
|
project.ID = projectID
|
|
task := &Task{}
|
|
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
if err := updateTaskWithChanges(tx, task, changes); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
err := tx.Model(project).Association("Tasks").Append(task).Error
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not add task")
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not add task")
|
|
}
|
|
|
|
return task, nil
|
|
}
|
|
|
|
func (r *ProjectRepository) RemoveTask(ctx context.Context, projectID int64, taskID int64) error {
|
|
project := &Project{}
|
|
project.ID = projectID
|
|
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
task := &Task{}
|
|
task.ID = taskID
|
|
|
|
err := tx.Model(project).Association("Tasks").Delete(task).Error
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not remove task relationship")
|
|
}
|
|
|
|
err = tx.Delete(task, "id = ? AND project_id = ?", taskID, projectID).Error
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not delete task")
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not remove task")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *ProjectRepository) UpdateTask(ctx context.Context, projectID, taskID int64, changes ProjectTaskChanges) (*Task, error) {
|
|
task := &Task{}
|
|
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
err := tx.Model(task).
|
|
Preload("Category").
|
|
First(task, "id = ? AND project_id = ?", taskID, projectID).
|
|
Error
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if err := updateTaskWithChanges(tx, task, changes); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if err := tx.Save(task).Error; err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not update task")
|
|
}
|
|
|
|
return task, nil
|
|
}
|
|
|
|
func (r *ProjectRepository) UpdateParams(ctx context.Context, projectID int64, changes ProjectParamsChanges) (*Project, error) {
|
|
project := &Project{}
|
|
project.ID = projectID
|
|
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
err := tx.Model(project).
|
|
Preload("ACL").
|
|
Preload("ACL.User").
|
|
Preload("Tasks").
|
|
Preload("Tasks.Category").
|
|
Preload("TaskCategories").
|
|
First(project, "id = ?", projectID).
|
|
Error
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if project.Params == nil {
|
|
project.Params = &ProjectParams{}
|
|
}
|
|
|
|
if changes.Currency != nil {
|
|
project.Params.Currency = *changes.Currency
|
|
}
|
|
|
|
if changes.HideFinancialPreviewOnPrint != nil {
|
|
project.Params.HideFinancialPreviewOnPrint = *changes.HideFinancialPreviewOnPrint
|
|
}
|
|
|
|
if changes.RoundUpEstimations != nil {
|
|
project.Params.RoundUpEstimations = *changes.RoundUpEstimations
|
|
}
|
|
|
|
if changes.TimeUnit != nil {
|
|
if project.Params.TimeUnit == nil {
|
|
project.Params.TimeUnit = &TimeUnit{}
|
|
}
|
|
|
|
if changes.TimeUnit.Acronym != nil {
|
|
project.Params.TimeUnit.Acronym = *changes.TimeUnit.Acronym
|
|
}
|
|
|
|
if changes.TimeUnit.Label != nil {
|
|
project.Params.TimeUnit.Label = *changes.TimeUnit.Label
|
|
}
|
|
}
|
|
|
|
if err := tx.Save(project).Error; err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not update project params")
|
|
}
|
|
|
|
return project, nil
|
|
}
|
|
|
|
func (r *ProjectRepository) AddTaskCategory(ctx context.Context, projectID int64, changes ProjectTaskCategoryChanges) (*TaskCategory, error) {
|
|
project := &Project{}
|
|
project.ID = projectID
|
|
taskCategory := &TaskCategory{}
|
|
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
if err := updateTaskCategoryWithChanges(tx, taskCategory, changes); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
err := tx.Model(project).Association("TaskCategories").Append(taskCategory).Error
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not add task category")
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not add task category")
|
|
}
|
|
|
|
return taskCategory, nil
|
|
}
|
|
|
|
func (r *ProjectRepository) RemoveTaskCategory(ctx context.Context, projectID int64, taskCategoryID int64) error {
|
|
project := &Project{}
|
|
project.ID = projectID
|
|
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
taskCategory := &TaskCategory{}
|
|
taskCategory.ID = taskCategoryID
|
|
|
|
var totalAssociatedTasks int
|
|
if err := tx.Model(&Task{}).Where("category_id = ?", taskCategoryID).Count(&totalAssociatedTasks).Error; err != nil {
|
|
return errors.Wrap(err, "could not count associated tasks")
|
|
}
|
|
|
|
if totalAssociatedTasks != 0 {
|
|
return errors.WithStack(ErrAssociatedTaskExist)
|
|
}
|
|
|
|
err := tx.Model(project).Association("TaskCategories").Delete(taskCategory).Error
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not remove task category relationship")
|
|
}
|
|
|
|
err = tx.Delete(taskCategory, "id = ? AND project_id = ?", taskCategoryID, projectID).Error
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not delete task category")
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not remove task category")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *ProjectRepository) UpdateTaskCategory(ctx context.Context, projectID, taskCategoryID int64, changes ProjectTaskCategoryChanges) (*TaskCategory, error) {
|
|
taskCategory := &TaskCategory{}
|
|
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
err := tx.Model(taskCategory).
|
|
First(taskCategory, "id = ? AND project_id = ?", taskCategoryID, projectID).
|
|
Error
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if err := updateTaskCategoryWithChanges(tx, taskCategory, changes); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if err := tx.Save(taskCategory).Error; err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not update task category")
|
|
}
|
|
|
|
return taskCategory, nil
|
|
}
|
|
|
|
func updateTaskWithChanges(db *gorm.DB, task *Task, changes ProjectTaskChanges) error {
|
|
if changes.Label != nil {
|
|
task.Label = changes.Label
|
|
}
|
|
|
|
if changes.CategoryID != nil {
|
|
taskCategory := &TaskCategory{}
|
|
taskCategory.ID = *changes.CategoryID
|
|
task.Category = taskCategory
|
|
}
|
|
|
|
if changes.Estimations == nil {
|
|
return nil
|
|
}
|
|
|
|
if task.Estimations == nil {
|
|
task.Estimations = &Estimations{}
|
|
}
|
|
|
|
if changes.Estimations.Pessimistic != nil {
|
|
task.Estimations.Pessimistic = *changes.Estimations.Pessimistic
|
|
}
|
|
|
|
if changes.Estimations.Likely != nil {
|
|
task.Estimations.Likely = *changes.Estimations.Likely
|
|
}
|
|
|
|
if changes.Estimations.Optimistic != nil {
|
|
task.Estimations.Optimistic = *changes.Estimations.Optimistic
|
|
}
|
|
|
|
if task.Estimations.Likely < task.Estimations.Optimistic {
|
|
task.Estimations.Likely = task.Estimations.Optimistic
|
|
}
|
|
|
|
if task.Estimations.Pessimistic < task.Estimations.Likely {
|
|
task.Estimations.Pessimistic = task.Estimations.Likely
|
|
}
|
|
|
|
if changes.CategoryID != nil {
|
|
taskCategory := &TaskCategory{}
|
|
if err := db.Find(taskCategory, "id = ?", *changes.CategoryID).Error; err != nil {
|
|
return errors.Wrap(err, "could not find task category")
|
|
}
|
|
|
|
task.Category = taskCategory
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func updateTaskCategoryWithChanges(db *gorm.DB, taskCategory *TaskCategory, changes ProjectTaskCategoryChanges) error {
|
|
if changes.Label != nil {
|
|
taskCategory.Label = *changes.Label
|
|
}
|
|
|
|
if changes.CostPerTimeUnit != nil {
|
|
taskCategory.CostPerTimeUnit = *changes.CostPerTimeUnit
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func NewProjectRepository(db *gorm.DB) *ProjectRepository {
|
|
return &ProjectRepository{db}
|
|
}
|