diff --git a/brewman/alembic/versions/0363f582ab28_rename_auth.py b/brewman/alembic/versions/0363f582ab28_rename_auth.py new file mode 100644 index 00000000..eb446d00 --- /dev/null +++ b/brewman/alembic/versions/0363f582ab28_rename_auth.py @@ -0,0 +1,71 @@ +"""rename auth + +Revision ID: 0363f582ab28 +Revises: ad8b2d208492 +Create Date: 2021-09-11 05:32:56.683107 + +""" +import sqlalchemy as sa + +from alembic import op +from sqlalchemy.dialects import postgresql + + +# revision identifiers, used by Alembic. +revision = "0363f582ab28" +down_revision = "ad8b2d208492" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.rename_table("auth_clients", "clients") + op.drop_constraint("uq_auth_clients_code", "clients", type_="unique") + op.drop_constraint("uq_auth_clients_name", "clients", type_="unique") + op.create_unique_constraint(op.f("uq_clients_code"), "clients", ["code"]) + op.create_unique_constraint(op.f("uq_clients_name"), "clients", ["name"]) + + op.rename_table("auth_login_history", "login_history") + op.drop_constraint("uq_auth_login_history_user_id", "login_history", type_="unique") + op.create_unique_constraint(op.f("uq_login_history_user_id"), "login_history", ["user_id", "client_id", "date"]) + + op.rename_table("auth_permissions", "permissions") + op.drop_constraint("uq_auth_permissions_name", "permissions", type_="unique") + op.create_unique_constraint(op.f("uq_permissions_name"), "permissions", ["name"]) + + op.rename_table("auth_roles", "roles") + op.drop_constraint("uq_auth_roles_name", "roles", type_="unique") + op.create_unique_constraint(op.f("uq_roles_name"), "roles", ["name"]) + + op.rename_table("auth_users", "users") + op.drop_constraint("uq_auth_users_username", "users", type_="unique") + op.create_unique_constraint(op.f("uq_users_username"), "users", ["username"]) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + + op.rename_table("users", "auth_users") + op.drop_constraint(op.f("uq_users_username"), "users", type_="unique") + op.create_unique_constraint("uq_auth_users_username", "users", ["username"]) + + op.rename_table("roles", "auth_roles") + op.drop_constraint(op.f("uq_roles_name"), "roles", type_="unique") + op.create_unique_constraint("uq_auth_roles_name", "roles", ["name"]) + + op.rename_table("permissions", "auth_permissions") + op.drop_constraint(op.f("uq_permissions_name"), "permissions", type_="unique") + op.create_unique_constraint("uq_auth_permissions_name", "permissions", ["name"]) + + op.rename_table("login_history", "auth_login_history") + op.drop_constraint(op.f("uq_login_history_user_id"), "login_history", type_="unique") + op.create_unique_constraint("uq_auth_login_history_user_id", "login_history", ["user_id", "client_id", "date"]) + + op.rename_table("clients", "auth_clients") + op.drop_constraint(op.f("uq_clients_name"), "clients", type_="unique") + op.drop_constraint(op.f("uq_clients_code"), "clients", type_="unique") + op.create_unique_constraint("uq_auth_clients_name", "clients", ["name"]) + op.create_unique_constraint("uq_auth_clients_code", "clients", ["code"]) + # ### end Alembic commands ### diff --git a/brewman/brewman/models/attendance.py b/brewman/brewman/models/attendance.py index 6f52248d..8b93c739 100644 --- a/brewman/brewman/models/attendance.py +++ b/brewman/brewman/models/attendance.py @@ -27,7 +27,7 @@ class Attendance(Base): attendance_type = Column("attendance_type", Integer) amount = Column("amount", Numeric(precision=5, scale=2)) creation_date = Column("creation_date", DateTime()) - user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("auth_users.id")) + user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id")) is_valid = Column("is_valid", Boolean) user = relationship("User", primaryjoin="User.id==Attendance.user_id") diff --git a/brewman/brewman/models/client.py b/brewman/brewman/models/client.py index 85a320b8..3b704ad1 100644 --- a/brewman/brewman/models/client.py +++ b/brewman/brewman/models/client.py @@ -16,7 +16,7 @@ from .meta import Base class Client(Base): - __tablename__ = "auth_clients" + __tablename__ = "clients" id = Column("client_id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) code = Column("code", Integer, unique=True, nullable=False) diff --git a/brewman/brewman/models/login_history.py b/brewman/brewman/models/login_history.py index f890d99d..f6cf9ff3 100644 --- a/brewman/brewman/models/login_history.py +++ b/brewman/brewman/models/login_history.py @@ -13,14 +13,14 @@ from .meta import Base class LoginHistory(Base): - __tablename__ = "auth_login_history" + __tablename__ = "login_history" __table_args__ = (UniqueConstraint("user_id", "client_id", "date"),) id = Column("login_history_id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("auth_users.id"), nullable=False) + user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) client_id = Column( "client_id", UUID(as_uuid=True), - ForeignKey("auth_clients.client_id"), + ForeignKey("clients.client_id"), nullable=False, ) date = Column("date", DateTime(), nullable=False) diff --git a/brewman/brewman/models/permission.py b/brewman/brewman/models/permission.py index d42062b3..980d423b 100644 --- a/brewman/brewman/models/permission.py +++ b/brewman/brewman/models/permission.py @@ -14,7 +14,7 @@ from .role_permission import role_permission class Permission(Base): - __tablename__ = "auth_permissions" + __tablename__ = "permissions" id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = Column("name", Unicode(255), unique=True) diff --git a/brewman/brewman/models/rate_contract.py b/brewman/brewman/models/rate_contract.py new file mode 100644 index 00000000..1d4f8066 --- /dev/null +++ b/brewman/brewman/models/rate_contract.py @@ -0,0 +1,124 @@ +import uuid + +from sqlalchemy import ( + Boolean, + Column, + ForeignKey, + Integer, + Numeric, + Unicode, + UniqueConstraint, + case, + func, + select, +) +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy.orm import Session, relationship + +from .meta import Base + + +class Product(Base): + __tablename__ = "products" + __table_args__ = (UniqueConstraint("name", "units"),) + + id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + code = Column("code", Integer, unique=True) + name = Column("name", Unicode(255), nullable=False) + units = Column("units", Unicode(255), nullable=False) + fraction = Column("fraction", Numeric(precision=15, scale=5), nullable=False) + fraction_units = Column("fraction_units", Unicode(255), nullable=False) + product_yield = Column("product_yield", Numeric(precision=15, scale=5), nullable=False) + product_group_id = Column( + "product_group_id", + UUID(as_uuid=True), + ForeignKey("product_groups.id"), + nullable=False, + ) + account_id = Column("account_id", UUID(as_uuid=True), ForeignKey("accounts.id"), nullable=False) + price = Column("cost_price", Numeric(precision=15, scale=2), nullable=False) + sale_price = Column("sale_price", Numeric(precision=15, scale=2), nullable=False) + is_active = Column("is_active", Boolean, nullable=False) + is_fixture = Column("is_fixture", Boolean, nullable=False) + is_purchased = Column("is_purchased", Boolean, nullable=False) + is_sold = Column("is_sold", Boolean, nullable=False) + + product_group = relationship("ProductGroup", back_populates="products") + batches = relationship("Batch", back_populates="product") + inventories = relationship("Inventory", back_populates="product") + recipes = relationship("Recipe", back_populates="product") + account = relationship("Account", primaryjoin="Account.id==Product.account_id", back_populates="products") + + def __init__( + self, + code=None, + name=None, + units=None, + fraction=None, + fraction_units=None, + product_yield=None, + product_group_id=None, + account_id=None, + price=None, + sale_price=None, + is_active=None, + is_purchased=None, + is_sold=None, + id_=None, + is_fixture=False, + ): + self.code = code + self.name = name + self.units = units + self.fraction = fraction + self.fraction_units = fraction_units + self.product_yield = product_yield + self.product_group_id = product_group_id + self.account_id = account_id + self.price = price + self.sale_price = sale_price + self.is_active = is_active + self.is_purchased = is_purchased + self.is_sold = is_sold + self.id = id_ + self.is_fixture = is_fixture + + @hybrid_property + def full_name(self): + return f"{self.name} ({self.units})" if self.units else self.name + + @full_name.expression + def full_name(cls): + return cls.name + case([(cls.units != "", " (" + cls.units + ")")], else_="") + + def create(self, db: Session): + self.code = db.execute(select(func.coalesce(func.max(Product.code), 0) + 1)).scalar_one() + db.add(self) + return self + + def can_delete(self, advanced_delete: bool): + if self.is_fixture: + return False, f"{self.name} is a fixture and cannot be edited or deleted." + if self.is_active: + return False, "Product is active" + if len(self.inventories) > 0 and not advanced_delete: + return False, "Product has entries" + return True, "" + + @classmethod + def query(cls, q, is_purchased: bool = None, active: bool = None, db: Session = None): + query_ = select(cls) + if active is not None: + query_ = query_.filter(cls.is_active == active) + if is_purchased is not None: + query_ = query_.filter(cls.is_purchased == is_purchased) + if q is not None: + for item in q.split(): + if item.strip() != "": + query_ = query_.filter(cls.name.ilike(f"%{item}%")) + return db.execute(query_).scalars().all() + + @classmethod + def suspense(cls): + return uuid.UUID("aa79a643-9ddc-4790-ac7f-a41f9efb4c15") diff --git a/brewman/brewman/models/rate_contract_item.py b/brewman/brewman/models/rate_contract_item.py new file mode 100644 index 00000000..441ed14a --- /dev/null +++ b/brewman/brewman/models/rate_contract_item.py @@ -0,0 +1,119 @@ +import uuid + +from sqlalchemy import ( + Boolean, + Column, + ForeignKey, + Integer, + Numeric, + Unicode, + UniqueConstraint, + case, + func, + select, DateTime, Date, +) +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy.orm import Session, relationship + +from .meta import Base + + +class RateContract(Base): + __tablename__ = "rate_contracts" + __table_args__ = (UniqueConstraint("name", "units"),) + + id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + + user = relationship("User", primaryjoin="User.id==Voucher.user_id", cascade=None) + creation_date = Column("creation_date", DateTime(), nullable=False) + last_edit_date = Column("last_edit_date", DateTime(), nullable=False) + + vendor_id = Column("vendor_id", UUID(as_uuid=True), ForeignKey("accounts.id"), nullable=False) + + # product_id = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False) + # price = Column("cost_price", Numeric(precision=15, scale=2), nullable=False) + + valid_from = Column("valid_from", Date(), nullable=True) + valid_till = Column("valid_till", Date(), nullable=True) + + user = relationship("User") + vendor = relationship("Account", back_populates="rate_contracts") + + # batches = relationship("Batch", back_populates="product") + # inventories = relationship("Inventory", back_populates="product") + # recipes = relationship("Recipe", back_populates="product") + # account = relationship("Account", primaryjoin="Account.id==Product.account_id", back_populates="products") + + def __init__( + self, + code=None, + name=None, + units=None, + fraction=None, + fraction_units=None, + product_yield=None, + product_group_id=None, + account_id=None, + price=None, + sale_price=None, + is_active=None, + is_purchased=None, + is_sold=None, + id_=None, + is_fixture=False, + ): + self.code = code + self.name = name + self.units = units + self.fraction = fraction + self.fraction_units = fraction_units + self.product_yield = product_yield + self.product_group_id = product_group_id + self.account_id = account_id + self.price = price + self.sale_price = sale_price + self.is_active = is_active + self.is_purchased = is_purchased + self.is_sold = is_sold + self.id = id_ + self.is_fixture = is_fixture + + @hybrid_property + def full_name(self): + return f"{self.name} ({self.units})" if self.units else self.name + + @full_name.expression + def full_name(cls): + return cls.name + case([(cls.units != "", " (" + cls.units + ")")], else_="") + + def create(self, db: Session): + self.code = db.execute(select(func.coalesce(func.max(Product.code), 0) + 1)).scalar_one() + db.add(self) + return self + + def can_delete(self, advanced_delete: bool): + if self.is_fixture: + return False, f"{self.name} is a fixture and cannot be edited or deleted." + if self.is_active: + return False, "Product is active" + if len(self.inventories) > 0 and not advanced_delete: + return False, "Product has entries" + return True, "" + + @classmethod + def query(cls, q, is_purchased: bool = None, active: bool = None, db: Session = None): + query_ = select(cls) + if active is not None: + query_ = query_.filter(cls.is_active == active) + if is_purchased is not None: + query_ = query_.filter(cls.is_purchased == is_purchased) + if q is not None: + for item in q.split(): + if item.strip() != "": + query_ = query_.filter(cls.name.ilike(f"%{item}%")) + return db.execute(query_).scalars().all() + + @classmethod + def suspense(cls): + return uuid.UUID("aa79a643-9ddc-4790-ac7f-a41f9efb4c15") diff --git a/brewman/brewman/models/role.py b/brewman/brewman/models/role.py index fd5dc4c8..a5667ca6 100644 --- a/brewman/brewman/models/role.py +++ b/brewman/brewman/models/role.py @@ -11,7 +11,7 @@ from .role_permission import role_permission class Role(Base): - __tablename__ = "auth_roles" + __tablename__ = "roles" id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = Column("name", Unicode(255), unique=True) diff --git a/brewman/brewman/models/role_permission.py b/brewman/brewman/models/role_permission.py index 678fd6fb..b388812d 100644 --- a/brewman/brewman/models/role_permission.py +++ b/brewman/brewman/models/role_permission.py @@ -13,6 +13,6 @@ role_permission = Table( "role_permissions", Base.metadata, Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4), - Column("permission_id", UUID(as_uuid=True), ForeignKey("auth_permissions.id")), - Column("role_id", UUID(as_uuid=True), ForeignKey("auth_roles.id")), + Column("permission_id", UUID(as_uuid=True), ForeignKey("permissions.id")), + Column("role_id", UUID(as_uuid=True), ForeignKey("roles.id")), ) diff --git a/brewman/brewman/models/user.py b/brewman/brewman/models/user.py index 04154880..88c4e6de 100644 --- a/brewman/brewman/models/user.py +++ b/brewman/brewman/models/user.py @@ -20,7 +20,7 @@ def encrypt(val): class User(Base): - __tablename__ = "auth_users" + __tablename__ = "users" id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = Column("username", Unicode(255), unique=True) diff --git a/brewman/brewman/models/user_role.py b/brewman/brewman/models/user_role.py index 540a6413..49b20ca4 100644 --- a/brewman/brewman/models/user_role.py +++ b/brewman/brewman/models/user_role.py @@ -13,6 +13,6 @@ user_role = Table( "user_roles", Base.metadata, Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4), - Column("user_id", UUID(as_uuid=True), ForeignKey("auth_users.id")), - Column("role_id", UUID(as_uuid=True), ForeignKey("auth_roles.id")), + Column("user_id", UUID(as_uuid=True), ForeignKey("users.id")), + Column("role_id", UUID(as_uuid=True), ForeignKey("roles.id")), ) diff --git a/brewman/brewman/models/voucher.py b/brewman/brewman/models/voucher.py index e4f86479..685db9bd 100644 --- a/brewman/brewman/models/voucher.py +++ b/brewman/brewman/models/voucher.py @@ -21,9 +21,9 @@ class Voucher(Base): creation_date = Column("creation_date", DateTime(), nullable=False) last_edit_date = Column("last_edit_date", DateTime(), nullable=False) _type = Column("voucher_type", Integer, nullable=False) - user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("auth_users.id"), nullable=False) + user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) posted = Column("is_posted", Boolean, nullable=False) - poster_id = Column("poster_id", UUID(as_uuid=True), ForeignKey("auth_users.id")) + poster_id = Column("poster_id", UUID(as_uuid=True), ForeignKey("users.id")) user = relationship("User", primaryjoin="User.id==Voucher.user_id", cascade=None) poster = relationship("User", primaryjoin="User.id==Voucher.poster_id", cascade=None)