Done: Product Group
Done: Product
This commit is contained in:
parent
aae48faf91
commit
9383cf84d4
brewman
@ -111,6 +111,18 @@ class Product(Base):
|
|||||||
return False, "Product has entries"
|
return False, "Product has entries"
|
||||||
return True, ""
|
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
|
@classmethod
|
||||||
def suspense(cls):
|
def suspense(cls):
|
||||||
return uuid.UUID("aa79a643-9ddc-4790-ac7f-a41f9efb4c15")
|
return uuid.UUID("aa79a643-9ddc-4790-ac7f-a41f9efb4c15")
|
||||||
@ -303,20 +315,20 @@ class AccountBase(Base):
|
|||||||
self.is_fixture = is_fixture
|
self.is_fixture = is_fixture
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls, type, name, reconcilable=None, active=None, dbsession=None):
|
def query(cls, q, type_, reconcilable=None, active=None, db=None):
|
||||||
query = dbsession.query(cls)
|
query_ = db.query(cls)
|
||||||
if type is not None:
|
if type_ is not None:
|
||||||
if not isinstance(type, int):
|
if not isinstance(type_, int):
|
||||||
type = int(type)
|
type_ = int(type_)
|
||||||
query = query.filter(cls.type == type)
|
query_ = query_.filter(cls.type == type_)
|
||||||
if reconcilable is not None:
|
if reconcilable is not None:
|
||||||
query = query.filter(cls.is_reconcilable == reconcilable)
|
query_ = query_.filter(cls.is_reconcilable == reconcilable)
|
||||||
if active is not None:
|
if active is not None:
|
||||||
query = query.filter(cls.is_active == active)
|
query_ = query_.filter(cls.is_active == active)
|
||||||
if name is not None:
|
if q is not None:
|
||||||
for item in name.split():
|
for item in q.split():
|
||||||
query = query.filter(cls.name.ilike("%" + item + "%"))
|
query_ = query_.filter(cls.name.ilike("%" + item + "%"))
|
||||||
return query.order_by(cls.name)
|
return query_.order_by(cls.name)
|
||||||
|
|
||||||
def create(self, dbsession):
|
def create(self, dbsession):
|
||||||
code = (
|
code = (
|
||||||
|
@ -151,7 +151,7 @@ async def show_term(
|
|||||||
count = c
|
count = c
|
||||||
|
|
||||||
list_ = []
|
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})
|
list_.append({"id": item.id, "name": item.name})
|
||||||
if count is not None and index == count - 1:
|
if count is not None and index == count - 1:
|
||||||
break
|
break
|
||||||
@ -195,7 +195,7 @@ def balance(id_: uuid.UUID, date, db: Session):
|
|||||||
return 0 if bal is None else bal
|
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:
|
if id_ is None:
|
||||||
account = {
|
account = {
|
||||||
"code": "(Auto)",
|
"code": "(Auto)",
|
||||||
@ -224,7 +224,7 @@ def account_info(id_, db):
|
|||||||
return account
|
return account
|
||||||
|
|
||||||
|
|
||||||
def delete_with_data(account, db):
|
def delete_with_data(account: Account, db: Session):
|
||||||
suspense_account = (
|
suspense_account = (
|
||||||
db.query(Account).filter(Account.id == Account.suspense()).first()
|
db.query(Account).filter(Account.id == Account.suspense()).first()
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import traceback
|
import traceback
|
||||||
import uuid
|
import uuid
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, status, Depends, Security
|
from fastapi import APIRouter, HTTPException, status, Depends, Security
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@ -28,10 +29,16 @@ def save(
|
|||||||
user: User = Security(get_user, scopes=["cost-centres"]),
|
user: User = Security(get_user, scopes=["cost-centres"]),
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
item = CostCentre(data.name)
|
item = CostCentre(name=data.name)
|
||||||
db.add(item)
|
db.add(item)
|
||||||
db.commit()
|
db.commit()
|
||||||
return cost_centre_info(item, db)
|
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:
|
except Exception:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
@ -1,245 +1,203 @@
|
|||||||
|
import traceback
|
||||||
import uuid
|
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 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
|
import brewman.schemas.master as schemas
|
||||||
from brewman.models.validation_exception import ValidationError
|
from ..core.security import User, get_current_active_user as get_user
|
||||||
from brewman.models.voucher import Voucher, Batch, Inventory, VoucherType
|
from ..db.session import SessionLocal
|
||||||
|
from ..models.master import Product, Account
|
||||||
from fastapi import APIRouter
|
from ..models.voucher import Voucher, Batch, Inventory, VoucherType
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/new") # "Products"
|
# Dependency
|
||||||
def save(request):
|
def get_db():
|
||||||
json = request.json_body
|
|
||||||
|
|
||||||
name = json.get("name", "").strip()
|
|
||||||
if name == "":
|
|
||||||
raise ValidationError("Name cannot be blank")
|
|
||||||
|
|
||||||
units = json.get("units", "").strip()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fraction = Decimal(json.get("fraction", 0))
|
db = SessionLocal()
|
||||||
if fraction <= 0:
|
yield db
|
||||||
raise ValidationError("Fraction must be a decimal > 0")
|
finally:
|
||||||
except (ValueError, InvalidOperation):
|
db.close()
|
||||||
raise ValidationError("Fraction must be a decimal > 0")
|
|
||||||
|
|
||||||
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:
|
try:
|
||||||
product_yield = Decimal(json.get("productYield", 1))
|
item = Product(
|
||||||
if product_yield <= 0 or product_yield > 1:
|
name=data.name,
|
||||||
raise ValidationError("Yield must be a decimal > 0 <= 1")
|
units=data.units,
|
||||||
except (ValueError, InvalidOperation):
|
fraction=data.fraction,
|
||||||
raise ValidationError("Yield must be a decimal > 0 <= 1")
|
fraction_units=data.fraction_units,
|
||||||
|
product_yield=data.product_yield,
|
||||||
product_group = json.get("productGroup", None)
|
product_group_id=data.product_group.id_,
|
||||||
if product_group is None:
|
account_id=Account.all_purchases(),
|
||||||
raise ValidationError("please choose a product group")
|
price=data.price,
|
||||||
product_group_id = uuid.UUID(product_group["id"])
|
sale_price=data.sale_price,
|
||||||
try:
|
is_active=data.is_active,
|
||||||
price = Decimal(json.get("price", 0))
|
is_purchased=data.is_purchased,
|
||||||
if price < 0:
|
is_sold=data.is_sold
|
||||||
raise ValidationError("Price must be a decimal >= 0")
|
).create(db)
|
||||||
except (ValueError, InvalidOperation):
|
db.commit()
|
||||||
raise ValidationError("Price must be a decimal >= 0")
|
return product_info(item.id, db)
|
||||||
try:
|
except SQLAlchemyError as e:
|
||||||
sale_price = Decimal(json.get("salePrice", 0))
|
db.rollback()
|
||||||
if price < 0:
|
raise HTTPException(
|
||||||
raise ValidationError("Sale Price must be a decimal >= 0")
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
except (ValueError, InvalidOperation):
|
detail=str(e),
|
||||||
raise ValidationError("Price must be a decimal >= 0")
|
)
|
||||||
is_active = json.get("isActive", True)
|
except Exception:
|
||||||
is_purchased = json.get("isPurchased", True)
|
db.rollback()
|
||||||
is_sold = json.get("isSold", True)
|
raise HTTPException(
|
||||||
item = Product(
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
0,
|
detail=traceback.format_exc(),
|
||||||
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.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"
|
@router.put("/{id_}", response_model=schemas.Product)
|
||||||
def delete(request):
|
def update(
|
||||||
product = (
|
id_: uuid.UUID,
|
||||||
request.dbsession.query(Product)
|
data: schemas.ProductIn,
|
||||||
.filter(Product.id == uuid.UUID(request.matchdict["id"]))
|
db: Session = Depends(get_db),
|
||||||
.first()
|
user: User = Security(get_user, scopes=["products"]),
|
||||||
)
|
):
|
||||||
can_delete, reason = product.can_delete(request.has_permission("Advanced Delete"))
|
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:
|
if can_delete:
|
||||||
delete_with_data(product, request)
|
delete_with_data(product, db)
|
||||||
transaction.commit()
|
db.commit()
|
||||||
return product_info(None, request.dbsession)
|
return product_info(None, db)
|
||||||
else:
|
else:
|
||||||
transaction.abort()
|
db.abort()
|
||||||
response = Response("Cannot delete product because {0}".format(reason))
|
raise HTTPException(
|
||||||
response.status_int = 500
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
return response
|
detail=f"Cannot delete account because {reason}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{id}") # "Products"
|
@router.get("/") # "Products"
|
||||||
def show_id(request):
|
def show_blank(
|
||||||
return product_info(uuid.UUID(request.matchdict.get("id", None)), request.dbsession)
|
db: Session = Depends(get_db), user: User = Security(get_user, scopes=["products"])
|
||||||
|
):
|
||||||
|
return product_info(None, db)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/new") # "Products"
|
@router.get("/list")
|
||||||
def show_blank(request):
|
def show_list(db: Session = Depends(get_db), user: User = Depends(get_user)):
|
||||||
return product_info(None, request.dbsession)
|
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"
|
@router.get("/query")
|
||||||
def show_list(l: bool):
|
async def show_term(
|
||||||
list_ = (
|
t: str = None,
|
||||||
request.dbsession.query(Product)
|
a: bool = None,
|
||||||
.order_by(desc(Product.is_active))
|
c: int = None,
|
||||||
.order_by(Product.product_group_id)
|
p: bool = None,
|
||||||
.order_by(Product.name)
|
e: bool = False,
|
||||||
.all()
|
db: Session = Depends(get_db),
|
||||||
)
|
current_user: User = Depends(get_user),
|
||||||
products = []
|
):
|
||||||
for item in list_:
|
count = c
|
||||||
products.append(
|
extended = e
|
||||||
|
list_ = []
|
||||||
|
for index, item in enumerate(Product.query(t, p, a, db)):
|
||||||
|
list_.append(
|
||||||
{
|
{
|
||||||
"id": item.id,
|
"id": item.id,
|
||||||
"code": item.code,
|
"name": item.full_name,
|
||||||
"name": item.name,
|
"price": item.price,
|
||||||
"units": item.units,
|
"units": item.units,
|
||||||
"costPrice": item.price,
|
|
||||||
"salePrice": item.sale_price,
|
|
||||||
"productGroup": item.product_group.name,
|
|
||||||
"isActive": item.is_active,
|
|
||||||
"fraction": item.fraction,
|
"fraction": item.fraction,
|
||||||
"fractionUnits": item.fraction_units,
|
"fractionUnits": item.fraction_units,
|
||||||
"isPurchased": item.is_purchased,
|
|
||||||
"isSold": item.is_sold,
|
|
||||||
"productYield": item.product_yield,
|
"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"
|
@router.get("/{id_}")
|
||||||
async def show_term(t: str):
|
def show_id(
|
||||||
term = request.GET.get("t", None)
|
id_: uuid.UUID,
|
||||||
term = term.strip() if term is not None and term.strip() != "" else None
|
db: Session = Depends(get_db),
|
||||||
active = request.GET.get("a", None)
|
user: User = Security(get_user, scopes=["accounts"]),
|
||||||
active = active if active is not None else None
|
):
|
||||||
count = request.GET.get("c", None)
|
return product_info(id_, db)
|
||||||
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"])
|
|
||||||
|
|
||||||
|
|
||||||
def product_info(id_, dbsession):
|
def product_info(id_: Optional[uuid.UUID], db: Session):
|
||||||
if id_ is None:
|
if id_ is None:
|
||||||
product = {
|
product = {
|
||||||
"code": "(Auto)",
|
"code": "(Auto)",
|
||||||
@ -249,7 +207,7 @@ def product_info(id_, dbsession):
|
|||||||
"isSold": False,
|
"isSold": False,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
product = dbsession.query(Product).filter(Product.id == id_).first()
|
product = db.query(Product).filter(Product.id == id_).first()
|
||||||
product = {
|
product = {
|
||||||
"id": product.id,
|
"id": product.id,
|
||||||
"code": product.code,
|
"code": product.code,
|
||||||
@ -270,17 +228,17 @@ def product_info(id_, dbsession):
|
|||||||
return product
|
return product
|
||||||
|
|
||||||
|
|
||||||
def delete_with_data(product, request):
|
def delete_with_data(product: Product, db: Session):
|
||||||
suspense_product = (
|
suspense_product = (
|
||||||
request.dbsession.query(Product)
|
db.query(Product)
|
||||||
.filter(Product.id == Product.suspense())
|
.filter(Product.id == Product.suspense())
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
suspense_batch = (
|
suspense_batch = (
|
||||||
request.dbsession.query(Batch).filter(Batch.id == Batch.suspense()).first()
|
db.query(Batch).filter(Batch.id == Batch.suspense()).first()
|
||||||
)
|
)
|
||||||
query = (
|
query = (
|
||||||
request.dbsession.query(Voucher)
|
db.query(Voucher)
|
||||||
.options(joinedload_all(Voucher.inventories, Inventory.product, innerjoin=True))
|
.options(joinedload_all(Voucher.inventories, Inventory.product, innerjoin=True))
|
||||||
.filter(Voucher.inventories.any(Inventory.product_id == product.id))
|
.filter(Voucher.inventories.any(Inventory.product_id == product.id))
|
||||||
.all()
|
.all()
|
||||||
@ -296,7 +254,7 @@ def delete_with_data(product, request):
|
|||||||
else:
|
else:
|
||||||
others = True
|
others = True
|
||||||
if not others and voucher.type == VoucherType.by_id("Issue"):
|
if not others and voucher.type == VoucherType.by_id("Issue"):
|
||||||
request.dbsession.delete(voucher)
|
db.delete(voucher)
|
||||||
else:
|
else:
|
||||||
if sus_inv is None:
|
if sus_inv is None:
|
||||||
prod_inv.product = suspense_product
|
prod_inv.product = suspense_product
|
||||||
@ -305,15 +263,11 @@ def delete_with_data(product, request):
|
|||||||
prod_inv.tax = 0
|
prod_inv.tax = 0
|
||||||
prod_inv.discount = 0
|
prod_inv.discount = 0
|
||||||
prod_inv.batch = suspense_batch
|
prod_inv.batch = suspense_batch
|
||||||
voucher.narration += "\nSuspense \u20B9{0:,.2f} is {1}".format(
|
voucher.narration += f"\nSuspense \u20B9{prod_inv.amount:,.2f} is {product.name}"
|
||||||
prod_inv.amount, product.name
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
sus_inv.quantity += prod_inv.amount
|
sus_inv.quantity += prod_inv.amount
|
||||||
request.dbsession.delete(prod_inv)
|
db.delete(prod_inv)
|
||||||
voucher.narration += "\nDeleted \u20B9{0:,.2f} of {1}".format(
|
voucher.narration += f"\nDeleted \u20B9{prod_inv.amount:,.2f} of {product.name}"
|
||||||
prod_inv.amount, product.name
|
|
||||||
)
|
|
||||||
for batch in product.batches:
|
for batch in product.batches:
|
||||||
request.dbsession.delete(batch)
|
db.delete(batch)
|
||||||
request.dbsession.delete(product)
|
db.delete(product)
|
||||||
|
@ -1,90 +1,150 @@
|
|||||||
|
import traceback
|
||||||
import uuid
|
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.master import ProductGroup
|
||||||
from brewman.models.validation_exception import ValidationError
|
|
||||||
from fastapi import APIRouter
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/new") # "Product Groups"
|
# Dependency
|
||||||
def save(request):
|
def get_db():
|
||||||
item = ProductGroup(request.json_body["name"])
|
try:
|
||||||
request.dbsession.add(item)
|
db = SessionLocal()
|
||||||
transaction.commit()
|
yield db
|
||||||
return product_group_info(item.id, request.dbsession)
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{id}") # "Product Groups"
|
@router.post("/", response_model=schemas.ProductGroup)
|
||||||
def update(request):
|
def save(
|
||||||
item = (
|
data: schemas.ProductGroupIn,
|
||||||
request.dbsession.query(ProductGroup)
|
db: Session = Depends(get_db),
|
||||||
.filter(ProductGroup.id == uuid.UUID(request.matchdict["id"]))
|
user: User = Security(get_user, scopes=["product-groups"]),
|
||||||
.first()
|
):
|
||||||
)
|
try:
|
||||||
if item.is_fixture:
|
item = ProductGroup(name=data.name)
|
||||||
raise ValidationError(
|
db.add(item)
|
||||||
"{0} is a fixture and cannot be edited or deleted.".format(item.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(),
|
||||||
)
|
)
|
||||||
item.name = request.json_body["name"]
|
|
||||||
transaction.commit()
|
|
||||||
return product_group_info(item.id, request.dbsession)
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{id}") # "Product Groups"
|
@router.put("/{id_}", response_model=schemas.ProductGroup)
|
||||||
def delete(request):
|
def update(
|
||||||
item = (
|
id_: uuid.UUID,
|
||||||
request.dbsession.query(ProductGroup)
|
data: schemas.ProductGroupIn,
|
||||||
.filter(ProductGroup.id == uuid.UUID(request.matchdict["id"]))
|
db: Session = Depends(get_db),
|
||||||
.first()
|
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:
|
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 {
|
return {
|
||||||
"id": product_group.id,
|
"name": "",
|
||||||
"name": product_group.name,
|
"isFixture": False,
|
||||||
"isFixture": product_group.is_fixture,
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"id": item.id,
|
||||||
|
"name": item.name,
|
||||||
|
"isFixture": item.is_fixture,
|
||||||
}
|
}
|
||||||
|
@ -11,23 +11,59 @@ def to_camel(string: str) -> str:
|
|||||||
return ''.join([first] + [word.capitalize() for word in others])
|
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
|
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
|
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_purchased: bool
|
||||||
is_sold: 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):
|
class Recipe(BaseModel):
|
||||||
id_: uuid.UUID
|
id_: uuid.UUID
|
||||||
@ -53,14 +89,8 @@ class RecipeItem(BaseModel):
|
|||||||
price: int
|
price: int
|
||||||
|
|
||||||
|
|
||||||
class ProductGroup(BaseModel):
|
|
||||||
id_: uuid.UUID
|
|
||||||
name: str
|
|
||||||
is_fixture: bool
|
|
||||||
|
|
||||||
|
|
||||||
class CostCentreLink(BaseModel):
|
class CostCentreLink(BaseModel):
|
||||||
id_: uuid.UUID
|
id_: uuid.UUID = Field(...)
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
fields = {'id_': 'id'}
|
fields = {'id_': 'id'}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user