Saltar para o conteúdo
PMPaulo Mota
Voltar à demo

Como foi feito

Folha de obra, por dentro

Três regras na base de dados: uma folha por fase e por dia, dezasseis horas no máximo por pessoa por dia somando todas as obras, e uma folha entregue que não se edita.

01

O problema

As horas andam num caderno dentro da carrinha e chegam ao escritório na sexta-feira, se chegarem. O material compra-se e ninguém sabe a que obra foi. No fim, a obra ou deu ou não deu, e não há maneira de saber porquê. O que falta é o registo no sítio onde acontece, feito por quem estava lá, no dia em que aconteceu.

02

As tabelas

Uma folha e três tabelas de linhas. Todas com RLS ligada e nenhuma política de leitura: o que a equipa custa nunca sai por um select.

TabelaPara que serve
obra_daysheetsA folha: obra, fase, dia, quem, tempo. E, num acerto, a folha que corrige e o motivo.
obra_daysheet_hoursHoras por pessoa, com o custo/hora copiado no momento do lançamento.
obra_daysheet_materialsMaterial gasto, ao artigo da mesma base de custos que o orçamento usa.
obra_daysheet_equipmentHoras de máquina, ao custo/hora do equipamento.
03

Uma folha por fase e por dia

É um índice único parcial: único quando não é um acerto, livre quando é. Duas folhas do mesmo dia para a mesma fase não são zelo, são horas lançadas duas vezes, e é a base de dados que o diz, não o botão.

sql
-- Unico quando nao e um acerto, livre quando e.
create unique index obra_daysheets_day_uniq
  on obra_daysheets (project_id, phase_id, day)
  where adjusts is null;
04

Dezasseis horas, contadas em todas as obras

Ninguém faz vinte horas em duas obras no mesmo dia, mas dois encarregados a preencher em dois telemóveis conseguem lançá-las se cada um só olhar para a sua folha. A conta é sobre todas as folhas daquele dia, e corre dentro de um advisory lock, senão as duas passam a verificação ao mesmo tempo e as duas escrevem.

sql
-- A conta e sobre todas as folhas daquele dia, nao so sobre esta.
-- O lock e para dois telemoveis nao passarem os dois ao mesmo tempo.
perform pg_advisory_xact_lock(
  hashtext(p_room || '|h|' || p_worker::text || '|' || v_s.day::text));

select coalesce(sum(h.hours), 0) into v_total
  from obra_daysheet_hours h
  join obra_daysheets d on d.id = h.sheet_id
 where h.worker_id = p_worker and d.day = v_s.day
   and h.sheet_id <> p_sheet;

if v_total + p_hours > 16 then
  raise exception 'mais de 16 horas no mesmo dia para %', v_w.name;
end if;
05

Corrigir é assinar, não editar

Uma folha entregue fecha. Corrigir abre uma folha de acerto, com motivo, que aponta para a original e pode levar horas negativas. As duas ficam no histórico e a soma dá o número certo. É o mesmo princípio da acção correctiva do HACCP e do acerto do relógio de ponto: apagar um erro apaga também a prova de que ele existiu.

sql
-- Uma folha entregue nao se edita. Corrigir abre outra, com motivo,
-- que aponta para a original e pode levar horas negativas.
if v_s.submitted_at is null then raise exception 'folha ainda aberta'; end if;
if v_s.adjusts is not null then raise exception 'um acerto nao se acerta'; end if;

insert into obra_daysheets (project_id, phase_id, day, author, adjusts, reason)
values (v_s.project_id, v_s.phase_id, v_s.day, p_author, v_s.id, btrim(p_reason));
06

Orçado contra real

O orçamento da fase é custo directo, sem margem, e o real é o que as folhas somam ao custo verdadeiro de cada pessoa e de cada máquina. É por isso que a hora com que se orçamenta tem de ser custo e não preço de venda: se fosse preço, o real ganhava sempre ao orçado e o mapa não servia para nada.

sql
-- Orcado e custo directo; real e o que as folhas somam.
select p.id, p.name, p.budget_cents,
       coalesce(h.cents, 0) + coalesce(m.cents, 0) + coalesce(e.cents, 0) as actual_cents,
       p.budget_cents
         - (coalesce(h.cents, 0) + coalesce(m.cents, 0) + coalesce(e.cents, 0)) as diff_cents
  from obra_phases p
  left join (select d.phase_id, sum(round(x.hours * x.hourly_cents)) cents
               from obra_daysheet_hours x
               join obra_daysheets d on d.id = x.sheet_id
              group by d.phase_id) h on h.phase_id = p.id
  left join ... m on m.phase_id = p.id
  left join ... e on e.phase_id = p.id
 where p.room_code = p_room;
07

O que faria diferente num cliente real

Funcionar sem rede, com as folhas guardadas no telemóvel e enviadas quando houver sinal, porque muita obra não tem cobertura. Fotografia da guia de remessa agarrada à linha de material. Ligação ao processamento de salários, para as horas não serem lançadas duas vezes. Assinatura do encarregado no ecrã. E autenticação, com cada encarregado a ver só as obras dele.