Files
dtf-system/schema.sql
Cauê Faleiros 98c951d374
Some checks failed
Validate, publish and deploy / validate (push) Successful in 2m2s
Validate, publish and deploy / publish-and-deploy (push) Failing after 8s
first commit
2026-09-15 16:42:34 -03:00

223 lines
9.7 KiB
SQL

-- =====================================================================
-- Módulo DTF — schema completo
-- =====================================================================
-- Duas bases:
-- NUVEM (PostgreSQL, no VPS) → pedido, arte, parte, job, agente
-- LOCAL (SQLite, na fábrica) → card, movimento, retrabalho, job_local
--
-- A separação é proposital: o kanban precisa funcionar com a internet fora.
-- =====================================================================
-- =====================================================================
-- NUVEM · PostgreSQL
-- =====================================================================
CREATE TABLE pedido (
id BIGSERIAL PRIMARY KEY,
numero_tiny TEXT NOT NULL UNIQUE, -- chave de tudo
cliente TEXT NOT NULL,
telefone TEXT NOT NULL,
metros NUMERIC(10,2) NOT NULL, -- metros comprados por arte
cliente_novo BOOLEAN NOT NULL DEFAULT false,-- dispara prova de cor
token TEXT NOT NULL UNIQUE, -- UUID v4 do link
token_expira TIMESTAMPTZ NOT NULL, -- 7 dias
lembrete_em TIMESTAMPTZ, -- 1h sem arte → cobra
criado_em TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ix_pedido_token ON pedido(token);
CREATE INDEX ix_pedido_semarte ON pedido(criado_em)
WHERE NOT EXISTS (SELECT 1 FROM arte WHERE arte.pedido_id = pedido.id);
CREATE TABLE arte (
id BIGSERIAL PRIMARY KEY,
pedido_id BIGINT NOT NULL REFERENCES pedido(id),
arquivo TEXT NOT NULL, -- chave no storage
arquivo_orig TEXT, -- original preservado
bytes BIGINT NOT NULL DEFAULT 0,
formato_orig TEXT, -- png, pdf, psd, cdr...
natureza TEXT, -- vetor | raster | conferir
repeticoes INT NOT NULL DEFAULT 1,
status TEXT NOT NULL DEFAULT 'recebida',
-- recebida | processando | aprovada | recusada
conferir_manual BOOLEAN NOT NULL DEFAULT false,-- CDR
motivo TEXT, -- por que foi recusada
avisos TEXT, -- aceitou mas com ressalva
dpi_efetivo NUMERIC(8,2),
largura_cm NUMERIC(8,2),
metros_totais NUMERIC(10,2),
minutos_maquina INT, -- estimativa; vira campo no card
cores_tensas TEXT, -- fora do gamut, se houver
virus_ok BOOLEAN NOT NULL DEFAULT false,-- ClamAV liberou
baixada BOOLEAN NOT NULL DEFAULT false,-- o agente já trouxe
criado_em TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ix_arte_pedido ON arte(pedido_id);
CREATE INDEX ix_arte_baixar ON arte(status, baixada, virus_ok);
CREATE TABLE parte (
id BIGSERIAL PRIMARY KEY,
arte_id BIGINT NOT NULL REFERENCES arte(id),
ordem INT NOT NULL,
metros NUMERIC(10,2) NOT NULL,
arquivo TEXT NOT NULL,
carimbada BOOLEAN NOT NULL DEFAULT false,-- só a última leva carimbo
UNIQUE (arte_id, ordem)
);
-- Fila de jobs. Tabela, não broker: no volume da Altus (algumas centenas de
-- pedidos/dia) Redis e Celery são complexidade sem retorno.
-- Consumir com: SELECT ... FOR UPDATE SKIP LOCKED
CREATE TABLE job (
id BIGSERIAL PRIMARY KEY,
tipo TEXT NOT NULL, -- preflight | whats | tiny | limpeza
payload JSONB NOT NULL,
tentativas INT NOT NULL DEFAULT 0,
proxima_em TIMESTAMPTZ NOT NULL DEFAULT now(),
erro TEXT,
feito BOOLEAN NOT NULL DEFAULT false,
criado_em TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ix_job_fila ON job(feito, proxima_em) WHERE NOT feito;
CREATE TABLE agente_heartbeat (
id SERIAL PRIMARY KEY,
visto_em TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- sem sinal por 15 min → alertar o TI
-- Artes guardadas para recompra. Prefixo separado no storage, retenção 12 meses.
CREATE TABLE arte_cliente (
id BIGSERIAL PRIMARY KEY,
numero_cliente TEXT NOT NULL,
token_cliente TEXT NOT NULL, -- link permanente
arte_id BIGINT REFERENCES arte(id),
arquivo TEXT NOT NULL, -- só a arte tratada
descricao TEXT,
criado_em TIMESTAMPTZ NOT NULL DEFAULT now(),
expira_em TIMESTAMPTZ NOT NULL
);
CREATE INDEX ix_arte_cliente ON arte_cliente(numero_cliente, token_cliente);
-- =====================================================================
-- LOCAL · SQLite (na fábrica)
-- =====================================================================
CREATE TABLE card (
arte_id INTEGER PRIMARY KEY,
pedido TEXT NOT NULL,
cliente TEXT NOT NULL,
metros REAL NOT NULL,
minutos_maquina INTEGER, -- estimativa. Herda os marcadores 30min/1h/3h
-- do Tiny. Sugerida por metros/20*60; quem
-- trata a arte ajusta só quando foge do normal
partes INTEGER NOT NULL DEFAULT 1,
coluna TEXT NOT NULL DEFAULT 'rec',
-- rec | tra | apc | fil | imp | cor | fin
-- NÃO existe coluna de aplicação: a sala só imprime o filme
desde TEXT NOT NULL, -- entrada na coluna atual (ISO)
maquina TEXT, -- 'Maq 1' … 'Maq 6'
reservado_por TEXT, -- máquina que puxou
reservado_em TEXT, -- expira em 3 min (ajuste da sala)
conferir_manual INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX ix_card_coluna ON card(coluna, desde);
-- A tabela mais importante do sistema. Dela saem TODOS os números que hoje
-- não existem: tempo por etapa, fila por máquina, retrabalho, ocupação real.
CREATE TABLE movimento (
id INTEGER PRIMARY KEY AUTOINCREMENT,
arte_id INTEGER NOT NULL,
pedido TEXT NOT NULL,
de TEXT NOT NULL,
para TEXT NOT NULL,
entrou_em TEXT NOT NULL,
saiu_em TEXT NOT NULL,
segundos INTEGER NOT NULL,
usuario TEXT NOT NULL, -- login do PCP. Se o PC tiver login
-- compartilhado, o indicador de retrabalho
-- por pessoa fica sem dono
maquina TEXT
);
CREATE INDEX ix_mov_arte ON movimento(arte_id);
CREATE INDEX ix_mov_saiu ON movimento(saiu_em);
CREATE INDEX ix_mov_etapa ON movimento(de, saiu_em);
CREATE TABLE retrabalho (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pedido_origem TEXT NOT NULL,
pedido_novo TEXT,
metros REAL NOT NULL,
causa TEXT NOT NULL,
-- arte_cliente | tratamento | impressao | perfil_cor | pedido
-- TRAVADA depois de aberta: com o indicador atrelado a bônus,
-- haveria incentivo para reclassificar falha de máquina como
-- culpa do cliente
aberto_por TEXT NOT NULL, -- Mayana: conhece o cliente e a reclamação
aberto_em TEXT NOT NULL,
vez INTEGER NOT NULL DEFAULT 1, -- 2ª sobe alçada, 3ª trava
alcada TEXT NOT NULL, -- sala | financeiro | diretoria
autorizado_por TEXT, -- Thales ou Alexandre: entra na meta deles
autorizado_em TEXT,
contestado_por TEXT, -- discorda da causa mas não pode mudá-la
contestado_em TEXT,
contestacao TEXT,
evidencia TEXT -- foto ou print da reclamação
);
CREATE INDEX ix_retra_data ON retrabalho(aberto_em);
CREATE INDEX ix_retra_causa ON retrabalho(causa, aberto_em);
CREATE TABLE job_local (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tipo TEXT NOT NULL, -- tiny_marcador | whats
payload TEXT NOT NULL,
tentativas INTEGER NOT NULL DEFAULT 0,
proxima_em REAL NOT NULL, -- 1, 5 e 30 min
erro TEXT,
feito INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX ix_joblocal ON job_local(feito, proxima_em);
-- Consumo de filme, para o aproveitamento. NÃO exige apontamento novo:
-- lê as transferências para o depósito de impressão no Tiny.
CREATE TABLE consumo_filme (
id INTEGER PRIMARY KEY AUTOINCREMENT,
em TEXT NOT NULL,
metros REAL NOT NULL,
origem TEXT NOT NULL DEFAULT 'tiny' -- transferência de depósito
);
-- =====================================================================
-- Consultas que o painel usa
-- =====================================================================
-- Tempo médio por etapa nos últimos N dias
-- SELECT de, COUNT(*) n, AVG(segundos)/60 media_min, MAX(segundos)/60 pior_min
-- FROM movimento WHERE saiu_em >= date('now','-7 days') GROUP BY de;
-- Velocidade real por máquina — é o número que valida ou derruba os 20 m/h
-- SELECT m.maquina, SUM(c.metros) metros, SUM(m.segundos)/3600.0 horas,
-- SUM(c.metros)/(SUM(m.segundos)/3600.0) m_por_hora
-- FROM movimento m JOIN card c ON c.arte_id = m.arte_id
-- WHERE m.de='imp' AND m.saiu_em >= date('now','-7 days') GROUP BY m.maquina;
-- Retrabalho por causa, com responsável
-- SELECT causa, COUNT(*) n, SUM(metros) m FROM retrabalho
-- WHERE aberto_em >= date('now','-30 days') GROUP BY causa;
-- Aproveitamento do filme
-- faturado = SELECT SUM(metros) FROM card WHERE coluna='fin' AND desde >= ...
-- consumido = SELECT SUM(metros) FROM consumo_filme WHERE em >= ...
-- aproveitamento = faturado / consumido