diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d3f3baf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,262 @@
+# Created by https://www.toptal.com/developers/gitignore/api/python,flask
+# Edit at https://www.toptal.com/developers/gitignore?templates=python,flask
+
+### Flask ###
+instance/*
+!instance/.gitignore
+.webassets-cache
+.env
+
+### Flask.Python Stack ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+
+# C extensions
+
+# Distribution / packaging
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+
+# Installer logs
+
+# Unit test / coverage reports
+
+# Translations
+
+# Django stuff:
+
+# Flask stuff:
+
+# Scrapy stuff:
+
+# Sphinx documentation
+
+# PyBuilder
+
+# Jupyter Notebook
+
+# IPython
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+
+# Celery stuff
+
+# SageMath parsed files
+
+# Environments
+
+# Spyder project settings
+
+# Rope project settings
+
+# mkdocs documentation
+
+# mypy
+
+# Pyre type checker
+
+# pytype static type analyzer
+
+# Cython debug symbols
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+
+### Python Patch ###
+# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
+poetry.toml
+
+# ruff
+.ruff_cache/
+
+# LSP config files
+pyrightconfig.json
+
+# End of https://www.toptal.com/developers/gitignore/api/python,flask
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bc6c28a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+install:
+ pip install -r requirements.txt
+
+run:
+ python app.py
diff --git a/README.md b/README.md
index fc51c2f..62b96f3 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,15 @@
# controles
+
+## Para instalar
+
+```bash
+make install
+```
+
+## Para executar
+
+```bash
+make run
+```
+
+Acesse por: http://127.0.0.1:5000
diff --git a/app.py b/app.py
index 6fa5a1a..83da760 100644
--- a/app.py
+++ b/app.py
@@ -1,213 +1,262 @@
-from flask import Flask, request, render_template, redirect, url_for, jsonify
+from flask import Flask, request, render_template, redirect, url_for
from functions.database import execute_query
+from functions.database import (
+ Base,
+ Militante,
+ CotaMensal,
+ TipoPagamento,
+ Pagamento,
+ MaterialVendido,
+ TipoMaterial,
+ VendaJornalAvulso,
+ engine,
+ AssinaturaAnual,
+ RelatorioCotasMensais,
+ RelatorioVendasMateriais,
+ engine,
+)
+from sqlalchemy import create_engine, and_
+from sqlalchemy.orm import sessionmaker
+from datetime import datetime
+
+Session = sessionmaker(bind=engine)
+session = Session()
app = Flask(__name__)
+
+def session_run(model):
+ session.add(model)
+ try:
+ session.commit()
+ except Exception as e:
+ print(e)
+ session.rollback()
+
+
# Rota para criar um novo militante
-@app.route('/militantes/novo', methods=['GET', 'POST'])
+@app.route("/militantes/novo", methods=["GET", "POST"])
def novo_militante():
- if request.method == 'POST':
- nome = request.form['nome']
- cpf = request.form['cpf']
- email = request.form['email']
- telefone = request.form['telefone']
- endereco = request.form['endereco']
- filiado = request.form.get('filiado', False)
+ if request.method == "POST":
+ novo_militante = Militante(
+ nome=request.form["nome"],
+ cpf=request.form["cpf"],
+ email=request.form["email"],
+ telefone=request.form["telefone"],
+ endereco=request.form["endereco"],
+ filiado=request.form.get("filiado", False),
+ )
- query = '''
- INSERT INTO militantes (nome, cpf, email, telefone, endereco, filiado)
- VALUES (%s, %s, %s, %s, %s, %s)
- '''
- execute_query(query, (nome, cpf, email, telefone, endereco, filiado))
- return redirect(url_for('listar_militantes'))
+ session_run(novo_militante)
+ # execute_query(query, (nome, cpf, email, telefone, endereco, filiado))
+ return redirect(url_for("listar_militantes"))
+
+ return render_template("novo_militante.html")
- return render_template('novo_militante.html')
# Rota para listar militantes
-@app.route('/militantes')
+@app.route("/militantes")
def listar_militantes():
- query = 'SELECT * FROM militantes'
- militantes = execute_query(query)
- return render_template('listar_militantes.html', militantes=militantes)
+ militantes = session.query(Militante).all()
+ return render_template("listar_militantes.html", militantes=militantes)
+
# Rota para criar uma nova cota mensal
-@app.route('/cotas/novo', methods=['GET', 'POST'])
+@app.route("/cotas/novo", methods=["GET", "POST"])
def nova_cota():
- if request.method == 'POST':
- militante_id = request.form['militante_id']
- valor_antigo = request.form['valor_antigo']
- valor_novo = request.form['valor_novo']
- data_alteracao = request.form['data_alteracao']
+ if request.method == "POST":
+ cotas_mensais = CotaMensal(
+ militante_id=request.form["militante_id"],
+ valor_antigo=request.form["valor_antigo"],
+ valor_novo=request.form["valor_novo"],
+ data_alteracao=datetime.strptime(request.form["data_alteracao"], "%Y-%m-%d")
+ )
- query = '''
- INSERT INTO cotas_mensais (militante_id, valor_antigo, valor_novo, data_alteracao)
- VALUES (%s, %s, %s, %s)
- '''
- execute_query(query, (militante_id, valor_antigo, valor_novo, data_alteracao))
- return redirect(url_for('listar_cotas'))
+ session_run(cotas_mensais)
+ return redirect(url_for("listar_cotas"))
+
+ return render_template("nova_cota.html")
- return render_template('nova_cota.html')
# Rota para listar cotas mensais
-@app.route('/cotas')
+@app.route("/cotas")
def listar_cotas():
- query = 'SELECT * FROM cotas_mensais'
- cotas = execute_query(query)
- return render_template('listar_cotas.html', cotas=cotas)
+ cotas = session.query(CotaMensal).all()
+ return render_template("listar_cotas.html", cotas=cotas)
+
# Rota para criar um novo pagamento
-@app.route('/pagamentos/novo', methods=['GET', 'POST'])
+@app.route("/pagamentos/novo", methods=["GET", "POST"])
def novo_pagamento():
- if request.method == 'POST':
- militante_id = request.form['militante_id']
- tipo_pagamento_id = request.form['tipo_pagamento_id']
- valor = request.form['valor']
- data_pagamento = request.form['data_pagamento']
+ if request.method == "POST":
+ pagamentos = Pagamento(
+ militante_id=request.form["militante_id"],
+ tipo_pagamento_id=request.form["tipo_pagamento_id"],
+ valor=request.form["valor"],
+ data_pagamento=datetime.strptime(request.form["data_pagamento"], "%Y-%m-%d")
+ )
- query = '''
- INSERT INTO pagamentos (militante_id, tipo_pagamento_id, valor, data_pagamento)
- VALUES (%s, %s, %s, %s)
- '''
- execute_query(query, (militante_id, tipo_pagamento_id, valor, data_pagamento))
- return redirect(url_for('listar_pagamentos'))
+ session_run(pagamentos)
+ return redirect(url_for("listar_pagamentos"))
+
+ return render_template("novo_pagamento.html")
- return render_template('novo_pagamento.html')
# Rota para listar pagamentos
-@app.route('/pagamentos')
+@app.route("/pagamentos")
def listar_pagamentos():
- query = 'SELECT * FROM pagamentos'
- pagamentos = execute_query(query)
- return render_template('listar_pagamentos.html', pagamentos=pagamentos)
+ pagamentos = session.query(Pagamento).all()
+ return render_template("listar_pagamentos.html", pagamentos=pagamentos)
+
# Rota para criar um novo material vendido
-@app.route('/materiais/novo', methods=['GET', 'POST'])
+@app.route("/materiais/novo", methods=["GET", "POST"])
def novo_material():
- if request.method == 'POST':
- militante_id = request.form['militante_id']
- tipo_material_id = request.form['tipo_material_id']
- descricao = request.form['descricao']
- valor = request.form['valor']
- data_venda = request.form['data_venda']
+ if request.method == "POST":
+ materiais_vendidos = MaterialVendido(
+ militante_id=request.form["militante_id"],
+ tipo_material_id=request.form["tipo_material_id"],
+ descricao=request.form["descricao"],
+ valor=request.form["valor"],
+ data_venda=datetime.strptime(request.form["data_venda"], "%Y-%m-%d"),
+ )
- query = '''
- INSERT INTO materiais_vendidos (militante_id, tipo_material_id, descricao, valor, data_venda)
- VALUES (%s, %s, %s, %s, %s)
- '''
- execute_query(query, (militante_id, tipo_material_id, descricao, valor, data_venda))
- return redirect(url_for('listar_materiais'))
+ session_run(materiais_vendidos)
+ return redirect(url_for("listar_materiais"))
+
+ return render_template("novo_material.html")
- return render_template('novo_material.html')
# Rota para listar materiais vendidos
-@app.route('/materiais')
+@app.route("/materiais")
def listar_materiais():
- query = 'SELECT * FROM materiais_vendidos'
- materiais = execute_query(query)
- return render_template('listar_materiais.html', materiais=materiais)
+ materiais = session.query(MaterialVendido).all()
+ return render_template("listar_materiais.html", materiais=materiais)
+
# Rota para criar uma nova venda de jornais avulsos
-@app.route('/jornais/novo', methods=['GET', 'POST'])
+@app.route("/jornais/novo", methods=["GET", "POST"])
def nova_venda_jornal():
- if request.method == 'POST':
- militante_id = request.form['militante_id']
- quantidade = request.form['quantidade']
- valor_total = request.form['valor_total']
- data_venda = request.form['data_venda']
+ if request.method == "POST":
+ vendas_jornais_avulsos = VendaJornalAvulso(
+ militante_id=request.form["militante_id"],
+ quantidade=request.form["quantidade"],
+ valor_total=request.form["valor_total"],
+ data_venda=datetime.strptime(request.form["data_venda"], "%Y-%m-%d"),
+ )
- query = '''
- INSERT INTO vendas_jornais_avulsos (militante_id, quantidade, valor_total, data_venda)
- VALUES (%s, %s, %s, %s)
- '''
- execute_query(query, (militante_id, quantidade, valor_total, data_venda))
- return redirect(url_for('listar_vendas_jornal'))
+ session_run(VendaJornalAvulso)
+ return redirect(url_for("listar_vendas_jornal"))
+
+ return render_template("nova_venda_jornal.html")
- return render_template('nova_venda_jornal.html')
# Rota para listar vendas de jornais avulsos
-@app.route('/jornais')
+@app.route("/jornais")
def listar_vendas_jornal():
- query = 'SELECT * FROM vendas_jornais_avulsos'
- vendas = execute_query(query)
- return render_template('listar_vendas_jornal.html', vendas=vendas)
+ vendas = session.query(VendaJornalAvulso).all()
+ return render_template("listar_vendas_jornal.html", vendas=vendas)
+
# Rota para criar uma nova assinatura anual
-@app.route('/assinaturas/novo', methods=['GET', 'POST'])
+@app.route("/assinaturas/novo", methods=["GET", "POST"])
def nova_assinatura():
- if request.method == 'POST':
- militante_id = request.form['militante_id']
- tipo_material_id = request.form['tipo_material_id']
- quantidade = request.form['quantidade']
- valor_total = request.form['valor_total']
- data_inicio = request.form['data_inicio']
- data_fim = request.form['data_fim']
+ if request.method == "POST":
+ assinaturas_anuais = AssinaturaAnual(
+ militante_id=request.form["militante_id"],
+ tipo_material_id=request.form["tipo_material_id"],
+ quantidade=request.form["quantidade"],
+ valor_total=request.form["valor_total"],
+ data_inicio=datetime.strptime(request.form["data_inicio"], "%Y-%m-%d"),
+ data_fim=datetime.strptime(request.form["data_fim"], "%Y-%m-%d")
+ )
- query = '''
- INSERT INTO assinaturas_anuais (militante_id, tipo_material_id, quantidade, valor_total, data_inicio, data_fim)
- VALUES (%s, %s, %s, %s, %s, %s)
- '''
- execute_query(query, (militante_id, tipo_material_id, quantidade, valor_total, data_inicio, data_fim))
- return redirect(url_for('listar_assinaturas'))
+ session_run(assinaturas_anuais)
+ return redirect(url_for("listar_assinaturas"))
+
+ return render_template("nova_assinatura.html")
- return render_template('nova_assinatura.html')
# Rota para listar assinaturas anuais
-@app.route('/assinaturas')
+@app.route("/assinaturas")
def listar_assinaturas():
- query = 'SELECT * FROM assinaturas_anuais'
- assinaturas = execute_query(query)
- return render_template('listar_assinaturas.html', assinaturas=assinaturas)
+ assinaturas = session.query(AssinaturaAnual).all()
+ return render_template("listar_assinaturas.html", assinaturas=assinaturas)
+
# Rota para criar um novo relatório de cotas mensais
-@app.route('/relatorios/cotas/novo', methods=['GET', 'POST'])
+@app.route("/relatorios/cotas/novo", methods=["GET", "POST"])
def novo_relatorio_cotas():
- if request.method == 'POST':
- setor_id = request.form['setor_id']
- comite_id = request.form['comite_id']
- total_cotas = request.form['total_cotas']
- data_relatorio = request.form['data_relatorio']
+ if request.method == "POST":
+ relatorio_cotas_mensais = RelatorioCotasMensais(
+ setor_id=request.form["setor_id"],
+ comite_id=request.form["comite_id"],
+ total_cotas=request.form["total_cotas"],
+ data_relatorio=datetime.strptime(request.form["data_relatorio"], "%Y-%m-%d")
+ )
- query = '''
- INSERT INTO relatorio_cotas_mensais (setor_id, comite_id, total_cotas, data_relatorio)
- VALUES (%s, %s, %s, %s)
- '''
- execute_query(query, (setor_id, comite_id, total_cotas, data_relatorio))
- return redirect(url_for('listar_relatorios_cotas'))
+ session_run(relatorio_cotas_mensais)
+ return redirect(url_for("listar_relatorios_cotas"))
+
+ return render_template("novo_relatorio_cotas.html")
- return render_template('novo_relatorio_cotas.html')
# Rota para listar relatórios de cotas mensais
-@app.route('/relatorios/cotas')
+@app.route("/relatorios/cotas")
def listar_relatorios_cotas():
- query = 'SELECT * FROM relatorio_cotas_mensais'
- relatorios = execute_query(query)
- return render_template('listar_relatorios_cotas.html', relatorios=relatorios)
+ relatorios = session.query(RelatorioCotasMensais).all()
+ return render_template("listar_relatorios_cotas.html", relatorios=relatorios)
+
# Rota para criar um novo relatório de vendas de materiais
-@app.route('/relatorios/vendas/novo', methods=['GET', 'POST'])
+@app.route("/relatorios/vendas/novo", methods=["GET", "POST"])
def novo_relatorio_vendas():
- if request.method == 'POST':
- setor_id = request.form['setor_id']
- comite_id = request.form['comite_id']
- total_vendas = request.form['total_vendas']
- data_relatorio = request.form['data_relatorio']
+ if request.method == "POST":
+ relatorio_vendas_materiais = RelatorioVendasMateriais(
+ setor_id=request.form["setor_id"],
+ comite_id=request.form["comite_id"],
+ total_vendas=request.form["total_vendas"],
+ data_relatorio=datetime.strptime(request.form["data_relatorio"], "%Y-%m-%d")
+ )
- query = '''
- INSERT INTO relatorio_vendas_materiais (setor_id, comite_id, total_vendas, data_relatorio)
- VALUES (%s, %s, %s, %s)
- '''
- execute_query(query, (setor_id, comite_id, total_vendas, data_relatorio))
- return redirect(url_for('listar_relatorios_vendas'))
+ session_run(relatorio_vendas_materiais)
+ return redirect(url_for("listar_relatorios_vendas"))
+
+ return render_template("novo_relatorio_vendas.html")
- return render_template('novo_relatorio_vendas.html')
# Rota para listar relatórios de vendas de materiais
-@app.route('/relatorios/vendas')
+@app.route("/relatorios/vendas")
def listar_relatorios_vendas():
- query = 'SELECT * FROM relatorio_vendas_materiais'
- relatorios = execute_query(query)
- return render_template('listar_relatorios_vendas.html', relatorios=relatorios)
+ relatorios = session.query(RelatorioVendasMateriais).all()
+ return render_template("listar_relatorios_vendas.html", relatorios=relatorios)
+
+
+@app.route("/")
+def home():
+ links = []
+ for rule in app.url_map.iter_rules():
+ # Filter out rules we can't navigate to in a browser
+ # and rules that require parameters
+ if "GET" in rule.methods and has_no_empty_params(rule):
+ url = url_for(rule.endpoint, **(rule.defaults or {}))
+ links.append((url, rule.endpoint))
+ l_html = ""
+ for l in links:
+ l_html += f'{l[1].replace("_"," ")}
'
+ home_html = f"""
+
Links
+ {l_html} + """ + return home_html + + +def has_no_empty_params(rule): + defaults = rule.defaults if rule.defaults is not None else () + arguments = rule.arguments if rule.arguments is not None else () + return len(defaults) >= len(arguments) + # Iniciar o servidor Flask -if __name__ == '__main__': - app.run(debug=True) \ No newline at end of file +if __name__ == "__main__": + app.run(debug=True) diff --git a/database.db b/database.db new file mode 100644 index 0000000..3ea1bba Binary files /dev/null and b/database.db differ diff --git a/functions/database.py b/functions/database.py index cc22955..9011125 100644 --- a/functions/database.py +++ b/functions/database.py @@ -1,8 +1,16 @@ import mysql.connector from mysql.connector import Error -from config.db_config import db_config +# from config.db_config import db_config +from sqlalchemy import create_engine, Column, Integer, String, Boolean, Numeric, Date, ForeignKey +from sqlalchemy.orm import relationship +from sqlalchemy.ext.declarative import declarative_base -def get_db_connection(): +Base = declarative_base() + +engine = create_engine('sqlite:///database.db', echo=True) +Base = declarative_base() + +def get_db_connection(db_config): try: connection = mysql.connector.connect(**db_config) return connection @@ -11,7 +19,7 @@ def get_db_connection(): return None def execute_query(query, params=None): - connection = get_db_connection() + connection = get_db_connection('') if connection is None: return None try: @@ -25,4 +33,143 @@ def execute_query(query, params=None): finally: if connection.is_connected(): cursor.close() - connection.close() \ No newline at end of file + connection.close() + +class Militante(Base): + __tablename__ = 'militantes' + + id = Column(Integer, primary_key=True, autoincrement=True) + nome = Column(String(100), nullable=False) + cpf = Column(String(14), unique=True) + email = Column(String(100), unique=True) + telefone = Column(String(15)) + endereco = Column(String(255)) + filiado = Column(Boolean, default=False) + + cotas_mensais = relationship("CotaMensal", back_populates="militante") + pagamentos = relationship("Pagamento", back_populates="militante") + materiais_vendidos = relationship("MaterialVendido", back_populates="militante") + vendas_jornais = relationship("VendaJornalAvulso", back_populates="militante") + assinaturas = relationship("AssinaturaAnual", back_populates="militante") + +class CotaMensal(Base): + __tablename__ = 'cotas_mensais' + + id = Column(Integer, primary_key=True, autoincrement=True) + militante_id = Column(Integer, ForeignKey('militantes.id')) + valor_antigo = Column(Numeric(10, 2), nullable=False) + valor_novo = Column(Numeric(10, 2), nullable=False) + data_alteracao = Column(Date, nullable=False) + + militante = relationship("Militante", back_populates="cotas_mensais") + +class TipoPagamento(Base): + __tablename__ = 'tipos_pagamento' + + id = Column(Integer, primary_key=True, autoincrement=True) + descricao = Column(String(100), nullable=False) + + pagamentos = relationship("Pagamento", back_populates="tipo_pagamento") + +class Pagamento(Base): + __tablename__ = 'pagamentos' + + id = Column(Integer, primary_key=True, autoincrement=True) + militante_id = Column(Integer, ForeignKey('militantes.id')) + tipo_pagamento_id = Column(Integer, ForeignKey('tipos_pagamento.id')) + valor = Column(Numeric(10, 2), nullable=False) + data_pagamento = Column(Date, nullable=False) + + militante = relationship("Militante", back_populates="pagamentos") + tipo_pagamento = relationship("TipoPagamento", back_populates="pagamentos") + +class TipoMaterial(Base): + __tablename__ = 'tipos_materiais' + + id = Column(Integer, primary_key=True, autoincrement=True) + descricao = Column(String(100), nullable=False) + + materiais_vendidos = relationship("MaterialVendido", back_populates="tipo_material") + assinaturas = relationship("AssinaturaAnual", back_populates="tipo_material") + +class MaterialVendido(Base): + __tablename__ = 'materiais_vendidos' + + id = Column(Integer, primary_key=True, autoincrement=True) + militante_id = Column(Integer, ForeignKey('militantes.id')) + tipo_material_id = Column(Integer, ForeignKey('tipos_materiais.id')) + descricao = Column(String(255), nullable=False) + valor = Column(Numeric(10, 2), nullable=False) + data_venda = Column(Date, nullable=False) + + militante = relationship("Militante", back_populates="materiais_vendidos") + tipo_material = relationship("TipoMaterial", back_populates="materiais_vendidos") + +class VendaJornalAvulso(Base): + __tablename__ = 'vendas_jornais_avulsos' + + id = Column(Integer, primary_key=True, autoincrement=True) + militante_id = Column(Integer, ForeignKey('militantes.id')) + quantidade = Column(Integer, nullable=False) + valor_total = Column(Numeric(10, 2), nullable=False) + data_venda = Column(Date, nullable=False) + + militante = relationship("Militante", back_populates="vendas_jornais") + +class AssinaturaAnual(Base): + __tablename__ = 'assinaturas_anuais' + + id = Column(Integer, primary_key=True, autoincrement=True) + militante_id = Column(Integer, ForeignKey('militantes.id')) + tipo_material_id = Column(Integer, ForeignKey('tipos_materiais.id')) + quantidade = Column(Integer, nullable=False) + valor_total = Column(Numeric(10, 2), nullable=False) + data_inicio = Column(Date, nullable=False) + data_fim = Column(Date, nullable=False) + + militante = relationship("Militante", back_populates="assinaturas") + tipo_material = relationship("TipoMaterial", back_populates="assinaturas") + +class Setor(Base): + __tablename__ = 'setores' + + id = Column(Integer, primary_key=True, autoincrement=True) + nome = Column(String(100), nullable=False) + + relatorios_cotas = relationship("RelatorioCotasMensais", back_populates="setor") + relatorios_vendas = relationship("RelatorioVendasMateriais", back_populates="setor") + +class ComiteCentral(Base): + __tablename__ = 'comites_centrais' + + id = Column(Integer, primary_key=True, autoincrement=True) + nome = Column(String(100), nullable=False) + + relatorios_cotas = relationship("RelatorioCotasMensais", back_populates="comite") + relatorios_vendas = relationship("RelatorioVendasMateriais", back_populates="comite") + +class RelatorioCotasMensais(Base): + __tablename__ = 'relatorio_cotas_mensais' + + id = Column(Integer, primary_key=True, autoincrement=True) + setor_id = Column(Integer, ForeignKey('setores.id')) + comite_id = Column(Integer, ForeignKey('comites_centrais.id')) + total_cotas = Column(Numeric(10, 2), nullable=False) + data_relatorio = Column(Date, nullable=False) + + setor = relationship("Setor", back_populates="relatorios_cotas") + comite = relationship("ComiteCentral", back_populates="relatorios_cotas") + +class RelatorioVendasMateriais(Base): + __tablename__ = 'relatorio_vendas_materiais' + + id = Column(Integer, primary_key=True, autoincrement=True) + setor_id = Column(Integer, ForeignKey('setores.id')) + comite_id = Column(Integer, ForeignKey('comites_centrais.id')) + total_vendas = Column(Numeric(10, 2), nullable=False) + data_relatorio = Column(Date, nullable=False) + + setor = relationship("Setor", back_populates="relatorios_vendas") + comite = relationship("ComiteCentral", back_populates="relatorios_vendas") + +Base.metadata.create_all(engine) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..960ca9c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,19 @@ +black==24.10.0 +blinker==1.9.0 +click==8.1.7 +Flask==3.1.0 +greenlet==3.1.1 +importlib_metadata==8.5.0 +itsdangerous==2.2.0 +Jinja2==3.1.4 +MarkupSafe==3.0.2 +mypy-extensions==1.0.0 +mysql-connector-python==9.1.0 +packaging==24.2 +pathspec==0.12.1 +platformdirs==4.3.6 +SQLAlchemy==2.0.36 +tomli==2.2.1 +typing_extensions==4.12.2 +Werkzeug==3.1.3 +zipp==3.21.0 diff --git a/templates/listar_assinaturas.html b/templates/listar_assinaturas.html index 51eb07a..64b2168 100644 --- a/templates/listar_assinaturas.html +++ b/templates/listar_assinaturas.html @@ -33,5 +33,6 @@ {% endfor %} + Home