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:
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 ###
|
||||
@@ -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
@@ -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
|
||||
@@ -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)
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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.
@@ -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,
|
||||
}
|
||||
@@ -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()],
|
||||
)
|
||||
@@ -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_
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
],
|
||||
)
|
||||
@@ -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()
|
||||
],
|
||||
)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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,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
|
||||
|
||||
@@ -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,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,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)
|
||||
|
||||
@@ -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,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,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]
|
||||
|
||||
@@ -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,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,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,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,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)
|
||||
|
||||
@@ -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,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,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,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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,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,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
|
||||
|
||||
@@ -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
Reference in new issue
Block a user