Skip to content

Instantly share code, notes, and snippets.

@BiggerNoise
Created March 3, 2014 21:10
Show Gist options
  • Select an option

  • Save BiggerNoise/9334673 to your computer and use it in GitHub Desktop.

Select an option

Save BiggerNoise/9334673 to your computer and use it in GitHub Desktop.

Revisions

  1. BiggerNoise created this gist Mar 3, 2014.
    60 changes: 60 additions & 0 deletions assign_case_command.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,60 @@
    class AssignCaseCommand < Command

    attribute :case, Case
    attribute :owner, User
    attribute :created_by, User
    attribute :comments, String
    attribute :distribute_at, DateTime
    attribute :distribute_rule_name, String
    attribute :require_initial, Boolean

    validates :case, :owner, presence: true


    def initialize(params)
    self.attributes = {
    case: params[:case] || Case.find(params[:case_id]),
    owner: params[:owner] || (params[:owner_id] && User.find(params[:owner_id])),
    created_by: params[:created_by] || (params[:created_by_id] && User.find(params[:created_by_id])),
    comments: params[:comments],
    distribute_at: params[:distribute_at],
    distribute_rule_name: params[:distribute_rule_name],
    require_initial: params.has_key?(:require_initial) ? params[:require_initial] : false
    }
    end

    protected

    def execute_self
    return false unless valid?
    @previous_owner = self.case.owner
    self.case.owner = owner
    self.case.distribute_at = distribute_at
    self.case.distribute_rule_name = distribute_rule_name
    self.case.save
    end

    def format_activity_comment
    result = "Case assigned to #{owner.full_name}"
    result << "; #{comments}" if comments
    end

    def prepare_sub_commands
    sub_commands = []

    sub_commands << CreateActivityCommand.new(case: self.case, created_by: created_by, comments: format_activity_comment, activity_type: 'CASEASSIGNED')

    # All non-closed tasks for the current owner of the case are assigned to the new owner of the case
    open_tasks_for_current_owner = self.case.tasks.select { |t| t.owner == @previous_owner && t.status != TaskStatus::CLOSED }
    unless @previous_owner.nil?
    open_tasks_for_current_owner.each { |t| sub_commands.push AssignTaskCommand.new(task: t, owner: owner, created_by: created_by, comments: comments) }
    end

    # if there wasn't an initial contact task, create one if require_initial == true
    if require_initial && !open_tasks_for_current_owner.any? { |t| t.task_type == Task::INITIAL_TASK_TYPE }
    sub_commands.push CreateTaskCommand.new(case: self.case, owner: owner, task_type: Task::INITIAL_TASK_TYPE, created_by: created_by, comments: comments)
    end

    sub_commands
    end
    end
    57 changes: 57 additions & 0 deletions assign_task_command.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    class AssignTaskCommand < Command
    attribute :task, Task
    attribute :owner, User
    attribute :due_at, DateTime
    attribute :created_by, User
    attribute :comments, String
    attribute :distribute_at, DateTime
    attribute :distribute_rule_name, String
    attribute :assign_case, Integer

    validates :task, :owner, presence: true

    def initialize(params)
    self.attributes = {
    task: params[:task] || (params.has_key?(:task_id) && Task.find(params[:task_id])),
    owner: params[:owner] || (params.has_key?(:owner_id) && User.find(params[:owner_id])),
    due_at: params[:due_at] || Task.default_due_date,
    created_by: params[:created_by] || (params[:created_by_id] && User.find(params[:created_by_id])),
    distribute_at: params[:distribute_at],
    distribute_rule_name: params[:distribute_rule_name],
    comments: params[:comments],
    assign_case: params[:assign_case]
    }
    end

    protected
    def format_activity_comment
    result = "Task assigned to #{owner.full_name}"
    result << "; #{comments}" if comments
    end

    def format_case_assignment_comment
    result = 'Case assignment followed task assignment'
    result << "; #{comments}" if comments
    end

    def execute_self
    @previous_owner = task.owner
    task.owner = owner
    task.due_at = due_at if task.due_at.nil? || due_at.nil? || due_at > task.due_at
    self.task.distribute_at = distribute_at
    self.task.distribute_rule_name = distribute_rule_name
    task.save
    end

    def prepare_sub_commands
    cmds = [CreateActivityCommand.new(case: task.case, task: task, created_by: created_by, activity_type: 'TASKASSIGNED', comments: format_activity_comment)]
    case
    when assign_case == AssignCase::UNASSIGNED && task.case.owner.nil?
    cmds << AssignCaseCommand.new(case: task.case, owner: owner, comments: format_case_assignment_comment)
    when assign_case == AssignCase::ALWAYS
    cmds << AssignCaseCommand.new(case: task.case, owner: owner, comments: format_case_assignment_comment)
    end

    cmds
    end
    end
    39 changes: 39 additions & 0 deletions command.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    #implement execute_self which returns true/false for success. Do not throw.
    # optionally implement prepare_sub_commands and return an array of subcommands
    class Command
    include ActiveModel::Validations
    include ActiveRecord::Serialization
    include Virtus

    def execute
    return false unless valid? && execute_self
    !!execute_sub_commands(prepare_sub_commands)
    end

    def execute!
    (valid? && execute_self) || raise(ActiveRecord::RecordInvalid.new(self))
    execute_sub_commands!(prepare_sub_commands)
    end

    def attributes_except(*exclusions)
    attr = attributes.dup
    exclusions.flatten.each { |e| attr.delete(e) }
    attr
    end

    protected

    def prepare_sub_commands
    []
    end

    def execute_sub_commands(sub_commands)
    sub_commands.each do |c|
    break nil unless c.execute
    end
    end

    def execute_sub_commands!(sub_commands)
    sub_commands.each { |c| c.execute! }
    end
    end
    108 changes: 108 additions & 0 deletions create_activity_command.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,108 @@
    class CreateActivityCommand < Command
    attr_reader :activity

    attribute :created_by, User
    attribute :parent_activity, Activity
    attribute :member, Case
    attribute :case, Case
    attribute :task, Task
    attribute :activity_type, String
    attribute :comments, String
    attribute :case_closed, Boolean
    attribute :task_closed, Boolean
    attribute :due_at, DateTime
    attribute :allow_closed_case_activity, Boolean
    attribute :allow_closed_task_activity, Boolean

    validates :member, :activity_type, presence: true
    validate :case_cannot_be_closed, :task_cannot_be_closed

    def initialize(params = {})
    self.created_by = params[:created_by] || User.where(id: params[:created_by_id]).first
    self.parent_activity = params[:parent_activity] # Lookups do not make sense here
    self.member = params[:member] || Member.where(id: params[:member_id]).first
    self.case = params[:case] || Case.where(id: params[:case_id]).first
    self.task = params[:task] || Task.where(id: params[:task_id]).first
    self.activity_type = params[:activity_type] || params[:activity_type_code]
    self.comments = params[:comments]
    self.case_closed = !!params[:case_closed]
    self.task_closed = !!params[:task_closed]
    self.due_at = params[:due_at] && params[:due_at].to_datetime
    self.allow_closed_case_activity = params.has_key?(:allow_closed_case_activity) ? params[:allow_closed_case_activity] : false
    self.allow_closed_task_activity = params.has_key?(:allow_closed_task_activity) ? params[:allow_closed_task_activity] : false

    self.member ||= (self.case && self.case.member)
    end

    def execute_self
    return false if invalid?

    create_activity
    self.case.last_activity = @activity if self.case
    self.task.last_activity = self.case.last_activity if self.task

    if case_closed && self.case
    close_case self.case
    elsif task_closed && self.task
    close_task self.task
    elsif task && due_at && due_at != task.due_at
    postpone_task task
    end

    # Task may already be saved by close/postpone
    self.task.save! if task && task.changed?
    self.case.save! if self.case
    true
    end

    private
    STORED_ATTRIBUTES = [:member, :case, :task, :activity_type, :created_by, :parent_activity, :comments, :task_closed, :case_closed]

    def create_activity
    @activity = Activity.create!(attributes.select {|a,_| STORED_ATTRIBUTES.include?(a)})
    end

    def close_case(c)
    close_open_tasks c

    c.closed_at = Time.current
    c.closed_by = created_by
    c.status = CaseStatus::CLOSED
    c.save!
    end

    def close_open_tasks(c)
    c.tasks.find_all { |t| t.status != TaskStatus::CLOSED }.each { |t| close_task t }
    end

    def close_task(t)
    return unless t
    t.closed_at = Time.current
    t.closed_by = created_by
    t.status = TaskStatus::CLOSED
    t.save!
    end

    def postpone_task(t)
    return unless t && due_at
    t.due_at = due_at
    t.save!
    end

    def case_cannot_be_closed
    if self.case and !allow_closed_case_activity and self.case.status == CaseStatus::CLOSED
    errors.add :case_id, 'cannot have new activity when its closed'
    end
    end

    def task_cannot_be_closed
    if self.task and !allow_closed_task_activity and self.task.status == TaskStatus::CLOSED
    errors.add :task_id, 'cannot have new activity when its closed'
    end
    end

    def persisted?
    false
    end

    end