PDV

De BIS Wiki
Ir para navegação Ir para pesquisar

Módulo para controle dos PDVs (Ponto de Venda) da Empresa.

Convênios

Relatório de Consumo Automático

Este recurso permite que o sistema envie relatórios de consumos por período para clientes automaticamente, de acordo com a frequência e período estipulados.

  • Requisitos
    • Agendamento da frequência e data - O sistema deve permitir que cada Convênio tenha um agendamento para do SchedulerVO para definir a data e a frequência de envio.
    • Endereço de E-mail - Os endereços de e-mails deverão ser salvos diretamente como uma propriedade do agendamento (SchedulerVO). Aceitar múltiplos endereços de e-mail separados por , ou ;. Detalhes como criar as propriedades na descrição da Task que fará o serviço. Na UI, os endereços de e-mail só devem estar habilitados caso algum agendamento seja definido. Caso contrário desligar os campos, afinal, sem SchedulerVO nem teremos onde salvar essas informações.
    • Cálculo do Início do Período Atual - Quando o agendamento for definido, o CRUD deve avaliar o SchedulerVO e de acordo com a frequência e data definidas, calcular qual a seria a data de início do período que o primeiro relatório será enviado. O cálculo deve ser feito da seguinte forma:
      • Frequência Mensal - Quando a frequência for definida como mensal, deve-se obter o dia do mês da data definida no SchedulerVO e montar a uma nova data descartando (zerando) o horário e decrementando 1 mês dessa data.
Note 64.png
Person
No futuro, quando o person estiver implementado, os e-mail deverão ser escolhidos de acordo com os disponíveis no Person associado ao Convênio


Stop 256.png
Ordem de Inserção
Note que o SchedulerVO precisa que o ID do PDVContractVO seja passado em uma das suas propriedades (necessária para a Task). Por outro lado, o PDVContractVO "precisa" ter o SchedulerVO já com ID para conseguir associa-lo corretamente no banco.

No caso do cadastro de um PDVContractVO novo (insert) você não têm o ID no objeto ainda, o que impede de cadastrar o SchedulerVO. Graças a essa dependência cíclica, se faz necessário definir o SchedulerVO como null no objeto para poder inseri-lo no banco. Depois de inserido e ter o ID em mãos conseguimos passar o Scheduler para o BISCore valide e insira no banco de dados. Depois de inserido o SchedulerVO e este também ter seu ID teremos que fazer uma atualização no PDVContractVO para associar o SchedulerVO nele.

  • Validações Extras
    • [ve0001] Validar se o sistema já tem uma caixa de e-mail configurada - O sistema não deve permitir o cadastro/alteração com agendamento automático caso o sistema ainda não tenha configurado qual a caixa de e-mail a ser usada para envio de e-mails. Evitando que o cadastro seja realizado com sucesso mas depois os e-mails não sejam enviados. E como isso ocorrerá de forma assíncrona (em background) e o BIS ainda não possui nenhum sistema de alertas, o erro do envio não será notificado para o usuário.

Task: Envio de Relatórios de Consumo Automático por E-mail

Esta task tem a finalidade de gerar relatórios de consumos de cada convênio e envia-los automaticamente por e-mail.

Para realizar a função desejada a tarefa precisará de duas propriedades:

  • email - Propriedade com todos os endereços de e-mails definidos pelo usuário separados por ',' ou ';'. RegExp para o split: "[,;]". Não esquecer de remover eventuais espaços que o usuário tenha colocado antes/depois dos tokens de split.
  • pdvcontractvo.id - Propriedade com o ID do PDVContractVO (Convênio) que devemos mandar o relatório.

Funcionamento

  1. A task deve obter o PDVContractVO do banco, incluindo as informações do SchedulerVO.
  2. Verificar a data de fechamento do último relatório emitido (lastBillClosure) para saber quando começar o próximo período.
  3. De acordo com a frequência calcular a data de fim de dos consumos conforme definido no requisito do Relatório de Consumo Automático. E gerar o relatório com base nessa data e ID do convênio em questão.
  4. Enviar o e-mail com cópia para todos os endereços definidos, com o relatório em anexo, e:
    1. Assunto: "Relatório de Consumos do Convênio - Fechamento 01/06/2015" - Sendo a data o dia do fechamento do período.
    2. Corpo do E-mail:
      Relatório de Consumos do Convênio: <Código do Convênio> - <Nome do Convênio>
      Período de Fechamento: <Data Inicial> à <Data Final>
      E-mail enviado automaticamente, responder para <E-mail da empresa>. * Só incluir esta linha caso o E-mail de Reply-To estiver configurado no sistema.

Relatórios

Extrato de Consumos

O Relatório "Extrato de Consumos" tem a finalidade de montar um extrato da movimentação do convênio, como se fosse um extrato bancário. Sua aparência e dados deve ser a mesma do relatório já existente (mas não documentado na wiki até o momento) "Consumos por Período".

A diferença deste novo relatório para o anterior consiste no seguinte:

  • Linha de Saldo Anterior - No início do relatório deverá constar uma linha inicial com a data igual a data inicial do filtro escolhido pelo usuário e a seguinte descrição: "Saldo Anterior". Na coluna Total deverá apresentar o saldo anterior ao início do relatório.
  • Total Acumulado - O relatório já apresenta uma somatória de total acumulado ao final da linha de totalização. No entanto no relatório já existente o Total Acumulado começa em "0,00". Tanto que sempre o primeiro totalizador é igual ao total do primeiro cupom. Neste relatório extrato, o valor inicial deverá ser igual ao calculado para a linha de Saldo Anterior. Assim, o primeiro "Total Acumulado" a aparecer no relatório será o resultado da soma entre o "Saldo Anterior" e o total do primeiro cupom.
  • Demais Lançamentos - O primeiro relatório só exibe os consumos do cliente, nenhum outro tipo de lançamento é exibido no relatório. Os tipos de lançamentos são definidos pela Enumeration CONTRACTSTATEMENTTYPE. Este relatório deverá exibir todos os lançamentos encontrados. Para cada tipo de lançamento teremos a seguinte regra de exibição:
    • CUPOM - Detalhar a venda do cupom do mesmo modo como no relatório "Consumos por Período".
    • PAYMENT_CHECKOUT - Este lançamento é um pagamento realizado na boca do caixa (diretamente no módulo de PDV com o operador de checkout). Este lançamento deve deixar completar apenas a coluna "Data Hora" com a data do lançamento, a coluna "Item" com o texto "Pagamento No Checkout", e a coluna "Total" com o valor do lançamento. Que já deve ser negativo por padrão.
    • PAYMENT_PAYSLIPDISCOUNT - Lançamento de Desconto em Folha de pagamento. Este lançamento é usado em convênios de funcionários, quando o convênio é "pago" na forma de desconto da folha de pagamento. Este deve ser preenchido de forma similar ao pagamento no checkout, exceto pela descrição que deve ser "Pagamento por Desconto em Folha de Pagamento".
    • PAYMENT_OTHER - Lançamento para pagamentos manuais no sistema. Quando o usuário deseja lançar algum pagamento recebido de outro modo, como recebido por depósito em conta, boleto, em mãos, etc. O preenchimento deve ser como nos outros pagamentos, mas com a descrição "Outras Formas de Pagamento".


Note 64.png
Casos de Else/Default
Este é um típico caso em que no futuro poderemos ter outras formas de lançamento no convênio. Por exemplo, ao receber pagamentos por boletos quando o CashFlow estiver pronto ele pode identificar e criar o lançamento sozinho no extrato do convênio.

Ao tratar os tipos de lançamentos inclua o caso "else", fazendo um log de erro, ou lance como um erro crítico para que o sistema registre o erro e chegue até a mão do desenvolvedor o problema. Lance algo como "Tipo de lançamento desconhecido pelo sistema!". Isso força que o desenvolvedor tome logo ciência do que está acontecendo.

Para casos com Enumeration em que todos os valores devem constar no tratamento, o melhor é usar um switch SEM o caso default. Isso porque se o switch não tem o default e o valor é uma enumeration o Java lança um warning de compilação. Chamando logo a atenção do desenvolvedor durante o desenvolvimento, e não só depois que o relatório de erro vier do cliente.


Para gerar o resultado do "Saldo Anterior" deve-se criar o método "findContractPreviousBalance(Long idContract, Date date)" no PDVContractDAO do módulo PDV. Este método recebe o ID do convênio e a data limite. Deve executar a consulta já com o conteúdo sumarizado conforme o seguinte SQL exemplo:

select 
sum(total)
from pdv_contractstatement 
where idpdv_contract = 9
and date < '2015-10-01 00:00:00';


Note 64.png
Saldo até a data limite, mas sem incluir!
Quando o resultado para o relatório é consultado a data limite é incluída na busca, isto é, os dados para o relatório são os registros cuja data seja "maior ou igual" a data mínima.

Por isso ao consultar o "Saldo Anterior" devemos tomar o cuidado de usar apenas o "menor". Evitando que registros possam ser incluídos tanto no "Saldo Anterior" como no corpo do relatório, causando uma diferença dos valores de "Total Acumulado" dependendo da data usada para fazer o filtro.

Embora a chance disso pareça remota de acontecer, as chances são muitas já que as telas de inclusão manual de pagamento incluem as data sempre com a "hora zero" do dia, e os filtros tendem a usar as mesmas horas quando a precisão é de apenas data (e mesmo nas de precisão de minutos, em caso de DateRage o horário padrão sugerido já é o próprio 0:00)


Opções do Relatório

Para a geração do relatório, devem ser oferecidas as seguintes opções:

  • Filtros:
    • por Período - Filtrar por um período de datas. Não permitir um período maior que dois anos para evitar uma sobrecarga do sistema. Não permitir se a data inicial não for informada, e caso a final não seja informada, verificar se a inicial não é mais de dois anos atrás. A mesma validação está feita no "Consumos por Período", pode ser reutilizada.
    • por Convênio - Filtrar pelo nome do convênio em um ComboBox.
    • por Tipo do Convênio - Filtrar pelos tipos de Convênios.


Note 64.png
Múltiplos Relatórios
No caso em que um único convênio não for selecionado, a geração do relatório deve criar os relatórios de cada cliente como se fosse um relatório independente, reiniciando a contagem de páginas, etc. Similar ao que já é feito no "Consumos por Período".

InfoBoard

Analítico

Finalizações de Vendas

"Finalizações de Vendas" é a maneira como a venda é finalizada no PDV. Neste caso, entende-se por venda cada vez que uma comanda (ou uma venda sem comanda) é iniciada no PDV. E por "finalização de venda" a maneira como o operador sai desse venda iniciada. As opções de finalizações disponíveis atualmente são:

  • SOLD - Quando a venda é realmente efetivada. O processo se completou e o valor foi faturado. Lembrando que esse status indica qualquer tipo de venda, seja por cartão, dinheiro, convênio, fiado (ainda não implementado), etc.
  • CANCELED - Quando a venda é cancelada. Uma venda é cancelada quando seus itens são excluídos e a venda não é mais realizada.
  • STORED - Quando a venda é interrompida para continuar posteriormente. No caso os itens da venda são salvos em uma comanda/mesa e podem ser reiniciada em uma nova venda posteriormente.
  • ERROR - Quando a o sistema detecta um erro mas não consegue se restaurar para continuar a venda que está em aberto. Neste caso o sistema marca a venda como ERRO porque não sabe o real status dessa venda.
  • ERROR_SYNC - Esse status define uma falha detectada no ECF. Este status indica que a venda foi feita pelo sistema e provavelmente recebida, mas que não foi possível reconhecer no ECF se o cupom foi impresso com sucesso. Quando esse erro é detectado (não é possível confirmar se o ECF imprimiu corretamente o cupom) o sistema questiona o operador para saber se o cupom foi impresso corretamente ou não. Caso responda não, um novo cupom é gerado automaticamente com o mesmo conteúdo e marcado como SOLD, no entanto por ser uma reimpressão ele receberá na coluna 'idpdv_cupom' o ID do cupom o qual ele representa uma reimpressão. Diferenciando assim uma venda de "reimpressão" de uma venda real.

Assim, este relatório tem a intenção de sumarizar essas quantidades para análise de vendas, cancelamentos, erros, falhas, etc.

  • Caminho do Relatório: Caixa/PDV > Analítico > Finalizações de Vendas
  • Campos de Filtros:
    • Periodo - Filtrar o relatório por um período de datas com precisão de minutos.
    • Status - Campo ComboBoxMultiSelect com as opções do status possíveis: 'CANCELED', 'SOLD', 'STORED', 'ERROR', 'ERROR_SYNC'. Cada um que seja selecionado deverá fazer parte do resultado. Inicialmente deixar selecionado todos os valores.
    • Reimpressões - ComboMultiSelect com duas opções: 'Vendas' e 'Reimpressões'. inicialmente apenas Vendas deve estar selecionados, fazendo com que as Reimpressões não apareçam no relatório.
  • Agrupamentos:
    • Horas do Dia - Agrupar pelas horas. Em caso de filtro com múltiplos dias, os valores dos diversos dias devem ser somados e exibidos juntos.
    • Dias da Semana - Agrupar pelos dias da semana. Em caso de filtro com múltiplas semanas, os valores de todos os Domingos, segundas, etc., devem se somados juntos.
    • Diário/Mensal/Anual - Agrupa os dados de acordo com seu dia, mês e ano.
    • Mensal/Anual - Agrupa os dados de acordo com seu mês e ano.
    • Diário - Agrupa os dados de acordo com o dia do mês. [Para implementação Futura]
    • Mensal - Agrupa os dados de acordo com o mês. [Para implementação Futura]
  • Colunas do Relatório
    • Status - Coluna com o status da finalização. Alinhado a Esquerda.
    • Colunas Totais e Agrupaveis:
      • Qtde. - Coluna com quantidade de finalizações totais. Alinhado a direita. Formatação número inteiro.
      • Part. Qtde - Coluna com o percentual de participação em relação à quantidade de cupons. Usar componente PercentualBar.
      • Total - Coluna com a somatória dos valores de venda dos cupons. Alinhado a direita. Formatação de valor monetário.
      • Part. Tot - Coluna com o percentual de participação em relação aos valores totais. Usar componente PercentualBar.
  • Gráfico
    • Pizza Qtde - Gráfico de Pizza similar aos de ICMS e demais para demonstrar a proporção em relação a quantidade. A "tooltip" do mouse em cima do gráfico deve demonstrar o label "Qtde.: xxxx".
    • Pizza Valor - Gráfico de Pizza idêntico ao anterior, mas ao invés de usar a quantidade, usar os valores totais.
    • Gráficos Disponíveis apenas quando houver GroupBy Definido:
      • Barras Qtde - Gráfico com aparência similar ao exemplo do Vaadin - http://demo.vaadin.com/charts/#DualAxesLineAndColumn.
        • Eixo X - Colocar os valores do GroupBy. Ex: As horas do dia, os dias da semana, os meses do ano, etc.
        • Séries - As séries serão os tipos de finalizações/status encontrados. Cada status deverá ser uma barra distinta e suas quantidades usadas para definir o tamanho da barra. Seu tooltip deve demonstrar o label "Qtde.: XXXX"
        • Série Secundária - Utilizar a soma de todas as barras de cada agrupamento para definir o valor da linha. De modo a indicar o total de finalizações daquele grupo.
      • Barras Tot - Gráfico identico ao anterior, mas usando o valor monetário dos cupons como definição dos valores das séries.


Exemplo de SQL para MySQL para obter os dados como se espera no relatório:

-- Relatório de Finalizações de cupoms
select status, count(*), sum(c.total)
-- , hour(c.date) -- Group By Horas do Dia
, weekday(c.date) -- Agrupamento pod dia da semana
from pdv_cupom c
where c.status in ('CANCELED', 'SOLD', 'STORED', 'ERROR', 'ERROR_SYNC') -- Filtar por tipo de Fechamento
AND c.DATE >= '2015-05-01 00:00:00' AND c.DATE <= '2015-05-31 23:59:59' -- Filtra por data
-- and c.idpdv_cupom is null -- Remove os cupoms de Reimpressão
-- and c.idpdv_cupom is not null -- Remove os cupons de venda, deixando apenas as reimpressões
group by status
-- , hour(c.date) -- Group By Horas do Dia
, weekday(c.date) -- Group By Dia da semana
order by status
-- , hour(c.date) -- Group By Horas do Dia
;

Vendas

por Tributação de ICMS

Este relatório agrupa o faturamento de acordo com o tipo de tributação usada na venda.

Exemplo de SQL para MySQL para obter os dados como se espera no relatório:

SELECT ci.icmstype, ci.icmstaxratio, SUM(ci.total) AS tot
FROM pdv_cupomitem ci LEFT JOIN pdv_cupom c ON c.id = ci.idpdv_cupom
-- WHERE OBRIGATÓRIOS
WHERE ci.status = 'SOLD' -- Filtra apenas os itens que foram vendidos no cupom, os itens cancelados não devem ser contabilizados no relatório
and (c.STATUS = 'SOLD' OR c.STATUS = 'ERROR_SYNC') -- Filtra apenas os cupoms que foram vendidos e vendidos sem confirmação do ECF.
-- WHERE de Filtros
-- and c.date >= '2015-03-01 00:00:00' and c.date <= '2015-03-31 23:59:59' -- Filtra por data
GROUP BY icmstype, ci.icmstaxratio -- Agrupa a soma pelo tipo de tributação e aliquota
ORDER BY tot desc; -- Organiza do maior faturamento para o menor.


Exibições

  • Listagem:
    • Tipo de Tributação - Define o tipo de tributação. Enum: ICMSTYPE.
    • Aliquota - Alíquota da tributação (quando existente). Valor em porcentagem.
    • Total - Faturamento total sumarizado.
    • Participação - Valor da representação do valor da coluna Total em relação a soma de todos os itens, utilizando o BISPercentualBarComponent no estilo GREEN para exibição da informação.
  • Gráfico Pizza:
    • Exibe o conteúdo como fatias em um gráfico de pizza.
  • Gráfico Colunas:
    • Exibe o conteúdo como colunas em um gráfico de colunas.


Filtros do Relatório

  • Período - Campo com data inicial e final para filtrar o periodo


por Código de Produto

O relatório de Faturamento por Código de Produto visa exibir as informações das vendas agrupadas pelo código do produto.

  • Caminho do Relatório - Caixa > Relatórios > Vendas > por Código de Item

Relacionamentos

  • PDVCupomItemVO
    • Left Join: ItemCodeVO - usado pois algum código de item que tenha sido vendido, mas tenha sido excluído vai constar na base de dados com o relacionamento 'null'. Sem o Left Join esses registros seriam removidos do resultado.
      • Left Join: ItemCategoryVO - usado como left join pelo mesmo motivo do ItemCodeVO. Como o relacionamento é feito com uma entidade que esta sendo anexada com LeftJoin, se este também não for registros seriam removidos do resultado.

por Categoria de Itens

Relatório para exibir o faturamento agrupado por Categoria e subCategorias. O tela deve exibir os dados de forma hierarquica, similar a própria janela de categoria de itens, só que com as colunas mencionadas abaixo.

Sugiro que o "bean" que trará os dados do CRUD para a tela faça uso do próprio ItemCategoryVO, e use já os métodos existentes para recuperar os nós conforme necessário, e em seguida, de acordo com os objetos recuperados, seja carregado diretamente do banco de dados os dados necessários sobre o faturamente e incorporados no Bean. Dessa forma poderemos manter o LazyLoad na tela e obter os totais a partir de diversas consultas rápidas no banco.

  • Caminho do Relatório: Caixa / PDV > Faturamento > por Categorias de Itens
  • Campos de Filtro
    • Período - Campo de filtro por período com precisão de minutos.
    • Categoria Raiz - Usar o ComboBoxHierarchical (vou adapta-lo para single selection também) para permitir que o usuário escolha um único ramo para visualizar. A categoria escolhida deverá ser o único elemento raiz do container, e apenas ele e seus filhos passarão a ser exibidos e calculados. Se este filtro for aplicado diretamente na obtenção dos ItemCategoryVOs raíz, o restante do código deve permanecer sem alterações.
  • Campos de Agrupamento
    • Agrupar - ComboBox com opções de agrupamento dos dados filtrados:
      • Dia/Mês/Ano - Permite o agrupamento do Faturamento por dia/mês/ano.
      • Mês/Ano - Permite o agrupamento do faturamento por mês/ano.
      • Dias da Semana - Permite o agrupamento do Faturamento por dias da semana (Dom-Sáb).
  • Validações
    • Caso o agrupamento esteja definido como 'dia/mês/ano' não permitir que o período extrapole 90 dias.
    • Caso o agrupamento esteja definido como 'mês/ano' não permitir que o período extrapole 60 meses.
  • Colunas do Relatório
    • Nome da Categoria - Nome da categoria. Alinhamento: Esquerda.
    • Tipo da Categoria - Tipo da categoria. Alinhamento: Centralizado. Collapsed.
    • Colunas Agrupaveis - As colunas abaixo quando algum agrupamento estiver definido devem se repetir e indicar o título do agrupamento. Por exemplo, se agrupada por mês, indicarem o mês: "Jan", "Fev", etc. (O segredo pra fazer isso será usar o novo componente BISGrid ;) Tão novo que nem existe ainda)
      • Faturamento - Faturamento dos itens apenas nesta categoria. Formato: Currency. Alinhamento direita.
      • Faturamento Grupo - Faturamento dos itens nesta categoria e nas categorias filhas. Formato: Currency. Alinhamento Direita.
      •  % Grupo - Participação do Faturamento em relação ao valor do 'Faturamento Grupo' do objeto pai. Usar a barra percentual. Deixa esta coluna em branco, nem colocar a barra com o 0% caso o valor do Faturamento seja zero.
      •  % Faturamento - Participação do Faturamento total. Nesta coluna não usar a Barra Percentual. Formato: Porcentagem com duas casas decimais. Alinhamento: Direita.
  • Menus de Contexto
    • "Visualizar Faturamento de Itens" - Este menu deve abrir o relatório de venda de itens (feito anteriormente) transportando o filtro de período, e definindo a categoria no filtro de categoria. A seleção do Menu também deve ser alterada, para deixar evidente que o cliente mudou de relatório, bem como permitir que ele volte para o relatório anterior novamente escolhendo-o no menu.
      Como fazer essa definição no menu eu não sei ainda... penso em passar a própria InfoBoardWindow como parametro ao instanciar as janelas internas. Fazer esse o novo padrão ao invés do construtor vazio. E obviamente na InfoBoardWindow Fazer alguns métodos que permitam a escolha do menu através de algum ID, talvez a própria classe da Window e deixa a InfoWindow iterar e encontrar o menu correto no container. Só seria bom algo padronizado assim pois acabará sendo comum termos links de um relatório para outro para "abrir" dados de um em outro...
  • Gráfico Stackedbar - http://demo.vaadin.com/charts/#StackedBar
    • O Eixo Vertical (Apples, oranges, Pears, etc. do exemplo) serão os dados agrupados. Por exemplo, no agrupamento por mês serão os meses disponíveis de acordo com o filtro: "Fevereiro", "Março", "Abril", etc.
    • As séries de dados (John, Jane e Joe do exemplo) serão as categorias raiz e os valores serão os valores da coluna 'Faturamento Grupo'. As categorias filhas não serão expostas no gráfico.
      Note que caso o filtro da 'Categoria Raiz' esteja definido, temos apenas um único objeto raiz. Neste caso a série de dados deverá ser esta única categoria raiz, utilizando o valor da coluna 'Faturamento', mais todas as suas categorias filha com o valor da coluna 'Faturamento do Grupo'.
  • Gráfico DualAxes - http://demo.vaadin.com/charts/#DualAxesLineAndColumn
    • A ideia desse gráfico é exibir as barras sem sobreposição, permitindo a comparação entre elas, além do total na linha (segundo eixo). Embora tenha passado o link do dual axes para entender a questão da linha total, a parte das barras deve ter um visual mais parecido com http://demo.vaadin.com/charts/#ToggledSeriesVisibility por conta da quantidade de séries de valores.
    • Assim como anterior, o eixo (agora horizontal) será a informação de acordo com o tipo do agrupamento.
    • Cada barra/serie de dados será uma categoria raiz. Ou no caso do filtro de 'Categoria Pai' estar definido as séries incluem a própria categoria pai além das suas categorias filhas. Usando as mesmas colunas de valores como no gráfico anterior.
    • O segundo eixo, responsável por gerar a linha, será uma linha totalizadora. Os valores de cada série será a soma de todos os valores das barras. Quando há o filtro de categoria pai, e estiver tudo correto obviamente, o valor da soma já será o valor da coluna 'Faturamento Grupo' do objeto pai (usado na primeira série de dados).

SQL Guia

O SQL abaixo pode servir de guia para a criação do método de consulta no DAO. Note que o código tem linhas comentadas no bloco de select e no groupby que fazem "pares" para realizar as consultas dos Agrupamentos solicitados no relatório. Ao descomentar uma das linhas de SELECT a linha equivalente de GROUP BY também deve ser descomentada para que o SQL funcione. Assim como a outra linha deverá ser comentada novamente.


SELECT 
-- day(c.date), month(c.date), year(c.date), -- Agrupamento Diário
month(c.date), year(c.date), -- Agrupamento por mês
-- weekday(c.date), -- Agrupamento pod dia da semana
icat.id, icat.name, sum(ci.total)
FROM pdv_cupomitem ci LEFT JOIN item_itemcodes ic on ic.code = ci.code LEFT JOIN item_itemcategory icat on icat.id = ic.iditemcategory, pdv_cupom c 
WHERE ci.idpdv_cupom = c.id and ci.status = 'SOLD' AND (c.STATUS = 'SOLD' OR c.STATUS = 'ERROR_SYNC') -- where obrigatória
-- AND c.DATE >= '2015-06-22 00:00:00' AND c.DATE <= '2015-07-22 23:59:59' -- Filtra por periodo
-- AND icat.id = 225 -- filtra pelo ID da categoria
GROUP BY icat.id
-- , day(c.date), month(c.date), year(c.date) -- Agrupamento diário
, month(c.date), year(c.date) -- Agrupamento por mês
-- , weekday(c.date) -- Agrupamento por dia da semana
;


Stop 256.png
Filtro de Categoria Pai
Embora "meio que óbvio" as vezes passa despercebido. Quando o texto acima fala que o category pai esteja definido, significa que o filtro está atualmente em uso. Não pode ser usado o campo do filtro para checar essa condição, uma vez que o usuário pode alterar esse valor e não clicar em procurar. Fazendo com que os dados existentes não sejam compatíveis com o valor do filtro. Gerar gráficos com base em dados "dessincronizados" acabariam com um relatório confuso e sem sentido.
Note 64.png
Vendas sem Categorias
Note que o SQL faz 2 Left Joins, e como a venda não "armazena" qual era a categoria do produto na época da venda, essa associação de venda por categoria é resgatada fazendo um join com o código da venda e a categoria em que o código atual está. Caso o código ou a categoria tenha sido excluído, o resultado poderá apresentar um categoria com "ID nulo", o que indica a impossibilidade de associar a venda à alguma categoria.

Quando isso correr o sistema deve "simular" uma categoria chamada "Sem Categoria" para apresentar esses valores. Caso contrário os totais declarados como faturado neste relatório podem não bater com outros do sistema.


Note 64.png
Criação dos Beans e IDs
Como o relatório é criado baseado em um objeto existente, o ItemCategoryVO, sugiro que o Bean que carregará todos copie as informações a serem utilizadas do ItemCategoryVO para ele. Como id, nome da categoria e tipo da categoria. Além dos outros campos que serão obtidos a partir do SQL de somatória de agrupamento do faturamento.

Para a categoria a ser simulada dos "Sem Categoria", sugiro que jogue o id -1. Por ser um Long funciona normalmente nas telas e UIs, e com certeza não fará conflito com outros objetos.


Note 64.png
Aproveitamento de Query e Performance
Nos meus testes com o SQL, usando o SQL oferecido acima, usando o filtro de ID da categoria a query levou 4s, sem o filtro de categoria a mesma query levou 4,5s. Pelo volume de categorias, é melhor realizar sempre a consulta SEM o id da categoria. Assim receberá todos os dados necessários, e provavelmente até mais do que necessitaria, mas evitaria ter que fazer múltiplas queries em que já a partir da segunda temos quase o dobro do tempo de consulta.

Assim, por conta de salvar a performance do banco, é melhor obter toda a query e salvar os valores obtidos nos beans de acordo. Seguindo essa lógico não teremos LazyLoad da consulta do banco de dados e retornaremos todos os Beans totalmente preenchidos. do CRUD para a tela. A manipulação dos dados, mesmo que feitos pelo java e em memória devem ser tratadas ainda no DAO. O acredito que o tempo não deva nem chegar ao tempo de outra query, mas se ficar ruim podemos facilmente implementar um paralelismo para esse tipo de informação, provavelmente usando os novos recursos do Java 8 (que eu não sei usar ainda rs).

Os dados a serem gerados/calculados são a somatória dos itens filhos, que não são geradas pelo SQL e depois do total calculado, o calculo das porcentagens de participação.


por Forma de Pagamento

Este relatório tem a finalidade de fornecer informações sobre as vendas em torno da forma de pagamento utilizada pelo usuário.

  • Caminho do Relatório: Caixa / PDV > Vendas > por Forma de Pagamento
  • Condições (WHERE) Fixos:
    • Status do Pagamento = 'DONE' - Apenas os pagamentos com o status DONE devem ser considerados. É comum ter alguns pagamentos com o status 'CANCELED', principalmente durante o uso de TEF quando um pagamento é estornado. Assim esses pagamentos não foram recebidos e não devem fazer parte desse relatório.
  • Campos de Filtro:
    • Período - Campo de filtro por período com precisão de minutos.
      Validações - Não permitir um período de consulta maior que 2 anos.
    • Status - Campo ComboBoxMultiSelect com as opções do status possíveis: 'SOLD', 'ERROR', 'ERROR_SYNC'. Cada um que seja selecionado deverá fazer parte do resultado. Inicialmente deixar selecionado apenas o 'SOLD'. Deixar este filtro escondido dentro de "Mais Filtros".
    • Reimpressões - ComboMultiSelect com duas opções: 'Vendas' e 'Reimpressões'. inicialmente apenas Vendas deve estar selecionados, fazendo com que as Reimpressões não apareçam no relatório.
  • Exibição dos Dados:
    • Agrupado Diariamente - Permite agrupar os valores por de acordo com seu dia, mês e ano.
    • Agrupado Mensalmente - Permite agrupar os valores por de acordo com seu mês e ano.
    • Sumarizado por Dias da Semana - Permite acumular os valores de acordo com o dia da semana (Dom-Sáb).
    • Sumarizado por Horas do Dia - Permite acumular os valores de acordo com a hora do dia.
  • Colunas do Relatório:
    • Colunas Fixas:
      • Forma de Pagamento - Exibe o nome da forma de pagamento. O mesmo nome exibido para o operador de caixa durante o uso do PDV.
      • Tipo - Exibe o tipo da forma de pagamento. O tipo é uma Enum do sistema, usara para 'enumerar' as diferentes formar de pagamento que o sistema consegue tratar.
    • Colunas Distinguíveis:
      • Sub-Tipo - Essa coluna não existe no banco de dados. Será criada a partir de valores de diferentes colunas dependendo da forma de pagamento, evitando de termos um monte de colunas que só serão usada para algumas formas de pagamentos. Sendo assim, essa coluna deve receber o conteúdo das seguintes colunas:
        Recomendo que no Bean, para transportar os valores, seja declarado como Object. Garantindo assim que qualquer valor possa ser definido, já que não teremos um tipo único. Como quando uma coluna está definida as outra obrigatoriamente são nulas, não é necessário se preocupar em verificar o tipo da forma de pagamento. Simplesmente verifique, se alguma das colunas não for nula use seu valor. Esse resultado pode obtido diretamente no SQL com a função coalesce(). (Só não sei se é uma boa ideia para tratar os tipos de cada coluna, já que uma é Enum e outra String).
        • Tipo do Convênio - contract_type - esta coluna só é populada quando o tipo do pagamento é convênio. Com uma Enum indicando se é de Clientes ou de Funcionários.
        • Rede Adquirente - tef_acquirernet - esta coluna só é populada quando o tipo de pagamento é TEF. Apresenta um valor em String recebida do sistema do TEF que indica qual foi a rede usada para a transação. Ex: 'Rede', 'Cielo' (ainda chamada de Visanet no sistema), 'GetNet', 'Elavon', etc.
      • Nome - Coluna utilizada para exibir uma identificação do pagamento. Similar à coluna Sub-Tipo, esta coluna não existe no banco, e será utilizada para exibir o valor de diferentes colunas de acordo com o tipo de pagamento.
        • Nome do Conveniado - contract_name - Quando o pagamento é Convênio, esta coluna apresenta o nome do convênio.
        • Bandeira do Cartão - tec_cardname - Quando o pagamento é TEF, esta coluna apresenta o nome retornado pelo sistema do TEF para indicar qual era a bandeira do cartão utilizado.
    • Colunas Adicionais:
      • Contagem - Indica o total de registros que foram sumarizados.
      • %Contagem - Indica a participação em relação à contagem total dos registros exibidos.
      • Recebido - Indica o valor total recebido na forma de pagamento.
      • %Recebido - Indica a participação em relação ao valor total recebido na forma de pagamento.


SELECT
-- ==> Colunas Fixas
cp.name, cp.type
-- ==> Colunas Distinguíveis
-- , cp.contract_type, cp.tef_acquirernet -- Usado quando o usuário escolher que quer Distinguir a coluna "Sub-Tipo"
-- , cp.contract_name, cp.tef_cardname -- Usado quando o usuário escolher que quer Distinguir a coluna "Nome"
-- ==> Colunas Adicionais
, COUNT(*) -- Contagem
, sum(cp.value) -- Recebido
-- ==> Colunas de GroupBy
-- , c.date -- Usada para os "Agrupado Diariamente"e "Agrupado Mensal" para extrair a data
-- , weekday(c.date) -- Usado no "Sumarizado por Dia Da Semana" para extratir o dia da semana
-- , hour(c.date) -- Usado no "Sumarizado por Horas do Dia"
FROM pdv_cupom c, pdv_cupompayments cp
WHERE c.id = cp.idpdv_cupom AND cp.status = 'DONE' -- Join e Where obrigatória
AND c.idpdv_cupom IS NULL -- Filtro de Reimpressão (Apenas Vendas)
-- AND c.idpdv_cupom IS NOT NULL -- Filtro de Reimpressão (Apenas Reimpressões)
AND c.status IN ('SOLD', 'ERROR_SYNC') -- Filtro por tipo de fechamento (Status)
AND c.date >= '2015-08-01 00:00:00' AND c.date <= '2015-08-31 23:59:59' -- Filtro por periodo
GROUP BY cp.name, cp.type -- Group By Obrigatório
-- ==> Colunas Distinguíveis
-- , cp.contract_type, cp.tef_acquirernet -- Usado quando o usuário escolher que quer Distinguir a coluna "Sub-Tipo"
-- , cp.contract_name, cp.tef_cardname -- Usado quando o usuário escolher que quer Distinguir a coluna "Nome"
-- , cp.contract_name, cp.name, cp.tef_acquirernet, cp.tef_cardname -- Usado quando o usuário escolher que quer Distinguir a coluna "Nome"
-- ==> Colunas Exibição dos Dados
-- , day(c.date), month(c.date), year(c.date) -- Agrupado Diariamente
-- , month(c.date), year(c.date) -- Agrupado Mensalmente
-- , weekday(c.DATE) -- Sumarizado por Dia da Semana
-- , hour(c.date) -- Sumarizado por Horas do Dia
;

Registro "Fake": Troco

Filtrando os dados pelos pagamentos do cupom obtemos os valores brutos dos pagamentos efetuados. Não obtemos o valor real do faturamento ou que ficou no caixa. Isso porque as formas de pagamentos podem gerar troco. O que indica que recebemos o tal valor, mas é possível que tenhamos devolvido algum troco. Não há um meio efetivo de já descontar o troco em cada forma de pagamento uma vez que mesmo durante a venda não é possível definir isso com precisão. Por exemplo, em uma compra com 4 amigos são realizados pagamentos separados sendo 2 em dinheiro, 1 em cartão e outro em cheque. No final o valor a ser devolvido seja de 10 reais. De qual pagamento é o troco? Múltiplos pagamentos podem aceitar troco, outros tantos podem aceitar só emissão de contra-vales. Não é trivial fazer essa distinção. Assim o sistema simplesmente guarda o "troco" da compra referenciando o cupom e não algum pagamento do cupom.

Como o total exibido pelo relatório acima não contempla os descontos de troco devolvidos, vamos criar mais dois registros no relatório para indicar o valor de Troco em Dinheiro e Troco em Contra-Vale. Para levantar esses valores corretamente será necessário realizar uma outra consulta ao banco de dados. Lembrando que todos os filtros e group by que forem usados na primeira consulta devem fazer parte da segunda consulta. Apenas os da exibição de dados, os Distinguíveis devem ser ignorados aqui.


Ao gerar os registros devem colocar nas colunas os seguintes valores:

  • Troco em Dinheiro:
    • Forma de Pagamento - Usar "Troco em Dinheiro"
    • Tipo - Usar "Troco"
  • Troco em Contra-Vale:
    • Forma de Pagamento - Usar "Troco em Contra-Vale"
    • Tipo - Usar "Troco"

Para as Colunas Distinguíveis deixar todas em branco, já as colunas do Agrupamento/Sumarização usar o resultado do banco, afinal utilizando os mesmos critérios de group by devemos ter valores para cada uma das colunas.


Note 64.png
Troco com Valor Negativo
Embora no banco os valores estejam positivos, o ideal para o relatório será exibir o valor em negativo. Primeiro para deixar 'óbvio' que o valor tem um fluxo de saída do caixa e não de entrada. Segundo porque principalmente no relatório em tela, quando o usuário seleciona os registros esses valores negativos ao serem "somados" pelo Footer serão na verdade descontados do total.

Facilitando assim que o usuário possa verificar o valor "líquido" do recebimento apenas selecionando os registros.


SQL para Obter os trocos retornados aos clientes, utilizando os mesmos GroupBy da consulta de recebimentos:

SELECT
-- Colunas Adicionais
COUNT(*) -- Contagem
, sum(c.moneychange) -- Valor do Troco em Dinheiro (Coluna "Recebido")
, sum(cct.value) -- Valor do Troco em Tíquete (Coluna "Recebido")
-- Colunas de GroupBy
-- , c.date -- Usada para os "Agrupado Diariamente"e "Agrupado Mensal" para extrair a data
-- , weekday(c.date) -- Usado no "Sumarizado por Dia Da Semana" para extratir o dia da semana
-- , hour(c.date) -- Usado no "Sumarizado por Horas do Dia"
FROM pdv_cupom c LEFT JOIN pdv_cupomchangeticket cct ON c.id = cct.idpdv_cupom
WHERE -- Join e Where obrigatória
c.idpdv_cupom IS NULL -- Filtro de Reimpressão (Apenas Vendas)
-- AND c.idpdv_cupom IS NOT NULL -- Filtro de Reimpressão (Apenas Reimpressões)
AND c.status IN ('SOLD', 'ERROR_SYNC') -- Filtro por tipo de fechamento (Status)
AND c.date >= '2015-08-01 00:00:00' AND c.date <= '2015-08-31 23:59:59' -- Filtro por periodo
-- GROUP BY
-- , cp.contract_type, cp.tef_acquirernet -- Usado quando o usuário escolher que quer Distinguir a coluna "Sub-Tipo"
-- , cp.contract_name, cp.tef_cardname -- Usado quando o usuário escolher que quer Distinguir a coluna "Nome"
-- , cp.contract_name, cp.name, cp.tef_acquirernet, cp.tef_cardname -- Usado quando o usuário escolher que quer Distinguir a coluna "Nome"

-- , day(c.date), month(c.date), year(c.date) -- Agrupado Diariamente
-- , month(c.date), year(c.date) -- Agrupado Mensalmente
-- , weekday(c.DATE) -- Sumarizado por Dia da Semana
-- , hour(c.date) -- Sumarizado por Horas do Dia
;