private List<Dados61R> getDados61R() {
    List<Dados61R> ld61r = new ArrayList<>();

    // agrupando as notas por mes+ano+id do produto
    Map<String, List<EcfNotaProduto>> mensal = new HashMap<>();
    for (EcfNota nota : notas) {
      String mesAno = Util.formataData(nota.getEcfNotaData(), "MMyyyy");
      for (EcfNotaProduto np : nota.getEcfNotaProdutos()) {
        String chave = mesAno + "-" + np.getProdProduto().getId();
        List<EcfNotaProduto> lista = mensal.get(chave);
        if (lista == null) {
          lista = new ArrayList<>();
          lista.add(np);
          mensal.put(chave, lista);
        } else {
          lista.add(np);
        }
      }
    }

    // gerando os valores do mes/ano por produto
    for (Entry<String, List<EcfNotaProduto>> entry : mensal.entrySet()) {
      double qtd = 0.00;
      double bruto = 0.00;
      char trib = 0;
      double aliq = 0.00;

      // soma os valores
      for (EcfNotaProduto np : entry.getValue()) {
        qtd += np.getEcfNotaProdutoQuantidade();
        bruto += np.getEcfNotaProdutoBruto();
        trib = np.getProdProduto().getProdProdutoTributacao();
        aliq = np.getProdProduto().getProdProdutoIcms();
      }

      Dados61R d61r = new Dados61R();
      d61r.setMesAno(Integer.valueOf(entry.getKey().substring(0, 6)));
      d61r.setCodigo(entry.getKey() + "");
      d61r.setQtd(qtd);
      d61r.setBruto(bruto);
      if (trib == 'T') {
        d61r.setBase_icms(bruto);
        d61r.setAliq_icms(aliq);
      }

      Util.normaliza(d61r);
      ld61r.add(d61r);
    }

    return ld61r;
  }
  private List<Dados61> getDados61() {
    List<Dados61> ld61 = new ArrayList<>();

    // agrupando as notas por dia+serie+subserie
    Map<String, List<EcfNota>> grupo = new HashMap<>();
    for (EcfNota nota : notas) {
      String chave =
          Util.formataData(nota.getEcfNotaData(), "ddMMyyyy")
              + nota.getEcfNotaSerie()
              + nota.getEcfNotaSubserie();
      List<EcfNota> lista = grupo.get(chave);
      if (lista == null) {
        lista = new ArrayList<>();
        lista.add(nota);
        grupo.put(chave, lista);
      } else {
        lista.add(nota);
      }
    }

    // soma os valores agrupados
    for (Entry<String, List<EcfNota>> entry : grupo.entrySet()) {
      Dados61 d61 = new Dados61();

      for (EcfNota nota : entry.getValue()) {
        d61.setData(nota.getEcfNotaData());
        d61.setModelo(2);
        d61.setSerie(nota.getEcfNotaSerie());
        d61.setSubserie(nota.getEcfNotaSubserie());
        if (nota.getEcfNotaNumero() < d61.getNumInicial()) {
          d61.setNumInicial(nota.getEcfNotaNumero());
        }
        if (nota.getEcfNotaNumero() > d61.getNumFinal()) {
          d61.setNumFinal(nota.getEcfNotaNumero());
        }
        if (!nota.isEcfNotaCancelada()) {
          d61.setValorTotal(d61.getValorTotal() + nota.getEcfNotaLiquido());
          double base_icms = 0.00;
          double valor_icms = 0.00;
          double isento = 0.00;
          double aliq = 0.00;
          for (EcfNotaProduto np : nota.getEcfNotaProdutos()) {
            if (np.getProdProduto().getProdProdutoTributacao() == 'T') {
              base_icms += np.getEcfNotaProdutoLiquido();
              valor_icms +=
                  (np.getEcfNotaProdutoLiquido() * np.getProdProduto().getProdProdutoIcms() / 100);
              aliq = np.getProdProduto().getProdProdutoIcms();
            } else if (np.getProdProduto().getProdProdutoTributacao() == 'I'
                || np.getProdProduto().getProdProdutoTributacao() == 'N') {
              isento += np.getEcfNotaProdutoLiquido();
            }
          }
          d61.setBase_icms(base_icms);
          d61.setValor_icms(valor_icms);
          d61.setValor_isento(isento);
          d61.setOutras(0.00);
          d61.setAliq_icms(aliq);
        }
      }

      Util.normaliza(d61);
      ld61.add(d61);
    }

    return ld61;
  }
  @Override
  public void executar() throws OpenPdvException {
    // ajustando a data fim para documento, pois o mesmo usa datetime
    Calendar cal = Calendar.getInstance();
    cal.setTime(fim);
    cal.add(Calendar.DAY_OF_MONTH, 1);
    fim = cal.getTime();

    // recupera as nfes emitidas no periodo
    FiltroData fd1 = new FiltroData("ecfNotaEletronicaData", ECompara.MAIOR_IGUAL, inicio);
    FiltroData fd2 = new FiltroData("ecfNotaEletronicaData", ECompara.MENOR, fim);
    GrupoFiltro gp1 = new GrupoFiltro(EJuncao.E, new IFiltro[] {fd1, fd2});
    EcfNotaEletronica ene = new EcfNotaEletronica();
    ene.setOrdemDirecao(EDirecao.ASC);
    nfes = service.selecionar(ene, 0, 0, gp1);

    // recupera as notas emitidas no periodo
    FiltroData fd3 = new FiltroData("ecfNotaData", ECompara.MAIOR_IGUAL, inicio);
    FiltroData fd4 = new FiltroData("ecfNotaData", ECompara.MENOR, fim);
    GrupoFiltro gp2 = new GrupoFiltro(EJuncao.E, new IFiltro[] {fd3, fd4});
    EcfNota en = new EcfNota();
    en.setOrdemDirecao(EDirecao.ASC);
    notas = service.selecionar(en, 0, 0, gp2);

    // recupera as leituras Z no periodo
    FiltroData fd7 = new FiltroData("ecfZMovimento", ECompara.MAIOR_IGUAL, inicio);
    FiltroData fd8 = new FiltroData("ecfZMovimento", ECompara.MENOR, fim);
    GrupoFiltro gf4 = new GrupoFiltro(EJuncao.E, new IFiltro[] {fd7, fd8});
    EcfZ ez = new EcfZ();
    ez.setOrdemDirecao(EDirecao.ASC);
    zs = service.selecionar(ez, 0, 0, gf4);

    // recupera os produtos com estoque maior que zero
    FiltroNumero fn = new FiltroNumero("prodProdutoEstoque", ECompara.DIFERENTE, 0);
    ProdProduto pp = new ProdProduto();
    pp.setCampoOrdem("prodProdutoId");
    estoque = service.selecionar(pp, 0, 0, fn);

    Sintegra sintegra = new Sintegra();
    // Dados 10
    sintegra.setDados10(getDados10());
    // Dados 11
    sintegra.setDados11(getDados11());
    // Dados 50
    sintegra.setDados50(getDados50());
    // Dados 54
    sintegra.setDados54(getDados54());
    // Dados 60M
    sintegra.setDados60M(getDados60M());
    // Dados 60R
    sintegra.setDados60R(getDados60R());
    // Dados 61
    sintegra.setDados61(getDados61());
    // Dados 61R
    sintegra.setDados61R(getDados61R());
    // Dados 74
    sintegra.setDados74(getDados74());
    // Dados 75
    sintegra.setDados75(getDados75());
    // Dados 90 e gerado internamento pelo PAF

    try {
      // gerar o arquivo
      path = PAF.gerarVendasPeriodo(sintegra);
    } catch (Exception ex) {
      throw new OpenPdvException(ex);
    }
  }