feat: implementa sistema de comprovantes com centralizações e PIX

This commit is contained in:
LS
2025-04-16 13:54:31 -03:00
parent 813c968efd
commit 8ff58cc51e
15 changed files with 581 additions and 480 deletions

View File

@@ -14,6 +14,9 @@ from flask_login import UserMixin
from .rbac import Role, Permission, role_permissions, user_roles
from .base import Base, engine, Session
import logging
import qrcode
from PIL import Image
import re
# Configurar caminho do banco de dados
db_dir = Path.home() / '.local' / 'share' / 'controles'
@@ -329,7 +332,6 @@ class Pagamento(Base):
data_pagamento = Column(Date, nullable=False)
militante = relationship("Militante", back_populates="pagamentos")
transacoes_pix = relationship("TransacaoPIX", back_populates="pagamento")
class TipoMaterial(Base):
__tablename__ = 'tipos_materiais'
@@ -608,6 +610,49 @@ class Relatorio(Base):
setor = relationship("Setor", foreign_keys=[setor_id])
cr = relationship("ComiteRegional", foreign_keys=[cr_id])
class CampanhaFinanceira(Base):
__tablename__ = 'campanhas_financeiras'
id = Column(Integer, primary_key=True, autoincrement=True)
nome = Column(String(100), nullable=False)
descricao = Column(Text)
data_inicio = Column(Date, nullable=False)
data_fim = Column(Date, nullable=False)
meta = Column(Numeric(10, 2), nullable=False)
valor_arrecadado = Column(Numeric(10, 2), default=0)
status = Column(String(20), default='Em andamento') # Em andamento, Concluída, Cancelada
comprovantes = relationship("Comprovante", back_populates="campanha")
class TipoComprovante(Base):
__tablename__ = 'tipos_comprovante'
id = Column(Integer, primary_key=True)
descricao = Column(String(50), nullable=False)
valor = Column(Numeric(10, 2), nullable=False)
class CentralizacaoComprovante(Base):
__tablename__ = 'centralizacoes_comprovante'
id = Column(Integer, primary_key=True, autoincrement=True)
comprovante_id = Column(Integer, ForeignKey('comprovantes.id'), nullable=False)
tipo_comprovante = Column(String(50), nullable=False) # Cota, Jornal, Assinatura, etc.
valor = Column(Numeric(10, 2), nullable=False)
comprovante = relationship("Comprovante", back_populates="centralizacoes")
class Comprovante(Base):
__tablename__ = 'comprovantes'
id = Column(Integer, primary_key=True)
militante_id = Column(Integer, ForeignKey('militantes.id'), nullable=False)
data_comprovante = Column(Date, nullable=False)
forma_pagamento = Column(String(20), nullable=False) # PIX, transferência/DOC, depósito, maquininha
campanha_id = Column(Integer, ForeignKey('campanhas_financeiras.id'))
militante = relationship("Militante", back_populates="comprovantes")
transacoes_pix = relationship("TransacaoPIX", back_populates="comprovante")
campanha = relationship("CampanhaFinanceira", back_populates="comprovantes")
centralizacoes = relationship("CentralizacaoComprovante", back_populates="comprovante", cascade="all, delete-orphan")
class TransacaoPIX(Base):
__tablename__ = 'transacoes_pix'
@@ -618,25 +663,9 @@ class TransacaoPIX(Base):
data_pagamento = Column(DateTime)
status = Column(String(20)) # Pendente, Pago, Expirado
qr_code = Column(Text)
pagamento_id = Column(Integer, ForeignKey('pagamentos.id'))
comprovante_id = Column(Integer, ForeignKey('comprovantes.id'))
pagamento = relationship("Pagamento", back_populates="transacoes_pix")
class TipoComprovante(Base):
__tablename__ = 'tipos_comprovante'
id = Column(Integer, primary_key=True)
descricao = Column(String(50), nullable=False)
valor = Column(Float, nullable=False)
class Comprovante(Base):
__tablename__ = 'comprovantes'
id = Column(Integer, primary_key=True)
militante_id = Column(Integer, ForeignKey('militantes.id'), nullable=False)
tipo_comprovante = Column(String(50)) # Cota, Jornal, Assinatura, etc.
data_comprovante = Column(Date, nullable=False)
militante = relationship("Militante", back_populates="comprovantes")
transacoes_pix = relationship("TransacaoPIX", back_populates="comprovante")
comprovante = relationship("Comprovante", back_populates="transacoes_pix")
def init_database():
"""Inicializa o banco de dados com dados básicos"""
@@ -677,9 +706,30 @@ def init_database():
session.add(comite)
session.commit()
# Gerar OTP para admin
admin_otp_secret = pyotp.random_base32()
print(f"Novo OTP gerado: {admin_otp_secret}")
# Verificar se existe QR code do admin
admin_otp_secret = None
qr_path = 'admin_qr.png'
if os.path.exists(qr_path):
try:
# Tentar ler o QR code existente
from pyzbar.pyzbar import decode
qr_data = decode(Image.open(qr_path))
if qr_data:
# O URI do OTP está no formato: otpauth://totp/Sistema%20de%20Controles:admin?secret=XXXXX&issuer=Sistema%20de%20Controles
uri = qr_data[0].data.decode('utf-8')
# Extrair o secret do URI
match = re.search(r'secret=([A-Z0-9]+)', uri)
if match:
admin_otp_secret = match.group(1)
print("OTP existente encontrado no QR code")
except Exception as e:
print(f"Erro ao ler QR code existente: {e}")
if not admin_otp_secret:
# Se não conseguiu ler o QR code ou ele não existe, gera um novo
admin_otp_secret = pyotp.random_base32()
print(f"Novo OTP gerado: {admin_otp_secret}")
# Criar usuário admin
admin_role = session.query(Role).filter_by(nome="Administrador").first()
@@ -698,23 +748,23 @@ def init_database():
session.add(admin)
session.commit()
# Gerar QR code
totp = pyotp.totp.TOTP(admin_otp_secret)
provisioning_uri = totp.provisioning_uri("admin", issuer_name="Sistema de Controles")
import qrcode
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(provisioning_uri)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save('admin_qr.png')
# Gerar QR code apenas se não existir
if not os.path.exists(qr_path):
totp = pyotp.totp.TOTP(admin_otp_secret)
provisioning_uri = totp.provisioning_uri("admin", issuer_name="Sistema de Controles")
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(provisioning_uri)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save(qr_path)
print("=== Usuário Admin Criado ===")
print(f"Username: admin")
print(f"Senha: admin123")
print(f"Email: {admin.email}")
print(f"OTP Secret: {admin_otp_secret}")
print(f"QR Code: admin_qr.png")
print(f"QR Code: {qr_path}")
# Importar e executar o seed após criar todas as dependências
from seed_data import seed_database

View File

@@ -68,6 +68,8 @@ class Permission(Base):
EDIT_OWN_DATA = "edit_own_data"
VIEW_CELL_DATA = "view_cell_data"
CREATE_MILITANT = "create_militant" # Nova permissão para criar militantes
MANAGE_MATERIALS = "manage_materials" # Nova permissão para gerenciar materiais
MANAGE_REPORTS = "manage_reports" # Nova permissão para gerenciar relatórios
# Permissões de célula
MANAGE_CELL_MEMBERS = "manage_cell_members"
@@ -102,13 +104,15 @@ class Permission(Base):
(Permission.VIEW_OWN_DATA, "Visualizar próprios dados"),
(Permission.EDIT_OWN_DATA, "Editar próprios dados"),
(Permission.VIEW_CELL_DATA, "Visualizar dados da célula"),
(Permission.CREATE_MILITANT, "Criar novos militantes"), # Nova permissão
(Permission.CREATE_MILITANT, "Criar novos militantes"),
(Permission.MANAGE_MATERIALS, "Gerenciar materiais"),
(Permission.MANAGE_REPORTS, "Gerenciar relatórios"),
# Permissões de célula
(Permission.MANAGE_CELL_MEMBERS, "Gerenciar membros da célula"),
(Permission.CREATE_CELL_MEMBER, "Criar membros na célula"),
(Permission.VIEW_CELL_REPORTS, "Visualizar relatórios da célula"),
(Permission.MANAGE_CELL_REPORTS, "Gerenciar relatórios da célula"), # Nova permissão
(Permission.MANAGE_CELL_REPORTS, "Gerenciar relatórios da célula"),
(Permission.REGISTER_CELL_PAYMENT, "Registrar pagamentos da célula"),
# Permissões de setor
@@ -193,7 +197,8 @@ def init_rbac():
session.query(Permission).filter_by(nome=Permission.CREATE_CELL_MEMBER).first(),
session.query(Permission).filter_by(nome=Permission.VIEW_CELL_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_CELL_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.REGISTER_CELL_PAYMENT).first()
session.query(Permission).filter_by(nome=Permission.REGISTER_CELL_PAYMENT).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_MATERIALS).first()
]
# Membro de Setor
@@ -207,7 +212,8 @@ def init_rbac():
session.query(Permission).filter_by(nome=Permission.VIEW_CELL_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_CELL_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.VIEW_SECTOR_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.REGISTER_SECTOR_PAYMENT).first()
session.query(Permission).filter_by(nome=Permission.REGISTER_SECTOR_PAYMENT).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_MATERIALS).first()
]
# Secretário de Setor
@@ -223,7 +229,8 @@ def init_rbac():
session.query(Permission).filter_by(nome=Permission.VIEW_SECTOR_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_SECTOR_CELLS).first(),
session.query(Permission).filter_by(nome=Permission.CREATE_SECTOR_CELL).first(),
session.query(Permission).filter_by(nome=Permission.REGISTER_SECTOR_PAYMENT).first()
session.query(Permission).filter_by(nome=Permission.REGISTER_SECTOR_PAYMENT).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_MATERIALS).first()
]
# Membro de CR
@@ -240,7 +247,8 @@ def init_rbac():
session.query(Permission).filter_by(nome=Permission.MANAGE_SECTOR_CELLS).first(),
session.query(Permission).filter_by(nome=Permission.CREATE_SECTOR_CELL).first(),
session.query(Permission).filter_by(nome=Permission.VIEW_CR_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.REGISTER_CR_PAYMENT).first()
session.query(Permission).filter_by(nome=Permission.REGISTER_CR_PAYMENT).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_MATERIALS).first()
]
# Secretário de CR
@@ -259,7 +267,8 @@ def init_rbac():
session.query(Permission).filter_by(nome=Permission.VIEW_CR_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_CR_SECTORS).first(),
session.query(Permission).filter_by(nome=Permission.CREATE_CR_SECTOR).first(),
session.query(Permission).filter_by(nome=Permission.REGISTER_CR_PAYMENT).first()
session.query(Permission).filter_by(nome=Permission.REGISTER_CR_PAYMENT).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_MATERIALS).first()
]
# Membro do CC
@@ -279,7 +288,8 @@ def init_rbac():
session.query(Permission).filter_by(nome=Permission.MANAGE_CR_SECTORS).first(),
session.query(Permission).filter_by(nome=Permission.CREATE_CR_SECTOR).first(),
session.query(Permission).filter_by(nome=Permission.VIEW_CC_REPORTS).first(),
session.query(Permission).filter_by(nome=Permission.REGISTER_CC_PAYMENT).first()
session.query(Permission).filter_by(nome=Permission.REGISTER_CC_PAYMENT).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_MATERIALS).first()
]
# Secretário Geral
@@ -302,7 +312,8 @@ def init_rbac():
session.query(Permission).filter_by(nome=Permission.MANAGE_CC_CRS).first(),
session.query(Permission).filter_by(nome=Permission.CREATE_CC_CR).first(),
session.query(Permission).filter_by(nome=Permission.REGISTER_CC_PAYMENT).first(),
session.query(Permission).filter_by(nome=Permission.SYSTEM_CONFIG).first()
session.query(Permission).filter_by(nome=Permission.SYSTEM_CONFIG).first(),
session.query(Permission).filter_by(nome=Permission.MANAGE_MATERIALS).first()
]
session.commit()