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.
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.
| Tabela | Para que serve |
|---|---|
| obra_daysheets | A folha: obra, fase, dia, quem, tempo. E, num acerto, a folha que corrige e o motivo. |
| obra_daysheet_hours | Horas por pessoa, com o custo/hora copiado no momento do lançamento. |
| obra_daysheet_materials | Material gasto, ao artigo da mesma base de custos que o orçamento usa. |
| obra_daysheet_equipment | Horas de máquina, ao custo/hora do equipamento. |
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.
-- 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;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.
-- 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;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.
-- 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));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.
-- 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;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.