feat: implementa sistema de comprovantes com centralizações e PIX
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user