Done: Product Group

Done: Product
This commit is contained in:
tanshu 2020-05-10 16:05:39 +05:30
parent aae48faf91
commit 9383cf84d4
6 changed files with 394 additions and 331 deletions

View File

@ -111,6 +111,18 @@ class Product(Base):
return False, "Product has entries"
return True, ""
@classmethod
def query(cls, term, is_purchased=None, active=None, db=None):
query_ = db.query(Product)
if active is not None:
query_ = query_.filter(Product.is_active == active)
if is_purchased is not None:
query_ = query_.filter(Product.is_purchased == is_purchased)
if term is not None:
for item in term.split():
if item.strip() != "":
query_ = query_.filter(Product.name.ilike("%" + item + "%"))
@classmethod
def suspense(cls):
return uuid.UUID("aa79a643-9ddc-4790-ac7f-a41f9efb4c15")
@ -303,20 +315,20 @@ class AccountBase(Base):
self.is_fixture = is_fixture
@classmethod
def list(cls, type, name, reconcilable=None, active=None, dbsession=None):
query = dbsession.query(cls)
if type is not None:
if not isinstance(type, int):
type = int(type)
query = query.filter(cls.type == type)
def query(cls, q, type_, reconcilable=None, active=None, db=None):
query_ = db.query(cls)
if type_ is not None:
if not isinstance(type_, int):
type_ = int(type_)
query_ = query_.filter(cls.type == type_)
if reconcilable is not None:
query = query.filter(cls.is_reconcilable == reconcilable)
query_ = query_.filter(cls.is_reconcilable == reconcilable)
if active is not None:
query = query.filter(cls.is_active == active)
if name is not None:
for item in name.split():
query = query.filter(cls.name.ilike("%" + item + "%"))
return query.order_by(cls.name)
query_ = query_.filter(cls.is_active == active)
if q is not None:
for item in q.split():
query_ = query_.filter(cls.name.ilike("%" + item + "%"))
return query_.order_by(cls.name)
def create(self, dbsession):
code = (

View File

@ -151,7 +151,7 @@ async def show_term(
count = c
list_ = []
for index, item in enumerate(AccountBase.list(t, q, r, a, db)):
for index, item in enumerate(AccountBase.query(q, t, r, a, db)):
list_.append({"id": item.id, "name": item.name})
if count is not None and index == count - 1:
break
@ -195,7 +195,7 @@ def balance(id_: uuid.UUID, date, db: Session):
return 0 if bal is None else bal
def account_info(id_, db):
def account_info(id_: uuid.UUID, db: Session):
if id_ is None:
account = {
"code": "(Auto)",
@ -224,7 +224,7 @@ def account_info(id_, db):
return account
def delete_with_data(account, db):
def delete_with_data(account: Account, db: Session):
suspense_account = (
db.query(Account).filter(Account.id == Account.suspense()).first()
)

View File

@ -1,6 +1,7 @@
import traceback
import uuid
from typing import List, Optional
from fastapi import APIRouter, HTTPException, status, Depends, Security
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
@ -28,10 +29,16 @@ def save(
user: User = Security(get_user, scopes=["cost-centres"]),
):
try:
item = CostCentre(data.name)
item = CostCentre(name=data.name)
db.add(item)
db.commit()
return cost_centre_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise HTTPException(

View File

@ -1,245 +1,203 @@
import traceback
import uuid
from decimal import Decimal, InvalidOperation
from typing import Optional
from fastapi import APIRouter, HTTPException, status, Depends, Security
from sqlalchemy import desc
from sqlalchemy.orm import joinedload_all
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import joinedload_all, Session
from brewman.models.master import Product, Account
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Voucher, Batch, Inventory, VoucherType
from fastapi import APIRouter
import brewman.schemas.master as schemas
from ..core.security import User, get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.master import Product, Account
from ..models.voucher import Voucher, Batch, Inventory, VoucherType
router = APIRouter()
@router.post("/new") # "Products"
def save(request):
json = request.json_body
name = json.get("name", "").strip()
if name == "":
raise ValidationError("Name cannot be blank")
units = json.get("units", "").strip()
# Dependency
def get_db():
try:
fraction = Decimal(json.get("fraction", 0))
if fraction <= 0:
raise ValidationError("Fraction must be a decimal > 0")
except (ValueError, InvalidOperation):
raise ValidationError("Fraction must be a decimal > 0")
db = SessionLocal()
yield db
finally:
db.close()
fraction_units = json.get("fractionUnits", "").strip()
@router.post("/", response_model=schemas.Product)
def save(
data: schemas.ProductIn,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["products"]),
):
try:
product_yield = Decimal(json.get("productYield", 1))
if product_yield <= 0 or product_yield > 1:
raise ValidationError("Yield must be a decimal > 0 <= 1")
except (ValueError, InvalidOperation):
raise ValidationError("Yield must be a decimal > 0 <= 1")
product_group = json.get("productGroup", None)
if product_group is None:
raise ValidationError("please choose a product group")
product_group_id = uuid.UUID(product_group["id"])
try:
price = Decimal(json.get("price", 0))
if price < 0:
raise ValidationError("Price must be a decimal >= 0")
except (ValueError, InvalidOperation):
raise ValidationError("Price must be a decimal >= 0")
try:
sale_price = Decimal(json.get("salePrice", 0))
if price < 0:
raise ValidationError("Sale Price must be a decimal >= 0")
except (ValueError, InvalidOperation):
raise ValidationError("Price must be a decimal >= 0")
is_active = json.get("isActive", True)
is_purchased = json.get("isPurchased", True)
is_sold = json.get("isSold", True)
item = Product(
0,
name,
units,
fraction,
fraction_units,
product_yield,
product_group_id,
Account.all_purchases(),
price,
sale_price,
is_active,
is_purchased,
is_sold,
).create(request.dbsession)
transaction.commit()
return product_info(item.id, request.dbsession)
@router.put("/{id}") # "Products"
def update(request):
item = (
request.dbsession.query(Product)
.filter(Product.id == uuid.UUID(request.matchdict["id"]))
.first()
)
if item.is_fixture:
raise ValidationError(
"{0} is a fixture and cannot be edited or deleted.".format(item.full_name)
item = Product(
name=data.name,
units=data.units,
fraction=data.fraction,
fraction_units=data.fraction_units,
product_yield=data.product_yield,
product_group_id=data.product_group.id_,
account_id=Account.all_purchases(),
price=data.price,
sale_price=data.sale_price,
is_active=data.is_active,
is_purchased=data.is_purchased,
is_sold=data.is_sold
).create(db)
db.commit()
return product_info(item.id, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=traceback.format_exc(),
)
item.name = request.json_body["name"].strip()
item.units = request.json_body["units"].strip()
try:
item.fraction = Decimal(request.json_body["fraction"])
if item.fraction <= 0:
raise ValidationError("Fraction must be a decimal > 0")
except (ValueError, InvalidOperation):
raise ValidationError("Fraction must be a decimal > 0")
item.fraction_units = request.json_body["fractionUnits"]
try:
item.product_yield = Decimal(request.json_body["productYield"])
if item.product_yield <= 0 or item.product_yield > 1:
raise ValidationError("Yield must be a decimal > 0 <= 1")
except (ValueError, InvalidOperation):
raise ValidationError("Yield must be a decimal > 0 <= 1")
item.product_group_id = uuid.UUID(request.json_body["productGroup"]["id"])
item.account_id = Account.all_purchases()
try:
item.price = Decimal(request.json_body["price"])
if item.price < 0:
raise ValidationError("Price must be a decimal >= 0")
except (ValueError, InvalidOperation):
raise ValidationError("Price must be a decimal >= 0")
try:
item.sale_price = Decimal(request.json_body["salePrice"])
if item.sale_price < 0:
raise ValidationError("Sale Price must be a decimal >= 0")
except (ValueError, InvalidOperation):
raise ValidationError("Sale Price must be a decimal >= 0")
item.is_active = request.json_body["isActive"]
item.is_fixture = request.json_body["isFixture"]
item.is_purchased = request.json_body["isPurchased"]
item.is_sold = request.json_body["isSold"]
transaction.commit()
return product_info(item.id, request.dbsession)
@router.delete("/{id}") # "Products"
def delete(request):
product = (
request.dbsession.query(Product)
.filter(Product.id == uuid.UUID(request.matchdict["id"]))
.first()
)
can_delete, reason = product.can_delete(request.has_permission("Advanced Delete"))
@router.put("/{id_}", response_model=schemas.Product)
def update(
id_: uuid.UUID,
data: schemas.ProductIn,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["products"]),
):
try:
item: Product = db.query(Product).filter(Product.id == id_).first()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
item.units = data.units
item.fraction = data.fraction
item.fraction_units = data.fraction_units
item.product_yield = data.product_yield
item.product_group_id = data.product_group.id_
item.account_id = Account.all_purchases()
item.price = data.price
item.sale_price = data.sale_price
item.is_active = data.is_active
item.is_purchased = data.is_purchased
item.is_sold = data.is_sold
db.commit()
return product_info(item.id, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
)
except Exception:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=traceback.format_exc(),
)
@router.delete("/{id_}")
def delete(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["products"]),
):
product: Product = db.query(Product).filter(Product.id == id_).first()
can_delete, reason = product.can_delete("Advanced Delete" in user.permissions)
if can_delete:
delete_with_data(product, request)
transaction.commit()
return product_info(None, request.dbsession)
delete_with_data(product, db)
db.commit()
return product_info(None, db)
else:
transaction.abort()
response = Response("Cannot delete product because {0}".format(reason))
response.status_int = 500
return response
db.abort()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Cannot delete account because {reason}",
)
@router.get("/{id}") # "Products"
def show_id(request):
return product_info(uuid.UUID(request.matchdict.get("id", None)), request.dbsession)
@router.get("/") # "Products"
def show_blank(
db: Session = Depends(get_db), user: User = Security(get_user, scopes=["products"])
):
return product_info(None, db)
@router.get("/new") # "Products"
def show_blank(request):
return product_info(None, request.dbsession)
@router.get("/list")
def show_list(db: Session = Depends(get_db), user: User = Depends(get_user)):
return [
{
"id": item.id,
"code": item.code,
"name": item.name,
"units": item.units,
"costPrice": item.price,
"salePrice": item.sale_price,
"productGroup": item.product_group.name,
"isActive": item.is_active,
"fraction": item.fraction,
"fractionUnits": item.fraction_units,
"isPurchased": item.is_purchased,
"isSold": item.is_sold,
"productYield": item.product_yield,
"isFixture": item.is_fixture,
}
for item in db.query(Product).order_by(desc(Product.is_active)).order_by(Product.product_group_id).order_by(Product.name).all()
]
@router.get("/", ) # "Authenticated"
def show_list(l: bool):
list_ = (
request.dbsession.query(Product)
.order_by(desc(Product.is_active))
.order_by(Product.product_group_id)
.order_by(Product.name)
.all()
)
products = []
for item in list_:
products.append(
@router.get("/query")
async def show_term(
t: str = None,
a: bool = None,
c: int = None,
p: bool = None,
e: bool = False,
db: Session = Depends(get_db),
current_user: User = Depends(get_user),
):
count = c
extended = e
list_ = []
for index, item in enumerate(Product.query(t, p, a, db)):
list_.append(
{
"id": item.id,
"code": item.code,
"name": item.name,
"name": item.full_name,
"price": item.price,
"units": item.units,
"costPrice": item.price,
"salePrice": item.sale_price,
"productGroup": item.product_group.name,
"isActive": item.is_active,
"fraction": item.fraction,
"fractionUnits": item.fraction_units,
"isPurchased": item.is_purchased,
"isSold": item.is_sold,
"productYield": item.product_yield,
"isFixture": item.is_fixture,
"isSold": item.is_sold,
"salePrice": item.sale_price,
} if extended else {
"id": item.id, "name": item.full_name, "price": item.price
}
)
return products
if count is not None and index == count - 1:
break
return sorted(list_, key=lambda k: k["name"])
@router.get("/") # "Authenticated"
async def show_term(t: str):
term = request.GET.get("t", None)
term = term.strip() if term is not None and term.strip() != "" else None
active = request.GET.get("a", None)
active = active if active is not None else None
count = request.GET.get("c", None)
count = None if count is None or count == "" else int(count)
is_purchased = request.GET.get("p", None)
is_purchased = is_purchased if is_purchased is not None else None
extended = request.GET.get("e", False)
def add_list(query):
local_results = []
for index, item in enumerate(query):
if extended:
product = {
"id": item.id,
"name": item.full_name,
"price": item.price,
"units": item.units,
"fraction": item.fraction,
"fractionUnits": item.fraction_units,
"productYield": item.product_yield,
"isSold": item.is_sold,
"salePrice": item.sale_price,
}
else:
product = {"id": item.id, "name": item.full_name, "price": item.price}
local_results.append(product)
if count is not None and index == count - 1:
break
return local_results
result_list = []
query = request.dbsession.query(Product)
if active is not None:
query = query.filter(Product.is_active == active)
if is_purchased is not None:
query = query.filter(Product.is_purchased == is_purchased)
if term is not None:
for item in term.split():
if item.strip() != "":
query = query.filter(Product.name.ilike("%" + item + "%"))
result_list += add_list(query)
return sorted(result_list, key=lambda k: k["name"])
@router.get("/{id_}")
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["accounts"]),
):
return product_info(id_, db)
def product_info(id_, dbsession):
def product_info(id_: Optional[uuid.UUID], db: Session):
if id_ is None:
product = {
"code": "(Auto)",
@ -249,7 +207,7 @@ def product_info(id_, dbsession):
"isSold": False,
}
else:
product = dbsession.query(Product).filter(Product.id == id_).first()
product = db.query(Product).filter(Product.id == id_).first()
product = {
"id": product.id,
"code": product.code,
@ -270,17 +228,17 @@ def product_info(id_, dbsession):
return product
def delete_with_data(product, request):
def delete_with_data(product: Product, db: Session):
suspense_product = (
request.dbsession.query(Product)
db.query(Product)
.filter(Product.id == Product.suspense())
.first()
)
suspense_batch = (
request.dbsession.query(Batch).filter(Batch.id == Batch.suspense()).first()
db.query(Batch).filter(Batch.id == Batch.suspense()).first()
)
query = (
request.dbsession.query(Voucher)
db.query(Voucher)
.options(joinedload_all(Voucher.inventories, Inventory.product, innerjoin=True))
.filter(Voucher.inventories.any(Inventory.product_id == product.id))
.all()
@ -296,7 +254,7 @@ def delete_with_data(product, request):
else:
others = True
if not others and voucher.type == VoucherType.by_id("Issue"):
request.dbsession.delete(voucher)
db.delete(voucher)
else:
if sus_inv is None:
prod_inv.product = suspense_product
@ -305,15 +263,11 @@ def delete_with_data(product, request):
prod_inv.tax = 0
prod_inv.discount = 0
prod_inv.batch = suspense_batch
voucher.narration += "\nSuspense \u20B9{0:,.2f} is {1}".format(
prod_inv.amount, product.name
)
voucher.narration += f"\nSuspense \u20B9{prod_inv.amount:,.2f} is {product.name}"
else:
sus_inv.quantity += prod_inv.amount
request.dbsession.delete(prod_inv)
voucher.narration += "\nDeleted \u20B9{0:,.2f} of {1}".format(
prod_inv.amount, product.name
)
db.delete(prod_inv)
voucher.narration += f"\nDeleted \u20B9{prod_inv.amount:,.2f} of {product.name}"
for batch in product.batches:
request.dbsession.delete(batch)
request.dbsession.delete(product)
db.delete(batch)
db.delete(product)

View File

@ -1,90 +1,150 @@
import traceback
import uuid
from typing import List, Optional
from fastapi import APIRouter, HTTPException, status, Depends, Security
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
import brewman.schemas.master as schemas
from ..core.security import User, get_current_active_user as get_user
from ..db.session import SessionLocal
from brewman.models.master import ProductGroup
from brewman.models.validation_exception import ValidationError
from fastapi import APIRouter
router = APIRouter()
@router.post("/new") # "Product Groups"
def save(request):
item = ProductGroup(request.json_body["name"])
request.dbsession.add(item)
transaction.commit()
return product_group_info(item.id, request.dbsession)
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.put("/{id}") # "Product Groups"
def update(request):
item = (
request.dbsession.query(ProductGroup)
.filter(ProductGroup.id == uuid.UUID(request.matchdict["id"]))
.first()
)
if item.is_fixture:
raise ValidationError(
"{0} is a fixture and cannot be edited or deleted.".format(item.name)
@router.post("/", response_model=schemas.ProductGroup)
def save(
data: schemas.ProductGroupIn,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["product-groups"]),
):
try:
item = ProductGroup(name=data.name)
db.add(item)
db.commit()
return product_group_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=traceback.format_exc(),
)
item.name = request.json_body["name"]
transaction.commit()
return product_group_info(item.id, request.dbsession)
@router.delete("/{id}") # "Product Groups"
def delete(request):
item = (
request.dbsession.query(ProductGroup)
.filter(ProductGroup.id == uuid.UUID(request.matchdict["id"]))
.first()
)
@router.put("/{id_}", response_model=schemas.ProductGroup)
def update(
id_: uuid.UUID,
data: schemas.ProductGroupIn,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["product-groups"]),
):
try:
item = db.query(ProductGroup).filter(ProductGroup.id == id_).first()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
db.commit()
return product_group_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=traceback.format_exc(),
)
@router.delete("/{id_}")
def delete(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["product-groups"]),
):
try:
item = db.query(ProductGroup).filter(ProductGroup.id == id_).first()
if item is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Product Group not found",
)
elif item.is_fixture:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
else:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Product Group deletion not implemented",
)
except Exception:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=traceback.format_exc(),
)
@router.get("/")
def show_blank(
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["product-groups"]),
):
return product_group_info(None, db)
@router.get("/list", response_model=List[schemas.ProductGroup])
async def show_list(db: Session = Depends(get_db), user: User = Depends(get_user)):
return[
{"id": item.id, "name": item.name, "isFixture": item.is_fixture}
for item in db.query(ProductGroup).order_by(ProductGroup.name).all()
]
@router.get("/{id_}", response_model=schemas.ProductGroup)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["product-groups"]),
):
item = db.query(ProductGroup).filter(ProductGroup.id == id_).first()
return product_group_info(item, db)
def product_group_info(item: Optional[ProductGroup], db):
if item is None:
response = Response("Product Group not Found")
response.status_int = 500
return response
elif item.is_fixture:
raise ValidationError(
"{0} is a fixture and cannot be edited or deleted.".format(item.name)
)
else:
response = Response("Product Group deletion not implemented")
response.status_int = 500
return response
@router.get("/{id}") # "Product Groups"
def show_id(request):
return product_group_info(
uuid.UUID(request.matchdict.get("id", None)), request.dbsession
)
@router.get("/new") # "Product Groups"
def show_blank(request):
return product_group_info(None, request.dbsession)
@router.get("/") # "Authenticated"
async def show_list(l: bool):
list_ = request.dbsession.query(ProductGroup).order_by(ProductGroup.name).all()
product_groups = []
for item in list_:
product_groups.append(
{"id": item.id, "name": item.name, "isFixture": item.is_fixture}
)
return product_groups
def product_group_info(id_, dbsession):
if id_ is None:
return {"name": ''}
else:
product_group = (
dbsession.query(ProductGroup).filter(ProductGroup.id == id_).first()
)
return {
"id": product_group.id,
"name": product_group.name,
"isFixture": product_group.is_fixture,
"name": "",
"isFixture": False,
}
else:
return {
"id": item.id,
"name": item.name,
"isFixture": item.is_fixture,
}

View File

@ -11,23 +11,59 @@ def to_camel(string: str) -> str:
return ''.join([first] + [word.capitalize() for word in others])
class Product(BaseModel):
class AccountLink(BaseModel):
id_: uuid.UUID = Field(...)
class Config:
fields = {'id_': 'id'}
class ProductGroupIn(BaseModel):
name: str = Field(..., min_length=1)
class ProductGroup(ProductGroupIn):
id_: uuid.UUID
code: int
name: str
units: str
fraction: Decimal
fraction_units: str
product_yield: Decimal
product_group_id: uuid.UUID
account_id: uuid.UUID
price: Decimal
sale_price: Decimal
is_active: bool
is_fixture: bool
class Config:
fields = {'id_': 'id'}
anystr_strip_whitespace = True
alias_generator = to_camel
class ProductGroupLink(BaseModel):
id_: uuid.UUID = Field(...)
class Config:
fields = {'id_': 'id'}
class ProductIn(BaseModel):
name: str = Field(..., min_length=1)
units: str
fraction: Decimal = Field(ge=0, multiple_of=0.00001, default=0)
fraction_units: str
product_yield: Decimal = Field(ge=0, le=1, multiple_of=0.00001, default=1)
product_group: ProductGroupLink = Field(...)
account_id: AccountLink = Field(...)
price: Decimal = Field(ge=0, multiple_of=0.01, default=0)
sale_price: Decimal = Field(ge=0, multiple_of=0.01, default=0)
is_active: bool
is_purchased: bool
is_sold: bool
class Config:
fields = {'id_': 'id'}
anystr_strip_whitespace = True
alias_generator = to_camel
class Product(ProductIn):
id_: uuid.UUID
code: int
is_fixture: bool
class Recipe(BaseModel):
id_: uuid.UUID
@ -53,14 +89,8 @@ class RecipeItem(BaseModel):
price: int
class ProductGroup(BaseModel):
id_: uuid.UUID
name: str
is_fixture: bool
class CostCentreLink(BaseModel):
id_: uuid.UUID
id_: uuid.UUID = Field(...)
class Config:
fields = {'id_': 'id'}