/*
Copyright 2012-2017 Jose Robson Mariano Alves
This file is part of bgfinancas.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package badernageral.bgfinancas.modelo;
import badernageral.bgfinancas.biblioteca.banco.Banco;
import badernageral.bgfinancas.biblioteca.contrato.Modelo;
import badernageral.bgfinancas.biblioteca.banco.Coluna;
import badernageral.bgfinancas.biblioteca.contrato.Categoria;
import badernageral.bgfinancas.biblioteca.contrato.Grafico;
import badernageral.bgfinancas.biblioteca.sistema.Janela;
import badernageral.bgfinancas.biblioteca.tipo.Funcao;
import badernageral.bgfinancas.biblioteca.utilitario.Datas;
import badernageral.bgfinancas.biblioteca.utilitario.Erro;
import badernageral.bgfinancas.biblioteca.utilitario.Validar;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.FormatStyle;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
public final class Despesa extends Banco<Despesa> implements Modelo, Grafico {
private static final String MODULO = RAIZ + "/modulo/despesa";
public static final String FXML = MODULO + "/Despesa.fxml";
public static final String FXML_CADASTRO_MULTIPLO = MODULO + "/DespesaCadastroMultiplo.fxml";
public static final String FXML_DESPESAS_AGENDADAS = MODULO + "/DespesasAgendadas.fxml";
public static final String FXML_FORMULARIO = MODULO + "/DespesaFormulario.fxml";
public static final String FXML_MODAL_DESPESA = MODULO + "/ModalDespesa.fxml";
public static final String TABELA = "despesas";
private final Coluna idDespesa = new Coluna(TABELA, "id_despesa");
private final Coluna idConta = new Coluna(TABELA, "id_conta");
private final Coluna idItem = new Coluna(TABELA, "id_item");
private final Coluna quantidade = new Coluna(TABELA, "quantidade");
private final Coluna valor = new Coluna(TABELA, "valor");
private final Coluna data = new Coluna(TABELA, "data");
private final Coluna hora = new Coluna(TABELA, "hora");
private final Coluna agendada = new Coluna(TABELA, "agendada");
private final Coluna parcela = new Coluna(TABELA, "parcela");
private final Coluna idCartaoCredito = new Coluna(TABELA, "id_cartao_credito");
private final Coluna idContaInner = new Coluna(Conta.TABELA, "id_conta");
private final Coluna nomeConta = new Coluna(Conta.TABELA, "nome", "", "nome_conta");
private final Coluna idCartaoCreditoLeft = new Coluna(CartaoCredito.TABELA, "id_cartao_credito");
private final Coluna nomeCartaoCredito = new Coluna(CartaoCredito.TABELA, "nome", "", "nome_cartao_credito");
private final Coluna idItemInner = new Coluna(DespesaItem.TABELA, "id_item");
private final Coluna idCategoria = new Coluna(DespesaItem.TABELA, "id_categoria");
private final Coluna nomeItem = new Coluna(DespesaItem.TABELA, "nome", "", "nome_item");
private final Coluna idCategoriaInner = new Coluna(DespesaCategoria.TABELA, "id_categoria");
private final Coluna nomeCategoria = new Coluna(DespesaCategoria.TABELA, "nome", "", "nome_categoria");
private final Coluna sumValor = new Coluna(TABELA, "valor", "sum_valor", Funcao.SOMAR);
private final Coluna sumQuantidade = new Coluna(TABELA, "quantidade", "sum_quantidade", Funcao.SOMAR);
private final Coluna semana = new Coluna(TABELA, "data", "semana", Funcao.SEMANA);
private final Coluna mes = new Coluna(TABELA, "data", "mes", Funcao.MES);
private final Coluna ano = new Coluna(TABELA, "data", "ano", Funcao.ANO);
private Boolean somenteAgendamento = false;
private Boolean especificarMesAno = false;
private LocalDate dataInicio;
private LocalDate dataFim;
private String tipo = idioma.getMensagem("despesa");
private String status = idioma.getMensagem("efetuado");
public Despesa() {
}
public Despesa(String idItem, String nomeItem, String nomeCategoria, String quantidade, String valor) {
this.idItem.setValor(idItem);
this.nomeItem.setValor(nomeItem);
this.nomeCategoria.setValor(nomeCategoria);
this.quantidade.setValor(quantidade);
this.valor.setValor(valor);
this.agendada.setValor("0");
}
public Despesa(String idDespesa, String idConta, String idItem, String quantidade, String valor, LocalDate data, String hora, String agendada, String cartao_credito) {
this.idDespesa.setValor(idDespesa);
this.idConta.setValor(idConta);
this.idItem.setValor(idItem);
this.quantidade.setValor(quantidade);
this.valor.setValor(valor);
this.data.setValor(Datas.toSqlData(data));
this.hora.setValor(hora);
this.agendada.setValor(agendada);
this.idCartaoCredito.setValor(cartao_credito);
}
@Override
protected Despesa instanciar(ResultSet rs) throws SQLException {
Despesa d = new Despesa(
rs.getString(idDespesa.getColuna()),
rs.getString(idConta.getColuna()),
rs.getString(idItem.getColuna()),
rs.getString(quantidade.getColuna()),
rs.getString(valor.getColuna()),
rs.getDate(data.getColuna()).toLocalDate(),
rs.getString(hora.getColuna()),
rs.getString(agendada.getColuna()),
rs.getString(idCartaoCredito.getColuna())
);
d.setParcela(rs.getString(parcela.getColuna()));
d.setNomeConta(rs.getString(nomeConta.getAliasColuna()));
d.setNomeItem(rs.getString(nomeItem.getAliasColuna()));
d.setNomeCategoria(rs.getString(nomeCategoria.getAliasColuna()));
d.setNomeCartaoCredito(rs.getString(nomeCartaoCredito.getAliasColuna()));
return d;
}
public Despesa getClone() {
return new Despesa(
idItem.getValor(),
nomeItem.getValor(),
nomeCategoria.getValor(),
quantidade.getValor(),
valor.getValor()
);
}
@Override
public boolean cadastrar() {
return this.insert(idConta, idItem, quantidade, valor, data, hora, agendada, parcela, idCartaoCredito).commit();
}
@Override
public boolean alterar() {
return this.update(idConta, quantidade, valor, data, agendada, idCartaoCredito).where(idDespesa, "=").commit();
}
@Override
public boolean excluir() {
return this.delete(idDespesa, "=").commit();
}
@Override
public Usuario consultar() {
System.out.println(idioma.getMensagem("nao_implementado"));
return null;
}
@Override
public ObservableList<Despesa> listar() {
try {
this.select(idDespesa, idConta, idItem, quantidade, valor, data, hora, agendada, parcela, idCartaoCredito, nomeConta, nomeItem, nomeCategoria, nomeCartaoCredito);
this.inner(idConta, idContaInner);
this.inner(idItem, idItemInner);
this.inner(idCategoria, idCategoriaInner);
this.left(idCartaoCredito, idCartaoCreditoLeft);
this.where(nomeConta, "LIKE", "(");
this.or(nomeItem, "LIKE");
this.or(valor, "=");
this.or(data, "=");
this.or(nomeCategoria, "LIKE", ")");
if (idItem.getValor() != null) {
this.and(idItem, "=");
}
if (idCategoria.getValor() != null) {
this.and(idCategoria, "=");
}
if (idConta.getValor() != null) {
this.and(idConta, "=");
}
if (idCartaoCredito.getValor() != null) {
if (idCartaoCredito.getValor().equals("NULL")) {
this.andIsNull(idCartaoCredito);
} else if (idCartaoCredito.getValor().equals("NOTNULL")) {
this.andIsNotNull(idCartaoCredito);
} else {
this.and(idCartaoCredito, "=");
}
}
agendada.setValor("1");
if (somenteAgendamento) {
this.and(agendada, "=");
} else {
this.and(agendada, "<>");
}
if (especificarMesAno) {
data.setValor(Datas.toSqlData(dataInicio));
this.and(data, ">=");
data.setValor(Datas.toSqlData(dataFim));
this.and(data, "<=");
}
if (somenteAgendamento) {
this.orderByAsc(data, nomeItem);
} else {
this.orderByDesc(data, hora);
}
ResultSet rs = this.query();
if (rs != null) {
List<Despesa> Linhas = new ArrayList<>();
while (rs.next()) {
Linhas.add(instanciar(rs));
}
ObservableList<Despesa> resultado = FXCollections.observableList(Linhas);
return resultado;
} else {
return null;
}
} catch (SQLException ex) {
Janela.showException(ex);
return null;
}
}
public boolean isDespesasAtrasadas() {
try {
this.select(idDespesa);
agendada.setValor("1");
this.where(agendada, "=");
data.setValor(Datas.toSqlData(LocalDate.now()));
this.and(data, "<=");
data.setValor(Configuracao.getPropriedade("data_notificacao"));
this.and(data, ">");
this.orderByDesc(data, hora);
ResultSet rs = this.query();
if (rs != null) {
return rs.next();
} else {
return false;
}
} catch (SQLException ex) {
Janela.showException(ex);
return false;
}
}
public String getIdDespesa() {
return idDespesa.getValor();
}
public String getIdConta() {
return idConta.getValor();
}
public String getIdItem() {
return idItem.getValor();
}
public BigDecimal getQuantidade() {
return new BigDecimal(quantidade.getValor());
}
public BigDecimal getValor() {
return new BigDecimal(valor.getValor());
}
public LocalDate getData() {
return Datas.getLocalDate(data.getValor());
}
public LocalTime getHora() {
return Datas.getLocalTime(hora.getValor());
}
public LocalDateTime getDataHora() {
return Datas.getLocalDateTime(getData(), getHora());
}
public String getAgendada() {
return agendada.getValor();
}
public String getNomeConta() {
return nomeConta.getValor();
}
public String getIdCartaoCredito() {
return idCartaoCredito.getValor();
}
public String getNomeCartaoCredito() {
return nomeCartaoCredito.getValor();
}
public String getNomeItem() {
if (agendada.getValor() != null && agendada.getValor().equals("1") && parcela.getValor() != null) {
return nomeItem.getValor() + " - " + parcela.getValor();
} else {
return nomeItem.getValor();
}
}
public String getNomeCategoria() {
return nomeCategoria.getValor();
}
public void setValor(String valor) {
this.valor.setValor(valor);
}
public Despesa setIdItem(String id_item) {
this.idItem.setValor(id_item);
return getThis();
}
public void setQuantidade(String quantidade) {
this.quantidade.setValor(quantidade);
}
public Despesa setIdConta(Categoria conta) {
this.idConta.setValor(conta.getIdCategoria());
return getThis();
}
public Despesa setIdCartaoCredito(Categoria cartao_credito) {
this.idCartaoCredito.setValor(cartao_credito.getIdCategoria());
return getThis();
}
public void setData(String data) {
this.data.setValor(data);
}
public void setHora(String hora) {
this.hora.setValor(hora);
}
public Despesa setAgendada(String agendada) {
this.agendada.setValor(agendada);
return getThis();
}
public void setParcela(String parcela) {
this.parcela.setValor(parcela);
}
public void setNomeConta(String conta) {
this.nomeConta.setValor(conta);
}
public void setNomeCartaoCredito(String cartao_credito) {
this.nomeCartaoCredito.setValor(cartao_credito);
}
public void setNomeItem(String item) {
this.nomeItem.setValor(item);
}
public void setNomeCategoria(String categoria) {
this.nomeCategoria.setValor(categoria);
}
public Despesa setIdCategoria(String idCategoria) {
this.idCategoria.setValor(idCategoria);
return this;
}
public Despesa setSomenteAgendamento() {
this.somenteAgendamento = true;
return getThis();
}
public Despesa setMesAno(int mes, int ano) {
this.especificarMesAno = true;
this.dataInicio = LocalDate.of(ano, mes, 1);
this.dataFim = dataInicio.withDayOfMonth(dataInicio.lengthOfMonth());
return getThis();
}
public Despesa setFiltro(String filtro) {
nomeConta.setValor(filtro);
nomeItem.setValor(filtro);
nomeCategoria.setValor(filtro);
try {
LocalDate filtroData = LocalDate.parse(filtro, DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT));
data.setValor(Datas.toSqlData(filtroData));
} catch (DateTimeParseException e) {
}
try {
Validar.decimal(filtro, null);
valor.setValor(filtro);
} catch (Erro e) {
}
return this;
}
public String getSumValor(LocalDate hoje) {
try {
if (idCategoria.getValor() != null) {
LocalDate inicio = hoje.withDayOfMonth(1);
LocalDate fim = hoje.withDayOfMonth(hoje.lengthOfMonth());
this.select(sumValor);
this.inner(idItem, idItemInner);
this.inner(idCategoria, idCategoriaInner);
this.where(idCategoria, "=");
data.setValor(Datas.toSqlData(inicio));
this.and(data, ">=");
data.setValor(Datas.toSqlData(fim));
this.and(data, "<=");
agendada.setValor("1");
this.and(agendada, "<>");
ResultSet rs = this.query();
if (rs.next()) {
String resultado = rs.getString(sumValor.getAliasColuna());
if (resultado != null) {
return resultado;
}
}
}
return "0.0";
} catch (SQLException ex) {
Janela.showException(ex);
return "0.0";
}
}
public ObservableList<Extrato> getExtrato(LocalDate inicio, LocalDate fim) {
try {
this.select(idItem, data, hora, nomeCategoria, nomeItem, valor, agendada);
this.inner(idItem, idItemInner);
this.inner(idCategoria, idCategoriaInner);
data.setValor(Datas.toSqlData(inicio));
this.where(data, ">=");
data.setValor(Datas.toSqlData(fim));
this.and(data, "<=");
this.orderByAsc(data,hora);
ResultSet rs = this.query();
if (rs != null) {
List<Extrato> objetos = new ArrayList<>();
while (rs.next()) {
String status = idioma.getMensagem("efetuado");
if(rs.getString(agendada.getColuna()).equals("1")){
status = idioma.getMensagem("agendado");
}
Extrato e = new Extrato(
idioma.getMensagem("despesa"),
rs.getString(data.getColuna()),
rs.getString(hora.getColuna()),
rs.getString(nomeCategoria.getAliasColuna()),
rs.getString(nomeItem.getAliasColuna()),
rs.getString(valor.getColuna()),
status
);
objetos.add(e);
}
ObservableList<Extrato> resultado = FXCollections.observableList(objetos);
return resultado;
} else {
return null;
}
} catch (SQLException ex) {
Janela.showException(ex);
return null;
}
}
public ObservableList<Despesa> listarPeriodoAgrupado(LocalDate inicio, LocalDate fim) {
try {
this.select(idItem, nomeCategoria, nomeItem, sumQuantidade, sumValor);
this.inner(idItem, idItemInner);
this.inner(idCategoria, idCategoriaInner);
data.setValor(Datas.toSqlData(inicio));
this.where(data, ">=");
data.setValor(Datas.toSqlData(fim));
this.and(data, "<=");
agendada.setValor("1");
this.and(agendada, "<>");
this.groupBy(idItem,nomeCategoria,nomeItem);
this.orderByAsc(nomeCategoria);
this.orderByDesc(sumValor);
ResultSet rs = this.query();
if (rs != null) {
List<Despesa> Linhas = new ArrayList<>();
while (rs.next()) {
Despesa d = new Despesa();
d.setNomeCategoria(rs.getString(nomeCategoria.getColuna()));
d.setNomeItem(rs.getString(nomeItem.getAliasColuna()));
d.setQuantidade(rs.getString(sumQuantidade.getAliasColuna()));
d.setValor(rs.getString(sumValor.getAliasColuna()));
Linhas.add(d);
}
ObservableList<Despesa> resultado = FXCollections.observableList(Linhas);
return resultado;
} else {
return null;
}
} catch (SQLException ex) {
Janela.showException(ex);
return null;
}
}
public ObservableList<Despesa> getRelatorioMensal(LocalDate hoje) {
return getRelatorioMensal(hoje, false);
}
public ObservableList<Despesa> getRelatorioMensal(LocalDate hoje, Boolean somente_agendamento) {
try {
LocalDate inicio = hoje.withDayOfMonth(1);
LocalDate fim = hoje.withDayOfMonth(hoje.lengthOfMonth());
this.select(sumValor, nomeCategoria);
this.inner(idItem, idItemInner);
this.inner(idCategoria, idCategoriaInner);
data.setValor(Datas.toSqlData(inicio));
this.and(data, ">=");
data.setValor(Datas.toSqlData(fim));
this.and(data, "<=");
agendada.setValor("1");
if (somente_agendamento) {
this.and(agendada, "=");
} else {
this.and(agendada, "<>");
}
this.groupBy(nomeCategoria);
this.orderByAsc(nomeCategoria);
ResultSet rs = this.query();
if (rs != null) {
List<Despesa> linhas = new ArrayList<>();
while (rs.next()) {
Despesa d = new Despesa();
d.setValor(rs.getString(sumValor.getAliasColuna()));
d.setNomeCategoria(rs.getString(nomeCategoria.getAliasColuna()));
linhas.add(d);
}
ObservableList<Despesa> resultado = FXCollections.observableList(linhas);
return resultado;
} else {
return null;
}
} catch (SQLException ex) {
Janela.showException(ex);
return null;
}
}
@Override
public List<XYChart.Series<String, Number>> getRelatorioMensalBarras(LocalDate inicio, LocalDate fim, String nome_categoria, String id_categoria, Integer tipo_categoria) {
try {
Coluna coluna = nomeCategoria;
if (nome_categoria != null) {
coluna = nomeItem;
nomeCategoria.setValor(nome_categoria);
}
this.select(sumValor, sumQuantidade, coluna);
this.inner(idItem, idItemInner);
this.inner(idCategoria, idCategoriaInner);
data.setValor(Datas.toSqlData(inicio));
this.and(data, ">=");
data.setValor(Datas.toSqlData(fim));
this.and(data, "<=");
if (agendada.getValor().equals("1")) {
this.and(agendada, "=");
} else {
agendada.setValor("1");
this.and(agendada, "<>");
agendada.setValor("0");
}
if (nome_categoria != null) {
this.and(nomeCategoria, "=");
}
if (id_categoria != null) {
if (tipo_categoria == 1) {
idConta.setValor(id_categoria);
this.and(idConta, "=");
} else if (tipo_categoria == 2) {
idCartaoCredito.setValor(id_categoria);
this.and(idCartaoCredito, "=");
}
} else if (tipo_categoria == 4) {
this.andIsNotNull(idCartaoCredito);
} else if (tipo_categoria == 3) {
this.andIsNull(idCartaoCredito);
}
this.groupBy(coluna);
this.orderByAsc(coluna);
ResultSet rs = this.query();
if (rs != null) {
List<XYChart.Series<String, Number>> categorias = new ArrayList<>();
while (rs.next()) {
XYChart.Series<String, Number> categoria = new XYChart.Series<>();
StringBuilder descricao = new StringBuilder(rs.getString(coluna.getAliasColuna()));
if (nome_categoria != null) {
descricao.insert(0, rs.getString(sumQuantidade.getAliasColuna()) + " ");
}
categoria.setName(descricao.toString());
categoria.getData().add(new XYChart.Data<>("", rs.getDouble(sumValor.getAliasColuna())));
categorias.add(categoria);
}
return categorias;
} else {
return null;
}
} catch (SQLException ex) {
Janela.showException(ex);
return null;
}
}
public void preencherRelatorioMensalLinhas(LineChart<String, Number> graficoLinhas, LocalDate inicio, LocalDate fim, List<String> categoriasSelecionadas, Categoria conta, String tipo) {
try {
if (tipo.equals(idioma.getMensagem("semanal"))) {
this.select(sumValor, nomeCategoria, ano, mes, semana);
} else if (tipo.equals(idioma.getMensagem("mensal"))) {
this.select(sumValor, nomeCategoria, ano, mes);
} else {
this.select(sumValor, nomeCategoria, ano);
}
this.inner(idItem, idItemInner);
this.inner(idCategoria, idCategoriaInner);
data.setValor(Datas.toSqlData(inicio));
this.and(data, ">=");
data.setValor(Datas.toSqlData(fim));
this.and(data, "<=");
agendada.setValor("1");
this.and(agendada, "<>");
if (categoriasSelecionadas.size() > 0) {
this.andIN(idCategoria, categoriasSelecionadas);
}
if (conta.getIdCategoria() != null) {
idConta.setValor(conta.getIdCategoria());
this.and(idConta, "=");
}
if (tipo.equals(idioma.getMensagem("semanal"))) {
this.groupBy(ano, mes, semana, nomeCategoria);
this.orderByAsc(ano, mes, semana);
} else if (tipo.equals(idioma.getMensagem("mensal"))) {
this.groupBy(ano, mes, nomeCategoria);
this.orderByAsc(ano, mes);
} else {
this.groupBy(ano, nomeCategoria);
this.orderByAsc(ano);
}
ResultSet rs = this.query();
BigDecimal valor_total = new BigDecimal("0.0");
List<XYChart.Series<String, Number>> categorias = new ArrayList<>();
if (rs != null) {
Set<String> datas = new LinkedHashSet<>();
XYChart.Series<String, Number> categoria = new XYChart.Series<>();
while (rs.next()) {
String nome_categoria = rs.getString(nomeCategoria.getAliasColuna());
try {
categoria = categorias.stream().filter(c -> c.getName().equals(nome_categoria)).findFirst().get();
} catch (NoSuchElementException ex) {
categoria = new XYChart.Series<>();
categoria.setName(nome_categoria);
categorias.add(categoria);
}
String descricao = "";
if (tipo.equals(idioma.getMensagem("semanal"))) {
descricao = rs.getString(semana.getAliasColuna()) + "/" + idioma.getNomeMes(rs.getInt(mes.getAliasColuna())).substring(0, 3) + "/" + rs.getString(ano.getAliasColuna());
} else if (tipo.equals(idioma.getMensagem("mensal"))) {
descricao = idioma.getNomeMes(rs.getInt(mes.getAliasColuna())).substring(0, 3) + "/" + rs.getString(ano.getAliasColuna());
} else {
descricao = rs.getString(ano.getAliasColuna());;
}
datas.add(descricao);
valor_total = valor_total.add(new BigDecimal(rs.getString(sumValor.getAliasColuna())));
categoria.getData().add(new XYChart.Data<>(descricao, rs.getDouble(sumValor.getAliasColuna())));
}
// Bug JavaFX
if (categorias.size() > 0) {
categoria = categorias.get(0);
for (String d : datas) {
try {
categoria.getData().stream().filter(c -> c.getXValue().equals(d)).findFirst().get();
} catch (NoSuchElementException ex) {
categoria.getData().add(new XYChart.Data<>(d, 0));
}
}
if (tipo.equals(idioma.getMensagem("semanal"))) {
categoria.getData().sort((Data<String, Number> a, Data<String, Number> b) -> {
String[] A = a.getXValue().split("/");
String[] B = b.getXValue().split("/");
if (Integer.parseInt(A[2]) > Integer.parseInt(B[2])) {
return 1;
} else if (Integer.parseInt(A[2]) < Integer.parseInt(B[2])) {
return -1;
} else if (Integer.parseInt(A[0]) > Integer.parseInt(B[0])) {
return 1;
} else {
return -1;
}
});
}
}
}
graficoLinhas.setTitle(idioma.getMensagem("categorias") + " - " + idioma.getMensagem("moeda") + " " + valor_total);
graficoLinhas.getData().setAll(categorias);
} catch (SQLException ex) {
Janela.showException(ex);
}
}
@Override
protected Despesa getThis() {
return this;
}
}