Skip to content

Instantly share code, notes, and snippets.

@guipdutra
Created June 23, 2014 13:57
Show Gist options
  • Save guipdutra/20013d7ec9dd825b1e9c to your computer and use it in GitHub Desktop.
Save guipdutra/20013d7ec9dd825b1e9c to your computer and use it in GitHub Desktop.
# encoding: utf-8
class Stub < ActiveRecord::Base
extend EnumerateIt
has_paper_trail
PAYMENT_METHODS = {
:manual => 1,
:bank_return => 2,
:national_simple => 3,
:internal => 4,
:refinanced => 5,
:administrative => 6,
:imported_stub => 7 # Stubs which should not appear on raio-x
}
has_enumeration_for :stub_type, :create_helpers => true
belongs_to :city_hall
belongs_to :taxpayer
belongs_to :yearly_payment
belongs_to :service_order
belongs_to :institution
has_many :stubbeds
has_many :stub_tax_credit_sources
has_many :underwritings, :through => :stubbeds, :order => 'underwritings.counterparty_official_name'
has_many :service_order_items, :as => :serviceable
has_one :debit, :as => :debitable, :conditions => {"debits.status" => Debit::ENABLED_STATES}
has_many :operation_logs, as: :loggable
# The association is made by code to save the code of banks that might not be in our banks table yet.
# This way an update in the banks table will take this guys to life .
belongs_to :bank, :foreign_key => :bank_code, :primary_key => :code
validates_presence_of :status, :incidence, :providers_count, :receivers_count,
:iss_value_cents, :fine_value_cents, :interest_value_cents,
:total_value_cents, :expiration_date, :payment_date,
:city_hall_id, :emission_date
validates_presence_of :payment_notes, :if => Proc.new { |s| s.payment_method == PAYMENT_METHODS[:manual] || s.payment_method == PAYMENT_METHODS[:administrative] }
# Auto calculated fields should not be negative
validates_numericality_of :iss_value_cents, :only_integer => true, :greater_than_or_equal_to => 0
validates_numericality_of :fine_value_cents, :only_integer => true, :greater_than_or_equal_to => 0
validates_numericality_of :interest_value_cents, :only_integer => true, :greater_than_or_equal_to => 0
validates_numericality_of :total_value_cents, :only_integer => true, :greater_than => 0, :unless => :need_total_value_cents_greater_than_zero?, :message => :currency_greater_than
validate :number_of_underwritings, :if => :need_underwritings?
validate :same_incidence, :if => :need_underwritings?
validate :close_month_restriction, :unless => :imported?
validate :payment_date_not_before_today, :unless => :imported?
validates_length_of :bank_code, :is => 3, :allow_nil => true
[:iss_value_cents, :fine_value_cents, :interest_value_cents, :discount_cents, :tax_credit_value_cents, :total_value_cents, :monetary_adjustment_cents].each do |field|
value_format field, :declare_accessible => false
end
date_format :payment_date
date_format :expiration_date
value_format :specific_discount_cents, :declare_accessible => false
before_create :update_stubbeds, :unless => :imported?
before_create :set_document_number
before_create :process_internal_stub, :pay_with_credit!, :set_providers_receivers_count, :unless => :imported?
before_create :before_create_imported_stub, :if => :imported?
after_create :digest_number
before_validation :set_transaction_fee, :unless => :imported?, :on => :create
before_validation :set_da_fee, :unless => :imported?, :on => :create
before_validation :set_discount, :unless => :imported?, :on => :create
before_validation :calculate_values, :unless => :imported?, :on => :create
before_validation :apply_discount, :unless => :installment_or_imported?, :on => :create
before_validation :apply_tax_credit, :unless => :imported?, :on => :create
before_validation :apply_transaction_fee, :unless => :imported?, :on => :create
before_validation :set_emission_date, :on => :create
before_validation :set_stub_type, :on => :create
before_validation :set_total_value_cents, :if => :imported?
before_create :fill_other_information, :unless => :imported?
after_create :update_tax_credit, :unless => :imported?
before_save :digest
after_save :update_associated_models, :unless => :imported?
after_commit :enqueue_stub_candidates_sweeper!, :unless => :imported?, :on => :update
validate :imported_fields_valid?, :if => :imported?
validates_presence_of :original_document_number, :if => :imported?
# Needs to be registered this way instead of before_save because otherwise the document number
# set before_create won't be available for the barcode (before_save run before before_create).
# Should be the last thing to run before db operation.
before_create :set_barcode_data
before_update :set_barcode_data
# yearly rate specific
scope :by_installment, order("installment_number, created_at DESC")
scope :installments, where("installment_number IS NOT NULL")
scope :for_future_periods, where(["incidence > ?", Time.now.utc.to_date.beginning_of_month])
scope :overdue, where(["expiration_date < ?", Time.now.utc.to_date])
# state filters
scope :pending, where(:status => "pending")
scope :paid, where("status = 'paid_off' OR status = 'compensated'")
scope :cancelled, where(:status => "cancelled")
# Stubs that are not a service order item yet
scope :serviceable, includes(:service_order_items).where(["service_order_items.id IS ? AND stubs.expiration_date < ?", nil, Time.now.utc.to_date - 1.month])
attr_accessor :underwriting_ids, :transaction_fee_applied,
:discount_eligible, :monetary_adjustment_error, :include_due_value,
:incidence_original, :imported, :reissue_without_fine_and_interest
include AASM
STATES = [:pending, :paid_off, :cancelled, :pa_cancelled, :refunded, :compensated, :debitted, :refinanced, :debitted_paid]
aasm :column => :status do
state :pending, :initial => true
state :paid_off, :enter => :set_paid_at
state :cancelled
state :pa_cancelled
state :refunded
state :compensated
state :debitted
state :refinanced
state :debitted_paid, :enter => :set_paid_at # TODO: check set_paid_at call at this point
event :pay do
transitions :from => [:pending, :refinanced], :to => :paid_off, :guard => Proc.new { |s| s.valid? }
end
event :cancel_it do
transitions :from => [:pending, :cancelled], :to => :cancelled
end
event :pa_cancel do
transitions :from => :paid_off, :to => :pa_cancelled
end
event :internal_cancel do
transitions :from => [:pending, :cancelled, :paid_off], :to => :cancelled
end
event :debit_it do
transitions :from => :pending, :to => :debitted
end
event :undebit_it do
transitions :from => [:debitted, :debitted_paid], :to => :pending
end
event :pay_debitted do
transitions :from => [:pending, :debitted], :to => :debitted_paid
end
end
define_index do
indexes taxpayer.cpf_cnpj, taxpayer.official_name, search_digest, document_number, search_context, expiration_date
has taxpayer(:city_hall_id), :as => :city_hall_id
has institution_id
has taxpayer_id
set_property :delta => ZinfinitSphinx::Delta
end
def need_total_value_cents_greater_than_zero?
(status == "refunded" || (need_underwritings? && !has_discount?) || elegible_to_pay_with_credit?) && specific_discount_cents == 0
end
def has_discount?
self.discount_cents > 0
end
def reissue_log
operation_logs.joins(:operation_log_header)
.where("operation_log_headers.operation" => "stub_reissue")
.where("loggable_id" => id, "loggable_type" => "Stub")
.limit(1).first
end
def need_underwritings?
installment_number.blank? && (type == :regular || type == :standalone) && !imported?
end
def number_of_underwritings
errors[:base] << I18n.t(:underwritings_needed, :scope => [:activerecord, :errors, :messages]) unless (underwriting_ids.present? && underwriting_ids.length > 0) || underwritings.length > 0
end
def same_incidence
# TODO - this finder is here because underwriting_ids is a attr_accessor
# we need to refactor the UW code and remove this hack.
validated_underwritings = if underwritings.present?
underwritings
elsif underwriting_ids.present?
Underwriting.find(underwriting_ids)
else
[]
end
errors[:base] << I18n.t(:different_incidences, :scope => [:activerecord, :errors, :messages]) if validated_underwritings.any? { |uw| uw.incidence != self.incidence }
end
def payment_date_not_before_today
# Validation to make sure that stubs are not created with payment_date in the past.
# Doesn't apply to already created stubs and to Simples Nacional stubs.
if new_record? && payment_date.present? && payment_date < Date.current && ![PAYMENT_METHODS[:national_simple], PAYMENT_METHODS[:imported_stub]].include?(payment_method)
errors.add(:payment_date_str, I18n.t(:payment_date_not_before_today, :scope => [:activerecord, :errors, :messages]))
end
end
def close_month_restriction
if city_hall && payment_date && city_hall.block_stub_out_of_incidence? && type == :regular && incidence < city_hall.year_close_date(incidence) && payment_date > city_hall.year_close_date(incidence)
year_close_date = I18n.l(city_hall.year_close_date(incidence))
error_message = I18n.t(:payment_beyond_close_month, :scope => [:activerecord, :errors, :messages], :year_close_date => year_close_date)
errors.add :payment_date_str, error_message
end
end
def to_s
"G #{formatted_document_number}"
end
class << self
# A few SQL partial helpers to make reuse possible and the conditions easier to understand
# in the class methods below.
def standard_conditions(taxpayer = nil, include_paid_stubs = false)
taxpayer_filter = taxpayer.present? ? "AND underwritings.taxpayer_id = #{taxpayer.id}" : ""
paid_stubs_filter = include_paid_stubs ? " OR underwritings.stub_status NOT IN ('paid_off', 'compensated')" : ""
"underwritings.state = 'issued' #{taxpayer_filter} AND (underwritings.stub_id IS NULL #{paid_stubs_filter}) AND (
(underwritings.underwriting_type = 'declaration' AND underwritings.value_cents <> 0 AND underwritings.iss_value_cents <> 0) OR
(underwritings.value_cents > 0 AND underwritings.iss_value_cents > 0))"
end
def taxed_with_nfe_conditions(taxpayer)
"underwritings.status = 'taxed' AND provider = 1 AND #{"nfes.provider_special_regimen <> 2 AND" unless taxpayer.city_hall.complementary_stub?}
(nfes.provider_national_simple = 0 OR nfes.accountant_in_national_simples = 1) AND nfes.provider_mei = 0 AND nfes.operation_nature <> 5 AND nfes.operation_nature <> 6 AND
(nfes.operation_nature != 2 OR (nfes.operation_nature = 2 AND nfes.city_id = nfes.provider_city_id))"
end
def taxed_without_nfe_conditions(taxpayer)
"underwritings.status = 'taxed' AND provider = 1 AND #{"taxpayers.special_regimen <> 2 AND" unless taxpayer.city_hall.complementary_stub?} underwritings.nfe_id IS NULL"
end
def retained_conditions(city_hall, debit_processor = false)
outside_condition = (debit_processor || !city_hall.receipt_for_retention?) ? "" : " OR (nfes.operation_nature = 2 AND nfes.provider_city_id = nfes.receiver_city_id AND underwritings.voucher_verified = 0)"
"underwritings.status = 'retained'
AND provider = 0
AND (
underwritings.nfe_id IS NULL
OR
(
nfes.operation_nature != 2
#{outside_condition}
)
)"
end
# Decides on the underwriting types that should be available in the selector
def underwriting_types(city_hall, taxpayer = nil)
types = [:taxed, :retained]
types.unshift(:both) if !city_hall.distinct_due_days? && !(city_hall.complementary_stub? && taxpayer.present? && taxpayer.yearly_payment? && !taxpayer.professional_society?)
types
end
def incidence_months(taxpayer, include_taxed = true, include_retained = true)
taxed_conditions_sql = include_taxed ? "(#{taxed_with_nfe_conditions(taxpayer)}) OR (#{taxed_without_nfe_conditions(taxpayer)})" : 0
retained_conditions_sql = include_retained ? "(#{retained_conditions(taxpayer.city_hall)})" : 0
incidences = Underwriting.select("underwritings.id, provider, underwritings.incidence, underwritings.status, underwritings.nfe_id, underwritings.taxpayer_id, underwritings.stub_id, underwritings.stub_status").
where("#{standard_conditions(taxpayer)} AND (#{taxed_conditions_sql} OR #{retained_conditions_sql})").
joins('LEFT OUTER JOIN `taxpayers` ON `taxpayers`.`id` = `underwritings`.`taxpayer_id`
LEFT JOIN `nfes` ON `nfes`.id = `underwritings`.nfe_id').
collect { |uw| uw.incidence }.compact.uniq
# Remove incidences without complementary stub possibility
if taxpayer.present? && taxpayer.yearly_payment? && !taxpayer.professional_society? && include_taxed && !include_retained && taxpayer.city_hall.complementary_stub?
return incidences.select do |incidence|
txds = Underwriting.select("underwritings.id, underwritings.iss_value_cents, taxpayer_id, underwritings.stub_id, underwritings.stub_status").
where(["underwritings.incidence = ? AND #{standard_conditions(taxpayer)} AND (#{taxed_conditions_sql})", incidence]).
joins('LEFT JOIN `nfes` ON `nfes`.id = `underwritings`.nfe_id').
includes([:stubs, :taxpayer])
total_iss = txds.inject(0) { |sum, uw| sum + uw.iss_value_cents }
total_iss > taxpayer.yearly_payment_discount
end
end
return incidences.sort
end
def taxed(start_date, taxpayer)
end_date = start_date.to_date.at_end_of_month rescue ""
Underwriting.where(["#{standard_conditions(taxpayer)} AND underwritings.incidence BETWEEN ? AND ?
AND ((#{taxed_with_nfe_conditions(taxpayer)}) OR (#{taxed_without_nfe_conditions(taxpayer)}))",
start_date, end_date]).
includes(:taxpayer, :counterparty_taxpayer, :nfe).
order('underwritings.number')
end
def retained(start_date, taxpayer)
end_date = start_date.to_date.at_end_of_month rescue ""
Underwriting.where(["#{standard_conditions(taxpayer)} AND (#{retained_conditions(taxpayer.city_hall)}) AND underwritings.incidence BETWEEN ? AND ?",
start_date, end_date]).
joins('LEFT JOIN `nfes` ON `nfes`.id = `underwritings`.nfe_id').
order('underwritings.counterparty_official_name, underwritings.number')
end
def recalculate(all, ids, start_date, taxpayer_id)
taxed = taxed(start_date, taxpayer_id)
retained = retained(start_date, taxpayer_id)
ids = ids || []
unless all
taxed = taxed.select { |i| i if ids.include?(i.id.to_s) }
retained = retained.select { |i| i if ids.include?(i.id.to_s) }
end
[taxed, retained]
end
end
def destroy
self.cancel!
end
def cancel!
if cancelled = cancel_it!
undo_tax_credits
end
cancelled
end
def set_paid_at
self.paid_at = Date.current
end
# this is necessary in order to digest the full stub number (which only generates AFTER creation)
def digest_number
digest(true)
end
def number_to_s
"G #{formatted_document_number}"
end
def digest(force_save=false)
number = number_to_s rescue ""
incid = I18n.l(incidence, :format => :short_month_and_year)
stat = I18n.t("stubs.status.#{status}").singularize
nf_numbers = underwritings.collect { |u| u.number }.join(" ")
stub_type = I18n.t("stubs.types_for_digest.#{type}")
self.search_digest = "#{number} #{incid} #{stat} #{nf_numbers} #{I18n.t('type.' + self.class.to_s.downcase)} #{stub_type}"
save(:validate => false) if force_save
end
def barcode
if febraban_stub?
FebrabanBarcode.create(
:segment => self.city_hall.segment.to_s,
:value => self.total_value_cents,
:identification_code => self.city_hall.identification_code,
:payment_date => self.payment_date,
:document_number => self.document_number
)
elsif bank_stub?
data_documento = (created_at.nil? ? Time.zone.now : created_at).to_date
sacado = [Taxpayer.format_cpf_cnpj(taxpayer.cpf_cnpj), taxpayer.city_registration, taxpayer.official_name].reject(&:blank?).join(" - ")
bank_stub_info = {
:cedente => "Prefeitura Municipal de #{city_hall.city_name}",
:documento_cedente => city_hall.cnpj,
:sacado => sacado,
:sacado_documento => taxpayer.cpf_cnpj,
:sacado_endereco => taxpayer.full_address,
:valor => total_value_cents / 100.00,
:agencia => city_hall.branch,
:conta_corrente => city_hall.account,
:convenio => city_hall.bank_identification_code,
:carteira => city_hall.bank_segment,
:numero_documento => document_number.to_s,
:dias_vencimento => payment_date - data_documento,
:data_documento => data_documento,
:data_processamento => Time.zone.now.to_date
# :quantidade => "" # pendente de confirmação ISSINTEL-720
}
b = BankStub.new(city_hall.bank, bank_stub_info)
end
end
def barcode_typeable
FebrabanBarcode.to_typeable(barcode_data)
end
def real_format(value)
sprintf('%.2f', value/100.to_f).gsub('.', ',')
end
def services_breakdown
stub_underwritings = if self.new_record?
# new records have underwriting_ids and not assoced underwritings.
Underwriting.where(:id => underwriting_ids).includes(:lc116)
else
# this is not supposed to happen like this but I'll keep for possible compability with
# edge cases.
self.underwritings.all.includes(:lc116)
end
if stub_underwritings.size > 0
services = {}
stub_underwritings.each do |uw|
lc116_code = uw.lc116_id.nil? ? 0 : uw.lc116.code
services[lc116_code] = 0 unless services[lc116_code]
services[lc116_code] += uw.iss_value_cents
services[lc116_code] -= uw.due_iss_value_cents
end
values = []
services.each do |code, value|
if code == 0
values << "R$ #{real_format(value)}"
else
values << "R$ #{real_format(value)} (#{code})"
end
end
return "Recolhimento por Código de Serviço:\n" + values.join('; ') + '.'
elsif self.fiscal_action?
"Ordem de Serviço #{self.service_order.formatted_number}"
else
""
end
end
def float_to_cents(value)
(("%.2f" % value).to_f * 100).to_i
end
def formatted_document_number
self.emission_date.strftime("%Y") + sprintf("%011d", document_number)
end
# Calculates the fine considering the dates and the city hall configuration
def self.calculate_fine(taxpayer, value, expiration_date, payment_date, city_hall)
FineCalculator.new(taxpayer, value, expiration_date, payment_date, city_hall).value
end
# Calculates the interest considering the dates and the city hall configuration
def self.calculate_interest(taxpayer, value, expiration_date, payment_date, city_hall)
InterestCalculator.new(taxpayer, value, expiration_date, payment_date, city_hall).value
end
# Calculates the monetary adjustment considering the dates and the city hall configuration
def self.calculate_monetary_adjustment(taxpayer, value, expiration_date, payment_date, city_hall)
MonetaryAdjustmentCalculator.new(taxpayer, value, expiration_date, payment_date, city_hall, true).value
end
# Create a stub for a given nfe
def self.create_for_nfe(nfe)
# define the correct due day according to iss retained flag
stub_due_day = nfe.city_hall.due_days(nfe.retain_iss? ? :retained : :taxed)
reference_date = [nfe.emission_date, nfe.rps_emission_date, nfe.competence].compact.min
expiration_date = calculate_payment_date(stub_due_day, reference_date, nfe.city_hall.due_in_businessdays)
@stub = Stub.new(
:city_hall_id => nfe.city_hall_id,
:taxpayer_id => nfe.provider.id,
:incidence => nfe.competence_or_rps_emission_date.beginning_of_month,
:payment_date => nfe.stub_payment_date,
:expiration_date => expiration_date,
:underwriting_ids => [nfe.underwritings.where(:provider => 1).first.try(:id)],
:standalone => true
)
UniqueConflict.try(5, Stub, 'index_stubs_on_city_hall_id_and_document_number') do |try|
@stub.save!
end
end
def self.calculate_payment_date(due_day, reference_date = Time.zone.now.to_date, in_businesdays = false)
y = (reference_date + 1.month).year
m = (reference_date + 1.month).month
d = in_businesdays ? BusinessdayDate.calculate_first_businessdays(y, m, due_day) : due_day
DateEqualizer.process(y, m, d)
end
def installment?
self.installment_number.present?
end
def imported?
self.imported || self.type == :imported_stub
end
def installment_or_imported?
installment? || imported?
end
def should_calculate_monetary_adjustment?
!self.internal? && (self.city_hall.monetary_adjustment? || self.city_hall.yearly_monetary_adjustment?)
end
# Set a new payment date and apply fine and interests
def delay_payment(new_payment_date)
self.payment_date = new_payment_date
self.apply_discount
self.apply_fine_and_interest
self.apply_tax_credit
self.apply_transaction_fee
end
def apply_fine_and_interest
if self.reissue_without_fine_and_interest
self.monetary_adjustment_cents = 0
self.fine_value_cents = 0
self.interest_value_cents = 0
elsif self.should_calculate_monetary_adjustment?
self.monetary_adjustment_cents = Stub.calculate_monetary_adjustment(self.taxpayer, self.iss_value_cents, self.expiration_date, self.payment_date, self.city_hall).to_i
self.fine_value_cents = self.internal ? 0 : Stub.calculate_fine(self.taxpayer, (self.iss_value_cents + self.monetary_adjustment_cents), self.expiration_date, self.payment_date, self.city_hall).to_i
self.interest_value_cents = self.internal ? 0 : Stub.calculate_interest(self.taxpayer, (self.iss_value_cents + self.monetary_adjustment_cents), self.expiration_date, self.payment_date, self.city_hall).to_i
else
self.fine_value_cents = self.internal ? 0 : Stub.calculate_fine(self.taxpayer, self.iss_value_cents, self.expiration_date, self.payment_date, self.city_hall).to_i
self.interest_value_cents = self.internal ? 0 : Stub.calculate_interest(self.taxpayer, self.iss_value_cents, self.expiration_date, self.payment_date, self.city_hall).to_i
end
self.total_value_cents = self.iss_value_cents + self.fine_value_cents + self.interest_value_cents + self.monetary_adjustment_cents + self.da_fees_cents
self.transaction_fee_applied = false
self.tax_credit_value_cents = 0
end
def apply_transaction_fee
self.total_value_cents += self.transaction_fee_cents unless (self.internal? || self.transaction_fee_applied || [:national_simple, :imported_stub].include?(self.type) )
self.transaction_fee_applied = true # to avoid double fee on multiple validation (yearly payment case)
end
def apply_discount
self.total_value_cents -= self.discount_cents
self.total_value_cents -= self.specific_discount_cents
end
def apply_tax_credit
if taxpayer && taxpayer.credit_value_cents(self.payment_date) > 0 && self.new_record? && self.tax_credit_value_cents == 0 && self.status != "refunded"
if self.total_value_cents >= taxpayer.credit_value_cents(self.payment_date)
self.apply_transaction_fee
self.tax_credit_value_cents = -taxpayer.credit_value_cents(self.payment_date)
self.total_value_cents -= taxpayer.credit_value_cents(self.payment_date)
else
self.tax_credit_value_cents = -self.total_value_cents
self.transaction_fee_applied = true
self.transaction_fee_cents = 0
self.total_value_cents = 0
end
end
end
def overdue?
self.payment_date < Time.zone.now.to_date
end
def type
if self.standalone?
:standalone
elsif self.internal?
:internal
elsif self.payment_method == PAYMENT_METHODS[:national_simple] && taxpayer.present? && taxpayer.mei?
:mei
elsif self.payment_method == PAYMENT_METHODS[:national_simple]
:national_simple
elsif self.original_document_number.present?
:imported_stub
elsif self.yearly_payment && self.yearly_payment.payment_type == YearlyPayment::PAYMENT_TYPES[:yearly_rate]
:yearly_rate
elsif self.yearly_payment && self.yearly_payment.payment_type == YearlyPayment::PAYMENT_TYPES[:estimate]
:estimate
elsif self.yearly_payment && self.yearly_payment.payment_type == YearlyPayment::PAYMENT_TYPES[:earning]
:earning
elsif self.status == "refinanced"
:refinanced
elsif self.status == "refunded"
:refund
elsif self.status == "compensated"
:compensated
elsif self.fiscal_action?
:fiscal_action
else
:regular
end
end
def incidence_str
I18n.l(self.incidence || Date.current, :format => :small_month_and_year)
end
def incidence_str=val
self.incidence = begin
month, year = val.to_s.split('/')
Date.civil(year.to_i, month.to_i, 1)
rescue
self.incidence_original = val
nil
end
end
def refinance!
self.status = "paid_off"
self.paid_at = Time.now.utc.to_date
self.payment_date ||= Time::now.to_date
self.payment_method = PAYMENT_METHODS[:refinanced]
self.save!
end
def service_declaration_underwritings
self.underwritings.collect(&:financial_service_declaration).compact.inject({}) do |result, financial_service_declaration|
declaration = financial_service_declaration.service_declaration_underwriting
result[declaration.id] = declaration if result[declaration.id].nil?
result
end.values
end
def underwritings_count
providers_count + receivers_count
end
private
def before_create_imported_stub
self.pay
self.payment_method = PAYMENT_METHODS[:imported_stub]
self.paid_at = self.payment_date
end
def set_total_value_cents
values = [self.iss_value_cents, self.fine_value_cents, self.interest_value_cents, self.monetary_adjustment_cents, self.discount_cents].map {|e| e || 0 }
self.total_value_cents = values[0] + values[1] + values[2] + values[3] - values[4]
end
def imported_fields_valid?
self.errors.add('incidence_str') if self.incidence.nil?
self.errors.add('payment_date_str') if self.payment_date.nil?
end
def fill_other_information
if !self.installment? && self.status != "refunded"
self.other_information = self.services_breakdown
end
end
def update_stubbeds
unless underwriting_ids.nil?
self.stubbeds.each do |s|
s.destroy unless underwriting_ids.include?(s.underwriting_id.to_s)
underwriting_ids.delete(s.underwriting_id.to_s)
end
underwriting_ids.each do |uw|
self.stubbeds.build(:underwriting_id => uw, :active => (status == 'pending' || status == 'paid_off' || status == 'compensated'))
end
end
end
def update_associated_models
if status == 'pending' || status == 'paid_off' || status == 'compensated' || status == 'debitted' || status == 'debitted_paid'
Underwriting.update_all({:stub_id => id, :stub_status => status, :updated_at => Time.now.utc}, {:id => stubbeds.collect(&:underwriting_id)})
Stubbed.update_all({ :active => 1, :updated_at => Time.now.utc }, { :stub_id => id })
else
Underwriting.update_all({:stub_id => nil, :stub_status => nil, :updated_at => Time.now.utc}, {:id => stubbeds.collect(&:underwriting_id)})
Stubbed.update_all({ :active => nil, :updated_at => Time.now.utc }, { :stub_id => id })
end
end
def undo_tax_credits
for credit in self.stub_tax_credit_sources
uw = credit.underwriting
uw.update_attribute(:balance_value_cents, uw.balance_value_cents+credit.value_cents)
credit.destroy
end
end
def update_tax_credit
unless self.status == "refunded"
credit_value_cents = self.tax_credit_value_cents.abs
taxpayer.underwriting_credits.each do |uw|
break if credit_value_cents == 0
uw_credit_value = CreditValueCalculator.new(uw, payment_date).value
credited_value_cents = 0
if credit_value_cents >= uw_credit_value
credited_value_cents = uw.balance_value_cents
uw.update_attribute(:balance_value_cents, 0)
credit_value_cents -= uw_credit_value
else
credited_value_cents = (uw.balance_value_cents - credit_value_cents)
uw.update_attribute(:balance_value_cents, (uw_credit_value - credit_value_cents))
credit_value_cents = 0
end
# add underwritings to the credit sources, in case this stub is cancelled and we need to put the credit back
self.stub_tax_credit_sources.create(:stub => self, :underwriting => uw, :value_cents => credited_value_cents)
end
end
end
def elegible_to_pay_with_credit?
tax_credit_value_cents.abs > 0 && total_value_cents == 0 && status != "refunded"
end
def pay_with_credit!
if elegible_to_pay_with_credit?
self.status = "compensated"
self.paid_at = Time.now.utc.to_date
self.payment_method = PAYMENT_METHODS[:internal]
end
end
def set_transaction_fee
has_taxed_uws, has_retained_uws = if type == :regular
[has_taxed_uws?, has_retained_uws?]
else
[false, false] # TransactionFeeCalculator does not use this information unless type == :regular, so no need to check it
end
self.transaction_fee_cents = TransactionFeeCalculator.new(self.type, self.city_hall, has_taxed_uws, has_retained_uws).value if self.city_hall
end
def set_da_fee
self.da_fees_cents = DaFeeCalculator.new(self.expiration_date, self.payment_date, self.city_hall, self.internal).value if self.city_hall
end
def set_discount
if !internal && !installment? && discount_eligible == "true" && Stub.discount_not_used?(taxpayer, incidence) && taxpayer.present?
self.discount_cents = taxpayer.yearly_payment_discount
end
end
def self.discount_not_used?(taxpayer, incidence)
Stub.count(:conditions => ["incidence = ? AND discount_cents > 0 AND status != 'cancelled' AND taxpayer_id = ?", incidence, taxpayer.id]) == 0
end
def calculate_values
if (self.new_record? && !underwriting_ids.nil?) && !self.installment?
uws = if new_record?
Underwriting.find(underwriting_ids)
else
self.underwritings
end
iss_total = uws.collect(&:iss_value_cents).compact.sum
unless self.include_due_value
iss_total -= uws.collect(&:due_iss_value_cents).compact.sum
end
self.iss_value_cents = iss_total
self.apply_fine_and_interest
elsif self.fiscal_action?
self.total_value_cents = self.iss_value_cents + self.fine_value_cents + self.interest_value_cents
self.transaction_fee_applied = false
self.tax_credit_value_cents = 0
self.apply_transaction_fee
end
end
# Defines the Stub number based on the city hall stub sequence
def set_document_number
self.document_number = city_hall.next_stub_number!
end
def set_stub_type
self.stub_type = city_hall.stub_type if city_hall.present?
end
def set_barcode_data
self.barcode_data = barcode.to_s unless self.payment_method == PAYMENT_METHODS[:national_simple]
end
def set_emission_date
self.emission_date = Date.current if self.emission_date.blank?
end
def process_internal_stub
if internal && taxpayer.autopay_internal_stub?
self.status = "paid_off"
self.paid_at = Time.now.utc.to_date
self.payment_method = PAYMENT_METHODS[:internal]
end
end
def set_providers_receivers_count
if underwriting_ids.present?
providers_data = Underwriting.count_and_value_data(underwriting_ids)
receivers_data = Underwriting.count_and_value_data(underwriting_ids, :receivers)
self.providers_count = providers_data.count.to_i
self.receivers_count = receivers_data.count.to_i
self.provider_value_cents = providers_data.sum_iss_value_cents.to_i
self.receiver_value_cents = receivers_data.sum_iss_value_cents.to_i
end
end
def enqueue_stub_candidates_sweeper!
Resque.enqueue(StubCandidateSweeperWorker, id)
end
# checks if any of the stub.underwritings is taxed
def has_taxed_uws?
uw_ids = get_underwriting_ids
taxed_uws = Stub.taxed(incidence, taxpayer).collect(&:id) if uw_ids && incidence.present? && taxpayer.present?
return false unless taxed_uws
uw_ids.any? { |id| taxed_uws.include?(id) }
end
# checks if any of the stub.underwritings is retained
def has_retained_uws?
uw_ids = get_underwriting_ids
retained_uws = Stub.retained(incidence, taxpayer).collect(&:id) if uw_ids && incidence.present? && taxpayer.present?
return false unless retained_uws
uw_ids.any? { |id| retained_uws.include?(id) }
end
def get_underwriting_ids
if underwriting_ids.present?
underwriting_ids.map(&:to_i)
elsif underwritings.size > 0
underwritings.collect(&:id)
else
nil
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment