Fix: Deleting voucher fucked up due to cascading changes was not
This commit is contained in:
parent
3d5d02a416
commit
952f030e8d
brewman
alembic/versions
brewman
overlord/src/app
41
brewman/alembic/versions/b13dbf97bd21_recipe_yield.py
Normal file
41
brewman/alembic/versions/b13dbf97bd21_recipe_yield.py
Normal file
@ -0,0 +1,41 @@
|
||||
"""recipe yield
|
||||
|
||||
Revision ID: b13dbf97bd21
|
||||
Revises: 0e6c5953a63f
|
||||
Create Date: 2021-11-08 11:51:07.061978
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "b13dbf97bd21"
|
||||
down_revision = "0e6c5953a63f"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column("recipes", "quantity", new_column_name="recipe_yield")
|
||||
op.alter_column(
|
||||
"recipe_items",
|
||||
"quantity",
|
||||
existing_type=sa.INTEGER(),
|
||||
type_=sa.Numeric(precision=15, scale=2),
|
||||
existing_nullable=False,
|
||||
)
|
||||
op.alter_column(
|
||||
"recipe_items",
|
||||
"price",
|
||||
existing_type=sa.INTEGER(),
|
||||
type_=sa.Numeric(precision=15, scale=5),
|
||||
existing_nullable=False,
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
@ -58,23 +58,6 @@ class Batch(Base):
|
||||
def amount(self):
|
||||
return self.quantity_remaining * self.rate * (1 + self.tax) * (1 - self.discount)
|
||||
|
||||
@classmethod
|
||||
def list(cls, q: str, include_nil: bool, date_: Optional[date], db: Session):
|
||||
query = (
|
||||
select(cls)
|
||||
.join(cls.sku)
|
||||
.join(StockKeepingUnit.product)
|
||||
.options(contains_eager(cls.sku).contains_eager(StockKeepingUnit.product))
|
||||
)
|
||||
if not include_nil:
|
||||
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.where(Product.name.ilike(f"%{item}%"))
|
||||
return db.execute(query.order_by(Product.name).order_by(cls.name)).scalars().all()
|
||||
|
||||
@classmethod
|
||||
def suspense(cls):
|
||||
return uuid.UUID("a955790e-93cf-493c-a816-c7d92b127383")
|
||||
|
@ -1,33 +1,44 @@
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from sqlalchemy import Column, Date, ForeignKey, Numeric, Unicode
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.orm import Mapped, relationship
|
||||
|
||||
from .meta import Base
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# if the target of the relationship is in another module
|
||||
# that cannot normally be imported at runtime
|
||||
from .recipe_item import RecipeItem
|
||||
from .stock_keeping_unit import StockKeepingUnit
|
||||
|
||||
|
||||
class Recipe(Base):
|
||||
__tablename__ = "recipes"
|
||||
|
||||
id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
sku_id = Column("sku_id", UUID(as_uuid=True), ForeignKey("stock_keeping_units.id"), nullable=False)
|
||||
id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
sku_id: uuid.UUID = Column("sku_id", UUID(as_uuid=True), ForeignKey("stock_keeping_units.id"), nullable=False)
|
||||
|
||||
quantity = Column("quantity", Numeric(precision=15, scale=2), nullable=False)
|
||||
cost_price = Column("cost_price", Numeric(precision=15, scale=2), nullable=False)
|
||||
sale_price = Column("sale_price", Numeric(precision=15, scale=2), nullable=False)
|
||||
notes = Column("notes", Unicode(255))
|
||||
recipe_yield: Decimal = Column("recipe_yield", Numeric(precision=15, scale=2), nullable=False)
|
||||
cost_price: Decimal = Column("cost_price", Numeric(precision=15, scale=2), nullable=False)
|
||||
sale_price: Decimal = Column("sale_price", Numeric(precision=15, scale=2), nullable=False)
|
||||
notes: str = Column("notes", Unicode(255))
|
||||
|
||||
valid_from = Column("valid_from", Date, nullable=True)
|
||||
valid_till = Column("valid_till", Date, nullable=True)
|
||||
valid_from: datetime.date = Column("valid_from", Date, nullable=True)
|
||||
valid_till: datetime.date = Column("valid_till", Date, nullable=True)
|
||||
|
||||
items = relationship("RecipeItem", back_populates="recipe")
|
||||
sku = relationship("StockKeepingUnit", back_populates="recipes")
|
||||
items: List["RecipeItem"] = relationship("RecipeItem", back_populates="recipe")
|
||||
sku: Mapped["StockKeepingUnit"] = relationship("StockKeepingUnit", back_populates="recipes")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sku_id=None,
|
||||
quantity=None,
|
||||
recipe_yield=None,
|
||||
cost_price=None,
|
||||
sale_price=None,
|
||||
valid_from=None,
|
||||
@ -36,7 +47,7 @@ class Recipe(Base):
|
||||
id_=None,
|
||||
):
|
||||
self.sku_id = sku_id
|
||||
self.quantity = quantity
|
||||
self.recipe_yield = recipe_yield
|
||||
self.cost_price = cost_price
|
||||
self.sale_price = sale_price
|
||||
self.valid_from = valid_from
|
||||
|
@ -1,24 +1,33 @@
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Integer, UniqueConstraint
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Integer, Numeric, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.orm import Mapped, relationship
|
||||
|
||||
from .meta import Base
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# if the target of the relationship is in another module
|
||||
# that cannot normally be imported at runtime
|
||||
from .product import Product
|
||||
from .recipe import Recipe
|
||||
|
||||
|
||||
class RecipeItem(Base):
|
||||
__tablename__ = "recipe_items"
|
||||
__table_args__ = (UniqueConstraint("recipe_id", "product_id"),)
|
||||
|
||||
id = Column("recipe_item_id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
recipe_id = Column("recipe_id", UUID(as_uuid=True), ForeignKey("recipes.id"), nullable=False)
|
||||
product_id = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False)
|
||||
quantity = Column("quantity", Integer, nullable=False)
|
||||
price = Column("price", Integer, nullable=False)
|
||||
id: uuid.UUID = Column("recipe_item_id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
recipe_id: uuid.UUID = Column("recipe_id", UUID(as_uuid=True), ForeignKey("recipes.id"), nullable=False)
|
||||
product_id: uuid.UUID = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False)
|
||||
quantity: int = Column("quantity", Numeric(precision=15, scale=2), nullable=False)
|
||||
price: int = Column("price", Numeric(precision=15, scale=5), nullable=False)
|
||||
|
||||
recipe = relationship("Recipe", back_populates="items")
|
||||
product = relationship("Product")
|
||||
recipe: Mapped["Recipe"] = relationship("Recipe", back_populates="items")
|
||||
product: Mapped["Product"] = relationship("Product")
|
||||
|
||||
def __init__(self, recipe_id=None, product_id=None, quantity=None, price=None, recipe=None, id_=None):
|
||||
self.recipe_id = recipe_id
|
||||
|
@ -40,11 +40,15 @@ class Voucher(Base):
|
||||
user: Mapped["User"] = relationship("User", primaryjoin="User.id==Voucher.user_id")
|
||||
poster: Mapped["User"] = relationship("User", primaryjoin="User.id==Voucher.poster_id")
|
||||
|
||||
journals: List["Journal"] = relationship("Journal", back_populates="voucher")
|
||||
journals: List["Journal"] = relationship("Journal", cascade="delete, delete-orphan", back_populates="voucher")
|
||||
|
||||
inventories: List["Inventory"] = relationship("Inventory", back_populates="voucher")
|
||||
employee_benefits: List["EmployeeBenefit"] = relationship("EmployeeBenefit", backref="voucher")
|
||||
incentives: List["Incentive"] = relationship("Incentive", backref="voucher")
|
||||
inventories: List["Inventory"] = relationship(
|
||||
"Inventory", cascade="delete, delete-orphan", back_populates="voucher"
|
||||
)
|
||||
employee_benefits: List["EmployeeBenefit"] = relationship(
|
||||
"EmployeeBenefit", cascade="delete, delete-orphan", backref="voucher"
|
||||
)
|
||||
incentives: List["Incentive"] = relationship("Incentive", cascade="delete, delete-orphan", backref="voucher")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -1,10 +1,14 @@
|
||||
import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import contains_eager
|
||||
|
||||
from ..core.security import get_current_active_user as get_user
|
||||
from ..db.session import SessionFuture
|
||||
from ..models.batch import Batch
|
||||
from ..models.product import Product
|
||||
from ..models.stock_keeping_unit import StockKeepingUnit
|
||||
from ..schemas.user import UserToken
|
||||
|
||||
|
||||
@ -14,13 +18,25 @@ router = APIRouter()
|
||||
@router.get("")
|
||||
def batch_term(
|
||||
q: str,
|
||||
d: str = None,
|
||||
d: str,
|
||||
current_user: UserToken = Depends(get_user),
|
||||
):
|
||||
date_ = None if not d else datetime.datetime.strptime(d, "%d-%b-%Y")
|
||||
date_ = datetime.datetime.strptime(d, "%d-%b-%Y")
|
||||
list_ = []
|
||||
with SessionFuture() as db:
|
||||
for item in Batch.list(q, include_nil=False, date_=date_, db=db):
|
||||
query = (
|
||||
select(Batch)
|
||||
.join(Batch.sku)
|
||||
.join(StockKeepingUnit.product)
|
||||
.where(Batch.quantity_remaining > 0, Batch.name <= date_)
|
||||
.options(contains_eager(Batch.sku).contains_eager(StockKeepingUnit.product))
|
||||
)
|
||||
if q is not None:
|
||||
for item in q.split():
|
||||
query = query.where(Product.name.ilike(f"%{item}%"))
|
||||
result = db.execute(query.order_by(Product.name).order_by(Batch.name)).scalars().all()
|
||||
|
||||
for item in result:
|
||||
text = (
|
||||
f"{item.sku.product.name} ({item.sku.units}) {item.quantity_remaining:.2f}@"
|
||||
f"{item.rate:.2f} from {item.name.strftime('%d-%b-%Y')}"
|
||||
|
@ -22,6 +22,7 @@ from ..models.journal import Journal
|
||||
from ..models.validations import check_journals_are_valid
|
||||
from ..models.voucher import Voucher
|
||||
from ..models.voucher_type import VoucherType
|
||||
from ..schemas.blank_voucher_info import BlankVoucherInfo
|
||||
from ..schemas.employee_benefit import EmployeeBenefit as EmployeeBenefitSchema
|
||||
from ..schemas.user import UserToken
|
||||
from . import get_lock_info
|
||||
@ -255,7 +256,7 @@ def show_blank(
|
||||
request: Request,
|
||||
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
|
||||
):
|
||||
additional_info = {"date": get_date(request.session), "type": VoucherType.EMPLOYEE_BENEFIT}
|
||||
additional_info = BlankVoucherInfo(date=get_date(request.session), type=VoucherType.EMPLOYEE_BENEFIT)
|
||||
with SessionFuture() as db:
|
||||
return blank_voucher(additional_info, db)
|
||||
|
||||
|
@ -25,6 +25,7 @@ from ..models.journal import Journal
|
||||
from ..models.validations import check_journals_are_valid
|
||||
from ..models.voucher import Voucher
|
||||
from ..models.voucher_type import VoucherType
|
||||
from ..schemas.blank_voucher_info import BlankVoucherInfo
|
||||
from ..schemas.incentive import Incentive as IncentiveSchema
|
||||
from ..schemas.user import UserToken
|
||||
from . import get_lock_info
|
||||
@ -208,7 +209,7 @@ def show_blank(
|
||||
d: str = None,
|
||||
user: UserToken = Security(get_user, scopes=["incentive"]),
|
||||
):
|
||||
additional_info = {"date": d or get_date(request.session), "type": VoucherType.INCENTIVE}
|
||||
additional_info = BlankVoucherInfo(date=d or get_date(request.session), type=VoucherType.INCENTIVE)
|
||||
with SessionFuture() as db:
|
||||
return blank_voucher(additional_info, db)
|
||||
|
||||
|
@ -25,6 +25,8 @@ from ..models.stock_keeping_unit import StockKeepingUnit
|
||||
from ..models.validations import check_inventories_are_valid, check_journals_are_valid
|
||||
from ..models.voucher import Voucher
|
||||
from ..models.voucher_type import VoucherType
|
||||
from ..schemas.blank_voucher_info import BlankVoucherInfo
|
||||
from ..schemas.cost_centre import CostCentreLink
|
||||
from ..schemas.inventory import Inventory as InventorySchema
|
||||
from ..schemas.user import UserToken
|
||||
from . import get_lock_info
|
||||
@ -381,15 +383,15 @@ def get_id(
|
||||
def show_blank(
|
||||
request: Request,
|
||||
date: str = None,
|
||||
source: str = None,
|
||||
destination: str = None,
|
||||
source: uuid.UUID = None,
|
||||
destination: uuid.UUID = None,
|
||||
user: UserToken = Security(get_user, scopes=["issue"]),
|
||||
) -> output.Voucher:
|
||||
date_ = date or get_date(request.session)
|
||||
additional_info = {"date": date_, "type": VoucherType.ISSUE}
|
||||
additional_info = BlankVoucherInfo(date=date_, type=VoucherType.ISSUE)
|
||||
if source:
|
||||
additional_info["source"] = source
|
||||
additional_info.source = CostCentreLink(id=source, name="")
|
||||
if destination:
|
||||
additional_info["destination"] = destination
|
||||
additional_info.destination = CostCentreLink(id=destination, name="")
|
||||
with SessionFuture() as db:
|
||||
return blank_voucher(additional_info, db)
|
||||
|
@ -19,6 +19,8 @@ from ..models.journal import Journal
|
||||
from ..models.validations import check_journals_are_valid
|
||||
from ..models.voucher import Voucher
|
||||
from ..models.voucher_type import VoucherType
|
||||
from ..schemas.account import AccountLink
|
||||
from ..schemas.blank_voucher_info import BlankVoucherInfo
|
||||
from ..schemas.user import UserToken
|
||||
from . import get_lock_info
|
||||
from .db_image import save_files, update_files
|
||||
@ -188,7 +190,7 @@ def get_id(
|
||||
@router.get("", response_model=output.Voucher)
|
||||
def show_blank(
|
||||
request: Request,
|
||||
a: str = None,
|
||||
a: uuid.UUID = None,
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
) -> output.Voucher:
|
||||
if request.scope.get("path") == "/api/payment":
|
||||
@ -198,8 +200,8 @@ def show_blank(
|
||||
else:
|
||||
type_ = VoucherType.JOURNAL
|
||||
|
||||
additional_info = {"date": get_date(request.session), "type": type_}
|
||||
additional_info = BlankVoucherInfo(date=get_date(request.session), type=type_)
|
||||
if a:
|
||||
additional_info["account"] = a
|
||||
additional_info.ac = AccountLink(id=a)
|
||||
with SessionFuture() as db:
|
||||
return blank_voucher(additional_info, db)
|
||||
|
@ -216,8 +216,6 @@ async def show_term_sku(
|
||||
fractionUnits=item.fraction_units,
|
||||
costPrice=sku.cost_price if rc_price is None else rc_price,
|
||||
salePrice=sku.sale_price,
|
||||
fraction=sku.fraction,
|
||||
productYield=sku.product_yield,
|
||||
isRateContracted=False if rc_price is None else True,
|
||||
)
|
||||
)
|
||||
@ -252,8 +250,6 @@ async def show_term_product(
|
||||
fractionUnits=item.fraction_units,
|
||||
costPrice=0,
|
||||
salePrice=0,
|
||||
fraction=1,
|
||||
productYield=1,
|
||||
isRateContracted=False,
|
||||
)
|
||||
)
|
||||
|
@ -26,6 +26,7 @@ from ..models.stock_keeping_unit import StockKeepingUnit
|
||||
from ..models.validations import check_inventories_are_valid, check_journals_are_valid
|
||||
from ..models.voucher import Voucher
|
||||
from ..models.voucher_type import VoucherType
|
||||
from ..schemas.blank_voucher_info import BlankVoucherInfo
|
||||
from ..schemas.inventory import Inventory as InventorySchema
|
||||
from ..schemas.user import UserToken
|
||||
from . import get_lock_info
|
||||
@ -363,7 +364,7 @@ def show_blank(
|
||||
request: Request,
|
||||
user: UserToken = Security(get_user, scopes=["purchase"]),
|
||||
) -> output.Voucher:
|
||||
additional_info = {"date": get_date(request.session), "type": VoucherType.PURCHASE}
|
||||
additional_info = BlankVoucherInfo(date=get_date(request.session), type=VoucherType.PURCHASE)
|
||||
with SessionFuture() as db:
|
||||
return blank_voucher(additional_info, db)
|
||||
|
||||
|
@ -23,6 +23,7 @@ from ..models.stock_keeping_unit import StockKeepingUnit
|
||||
from ..models.validations import check_inventories_are_valid, check_journals_are_valid
|
||||
from ..models.voucher import Voucher
|
||||
from ..models.voucher_type import VoucherType
|
||||
from ..schemas.blank_voucher_info import BlankVoucherInfo
|
||||
from ..schemas.inventory import Inventory as InventorySchema
|
||||
from ..schemas.user import UserToken
|
||||
from . import get_lock_info
|
||||
@ -329,6 +330,6 @@ def show_blank(
|
||||
request: Request,
|
||||
user: UserToken = Security(get_user, scopes=["purchase-return"]),
|
||||
) -> output.Voucher:
|
||||
additional_info = {"date": get_date(request.session), "type": VoucherType.PURCHASE_RETURN}
|
||||
additional_info = BlankVoucherInfo(date=get_date(request.session), type=VoucherType.PURCHASE_RETURN)
|
||||
with SessionFuture() as db:
|
||||
return blank_voucher(additional_info, db)
|
||||
|
@ -1,13 +1,13 @@
|
||||
import uuid
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import brewman.schemas.voucher as output
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Security, status
|
||||
from sqlalchemy import and_, distinct, func, or_, select
|
||||
from sqlalchemy import and_, delete, distinct, func, or_, select
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@ -21,11 +21,14 @@ from ..models.attendance_type import AttendanceType
|
||||
from ..models.cost_centre import CostCentre
|
||||
from ..models.db_image import DbImage
|
||||
from ..models.employee import Employee
|
||||
from ..models.employee_benefit import EmployeeBenefit
|
||||
from ..models.incentive import Incentive
|
||||
from ..models.inventory import Inventory
|
||||
from ..models.journal import Journal
|
||||
from ..models.voucher import Voucher
|
||||
from ..models.voucher_type import VoucherType
|
||||
from ..routers import get_lock_info
|
||||
from ..schemas.blank_voucher_info import BlankVoucherInfo
|
||||
from ..schemas.user import UserToken
|
||||
|
||||
|
||||
@ -163,11 +166,19 @@ def delete_voucher(
|
||||
for image in images:
|
||||
db.delete(image)
|
||||
db.commit()
|
||||
json_voucher.type_ = VoucherType[json_voucher.type_.replace(" ", "_").upper()]
|
||||
return blank_voucher(info=json_voucher, db=db)
|
||||
|
||||
return blank_voucher(
|
||||
info=BlankVoucherInfo(
|
||||
date=json_voucher.date_,
|
||||
type=VoucherType[json_voucher.type_],
|
||||
source=json_voucher.source,
|
||||
destination=json_voucher.destination,
|
||||
),
|
||||
db=db,
|
||||
)
|
||||
|
||||
|
||||
def voucher_info(voucher, db: Session) -> output.Voucher:
|
||||
def voucher_info(voucher: Voucher, db: Session) -> output.Voucher:
|
||||
json_voucher = output.Voucher(
|
||||
id=voucher.id,
|
||||
date=voucher.date,
|
||||
@ -200,75 +211,77 @@ def voucher_info(voucher, db: Session) -> output.Voucher:
|
||||
item = [j for j in voucher.journals if j.debit == 1][0]
|
||||
json_voucher.vendor = output.AccountLink(id=item.account.id, name=item.account.name)
|
||||
else:
|
||||
for item in voucher.journals:
|
||||
for journal in voucher.journals:
|
||||
json_voucher.journals.append(
|
||||
output.Journal(
|
||||
id=item.id,
|
||||
debit=item.debit,
|
||||
amount=item.amount,
|
||||
account=output.AccountLink(id=item.account.id, name=item.account.name),
|
||||
costCentre=output.CostCentreLink(id=item.cost_centre_id),
|
||||
id=journal.id,
|
||||
debit=journal.debit,
|
||||
amount=journal.amount,
|
||||
account=output.AccountLink(id=journal.account.id, name=journal.account.name),
|
||||
costCentre=output.CostCentreLink(id=journal.cost_centre_id),
|
||||
)
|
||||
)
|
||||
for item in voucher.employee_benefits:
|
||||
for benefit in voucher.employee_benefits:
|
||||
json_voucher.employee_benefits.append(
|
||||
output.EmployeeBenefit(
|
||||
grossSalary=item.gross_salary,
|
||||
daysWorked=item.days_worked,
|
||||
esiEmployee=item.esi_ee,
|
||||
pfEmployee=item.pf_ee,
|
||||
esiEmployer=item.esi_er,
|
||||
pfEmployer=item.pf_er,
|
||||
grossSalary=benefit.gross_salary,
|
||||
daysWorked=benefit.days_worked,
|
||||
esiEmployee=benefit.esi_ee,
|
||||
pfEmployee=benefit.pf_ee,
|
||||
esiEmployer=benefit.esi_er,
|
||||
pfEmployer=benefit.pf_er,
|
||||
employee=output.EmployeeLink(
|
||||
id=item.journal.account.id,
|
||||
name=item.journal.account.name,
|
||||
designation=item.journal.account.designation,
|
||||
id=benefit.journal.account.id,
|
||||
name=benefit.journal.account.name,
|
||||
designation=benefit.journal.account.designation,
|
||||
costCentre=output.CostCentreLink(
|
||||
id=item.journal.account.cost_centre.id,
|
||||
name=item.journal.account.cost_centre.name,
|
||||
id=benefit.journal.account.cost_centre.id,
|
||||
name=benefit.journal.account.cost_centre.name,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
for item in voucher.incentives:
|
||||
employee: Employee = db.execute(select(Employee).where(Employee.id == item.journal.account_id)).scalar_one()
|
||||
for incentive in voucher.incentives:
|
||||
employee: Employee = db.execute(
|
||||
select(Employee).where(Employee.id == incentive.journal.account_id)
|
||||
).scalar_one()
|
||||
json_voucher.incentives.append(
|
||||
output.Incentive(
|
||||
employeeId=item.journal.account_id,
|
||||
name=item.journal.account.name,
|
||||
employeeId=incentive.journal.account_id,
|
||||
name=incentive.journal.account.name,
|
||||
designation=employee.designation,
|
||||
department=item.journal.account.cost_centre.name,
|
||||
daysWorked=item.days_worked,
|
||||
points=item.points,
|
||||
department=incentive.journal.account.cost_centre.name,
|
||||
daysWorked=incentive.days_worked,
|
||||
points=incentive.points,
|
||||
)
|
||||
)
|
||||
if len(json_voucher.incentives) > 0:
|
||||
json_voucher.incentive = next(x.amount for x in voucher.journals if x.account_id == Account.incentive_id())
|
||||
for item in voucher.inventories:
|
||||
for inventory in voucher.inventories:
|
||||
text = (
|
||||
f"{item.batch.sku.product.name} ({item.batch.sku.units}) {item.batch.quantity_remaining:.2f}@"
|
||||
f"{item.batch.rate:.2f} from {item.batch.name.strftime('%d-%b-%Y')}"
|
||||
f"{inventory.batch.sku.product.name} ({inventory.batch.sku.units}) {inventory.batch.quantity_remaining:.2f}@"
|
||||
f"{inventory.batch.rate:.2f} from {inventory.batch.name.strftime('%d-%b-%Y')}"
|
||||
)
|
||||
json_voucher.inventories.append(
|
||||
output.Inventory(
|
||||
id=item.id,
|
||||
quantity=item.quantity,
|
||||
rate=item.rate,
|
||||
tax=item.tax,
|
||||
discount=item.discount,
|
||||
amount=item.amount,
|
||||
id=inventory.id,
|
||||
quantity=inventory.quantity,
|
||||
rate=inventory.rate,
|
||||
tax=inventory.tax,
|
||||
discount=inventory.discount,
|
||||
amount=inventory.amount,
|
||||
batch=output.Batch(
|
||||
id=item.batch.id,
|
||||
id=inventory.batch.id,
|
||||
name=text,
|
||||
quantityRemaining=item.batch.quantity_remaining,
|
||||
tax=item.batch.tax,
|
||||
discount=item.batch.discount,
|
||||
rate=item.batch.rate,
|
||||
quantityRemaining=inventory.batch.quantity_remaining,
|
||||
tax=inventory.batch.tax,
|
||||
discount=inventory.batch.discount,
|
||||
rate=inventory.batch.rate,
|
||||
sku=output.ProductLink(
|
||||
id=item.batch.sku.id,
|
||||
name=f"{item.batch.sku.product.name} ({item.batch.sku.units})"
|
||||
if item.batch.sku.units
|
||||
else item.batch.sku.product.name,
|
||||
id=inventory.batch.sku.id,
|
||||
name=f"{inventory.batch.sku.product.name} ({inventory.batch.sku.units})"
|
||||
if inventory.batch.sku.units
|
||||
else inventory.batch.sku.product.name,
|
||||
),
|
||||
),
|
||||
)
|
||||
@ -285,21 +298,10 @@ def voucher_info(voucher, db: Session) -> output.Voucher:
|
||||
return json_voucher
|
||||
|
||||
|
||||
def blank_voucher(info, db: Session) -> output.Voucher:
|
||||
if "type" not in info:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Voucher Type is null",
|
||||
)
|
||||
type_ = info["type"]
|
||||
if "date" not in info:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Date cannot be null",
|
||||
)
|
||||
def blank_voucher(info: BlankVoucherInfo, db: Session) -> output.Voucher:
|
||||
json_voucher = output.Voucher(
|
||||
type=type_.name,
|
||||
date=info["date"],
|
||||
type=info.type_.name,
|
||||
date=info.date_,
|
||||
isStarred=False,
|
||||
posted=False,
|
||||
narration="",
|
||||
@ -309,65 +311,56 @@ def blank_voucher(info, db: Session) -> output.Voucher:
|
||||
employeeBenefits=[],
|
||||
files=[],
|
||||
)
|
||||
if type_ == VoucherType.JOURNAL:
|
||||
if info.type_ == VoucherType.JOURNAL:
|
||||
pass
|
||||
elif type_ == VoucherType.PAYMENT:
|
||||
elif info.type_ == VoucherType.PAYMENT:
|
||||
account = None
|
||||
if info and "account" in info and info["account"]:
|
||||
account = (
|
||||
db.execute(select(AccountBase).where(AccountBase.id == uuid.UUID(info["account"])))
|
||||
.scalars()
|
||||
.one_or_none()
|
||||
)
|
||||
if info.account is not None:
|
||||
account = db.execute(select(AccountBase).where(AccountBase.id == info.account.id_)).scalars().one_or_none()
|
||||
if account is not None:
|
||||
j_account = output.AccountLink(id=account.id, name=account.name)
|
||||
else:
|
||||
j_account = output.AccountLink(id=AccountBase.cash_in_hand()["id"], name=AccountBase.cash_in_hand()["name"])
|
||||
json_voucher.journals.append(output.Journal(account=j_account, amount=0, debit=-1))
|
||||
elif type_ == VoucherType.RECEIPT:
|
||||
elif info.type_ == VoucherType.RECEIPT:
|
||||
account = None
|
||||
if info and "account" in info and info["account"]:
|
||||
account = (
|
||||
db.execute(select(AccountBase).where(AccountBase.id == uuid.UUID(info["account"])))
|
||||
.scalars()
|
||||
.one_or_none()
|
||||
)
|
||||
if info.account is not None:
|
||||
account = db.execute(select(AccountBase).where(AccountBase.id == info.account.id_)).scalars().one_or_none()
|
||||
if account is not None:
|
||||
j_account = output.AccountLink(id=account.id, name=account.name)
|
||||
else:
|
||||
j_account = output.AccountLink(id=AccountBase.cash_in_hand()["id"], name=AccountBase.cash_in_hand()["name"])
|
||||
json_voucher.journals.append(output.Journal(account=j_account, amount=0, debit=1))
|
||||
elif type_ == VoucherType.PURCHASE:
|
||||
elif info.type_ == VoucherType.PURCHASE:
|
||||
json_voucher.vendor = output.AccountLink(
|
||||
id=AccountBase.local_purchase()["id"], name=AccountBase.local_purchase()["name"]
|
||||
)
|
||||
elif type_ == VoucherType.PURCHASE_RETURN:
|
||||
elif info.type_ == VoucherType.PURCHASE_RETURN:
|
||||
json_voucher.vendor = output.AccountLink(
|
||||
id=AccountBase.local_purchase()["id"], name=AccountBase.local_purchase()["name"]
|
||||
)
|
||||
elif type_ == VoucherType.ISSUE:
|
||||
if "source" in info:
|
||||
json_voucher.source = output.CostCentreLink(id=info["source"])
|
||||
elif info.type_ == VoucherType.ISSUE:
|
||||
if info.source is not None:
|
||||
json_voucher.source = output.CostCentreLink(id=info.source.id_)
|
||||
else:
|
||||
json_voucher.source = output.CostCentreLink(id=CostCentre.cost_centre_purchase())
|
||||
if "destination" in info:
|
||||
json_voucher.destination = output.CostCentreLink(id=info["destination"])
|
||||
if info.destination is not None:
|
||||
json_voucher.destination = output.CostCentreLink(id=info.destination.id_)
|
||||
else:
|
||||
json_voucher.destination = output.CostCentreLink(id=CostCentre.cost_centre_kitchen())
|
||||
elif type_ == VoucherType.EMPLOYEE_BENEFIT:
|
||||
elif info.type_ == VoucherType.EMPLOYEE_BENEFIT:
|
||||
pass
|
||||
elif type_ == VoucherType.INCENTIVE:
|
||||
json_voucher.incentives, json_voucher.incentive = incentive_employees(info["date"], db)
|
||||
elif info.type_ == VoucherType.INCENTIVE:
|
||||
json_voucher.incentives, json_voucher.incentive = incentive_employees(info.date_, db)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f'Voucher of type "{type_}" does not exist.',
|
||||
detail=f'Voucher of type "{info.type_.name}" does not exist.',
|
||||
)
|
||||
return json_voucher
|
||||
|
||||
|
||||
def incentive_employees(date_, db: Session) -> Tuple[List[output.Incentive], Decimal]:
|
||||
date_ = datetime.strptime(date_, "%d-%b-%Y")
|
||||
def incentive_employees(date_: date, db: Session) -> Tuple[List[output.Incentive], Decimal]:
|
||||
start_date = get_first_day(date_)
|
||||
finish_date = date_
|
||||
details: List[output.Incentive] = []
|
||||
@ -427,7 +420,7 @@ def incentive_employees(date_, db: Session) -> Tuple[List[output.Incentive], Dec
|
||||
return details, amount
|
||||
|
||||
|
||||
def check_voucher_edit_allowed(voucher: Voucher, user: UserToken):
|
||||
def check_voucher_edit_allowed(voucher: Voucher, user: UserToken) -> None:
|
||||
if voucher.posted and "edit-posted-vouchers" not in user.permissions:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
|
27
brewman/brewman/schemas/blank_voucher_info.py
Normal file
27
brewman/brewman/schemas/blank_voucher_info.py
Normal file
@ -0,0 +1,27 @@
|
||||
from datetime import date, datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, validator
|
||||
|
||||
from ..models.voucher_type import VoucherType
|
||||
from . import to_camel
|
||||
from .account import AccountLink
|
||||
from .cost_centre import CostCentreLink
|
||||
|
||||
|
||||
class BlankVoucherInfo(BaseModel):
|
||||
type_: VoucherType
|
||||
date_: date
|
||||
account: Optional[AccountLink]
|
||||
source: Optional[CostCentreLink]
|
||||
destination: Optional[CostCentreLink]
|
||||
|
||||
class Config:
|
||||
alias_generator = to_camel
|
||||
json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")}
|
||||
|
||||
@validator("date_", pre=True)
|
||||
def parse_date(cls, value):
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
@ -12,8 +12,6 @@ class ProductSku(BaseModel):
|
||||
fraction_units: str
|
||||
cost_price: Decimal
|
||||
sale_price: Decimal
|
||||
fraction: Decimal
|
||||
product_yield: Decimal
|
||||
is_rate_contracted: bool
|
||||
|
||||
class Config:
|
||||
|
@ -12,9 +12,9 @@ from .recipe_item import RecipeItem
|
||||
|
||||
|
||||
class RecipeIn(BaseModel):
|
||||
product: ProductLink
|
||||
sku: ProductLink
|
||||
|
||||
quantity: Decimal
|
||||
recipe_yield: Decimal
|
||||
cost_price: Decimal
|
||||
sale_price: Decimal
|
||||
notes: str
|
||||
@ -56,7 +56,7 @@ class Recipe(RecipeIn):
|
||||
|
||||
|
||||
class RecipeBlank(RecipeIn):
|
||||
product: Optional[ProductLink] # type: ignore[assignment]
|
||||
sku: Optional[ProductLink] # type: ignore[assignment]
|
||||
|
||||
class Config:
|
||||
anystr_strip_whitespace = True
|
||||
|
@ -1,5 +1,6 @@
|
||||
import uuid
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
|
||||
from brewman.schemas.product import ProductLink
|
||||
@ -9,8 +10,8 @@ from pydantic import BaseModel
|
||||
class RecipeItem(BaseModel):
|
||||
id_: Optional[uuid.UUID]
|
||||
product: ProductLink
|
||||
quantity: int
|
||||
price: int
|
||||
quantity: Decimal
|
||||
price: Decimal
|
||||
|
||||
class Config:
|
||||
fields = {"id_": "id"}
|
||||
|
@ -3,8 +3,6 @@ export class ProductSku {
|
||||
name: string;
|
||||
costPrice: number;
|
||||
salePrice: number;
|
||||
fraction: number;
|
||||
productYield: number;
|
||||
fractionUnits: string;
|
||||
|
||||
isRateContracted: boolean;
|
||||
@ -14,8 +12,6 @@ export class ProductSku {
|
||||
this.name = '';
|
||||
this.costPrice = 0;
|
||||
this.salePrice = 0;
|
||||
this.fraction = 0;
|
||||
this.productYield = 0;
|
||||
this.fractionUnits = '';
|
||||
this.isRateContracted = false;
|
||||
|
||||
|
@ -94,7 +94,7 @@
|
||||
<mat-label>Sale Price</mat-label>
|
||||
<input matInput type="number" placeholder="Sale Price" formControlName="salePrice" />
|
||||
</mat-form-field>
|
||||
<button mat-raised-button color="primary" (click)="addRow()" fxFlex="15">Add</button>
|
||||
<button mat-raised-button color="primary" (click)="addRow()" fxFlex>Add</button>
|
||||
</div>
|
||||
</form>
|
||||
<mat-table [dataSource]="dataSource" aria-label="Elements">
|
||||
|
Loading…
x
Reference in New Issue
Block a user