class Contabilidade::Relatorios::LeiDeFinancasController < ApplicationController
	include ControllerConcern
	include ContabilidadeControllerConcern

	before_action :authenticate_usuario!
	before_action :autoriza_usuario!, except: [:imprimir]
	before_action :disponibiliza_dependencias

	def imprimir
		bloqueia_usuario_com_base_em :read, "contabilidade/lei_de_financas"
		relatorio = params[:relatorio]
		titulo = params[:numero_anexo_lei]

		if respond_to? relatorio.to_s, :private
			loa = CombinePDF.new
			loa << CombinePDF.parse(send(relatorio))
			send_data loa.to_pdf, filename: "#{relatorio} - anexo_#{titulo}.pdf", type: "application/pdf", disposition: 'inline'
		else
			redirect_to :back, alert: 'Relatório selecionado não existe'
		end
	end

	def lei_de_financas
		@anexo = params[:numero_anexo_lei].to_s
		@data_final = params[:data_final].to_date
		@inicio_do_mes = @data_final.beginning_of_month
		@data_inicial = Date.new(contexto_atual.exercicio, 1, 1)
		@inicio_do_ano = Date.parse("01/01/#{contexto_atual.exercicio}").to_date

		if params[:unidade_orcamentaria] == "consolidado" && !params[:orgao].present?
			@unidade_orcamentaria = contexto_atual.unidades_orcamentarias
			unidade_orcamentaria = "Consolidado"
			@consolidado = true 
		elsif params[:unidade_orcamentaria] == "todas_unidades" && !params[:orgao].present?
			@unidade_orcamentaria = contexto_atual.unidades_orcamentarias
			unidade_orcamentaria = "Todas Unidades"
			@todas_unidades = true
		elsif params[:unidade_orcamentaria].present? && !params[:orgao].present?
			@unidade_orcamentaria = Loa::UnidadeOrcamentaria.find(params[:unidade_orcamentaria])
			@consolidado = false
			@todas_unidades = false
		else
			flash[:error] = "Por favor, selecione uma Unidade Orçamentária."
		end
		
		if params[:orgao] == "consolidado" && !params[:unidade_orcamentaria].present?
			@orgao = contexto_atual.orgaos
			orgao = "Consolidado"
			@consolidado = true
		elsif params[:orgao] == "todas_unidades" && !params[:unidade_orcamentaria].present?
			@orgao = contexto_atual.orgaos
			orgao = "Todos os Órgãos"
			@todas_unidades = true
		elsif params[:orgao].present? && !params[:unidade_orcamentaria].present?
			@orgao = Loa::Orgao.find(params[:orgao])
			@consolidado = false
			@todas_unidades = false
		else
			flash[:error] = "Por favor, selecione uma Unidade Orçamentária."
		end

		
		if params[:unidade_orcamentaria].to_i > 0
			responsavel = @unidade_orcamentaria.assessor_de_contabilidade_responsavel
			@nome_responsavel = responsavel.pessoa.nome rescue ""
			@documento_responsavel = responsavel.crc rescue ""
		else
			responsavel = ConfiguracaoSim.joins(:pessoa).where("base_pessoas.cnpj is not null").where(tipo_de_responsavel: :responsavel_registro_contabel, tipo_sim: :executivo).order(:data_de_entrada).last
			@nome_responsavel = responsavel.pessoa.nome rescue ""
			@documento_responsavel = responsavel.crc rescue ""
		end
		
		@template = ""
		if @anexo == '1'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_01.pdf.slim"
			@nome_anexo = "Anexo I, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Demonstração da Receita e Despesa Segundo as Categorias Econômicas"
			@despesas = pesquisa_de_despesas_por_classificacao_economica(@data_inicial, @data_final, @unidade_orcamentaria)
			@receitas = pesquisa_de_receitas_por_classificacao_economica(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = 'layouts/_rodape_com_assinatura_lei_financas.html'
			orientation = 'Landscape'
		elsif @anexo == '2_receita'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_02_receita.pdf.slim"
			@nome_anexo = "Anexo II, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Receita - Quadro da Receita por Natureza"
			@receitas = pesquisa_de_receitas_por_classificacao_economica(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Portrait'
		elsif @anexo == '2_despesa'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_02_despesa.pdf.slim"
			@nome_anexo = "Anexo II, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Despesa - Quadro da Despesa por Natureza"
			@despesas = pesquisa_de_despesas_por_sub_elemento_de_despesa(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Portrait'
		elsif @anexo == '6'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_06.pdf.slim"
			@nome_anexo = "Anexo VI, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Demonstrativo da despesa por programa de trabalho"
			@despesas = pesquisa_de_despesas_por_funcao_e_subfuncao(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Landscape'
		elsif @anexo == '7'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_07.pdf.slim"
			@nome_anexo = "Anexo VII, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Demonstrativo da despesa por programa de governo"
			@despesas = pesquisa_de_despesas_por_funcao_e_subfuncao(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Landscape'
		elsif @anexo == '8'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_08.pdf.slim"
			@nome_anexo = "Anexo VIII, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Demonstrativo da despesa por vínculo de recurso"
			@despesas = pesquisa_de_despesas_por_vinculo_de_recurso(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Landscape'
		elsif @anexo == '9'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_09.pdf.slim"
			@nome_anexo = "Anexo IX, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Demonstrativo da despesa por orgãos e funções"
			@despesas = pesquisa_de_despesas_por_unidade_orcamentaria_e_funcao(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Portrait'
		elsif @anexo == '10'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_10.pdf.slim"
			@nome_anexo = "Anexo X, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Comparativo de receita orçada e arrecadada"
			@receitas = pesquisa_de_receitas_por_classificacao_economica(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Landscape'
		elsif @anexo == '11'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_11.pdf.slim"
			@nome_anexo = "Anexo XI, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Comparativo da despesa autorizada com a realizada"
			@despesas = pesquisa_de_despesas_por_orgao_e_unidade_orcamentaria(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Landscape'

		elsif @anexo == '16'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_16.pdf.slim"
			@nome_anexo = "Anexo XVI, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Demonstração da dívida consolidada"
			@despesas = pesquisa_de_demonstracao_da_divida_fundada_consolidada(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Landscape'

		elsif @anexo == '17'
			@template = "contabilidade/relatorios/lei_de_financas/anexo_17.pdf.slim"
			@nome_anexo = "Anexo XVII, da Lei nº 4320/64. (Portaria SOF nº 8, de 04/02/1985)"
			@nome_relatorio = "Demonstração de Dívida Flutuante"
			@consignacoes = pesquisa_de_consignacoes_por_unidade_orcamentaria(@data_inicial, @data_final, @unidade_orcamentaria)
			@empenhos_rp = pesquisa_de_restos_a_pagar_por_unidade_orcamentaria(@data_inicial, @data_final, @unidade_orcamentaria)
			@data_por_extenso = data_do_relatorio_por_extenso(@data_final)
			@footer = nil
			orientation = 'Portrait'

		end

		render_to_string pdf: "lei_de_financas",
			template: @template,
			orientation: orientation,
			disable_smart_shrinking: true,

			dpi: '96',
			header: {
				html: {
					template: 'layouts/_cabecalho_pdf.html.slim',
					locals: {
						titulo1: "#{@nome_anexo}",
						titulo2: "#{@nome_relatorio}",
						titulo3: "#{@consolidado ? "CONSOLIDADO" : @todas_unidades ? "TODAS AS UNIDADES" : @unidade_orcamentaria.codigo_e_nome }",
						titulo4: "De: #{@data_inicial} Até: #{@data_final}"
					}
				},
				spacing: 5
			},
			footer: {
				html: {
					template: 'layouts/_rodape_pdf.html.slim',
				}
			},
			margin: @configuracoes.margens_customizadas(top: 0)
	end

	def pesquisa_de_receitas_por_classificacao_economica(data_inicial, data_final, unidade_orcamentaria)
		if @consolidado
			@unidade_orcamentaria = Loa::UnidadeOrcamentaria.find(unidade_orcamentaria.ids)
		else
			@unidade_orcamentaria = Loa::UnidadeOrcamentaria.find(unidade_orcamentaria.id)
		end
		@naturezas_da_receita = contexto_atual.naturezas_da_receita
	end

	def pesquisa_de_despesas_por_classificacao_economica(data_inicial, data_final, unidade_orcamentaria)
		@categorias_de_natureza_da_despesa = contexto_atual.categorias_economicas

		codigo_despesa_corrente = "30000000"
		codigo_despesa_capital = "40000000"
		@total_da_despesa_corrente = 0.0
		@total_da_despesa_de_capital = 0.0
		@total_de_despesas = 0.0

		despesa_corrente = @categorias_de_natureza_da_despesa.find_by(codigo: codigo_despesa_corrente)
		hash_categoria_despesas_correntes = {nome: despesa_corrente.descricao, valor: despesa_corrente.saldo_total_da_despesa(unidade_orcamentaria, data_final), nivel: 0}
		@total_da_despesa_corrente = hash_categoria_despesas_correntes[:valor]
		@despesas_correntes  = despesa_corrente.grupos_de_natureza_da_despesa.map {|grupo|
			{descicao: grupo.descricao, total: grupo.saldo_total_da_despesa(unidade_orcamentaria, data_final), nivel: 1}
		}
		@despesas_correntes.unshift(hash_categoria_despesas_correntes)

		despesa_de_capital = @categorias_de_natureza_da_despesa.find_by(codigo: codigo_despesa_capital)
		hash_categoria_despesas_de_capital = {nome: despesa_de_capital.descricao, valor: despesa_de_capital.saldo_total_da_despesa(unidade_orcamentaria, data_final), nivel: 0}
		@total_da_despesa_de_capital = hash_categoria_despesas_de_capital[:valor]
		@despesas_de_capital  = despesa_de_capital.grupos_de_natureza_da_despesa.map {|grupo|
			{descicao: grupo.descricao, total: grupo.saldo_total_da_despesa(unidade_orcamentaria, data_final), nivel: 1}
		}
		@despesas_de_capital.unshift(hash_categoria_despesas_de_capital)

		@total_de_despesas = @total_da_despesa_corrente + @total_da_despesa_de_capital
	end

	def pesquisa_de_despesas_por_sub_elemento_de_despesa(data_inicial, data_final, unidade_orcamentaria)
		if @consolidado
			Loa::UnidadeOrcamentaria.find(unidade_orcamentaria.ids)
		else
			@unidade_orcamentaria = Loa::UnidadeOrcamentaria.find(unidade_orcamentaria.id)
		end
		
		@categorias_de_natureza_da_despesa = contexto_atual.categorias_economicas
		@hierarquia_de_despesas = montar_hierarquia_de_despesas(@categorias_de_natureza_da_despesa, unidade_orcamentaria, data_final)
	end

	def pesquisa_de_despesas_por_funcao_e_subfuncao(data_inicial, data_final, unidades_orcamentarias)
		subacoes = contexto_atual.subacoes
		@hierarquia_de_despesas_por_funcao_e_subfuncao = montar_hierarquia_de_despesas_por_funcao_e_subfuncao(subacoes, unidades_orcamentarias, data_final)
	end

	def pesquisa_de_despesas_por_vinculo_de_recurso(data_inicial, data_final, unidade_orcamentaria)
		@subacoes = contexto_atual.subacoes
		@fontes_de_recursos = contexto_atual.fontes_de_recursos
		@hierarquia_de_despesas_por_vinculo_de_recurso = montar_hierarquia_de_despesas_por_vinculo_de_recurso(@subacoes, unidade_orcamentaria, data_final)
	end
	
	def pesquisa_de_despesas_por_unidade_orcamentaria_e_funcao(data_inicial, data_final, unidades_orcamentarias)
		if @consolidado || @todas_unidades
			unidades_orcamentarias = contexto_atual.unidades_orcamentarias
		end
		
		subacoes = contexto_atual.subacoes
		@fontes_de_recursos = contexto_atual.fontes_de_recursos
		@hierarquia_de_despesas_por_orgao_e_funcao = montar_hierarquia_de_despesas_por_orgao_e_funcao(subacoes, unidades_orcamentarias, data_final)
	end
		
	def pesquisa_de_despesas_por_orgao_e_unidade_orcamentaria(data_inicial, data_final, unidades_orcamentarias)
		if @consolidado || @todas_unidades
			orgaos = unidades_orcamentarias.map(&:orgao)
		else
			orgaos = unidades_orcamentarias.try(:orgao)
		end

		categorias_economicas = contexto_atual.categorias_economicas
		@hierarquia_de_despesas_por_orgao_e_unidade_orcamentaria = montar_hierarquia_de_despesas_por_orgao_e_unidade_orcamentaria(categorias_economicas, orgaos, unidades_orcamentarias, data_final)
	end

	def pesquisa_de_demonstracao_da_divida_fundada_consolidada(data_inicial, data_final, unidades_orcamentarias)
		@subcontas = Contabilidade::SubContaPcasp.where(topico_da_conta: [7, 9], divida_consolidada: true, orcamento: contexto_atual)
		if unidades_orcamentarias.is_a?(ActiveRecord::Base)
			unidades = [unidades_orcamentarias.id] + unidades_orcamentarias.unidades_orcamentaria_vinculada.map(&:unidade_orcamentaria_vinculada_id)
		else
			unidades = unidades_orcamentarias.pluck(:id) + unidades_orcamentarias.map { |i| i.unidades_orcamentaria_vinculada.map(&:unidade_orcamentaria_vinculada_id) }.flatten.compact.uniq
		end
		@movimentos_do_plano_de_contas = Contabilidade::MovimentacaoDoPlanoDeContas.where(sub_conta_pcasp: @subcontas, unidade_orcamentaria: [unidades]).where("data_de_lancamento <= ? AND (codigo_da_conta like any (array['21_______', '22_______']))", data_final)
		
		@data_inicial = data_inicial
		@data_final = data_final
		@movimentos_por_subcontas = @movimentos_do_plano_de_contas.sort_by { |i| i.sub_conta_pcasp.descricao }.group_by { |i| i.sub_conta_pcasp.descricao }
	end
	
	def pesquisa_de_consignacoes_por_unidade_orcamentaria(data_inicial, data_final, unidade_orcamentaria)
		montar_hierarquia_de_consignacoes(data_inicial, data_final, unidade_orcamentaria)
	end
	
	def pesquisa_de_restos_a_pagar_por_unidade_orcamentaria(data_inicial, data_final, unidade_orcamentaria)
		@hierarquia_de_empenhos_de_restos_a_pagar = montar_hierarquia_de_empenhos_de_restos_a_pagar(data_inicial, data_final, unidade_orcamentaria)
		@hierarquia_de_empenhos_do_exercicio = montar_hierarquia_de_empenhos_do_exercicio(data_inicial, data_final, unidade_orcamentaria)
	end
	
	def montar_hierarquia_de_despesas_por_subacao(subacoes, unidade_orcamentaria, data_final)
		subacoes.map do |subacao|
			if @consolidado
				orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidade_orcamentaria.ids}).pluck(:id)
			else
				orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidade_orcamentaria.id}).pluck(:id)
			end

			empenhado_ate_o_mes =  Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.do_orcamento.where('orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
			valor_anulado =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: :orcamento_da_despesa).where("orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
			saldo_empenhado_no_mes = (empenhado_ate_o_mes.sum(&:definir_valor_do_empenho) - valor_anulado.sum(:valor)).to_d

			{
				codigo: subacao.funcao.codigo,
				descricao: subacao.funcao.nome,
				subfuncao: {
					codigo: subacao.subfuncao.codigo,
					descricao: subacao.subfuncao.nome,
					programa: {
						codigo: subacao.acao.programa_de_governo.codigo,
						descricao: subacao.acao.programa_de_governo.nome,
						acao: {
							codigo: subacao.acao.codigo_completo,
							descricao: subacao.acao.nome,
							valor: saldo_empenhado_no_mes
						}
					}
				}
			}
		end
	end

	def montar_hierarquia_de_despesas_por_funcao_e_subfuncao(subacoes, unidades_orcamentarias, data_final)
		if @todas_unidades
			subacoes.flat_map do |subacao|
				unidades_orcamentarias.distinct.flat_map do |unidade_orcamentaria|
					orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidade_orcamentaria.id}).pluck(:id)
					empenhado_ate_o_mes =  Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.do_orcamento.where('orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
					valor_anulado =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: :orcamento_da_despesa).where("orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
					saldo_empenhado_no_mes = (empenhado_ate_o_mes.sum(&:definir_valor_do_empenho) - valor_anulado.sum(:valor)).to_d

					if saldo_empenhado_no_mes.present? && saldo_empenhado_no_mes > 0
						{
							codigo: subacao.funcao.codigo,
							descricao: subacao.funcao.nome,
							subfuncao: {
								codigo: subacao.subfuncao.codigo,
								descricao: subacao.subfuncao.nome,
								programa: {
									codigo: subacao.acao.programa_de_governo.codigo,
									descricao: subacao.acao.programa_de_governo.nome,
									acao: {
										codigo: subacao.acao.codigo_completo,
										descricao: subacao.acao.nome,
										valor: saldo_empenhado_no_mes,
										unidade: unidade_orcamentaria,
									}
								}
							}
						}
					end
				end
			end
		else
			subacoes.map do |subacao|
				if @consolidado
					orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidades_orcamentarias.ids}).pluck(:id)
				else
					orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidades_orcamentarias.id}).pluck(:id)
				end

				empenhado_ate_o_mes =  Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.do_orcamento.where('orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
				valor_anulado =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: :orcamento_da_despesa).where("orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
				saldo_empenhado_no_mes = (empenhado_ate_o_mes.sum(&:definir_valor_do_empenho) - valor_anulado.sum(:valor)).to_d

				{
					codigo: subacao.funcao.codigo,
					descricao: subacao.funcao.nome,
					subfuncao: {
						codigo: subacao.subfuncao.codigo,
						descricao: subacao.subfuncao.nome,
						programa: {
							codigo: subacao.acao.programa_de_governo.codigo,
							descricao: subacao.acao.programa_de_governo.nome,
							acao: {
								codigo: subacao.acao.codigo_completo,
								descricao: subacao.acao.nome,
								valor: saldo_empenhado_no_mes,
							}
						}
					}
				}
			end
		end
	end
	
	def montar_hierarquia_de_despesas_por_orgao_e_funcao(subacoes, unidades_orcamentarias, data_final)
		if @todas_unidades || @consolidado
			orgaos = unidades_orcamentarias.map(&:orgao).uniq
			subacoes.flat_map do |subacao|
				orgaos.flat_map do |orgao|
					unidade_orcamentaria_id = orgao.unidades_orcamentarias.ids
					orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidade_orcamentaria_id}).pluck(:id)
					empenhado_ate_o_mes =  Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.do_orcamento.where('orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
					valor_anulado =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: :orcamento_da_despesa).where("orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
					saldo_empenhado_no_mes = (empenhado_ate_o_mes.sum(&:definir_valor_do_empenho) - valor_anulado.sum(:valor)).to_d

					if saldo_empenhado_no_mes.present? && saldo_empenhado_no_mes > 0
						{
							codigo: subacao.funcao.codigo,
							descricao: subacao.funcao.nome,
							orgao: {
								codigo: orgao.id,
								descricao: orgao.codigo_e_nome,
								valor: saldo_empenhado_no_mes,
							}
						}
					end
				end
			end
		else
			subacoes.map do |subacao|
				unidade_orcamentaria = unidades_orcamentarias
				orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidade_orcamentaria.id}).pluck(:id)
				
				fontes_de_recursos_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).flat_map(&:fonte_de_recursos).uniq.pluck(:id)
				valor_empenhado_fonte = Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.do_orcamento.joins(orcamento_da_despesa: :fonte_de_recursos).where('base_fontes_de_recursos.id IN (?) AND orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', fontes_de_recursos_ids, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
				valor_anulado_fonte =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: [orcamento_da_despesa: :fonte_de_recursos]).where("base_fontes_de_recursos.id IN (?) AND orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", fontes_de_recursos_ids, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
				saldo_empenhado_no_mes = (valor_empenhado_fonte.sum(&:definir_valor_do_empenho) - valor_anulado_fonte.sum(:valor)).to_d
				
				if saldo_empenhado_no_mes.present? && saldo_empenhado_no_mes > 0
					{
						codigo: subacao.funcao.codigo,
						descricao: subacao.funcao.nome,
						orgao: {
							codigo: unidade_orcamentaria.orgao.id,
							descricao: unidade_orcamentaria.orgao.codigo_e_nome,
							valor: saldo_empenhado_no_mes,
						}
					}
				end
			end
		end
	end
	
	def montar_hierarquia_de_despesas_por_orgao_e_unidade_orcamentaria(categorias_economicas, orgaos, unidades_orcamentarias, data_final)
		if @todas_unidades || @consolidado
			categorias_economicas.uniq.flat_map do |categoria_economica|
				unidades_orcamentarias.uniq.flat_map do |unidade_orcamentaria|
					orgao = unidade_orcamentaria.orgao
					orcamentos_por_categoria = Loa::OrcamentoDaDespesa.joins(elemento_de_despesa_por_subacao: [elemento_de_despesa: [modalidade_de_aplicacao: [grupo_de_natureza_da_despesa: :categoria_economica]]]).joins(elemento_de_despesa_por_subacao: :subacao).where("base_categorias_economicas.id = ? AND loa_subacoes.unidade_orcamentaria_id = ?", categoria_economica.id, unidade_orcamentaria.id)
					valor_total_loa = orcamentos_por_categoria.sum(&:valor) - orcamentos_por_categoria.joins(:lancamentos_do_orcamento_da_despesa).where('contabilidade_lancamentos_do_orcamento_da_despesa.modulo_type = ? AND contabilidade_lancamentos_do_orcamento_da_despesa.data_do_lancamento BETWEEN ? AND ?', Contabilidade::DotacaoOrigem, @data_inicial, @data_final).sum('coalesce(contabilidade_lancamentos_do_orcamento_da_despesa.valor, 0)') + orcamentos_por_categoria.joins(:lancamentos_do_orcamento_da_despesa).where('contabilidade_lancamentos_do_orcamento_da_despesa.modulo_type = ? AND contabilidade_lancamentos_do_orcamento_da_despesa.data_do_lancamento BETWEEN ? AND ?', Contabilidade::DotacaoDestino, @data_inicial, @data_final).sum('coalesce(contabilidade_lancamentos_do_orcamento_da_despesa.valor, 0)')
					valor_total_credito_especial =   orcamentos_por_categoria.sum { |orcamento| orcamento.valor_total_acrescentado_na_alteracao_por_tipo_de_credito(1, @data_final) } - orcamentos_por_categoria.sum { |orcamento| orcamento.valor_total_reduzido_na_alteracao_por_tipo_de_credito(1, @data_final) }
					valor_total_credito_extraordinario = orcamentos_por_categoria.sum { |orcamento| orcamento.valor_total_acrescentado_na_alteracao_por_tipo_de_credito(2, @data_final) } - orcamentos_por_categoria.sum { |orcamento| orcamento.valor_total_reduzido_na_alteracao_por_tipo_de_credito(2, @data_final) }
						{
							codigo: categoria_economica.codigo,	
							descricao: categoria_economica.descricao,
							orgao: {
								codigo: orgao.id,
								descricao: orgao.codigo_e_nome,
								unidade: {
									codigo: unidade_orcamentaria.id,
									descricao: unidade_orcamentaria.codigo_completo_e_nome,
									loa: valor_total_loa - (valor_total_credito_especial.abs + valor_total_credito_extraordinario.abs),
									especial: valor_total_credito_especial.abs + valor_total_credito_extraordinario.abs,
									valor: categoria_economica.saldo_total_da_despesa(unidade_orcamentaria, data_final),
								}
							}
						}
					end
			end
		else
			categorias_economicas.map do |categoria_economica|
				orcamentos_por_categoria = Loa::OrcamentoDaDespesa.joins(elemento_de_despesa_por_subacao: [elemento_de_despesa: [modalidade_de_aplicacao: [grupo_de_natureza_da_despesa: :categoria_economica]]]).joins(elemento_de_despesa_por_subacao: :subacao).where("base_categorias_economicas.id = ? AND loa_subacoes.unidade_orcamentaria_id = ?", categoria_economica.id, unidades_orcamentarias.id)
				valor_total_loa = orcamentos_por_categoria.sum(&:valor) - orcamentos_por_categoria.joins(:lancamentos_do_orcamento_da_despesa).where('contabilidade_lancamentos_do_orcamento_da_despesa.modulo_type = ? AND contabilidade_lancamentos_do_orcamento_da_despesa.data_do_lancamento BETWEEN ? AND ?', Contabilidade::DotacaoOrigem, @data_inicial, @data_final).sum('coalesce(contabilidade_lancamentos_do_orcamento_da_despesa.valor, 0)') + orcamentos_por_categoria.joins(:lancamentos_do_orcamento_da_despesa).where('contabilidade_lancamentos_do_orcamento_da_despesa.modulo_type = ? AND contabilidade_lancamentos_do_orcamento_da_despesa.data_do_lancamento BETWEEN ? AND ?', Contabilidade::DotacaoDestino, @data_inicial, @data_final).sum('coalesce(contabilidade_lancamentos_do_orcamento_da_despesa.valor, 0)')
				valor_total_credito_especial =   orcamentos_por_categoria.sum { |orcamento| orcamento.valor_total_acrescentado_na_alteracao_por_tipo_de_credito(1, @data_final) } - orcamentos_por_categoria.sum { |orcamento| orcamento.valor_total_reduzido_na_alteracao_por_tipo_de_credito(1, @data_final) }
				valor_total_credito_extraordinario = orcamentos_por_categoria.sum { |orcamento| orcamento.valor_total_acrescentado_na_alteracao_por_tipo_de_credito(2, @data_final) } - orcamentos_por_categoria.sum { |orcamento| orcamento.valor_total_reduzido_na_alteracao_por_tipo_de_credito(2, @data_final) }
				{
					codigo: categoria_economica.codigo,	
					descricao: categoria_economica.descricao,
					orgao: {
						codigo: orgaos.id,
						descricao: orgaos.codigo_e_nome,
						unidade: {
							codigo: unidades_orcamentarias.id,
							descricao: unidades_orcamentarias.codigo_completo_e_nome,
							loa: valor_total_loa - (valor_total_credito_especial.abs + valor_total_credito_extraordinario.abs),
							especial: valor_total_credito_especial.abs + valor_total_credito_extraordinario.abs,
							valor: categoria_economica.saldo_total_da_despesa(unidades_orcamentarias, data_final),
						}
					}
				}
				
			end
		end
	end

	def montar_hierarquia_de_despesas_por_vinculo_de_recurso(subacoes, unidades_orcamentarias, data_final)
		if @todas_unidades
			subacoes.flat_map do |subacao|
				unidades_orcamentarias.compact.flat_map do |unidade_orcamentaria|
					orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: { id: unidade_orcamentaria.id }).pluck(:id)
					fontes_de_recursos = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).flat_map(&:fonte_de_recursos).uniq

					fontes_de_recursos_ordinaria = fontes_de_recursos.compact.select { |fonte| !fonte[:fonte_vinculada] }.pluck(:fonte_vinculada)
					valor_empenhado_fonte_ordinaria = Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.joins(orcamento_da_despesa: :fonte_de_recursos).where('base_fontes_de_recursos.id IN (?) AND base_fontes_de_recursos.fonte_vinculada IN (?) AND orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', fontes_de_recursos.pluck(:id), fontes_de_recursos_ordinaria, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
					valor_anulado_fonte_ordinaria =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: [orcamento_da_despesa: :fonte_de_recursos]).where("base_fontes_de_recursos.id IN (?) AND base_fontes_de_recursos.fonte_vinculada IN (?) AND orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", fontes_de_recursos.pluck(:id), fontes_de_recursos_ordinaria, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
					saldo_fonte_ordinaria = (valor_empenhado_fonte_ordinaria.sum(&:definir_valor_do_empenho) - valor_anulado_fonte_ordinaria.sum(:valor)).to_d

					fontes_de_recursos_vinculada = fontes_de_recursos.compact.select { |fonte| fonte[:fonte_vinculada] }.pluck(:fonte_vinculada)
					valor_empenhado_fonte_vinculada = Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.do_orcamento.joins(orcamento_da_despesa: :fonte_de_recursos).where('base_fontes_de_recursos.id IN (?) AND base_fontes_de_recursos.fonte_vinculada IN (?) AND orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', fontes_de_recursos.pluck(:id), fontes_de_recursos_vinculada, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
					valor_anulado_fonte_vinculada =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: [orcamento_da_despesa: :fonte_de_recursos]).where("base_fontes_de_recursos.id IN (?) AND base_fontes_de_recursos.fonte_vinculada IN (?) AND orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", fontes_de_recursos.pluck(:id), fontes_de_recursos_vinculada, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
					saldo_fonte_vinculada = (valor_empenhado_fonte_vinculada.sum(&:definir_valor_do_empenho) - valor_anulado_fonte_vinculada.sum(:valor)).to_d

					saldo_total = (saldo_fonte_ordinaria + saldo_fonte_vinculada).to_d

					if saldo_total.present? && saldo_total > 0
						{
							codigo: subacao.funcao.codigo,
							descricao: subacao.funcao.nome,
							subfuncao: {
								codigo: subacao.subfuncao.codigo,
								descricao: subacao.subfuncao.nome,
								programa: {
									codigo: subacao.acao.programa_de_governo.codigo,
									descricao: subacao.acao.programa_de_governo.nome,
									acao: {
										codigo: subacao.acao.codigo_completo,
										descricao: subacao.acao.nome,
										valor_fonte_ordinaria: saldo_fonte_ordinaria,
										valor_fonte_vinculada: saldo_fonte_vinculada,
										unidade: unidade_orcamentaria,
									}
								}
							}
						}
					end
				end
			end
		else
			subacoes.map do |subacao|
				if @consolidado
					orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidades_orcamentarias.ids}).pluck(:id)
				else
					orcamentos_da_despesa_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: unidades_orcamentarias.id}).pluck(:id)
				end

				fontes_de_recursos_ids = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).flat_map(&:fonte_de_recursos).uniq.pluck(:id)

				fontes_de_recursos_ordinaria = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).flat_map(&:fonte_de_recursos).uniq.select { |fonte| fonte[:fonte_vinculada] == false }.pluck(:fonte_vinculada)
				valor_empenhado_fonte_ordinaria = Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.do_orcamento.do_orcamento.joins(orcamento_da_despesa: :fonte_de_recursos).where('base_fontes_de_recursos.id IN (?) AND base_fontes_de_recursos.fonte_vinculada IN (?) AND orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', fontes_de_recursos_ids, fontes_de_recursos_ordinaria, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
				valor_anulado_fonte_ordinaria =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: [orcamento_da_despesa: :fonte_de_recursos]).where("base_fontes_de_recursos.id IN (?) AND base_fontes_de_recursos.fonte_vinculada IN (?) AND orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", fontes_de_recursos_ids, fontes_de_recursos_ordinaria, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
				saldo_fonte_ordinaria = (valor_empenhado_fonte_ordinaria.sum(&:definir_valor_do_empenho) - valor_anulado_fonte_ordinaria.sum(:valor)).to_d

				fontes_de_recursos_vinculada = subacao.acao.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]).flat_map(&:fonte_de_recursos).uniq.select { |fonte| fonte[:fonte_vinculada] == true }.pluck(:fonte_vinculada)
				valor_empenhado_fonte_vinculada = Contabilidade::Empenho.confirmados_ou_anulados_e_superiores.do_orcamento.do_orcamento.joins(orcamento_da_despesa: :fonte_de_recursos).where('base_fontes_de_recursos.id IN (?) AND base_fontes_de_recursos.fonte_vinculada IN (?) AND orcamento_da_despesa_id IN (?) AND data_do_empenho <= ? AND orcamento_id = ?', fontes_de_recursos_ids, fontes_de_recursos_vinculada, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
				valor_anulado_fonte_vinculada =  Contabilidade::AnulacaoDoEmpenho.confirmados.do_orcamento.joins(empenho: [orcamento_da_despesa: :fonte_de_recursos]).where("base_fontes_de_recursos.id IN (?) AND base_fontes_de_recursos.fonte_vinculada IN (?) AND orcamento_da_despesa_id IN (?) and contabilidade_anulacoes_do_empenho.data_da_anulacao <= ? AND contabilidade_empenhos.orcamento_id = ?", fontes_de_recursos_ids, fontes_de_recursos_vinculada, orcamentos_da_despesa_ids, @data_final, contexto_atual.id)
				saldo_fonte_vinculada = (valor_empenhado_fonte_vinculada.sum(&:definir_valor_do_empenho) - valor_anulado_fonte_vinculada.sum(:valor)).to_d
				
				{
					codigo: subacao.funcao.codigo,
					descricao: subacao.funcao.nome,
					subfuncao: {
						codigo: subacao.subfuncao.codigo,
						descricao: subacao.subfuncao.nome,
						programa: {
							codigo: subacao.acao.programa_de_governo.codigo,
							descricao: subacao.acao.programa_de_governo.nome,
							acao: {
								codigo: subacao.acao.codigo_completo,
								descricao: subacao.acao.nome,
								valor_fonte_ordinaria: saldo_fonte_ordinaria,
								valor_fonte_vinculada: saldo_fonte_vinculada,
							}
						}
					}
				}
			end
		end
	end

	def montar_hierarquia_de_despesas(categorias, unidades_orcamentarias, data_final)
		if unidades_orcamentarias.is_a?(Loa::UnidadeOrcamentaria)
			categorias.map do |categoria|
				{
					codigo: codigo_formatado(categoria.codigo),
					descricao: categoria.descricao,
					valor: categoria.saldo_total_da_despesa(unidades_orcamentarias.id, data_final),
					nivel_filho: categoria.grupos_de_natureza_da_despesa.map do |grupo|
						{
							codigo: codigo_formatado(grupo.codigo),
							descricao: grupo.descricao,
							valor: grupo.saldo_total_da_despesa(unidades_orcamentarias.id, data_final),
							nivel_filho: grupo.modalidades_de_aplicacao.map do |modalidade|
								{
									codigo: codigo_formatado(modalidade.codigo),
									descricao: modalidade.descricao,
									valor: modalidade.saldo_total_da_despesa(unidades_orcamentarias.id, data_final),
									nivel_filho: modalidade.elementos_de_despesa.map do |elemento|
										{
											codigo: codigo_formatado(elemento.codigo),
											descricao: elemento.descricao,
											valor: elemento.saldo_total_da_despesa(unidades_orcamentarias.id, data_final, true),
										}
									end
								}
							end
						}
					end
				}
			end
		else
			categorias.map do |categoria|
				{
					codigo: codigo_formatado(categoria.codigo),
					descricao: categoria.descricao,
					valor: categoria.saldo_total_da_despesa(unidades_orcamentarias, data_final),
					nivel_filho: categoria.grupos_de_natureza_da_despesa.map do |grupo|
						{
							codigo: codigo_formatado(grupo.codigo),
							descricao: grupo.descricao,
							valor: grupo.saldo_total_da_despesa(unidades_orcamentarias, data_final),
							nivel_filho: grupo.modalidades_de_aplicacao.map do |modalidade|
								{
									codigo: codigo_formatado(modalidade.codigo),
									descricao: modalidade.descricao,
									valor: modalidade.saldo_total_da_despesa(unidades_orcamentarias, data_final),
									nivel_filho: modalidade.elementos_de_despesa.map do |elemento|
										{
											codigo: codigo_formatado(elemento.codigo),
											descricao: elemento.descricao,
											valor: elemento.saldo_total_da_despesa(unidades_orcamentarias, data_final, true),
										}
									end
								}
							end
						}
					end
				}
			end
		end
	end
	
	def montar_hierarquia_de_consignacoes(data_inicial, data_final, unidade_orcamentaria)
		if @consolidado
			unidade_orcamentaria.flat_map do |unidade|
				unidade.contas_extra_orcamentarias.flat_map do |conta_extra_orcamentaria|
					saldo_exercicio_anterior = conta_extra_orcamentaria.saldo_inicial_por_unidade_orcamentaria(unidade.id)
					
					valor_arrecadado_exericio_atual = Contabilidade::TalaoDeReceita.joins(:conta_extra_orcamentaria).where('contabilidade_contas_extra_orcamentarias.id = ?', conta_extra_orcamentaria.id).where('data_do_talao <= ? AND unidade_orcamentaria_id = ?', data_final, unidade.id).sum(:valor).to_d
					valor_anulado_exercicio_atual = Contabilidade::AnulacaoDoTalaoDeReceita.joins(talao_de_receita: :conta_extra_orcamentaria).where('contabilidade_contas_extra_orcamentarias.id = ?', conta_extra_orcamentaria.id).where('data_da_anulacao <= ? AND contabilidade_taloes_de_receita.unidade_orcamentaria_id = ?', data_final, unidade.id).sum(:valor).to_d
					saldo_exercicio_atual = valor_arrecadado_exericio_atual - valor_anulado_exercicio_atual
					
					estorno_de_despesa_extra_ate_o_mes = conta_extra_orcamentaria.despesas_extra_orcamentarias.where('data_de_emissao <= ? AND unidade_orcamentaria_id = ? AND estornada = ?', data_final, unidade.id, true).sum(&:valor_total).to_d
					pagamento_de_despesa_extra_ate_o_mes = conta_extra_orcamentaria.despesas_extra_orcamentarias.where('data_de_emissao <= ? AND unidade_orcamentaria_id = ?', data_final, unidade.id).sum(&:valor_total).to_d
					saldo_de_despesa_extra_ate_o_mes = pagamento_de_despesa_extra_ate_o_mes - estorno_de_despesa_extra_ate_o_mes
					
					{
						conta: {
							codigo: conta_extra_orcamentaria.codigo_completo_e_descricao,
							classe_pcasp: conta_extra_orcamentaria.localizar(:classe_pcasp),
							unidade_orcamentaria: unidade.sigla,
							valores: {
								saldo_exercicio_anterior: saldo_exercicio_anterior,
								valor_incricao: saldo_exercicio_atual,
								valor_baixa: saldo_de_despesa_extra_ate_o_mes,
								saldo_exercicio_seguinte: ((saldo_exercicio_anterior + saldo_exercicio_atual) - saldo_de_despesa_extra_ate_o_mes),
							}
						}
					}
				end
			end
		else
			unidade_orcamentaria.contas_extra_orcamentarias.map do |conta_extra_orcamentaria|
				saldo_exercicio_anterior = conta_extra_orcamentaria.saldo_inicial_por_unidade_orcamentaria(unidade_orcamentaria.id)
				
				valor_arrecadado_exericio_atual = Contabilidade::TalaoDeReceita.joins(:conta_extra_orcamentaria).where('contabilidade_contas_extra_orcamentarias.id = ?', conta_extra_orcamentaria.id).where('data_do_talao <= ? AND unidade_orcamentaria_id = ?', data_final, unidade_orcamentaria.id).sum(:valor).to_d
				valor_anulado_exercicio_atual = Contabilidade::AnulacaoDoTalaoDeReceita.joins(talao_de_receita: :conta_extra_orcamentaria).where('contabilidade_contas_extra_orcamentarias.id = ?', conta_extra_orcamentaria.id).where('data_da_anulacao <= ? AND contabilidade_taloes_de_receita.unidade_orcamentaria_id = ?', data_final, unidade_orcamentaria.id).sum(:valor).to_d
				saldo_exercicio_atual = valor_arrecadado_exericio_atual - valor_anulado_exercicio_atual
				
				estorno_de_despesa_extra_ate_o_mes = conta_extra_orcamentaria.despesas_extra_orcamentarias.where('data_de_emissao <= ? AND unidade_orcamentaria_id = ? AND estornada = ?', data_final, unidade_orcamentaria.id, true).sum(&:valor_total).to_d
				pagamento_de_despesa_extra_ate_o_mes = conta_extra_orcamentaria.despesas_extra_orcamentarias.where('data_de_emissao <= ? AND unidade_orcamentaria_id = ?', data_final, unidade_orcamentaria.id).sum(&:valor_total).to_d
				saldo_de_despesa_extra_ate_o_mes = pagamento_de_despesa_extra_ate_o_mes - estorno_de_despesa_extra_ate_o_mes
				
				{
					conta: {
						codigo: conta_extra_orcamentaria.codigo_completo_e_descricao,
						classe_pcasp: conta_extra_orcamentaria.localizar(:classe_pcasp),
						valores: {
							saldo_exercicio_anterior: saldo_exercicio_anterior,
							valor_incricao: saldo_exercicio_atual,
							valor_baixa: saldo_de_despesa_extra_ate_o_mes,
							saldo_exercicio_seguinte: ((saldo_exercicio_anterior + saldo_exercicio_atual) - saldo_de_despesa_extra_ate_o_mes),
						}
					}
				}
			end
		end
	end
	
	def montar_hierarquia_de_empenhos_de_restos_a_pagar(data_inicial, data_final, unidade_orcamentaria)
		@total_exercicio_anterior_nao_processado = 0
		@total_incricao_nao_processado = 0
		@total_baixa_nao_processado = 0
		@total_exercicio_anterior_processado = 0
		@total_incricao_processado = 0
		@total_baixa_processado = 0
		
		if @consolidado
			unidade_orcamentaria.flat_map do |unidade|
				orcamentos = Orcamento.where("exercicio < ?", unidade.orcamento.exercicio).all.order("exercicio ASC")
				orcamentos.compact.flat_map do |orcamento|
					unidade_orcamentaria_anterior = unidade_orcamentaria_do_exercicio_atual(orcamento, unidade) or next
					unidade_orcamentaria_anterior.empenhos.confirmados_ou_anulados_e_superiores.de_restos_a_pagar.compact.flat_map do |empenho|
						
						valor_exercicio_anterior_processado = empenho.valor_processado(orcamento)
						valor_incricao_processado = empenho.valor_liquidado_empenhado_a_pagar
						valor_baixa_processado = empenho.valor_processado_pago + empenho.valor_cancelado_processado_no_orcamento_atual.to_d
						@total_exercicio_anterior_processado += valor_exercicio_anterior_processado
						@total_incricao_processado += valor_incricao_processado
						@total_baixa_processado += valor_baixa_processado
						@total_exercicio_seguinte_processado = (@total_exercicio_anterior_processado + @total_incricao_processado) - @total_baixa_processado
						
						valor_exercicio_anterior_nao_processado = empenho.valor_nao_processado(orcamento)
						valor_incricao_nao_processado = empenho.valor_liquidado_empenhado_a_pagar
						valor_baixa_nao_processado = empenho.valor_nao_processado_pago + empenho.valor_cancelado_nao_processado_no_orcamento_atual.to_d
						@total_exercicio_anterior_nao_processado += valor_exercicio_anterior_nao_processado
						@total_incricao_nao_processado += valor_incricao_nao_processado
						@total_baixa_nao_processado += valor_baixa_nao_processado
						@total_exercicio_seguinte_nao_processado = (@total_exercicio_anterior_nao_processado + @total_incricao_nao_processado) - @total_baixa_nao_processado
						
						{
							resto_a_pagar: {
								exercicio: orcamento.exercicio,
								unidade_orcamentaria: unidade.sigla,
								valor_processado: {
									valor_exercicio_anterior: valor_exercicio_anterior_processado,
									valor_incricao: valor_incricao_processado,
									valor_baixa: valor_baixa_processado,
									valor_exercicio_seguinte: (valor_exercicio_anterior_processado + valor_incricao_processado) - valor_baixa_processado,
								},
								valor_nao_processado: {
									valor_exercicio_anterior: valor_exercicio_anterior_nao_processado,
									valor_incricao: valor_incricao_nao_processado,
									valor_baixa: valor_baixa_nao_processado,
									valor_exercicio_seguinte: (valor_exercicio_anterior_nao_processado + valor_incricao_nao_processado) - valor_baixa_nao_processado,
								},
							}
						}
					end
				end.compact!
			end
		else
			orcamento_atual = Orcamento.find_by_exercicio(data_final.year)
			orcamentos = Orcamento.where("exercicio < ?", orcamento_atual.exercicio).all.order("exercicio ASC")

			orcamentos.compact.flat_map do |orcamento|
				unidade_orcamentaria_anterior = unidade_orcamentaria_do_exercicio_atual(orcamento, @unidade_orcamentaria) or next
				unidade_orcamentaria_anterior.empenhos.confirmados_ou_anulados_e_superiores.de_restos_a_pagar.compact.flat_map do |empenho|
					
					valor_exercicio_anterior_processado = empenho.valor_processado(orcamento)
					valor_incricao_processado = empenho.valor_liquidado_empenhado_a_pagar
					valor_baixa_processado = empenho.valor_processado_pago + empenho.valor_cancelado_processado_no_orcamento_atual.to_d
					@total_exercicio_anterior_processado += valor_exercicio_anterior_processado
					@total_incricao_processado += valor_incricao_processado
					@total_baixa_processado += valor_baixa_processado
					@total_exercicio_seguinte_processado = (@total_exercicio_anterior_processado + @total_incricao_processado) - @total_baixa_processado
					
					valor_exercicio_anterior_nao_processado = empenho.valor_nao_processado(orcamento)
					valor_incricao_nao_processado = empenho.valor_liquidado_empenhado_a_pagar
					valor_baixa_nao_processado = empenho.valor_nao_processado_pago + empenho.valor_cancelado_nao_processado_no_orcamento_atual.to_d

					@total_exercicio_anterior_nao_processado += valor_exercicio_anterior_nao_processado
					@total_incricao_nao_processado += valor_incricao_nao_processado
					@total_baixa_nao_processado += valor_baixa_nao_processado
					@total_exercicio_seguinte_nao_processado = (@total_exercicio_anterior_nao_processado + @total_incricao_nao_processado) - @total_baixa_nao_processado
					
					{
						resto_a_pagar: {
							exercicio: orcamento.exercicio,
							valor_processado: {
								valor_exercicio_anterior: valor_exercicio_anterior_processado,
								valor_incricao: valor_incricao_processado,
								valor_baixa: valor_baixa_processado,
								valor_exercicio_seguinte: (valor_exercicio_anterior_processado + valor_incricao_processado) - valor_baixa_processado,
							},
							valor_nao_processado: {
								valor_exercicio_anterior: valor_exercicio_anterior_nao_processado,
								valor_incricao: valor_incricao_nao_processado,
								valor_baixa: valor_baixa_nao_processado,
								valor_exercicio_seguinte: (valor_exercicio_anterior_nao_processado + valor_incricao_nao_processado) - valor_baixa_nao_processado,
							},
						}
					}
					
				end
			end.compact!
		end
	end
	
	def montar_hierarquia_de_empenhos_do_exercicio(data_inicial, data_final, unidade_orcamentaria)
		@total_exercicio_anterior_nao_processado_do_orcamento = 0
		@total_incricao_nao_processado_do_orcamento = 0
		@total_baixa_nao_processado_do_orcamento = 0
		@total_exercicio_anterior_processado_do_orcamento = 0
		@total_incricao_processado_do_orcamento = 0
		@total_baixa_processado_do_orcamento = 0
		
		orcamento_atual = Orcamento.find_by_exercicio(data_final.year)

		if @consolidado
			unidade_orcamentaria.flat_map do |unidade|
				unidade.empenhos.confirmados_ou_anulados_e_superiores.do_orcamento.flat_map do |empenho|
					
					valor_exercicio_anterior_processado = 0
					valor_incricao_processado = empenho.saldo_liquidado_a_pagar
					valor_baixa_processado = empenho.valor_processado_pago
					@total_exercicio_anterior_processado_do_orcamento += valor_exercicio_anterior_processado
					@total_incricao_processado_do_orcamento += valor_incricao_processado
					@total_baixa_processado_do_orcamento += valor_baixa_processado
					@total_exercicio_seguinte_processado_do_orcamento = (@total_exercicio_anterior_processado_do_orcamento + @total_incricao_processado_do_orcamento) - @total_baixa_processado_do_orcamento
					
					valor_exercicio_anterior_nao_processado = 0
					valor_incricao_nao_processado = empenho.saldo_ate_a_data(data_final)
					valor_baixa_nao_processado = empenho.valor_processado_pago
					@total_exercicio_anterior_nao_processado_do_orcamento += valor_exercicio_anterior_nao_processado
					@total_incricao_nao_processado_do_orcamento += valor_incricao_nao_processado
					@total_baixa_nao_processado_do_orcamento += valor_baixa_nao_processado
					@total_exercicio_seguinte_nao_processado_do_orcamento = (@total_exercicio_anterior_nao_processado_do_orcamento + @total_incricao_nao_processado_do_orcamento) - @total_baixa_nao_processado_do_orcamento
					
					{
						exercicio_atual: {
							exercicio: orcamento_atual.exercicio,
							unidade_orcamentaria: unidade.sigla,
							valor_processado: {
								valor_exercicio_anterior: valor_exercicio_anterior_processado,
								valor_incricao: valor_incricao_processado,
								valor_baixa: valor_baixa_processado,
								valor_exercicio_seguinte: (valor_exercicio_anterior_processado + valor_incricao_processado) - valor_baixa_processado,
							},
							valor_nao_processado: {
								valor_exercicio_anterior: valor_exercicio_anterior_nao_processado,
								valor_incricao: valor_incricao_nao_processado,
								valor_baixa: valor_baixa_nao_processado,
								valor_exercicio_seguinte: (valor_exercicio_anterior_nao_processado + valor_incricao_nao_processado) - valor_baixa_nao_processado,
							}
						}
					}
				end
			end
		else
			unidade_orcamentaria.empenhos.confirmados_ou_anulados_e_superiores.do_orcamento.flat_map do |empenho|
				
				valor_exercicio_anterior_processado = 0
				valor_incricao_processado = empenho.saldo_liquidado_a_pagar
				valor_baixa_processado = empenho.valor_processado_pago
				
				@total_exercicio_anterior_processado_do_orcamento += valor_exercicio_anterior_processado
				@total_incricao_processado_do_orcamento += valor_incricao_processado
				@total_baixa_processado_do_orcamento += valor_baixa_processado
				@total_exercicio_seguinte_processado_do_orcamento = (@total_exercicio_anterior_processado_do_orcamento + @total_incricao_processado_do_orcamento) - @total_baixa_processado_do_orcamento
				
				valor_exercicio_anterior_nao_processado = 0
				valor_incricao_nao_processado = empenho.saldo_ate_a_data(data_final)
				valor_baixa_nao_processado = empenho.valor_processado_pago
				
				@total_exercicio_anterior_nao_processado_do_orcamento += valor_exercicio_anterior_nao_processado
				@total_incricao_nao_processado_do_orcamento += valor_incricao_nao_processado
				@total_baixa_nao_processado_do_orcamento += valor_baixa_nao_processado
				@total_exercicio_seguinte_nao_processado_do_orcamento = (@total_exercicio_anterior_nao_processado_do_orcamento + @total_incricao_nao_processado_do_orcamento) - @total_baixa_nao_processado_do_orcamento
				
				{
					exercicio_atual: {
						exercicio: orcamento_atual.exercicio,
						valor_processado: {
							valor_exercicio_anterior: valor_exercicio_anterior_processado,
							valor_incricao: valor_incricao_processado,
							valor_baixa: valor_baixa_processado,
							valor_exercicio_seguinte: (valor_exercicio_anterior_processado + valor_incricao_processado) - valor_baixa_processado,
						},
						valor_nao_processado: {
							valor_exercicio_anterior: valor_exercicio_anterior_nao_processado,
							valor_incricao: valor_incricao_nao_processado,
							valor_baixa: valor_baixa_nao_processado,
							valor_exercicio_seguinte: (valor_exercicio_anterior_nao_processado + valor_incricao_nao_processado) - valor_baixa_nao_processado,
						}
					}
				}
			end
		end
	end
	
	def data_do_relatorio_por_extenso(data_final)
		"#{data_final.strftime("%d")} de #{l(data_final, format: :mes).capitalize} de #{data_final.year}"
	end

	private
	def disponibiliza_dependencias
		@configuracoes = Configuracao.last
		@estado = Base::Estado.find(@configuracoes.estado_id).uf if @configuracoes.estado_id.present?
		@cidade = Base::Cidade.find(@configuracoes.cidade_id).nome if @configuracoes.cidade_id.present?
	end

	def codigo_formatado(codigo)
		codigo.gsub(/(\d{1})(\d{1})(\d{2})(\d{2})(\d{2})/, '\1.\2.\3.\4.\5')
	end
	
				
	def unidade_orcamentaria_do_exercicio_atual(orcamento_atual = nil, unidade_orcamentaria=nil)
		orcamento_atual = self.orcamento unless orcamento_atual.present?
		if unidade_orcamentaria.orgao.orcamento.id == orcamento_atual.id
			return unidade_orcamentaria
		else
			cod_orgao = unidade_orcamentaria.orgao.codigo
			cod_unidade = unidade_orcamentaria.codigo
			unidade_orcamentaria_do_exercicio = Loa::UnidadeOrcamentaria.joins(:orgao).find_by(codigo: cod_unidade, loa_orgaos: {codigo: cod_orgao, orcamento_id: orcamento_atual.id})
				if unidade_orcamentaria_do_exercicio.blank?
					unidade_orcamentaria_do_exercicio = orcamento_atual.unidades_orcamentarias.joins(:unidades_orcamentaria_vinculada)
						.find_by('loa_unidades_orcamentaria_vinculada.unidade_orcamentaria_vinculada_id = ?', unidade_orcamentaria.id)
				end
			return unidade_orcamentaria_do_exercicio
		end
	end
end
