Created
June 23, 2014 13:57
-
-
Save guipdutra/20013d7ec9dd825b1e9c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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