diff --git a/brewman/alembic/versions/b13dbf97bd21_recipe_yield.py b/brewman/alembic/versions/b13dbf97bd21_recipe_yield.py
new file mode 100644
index 00000000..c923281b
--- /dev/null
+++ b/brewman/alembic/versions/b13dbf97bd21_recipe_yield.py
@@ -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
diff --git a/brewman/brewman/models/batch.py b/brewman/brewman/models/batch.py
index 0a5bab4c..947b81ec 100644
--- a/brewman/brewman/models/batch.py
+++ b/brewman/brewman/models/batch.py
@@ -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")
diff --git a/brewman/brewman/models/recipe.py b/brewman/brewman/models/recipe.py
index 9cd82135..7eef0f15 100644
--- a/brewman/brewman/models/recipe.py
+++ b/brewman/brewman/models/recipe.py
@@ -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
diff --git a/brewman/brewman/models/recipe_item.py b/brewman/brewman/models/recipe_item.py
index 35ca1e3a..5ae65ddf 100644
--- a/brewman/brewman/models/recipe_item.py
+++ b/brewman/brewman/models/recipe_item.py
@@ -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
diff --git a/brewman/brewman/models/voucher.py b/brewman/brewman/models/voucher.py
index 41437e50..4534814a 100644
--- a/brewman/brewman/models/voucher.py
+++ b/brewman/brewman/models/voucher.py
@@ -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,
diff --git a/brewman/brewman/routers/batch.py b/brewman/brewman/routers/batch.py
index 9ea043ac..f7c05405 100644
--- a/brewman/brewman/routers/batch.py
+++ b/brewman/brewman/routers/batch.py
@@ -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')}"
diff --git a/brewman/brewman/routers/employee_benefit.py b/brewman/brewman/routers/employee_benefit.py
index 21bd81b6..831eee2c 100644
--- a/brewman/brewman/routers/employee_benefit.py
+++ b/brewman/brewman/routers/employee_benefit.py
@@ -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)
diff --git a/brewman/brewman/routers/incentive.py b/brewman/brewman/routers/incentive.py
index e6361d57..62e4360b 100644
--- a/brewman/brewman/routers/incentive.py
+++ b/brewman/brewman/routers/incentive.py
@@ -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)
diff --git a/brewman/brewman/routers/issue.py b/brewman/brewman/routers/issue.py
index 4e2c59b0..d0a084d3 100644
--- a/brewman/brewman/routers/issue.py
+++ b/brewman/brewman/routers/issue.py
@@ -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)
diff --git a/brewman/brewman/routers/journal.py b/brewman/brewman/routers/journal.py
index 414b7f23..9ec0374a 100644
--- a/brewman/brewman/routers/journal.py
+++ b/brewman/brewman/routers/journal.py
@@ -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)
diff --git a/brewman/brewman/routers/product.py b/brewman/brewman/routers/product.py
index 2f855c0e..9eef1cf9 100644
--- a/brewman/brewman/routers/product.py
+++ b/brewman/brewman/routers/product.py
@@ -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,
)
)
diff --git a/brewman/brewman/routers/purchase.py b/brewman/brewman/routers/purchase.py
index f6ab2bbe..5283c9eb 100644
--- a/brewman/brewman/routers/purchase.py
+++ b/brewman/brewman/routers/purchase.py
@@ -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)
diff --git a/brewman/brewman/routers/purchase_return.py b/brewman/brewman/routers/purchase_return.py
index 25070ac0..6ed808b7 100644
--- a/brewman/brewman/routers/purchase_return.py
+++ b/brewman/brewman/routers/purchase_return.py
@@ -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)
diff --git a/brewman/brewman/routers/voucher.py b/brewman/brewman/routers/voucher.py
index 557a1b99..580182e7 100644
--- a/brewman/brewman/routers/voucher.py
+++ b/brewman/brewman/routers/voucher.py
@@ -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,
diff --git a/brewman/brewman/schemas/blank_voucher_info.py b/brewman/brewman/schemas/blank_voucher_info.py
new file mode 100644
index 00000000..e1f552cd
--- /dev/null
+++ b/brewman/brewman/schemas/blank_voucher_info.py
@@ -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()
diff --git a/brewman/brewman/schemas/product_sku.py b/brewman/brewman/schemas/product_sku.py
index 45a09588..b6255470 100644
--- a/brewman/brewman/schemas/product_sku.py
+++ b/brewman/brewman/schemas/product_sku.py
@@ -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:
diff --git a/brewman/brewman/schemas/recipe.py b/brewman/brewman/schemas/recipe.py
index 17f0cc4c..a9d90555 100644
--- a/brewman/brewman/schemas/recipe.py
+++ b/brewman/brewman/schemas/recipe.py
@@ -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
diff --git a/brewman/brewman/schemas/recipe_item.py b/brewman/brewman/schemas/recipe_item.py
index a6a8ddc0..66678217 100644
--- a/brewman/brewman/schemas/recipe_item.py
+++ b/brewman/brewman/schemas/recipe_item.py
@@ -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"}
diff --git a/overlord/src/app/core/product-sku.ts b/overlord/src/app/core/product-sku.ts
index f4f3e1b7..ef11007c 100644
--- a/overlord/src/app/core/product-sku.ts
+++ b/overlord/src/app/core/product-sku.ts
@@ -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;
diff --git a/overlord/src/app/product/product-detail/product-detail.component.html b/overlord/src/app/product/product-detail/product-detail.component.html
index cb347e16..77f62011 100644
--- a/overlord/src/app/product/product-detail/product-detail.component.html
+++ b/overlord/src/app/product/product-detail/product-detail.component.html
@@ -94,7 +94,7 @@
Sale Price
-
+