🚀 Initial commit: Versão atual do TrackSteel APP

This commit is contained in:
2026-03-18 21:17:53 +00:00
commit bde410c9ad
633 changed files with 108150 additions and 0 deletions

View File

@@ -0,0 +1,357 @@
-- Função RPC otimizada para buscar itens disponíveis para apontamento
-- Esta função substitui múltiplas queries por uma única consulta otimizada
CREATE OR REPLACE FUNCTION get_itens_disponiveis(
p_of_number TEXT DEFAULT NULL,
p_processo_id UUID DEFAULT NULL,
p_include_componentes BOOLEAN DEFAULT TRUE,
p_limit INTEGER DEFAULT 100
)
RETURNS TABLE (
item_id UUID,
item_type TEXT, -- 'peca' ou 'componente'
marca TEXT,
descricao TEXT,
peso_unitario NUMERIC,
quantidade_total NUMERIC,
quantidade_processada NUMERIC,
quantidade_disponivel NUMERIC,
etapa_fase TEXT,
processo_atual_id UUID,
processo_atual_nome TEXT,
processo_atual_ordem INTEGER,
proximos_processos JSONB,
peca_pai_id UUID,
peca_pai_marca TEXT
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
WITH
-- CTE para buscar peças da OF
pecas_of AS (
SELECT
p.id,
p.marca,
p.descricao,
p.peso_unitario,
p.quantidade,
p.etapa_fase,
p.of_number
FROM pecas p
WHERE (p_of_number IS NULL OR p.of_number = p_of_number)
),
-- CTE para buscar componentes das peças (se solicitado)
componentes_of AS (
SELECT
c.id,
c.marca_componente as marca,
c.descricao,
c.peso_unitario,
(c.quantidade_por_peca * p.quantidade) as quantidade,
c.peca_id,
p.marca as peca_pai_marca,
p.of_number
FROM componentes_peca c
INNER JOIN pecas_of p ON c.peca_id = p.id
WHERE p_include_componentes = true
),
-- CTE para calcular quantidades já processadas de peças
pecas_processadas AS (
SELECT
ap.peca_id,
ap.processo_id,
SUM(ap.quantidade_produzida) as quantidade_processada
FROM apontamentos_producao ap
INNER JOIN pecas_of p ON ap.peca_id = p.id
WHERE ap.tipo_apontamento = 'peca'
AND (p_of_number IS NULL OR ap.of_number = p_of_number)
AND (p_processo_id IS NULL OR ap.processo_id = p_processo_id)
GROUP BY ap.peca_id, ap.processo_id
),
-- CTE para calcular quantidades já processadas de componentes
componentes_processados AS (
SELECT
ap.componente_id,
ap.processo_id,
SUM(ap.quantidade_produzida) as quantidade_processada
FROM apontamentos_producao ap
INNER JOIN componentes_of c ON ap.componente_id = c.id
WHERE ap.tipo_apontamento = 'componente'
AND (p_of_number IS NULL OR ap.of_number = p_of_number)
AND (p_processo_id IS NULL OR ap.processo_id = p_processo_id)
GROUP BY ap.componente_id, ap.processo_id
),
-- CTE para buscar processos disponíveis
processos_disponiveis AS (
SELECT
pf.id,
pf.nome,
pf.ordem,
pf.ativo
FROM processos_fabricacao pf
WHERE pf.ativo = true
AND (p_processo_id IS NULL OR pf.id = p_processo_id)
ORDER BY pf.ordem
),
-- CTE para determinar próximos processos para cada item
proximos_processos_cte AS (
SELECT
pd.id as processo_id,
COALESCE(
jsonb_agg(
jsonb_build_object(
'id', pd_next.id,
'nome', pd_next.nome,
'ordem', pd_next.ordem
) ORDER BY pd_next.ordem
) FILTER (WHERE pd_next.id IS NOT NULL),
'[]'::jsonb
) as proximos_processos
FROM processos_disponiveis pd
LEFT JOIN processos_disponiveis pd_next ON pd_next.ordem > pd.ordem
GROUP BY pd.id
)
-- Query principal: UNION de peças e componentes
SELECT
-- Peças
p.id as item_id,
'peca'::TEXT as item_type,
p.marca,
p.descricao,
p.peso_unitario,
p.quantidade as quantidade_total,
COALESCE(pp.quantidade_processada, 0) as quantidade_processada,
GREATEST(p.quantidade - COALESCE(pp.quantidade_processada, 0), 0) as quantidade_disponivel,
p.etapa_fase,
pd.id as processo_atual_id,
pd.nome as processo_atual_nome,
pd.ordem as processo_atual_ordem,
COALESCE(ppc.proximos_processos, '[]'::jsonb) as proximos_processos,
NULL::UUID as peca_pai_id,
NULL::TEXT as peca_pai_marca
FROM pecas_of p
CROSS JOIN processos_disponiveis pd
LEFT JOIN pecas_processadas pp ON p.id = pp.peca_id AND pd.id = pp.processo_id
LEFT JOIN proximos_processos_cte ppc ON pd.id = ppc.processo_id
WHERE GREATEST(p.quantidade - COALESCE(pp.quantidade_processada, 0), 0) > 0
UNION ALL
SELECT
-- Componentes
c.id as item_id,
'componente'::TEXT as item_type,
c.marca,
c.descricao,
c.peso_unitario,
c.quantidade as quantidade_total,
COALESCE(cp.quantidade_processada, 0) as quantidade_processada,
GREATEST(c.quantidade - COALESCE(cp.quantidade_processada, 0), 0) as quantidade_disponivel,
NULL::TEXT as etapa_fase, -- Componentes não têm etapa_fase
pd.id as processo_atual_id,
pd.nome as processo_atual_nome,
pd.ordem as processo_atual_ordem,
COALESCE(ppc.proximos_processos, '[]'::jsonb) as proximos_processos,
c.peca_id as peca_pai_id,
c.peca_pai_marca
FROM componentes_of c
CROSS JOIN processos_disponiveis pd
LEFT JOIN componentes_processados cp ON c.id = cp.componente_id AND pd.id = cp.processo_id
LEFT JOIN proximos_processos_cte ppc ON pd.id = ppc.processo_id
WHERE p_include_componentes = true
AND GREATEST(c.quantidade - COALESCE(cp.quantidade_processada, 0), 0) > 0
ORDER BY
item_type DESC, -- Peças primeiro, depois componentes
processo_atual_ordem ASC,
marca ASC
LIMIT p_limit;
END;
$$;
-- Comentários sobre a função
COMMENT ON FUNCTION get_itens_disponiveis IS 'Função otimizada para buscar itens (peças e componentes) disponíveis para apontamento de produção';
-- Criar índices para otimizar a performance da função RPC
CREATE INDEX IF NOT EXISTS idx_apontamentos_producao_of_tipo_processo
ON apontamentos_producao(of_number, tipo_apontamento, processo_id);
CREATE INDEX IF NOT EXISTS idx_apontamentos_producao_peca_processo
ON apontamentos_producao(peca_id, processo_id)
WHERE tipo_apontamento = 'peca';
CREATE INDEX IF NOT EXISTS idx_apontamentos_producao_componente_processo
ON apontamentos_producao(componente_id, processo_id)
WHERE tipo_apontamento = 'componente';
CREATE INDEX IF NOT EXISTS idx_pecas_of_number
ON pecas(of_number);
CREATE INDEX IF NOT EXISTS idx_componentes_peca_peca_id
ON componentes_peca(peca_id);
CREATE INDEX IF NOT EXISTS idx_processos_fabricacao_ativo_ordem
ON processos_fabricacao(ativo, ordem)
WHERE ativo = true;
-- Função auxiliar para buscar estatísticas de performance
CREATE OR REPLACE FUNCTION get_apontamentos_stats(
p_of_number TEXT DEFAULT NULL,
p_data_inicio DATE DEFAULT NULL,
p_data_fim DATE DEFAULT NULL
)
RETURNS TABLE (
total_apontamentos BIGINT,
total_pecas BIGINT,
total_componentes BIGINT,
processos_utilizados BIGINT,
quantidade_total_produzida NUMERIC,
periodo_inicio DATE,
periodo_fim DATE
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
SELECT
COUNT(*) as total_apontamentos,
COUNT(*) FILTER (WHERE ap.tipo_apontamento = 'peca') as total_pecas,
COUNT(*) FILTER (WHERE ap.tipo_apontamento = 'componente') as total_componentes,
COUNT(DISTINCT ap.processo_id) as processos_utilizados,
SUM(ap.quantidade_produzida) as quantidade_total_produzida,
MIN(ap.data_apontamento::DATE) as periodo_inicio,
MAX(ap.data_apontamento::DATE) as periodo_fim
FROM apontamentos_producao ap
WHERE (p_of_number IS NULL OR ap.of_number = p_of_number)
AND (p_data_inicio IS NULL OR ap.data_apontamento::DATE >= p_data_inicio)
AND (p_data_fim IS NULL OR ap.data_apontamento::DATE <= p_data_fim);
END;
$$;
COMMENT ON FUNCTION get_apontamentos_stats IS 'Função para obter estatísticas de apontamentos de produção';
-- Função para validação sequencial otimizada
CREATE OR REPLACE FUNCTION validate_sequencia_processos(
p_of_number TEXT,
p_peca_id UUID DEFAULT NULL,
p_componente_id UUID DEFAULT NULL
)
RETURNS TABLE (
item_id UUID,
item_type TEXT,
marca TEXT,
processo_atual_id UUID,
processo_atual_nome TEXT,
processo_atual_ordem INTEGER,
pode_apontar BOOLEAN,
motivo_bloqueio TEXT,
processos_pendentes JSONB
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
WITH
-- Buscar todos os processos ordenados
processos_ordenados AS (
SELECT id, nome, ordem
FROM processos_fabricacao
WHERE ativo = true
ORDER BY ordem
),
-- Buscar apontamentos existentes
apontamentos_existentes AS (
SELECT
ap.peca_id,
ap.componente_id,
ap.processo_id,
pf.ordem as processo_ordem,
SUM(ap.quantidade_produzida) as quantidade_apontada
FROM apontamentos_producao ap
INNER JOIN processos_fabricacao pf ON ap.processo_id = pf.id
WHERE ap.of_number = p_of_number
AND (p_peca_id IS NULL OR ap.peca_id = p_peca_id)
AND (p_componente_id IS NULL OR ap.componente_id = p_componente_id)
GROUP BY ap.peca_id, ap.componente_id, ap.processo_id, pf.ordem
),
-- Determinar próximo processo válido para cada item
proximos_processos_validos AS (
SELECT
COALESCE(ae.peca_id, p_peca_id) as peca_id,
COALESCE(ae.componente_id, p_componente_id) as componente_id,
CASE
WHEN ae.processo_id IS NULL THEN (
SELECT id FROM processos_ordenados ORDER BY ordem LIMIT 1
)
ELSE (
SELECT po.id
FROM processos_ordenados po
WHERE po.ordem > ae.processo_ordem
ORDER BY po.ordem
LIMIT 1
)
END as proximo_processo_id
FROM (
SELECT DISTINCT
peca_id,
componente_id,
MAX(processo_ordem) as processo_ordem,
MAX(processo_id) as processo_id
FROM apontamentos_existentes
GROUP BY peca_id, componente_id
UNION ALL
-- Incluir itens que ainda não têm apontamentos
SELECT p_peca_id, p_componente_id, NULL, NULL
WHERE NOT EXISTS (
SELECT 1 FROM apontamentos_existentes
WHERE peca_id = p_peca_id OR componente_id = p_componente_id
)
) ae
)
SELECT
COALESCE(ppv.peca_id, ppv.componente_id) as item_id,
CASE WHEN ppv.peca_id IS NOT NULL THEN 'peca' ELSE 'componente' END as item_type,
COALESCE(p.marca, c.marca_componente) as marca,
po.id as processo_atual_id,
po.nome as processo_atual_nome,
po.ordem as processo_atual_ordem,
(ppv.proximo_processo_id IS NOT NULL) as pode_apontar,
CASE
WHEN ppv.proximo_processo_id IS NULL THEN 'Todos os processos já foram concluídos'
ELSE NULL
END as motivo_bloqueio,
COALESCE(
jsonb_agg(
jsonb_build_object(
'id', po_pendente.id,
'nome', po_pendente.nome,
'ordem', po_pendente.ordem
) ORDER BY po_pendente.ordem
) FILTER (WHERE po_pendente.id IS NOT NULL),
'[]'::jsonb
) as processos_pendentes
FROM proximos_processos_validos ppv
LEFT JOIN pecas p ON ppv.peca_id = p.id
LEFT JOIN componentes_peca c ON ppv.componente_id = c.id
LEFT JOIN processos_ordenados po ON ppv.proximo_processo_id = po.id
LEFT JOIN processos_ordenados po_pendente ON po_pendente.ordem >= po.ordem
GROUP BY
ppv.peca_id, ppv.componente_id, p.marca, c.marca_componente,
po.id, po.nome, po.ordem, ppv.proximo_processo_id;
END;
$$;
COMMENT ON FUNCTION validate_sequencia_processos IS 'Função para validar sequência de processos e determinar próximos passos válidos';