PW-567 add the old canvas formula
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# coding=UTF-8
|
||||
|
||||
CANVAS = {
|
||||
'max_long_side': 500,
|
||||
'min_long_side': 20,
|
||||
'max_short_side': 150,
|
||||
'min_short_side': 20
|
||||
}
|
||||
|
||||
CANVAS_FRAME = {
|
||||
'max_long_side': 150,
|
||||
'min_long_side': 40,
|
||||
'max_short_side': 150,
|
||||
'min_short_side': 40,
|
||||
}
|
||||
|
||||
|
||||
def calculate_canvas_frame_limits(width, height):
|
||||
if width > height:
|
||||
width = min(width, CANVAS_FRAME.get('max_long_side'))
|
||||
width = max(width, CANVAS_FRAME.get('min_long_side'))
|
||||
height = min(height, CANVAS_FRAME.get('max_short_side'))
|
||||
height = max(height, CANVAS_FRAME.get('min_short_side'))
|
||||
else:
|
||||
height = min(height, CANVAS_FRAME.get('max_long_side'))
|
||||
height = max(height, CANVAS_FRAME.get('min_long_side'))
|
||||
width = min(width, CANVAS_FRAME.get('max_short_side'))
|
||||
width = max(width, CANVAS_FRAME.get('min_short_side'))
|
||||
|
||||
# round to nearest 10
|
||||
width = round(width, -1)
|
||||
height = round(height, -1)
|
||||
|
||||
return width, height
|
||||
|
||||
|
||||
def calculate_canvas_limits(width, height, framed=False):
|
||||
if framed:
|
||||
return calculate_canvas_frame_limits(width, height)
|
||||
|
||||
if width > height:
|
||||
width = min(width, CANVAS.get('max_long_side'))
|
||||
width = max(width, CANVAS.get('min_long_side'))
|
||||
height = min(height, CANVAS.get('max_short_side'))
|
||||
height = max(height, CANVAS.get('min_short_side'))
|
||||
else:
|
||||
height = min(height, CANVAS.get('max_long_side'))
|
||||
height = max(height, CANVAS.get('min_long_side'))
|
||||
width = min(width, CANVAS.get('max_short_side'))
|
||||
width = max(width, CANVAS.get('min_short_side'))
|
||||
|
||||
return width, height
|
||||
+81
-1
@@ -1,7 +1,9 @@
|
||||
# coding=UTF-8
|
||||
|
||||
""" Price formulas """
|
||||
|
||||
from api.lib.utils import round_half_up
|
||||
from api.lib.limits import calculate_canvas_limits
|
||||
|
||||
|
||||
def wallpaper_price(width, height, material_price, market, designer=None, reseller=None, inc_vat=True):
|
||||
m2_price = material_price
|
||||
@@ -96,3 +98,81 @@ def canvas_price(width, height, market, framed=True, designer=None, reseller=Non
|
||||
price *= market.vat
|
||||
|
||||
return round(price)
|
||||
|
||||
|
||||
def canvas_frame_price(width, height, stock_price, additional_price=None):
|
||||
width, height = calculate_canvas_limits(width, height, framed=True)
|
||||
circumference = (2 * width) + (2 * height)
|
||||
circumference = max(circumference, 170)
|
||||
|
||||
price = stock_price * circumference
|
||||
|
||||
# if width or height goes above this limit additional costs are added
|
||||
if additional_price:
|
||||
additional_price_limit = 120
|
||||
if width > additional_price_limit or height > additional_price_limit:
|
||||
price += additional_price
|
||||
|
||||
return price
|
||||
|
||||
|
||||
def old_canvas_diy_frame_price(width, height, market, reseller=None, inc_vat=True):
|
||||
# todo: when product models are in place we should use stockproduct ""Do it yourself frame""
|
||||
# instead of hardcoded values
|
||||
stock_price = 1.61084
|
||||
additional_price = 94.750000
|
||||
price = canvas_frame_price(width, height, stock_price, additional_price)
|
||||
|
||||
# exchange to local currency
|
||||
price *= market.exchange_rate
|
||||
|
||||
# multiply price with market price adjustments
|
||||
price *= market.price_adjustments
|
||||
|
||||
# add reseller price premium
|
||||
if reseller:
|
||||
price *= (float(reseller.pricepremium) / 100) + 1
|
||||
|
||||
# add vat
|
||||
if inc_vat:
|
||||
price *= market.vat
|
||||
|
||||
return round_half_up(price)
|
||||
|
||||
|
||||
def old_canvas_price(width, height, material_price, market, framed=True, designer=None, reseller=None, inc_vat=True):
|
||||
width, height = calculate_canvas_limits(width, height, framed)
|
||||
# calculate square meter
|
||||
sqm = (width / 100) * (height / 100)
|
||||
sqm = max(sqm, 0.2) # sqm can never go below 0.2
|
||||
|
||||
# calculate price per square meter
|
||||
price = sqm * material_price
|
||||
|
||||
if framed:
|
||||
# todo: when product models are in place we should use stockproduct
|
||||
# "Canvasframe" instead of hardcoded values
|
||||
stock_price = 1.04230824
|
||||
additional_price = 94.750000
|
||||
price += canvas_frame_price(width, height,
|
||||
stock_price, additional_price)
|
||||
|
||||
# add designer price premium
|
||||
if designer:
|
||||
price *= (float(designer.pricepremium) / 100) + 1
|
||||
|
||||
# add reseller price premium
|
||||
if reseller:
|
||||
price *= (float(reseller.pricepremium) / 100) + 1
|
||||
|
||||
# exchange to local currency
|
||||
price *= market.exchange_rate
|
||||
|
||||
# multiply price with market price adjustments
|
||||
price *= market.price_adjustments
|
||||
|
||||
# add vat
|
||||
if inc_vat:
|
||||
price *= market.vat
|
||||
|
||||
return round_half_up(price)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
|
||||
|
||||
def round_half_up(val):
|
||||
return int(Decimal(val).quantize(0, ROUND_HALF_UP))
|
||||
@@ -1,9 +1,24 @@
|
||||
# coding=UTF-8
|
||||
|
||||
from flask_sqlalchemy import BaseQuery
|
||||
from api import db
|
||||
|
||||
|
||||
class ContractCustomerQuery(BaseQuery):
|
||||
|
||||
def reseller_store(self, url, territory):
|
||||
return self.filter(
|
||||
ContractCustomer.dealerstore == True,
|
||||
ContractCustomer.dealerurl == url,
|
||||
ContractCustomer.country == territory).first()
|
||||
|
||||
|
||||
class ContractCustomer(db.Model):
|
||||
|
||||
__tablename__ = 'contract_customers'
|
||||
|
||||
query_class = ContractCustomerQuery
|
||||
|
||||
id = db.Column('contractcustomerid', db.Integer, primary_key=True)
|
||||
country = db.Column(db.String(2))
|
||||
pricepremium = db.Column(db.Numeric, nullable=False, default=0)
|
||||
|
||||
@@ -78,7 +78,7 @@ _markets = [
|
||||
Market('United kingdom', 'en_GB', 'GBP', 1.20, 'GBR'),
|
||||
Market('Denmark', 'da_DK', 'DKK', 1.25, 'DNK'),
|
||||
Market('Finland', 'fi_FI', 'EUR', 1.24, 'FIN'),
|
||||
Market('Russia', 'ru_RU', 'RUB', 1, 'RUS'),
|
||||
Market('Russia', 'ru_RU', 'EUR', 1, 'RUS'),
|
||||
Market('Germany', 'de_DE', 'EUR', 1.19, 'DEU'),
|
||||
Market('Netherlands', 'nl_NL', 'EUR', 1.21, 'NLD'),
|
||||
Market('Austria', 'de_AT', 'EUR', 1.20, 'AUT'),
|
||||
|
||||
+46
-29
@@ -2,33 +2,33 @@ from flask import Blueprint, request, jsonify, abort
|
||||
from api.models.product import Material
|
||||
from api.models.contract_customer import ContractCustomer
|
||||
from api.models import market as market_model
|
||||
from api.lib.prices import wallpaper_price, canvas_price
|
||||
from api.lib.prices import wallpaper_price, old_canvas_price, old_canvas_diy_frame_price
|
||||
from api.lib.limits import calculate_canvas_limits
|
||||
from api.validators import validate_territory, validate_number, validate_keys_exists
|
||||
|
||||
|
||||
mod = Blueprint('prices', __name__, url_prefix='/prices')
|
||||
|
||||
|
||||
def get_reseller():
|
||||
dealerurl = request.args.get('dealerurl')
|
||||
territory = request.args.get('territory')
|
||||
if dealerurl and territory:
|
||||
return ContractCustomer.query.reseller_store(dealerurl, territory)
|
||||
return None
|
||||
|
||||
|
||||
@mod.route("/wallpaper", methods=['GET'])
|
||||
def wallpaper():
|
||||
validate_keys_exists(request.args, 'width', 'height', 'territory')
|
||||
validate_number(request.args.get('width'), request.args.get('height'))
|
||||
validate_territory(request.args.get('territory'))
|
||||
|
||||
width = request.args.get('width', type=int)
|
||||
height = request.args.get('height', type=int)
|
||||
market = market_model.from_territory(request.args.get('territory'))
|
||||
|
||||
params = {
|
||||
'width': request.args.get('width', type=int),
|
||||
'height': request.args.get('height', type=int),
|
||||
'market': market,
|
||||
}
|
||||
|
||||
dealerurl = request.args.get('dealerurl')
|
||||
if dealerurl:
|
||||
reseller = ContractCustomer.query.filter_by(
|
||||
dealerstore=True, dealerurl=dealerurl, country=market.territory).first()
|
||||
if reseller:
|
||||
params['reseller'] = reseller
|
||||
reseller = get_reseller()
|
||||
|
||||
materials = Material.query.wallpapers().all()
|
||||
data = []
|
||||
@@ -36,7 +36,7 @@ def wallpaper():
|
||||
data.append({
|
||||
'material': material.name,
|
||||
'material_id': material.id,
|
||||
'price': wallpaper_price(material_price=material.price, **params),
|
||||
'price': wallpaper_price(width, height, material.price, market, reseller=reseller),
|
||||
})
|
||||
|
||||
return jsonify(data=data)
|
||||
@@ -47,28 +47,45 @@ def canvas():
|
||||
validate_keys_exists(request.args, 'width', 'height', 'territory')
|
||||
validate_number(request.args.get('width'), request.args.get('height'))
|
||||
validate_territory(request.args.get('territory'))
|
||||
|
||||
market = market_model.from_territory(request.args.get('territory'))
|
||||
|
||||
params = {
|
||||
'width': request.args.get('width', type=int),
|
||||
'height': request.args.get('height', type=int),
|
||||
'market': market
|
||||
}
|
||||
|
||||
dealerurl = request.args.get('dealerurl')
|
||||
if dealerurl:
|
||||
reseller = ContractCustomer.query.filter_by(
|
||||
dealerstore=True, dealerurl=dealerurl, country=market.territory).first()
|
||||
if reseller:
|
||||
params['reseller'] = reseller
|
||||
reseller = get_reseller()
|
||||
material = Material.query.canvas().first()
|
||||
|
||||
data = []
|
||||
|
||||
for framed in [True, False]:
|
||||
width, height = calculate_canvas_limits(
|
||||
request.args.get('width', type=int),
|
||||
request.args.get('height', type=int),
|
||||
framed
|
||||
)
|
||||
data.append({
|
||||
'framed': framed,
|
||||
'price': canvas_price(framed=framed, **params)
|
||||
'width': width,
|
||||
'height': height,
|
||||
'price': old_canvas_price(width, height, material.price, market, reseller=reseller, framed=framed)
|
||||
})
|
||||
|
||||
return jsonify(data=data)
|
||||
|
||||
|
||||
@mod.route('/diy-frame', methods=['GET'])
|
||||
def diy_frame():
|
||||
validate_keys_exists(request.args, 'width', 'height', 'territory')
|
||||
validate_number(request.args.get('width'), request.args.get('height'))
|
||||
validate_territory(request.args.get('territory'))
|
||||
market = market_model.from_territory(request.args.get('territory'))
|
||||
reseller = get_reseller()
|
||||
width, height = calculate_canvas_limits(
|
||||
request.args.get('width', type=int),
|
||||
request.args.get('height', type=int),
|
||||
framed=True
|
||||
)
|
||||
|
||||
data = [{
|
||||
'width': width,
|
||||
'height': height,
|
||||
'price': old_canvas_diy_frame_price(width, height, market, reseller=reseller)
|
||||
}]
|
||||
|
||||
return jsonify(data=data)
|
||||
|
||||
+37
-3
@@ -43,7 +43,7 @@ Sample response
|
||||
|
||||
## Calculate canvas price
|
||||
|
||||
Calculate price inc VAT of a canvas product. The response will contain information for both framed/not framed canvases.
|
||||
Calculate price inc VAT of a canvas product. The response will contain information for both framed/not framed canvases. Note that width and height parameters may be adjusted if they go over or under the limits that are valid for a canvas. The values that are used in the price-calculation are always returned in the response.
|
||||
|
||||
| Parameter | Description |
|
||||
| --------- | -----| -------- | -
|
||||
@@ -63,11 +63,45 @@ Sample response
|
||||
"data": [
|
||||
{
|
||||
"framed": true,
|
||||
"price": 284
|
||||
"price": 284,
|
||||
"width": 100,
|
||||
"height": 50,
|
||||
},
|
||||
{
|
||||
"framed": false,
|
||||
"price": 249
|
||||
"price": 249,
|
||||
"width": 100,
|
||||
"height": 50,
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
| Parameter | Description |
|
||||
| --------- | -----| -------- | -
|
||||
| `width` | Canvas width in cm e.g `100'. |
|
||||
| `height` | Canvas height in cm e.g `50` |
|
||||
| `territory` | Two letter iso code e.g `SE`. Territory affect the price in different ways like VAT, currency exchanges etc. |
|
||||
| `dealerurl` | Optional parameter e.g `ackes`. Should always be used when calling the api from a dealerstore. If a dealerstore exists with this url additional fees will be applied to the price. |
|
||||
|
||||
|
||||
Sample request
|
||||
|
||||
`curl -X GET 'https://api2.photowall.com/prices/diy-frame?width=150&height=150&territory=SE`
|
||||
|
||||
Sample response
|
||||
```
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"height": 150,
|
||||
"price": 1327,
|
||||
"width": 150
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# coding=UTF-8
|
||||
|
||||
import unittest2
|
||||
from api.lib.limits import calculate_canvas_limits, calculate_canvas_frame_limits
|
||||
|
||||
|
||||
class TestCalculate(unittest2.TestCase):
|
||||
|
||||
def test_calculate_canvas_limits(self):
|
||||
self.assertEqual((100, 100), calculate_canvas_limits(100, 100))
|
||||
self.assertEqual((500, 100), calculate_canvas_limits(800, 100))
|
||||
self.assertEqual((100, 500), calculate_canvas_limits(100, 800))
|
||||
self.assertEqual((20, 20), calculate_canvas_limits(10, 10))
|
||||
self.assertEqual((150, 50), calculate_canvas_limits(
|
||||
400, 50, True), "Framed canvas")
|
||||
|
||||
def test_calculate_canvas_frame_limits(self):
|
||||
self.assertEqual((100, 100), calculate_canvas_frame_limits(100, 100))
|
||||
self.assertEqual((40, 40), calculate_canvas_frame_limits(1, 1))
|
||||
self.assertEqual((150, 150), calculate_canvas_frame_limits(200, 200))
|
||||
self.assertEqual((150, 40), calculate_canvas_frame_limits(200, 10))
|
||||
self.assertEqual((110, 120), calculate_canvas_frame_limits(112, 117), "round to nearest 10")
|
||||
@@ -1,7 +1,7 @@
|
||||
# coding=UTF-8
|
||||
|
||||
import unittest2
|
||||
from api.lib.prices import wallpaper_price, canvas_price, canvas_cm2_price
|
||||
from api.lib.prices import *
|
||||
from tests.factories import DesignerFactory, ContractCustomerFactory, InquiryFactory
|
||||
from api.models import market
|
||||
|
||||
@@ -80,3 +80,46 @@ class TestPrice(unittest2.TestCase):
|
||||
|
||||
self.assertEqual(436, canvas_price(width=50, height=50,
|
||||
market=denmark, framed=True, inc_vat=False))
|
||||
|
||||
def test_canvas_frame_price(self):
|
||||
self.assertAlmostEqual(208.46, canvas_frame_price(
|
||||
50, 50, stock_price=1.04230824, additional_price=94.750000), places=2)
|
||||
self.assertAlmostEqual(177.19, canvas_frame_price(
|
||||
1, 1, stock_price=1.04230824, additional_price=94.750000), places=2)
|
||||
self.assertAlmostEqual(625.38, canvas_frame_price(
|
||||
500, 500, stock_price=1.04230824, additional_price=None), places=2)
|
||||
|
||||
def test_old_canvas_diy_frame_price(self):
|
||||
self.assertEqual(
|
||||
403, old_canvas_diy_frame_price(50, 50, market=sweden))
|
||||
self.assertEqual(801, old_canvas_diy_frame_price(
|
||||
100, 110, market=denmark))
|
||||
self.assertEqual(463, old_canvas_diy_frame_price(
|
||||
50, 50, market=sweden, reseller=reseller))
|
||||
self.assertEqual(1327, old_canvas_diy_frame_price(
|
||||
500, 500, market=sweden))
|
||||
|
||||
def test_old_canvas_price(self):
|
||||
self.assertEqual(250, old_canvas_price(
|
||||
width=50, height=50, material_price=799.2, market=sweden, framed=False))
|
||||
|
||||
self.assertEqual(200, old_canvas_price(
|
||||
width=1, height=1, material_price=799.2, market=sweden, framed=False))
|
||||
|
||||
self.assertEqual(7493, old_canvas_price(
|
||||
width=500, height=500, material_price=799.2, market=sweden, framed=False))
|
||||
|
||||
self.assertEqual(5994, old_canvas_price(width=500, height=500,
|
||||
material_price=799.2, market=sweden, framed=False, inc_vat=False))
|
||||
self.assertEqual(7493, old_canvas_price(
|
||||
width=500, height=500, material_price=799.2, market=sweden, framed=False))
|
||||
self.assertEqual(236, old_canvas_price(
|
||||
width=50, height=50, material_price=799.2, market=denmark, framed=False))
|
||||
self.assertEqual(294, old_canvas_price(
|
||||
width=50, height=50, material_price=799.2, market=sweden, designer=designer, framed=False))
|
||||
self.assertEqual(510, old_canvas_price(
|
||||
width=50, height=50, material_price=799.2, market=sweden, framed=True))
|
||||
self.assertEqual(2972, old_canvas_price(
|
||||
width=150, height=140, material_price=799.2, market=sweden, framed=True))
|
||||
self.assertEqual(287, old_canvas_price(
|
||||
width=50, height=50, material_price=799.2, market=sweden, framed=False, reseller=reseller))
|
||||
@@ -0,0 +1,49 @@
|
||||
import json
|
||||
import flask_testing
|
||||
from api import create_app, db
|
||||
from api.models.product import Material, MaterialPrice
|
||||
|
||||
|
||||
class TestEndpoints(flask_testing.TestCase):
|
||||
|
||||
def create_app(self):
|
||||
return create_app('tests.config')
|
||||
|
||||
def setUp(self):
|
||||
db.create_all()
|
||||
|
||||
def tearDown(self):
|
||||
db.session.remove()
|
||||
db.drop_all()
|
||||
|
||||
def _add_material(self, name, groupid, price):
|
||||
material = Material(name=name)
|
||||
material.material_price = MaterialPrice(price=price, groupid=groupid)
|
||||
db.session.add(material)
|
||||
db.session.commit()
|
||||
return material
|
||||
|
||||
def test_canvas(self):
|
||||
self._add_material('standard-canvas', 2, 799.9)
|
||||
response = self.client.get(
|
||||
'/prices/canvas?width=200&height=10&territory=SE')
|
||||
self.assert200(response)
|
||||
products = response.json['data']
|
||||
for product in products:
|
||||
if product['framed']:
|
||||
self.assertEqual(150, product['width'])
|
||||
self.assertEqual(40, product['height'])
|
||||
self.assertEqual(1213, product['price'])
|
||||
else:
|
||||
self.assertEqual(200, product['width'])
|
||||
self.assertEqual(20, product['height'])
|
||||
self.assertEqual(400, product['price'])
|
||||
|
||||
def test_diy_frame(self):
|
||||
response = self.client.get(
|
||||
'/prices/diy-frame?width=200&height=50&territory=SE')
|
||||
self.assert200(response)
|
||||
product = response.json['data'][0]
|
||||
self.assertEqual(150, product['width'], "it adjusts measurements")
|
||||
self.assertEqual(50, product['height'])
|
||||
self.assertEqual(924, product['price'])
|
||||
Reference in New Issue
Block a user