Compare commits

...
10 Commits
7 changed files with 388 additions and 26 deletions
+153 -1
View File
@@ -3,6 +3,39 @@
from api.lib.utils import round_half_up from api.lib.utils import round_half_up
from api.models.external_print_product import get_external_print_product from api.models.external_print_product import get_external_print_product
from api.models.price_adjustments import get_price_adjustment from api.models.price_adjustments import get_price_adjustment
from api.models.prices import get_wallpaper_m2_price, get_external_product_price, get_own_image_wallpaper_m2_price, get_external_product_own_image_price
def wallpaper_m2_price_new(
product_id,
material_id,
market,
inquiry=None,
):
own_image = inquiry is not None and inquiry.productId is None
if own_image:
price_m2_incl_vat = get_own_image_wallpaper_m2_price(material_id, market.id)
segment = "OWN-IMAGE"
else:
prices = get_wallpaper_m2_price(product_id, material_id, market.id)
price_m2_incl_vat = prices["price"]
segment = prices["segment"]
price_m2_excl_vat = price_m2_incl_vat / market.vat
segment_price = price_m2_incl_vat
if inquiry:
inquiry_premium = (float(inquiry.pricepremium) / 100) + 1
price_m2_excl_vat = price_m2_excl_vat * inquiry_premium
price_m2_incl_vat = price_m2_excl_vat * market.vat
return {
"incl_vat": price_m2_incl_vat,
"excl_vat": price_m2_excl_vat,
"segment": segment,
"segment_price": segment_price,
}
def wallpaper_m2_price( def wallpaper_m2_price(
@@ -41,6 +74,46 @@ def wallpaper_m2_price(
return price return price
def wallpaper_price_new(
product_id,
width,
height,
material,
market,
unit,
inquiry=None,
):
m2_prices = wallpaper_m2_price_new(product_id, material.id, market, inquiry)
if (unit == 'inch'):
width = width * 2.54
height = height * 2.54
sqm = (width / 100) * (height / 100)
if (market.name == 'US'):
# On inch markets, minimum size is 1 sq feet (12x12 inch = 30.48x30.48 cm = 0.09290304 m2)
min_sqm = 0.09290304
else:
# Elsewhere, minimum size is 1 sq meter
min_sqm = 1
sqm = max(sqm, min_sqm)
price_excl_vat = m2_prices["excl_vat"] * sqm
price_incl_vat = price_excl_vat * market.vat
return {
"incl_vat": price_incl_vat,
"excl_vat": price_excl_vat,
"m2_price_incl_vat": m2_prices["incl_vat"],
"m2_price_excl_vat": m2_prices["excl_vat"],
"segment": m2_prices["segment"],
"segment_price": m2_prices["incl_vat"],
}
def wallpaper_price( def wallpaper_price(
width, width,
height, height,
@@ -86,6 +159,33 @@ def _designer_pricepremium(designer, group_id):
raise ValueError("Unexpected group id: {}".format(group_id)) raise ValueError("Unexpected group id: {}".format(group_id))
def external_product_own_image_price(external_product_id, market):
price_incl_vat = get_external_product_own_image_price(external_product_id, market.id)
price_excl_vat = price_incl_vat / market.vat
return {
'incl_vat': price_incl_vat,
'excl_vat': price_excl_vat,
'segment': 'OWN-IMAGE',
'segment_price': price_incl_vat,
}
def external_product_price_new(product_id, external_product_id, market):
prices = get_external_product_price(product_id, external_product_id, market.id)
price_incl_vat = prices['price']
price_excl_vat = price_incl_vat / market.vat
return {
'incl_vat': price_incl_vat,
'excl_vat': price_excl_vat,
'segment': prices['segment'],
'segment_price': price_incl_vat,
}
def external_product_price( def external_product_price(
id, market, designer=None, inquiry=None, rounded=True, inc_vat=True id, market, designer=None, inquiry=None, rounded=True, inc_vat=True
): ):
@@ -112,6 +212,57 @@ def external_product_price(
return price return price
def inquiry_price_new(
inquiry, **kwargs
):
width = kwargs.get("width", inquiry.width)
height = kwargs.get("height", inquiry.height)
unit = kwargs.get("unit", None)
material = kwargs.get("material", inquiry.material)
external_id = kwargs.get("external_id")
product_id = inquiry.productId
is_own_image = product_id is None
if external_id:
if not is_own_image:
raise ValueError("Manual inquiries only available for wallpapers")
prices = external_product_own_image_price(external_id, inquiry.market)
return prices
elif inquiry.product_group == "photo-wallpaper":
if is_own_image:
prices = wallpaper_price_new(None, width, height, material, inquiry.market, unit, inquiry)
else:
prices = wallpaper_price_new(product_id, width, height, material, inquiry.market, unit, inquiry)
else:
raise ValueError("Invalid product group: {}".format(inquiry.product_group))
price_excl_vat = prices["excl_vat"]
effect_price = inquiry.price_effect
if effect_price:
price_excl_vat += float(effect_price)
price_excl_retouch_excl_vat = price_excl_vat
price_excl_retouch_incl_vat = price_excl_retouch_excl_vat * inquiry.market.vat
retouch_price = inquiry.local_price_retouch(rounded=False, inc_vat=False)
if retouch_price:
price_excl_vat += retouch_price
price_incl_vat = price_excl_vat * inquiry.market.vat
return {
"incl_vat": price_incl_vat,
"excl_vat": price_excl_vat,
"excl_retouch_incl_vat": price_excl_retouch_incl_vat,
"excl_retouch_excl_vat": price_excl_retouch_excl_vat,
"m2_price_incl_vat": prices["m2_price_incl_vat"],
"m2_price_excl_vat": prices["m2_price_excl_vat"],
"segment": prices["segment"],
"segment_price": prices["segment_price"],
}
def inquiry_price( def inquiry_price(
inquiry, inc_price_retouch=True, rounded=True, inc_vat=True, **kwargs inquiry, inc_price_retouch=True, rounded=True, inc_vat=True, **kwargs
): ):
@@ -171,7 +322,7 @@ def wallpaper_kit_price(
'AU': 23.64, 'AU': 23.64,
'BE': 17.25, 'BE': 17.25,
'CA': 24, 'CA': 24,
'CH': 19.09, 'CH': 20.91,
'DE': 16.36, 'DE': 16.36,
'DK': 132.67, 'DK': 132.67,
'EE': 16.96, 'EE': 16.96,
@@ -186,6 +337,7 @@ def wallpaper_kit_price(
'NL': 16.53, 'NL': 16.53,
'NO': 154.26, 'NO': 154.26,
'PL': 19.31, 'PL': 19.31,
'RO': 19.96,
'SE': 159.2, 'SE': 159.2,
'SG': 19.81, 'SG': 19.81,
'US': 19.81, 'US': 19.81,
+11 -2
View File
@@ -30,7 +30,13 @@ def get_external_print_product(id):
return external_product return external_product
def get_cheapest_canvas_product(): def get_cheapest_canvas_product(new_version):
if new_version:
# We simply return the smallest size, assuming that is also the cheapest.
# It is technically possible that a larger size is cheaper, but unlikely, so we avoid the extra complexity
# of calculating the guaranteed cheapest product.
return 'canvas_200x300-mm-8x12-inch_canvas_wood-fsc-slim_4-0_ver'
sql = "select external_product_id from external_print_products where group_id = 2 order by price_sek limit 1" sql = "select external_product_id from external_print_products where group_id = 2 order by price_sek limit 1"
result = db.session.execute(sql) result = db.session.execute(sql)
external_id = result.first()[0] external_id = result.first()[0]
@@ -38,7 +44,10 @@ def get_cheapest_canvas_product():
return external_id return external_id
def get_cheapest_poster_product(): def get_cheapest_poster_product(new_version):
if new_version:
return 'flat_250x250-mm-10x10-inch_200-gsm-80lb-uncoated_4-0_ver'
sql = "select external_product_id from external_print_products where group_id in (7,8) order by price_sek limit 1" 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) result = db.session.execute(sql)
external_id = result.first()[0] external_id = result.first()[0]
+1 -1
View File
@@ -17,7 +17,6 @@ class Inquiry(db.Model):
price_retouch = db.Column(db.Numeric) price_retouch = db.Column(db.Numeric)
width = db.Column(db.Integer) width = db.Column(db.Integer)
height = db.Column(db.Integer) height = db.Column(db.Integer)
framed = db.Column(db.Integer, nullable=False, default=0)
dealer_store_id = db.Column( dealer_store_id = db.Column(
"dealer_store", "dealer_store",
db.Integer, db.Integer,
@@ -43,6 +42,7 @@ class Inquiry(db.Model):
return 0 return 0
price = float(self.price_retouch) price = float(self.price_retouch)
price *= self.market.exchange_rate price *= self.market.exchange_rate
# Todo: review if the retouch price should no longer use the price_adjustments value
price *= self.market.price_adjustments price *= self.market.price_adjustments
if inc_vat: if inc_vat:
price *= self.market.vat price *= self.market.vat
+113
View File
@@ -0,0 +1,113 @@
# coding=UTF-8
from api.extensions import db
def get_wallpaper_m2_price(product_id, material_id, market_id):
sql = """
select
price, ps.name
from
v_price_product_segments v
join prices_wallpaper_m2 p on v.segment_id = p.segment_id
join price_segments ps on v.segment_id = ps.id
where
productid = :product_id and market_id = :market_id and material_id = :material_id
order by priority desc
limit 1;
"""
result = db.session.execute(sql, {
"product_id": product_id,
"market_id": market_id,
"material_id": material_id,
})
rows = result.fetchall()
for row in rows:
[price, segment] = row
return {
"price": float(price),
"segment": segment,
}
raise Exception("Failed reading wallpaper m2 price")
def get_external_product_price(product_id, external_product_id, market_id):
sql = """
select
price, price_segments.name
from
v_price_product_segments v
join prices_external_products p on v.segment_id = p.segment_id
join price_segments on v.segment_id = price_segments.id
where
productid = :product_id and market_id = :market_id and external_product_id = :external_id
order by priority desc
limit 1;
"""
result = db.session.execute(sql, {
"product_id": product_id,
"market_id": market_id,
"external_id": external_product_id,
})
rows = result.fetchall()
for row in rows:
[price, segment] = row
return {
"price": float(price),
"segment": segment,
}
raise Exception("Failed reading external product price")
def get_own_image_wallpaper_m2_price(material_id, market_id):
sql = """
select
price
from
prices_wallpaper_m2
where
market_id = :market_id and material_id = :material_id
and segment_id = (select id from price_segments where name = 'OWN-IMAGE')
"""
result = db.session.execute(sql, {
"market_id": market_id,
"material_id": material_id,
})
rows = result.fetchall()
for row in rows:
price = row[0]
return float(price)
raise Exception("Failed reading own image wallpaper m2 price")
def get_external_product_own_image_price(external_product_id, market_id):
sql = """
select
price
from
prices_external_products
where
market_id = :market_id and external_product_id = :external_id
and segment_id = (select id from price_segments where name = 'OWN-IMAGE')
"""
result = db.session.execute(sql, {
"market_id": market_id,
"external_id": external_product_id,
})
rows = result.fetchall()
for row in rows:
price = row[0]
return float(price)
raise Exception("Failed reading external product own image price")
+2 -1
View File
@@ -69,8 +69,9 @@ class Material(db.Model):
WALLPAPER_MATERIALS = [ WALLPAPER_MATERIALS = [
"standard-wallpaper", "standard-wallpaper",
# 'self-adhesive-wallpaper', // isn't sold now
"premium-wallpaper", "premium-wallpaper",
"self-adhesive-wallpaper",
"matte-wallpaper",
] ]
CANVAS_MATERIALS = ["standard-canvas"] CANVAS_MATERIALS = ["standard-canvas"]
+104 -17
View File
@@ -15,6 +15,9 @@ from api.lib.prices import (
inquiry_price, inquiry_price,
wallpaper_kit_price, wallpaper_kit_price,
external_product_price, external_product_price,
wallpaper_price_new,
external_product_price_new,
inquiry_price_new,
) )
mod = Blueprint("prices", __name__, url_prefix="/prices") mod = Blueprint("prices", __name__, url_prefix="/prices")
@@ -42,19 +45,38 @@ def get_designer():
@validate_number("width", "height") @validate_number("width", "height")
@validate_exists("market") @validate_exists("market")
def wallpaper(): def wallpaper():
width = request.args.get("width", type=int) width = request.args.get("width", type=float)
height = request.args.get("height", type=int) height = request.args.get("height", type=float)
market_id = request.args.get("market", type=int) market_id = request.args.get("market", type=int)
product_id = request.args.get("product", type=int)
unit = request.args.get("unit", type=str)
new_version = request.args.get("new_version") is not None
market = Market.query.get_or_404(market_id) market = Market.query.get_or_404(market_id)
designer = get_designer() designer = get_designer()
reseller = get_reseller() reseller = get_reseller()
if width is None or height is None: if width is None or height is None:
raise ValidationError("width and height must be integers") raise ValidationError("width and height must be float")
materials = Material.query.wallpapers().all() materials = Material.query.wallpapers().all()
data = [] data = []
for material in materials: for material in materials:
if new_version:
prices = wallpaper_price_new(product_id, width, height, material, market, unit)
data.append(
{
"material": material.name,
"material_id": material.id,
"m2_price": prices["m2_price_incl_vat"],
"m2_price_excl_vat": prices["m2_price_excl_vat"],
"price": prices["incl_vat"],
"price_excl_vat": prices["excl_vat"],
"segment": prices["segment"],
"segment_price": prices["segment_price"],
}
)
continue
data.append( data.append(
{ {
"material": material.name, "material": material.name,
@@ -125,8 +147,18 @@ def get_inquiry_measurements(inquiry):
# first try to get it from the query string. If that fails try to get it # first try to get it from the query string. If that fails try to get it
# from the inquiry row in the database # from the inquiry row in the database
width = request.args.get("width", type=int, default=inquiry.width) unit = request.args.get("unit", type=str, default=None)
height = request.args.get("height", type=int, default=inquiry.height) width = request.args.get("width", type=float, default=None)
height = request.args.get("height", type=float, default=None)
if unit and not (width and height):
raise Exception("With unit in query string width and height must also be there.")
if not width:
width = inquiry.width
if not height:
height = inquiry.height
# if we still are missing a width/height just set them to 1 to avoid # if we still are missing a width/height just set them to 1 to avoid
# errors in the price calculations # errors in the price calculations
@@ -136,22 +168,41 @@ def get_inquiry_measurements(inquiry):
if not height: if not height:
height = 1 height = 1
return width, height return width, height, unit
def inquiry_wallpaper(inquiry): def inquiry_wallpaper(inquiry, new_version=False):
width, height = get_inquiry_measurements(inquiry) width, height, unit = get_inquiry_measurements(inquiry)
data = { data = {
"id": inquiry.id, "id": inquiry.id,
"group": "wallpaper", "group": "wallpaper",
"width": width, "width": width,
"height": height, "height": height,
"unit": unit,
"retouch_price": inquiry.local_price_retouch(rounded=False, inc_vat=True), "retouch_price": inquiry.local_price_retouch(rounded=False, inc_vat=True),
} }
materials = Material.query.wallpapers().all() materials = Material.query.wallpapers().all()
prices = [] prices = []
for material in materials: for material in materials:
if new_version:
price = inquiry_price_new(inquiry, material=material, width=width, height=height, unit=unit)
prices.append(
{
"material": material.name,
"material_id": material.id,
"m2_price": price["m2_price_incl_vat"],
"m2_price_excl_vat": price["m2_price_excl_vat"],
"inquiry_price": price["incl_vat"],
"inquiry_price_excl_vat": price["excl_vat"],
"inquiry_price_excl_retouch": price["excl_retouch_incl_vat"],
"inquiry_price_excl_retouch_excl_vat": price["excl_retouch_excl_vat"],
"segment": price["segment"],
"segment_price": price["segment_price"],
}
)
continue
prices.append( prices.append(
{ {
"material": material.name, "material": material.name,
@@ -215,7 +266,26 @@ def inquiry_wallpaper(inquiry):
return data return data
def inquiry_external_product(inquiry, external_id): def inquiry_external_product(inquiry, external_id, new_version):
if new_version:
result = inquiry_price_new(inquiry, external_id=external_id)
data = {
"id": inquiry.id,
# No manual inquiries for external products, hence no retouch price
"retouch_price": 0,
"prices": [
{
"inquiry_price": result["incl_vat"],
"inquiry_price_excl_vat": result["excl_vat"],
"inquiry_price_excl_retouch": result["incl_vat"],
"inquiry_price_excl_retouch_excl_vat": result["excl_vat"],
"segment": result["segment"],
"segment_price": result["segment_price"],
}
],
}
return data
data = { data = {
"id": inquiry.id, "id": inquiry.id,
"retouch_price": inquiry.local_price_retouch(rounded=False, inc_vat=True), "retouch_price": inquiry.local_price_retouch(rounded=False, inc_vat=True),
@@ -245,19 +315,20 @@ def inquiry(inquiry_id):
inquiry = Inquiry.query.get_or_404(inquiry_id) inquiry = Inquiry.query.get_or_404(inquiry_id)
external_product_id = request.args.get("external_product_id") external_product_id = request.args.get("external_product_id")
listprice = request.args.get("listprice") listprice = request.args.get("listprice")
new_version = request.args.get("new_version") is not None
if (listprice): if listprice:
if (listprice == 'canvas'): if listprice == 'canvas':
external_product_id = get_cheapest_canvas_product() external_product_id = get_cheapest_canvas_product(new_version)
elif (listprice == 'poster'): elif listprice == 'poster':
external_product_id = get_cheapest_poster_product() external_product_id = get_cheapest_poster_product(new_version)
else: else:
raise ValueError("Invalid listprice group {}".format(listprice)) raise ValueError("Invalid listprice group {}".format(listprice))
if external_product_id: if external_product_id:
data = inquiry_external_product(inquiry, external_product_id) data = inquiry_external_product(inquiry, external_product_id, new_version)
elif inquiry.product_group == "photo-wallpaper": elif inquiry.product_group == "photo-wallpaper":
data = inquiry_wallpaper(inquiry) data = inquiry_wallpaper(inquiry, new_version)
else: else:
raise ValueError("Invalid group {}".format(inquiry.product_group)) raise ValueError("Invalid group {}".format(inquiry.product_group))
@@ -270,8 +341,24 @@ def inquiry(inquiry_id):
def external_product(): def external_product():
market_id = request.args.get("market", type=int) market_id = request.args.get("market", type=int)
market = Market.query.get_or_404(market_id) market = Market.query.get_or_404(market_id)
designer = get_designer()
id = request.args.get("id", type=str) id = request.args.get("id", type=str)
new_version = request.args.get("new_version") is not None
if new_version:
product_id = request.args.get("product", type=int)
prices = external_product_price_new(product_id, id, market)
data = [
{
"id": id,
"price": prices["incl_vat"],
"price_excl_vat": prices["excl_vat"],
"segment": prices["segment"],
"segment_price": prices["segment_price"],
}
]
return jsonify(data=data)
designer = get_designer()
data = [ data = [
{ {
+4 -4
View File
@@ -263,10 +263,10 @@ class TestPrice(unittest2.TestCase):
) )
def test_wallpaper_kit_price(self): def test_wallpaper_kit_price(self):
self.assertEqual(143.2, wallpaper_kit_price(market=sweden, rounded=False, inc_vat=False)) self.assertEqual(159.2, wallpaper_kit_price(market=sweden, rounded=False, inc_vat=False))
self.assertEqual(179.0, wallpaper_kit_price(market=sweden, rounded=False, inc_vat=True)) self.assertEqual(199.0, wallpaper_kit_price(market=sweden, rounded=False, inc_vat=True))
self.assertEqual(18.0, wallpaper_kit_price(market=us)) self.assertEqual(21.0, wallpaper_kit_price(market=us))
self.assertEqual(169.0, wallpaper_kit_price(market=denmark)) self.assertEqual(197.0, wallpaper_kit_price(market=denmark))
class TestPriceWithPriceAdjustment(unittest2.TestCase): class TestPriceWithPriceAdjustment(unittest2.TestCase):