summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorMarius Peter <marius.peter@tutanota.com>2022-05-15 13:17:35 +0200
committerMarius Peter <marius.peter@tutanota.com>2022-05-15 13:17:35 +0200
commite084f90c19e779592f6e3f14eb8f70a7287f0dad (patch)
tree1215177356105452c39a916fd050f79c9721f523 /app
parent1dad22d3e45d6506c8624e3a7111240fcc4ab786 (diff)
Modularization.
Diffstat (limited to 'app')
-rw-r--r--app/main.py75
-rw-r--r--app/model.py161
-rw-r--r--app/models.py260
-rw-r--r--app/modules/__init__.py8
-rw-r--r--app/modules/auth/forms.py29
-rw-r--r--app/modules/auth/routes.py73
-rw-r--r--app/modules/common.py75
-rw-r--r--app/modules/customers/forms.py36
-rw-r--r--app/modules/customers/routes.py38
-rw-r--r--app/modules/ferti/forms.py47
-rw-r--r--app/modules/ferti/routes.py46
-rw-r--r--app/modules/forms.py6
-rw-r--r--app/modules/forms.py.bkp (renamed from app/forms.py)51
-rw-r--r--app/modules/invoices/forms.py33
-rw-r--r--app/modules/invoices/routes.py47
-rw-r--r--app/modules/modules.py.bkp136
-rw-r--r--app/modules/orders/forms.py35
-rw-r--r--app/modules/orders/routes.py30
-rw-r--r--app/modules/products/forms.py23
-rw-r--r--app/modules/products/routes.py26
-rw-r--r--app/routes.py135
21 files changed, 1061 insertions, 309 deletions
diff --git a/app/main.py b/app/main.py
new file mode 100644
index 0000000..ac5625f
--- /dev/null
+++ b/app/main.py
@@ -0,0 +1,75 @@
+# -*- mode: python; -*-
+
+"""
+routes.py module
+----------------
+
+This Python module contains the logic supporting:
+1. Navigating between website pages
+2. Interpreting user requests to the server
+3. Dispatching requested content back to the user
+
+Python dependencies:
+- flask: provides web application features
+- forms: provides secure user form submission
+- sqlalchemy: provides communication with database on server.
+
+Personal imports:
+These are used to avoid cluttering this file with
+placeholder data for posts' content.
+"""
+
+
+from flask import (
+ Blueprint,
+ render_template,
+ send_file,
+ request,
+ redirect,
+ flash,
+ url_for,
+ jsonify,
+ abort,
+)
+from flask_login import login_required, current_user
+
+
+# from datetime import datetime
+# import inspect
+
+from . import db
+
+main = Blueprint("main", __name__)
+
+from .models import Module, User
+
+modules = [
+ "auth",
+ "customers",
+ "ferti",
+ "invoices",
+ "orders",
+ "products",
+]
+
+
+@main.route("/")
+@main.route("/index")
+def home():
+ """This is our project home page."""
+ modules = Module.query.all()
+ return render_template(
+ "home.html", user=current_user, project="fapg", modules=modules
+ )
+
+
+@main.route("/download-database")
+def download_database():
+ return send_file("fapg.db")
+
+
+@main.route("/modules")
+def all_modules():
+ # If unlogged, should present promotional material for available
+ # modules.
+ return render_template("modules-promo.html", modules=modules)
diff --git a/app/model.py b/app/model.py
deleted file mode 100644
index 1cf59d8..0000000
--- a/app/model.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# import SQLAlchemy
-from flask_sqlalchemy import SQLAlchemy
-from datetime import datetime
-
-db = SQLAlchemy()
-
-
-class Module(db.Model):
- name = db.Column(
- "Name", db.String(20), unique=True, nullable=False, primary_key=True
- )
- description = db.Column("Description", db.String(50), nullable=False)
-
- def __repr__(self):
- return f"<Module {self.name}>"
-
-
-class Customer(db.Model):
- primary_key = db.Column("CustomerId", db.Integer, nullable=False, primary_key=True)
- name = db.Column("Name", db.String(20), nullable=False)
- name_alternative = db.Column("NameAlternative", db.String(20))
- date_time_created = db.Column("DateTimeCreated", db.String(20), nullable=False)
- date_time_updated = db.Column("DateTimeUpdated", db.String(20), nullable=False)
- code_customer = db.Column("CodeCustomer", db.String(20), unique=True)
- code_accounting = db.Column("CodeAccounting", db.String(20), unique=True)
- address = db.Column("Address", db.String(20))
- postal_code = db.Column("PostalCode", db.Integer)
- city = db.Column("City", db.String(20))
- country = db.Column("Country", db.String(20))
- phone = db.Column("Phone", db.String(20))
- website = db.Column("Website", db.String(20))
- email = db.Column("Email", db.String(20))
- professional_id_1 = db.Column("ProfessionalId1", db.String(20), unique=True)
- professional_id_2 = db.Column("ProfessionalId2", db.String(20), unique=True)
- tax_id = db.Column("TaxId", db.String(20), unique=True)
- payment_terms = db.Column("PaymentTerms", db.String(20), unique=True)
-
- def __repr__(self):
- return f"<Customer {self.name}>"
-
-
-class Product(db.Model):
- primary_key = db.Column("ProductId", db.Integer, primary_key=True)
- name = db.Column("Name", db.String(20), nullable=False, unique=True)
- code_accounting = db.Column("CodeAccounting", db.String(20))
- unit_weight = db.Column("UnitWeight", db.Float)
- price_net = db.Column("PriceNet", db.Float)
- price_gross = db.Column("PriceGross", db.Float)
- tax_rate = db.Column("TaxRate", db.Float)
- date_time_created = db.Column(
- "DateTimeCreated",
- db.String,
- default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- )
- date_time_updated = db.Column(
- "DateTimeUpdated",
- db.String,
- default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- )
-
- def __init__(
- self,
- name,
- code_accounting,
- unit_weight,
- price_net,
- price_gross,
- tax_rate,
- ):
- self.name = name
- self.code_accounting = code_accounting
- self.unit_weight = unit_weight
- self.price_net = price_net
- self.price_gross = price_gross
- self.tax_rate = tax_rate
-
- def __repr__(self):
- return f"<Product {self.name}>"
-
-
-class Log(db.Model):
- primary_key = db.Column("LogId", db.Integer, primary_key=True)
- target = db.Column("Target", db.String, default="False")
- nno3 = db.Column("NNO3", db.Float, default=0)
- p = db.Column("P", db.Float, default=0)
- k = db.Column("K", db.Float, default=0)
- ca = db.Column("Ca", db.Float, default=0)
- mg = db.Column("Mg", db.Float, default=0)
- s = db.Column("S", db.Float, default=0)
- na = db.Column("Na", db.Float, default=0)
- cl = db.Column("Cl", db.Float, default=0)
- fe = db.Column("Fe", db.Float, default=0)
- zn = db.Column("Zn", db.Float, default=0)
- b = db.Column("B", db.Float, default=0)
- mn = db.Column("Mn", db.Float, default=0)
- cu = db.Column("Cu", db.Float, default=0)
- mo = db.Column("Mo", db.Float, default=0)
- si = db.Column("Si", db.Float, default=0)
- nnh4 = db.Column("NNH4", db.Float, default=0)
- date_time_created = db.Column(
- "DateTimeCreated",
- db.String,
- default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- )
- date_time_updated = db.Column(
- "DateTimeUpdated",
- db.String,
- default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- )
-
- def __init__(
- self, target, nno3, p, k, ca, mg, s, na, cl, fe, zn, b, mn, cu, mo, si, nnh4
- ):
- self.target = target
- self.nno3 = nno3
- self.p = p
- self.k = k
- self.ca = ca
- self.mg = mg
- self.s = s
- self.na = na
- self.cl = cl
- self.fe = fe
- self.zn = zn
- self.b = b
- self.mn = mn
- self.cu = cu
- self.mo = mo
- self.si = si
- self.nnh4 = nnh4
- self.date_time_created = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
-
- def __repr__(self):
- return f"<Log from {self.date_time_created} updated {self.date_time_updated}>"
-
-
-# Everything after here is garbage
-
-
-class User(db.Model):
- id = db.Column(db.Integer, primary_key=True)
- name_first = db.Column(db.String(20), nullable=False)
- name_last = db.Column(db.String(20), nullable=False)
- email = db.Column(db.String(20), nullable=False)
- phone_mobile = db.Column(db.Integer, nullable=False)
- phone_alternative = db.Column(db.Integer)
- updated = db.Column(db.String)
-
- def __repr__(self):
- return f"<User {self.name_first} {self.name_last}>"
-
-
-class Order(db.Model): # TODO
- id = db.Column(db.Integer, primary_key=True)
- date = db.Column(db.String(20), nullable=False)
- customer = db.Column(db.String(20), nullable=False)
- content = db.Column(db.String(10), nullable=False)
- updated = db.Column(db.String)
-
- def __repr__(self):
- return f"<Order {self.name} at {self.updated}>"
diff --git a/app/models.py b/app/models.py
new file mode 100644
index 0000000..507d135
--- /dev/null
+++ b/app/models.py
@@ -0,0 +1,260 @@
+# -*- mode: python; -*-
+
+
+from flask_sqlalchemy import SQLAlchemy
+
+from datetime import datetime
+from flask_login import UserMixin
+from . import db
+
+
+def date_time_now():
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+
+
+class User(UserMixin, db.Model):
+ __tablename__ = "User"
+
+ def get_id(self):
+ return self.primary_key
+
+ primary_key = db.Column("UserId", db.Integer, primary_key=True)
+ username = db.Column("Username", db.String(20), nullable=False)
+ hashed_password = db.Column("HashedPassword", db.String(100), nullable=False)
+ name_first = db.Column("NameFirst", db.String(20), nullable=False)
+ name_last = db.Column("NameLast", db.String(20), nullable=False)
+ email = db.Column("Email", db.String(20), nullable=False)
+ phone_mobile = db.Column("PhoneMobile", db.Integer)
+ phone_alternative = db.Column("PhoneAlternative", db.Integer)
+ date_time_created = db.Column(
+ "DateTimeCreated", db.String, server_default=date_time_now()
+ )
+ date_time_updated = db.Column(
+ "DateTimeUpdated", db.String, server_onupdate=date_time_now()
+ )
+
+ def __repr__(self):
+ return f"<User {self.name_first} {self.name_last}>"
+
+
+class Module(db.Model):
+ __tablename__ = "Module"
+ name = db.Column(
+ "Name", db.String(20), unique=True, nullable=False, primary_key=True
+ )
+ description = db.Column("Description", db.String(50), nullable=False)
+
+ def __repr__(self):
+ return f"<Module {self.name}>"
+
+
+class Customer(db.Model):
+ __tablename__ = "Customer"
+ primary_key = db.Column("CustomerId", db.Integer, nullable=False, primary_key=True)
+ name = db.Column("Name", db.String(20), nullable=False)
+ name_alternative = db.Column("NameAlternative", db.String(20))
+ code_customer = db.Column("CodeCustomer", db.String(20), unique=True)
+ code_accounting = db.Column("CodeAccounting", db.String(20), unique=True)
+ address = db.Column("Address", db.String(20))
+ postal_code = db.Column("PostalCode", db.Integer)
+ city = db.Column("City", db.String(20))
+ country = db.Column("Country", db.String(20))
+ phone = db.Column("Phone", db.String(20))
+ website = db.Column("Website", db.String(20))
+ email = db.Column("Email", db.String(20))
+ professional_id_1 = db.Column("ProfessionalId1", db.String(20), unique=True)
+ professional_id_2 = db.Column("ProfessionalId2", db.String(20), unique=True)
+ tax_id = db.Column("TaxId", db.String(20), unique=True)
+ payment_terms = db.Column("PaymentTerms", db.String(20), unique=True)
+ date_time_created = db.Column("DateTimeCreated", db.String(20), nullable=False)
+ date_time_updated = db.Column("DateTimeUpdated", db.String(20), nullable=False)
+ invoices = db.relationship("Invoice", back_populates="customer")
+
+ def __init__(
+ self,
+ name,
+ name_alternative,
+ code_customer,
+ code_accounting,
+ address,
+ postal_code,
+ city,
+ country,
+ phone,
+ website,
+ email,
+ professional_id_1,
+ professional_id_2,
+ tax_id,
+ payment_terms,
+ ):
+ self.name = name
+ self.name_alternative = name_alternative
+ self.code_customer = code_customer
+ self.code_accounting = code_accounting
+ self.address = address
+ self.postal_code = postal_code
+ self.city = city
+ self.country = country
+ self.phone = phone
+ self.website = website
+ self.email = email
+ self.professional_id_1 = professional_id_1
+ self.professional_id_2 = professional_id_2
+ self.tax_id = tax_id
+ self.payment_terms = payment_terms
+
+ def __repr__(self):
+ return f"<Customer {self.name}>"
+
+
+class Invoice(db.Model):
+ __tablename__ = "Invoice"
+ primary_key = db.Column("InvoiceId", db.Integer, primary_key=True)
+ invoice_id_alt = db.Column("InvoiceIdAlt", db.String)
+ customer_reference = db.Column("CustomerReference", db.String)
+ date_billed = db.Column("DateBilled", db.String)
+ date_due = db.Column("DateDue", db.String)
+ amount_net = db.Column("AmountNet", db.String)
+ amount_gross = db.Column("AmountGross", db.String)
+ amount_tax = db.Column("AmountTax", db.String)
+ date_time_created = db.Column(
+ "DateTimeCreated", db.String, default=date_time_now()
+ )
+ customer_id = db.Column(
+ "CustomerId", db.Integer, db.ForeignKey("Customer.CustomerId")
+ )
+ customer = db.relationship("Customer", back_populates="invoices")
+
+ def __init__(
+ self,
+ invoice_id_alt,
+ customer_id,
+ customer_reference,
+ date_billed,
+ date_due,
+ amount_net,
+ amount_gross,
+ amount_tax,
+ ):
+ self.invoice_id_alt = invoice_id_alt
+ self.customer_id = customer_id
+ self.customer_reference = customer_reference
+ self.date_billed = date_billed
+ self.date_due = date_due
+ self.amount_net = amount_net
+ self.amount_gross = amount_gross
+ self.amount_tax = amount_tax
+
+ def __repr__(self):
+ return f"<Invoice for {self.customer.name} at {self.updated}>"
+
+
+class Product(db.Model):
+ __tablename__ = "Product"
+ primary_key = db.Column("ProductId", db.Integer, primary_key=True)
+ name = db.Column("Name", db.String(20), nullable=False, unique=True)
+ code_accounting = db.Column("CodeAccounting", db.String(20))
+ unit_weight = db.Column("UnitWeight", db.Float)
+ price_net = db.Column("PriceNet", db.Float)
+ price_gross = db.Column("PriceGross", db.Float)
+ tax_rate = db.Column("TaxRate", db.Float)
+ date_time_created = db.Column(
+ "DateTimeCreated", db.String, server_default=date_time_now()
+ )
+ date_time_updated = db.Column(
+ "DateTimeUpdated", db.String, server_onupdate=date_time_now()
+ )
+
+ def __init__(
+ self,
+ name,
+ code_accounting,
+ unit_weight,
+ price_net,
+ price_gross,
+ tax_rate,
+ ):
+ self.name = name
+ self.code_accounting = code_accounting
+ self.unit_weight = unit_weight
+ self.price_net = price_net
+ self.price_gross = price_gross
+ self.tax_rate = tax_rate
+
+ def __repr__(self):
+ return f"<Product {self.name}>"
+
+
+class FertiLog(db.Model):
+ __tablename__ = "Fertilog"
+ primary_key = db.Column("FertiLogId", db.Integer, primary_key=True)
+ nno3 = db.Column("NNO3", db.Float, default=0)
+ p = db.Column("P", db.Float, default=0)
+ k = db.Column("K", db.Float, default=0)
+ ca = db.Column("Ca", db.Float, default=0)
+ mg = db.Column("Mg", db.Float, default=0)
+ s = db.Column("S", db.Float, default=0)
+ na = db.Column("Na", db.Float, default=0)
+ cl = db.Column("Cl", db.Float, default=0)
+ fe = db.Column("Fe", db.Float, default=0)
+ zn = db.Column("Zn", db.Float, default=0)
+ b = db.Column("B", db.Float, default=0)
+ mn = db.Column("Mn", db.Float, default=0)
+ cu = db.Column("Cu", db.Float, default=0)
+ mo = db.Column("Mo", db.Float, default=0)
+ si = db.Column("Si", db.Float, default=0)
+ nnh4 = db.Column("NNH4", db.Float, default=0)
+ date_time_created = db.Column(
+ "DateTimeCreated", db.String, server_default=date_time_now()
+ )
+ date_time_updated = db.Column(
+ "DateTimeUpdated", db.String, server_onupdate=date_time_now()
+ )
+
+ def __init__(self, nno3, p, k, ca, mg, s, na, cl, fe, zn, b, mn, cu, mo, si, nnh4):
+ self.nno3 = nno3
+ self.p = p
+ self.k = k
+ self.ca = ca
+ self.mg = mg
+ self.s = s
+ self.na = na
+ self.cl = cl
+ self.fe = fe
+ self.zn = zn
+ self.b = b
+ self.mn = mn
+ self.cu = cu
+ self.mo = mo
+ self.si = si
+ self.nnh4 = nnh4
+ self.date_time_created = date_time_now
+
+ def __repr__(self):
+ return f"<Log ID {self.primary_key}>"
+
+
+class FertiTarget(FertiLog):
+ __tablename__ = "Fertitarget"
+ primary_key = db.Column("FertiTargetId", db.Integer, primary_key=True)
+ targeted_log = db.Column(
+ "FertiLogId", db.Integer, db.ForeignKey("Fertilog.FertiLogId")
+ )
+
+ def __repr__(self):
+ return f"<Target ID {self.primary_key}>"
+
+
+# Everything after here is garbage
+
+
+class Order(db.Model): # TODO
+ id = db.Column(db.Integer, primary_key=True)
+ date = db.Column(db.String(20), nullable=False)
+ customer = db.Column(db.String(20), nullable=False)
+ content = db.Column(db.String(10), nullable=False)
+ updated = db.Column(db.String)
+
+ def __repr__(self):
+ return f"<Order {self.name} at {self.updated}>"
diff --git a/app/modules/__init__.py b/app/modules/__init__.py
new file mode 100644
index 0000000..56f1d5a
--- /dev/null
+++ b/app/modules/__init__.py
@@ -0,0 +1,8 @@
+# -*- mode: python; -*-
+
+from .common import common
+from .auth.routes import auth
+from .products.routes import products
+from .customers.routes import customers
+from .ferti.routes import ferti
+from .invoices.routes import invoices
diff --git a/app/modules/auth/forms.py b/app/modules/auth/forms.py
new file mode 100644
index 0000000..c051133
--- /dev/null
+++ b/app/modules/auth/forms.py
@@ -0,0 +1,29 @@
+from flask_wtf import FlaskForm
+from wtforms import (
+ SubmitField,
+ SelectField,
+ HiddenField,
+ StringField,
+ PasswordField,
+ IntegerField,
+ FloatField,
+ BooleanField,
+ DateTimeField,
+)
+from wtforms.validators import InputRequired, Length, NumberRange
+
+
+class LoginForm(FlaskForm):
+ username = StringField("Username", validators=[InputRequired()])
+ password = PasswordField("Password", validators=[InputRequired()])
+ remember = BooleanField("Remember")
+ submit = SubmitField("Login")
+
+
+class RegisterForm(LoginForm):
+ name_first = StringField("First name")
+ name_last = StringField("Last name")
+ email = StringField("E-mail address")
+ phone_mobile = StringField("Phone number (mobile)")
+ phone_alternative = StringField("Phone number (alternative)")
+ submit = SubmitField("Register")
diff --git a/app/modules/auth/routes.py b/app/modules/auth/routes.py
new file mode 100644
index 0000000..dd9e396
--- /dev/null
+++ b/app/modules/auth/routes.py
@@ -0,0 +1,73 @@
+# -*- mode: python; -*-
+
+from flask import Blueprint, render_template, redirect, url_for, request, flash
+from flask_login import login_user, login_required, logout_user
+from werkzeug.security import generate_password_hash, check_password_hash
+
+from ... import db
+from ...models import User
+from .forms import LoginForm, RegisterForm
+
+
+auth = Blueprint("auth", __name__)
+
+
+@auth.route("/login", methods=["GET", "POST"])
+def login():
+ form = LoginForm()
+ if form.validate_on_submit():
+ req = request.form
+ # print(req["remember"])
+ remember = True if req.get("remember") else False
+ user = User.query.filter_by(username=req["username"]).first()
+ if user is None:
+ flash("User not registered.", "error")
+ return redirect(url_for("auth.register"))
+ if check_password_hash(user.hashed_password, req["password"]) is False:
+ flash("Wrong password.", "error")
+ return redirect(url_for("auth.login"))
+ login_user(user, remember=remember)
+ flash(
+ f"Logged in as user {user.username} successfully. "
+ + f"You will {'not' if remember is False else ''} be remembered next time!"
+ )
+ return redirect(url_for("main.home"))
+ return render_template("modules/login.html", form=form)
+
+
+@auth.route("/register", methods=["GET", "POST"])
+def register():
+ form = RegisterForm()
+ if form.validate_on_submit():
+ req = request.form
+ user_already_exists = User.query.filter_by(
+ name_first=req["name_first"],
+ name_last=req["name_last"],
+ ).first()
+ if user_already_exists:
+ flash(
+ f"User {req['name_first']} {req['name_last']} already exists.", "error"
+ )
+ return redirect(url_for("auth.login"))
+ new_user = User(
+ username=req["username"],
+ hashed_password=generate_password_hash(req["password"], method="sha256"),
+ name_first=req["name_first"],
+ name_last=req["name_last"],
+ email=req["email"],
+ phone_mobile=req["phone_mobile"],
+ phone_alternative=req["phone_alternative"],
+ )
+ db.session.add(new_user)
+ db.session.commit()
+ flash(f"Created user {req['name_first']} {req['name_last']} successfully.")
+ return redirect(url_for("main.home"))
+ return render_template("register.html", form=form)
+
+
+@auth.route("/logout")
+@login_required
+def logout():
+ logout_user()
+ flash(f"Logged out successfully.")
+ return redirect(url_for("main.home"))
diff --git a/app/modules/common.py b/app/modules/common.py
new file mode 100644
index 0000000..9e96f41
--- /dev/null
+++ b/app/modules/common.py
@@ -0,0 +1,75 @@
+# -*- mode: python; -*-
+
+import inspect
+from flask import Blueprint, request, render_template, redirect, flash, jsonify
+from flask_login import login_required, current_user
+
+from .. import db
+from .. import models
+from . import forms
+
+from wtforms import SelectField
+
+
+common = Blueprint("common", __name__)
+
+
+@common.route("/modules/<module>/add/<table>", methods=["GET", "POST"])
+@login_required
+def add_item(module, table):
+ """Add new item to table accessible via module."""
+ # print("db table keys are", db.metadata.tables.keys())
+ if table not in db.metadata.tables.keys():
+ return render_template("errors/item-not-found.html", table=table)
+ form = getattr(forms, f"Add{table}")()
+ if form.validate_on_submit():
+ model = getattr(models, table)
+ table_fields = inspect.signature(model).parameters
+ form_values = {key: request.form[key] for key in table_fields}
+ print(f"Ready to insert {form_values}")
+ record = model(**form_values)
+ db.session.add(record)
+ db.session.commit()
+ item_pk = model.query.order_by(model.primary_key.desc()).first().primary_key
+ flash(f"Successfully added item #{item_pk} to {table} table.", "info")
+ return redirect(f"/modules/{module}")
+ return render_template("modules/add-item.html", table=table, form=form)
+
+
+@common.route("/modules/<module>/edit/<table>/<int:pk>", methods=["GET", "POST"])
+@login_required
+def edit_item(module, table, pk):
+ """Edit existing item in table accessible via module."""
+ if table not in db.metadata.tables.keys():
+ return render_template("errors/item-not-found.html", table=table)
+ model = getattr(models, table)
+ item = model.query.filter_by(primary_key=pk).first()
+ # Instantiate form with selected item's field values.
+ form = getattr(forms, f"Add{table}")(**item.__dict__)
+ if form.validate_on_submit():
+ table_fields = inspect.signature(model).parameters
+ form_values = {key: request.form[key] for key in table_fields}
+ print(f"Ready to update {form_values}")
+ model.query.filter_by(primary_key=pk).update(form_values)
+ db.session.commit()
+ flash(f"Successfully edited item #{pk} in {table} table.", "info")
+ return redirect(f"/modules/{module}")
+ return render_template("modules/edit-item.html", table=table, pk=pk, form=form)
+
+
+@common.route("/modules/<module>/delete/<table>/<int:pk>", methods=["POST"])
+@login_required
+def delete_item(module, table, pk):
+ """Delete item with Primary Key = pk from table in module."""
+ model = getattr(models, table)
+ record = model.query.filter_by(primary_key=pk).first()
+ db.session.delete(record)
+ db.session.commit()
+ flash(f"Successfully removed item #{pk} from {table} table.", "info")
+ return redirect(f"/modules/{module}")
+
+
+@common.route("/modules/settings")
+@login_required
+def settings():
+ return render_template("modules/settings.html", user=current_user)
diff --git a/app/modules/customers/forms.py b/app/modules/customers/forms.py
new file mode 100644
index 0000000..2c5bcaa
--- /dev/null
+++ b/app/modules/customers/forms.py
@@ -0,0 +1,36 @@
+# -*- mode: python; -*-
+
+
+from flask_wtf import FlaskForm
+from wtforms import (
+ SubmitField,
+ SelectField,
+ HiddenField,
+ StringField,
+ PasswordField,
+ IntegerField,
+ FloatField,
+ BooleanField,
+ DateTimeField,
+)
+from wtforms.validators import InputRequired, Length, NumberRange
+
+
+class AddCustomer(FlaskForm):
+ name = StringField("Customer name", validators=[InputRequired()])
+ name_alternative = StringField("Alternative name")
+ # date_time_created = DateTimeField("Creation date")
+ code_customer = StringField("Customer code")
+ code_accounting = StringField("Accounting code")
+ address = StringField("Address")
+ postal_code = StringField("Postal Code")
+ city = StringField("City")
+ country = StringField("Country")
+ phone = StringField("Phone")
+ website = StringField("Website")
+ email = StringField("E-mail")
+ professional_id_1 = StringField("Professional ID 1")
+ professional_id_2 = StringField("Professional ID 2")
+ tax_id = StringField("Tax ID")
+ payment_terms = StringField("Payment Terms", default="Tu vas payer sale chien!")
+ submit = SubmitField("Add/Update Product")
diff --git a/app/modules/customers/routes.py b/app/modules/customers/routes.py
new file mode 100644
index 0000000..c8ae072
--- /dev/null
+++ b/app/modules/customers/routes.py
@@ -0,0 +1,38 @@
+# -*- mode: python; -*-
+
+
+from flask import (
+ Blueprint,
+ render_template,
+ send_file,
+ request,
+ redirect,
+ flash,
+ url_for,
+ jsonify,
+ abort,
+)
+from flask_login import login_required, current_user
+import inspect
+
+
+from ... import db
+from ...models import *
+from .forms import *
+
+customers = Blueprint("customers", __name__)
+
+
+@customers.route("/modules/customers")
+@login_required
+def view():
+ modules = Module.query.all()
+ cust = Customer.query.order_by(Customer.primary_key.desc())
+ module = "customers"
+ flash(f"Successfully accessed module {module}.", "info")
+ flash(f"Still fighting against styling in {module}.", "error")
+ return render_template(
+ f"modules/{module}.html",
+ modules=modules,
+ customers=cust,
+ )
diff --git a/app/modules/ferti/forms.py b/app/modules/ferti/forms.py
new file mode 100644
index 0000000..d926931
--- /dev/null
+++ b/app/modules/ferti/forms.py
@@ -0,0 +1,47 @@
+from flask_wtf import FlaskForm
+from wtforms import (
+ SubmitField,
+ SelectField,
+ HiddenField,
+ StringField,
+ PasswordField,
+ IntegerField,
+ FloatField,
+ BooleanField,
+ DateTimeField,
+)
+from wtforms.validators import InputRequired, Length, NumberRange
+
+
+class AddFertiLog(FlaskForm):
+ target = SelectField(
+ "Type",
+ choices=[("False", "Log"), ("True", "Target")],
+ validators=[InputRequired()],
+ )
+ nno3 = FloatField("NNO3", default=0)
+ p = FloatField("P", default=0)
+ k = FloatField("K", default=0)
+ ca = FloatField("Ca", default=0)
+ mg = FloatField("Mg", default=0)
+ s = FloatField("S", default=0)
+ na = FloatField("Na", default=0)
+ cl = FloatField("Cl", default=0)
+ fe = FloatField("Fe", default=0)
+ zn = FloatField("Zn", default=0)
+ b = FloatField("B", default=0)
+ mn = FloatField("Mn", default=0)
+ cu = FloatField("Cu", default=0)
+ mo = FloatField("Mo", default=0)
+ si = FloatField("Si", default=0)
+ nnh4 = FloatField("NNH4", default=0)
+ submit = SubmitField("Add/Update Log")
+
+
+class AddFertiTarget(AddFertiLog):
+ targeted_log = SelectField(
+ "Log to target",
+ choices=[("value1", "Last"), ("value2", "Named")],
+ validators=[InputRequired()],
+ )
+ submit = SubmitField("Add/Update Log")
diff --git a/app/modules/ferti/routes.py b/app/modules/ferti/routes.py
new file mode 100644
index 0000000..b3bb03f
--- /dev/null
+++ b/app/modules/ferti/routes.py
@@ -0,0 +1,46 @@
+# -*- mode: python; -*-
+
+
+from flask import (
+ Blueprint,
+ render_template,
+ send_file,
+ request,
+ redirect,
+ flash,
+ url_for,
+ jsonify,
+ abort,
+)
+from flask_login import login_required, current_user
+
+
+# from datetime import datetime
+import inspect
+
+
+
+
+from ... import db
+from ...models import *
+from .forms import *
+
+
+ferti = Blueprint("ferti", __name__)
+
+
+
+@ferti.route("/modules/ferti")
+@login_required
+def view():
+ modules = Module.query.all()
+ logs = Fertilog.query.order_by(Fertilog.primary_key.desc()).all()
+ targets = Fertitarget.query.all()
+ module = "ferti"
+ flash(f"Latest target is {targets}.", "info")
+ flash(f"Still fighting against styling in {module}.", "error")
+ return render_template(
+ f"modules/{module}.html",
+ modules=modules,
+ logs=logs,
+ )
diff --git a/app/modules/forms.py b/app/modules/forms.py
new file mode 100644
index 0000000..f98995d
--- /dev/null
+++ b/app/modules/forms.py
@@ -0,0 +1,6 @@
+# -*- mode: python; -*-
+
+from .customers.forms import AddCustomer
+from .ferti.forms import AddFertiLog, AddFertiTarget
+from .invoices.forms import AddInvoice
+from .products.forms import AddProduct
diff --git a/app/forms.py b/app/modules/forms.py.bkp
index 302db58..f0f6bfb 100644
--- a/app/forms.py
+++ b/app/modules/forms.py.bkp
@@ -1,23 +1,36 @@
-"""
-Form classes must be named 'Add{Item}', Item referring to the
-model concerned.
-"""
+# -*- mode: python; -*-
from flask_wtf import FlaskForm
from wtforms import (
SubmitField,
SelectField,
- RadioField,
HiddenField,
StringField,
PasswordField,
IntegerField,
FloatField,
+ BooleanField,
DateTimeField,
)
from wtforms.validators import InputRequired, Length, NumberRange
+class LoginForm(FlaskForm):
+ username = StringField("Username", validators=[InputRequired()])
+ password = PasswordField("Password", validators=[InputRequired()])
+ remember = BooleanField("Remember")
+ submit = SubmitField("Login")
+
+
+class RegisterForm(LoginForm):
+ name_first = StringField("First name")
+ name_last = StringField("Last name")
+ email = StringField("E-mail address")
+ phone_mobile = StringField("Phone number (mobile)")
+ phone_alternative = StringField("Phone number (alternative)")
+ submit = SubmitField("Register")
+
+
class AddCustomer(FlaskForm):
name = StringField("Customer name", validators=[InputRequired()])
name_alternative = StringField("Alternative name")
@@ -48,7 +61,7 @@ class AddProduct(FlaskForm):
submit = SubmitField("Add/Update Product")
-class AddLog(FlaskForm):
+class AddFertilog(FlaskForm):
target = SelectField(
"Type",
choices=[("False", "Log"), ("True", "Target")],
@@ -73,6 +86,16 @@ class AddLog(FlaskForm):
submit = SubmitField("Add/Update Log")
+class AddFertitarget(AddFertilog):
+ targeted_log = SelectField(
+ "Log to target",
+ choices=[("value1", "Last"), ("value2", "Named")],
+ validators=[InputRequired()],
+ )
+ submit = SubmitField("Add/Update Log")
+
+
+
class AddOrder(FlaskForm): # TODO
name = StringField("Product name", validators=[InputRequired()])
code_accounting = StringField("Accounting code", default=0)
@@ -84,10 +107,12 @@ class AddOrder(FlaskForm): # TODO
class AddInvoice(FlaskForm):
- name = StringField("Product name", validators=[InputRequired()])
- code_accounting = StringField("Accounting code", default=0)
- unit_weight = FloatField("Unit weight", default=0)
- price_net = FloatField("Price (net)", default=0)
- price_gross = FloatField("Price (gross)", default=0)
- tax_rate = FloatField("Tax rate", default=0)
- submit = SubmitField("Add/Update Product")
+ invoice_id_alt = StringField("Alternative invoice ID")
+ customer_id = StringField("Customer ID")
+ customer_reference = StringField("Customer reference")
+ date_billed = StringField("Date billed")
+ date_due = StringField("Date due")
+ amount_net = FloatField("Amount without tax", default=0)
+ amount_gross = FloatField("Amount with tax", default=0)
+ amount_tax = FloatField("Amount of tax", default=0)
+ submit = SubmitField("Add new Invoice")
diff --git a/app/modules/invoices/forms.py b/app/modules/invoices/forms.py
new file mode 100644
index 0000000..8ef7bd0
--- /dev/null
+++ b/app/modules/invoices/forms.py
@@ -0,0 +1,33 @@
+from flask_wtf import FlaskForm
+from wtforms import (
+ SubmitField,
+ SelectField,
+ HiddenField,
+ StringField,
+ PasswordField,
+ IntegerField,
+ FloatField,
+ BooleanField,
+ DateTimeField,
+)
+from wtforms.validators import InputRequired, Length, NumberRange
+
+from ...models import Customer
+
+
+class AddInvoice(FlaskForm):
+ customer_id = SelectField("Customer", choices=[])
+ invoice_id_alt = StringField("Alternative invoice ID")
+ customer_reference = StringField("Customer reference")
+ date_billed = StringField("Date billed")
+ date_due = StringField("Date due")
+ amount_net = FloatField("Amount without tax", default=0)
+ amount_gross = FloatField("Amount with tax", default=0)
+ amount_tax = FloatField("Amount of tax", default=0)
+ submit = SubmitField("Add new Invoice")
+
+ def __init__(self, **kwargs):
+ super().__init__()
+ self.customer_id.choices = [
+ (cus.primary_key, cus.name) for cus in Customer.query.all()
+ ]
diff --git a/app/modules/invoices/routes.py b/app/modules/invoices/routes.py
new file mode 100644
index 0000000..b8a0a68
--- /dev/null
+++ b/app/modules/invoices/routes.py
@@ -0,0 +1,47 @@
+# -*- mode: python; -*-
+
+
+from flask import (
+ Blueprint,
+ render_template,
+ send_file,
+ request,
+ redirect,
+ flash,
+ url_for,
+ jsonify,
+ abort,
+)
+from flask_login import login_required, current_user
+
+
+from ... import db
+from ...models import *
+from .forms import *
+
+
+invoices = Blueprint("invoices", __name__)
+
+
+
+@invoices.route("/modules/invoices")
+@login_required
+def view():
+ modules = Module.query.all()
+ page = request.args.get('page', 1, type=int)
+ invoices = Invoice.query.order_by(Invoice.primary_key.desc()).paginate(page=page, per_page=5)
+ module = "invoices"
+ flash(f"Successfully accessed module {module}.", "info")
+ flash(f"Still fighting against styling in {module}.", "error")
+ return render_template(
+ f"modules/{module}.html",
+ modules=modules,
+ invoices=invoices,
+ )
+
+
+@invoices.route("/modules/invoices/preview/<int:pk>")
+@login_required
+def preview(pk):
+ invoice = Invoice.query.filter_by(primary_key=pk).first()
+ return render_template("modules/invoice-preview.html", invoice=invoice)
diff --git a/app/modules/modules.py.bkp b/app/modules/modules.py.bkp
new file mode 100644
index 0000000..b962a0f
--- /dev/null
+++ b/app/modules/modules.py.bkp
@@ -0,0 +1,136 @@
+# -*- mode: python; -*-
+
+
+from flask import (
+ Blueprint,
+ render_template,
+ send_file,
+ request,
+ redirect,
+ flash,
+ url_for,
+ jsonify,
+ abort,
+)
+from flask_login import login_required, current_user
+
+
+# from datetime import datetime
+import inspect
+
+from . import db
+
+modules = Blueprint("modules", __name__)
+
+
+from .models import *
+from .forms import *
+
+
+@modules.route("/modules")
+def modules_home():
+ return redirect("/index")
+
+
+@modules.route("/add-<item>", methods=["GET", "POST"])
+@login_required
+def add_item(item):
+ """Add a new item to a corresponding database table.
+
+ The item must match a database model class name (table). Then, we
+ match the model class attributes with request.form values.
+
+ """
+ print("db table keys are", db.metadata.tables.keys())
+ if item.capitalize() not in db.metadata.tables.keys():
+ return render_template("errors/item-not-found.html", item=item)
+ if request.method == "GET":
+ form = globals()[f"Add{item.capitalize()}"]()
+ return render_template("add-item.html", item=item, form=form)
+ if request.method == "POST":
+ table = globals()[item.capitalize()]
+ table_fields = inspect.signature(table).parameters
+ form_values = {key: request.form[key] for key in table_fields}
+ print(f"Ready to insert {form_values}")
+ record = table(**form_values)
+ db.session.add(record)
+ db.session.commit()
+ item_pk = table.query.order_by(table.primary_key.desc()).first().primary_key
+ flash(
+ f"Successfully added item #{item_pk} to {table.__table__.name.capitalize()} table.",
+ "info",
+ )
+ return redirect("/modules")
+
+
+@modules.route("/delete-<pk>-from-<table>", methods=["POST"])
+@login_required
+def delete_item(pk, table):
+ """Delete item with Primary Key = pk from corresponding database
+ table.
+ """
+ model = globals()[table]
+ record = model.query.filter_by(primary_key=pk).first()
+ db.session.delete(record)
+ db.session.commit()
+ flash(f"Successfully removed item #{pk} from {table} table.", "info")
+ return redirect("/modules")
+
+
+@modules.route("/modules/customers")
+@login_required
+def customers():
+ modules = Module.query.all()
+ customers = Customer.query.order_by(Customer.primary_key.desc())
+ module = "customers"
+ flash(f"Successfully accessed module {module}.", "info")
+ flash(f"Still fighting against styling in {module}.", "error")
+ return render_template(
+ f"modules/{module}.html",
+ modules=modules,
+ customers=customers,
+ )
+
+
+@modules.route("/modules/products")
+@login_required
+def products():
+ modules = Module.query.all()
+ products = Product.query.order_by(Product.primary_key.desc())
+ module = "products"
+ flash(f"Successfully accessed module {module}.", "info")
+ flash(f"Still fighting against styling in {module}.", "error")
+ return render_template(
+ f"modules/{module}.html",
+ modules=modules,
+ products=products,
+ )
+
+
+@modules.route("/modules/ferti")
+@login_required
+def ferti():
+ modules = Module.query.all()
+ logs = Fertilog.query.order_by(Fertilog.primary_key.desc()).all()
+ targets = Fertitarget.query.all()
+ module = "ferti"
+ flash(f"Latest target is {targets}.", "info")
+ flash(f"Still fighting against styling in {module}.", "error")
+ return render_template(
+ f"modules/{module}.html",
+ modules=modules,
+ logs=logs,
+ )
+
+
+# @main.route("/add-invoice", methods=["GET", "POST"])
+# def add_invoice():
+# form = AddInvoice()
+# if request.method == "GET":
+# return render_template("add-invoice.html")
+
+
+# @main.route("/preview-invoice", methods=["GET", "POST"])
+# def preview_invoice():
+# if request.method == "GET":
+# return render_template("preview-invoice.html")
diff --git a/app/modules/orders/forms.py b/app/modules/orders/forms.py
new file mode 100644
index 0000000..66ab47b
--- /dev/null
+++ b/app/modules/orders/forms.py
@@ -0,0 +1,35 @@
+from flask_wtf import FlaskForm
+from wtforms import (
+ SubmitField,
+ SelectField,
+ HiddenField,
+ StringField,
+ PasswordField,
+ IntegerField,
+ FloatField,
+ BooleanField,
+ DateTimeField,
+)
+from wtforms.validators import InputRequired, Length, NumberRange
+
+
+class AddOrder(FlaskForm): # TODO
+ name = StringField("Product name", validators=[InputRequired()])
+ code_accounting = StringField("Accounting code", default=0)
+ unit_weight = FloatField("Unit weight", default=0)
+ price_net = FloatField("Price (net)", default=0)
+ price_gross = FloatField("Price (gross)", default=0)
+ tax_rate = FloatField("Tax rate", default=0)
+ submit = SubmitField("Add/Update Product")
+
+
+class AddInvoice(FlaskForm):
+ invoice_id_alt = StringField("Alternative invoice ID")
+ customer_id = StringField("Customer ID")
+ customer_reference = StringField("Customer reference")
+ date_billed = StringField("Date billed")
+ date_due = StringField("Date due")
+ amount_net = FloatField("Amount without tax", default=0)
+ amount_gross = FloatField("Amount with tax", default=0)
+ amount_tax = FloatField("Amount of tax", default=0)
+ submit = SubmitField("Add new Invoice")
diff --git a/app/modules/orders/routes.py b/app/modules/orders/routes.py
new file mode 100644
index 0000000..4027a44
--- /dev/null
+++ b/app/modules/orders/routes.py
@@ -0,0 +1,30 @@
+# -*- mode: python; -*-
+
+
+from flask import (
+ Blueprint,
+ render_template,
+ send_file,
+ request,
+ redirect,
+ flash,
+ url_for,
+ jsonify,
+ abort,
+)
+from flask_login import login_required, current_user
+
+
+# from datetime import datetime
+import inspect
+
+
+
+from ... import db
+from ...models import *
+from .forms import *
+
+
+orders = Blueprint("orders", __name__)
+
+
diff --git a/app/modules/products/forms.py b/app/modules/products/forms.py
new file mode 100644
index 0000000..f00441f
--- /dev/null
+++ b/app/modules/products/forms.py
@@ -0,0 +1,23 @@
+from flask_wtf import FlaskForm
+from wtforms import (
+ SubmitField,
+ SelectField,
+ HiddenField,
+ StringField,
+ PasswordField,
+ IntegerField,
+ FloatField,
+ BooleanField,
+ DateTimeField,
+)
+from wtforms.validators import InputRequired, Length, NumberRange
+
+
+class AddProduct(FlaskForm):
+ name = StringField("Product name", validators=[InputRequired()])
+ code_accounting = StringField("Accounting code", default=0)
+ unit_weight = FloatField("Unit weight", default=0)
+ price_net = FloatField("Price (net)", default=0)
+ price_gross = FloatField("Price (gross)", default=0)
+ tax_rate = FloatField("Tax rate", default=0)
+ submit = SubmitField("Add/Update Product")
diff --git a/app/modules/products/routes.py b/app/modules/products/routes.py
new file mode 100644
index 0000000..735fef9
--- /dev/null
+++ b/app/modules/products/routes.py
@@ -0,0 +1,26 @@
+# -*- mode: python; -*-
+
+
+from flask import Blueprint, render_template, request, redirect, flash
+from flask_login import login_required
+
+from ... import db
+from ...models import Module, Product
+from .forms import *
+
+products = Blueprint("products", __name__)
+
+
+@products.route("/modules/products")
+@login_required
+def list():
+ modules = Module.query.all()
+ prods = Product.query.order_by(Product.primary_key.desc())
+ module = "products"
+ flash(f"Successfully accessed module {module}.", "info")
+ flash(f"Still fighting against styling in {module}.", "error")
+ return render_template(
+ f"modules/{module}.html",
+ modules=modules,
+ products=prods,
+ )
diff --git a/app/routes.py b/app/routes.py
deleted file mode 100644
index 11cd3c2..0000000
--- a/app/routes.py
+++ /dev/null
@@ -1,135 +0,0 @@
-"""
-routes.py module
-----------------
-
-This Python module contains the logic supporting:
-1. Navigating between website pages
-2. Interpreting user requests to the server
-3. Dispatching requested content back to the user
-
-Python dependencies:
-- flask: provides web application features
-- forms: provides secure user form submission
-- sqlalchemy: provides communication with database on server.
-
-Personal imports:
-These are used to avoid cluttering this file with
-placeholder data for posts' content.
-"""
-
-
-from flask import Flask, render_template, request, redirect, flash, url_for, jsonify
-from flask_bootstrap import Bootstrap
-
-from datetime import datetime
-import inspect
-
-from model import *
-from forms import *
-
-app = Flask(__name__)
-
-
-app.config.from_pyfile("../config.py")
-db.init_app(app)
-
-
-@app.route("/")
-@app.route("/fapg/home")
-def project():
- """This is our project welcome page."""
- michel = User(
- name_first="Michel",
- name_last="Peter",
- email="le-boss@fapg.com",
- phone_mobile="00000000",
- phone_alternative="0000000000",
- updated="2022-04-21",
- )
- modules = Module.query.all()
- print(module.name for module in modules)
- return render_template("home.html", user=michel, project="fapg", modules=modules)
-
-
-@app.route("/modules")
-def all_modules():
- return redirect("/")
-
-
-@app.route("/modules/<module>")
-def render_module(module):
- modules = Module.query.all()
- if module not in [mod.name for mod in modules]:
- return render_template("errors/module-not-found.html", module=module)
- customers = Customer.query.order_by(Customer.primary_key.desc())
- products = Product.query.order_by(Product.primary_key.desc())
- logs = Log.query.order_by(Log.primary_key.desc())
- latest_target = (
- Log.query.filter_by(target="True").order_by(Log.primary_key.desc()).first()
- )
- flash(f"Successfully accessed module {module}.", "info")
- flash(f"Still fighting against styling in {module}.", "error")
- return render_template(
- f"modules/{module}.html",
- module=module,
- modules=modules,
- customers=customers,
- products=products,
- logs=logs,
- target=latest_target,
- )
-
-
-@app.route("/delete-<pk>-from-<table>", methods=["POST"])
-def delete_item(pk, table):
- """Delete item with Primary Key = pk from corresponding database
- table.
- """
- model = globals()[table]
- record = model.query.filter_by(primary_key=pk).first()
- db.session.delete(record)
- db.session.commit()
- flash(f"Successfully removed item #{pk} from {table} table.", "info")
- return redirect("/modules")
-
-
-@app.route("/add-<item>", methods=["GET", "POST"])
-def add_item(item):
- """Add a new item to a corresponding database table.
-
- The item must match a database model class name (table). Then, we
- match the model class attributes with request.form values.
-
- """
- if item not in db.metadata.tables.keys():
- return render_template("errors/item-not-found.html", item=item)
- if request.method == "GET":
- form = globals()[f"Add{item.capitalize()}"]()
- return render_template("add-item.html", item=item, form=form)
- if request.method == "POST":
- table = globals()[item.capitalize()]
- table_fields = inspect.signature(table).parameters
- form_values = [request.form[key] for key in table_fields]
- debug = f"Ready to insert {form_values}"
- record = table(*form_values)
- db.session.add(record)
- db.session.commit()
- item_pk = table.query.order_by(table.primary_key.desc()).first().primary_key
- flash(
- f"Successfully added item #{item_pk} to {table.__table__.name.capitalize()} table.",
- "info",
- )
- return redirect(f"/modules")
-
-
-@app.route("/add-invoice", methods=["GET", "POST"])
-def add_invoice():
- form = AddInvoice()
- if request.method == "GET":
- return render_template("add-invoice.html")
-
-
-@app.route("/preview-invoice", methods=["GET", "POST"])
-def preview_invoice():
- if request.method == "GET":
- return render_template("preview-invoice.html")
Copyright 2019--2024 Marius PETER