diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73baf91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,264 @@ +# 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 + +database.db + +# 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/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 diff --git a/templates/listar_cotas.html b/templates/listar_cotas.html index 4ab4e33..5dd5760 100644 --- a/templates/listar_cotas.html +++ b/templates/listar_cotas.html @@ -29,5 +29,6 @@ {% endfor %} + Home diff --git a/templates/listar_materiais.html b/templates/listar_materiais.html index e78b172..c392f3e 100644 --- a/templates/listar_materiais.html +++ b/templates/listar_materiais.html @@ -31,5 +31,6 @@ {% endfor %} + Home diff --git a/templates/listar_militantes.html b/templates/listar_militantes.html index 416e70b..77ac145 100644 --- a/templates/listar_militantes.html +++ b/templates/listar_militantes.html @@ -12,5 +12,6 @@ {% endfor %} Adicionar Novo Militante + Home \ No newline at end of file diff --git a/templates/listar_pagamentos.html b/templates/listar_pagamentos.html index ffdf8fd..c5317bc 100644 --- a/templates/listar_pagamentos.html +++ b/templates/listar_pagamentos.html @@ -29,5 +29,6 @@ {% endfor %} + Home diff --git a/templates/listar_relatorios_cotas.html b/templates/listar_relatorios_cotas.html index 9abf22b..1fac21d 100644 --- a/templates/listar_relatorios_cotas.html +++ b/templates/listar_relatorios_cotas.html @@ -29,5 +29,6 @@ {% endfor %} + Home diff --git a/templates/listar_relatorios_vendas.html b/templates/listar_relatorios_vendas.html index 09133c8..63635fa 100644 --- a/templates/listar_relatorios_vendas.html +++ b/templates/listar_relatorios_vendas.html @@ -29,5 +29,6 @@ {% endfor %} + Home diff --git a/templates/listar_vendas_jornal.html b/templates/listar_vendas_jornal.html index 54d9c9e..d54304f 100644 --- a/templates/listar_vendas_jornal.html +++ b/templates/listar_vendas_jornal.html @@ -29,5 +29,6 @@ {% endfor %} + Home diff --git a/templates/nova_assinatura.html b/templates/nova_assinatura.html index 3619c9e..3205ce3 100644 --- a/templates/nova_assinatura.html +++ b/templates/nova_assinatura.html @@ -34,5 +34,6 @@ Voltar para Lista + Home diff --git a/templates/nova_cota.html b/templates/nova_cota.html index 2c3c2fb..ece5bcc 100644 --- a/templates/nova_cota.html +++ b/templates/nova_cota.html @@ -26,5 +26,6 @@ Voltar para Lista + Home diff --git a/templates/nova_venda_jornal.html b/templates/nova_venda_jornal.html index a625c71..91b6939 100644 --- a/templates/nova_venda_jornal.html +++ b/templates/nova_venda_jornal.html @@ -26,5 +26,6 @@ Voltar para Lista + Home diff --git a/templates/novo_material.html b/templates/novo_material.html index 6dd9ce2..1a71b8f 100644 --- a/templates/novo_material.html +++ b/templates/novo_material.html @@ -30,5 +30,6 @@ Voltar para Lista + Home diff --git a/templates/novo_militante.html b/templates/novo_militante.html index c22460f..f34c634 100644 --- a/templates/novo_militante.html +++ b/templates/novo_militante.html @@ -15,5 +15,6 @@ Filiado:
+ Home \ No newline at end of file diff --git a/templates/novo_pagamento.html b/templates/novo_pagamento.html index 8d8170f..6a32bd9 100644 --- a/templates/novo_pagamento.html +++ b/templates/novo_pagamento.html @@ -26,5 +26,6 @@ Voltar para Lista + Home diff --git a/templates/novo_relatorio_cotas.html b/templates/novo_relatorio_cotas.html index 4f6016d..1b5f3e6 100644 --- a/templates/novo_relatorio_cotas.html +++ b/templates/novo_relatorio_cotas.html @@ -26,5 +26,6 @@ Voltar para Lista + Home diff --git a/templates/novo_relatorio_vendas.html b/templates/novo_relatorio_vendas.html index 741261a..03dc8cb 100644 --- a/templates/novo_relatorio_vendas.html +++ b/templates/novo_relatorio_vendas.html @@ -26,5 +26,6 @@ Voltar para Lista + Home