Add external product prices (#47)
Co-authored-by: Niklas Fondberg <niklas.fondberg@photowall.se>
This commit is contained in:
co-authored by
Niklas Fondberg
parent
f0b18c6757
commit
b1177a1e1c
+51
-1
@@ -4,6 +4,7 @@
|
||||
from api.lib.utils import round_half_up
|
||||
from api.lib.limits import calculate_canvas_limits
|
||||
from api.lib import pwinty
|
||||
from api.models.external_print_product import get_external_print_product
|
||||
|
||||
|
||||
def wallpaper_m2_price(
|
||||
@@ -297,6 +298,45 @@ def _price_for_poster_hanger_size(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(
|
||||
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)
|
||||
sku = kwargs.get("sku")
|
||||
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(
|
||||
width,
|
||||
height,
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
import time
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
def round_half_up(val):
|
||||
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
|
||||
@@ -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
@@ -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 api.helpers import check_api_key
|
||||
from api.validators import validate_number, validate_any_of, validate_exists
|
||||
@@ -20,6 +21,7 @@ from api.lib.prices import (
|
||||
wallpaper_m2_price,
|
||||
inquiry_price,
|
||||
wallpaper_kit_price,
|
||||
external_product_price,
|
||||
)
|
||||
from api.lib.limits import calculate_canvas_limits
|
||||
|
||||
@@ -524,6 +526,32 @@ def inquiry_poster(inquiry):
|
||||
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):
|
||||
sku = request.args.get("sku", type=str)
|
||||
data = {
|
||||
@@ -563,8 +591,11 @@ def inquiry_framed_print(inquiry):
|
||||
@mod.route("/inquiry/<int:inquiry_id>", methods=["GET"])
|
||||
def inquiry(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)
|
||||
if inquiry.product_group == "photo-wallpaper":
|
||||
if external_id:
|
||||
data = inquiry_external_product(inquiry)
|
||||
elif inquiry.product_group == "photo-wallpaper":
|
||||
data = inquiry_wallpaper(inquiry)
|
||||
elif inquiry.product_group == "canvas":
|
||||
data = inquiry_canvas(inquiry)
|
||||
@@ -592,6 +623,40 @@ def list_prices_wallpaper(market_id):
|
||||
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")
|
||||
def list_prices_canvas(market_id):
|
||||
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)
|
||||
|
||||
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
@@ -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 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
|
||||
|
||||
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.
|
||||
|
||||
| Parameter | Description |
|
||||
| --------- | ------------|
|
||||
| `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 |
|
||||
| `external_product_id` | e.g. `canvas_400x600-mm-16x24-inch_canvas_wood-fsc-slim_4-0_ver` |
|
||||
|
||||
|
||||
Wallpaper inquiry - sample request
|
||||
|
||||
Reference in New Issue
Block a user