Entregar uma reconciliação de vendas

SQLiteBeginner
Pratique Agora

Introdução

A PAPER & PIER precisa de uma entrega de fechamento: uma linha CSV por pedido e um resumo para cada banca. As duas bancas podem ter diferença geral zero enquanto alguns pedidos individuais continuam com pagamento insuficiente ou em excesso. A entrega deve manter essas exceções, em vez de anunciar sucesso a partir de um único total geral.

Você salvará uma consulta SQL reutilizável e usará o exportador fornecido para criar arquivos CSV reais a partir dos resultados efetivos dela. Primeiro, conclua Manter o total correto de cada pedido ou conheça SUM/GROUP BY, LEFT JOIN, COALESCE e o problema de duplicação em junções. Expressões de tabela comuns e classificação de status são explicadas aqui. Use Terminal durante todo o lab; esta unidade tem seu próprio registro de vendas independente com seis pedidos.

Mantenha inalteradas as quatro tabelas de origem e as ferramentas fornecidas. Altere apenas query.sql e arquivos em reports/; não adicione objetos ao banco de dados. Todos os valores monetários são centavos inteiros. Esta é uma reconciliação simplificada de recebimentos, sem impostos nem liquidação por provedores de pagamento. O exportador trata da codificação CSV e dos cálculos do resumo; você não precisa escrever Python.

Salvar uma consulta reutilizável por pedido

Nesta etapa, você calculará os recebimentos líquidos, a diferença e o status de cada pedido usando uma única instrução SQL autossuficiente. Comece em Terminal:

cd /home/labex/project/sales-desk
sqlite3 -header -column ledger.db "SELECT * FROM orders ORDER BY order_id;"

Este registro de vendas independente tem A1 a A6, incluindo dois pedidos com o rótulo Alex Kim e rótulos contendo vírgulas ou caracteres de aspas. Os IDs distinguem os pedidos; preserve os rótulos exatamente como armazenados. Analise cada lista de fatos antes de derivar os saldos:

sqlite3 -header -column ledger.db "SELECT * FROM items ORDER BY item_id; SELECT * FROM payments ORDER BY payment_id; SELECT * FROM refunds ORDER BY refund_id;"

Como antes, apenas pagamentos e reembolsos succeeded contam. Dois eventos bem-sucedidos com valores iguais contam ambos. Fatos elegíveis ausentes contribuem com zero. Os cálculos são:

Saída Definição
due_cents Soma da quantidade de itens multiplicada pelo preço unitário em centavos
paid_cents Soma dos centavos de pagamentos bem-sucedidos
refunded_cents Soma dos centavos de reembolsos bem-sucedidos
net_cents Pago menos reembolsado
difference_cents Líquido menos devido
status settled para diferença zero, underpaid para negativa, overpaid para positiva

Uma expressão de tabela comum (CTE) é um SELECT intermediário nomeado, disponível apenas dentro de uma instrução. WITH name AS (...) a introduz; vírgulas separam várias CTEs. Ela oferece o mesmo raciocínio de separar fatos das views anteriores, sem criar objetos no banco de dados nem depender de arquivos de outro lab.

Primeiro, três CTEs agregam cada fato independentemente. Depois, totals começa pelos pedidos e preenche totais ausentes com zero. balances calcula o líquido e a diferença a partir desses totais nomeados. O último SELECT usa CASE WHEN ... THEN ... ELSE ... END para escolher o status pelo sinal da diferença. Essas camadas tornam os aliases disponíveis para o SELECT seguinte, em vez de depender do alias de outra expressão na mesma lista de SELECT. SELECT * na camada intermediária de saldos carrega os totais adiante; o SELECT final lista explicitamente exatamente nove colunas na ordem exigida.

Salve a instrução completa. cat > substitui o arquivo; o here-document entre aspas preserva o SQL literalmente. Inclua a linha final SQL:

cat > query.sql <<'SQL'
WITH
item_totals AS (
  SELECT order_id, SUM(quantity * unit_cents) AS due_cents
  FROM items GROUP BY order_id
),
payment_totals AS (
  SELECT order_id, SUM(amount_cents) AS paid_cents
  FROM payments WHERE status = 'succeeded' GROUP BY order_id
),
refund_totals AS (
  SELECT order_id, SUM(amount_cents) AS refunded_cents
  FROM refunds WHERE status = 'succeeded' GROUP BY order_id
),
totals AS (
  SELECT o.order_id, o.customer, o.stall,
         COALESCE(i.due_cents, 0) AS due_cents,
         COALESCE(p.paid_cents, 0) AS paid_cents,
         COALESCE(r.refunded_cents, 0) AS refunded_cents
  FROM orders AS o
  LEFT JOIN item_totals AS i ON i.order_id = o.order_id
  LEFT JOIN payment_totals AS p ON p.order_id = o.order_id
  LEFT JOIN refund_totals AS r ON r.order_id = o.order_id
),
balances AS (
  SELECT *, paid_cents - refunded_cents AS net_cents,
         paid_cents - refunded_cents - due_cents AS difference_cents
  FROM totals
)
SELECT order_id, customer, stall, due_cents, paid_cents,
       refunded_cents, net_cents, difference_cents,
       CASE WHEN difference_cents = 0 THEN 'settled'
            WHEN difference_cents < 0 THEN 'underpaid'
            ELSE 'overpaid' END AS status
FROM balances
ORDER BY order_id;
SQL

Não inclua comandos do SQLite iniciados por ponto nem comandos do shell em query.sql: o exportador executa uma instrução SELECT/WITH comum. Ela deve funcionar a partir das quatro tabelas de origem em outro registro de vendas com o mesmo esquema, incluindo IDs de pedidos e valores diferentes. Linhas de resultado fixas no código ou views do banco de dados não forneceriam essa consulta reutilizável.

< envia este arquivo à CLI do SQLite para uma primeira execução legível:

sqlite3 -header -column ledger.db < query.sql

Interprete as linhas reais, não apenas um total. Os saldos esperados são:

Pedido Devido Líquido Diferença Status
A1 800 800 0 settled
A2 250 0 -250 underpaid
A3 500 500 0 settled
A4 200 250 50 overpaid
A5 600 850 250 overpaid
A6 400 350 -50 underpaid

A2 permanece visível sem um pagamento. A3 mantém os dois reembolsos bem-sucedidos de 50 centavos. O reembolso com falha de A6 não reduz seus recebimentos líquidos. A1 e A2 permanecem separados, apesar de compartilharem um rótulo de cliente. As cinco colunas de centavos contêm inteiros reais, incluindo diferenças negativas; não as transforme em strings de moeda formatadas.

Para um erro de sintaxe, leia o local informado e substitua query.sql pelo bloco completo corrigido antes de executar novamente. Para um caminho incorreto, retorne usando o cd absoluto acima e use ls para encontrar query.sql. Não esvazie o registro de vendas, não modifique valores para forçar um equilíbrio nem adicione tabelas de resultados armazenados.

Entregar e consultar os arquivos CSV reais

Nesta etapa, você exportará a consulta salva e consultará seus arquivos reais de entrega, incluindo rótulos que exigem aspas no CSV. Comece em Terminal, em /home/labex/project/sales-desk.

./export é um executável fornecido no diretório atual. Ele lê query.sql, executa essa instrução sem alterações sobre ledger.db usando uma conexão somente leitura e grava as linhas efetivamente retornadas em reports/orders.csv. Ele deriva reports/summary.csv apenas dessas linhas retornadas. Não corrige uma consulta errada nem adiciona pedidos ausentes. O diretório preparado reports é o local dos dois arquivos:

./export

A mensagem informa seis linhas de consulta e nomeia os dois arquivos de saída. O leitor abaixo mostrará dois resumos de banca. Uma exportação bem-sucedida significa que os arquivos foram gravados, não que seus cálculos estão corretos. Se a consulta estiver ausente ou inválida, o exportador informará uma falha. Arquivos antigos de uma exportação anterior bem-sucedida ainda podem estar presentes: corrija a consulta salva e execute ./export novamente antes de considerá-los atuais.

./review é o leitor CSV fornecido. Ele lê os arquivos salvos sem consultar o banco de dados e exibe cada linha interpretada como um registro com campos identificados. Uma saída ausente ou alterada continua ausente ou alterada:

./review

Os seis registros de pedido devem corresponder à tabela da etapa anterior, com os rótulos originais exatos de cliente e banca. As colunas do resumo são:

Coluna Significado
stall Rótulo exato da banca
order_count Número de pedidos retornados para esta banca
due_cents, net_cents, difference_cents Somas das colunas correspondentes dos pedidos
settled_count, underpaid_count, overpaid_count Contagens dos status correspondentes dos pedidos

North, Pier tem três pedidos: devido 1650, líquido 1650, diferença 0 e um de cada status. South também tem três: devido 1100, líquido 1100, diferença 0 e um de cada status. Nas duas bancas, há quatro pedidos não quitados, embora a diferença geral seja zero. Pagamentos em excesso e insuficientes se anulam em um total; eles não quitam o pedido de outra pessoa.

CSV significa valores separados por vírgulas, mas uma vírgula dentro de um rótulo faz parte de um único campo. O CSV padrão envolve esses campos com aspas duplas e representa uma aspa dentro de um campo com dois caracteres de aspas. cat exibe os bytes brutos do arquivo como texto; compare essa codificação com os rótulos corretamente interpretados por ./review:

cat reports/orders.csv
cat reports/summary.csv

No arquivo de pedidos, o rótulo de cliente Paper, "Pine" é codificado como "Paper, ""Pine"""; North, Pier é codificado como "North, Pier". Esses caracteres extras de aspas são sintaxe CSV, não alterações nos rótulos. Dividir cada linha em cada vírgula corromperia esses registros; o gravador e o leitor fornecidos tratam do formato.

Se um CSV estiver ausente, execute ./export novamente após corrigir query.sql e leia os dois arquivos de novo. Não edite manualmente um resumo para ocultar pedidos ausentes, não substitua o exportador/leitor nem altere registros de origem. Sua entrega consiste no query.sql reutilizável e nos dois arquivos CSV reais. Os arquivos devem conter uma linha para cada pedido de origem, as nove colunas exatas dos pedidos e as oito colunas de resumo mostradas acima. A ordem das linhas não é o resultado de negócio; as identidades, os valores, os rótulos e seus relacionamentos são.

Resumo

Você criou uma consulta CTE reutilizável, classificou cada pedido a partir dos recebimentos líquidos menos o valor de seus itens e entregou dois arquivos CSV reais. Interpretou rótulos corretamente delimitados por aspas e distinguiu totais equilibrados das bancas de pedidos individualmente quitados.