From ae1df5f608906fb97e2ffb93476cdff4791cffc2 Mon Sep 17 00:00:00 2001 From: Artemiy Solopov Date: Wed, 22 Oct 2025 02:01:10 +0300 Subject: [PATCH] Task status model + default task status generation --- app/jobs/project_post_init_job.rb | 18 ------------- app/jobs/projects/post_init_job.rb | 25 +++++++++++++++++++ app/models/project.rb | 5 ++-- app/models/task_status.rb | 10 ++++++++ .../20251021204013_create_task_statuses.rb | 13 ++++++++++ db/schema.rb | 14 ++++++++++- lib/projects/create_default_task_statuses.rb | 18 +++++++++++++ 7 files changed, 82 insertions(+), 21 deletions(-) delete mode 100644 app/jobs/project_post_init_job.rb create mode 100644 app/jobs/projects/post_init_job.rb create mode 100644 app/models/task_status.rb create mode 100644 db/migrate/20251021204013_create_task_statuses.rb create mode 100644 lib/projects/create_default_task_statuses.rb diff --git a/app/jobs/project_post_init_job.rb b/app/jobs/project_post_init_job.rb deleted file mode 100644 index 4b38d58..0000000 --- a/app/jobs/project_post_init_job.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -class ProjectPostInitJob < ApplicationJob - queue_as :default - - def perform(project_id) - project = Project.preparing.find(project_id) - - create_tasks_number_sequence(project) - project.update!(status: :ready) - end - - private - - def create_tasks_number_sequence(project) - Project.connection.execute "CREATE SEQUENCE IF NOT EXISTS #{project.tasks_number_sequence_name} AS INT UNSIGNED" - end -end diff --git a/app/jobs/projects/post_init_job.rb b/app/jobs/projects/post_init_job.rb new file mode 100644 index 0000000..495c302 --- /dev/null +++ b/app/jobs/projects/post_init_job.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Projects + class PostInitJob < ApplicationJob + queue_as :default + + include Projects::CreateDefaultTaskStatuses + + def perform(project_id) + project = Project.preparing.find(project_id) + + project.transaction do + create_tasks_number_sequence(project) + create_default_task_statuses(project) + project.update!(status: :ready) + end + end + + private + + def create_tasks_number_sequence(project) + Project.connection.execute "CREATE SEQUENCE IF NOT EXISTS #{project.tasks_number_sequence_name} AS INT UNSIGNED" + end + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 898d729..19bdf44 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,12 +1,13 @@ # frozen_string_literal: true class Project < ApplicationRecord - enum :status, %i[preparing ready archived].index_by(&:itself), default: :preparing + enum :status, %w[preparing ready archived].index_by(&:itself), default: :preparing validates :name, :code, presence: true validates :code, exclusion: { in: %w[new] }, uniqueness: true, format: { with: /\A[a-z]{2,}\z/ } has_many :tasks, dependent: :restrict_with_exception + has_many :task_statuses, dependent: :destroy has_rich_text :description @@ -35,7 +36,7 @@ class Project < ApplicationRecord private def schedule_post_init_job - ProjectPostInitJob.perform_later id + Projects::PostInitJob.perform_later id end def drop_tasks_number_sequence diff --git a/app/models/task_status.rb b/app/models/task_status.rb new file mode 100644 index 0000000..45573fa --- /dev/null +++ b/app/models/task_status.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class TaskStatus < ApplicationRecord + belongs_to :project + + enum :category, { backlog: 100, analysis: 1000, development: 20_000, fulfillment: 60_000 } + + validates :name, presence: true, uniqueness: { scope: :project } + validates :category, presence: true +end diff --git a/db/migrate/20251021204013_create_task_statuses.rb b/db/migrate/20251021204013_create_task_statuses.rb new file mode 100644 index 0000000..a0e1a82 --- /dev/null +++ b/db/migrate/20251021204013_create_task_statuses.rb @@ -0,0 +1,13 @@ +class CreateTaskStatuses < ActiveRecord::Migration[8.0] + def change + create_table :task_statuses do |t| + t.references :project, null: false, foreign_key: true + t.string :name, null: false + t.integer :category, limit: 2, unsigned: true, null: false + + t.index %i[project_id name], unique: true + t.index %i[project_id category name] + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 9a44e05..bc1539c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_10_21_180400) do +ActiveRecord::Schema[8.0].define(version: 2025_10_21_204013) do create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| t.string "name", null: false t.text "body", size: :long @@ -69,6 +69,17 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_21_180400) do t.index ["user_id"], name: "index_sessions_on_user_id" end + create_table "task_statuses", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| + t.bigint "project_id", null: false + t.string "name", null: false + t.integer "category", limit: 2, null: false, unsigned: true + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["project_id", "category", "name"], name: "index_task_statuses_on_project_id_and_category_and_name" + t.index ["project_id", "name"], name: "index_task_statuses_on_project_id_and_name", unique: true + t.index ["project_id"], name: "index_task_statuses_on_project_id" + end + create_table "tasks", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| t.bigint "project_id", null: false t.integer "number", null: false @@ -89,5 +100,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_21_180400) do add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "sessions", "users" + add_foreign_key "task_statuses", "projects" add_foreign_key "tasks", "projects" end diff --git a/lib/projects/create_default_task_statuses.rb b/lib/projects/create_default_task_statuses.rb new file mode 100644 index 0000000..a92a1fe --- /dev/null +++ b/lib/projects/create_default_task_statuses.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Projects + module CreateDefaultTaskStatuses + module_function + + def create_default_task_statuses(project) + # TODO: make it configurable/templatable? + + project.transaction do + project.task_statuses.create!(category: :backlog, name: 'Backlog') + project.task_statuses.create!(category: :analysis, name: 'To do') + project.task_statuses.create!(category: :development, name: 'In development') + project.task_statuses.create!(category: :fulfillment, name: 'Done') + end + end + end +end