class Loa::OrcamentoDaDespesa < ApplicationRecord
	has_paper_trail
	include GeradorDeEventosContabeis

	belongs_to :elemento_de_despesa_por_subacao, required: true, inverse_of: :orcamentos_da_despesa
	belongs_to :fonte_de_recursos, class_name: "Base::FonteDeRecursos", required: true

	delegate :elemento_de_despesa, to: :elemento_de_despesa_por_subacao
	delegate :codigo_e_descricao, to: :elemento_de_despesa, prefix: :elemento_de_despesa

	delegate :subacao, to: :elemento_de_despesa_por_subacao, :allow_nil => true
	delegate :acao, to: :subacao, :allow_nil => true
	delegate :codigo_e_nome, to: :acao, prefix: :acao, :allow_nil => true
	delegate :funcao, to: :subacao, :allow_nil => true

	delegate :codigo_e_descricao, to: :fonte_de_recursos, prefix: :fonte_de_recursos

	has_many :orcamentos_da_despesa_por_projetos, class_name: 'Licitacao::OrcamentoDaDespesaPorProjeto', dependent: :destroy
	has_many :orcamentos_da_despesa_por_pedido, class_name: 'Licitacao::OrcamentoDaDespesaPorPedido', dependent: :destroy
	has_many :orcamentos_da_despesa_do_contrato, class_name: 'Licitacao::OrcamentoDaDespesaDoContrato', dependent: :destroy
	has_many :orcamentos_da_despesa_do_aditivo, class_name: 'Licitacao::OrcamentoDaDespesaDoAditivo', dependent: :destroy
	has_many :orcamentos_da_despesa_por_diaria, class_name: 'Contabilidade::OrcamentoDaDespesaPorDiaria', dependent: :destroy
	#Retirado momentaneamente a pedido do consultor Lukas, pois segundo ele, o PCA não é pra ter ligação nenhuma com o sistema e é só um informativo que com base nos *relatorios* dele será usado para criação do orçamento
	has_many :orcamentos_da_despesa_por_acao, class_name: 'Pca::OrcamentoDaDespesaPorAcao', dependent: :destroy
	has_many :lancamentos_do_orcamento_da_despesa, class_name: 'Contabilidade::LancamentoDoOrcamentoDaDespesa', dependent: :destroy
	has_many :empenhos, class_name: 'Contabilidade::Empenho', dependent: :restrict_with_exception
	has_many :anulacoes_do_empenho, through: :empenhos, class_name: 'Contabilidade::AnulacaoDoEmpenho'
	has_many :liquidacoes, through: :empenhos, class_name: 'Contabilidade::Liquidacao'

	has_many :bloqueio_de_dotacoes, class_name: 'Contabilidade::BloqueioDeDotacao'
	has_many :dotacoes_destino, class_name: 'Contabilidade::DotacaoDestino', foreign_key: "dotacao_id"
	has_many :dotacoes_origem, class_name: 'Contabilidade::DotacaoOrigem', foreign_key: "dotacao_id"

	validates_presence_of :fonte_de_recursos_id
	validates_presence_of :elemento_de_despesa_por_subacao
	validates_presence_of :valor
	validates_presence_of :status_do_orcamento

	validates_presence_of :iduso, if: Proc.new { |despesa| despesa.elemento_de_despesa_por_subacao.try(:subacao).try(:unidade_orcamentaria).try(:orgao).try(:orcamento).try(:trabalhar_com_iduso) }

	validates_uniqueness_of :fonte_de_recursos_id, scope: :elemento_de_despesa_por_subacao_id, if: Proc.new { self.persisted? }

	validates_numericality_of :valor, greater_than: 0, unless: Proc.new { self.valor_orcado || de_alteracao_orcamentaria? }

	validate :valida_cotas_orcamentarias

	after_save :lancar_movimento_orcamentario, if: proc { self.valor_fixado.to_d > 0 }

	enum status_do_orcamento: STATUS_DO_ORCAMENTO

	scope :social, -> { joins(elemento_de_despesa_por_subacao:[subacao: :tipo_de_orcamento]).where("base_tipos_de_orcamento.codigo =?", 'S') }
	scope :fiscal, -> { joins(elemento_de_despesa_por_subacao:[subacao: :tipo_de_orcamento]).where("base_tipos_de_orcamento.codigo =?", 'F') }

	scope :do_tesouro, -> { joins(fonte_de_recursos: :grupo_da_fonte_de_recursos).where("base_grupos_das_fontes_de_recursos.codigo =?", '1') }
	scope :de_outras_fontes, -> { joins(fonte_de_recursos: :grupo_da_fonte_de_recursos).where("base_grupos_das_fontes_de_recursos.codigo =?", '2') }

	# FAKE para permitir criar campo de formulário com ransack
	ransacker :valor_minimo do
		Arel.sql("999999999999999") # usar gt_eq no metodo search da view
	end
	ransacker :valor_maximo do
		Arel.sql("0.0000000000001") # usar lt_eq no metodo search da view
	end

	enum iduso: {
		recursos_não_destinados_à_contrapartida: 0,
		contrapartida_de_empréstimos_do_BIRD: 1,
		contrapartida_de_empréstimos_do_BID: 2,
		Contrapartida_de_empréstimos_por_desempenho: 3,
		contrapartida_de_outros_empréstimos: 4,
		contrapartida_de_doações: 5,
		recursos_destinados_à_aplicação_mínima_em_ações_e_serviços_públicos_de_saúde: 6
	}

	def lancar_movimento_orcamentario
		lancamento_existente = lancamentos_do_orcamento_da_despesa.find_by(modulo_type: 'Loa::OrcamentoDaDespesa')

		if lancamento_existente.present? && lancamento_existente.valor.to_f != valor_fixado.to_f
			apagar_movimento_orcamentario
		end

		data = elemento_de_despesa_por_subacao.subacao.orcamento.exercicio.to_s + "-01-01" rescue nil

		if valor_fixado.to_f > 0 && data.present?
			movimentacao = lancamentos_do_orcamento_da_despesa.new(
				data_do_lancamento: (Time.first_business_day(Date.parse(data) + 1.day)).to_date,
				valor: self.valor_fixado,
				modulo: self
			)
			movimentacao.save
		end
	end

	def apagar_movimento_orcamentario
		lancamentos_do_orcamento_da_despesa.where(modulo_type: 'Loa::OrcamentoDaDespesa').try(:destroy_all)
	end

	# BOOLEANS
	def de_alteracao_orcamentaria?
		if elemento_de_despesa_por_subacao.present? && acao.present?
			acao.solicitacao_de_alteracao_orcamentaria.present? ||
				acao.programa_de_governo.solicitacao_de_alteracao_orcamentaria.present? ||
					self.de_credito_adicional
		else
			return false
		end
	end

	def valida_cotas_orcamentarias
		# # DESATIVADO A PEDIDO DO LUKAS
		# if contexto_atual.present? && contexto_atual.por_cotas? && self.fonte_de_recursos_id.present?
		# 	valor_da_cota = Loa::FonteDaCotaOrcamentaria.joins(:cota_orcamentaria)
		# 		.where(loa_cotas_orcamentarias: {
		# 			orcamento_id: contexto_atual.id, 
		# 			unidade_orcamentaria_id: self.elemento_de_despesa_por_subacao.subacao.unidade_orcamentaria_id
		# 		}, fonte_de_recursos_id: self.fonte_de_recursos_id).first.try(:valor).to_d

		# 	valor_utilizado = Loa::OrcamentoDaDespesa
		# 		.joins(elemento_de_despesa_por_subacao: :subacao)
		# 		.where(loa_subacoes: {unidade_orcamentaria_id: self.elemento_de_despesa_por_subacao.subacao.unidade_orcamentaria_id})
		# 		.where(fonte_de_recursos_id: self.fonte_de_recursos_id)
		# 		.where.not(id: self.id)
		# 		.sum(:valor).to_d

		# 	saldo = valor_da_cota - valor_utilizado - self.valor.to_d

		# 	errors.add(:valor, "valor maior que o saldo disponível para a cota orcamentária") if saldo.negative?
		# end
	end

	def sub_elementos_da_despesa
		elemento_de_despesa.sub_elementos_de_despesa
	end

	# VALORES
	def valor_fixado
		self.valor
	end

	def valor_bloqueado
		bloqueio_de_dotacoes.ativos.sum(:valor).to_d
	end

	def valor_anulado
		anulacoes_do_empenho.confirmados.do_orcamento.sum(:valor).to_d
	end

	def valor_comprometido
		orcamentos_da_despesa_por_projetos.joins(:projeto).sum(&:valor_comprometido_por_dotacao).to_d + empenhos.solicitado.where(vincula_processo_contrato: false).sum(&:definir_valor_do_empenho)
	end

	def valor_comprometido_com_empenhos
		empenhos.solicitado.sum(&:definir_valor_do_empenho)
	end

	def valor_total_acrescentado_na_alteracao
		lancamentos_do_orcamento_da_despesa.where(modulo_type: "Contabilidade::DotacaoDestino").sum(:valor)
	end

	def valor_total_reduzido_na_alteracao
		lancamentos_do_orcamento_da_despesa.where(modulo_type: "Contabilidade::DotacaoOrigem").sum(:valor)
	end

	def diferenca_entre_total_acrescentado_e_reduzido
		(valor_total_acrescentado_na_alteracao - valor_total_reduzido_na_alteracao).to_d
	end

	def valor_total_acrescentado_na_alteracao_por_tipo_de_credito(credito, data_final)
		tipo_de_credito = Contabilidade::SolicitacaoDeAlteracaoOrcamentaria.tipo_de_creditos.keys[credito] 
		dotacoes_confirmadas = dotacoes_destino.filter { |a| a.solicitacao_de_alteracao_orcamentaria.tipo_de_credito == tipo_de_credito && a.lancamento_do_orcamento_da_despesa.data_do_lancamento <= data_final}.sum(&:valor)
	end
	
	def valor_total_reduzido_na_alteracao_por_tipo_de_credito(credito, data_final)
		tipo_de_credito = Contabilidade::SolicitacaoDeAlteracaoOrcamentaria.tipo_de_creditos.keys[credito] 
		dotacoes_confirmadas = dotacoes_origem.filter { |a| a.solicitacao_de_alteracao_orcamentaria.tipo_de_credito == tipo_de_credito && a.lancamento_do_orcamento_da_despesa.data_do_lancamento <= data_final}.sum(&:valor)
	end
	
	def valor_total_acrescentado_na_alteracao_por_decreto decreto
		dotacoes_destino.joins(:solicitacao_de_alteracao_orcamentaria).where(contabilidade_solicitacao_de_alteracao_orcamentarias: {decreto_id: decreto.id}).sum(:valor)
	end

	def valor_total_reduzido_na_alteracao_por_decreto decreto
		dotacoes_origem.joins(:solicitacao_de_alteracao_orcamentaria).where(contabilidade_solicitacao_de_alteracao_orcamentarias: {decreto_id: decreto.id}).sum(:valor)
	end

	def diferenca_entre_total_acrescentado_e_reduzido_por_decreto decreto
		(valor_total_acrescentado_na_alteracao_por_decreto(decreto).to_d - valor_total_reduzido_na_alteracao_por_decreto(decreto).to_d).to_d
	end

	def saldo
		valor_fixado.to_d - total_empenhado.to_d - valor_bloqueado.to_d - valor_total_reduzido_na_alteracao.to_d + valor_anulado.to_d + valor_total_acrescentado_na_alteracao.to_d
	end


	def valor_loa_e_creditos
		valor_fixado.to_d - valor_bloqueado.to_d - valor_total_reduzido_na_alteracao.to_d + valor_anulado.to_d + valor_total_acrescentado_na_alteracao.to_d
	end

	def valor_dotacao_orcamentaria_atualizada
		valor_fixado.to_d - valor_total_reduzido_na_alteracao.to_d + valor_total_acrescentado_na_alteracao.to_d
	end

	def primeiro_lancamento
		lancamentos_do_orcamento_da_despesa.find_by(modulo_type: "Loa::OrcamentoDaDespesa")
	end

	def total_empenhado
		lancamentos_do_orcamento_da_despesa.where(modulo_type: "Contabilidade::Empenho").sum(&:valor)
	end

	def valor_do_defict
		valor_empenhado.to_d - valor_fixado.to_d
	end

	def saldo_disponivel_para_novos_processos
		saldo_disponivel = saldo.to_d - valor_comprometido.to_d
		return (saldo_disponivel < 0 ? 0 : saldo_disponivel)
	end

	def percentual_do_empenhado_sobre_o_valor_orcado
		if self.valor.to_d > 0
			((self.valor_empenhado.to_d * 100) / self.valor.to_d).round(2)
		else
			self.valor_empenhado.to_d.round(2)
		end
	end

	def unidade_orcamentaria
		subacao.try(:unidade_orcamentaria)
	end

	def orgao
		unidade_orcamentaria.try(:orgao)
	end

	def tipo_de_orcamento
		return elemento_de_despesa_por_subacao.present? ? "#{elemento_de_despesa_por_subacao.subacao.tipo_de_orcamento.codigo}" : ""
	end

	def grupo_de_natureza_da_despesa
		return elemento_de_despesa_por_subacao.present? ? "#{elemento_de_despesa_por_subacao.elemento_de_despesa.modalidade_de_aplicacao.grupo_de_natureza_da_despesa.codigo[1]}" : ""
	end

	def modalidade_de_aplicacao
		return elemento_de_despesa_por_subacao.present? ? "#{elemento_de_despesa_por_subacao.elemento_de_despesa.modalidade_de_aplicacao.codigo[2..3]}" : ""
	end

	def atualiza_saldo_dos_lancamentos
		lancamentos_do_orcamento_da_despesa.where(saldo: nil).each do |lancamento|
			lancamento.update(saldo: saldo.to_d)
		end
	end

	def orcamentos_da_despesa_por_projetos_comprometidos
		# retorna os orcamentos da despesa por projetos dos projetos que ainda não possuem empenhos ou apenas empenhado parcialmente.
		orcamentos_da_despesa_por_projetos.joins(:projeto).select {|oddpp| oddpp.valor_comprometido_por_dotacao.to_f != 0}
	end

	def verifica_eventos_contabeis
		if empenhos.any? && elemento_de_despesa_por_subacao.sub_elementos_de_despesa.joins(orcamentos_da_despesa_por_evento_contabil: :evento_contabil).where('contabilidade_eventos_contabeis.classe = ? OR contabilidade_eventos_contabeis.classe = ?', 51,52).blank?
			raise "Não existe eventos da classe 51 ou 52 para a dotação " << " (id: " << "#{self.id.to_s})"
		end
	end

	def valida_se_possui_dotacao_negativa_no_mes(data_referencia, valida_apenas_do_mes=false)
		saldo = self.valor.to_d
		lancamentos_do_orcamento_da_despesa.where.not(modulo_type: ['Loa::OrcamentoDaDespesa', 'Contabilidade::BloqueioDeDotacao']).order(:data_do_lancamento).group_by{|lanc| lanc.data_do_lancamento}.each do |data, lancamentos|
			val_gasto_no_dia =  Contabilidade::LancamentoDoOrcamentoDaDespesa.where("id IN (?) AND modulo_type <> 'Contabilidade::DotacaoDestino'", lancamentos.map(&:id)).sum(&:valor).to_d
			val_incrementado_no_dia = Contabilidade::LancamentoDoOrcamentoDaDespesa.where(id: lancamentos.map(&:id), modulo_type: 'Contabilidade::DotacaoDestino').sum(:valor).to_d
			saldo += (val_incrementado_no_dia - val_gasto_no_dia).to_d

			if saldo.negative? && ((valida_apenas_do_mes == true && data.between?(data_referencia, data_referencia.end_of_month)) || valida_apenas_do_mes == false )
				raise "Dotação: (#{self.elemento_de_despesa_por_subacao&.subacao&.acao&.codigo_completo} #{self.elemento_de_despesa_por_subacao&.elemento_de_despesa&.codigo_formatado} #{self.fonte_de_recursos&.codigo_completo}), possui saldo negativo em #{data} no valor de #{saldo.abs.real_contabil} deve ser corrigido para envio da remessa do SIM."
			end
		end
	end
	# STRINGS
	def codigo_e_descricao_fonte
		fonte_de_recursos.try(:codigo_completo_e_descricao)
	end

	def classificacao_detalhada
		"P/A: #{subacao.try(:acao).try(:codigo_completo)} | Elem. de Despesa: #{elemento_de_despesa_por_subacao.elemento_de_despesa.codigo_formatado} | FR: #{fonte_de_recursos.try(:codigo_completo)}"
	end

	def classificacao_completa
		"#{orgao.try(:codigo)} #{unidade_orcamentaria.try(:codigo_formatado)} #{subacao.try(:funcao).try(:codigo)} #{subacao.try(:subfuncao).try(:codigo)} #{subacao.try(:acao).try(:programa_de_governo).try(:codigo)} #{subacao.try(:acao).try(:codigo_completo)} #{subacao.try(:codigo) if fonte_de_recursos.try(:modulo).try(:trabalha_com_subacao?)} #{elemento_de_despesa_por_subacao.try(:elemento_de_despesa).try(:codigo_formatado)} #{fonte_de_recursos.try(:codigo_completo)}"
	end

	def classificacao_parcial
		"#{orgao.try(:codigo)}
		#{unidade_orcamentaria.try(:codigo_formatado)}
		#{subacao.try(:funcao).try(:codigo)}
		#{subacao.try(:subfuncao).try(:codigo)}
		#{subacao.try(:acao).try(:programa_de_governo).try(:codigo)}"
	end

	def funcional_programatica
		"#{orgao.try(:codigo)} #{unidade_orcamentaria.try(:codigo_formatado)} #{subacao.try(:funcao).try(:codigo)} #{subacao.try(:subfuncao).try(:codigo)} #{subacao.try(:acao).try(:programa_de_governo).try(:codigo)} #{subacao.try(:acao).try(:codigo_completo)}"
	end

	def funcional_programatica_com_nome
		"#{orgao.try(:codigo)} #{unidade_orcamentaria.try(:codigo_formatado)} #{subacao.try(:funcao).try(:codigo)} #{subacao.try(:subfuncao).try(:codigo)} #{subacao.try(:acao).try(:programa_de_governo).try(:codigo)} #{subacao.try(:acao).try(:codigo_completo)} #{subacao.try(:acao).try(:nome)}"
	end

	def funcao_programatica_do_sim
		"#{orgao.try(:codigo)}
		#{unidade_orcamentaria.try(:codigo_formatado)}
		#{subacao.try(:funcao).try(:codigo)}
		#{subacao.try(:subfuncao).try(:codigo)}
		#{subacao.try(:acao).try(:programa_de_governo).try(:codigo)}
		#{subacao.try(:acao).try(:codigo_completo)}
		#{subacao.try(:codigo)}
		#{elemento_de_despesa_por_subacao.elemento_de_despesa.codigo_formatado}"
	end

	# TCM
	def to_sim_orcamento_da_despesa(valor_agrupado=nil)
		begin
			texto = ""
			texto << "202".to_s.sim_preenche(3) + ","
			texto << Configuracao.first.codigo_do_municipio_no_tcm.to_s.sim_preenche(3) + ","
			texto << elemento_de_despesa_por_subacao.subacao.unidade_orcamentaria.orgao.orcamento.exercicio.to_s + '00' + "," #3
			texto << elemento_de_despesa_por_subacao.subacao.unidade_orcamentaria.orgao.codigo.to_s.sim_preenche(2) + ","
			texto << elemento_de_despesa_por_subacao.subacao.unidade_orcamentaria.codigo.to_s.codigo_uo_to_sim + "," #Código da Unidade Orçamentária
			texto << elemento_de_despesa_por_subacao.elemento_de_despesa.codigo.to_s.sim_limite(8) + ","
			texto << elemento_de_despesa_por_subacao.elemento_de_despesa.descricao.to_s.sim_limite(120) + "," #Descrição do Elemento de Despesa
			
			unless valor_agrupado.nil?
				texto << valor_agrupado.to_s.sim_valor
			else
				if valor_fixado.to_f == 0 
					texto << valor_total_acrescentado_na_alteracao.to_s.sim_valor # Valor Total Fixado no Orçamento
				else
					texto << valor.to_s.sim_valor # Valor Total Fixado no Orçamento
				end
			end
			
			return texto
		rescue => e
			if e.class.to_s == "NoMethodError"
				atributo_falho = e.message.split(" ")[2]
				coluna = CSV.parse(texto, :headers => false).last.count
				raise e.mensagem_traduzida(self, "", atributo_falho, coluna)
			else
				raise e
			end
		end
	end

	def lancamentos_mes(data, tipo)
		self.lancamentos_do_orcamento_da_despesa.where('modulo_type = ?  AND data_do_lancamento >= ? AND data_do_lancamento <?', tipo , data , data + 1.months)
	end

	def valor_bloqueado_periodo(data)
		bloqueio_de_dotacoes.where('data_do_bloqueio >= ? AND data_do_bloqueio < ?', data , data + 1.months).sum(&:valor)
	end

	def valor_suplementado_mes(data)
		dotacoes = dotacoes_destino.joins(:solicitacao_de_alteracao_orcamentaria).where('data_da_solicitacao >= ? AND data_da_solicitacao < ?',data , data + 1.months).where(contabilidade_solicitacao_de_alteracao_orcamentarias: {unidade_orcamentaria_id: unidade_orcamentaria.id})
		valor_suplementado = dotacoes.sum(&:valor) + valor_acrescido_dotacao_mes(data)
		valor_suplementado > 0 ? valor_suplementado.to_d : 0
	end

	def valor_suplementado_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_suplementado_no_mes).to_d
	end

	def valor_das_anulacoes_de_dotacoes_mes(data)
		dotacoes = dotacoes_origem.joins(:solicitacao_de_alteracao_orcamentaria).where('data_da_solicitacao >= ? AND data_da_solicitacao < ?',data , data + 1.months).where(contabilidade_solicitacao_de_alteracao_orcamentarias: {unidade_orcamentaria_id: unidade_orcamentaria.id})
		valor_anulado = dotacoes.sum(&:valor)
		valor_anulado > 0 ? valor_anulado.to_d : 0
	end

	def valor_das_anulacoes_de_dotacoes_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_anulado_de_dotacoes_no_mes).to_d
	end

	def valor_acrescido_dotacao_mes(data)
		lancamentos = lancamentos_mes(data , Contabilidade::DotacaoDestino)
		valor_acrescido = lancamentos.sum(&:valor)
		valor_acrescido > 0 ? valor_acrescido.to_d : 0
	end

	def valor_acrescido_dotacao_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_acrescido_dotacao_mes).to_d
	end

	def valor_subtraido_dotacao_mes(data)
		lancamentos = lancamentos_mes(data , Contabilidade::DotacaoOrigem)
		valor_subtraido = lancamentos.sum(&:valor)
		valor_subtraido > 0 ? valor_subtraido.to_d : 0
	end

	def valor_subtraido_dotacao_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_subtraido_dotacao_mes).to_d
	end

	def valor_do_saldo_da_dotacao(data, tipo_de_poder)
		saldo = valor.to_f - valor_empenhado_ate_o_periodo(data, tipo_de_poder).to_f - valor_subtraido_dotacao_periodo(data, tipo_de_poder).to_f - valor_das_anulacoes_de_dotacoes_periodo(data, tipo_de_poder).to_f + valor_das_anulacoes_de_empenhos_periodo(data, tipo_de_poder).to_f + valor_suplementado_periodo(data, tipo_de_poder)
		saldo > 0 ? saldo.to_d : 0
	end

	def saldo_da_dotacao_por_data(data)
		lancamentos = self.lancamentos_do_orcamento_da_despesa
			.where.not(modulo_type: "Loa::OrcamentoDaDespesa")
			.where("data_do_lancamento <= ?", data)
			.map { |i| i.modulo_type == "Contabilidade::DotacaoDestino" ? -i.valor : i.valor }
		return self.primeiro_lancamento&.valor.to_d - lancamentos.sum.to_d
	end

	def valor_empenhado_mes(data)
		empenhos = lancamentos_mes(data, Contabilidade::Empenho)
		valor_empenhado = empenhos.sum(&:valor)
		valor_empenhado > 0 ? valor_empenhado.to_d : 0
	end

	def valor_empenhado_ate_o_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_empenhado_no_mes).to_d
	end

	def valor_das_anulacoes_de_empenhos_mes(data)
		anulacoes_dos_empenhos = anulacoes_do_empenho.confirmados.where('contabilidade_anulacoes_do_empenho.valor is not null AND data_da_anulacao >= ? AND data_da_anulacao < ?', data , data + 1.months)
		valor_anulado = anulacoes_dos_empenhos.sum(&:valor)
		valor_anulado > 0 ? valor_anulado.to_d : 0
	end

	def valor_das_anulacoes_de_empenhos_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_das_anulacoes_de_empenhos_no_mes).to_d
	end

	def valor_empenhado_a_pagar(data, tipo_de_poder)
		valor = valor_empenhado_ate_o_periodo(data, tipo_de_poder).to_d - valor_pago_ate_o_periodo(data, tipo_de_poder).to_d
		valor > 0 ? valor.to_d : 0
	end

	def valor_liquidado_mes(data)
		#status das a serem comparadados são: confirmado(2), enviado_ao_financeiro(5), recebido_pelo_financeiro(6) e autorizado (8)
		lqs = self.liquidacoes.where('contabilidade_liquidacoes.status in (2,5,6,8) AND data_da_liquidacao >= ? AND data_da_liquidacao < ?', data ,data + 1.months)
		liquidacoes = lqs.where('contabilidade_liquidacoes.restos_a_pagar <> true')
		# liquidacoes_rp = lqs.where('contabilidade_liquidacoes.restos_a_pagar = true').select{|lq| lq.eh_processada? == false}
		# liquidacoes = liquidacoes + liquidacoes_rp
		valor_liquidado = liquidacoes.sum(&:valor)
		valor_liquidado > 0 ? valor_liquidado.to_d : 0
	end

	def valor_liquidado_ate_o_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_liquidado_no_mes).to_d
	end

	def valor_dos_estornos_de_liquidacoes_mes(data)
		estorno_de_liquidacoes = Contabilidade::EstornoDeLiquidacao.confirmado.where(liquidacao_id: self.liquidacoes.ids).where('data_do_estorno >= ? AND data_do_estorno < ?', data, data + 1.months)
		valor_estornado = estorno_de_liquidacoes.sum(&:valor)
		valor_estornado > 0 ? valor_estornado.to_d : 0
	end

	def valor_dos_estornos_de_liquidacoes_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_dos_estornos_de_liquidacoes_no_mes).to_d
	end

	def valor_pago_mes(data, poder_associado)
		orcamento = Orcamento.find_by(exercicio: data.year)
		pagamentos = orcamento.pagamentos.joins(liquidacao: [empenho: [orcamento_da_despesa:[elemento_de_despesa_por_subacao: [subacao: [unidade_orcamentaria: :tipo_de_unidade_administrativa]]]]]).where('loa_orcamentos_da_despesa.id in (?) AND contabilidade_pagamentos.status not in (1) AND contabilidade_pagamentos.data >= ? AND contabilidade_pagamentos.data < ? AND base_tipos_de_unidades_administrativas.poder_associado = ? ', self.id, data, data + 1.months, poder_associado).distinct.all
		valor_pago = pagamentos.sum(&:valor)
		valor_pago > 0 ? valor_pago.to_d : 0
	end

	def valor_pago_ate_o_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_pago_no_mes).to_d
	end

	def valor_estornado_mes(data)
		estornos = Contabilidade::EstornoDePagamento.joins(pagamento: [liquidacao: :empenho]).where(
			'contabilidade_empenhos.id in (?) AND contabilidade_estornos_de_pagamento.data_do_estorno >= ? AND contabilidade_estornos_de_pagamento.data_do_estorno < ?', self.empenhos.ids, data, data + 1.months
		)
		valor_estornado = estornos.sum(&:valor)
		valor_estornado > 0 ? valor_estornado.to_d : 0
	end

	def valor_estornado_periodo(data, tipo_de_poder)
		Loa::BalanceteDeDespesaOrcamentaria.where('mes_referencia <= ? AND ano_referencia = ? AND orcamento_da_despesa_id = ? AND tipo_de_poder = ?', data.month, data.year, self.id, tipo_de_poder).sum(:valor_dos_estornos_de_pago_no_mes).to_d
	end

	def criar_balancete_de_despesas_orcamentarias(data_referencia, exercicio_e_mes_de_geracao, poder_associado)
		ano = exercicio_e_mes_de_geracao.slice(0,4)
		mes = exercicio_e_mes_de_geracao.slice(4,5)
		tipo_de_poder = poder_associado == 1 ? 2 : 3

		balancete = Loa::BalanceteDeDespesaOrcamentaria.find_or_create_by(ano_referencia: ano.to_i, mes_referencia: mes.to_i, orcamento_da_despesa_id: self.id.to_i, tipo_de_poder: tipo_de_poder)

		balancete.update_columns(
			valor_suplementado_no_mes: valor_suplementado_mes(data_referencia),
			valor_anulado_de_dotacoes_no_mes: valor_das_anulacoes_de_dotacoes_mes(data_referencia),
			valor_subtraido_dotacao_mes: valor_subtraido_dotacao_mes(data_referencia),
			valor_acrescido_dotacao_mes: valor_acrescido_dotacao_mes(data_referencia),
			valor_empenhado_no_mes: valor_empenhado_mes(data_referencia),
			valor_das_anulacoes_de_empenhos_no_mes: valor_das_anulacoes_de_empenhos_mes(data_referencia),
			valor_liquidado_no_mes: valor_liquidado_mes(data_referencia),
			valor_dos_estornos_de_liquidacoes_no_mes: valor_dos_estornos_de_liquidacoes_mes(data_referencia),
			valor_pago_no_mes: valor_pago_mes(data_referencia, poder_associado),
			valor_dos_estornos_de_pago_no_mes: valor_dos_estornos_de_liquidacoes_mes(data_referencia),
			tipo_de_poder: tipo_de_poder
		)
	end

	def to_sim_balancetes_de_despesas_orcamentarias(data_referencia, exercicio_e_mes_de_geracao, poder_associado)
		begin
			subacao = elemento_de_despesa_por_subacao.subacao
			acao = subacao.acao
			unidade_orcamentaria = subacao.unidade_orcamentaria
			orgao = unidade_orcamentaria.orgao
			tipo_de_poder = poder_associado == 1 ? 2 : 3

			self.criar_balancete_de_despesas_orcamentarias(data_referencia, exercicio_e_mes_de_geracao, poder_associado)

			ano = exercicio_e_mes_de_geracao.slice(0,4).to_i
			mes = exercicio_e_mes_de_geracao.slice(4,5).to_i
			balancete = Loa::BalanceteDeDespesaOrcamentaria.find_or_create_by(ano_referencia: ano, mes_referencia: mes, orcamento_da_despesa_id: self.id.to_i, tipo_de_poder: tipo_de_poder)

			texto = ""
			texto << "302".to_s.sim_preenche(3) + "," #1
			texto << Configuracao.first.codigo_do_municipio_no_tcm.to_s.sim_preenche(3) + ","#2
			texto << orgao.orcamento.exercicio.to_s.sim_limite_sem_aspas(4, "00") + "," #3 Exercício do Orçamento
			texto << orgao.codigo.to_s.sim_preenche(2) + "," #4 Código do Órgão
			texto << unidade_orcamentaria.codigo.to_s.codigo_uo_to_sim + "," #5 Código da Unidade Orçamentária
			texto << subacao.funcao.codigo.to_s.sim_preenche(2) + "," #6 Código da Função
			texto << subacao.subfuncao.codigo.to_s.sim_preenche(3) + "," #7 Código da Subfunção
			texto << acao.programa_de_governo.codigo.to_s.sim_preenche(4) + "," #8 Código do Programa
			texto << acao.natureza_da_acao.codigo.to_s.sim_limite(1) + "," #9 Código de Projeto ou Atividade
			texto << acao.codigo.to_s.sim_preenche(3) + "," #10 Número do Projeto ou Atividade
			texto << subacao.codigo.to_s.sim_preenche(4) + "," #11 Número do Subprojeto ou Subatividad
			texto << elemento_de_despesa_por_subacao.elemento_de_despesa.codigo.to_s.sim_limite(8) + "," #12 Código do Elemento de Despesa
			texto << self.try(:fonte_de_recursos).try(:codigo_completo).to_s.sim_limite(1) + "," #13 Código do Grupo da Fonte
			texto << self.try(:fonte_de_recursos).try(:codigo_sim) + "," #14 Código da Especificação da Fonte
			texto << exercicio_e_mes_de_geracao.sim_limite_sem_aspas(6)  + ","#15 data referência da documentação
			texto << "G".sim_limite(1) + "," #16 tipo do balancete (segundo o consultor Lukas sempre ira ser "G")
			texto << valor.to_s.sim_valor + "," #17 valor fixado no orcamento
			texto << balancete.valor_suplementado_no_mes.to_s.sim_valor + "," #18 valor suplementado no mês
			texto << valor_suplementado_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #19 valor suplementado até o mês
			texto << balancete.valor_anulado_de_dotacoes_no_mes.to_s.sim_valor + "," #20 valor das anulacoes de dotacão no mês
			texto << valor_das_anulacoes_de_dotacoes_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #21 valor das anulacoes de dotacão até o mês
			texto << balancete.valor_subtraido_dotacao_mes.to_s.sim_valor + "," #22 valor subtraido na dotação por transposição, transferência ou remanejamento no mês
			texto << valor_subtraido_dotacao_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #23 valor subtraido na dotação por transposição, transferência ou remanejamento até o mês
			texto << balancete.valor_acrescido_dotacao_mes.to_s.sim_valor + "," #24 valor acrescido na dotação por transposição, transferência ou remanejamento no mês
			texto << valor_acrescido_dotacao_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #25 valor acrescido na dotação por transposição, transferência ou remanejamento até o mês
			texto << valor_do_saldo_da_dotacao(data_referencia, tipo_de_poder).to_s.sim_valor + "," #26 valor do saldo da dotação
			texto << balancete.valor_empenhado_no_mes.to_s.sim_valor + "," #27 valor empenhado no mês
			texto << valor_empenhado_ate_o_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #28 valor empenhado até o mês
			texto << balancete.valor_das_anulacoes_de_empenhos_no_mes.to_s.sim_valor + "," #29
			texto << valor_das_anulacoes_de_empenhos_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #30
			texto << valor_empenhado_a_pagar(data_referencia, tipo_de_poder).to_s.sim_valor + "," #31
			texto << balancete.valor_liquidado_no_mes.to_s.sim_valor + "," #32
			texto <<  valor_liquidado_ate_o_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #33
			texto << balancete.valor_dos_estornos_de_liquidacoes_no_mes.to_s.sim_valor + "," #34
			texto << valor_dos_estornos_de_liquidacoes_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #35
			texto << balancete.valor_pago_no_mes.to_s.sim_valor + ","  #36
			texto << valor_pago_ate_o_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #37
			texto << balancete.valor_dos_estornos_de_pago_no_mes.to_s.sim_valor + "," #38
			texto << valor_estornado_periodo(data_referencia, tipo_de_poder).to_s.sim_valor + "," #39
			texto << "0.00" + "," #40 consultor Lukas falou que sempre vai ser esse 0.00 apartir deste campo
			texto << "0.00" + "," #41
			texto << "0.00" + ","#42
			texto << "0.00" #43

			return texto
		rescue => e
			if e.class.to_s == "NoMethodError"
				atributo_falho = e.message.split(" ")[2]
				coluna = CSV.parse(texto, :headers => false).last.count
				raise e.mensagem_traduzida(self, self.id, atributo_falho, coluna)
			else
				raise e
			end
		end
	end

	def verifica_se_balancete_valido(data_referencia, poder_associado)
		tipo_de_poder = poder_associado == 1 ? 2 : 3
		soma = 0
  
		valores = [valor, 
			valor_suplementado_mes(data_referencia),
			valor_suplementado_periodo(data_referencia, poder_associado),
			valor_das_anulacoes_de_dotacoes_mes(data_referencia),
			valor_das_anulacoes_de_dotacoes_periodo(data_referencia, tipo_de_poder),
			valor_subtraido_dotacao_mes(data_referencia),
			valor_subtraido_dotacao_periodo(data_referencia, tipo_de_poder),
			valor_acrescido_dotacao_mes(data_referencia),
			valor_acrescido_dotacao_periodo(data_referencia, tipo_de_poder),
			valor_do_saldo_da_dotacao(data_referencia, tipo_de_poder),
			valor_empenhado_mes(data_referencia),
			valor_empenhado_ate_o_periodo(data_referencia, tipo_de_poder),
			valor_das_anulacoes_de_empenhos_mes(data_referencia),
			valor_das_anulacoes_de_empenhos_periodo(data_referencia, tipo_de_poder),
			valor_empenhado_a_pagar(data_referencia, tipo_de_poder),
			valor_liquidado_mes(data_referencia),
			valor_liquidado_ate_o_periodo(data_referencia, tipo_de_poder),
			valor_dos_estornos_de_liquidacoes_mes(data_referencia),
			valor_dos_estornos_de_liquidacoes_periodo(data_referencia, tipo_de_poder),
			valor_pago_mes(data_referencia, poder_associado),
			valor_pago_ate_o_periodo(data_referencia, tipo_de_poder),
			valor_dos_estornos_de_liquidacoes_mes(data_referencia),
			valor_estornado_periodo(data_referencia, tipo_de_poder),
		]
	
		valores.each do |valor|
			soma += valor.to_f
		end
		
		soma > 0
	end

	def to_sim_elementos
		begin
			exercicio_do_orcamento = subacao.unidade_orcamentaria.orgao.orcamento.exercicio.to_s
			
			texto = ""
			texto << "204".to_s.sim_preenche(3) + "," #1
			texto << Configuracao.first.codigo_do_municipio_no_tcm.to_s.sim_preenche(3) + "," #2
			texto << exercicio_do_orcamento + '00' + "," #3
			texto << subacao.unidade_orcamentaria.orgao.codigo.to_s.sim_preenche(2) + "," #Código do Órgão
			texto << subacao.unidade_orcamentaria.codigo.to_s.codigo_uo_to_sim + "," #Código da Unidade Orçamentária
			texto << subacao.funcao.codigo.to_s.sim_preenche(2) + "," #Código da Função
			texto << subacao.subfuncao.codigo.to_s.sim_preenche(3) + "," #Código da Subfunção
			texto << subacao.acao.programa_de_governo.codigo.to_s.sim_preenche(4) + "," #Código do Programa
			texto << subacao.acao.natureza_da_acao.codigo.to_s.sim_limite(1) + "," #Código de Projeto ou Atividade
			texto << subacao.acao.codigo.to_s.sim_preenche(3) + "," #Número do Projeto ou Atividade
			texto << subacao.codigo.to_s.sim_preenche(4) + "," #Número do Subprojeto ou Subatividade
			texto << elemento_de_despesa.codigo.to_s.sim_limite(8) + "," #Código do Elemento de Despesa
			texto << fonte_de_recursos.codigo_completo.to_s.sim_limite(1) + "," #Código do Grupo da Fonte
			texto << fonte_de_recursos.codigo_sim + "," #Código da Especificação da Fonte
			texto << valor.to_s.sim_valor #Valor fixado
			return texto
		rescue => e
			if e.class.to_s == "NoMethodError"
				atributo_falho = e.message.split(" ")[2]
				coluna = CSV.parse(texto, :headers => false).last.count
				raise e.mensagem_traduzida(self, "", atributo_falho, coluna)
			else
				raise e
			end
		end
	end
end
