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

This commit is contained in:
tanshu committed 2021-11-08 17:19:27 +05:30
1 parent 3d5d02a416
commit 952f030e8d
20 files changed
+248 -165

No files matched your search

@@ -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
-17
View File
@@ -58,23 +58,6 @@ class Batch(Base):
def amount(self): def amount(self):
return self.quantity_remaining * self.rate * (1 + self.tax) * (1 - self.discount) 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 @classmethod
def suspense(cls): def suspense(cls):
return uuid.UUID("a955790e-93cf-493c-a816-c7d92b127383") return uuid.UUID("a955790e-93cf-493c-a816-c7d92b127383")
+24 -13
View File
@@ -1,33 +1,44 @@
import datetime
import uuid import uuid
from decimal import Decimal
from typing import TYPE_CHECKING, List
from sqlalchemy import Column, Date, ForeignKey, Numeric, Unicode from sqlalchemy import Column, Date, ForeignKey, Numeric, Unicode
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship from sqlalchemy.orm import Mapped, relationship
from .meta import Base 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): class Recipe(Base):
__tablename__ = "recipes" __tablename__ = "recipes"
id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) id: uuid.UUID = 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) 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) recipe_yield: Decimal = Column("recipe_yield", Numeric(precision=15, scale=2), nullable=False)
cost_price = Column("cost_price", Numeric(precision=15, scale=2), nullable=False) cost_price: Decimal = Column("cost_price", Numeric(precision=15, scale=2), nullable=False)
sale_price = Column("sale_price", Numeric(precision=15, scale=2), nullable=False) sale_price: Decimal = Column("sale_price", Numeric(precision=15, scale=2), nullable=False)
notes = Column("notes", Unicode(255)) notes: str = Column("notes", Unicode(255))
valid_from = Column("valid_from", Date, nullable=True) valid_from: datetime.date = Column("valid_from", Date, nullable=True)
valid_till = Column("valid_till", Date, nullable=True) valid_till: datetime.date = Column("valid_till", Date, nullable=True)
items = relationship("RecipeItem", back_populates="recipe") items: List["RecipeItem"] = relationship("RecipeItem", back_populates="recipe")
sku = relationship("StockKeepingUnit", back_populates="recipes") sku: Mapped["StockKeepingUnit"] = relationship("StockKeepingUnit", back_populates="recipes")
def __init__( def __init__(
self, self,
sku_id=None, sku_id=None,
quantity=None, recipe_yield=None,
cost_price=None, cost_price=None,
sale_price=None, sale_price=None,
valid_from=None, valid_from=None,
@@ -36,7 +47,7 @@ class Recipe(Base):
id_=None, id_=None,
): ):
self.sku_id = sku_id self.sku_id = sku_id
self.quantity = quantity self.recipe_yield = recipe_yield
self.cost_price = cost_price self.cost_price = cost_price
self.sale_price = sale_price self.sale_price = sale_price
self.valid_from = valid_from self.valid_from = valid_from
+18 -9
View File
@@ -1,24 +1,33 @@
import uuid 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.dialects.postgresql import UUID
from sqlalchemy.orm import relationship from sqlalchemy.orm import Mapped, relationship
from .meta import Base 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): class RecipeItem(Base):
__tablename__ = "recipe_items" __tablename__ = "recipe_items"
__table_args__ = (UniqueConstraint("recipe_id", "product_id"),) __table_args__ = (UniqueConstraint("recipe_id", "product_id"),)
id = Column("recipe_item_id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) id: uuid.UUID = 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) recipe_id: uuid.UUID = 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) product_id: uuid.UUID = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False)
quantity = Column("quantity", Integer, nullable=False) quantity: int = Column("quantity", Numeric(precision=15, scale=2), nullable=False)
price = Column("price", Integer, nullable=False) price: int = Column("price", Numeric(precision=15, scale=5), nullable=False)
recipe = relationship("Recipe", back_populates="items") recipe: Mapped["Recipe"] = relationship("Recipe", back_populates="items")
product = relationship("Product") product: Mapped["Product"] = relationship("Product")
def __init__(self, recipe_id=None, product_id=None, quantity=None, price=None, recipe=None, id_=None): def __init__(self, recipe_id=None, product_id=None, quantity=None, price=None, recipe=None, id_=None):
self.recipe_id = recipe_id self.recipe_id = recipe_id
+8 -4
View File
@@ -40,11 +40,15 @@ class Voucher(Base):
user: Mapped["User"] = relationship("User", primaryjoin="User.id==Voucher.user_id") user: Mapped["User"] = relationship("User", primaryjoin="User.id==Voucher.user_id")
poster: Mapped["User"] = relationship("User", primaryjoin="User.id==Voucher.poster_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") inventories: List["Inventory"] = relationship(
employee_benefits: List["EmployeeBenefit"] = relationship("EmployeeBenefit", backref="voucher") "Inventory", cascade="delete, delete-orphan", back_populates="voucher"
incentives: List["Incentive"] = relationship("Incentive", backref="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__( def __init__(
self, self,
+19 -3
View File
@@ -1,10 +1,14 @@
import datetime import datetime
from fastapi import APIRouter, Depends 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 ..core.security import get_current_active_user as get_user
from ..db.session import SessionFuture from ..db.session import SessionFuture
from ..models.batch import Batch from ..models.batch import Batch
from ..models.product import Product
from ..models.stock_keeping_unit import StockKeepingUnit
from ..schemas.user import UserToken from ..schemas.user import UserToken
@@ -14,13 +18,25 @@ router = APIRouter()
@router.get("") @router.get("")
def batch_term( def batch_term(
q: str, q: str,
d: str = None, d: str,
current_user: UserToken = Depends(get_user), 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_ = [] list_ = []
with SessionFuture() as db: 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 = ( text = (
f"{item.sku.product.name} ({item.sku.units}) {item.quantity_remaining:.2f}@" f"{item.sku.product.name} ({item.sku.units}) {item.quantity_remaining:.2f}@"
f"{item.rate:.2f} from {item.name.strftime('%d-%b-%Y')}" f"{item.rate:.2f} from {item.name.strftime('%d-%b-%Y')}"
+2 -1
View File
@@ -22,6 +22,7 @@ from ..models.journal import Journal
from ..models.validations import check_journals_are_valid from ..models.validations import check_journals_are_valid
from ..models.voucher import Voucher from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType from ..models.voucher_type import VoucherType
from ..schemas.blank_voucher_info import BlankVoucherInfo
from ..schemas.employee_benefit import EmployeeBenefit as EmployeeBenefitSchema from ..schemas.employee_benefit import EmployeeBenefit as EmployeeBenefitSchema
from ..schemas.user import UserToken from ..schemas.user import UserToken
from . import get_lock_info from . import get_lock_info
@@ -255,7 +256,7 @@ def show_blank(
request: Request, request: Request,
user: UserToken = Security(get_user, scopes=["employee-benefit"]), 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: with SessionFuture() as db:
return blank_voucher(additional_info, db) return blank_voucher(additional_info, db)
+2 -1
View File
@@ -25,6 +25,7 @@ from ..models.journal import Journal
from ..models.validations import check_journals_are_valid from ..models.validations import check_journals_are_valid
from ..models.voucher import Voucher from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType from ..models.voucher_type import VoucherType
from ..schemas.blank_voucher_info import BlankVoucherInfo
from ..schemas.incentive import Incentive as IncentiveSchema from ..schemas.incentive import Incentive as IncentiveSchema
from ..schemas.user import UserToken from ..schemas.user import UserToken
from . import get_lock_info from . import get_lock_info
@@ -208,7 +209,7 @@ def show_blank(
d: str = None, d: str = None,
user: UserToken = Security(get_user, scopes=["incentive"]), 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: with SessionFuture() as db:
return blank_voucher(additional_info, db) return blank_voucher(additional_info, db)
+7 -5
View File
@@ -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.validations import check_inventories_are_valid, check_journals_are_valid
from ..models.voucher import Voucher from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType 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.inventory import Inventory as InventorySchema
from ..schemas.user import UserToken from ..schemas.user import UserToken
from . import get_lock_info from . import get_lock_info
@@ -381,15 +383,15 @@ def get_id(
def show_blank( def show_blank(
request: Request, request: Request,
date: str = None, date: str = None,
source: str = None, source: uuid.UUID = None,
destination: str = None, destination: uuid.UUID = None,
user: UserToken = Security(get_user, scopes=["issue"]), user: UserToken = Security(get_user, scopes=["issue"]),
) -> output.Voucher: ) -> output.Voucher:
date_ = date or get_date(request.session) date_ = date or get_date(request.session)
additional_info = {"date": date_, "type": VoucherType.ISSUE} additional_info = BlankVoucherInfo(date=date_, type=VoucherType.ISSUE)
if source: if source:
additional_info["source"] = source additional_info.source = CostCentreLink(id=source, name="")
if destination: if destination:
additional_info["destination"] = destination additional_info.destination = CostCentreLink(id=destination, name="")
with SessionFuture() as db: with SessionFuture() as db:
return blank_voucher(additional_info, db) return blank_voucher(additional_info, db)
+5 -3
View File
@@ -19,6 +19,8 @@ from ..models.journal import Journal
from ..models.validations import check_journals_are_valid from ..models.validations import check_journals_are_valid
from ..models.voucher import Voucher from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType from ..models.voucher_type import VoucherType
from ..schemas.account import AccountLink
from ..schemas.blank_voucher_info import BlankVoucherInfo
from ..schemas.user import UserToken from ..schemas.user import UserToken
from . import get_lock_info from . import get_lock_info
from .db_image import save_files, update_files from .db_image import save_files, update_files
@@ -188,7 +190,7 @@ def get_id(
@router.get("", response_model=output.Voucher) @router.get("", response_model=output.Voucher)
def show_blank( def show_blank(
request: Request, request: Request,
a: str = None, a: uuid.UUID = None,
user: UserToken = Security(get_user, scopes=["journal"]), user: UserToken = Security(get_user, scopes=["journal"]),
) -> output.Voucher: ) -> output.Voucher:
if request.scope.get("path") == "/api/payment": if request.scope.get("path") == "/api/payment":
@@ -198,8 +200,8 @@ def show_blank(
else: else:
type_ = VoucherType.JOURNAL type_ = VoucherType.JOURNAL
additional_info = {"date": get_date(request.session), "type": type_} additional_info = BlankVoucherInfo(date=get_date(request.session), type=type_)
if a: if a:
additional_info["account"] = a additional_info.ac = AccountLink(id=a)
with SessionFuture() as db: with SessionFuture() as db:
return blank_voucher(additional_info, db) return blank_voucher(additional_info, db)
-4
View File
@@ -216,8 +216,6 @@ async def show_term_sku(
fractionUnits=item.fraction_units, fractionUnits=item.fraction_units,
costPrice=sku.cost_price if rc_price is None else rc_price, costPrice=sku.cost_price if rc_price is None else rc_price,
salePrice=sku.sale_price, salePrice=sku.sale_price,
fraction=sku.fraction,
productYield=sku.product_yield,
isRateContracted=False if rc_price is None else True, isRateContracted=False if rc_price is None else True,
) )
) )
@@ -252,8 +250,6 @@ async def show_term_product(
fractionUnits=item.fraction_units, fractionUnits=item.fraction_units,
costPrice=0, costPrice=0,
salePrice=0, salePrice=0,
fraction=1,
productYield=1,
isRateContracted=False, isRateContracted=False,
) )
) )
+2 -1
View File
@@ -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.validations import check_inventories_are_valid, check_journals_are_valid
from ..models.voucher import Voucher from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType from ..models.voucher_type import VoucherType
from ..schemas.blank_voucher_info import BlankVoucherInfo
from ..schemas.inventory import Inventory as InventorySchema from ..schemas.inventory import Inventory as InventorySchema
from ..schemas.user import UserToken from ..schemas.user import UserToken
from . import get_lock_info from . import get_lock_info
@@ -363,7 +364,7 @@ def show_blank(
request: Request, request: Request,
user: UserToken = Security(get_user, scopes=["purchase"]), user: UserToken = Security(get_user, scopes=["purchase"]),
) -> output.Voucher: ) -> 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: with SessionFuture() as db:
return blank_voucher(additional_info, db) return blank_voucher(additional_info, db)
+2 -1
View File
@@ -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.validations import check_inventories_are_valid, check_journals_are_valid
from ..models.voucher import Voucher from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType from ..models.voucher_type import VoucherType
from ..schemas.blank_voucher_info import BlankVoucherInfo
from ..schemas.inventory import Inventory as InventorySchema from ..schemas.inventory import Inventory as InventorySchema
from ..schemas.user import UserToken from ..schemas.user import UserToken
from . import get_lock_info from . import get_lock_info
@@ -329,6 +330,6 @@ def show_blank(
request: Request, request: Request,
user: UserToken = Security(get_user, scopes=["purchase-return"]), user: UserToken = Security(get_user, scopes=["purchase-return"]),
) -> output.Voucher: ) -> 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: with SessionFuture() as db:
return blank_voucher(additional_info, db) return blank_voucher(additional_info, db)
+84 -91
View File
@@ -1,13 +1,13 @@
import uuid import uuid
from datetime import datetime from datetime import date, datetime
from decimal import Decimal from decimal import Decimal
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
import brewman.schemas.voucher as output import brewman.schemas.voucher as output
from fastapi import APIRouter, HTTPException, Security, status 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.exc import SQLAlchemyError
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -21,11 +21,14 @@ from ..models.attendance_type import AttendanceType
from ..models.cost_centre import CostCentre from ..models.cost_centre import CostCentre
from ..models.db_image import DbImage from ..models.db_image import DbImage
from ..models.employee import Employee from ..models.employee import Employee
from ..models.employee_benefit import EmployeeBenefit
from ..models.incentive import Incentive
from ..models.inventory import Inventory from ..models.inventory import Inventory
from ..models.journal import Journal from ..models.journal import Journal
from ..models.voucher import Voucher from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType from ..models.voucher_type import VoucherType
from ..routers import get_lock_info from ..routers import get_lock_info
from ..schemas.blank_voucher_info import BlankVoucherInfo
from ..schemas.user import UserToken from ..schemas.user import UserToken
@@ -163,11 +166,19 @@ def delete_voucher(
for image in images: for image in images:
db.delete(image) db.delete(image)
db.commit() 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( json_voucher = output.Voucher(
id=voucher.id, id=voucher.id,
date=voucher.date, 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] 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) json_voucher.vendor = output.AccountLink(id=item.account.id, name=item.account.name)
else: else:
for item in voucher.journals: for journal in voucher.journals:
json_voucher.journals.append( json_voucher.journals.append(
output.Journal( output.Journal(
id=item.id, id=journal.id,
debit=item.debit, debit=journal.debit,
amount=item.amount, amount=journal.amount,
account=output.AccountLink(id=item.account.id, name=item.account.name), account=output.AccountLink(id=journal.account.id, name=journal.account.name),
costCentre=output.CostCentreLink(id=item.cost_centre_id), 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( json_voucher.employee_benefits.append(
output.EmployeeBenefit( output.EmployeeBenefit(
grossSalary=item.gross_salary, grossSalary=benefit.gross_salary,
daysWorked=item.days_worked, daysWorked=benefit.days_worked,
esiEmployee=item.esi_ee, esiEmployee=benefit.esi_ee,
pfEmployee=item.pf_ee, pfEmployee=benefit.pf_ee,
esiEmployer=item.esi_er, esiEmployer=benefit.esi_er,
pfEmployer=item.pf_er, pfEmployer=benefit.pf_er,
employee=output.EmployeeLink( employee=output.EmployeeLink(
id=item.journal.account.id, id=benefit.journal.account.id,
name=item.journal.account.name, name=benefit.journal.account.name,
designation=item.journal.account.designation, designation=benefit.journal.account.designation,
costCentre=output.CostCentreLink( costCentre=output.CostCentreLink(
id=item.journal.account.cost_centre.id, id=benefit.journal.account.cost_centre.id,
name=item.journal.account.cost_centre.name, name=benefit.journal.account.cost_centre.name,
), ),
), ),
) )
) )
for item in voucher.incentives: for incentive in voucher.incentives:
employee: Employee = db.execute(select(Employee).where(Employee.id == item.journal.account_id)).scalar_one() employee: Employee = db.execute(
select(Employee).where(Employee.id == incentive.journal.account_id)
).scalar_one()
json_voucher.incentives.append( json_voucher.incentives.append(
output.Incentive( output.Incentive(
employeeId=item.journal.account_id, employeeId=incentive.journal.account_id,
name=item.journal.account.name, name=incentive.journal.account.name,
designation=employee.designation, designation=employee.designation,
department=item.journal.account.cost_centre.name, department=incentive.journal.account.cost_centre.name,
daysWorked=item.days_worked, daysWorked=incentive.days_worked,
points=item.points, points=incentive.points,
) )
) )
if len(json_voucher.incentives) > 0: if len(json_voucher.incentives) > 0:
json_voucher.incentive = next(x.amount for x in voucher.journals if x.account_id == Account.incentive_id()) 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 = ( text = (
f"{item.batch.sku.product.name} ({item.batch.sku.units}) {item.batch.quantity_remaining:.2f}@" f"{inventory.batch.sku.product.name} ({inventory.batch.sku.units}) {inventory.batch.quantity_remaining:.2f}@"
f"{item.batch.rate:.2f} from {item.batch.name.strftime('%d-%b-%Y')}" f"{inventory.batch.rate:.2f} from {inventory.batch.name.strftime('%d-%b-%Y')}"
) )
json_voucher.inventories.append( json_voucher.inventories.append(
output.Inventory( output.Inventory(
id=item.id, id=inventory.id,
quantity=item.quantity, quantity=inventory.quantity,
rate=item.rate, rate=inventory.rate,
tax=item.tax, tax=inventory.tax,
discount=item.discount, discount=inventory.discount,
amount=item.amount, amount=inventory.amount,
batch=output.Batch( batch=output.Batch(
id=item.batch.id, id=inventory.batch.id,
name=text, name=text,
quantityRemaining=item.batch.quantity_remaining, quantityRemaining=inventory.batch.quantity_remaining,
tax=item.batch.tax, tax=inventory.batch.tax,
discount=item.batch.discount, discount=inventory.batch.discount,
rate=item.batch.rate, rate=inventory.batch.rate,
sku=output.ProductLink( sku=output.ProductLink(
id=item.batch.sku.id, id=inventory.batch.sku.id,
name=f"{item.batch.sku.product.name} ({item.batch.sku.units})" name=f"{inventory.batch.sku.product.name} ({inventory.batch.sku.units})"
if item.batch.sku.units if inventory.batch.sku.units
else item.batch.sku.product.name, else inventory.batch.sku.product.name,
), ),
), ),
) )
@@ -285,21 +298,10 @@ def voucher_info(voucher, db: Session) -> output.Voucher:
return json_voucher return json_voucher
def blank_voucher(info, db: Session) -> output.Voucher: def blank_voucher(info: BlankVoucherInfo, 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",
)
json_voucher = output.Voucher( json_voucher = output.Voucher(
type=type_.name, type=info.type_.name,
date=info["date"], date=info.date_,
isStarred=False, isStarred=False,
posted=False, posted=False,
narration="", narration="",
@@ -309,65 +311,56 @@ def blank_voucher(info, db: Session) -> output.Voucher:
employeeBenefits=[], employeeBenefits=[],
files=[], files=[],
) )
if type_ == VoucherType.JOURNAL: if info.type_ == VoucherType.JOURNAL:
pass pass
elif type_ == VoucherType.PAYMENT: elif info.type_ == VoucherType.PAYMENT:
account = None account = None
if info and "account" in info and info["account"]: if info.account is not None:
account = ( account = db.execute(select(AccountBase).where(AccountBase.id == info.account.id_)).scalars().one_or_none()
db.execute(select(AccountBase).where(AccountBase.id == uuid.UUID(info["account"])))
.scalars()
.one_or_none()
)
if account is not None: if account is not None:
j_account = output.AccountLink(id=account.id, name=account.name) j_account = output.AccountLink(id=account.id, name=account.name)
else: else:
j_account = output.AccountLink(id=AccountBase.cash_in_hand()["id"], name=AccountBase.cash_in_hand()["name"]) 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)) json_voucher.journals.append(output.Journal(account=j_account, amount=0, debit=-1))
elif type_ == VoucherType.RECEIPT: elif info.type_ == VoucherType.RECEIPT:
account = None account = None
if info and "account" in info and info["account"]: if info.account is not None:
account = ( account = db.execute(select(AccountBase).where(AccountBase.id == info.account.id_)).scalars().one_or_none()
db.execute(select(AccountBase).where(AccountBase.id == uuid.UUID(info["account"])))
.scalars()
.one_or_none()
)
if account is not None: if account is not None:
j_account = output.AccountLink(id=account.id, name=account.name) j_account = output.AccountLink(id=account.id, name=account.name)
else: else:
j_account = output.AccountLink(id=AccountBase.cash_in_hand()["id"], name=AccountBase.cash_in_hand()["name"]) 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)) 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( json_voucher.vendor = output.AccountLink(
id=AccountBase.local_purchase()["id"], name=AccountBase.local_purchase()["name"] 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( json_voucher.vendor = output.AccountLink(
id=AccountBase.local_purchase()["id"], name=AccountBase.local_purchase()["name"] id=AccountBase.local_purchase()["id"], name=AccountBase.local_purchase()["name"]
) )
elif type_ == VoucherType.ISSUE: elif info.type_ == VoucherType.ISSUE:
if "source" in info: if info.source is not None:
json_voucher.source = output.CostCentreLink(id=info["source"]) json_voucher.source = output.CostCentreLink(id=info.source.id_)
else: else:
json_voucher.source = output.CostCentreLink(id=CostCentre.cost_centre_purchase()) json_voucher.source = output.CostCentreLink(id=CostCentre.cost_centre_purchase())
if "destination" in info: if info.destination is not None:
json_voucher.destination = output.CostCentreLink(id=info["destination"]) json_voucher.destination = output.CostCentreLink(id=info.destination.id_)
else: else:
json_voucher.destination = output.CostCentreLink(id=CostCentre.cost_centre_kitchen()) json_voucher.destination = output.CostCentreLink(id=CostCentre.cost_centre_kitchen())
elif type_ == VoucherType.EMPLOYEE_BENEFIT: elif info.type_ == VoucherType.EMPLOYEE_BENEFIT:
pass pass
elif type_ == VoucherType.INCENTIVE: elif info.type_ == VoucherType.INCENTIVE:
json_voucher.incentives, json_voucher.incentive = incentive_employees(info["date"], db) json_voucher.incentives, json_voucher.incentive = incentive_employees(info.date_, db)
else: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, 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 return json_voucher
def incentive_employees(date_, db: Session) -> Tuple[List[output.Incentive], Decimal]: def incentive_employees(date_: date, db: Session) -> Tuple[List[output.Incentive], Decimal]:
date_ = datetime.strptime(date_, "%d-%b-%Y")
start_date = get_first_day(date_) start_date = get_first_day(date_)
finish_date = date_ finish_date = date_
details: List[output.Incentive] = [] details: List[output.Incentive] = []
@@ -427,7 +420,7 @@ def incentive_employees(date_, db: Session) -> Tuple[List[output.Incentive], Dec
return details, amount 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: if voucher.posted and "edit-posted-vouchers" not in user.permissions:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, 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()
-2
View File
@@ -12,8 +12,6 @@ class ProductSku(BaseModel):
fraction_units: str fraction_units: str
cost_price: Decimal cost_price: Decimal
sale_price: Decimal sale_price: Decimal
fraction: Decimal
product_yield: Decimal
is_rate_contracted: bool is_rate_contracted: bool
class Config: class Config:
+3 -3
View File
@@ -12,9 +12,9 @@ from .recipe_item import RecipeItem
class RecipeIn(BaseModel): class RecipeIn(BaseModel):
product: ProductLink sku: ProductLink
quantity: Decimal recipe_yield: Decimal
cost_price: Decimal cost_price: Decimal
sale_price: Decimal sale_price: Decimal
notes: str notes: str
@@ -56,7 +56,7 @@ class Recipe(RecipeIn):
class RecipeBlank(RecipeIn): class RecipeBlank(RecipeIn):
product: Optional[ProductLink] # type: ignore[assignment] sku: Optional[ProductLink] # type: ignore[assignment]
class Config: class Config:
anystr_strip_whitespace = True anystr_strip_whitespace = True
+3 -2
View File
@@ -1,5 +1,6 @@
import uuid import uuid
from decimal import Decimal
from typing import Optional from typing import Optional
from brewman.schemas.product import ProductLink from brewman.schemas.product import ProductLink
@@ -9,8 +10,8 @@ from pydantic import BaseModel
class RecipeItem(BaseModel): class RecipeItem(BaseModel):
id_: Optional[uuid.UUID] id_: Optional[uuid.UUID]
product: ProductLink product: ProductLink
quantity: int quantity: Decimal
price: int price: Decimal
class Config: class Config:
fields = {"id_": "id"} fields = {"id_": "id"}
-4
View File
@@ -3,8 +3,6 @@ export class ProductSku {
name: string; name: string;
costPrice: number; costPrice: number;
salePrice: number; salePrice: number;
fraction: number;
productYield: number;
fractionUnits: string; fractionUnits: string;
isRateContracted: boolean; isRateContracted: boolean;
@@ -14,8 +12,6 @@ export class ProductSku {
this.name = ''; this.name = '';
this.costPrice = 0; this.costPrice = 0;
this.salePrice = 0; this.salePrice = 0;
this.fraction = 0;
this.productYield = 0;
this.fractionUnits = ''; this.fractionUnits = '';
this.isRateContracted = false; this.isRateContracted = false;
@@ -94,7 +94,7 @@
<mat-label>Sale Price</mat-label> <mat-label>Sale Price</mat-label>
<input matInput type="number" placeholder="Sale Price" formControlName="salePrice" /> <input matInput type="number" placeholder="Sale Price" formControlName="salePrice" />
</mat-form-field> </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> </div>
</form> </form>
<mat-table [dataSource]="dataSource" aria-label="Elements"> <mat-table [dataSource]="dataSource" aria-label="Elements">