diff --git a/flaschengeist/plugins/pricelist/__init__.py b/flaschengeist/plugins/pricelist/__init__.py index b4df1ac..afe21e4 100644 --- a/flaschengeist/plugins/pricelist/__init__.py +++ b/flaschengeist/plugins/pricelist/__init__.py @@ -214,10 +214,87 @@ def get_drinks(identifier=None): if identifier: result = pricelist_controller.get_drink(identifier, public=public) + return jsonify(result) else: - result = pricelist_controller.get_drinks(public=public) - logger.debug(f"GET drink {result}") - return jsonify(result) + limit = request.args.get("limit") + offset = request.args.get("offset") + search_name = request.args.get("search_name") + search_key = request.args.get("search_key") + ingredient = request.args.get("ingredient", type=bool) + receipt = request.args.get("receipt", type=bool) + try: + if limit is not None: + limit = int(limit) + if offset is not None: + offset = int(offset) + if ingredient is not None: + ingredient = bool(ingredient) + if receipt is not None: + receipt = bool(receipt) + except ValueError: + raise BadRequest + drinks, count = pricelist_controller.get_drinks( + public=public, + limit=limit, + offset=offset, + search_name=search_name, + search_key=search_key, + ingredient=ingredient, + receipt=receipt, + ) + mop = drinks.copy() + logger.debug(f"GET drink {drinks}, {count}") + # return jsonify({"drinks": drinks, "count": count}) + return jsonify({"drinks": drinks, "count": count}) + + +@PriceListPlugin.blueprint.route("/list", methods=["GET"]) +def get_pricelist(): + """Get Priclist + Route: ``/pricelist/list`` | Method: ``GET`` + + Returns: + JSON encoded list of DrinkPrices or HTTP-KeyError + """ + public = True + try: + extract_session() + public = False + except Unauthorized: + public = True + + limit = request.args.get("limit") + offset = request.args.get("offset") + search_name = request.args.get("search_name") + search_key = request.args.get("search_key") + ingredient = request.args.get("ingredient", type=bool) + receipt = request.args.get("receipt", type=bool) + descending = request.args.get("descending", type=bool) + sortBy = request.args.get("sortBy") + try: + if limit is not None: + limit = int(limit) + if offset is not None: + offset = int(offset) + if ingredient is not None: + ingredient = bool(ingredient) + if receipt is not None: + receipt = bool(receipt) + if descending is not None: + descending = bool(descending) + except ValueError: + raise BadRequest + pricelist, count = pricelist_controller.get_pricelist( + public=public, + limit=limit, + offset=offset, + search_name=search_name, + search_key=search_key, + descending=descending, + sortBy=sortBy, + ) + logger.debug(f"GET pricelist {pricelist}, {count}") + return jsonify({"pricelist": pricelist, "count": count}) @PriceListPlugin.blueprint.route("/drinks/search/", methods=["GET"]) diff --git a/flaschengeist/plugins/pricelist/models.py b/flaschengeist/plugins/pricelist/models.py index ffde797..43cf792 100644 --- a/flaschengeist/plugins/pricelist/models.py +++ b/flaschengeist/plugins/pricelist/models.py @@ -48,7 +48,8 @@ class DrinkPrice(db.Model, ModelSerializeMixin): id: int = db.Column("id", db.Integer, primary_key=True) price: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) volume_id_ = db.Column("volume_id", db.Integer, db.ForeignKey("drink_price_volume.id")) - volume = db.relationship("DrinkPriceVolume", back_populates="prices") + volume: "DrinkPriceVolume" = None + _volume: "DrinkPriceVolume" = db.relationship("DrinkPriceVolume", back_populates="_prices", join_depth=1) public: bool = db.Column(db.Boolean, default=True) description: Optional[str] = db.Column(db.String(30)) @@ -76,16 +77,17 @@ class DrinkIngredient(db.Model, ModelSerializeMixin): id: int = db.Column("id", db.Integer, primary_key=True) volume: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False), nullable=False) ingredient_id: int = db.Column(db.Integer, db.ForeignKey("drink.id")) - # drink_ingredient: Drink = db.relationship("Drink") - # price: float = 0 + cost_per_volume: float + name: str + _drink_ingredient: Drink = db.relationship("Drink") + @property + def cost_per_volume(self): + return self._drink_ingredient.cost_per_volume if self._drink_ingredient else None -# @property -# def price(self): -# try: -# return self.drink_ingredient.cost_price_pro_volume * self.volume -# except AttributeError: -# pass + @property + def name(self): + return self._drink_ingredient.name if self._drink_ingredient else None class Ingredient(db.Model, ModelSerializeMixin): @@ -120,11 +122,15 @@ class DrinkPriceVolume(db.Model, ModelSerializeMixin): __tablename__ = "drink_price_volume" id: int = db.Column("id", db.Integer, primary_key=True) drink_id = db.Column(db.Integer, db.ForeignKey("drink.id")) + drink: "Drink" = None + _drink: "Drink" = db.relationship("Drink", back_populates="_volumes") volume: float = db.Column(db.Numeric(precision=5, scale=2, asdecimal=False)) min_prices: list[MinPrices] = [] # ingredients: list[Ingredient] = [] - - prices: list[DrinkPrice] = db.relationship(DrinkPrice, back_populates="volume", cascade="all,delete,delete-orphan") + prices: list[DrinkPrice] = [] + _prices: list[DrinkPrice] = db.relationship( + DrinkPrice, back_populates="_volume", cascade="all,delete,delete-orphan" + ) ingredients: list[Ingredient] = db.relationship("Ingredient", foreign_keys=Ingredient.volume_id) def __repr__(self): @@ -152,7 +158,10 @@ class Drink(db.Model, ModelSerializeMixin): tags: Optional[list[Tag]] = db.relationship("Tag", secondary=drink_tag_association, cascade="save-update, merge") type: Optional[DrinkType] = db.relationship("DrinkType", foreign_keys=[_type_id]) - volumes: list[DrinkPriceVolume] = db.relationship(DrinkPriceVolume) + volumes: list[DrinkPriceVolume] = [] + _volumes: list[DrinkPriceVolume] = db.relationship( + DrinkPriceVolume, back_populates="_drink", cascade="all,delete,delete-orphan" + ) def __repr__(self): return f"Drink({self.id},{self.name},{self.volumes})" diff --git a/flaschengeist/plugins/pricelist/pricelist_controller.py b/flaschengeist/plugins/pricelist/pricelist_controller.py index 1c1491e..136711c 100644 --- a/flaschengeist/plugins/pricelist/pricelist_controller.py +++ b/flaschengeist/plugins/pricelist/pricelist_controller.py @@ -131,13 +131,130 @@ def _create_public_drink(drink): return None -def get_drinks(name=None, public=False): +def get_drinks( + name=None, public=False, limit=None, offset=None, search_name=None, search_key=None, ingredient=False, receipt=None +): + count = None if name: - drinks = Drink.query.filter(Drink.name.contains(name)).all() - drinks = Drink.query.all() + query = Drink.query.filter(Drink.name.contains(name)) + else: + query = Drink.query + if ingredient: + query = query.filter(Drink.cost_per_volume >= 0) + if receipt: + query = query.filter(Drink._volumes.any(DrinkPriceVolume.ingredients != None)) if public: - return [_create_public_drink(drink) for drink in drinks if _create_public_drink(drink)] - return drinks + query = query.filter(Drink._volumes.any(DrinkPriceVolume._prices.any(DrinkPrice.public))) + if search_name: + if search_key == "name": + query = query.filter(Drink.name.contains(search_name)) + elif search_key == "article_id": + query = query.filter(Drink.article_id.contains(search_name)) + elif search_key == "drink_type": + query = query.filter(Drink.type.has(DrinkType.name.contains(search_name))) + elif search_key == "tags": + query = query.filter(Drink.tags.any(Tag.name.contains(search_name))) + else: + query = query.filter( + (Drink.name.contains(search_name)) + | (Drink.article_id.contains(search_name)) + | (Drink.type.has(DrinkType.name.contains(search_name))) + | (Drink.tags.any(Tag.name.contains(search_name))) + ) + query = query.order_by(Drink.name.asc()) + + if limit is not None: + count = query.count() + query = query.limit(limit) + if offset is not None: + query = query.offset(offset) + drinks = query.all() + for drink in drinks: + for volume in drink._volumes: + volume.prices = volume._prices + drink.volumes = drink._volumes + + return drinks, count + + +def get_pricelist( + public=False, limit=None, offset=None, search_name=None, search_key=None, sortBy=None, descending=False +): + count = None + query = DrinkPrice.query + if public: + query = query.filter(DrinkPrice.public) + if search_name: + if search_key == "name": + query = query.filter(DrinkPrice._volume.has(DrinkPriceVolume._drink.has(Drink.name.contains(search_name)))) + if search_key == "type": + query = query.filter( + DrinkPrice._volume.has( + DrinkPriceVolume._drink.has(Drink.type.has(DrinkType.name.contains(search_name))) + ) + ) + if search_key == "tags": + query = query.filter( + DrinkPrice._volume.has(DrinkPriceVolume._drink.has(Drink.tags.any(Tag.name.conaitns(search_name)))) + ) + if search_key == "volume": + query = query.filter(DrinkPrice._volume.has(DrinkPriceVolume.volume == float(search_name))) + if search_key == "price": + query = query.filter(DrinkPrice.price == float(search_name)) + if search_key == "description": + query = query.filter(DrinkPrice.description.contains(search_name)) + else: + try: + search_name = float(search_name) + query = query.filter( + (DrinkPrice._volume.has(DrinkPriceVolume.volume == float(search_name))) + | (DrinkPrice.price == float(search_name)) + ) + except: + query = query.filter( + (DrinkPrice._volume.has(DrinkPriceVolume._drink.has(Drink.name.contains(search_name)))) + | ( + DrinkPrice._volume.has( + DrinkPriceVolume._drink.has(Drink.type.has(DrinkType.name.contains(search_name))) + ) + ) + | ( + DrinkPrice._volume.has( + DrinkPriceVolume._drink.has(Drink.tags.any(Tag.name.contains(search_name))) + ) + ) + | (DrinkPrice.description.contains(search_name)) + ) + if sortBy == "type": + query = ( + query.join(DrinkPrice._volume) + .join(DrinkPriceVolume._drink) + .join(Drink.type) + .order_by(DrinkType.name.desc() if descending else DrinkType.name.asc()) + ) + elif sortBy == "volume": + query = query.join(DrinkPrice._volume).order_by( + DrinkPriceVolume.volume.desc() if descending else DrinkPriceVolume.volume.asc() + ) + elif sortBy == "price": + query = query.order_by(DrinkPrice.price.desc() if descending else DrinkPrice.price.asc()) + else: + query = ( + query.join(DrinkPrice._volume) + .join(DrinkPriceVolume._drink) + .order_by(Drink.name.desc() if descending else Drink.name.asc()) + ) + if limit is not None: + count = query.count() + query = query.limit(limit) + if offset is not None: + query = query.offset(offset) + + prices = query.all() + for price in prices: + price._volume.drink = price._volume._drink + price.volume = price._volume + return prices, count def get_drink(identifier, public=False): @@ -152,6 +269,9 @@ def get_drink(identifier, public=False): raise NotFound if public: return _create_public_drink(drink) + for volume in drink._volumes: + volume.prices = volume._prices + drink.volumes = drink._volumes return drink @@ -188,11 +308,15 @@ def update_drink(identifier, data): if drink_type: drink.type = drink_type if volumes is not None and session.user_.has_permission(EDIT_VOLUME): - drink.volumes = [] - drink.volumes = set_volumes(volumes) + drink._volumes = [] + drink._volumes = set_volumes(volumes) if len(tags) > 0: drink.tags = tags db.session.commit() + for volume in drink._volumes: + volume.prices = volume._prices + drink.volumes = drink._volumes + return drink except (NotFound, KeyError): raise BadRequest @@ -209,6 +333,9 @@ def set_volumes(volumes): def delete_drink(identifier): drink = get_drink(identifier) + if drink.uuid: + path = config["pricelist"]["path"] + delete_picture(f"{path}/{drink.uuid}") db.session.delete(drink) db.session.commit() @@ -257,7 +384,7 @@ def set_prices(prices, volume): for _price in prices: price = set_price(_price) _prices.append(price) - volume.prices = _prices + volume._prices = _prices def set_ingredients(ingredients, volume): @@ -315,6 +442,10 @@ def delete_price(identifier): def set_drink_ingredient(data): allowed_keys = DrinkIngredient().serialize().keys() values = {key: value for key, value in data.items() if key in allowed_keys} + if "cost_per_volume" in values: + values.pop("cost_per_volume") + if "name" in values: + values.pop("name") ingredient_id = values.pop("id", -1) if ingredient_id < 0: drink_ingredient = DrinkIngredient(**values)