Roudrigus commited on
Commit
266f1f7
·
verified ·
1 Parent(s): d6d6963

Update models.py

Browse files
Files changed (1) hide show
  1. models.py +475 -463
models.py CHANGED
@@ -1,463 +1,475 @@
1
- # -*- coding: utf-8 -*-
2
- from sqlalchemy import (
3
- Column,
4
- Integer,
5
- String,
6
- Date,
7
- DateTime,
8
- Boolean,
9
- ForeignKey,
10
- Text
11
- )
12
- from sqlalchemy.orm import relationship
13
- # If you face cyclic import issues, place Base here via declarative_base:
14
- # from sqlalchemy.orm import declarative_base
15
- # Base = declarative_base()
16
- from datetime import datetime
17
- from banco import Base
18
- from sqlalchemy.sql import func # server_default em AvisoGlobal
19
-
20
- # =====================================================
21
- # TABELA EQUIPAMENTOS
22
- # =====================================================
23
- class Equipamento(Base):
24
- __tablename__ = "equipamentos"
25
-
26
- id = Column(Integer, primary_key=True, index=True)
27
-
28
- # Identificação
29
- fpso1 = Column(String, index=True, nullable=False)
30
- fpso = Column(String, index=True, nullable=False)
31
- data_coleta = Column(String, index=True, nullable=False) # sugestão futura: Date
32
-
33
- # Responsáveis
34
- especialista = Column(String, nullable=False)
35
- conferente = Column(String, nullable=False)
36
- osm = Column(String, nullable=False)
37
-
38
- # Operacional
39
- modal = Column(String, index=True, nullable=False)
40
- quant_equip = Column(Integer, default=0)
41
- mrob = Column(String, nullable=False)
42
-
43
- # Métricas
44
- linhas_osm = Column(Integer, default=0)
45
- linhas_mrob = Column(Integer, default=0)
46
- linhas_erros = Column(Integer, default=0)
47
-
48
- # Erros
49
- erro_storekeeper = Column(String)
50
- erro_operacao = Column(String)
51
- erro_especialista = Column(String)
52
- erro_outros = Column(String)
53
-
54
- # Dados complementares
55
- inclusao_exclusao = Column(String)
56
- po = Column(String)
57
- part_number = Column(String)
58
- material = Column(String)
59
-
60
- solicitante = Column(String, nullable=False)
61
- motivo = Column(String)
62
- requisitante = Column(String, nullable=False)
63
- nota_fiscal = Column(String, nullable=False)
64
- impacto = Column(String, nullable=False)
65
- dimensao = Column(String, nullable=False)
66
-
67
- observacoes = Column(String)
68
-
69
- # Dia de inclusão
70
- dia_inclusao = Column(String, nullable=True, index=True)
71
-
72
- # Auditoria
73
- data_hora_input = Column(DateTime, default=datetime.utcnow, index=True)
74
-
75
-
76
- # =====================================================
77
- # TABELA FPSOS
78
- # =====================================================
79
- class FPSO(Base):
80
- __tablename__ = "fpsos"
81
-
82
- id = Column(Integer, primary_key=True)
83
- nome = Column(String, unique=True, nullable=False, index=True)
84
- ativo = Column(Boolean, default=True)
85
- data_cadastro = Column(DateTime, default=datetime.utcnow)
86
-
87
-
88
- # =====================================================
89
- # LOG DE AUDITORIA (OFICIAL)
90
- # =====================================================
91
- class LogAcesso(Base):
92
- __tablename__ = "log_acesso"
93
-
94
- id = Column(Integer, primary_key=True)
95
-
96
- usuario = Column(String, nullable=False, index=True)
97
- acao = Column(String, nullable=False)
98
- tabela = Column(String, nullable=True)
99
- registro_id = Column(Integer, nullable=True)
100
-
101
- data_hora = Column(DateTime, default=datetime.utcnow, index=True)
102
-
103
-
104
- # =====================================================
105
- # USUÁRIOS
106
- # =====================================================
107
- class Usuario(Base):
108
- __tablename__ = "usuarios"
109
-
110
- id = Column(Integer, primary_key=True)
111
-
112
- # login & segurança
113
- usuario = Column(String, unique=True, nullable=False, index=True)
114
- senha = Column(String, nullable=False)
115
-
116
- # perfil & status
117
- perfil = Column(String, nullable=False) # admin | usuario | consulta
118
- ativo = Column(Boolean, default=True)
119
-
120
- # auditoria
121
- data_criacao = Column(DateTime, default=datetime.utcnow)
122
-
123
- # UI/contato
124
- nome = Column(String, nullable=True, index=True)
125
- email = Column(String, unique=True, index=True, nullable=True)
126
-
127
- # aniversário
128
- data_aniversario = Column(Date, nullable=True)
129
-
130
-
131
- # =====================================================
132
- # QUIZ - PERGUNTAS
133
- # =====================================================
134
- class QuizPergunta(Base):
135
- __tablename__ = "quiz_perguntas"
136
-
137
- id = Column(Integer, primary_key=True)
138
- pergunta = Column(String, nullable=False)
139
- ativo = Column(Boolean, default=True)
140
- data_criacao = Column(DateTime, default=datetime.utcnow)
141
-
142
- respostas = relationship(
143
- "QuizResposta",
144
- back_populates="pergunta",
145
- cascade="all, delete-orphan"
146
- )
147
-
148
-
149
- # =====================================================
150
- # QUIZ - RESPOSTAS
151
- # =====================================================
152
- class QuizResposta(Base):
153
- __tablename__ = "quiz_respostas"
154
-
155
- id = Column(Integer, primary_key=True)
156
- pergunta_id = Column(Integer, ForeignKey("quiz_perguntas.id"), nullable=False)
157
- texto = Column(String, nullable=False)
158
- correta = Column(Boolean, default=False)
159
-
160
- pergunta = relationship("QuizPergunta", back_populates="respostas")
161
-
162
-
163
- # =====================================================
164
- # QUIZ - PONTUAÇÃO / RANKING
165
- # =====================================================
166
- class QuizPontuacao(Base):
167
- __tablename__ = "quiz_pontuacoes"
168
-
169
- id = Column(Integer, primary_key=True)
170
- usuario = Column(String, nullable=False, index=True)
171
- pontos = Column(Integer, nullable=False, default=0)
172
- data = Column(DateTime, default=datetime.utcnow, index=True)
173
-
174
-
175
- # =====================================================
176
- # VÍDEOS - CATEGORIAS
177
- # =====================================================
178
- class VideoCategoria(Base):
179
- __tablename__ = "video_categorias"
180
-
181
- id = Column(Integer, primary_key=True)
182
- nome = Column(String, nullable=False, unique=True)
183
- ativo = Column(Boolean, default=True)
184
- data_criacao = Column(DateTime, default=datetime.utcnow)
185
-
186
-
187
- # =====================================================
188
- # VÍDEOS
189
- # =====================================================
190
- class Video(Base):
191
- __tablename__ = "videos"
192
-
193
- id = Column(Integer, primary_key=True)
194
- titulo = Column(String, nullable=False)
195
- descricao = Column(String)
196
- url = Column(String, nullable=False)
197
-
198
- categoria_id = Column(Integer, ForeignKey("video_categorias.id"))
199
- categoria = relationship("VideoCategoria")
200
-
201
- ativo = Column(Boolean, default=True)
202
- data_criacao = Column(DateTime, default=datetime.utcnow)
203
-
204
-
205
- # =====================================================
206
- # CALENDÁRIO - EVENTOS / LEMBRETES
207
- # =====================================================
208
- class EventoCalendario(Base):
209
- __tablename__ = "eventos_calendario"
210
-
211
- id = Column(Integer, primary_key=True)
212
- titulo = Column(String, nullable=False)
213
- descricao = Column(String)
214
- data_evento = Column(Date, nullable=False)
215
- data_lembrete = Column(Date)
216
- ativo = Column(Boolean, default=True)
217
- usuario_criacao = Column(String, nullable=False)
218
- data_criacao = Column(DateTime, default=datetime.utcnow)
219
-
220
-
221
- # =====================================================
222
- # IOI-RUN - SUGESTÕES DO SISTEMA
223
- # =====================================================
224
- class IOIRunSugestao(Base):
225
- __tablename__ = "ioirun_sugestao"
226
-
227
- id = Column(Integer, primary_key=True, index=True)
228
-
229
- # Identificação do autor
230
- usuario = Column(String, nullable=False, index=True)
231
-
232
- # Conteúdo
233
- area = Column(String, nullable=True, index=True)
234
- mensagem = Column(Text, nullable=False)
235
-
236
- # Resposta do time (admin)
237
- resposta = Column(Text, nullable=True)
238
- status = Column(String, default="pendente", nullable=False, index=True) # pendente | respondida
239
- responsavel = Column(String, nullable=True)
240
-
241
- # Auditoria
242
- data_envio = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
243
- data_resposta = Column(DateTime, nullable=True)
244
-
245
-
246
- # =====================================================
247
- # RNC - REGISTRO DE NÃO CONFORMIDADES (FOR-SGQ-08)
248
- # =====================================================
249
- class RNC(Base):
250
- __tablename__ = "rnc"
251
-
252
- id = Column(Integer, primary_key=True, index=True)
253
-
254
- # Identificação
255
- codigo = Column(String(20), unique=True, index=True) # ex.: RNC-2026-0001
256
- titulo = Column(String(200), nullable=False)
257
- descricao = Column(Text, nullable=False)
258
-
259
- # Cabeçalho do formulário
260
- data_form = Column(Date, nullable=True, index=True) # Data do formulário
261
- emitente = Column(String(120), nullable=True, index=True)
262
- rnc_cliente_numero = Column(String(50), nullable=True)
263
- cliente_emitente = Column(String(120), nullable=True)
264
- area_solicitante = Column(String(120), nullable=True, index=True)
265
- area_notificada = Column(String(120), nullable=True, index=True)
266
- origem = Column(String(50), nullable=True, index=True) # Auditoria Interna/Externa/Outras
267
-
268
- # Envolvidos
269
- envolvido1_nome = Column(String(120), nullable=True)
270
- envolvido1_matricula = Column(String(50), nullable=True)
271
- envolvido1_funcao = Column(String(120), nullable=True)
272
- envolvido2_nome = Column(String(120), nullable=True)
273
- envolvido2_matricula = Column(String(50), nullable=True)
274
- envolvido2_funcao = Column(String(120), nullable=True)
275
-
276
- # Classificação
277
- tipo = Column(String(50), nullable=True)
278
- severidade = Column(String(20), nullable=True)
279
- prioridade = Column(String(20), nullable=True)
280
-
281
- # Status e prazos
282
- status = Column(String(30), default="Aberta", nullable=False, index=True)
283
- data_abertura = Column(DateTime, default=datetime.utcnow, index=True)
284
- prazo = Column(DateTime, nullable=True, index=True)
285
- encerrada_em = Column(DateTime, nullable=True, index=True)
286
-
287
- # Responsáveis
288
- responsavel = Column(String(120), nullable=True, index=True)
289
- criado_por = Column(String(120), nullable=False, index=True)
290
-
291
- # Complementares
292
- cliente = Column(String(120), nullable=True)
293
- local = Column(String(120), nullable=True)
294
-
295
- # Análise das causas
296
- metodologia = Column(String(120), nullable=True) # ex.: Ishikawa, 5 Porquês
297
- causa_raiz = Column(Text, nullable=True) # descrição da causa raiz
298
- ishikawa_json = Column(Text, nullable=True) # opcional: armazenar estrutura Ishikawa em JSON
299
-
300
- # Auditoria
301
- data_hora_input = Column(DateTime, default=datetime.utcnow, index=True)
302
-
303
- # Relacionamentos
304
- comentarios = relationship("RNCComentario", back_populates="rnc", cascade="all, delete-orphan")
305
- acoes = relationship("RNCAcaoCorretiva", back_populates="rnc", cascade="all, delete-orphan")
306
- anexos = relationship("RNCAnexo", back_populates="rnc", cascade="all, delete-orphan")
307
-
308
-
309
- # =====================================================
310
- # RNC - COMENTÁRIOS / TIMELINE
311
- # =====================================================
312
- class RNCComentario(Base):
313
- __tablename__ = "rnc_comentario"
314
-
315
- id = Column(Integer, primary_key=True, index=True)
316
- rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
317
-
318
- data = Column(DateTime, default=datetime.utcnow, index=True)
319
- autor = Column(String(120), nullable=False, index=True)
320
- mensagem = Column(Text, nullable=False)
321
-
322
- status_novo = Column(String(30), nullable=True, index=True)
323
- prazo_novo = Column(DateTime, nullable=True, index=True)
324
- responsavel_novo = Column(String(120), nullable=True, index=True)
325
-
326
- rnc = relationship("RNC", back_populates="comentarios")
327
-
328
-
329
- # =====================================================
330
- # RNC - AÇÕES CORRETIVAS / PREVENTIVAS
331
- # =====================================================
332
- class RNCAcaoCorretiva(Base):
333
- __tablename__ = "rnc_acao"
334
-
335
- id = Column(Integer, primary_key=True, index=True)
336
- rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
337
-
338
- descricao = Column(Text, nullable=False)
339
- responsavel = Column(String(120), nullable=True, index=True)
340
- prazo = Column(DateTime, nullable=True, index=True)
341
-
342
- status = Column(String(30), default="Planejada", nullable=False, index=True)
343
- eficacia = Column(String(30), nullable=True, index=True)
344
- conclusao_em = Column(DateTime, nullable=True, index=True)
345
-
346
- rnc = relationship("RNC", back_populates="acoes")
347
-
348
-
349
- # =====================================================
350
- # RNC - ANEXOS
351
- # =====================================================
352
- class RNCAnexo(Base):
353
- __tablename__ = "rnc_anexo"
354
-
355
- id = Column(Integer, primary_key=True, index=True)
356
- rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
357
-
358
- nome_arquivo = Column(String(255), nullable=False)
359
- caminho = Column(String(500), nullable=False)
360
- conteudo_tipo = Column(String(120), nullable=True)
361
-
362
- enviado_por = Column(String(120), nullable=True, index=True)
363
- enviado_em = Column(DateTime, default=datetime.utcnow, index=True)
364
-
365
- rnc = relationship("RNC", back_populates="anexos")
366
-
367
-
368
- # =====================================================
369
- # AVISO GLOBAL (banner superior)
370
- # =====================================================
371
- class AvisoGlobal(Base):
372
- __tablename__ = "aviso_global"
373
-
374
- id = Column(Integer, primary_key=True, index=True)
375
- mensagem = Column(Text, nullable=False)
376
-
377
- # Estilo/visual
378
- bg_color = Column(String(32), default="#FFF3CD")
379
- text_color = Column(String(32), default="#664D03")
380
- largura = Column(String(16), default="100%")
381
- efeito = Column(String(16), default="marquee")
382
- velocidade = Column(Integer, default=20)
383
- font_size = Column(Integer, default=14)
384
-
385
- # Controle/estado
386
- ativo = Column(Boolean, default=True, index=True)
387
-
388
- # Auditoria
389
- created_at = Column(DateTime(timezone=True), server_default=func.now())
390
- updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
391
-
392
-
393
- # =====================================================
394
- # RECEBIMENTO — PLANILHA OFICIAL (UM REGISTRO POR LINHA)
395
- # Alinhado ao layout oficial de 37 colunas
396
- # =====================================================
397
- class RecebimentoRegistro(Base):
398
- __tablename__ = "recebimento_registros"
399
-
400
- # PK interno + ID da planilha
401
- id = Column(Integer, primary_key=True, autoincrement=True, index=True)
402
- id_planilha = Column(Integer, nullable=True, index=True, unique=True) # ID da planilha, se houver
403
-
404
- # Datas
405
- data = Column(Date, nullable=True)
406
- data_emissao = Column(Date, nullable=True)
407
-
408
- # Horas (texto HH:MM:SS para compatibilidade)
409
- hora_chegada_portaria = Column(String(8), nullable=True)
410
- hora_chegada_ifs = Column(String(8), nullable=True)
411
- hora_saida_ifs_wms = Column(String(8), nullable=True)
412
- hora_liberacao_operacao = Column(String(8), nullable=True)
413
- hora_chegada_operacao = Column(String(8), nullable=True)
414
- hora_saida_operacao = Column(String(8), nullable=True)
415
- hora_retorno_operacao = Column(String(8), nullable=True)
416
- hora_liberacao_motorista = Column(String(8), nullable=True)
417
-
418
- # Dados principais
419
- placa_veiculo = Column(String(50), nullable=True)
420
- transportadora = Column(String(255), nullable=True)
421
- po = Column(String(60), nullable=True)
422
- incoterms = Column(String(30), nullable=True)
423
- qtd_sku = Column(Integer, nullable=True)
424
- nota_fiscal = Column(String(80), nullable=True)
425
- fornecedor = Column(String(255), nullable=True)
426
-
427
- # Bools (SIM/NÃO/N/A)
428
- quimicos = Column(Boolean, nullable=True)
429
- fds = Column(Boolean, nullable=True)
430
- repetro = Column(Boolean, nullable=True)
431
- aprovado = Column(Boolean, nullable=True)
432
-
433
- # Status/Texto
434
- natureza_operacao = Column(String(120), nullable=True)
435
- tipo_operacao = Column(String(120), nullable=True)
436
- barco = Column(String(80), nullable=True)
437
-
438
- # Campo alinhado com a coluna "DIVERGENCIA" do layout oficial
439
- divergencia = Column(String(200), nullable=True)
440
-
441
- ifs = Column(String(120), nullable=True)
442
- wms = Column(String(120), nullable=True)
443
- fotografia = Column(String(255), nullable=True)
444
- entrega = Column(String(120), nullable=True)
445
- projeto = Column(String(120), nullable=True)
446
- good_receipt = Column(String(120), nullable=True)
447
- divergencia_recebimento = Column(String(255), nullable=True)
448
- qualidade = Column(String(120), nullable=True)
449
- divergencia_qualidade = Column(String(255), nullable=True)
450
- observacao = Column(Text, nullable=True)
451
- agendamento = Column(String(120), nullable=True)
452
- responsavel = Column(String(120), nullable=True)
453
-
454
- # Novos campos (opcionais) para colunas adicionais do layout
455
- po_alt = Column(String(60), nullable=True) # mapeia "P.O" (alternativo)
456
- pn = Column(String(120), nullable=True) # mapeia "PN"
457
- lot_batch = Column(String(120), nullable=True) # mapeia "LOT BATCH"
458
-
459
- # Auditoria mínima
460
- created_by = Column(String(150), nullable=True)
461
- updated_by = Column(String(150), nullable=True)
462
- created_at = Column(DateTime, default=datetime.utcnow)
463
- updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from sqlalchemy import (
3
+ Column,
4
+ Integer,
5
+ String,
6
+ Date,
7
+ DateTime,
8
+ Boolean,
9
+ ForeignKey,
10
+ Text
11
+ )
12
+ from sqlalchemy.orm import relationship
13
+ # If you face cyclic import issues, place Base here via declarative_base:
14
+ # from sqlalchemy.orm import declarative_base
15
+ # Base = declarative_base()
16
+ from datetime import datetime
17
+ from banco import Base
18
+ from sqlalchemy.sql import func # server_default em AvisoGlobal
19
+
20
+ # =====================================================
21
+ # TABELA EQUIPAMENTOS
22
+ # =====================================================
23
+ class Equipamento(Base):
24
+ __tablename__ = "equipamentos"
25
+
26
+ id = Column(Integer, primary_key=True, index=True)
27
+
28
+ # Identificação
29
+ fpso1 = Column(String, index=True, nullable=False)
30
+ fpso = Column(String, index=True, nullable=False)
31
+ data_coleta = Column(String, index=True, nullable=False) # sugestão futura: Date
32
+
33
+ # Responsáveis
34
+ especialista = Column(String, nullable=False)
35
+ conferente = Column(String, nullable=False)
36
+ osm = Column(String, nullable=False)
37
+
38
+ # Operacional
39
+ modal = Column(String, index=True, nullable=False)
40
+ quant_equip = Column(Integer, default=0)
41
+ mrob = Column(String, nullable=False)
42
+
43
+ # Métricas
44
+ linhas_osm = Column(Integer, default=0)
45
+ linhas_mrob = Column(Integer, default=0)
46
+ linhas_erros = Column(Integer, default=0)
47
+
48
+ # Erros
49
+ erro_storekeeper = Column(String)
50
+ erro_operacao = Column(String)
51
+ erro_especialista = Column(String)
52
+ erro_outros = Column(String)
53
+
54
+ # Dados complementares
55
+ inclusao_exclusao = Column(String)
56
+ po = Column(String)
57
+ part_number = Column(String)
58
+ material = Column(String)
59
+
60
+ solicitante = Column(String, nullable=False)
61
+ motivo = Column(String)
62
+ requisitante = Column(String, nullable=False)
63
+ nota_fiscal = Column(String, nullable=False)
64
+ impacto = Column(String, nullable=False)
65
+ dimensao = Column(String, nullable=False)
66
+
67
+ observacoes = Column(String)
68
+
69
+ # Dia de inclusão
70
+ dia_inclusao = Column(String, nullable=True, index=True)
71
+
72
+ # Auditoria
73
+ data_hora_input = Column(DateTime, default=datetime.utcnow, index=True)
74
+
75
+
76
+ # =====================================================
77
+ # TABELA FPSOS
78
+ # =====================================================
79
+ class FPSO(Base):
80
+ __tablename__ = "fpsos"
81
+
82
+ id = Column(Integer, primary_key=True)
83
+ nome = Column(String, unique=True, nullable=False, index=True)
84
+ ativo = Column(Boolean, default=True)
85
+ data_cadastro = Column(DateTime, default=datetime.utcnow)
86
+
87
+
88
+ # =====================================================
89
+ # LOG DE AUDITORIA (OFICIAL)
90
+ # =====================================================
91
+ class LogAcesso(Base):
92
+ __tablename__ = "log_acesso"
93
+
94
+ id = Column(Integer, primary_key=True)
95
+
96
+ usuario = Column(String, nullable=False, index=True)
97
+ acao = Column(String, nullable=False)
98
+ tabela = Column(String, nullable=True)
99
+ registro_id = Column(Integer, nullable=True)
100
+
101
+ data_hora = Column(DateTime, default=datetime.utcnow, index=True)
102
+
103
+
104
+ # =====================================================
105
+ # USUÁRIOS
106
+ # =====================================================
107
+ class Usuario(Base):
108
+ __tablename__ = "usuarios"
109
+
110
+ id = Column(Integer, primary_key=True)
111
+
112
+ # login & segurança
113
+ usuario = Column(String, unique=True, nullable=False, index=True)
114
+ senha = Column(String, nullable=False) # Armazena o HASH (bcrypt) da senha
115
+
116
+ # perfil & status
117
+ perfil = Column(String, nullable=False) # admin | usuario | consulta
118
+ ativo = Column(Boolean, default=True)
119
+
120
+ # auditoria
121
+ data_criacao = Column(DateTime, default=datetime.utcnow)
122
+
123
+ # UI/contato
124
+ nome = Column(String, nullable=True, index=True)
125
+ email = Column(String, unique=True, index=True, nullable=True)
126
+
127
+ # aniversário
128
+ data_aniversario = Column(Date, nullable=True)
129
+
130
+ # ---- Compatibilidade (alias) ----
131
+ @property
132
+ def senha_hash(self) -> str | None:
133
+ """
134
+ Alias compatível para códigos que esperam 'senha_hash'.
135
+ Retorna o valor da coluna 'senha' (que deve conter o HASH bcrypt).
136
+ Não altera o schema e não exige migration.
137
+ """
138
+ return self.senha
139
+
140
+ def __repr__(self) -> str:
141
+ return f"<Usuario id={self.id} usuario={self.usuario!r} perfil={self.perfil!r} ativo={self.ativo}>"
142
+
143
+ # =====================================================
144
+ # QUIZ - PERGUNTAS
145
+ # =====================================================
146
+ class QuizPergunta(Base):
147
+ __tablename__ = "quiz_perguntas"
148
+
149
+ id = Column(Integer, primary_key=True)
150
+ pergunta = Column(String, nullable=False)
151
+ ativo = Column(Boolean, default=True)
152
+ data_criacao = Column(DateTime, default=datetime.utcnow)
153
+
154
+ respostas = relationship(
155
+ "QuizResposta",
156
+ back_populates="pergunta",
157
+ cascade="all, delete-orphan"
158
+ )
159
+
160
+
161
+ # =====================================================
162
+ # QUIZ - RESPOSTAS
163
+ # =====================================================
164
+ class QuizResposta(Base):
165
+ __tablename__ = "quiz_respostas"
166
+
167
+ id = Column(Integer, primary_key=True)
168
+ pergunta_id = Column(Integer, ForeignKey("quiz_perguntas.id"), nullable=False)
169
+ texto = Column(String, nullable=False)
170
+ correta = Column(Boolean, default=False)
171
+
172
+ pergunta = relationship("QuizPergunta", back_populates="respostas")
173
+
174
+
175
+ # =====================================================
176
+ # QUIZ - PONTUAÇÃO / RANKING
177
+ # =====================================================
178
+ class QuizPontuacao(Base):
179
+ __tablename__ = "quiz_pontuacoes"
180
+
181
+ id = Column(Integer, primary_key=True)
182
+ usuario = Column(String, nullable=False, index=True)
183
+ pontos = Column(Integer, nullable=False, default=0)
184
+ data = Column(DateTime, default=datetime.utcnow, index=True)
185
+
186
+
187
+ # =====================================================
188
+ # VÍDEOS - CATEGORIAS
189
+ # =====================================================
190
+ class VideoCategoria(Base):
191
+ __tablename__ = "video_categorias"
192
+
193
+ id = Column(Integer, primary_key=True)
194
+ nome = Column(String, nullable=False, unique=True)
195
+ ativo = Column(Boolean, default=True)
196
+ data_criacao = Column(DateTime, default=datetime.utcnow)
197
+
198
+
199
+ # =====================================================
200
+ # VÍDEOS
201
+ # =====================================================
202
+ class Video(Base):
203
+ __tablename__ = "videos"
204
+
205
+ id = Column(Integer, primary_key=True)
206
+ titulo = Column(String, nullable=False)
207
+ descricao = Column(String)
208
+ url = Column(String, nullable=False)
209
+
210
+ categoria_id = Column(Integer, ForeignKey("video_categorias.id"))
211
+ categoria = relationship("VideoCategoria")
212
+
213
+ ativo = Column(Boolean, default=True)
214
+ data_criacao = Column(DateTime, default=datetime.utcnow)
215
+
216
+
217
+ # =====================================================
218
+ # CALENDÁRIO - EVENTOS / LEMBRETES
219
+ # =====================================================
220
+ class EventoCalendario(Base):
221
+ __tablename__ = "eventos_calendario"
222
+
223
+ id = Column(Integer, primary_key=True)
224
+ titulo = Column(String, nullable=False)
225
+ descricao = Column(String)
226
+ data_evento = Column(Date, nullable=False)
227
+ data_lembrete = Column(Date)
228
+ ativo = Column(Boolean, default=True)
229
+ usuario_criacao = Column(String, nullable=False)
230
+ data_criacao = Column(DateTime, default=datetime.utcnow)
231
+
232
+
233
+ # =====================================================
234
+ # IOI-RUN - SUGESTÕES DO SISTEMA
235
+ # =====================================================
236
+ class IOIRunSugestao(Base):
237
+ __tablename__ = "ioirun_sugestao"
238
+
239
+ id = Column(Integer, primary_key=True, index=True)
240
+
241
+ # Identificação do autor
242
+ usuario = Column(String, nullable=False, index=True)
243
+
244
+ # Conteúdo
245
+ area = Column(String, nullable=True, index=True)
246
+ mensagem = Column(Text, nullable=False)
247
+
248
+ # Resposta do time (admin)
249
+ resposta = Column(Text, nullable=True)
250
+ status = Column(String, default="pendente", nullable=False, index=True) # pendente | respondida
251
+ responsavel = Column(String, nullable=True)
252
+
253
+ # Auditoria
254
+ data_envio = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
255
+ data_resposta = Column(DateTime, nullable=True)
256
+
257
+
258
+ # =====================================================
259
+ # RNC - REGISTRO DE NÃO CONFORMIDADES (FOR-SGQ-08)
260
+ # =====================================================
261
+ class RNC(Base):
262
+ __tablename__ = "rnc"
263
+
264
+ id = Column(Integer, primary_key=True, index=True)
265
+
266
+ # Identificação
267
+ codigo = Column(String(20), unique=True, index=True) # ex.: RNC-2026-0001
268
+ titulo = Column(String(200), nullable=False)
269
+ descricao = Column(Text, nullable=False)
270
+
271
+ # Cabeçalho do formulário
272
+ data_form = Column(Date, nullable=True, index=True) # Data do formulário
273
+ emitente = Column(String(120), nullable=True, index=True)
274
+ rnc_cliente_numero = Column(String(50), nullable=True)
275
+ cliente_emitente = Column(String(120), nullable=True)
276
+ area_solicitante = Column(String(120), nullable=True, index=True)
277
+ area_notificada = Column(String(120), nullable=True, index=True)
278
+ origem = Column(String(50), nullable=True, index=True) # Auditoria Interna/Externa/Outras
279
+
280
+ # Envolvidos
281
+ envolvido1_nome = Column(String(120), nullable=True)
282
+ envolvido1_matricula = Column(String(50), nullable=True)
283
+ envolvido1_funcao = Column(String(120), nullable=True)
284
+ envolvido2_nome = Column(String(120), nullable=True)
285
+ envolvido2_matricula = Column(String(50), nullable=True)
286
+ envolvido2_funcao = Column(String(120), nullable=True)
287
+
288
+ # Classificação
289
+ tipo = Column(String(50), nullable=True)
290
+ severidade = Column(String(20), nullable=True)
291
+ prioridade = Column(String(20), nullable=True)
292
+
293
+ # Status e prazos
294
+ status = Column(String(30), default="Aberta", nullable=False, index=True)
295
+ data_abertura = Column(DateTime, default=datetime.utcnow, index=True)
296
+ prazo = Column(DateTime, nullable=True, index=True)
297
+ encerrada_em = Column(DateTime, nullable=True, index=True)
298
+
299
+ # Responsáveis
300
+ responsavel = Column(String(120), nullable=True, index=True)
301
+ criado_por = Column(String(120), nullable=False, index=True)
302
+
303
+ # Complementares
304
+ cliente = Column(String(120), nullable=True)
305
+ local = Column(String(120), nullable=True)
306
+
307
+ # Análise das causas
308
+ metodologia = Column(String(120), nullable=True) # ex.: Ishikawa, 5 Porquês
309
+ causa_raiz = Column(Text, nullable=True) # descrição da causa raiz
310
+ ishikawa_json = Column(Text, nullable=True) # opcional: armazenar estrutura Ishikawa em JSON
311
+
312
+ # Auditoria
313
+ data_hora_input = Column(DateTime, default=datetime.utcnow, index=True)
314
+
315
+ # Relacionamentos
316
+ comentarios = relationship("RNCComentario", back_populates="rnc", cascade="all, delete-orphan")
317
+ acoes = relationship("RNCAcaoCorretiva", back_populates="rnc", cascade="all, delete-orphan")
318
+ anexos = relationship("RNCAnexo", back_populates="rnc", cascade="all, delete-orphan")
319
+
320
+
321
+ # =====================================================
322
+ # RNC - COMENTÁRIOS / TIMELINE
323
+ # =====================================================
324
+ class RNCComentario(Base):
325
+ __tablename__ = "rnc_comentario"
326
+
327
+ id = Column(Integer, primary_key=True, index=True)
328
+ rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
329
+
330
+ data = Column(DateTime, default=datetime.utcnow, index=True)
331
+ autor = Column(String(120), nullable=False, index=True)
332
+ mensagem = Column(Text, nullable=False)
333
+
334
+ status_novo = Column(String(30), nullable=True, index=True)
335
+ prazo_novo = Column(DateTime, nullable=True, index=True)
336
+ responsavel_novo = Column(String(120), nullable=True, index=True)
337
+
338
+ rnc = relationship("RNC", back_populates="comentarios")
339
+
340
+
341
+ # =====================================================
342
+ # RNC - AÇÕES CORRETIVAS / PREVENTIVAS
343
+ # =====================================================
344
+ class RNCAcaoCorretiva(Base):
345
+ __tablename__ = "rnc_acao"
346
+
347
+ id = Column(Integer, primary_key=True, index=True)
348
+ rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
349
+
350
+ descricao = Column(Text, nullable=False)
351
+ responsavel = Column(String(120), nullable=True, index=True)
352
+ prazo = Column(DateTime, nullable=True, index=True)
353
+
354
+ status = Column(String(30), default="Planejada", nullable=False, index=True)
355
+ eficacia = Column(String(30), nullable=True, index=True)
356
+ conclusao_em = Column(DateTime, nullable=True, index=True)
357
+
358
+ rnc = relationship("RNC", back_populates="acoes")
359
+
360
+
361
+ # =====================================================
362
+ # RNC - ANEXOS
363
+ # =====================================================
364
+ class RNCAnexo(Base):
365
+ __tablename__ = "rnc_anexo"
366
+
367
+ id = Column(Integer, primary_key=True, index=True)
368
+ rnc_id = Column(Integer, ForeignKey("rnc.id"), nullable=False, index=True)
369
+
370
+ nome_arquivo = Column(String(255), nullable=False)
371
+ caminho = Column(String(500), nullable=False)
372
+ conteudo_tipo = Column(String(120), nullable=True)
373
+
374
+ enviado_por = Column(String(120), nullable=True, index=True)
375
+ enviado_em = Column(DateTime, default=datetime.utcnow, index=True)
376
+
377
+ rnc = relationship("RNC", back_populates="anexos")
378
+
379
+
380
+ # =====================================================
381
+ # AVISO GLOBAL (banner superior)
382
+ # =====================================================
383
+ class AvisoGlobal(Base):
384
+ __tablename__ = "aviso_global"
385
+
386
+ id = Column(Integer, primary_key=True, index=True)
387
+ mensagem = Column(Text, nullable=False)
388
+
389
+ # Estilo/visual
390
+ bg_color = Column(String(32), default="#FFF3CD")
391
+ text_color = Column(String(32), default="#664D03")
392
+ largura = Column(String(16), default="100%")
393
+ efeito = Column(String(16), default="marquee")
394
+ velocidade = Column(Integer, default=20)
395
+ font_size = Column(Integer, default=14)
396
+
397
+ # Controle/estado
398
+ ativo = Column(Boolean, default=True, index=True)
399
+
400
+ # Auditoria
401
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
402
+ updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
403
+
404
+
405
+ # =====================================================
406
+ # RECEBIMENTO — PLANILHA OFICIAL (UM REGISTRO POR LINHA)
407
+ # Alinhado ao layout oficial de 37 colunas
408
+ # =====================================================
409
+ class RecebimentoRegistro(Base):
410
+ __tablename__ = "recebimento_registros"
411
+
412
+ # PK interno + ID da planilha
413
+ id = Column(Integer, primary_key=True, autoincrement=True, index=True)
414
+ id_planilha = Column(Integer, nullable=True, index=True, unique=True) # ID da planilha, se houver
415
+
416
+ # Datas
417
+ data = Column(Date, nullable=True)
418
+ data_emissao = Column(Date, nullable=True)
419
+
420
+ # Horas (texto HH:MM:SS para compatibilidade)
421
+ hora_chegada_portaria = Column(String(8), nullable=True)
422
+ hora_chegada_ifs = Column(String(8), nullable=True)
423
+ hora_saida_ifs_wms = Column(String(8), nullable=True)
424
+ hora_liberacao_operacao = Column(String(8), nullable=True)
425
+ hora_chegada_operacao = Column(String(8), nullable=True)
426
+ hora_saida_operacao = Column(String(8), nullable=True)
427
+ hora_retorno_operacao = Column(String(8), nullable=True)
428
+ hora_liberacao_motorista = Column(String(8), nullable=True)
429
+
430
+ # Dados principais
431
+ placa_veiculo = Column(String(50), nullable=True)
432
+ transportadora = Column(String(255), nullable=True)
433
+ po = Column(String(60), nullable=True)
434
+ incoterms = Column(String(30), nullable=True)
435
+ qtd_sku = Column(Integer, nullable=True)
436
+ nota_fiscal = Column(String(80), nullable=True)
437
+ fornecedor = Column(String(255), nullable=True)
438
+
439
+ # Bools (SIM/NÃO/N/A)
440
+ quimicos = Column(Boolean, nullable=True)
441
+ fds = Column(Boolean, nullable=True)
442
+ repetro = Column(Boolean, nullable=True)
443
+ aprovado = Column(Boolean, nullable=True)
444
+
445
+ # Status/Texto
446
+ natureza_operacao = Column(String(120), nullable=True)
447
+ tipo_operacao = Column(String(120), nullable=True)
448
+ barco = Column(String(80), nullable=True)
449
+
450
+ # Campo alinhado com a coluna "DIVERGENCIA" do layout oficial
451
+ divergencia = Column(String(200), nullable=True)
452
+
453
+ ifs = Column(String(120), nullable=True)
454
+ wms = Column(String(120), nullable=True)
455
+ fotografia = Column(String(255), nullable=True)
456
+ entrega = Column(String(120), nullable=True)
457
+ projeto = Column(String(120), nullable=True)
458
+ good_receipt = Column(String(120), nullable=True)
459
+ divergencia_recebimento = Column(String(255), nullable=True)
460
+ qualidade = Column(String(120), nullable=True)
461
+ divergencia_qualidade = Column(String(255), nullable=True)
462
+ observacao = Column(Text, nullable=True)
463
+ agendamento = Column(String(120), nullable=True)
464
+ responsavel = Column(String(120), nullable=True)
465
+
466
+ # Novos campos (opcionais) para colunas adicionais do layout
467
+ po_alt = Column(String(60), nullable=True) # mapeia "P.O" (alternativo)
468
+ pn = Column(String(120), nullable=True) # mapeia "PN"
469
+ lot_batch = Column(String(120), nullable=True) # mapeia "LOT BATCH"
470
+
471
+ # Auditoria mínima
472
+ created_by = Column(String(150), nullable=True)
473
+ updated_by = Column(String(150), nullable=True)
474
+ created_at = Column(DateTime, default=datetime.utcnow)
475
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)