Add external product prices (#47)

Co-authored-by: Niklas Fondberg <niklas.fondberg@photowall.se>
This commit is contained in:
Fredrik Ringqvist
2022-02-17 12:52:04 +01:00
committed by GitHub
co-authored by Niklas Fondberg
parent f0b18c6757
commit b1177a1e1c
5 changed files with 244 additions and 3 deletions
+51 -1
View File
@@ -4,6 +4,7 @@
from api.lib.utils import round_half_up from api.lib.utils import round_half_up
from api.lib.limits import calculate_canvas_limits from api.lib.limits import calculate_canvas_limits
from api.lib import pwinty from api.lib import pwinty
from api.models.external_print_product import get_external_print_product
def wallpaper_m2_price( def wallpaper_m2_price(
@@ -297,6 +298,45 @@ def _price_for_poster_hanger_size(size):
raise ValueError("Invalid poster hanger size: {}".format(size)) raise ValueError("Invalid poster hanger size: {}".format(size))
def _designer_pricepremium(designer, group_id):
if group_id in [1,3,6]:
return designer.pricepremium_wallpaper
if group_id == 2:
return designer.pricepremium_canvas
if group_id == 7:
return designer.pricepremium_poster
if group_id == 8:
return designer.pricepremium_framed_print
raise ValueError("Unexpected group id: {}".format(group_id))
def external_product_price(
id, market, designer=None, inquiry=None, rounded=True, inc_vat=True
):
external_product = get_external_print_product(id)
price = external_product["price"]
price *= market.price_adjustments
if designer:
designer_pricepremium = _designer_pricepremium(designer, external_product["group_id"])
price *= (float(designer_pricepremium) / 100) + 1
if inquiry:
price *= (float(inquiry.pricepremium) / 100) + 1
price *= market.exchange_rate
if inc_vat:
price *= market.vat
if rounded:
price = round_half_up(price)
return price
def framed_print_price( def framed_print_price(
sku, market, designer=None, reseller=None, inquiry=None, rounded=True, inc_vat=True sku, market, designer=None, reseller=None, inquiry=None, rounded=True, inc_vat=True
): ):
@@ -445,8 +485,18 @@ def inquiry_price(
hanger = kwargs.get("hanger", inquiry.hanger) hanger = kwargs.get("hanger", inquiry.hanger)
sku = kwargs.get("sku") sku = kwargs.get("sku")
reseller = kwargs.get("reseller", inquiry.dealer_store) reseller = kwargs.get("reseller", inquiry.dealer_store)
external_id = kwargs.get("external_id")
if inquiry.product_group == "photo-wallpaper": if external_id:
price = external_product_price(
id=external_id,
market=inquiry.market,
designer=inquiry.designer,
inquiry=inquiry,
rounded=False,
inc_vat=False,
)
elif inquiry.product_group == "photo-wallpaper":
price = wallpaper_price( price = wallpaper_price(
width, width,
height, height,
+20
View File
@@ -1,5 +1,25 @@
from decimal import Decimal, ROUND_HALF_UP from decimal import Decimal, ROUND_HALF_UP
import time
from functools import lru_cache
def round_half_up(val): def round_half_up(val):
return int(Decimal(val).quantize(0, ROUND_HALF_UP)) return int(Decimal(val).quantize(0, ROUND_HALF_UP))
def lru_with_ttl(*, ttl_seconds, maxsize=128):
"""
A decorator to apply LRU in-memory cache to a function with defined maximum(!) TTL in seconds.
Be design an actual TTL may be shorter then the passed value (in rare randomized cases). But it can't be higher.
:param ttl_seconds: TTL for a cache record in seconds
:param maxsize: Maximum size of the LRU cache (a functools.lru_cache argument)
:return: decorated function
"""
def deco(foo):
@lru_cache(maxsize=maxsize)
def cached_with_ttl(*args, ttl_hash, **kwargs):
return foo(*args, **kwargs)
def inner(*args, **kwargs):
return cached_with_ttl(*args, ttl_hash=round(time.time() / ttl_seconds), **kwargs)
return inner
return deco
+46
View File
@@ -0,0 +1,46 @@
# coding=UTF-8
from api.extensions import db
from api.lib.utils import lru_with_ttl
@lru_with_ttl(ttl_seconds=300)
def get_all_external_print_products():
sql="select external_product_id, price_sek, group_id from external_print_products"
result = db.session.execute(sql)
rows = result.fetchall()
prices = {}
for row in rows:
external_id = row[0]
price = float(row[1])
group_id = row[2]
prices[external_id] = {"price": price, "group_id": group_id}
return prices
def get_external_print_product(id):
all = get_all_external_print_products()
if id not in all:
raise ValueError("Invalid external product id: {}".format(id))
external_product = all.get(id)
return external_product
def get_cheapest_canvas_product():
sql = "select external_product_id from external_print_products where group_id = 2 order by price_sek limit 1"
result = db.session.execute(sql)
external_id = result.first()[0]
return external_id
def get_cheapest_poster_product():
sql = "select external_product_id from external_print_products where group_id in (7,8) order by price_sek limit 1"
result = db.session.execute(sql)
external_id = result.first()[0]
return external_id
+98 -1
View File
@@ -1,3 +1,4 @@
from api.models.external_print_product import get_cheapest_canvas_product, get_cheapest_poster_product
from flask import Blueprint, request, jsonify, abort from flask import Blueprint, request, jsonify, abort
from api.helpers import check_api_key from api.helpers import check_api_key
from api.validators import validate_number, validate_any_of, validate_exists from api.validators import validate_number, validate_any_of, validate_exists
@@ -20,6 +21,7 @@ from api.lib.prices import (
wallpaper_m2_price, wallpaper_m2_price,
inquiry_price, inquiry_price,
wallpaper_kit_price, wallpaper_kit_price,
external_product_price,
) )
from api.lib.limits import calculate_canvas_limits from api.lib.limits import calculate_canvas_limits
@@ -524,6 +526,32 @@ def inquiry_poster(inquiry):
return data return data
def inquiry_external_product(inquiry):
external_id = request.args.get("external_product_id", type=str)
data = {
"id": inquiry.id,
"retouch_price": inquiry.local_price_retouch(rounded=False, inc_vat=True),
"prices": [
{
"inquiry_price": inquiry_price(
inquiry, external_id=external_id, rounded=False, inc_vat=True
),
"inquiry_price_excl_vat": inquiry_price(
inquiry, external_id=external_id, rounded=False, inc_vat=False
),
"inquiry_price_excl_retouch": inquiry_price(
inquiry, external_id=external_id, rounded=False, inc_vat=True, inc_price_retouch=False,
),
"inquiry_price_excl_retouch_excl_vat": inquiry_price(
inquiry, external_id=external_id, rounded=False, inc_vat=False, inc_price_retouch=False,
),
}
],
}
return data
def inquiry_framed_print(inquiry): def inquiry_framed_print(inquiry):
sku = request.args.get("sku", type=str) sku = request.args.get("sku", type=str)
data = { data = {
@@ -563,8 +591,11 @@ def inquiry_framed_print(inquiry):
@mod.route("/inquiry/<int:inquiry_id>", methods=["GET"]) @mod.route("/inquiry/<int:inquiry_id>", methods=["GET"])
def inquiry(inquiry_id): def inquiry(inquiry_id):
inquiry = Inquiry.query.get_or_404(inquiry_id) inquiry = Inquiry.query.get_or_404(inquiry_id)
external_id = request.args.get("external_product_id")
sku = request.args.get("sku", type=str) sku = request.args.get("sku", type=str)
if inquiry.product_group == "photo-wallpaper": if external_id:
data = inquiry_external_product(inquiry)
elif inquiry.product_group == "photo-wallpaper":
data = inquiry_wallpaper(inquiry) data = inquiry_wallpaper(inquiry)
elif inquiry.product_group == "canvas": elif inquiry.product_group == "canvas":
data = inquiry_canvas(inquiry) data = inquiry_canvas(inquiry)
@@ -592,6 +623,40 @@ def list_prices_wallpaper(market_id):
return jsonify(result) return jsonify(result)
@mod.route("/list-prices/<int:market_id>/canvas_external")
def list_prices_canvas_external(market_id):
canvases = PrintProduct.query.canvases().all()
market = Market.query.get_or_404(market_id)
external_product_id = get_cheapest_canvas_product()
result = {}
for canvas in canvases:
result[canvas.id] = external_product_price(
id = external_product_id,
market=market,
designer=canvas.product.designer
)
return jsonify(result)
@mod.route("/list-prices/<int:market_id>/poster_external")
def list_prices_poster_external(market_id):
posters = PrintProduct.query.posters().all()
market = Market.query.get_or_404(market_id)
external_product_id = get_cheapest_poster_product()
result = {}
for poster in posters:
result[poster.id] = external_product_price(
id = external_product_id,
market=market,
designer=poster.product.designer
)
return jsonify(result)
@mod.route("/list-prices/<int:market_id>/canvas") @mod.route("/list-prices/<int:market_id>/canvas")
def list_prices_canvas(market_id): def list_prices_canvas(market_id):
canvases = PrintProduct.query.canvases().all() canvases = PrintProduct.query.canvases().all()
@@ -661,3 +726,35 @@ def list_prices_framed_prints(market_id):
result[printid] = framed_print_price(sku=sku, market=market, designer=designer) result[printid] = framed_print_price(sku=sku, market=market, designer=designer)
return jsonify(result) return jsonify(result)
@mod.route("/external-product", methods=["GET"])
@validate_exists("market")
@validate_exists("id")
def external_product():
market_id = request.args.get("market", type=int)
market = Market.query.get_or_404(market_id)
designer = get_designer()
id = request.args.get("id", type=str)
data = [
{
"id": id,
"price": external_product_price(
id,
market,
designer=designer,
rounded=False,
inc_vat=True,
),
"price_excl_vat": external_product_price(
id,
market,
designer=designer,
rounded=False,
inc_vat=False,
),
}
]
return jsonify(data=data)
+29 -1
View File
@@ -152,6 +152,33 @@ Sample response
} }
``` ```
## Calculate external product price
Calculate price inc VAT of a print product printed with an external partner.
| Parameter | Description |
| --------- | ------------|
| `id` | External product id |
| `market` | Market id e.g `1` for sweden. Markets affect the price in different ways like VAT, currency exchanges etc. |
| `product` | Optional parameter e.g `43894`. If a product with this id exists the api will calculate the price for the given product. |
Sample request
`curl -X GET 'https://api.photowall.com/prices/external_product?id=flat_450x600-mm-18x24-inch_200-gsm-80lb-uncoated_4-0_ver&market=1`
Sample response
```
{
"data": [
{
"id": "flat_450x600-mm-18x24-inch_200-gsm-80lb-uncoated_4-0_ver"
"price": 557.5,
"price_excl_vat": 446.0,
}
]
}
```
## Calculate "Do it yourself frame" price ## Calculate "Do it yourself frame" price
Calculate the price for the "do it yourself frame" (DIY). Note that width and height parameters may be adjusted if they go over or under the limits that are valid for a canvas-frame. The values that are used in the price-calculation are always returned in the response. Calculate the price for the "do it yourself frame" (DIY). Note that width and height parameters may be adjusted if they go over or under the limits that are valid for a canvas-frame. The values that are used in the price-calculation are always returned in the response.
@@ -211,13 +238,14 @@ Sample response
## Calculate inquiry price ## Calculate inquiry price
Calculates the price of an inquiry. For wallpapers inquiries the response will contain price information for all available materials and for canvas it will contain the price for both the framed and the not-framed canvas. The `group` attribute tells which kind of product you are dealing with. Calculates the price of an inquiry. For wallpapers inquiries the response will contain price information for all available materials and for canvas it will contain the price for both the framed and the not-framed canvas. External print products uses the `external_product_id` attribute. The `group` attribute tells which kind of product you are dealing with.
The width and height stored on the inquiry will be used in the calculations by default. If you want to override these values you can send in your own width/height parameters in the request. The width and height stored on the inquiry will be used in the calculations by default. If you want to override these values you can send in your own width/height parameters in the request.
| Parameter | Description | | Parameter | Description |
| --------- | ------------| | --------- | ------------|
| `width` | Width in cm e.g `100`. Defaults to the inquiry stored width | | `width` | Width in cm e.g `100`. Defaults to the inquiry stored width |
| `height` | Height in cm e.g `50`. Defaults to the inquiry stored height | | `height` | Height in cm e.g `50`. Defaults to the inquiry stored height |
| `external_product_id` | e.g. `canvas_400x600-mm-16x24-inch_canvas_wood-fsc-slim_4-0_ver` |
Wallpaper inquiry - sample request Wallpaper inquiry - sample request