Skip to content

Módulo 5 - Folha de Pagamento & Retenções (payroll)

Módulo 5: Folha de Pagamento & Retenções (payroll)

Section titled “Módulo 5: Folha de Pagamento & Retenções (payroll)”

Falhas inesperadas ao gravar lançamentos ou criar/fechar uma execução de folha passam por atomicServerError. O painel administrativo conserva o diagnóstico sanitizado por ação; nenhuma das quatro experiências recebe stack, SQL ou mensagem interna do D1. Regras de fechamento e validações de negócio permanecem respostas específicas.

O módulo Payroll automatiza o fechamento financeiro quinzenal ou mensal das funcionárias de limpeza, aplicando bonificações de pontualidade e retenção de fundos de emergência (Cash Reserve).

A geração prepara o período em lote: serviços, IDs já pagos, cancelamentos posteriores e lucro distribuível são indexados uma única vez antes dos lançamentos individuais. Assim, aumentar a equipe não repete a leitura integral dos mesmos atendimentos para cada profissional. As regras de elegibilidade e os valores permanecem idênticos — incluindo custo manual de cancelamento, divisão por equipe, Cash in Hand, adiantamentos, participação nos lucros e proteção contra pagamento duplicado — em navegador, PWA e shells nativos.

A persistência da geração também é indivisível. POST /api/atomic/payrollGenerate recebe o ciclo, todos os holerites DRAFT e o depósito automático da reserva; um único batch D1 grava ou rejeita o conjunto inteiro. Um claim interno gerado com Web Crypto fica temporariamente no ciclo: somente o batch que criou esse ciclo pode inserir seus holerites e reserva. Assim, um retry concorrente ou payload divergente nunca anexa dependentes ao ciclo vencedor. Retry com IDs e conteúdo equivalentes devolve sucesso idempotente; qualquer diferença recebe 409. Colisão de holerite/reserva provoca rollback integral. O saldo, autor e horário da reserva e do ciclo são derivados pelo servidor, eliminando snapshots, identidade e relógio do dispositivo. A resposta autoritativa atualiza o cliente e a reconciliação por ID evita duplicar stores quando uma resposta perdida é recuperada. Uma trava síncrona bloqueia toque duplo, CFButton anuncia carregamento e stores, log e toast de sucesso só avançam depois da confirmação integral — não existem mais estados Partial Payroll ou Run Not Recorded.

O fechamento usa uma trava síncrona antes do primeiro await, compartilhada por confirmação, geração suplementar e saída do diálogo. O Worker repete a regra decisiva no UPDATE: somente um ciclo OPEN sem tarefas abertas dentro de periodStart/periodEnd pode ser fechado. A primeira decisão concorrente vence; as demais recebem 409 sem reescrever assinatura ou horário. closedBy é resolvido pela sessão/registro da profissional e closedAt pelo relógio do D1, portanto o aparelho não pode forjar a auditoria. Datas autoritativas retornadas diretamente à interface usam ISO 8601 UTC com sufixo Z, evitando interpretações diferentes entre Safari/iOS e Chromium/Android/desktop. A resposta autoritativa alimenta store e log. Falha retorna false, mantém a folha aberta com os dados de verificação e permite retry. Loading e trava são liberados em finally, portanto uma exceção não congela a interface e dois toques no mesmo frame não duplicam o fechamento. A criação individual de ciclo é somente insert: colisão de ID não pode alterar rótulo, totais ou um período já fechado; autor e horário também são derivados no servidor.

A aprovação e a liquidação também são confirmações autoritativas. POST /api/atomic/payrollEntries cria somente DRAFT e recusa colisão de ID sem sobrescrever valores; PUT aceita exclusivamente DRAFT → APPROVED → PAID, exigindo o estado esperado no próprio UPDATE. Regressão, salto ou uma segunda decisão concorrente retorna 409 com o status e o valor de adiantamentos persistidos para a tela se reconciliar. Cada holerite possui ownership síncrono contra toque duplo, mantém o botão desabilitado e ocupado enquanto salva e só altera o estado local ou anuncia Fiscal Period Sealed depois da aceitação do Worker. Ao marcar PAID, um token criptográfico limita o mesmo batch transacional do D1 à decisão vencedora: ele grava o status, liquida todos os adiantamentos pendentes daquela profissional e reconcilia advances_amount; uma falha ou disputa não pode deixar apenas parte desses dados confirmada. O cache de profissionais é revalidado ao terminar, mantendo desktop, PWA, Android e iOS na mesma fonte de verdade. Os serviços incluídos já ficam selados por includedRequestIds; não recebem mais a versão artificial 9999.


💵 1. Componentes do Pagamento da Funcionária

Section titled “💵 1. Componentes do Pagamento da Funcionária”

O valor líquido repassado a uma funcionária em cada período de pagamento é calculado como:

$$\text{Repasse Líquido} = \text{Total de Horas Trabalhadas} \times \text{Taxa/Hora} + \text{Bônus} - \text{Retenção de Reserva} - \text{Deduções em Dinheiro (Cash in Hand)} - \text{Adiantamentos}$$

  • Bônus de Avaliação 5 Estrelas: Bonificação automática quando o cliente deixa avaliação máxima.
  • Bônus de Pontualidade: Aplicado quando 100% dos check-ins GPS foram realizados no horário correto.
  • Cash Reserve (Reserva de Caixa): Retenção percentual temporária para cobrir eventuais avarias acidentais em imóveis. O saldo fica visível para a funcionária e é liberado periodicamente.

Alocações manuais da reserva são exclusivas de administradores tanto na UI quanto em POST /api/atomic/cashReserve. O cliente envia somente a intenção de saque; um único INSERT ... SELECT calcula o saldo a partir do último lançamento persistido e recusa overdraft com 409, sem confiar em balanceAfter, autor ou horário do dispositivo. A busca do saldo usa datetime(created_at), portanto linhas históricas em ISO e linhas SQLite não trocam de ordem por comparação textual. Novas linhas mantêm o formato SQLite uniforme no armazenamento, enquanto a resposta converte created_at para ISO UTC. O servidor fixa identidade e timestamp e devolve a linha aceita. A store só a publica depois dessa confirmação, e o formulário usa CFInput/CFButton, lock síncrono, loading acessível e erro persistente para retry. O mesmo ID com a mesma intenção é idempotente; reutilização para outro valor é recusada.

O boot mantém o ledger limitado sem perder o saldo atual: seleciona os 200 movimentos mais recentes em ordem decrescente e inverte somente a janela retornada para o consumo cronológico da interface. Assim o cartão, o próximo depósito e o próximo saque continuam partindo do lançamento mais novo mesmo depois de anos de histórico.

Os quatro KPIs do ciclo ativo consideram somente holerites DRAFT e APPROVED; lançamentos PAID permanecem no histórico e na tabela, mas não inflam novamente Total Earnings, Cash with Crew, Company to Pay ou To Collect. O cálculo percorre as entradas uma vez e arredonda o agregado a centavos. Novos holerites confirmados são inseridos no topo, preservando a mesma ordenação decrescente recebida no boot, e a formatação monetária mantém exatamente duas casas em todos os runtimes.

  • Compensação Automática de Cash in Hand: Faturas recebidas em dinheiro ou transferências diretas para a conta da funcionária (EMPLOYEE_ACCOUNT) excluídas do QuickBooks são tratadas como recebimentos antecipados e deduzidas automaticamente do valor líquido a transferir no fechamento da folha.

  • Serviço de Períodos: src/features/payroll/services/PayrollPeriodService.ts
  • Liquidação da Folha: src/features/payroll/services/PayrollSettlementLogic.ts
  • Geração de Holerite (Payslip): src/components/financials/employee/PayslipDocument.tsx
  • Zustand Store: src/stores/payrollStore.ts
  • Painel de Pagamentos: src/pages/Payroll.tsx