app/models/ncr/work_order.rb
require "csv"
module Ncr
# Make sure all table names use "ncr_XXX"
def self.table_name_prefix
"ncr_"
end
EXPENSE_TYPES = %w(BA60 BA61 BA80)
BUILDING_NUMBERS = YAML.load_file("#{Rails.root}/config/data/ncr/building_numbers.yml")
class WorkOrder < ActiveRecord::Base
# must define before include PurchaseCardMixin
def self.purchase_amount_column_name
:amount
end
include ClientDataMixin
include PurchaseCardMixin
# This is a hack to be able to attribute changes to the correct user. This attribute needs to be set explicitly, then the update comment will use them as the "commenter". Defaults to the requester.
attr_accessor :modifier
belongs_to :ncr_organization, class_name: Ncr::Organization
belongs_to :approving_official, class_name: User
validates :approving_official, presence: true
validate :frozen_approving_official_not_changed
validates :amount, presence: true
validates :cl_number, format: {
with: /\ACL\d{7}\z/,
message: "must start with 'CL', followed by seven numbers"
}, allow_blank: true
validates :expense_type, inclusion: { in: EXPENSE_TYPES }, presence: true
validates :function_code, format: {
with: /\APG[A-Z0-9]{3}\z/,
message: "must start with 'PG', followed by three letters or numbers"
}, allow_blank: true
validates :project_title, presence: true
validates :vendor, presence: true
validates :building_number, presence: true, if: :not_ba60?
validates :rwa_number, presence: true, if: :ba80?
validates :rwa_number, format: {
with: /\A[a-zA-Z][0-9]{7}\z/,
message: "must be one letter followed by 7 numbers"
}, allow_blank: true
validates :soc_code, format: {
with: /\A[A-Z0-9]{3}\z/,
message: "must be three letters or numbers"
}, allow_blank: true
def self.all_system_approvers
[
Ncr::Mailboxes.ba61_tier1_budget_team,
Ncr::Mailboxes.ba61_tier1_budget,
Ncr::Mailboxes.ba61_tier2_budget,
Ncr::Mailboxes.ba80_budget,
Ncr::Mailboxes.ool_ba80_budget,
]
end
def approver_email_frozen?
approval = individual_steps.first
approval && !approval.actionable?
end
def requires_approval?
!emergency
end
def for_whsc_organization?
ncr_organization.try(:whsc?)
end
def for_ool_organization?
ncr_organization.try(:ool?)
end
def ba_6x_tier1_team?
expense_type.match(/^BA6[01]$/) && ncr_organization.try(:ba_6x_tier1_team?)
end
def organization_code_and_name
ncr_organization.try(:code_and_name)
end
def setup_approvals_and_observers
manager = ApprovalManager.new(self)
manager.setup_approvals_and_observers
end
def current_approver
if pending?
currently_awaiting_step_users.first
elsif approving_official
approving_official
elsif emergency and approvers.empty?
nil
else
User.for_email(system_approver_emails.first)
end
end
def final_approver
if !emergency and approvers.any?
approvers.last
end
end
def budget_approvals
individual_steps.offset(1)
end
def budget_approvers
budget_approvals.map(&:completed_by)
end
def editable?
true
end
def ba80?
expense_type == "BA80"
end
def ba61?
expense_type == "BA61"
end
def not_ba60?
expense_type != "BA60"
end
def total_price
amount || 0.0
end
# may be replaced with paper-trail or similar at some point
def version
updated_at.to_i
end
def building_id
regex = /\A(\w{8}) .*\z/
if building_number && regex.match(building_number)
regex.match(building_number)[1]
else
building_number
end
end
def name
project_title
end
def public_identifier
"FY" + fiscal_year.to_s.rjust(2, "0") + "-#{proposal.id}"
end
def fiscal_year
year = created_at.nil? ? Time.zone.now.year : created_at.year
month = created_at.nil? ? Time.zone.now.month : created_at.month
if month >= 10
year += 1
end
year % 100 # convert to two-digit
end
def restart_budget_approvals
proposal.class.transaction do
budget_approvals.each(&:restart!)
proposal.reset_status
proposal.root_step.initialize!
if modifier
proposal.add_restart_comment(modifier)
end
end
end
def self.expense_type_options
EXPENSE_TYPES.map { |expense_type| [expense_type, expense_type] }
end
def initialize_steps
setup_approvals_and_observers
end
def self.permitted_params(params, work_order_instance)
permitted = Ncr::WorkOrderFields.new.relevant(params[:ncr_work_order][:expense_type])
if work_order_instance
permitted.delete(:emergency) # emergency field cannot be edited
end
params.require(:ncr_work_order).permit(*permitted)
end
def setup_and_email_subscribers(comment)
Ncr::WorkOrderUpdater.new(
work_order: self,
update_comment: comment
).run
end
def normalize_input(current_user)
self.modifier = current_user
Ncr::WorkOrderValueNormalizer.new(self).run
end
def self.special_keys
%w(is_tock_billable date_requested not_to_exceed)
end
def self.display_update_not_to_exceed(obj)
obj[:value] == true ? "Not to exceed" : "Exact"
end
def self.display_update_ncr_organization_id(obj)
Ncr::Organization.find(obj[:value])
end
def self.display_update_direct_pay(obj)
obj[:value] == true ? "Direct pay will be used" : "Direct pay will not be used"
end
private
def frozen_approving_official_not_changed
if persisted? && approving_official_id_changed? && approver_email_frozen?
errors.add(:approving_official, "Approving official cannot be changed")
end
end
end
end