From df1610557ffc5fb314250ff08cfa85938962c496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Gr=C3=B6ger?= Date: Mon, 15 Mar 2021 19:56:51 +0100 Subject: [PATCH] [pricelist] break api, new model --- flaschengeist/plugins/pricelist/__init__.py | 30 ++++- flaschengeist/plugins/pricelist/models.py | 90 ++++++++++--- .../plugins/pricelist/pricelist_controller.py | 126 ++++++------------ 3 files changed, 140 insertions(+), 106 deletions(-) diff --git a/flaschengeist/plugins/pricelist/__init__.py b/flaschengeist/plugins/pricelist/__init__.py index 8c9e3d9..5df895b 100644 --- a/flaschengeist/plugins/pricelist/__init__.py +++ b/flaschengeist/plugins/pricelist/__init__.py @@ -124,4 +124,32 @@ def create_drink(current_session): item in data for item in ["name", "ingredients"] ): raise BadRequest("No correct Keys to create drink") - return jsonify(pricelist_controller.create_drink(data)) + return "jsonify(pricelist_controller.create_drink(data))" + + +@pricelist_bp.route("/prices", methods=["GET"]) +@pricelist_bp.route("/prices/", methods=["GET"]) +def get_prices(identifier=None): + if identifier: + result = pricelist_controller.get_price(identifier) + else: + result = pricelist_controller.get_prices() + return jsonify(result) + + +@pricelist_bp.route("/prices/volumes/", methods=["POST"]) +def create_price(identifier): + data = request.get_json() + return jsonify(pricelist_controller.set_price(identifier, data)) + + +@pricelist_bp.route("/prices/", methods=["PUT"]) +def modify_price(identifier): + data = request.get_json() + return jsonify(pricelist_controller.update_price(identifier, data)) + + +@pricelist_bp.route("/prices/", methods=["DELETE"]) +def delete_price(identifier): + pricelist_controller.delete_price(identifier) + return NO_CONTENT diff --git a/flaschengeist/plugins/pricelist/models.py b/flaschengeist/plugins/pricelist/models.py index 1eb9ae6..c64d6c3 100644 --- a/flaschengeist/plugins/pricelist/models.py +++ b/flaschengeist/plugins/pricelist/models.py @@ -43,28 +43,78 @@ class DrinkPrice(db.Model, ModelSerializeMixin): __tablename__ = "drink_price" id: int = db.Column("id", db.Integer, primary_key=True) - volume: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False), nullable=False) price: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) - drink_id = db.Column("drink_id", db.Integer, db.ForeignKey("drink.id")) - drink = db.relationship("Drink", back_populates="prices") - no_auto: bool = db.Column(db.Boolean, default=False) + volume_id_ = db.Column("volume_id", db.Integer, db.ForeignKey("drink_price_volume.id")) + volume = db.relationship("DrinkPriceVolume", back_populates="prices") public: bool = db.Column(db.Boolean, default=True) description: Optional[str] = db.Column(db.String(30)) - round_step: float = db.Column(db.Numeric(precision=3, scale=2, asdecimal=False), nullable=False, default=0.5) -class Ingredient(db.Model, ModelSerializeMixin): +class ExtraIngredient(db.Model, ModelSerializeMixin): """ - Drink Build + ExtraIngredient + """ + + __tablename__ = "extra_ingredient" + id: int = db.Column("id", db.Integer, primary_key=True) + name: str = db.Column(db.String(30), unique=True, nullable=False) + price: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) + + +class DrinkIngredient(db.Model, ModelSerializeMixin): + """ + Drink Ingredient """ __tablename__ = "drink_ingredient" id: int = db.Column("id", db.Integer, primary_key=True) volume: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False), nullable=False) - drink_parent_id: int = db.Column("drink_parent_id", db.Integer, db.ForeignKey("drink.id")) - drink_parent = db.relationship("Drink", foreign_keys=drink_parent_id) drink_ingredient_id: int = db.Column("drink_ingredient_id", db.Integer, db.ForeignKey("drink.id")) - drink_ingredient: "Drink" = db.relationship("Drink", foreign_keys=drink_ingredient_id) + drink_ingredient: "Drink" = db.relationship("Drink") + price: float = 0 + + @property + def price(self): + return self.drink_ingredient.cost_price_pro_volume * self.volume + + +class Ingredient(db.Model, ModelSerializeMixin): + """ + Ingredient Associationtable + """ + + __tablename__ = "ingredient_association" + id: int = db.Column("id", db.Integer, primary_key=True) + volume_id: int = db.Column(db.Integer, db.ForeignKey("drink_price_volume.id")) + drink_ingredient_id = db.Column(db.Integer, db.ForeignKey("drink_ingredient.id")) + drink_ingredient: DrinkIngredient = db.relationship(DrinkIngredient) + extra_ingredient_id = db.Column(db.Integer, db.ForeignKey("extra_ingredient.id")) + extra_ingredient: ExtraIngredient = db.relationship(ExtraIngredient) + + +class DrinkPriceVolume(db.Model, ModelSerializeMixin): + """ + Drink Volumes and Prices + """ + + __tablename__ = "drink_price_volume" + id: int = db.Column("id", db.Integer, primary_key=True) + volume: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) + prices: [DrinkPrice] = db.relationship(DrinkPrice, back_populates="volume", cascade="all,delete,delete-orphan") + ingredients: [DrinkIngredient or ExtraIngredient] = [] + _ingredients: [Ingredient] = db.relationship("Ingredient", foreign_keys=Ingredient.volume_id) + + drink_id = db.Column(db.Integer, db.ForeignKey("drink.id"), nullable=False) + + @property + def ingredients(self): + retVal = [] + for ingredient in self._ingredients: + if ingredient.drink_ingredient_id != None: + retVal.append(ingredient.drink_ingredient) + if ingredient.extra_ingredient_id != None: + retVal.append(ingredient.extra_ingredient) + return retVal class Drink(db.Model, ModelSerializeMixin): @@ -74,17 +124,15 @@ class Drink(db.Model, ModelSerializeMixin): __tablename__ = "drink" id: int = db.Column("id", db.Integer, primary_key=True) + article_id: Optional[str] = db.Column(db.String(64)) + package_size: Optional[int] = db.Column(db.Integer) name: str = db.Column(db.String(60), nullable=False) - volume: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) - cost_price: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) - discount: float = db.Column(db.Numeric(precision=3, scale=2, asdecimal=False), nullable=False) - extra_charge: Optional[float] = db.Column(db.Numeric(precision=3, scale=2, asdecimal=False), default=0) - prices: [DrinkPrice] = db.relationship( - "DrinkPrice", back_populates="drink", cascade="all,delete,delete-orphan", order_by=[DrinkPrice.volume] - ) - ingredients: [Ingredient] = db.relationship( - "Ingredient", back_populates="drink_parent", foreign_keys=Ingredient.drink_parent_id - ) + volume: Optional[float] = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) + cost_price_pro_volume: Optional[float] = db.Column(db.Numeric(precision=5, scale=3, asdecimal=False)) + cost_price_package_netto: Optional[float] = db.Column(db.Numeric(precision=5, scale=3, asdecimal=False)) + tags: [Optional[Tag]] = db.relationship("Tag", secondary=drink_tag_association, cascade="save-update, merge") type_id_ = db.Column("type_id", db.Integer, db.ForeignKey("drink_type.id")) - type = db.relationship("DrinkType") + type: DrinkType = db.relationship("DrinkType") + + volumes: [DrinkPriceVolume] = db.relationship(DrinkPriceVolume) diff --git a/flaschengeist/plugins/pricelist/pricelist_controller.py b/flaschengeist/plugins/pricelist/pricelist_controller.py index 93c365c..d308504 100644 --- a/flaschengeist/plugins/pricelist/pricelist_controller.py +++ b/flaschengeist/plugins/pricelist/pricelist_controller.py @@ -3,7 +3,7 @@ from sqlalchemy.exc import IntegrityError from flaschengeist import logger from flaschengeist.database import db -from .models import Drink, DrinkPrice, Ingredient, Tag, DrinkType +from .models import Drink, DrinkPrice, Ingredient, Tag, DrinkType, DrinkPriceVolume, DrinkIngredient, ExtraIngredient from math import ceil @@ -103,56 +103,6 @@ def delete_drink_type(identifier): raise BadRequest("DrinkType still in use") -def round_price(price, round_step): - return round(ceil(float(price) / round_step) * round_step * 100) / 100 - - -def calc_prices(drink, prices): - retVal = [] - if len(drink.ingredients) > 0: - return calc_price_by_ingredients(drink, prices) - allowed_keys = DrinkPrice().serialize().keys() - for price in prices: - values = {key: value for key, value in price.items() if key in allowed_keys} - if values.get("no_auto"): - retVal.append(DrinkPrice(**values)) - else: - volume = float(values.pop("volume")) - if "price" in values: - values.pop("price") - _price = float(drink.cost_price) / float(drink.volume) * volume - _price += _price * float(drink.discount) - if drink.extra_charge: - _price += float(drink.extra_charge) - _price = round_price(_price, float(price.get("round_step"))) - retVal.append(DrinkPrice(volume=volume, price=_price, **values)) - return retVal - - -def calc_price_by_ingredients(drink, prices): - allowed_keys = DrinkPrice().serialize().keys() - retVal = [] - for price in prices: - values = {key: value for key, value in price.items() if key in allowed_keys} - if values.get("no_auto"): - retVal.append(DrinkPrice(**values)) - else: - volume = float(values.pop("volume")) - if "price" in values: - values.pop("price") - _price = 0 - for ingredient in drink.ingredients: - _price = ( - float(ingredient.drink_ingredient.cost_price) - / float(ingredient.drink_ingredient.volume) - * float(ingredient.volume) - ) - _price += _price * float(drink.discount) + float(drink.extra_charge) - _price = round_price(_price, price.get("round_step")) - retVal.append(DrinkPrice(volume=volume, price=_price, **values)) - return retVal - - def get_drinks(name=None): if name: return Drink.query.filter(Drink.name.contains(name)).all() @@ -161,49 +111,57 @@ def get_drinks(name=None): def get_drink(identifier): if isinstance(identifier, int): - retVal = Drink.query.get(identifier) + return Drink.query.get(identifier) elif isinstance(identifier, str): - retVal = Drink.query.filter(Tag.name == identifier).one_or_none() + return Drink.query.filter(Tag.name == identifier).one_or_none() else: logger.debug("Invalid identifier type for Drink") raise BadRequest - if not retVal: - raise NotFound - return retVal + raise NotFound -def add_prices(drink, prices): - for price in prices: - drink.prices.append(price) +def get_volume(identifier): + return DrinkPriceVolume.query.get(identifier) -def add_ingredients(drink, ingredients): - for identifier, volume in ingredients: - ingredient = Ingredient(volume=volume, drink_ingredient=get_drink(identifier)) - drink.ingredients.append(ingredient) +def get_price(identifier): + if isinstance(identifier, int): + return DrinkPrice.query.get(identifier) + raise NotFound -def create_drink(data): - allowed_keys = Drink().serialize().keys() +def get_prices(volume_id=None): + if volume_id: + return DrinkPrice.query.filter(DrinkPrice.volume_id_ == volume_id).all() + return DrinkPrice.query.all() + + +def set_price(identifier, data): + allowed_keys = DrinkPrice().serialize().keys() values = {key: value for key, value in data.items() if key in allowed_keys} - prices = values.pop("prices", []) - ingredients = values.pop("ingredients", []) - if "id" in values: - values.pop("id") - - drink = Drink(**values) - add_ingredients(drink, ingredients) - drink.prices = calc_prices(drink, prices) - db.session.add(drink) - update() - return drink + price = DrinkPrice(**values) + volume = get_volume(identifier) + if not volume: + raise BadRequest + volume.prices.append(price) + db.session.add(price) + db.session.commit() + return price -def delete_drink(identifier): - drink = get_drink(identifier) - for price in drink.prices: - db.session.delete(price) - for ingredient in drink.ingredients: - db.session.delete(ingredient) - db.session.delete(drink) - update() +def update_price(identifier, data): + allowed_keys = DrinkPrice().serialize().keys() + if "id" in data: + data.pop("id") + values = {key: value for key, value in data.items() if key in allowed_keys} + price = get_price(identifier) + for key, value in values: + setattr(price, key, value) + db.session.commit() + return price + + +def delete_price(identifier): + price = get_price(identifier) + db.session.delete(price) + db.session.commit()