Removed timezone information from columns. Time will be stored in UTC coordinates.

Moved to Sqlalchemy 1.4 model and SessionFuture.

Upgraded to Angular 12

Upgraded the python dependencies
This commit is contained in:
tanshu committed 2021-09-07 12:59:18 +05:30
1 parent 0bd6f8eb03
commit 350edf7126
129 files changed
+2720 -3027

No files matched your search

@@ -0,0 +1,74 @@
"""timezone
Revision ID: 226cadcec9ac
Revises: 002f51d87565
Create Date: 2021-04-19 09:58:54.556818
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '226cadcec9ac'
down_revision = '002f51d87565'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('attendances', 'creation_date',
existing_type=postgresql.TIMESTAMP(timezone=True),
type_=sa.DateTime(),
existing_nullable=True)
op.alter_column('auth_clients', 'creation_date',
existing_type=postgresql.TIMESTAMP(timezone=True),
type_=sa.DateTime(),
existing_nullable=False)
op.alter_column('auth_login_history', 'date',
existing_type=postgresql.TIMESTAMP(timezone=True),
type_=sa.DateTime(),
existing_nullable=False)
op.alter_column('images', 'creation_date',
existing_type=postgresql.TIMESTAMP(timezone=True),
type_=sa.DateTime(),
existing_nullable=False)
op.alter_column('vouchers', 'creation_date',
existing_type=postgresql.TIMESTAMP(timezone=True),
type_=sa.DateTime(),
existing_nullable=False)
op.alter_column('vouchers', 'last_edit_date',
existing_type=postgresql.TIMESTAMP(timezone=True),
type_=sa.DateTime(),
existing_nullable=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('vouchers', 'last_edit_date',
existing_type=sa.DateTime(),
type_=postgresql.TIMESTAMP(timezone=True),
existing_nullable=False)
op.alter_column('vouchers', 'creation_date',
existing_type=sa.DateTime(),
type_=postgresql.TIMESTAMP(timezone=True),
existing_nullable=False)
op.alter_column('images', 'creation_date',
existing_type=sa.DateTime(),
type_=postgresql.TIMESTAMP(timezone=True),
existing_nullable=False)
op.alter_column('auth_login_history', 'date',
existing_type=sa.DateTime(),
type_=postgresql.TIMESTAMP(timezone=True),
existing_nullable=False)
op.alter_column('auth_clients', 'creation_date',
existing_type=sa.DateTime(),
type_=postgresql.TIMESTAMP(timezone=True),
existing_nullable=False)
op.alter_column('attendances', 'creation_date',
existing_type=sa.DateTime(),
type_=postgresql.TIMESTAMP(timezone=True),
existing_nullable=True)
# ### end Alembic commands ###
+4 -11
View File
@@ -9,10 +9,10 @@ from jose import jwt
from jose.exceptions import ExpiredSignatureError
from jwt import PyJWTError
from pydantic import BaseModel, ValidationError
from sqlalchemy import select
from sqlalchemy.orm import Session
from ..core.config import settings
from ..db.session import SessionLocal
from ..models.client import Client
from ..models.user import User as UserModel
@@ -39,15 +39,6 @@ def f7(seq):
return [x for x in seq if not (x in seen or seen_add(x))]
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
def create_access_token(*, data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
@@ -75,7 +66,9 @@ def authenticate_user(username: str, password: str, db: Session) -> Optional[Use
def client_allowed(user: UserModel, client_id: int, otp: Optional[int] = None, db: Session = None) -> (bool, Client):
client = db.query(Client).filter(Client.code == client_id).first() if client_id else None
client: Client = (
db.execute(select(Client).where(Client.code == client_id)).scalar_one_or_none() if client_id else None
)
if client is None:
client = Client.create(db)
allowed = "clients" in set([p.name.replace(" ", "-").lower() for r in user.roles for p in r.permissions])
+26 -26
View File
@@ -1,28 +1,28 @@
# Import all the models, so that Base has them before being
# imported by Alembic
from brewman.db.base_class import Base # noqa: F401
from brewman.models.account import Account # noqa: F401
from brewman.models.account_base import AccountBase # noqa: F401
from brewman.models.attendance import Attendance # noqa: F401
from brewman.models.batch import Batch # noqa: F401
from brewman.models.client import Client # noqa: F401
from brewman.models.cost_centre import CostCentre # noqa: F401
from brewman.models.db_image import DbImage # noqa: F401
from brewman.models.db_setting import DbSetting # noqa: F401
from brewman.models.employee import Employee # noqa: F401
from brewman.models.emplyee_benefit import EmployeeBenefit # noqa: F401
from brewman.models.fingerprint import Fingerprint # noqa: F401
from brewman.models.incentive import Incentive # noqa: F401
from brewman.models.inventory import Inventory # noqa: F401
from brewman.models.journal import Journal # noqa: F401
from brewman.models.login_history import LoginHistory # noqa: F401
from brewman.models.permission import Permission # noqa: F401
from brewman.models.product import Product # noqa: F401
from brewman.models.product_group import ProductGroup # noqa: F401
from brewman.models.recipe import Recipe # noqa: F401
from brewman.models.recipe_item import RecipeItem # noqa: F401
from brewman.models.role import Role # noqa: F401
from brewman.models.role_permission import role_permission # noqa: F401
from brewman.models.user import User # noqa: F401
from brewman.models.user_role import user_role # noqa: F401
from brewman.models.voucher import Voucher # noqa: F401
from .db.base_class import Base # noqa: F401
from .models.account import Account # noqa: F401
from .models.account_base import AccountBase # noqa: F401
from .models.attendance import Attendance # noqa: F401
from .models.batch import Batch # noqa: F401
from .models.client import Client # noqa: F401
from .models.cost_centre import CostCentre # noqa: F401
from .models.db_image import DbImage # noqa: F401
from .models.db_setting import DbSetting # noqa: F401
from .models.employee import Employee # noqa: F401
from .models.emplyee_benefit import EmployeeBenefit # noqa: F401
from .models.fingerprint import Fingerprint # noqa: F401
from .models.incentive import Incentive # noqa: F401
from .models.inventory import Inventory # noqa: F401
from .models.journal import Journal # noqa: F401
from .models.login_history import LoginHistory # noqa: F401
from .models.permission import Permission # noqa: F401
from .models.product import Product # noqa: F401
from .models.product_group import ProductGroup # noqa: F401
from .models.recipe import Recipe # noqa: F401
from .models.recipe_item import RecipeItem # noqa: F401
from .models.role import Role # noqa: F401
from .models.role_permission import role_permission # noqa: F401
from .models.user import User # noqa: F401
from .models.user_role import user_role # noqa: F401
from .models.voucher import Voucher # noqa: F401
+2 -2
View File
@@ -8,5 +8,5 @@ from ..core.config import settings
logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(settings.LOG_LEVEL)
engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True, future=True)
SessionFuture = sessionmaker(autoflush=False, bind=engine, future=True)
+3 -1
View File
@@ -13,6 +13,7 @@ from .routers import (
attendance_report,
attendance_types,
batch,
client,
cost_centre,
credit_salary,
db_image,
@@ -35,9 +36,10 @@ from .routers import (
rebase,
recipe,
reset_stock,
role,
user,
voucher,
)
from .routers.auth import client, role, user
from .routers.reports import (
balance_sheet,
cash_flow,
+5 -1
View File
@@ -1,9 +1,13 @@
from brewman.models.account_base import AccountBase
from sqlalchemy.orm import relationship
from .account_base import AccountBase
class Account(AccountBase):
__mapper_args__ = {"polymorphic_identity": ""}
products = relationship("Product", primaryjoin="Account.id==Product.account_id", back_populates="account")
def can_delete(self, advanced_delete):
if len(self.products) > 0:
return False, "Account has products"
+20 -16
View File
@@ -1,11 +1,12 @@
import uuid
from brewman.models.account_type import AccountType
from brewman.models.meta import Base
from sqlalchemy import Boolean, Column, ForeignKey, Integer, Unicode, func
from sqlalchemy import Boolean, Column, ForeignKey, Integer, Unicode, func, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Session, relationship
from .account_type import AccountType
from .meta import Base
class AccountBase(Base):
__tablename__ = "accounts"
@@ -29,6 +30,7 @@ class AccountBase(Base):
__mapper_args__ = {"polymorphic_on": account_type}
journals = relationship("Journal", back_populates="account")
cost_centre = relationship("CostCentre", back_populates="accounts")
@property
def __name__(self):
@@ -61,28 +63,29 @@ class AccountBase(Base):
self.is_fixture = is_fixture
@classmethod
def query(cls, q, type_, reconcilable=None, active=None, db=None):
query_ = db.query(cls)
def query(cls, q: str, type_: int, reconcilable: bool = None, active: bool = None, db: Session = None):
query_ = select(cls)
if type_ is not None:
if not isinstance(type_, int):
type_ = int(type_)
query_ = query_.filter(cls.type == type_)
query_ = query_.where(cls.type == type_)
if reconcilable is not None:
query_ = query_.filter(cls.is_reconcilable == reconcilable)
query_ = query_.where(cls.is_reconcilable == reconcilable)
if active is not None:
query_ = query_.filter(cls.is_active == active)
query_ = query_.where(cls.is_active == active)
if q is not None:
for item in q.split():
query_ = query_.filter(cls.name.ilike(f"%{item}%"))
return query_.order_by(cls.name)
query_ = query_.where(cls.name.ilike(f"%{item}%"))
return db.execute(query_.order_by(cls.name)).scalars().all()
def create(self, db: Session):
code = db.query(func.max(AccountBase.code)).filter(AccountBase.type == self.type).one()[0]
self.code = 1 if code is None else code + 1
self.code = db.execute(
select(func.coalesce(func.max(AccountBase.code), 0) + 1).where(AccountBase.type == self.type)
).scalar_one()
db.add(self)
return self
def can_delete(self, advanced_delete):
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:
@@ -92,9 +95,10 @@ class AccountBase(Base):
return True, ""
@classmethod
def get_code(cls, type_, db: Session):
code = db.query(func.max(AccountBase.code)).filter(AccountBase.type == type_).one()[0]
return 1 if code is None else code + 1
def get_code(cls, type_: int, db: Session):
return db.execute(
select(func.coalesce(func.max(AccountBase.code), 0) + 1).where(AccountBase.type == type_)
).scalar_one()
@classmethod
def all_purchases(cls):
+21 -8
View File
@@ -2,11 +2,21 @@ import uuid
from datetime import datetime
from brewman.models.meta import Base
from sqlalchemy import Boolean, Column, Date, DateTime, ForeignKey, Integer, Numeric
from sqlalchemy import (
Boolean,
Column,
Date,
DateTime,
ForeignKey,
Integer,
Numeric,
select,
)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Session, relationship
from .meta import Base
class Attendance(Base):
__tablename__ = "attendances"
@@ -16,7 +26,7 @@ class Attendance(Base):
date = Column("date", Date, nullable=False)
attendance_type = Column("attendance_type", Integer)
amount = Column("amount", Numeric(precision=5, scale=2))
creation_date = Column("creation_date", DateTime(timezone=True))
creation_date = Column("creation_date", DateTime())
user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("auth_users.id"))
is_valid = Column("is_valid", Boolean)
@@ -44,11 +54,14 @@ class Attendance(Base):
def create(self, db: Session):
old = (
db.query(Attendance)
.filter(Attendance.date == self.date)
.filter(Attendance.employee_id == self.employee_id)
.filter(Attendance.is_valid == True) # noqa: E712
.first()
db.execute(
select(Attendance)
.filter(Attendance.date == self.date)
.filter(Attendance.employee_id == self.employee_id)
.filter(Attendance.is_valid == True) # noqa: E712
)
.scalars()
.one_or_none()
)
if old is None or old.attendance_type != self.attendance_type:
if old is not None:
+14 -10
View File
@@ -1,11 +1,14 @@
import uuid
from brewman.models.meta import Base
from brewman.models.product import Product
from sqlalchemy import Column, Date, ForeignKey, Numeric
from datetime import date
from sqlalchemy import Column, Date, ForeignKey, Numeric, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Session, relationship
from .meta import Base
from .product import Product
class Batch(Base):
__tablename__ = "batches"
@@ -19,6 +22,7 @@ class Batch(Base):
discount = Column("discount", Numeric(precision=15, scale=5), nullable=False)
inventories = relationship("Inventory", backref="batch", cascade=None, cascade_backrefs=False)
product = relationship("Product", back_populates="batches")
def __init__(
self,
@@ -45,16 +49,16 @@ class Batch(Base):
return self.quantity_remaining * self.rate * (1 + self.tax) * (1 - self.discount)
@classmethod
def list(cls, q, include_nil, date, db: Session):
query = db.query(cls).join(cls.product)
def list(cls, q: str, include_nil: bool, date_: date, db: Session):
query = select(cls).join(cls.product)
if not include_nil:
query = query.filter(cls.quantity_remaining > 0)
if date is not None:
query = query.filter(cls.name <= date)
query = query.where(cls.quantity_remaining > 0)
if date_ is not None:
query = query.where(cls.name <= date_)
if q is not None:
for item in q.split():
query = query.filter(Product.name.ilike(f"%{item}%"))
return query.order_by(Product.name).order_by(cls.name).all()
query = query.where(Product.name.ilike(f"%{item}%"))
return db.execute(query.order_by(Product.name).order_by(cls.name)).scalars().all()
@classmethod
def suspense(cls):
+1 -1
View File
@@ -23,7 +23,7 @@ class Client(Base):
name = Column("name", Unicode(255), unique=True, nullable=False)
enabled = Column("enabled", Boolean, nullable=False)
otp = Column("otp", Integer)
creation_date = Column("creation_date", DateTime(timezone=True), nullable=False)
creation_date = Column("creation_date", DateTime(), nullable=False)
login_history: List[LoginHistory] = relationship("LoginHistory", order_by=desc(LoginHistory.date), backref="client")
+3 -2
View File
@@ -1,10 +1,11 @@
import uuid
from brewman.models.meta import Base
from sqlalchemy import Boolean, Column, Unicode
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from .meta import Base
class CostCentre(Base):
__tablename__ = "cost_centres"
@@ -13,7 +14,7 @@ class CostCentre(Base):
name = Column("name", Unicode(255), unique=True)
is_fixture = Column("is_fixture", Boolean, nullable=False)
accounts = relationship("AccountBase", backref="cost_centre")
accounts = relationship("AccountBase", back_populates="cost_centre")
journals = relationship("Journal", back_populates="cost_centre")
@property
+3 -2
View File
@@ -2,10 +2,11 @@ import uuid
from datetime import datetime
from brewman.models.meta import Base
from sqlalchemy import Column, DateTime, Unicode
from sqlalchemy.dialects.postgresql import BYTEA, UUID
from .meta import Base
class DbImage(Base):
__tablename__ = "images"
@@ -15,7 +16,7 @@ class DbImage(Base):
resource_type = Column("resource_type", Unicode(255), nullable=False)
image = Column("image", BYTEA, nullable=False)
thumbnail = Column("thumbnail", BYTEA, nullable=False)
creation_date = Column("creation_date", DateTime(timezone=True), nullable=False)
creation_date = Column("creation_date", DateTime(), nullable=False)
def __init__(
self,
+6 -4
View File
@@ -1,8 +1,9 @@
from brewman.models.account_base import AccountBase
from sqlalchemy import Column, Date, ForeignKey, Integer, Numeric, Unicode, func
from sqlalchemy import Column, Date, ForeignKey, Integer, Numeric, Unicode, func, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Session, relationship
from .account_base import AccountBase
class Employee(AccountBase):
__tablename__ = "employees"
@@ -47,8 +48,9 @@ class Employee(AccountBase):
)
def create(self, db: Session):
code = db.query(func.max(AccountBase.code)).filter(AccountBase.type == self.type).one()[0]
self.code = 1 if code is None else code + 1
self.code = db.execute(
select(func.coalesce(func.max(AccountBase.code), 0) + 1).filter(AccountBase.type == self.type)
).scalar_one()
self.name += f" ({str(self.code)})"
db.add(self)
return self
+3 -2
View File
@@ -1,11 +1,12 @@
import uuid
from brewman.models.journal import Journal
from brewman.models.meta import Base
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import backref, relationship
from .journal import Journal
from .meta import Base
class EmployeeBenefit(Base):
__tablename__ = "employee_benefit"
+2 -1
View File
@@ -1,9 +1,10 @@
import uuid
from brewman.models.meta import Base
from sqlalchemy import Column, DateTime, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from .meta import Base
class Fingerprint(Base):
__tablename__ = "fingerprints"
+3 -2
View File
@@ -1,11 +1,12 @@
import uuid
from brewman.models.journal import Journal
from brewman.models.meta import Base
from sqlalchemy import Column, ForeignKey, Numeric
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import backref, relationship
from .journal import Journal
from .meta import Base
class Incentive(Base):
__tablename__ = "incentives"
+3 -1
View File
@@ -1,11 +1,12 @@
import uuid
from brewman.models.meta import Base
from sqlalchemy import Column, ForeignKey, Numeric, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from .meta import Base
class Inventory(Base):
__tablename__ = "inventories"
@@ -26,6 +27,7 @@ class Inventory(Base):
discount = Column("discount", Numeric(precision=15, scale=5), nullable=False)
voucher = relationship("Voucher", back_populates="inventories")
product = relationship("Product", back_populates="inventories")
def __init__(
self,
+2 -1
View File
@@ -1,11 +1,12 @@
import uuid
from brewman.models.meta import Base
from sqlalchemy import Column, ForeignKey, Integer, Numeric
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from .meta import Base
class Journal(Base):
__tablename__ = "journals"
+4 -1
View File
@@ -6,6 +6,7 @@ from datetime import datetime
from sqlalchemy import Column, DateTime, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from sqlalchemy.schema import ForeignKey
from .meta import Base
@@ -22,7 +23,9 @@ class LoginHistory(Base):
ForeignKey("auth_clients.client_id"),
nullable=False,
)
date = Column("date", DateTime(timezone=True), nullable=False)
date = Column("date", DateTime(), nullable=False)
user = relationship("User", back_populates="login_history")
def __init__(self, user_id=None, client_id=None, date=None, id_=None):
self.user_id = user_id
+1 -1
View File
@@ -19,7 +19,7 @@ class Permission(Base):
id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column("name", Unicode(255), unique=True)
roles: List[Role] = relationship("Role", secondary=role_permission, backref="permissions")
roles: List[Role] = relationship("Role", secondary=role_permission, back_populates="permissions")
def __init__(self, name=None, id_=None):
self.name = name
+21 -13
View File
@@ -1,6 +1,5 @@
import uuid
from brewman.models.meta import Base
from sqlalchemy import (
Boolean,
Column,
@@ -9,11 +8,16 @@ from sqlalchemy import (
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"
@@ -40,10 +44,11 @@ class Product(Base):
is_purchased = Column("is_purchased", Boolean, nullable=False)
is_sold = Column("is_sold", Boolean, nullable=False)
batches = relationship("Batch", backref="product")
inventories = relationship("Inventory", backref="product")
recipes = relationship("Recipe", backref="product")
account = relationship("Account", primaryjoin="Account.id==Product.account_id", backref="products")
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,
@@ -79,17 +84,20 @@ class Product(Base):
self.id = id_
self.is_fixture = is_fixture
@property
@hybrid_property
def full_name(self):
return "{0} ({1})".format(self.name, self.units)
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):
code = db.query(func.max(Product.code)).one()[0]
self.code = 1 if code is None else code + 1
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):
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:
@@ -99,8 +107,8 @@ class Product(Base):
return True, ""
@classmethod
def query(cls, q, is_purchased=None, active=None, db=None):
query_ = db.query(cls)
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:
@@ -109,7 +117,7 @@ class Product(Base):
for item in q.split():
if item.strip() != "":
query_ = query_.filter(cls.name.ilike(f"%{item}%"))
return query_
return db.execute(query_).scalars().all()
@classmethod
def suspense(cls):
+3 -2
View File
@@ -1,10 +1,11 @@
import uuid
from brewman.models.meta import Base
from sqlalchemy import Boolean, Column, Unicode
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from .meta import Base
class ProductGroup(Base):
__tablename__ = "product_groups"
@@ -13,7 +14,7 @@ class ProductGroup(Base):
name = Column("name", Unicode(255), unique=True)
is_fixture = Column("is_fixture", Boolean, nullable=False)
products = relationship("Product", backref="product_group")
products = relationship("Product", back_populates="product_group")
def __init__(self, name=None, id_=None, is_fixture=False):
self.name = name
+7 -5
View File
@@ -2,11 +2,12 @@ import uuid
from datetime import date
from brewman.models.meta import Base
from sqlalchemy import Column, Date, ForeignKey, Numeric, Unicode
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from .meta import Base
class Recipe(Base):
__tablename__ = "recipes"
@@ -19,13 +20,14 @@ class Recipe(Base):
sale_price = Column("sale_price", Numeric(precision=15, scale=2), nullable=False)
notes = Column("notes", Unicode(255))
valid_from = Column("valid_from", Date, nullable=False)
valid_to = Column("valid_to", Date, nullable=False)
valid_from = Column("valid_from", Date, nullable=True)
valid_till = Column("valid_till", Date, nullable=True)
effective_from = Column("effective_from", Date, nullable=False)
effective_to = Column("effective_to", Date)
recipe_items = relationship("RecipeItem", backref="recipe")
product = relationship("Product", back_populates="recipes")
def __init__(
self,
@@ -34,7 +36,7 @@ class Recipe(Base):
cost_price=None,
sale_price=None,
valid_from=None,
valid_to=None,
valid_till=None,
notes=None,
effective_from=None,
id_=None,
@@ -44,7 +46,7 @@ class Recipe(Base):
self.cost_price = cost_price
self.sale_price = sale_price
self.valid_from = valid_from
self.valid_to = valid_to
self.valid_till = valid_till
self.notes = "" if notes is None else notes
self.effective_from = date.today() if effective_from is None else effective_from
self.effective_to = None
+2 -1
View File
@@ -1,10 +1,11 @@
import uuid
from brewman.models.meta import Base
from sqlalchemy import Column, ForeignKey, Integer, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from .meta import Base
class RecipeItem(Base):
__tablename__ = "recipe_items"
+4
View File
@@ -4,8 +4,10 @@ import uuid
from sqlalchemy import Column, Unicode
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from .meta import Base
from .role_permission import role_permission
class Role(Base):
@@ -14,6 +16,8 @@ class Role(Base):
id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column("name", Unicode(255), unique=True)
permissions = relationship("Permission", secondary=role_permission, back_populates="roles")
def __init__(self, name=None, id_=None):
self.name = name
self.id = id_
+5 -3
View File
@@ -5,7 +5,7 @@ import uuid
from hashlib import md5
from typing import List, Optional
from sqlalchemy import Boolean, Column, Unicode, desc
from sqlalchemy import Boolean, Column, Unicode, desc, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Session, relationship, synonym
@@ -28,7 +28,9 @@ class User(Base):
locked_out = Column("disabled", Boolean)
roles: List[Role] = relationship("Role", secondary=user_role)
login_history: List[LoginHistory] = relationship("LoginHistory", order_by=desc(LoginHistory.date), backref="user")
login_history: List[LoginHistory] = relationship(
"LoginHistory", order_by=desc(LoginHistory.date), back_populates="user"
)
def _get_password(self):
return self._password
@@ -53,7 +55,7 @@ class User(Base):
def auth(cls, name: str, password: str, db: Session) -> Optional[User]:
if password is None:
return None
user = db.query(User).filter(User.name.ilike(name)).first()
user = db.execute(select(cls).where(cls.name.ilike(name))).scalars().one_or_none()
if not user:
return None
if user.password != encrypt(password) or user.locked_out:
+2 -1
View File
@@ -3,9 +3,10 @@ from typing import Union
import brewman.schemas.input as schema_in
import brewman.schemas.voucher as schema
from brewman.models.voucher import Voucher
from fastapi import HTTPException, status
from .voucher import Voucher
def check_journals_are_valid(voucher: Voucher):
if len(voucher.journals) < 2:
+2 -2
View File
@@ -18,8 +18,8 @@ class Voucher(Base):
is_reconciled = Column("is_reconciled", Boolean, nullable=False)
reconcile_date = Column("reconcile_date", Date, nullable=False)
is_starred = Column("is_starred", Boolean, nullable=False)
creation_date = Column("creation_date", DateTime(timezone=True), nullable=False)
last_edit_date = Column("last_edit_date", DateTime(timezone=True), nullable=False)
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)
posted = Column("is_posted", Boolean, nullable=False)
+2 -1
View File
@@ -5,6 +5,7 @@ from datetime import date, timedelta
from decimal import Decimal
from typing import Optional
from sqlalchemy import select
from sqlalchemy.orm import Session
from ..models.db_setting import DbSetting
@@ -13,7 +14,7 @@ from ..models.db_setting import DbSetting
def get_lock_info(db: Session) -> (Optional[date], Optional[date]):
start: Optional[date]
finish: Optional[date]
data = db.query(DbSetting).filter(DbSetting.name == "Lock Info").first()
data = db.execute(select(DbSetting).where(DbSetting.name == "Lock Info")).scalars().one_or_none()
if data is None:
return None, None
+75 -87
View File
@@ -7,12 +7,12 @@ from typing import List
import brewman.schemas.account as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import func
from sqlalchemy import func, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session, joinedload
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.account_base import AccountBase
from ..models.account_type import AccountType
@@ -26,112 +26,96 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=schemas.Account)
def save(
data: schemas.AccountIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["accounts"]),
) -> schemas.Account:
try:
item = Account(
name=data.name,
type_=data.type,
is_starred=data.is_starred,
is_active=data.is_active,
is_reconcilable=data.is_reconcilable,
cost_centre_id=data.cost_centre.id_,
).create(db)
db.commit()
return account_info(item)
with SessionFuture() as db:
item = Account(
name=data.name,
type_=data.type,
is_starred=data.is_starred,
is_active=data.is_active,
is_reconcilable=data.is_reconcilable,
cost_centre_id=data.cost_centre.id_,
).create(db)
db.commit()
return account_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.put("/{id_}", response_model=schemas.Account)
def update(
def update_route(
id_: uuid.UUID,
data: schemas.AccountIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["accounts"]),
) -> schemas.Account:
try:
item: Account = db.query(Account).filter(Account.id == id_).first()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
if not item.type == data.type:
item.code = Account.get_code(data.type, db)
item.type = data.type
item.name = data.name
item.is_active = data.is_active
item.is_reconcilable = data.is_reconcilable
item.is_starred = data.is_starred
item.cost_centre_id = data.cost_centre.id_
db.commit()
return account_info(item)
with SessionFuture() as db:
item: Account = db.execute(select(Account).where(Account.id == id_)).scalar_one()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
if not item.type == data.type:
item.code = Account.get_code(data.type, db)
item.type = data.type
item.name = data.name
item.is_active = data.is_active
item.is_reconcilable = data.is_reconcilable
item.is_starred = data.is_starred
item.cost_centre_id = data.cost_centre.id_
db.commit()
return account_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.delete("/{id_}", response_model=schemas.AccountBlank)
def delete(
def delete_route(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["accounts"]),
) -> schemas.AccountBlank:
account: Account = db.query(Account).filter(Account.id == id_).first()
can_delete, reason = account.can_delete("advanced-delete" in user.permissions)
if can_delete:
delete_with_data(account, db)
db.commit()
return account_blank()
else:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Cannot delete account because {reason}",
)
with SessionFuture() as db:
account: Account = db.execute(select(Account).where(Account.id == id_)).scalar_one()
can_delete, reason = account.can_delete("advanced-delete" in user.permissions)
if can_delete:
delete_with_data(account, db)
db.commit()
return account_blank()
else:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Cannot delete account because {reason}",
)
@router.get("", response_model=schemas.AccountBlank)
def show_blank(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["accounts"]),
) -> schemas.AccountBlank:
return account_blank()
@router.get("/list", response_model=List[schemas.Account])
async def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.Account]:
return [
account_info(item)
for item in db.query(Account).order_by(Account.type).order_by(Account.name).order_by(Account.code).all()
]
async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Account]:
with SessionFuture() as db:
return [
account_info(item)
for item in db.execute(select(Account).order_by(Account.type).order_by(Account.name).order_by(Account.code))
.scalars()
.all()
]
@router.get("/query")
@@ -141,15 +125,15 @@ async def show_term(
r: bool = None,
a: bool = None,
c: int = None,
db: Session = Depends(get_db),
current_user: UserToken = Depends(get_user),
):
count = c
list_ = []
for index, item in enumerate(AccountBase.query(q, t, r, a, db)):
list_.append({"id": item.id, "name": item.name})
if count is not None and index == count - 1:
break
with SessionFuture() as db:
for index, item in enumerate(AccountBase.query(q, t, r, a, db)):
list_.append({"id": item.id, "name": item.name})
if count is not None and index == count - 1:
break
return list_
@@ -157,34 +141,35 @@ async def show_term(
async def show_balance(
id_: uuid.UUID,
d: str = None,
db: Session = Depends(get_db),
user: UserToken = Depends(get_user),
):
date = None if d is None or d == "" else datetime.strptime(d, "%d-%b-%Y")
return {"date": balance(id_, date, db), "total": balance(id_, None, db)}
with SessionFuture() as db:
return {"date": balance(id_, date, db), "total": balance(id_, None, db)}
@router.get("/{id_}", response_model=schemas.Account)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["accounts"]),
) -> schemas.Account:
item: Account = db.query(Account).filter(Account.id == id_).first()
return account_info(item)
with SessionFuture() as db:
item: Account = db.execute(select(Account).where(Account.id == id_)).scalar_one()
return account_info(item)
def balance(id_: uuid.UUID, date, db: Session) -> Decimal:
account = db.query(AccountBase).filter(AccountBase.id == id_).first()
account = db.execute(select(AccountBase).where(AccountBase.id == id_)).scalar_one()
if not account.type_object.balance_sheet:
return 0
bal = db.query(func.sum(Journal.amount * Journal.debit)).join(Journal.voucher)
bal = select(func.sum(Journal.amount * Journal.debit)).join(Journal.voucher)
if date is not None:
bal = bal.filter(Voucher.date <= date)
bal = bal.where(Voucher.date <= date)
bal = bal.filter(Voucher.type != VoucherType.by_name("Issue").id).filter(Journal.account_id == id_).scalar()
return 0 if bal is None else bal
bal = bal.where(Voucher.type != VoucherType.by_name("Issue").id).where(Journal.account_id == id_)
result = db.execute(bal).scalar()
return 0 if result is None else result
def account_info(item: Account) -> schemas.Account:
@@ -217,11 +202,14 @@ def account_blank() -> schemas.AccountBlank:
def delete_with_data(account: Account, db: Session) -> None:
suspense_account = db.query(Account).filter(Account.id == Account.suspense()).first()
suspense_account = db.execute(select(Account).where(Account.id == Account.suspense())).scalar_one()
query: List[Voucher] = (
db.query(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.filter(Voucher.journals.any(Journal.account_id == account.id))
db.execute(
select(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.where(Voucher.journals.any(Journal.account_id == account.id))
)
.scalars()
.all()
)
+41 -47
View File
@@ -2,14 +2,14 @@ from datetime import date, datetime, timedelta
import brewman.schemas.voucher as schemas
from fastapi import APIRouter, Depends, HTTPException, Request, Security, status
from sqlalchemy import or_
from fastapi import APIRouter, HTTPException, Request, Security, status
from sqlalchemy import or_, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, set_date
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.attendance import Attendance
from ..models.employee import Employee
from ..routers.fingerprint import get_prints
@@ -19,15 +19,6 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.Attendance)
def attendance_blank(request: Request, user: UserToken = Security(get_user, scopes=["attendance"])):
return {"date": get_date(request.session), "body": []}
@@ -37,40 +28,47 @@ def attendance_blank(request: Request, user: UserToken = Security(get_user, scop
def attendance_date(
date_: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["attendance"]),
):
set_date(date_, request.session)
return {
"date": date_,
"body": attendance_date_report(datetime.strptime(date_, "%d-%b-%Y"), db),
}
with SessionFuture() as db:
return {
"date": date_,
"body": attendance_date_report(datetime.strptime(date_, "%d-%b-%Y"), db),
}
def attendance_date_report(date_: date, db: Session):
body = []
employees = (
db.query(Employee)
.filter(Employee.joining_date <= date_)
.filter(
or_(
Employee.is_active,
Employee.leaving_date >= date_,
db.execute(
select(Employee)
.where(
Employee.joining_date <= date_,
or_(
Employee.is_active,
Employee.leaving_date >= date_,
),
)
.order_by(Employee.cost_centre_id, Employee.designation, Employee.name)
)
.order_by(Employee.cost_centre_id)
.order_by(Employee.designation)
.order_by(Employee.name)
.scalars()
.all()
)
for item in employees:
att = (
db.query(Attendance)
.filter(Attendance.employee_id == item.id)
.filter(Attendance.date == date_)
.filter(Attendance.is_valid == True) # noqa: E712
.first()
db.execute(
select(Attendance).where(
Attendance.employee_id == item.id,
Attendance.date == date_,
Attendance.is_valid == True, # noqa: E712
)
)
.scalars()
.one_or_none()
)
att = 0 if att is None else att.attendance_type
prints, hours_worked, full_day = get_prints(item.id, date_, db)
@@ -94,31 +92,27 @@ def attendance_date_report(date_: date, db: Session):
def save(
date_: str,
data: schemas.Attendance,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["attendance"]),
):
try:
att_date = datetime.strptime(date_, "%d-%b-%Y").date()
for item in data.body:
if item.attendance_type.id_ != 0:
attendance = Attendance(
employee_id=item.id_,
date=att_date,
attendance_type=item.attendance_type.id_,
user_id=user.id_,
)
attendance.create(db)
db.commit()
return {"date": date_, "body": attendance_date_report(att_date, db)}
with SessionFuture() as db:
for item in data.body:
if item.attendance_type.id_ != 0:
attendance = Attendance(
employee_id=item.id_,
date=att_date,
attendance_type=item.attendance_type.id_,
user_id=user.id_,
)
attendance.create(db)
db.commit()
return {"date": date_, "body": attendance_date_report(att_date, db)}
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def date_range(start: date, stop: date, step=timedelta(days=1), inclusive=False):
+30 -34
View File
@@ -3,12 +3,12 @@ import io
from datetime import date, datetime
from fastapi import APIRouter, Depends
from fastapi import APIRouter
from fastapi.responses import StreamingResponse
from sqlalchemy import or_
from sqlalchemy import or_, select
from sqlalchemy.orm import Session
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.attendance import Attendance
from ..models.attendance_type import AttendanceType
from ..models.employee import Employee
@@ -18,33 +18,24 @@ from .attendance import date_range
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("")
def get_report(
s: str = None,
f: str = None,
db: Session = Depends(get_db),
# user: UserToken = Security(get_user, scopes=["attendance"]) ## removed as jwt headers are a pain in the ass
):
try:
output = io.StringIO()
attendance_record(
datetime.strptime(s, "%d-%b-%Y"),
datetime.strptime(f, "%d-%b-%Y"),
output,
db,
)
headers = {"Content-Disposition": "attachment; filename = attendance-record.csv"}
output.seek(0)
return StreamingResponse(output, media_type="text/csv", headers=headers)
with SessionFuture() as db:
output = io.StringIO()
attendance_record(
datetime.strptime(s, "%d-%b-%Y"),
datetime.strptime(f, "%d-%b-%Y"),
output,
db,
)
headers = {"Content-Disposition": "attachment; filename = attendance-record.csv"}
output.seek(0)
return StreamingResponse(output, media_type="text/csv", headers=headers)
finally:
pass
# output.close()
@@ -57,14 +48,15 @@ def attendance_record(start_date: date, finish_date: date, output, db: Session):
not_set = AttendanceType.by_id(0)
employees = (
db.query(Employee)
.filter(Employee.joining_date <= finish_date)
.filter(or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id)
.order_by(Employee.designation)
.order_by(Employee.name)
db.execute(
select(Employee)
.where(Employee.joining_date <= finish_date, or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id, Employee.designation, Employee.name)
)
.scalars()
.all()
)
writer = csv.writer(output)
writer.writerow(header)
for employee in employees:
@@ -79,11 +71,15 @@ def attendance_record(start_date: date, finish_date: date, output, db: Session):
row_value = ["", "", "", "", employee.salary, employee.points]
for date_ in date_range(start_date, finish_date, inclusive=True):
att = (
db.query(Attendance)
.filter(Attendance.employee_id == employee.id)
.filter(Attendance.date == date_)
.filter(Attendance.is_valid == True) # noqa: E712
.first()
db.execute(
select(Attendance).where(
Attendance.employee_id == employee.id,
Attendance.date == date_,
Attendance.is_valid == True, # noqa: E712
)
)
.scalars()
.one_or_none()
)
att = not_set if att is None else AttendanceType.by_id(att.attendance_type)
row_display.append(att.name)
Whitespace-only changes.
-115
View File
@@ -1,115 +0,0 @@
import uuid
from typing import List
import brewman.schemas.client as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ...core.security import get_current_active_user as get_user
from ...db.session import SessionLocal
from ...models.client import Client
from ...models.login_history import LoginHistory
from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.put("/{id_}")
def update(
id_: uuid.UUID,
data: schemas.ClientIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["clients"]),
):
try:
item: Client = db.query(Client).filter(Client.id == id_).first()
item.enabled = data.enabled
if item.enabled:
item.otp = None
item.name = data.name
db.commit()
return {}
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.delete("/{id_}")
def delete(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["clients"]),
):
try:
item: Client = db.query(Client).filter(Client.id == id_).first()
db.execute(LoginHistory.__table__.delete(LoginHistory.client_id == item.id))
db.delete(item)
db.commit()
return {}
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("/list", response_model=List[schemas.ClientList])
async def show_list(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["clients"]),
) -> List[schemas.ClientList]:
list_ = db.query(Client).order_by(Client.name).all()
clients = []
for item in list_:
clients.append(
schemas.ClientList(
id=item.id,
code=item.code,
name=item.name,
enabled=item.enabled,
otp=item.otp,
creationDate=item.creation_date,
lastUser=item.login_history[0].user.name if len(item.login_history) else "None",
lastDate=item.login_history[0].date if len(item.login_history) else None,
)
)
return clients
@router.get("/{id_}")
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["clients"]),
):
item: Client = db.query(Client).filter(Client.id == id_).first()
return {
"id": item.id,
"code": item.code,
"name": item.name,
"enabled": item.enabled,
"otp": item.otp,
}
-218
View File
@@ -1,218 +0,0 @@
import uuid
from typing import List
import brewman.schemas.user as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ...core.security import get_current_active_user as get_user
from ...db.session import SessionLocal
from ...models.role import Role
from ...models.user import User
from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=schemas.User)
def save(
data: schemas.UserIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.User:
try:
item = User(name=data.name, password=data.password, locked_out=data.locked_out)
db.add(item)
add_roles(item, data.roles, db)
db.commit()
return user_info(item, db, user)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("/me", response_model=schemas.User)
def show_me(
db: Session = Depends(get_db),
user: UserToken = Depends(get_user),
) -> schemas.User:
item = db.query(User).filter(User.id == user.id_).first()
return user_info(item, db, user)
@router.put("/me", response_model=schemas.User)
def update_me(
data: schemas.UserIn,
db: Session = Depends(get_db),
user: UserToken = Depends(get_user),
) -> schemas.User:
try:
item: User = db.query(User).filter(User.id == user.id_).first()
if "users" in user.permissions:
item.name = data.name
item.locked_out = data.locked_out
add_roles(item, data.roles, db)
if data.password and item.password != data.password:
item.password = data.password
db.commit()
return user_info(item, db, user)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.put("/{id_}", response_model=schemas.User)
def update(
id_: uuid.UUID,
data: schemas.UserIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.User:
try:
item: User = db.query(User).filter(User.id == id_).first()
item.name = data.name
if data.password and item.password != data.password:
item.password = data.password
item.locked_out = data.locked_out
add_roles(item, data.roles, db)
db.commit()
return user_info(item, db, user)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def add_roles(user: User, roles: List[schemas.RoleItem], db: Session) -> None:
for role in roles:
ug = [g for g in user.roles if g.id == role.id_]
ug = None if len(ug) == 0 else ug[0]
if role.enabled and ug is None:
user.roles.append(db.query(Role).filter(Role.id == role.id_).one())
elif not role.enabled and ug:
user.roles.remove(ug)
@router.delete("/{id_}")
def delete(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
):
try:
item: User = db.query(User).filter(User.id == id_).first()
if item is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="User not found",
)
else:
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED,
detail="User deletion not implemented",
)
except Exception:
db.rollback()
raise
@router.get("")
def show_blank(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
):
return user_blank(db)
@router.get("/list", response_model=List[schemas.UserList])
async def show_list(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> List[schemas.UserList]:
return [
schemas.UserList(
id=item.id,
name=item.name,
lockedOut=item.locked_out,
roles=[p.name for p in sorted(item.roles, key=lambda p: p.name)],
lastDevice=item.login_history[0].client.name if len(item.login_history) else "None",
lastDate=item.login_history[0].date if len(item.login_history) else None,
)
for item in db.query(User).order_by(User.name).all()
]
@router.get("/active")
async def show_active(db: Session = Depends(get_db), user: UserToken = Depends(get_user)):
return [
{"name": item.name}
for item in db.query(User).filter(User.locked_out == False).order_by(User.name) # noqa: E712
]
@router.get("/{id_}", response_model=schemas.User)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.User:
item = db.query(User).filter(User.id == id_).first()
return user_info(item, db, user)
def user_info(item: User, db: Session, user: UserToken) -> schemas.User:
return schemas.User(
id=item.id,
name=item.name,
password="",
lockedOut=item.locked_out,
roles=[
schemas.RoleItem(
id=r.id,
name=r.name,
enabled=True if r in item.roles else False,
)
for r in db.query(Role).order_by(Role.name).all()
]
if "advanced-delete" in user.permissions
else [],
)
def user_blank(db: Session) -> schemas.UserBlank:
return schemas.UserBlank(
name="",
password="",
lockedOut=False,
roles=[schemas.RoleItem(id=r.id, name=r.name, enabled=False) for r in db.query(Role).order_by(Role.name).all()],
)
+25 -35
View File
@@ -1,10 +1,9 @@
import datetime
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.batch import Batch
from ..schemas.user import UserToken
@@ -12,45 +11,36 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("")
def batch_term(
q: str,
c: int = None,
d: str = None,
db: Session = Depends(get_db),
current_user: UserToken = Depends(get_user),
):
date = None if not d else datetime.datetime.strptime(d, "%d-%b-%Y")
list_ = []
for index, item in enumerate(Batch.list(q, include_nil=False, date=date, db=db)):
text = (
f"{item.product.name} ({item.product.units}) {item.quantity_remaining:.2f}@"
f"{item.rate:.2f} from {item.name.strftime('%d-%b-%Y')}"
)
list_.append(
{
"id": item.id,
"name": text,
"quantityRemaining": round(item.quantity_remaining, 2),
"rate": round(item.rate, 2),
"tax": round(item.tax, 5),
"discount": round(item.discount, 5),
"product": {
"id": item.product.id,
"name": item.product.name,
"units": item.product.units,
},
}
)
if c is not None and index == c - 1:
break
return list_
with SessionFuture() as db:
for index, item in enumerate(Batch.list(q, include_nil=False, date=date, db=db)):
text = (
f"{item.product.name} ({item.product.units}) {item.quantity_remaining:.2f}@"
f"{item.rate:.2f} from {item.name.strftime('%d-%b-%Y')}"
)
list_.append(
{
"id": item.id,
"name": text,
"quantityRemaining": round(item.quantity_remaining, 2),
"rate": round(item.rate, 2),
"tax": round(item.tax, 5),
"discount": round(item.discount, 5),
"product": {
"id": item.product.id,
"name": item.product.name,
"units": item.product.units,
},
}
)
if c is not None and index == c - 1:
break
return list_
+98
View File
@@ -0,0 +1,98 @@
import uuid
from typing import List
import brewman.schemas.client as schemas
from fastapi import APIRouter, HTTPException, Security, status
from sqlalchemy import delete, select
from sqlalchemy.exc import SQLAlchemyError
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionFuture
from ..models.client import Client
from ..models.login_history import LoginHistory
from ..schemas.user import UserToken
router = APIRouter()
@router.put("/{id_}")
def update_route(
id_: uuid.UUID,
data: schemas.ClientIn,
user: UserToken = Security(get_user, scopes=["clients"]),
):
try:
with SessionFuture() as db:
item: Client = db.execute(select(Client).where(Client.id == id_)).scalar_one()
item.enabled = data.enabled
if item.enabled:
item.otp = None
item.name = data.name
db.commit()
return {}
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
@router.delete("/{id_}")
def delete_route(
id_: uuid.UUID,
user: UserToken = Security(get_user, scopes=["clients"]),
):
try:
with SessionFuture() as db:
item: Client = db.execute(select(Client).where(Client.id == id_)).scalar_one()
db.execute(delete(LoginHistory).where(LoginHistory.client_id == item.id))
db.delete(item)
db.commit()
return {}
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
@router.get("/list", response_model=List[schemas.ClientList])
async def show_list(
user: UserToken = Security(get_user, scopes=["clients"]),
) -> List[schemas.ClientList]:
with SessionFuture() as db:
list_ = db.execute(select(Client).order_by(Client.name)).scalars().all()
clients = []
for item in list_:
clients.append(
schemas.ClientList(
id=item.id,
code=item.code,
name=item.name,
enabled=item.enabled,
otp=item.otp,
creationDate=item.creation_date,
lastUser=item.login_history[0].user.name if len(item.login_history) else "None",
lastDate=item.login_history[0].date if len(item.login_history) else None,
)
)
return clients
@router.get("/{id_}")
def show_id(
id_: uuid.UUID,
user: UserToken = Security(get_user, scopes=["clients"]),
):
with SessionFuture() as db:
item: Client = db.execute(select(Client).where(Client.id == id_)).scalar_one()
return {
"id": item.id,
"code": item.code,
"name": item.name,
"enabled": item.enabled,
"otp": item.otp,
}
+42 -57
View File
@@ -5,126 +5,111 @@ from typing import List
import brewman.schemas.cost_centre as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import delete, select, update
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account_base import AccountBase
from ..models.cost_centre import CostCentre
from ..models.journal import Journal
from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=schemas.CostCentre)
def save(
data: schemas.CostCentreIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cost-centres"]),
) -> schemas.CostCentre:
try:
item = CostCentre(name=data.name)
db.add(item)
db.commit()
return cost_centre_info(item)
with SessionFuture() as db:
item = CostCentre(name=data.name)
db.add(item)
db.commit()
return cost_centre_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.put("/{id_}", response_model=schemas.CostCentre)
def update(
def update_route(
id_: uuid.UUID,
data: schemas.CostCentreIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cost-centres"]),
) -> schemas.CostCentre:
try:
item = db.query(CostCentre).filter(CostCentre.id == id_).first()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
db.commit()
return cost_centre_info(item)
with SessionFuture() as db:
item = db.execute(select(CostCentre).where(CostCentre.id == id_)).scalar_one()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
db.commit()
return cost_centre_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.delete("/{id_}", response_model=schemas.CostCentreBlank)
def delete(
def delete_route(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cost-centres"]),
):
try:
item: CostCentre = db.query(CostCentre).filter(CostCentre.id == id_).first()
if item is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Cost Centre not found",
)
with SessionFuture() as db:
item: CostCentre = db.execute(select(CostCentre).where(CostCentre.id == id_)).scalar_one()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED,
detail="Cost Centre deletion not implemented",
db.execute(
update(AccountBase)
.where(AccountBase.cost_centre_id == item.id)
.values(cost_centre_id=CostCentre.cost_centre_overall())
)
except Exception:
db.rollback()
raise
db.execute(
update(Journal)
.where(Journal.cost_centre_id == item.id)
.values(cost_centre_id=CostCentre.cost_centre_overall())
)
db.execute(delete(CostCentre).where(CostCentre.id == item.id))
db.commit()
@router.get("", response_model=schemas.CostCentreBlank)
def show_blank(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cost-centres"]),
) -> schemas.CostCentreBlank:
return cost_centre_blank()
@router.get("/list", response_model=List[schemas.CostCentre])
async def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.CostCentre]:
return [cost_centre_info(item) for item in db.query(CostCentre).order_by(CostCentre.name).all()]
async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.CostCentre]:
with SessionFuture() as db:
return [
cost_centre_info(item) for item in db.execute(select(CostCentre).order_by(CostCentre.name)).scalars().all()
]
@router.get("/{id_}", response_model=schemas.CostCentre)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cost-centres"]),
) -> schemas.CostCentre:
item = db.query(CostCentre).filter(CostCentre.id == id_).first()
return cost_centre_info(item)
with SessionFuture() as db:
item = db.execute(select(CostCentre).where(CostCentre.id == id_)).scalar_one()
return cost_centre_info(item)
def cost_centre_info(item: CostCentre) -> schemas.CostCentre:
+35 -40
View File
@@ -1,13 +1,13 @@
from calendar import monthrange
from datetime import date, datetime
from fastapi import APIRouter, Body, Depends, HTTPException, Security, status
from sqlalchemy import or_
from fastapi import APIRouter, Body, HTTPException, Security, status
from sqlalchemy import or_, select
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_first_day, get_last_day
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.attendance import Attendance
from ..models.attendance_type import AttendanceType
@@ -21,39 +21,29 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("")
def credit_salary(
month: str = Body(..., embed=True),
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["attendance"]),
):
month = datetime.strptime(month, "%d-%b-%Y").date()
start_date = get_first_day(month)
finish_date = get_last_day(month)
voucher = Voucher(
date=finish_date,
narration="Auto Generated Salary Entry",
user_id=user.id_,
type_=VoucherType.by_name("Journal"),
posted=True,
poster_id=user.id_,
)
db.add(voucher)
for item in salary_journals(start_date, finish_date, db):
voucher.journals.append(item)
db.add(item)
db.commit()
return {"message": "Salary Entry created"}
with SessionFuture() as db:
voucher = Voucher(
date=finish_date,
narration="Auto Generated Salary Entry",
user_id=user.id_,
type_=VoucherType.by_name("Journal"),
posted=True,
poster_id=user.id_,
)
db.add(voucher)
for item in salary_journals(start_date, finish_date, db):
voucher.journals.append(item)
db.add(item)
db.commit()
return {"message": "Salary Entry created"}
def salary_journals(start_date: date, finish_date: date, db: Session):
@@ -61,21 +51,26 @@ def salary_journals(start_date: date, finish_date: date, db: Session):
amount = 0
journals = []
employees = (
db.query(Employee)
.filter(Employee.joining_date <= finish_date)
.filter(or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id)
.order_by(Employee.designation)
.order_by(Employee.name)
db.execute(
select(Employee)
.where(Employee.joining_date <= finish_date, or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id, Employee.designation, Employee.name)
)
.scalars()
.all()
)
for employee in employees:
att = (
db.query(Attendance)
.filter(Attendance.employee_id == employee.id)
.filter(Attendance.date >= start_date)
.filter(Attendance.date <= finish_date)
.filter(Attendance.is_valid == True) # noqa: E712
db.execute(
select(Attendance).where(
Attendance.employee_id == employee.id,
Attendance.date >= start_date,
Attendance.date <= finish_date,
Attendance.is_valid == True, # noqa: E712
)
)
.scalars()
.all()
)
att = sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att))
@@ -90,7 +85,7 @@ def salary_journals(start_date: date, finish_date: date, db: Session):
cost_centre_id=employee.cost_centre_id,
)
)
salary = db.query(Account).filter(Account.id == Account.salary_id()).first()
salary = db.execute(select(Account).where(Account.id == Account.salary_id())).scalar_one()
if amount == 0:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+12 -19
View File
@@ -5,34 +5,27 @@ from typing import List
import brewman.schemas.voucher as output
from fastapi import APIRouter, Depends
from fastapi import APIRouter
from fastapi.responses import StreamingResponse
from sqlalchemy import select
from sqlalchemy.orm import Session
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.db_image import DbImage
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("/{id_}/{type_}")
def db_image(id_: uuid.UUID, type_: str, db: Session = Depends(get_db)):
item = db.query(DbImage).filter(DbImage.id == id_).first()
if type_ == "thumbnail":
item = BytesIO(item.thumbnail)
else:
item = BytesIO(item.image)
return StreamingResponse(item, media_type="image/jpeg")
def db_image(id_: uuid.UUID, type_: str):
with SessionFuture() as db:
item = db.execute(select(DbImage).where(DbImage.id == id_)).scalar_one()
if type_ == "thumbnail":
item = BytesIO(item.thumbnail)
else:
item = BytesIO(item.image)
return StreamingResponse(item, media_type="image/jpeg")
def save_files(voucher_id: uuid.UUID, i: List[bytes], t: List[bytes], db: Session):
@@ -52,7 +45,7 @@ def update_files(
i = i or []
t = t or []
old = [f.id_ for f in data if f.id_]
images = db.query(DbImage).filter(DbImage.resource_id == voucher_id).all()
images = db.execute(select(DbImage).where(DbImage.resource_id == voucher_id)).scalars().all()
for image in [i for i in images if i.id not in old]:
db.delete(image)
for index, value in enumerate(i):
+20 -28
View File
@@ -1,9 +1,9 @@
from fastapi import APIRouter, Depends, Security
from sqlalchemy import desc, distinct, func, over
from fastapi import APIRouter, Security
from sqlalchemy import delete, desc, distinct, func, over, select
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.attendance import Attendance
from ..schemas.user import UserToken
@@ -11,56 +11,48 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("")
def post_check_db(db: Session = Depends(get_db), user: UserToken = Security(get_user)):
def post_check_db(user: UserToken = Security(get_user)):
info = {}
duplicate_attendances = get_duplicate_attendances(db)
if duplicate_attendances > 0:
fix_duplicate_attendances(db)
info["attendanceCount"] = duplicate_attendances
db.commit()
return info
with SessionFuture() as db:
duplicate_attendances = get_duplicate_attendances(db)
if duplicate_attendances > 0:
fix_duplicate_attendances(db)
info["attendanceCount"] = duplicate_attendances
db.commit()
return info
def get_duplicate_attendances(db: Session) -> int:
sub_query = (
db.query(
select(
over(
distinct(func.first_value(Attendance.id)),
partition_by=[Attendance.employee_id, Attendance.date],
)
)
.filter(Attendance.is_valid == True) # noqa: E712
.where(Attendance.is_valid == True) # noqa: E712
.subquery()
)
query = (
db.query(func.count(Attendance.id))
.filter(~Attendance.id.in_(sub_query))
.filter(Attendance.is_valid == True) # noqa: E712
query = db.execute(
select(func.count(Attendance.id)).where(
~Attendance.id.in_(sub_query), Attendance.is_valid == True # noqa: E712
)
)
return query.scalar()
def fix_duplicate_attendances(db: Session) -> None:
sub = (
db.query(
select(
over(
distinct(func.first_value(Attendance.id)),
partition_by=[Attendance.employee_id, Attendance.date],
order_by=desc(Attendance.creation_date),
)
)
.filter(Attendance.is_valid == True) # noqa: E712
.where(Attendance.is_valid == True) # noqa: E712
.subquery()
)
db.query(Attendance).filter(~Attendance.id.in_(sub)).filter(Attendance.is_valid == True).delete(False) # noqa: E712
db.execute(delete(Attendance).where(~Attendance.id.in_(sub), Attendance.is_valid == True)) # noqa: E712
+88 -99
View File
@@ -6,12 +6,12 @@ from typing import List
import brewman.schemas.employee as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import desc
from sqlalchemy import desc, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session, joinedload
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.cost_centre import CostCentre
from ..models.employee import Employee
@@ -24,156 +24,142 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=schemas.Employee)
def save(
data: schemas.EmployeeIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["employees"]),
) -> schemas.Employee:
try:
item = Employee(
name=data.name,
is_starred=data.is_starred,
is_active=data.is_active,
cost_centre_id=data.cost_centre.id_,
designation=data.designation,
salary=data.salary,
points=data.points,
joining_date=data.joining_date,
leaving_date=None if data.is_active else data.leaving_date,
).create(db)
db.commit()
return employee_info(item)
with SessionFuture() as db:
item = Employee(
name=data.name,
is_starred=data.is_starred,
is_active=data.is_active,
cost_centre_id=data.cost_centre.id_,
designation=data.designation,
salary=data.salary,
points=data.points,
joining_date=data.joining_date,
leaving_date=None if data.is_active else data.leaving_date,
).create(db)
db.commit()
return employee_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.put("/{id_}", response_model=schemas.Employee)
def update(
def update_route(
id_: uuid.UUID,
data: schemas.EmployeeIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["employees"]),
) -> schemas.Employee:
try:
item: Employee = db.query(Employee).filter(Employee.id == id_).first()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
item.cost_centre_id = data.cost_centre.id_
item.designation = data.designation
item.salary = data.salary
item.points = data.points
item.joining_date = data.joining_date
item.is_starred = data.is_starred
item.is_active = data.is_active
item.leaving_date = data.leaving_date
db.commit()
return employee_info(item)
with SessionFuture() as db:
item: Employee = db.execute(select(Employee).where(Employee.id == id_)).scalar_one()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
item.cost_centre_id = data.cost_centre.id_
item.designation = data.designation
item.salary = data.salary
item.points = data.points
item.joining_date = data.joining_date
item.is_starred = data.is_starred
item.is_active = data.is_active
item.leaving_date = data.leaving_date
db.commit()
return employee_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.delete("/{id_}", response_model=schemas.EmployeeBlank)
def delete(
def delete_route(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["employees"]),
) -> schemas.EmployeeBlank:
employee: Employee = db.query(Employee).filter(Employee.id == id_).first()
can_delete, reason = employee.can_delete("advanced-delete" in user.permissions)
if can_delete:
delete_with_data(employee, db)
db.commit()
return employee_blank()
else:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Cannot delete account because {reason}",
)
with SessionFuture() as db:
employee: Employee = db.execute(select(Employee).where(Employee.id == id_)).scalar_one()
can_delete, reason = employee.can_delete("advanced-delete" in user.permissions)
if can_delete:
delete_with_data(employee, db)
db.commit()
return employee_blank()
else:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Cannot delete account because {reason}",
)
@router.get("", response_model=schemas.EmployeeBlank)
def show_blank(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["employees"]),
) -> schemas.EmployeeBlank:
return employee_blank()
@router.get("/list", response_model=List[schemas.Employee])
async def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)):
return [
employee_info(item)
for item in db.query(Employee)
.order_by(desc(Employee.is_active))
.order_by(Account.cost_centre_id)
.order_by(Employee.designation)
.order_by(Employee.name)
.all()
]
async def show_list(user: UserToken = Depends(get_user)):
with SessionFuture() as db:
return [
employee_info(item)
for item in db.execute(
select(Employee)
.order_by(desc(Employee.is_active))
.order_by(Account.cost_centre_id)
.order_by(Employee.designation)
.order_by(Employee.name)
)
.scalars()
.all()
]
@router.get("/query")
async def show_term(
q: str,
c: int = None,
db: Session = Depends(get_db),
current_user: UserToken = Depends(get_user),
):
list_ = []
for index, item in enumerate(Employee.query(q=q, type_=10, db=db)):
list_.append(
{
"id": item.id,
"name": item.name,
"designation": item.designation,
"costCentre": {
"id": item.cost_centre.id,
"name": item.cost_centre.name,
},
}
)
if c is not None and index == c - 1:
break
with SessionFuture() as db:
for index, item in enumerate(Employee.query(q=q, type_=10, db=db)):
list_.append(
{
"id": item.id,
"name": item.name,
"designation": item.designation,
"costCentre": {
"id": item.cost_centre.id,
"name": item.cost_centre.name,
},
}
)
if c is not None and index == c - 1:
break
return list_
@router.get("/{id_}", response_model=schemas.Employee)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["employees"]),
) -> schemas.Employee:
item: Employee = db.query(Employee).filter(Employee.id == id_).first()
return employee_info(item)
with SessionFuture() as db:
item: Employee = db.execute(select(Employee).where(Employee.id == id_)).scalar_one()
return employee_info(item)
def employee_info(employee: Employee) -> schemas.Employee:
@@ -212,11 +198,14 @@ def employee_blank() -> schemas.EmployeeBlank:
def delete_with_data(employee: Employee, db: Session):
suspense_account = db.query(Account).filter(Account.id == Account.suspense()).first()
suspense_account = db.execute(select(Account).where(Account.id == Account.suspense())).scalar_one()
query = (
db.query(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.filter(Voucher.journals.any(Journal.account_id == employee.id))
db.execute(
select(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.where(Voucher.journals.any(Journal.account_id == employee.id))
)
.scalars()
.all()
)
+51 -59
View File
@@ -4,12 +4,13 @@ from datetime import date, datetime
import brewman.schemas.voucher as schemas
from fastapi import APIRouter, Depends, HTTPException, Request, Security, status
from fastapi import APIRouter, Request, Security
from sqlalchemy import select
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_finish_date, get_start_date
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.attendance import Attendance
from ..models.employee import Employee
from ..routers.fingerprint import get_prints
@@ -20,15 +21,6 @@ from .attendance import date_range
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.EmployeeAttendance)
def show_blank(
request: Request,
@@ -48,42 +40,42 @@ def employee_attendance_report(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["attendance"]),
):
employee: Employee = db.query(Employee).filter(Employee.id == id_).first()
if employee is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Employee not found",
with SessionFuture() as db:
employee: Employee = db.execute(select(Employee).where(Employee.id == id_)).scalar_one()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
info = {
"startDate": start_date,
"finishDate": finish_date,
"employee": {"id": employee.id, "name": employee.name},
}
start_date = datetime.strptime(start_date, "%d-%b-%Y").date()
finish_date = datetime.strptime(finish_date, "%d-%b-%Y").date()
start_date = employee.joining_date if employee.joining_date > start_date else start_date
finish_date = (
employee.leaving_date if not employee.is_active and employee.leaving_date < finish_date else finish_date
)
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
info = {
"startDate": start_date,
"finishDate": finish_date,
"employee": {"id": employee.id, "name": employee.name},
}
start_date = datetime.strptime(start_date, "%d-%b-%Y").date()
finish_date = datetime.strptime(finish_date, "%d-%b-%Y").date()
start_date = employee.joining_date if employee.joining_date > start_date else start_date
finish_date = (
employee.leaving_date if not employee.is_active and employee.leaving_date < finish_date else finish_date
)
info["body"] = employee_attendance(employee, start_date, finish_date, db)
return info
info["body"] = employee_attendance(employee, start_date, finish_date, db)
return info
def employee_attendance(employee: Employee, start_date: date, finish_date: date, db: Session):
list_ = []
for item in date_range(start_date, finish_date, inclusive=True):
att = (
db.query(Attendance)
.filter(Attendance.employee_id == employee.id)
.filter(Attendance.date == item)
.filter(Attendance.is_valid == True) # noqa: E712
.first()
db.execute(
select(Attendance).where(
Attendance.employee_id == employee.id,
Attendance.date == item,
Attendance.is_valid == True, # noqa: E712
)
)
.scalars()
.one_or_none()
)
att = 0 if att is None else att.attendance_type
prints, hours_worked, full_day = get_prints(employee.id, item, db)
list_.append(
@@ -102,30 +94,30 @@ def employee_attendance(employee: Employee, start_date: date, finish_date: date,
def save_employee_attendance(
id_: uuid.UUID,
data: schemas.EmployeeAttendance,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["attendance"]),
):
start_date = None
finish_date = None
employee: Employee = db.query(Employee).filter(Employee.id == id_).first()
for item in data.body:
if start_date is None:
start_date = item.date_
finish_date = item.date_
with SessionFuture() as db:
employee: Employee = db.execute(select(Employee).where(Employee.id == id_)).scalar_one()
for item in data.body:
if start_date is None:
start_date = item.date_
finish_date = item.date_
attendance_type = item.attendance_type.id_
if attendance_type != 0:
attendance = Attendance(
employee_id=employee.id,
date=item.date_,
attendance_type=attendance_type,
user_id=user.id_,
)
attendance.create(db)
db.commit()
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"employee": {"id": employee.id, "name": employee.name},
"body": employee_attendance(employee, start_date, finish_date, db),
}
attendance_type = item.attendance_type.id_
if attendance_type != 0:
attendance = Attendance(
employee_id=employee.id,
date=item.date_,
attendance_type=attendance_type,
user_id=user.id_,
)
attendance.create(db)
db.commit()
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"employee": {"id": employee.id, "name": employee.name},
"body": employee_attendance(employee, start_date, finish_date, db),
}
+36 -56
View File
@@ -8,12 +8,13 @@ import brewman.schemas.input as schema_in
import brewman.schemas.voucher as output
from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status
from sqlalchemy import select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, get_last_day, set_date
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account_base import AccountBase
from ..models.employee import Employee
from ..models.emplyee_benefit import EmployeeBenefit
@@ -34,20 +35,10 @@ from .voucher import (
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=output.Voucher)
def save_route(
request: Request,
data: schema_in.EmployeeBenefitIn = Depends(schema_in.EmployeeBenefitIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
@@ -55,24 +46,21 @@ def save_route(
try:
dt = get_last_day(data.date_)
days_in_month = dt.day
item: Voucher = save(data, dt, user, db)
exp, total = save_employee_benefits(item, data.employee_benefits, days_in_month, db)
save_journals(item, exp, total, db)
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
with SessionFuture() as db:
item: Voucher = save(data, dt, user, db)
exp, total = save_employee_benefits(item, data.employee_benefits, days_in_month, db)
save_journals(item, exp, total, db)
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def save(data: schema_in.EmployeeBenefitIn, date_: date, user: UserToken, db: Session) -> Voucher:
@@ -96,7 +84,7 @@ def save_employee_benefits(
):
total_exp, total_total = 0, 0
for item in employee_benefits:
account = db.query(Employee).filter(Employee.id == item.employee.id_).first()
account = db.execute(select(Employee).where(Employee.id == item.employee.id_)).scalar_one()
gross_salary = item.gross_salary
days_worked = item.days_worked
esi_ee, esi_er, esi_both = esi_contribution(gross_salary, days_worked, days_in_month)
@@ -126,7 +114,7 @@ def save_employee_benefits(
def save_journals(voucher: Voucher, exp: int, total: int, db: Session):
account = db.query(AccountBase).filter(AccountBase.id == AccountBase.esi_pf_expense()).first()
account = db.execute(select(AccountBase).where(AccountBase.id == AccountBase.esi_pf_expense())).scalar_one()
journal = Journal(
amount=exp,
debit=1,
@@ -135,7 +123,7 @@ def save_journals(voucher: Voucher, exp: int, total: int, db: Session):
)
db.add(journal)
voucher.journals.append(journal)
account = db.query(AccountBase).filter(AccountBase.id == AccountBase.esi_pf_payable()).first()
account = db.execute(select(AccountBase).where(AccountBase.id == AccountBase.esi_pf_payable())).scalar_one()
journal = Journal(
amount=total,
debit=-1,
@@ -151,7 +139,6 @@ def update_route(
id_: uuid.UUID,
request: Request,
data: schema_in.EmployeeBenefitIn = Depends(schema_in.EmployeeBenefitIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
@@ -159,32 +146,29 @@ def update_route(
try:
dt = get_last_day(data.date_)
days_in_month = dt.day
item: Voucher = update(id_, data, user, db)
if dt != item.date:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail="Date Cannot be changed for Employee Benefit voucher!",
)
exp, total = update_employee_benefits(item, data.employee_benefits, days_in_month, db)
update_journals(item, exp, total)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = update_voucher(id_, data, user, db)
if dt != item.date:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail="Date Cannot be changed for Employee Benefit voucher!",
)
exp, total = update_employee_benefits(item, data.employee_benefits, days_in_month, db)
update_journals(item, exp, total)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def update(id_: uuid.UUID, data: schema_in.EmployeeBenefitIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
def update_voucher(id_: uuid.UUID, data: schema_in.EmployeeBenefitIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
check_voucher_lock_info(voucher.date, data.date_, db)
check_voucher_edit_allowed(voucher, user)
voucher.is_starred = data.is_starred
@@ -229,31 +213,27 @@ def update_journals(voucher: Voucher, exp: int, total: int):
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
):
try:
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
):
additional_info = {"date": get_date(request.session), "type": "Employee Benefit"}
return blank_voucher(additional_info, db)
with SessionFuture() as db:
return blank_voucher(additional_info, db)
def esi_contribution(gross_salary, days_worked, days_in_month):
+35 -43
View File
@@ -5,13 +5,13 @@ from datetime import date, datetime, time, timedelta
from io import StringIO
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from sqlalchemy import bindparam
from sqlalchemy import bindparam, select
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.employee import Employee
from ..models.fingerprint import Fingerprint
from ..routers import get_lock_info
@@ -21,54 +21,41 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("")
def upload_prints(
db: Session = Depends(get_db),
fingerprints: UploadFile = File(None),
user: UserToken = Depends(get_user),
):
try:
start, finish = get_lock_info(db)
employees = {}
for id_, code in db.query(Employee.id, Employee.code).all():
employees[code] = id_
file_data = read_file(fingerprints)
prints = [d for d in fp(file_data, employees) if start <= d["date"].date() <= finish]
paged_data = [prints[i : i + 100] for i in range(0, len(prints), 100)]
for i, page in enumerate(paged_data):
print(f"Processing page {i} of {len(paged_data)}")
db.execute(
pg_insert(Fingerprint)
.values(
{
"id": bindparam("id"),
"employee_id": bindparam("employee_id"),
"date": bindparam("date"),
}
with SessionFuture() as db:
start, finish = get_lock_info(db)
employees = {}
for id_, code in db.execute(select(Employee.id, Employee.code)).all():
employees[code] = id_
file_data = read_file(fingerprints)
prints = [d for d in fp(file_data, employees) if start <= d["date"].date() <= finish]
paged_data = [prints[i : i + 100] for i in range(0, len(prints), 100)]
for i, page in enumerate(paged_data):
print(f"Processing page {i} of {len(paged_data)}")
db.execute(
pg_insert(Fingerprint)
.values(
{
"id": bindparam("id"),
"employee_id": bindparam("employee_id"),
"date": bindparam("date"),
}
)
.on_conflict_do_nothing(),
page,
)
.on_conflict_do_nothing(),
page,
)
db.commit()
return {}
db.commit()
return {}
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def read_file(input_file: UploadFile):
@@ -127,11 +114,16 @@ def fp(file_data, employees):
def get_prints(employee_id: uuid.UUID, date_: date, db: Session):
prints = (
db.query(Fingerprint)
.filter(Fingerprint.employee_id == employee_id)
.filter(Fingerprint.date >= datetime.combine(date_, time(hour=7)))
.filter(Fingerprint.date < datetime.combine(date_ + timedelta(days=1), time(hour=7)))
.order_by(Fingerprint.date)
db.execute(
select(Fingerprint)
.where(
Fingerprint.employee_id == employee_id,
Fingerprint.date >= datetime.combine(date_, time(hour=7)),
Fingerprint.date < datetime.combine(date_ + timedelta(days=1), time(hour=7)),
)
.order_by(Fingerprint.date)
)
.scalars()
.all()
)
+52 -72
View File
@@ -8,13 +8,13 @@ import brewman.schemas.input as schema_in
import brewman.schemas.voucher as output
from fastapi import APIRouter, Depends, HTTPException, Request, Security, status
from sqlalchemy import and_, func, or_
from sqlalchemy import and_, func, or_, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, get_first_day, set_date
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.attendance import Attendance
from ..models.attendance_type import AttendanceType
@@ -36,43 +36,30 @@ from .voucher import (
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=output.Voucher)
def save_route(
request: Request,
data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form),
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["incentive"]),
):
try:
item: Voucher = save(data, user, db)
employees = get_employees(get_first_day(data.date_), data.date_, data.incentives, None, db)
amount = balance(data.date_, None, db) * Decimal(0.9) # 10% for Deb Dip
total_points = sum(e.points * e.days_worked for e in employees)
point_value = round(amount / total_points, 2)
save_incentives(item, employees, point_value, db)
check_journals_are_valid(item)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
with SessionFuture() as db:
item: Voucher = save(data, user, db)
employees = get_employees(get_first_day(data.date_), data.date_, data.incentives, None, db)
amount = balance(data.date_, None, db) * Decimal(0.9) # 10% for Deb Dip
total_points = sum(e.points * e.days_worked for e in employees)
point_value = round(amount / total_points, 2)
save_incentives(item, employees, point_value, db)
check_journals_are_valid(item)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def save(data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher:
@@ -110,7 +97,7 @@ def save_incentives(
db.add(inc)
total_amount += item_amount
sc = db.query(Account).filter(Account.id == Account.incentive_id()).first()
sc = db.execute(select(Account).where(Account.id == Account.incentive_id())).scalar_one()
journal = Journal(amount=total_amount, debit=1, account_id=sc.id, cost_centre_id=sc.cost_centre_id)
voucher.journals.append(journal)
db.add(journal)
@@ -121,33 +108,29 @@ def update_route(
id_: uuid.UUID,
request: Request,
data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form),
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["incentive"]),
):
try:
item: Voucher = update(id_, data, user, db)
employees = get_employees(get_first_day(data.date_), data.date_, data.incentives, item.journals, db)
amount = balance(data.date_, item.id, db) * Decimal(0.9) # 10% for Deb Dip
total_points = sum(e.points * e.days_worked for e in employees)
point_value = round(amount / total_points, 2)
update_incentives(item, employees, point_value, db)
check_journals_are_valid(item)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = update_voucher(id_, data, user, db)
employees = get_employees(get_first_day(data.date_), data.date_, data.incentives, item.journals, db)
amount = balance(data.date_, item.id, db) * Decimal(0.9) # 10% for Deb Dip
total_points = sum(e.points * e.days_worked for e in employees)
point_value = round(amount / total_points, 2)
update_incentives(item, employees, point_value, db)
check_journals_are_valid(item)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def update(id_: uuid.UUID, data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
def update_voucher(id_: uuid.UUID, data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
check_voucher_lock_info(voucher.date, data.date_, db)
check_voucher_edit_allowed(voucher, user)
voucher.is_starred = data.is_starred
@@ -180,32 +163,28 @@ def update_incentives(
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["incentive"]),
):
try:
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request,
d: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["incentive"]),
):
additional_info = {"date": d or get_date(request.session), "type": "Incentive"}
return blank_voucher(additional_info, db)
with SessionFuture() as db:
return blank_voucher(additional_info, db)
def get_employees(
@@ -216,13 +195,10 @@ def get_employees(
db: Session,
) -> List[schema_in.IncentiveEmployee]:
details = []
employees = (
db.query(Employee)
.filter(Employee.joining_date <= finish_date)
.filter(or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id)
.order_by(Employee.designation)
.order_by(Employee.name)
employees = db.execute(
select(Employee)
.where(Employee.joining_date <= finish_date, or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id, Employee.designation, Employee.name)
.all()
)
@@ -230,11 +206,15 @@ def get_employees(
for employee in employees:
att = (
db.query(Attendance)
.filter(Attendance.employee_id == employee.id)
.filter(Attendance.date >= start_date)
.filter(Attendance.date <= finish_date)
.filter(Attendance.is_valid == True) # noqa: E712
db.execute(
select(Attendance).where(
Attendance.employee_id == employee.id,
Attendance.date >= start_date,
Attendance.date <= finish_date,
Attendance.is_valid == True, # noqa: E712
)
)
.scalars()
.all()
)
att = 0.5 * round(sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att)) / 0.5)
@@ -251,10 +231,10 @@ def get_employees(
def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session):
amount = (
db.query(func.sum(Journal.amount * Journal.debit))
amount = db.execute(
select(func.sum(Journal.amount * Journal.debit))
.join(Journal.voucher)
.filter(
.where(
Journal.account_id == Account.incentive_id(),
Voucher.type != VoucherType.by_name("Issue").id,
or_(
@@ -267,9 +247,9 @@ def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session):
)
)
if voucher_id is not None:
amount = amount.filter(Voucher.id != voucher_id)
amount = amount.scalar()
return 0 if amount is None else amount * -1
amount = amount.where(Voucher.id != voucher_id)
result: Decimal = amount.scalar()
return 0 if result is None else result * -1
def check_if_employees_changed(
+33 -53
View File
@@ -8,12 +8,13 @@ import brewman.schemas.input as schema_in
import brewman.schemas.voucher as output
from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status
from sqlalchemy import select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, set_date
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account_base import AccountBase
from ..models.batch import Batch
from ..models.cost_centre import CostCentre
@@ -35,44 +36,31 @@ from .voucher import (
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=output.Voucher)
def save_route(
request: Request,
data: schema_in.IssueIn = Depends(schema_in.IssueIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["issue"]),
):
try:
item, batch_consumed = save(data, user, db)
amount = save_inventories(item, data.inventories, batch_consumed, db)
check_inventories_are_valid(item)
save_journals(item, data.source, data.destination, amount, db)
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
with SessionFuture() as db:
item, batch_consumed = save(data, user, db)
amount = save_inventories(item, data.inventories, batch_consumed, db)
check_inventories_are_valid(item)
save_journals(item, data.source, data.destination, amount, db)
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def save(data: schema_in.IssueIn, user: UserToken, db: Session) -> (Voucher, Optional[bool]):
@@ -107,7 +95,7 @@ def save_inventories(
) -> Decimal:
amount: Decimal = Decimal(0)
for item in inventories:
batch = db.query(Batch).filter(Batch.id == item.batch.id_).first()
batch = db.execute(select(Batch).where(Batch.id == item.batch.id_)).scalar_one()
if batch_consumed and item.quantity > batch.quantity_remaining:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
@@ -170,35 +158,31 @@ def update_route(
id_: uuid.UUID,
request: Request,
data: schema_in.IssueIn = Depends(schema_in.IssueIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["issue"]),
):
try:
item, batch_consumed = update(id_, data, user, db)
amount = update_inventories(item, data.inventories, batch_consumed, db)
check_inventories_are_valid(item)
update_journals(item, data.source, data.destination, amount)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
# item: Voucher = db.query(Voucher).filter(Voucher.id == item.id).first()
return voucher_info(item, db)
with SessionFuture() as db:
item, batch_consumed = update_voucher(id_, data, user, db)
amount = update_inventories(item, data.inventories, batch_consumed, db)
check_inventories_are_valid(item)
update_journals(item, data.source, data.destination, amount)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
# item: Voucher = db.execute(select(Voucher).where(Voucher.id == item.id)).scalar_one()
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def update(id_: uuid.UUID, data: schema_in.IssueIn, user: UserToken, db: Session) -> (Voucher, Optional[bool]):
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
def update_voucher(id_: uuid.UUID, data: schema_in.IssueIn, user: UserToken, db: Session) -> (Voucher, Optional[bool]):
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
check_voucher_lock_info(voucher.date, data.date_, db)
check_voucher_edit_allowed(voucher, user)
voucher.date = data.date_
@@ -249,7 +233,7 @@ def update_inventories(
for j in range(len(inventories), 0, -1):
i = inventories[j - 1]
if item.id == i.id_:
batch = db.query(Batch).filter(Batch.id == i.batch.id_).first()
batch = db.execute(select(Batch).where(Batch.id == i.batch.id_)).scalar_one()
found = True
if item.batch_id != batch.id:
raise HTTPException(
@@ -321,21 +305,17 @@ def update_journals(
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["issue"]),
):
try:
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("", response_model=output.Voucher)
@@ -344,7 +324,6 @@ def show_blank(
date: str = None,
source: str = None,
destination: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["issue"]),
):
date_ = date or get_date(request.session)
@@ -353,4 +332,5 @@ def show_blank(
additional_info["source"] = source
if destination:
additional_info["destination"] = destination
return blank_voucher(additional_info, db)
with SessionFuture() as db:
return blank_voucher(additional_info, db)
+16 -27
View File
@@ -1,12 +1,11 @@
from datetime import datetime
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Security
from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlalchemy.orm.util import aliased
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.journal import Journal
from ..db.session import SessionFuture
from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType
from ..schemas.user import UserToken
@@ -15,42 +14,32 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("/{date}")
def grid_date(
date: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["issue"]),
):
date = datetime.strptime(date, "%d-%b-%Y")
return get_grid(date, db)
with SessionFuture() as db:
return get_grid(date, db)
def get_grid(date, db):
def get_grid(date, db: Session):
list_ = []
source_journal = aliased(Journal)
destination_journal = aliased(Journal)
query = (
db.query(Voucher, source_journal.amount)
.join(source_journal, Voucher.journals)
.join(destination_journal, Voucher.journals)
.filter(Voucher.date == date)
.filter(Voucher.type == VoucherType.by_name("Issue").id)
.order_by(Voucher.creation_date)
db.execute(
select(Voucher)
.join(Voucher.journals)
.where(Voucher.date == date, Voucher.type == VoucherType.by_name("Issue").id)
.order_by(Voucher.creation_date)
)
.unique()
.scalars()
.all()
)
for voucher, amount in query:
for voucher in query:
amount = voucher.journals[0].amount
list_.append(
{
"id": str(voucher.id),
+28 -49
View File
@@ -7,12 +7,13 @@ import brewman.schemas.input as schema_in
import brewman.schemas.voucher as output
from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status
from sqlalchemy import select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, set_date
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account_base import AccountBase
from ..models.journal import Journal
from ..models.validations import check_journals_are_valid
@@ -31,42 +32,29 @@ from .voucher import (
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=output.Voucher)
def save_route(
request: Request,
data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["journal"]),
):
try:
item: Voucher = save(data, user, db)
db.flush()
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
with SessionFuture() as db:
item: Voucher = save(data, user, db)
db.flush()
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def save(data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
@@ -80,7 +68,7 @@ def save(data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
)
db.add(voucher)
for item in data.journals:
account: AccountBase = db.query(AccountBase).filter(AccountBase.id == item.account.id_).first()
account: AccountBase = db.execute(select(AccountBase).where(AccountBase.id == item.account.id_)).scalar_one()
journal = Journal(
amount=round(item.amount, 2),
debit=item.debit,
@@ -97,18 +85,18 @@ def update_route(
id_: uuid.UUID,
request: Request,
data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["journal"]),
):
try:
item: Voucher = update(id_, data, user, db)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = update_voucher(id_, data, user, db)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
@@ -120,8 +108,8 @@ def update_route(
raise
def update(id_: uuid.UUID, data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
def update_voucher(id_: uuid.UUID, data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
check_voucher_lock_info(voucher.date, data.date_, db)
check_voucher_edit_allowed(voucher, user)
voucher.date = data.date_
@@ -137,7 +125,7 @@ def update(id_: uuid.UUID, data: schema_in.JournalIn, user: UserToken, db: Sessi
for j in range(len(data.journals), 0, -1):
new_item = data.journals[j - 1]
if new_item.id_ is not None and item.id == new_item.id_:
account = db.query(AccountBase).filter(AccountBase.id == new_item.account.id_).first()
account = db.execute(select(AccountBase).where(AccountBase.id == new_item.account.id_)).scalar_one()
found = True
item.debit = new_item.debit
item.amount = round(new_item.amount, 2)
@@ -148,7 +136,7 @@ def update(id_: uuid.UUID, data: schema_in.JournalIn, user: UserToken, db: Sessi
if not found:
voucher.journals.remove(item)
for new_item in data.journals:
account = db.query(AccountBase).filter(AccountBase.id == new_item.account.id_).first()
account = db.execute(select(AccountBase).where(AccountBase.id == new_item.account.id_)).scalar_one()
journal = Journal(
amount=new_item.amount,
debit=round(new_item.debit, 2),
@@ -163,33 +151,23 @@ def update(id_: uuid.UUID, data: schema_in.JournalIn, user: UserToken, db: Sessi
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["journal"]),
):
try:
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
if item is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Voucher not found",
)
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request,
a: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["journal"]),
):
if request.scope.get("path") == "/api/payment":
@@ -202,4 +180,5 @@ def show_blank(
additional_info = {"date": get_date(request.session), "type": type_}
if a:
additional_info["account"] = a
return blank_voucher(additional_info, db)
with SessionFuture() as db:
return blank_voucher(additional_info, db)
+23 -32
View File
@@ -1,8 +1,8 @@
from fastapi import APIRouter, Depends, Security
from sqlalchemy.orm import Session
from fastapi import APIRouter, Security
from sqlalchemy import select
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.db_setting import DbSetting
from ..schemas.settings import LockInformation
from ..schemas.user import UserToken
@@ -11,19 +11,9 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=LockInformation)
def post(
data_in: LockInformation,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["lock-date"]),
):
data = {
@@ -44,38 +34,39 @@ def post(
else:
data["Finish"]["Date"] = data_in.newer_date
lock_date = db.query(DbSetting).filter(DbSetting.name == "Lock Info").first()
if lock_date is not None:
lock_date.data = data
else:
lock_date = DbSetting(name="Lock Info", data=data)
db.add(lock_date)
db.commit()
with SessionFuture() as db:
lock_date = db.execute(select(DbSetting).where(DbSetting.name == "Lock Info")).scalar_one()
if lock_date is not None:
lock_date.data = data
else:
lock_date = DbSetting(name="Lock Info", data=data)
db.add(lock_date)
db.commit()
return get_info(data)
@router.delete("")
def delete(
db: Session = Depends(get_db),
def delete_route(
user: UserToken = Security(get_user, scopes=["lock-date"]),
):
lock_date = db.query(DbSetting).filter(DbSetting.name == "Lock Info").first()
if lock_date is not None and lock_date.data is not None:
lock_date.data = None
db.commit()
with SessionFuture() as db:
lock_date = db.execute(select(DbSetting).where(DbSetting.name == "Lock Info")).scalar_one()
if lock_date is not None and lock_date.data is not None:
lock_date.data = None
db.commit()
return get_info({})
@router.get("")
def get(
db: Session = Depends(get_db),
user: UserToken = Security(get_user),
):
data = db.query(DbSetting).filter(DbSetting.name == "Lock Info").first()
if data is None:
return get_info({})
else:
return get_info(data.data)
with SessionFuture() as db:
data = db.execute(select(DbSetting).where(DbSetting.name == "Lock Info")).scalars().one_or_none()
if data is None:
return get_info({})
else:
return get_info(data.data)
def get_info(data):
+59 -65
View File
@@ -12,8 +12,7 @@ from fastapi import (
)
from fastapi.responses import JSONResponse
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy import and_, or_
from sqlalchemy.orm import Session
from sqlalchemy import delete, or_, select
from .. import __version__
from ..core.config import settings
@@ -24,7 +23,7 @@ from ..core.security import (
create_access_token,
get_current_active_user,
)
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.client import Client
from ..models.login_history import LoginHistory
from ..schemas.user import UserToken
@@ -33,79 +32,74 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("/token", response_model=Token)
async def login_for_access_token(
response: Response,
form_data: OAuth2PasswordRequestForm = Depends(),
client_id: int = Cookie(None),
otp: int = Form(None),
db: Session = Depends(get_db),
):
user = authenticate_user(form_data.username, form_data.password, db)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
allowed, client = client_allowed(user, client_id, otp, db)
db.flush()
if allowed:
history = LoginHistory(user.id, client.id)
db.add(history)
with SessionFuture() as db:
user = authenticate_user(form_data.username, form_data.password, db)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
allowed, client = client_allowed(user, client_id, otp, db)
db.flush()
db.execute(
LoginHistory.__table__.delete(
or_(
LoginHistory.date < datetime.utcnow() - timedelta(days=30),
LoginHistory.client_id.in_(
db.query(Client.id)
.filter(
Client.creation_date < datetime.utcnow() - timedelta(days=3),
Client.enabled == False, # noqa: E712
)
.subquery()
),
if allowed:
history = LoginHistory(user.id, client.id)
db.add(history)
db.flush()
db.execute(
delete(LoginHistory)
.where(
or_(
LoginHistory.date < datetime.utcnow() - timedelta(days=30),
LoginHistory.client_id.in_(
select(Client.id)
.where(
Client.creation_date < datetime.utcnow() - timedelta(days=3),
Client.enabled == False, # noqa: E712
)
.subquery()
),
)
)
.execution_options(synchronize_session=False)
)
db.execute(
delete(Client).where(
Client.creation_date < datetime.utcnow() - timedelta(days=3), Client.enabled == False # noqa: E712
)
)
)
db.execute(
Client.__table__.delete(
and_(Client.creation_date < datetime.utcnow() - timedelta(days=3), Client.enabled == False) # noqa: E712
db.commit()
response.set_cookie(key="client_id", value=str(client.code), max_age=10 * 365 * 24 * 60 * 60)
if not allowed:
not_allowed_response = JSONResponse(
status_code=status.HTTP_401_UNAUTHORIZED,
headers={"WWW-Authenticate": "Bearer"},
content={"detail": "Client is not registered"},
)
not_allowed_response.set_cookie(key="client_id", value=str(client.code), max_age=10 * 365 * 24 * 60 * 60)
return not_allowed_response
access_token_expires = timedelta(minutes=settings.JWT_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={
"sub": user.name,
"scopes": ["authenticated"]
+ list(
set([p.name.replace(" ", "-").lower() for r in user.roles for p in r.permissions])
), # noqa: W503
"userId": str(user.id),
"lockedOut": user.locked_out,
"ver": __version__.__version__,
},
expires_delta=access_token_expires,
)
)
db.commit()
response.set_cookie(key="client_id", value=str(client.code), max_age=10 * 365 * 24 * 60 * 60)
if not allowed:
not_allowed_response = JSONResponse(
status_code=status.HTTP_401_UNAUTHORIZED,
headers={"WWW-Authenticate": "Bearer"},
content={"detail": "Client is not registered"},
)
not_allowed_response.set_cookie(key="client_id", value=str(client.code), max_age=10 * 365 * 24 * 60 * 60)
return not_allowed_response
access_token_expires = timedelta(minutes=settings.JWT_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={
"sub": user.name,
"scopes": ["authenticated"]
+ list(set([p.name.replace(" ", "-").lower() for r in user.roles for p in r.permissions])), # noqa: W503
"userId": str(user.id),
"lockedOut": user.locked_out,
"ver": __version__.__version__,
},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
return {"access_token": access_token, "token_type": "bearer"}
@router.post("/refresh", response_model=Token)
+19 -27
View File
@@ -1,8 +1,9 @@
from fastapi import APIRouter, Depends, Security
from fastapi import APIRouter, Security
from sqlalchemy import select
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.db_setting import DbSetting
from ..models.user import User
from ..schemas.settings import Maintenance
@@ -12,45 +13,36 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=Maintenance)
def get_maintenance(
db: Session = Depends(get_db),
user: UserToken = Security(get_user),
):
data = db.query(DbSetting).filter(DbSetting.name == "Maintenance").first()
return info(data, db)
with SessionFuture() as db:
data = db.execute(select(DbSetting).where(DbSetting.name == "Maintenance")).scalars().one_or_none()
return info(data, db)
@router.post("", response_model=Maintenance)
def set_maintenance(
data: Maintenance,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["maintenance"]),
):
maintenance = db.query(DbSetting).filter(DbSetting.name == "Maintenance").first()
if data.enabled is False and maintenance is not None:
db.delete(maintenance)
maintenance = None
elif data.enabled is True and maintenance is None:
maintenance = DbSetting(name="Maintenance", data=user.id_)
db.add(maintenance)
elif data.enabled is True and maintenance.data != user.id_:
maintenance.data = user.id_
db.commit()
return info(maintenance, db)
with SessionFuture() as db:
maintenance = db.execute(select(DbSetting).where(DbSetting.name == "Maintenance")).scalars().one_or_none()
if data.enabled is False and maintenance is not None:
db.delete(maintenance)
maintenance = None
elif data.enabled is True and maintenance is None:
maintenance = DbSetting(name="Maintenance", data=user.id_)
db.add(maintenance)
elif data.enabled is True and maintenance.data != user.id_:
maintenance.data = user.id_
db.commit()
return info(maintenance, db)
def info(data, db: Session):
if data is None:
return {"enabled": False, "user": ""}
user = db.query(User).filter(User.id == data.data).one()
user = db.execute(select(User).where(User.id == data.data)).scalar_one()
return {"enabled": True, "user": user.name}
+97 -109
View File
@@ -5,12 +5,12 @@ from typing import List
import brewman.schemas.product as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import desc
from sqlalchemy import desc, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session, joinedload
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.batch import Batch
from ..models.inventory import Inventory
@@ -23,127 +23,112 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=schemas.Product)
def save(
data: schemas.ProductIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["products"]),
) -> schemas.Product:
try:
item = Product(
name=data.name,
units=data.units,
fraction=round(data.fraction, 5),
fraction_units=data.fraction_units,
product_yield=round(data.product_yield, 5),
product_group_id=data.product_group.id_,
account_id=Account.all_purchases(),
price=round(data.price, 2),
sale_price=round(data.sale_price, 2),
is_active=data.is_active,
is_purchased=data.is_purchased,
is_sold=data.is_sold,
).create(db)
db.commit()
return product_info(item)
with SessionFuture() as db:
item = Product(
name=data.name,
units=data.units,
fraction=round(data.fraction, 5),
fraction_units=data.fraction_units,
product_yield=round(data.product_yield, 5),
product_group_id=data.product_group.id_,
account_id=Account.all_purchases(),
price=round(data.price, 2),
sale_price=round(data.sale_price, 2),
is_active=data.is_active,
is_purchased=data.is_purchased,
is_sold=data.is_sold,
).create(db)
db.commit()
return product_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.put("/{id_}", response_model=schemas.Product)
def update(
def update_route(
id_: uuid.UUID,
data: schemas.ProductIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["products"]),
) -> schemas.Product:
try:
item: Product = db.query(Product).filter(Product.id == id_).first()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
item.units = data.units
item.fraction = round(data.fraction, 5)
item.fraction_units = data.fraction_units
item.product_yield = round(data.product_yield, 5)
item.product_group_id = data.product_group.id_
item.account_id = Account.all_purchases()
item.price = round(data.price, 2)
item.sale_price = round(data.sale_price, 2)
item.is_active = data.is_active
item.is_purchased = data.is_purchased
item.is_sold = data.is_sold
db.commit()
return product_info(item)
with SessionFuture() as db:
item: Product = db.execute(select(Product).where(Product.id == id_)).scalar_one()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
item.units = data.units
item.fraction = round(data.fraction, 5)
item.fraction_units = data.fraction_units
item.product_yield = round(data.product_yield, 5)
item.product_group_id = data.product_group.id_
item.account_id = Account.all_purchases()
item.price = round(data.price, 2)
item.sale_price = round(data.sale_price, 2)
item.is_active = data.is_active
item.is_purchased = data.is_purchased
item.is_sold = data.is_sold
db.commit()
return product_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.delete("/{id_}", response_model=schemas.ProductBlank)
def delete(
def delete_route(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["products"]),
) -> schemas.ProductBlank:
item: Product = db.query(Product).filter(Product.id == id_).first()
can_delete, reason = item.can_delete("advanced-delete" in user.permissions)
with SessionFuture() as db:
item: Product = db.execute(select(Product).where(Product.id == id_)).scalar_one()
can_delete, reason = item.can_delete("advanced-delete" in user.permissions)
if can_delete:
delete_with_data(item, db)
db.commit()
return product_blank()
else:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Cannot delete product because {reason}",
)
if can_delete:
delete_with_data(item, db)
db.commit()
return product_blank()
else:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Cannot delete product because {reason}",
)
@router.get("", response_model=schemas.ProductBlank)
def show_blank(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["products"]),
) -> schemas.ProductBlank:
return product_blank()
@router.get("/list", response_model=List[schemas.Product])
def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.Product]:
return [
product_info(item)
for item in db.query(Product)
.order_by(desc(Product.is_active))
.order_by(Product.product_group_id)
.order_by(Product.name)
.all()
]
def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Product]:
with SessionFuture() as db:
return [
product_info(item)
for item in db.execute(
select(Product)
.order_by(desc(Product.is_active))
.order_by(Product.product_group_id)
.order_by(Product.name)
)
.scalars()
.all()
]
@router.get("/query")
@@ -153,41 +138,41 @@ async def show_term(
c: int = None,
p: bool = None,
e: bool = False,
db: Session = Depends(get_db),
current_user: UserToken = Depends(get_user),
):
count = c
extended = e
list_ = []
for index, item in enumerate(Product.query(q, p, a, db)):
list_.append(
{
"id": item.id,
"name": item.full_name,
"price": item.price,
"units": item.units,
"fraction": item.fraction,
"fractionUnits": item.fraction_units,
"productYield": item.product_yield,
"isSold": item.is_sold,
"salePrice": item.sale_price,
}
if extended
else {"id": item.id, "name": item.full_name, "price": item.price}
)
if count is not None and index == count - 1:
break
with SessionFuture() as db:
for index, item in enumerate(Product.query(q, p, a, db)):
list_.append(
{
"id": item.id,
"name": item.name,
"price": item.price,
"units": item.units,
"fraction": item.fraction,
"fractionUnits": item.fraction_units,
"productYield": item.product_yield,
"isSold": item.is_sold,
"salePrice": item.sale_price,
}
if extended
else {"id": item.id, "name": item.full_name, "price": item.price}
)
if count is not None and index == count - 1:
break
return sorted(list_, key=lambda k: k["name"])
@router.get("/{id_}", response_model=schemas.Product)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["products"]),
) -> schemas.Product:
item: Product = db.query(Product).filter(Product.id == id_).first()
return product_info(item)
with SessionFuture() as db:
item: Product = db.execute(select(Product).where(Product.id == id_)).scalar_one()
return product_info(item)
def product_info(product: Product) -> schemas.Product:
@@ -227,12 +212,15 @@ def product_blank() -> schemas.ProductBlank:
def delete_with_data(product: Product, db: Session) -> None:
suspense_product = db.query(Product).filter(Product.id == Product.suspense()).first()
suspense_batch = db.query(Batch).filter(Batch.id == Batch.suspense()).first()
suspense_product = db.execute(select(Product).where(Product.id == Product.suspense())).scalar_one()
suspense_batch = db.execute(select(Batch).where(Batch.id == Batch.suspense())).scalar_one()
query = (
db.query(Voucher)
.options(joinedload(Voucher.inventories, innerjoin=True).joinedload(Inventory.product, innerjoin=True))
.filter(Voucher.inventories.any(Inventory.product_id == product.id))
db.execute(
select(Voucher)
.options(joinedload(Voucher.inventories, innerjoin=True).joinedload(Inventory.product, innerjoin=True))
.where(Voucher.inventories.any(Inventory.product_id == product.id))
)
.scalars()
.all()
)
+30 -48
View File
@@ -5,11 +5,11 @@ from typing import List
import brewman.schemas.product_group as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.product_group import ProductGroup
from ..schemas.user import UserToken
@@ -17,73 +17,55 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=schemas.ProductGroup)
def save(
data: schemas.ProductGroupIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["product-groups"]),
) -> schemas.ProductGroup:
try:
item = ProductGroup(name=data.name)
db.add(item)
db.commit()
return product_group_info(item)
with SessionFuture() as db:
item = ProductGroup(name=data.name)
db.add(item)
db.commit()
return product_group_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.put("/{id_}", response_model=schemas.ProductGroup)
def update(
def update_route(
id_: uuid.UUID,
data: schemas.ProductGroupIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["product-groups"]),
) -> schemas.ProductGroup:
try:
item = db.query(ProductGroup).filter(ProductGroup.id == id_).first()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
db.commit()
return product_group_info(item)
with SessionFuture() as db:
item = db.execute(select(ProductGroup).where(ProductGroup.id == id_)).scalar_one()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
db.commit()
return product_group_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.delete("/{id_}", response_model=schemas.ProductGroupBlank)
def delete(
def delete_route(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["product-groups"]),
) -> schemas.ProductGroupBlank:
try:
item = db.query(ProductGroup).filter(ProductGroup.id == id_).first()
with SessionFuture() as db:
item = db.execute(select(ProductGroup).where(ProductGroup.id == id_)).scalar_one()
if item is None:
raise HTTPException(
@@ -100,32 +82,32 @@ def delete(
status_code=status.HTTP_501_NOT_IMPLEMENTED,
detail="Product Group deletion not implemented",
)
except Exception:
db.rollback()
raise
@router.get("", response_model=schemas.ProductGroupBlank)
def show_blank(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["product-groups"]),
) -> schemas.ProductGroupBlank:
return product_group_blank()
@router.get("/list", response_model=List[schemas.ProductGroup])
async def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.ProductGroup]:
return [product_group_info(item) for item in db.query(ProductGroup).order_by(ProductGroup.name).all()]
async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.ProductGroup]:
with SessionFuture() as db:
return [
product_group_info(item)
for item in db.execute(select(ProductGroup).order_by(ProductGroup.name)).scalars().all()
]
@router.get("/{id_}", response_model=schemas.ProductGroup)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["product-groups"]),
) -> schemas.ProductGroup:
item = db.query(ProductGroup).filter(ProductGroup.id == id_).first()
return product_group_info(item)
with SessionFuture() as db:
item = db.execute(select(ProductGroup).where(ProductGroup.id == id_)).scalar_one()
return product_group_info(item)
def product_group_info(item: ProductGroup) -> schemas.ProductGroup:
+39 -63
View File
@@ -8,13 +8,13 @@ import brewman.schemas.input as schema_in
import brewman.schemas.voucher as output
from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status
from sqlalchemy import func
from sqlalchemy import func, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, set_date
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account_base import AccountBase
from ..models.batch import Batch
from ..models.inventory import Inventory
@@ -36,44 +36,31 @@ from .voucher import (
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=output.Voucher)
def save_route(
request: Request,
data: schema_in.PurchaseIn = Depends(schema_in.PurchaseIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["purchase"]),
):
try:
item: Voucher = save(data, user, db)
save_inventories(item, data.inventories, db)
check_inventories_are_valid(item)
save_journals(item, data.vendor, db)
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
with SessionFuture() as db:
item: Voucher = save(data, user, db)
save_inventories(item, data.inventories, db)
check_inventories_are_valid(item)
save_journals(item, data.vendor, db)
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher:
@@ -91,7 +78,7 @@ def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher:
def save_inventories(voucher: Voucher, inventories: List[schema_in.Inventory], db: Session):
for item in inventories:
product: Product = db.query(Product).filter(Product.id == item.product.id_).first()
product: Product = db.execute(select(Product).where(Product.id == item.product.id_)).scalar_one()
batch = Batch(
name=voucher.date,
product=product,
@@ -116,7 +103,7 @@ def save_inventories(voucher: Voucher, inventories: List[schema_in.Inventory], d
def save_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session):
vendor = db.query(AccountBase).filter(AccountBase.id == ven.id_).first()
vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one()
journals = {}
amount = 0
for item in voucher.inventories:
@@ -147,34 +134,30 @@ def update_route(
id_: uuid.UUID,
request: Request,
data: schema_in.PurchaseIn = Depends(schema_in.PurchaseIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["purchase"]),
):
try:
item: Voucher = update(id_, data, user, db)
update_inventory(item, data.inventories, db)
check_inventories_are_valid(item)
update_journals(item, data.vendor, db)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = update_voucher(id_, data, user, db)
update_inventory(item, data.inventories, db)
check_inventories_are_valid(item)
update_journals(item, data.vendor, db)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def update(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
check_voucher_lock_info(voucher.date, data.date_, db)
check_voucher_edit_allowed(voucher, user)
voucher.date = data.date_
@@ -193,7 +176,7 @@ def update_inventory(voucher: Voucher, new_inventories: List[schema_in.Inventory
for j in range(len(new_inventories), 0, -1):
new_inventory = new_inventories[j - 1]
if new_inventory.id_ == item.id:
product = db.query(Product).filter(Product.id == new_inventory.product.id_).first()
product = db.execute(select(Product).where(Product.id == new_inventory.product.id_)).scalar_one()
found = True
if item.product_id != new_inventory.product.id_:
raise HTTPException(
@@ -225,12 +208,9 @@ def update_inventory(voucher: Voucher, new_inventories: List[schema_in.Inventory
# TODO: Update all references of the batch with the new rates
break
if not found:
has_been_issued = (
db.query(func.count(Inventory.id))
.filter(Inventory.batch_id == item.batch.id)
.filter(Inventory.id != item.id)
.scalar()
)
has_been_issued = db.execute(
select(func.count(Inventory.id)).where(Inventory.batch_id == item.batch.id, Inventory.id != item.id)
).scalar()
if has_been_issued > 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
@@ -241,7 +221,7 @@ def update_inventory(voucher: Voucher, new_inventories: List[schema_in.Inventory
db.delete(item)
voucher.inventories.remove(item)
for new_inventory in new_inventories:
product = db.query(Product).filter(Product.id == new_inventory.product.id_).first()
product = db.execute(select(Product).where(Product.id == new_inventory.product.id_)).scalar_one()
batch = Batch(
name=voucher.date,
product_id=product.id,
@@ -268,11 +248,11 @@ def update_inventory(voucher: Voucher, new_inventories: List[schema_in.Inventory
def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db):
vendor = db.query(AccountBase).filter(AccountBase.id == ven.id_).first()
vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one()
journals = {}
amount = 0
for item in voucher.inventories:
product = db.query(Product).filter(Product.id == item.product_id).first()
product = db.execute(select(Product).where(Product.id == item.product_id)).scalar_one()
account = product.account
amount += item.amount
if account.id in journals:
@@ -309,28 +289,24 @@ def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db):
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["purchase"]),
):
try:
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["purchase"]),
):
additional_info = {"date": get_date(request.session), "type": "Purchase"}
return blank_voucher(additional_info, db)
with SessionFuture() as db:
return blank_voucher(additional_info, db)
+33 -49
View File
@@ -8,12 +8,13 @@ import brewman.schemas.input as schema_in
import brewman.schemas.voucher as output
from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status
from sqlalchemy import select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, set_date
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account_base import AccountBase
from ..models.batch import Batch
from ..models.inventory import Inventory
@@ -34,44 +35,31 @@ from .voucher import (
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=output.Voucher)
def save_route(
request: Request,
data: schema_in.PurchaseIn = Depends(schema_in.PurchaseIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["purchase-return"]),
):
try:
item: Voucher = save(data, user, db)
save_inventories(item, data.inventories, db)
check_inventories_are_valid(item)
save_journals(item, data.vendor, db)
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
with SessionFuture() as db:
item: Voucher = save(data, user, db)
save_inventories(item, data.inventories, db)
check_inventories_are_valid(item)
save_journals(item, data.vendor, db)
check_journals_are_valid(item)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher:
@@ -89,7 +77,7 @@ def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher:
def save_inventories(voucher: Voucher, inventories: List[schema_in.Inventory], db: Session):
for item in inventories:
batch = db.query(Batch).filter(Batch.id == item.batch.id_).first()
batch = db.execute(select(Batch).where(Batch.id == item.batch.id_)).scalar_one()
if item.quantity > batch.quantity_remaining:
raise HTTPException(
@@ -117,7 +105,7 @@ def save_inventories(voucher: Voucher, inventories: List[schema_in.Inventory], d
def save_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session):
vendor = db.query(AccountBase).filter(AccountBase.id == ven.id_).first()
vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one()
journals = {}
amount = 0
for item in voucher.inventories:
@@ -148,21 +136,21 @@ def update_route(
id_: uuid.UUID,
request: Request,
data: schema_in.PurchaseIn = Depends(schema_in.PurchaseIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["purchase-return"]),
):
try:
item: Voucher = update(id_, data, user, db)
update_inventory(item, data.inventories, db)
check_inventories_are_valid(item)
update_journals(item, data.vendor, db)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = update_voucher(id_, data, user, db)
update_inventory(item, data.inventories, db)
check_inventories_are_valid(item)
update_journals(item, data.vendor, db)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
@@ -174,8 +162,8 @@ def update_route(
raise
def update(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
check_voucher_lock_info(voucher.date, data.date_, db)
check_voucher_edit_allowed(voucher, user)
voucher.date = data.date_
@@ -224,7 +212,7 @@ def update_inventory(voucher: Voucher, new_inventories: List[schema_in.Inventory
def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db):
vendor = db.query(AccountBase).filter(AccountBase.id == ven.id_).first()
vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one()
journals = {}
amount = 0
for item in voucher.inventories:
@@ -264,28 +252,24 @@ def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db):
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["purchase-return"]),
):
try:
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
return voucher_info(item, db)
with SessionFuture() as db:
item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["purchase-return"]),
):
additional_info = {"date": get_date(request.session), "type": "Purchase Return"}
return blank_voucher(additional_info, db)
with SessionFuture() as db:
return blank_voucher(additional_info, db)
+73 -76
View File
@@ -4,12 +4,12 @@ from datetime import date, datetime, timedelta
from decimal import Decimal
from typing import List
from fastapi import APIRouter, Depends, Security
from sqlalchemy import and_, distinct, func
from sqlalchemy.orm import Session, aliased, joinedload
from fastapi import APIRouter, Security
from sqlalchemy import and_, delete, distinct, func, select
from sqlalchemy.orm import Session, aliased
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.account_base import AccountBase
from ..models.attendance import Attendance
@@ -30,52 +30,48 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("/{date_}")
def rebase(
date_: str,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["rebase"]),
):
# request.dbsession.execute('SET statement_timeout TO 300000;') # 5 minutes
date_ = datetime.strptime(date_, "%d-%b-%Y")
voucher_l = opening_accounts(date_, user.id_, db)
voucher_b = opening_batches(date_, user.id_, db)
starred_vouchers = save_starred(date_, db)
db.flush()
delete_data(date_, starred_vouchers, db)
db.add(voucher_l)
for j in voucher_l.journals:
db.add(j)
db.flush()
db.add(voucher_b)
for j in voucher_b.journals:
db.add(j)
for i in voucher_b.inventories:
db.add(i)
db.flush()
cleanup_lint(date_, db)
# request.dbsession.execute('RESET statement_timeout;')
db.commit()
return {}
with SessionFuture() as db:
date_ = datetime.strptime(date_, "%d-%b-%Y")
voucher_l = opening_accounts(date_, user.id_, db)
voucher_b = opening_batches(date_, user.id_, db)
starred_vouchers = save_starred(date_, db)
db.flush()
delete_data(date_, starred_vouchers, db)
db.add(voucher_l)
for j in voucher_l.journals:
db.add(j)
db.flush()
db.add(voucher_b)
for j in voucher_b.journals:
db.add(j)
for i in voucher_b.inventories:
db.add(i)
db.flush()
cleanup_lint(date_, db)
# request.dbsession.execute('RESET statement_timeout;')
db.commit()
return {}
def save_starred(date_: date, db: Session):
accounts = [i.id for i in db.query(AccountBase.id).filter(AccountBase.is_starred == True).all()] # noqa: E712
accounts = [
i.id for i in db.execute(select(AccountBase.id).where(AccountBase.is_starred == True)).all() # noqa: E712
]
vouchers = []
query = (
db.query(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.filter(Voucher.date < date_)
.filter(Voucher.journals.any(Journal.account_id.in_(accounts)))
db.execute(
select(Voucher)
.join(Voucher.journals)
.join(Journal.account)
.where(Voucher.date < date_, Voucher.journals.any(Journal.account_id.in_(accounts)))
)
.unique()
.scalars()
.all()
)
@@ -112,18 +108,19 @@ def save_starred(date_: date, db: Session):
def opening_accounts(date_: date, user_id: uuid.UUID, db: Session):
running_total = 0
sum_func = func.sum(Journal.signed_amount)
query = (
db.query(AccountBase, sum_func)
query = db.execute(
select(AccountBase, sum_func)
.join(Journal, Voucher.journals)
.join(AccountBase, Journal.account)
.filter(AccountBase.is_starred == False) # noqa: E712
.filter(AccountBase.id != AccountBase.suspense())
.filter(Voucher.date < date_)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.where(
AccountBase.is_starred == False, # noqa: E712
AccountBase.id != AccountBase.suspense(),
Voucher.date < date_,
Voucher.type != VoucherType.by_name("Issue").id,
)
.having(sum_func != 0)
.group_by(AccountBase)
.all()
)
).all()
dt = date_ - timedelta(days=1)
voucher = Voucher(
@@ -158,17 +155,15 @@ def opening_accounts(date_: date, user_id: uuid.UUID, db: Session):
def opening_batches(date_: date, user_id: uuid.UUID, db: Session):
total = 0
sum_func = func.sum(Journal.debit * Inventory.quantity)
query = (
db.query(Batch, sum_func)
query = db.execute(
select(Batch, sum_func)
.join(Journal, Voucher.journals)
.join(Inventory, Voucher.inventories)
.join(Batch, Inventory.batch)
.filter(Voucher.date < date_)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.where(Voucher.date < date_, Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.having(sum_func != 0)
.group_by(Batch)
.all()
)
).all()
dt = date_ - timedelta(days=1)
voucher = Voucher(
@@ -213,14 +208,14 @@ def opening_batches(date_: date, user_id: uuid.UUID, db: Session):
def delete_data(date_: date, vouchers: List[Voucher], db: Session):
sub_voucher = aliased(Voucher)
sub_query = db.query(sub_voucher.id).filter(sub_voucher.date < date_).subquery()
sub_query = select(sub_voucher.id).where(sub_voucher.date < date_).subquery()
db.execute(Inventory.__table__.delete(Inventory.voucher_id.in_(sub_query)))
db.execute(EmployeeBenefit.__table__.delete(EmployeeBenefit.voucher_id.in_(sub_query)))
db.execute(Incentive.__table__.delete(Incentive.voucher_id.in_(sub_query)))
db.execute(Journal.__table__.delete(and_(Journal.voucher_id.in_(sub_query), ~Journal.voucher_id.in_(vouchers))))
db.execute(delete(Inventory).where(Inventory.voucher_id.in_(sub_query)))
db.execute(delete(EmployeeBenefit).where(EmployeeBenefit.voucher_id.in_(sub_query)))
db.execute(delete(Incentive).where(Incentive.voucher_id.in_(sub_query)))
db.execute(delete(Journal).where(and_(Journal.voucher_id.in_(sub_query), ~Journal.voucher_id.in_(vouchers))))
db.execute(
DbImage.__table__.delete(
delete(DbImage).where(
and_(
DbImage.resource_type == "voucher",
DbImage.resource_id.in_(sub_query),
@@ -228,25 +223,27 @@ def delete_data(date_: date, vouchers: List[Voucher], db: Session):
)
)
)
db.execute(Voucher.__table__.delete(and_(Voucher.date < date_, ~Voucher.id.in_(vouchers))))
db.execute(delete(Voucher).where(Voucher.date < date_, ~Voucher.id.in_(vouchers)))
def cleanup_lint(date_: date, db: Session):
# Insert executes on the end so keep list of batches and journals
db.execute(Batch.__table__.delete(~Batch.id.in_(db.query(distinct(Inventory.batch_id)).subquery())))
db.execute(Fingerprint.__table__.delete(Fingerprint.date < date_))
db.execute(Attendance.__table__.delete(Attendance.date < date_))
db.execute(delete(Batch).where(~Batch.id.in_(select(distinct(Inventory.batch_id)).subquery())))
db.execute(delete(Fingerprint).where(Fingerprint.date < date_))
db.execute(delete(Attendance).where(Attendance.date < date_))
db.execute(
Employee.__table__.delete(
delete(Employee).where(
and_(
~Employee.id.in_(db.query(distinct(Journal.account_id)).subquery()),
~Employee.id.in_(db.query(distinct(Fingerprint.employee_id)).subquery()),
~Employee.id.in_(db.query(distinct(Attendance.employee_id)).subquery()),
~Employee.id.in_(select(distinct(Journal.account_id)).subquery()),
~Employee.id.in_(select(distinct(Fingerprint.employee_id)).subquery()),
~Employee.id.in_(select(distinct(Attendance.employee_id)).subquery()),
Employee.id.in_(
db.query(AccountBase.id)
.filter(AccountBase.is_fixture == False) # noqa: E712
.filter(AccountBase.is_active == False)
.filter(AccountBase.is_starred == False)
select(AccountBase.id)
.where(
AccountBase.is_fixture == False, # noqa: E712
AccountBase.is_active == False, # noqa: E712
AccountBase.is_starred == False, # noqa: E712
)
.subquery()
),
Employee.leaving_date < date_,
@@ -254,17 +251,17 @@ def cleanup_lint(date_: date, db: Session):
)
)
db.execute(
AccountBase.__table__.delete(
delete(AccountBase).where(
and_(
~AccountBase.id.in_(db.query(Employee.id).subquery()),
~AccountBase.id.in_(select(Employee.id).subquery()),
AccountBase.account_type == Employee.__mapper_args__["polymorphic_identity"],
)
)
)
db.execute(
Account.__table__.delete(
delete(Account).where(
and_(
~Account.id.in_(db.query(distinct(Journal.account_id)).subquery()),
~Account.id.in_(select(distinct(Journal.account_id)).subquery()),
Account.is_fixture == False, # noqa: E712
Account.is_starred == False,
Account.account_type == Account.__mapper_args__["polymorphic_identity"],
+233 -212
View File
@@ -1,158 +1,158 @@
import datetime
import time
import uuid
from decimal import Decimal, InvalidOperation
from datetime import date, timedelta
from typing import List
from brewman.models.voucher import Voucher
from fastapi import APIRouter
from sqlalchemy import desc, func, or_
import brewman.schemas.recipe as schemas
from fastapi import APIRouter, Depends, HTTPException, Request, Security, status
from sqlalchemy import desc, func, or_, select
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_finish_date, get_start_date, set_period
from ..db.session import SessionFuture
from ..models.cost_centre import CostCentre
from ..models.inventory import Inventory
from ..models.journal import Journal
from ..models.product import Product
from ..models.recipe import Recipe
from ..models.recipe_item import RecipeItem
from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType
from ..schemas.user import UserToken
router = APIRouter()
# @router.post("/new") # "Recipes"
@router.post("/{id}") # "Recipes"
def save(request):
json = request.json_body
recipe_product = (
request.dbsession.query(Product).filter(Product.id == uuid.UUID(json["Product"]["ProductID"])).first()
)
@router.post("", response_model=schemas.Recipe)
def save(
data: schemas.RecipeIn,
request: Request,
user: UserToken = Security(get_user),
) -> schemas.Recipe:
with SessionFuture() as db:
recipe_product: Product = db.execute(select(Product).where(Product.id == data.product.id_)).scalar_one()
try:
valid_from = datetime.date(*(time.strptime(request.json_body["ValidFrom"], "%d-%b-%Y")[0:3]))
except (ValueError, KeyError, TypeError):
raise ValidationError("Valid From is not a valid date")
try:
valid_to = datetime.date(*(time.strptime(request.json_body["ValidTo"], "%d-%b-%Y")[0:3]))
except (ValueError, KeyError, TypeError):
raise ValidationError("Valid To is not a valid date")
if data.valid_till < data.valid_from:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Valid Till cannot be less than valid from",
)
sale_price = 0
recipe_cost = 0
if len(data.items) == 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Recipe has no ingredients",
)
if valid_to < valid_from:
raise ValidationError("Valid To cannot be less than valid from")
try:
recipe_quantity = Decimal(json.get("Quantity", 0))
if recipe_quantity < 0:
raise ValidationError("Quantity must be a decimal >= 0")
except (ValueError, InvalidOperation):
raise ValidationError("Quantity must be a decimal >= 0")
recipe = Recipe(
product_id=data.product.id_,
quantity=round(data.quantity, 2),
sale_price=round(data.sale_price, 2),
valid_from=data.valid_from,
valid_till=data.valid_till,
)
for item in data.items:
product: Product = db.execute(select(Product).where(Product.id == item.product.id_)).scalar_one()
quantity = round(data.quantity, 2)
if product.is_purchased:
ingredient_cost = get_purchased_product_cost(product.id, data.valid_from, data.valid_till, db)
else:
ingredient_cost = get_sub_product_cost(product.id, data.valid_from, data.valid_till, db)
cost_per_unit = ingredient_cost / (product.fraction * product.product_yield)
recipe_cost += cost_per_unit * quantity
recipe.recipe_items.append(RecipeItem(None, product.id, quantity, ingredient_cost))
sale_price = 0
if recipe_product.is_sold:
try:
sale_price = Decimal(json.get("SalePrice", 0))
if sale_price < 0:
raise ValidationError("Sale Price must be a decimal >= 0")
except (ValueError, InvalidOperation):
raise ValidationError("Sale Price must be a decimal >= 0")
recipe.cost_price = round(recipe_cost / recipe.quantity, 2)
if recipe_product.is_sold:
recipe_product.sale_price = sale_price
recipe_cost = 0
if len(json["RecipeItems"]) == 0:
raise ValidationError("Recipe has no ingredients")
recipe = Recipe(
product_id=recipe_product.id,
quantity=recipe_quantity,
sale_price=sale_price,
valid_from=valid_from,
valid_to=valid_to,
)
for item in json["RecipeItems"]:
product = request.dbsession.query(Product).filter(Product.id == uuid.UUID(item["Product"]["ProductID"])).first()
quantity = round(Decimal(item["Quantity"]), 2)
if product.is_purchased:
ingredient_cost = get_purchased_product_cost(product.id, valid_from, valid_to, request.dbsession)
else:
ingredient_cost = get_sub_product_cost(product.id, valid_from, valid_to, request.dbsession)
cost_per_unit = ingredient_cost / (product.fraction * product.product_yield)
recipe_cost += cost_per_unit * quantity
recipe.recipe_items.append(RecipeItem(None, product.id, quantity, ingredient_cost))
recipe.cost_price = round(recipe_cost / recipe_quantity, 2)
if recipe_product.is_sold:
recipe_product.sale_price = sale_price
save_recipe(recipe, request.dbsession)
transaction.commit()
set_period(valid_from, valid_to, request)
return recipe_info(recipe.id, request)
save_recipe(recipe, db)
db.commit()
set_period(data.valid_from, data.valid_till, request.session)
return recipe_info(recipe)
def save_recipe(recipe, dbsession):
product = dbsession.query(Product).filter(Product.id == recipe.product_id).first()
def save_recipe(recipe: Recipe, db: Session):
product: Product = db.execute(select(Product).where(Product.id == recipe.product_id)).scalar_one()
product.price = recipe.cost_price
update_old_rows(
recipe.product_id,
recipe.valid_from,
recipe.valid_to,
recipe.valid_till,
recipe.effective_from,
dbsession,
db,
)
dbsession.add(recipe)
db.add(recipe)
for item in recipe.recipe_items:
item.recipe_id = recipe.id
dbsession.add(item)
db.add(item)
def get_purchased_product_cost(product_id, start_date, finish_date, dbsession):
def get_purchased_product_cost(product_id: uuid.UUID, start_date: date, finish_date: date, db: Session):
quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity")
amount_sum = func.sum(
Journal.debit * Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * (1 - Inventory.discount)
).label("amount")
costing = (
dbsession.query(quantity_sum, amount_sum)
costing = db.execute(
select(quantity_sum, amount_sum)
.join(Product.inventories)
.join(Inventory.voucher)
.join(Voucher.journals)
.filter(Inventory.product_id == product_id)
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type == VoucherType.by_name("Issue").id)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.where(
Inventory.product_id == product_id,
Voucher.date >= start_date,
Voucher.date <= finish_date,
Voucher.type == VoucherType.by_name("Issue").id,
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
.group_by(Product)
.first()
)
).one_or_none()
if costing is None:
product = dbsession.query(Product).filter(Product.id == product_id).first()
product = db.execute(select(Product).where(Product.id == product_id)).scalar_one()
return product.price
else:
quantity, amount = costing
return amount / quantity
def get_sub_product_cost(product_id, start_date, finish_date, dbsession):
product = dbsession.query(Product).filter(Product.id == product_id).first()
def get_sub_product_cost(product_id: uuid.UUID, start_date: date, finish_date: date, db: Session):
product: Product = db.execute(select(Product).where(Product.id == product_id)).scalar_one()
old_recipe = (
dbsession.query(Recipe)
.filter(Recipe.product_id == product_id)
.filter(Recipe.effective_to == None)
.filter(Recipe.valid_from == start_date)
.filter(Recipe.valid_to == finish_date)
.first()
db.execute(
select(Recipe).where(
Recipe.product_id == product_id,
Recipe.effective_to == None,
Recipe.valid_from == start_date,
Recipe.valid_till == finish_date,
)
)
.scalars()
.one_or_none()
)
if old_recipe is not None:
return old_recipe.cost_price
old_recipe = (
dbsession.query(Recipe)
.filter(Recipe.product_id == product_id)
.filter(Recipe.effective_to == None)
.order_by(desc(Recipe.valid_to))
.first()
db.execute(
select(Recipe)
.where(Recipe.product_id == product_id, Recipe.effective_to == None)
.order_by(desc(Recipe.valid_till))
)
.scalars()
.one_or_none()
)
if old_recipe is None:
raise ValidationError("Ingredient XXX has no recipe, cannot determine cost")
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Ingredient XXX has no recipe, cannot determine cost",
)
recipe_cost = 0
recipe = Recipe(
@@ -160,31 +160,36 @@ def get_sub_product_cost(product_id, start_date, finish_date, dbsession):
quantity=old_recipe.quantity,
sale_price=product.sale_price,
valid_from=start_date,
valid_to=finish_date,
valid_till=finish_date,
)
for item in old_recipe.recipe_items:
if item.product.is_purchased:
ingredient_cost = get_purchased_product_cost(item.product_id, start_date, finish_date, dbsession)
ingredient_cost = get_purchased_product_cost(item.product_id, start_date, finish_date, db)
else:
ingredient_cost = get_sub_product_cost(item.product_id, start_date, finish_date, dbsession)
ingredient_cost = get_sub_product_cost(item.product_id, start_date, finish_date, db)
cost_per_unit = ingredient_cost / (item.product.fraction * item.product.product_yield)
recipe_cost += cost_per_unit * item.quantity
recipe.recipe_items.append(RecipeItem(None, item.product_id, item.quantity, ingredient_cost))
recipe.cost_price = round(recipe_cost / old_recipe.quantity, 2)
save_recipe(recipe, dbsession)
save_recipe(recipe, db)
return recipe_cost
def update_old_rows(product_id, valid_from, valid_to, effective_date, dbsession):
def update_old_rows(product_id, valid_from: date, valid_till: date, effective_date: date, db: Session):
old = (
dbsession.query(Recipe)
.filter(Recipe.product_id == product_id)
.filter(Recipe.valid_from < valid_to)
.filter(Recipe.valid_to > valid_from)
.filter(or_(Recipe.effective_to == None, Recipe.effective_to >= effective_date))
.order_by(Recipe.effective_from)
db.execute(
select(Recipe)
.where(
Recipe.product_id == product_id,
Recipe.valid_from < valid_till,
Recipe.valid_till > valid_from,
or_(Recipe.effective_to == None, Recipe.effective_to >= effective_date),
)
.order_by(Recipe.effective_from)
)
.scalars()
.all()
)
@@ -198,20 +203,20 @@ def update_old_rows(product_id, valid_from, valid_to, effective_date, dbsession)
cost_price=item.cost_price,
sale_price=item.sale_price,
valid_from=item.valid_from,
valid_to=valid_from - datetime.timedelta(days=1),
valid_till=valid_from - timedelta(days=1),
effective_from=effective_date,
)
for ri in item.recipe_items:
recipe.recipe_items.append(RecipeItem(None, ri.product_id, ri.quantity, ri.price))
new_recipes.append(recipe)
if item.valid_to > valid_to:
if item.valid_till > valid_till:
recipe = Recipe(
product_id=item.product_id,
quantity=item.quantity,
cost_price=item.cost_price,
sale_price=item.sale_price,
valid_from=valid_to + datetime.timedelta(days=1),
valid_to=item.valid_to,
valid_from=valid_till + timedelta(days=1),
valid_till=item.valid_till,
effective_from=effective_date,
)
for ri in item.recipe_items:
@@ -220,117 +225,133 @@ def update_old_rows(product_id, valid_from, valid_to, effective_date, dbsession)
if item.effective_from == effective_date and item.effective_to is None:
for ri in item.recipe_items:
dbsession.delete(ri)
dbsession.delete(item)
db.delete(ri)
db.delete(item)
else:
item.effective_to = effective_date
for recipe in new_recipes:
dbsession.add(recipe)
db.add(recipe)
for item in recipe.recipe_items:
item.recipe_id = recipe.id
dbsession.add(item)
db.add(item)
@router.delete("/{id}") # "Recipes"
def delete(request):
recipe = request.dbsession.query(Recipe).filter(Recipe.id == uuid.UUID(request.matchdict["id"])).first()
if len(recipe.product.recipes) > 1:
request.dbsession.delete(recipe)
else:
request.dbsession.delete(recipe)
request.dbsession.delete(recipe.product)
transaction.commit()
return recipe_info(None, request)
@router.delete("/{id_}", response_model=schemas.RecipeBlank)
def delete_route(
id_: uuid.UUID,
user: UserToken = Security(get_user, scopes=["recipes"]),
) -> schemas.RecipeBlank:
with SessionFuture() as db:
recipe: Recipe = db.execute(select(Recipe).where(Recipe.id == id_)).scalar_one()
if len(recipe.product.recipes) > 1:
db.delete(recipe)
else:
db.delete(recipe)
db.delete(recipe.product)
db.commit()
return recipe_blank()
@router.get("/{id}") # "Recipes"
def show_id(request):
return recipe_info(uuid.UUID(request.matchdict["id"]), request)
@router.get("", response_model=schemas.RecipeBlank)
def show_blank(
user: UserToken = Security(get_user, scopes=["recipes"]),
) -> schemas.RecipeBlank:
return recipe_blank()
@router.get("/new") # "Recipes"
def show_blank(request):
return recipe_info(None, request)
@router.get("/") # "Authenticated"
async def show_list(l: bool):
list_ = (
request.dbsession.query(Recipe)
.join(Recipe.product)
.filter(Recipe.effective_to == None)
.order_by(Recipe.product_id)
.order_by(desc(Recipe.valid_to))
.all()
)
recipes = []
product_id = None
for item in list_:
if item.product_id != product_id:
recipe = {
"ProductID": item.product_id,
"Name": item.product.name,
"ProductGroup": item.product.product_group.name,
"Prices": [],
}
recipes.append(recipe)
product_id = item.product_id
costing = 0
if item.product.is_sold and item.sale_price != 0 and item.cost_price != 0:
costing = item.cost_price / item.sale_price
recipe["Prices"].append(
{
"ValidFrom": item.valid_from.strftime("%d-%b-%Y"),
"ValidTo": item.valid_to.strftime("%d-%b-%Y"),
"CostPrice": item.cost_price,
"SalePrice": item.sale_price,
"Costing": costing,
"Url": request.route_url("recipe_id", id=item.id),
}
)
return recipes
def recipe_info(id_, request):
if id_ is None:
info = {
"Quantity": 1,
"ValidFrom": get_start_date(request),
"ValidTo": get_finish_date(request),
"RecipeItems": [],
}
else:
recipe = request.dbsession.query(Recipe).filter(Recipe.id == id_).one()
info = {
"RecipeID": recipe.id,
"Product": {
"ProductID": recipe.product_id,
"Name": recipe.product.name,
"Units": recipe.product.units,
"SalePrice": recipe.product.sale_price,
"IsSold": recipe.product.is_sold,
},
"Quantity": recipe.quantity,
"SalePrice": recipe.sale_price,
"CostPrice": recipe.cost_price,
"ValidFrom": recipe.valid_from.strftime("%d-%b-%Y"),
"ValidTo": recipe.valid_to.strftime("%d-%b-%Y"),
"RecipeItems": [],
}
for item in recipe.recipe_items:
info["RecipeItems"].append(
{
"Product": {
"ProductID": item.product.id,
"Name": item.product.name,
"Units": item.product.units,
"FractionUnits": item.product.fraction_units,
"Fraction": item.product.fraction,
"ProductYield": item.product.product_yield,
},
"Quantity": item.quantity,
"Price": item.price,
}
@router.get("/list", response_model=List[schemas.Recipe])
async def show_list(
user: UserToken = Depends(get_user),
) -> List[schemas.Recipe]:
with SessionFuture() as db:
list_ = (
db.execute(
select(Recipe)
.join(Recipe.product)
.where(Recipe.effective_to == None)
.order_by(Recipe.product_id)
.order_by(desc(Recipe.valid_till))
)
return info
.scalars()
.all()
)
return [
schemas.Recipe(
id=item.id,
product=schemas.ProductLink(id=item.product.id, name=item.product.name),
quantity=item.quantity,
costPrice=item.cost_price,
salePrice=item.sale_price,
notes="",
validFrom=item.valid_from,
validTill=item.valid_till,
effectiveFrom=item.valid_from,
effectiveTill=item.valid_till,
items=[],
)
for item in list_
]
@router.get("/{id_}", response_model=schemas.Recipe)
def show_id(
id_: uuid.UUID,
user: UserToken = Security(get_user, scopes=["recipes"]),
) -> schemas.Recipe:
with SessionFuture() as db:
item: Recipe = db.execute(select(Recipe).where(Recipe.id == id_)).scalar_one()
return recipe_info(item)
def recipe_info(recipe: Recipe) -> schemas.Recipe:
return schemas.Recipe(
id=recipe.id,
product=schemas.ProductLink(
id=recipe.product_id,
name=recipe.product.name,
units=recipe.product.units,
salePrice=recipe.product.sale_price,
isSold=recipe.product.is_sold,
),
quantity=recipe.quantity,
salePrice=recipe.sale_price,
costPrice=recipe.cost_price,
validFrom=recipe.valid_from,
validTill=recipe.valid_till,
notes="",
items=[
schemas.RecipeItem(
id=item.id,
product=schemas.ProductLink(
id=item.product.id,
name=item.product.name,
units=item.product.units,
fractionUnits=item.product.fraction_units,
fraction=item.product.fraction,
productYield=item.product.product_yield,
),
quantity=item.quantity,
price=item.price,
)
for item in recipe.recipe_items
],
)
def recipe_blank() -> schemas.RecipeBlank:
# if id_ is None:
# info = {
# "Quantity": 1,
# "ValidFrom": get_start_date(request),
# "ValidTo": get_finish_date(request),
# "RecipeItems": [],
# }
# else:
return schemas.RecipeBlank(
quantity=0,
costPrice=0,
salePrice=0,
items=[],
notes="",
)
@@ -3,13 +3,13 @@ from typing import List
import brewman.schemas.balance_sheet as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import desc, func
from sqlalchemy.sql.expression import desc, func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.account_base import AccountBase
from ...models.account_type import AccountType
from ...models.journal import Journal
@@ -23,15 +23,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.BalanceSheet)
def report_blank(
request: Request,
@@ -44,12 +35,12 @@ def report_blank(
def report_data(
date_: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["balance-sheet"]),
) -> schemas.BalanceSheet:
body, footer = build_balance_sheet(datetime.strptime(date_, "%d-%b-%Y"), db)
set_period(get_start_date(request.session), date_, request.session)
return schemas.BalanceSheet(date=date_, body=body, footer=footer)
with SessionFuture() as db:
body, footer = build_balance_sheet(datetime.strptime(date_, "%d-%b-%Y"), db)
set_period(get_start_date(request.session), date_, request.session)
return schemas.BalanceSheet(date=date_, body=body, footer=footer)
def build_balance_sheet(date_: date, db: Session) -> (List[schemas.BalanceSheetItem], schemas.BalanceSheetItem):
@@ -86,18 +77,14 @@ def build_balance_sheet(date_: date, db: Session) -> (List[schemas.BalanceSheetI
}
amount_sum = func.sum(Journal.amount * Journal.debit)
query = (
db.query(AccountBase, amount_sum)
query = db.execute(
select(AccountBase, amount_sum)
.join(Journal.voucher)
.join(Journal.account)
.filter(Voucher.date <= date_)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(AccountBase.type.in_(type_list))
.where(Voucher.date <= date_, Voucher.type != VoucherType.by_name("Issue").id, AccountBase.type.in_(type_list))
.group_by(AccountBase)
.order_by(AccountBase.type)
.order_by(desc(func.abs(amount_sum)))
.all()
)
.order_by(AccountBase.type, desc(func.abs(amount_sum)))
).all()
counter = 0
for account, amount in query:
+57 -65
View File
@@ -2,14 +2,13 @@ import datetime
import brewman.schemas.cash_flow as schemas
from fastapi import APIRouter, Depends, Request, Security
from sqlalchemy.orm import Session
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm.util import aliased
from sqlalchemy.sql.expression import desc, func
from sqlalchemy.sql.expression import desc, func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.account_base import AccountBase
from ...models.account_type import AccountType
from ...models.journal import Journal
@@ -21,15 +20,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.CashFlow)
def report_blank(
request: Request,
@@ -48,17 +38,17 @@ def report_data(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cash-flow"]),
):
body, footer = build_report(s, f, db)
set_period(s, f, request.session)
return {
"startDate": s,
"finishDate": f,
"body": body,
"footer": footer,
}
with SessionFuture() as db:
body, footer = build_report(s, f, db)
set_period(s, f, request.session)
return {
"startDate": s,
"finishDate": f,
"body": body,
"footer": footer,
}
@router.get("/{id_}")
@@ -67,17 +57,17 @@ def report_id(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cash-flow"]),
):
details, footer = build_report_id(id_, s, f, db)
set_period(s, f, request.session)
return {
"startDate": s,
"finishDate": f,
"body": {"details": details},
"footer": footer,
}
with SessionFuture() as db:
details, footer = build_report_id(id_, s, f, db)
set_period(s, f, request.session)
return {
"startDate": s,
"finishDate": f,
"body": {"details": details},
"footer": footer,
}
def build_report(start_date, finish_date, db):
@@ -86,25 +76,25 @@ def build_report(start_date, finish_date, db):
sub_account = aliased(AccountBase)
sub_query = (
db.query(sub_voucher.id)
select(sub_voucher.id)
.join(sub_journal, sub_voucher.journals)
.join(sub_account, sub_journal.account)
.filter(sub_account.type == AccountType.by_name("Cash").id)
.filter(sub_voucher.date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"))
.filter(sub_voucher.date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"))
.where(
sub_account.type == AccountType.by_name("Cash").id,
sub_voucher.date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"),
sub_voucher.date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"),
)
.subquery()
)
query = (
db.query(AccountBase.type, func.sum(Journal.signed_amount))
query = db.execute(
select(AccountBase.type, func.sum(Journal.signed_amount))
.join(Journal, Voucher.journals)
.join(AccountBase, Journal.account)
.filter(Voucher.id.in_(sub_query))
.filter(AccountBase.type != AccountType.by_name("Cash").id)
.where(Voucher.id.in_(sub_query), AccountBase.type != AccountType.by_name("Cash").id)
.group_by(AccountBase.type)
.order_by(func.sum(Journal.signed_amount))
.all()
)
).all()
total_amount = 0
cf = {"operating": [], "investing": [], "financing": []}
@@ -119,25 +109,27 @@ def build_report(start_date, finish_date, db):
}
)
opening = (
db.query(func.sum(Journal.amount * Journal.debit))
opening = db.execute(
select(func.sum(Journal.amount * Journal.debit))
.join(Journal.voucher)
.join(Journal.account)
.filter(Voucher.date < start_date)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(AccountBase.type == AccountType.by_name("Cash").id)
.scalar()
)
.where(
Voucher.date < start_date,
Voucher.type != VoucherType.by_name("Issue").id,
AccountBase.type == AccountType.by_name("Cash").id,
)
).scalar()
closing = (
db.query(func.sum(Journal.amount * Journal.debit))
closing = db.execute(
select(func.sum(Journal.amount * Journal.debit))
.join(Journal.voucher)
.join(Journal.account)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(AccountBase.type == AccountType.by_name("Cash").id)
.scalar()
)
.where(
Voucher.date <= finish_date,
Voucher.type != VoucherType.by_name("Issue").id,
AccountBase.type == AccountType.by_name("Cash").id,
)
).scalar()
return (
cf,
@@ -162,25 +154,25 @@ def build_report_id(account_type, start_date, finish_date, db):
sub_account = aliased(AccountBase)
sub_query = (
db.query(sub_voucher.id)
select(sub_voucher.id)
.join(sub_journal, sub_voucher.journals)
.join(sub_account, sub_journal.account)
.filter(sub_account.type == AccountType.by_name("Cash").id)
.filter(sub_voucher.date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"))
.filter(sub_voucher.date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"))
.where(
sub_account.type == AccountType.by_name("Cash").id,
sub_voucher.date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"),
sub_voucher.date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"),
)
.subquery()
)
query = (
db.query(AccountBase, func.sum(Journal.signed_amount))
query = db.execute(
select(AccountBase, func.sum(Journal.signed_amount))
.join(Journal, Voucher.journals)
.join(AccountBase, Journal.account)
.filter(Voucher.id.in_(sub_query))
.filter(AccountBase.type == account_type)
.where(Voucher.id.in_(sub_query), AccountBase.type == account_type)
.group_by(AccountBase)
.order_by(desc(func.sum(Journal.amount)))
.all()
)
).all()
total_amount = 0
for account, amount in query:
@@ -4,13 +4,13 @@ from typing import List
import brewman.schemas.closing_stock as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.cost_centre import CostCentre
from ...models.inventory import Inventory
from ...models.journal import Journal
@@ -22,15 +22,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.ClosingStock)
def report_blank(
request: Request,
@@ -43,30 +34,28 @@ def report_blank(
def report_data(
date_: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["closing-stock"]),
) -> schemas.ClosingStock:
set_period(get_start_date(request.session), date_, request.session)
return schemas.ClosingStock(
date=date_,
body=build_report(datetime.strptime(date_, "%d-%b-%Y").date(), db),
)
with SessionFuture() as db:
return schemas.ClosingStock(
date=date_,
body=build_report(datetime.strptime(date_, "%d-%b-%Y").date(), db),
)
def build_report(date_: date, db: Session) -> List[schemas.ClosingStockItem]:
amount_sum = func.sum(Journal.debit * Inventory.quantity * Inventory.rate * (1 + Inventory.tax)).label("amount")
quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity")
query = (
db.query(Product, quantity_sum, amount_sum)
query = db.execute(
select(Product, quantity_sum, amount_sum)
.join(Product.inventories)
.join(Inventory.voucher)
.join(Voucher.journals)
.filter(Voucher.date <= date_)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.where(Voucher.date <= date_, Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.group_by(Product)
.order_by(amount_sum.desc())
.all()
)
).all()
body = []
for product, quantity, amount in query:
@@ -83,26 +72,22 @@ def build_report(date_: date, db: Session) -> List[schemas.ClosingStockItem]:
def get_opening_stock(date_: date, db: Session) -> Decimal:
opening_stock = (
db.query(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit))
opening_stock = db.execute(
select(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit))
.join(Journal.voucher)
.join(Journal.account)
.join(Voucher.inventories)
.filter(Voucher.date < date_)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.scalar()
)
.where(Voucher.date < date_, Journal.cost_centre_id == CostCentre.cost_centre_purchase())
).scalar()
return 0 if opening_stock is None else opening_stock
def get_closing_stock(date_, db: Session) -> Decimal:
closing_stock = (
db.query(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit))
closing_stock = db.execute(
select(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit))
.join(Journal.voucher)
.join(Journal.account)
.join(Voucher.inventories)
.filter(Voucher.date <= date_)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.scalar()
)
.where(Voucher.date <= date_, Journal.cost_centre_id == CostCentre.cost_centre_purchase())
).scalar()
return 0 if closing_stock is None else closing_stock
+19 -23
View File
@@ -3,12 +3,13 @@ from typing import List
import brewman.schemas.daybook as schemas
from fastapi import APIRouter, Depends, Request, Security
from sqlalchemy.orm import Session, joinedload
from fastapi import APIRouter, Request, Security
from sqlalchemy import select
from sqlalchemy.orm import Session
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.journal import Journal
from ...models.voucher import Voucher
from ...models.voucher_type import VoucherType
@@ -18,15 +19,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.Daybook)
def report_blank(
request: Request,
@@ -44,27 +36,31 @@ def report_data(
start: str,
finish: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["daybook"]),
) -> schemas.Daybook:
start_date = datetime.strptime(start, "%d-%b-%Y")
finish_date = datetime.strptime(finish, "%d-%b-%Y")
body = build_report(start_date, finish_date, db)
set_period(start, finish, request.session)
return schemas.Daybook(startDate=start_date, finishDate=finish_date, body=body)
with SessionFuture() as db:
body = build_report(start_date, finish_date, db)
set_period(start, finish, request.session)
return schemas.Daybook(startDate=start_date, finishDate=finish_date, body=body)
def build_report(start_date: date, finish_date: date, db: Session) -> List[schemas.DaybookItem]:
body = []
query = (
db.query(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.order_by(Voucher.date)
.order_by(Voucher.last_edit_date)
db.execute(
select(Voucher)
.join(Voucher.journals)
.join(Journal.account)
.where(
Voucher.date >= start_date, Voucher.date <= finish_date, Voucher.type != VoucherType.by_name("Issue").id
)
.order_by(Voucher.date, Voucher.last_edit_date)
)
.unique()
.scalars()
.all()
)
+38 -40
View File
@@ -5,13 +5,13 @@ from typing import List
import brewman.schemas.ledger as schemas
from fastapi import APIRouter, Depends, Request, Security
from sqlalchemy.orm import Session, joinedload
from sqlalchemy.sql.expression import func
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.account_base import AccountBase
from ...models.journal import Journal
from ...models.voucher import Voucher
@@ -22,15 +22,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.Ledger)
def show_blank(
request: Request,
@@ -50,20 +41,20 @@ def show_data(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["ledger"]),
) -> schemas.Ledger:
account = db.query(AccountBase).filter(AccountBase.id == id_).first()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
body = build_report(account.id, start_date, finish_date, db)
set_period(start_date, finish_date, request.session)
return schemas.Ledger(
startDate=start_date,
finishDate=finish_date,
account=schemas.AccountLink(id=account.id, name=account.name),
body=body,
)
with SessionFuture() as db:
account = db.execute(select(AccountBase).where(AccountBase.id == id_)).scalar_one()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
body = build_report(account.id, start_date, finish_date, db)
set_period(start_date, finish_date, request.session)
return schemas.Ledger(
startDate=start_date,
finishDate=finish_date,
account=schemas.AccountLink(id=account.id, name=account.name),
body=body,
)
def build_report(account_id: uuid.UUID, start_date: str, finish_date: str, db: Session) -> List[schemas.LedgerItem]:
@@ -72,14 +63,20 @@ def build_report(account_id: uuid.UUID, start_date: str, finish_date: str, db: S
body.append(opening)
query = (
db.query(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.filter(Voucher.journals.any(Journal.account_id == account_id))
.filter(Voucher.date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"))
.filter(Voucher.date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"))
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.order_by(Voucher.date)
.order_by(Voucher.last_edit_date)
db.execute(
select(Voucher)
.join(Voucher.journals)
.join(Journal.account)
.where(
Voucher.journals.any(Journal.account_id == account_id),
Voucher.date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"),
Voucher.date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"),
Voucher.type != VoucherType.by_name("Issue").id,
)
.order_by(Voucher.date, Voucher.last_edit_date)
)
.unique()
.scalars()
.all()
)
@@ -122,14 +119,15 @@ def build_report(account_id: uuid.UUID, start_date: str, finish_date: str, db: S
def opening_balance(account_id: uuid.UUID, start_date: str, db: Session) -> schemas.LedgerItem:
opening = (
db.query(func.sum(Journal.amount * Journal.debit))
opening = db.execute(
select(func.sum(Journal.amount * Journal.debit))
.join(Journal.voucher)
.filter(Voucher.date < datetime.datetime.strptime(start_date, "%d-%b-%Y"))
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(Journal.account_id == account_id)
.scalar()
)
.where(
Voucher.date < datetime.datetime.strptime(start_date, "%d-%b-%Y"),
Voucher.type != VoucherType.by_name("Issue").id,
Journal.account_id == account_id,
)
).scalar()
opening = 0 if opening is None else opening
if opening < 0:
credit = opening * -1
@@ -3,13 +3,13 @@ from typing import List
import brewman.schemas.net_transactions as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import desc, func
from sqlalchemy.sql.expression import desc, func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.account_base import AccountBase
from ...models.journal import Journal
from ...models.voucher import Voucher
@@ -20,15 +20,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.NetTransactions)
def show_blank(
request: Request,
@@ -46,35 +37,31 @@ def show_data(
start: str,
finish: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["net-transactions"]),
) -> schemas.NetTransactions:
set_period(start, finish, request.session)
return schemas.NetTransactions(
startDate=start,
finishDate=finish,
body=build_report(
datetime.strptime(start, "%d-%b-%Y"),
datetime.strptime(finish, "%d-%b-%Y"),
db,
),
)
with SessionFuture() as db:
return schemas.NetTransactions(
startDate=start,
finishDate=finish,
body=build_report(
datetime.strptime(start, "%d-%b-%Y"),
datetime.strptime(finish, "%d-%b-%Y"),
db,
),
)
def build_report(start_date: date, finish_date: date, db: Session) -> List[schemas.NetTransactionsItem]:
amount_sum = func.sum(Journal.amount * Journal.debit).label("amount")
query = (
db.query(AccountBase, amount_sum)
query = db.execute(
select(AccountBase, amount_sum)
.join(Journal.voucher)
.join(Journal.account)
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.where(Voucher.date >= start_date, Voucher.date <= finish_date, Voucher.type != VoucherType.by_name("Issue").id)
.group_by(AccountBase)
.order_by(AccountBase.type)
.order_by(desc(func.abs(amount_sum)))
.all()
)
.order_by(AccountBase.type, desc(func.abs(amount_sum)))
).all()
body = []
for account, amount in query:
@@ -6,13 +6,13 @@ from typing import List
import brewman.schemas.product_ledger as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session, joinedload
from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.cost_centre import CostCentre
from ...models.inventory import Inventory
from ...models.journal import Journal
@@ -25,15 +25,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.ProductLedger)
def show_blank(
request: Request,
@@ -53,25 +44,25 @@ def show_data(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["product-ledger"]),
) -> schemas.ProductLedger:
product = db.query(Product).filter(Product.id == id_).first()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
body = build_report(
product.id,
datetime.strptime(start_date, "%d-%b-%Y"),
datetime.strptime(finish_date, "%d-%b-%Y"),
db,
)
set_period(start_date, finish_date, request.session)
return schemas.ProductLedger(
startDate=start_date,
finishDate=finish_date,
product=schemas.ProductLink(id=product.id, name=product.name),
body=body,
)
with SessionFuture() as db:
product = db.execute(select(Product).where(Product.id == id_)).scalar_one()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
body = build_report(
product.id,
datetime.strptime(start_date, "%d-%b-%Y"),
datetime.strptime(finish_date, "%d-%b-%Y"),
db,
)
set_period(start_date, finish_date, request.session)
return schemas.ProductLedger(
startDate=start_date,
finishDate=finish_date,
product=schemas.ProductLink(id=product.id, name=product.name),
body=body,
)
def build_report(
@@ -81,22 +72,22 @@ def build_report(
running_total_q, running_total_a, opening = opening_balance(product_id, start_date, db)
body.append(opening)
query = (
db.query(Voucher, Inventory, Journal)
query = db.execute(
select(Voucher, Inventory, Journal)
.options(
joinedload(Journal.account, innerjoin=True),
joinedload(Journal.cost_centre, innerjoin=True),
)
.filter(Voucher.id == Inventory.voucher_id)
.filter(Voucher.id == Journal.voucher_id)
.filter(Inventory.product_id == product_id)
.filter(Journal.cost_centre_id != CostCentre.cost_centre_purchase())
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.order_by(Voucher.date)
.order_by(Voucher.last_edit_date)
.all()
)
.where(
Voucher.id == Inventory.voucher_id,
Voucher.id == Journal.voucher_id,
Inventory.product_id == product_id,
Journal.cost_centre_id != CostCentre.cost_centre_purchase(),
Voucher.date >= start_date,
Voucher.date <= finish_date,
)
.order_by(Voucher.date, Voucher.last_edit_date)
).all()
for row in query:
journal_debit = row.Journal.debit * -1
@@ -141,20 +132,21 @@ def build_report(
def opening_balance(
product_id: uuid.UUID, start_date: date, db: Session
) -> (Decimal, Decimal, schemas.ProductLedgerItem):
quantity, amount = (
db.query(
quantity, amount = db.execute(
select(
func.sum(Inventory.quantity * Journal.debit),
func.sum(Inventory.amount * Journal.debit),
)
.join(Inventory.voucher)
.join(Voucher.journals)
.filter(Voucher.id == Inventory.voucher_id)
.filter(Voucher.id == Journal.voucher_id)
.filter(Inventory.product_id == product_id)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.filter(Voucher.date < start_date)
.one()
)
.where(
Voucher.id == Inventory.voucher_id,
Voucher.id == Journal.voucher_id,
Inventory.product_id == product_id,
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
Voucher.date < start_date,
)
).one()
if quantity and quantity > 0:
debit_quantity = quantity
+32 -38
View File
@@ -4,13 +4,13 @@ from typing import List
import brewman.schemas.profit_loss as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import desc, func
from sqlalchemy.sql.expression import desc, func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.account_base import AccountBase
from ...models.account_type import AccountType
from ...models.journal import Journal
@@ -23,15 +23,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.ProfitLoss)
def report_blank(
request: Request,
@@ -50,17 +41,19 @@ def report_data(
start: str,
finish: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["profit-&-loss"]),
) -> schemas.ProfitLoss:
body, footer = build_profit_loss(datetime.strptime(start, "%d-%b-%Y"), datetime.strptime(finish, "%d-%b-%Y"), db)
set_period(start, finish, request.session)
return schemas.ProfitLoss(
startDate=start,
finishDate=finish,
body=body,
footer=footer,
)
with SessionFuture() as db:
body, footer = build_profit_loss(
datetime.strptime(start, "%d-%b-%Y"), datetime.strptime(finish, "%d-%b-%Y"), db
)
set_period(start, finish, request.session)
return schemas.ProfitLoss(
startDate=start,
finishDate=finish,
body=body,
footer=footer,
)
def build_profit_loss(
@@ -71,19 +64,19 @@ def build_profit_loss(
groups = {}
amount_sum = func.sum(Journal.amount * Journal.debit)
query = (
db.query(AccountBase, amount_sum)
query = db.execute(
select(AccountBase, amount_sum)
.join(Journal.voucher)
.join(Journal.account)
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(AccountBase.type.in_(profit_type_list))
.where(
Voucher.date >= start_date,
Voucher.date <= finish_date,
Voucher.type != VoucherType.by_name("Issue").id,
AccountBase.type.in_(profit_type_list),
)
.group_by(AccountBase)
.order_by(AccountBase.type)
.order_by(desc(func.abs(amount_sum)))
.all()
)
.order_by(AccountBase.type, desc(func.abs(amount_sum)))
).all()
# Get opening / closing stock
opening_stock = get_opening_stock(start_date, db)
@@ -141,13 +134,14 @@ def build_profit_loss(
def get_accumulated_profit(finish_date: date, db: Session) -> Decimal:
type_list = [i.id for i in AccountType.list() if i.balance_sheet is False]
accumulated_profit = (
db.query(func.sum(Journal.amount * Journal.debit))
accumulated_profit = db.execute(
select(func.sum(Journal.amount * Journal.debit))
.join(Journal.voucher)
.join(Journal.account)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(AccountBase.type.in_(type_list))
.scalar()
)
.where(
Voucher.date <= finish_date,
Voucher.type != VoucherType.by_name("Issue").id,
AccountBase.type.in_(type_list),
)
).scalar()
return 0 if accumulated_profit is None else accumulated_profit
@@ -3,12 +3,13 @@ from typing import List
import brewman.schemas.purchase_entries as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy import select
from sqlalchemy.orm import Session
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.voucher import Voucher
from ...models.voucher_type import VoucherType
from ...schemas.user import UserToken
@@ -17,15 +18,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.PurchaseEntries)
def report_blank(
request: Request,
@@ -43,24 +35,32 @@ def report_data(
start: str,
finish: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["purchase-entries"]),
) -> schemas.PurchaseEntries:
start_date = datetime.strptime(start, "%d-%b-%Y")
finish_date = datetime.strptime(finish, "%d-%b-%Y")
body = build_report(start_date, finish_date, db)
set_period(start, finish, request.session)
return schemas.PurchaseEntries(startDate=start, finishDate=finish, body=body)
with SessionFuture() as db:
body = build_report(start_date, finish_date, db)
set_period(start, finish, request.session)
return schemas.PurchaseEntries(startDate=start, finishDate=finish, body=body)
def build_report(start_date: date, finish_date: date, db: Session) -> List[schemas.PurchaseEntriesItem]:
body = []
query: List[Voucher] = (
db.query(Voucher)
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type == VoucherType.by_name("Purchase").id)
.order_by(Voucher.date)
db.execute(
select(Voucher)
.join(Voucher.journals)
.join(Voucher.inventories)
.where(
Voucher.date >= start_date,
Voucher.date <= finish_date,
Voucher.type == VoucherType.by_name("Purchase").id,
)
.order_by(Voucher.date)
)
.unique()
.scalars()
.all()
)
+21 -29
View File
@@ -4,13 +4,13 @@ from typing import List
import brewman.schemas.purchases as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import desc, func
from sqlalchemy.sql.expression import desc, func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.cost_centre import CostCentre
from ...models.inventory import Inventory
from ...models.journal import Journal
@@ -23,15 +23,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.Purchases)
def report_blank(
request: Request,
@@ -50,17 +41,17 @@ def report_data(
start: str,
finish: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["purchases"]),
) -> schemas.Purchases:
body, footer = build_report(start, finish, db)
set_period(start, finish, request.session)
return schemas.Purchases(
startDate=start,
finishDate=finish,
body=body,
footer=footer,
)
with SessionFuture() as db:
body, footer = build_report(start, finish, db)
set_period(start, finish, request.session)
return schemas.Purchases(
startDate=start,
finishDate=finish,
body=body,
footer=footer,
)
def build_report(
@@ -69,19 +60,20 @@ def build_report(
body = []
quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity")
amount_sum = func.sum(Journal.debit * Inventory.quantity * Inventory.rate * (1 + Inventory.tax)).label("amount")
query = (
db.query(Product, quantity_sum, amount_sum)
query = db.execute(
select(Product, quantity_sum, amount_sum)
.join(Product.inventories)
.join(Inventory.voucher)
.join(Voucher.journals)
.filter(Voucher.date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"))
.filter(Voucher.date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"))
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.where(
Voucher.date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"),
Voucher.date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"),
Voucher.type != VoucherType.by_name("Issue").id,
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
.group_by(Product)
.order_by(desc(amount_sum))
.all()
)
).all()
total_amount = 0
for product, quantity, amount in query:
@@ -5,13 +5,13 @@ from typing import List
import brewman.schemas.raw_material_cost as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import case, func
from sqlalchemy.sql.expression import case, func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.account_base import AccountBase
from ...models.cost_centre import CostCentre
from ...models.inventory import Inventory
@@ -25,15 +25,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.RawMaterialCost)
def report_blank(
request: Request,
@@ -52,17 +43,17 @@ def report_data(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["raw-material-cost"]),
) -> schemas.RawMaterialCost:
body, footer = build_report(datetime.strptime(s, "%d-%b-%Y"), datetime.strptime(f, "%d-%b-%Y"), db)
set_period(s, f, request.session)
return schemas.RawMaterialCost(
startDate=s,
finishDate=f,
body=body,
footer=footer,
)
with SessionFuture() as db:
body, footer = build_report(datetime.strptime(s, "%d-%b-%Y"), datetime.strptime(f, "%d-%b-%Y"), db)
set_period(s, f, request.session)
return schemas.RawMaterialCost(
startDate=s,
finishDate=f,
body=body,
footer=footer,
)
@router.get("/{id_}", response_model=schemas.RawMaterialCost)
@@ -71,17 +62,17 @@ def report_id(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["raw-material-cost"]),
) -> schemas.RawMaterialCost:
body = build_report_id(id_, datetime.strptime(s, "%d-%b-%Y"), datetime.strptime(f, "%d-%b-%Y"), db)
set_period(s, f, request.session)
return schemas.RawMaterialCost(
id=id_,
startDate=s,
finishDate=f,
body=body,
)
with SessionFuture() as db:
body = build_report_id(id_, datetime.strptime(s, "%d-%b-%Y"), datetime.strptime(f, "%d-%b-%Y"), db)
set_period(s, f, request.session)
return schemas.RawMaterialCost(
id=id_,
startDate=s,
finishDate=f,
body=body,
)
def build_report(
@@ -91,19 +82,20 @@ def build_report(
sum_issue = func.sum(case([(AccountBase.type == 2, Journal.signed_amount)], else_=0)).label("issue")
sum_sale = func.sum(case([(AccountBase.type == 3, Journal.signed_amount * -1)], else_=0)).label("sale")
query = (
db.query(CostCentre, sum_issue, sum_sale)
query = db.execute(
select(CostCentre, sum_issue, sum_sale)
.join(CostCentre.journals)
.join(Journal.voucher)
.join(Journal.account)
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Journal.cost_centre_id != CostCentre.cost_centre_purchase())
.filter(AccountBase.type.in_([2, 3]))
.where(
Voucher.date >= start_date,
Voucher.date <= finish_date,
Journal.cost_centre_id != CostCentre.cost_centre_purchase(),
AccountBase.type.in_([2, 3]),
)
.group_by(CostCentre)
.order_by(sum_sale.desc())
.all()
)
).all()
issues = 0
sales = 0
@@ -132,23 +124,21 @@ def build_report_id(
sum_net = func.sum(Inventory.rate * Inventory.quantity * Journal.debit).label("net")
sum_gross = func.sum(Inventory.amount * Journal.debit).label("gross")
query = (
db.query(Product, sum_quantity, sum_net, sum_gross)
query = db.execute(
select(Product, sum_quantity, sum_net, sum_gross)
.join(Product.inventories)
.join(Inventory.voucher)
.join(Voucher.journals)
.join(Product.product_group)
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type == 3)
.filter(Journal.cost_centre_id == cost_centre_id)
.group_by(Product)
.group_by(Journal.debit)
.group_by(ProductGroup.name)
.order_by(ProductGroup.name)
.order_by(sum_net.desc())
.all()
)
.where(
Voucher.date >= start_date,
Voucher.date <= finish_date,
Voucher.type == 3,
Journal.cost_centre_id == cost_centre_id,
)
.group_by(Product, Journal.debit, ProductGroup.name)
.order_by(ProductGroup.name, sum_net.desc())
).all()
groups = {}
counter = 0
+58 -69
View File
@@ -3,11 +3,11 @@ import uuid
from fastapi import APIRouter, Depends, Request, Security
from sqlalchemy.orm import Session, joinedload
from sqlalchemy.sql.expression import and_, func, or_
from sqlalchemy.sql.expression import and_, func, or_, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.account_base import AccountBase
from ...models.journal import Journal
from ...models.voucher import Voucher
@@ -18,15 +18,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("")
def show_blank(
request: Request,
@@ -46,45 +37,42 @@ def show_data(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["reconcile"]),
):
account = db.query(AccountBase).filter(AccountBase.id == id_).first()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
body = build_report(account.id, start_date, finish_date, db)
set_period(start_date, finish_date, request.session)
return {
"startDate": start_date,
"finishDate": finish_date,
"account": {"id": account.id, "name": account.name},
"body": body,
}
with SessionFuture() as db:
account = db.execute(select(AccountBase).where(AccountBase.id == id_)).scalar_one()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
body = build_report(account.id, start_date, finish_date, db)
set_period(start_date, finish_date, request.session)
return {
"startDate": start_date,
"finishDate": finish_date,
"account": {"id": account.id, "name": account.name},
"body": body,
}
def build_report(account_id, start_date, finish_date, db):
opening = opening_balance(account_id, start_date, db)
body = [opening]
query = (
db.query(Voucher)
query = db.execute(
select(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.filter(Voucher.journals.any(Journal.account_id == account_id))
.filter(
.where(
Voucher.journals.any(Journal.account_id == account_id),
or_(
Voucher.is_reconciled == False,
and_(
Voucher.reconcile_date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"),
Voucher.reconcile_date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"),
),
)
),
Voucher.type != VoucherType.by_name("Issue").id,
)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.order_by(Voucher.is_reconciled)
.order_by(Voucher.reconcile_date)
.order_by(Voucher.last_edit_date)
.all()
)
.order_by(Voucher.is_reconciled, Voucher.reconcile_date, Voucher.last_edit_date)
).all()
for voucher in query:
debit = 0
@@ -120,16 +108,17 @@ def build_report(account_id, start_date, finish_date, db):
return body
def opening_balance(account_id, start_date, db):
opening = (
db.query(func.sum(Journal.amount * Journal.debit))
def opening_balance(account_id: uuid.UUID, start_date, db: Session):
opening = db.execute(
select(func.sum(Journal.amount * Journal.debit))
.join(Journal.voucher)
.filter(Voucher.reconcile_date < datetime.datetime.strptime(start_date, "%d-%b-%Y"))
.filter(Voucher.is_reconciled == True)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(Journal.account_id == account_id)
.scalar()
)
.where(
Voucher.reconcile_date < datetime.datetime.strptime(start_date, "%d-%b-%Y"),
Voucher.is_reconciled == True,
Voucher.type != VoucherType.by_name("Issue").id,
Journal.account_id == account_id,
)
).scalar()
opening = 0 if opening is None else opening
if opening < 0:
credit = opening * -1
@@ -157,31 +146,31 @@ def save(
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["reconcile"]),
):
account = db.query(AccountBase).filter(AccountBase.id == id_).first()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
raise Exception("not fixed")
# for item in request.json_body["body"]:
# if "id" not in item or item["id"] is None:
# continue
#
# voucher = (
# request.dbsession.query(Voucher)
# .filter(Voucher.id == uuid.UUID(item["id"]))
# .first()
# )
# is_reconciled = item["isReconciled"]
# reconcile_date = datetime.datetime.strptime(item["reconcileDate"], "%d-%b-%Y")
# voucher.is_reconciled = is_reconciled
# voucher.reconcile_date = reconcile_date
# transaction.commit()
# body = build_report(account.id, start_date, finish_date, request)
# return {
# "startDate": start_date,
# "finishDate": finish_date,
# "account": {"id": account.id, "name": account.name},
# "body": body
# }
with SessionFuture() as db:
account = db.execute(select(AccountBase).where(AccountBase.id == id_)).scalar_one()
start_date = s if s is not None else get_start_date(request.session)
finish_date = f if f is not None else get_finish_date(request.session)
raise Exception("not fixed")
# for item in request.json_body["body"]:
# if "id" not in item or item["id"] is None:
# continue
#
# voucher = (
# request.dbsession.query(Voucher)
# .where(Voucher.id == uuid.UUID(item["id"]))
# .first()
# )
# is_reconciled = item["isReconciled"]
# reconcile_date = datetime.datetime.strptime(item["reconcileDate"], "%d-%b-%Y")
# voucher.is_reconciled = is_reconciled
# voucher.reconcile_date = reconcile_date
# transaction.commit()
# body = build_report(account.id, start_date, finish_date, request)
# return {
# "startDate": start_date,
# "finishDate": finish_date,
# "account": {"id": account.id, "name": account.name},
# "body": body
# }
@@ -4,13 +4,13 @@ from typing import List
import brewman.schemas.stock_movement as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.cost_centre import CostCentre
from ...models.inventory import Inventory
from ...models.journal import Journal
@@ -23,25 +23,16 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.StockMovement)
def report_blank(
request: Request,
user: UserToken = Security(get_user, scopes=["stock-movement"]),
) -> schemas.StockMovement:
return {
"startDate": get_start_date(request.session),
"finishDate": get_finish_date(request.session),
"body": [],
}
return schemas.StockMovement(
startDate=get_start_date(request.session),
finishDate=get_finish_date(request.session),
body=[],
)
@router.get("/{start}/{finish}", response_model=schemas.StockMovement)
@@ -49,27 +40,25 @@ def report_data(
start: str,
finish: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["stock-movement"]),
) -> schemas.StockMovement:
body = build_stock_movement(datetime.strptime(start, "%d-%b-%Y"), datetime.strptime(finish, "%d-%b-%Y"), db)
set_period(start, finish, request.session)
return {"startDate": start, "finishDate": finish, "body": body}
with SessionFuture() as db:
body = build_stock_movement(datetime.strptime(start, "%d-%b-%Y"), datetime.strptime(finish, "%d-%b-%Y"), db)
set_period(start, finish, request.session)
return schemas.StockMovement(startDate=start, finishDate=finish, body=body)
def build_stock_movement(start_date: date, finish_date: date, db: Session) -> List[schemas.StockMovementItem]:
dict_ = {}
quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity")
openings = (
db.query(Product, quantity_sum)
openings = db.execute(
select(Product, quantity_sum)
.join(Product.inventories)
.join(Inventory.voucher)
.join(Voucher.journals)
.filter(Voucher.date < start_date)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.where(Voucher.date < start_date, Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.group_by(Product)
.all()
)
).all()
for product, quantity in openings:
dict_[product.id] = schemas.StockMovementItem(
id=product.id,
@@ -81,18 +70,19 @@ def build_stock_movement(start_date: date, finish_date: date, db: Session) -> Li
closing=0,
url=["/", "product-ledger", str(product.id)],
)
purchases = (
db.query(Product, quantity_sum)
purchases = db.execute(
select(Product, quantity_sum)
.join(Product.inventories)
.join(Inventory.voucher)
.join(Voucher.journals)
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.where(
Voucher.date >= start_date,
Voucher.date <= finish_date,
Voucher.type != VoucherType.by_name("Issue").id,
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
.group_by(Product)
.all()
)
).all()
for product, quantity in purchases:
if product.id in dict_:
dict_[product.id].purchase = Decimal(round(quantity, 2))
@@ -107,18 +97,19 @@ def build_stock_movement(start_date: date, finish_date: date, db: Session) -> Li
closing=0,
url=["/", "product-ledger", str(product.id)],
)
issues = (
db.query(Product, quantity_sum)
issues = db.execute(
select(Product, quantity_sum)
.join(Product.inventories)
.join(Inventory.voucher)
.join(Voucher.journals)
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.filter(Voucher.type == VoucherType.by_name("Issue").id)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.where(
Voucher.date >= start_date,
Voucher.date <= finish_date,
Voucher.type == VoucherType.by_name("Issue").id,
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
.group_by(Product)
.all()
)
).all()
for product, quantity in issues:
if product.id in dict_:
dict_[product.id].issue = Decimal(round(quantity * -1, 2))
@@ -3,13 +3,13 @@ from typing import List
import brewman.schemas.trial_balance as schemas
from fastapi import APIRouter, Depends, Request, Security
from fastapi import APIRouter, Request, Security
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import func, select
from ...core.security import get_current_active_user as get_user
from ...core.session import get_finish_date, get_start_date, set_period
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.account_base import AccountBase
from ...models.journal import Journal
from ...models.voucher import Voucher
@@ -20,15 +20,6 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=schemas.TrialBalance)
def report_blank(
request: Request,
@@ -41,29 +32,26 @@ def report_blank(
def report_data(
date_: str,
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["trial-balance"]),
) -> schemas.TrialBalance:
set_period(get_start_date(request.session), date_, request.session)
return schemas.TrialBalance(
date=date_,
body=build_report(datetime.strptime(date_, "%d-%b-%Y"), db),
)
with SessionFuture() as db:
return schemas.TrialBalance(
date=date_,
body=build_report(datetime.strptime(date_, "%d-%b-%Y"), db),
)
def build_report(date_: date, db: Session) -> List[schemas.TrialBalanceItem]:
amount_sum = func.sum(Journal.amount * Journal.debit).label("amount")
query = (
db.query(AccountBase, amount_sum)
query = db.execute(
select(AccountBase, amount_sum)
.join(Journal.voucher)
.join(Journal.account)
.filter(Voucher.date <= date_)
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.where(Voucher.date <= date_, Voucher.type != VoucherType.by_name("Issue").id)
.group_by(AccountBase)
.order_by(AccountBase.type)
.order_by(func.abs(amount_sum).desc())
.all()
)
.order_by(AccountBase.type, func.abs(amount_sum).desc())
).all()
body = []
for account, amount in query:
+15 -21
View File
@@ -2,11 +2,12 @@ from typing import List
import brewman.schemas.unposted as schemas
from fastapi import APIRouter, Depends, Request, Security
from sqlalchemy.orm import Session, joinedload
from fastapi import APIRouter, Security
from sqlalchemy import select
from sqlalchemy.orm import Session
from ...core.security import get_current_active_user as get_user
from ...db.session import SessionLocal
from ...db.session import SessionFuture
from ...models.journal import Journal
from ...models.voucher import Voucher
from ...models.voucher_type import VoucherType
@@ -16,34 +17,27 @@ from ...schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("", response_model=List[schemas.Unposted])
def report_data(
request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["post-vouchers"]),
) -> List[schemas.Unposted]:
return build_report(db)
with SessionFuture() as db:
return build_report(db)
def build_report(db: Session) -> List[schemas.Unposted]:
body = []
query = (
db.query(Voucher)
.options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True))
.filter(Voucher.posted == False) # noqa: E712
.filter(Voucher.type != VoucherType.by_name("Issue").id)
.order_by(Voucher.date)
.order_by(Voucher.last_edit_date)
db.execute(
select(Voucher)
.join(Voucher.journals)
.join(Journal.account)
.where(Voucher.posted == False, Voucher.type != VoucherType.by_name("Issue").id) # noqa: E712
.order_by(Voucher.date, Voucher.last_edit_date)
)
.unique()
.scalars()
.all()
)
+46 -46
View File
@@ -1,11 +1,13 @@
import uuid
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import func
from decimal import Decimal
from fastapi import APIRouter, HTTPException, Security, status
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account_base import AccountBase
from ..models.batch import Batch
from ..models.cost_centre import CostCentre
@@ -21,65 +23,62 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("/{id_}")
def rebase(
def reset_stock(
id_: uuid.UUID,
item: ResetStock,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["reset-stock"]),
):
product: Product = db.query(Product).filter(Product.id == id_).first()
with SessionFuture() as db:
product: Product = db.execute(select(Product).where(Product.id == id_)).scalar_one()
if item.reset_date > item.stock_date:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Reset cannot be after the stock date",
)
if item.reset_date > item.stock_date:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Reset cannot be after the stock date",
)
change = round(item.quantity, 2) - get_closing_stock(product, item.stock_date, db=db)
if change == 0:
return {"No Change Needed"}
final = get_closing_stock(product, db=db)
if final + change < 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Current Quantity will get negative. Cannot proceed",
)
change = round(item.quantity, 2) - get_closing_stock(product, item.stock_date, db=db)
if change == 0:
return {"No Change Needed"}
final = get_closing_stock(product, db=db)
if final + change < 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Current Quantity will get negative. Cannot proceed",
)
batch = get_last_batch(product, db)
set_batches(batch, final + change, db)
batch = get_last_batch(product, db)
set_batches(batch, final + change, db)
create_voucher(batch, change, item.reset_date, user.id_, db)
db.commit()
return {}
create_voucher(batch, change, item.reset_date, user.id_, db)
db.commit()
return {}
def get_closing_stock(product, finish_date=None, db=None):
query = (
db.query(func.sum(Inventory.quantity * Journal.debit))
select(func.sum(Inventory.quantity * Journal.debit))
.join(Voucher)
.filter(Voucher.id == Inventory.voucher_id)
.filter(Voucher.id == Journal.voucher_id)
.filter(Inventory.product_id == product.id)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.where(
Voucher.id == Inventory.voucher_id,
Voucher.id == Journal.voucher_id,
Inventory.product_id == product.id,
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
)
if finish_date is not None:
query = query.filter(Voucher.date <= finish_date)
query = query.one()
return 0 if query is None else query[0]
query = query.where(Voucher.date <= finish_date)
result = db.execute(query).scalar()
return 0 if result is None else result
def get_last_batch(product, db):
batch = db.query(Batch).filter(Batch.product_id == product.id).order_by(Batch.name.desc()).first()
batch = (
db.execute(select(Batch).where(Batch.product_id == product.id).order_by(Batch.name.desc()))
.scalars()
.one_or_none()
)
if batch is None:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
@@ -88,12 +87,13 @@ def get_last_batch(product, db):
return batch
def set_batches(batch, quantity, db):
def set_batches(batch: Batch, quantity: Decimal, db: Session):
batch.quantity_remaining = quantity
batches = db.query(Batch).filter(Batch.id != batch.id).filter(Batch.product_id == batch.product_id)
batches = (
db.execute(select(Batch).where(Batch.id != batch.id, Batch.product_id == batch.product_id)).scalars().all()
)
for item in batches:
item.quantity_remaining = 0
pass
def create_voucher(batch, quantity, date_, user_id, db):
@@ -4,74 +4,61 @@ from typing import List
import brewman.schemas.role as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from fastapi import APIRouter, HTTPException, Security, status
from sqlalchemy import select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from sqlalchemy.sql.functions import count
from ...core.security import get_current_active_user as get_user
from ...db.session import SessionLocal
from ...models.permission import Permission
from ...models.role import Role
from ...schemas.user import UserToken
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionFuture
from ..models.permission import Permission
from ..models.role import Role
from ..models.role_permission import role_permission
from ..models.user_role import user_role
from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=schemas.Role)
def save(
data: schemas.RoleIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.Role:
try:
item = Role(data.name)
db.add(item)
add_permissions(item, data.permissions, db)
db.commit()
return role_info(item, db)
with SessionFuture() as db:
item = Role(data.name)
db.add(item)
add_permissions(item, data.permissions, db)
db.commit()
return role_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
@router.put("/{id_}", response_model=schemas.Role)
def update(
def update_route(
id_: uuid.UUID,
data: schemas.RoleIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.Role:
try:
item: Role = db.query(Role).filter(Role.id == id_).first()
item.name = data.name
add_permissions(item, data.permissions, db)
db.commit()
return role_info(item, db)
with SessionFuture() as db:
item: Role = db.execute(select(Role).where(Role.id == id_)).scalar_one()
item.name = data.name
add_permissions(item, data.permissions, db)
db.commit()
return role_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def add_permissions(role: Role, permissions: List[schemas.PermissionItem], db: Session) -> None:
@@ -79,70 +66,65 @@ def add_permissions(role: Role, permissions: List[schemas.PermissionItem], db: S
gp = [p for p in role.permissions if p.id == permission.id_]
gp = None if len(gp) == 0 else gp[0]
if permission.enabled and gp is None:
role.permissions.append(db.query(Permission).filter(Permission.id == permission.id_).one())
role.permissions.append(db.execute(select(Permission).where(Permission.id == permission.id_)).scalar_one())
elif not permission.enabled and gp:
role.permissions.remove(gp)
@router.delete("/{id_}", response_model=schemas.RoleBlank)
def delete(
def delete_route(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.RoleBlank:
try:
item: Role = db.query(Role).filter(Role.id == id_).first()
if item is None:
with SessionFuture() as db:
if db.execute(select(count(user_role.c.id)).where(user_role.c.role_id == id_)).scalar_one() > 0:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Role not found",
detail="This Role has been assigned to users and cannot be deleted.",
)
else:
if db.execute(select(count(role_permission.c.id)).where(role_permission.c.role_id == id_)).scalar_one() > 0:
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED,
detail="Role deletion not implemented",
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="This Role has permissions and cannot be deleted.",
)
except Exception:
db.rollback()
raise
# item: Role = db.execute(select(Role).where(Role.id == id_)).scalar_one()
item: Role = db.execute(select(Role).where(Role.id == id_)).scalar_one()
db.delete(item)
db.commit()
return role_blank(db)
@router.get("", response_model=schemas.RoleBlank)
def show_blank(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.RoleBlank:
return role_blank(db)
with SessionFuture() as db:
return role_blank(db)
@router.get("/list", response_model=List[schemas.RoleList])
async def show_list(
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> List[schemas.RoleList]:
return [
schemas.RoleList(
id=item.id,
name=item.name,
permissions=[p.name for p in sorted(item.permissions, key=lambda p: p.name)],
)
for item in db.query(Role).order_by(Role.name).all()
]
with SessionFuture() as db:
return [
schemas.RoleList(
id=item.id,
name=item.name,
permissions=[p.name for p in sorted(item.permissions, key=lambda p: p.name)],
)
for item in db.execute(select(Role).order_by(Role.name)).scalars().all()
]
@router.get("/{id_}", response_model=schemas.Role)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.Role:
item: Role = db.query(Role).filter(Role.id == id_).first()
if item is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Role not found",
)
return role_info(item, db)
with SessionFuture() as db:
item: Role = db.execute(select(Role).where(Role.id == id_)).scalar_one()
return role_info(item, db)
def role_info(item: Role, db: Session) -> schemas.Role:
@@ -155,7 +137,7 @@ def role_info(item: Role, db: Session) -> schemas.Role:
name=p.name,
enabled=True if p in item.permissions else False,
)
for p in db.query(Permission).order_by(Permission.name).all()
for p in db.execute(select(Permission).order_by(Permission.name)).scalars().all()
],
)
@@ -165,6 +147,6 @@ def role_blank(db: Session) -> schemas.RoleBlank:
name="",
permissions=[
schemas.PermissionItem(id=p.id, name=p.name, enabled=False)
for p in db.query(Permission).order_by(Permission.name).all()
for p in db.execute(select(Permission).order_by(Permission.name)).scalars().all()
],
)
+206
View File
@@ -0,0 +1,206 @@
import uuid
from typing import List
import brewman.schemas.user as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import delete, select, update
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionFuture
from ..models.attendance import Attendance
from ..models.login_history import LoginHistory
from ..models.role import Role
from ..models.user import User
from ..models.user_role import user_role
from ..models.voucher import Voucher
from ..schemas.user import UserToken
router = APIRouter()
@router.post("", response_model=schemas.User)
def save(
data: schemas.UserIn,
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.User:
try:
with SessionFuture() as db:
item = User(name=data.name, password=data.password, locked_out=data.locked_out)
db.add(item)
add_roles(item, data.roles, db)
db.commit()
return user_info(item, db, user)
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
@router.get("/me", response_model=schemas.User)
def show_me(
user: UserToken = Depends(get_user),
) -> schemas.User:
with SessionFuture() as db:
item = db.execute(select(User).where(User.id == user.id_)).scalar_one()
return user_info(item, db, user)
@router.put("/me", response_model=schemas.User)
def update_me(
data: schemas.UserIn,
user: UserToken = Depends(get_user),
) -> schemas.User:
try:
with SessionFuture() as db:
item: User = db.execute(select(User).where(User.id == user.id_)).scalar_one()
if "users" in user.permissions:
item.name = data.name
item.locked_out = data.locked_out
add_roles(item, data.roles, db)
if data.password and item.password != data.password:
item.password = data.password
db.commit()
return user_info(item, db, user)
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
@router.put("/{id_}", response_model=schemas.User)
def update_route(
id_: uuid.UUID,
data: schemas.UserIn,
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.User:
try:
with SessionFuture() as db:
item: User = db.execute(select(User).where(User.id == id_)).scalar_one()
item.name = data.name
if data.password and item.password != data.password:
item.password = data.password
item.locked_out = data.locked_out
add_roles(item, data.roles, db)
db.commit()
return user_info(item, db, user)
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
def add_roles(user: User, roles: List[schemas.RoleItem], db: Session) -> None:
for role in roles:
ug = [g for g in user.roles if g.id == role.id_]
ug = None if len(ug) == 0 else ug[0]
if role.enabled and ug is None:
user.roles.append(db.execute(select(Role).where(Role.id == role.id_)).one())
elif not role.enabled and ug:
user.roles.remove(ug)
@router.delete("/{id_}")
def delete_route(
id_: uuid.UUID,
user: UserToken = Security(get_user, scopes=["users"]),
):
if id_ == user.id_:
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED,
detail="You cannot delete yourself",
)
with SessionFuture() as db:
item: User = db.execute(select(User).where(User.id == id_)).scalar_one()
admin: User = db.execute(select(User).where(User.name.ilike("admin"))).scalar_one()
db.execute(update(Voucher).where(Voucher.user_id == item.id).values(user_id=admin.id))
db.execute(update(Attendance).where(Attendance.user_id == item.id).values(user_id=admin.id))
db.execute(delete(LoginHistory).where(LoginHistory.user_id == item.id))
db.execute(delete(user_role).where(user_role.c.user_id == item.id))
db.execute(delete(User).where(User.id == item.id))
db.commit()
@router.get("", response_model=schemas.UserBlank)
def show_blank(
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.UserBlank:
with SessionFuture() as db:
return user_blank(db)
@router.get("/list", response_model=List[schemas.UserList])
async def show_list(
user: UserToken = Security(get_user, scopes=["users"]),
) -> List[schemas.UserList]:
with SessionFuture() as db:
return [
schemas.UserList(
id=item.id,
name=item.name,
lockedOut=item.locked_out,
roles=[p.name for p in sorted(item.roles, key=lambda p: p.name)],
lastDevice=item.login_history[0].client.name if len(item.login_history) else "None",
lastDate=item.login_history[0].date if len(item.login_history) else None,
)
for item in db.execute(select(User).order_by(User.name)).scalars().all()
]
@router.get("/active")
async def show_active(user: UserToken = Depends(get_user)):
with SessionFuture() as db:
return [
{"name": name}
for name in db.execute(select(User.name).where(User.locked_out == False).order_by(User.name)) # noqa: E712
.scalars()
.all()
]
@router.get("/{id_}", response_model=schemas.User)
def show_id(
id_: uuid.UUID,
user: UserToken = Security(get_user, scopes=["users"]),
) -> schemas.User:
with SessionFuture() as db:
item = db.execute(select(User).where(User.id == id_)).scalar_one()
return user_info(item, db, user)
def user_info(item: User, db: Session, user: UserToken) -> schemas.User:
return schemas.User(
id=item.id,
name=item.name,
password="",
lockedOut=item.locked_out,
roles=[
schemas.RoleItem(
id=r.id,
name=r.name,
enabled=True if r in item.roles else False,
)
for r in db.execute(select(Role).order_by(Role.name)).scalars().all()
]
if "advanced-delete" in user.permissions
else [],
)
def user_blank(db: Session) -> schemas.UserBlank:
return schemas.UserBlank(
name="",
password="",
lockedOut=False,
roles=[
schemas.RoleItem(id=r.id, name=r.name, enabled=False)
for r in db.execute(select(Role).order_by(Role.name)).scalars().all()
],
)
+90 -95
View File
@@ -6,14 +6,14 @@ from typing import Optional
import brewman.schemas.voucher as output
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import and_, func, or_
from fastapi import APIRouter, HTTPException, Security, status
from sqlalchemy import and_, func, or_, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_first_day
from ..db.session import SessionLocal
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.account_base import AccountBase
from ..models.attendance import Attendance
@@ -32,37 +32,24 @@ from ..schemas.user import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("/post-voucher/{id_}", response_model=output.Voucher)
def post_voucher(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["post-vouchers"]),
):
try:
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
check_voucher_lock_info(voucher.date, voucher.date, db)
voucher.posted = True
voucher.poster_id = user.id_
db.commit()
return voucher_info(voucher, db)
with SessionFuture() as db:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
check_voucher_lock_info(voucher.date, voucher.date, db)
voucher.posted = True
voucher.poster_id = user.id_
db.commit()
return voucher_info(voucher, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def check_delete_permissions(voucher: Voucher, user: UserToken):
@@ -89,66 +76,63 @@ def check_delete_permissions(voucher: Voucher, user: UserToken):
@router.delete("/delete/{id_}")
def delete_voucher(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: UserToken = Security(get_user),
):
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
images = db.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
check_delete_permissions(voucher, user)
check_voucher_lock_info(None, voucher.date, db)
json_voucher = voucher_info(voucher, db)
batches_to_delete = []
if voucher.type == VoucherType.by_name("Issue").id:
for item in voucher.journals:
if item.debit == 1:
destination = item.cost_centre_id
with SessionFuture() as db:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
images = db.execute(select(DbImage).where(DbImage.resource_id == voucher.id)).scalars().all()
check_delete_permissions(voucher, user)
check_voucher_lock_info(None, voucher.date, db)
json_voucher = voucher_info(voucher, db)
batches_to_delete = []
if voucher.type == VoucherType.by_name("Issue").id:
for item in voucher.journals:
if item.debit == 1:
destination = item.cost_centre_id
else:
source = item.cost_centre_id
if source == CostCentre.cost_centre_purchase():
batch_consumed = True
elif destination == CostCentre.cost_centre_purchase():
batch_consumed = False
else:
source = item.cost_centre_id
if source == CostCentre.cost_centre_purchase():
batch_consumed = True
elif destination == CostCentre.cost_centre_purchase():
batch_consumed = False
else:
batch_consumed = None
batch_consumed = None
if batch_consumed is None:
pass
elif batch_consumed:
if batch_consumed is None:
pass
elif batch_consumed:
for item in voucher.inventories:
item.batch.quantity_remaining += item.quantity
else:
for item in voucher.inventories:
if item.batch.quantity_remaining < item.quantity:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"Only {item.batch.quantity_remaining} of {item.product.name} remaining.\n"
f"So it cannot be deleted",
)
item.batch.quantity_remaining -= item.quantity
elif voucher.type == VoucherType.by_name("Purchase").id:
for item in voucher.inventories:
item.batch.quantity_remaining += item.quantity
else:
for item in voucher.inventories:
if item.batch.quantity_remaining < item.quantity:
uses = db.execute(
select(func.count(Inventory.id)).where(Inventory.batch_id == item.batch.id, Inventory.id != item.id)
).scalar()
if uses > 0:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"Only {item.batch.quantity_remaining} of {item.product.name} remaining.\n"
f"So it cannot be deleted",
detail=f"{item.product.name} has been issued and cannot be deleted",
)
item.batch.quantity_remaining -= item.quantity
elif voucher.type == VoucherType.by_name("Purchase").id:
for item in voucher.inventories:
uses = (
db.query(func.count(Inventory.id))
.filter(Inventory.batch_id == item.batch.id)
.filter(Inventory.id != item.id)
.scalar()
)
if uses > 0:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=f"{item.product.name} has been issued and cannot be deleted",
)
batches_to_delete.append(item.batch)
elif voucher.type == VoucherType.by_name("Purchase Return").id:
for item in voucher.inventories:
item.batch.quantity_remaining += item.quantity
for b in batches_to_delete:
db.delete(b)
db.delete(voucher)
for image in images:
db.delete(image)
db.commit()
return blank_voucher(info=json_voucher, db=db)
batches_to_delete.append(item.batch)
elif voucher.type == VoucherType.by_name("Purchase Return").id:
for item in voucher.inventories:
item.batch.quantity_remaining += item.quantity
for b in batches_to_delete:
db.delete(b)
db.delete(voucher)
for image in images:
db.delete(image)
db.commit()
return blank_voucher(info=json_voucher, db=db)
def voucher_info(voucher, db):
@@ -215,7 +199,7 @@ def voucher_info(voucher, db):
}
)
for item in voucher.incentives:
employee = db.query(Employee).filter(Employee.id == item.journal.account_id).first()
employee = db.execute(select(Employee).where(Employee.id == item.journal.account_id)).scalar_one()
json_voucher["incentives"].append(
{
"employeeId": item.journal.account_id,
@@ -261,7 +245,7 @@ def voucher_info(voucher, db):
},
}
)
images = db.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
images = db.execute(select(DbImage).where(DbImage.resource_id == voucher.id)).scalars().all()
for image in images:
json_voucher["files"].append(
{
@@ -300,7 +284,11 @@ def blank_voucher(info, db):
elif type_ == "Payment":
account = None
if info and "account" in info and info["account"]:
account = db.query(AccountBase).filter(AccountBase.id == uuid.UUID(info["account"])).first()
account = (
db.execute(select(AccountBase).where(AccountBase.id == uuid.UUID(info["account"])))
.scalars()
.one_or_none()
)
if account is not None:
account = {"id": account.id, "name": account.name}
else:
@@ -309,7 +297,11 @@ def blank_voucher(info, db):
elif type_ == "Receipt":
account = None
if info and "account" in info and info["account"]:
account = db.query(AccountBase).filter(AccountBase.id == uuid.UUID(info["account"])).first()
account = (
db.execute(select(AccountBase).where(AccountBase.id == uuid.UUID(info["account"])))
.scalars()
.one_or_none()
)
if account is not None:
account = {"id": account.id, "name": account.name}
else:
@@ -348,21 +340,25 @@ def incentive_employees(date_, db: Session):
finish_date = date_
details = []
employees = (
db.query(Employee)
.filter(Employee.joining_date <= finish_date)
.filter(or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id)
.order_by(Employee.designation)
.order_by(Employee.name)
db.execute(
select(Employee)
.where(Employee.joining_date <= finish_date, or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id, Employee.designation, Employee.name)
)
.scalars()
.all()
)
for employee in employees:
att = (
db.query(Attendance)
.filter(Attendance.employee_id == employee.id)
.filter(Attendance.date >= start_date)
.filter(Attendance.date <= finish_date)
.filter(Attendance.is_valid == True) # noqa: E712
db.execute(
select(Attendance).where(
Attendance.employee_id == employee.id,
Attendance.date >= start_date,
Attendance.date <= finish_date,
Attendance.is_valid == True, # noqa: E712
)
)
.scalars()
.all()
)
att = sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att))
@@ -377,10 +373,10 @@ def incentive_employees(date_, db: Session):
}
)
amount = (
db.query(func.sum(Journal.amount * Journal.debit))
amount = db.execute(
select(func.sum(Journal.amount * Journal.debit))
.join(Journal.voucher)
.filter(
.where(
Journal.account_id == Account.incentive_id(),
Voucher.type != VoucherType.by_name("Issue").id,
or_(
@@ -391,8 +387,7 @@ def incentive_employees(date_, db: Session):
),
),
)
.scalar()
)
).scalar()
amount = 0 if amount is None else amount * Decimal(0.9) * -1
return details, amount
+3 -2
View File
@@ -2,10 +2,11 @@ import uuid
from typing import Optional
from brewman.schemas import to_camel
from brewman.schemas.cost_centre import CostCentreLink
from pydantic import BaseModel, Field
from . import to_camel
from .cost_centre import CostCentreLink
class AccountLink(BaseModel):
id_: uuid.UUID = Field(...)
+2 -1
View File
@@ -2,10 +2,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class BalanceSheetItem(BaseModel):
name: Optional[str]
+2 -1
View File
@@ -2,10 +2,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from pydantic import Field, validator
from pydantic.main import BaseModel
from . import to_camel
class CashFlowItem(BaseModel):
name: str
+2 -1
View File
@@ -3,9 +3,10 @@ import uuid
from datetime import datetime
from typing import Optional
from brewman.schemas import to_camel
from pydantic.main import BaseModel
from . import to_camel
class ClientIn(BaseModel):
name: str
+2 -1
View File
@@ -2,10 +2,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class ClosingStockItem(BaseModel):
product: str
+2 -1
View File
@@ -2,9 +2,10 @@ import uuid
from typing import Optional
from brewman.schemas import to_camel
from pydantic import BaseModel, Field
from . import to_camel
class CostCentreIn(BaseModel):
name: str = Field(..., min_length=1)
+2 -1
View File
@@ -4,10 +4,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class DaybookItem(BaseModel):
id_: Optional[uuid.UUID]
+4 -3
View File
@@ -4,11 +4,12 @@ from datetime import date, datetime
from decimal import Decimal
from typing import Optional
from brewman.schemas import to_camel
from brewman.schemas.account import AccountBase
from brewman.schemas.cost_centre import CostCentreLink
from pydantic import BaseModel, Field, validator
from . import to_camel
from .account import AccountBase
from .cost_centre import CostCentreLink
class EmployeeIn(AccountBase):
designation: str
+5 -10
View File
@@ -5,19 +5,14 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from brewman.schemas.account import AccountLink
from brewman.schemas.cost_centre import CostCentreLink
from brewman.schemas.voucher import (
EmployeeBenefit,
Incentive,
Inventory,
Journal,
VoucherIn,
)
from fastapi import Form
from pydantic import BaseModel, Field, validator
from . import to_camel
from .account import AccountLink
from .cost_centre import CostCentreLink
from .voucher import EmployeeBenefit, Incentive, Inventory, Journal, VoucherIn
class JournalIn(VoucherIn):
journals: List[Journal]
+3 -2
View File
@@ -4,11 +4,12 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from brewman.schemas.account import AccountLink
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
from .account import AccountLink
class LedgerItem(BaseModel):
id_: Optional[uuid.UUID]
+2 -1
View File
@@ -2,9 +2,10 @@ import uuid
from datetime import datetime
from brewman.schemas import to_camel
from pydantic import BaseModel
from . import to_camel
class LoginHistory(BaseModel):
id_: uuid.UUID
+2 -1
View File
@@ -2,10 +2,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class NetTransactionsItem(BaseModel):
type_: str
+3 -2
View File
@@ -3,10 +3,11 @@ import uuid
from decimal import Decimal
from typing import Optional
from brewman.schemas import to_camel
from brewman.schemas.product_group import ProductGroupLink
from pydantic import BaseModel, Field
from . import to_camel
from .product_group import ProductGroupLink
class ProductLink(BaseModel):
id_: uuid.UUID = Field(...)
+2 -1
View File
@@ -2,9 +2,10 @@ import uuid
from typing import Optional
from brewman.schemas import to_camel
from pydantic import BaseModel, Field
from . import to_camel
class ProductGroupIn(BaseModel):
name: str = Field(..., min_length=1)
+3 -2
View File
@@ -4,11 +4,12 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from brewman.schemas.product import ProductLink
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
from .product import ProductLink
class ProductLedgerItem(BaseModel):
id_: Optional[uuid.UUID]
+2 -1
View File
@@ -2,10 +2,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class ProfitLossItem(BaseModel):
group: Optional[str]
+2 -1
View File
@@ -2,10 +2,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class PurchaseEntriesItem(BaseModel):
date_: date
+2 -1
View File
@@ -2,10 +2,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class PurchasesItem(BaseModel):
name: str
+2 -1
View File
@@ -4,10 +4,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class RawMaterialCostItem(BaseModel):
name: Optional[str]
+55 -13
View File
@@ -2,29 +2,71 @@ import uuid
from datetime import date
from decimal import Decimal
from typing import List, Optional
from pydantic import BaseModel
from pydantic import BaseModel, Field
from . import to_camel
class Recipe(BaseModel):
id_: uuid.UUID
product_id: uuid.UUID
class ProductLink(BaseModel):
id_: uuid.UUID = Field(...)
name: Optional[str]
units: Optional[str]
sale_price: Optional[Decimal]
is_sold: Optional[bool]
fraction_units: Optional[str]
fraction: Optional[Decimal]
product_yield: Optional[Decimal]
class Config:
alias_generator = to_camel
class RecipeItem(BaseModel):
id_: Optional[uuid.UUID]
product: ProductLink
quantity: int
price: int
class Config:
fields = {"id_": "id"}
class RecipeIn(BaseModel):
product: ProductLink
quantity: Decimal
cost_price: Decimal
sale_price: Decimal
notes: str
valid_from: date
valid_till: date
valid_from: Optional[date]
valid_till: Optional[date]
effective_from: date
effective_till: date
effective_from: Optional[date]
effective_till: Optional[date]
items: List[RecipeItem]
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {date: lambda v: v.strftime("%d-%b-%Y") if v is not None else None}
class RecipeItem(BaseModel):
class Recipe(RecipeIn):
id_: uuid.UUID
recipe_id: uuid.UUID
product_id: uuid.UUID
quantity: int
price: int
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")}
class RecipeBlank(RecipeIn):
product: Optional[ProductLink]
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
+2 -1
View File
@@ -2,10 +2,11 @@ import uuid
from typing import List
from brewman.schemas.permission import PermissionItem
from pydantic import Field
from pydantic.main import BaseModel
from .permission import PermissionItem
class RoleIn(BaseModel):
name: str = Field(..., min_length=1)
+2 -1
View File
@@ -2,9 +2,10 @@ from datetime import date, datetime
from decimal import Decimal
from typing import Optional
from brewman.schemas import to_camel
from pydantic import BaseModel, validator
from . import to_camel
class LockInformation(BaseModel):
lock_older: bool
+2 -1
View File
@@ -4,10 +4,11 @@ from datetime import date, datetime
from decimal import Decimal
from typing import List
from brewman.schemas import to_camel
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class StockMovementItem(BaseModel):
id_: uuid.UUID
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff. Show more