Fix: Deleting voucher fucked up due to cascading changes was not

This commit is contained in:
Amritanshu Agrawal 2021-11-08 17:19:27 +05:30
parent 3d5d02a416
commit 952f030e8d
20 changed files with 248 additions and 165 deletions

@ -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,

@ -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">