From d41f5b2b32752e234a3c1feacc43ea461d152c22 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Tue, 9 Aug 2016 14:59:02 +0100 Subject: [PATCH 01/10] list referrals --- src/budy/controllers/api/__init__.py | 2 ++ src/budy/controllers/api/referral.py | 52 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/budy/controllers/api/referral.py diff --git a/src/budy/controllers/api/__init__.py b/src/budy/controllers/api/__init__.py index e8e6291c..1148c90b 100644 --- a/src/budy/controllers/api/__init__.py +++ b/src/budy/controllers/api/__init__.py @@ -48,6 +48,7 @@ from . import media from . import order from . import product +from . import referral from . import root from . import subscription from . import voucher @@ -66,6 +67,7 @@ from .media import MediaApiController from .order import OrderApiController from .product import ProductApiController +from .referral import ReferralApiController from .root import RootApiController from .subscription import SubscriptionApiController from .voucher import VoucherApiController diff --git a/src/budy/controllers/api/referral.py b/src/budy/controllers/api/referral.py new file mode 100644 index 00000000..aaa1dd5c --- /dev/null +++ b/src/budy/controllers/api/referral.py @@ -0,0 +1,52 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Hive Budy +# Copyright (c) 2008-2016 Hive Solutions Lda. +# +# This file is part of Hive Budy. +# +# Hive Budy is free software: you can redistribute it and/or modify +# it under the terms of the Apache License as published by the Apache +# Foundation, either version 2.0 of the License, or (at your option) any +# later version. +# +# Hive Budy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Apache License for more details. +# +# You should have received a copy of the Apache License along with +# Hive Budy. If not, see . + +__author__ = "João Magalhães " +""" The author(s) of the module """ + +__version__ = "1.0.0" +""" The version of the module """ + +__revision__ = "$LastChangedRevision$" +""" The revision number of the module """ + +__date__ = "$LastChangedDate$" +""" The last change date of the module """ + +__copyright__ = "Copyright (c) 2008-2016 Hive Solutions Lda." +""" The copyright for the module """ + +__license__ = "Apache License, Version 2.0" +""" The license for the module """ + +import appier + +import budy + +from . import root + +class ReferralApiController(root.RootApiController): + + @appier.route("/api/referrals", "GET", json = True) + def list(self): + object = appier.get_object(alias = True, find = True) + referrals = budy.Referral.find(map = True, **object) + return referrals From 17f653cae8c3a53e577933a1978e82efe2c72178 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Thu, 8 Sep 2016 12:18:51 +0100 Subject: [PATCH 02/10] add taxes and shipping to export --- src/budy/controllers/api/order.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/budy/controllers/api/order.py b/src/budy/controllers/api/order.py index 6c54d113..34e711b0 100644 --- a/src/budy/controllers/api/order.py +++ b/src/budy/controllers/api/order.py @@ -83,6 +83,7 @@ def complex_csv(self): "size", "quantity", "total", + "taxes", "currency", "first_name", "last_name", @@ -97,7 +98,8 @@ def complex_csv(self): "shipping_state", "shipping_postal_code", "shipping_country", - "shipping_phone" + "shipping_phone", + "shipping_cost" )] for order in orders: for line in order.lines: @@ -105,6 +107,7 @@ def complex_csv(self): account = order.account shipping_address = order.shipping_address billing_address = order.billing_address + shipping_cost = order.shipping_cost order_s = ( order.id, order.reference, @@ -118,6 +121,7 @@ def complex_csv(self): line.size, line.quantity, line.total, + line.taxes, line.currency, billing_address.first_name, billing_address.last_name, @@ -132,7 +136,8 @@ def complex_csv(self): shipping_address and shipping_address.state, shipping_address and shipping_address.postal_code, shipping_address and shipping_address.country, - shipping_address and shipping_address.phone_number + shipping_address and shipping_address.phone_number, + shipping_cost ) orders_s.append(order_s) result = appier.serialize_csv(orders_s, delimiter = ",") From d1c6c5bcdf20e96737bd28229fba9d5625f7ab67 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Thu, 8 Sep 2016 12:19:16 +0100 Subject: [PATCH 03/10] Taxes per bundle line --- src/budy/models/bundle.py | 2 +- src/budy/models/bundle_line.py | 28 ++++++++++++++++++- src/budy/models/measurement.py | 8 ++++++ src/budy/models/product.py | 49 ++++++++++++++++++++++++++++++++-- 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/budy/models/bundle.py b/src/budy/models/bundle.py index fe13f704..0149e80c 100644 --- a/src/budy/models/bundle.py +++ b/src/budy/models/bundle.py @@ -303,7 +303,7 @@ def calculate(self): self.quantity = sum(line.quantity for line in lines) self.sub_total = sum(line.total for line in lines) self.discount = self.calculate_discount() - self.taxes = self.calculate_taxes() + self.taxes = self.calculate_taxes() or sum(line.total_taxes for line in lines) self.shipping_cost = self.calculate_shipping() self.total = self.sub_total - self.discount + self.shipping_cost diff --git a/src/budy/models/bundle_line.py b/src/budy/models/bundle_line.py index c0d27536..3ecb61f9 100644 --- a/src/budy/models/bundle_line.py +++ b/src/budy/models/bundle_line.py @@ -49,6 +49,10 @@ class BundleLine(base.BudyBase): type = commons.Decimal ) + taxes = appier.field( + type = commons.Decimal + ) + currency = appier.field() country = appier.field() @@ -63,6 +67,11 @@ class BundleLine(base.BudyBase): initial = commons.Decimal(0.0) ) + total_taxes = appier.field( + type = commons.Decimal, + initial = commons.Decimal(0.0) + ) + size = appier.field( type = int ) @@ -98,6 +107,11 @@ def pre_save(self): def calculate(self, currency = None, country = None, force = False): currency = currency or self.currency country = country or self.country + self.total_taxes = self.quantity * self.get_taxes( + currency = currency, + country = country, + force = force + ) self.total = self.quantity * self.get_price( currency = currency, country = country, @@ -114,7 +128,7 @@ def measure(self, currency = None, country = None, force = False): def get_price(self, currency = None, country = None, force = False): is_dirty = self.is_dirty(currency = currency, country = country) - if not is_dirty and not force: return self.price + if self.price and not is_dirty and not force: return self.price self.price = self.merchandise.get_price( currency = currency, country = country, @@ -124,6 +138,18 @@ def get_price(self, currency = None, country = None, force = False): self.country = country return self.price + def get_taxes(self, currency = None, country = None, force = False): + is_dirty = self.is_dirty(currency = currency, country = country) + if self.taxes and not is_dirty and not force: return self.taxes + self.taxes = self.merchandise.get_taxes( + currency = currency, + country = country, + attributes = self.attributes + ) + self.currency = self.merchandise.get_currency(currency = currency) + self.country = country + return self.taxes + def get_size(self, currency = None, country = None, force = False): if not self.product: return None, None return self.product.get_size( diff --git a/src/budy/models/measurement.py b/src/budy/models/measurement.py index 82360333..10cce8b5 100644 --- a/src/budy/models/measurement.py +++ b/src/budy/models/measurement.py @@ -177,6 +177,14 @@ def get_price( ): return self.price + def get_taxes( + self, + currency = None, + country = None, + attributes = None + ): + return self.taxes + def get_currency(self, currency = None): return currency diff --git a/src/budy/models/product.py b/src/budy/models/product.py index 269365af..e23ba45d 100644 --- a/src/budy/models/product.py +++ b/src/budy/models/product.py @@ -548,6 +548,21 @@ def get_price( attributes = attributes ) + def get_taxes( + self, + currency = None, + country = None, + attributes = None + ): + if not self.price_provider: return self.taxes + method = getattr(self, "get_taxes_%s" % self.price_provider, None) + if not method: return self.taxes + return method( + currency = currency, + country = country, + attributes = attributes + ) + def get_price_ripe( self, currency = None, @@ -555,6 +570,37 @@ def get_price_ripe( attributes = None ): if not self.price_url: return self.price + + result = self.get_availability_ripe( + currency = currency, + country = country, + attributes = attributes + ) + total = result["total"] + return total["price_final"] + + def get_taxes_ripe( + self, + currency = None, + country = None, + attributes = None + ): + if not self.price_url: return self.price + + result = self.get_availability_ripe( + currency = currency, + country = country, + attributes = attributes + ) + total = result["total"] + return total["ddp"] + total["vat"] + + def get_availability_ripe( + self, + currency = None, + country = None, + attributes = None + ): attributes_m = json.loads(attributes) p = [] parts = attributes_m.get("parts", {}) @@ -580,8 +626,7 @@ def get_price_ripe( self.price_url, params = params ) - total = result["total"] - return total["price_final"] + return result def get_currency(self, currency = None): if not self.price_provider: return self.currency or currency From c4df564e7f17ee39cbacbedbca724674e0426c86 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Tue, 13 Sep 2016 12:27:07 +0100 Subject: [PATCH 04/10] bundle_line get_taxes done via get_price --- src/budy/models/bundle_line.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/budy/models/bundle_line.py b/src/budy/models/bundle_line.py index 3ecb61f9..b15efcf1 100644 --- a/src/budy/models/bundle_line.py +++ b/src/budy/models/bundle_line.py @@ -128,19 +128,12 @@ def measure(self, currency = None, country = None, force = False): def get_price(self, currency = None, country = None, force = False): is_dirty = self.is_dirty(currency = currency, country = country) - if self.price and not is_dirty and not force: return self.price + if not is_dirty and not force: return self.price self.price = self.merchandise.get_price( currency = currency, country = country, attributes = self.attributes ) - self.currency = self.merchandise.get_currency(currency = currency) - self.country = country - return self.price - - def get_taxes(self, currency = None, country = None, force = False): - is_dirty = self.is_dirty(currency = currency, country = country) - if self.taxes and not is_dirty and not force: return self.taxes self.taxes = self.merchandise.get_taxes( currency = currency, country = country, @@ -148,6 +141,11 @@ def get_taxes(self, currency = None, country = None, force = False): ) self.currency = self.merchandise.get_currency(currency = currency) self.country = country + self.get_taxes(currency = currency, country = country) + return self.price + + def get_taxes(self, currency = None, country = None, force = False): + self.get_price(currency = currency, country = country, force = force) return self.taxes def get_size(self, currency = None, country = None, force = False): @@ -178,6 +176,7 @@ def is_dirty(self, currency = None, country = None): is_dirty = not self.currency == currency is_dirty |= not self.country == country is_dirty |= not hasattr(self, "price") or self.price == None + is_dirty |= not hasattr(self, "taxes") or self.taxes == None return is_dirty def is_valid(self): From 6295b9061241ad53f677e88f6bd11f255555d242 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Fri, 16 Sep 2016 15:08:18 +0100 Subject: [PATCH 05/10] Import referrals by CSV --- src/budy/models/referral.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/budy/models/referral.py b/src/budy/models/referral.py index 3e76614f..94a84f79 100644 --- a/src/budy/models/referral.py +++ b/src/budy/models/referral.py @@ -65,3 +65,23 @@ def list_names(cls): @classmethod def order_name(self): return ["id", -1] + + @classmethod + @appier.operation( + name = "Import CSV", + parameters = ( + ("CSV File", "file", "file"), + ("Empty source", "empty", bool, True) + ) + ) + def import_csv_s(cls, file, empty): + def callback(line): + store, name = line + composed_name = "%s|%s" % (name, store) + referral = cls( + name = composed_name + ) + referral.save() + + if empty: cls.delete_c() + cls._csv_import(file, callback) From f86087ffb2dc9331c3b24698c630eae25aa2b26e Mon Sep 17 00:00:00 2001 From: Afonso Neves Caldas Date: Wed, 21 Sep 2016 15:55:17 +0100 Subject: [PATCH 06/10] moved taxes calculation to eval_taxes --- src/budy/models/bundle.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/budy/models/bundle.py b/src/budy/models/bundle.py index 8105ed17..02223cbd 100644 --- a/src/budy/models/bundle.py +++ b/src/budy/models/bundle.py @@ -190,7 +190,10 @@ def eval_shipping(cls, *args, **kwargs): @classmethod def eval_taxes(cls, *args, **kwargs): taxes = appier.conf("BUDY_TAXES", None) - if not taxes: return 0.0 + if not taxes: + bundle = args[3] + lines = bundle.lines + return sum(line.total_taxes for line in lines) taxes = eval(taxes) return taxes(*args, **kwargs) @@ -331,7 +334,7 @@ def calculate(self): self.quantity = sum(line.quantity for line in lines) self.sub_total = sum(line.total for line in lines) self.discount = self.calculate_discount() - self.taxes = self.calculate_taxes() or sum(line.total_taxes for line in lines) + self.taxes = self.calculate_taxes() self.shipping_cost = self.calculate_shipping() self.total = self.sub_total - self.discount + self.shipping_cost From dca1f3d0b8fd174934c568085b813d7ae007b668 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Thu, 22 Sep 2016 12:46:44 +0100 Subject: [PATCH 07/10] Move build_taxes sum by line to build_taxes --- src/budy/models/bundle.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/budy/models/bundle.py b/src/budy/models/bundle.py index 02223cbd..ed15cfa1 100644 --- a/src/budy/models/bundle.py +++ b/src/budy/models/bundle.py @@ -190,10 +190,7 @@ def eval_shipping(cls, *args, **kwargs): @classmethod def eval_taxes(cls, *args, **kwargs): taxes = appier.conf("BUDY_TAXES", None) - if not taxes: - bundle = args[3] - lines = bundle.lines - return sum(line.total_taxes for line in lines) + if not taxes: return 0.0 taxes = eval(taxes) return taxes(*args, **kwargs) @@ -366,12 +363,21 @@ def build_discount(self): return discount def build_taxes(self): - return self.__class__.eval_taxes( + taxes = 0.0 + join_taxes = appier.conf("BUDY_JOIN_TAXES", True, cast = bool) + taxes_dynamic = self.__class__.eval_taxes( self.sub_total, self.taxes, self.quantity, self ) + taxes_lines = sum(line.total_taxes for line in self.lines) + if join_taxes: + taxes += taxes_dynamic + taxes += taxes_lines + else: + taxes += max(taxes_dynamic, taxes_lines) + return taxes def build_shipping(self): return self.__class__.eval_shipping( From 7f6fd92981d1ffb8f17655e4502965ceca36a5f8 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Tue, 27 Sep 2016 15:05:11 +0100 Subject: [PATCH 08/10] Change order owner. Fixes #873. --- src/budy/controllers/api/order.py | 13 +++++++++++++ src/budy/models/order.py | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/budy/controllers/api/order.py b/src/budy/controllers/api/order.py index 76722e0a..bc66cab5 100644 --- a/src/budy/controllers/api/order.py +++ b/src/budy/controllers/api/order.py @@ -367,6 +367,19 @@ def set_meta(self, key): order = order.reload(map = True) return order + @appier.route("/api/orders//account", "PUT", json = True) + @appier.ensure(token = "user") + def set_account(self, key): + data = appier.request_json() + username = data["username"] + order = budy.Order.get(key = key, rules = False) + account = budy.BudyAccount.from_session() + order.verify_account(account) + new_account = budy.BudyAccount.get(username = username) + order.set_account_s(new_account) + order = order.reload(map = True) + return order + @appier.route("/api/orders//wait_payment", "PUT", json = True) @appier.ensure(token = "user") def wait_payment(self, key): diff --git a/src/budy/models/order.py b/src/budy/models/order.py index ac8dad03..c80eb36c 100644 --- a/src/budy/models/order.py +++ b/src/budy/models/order.py @@ -322,7 +322,10 @@ def build_discount(self): if self.discount_voucher > base_discount: return self.discount_voucher return base_discount - def set_account_s(self, account): + def set_account_s(self, account, force = False): + if not force: + self.verify_base() + appier.verify(self.status == "created") self.account = account self.store = self.account.store self.save() From 6eba36182e386b078509bccdf8237080d95e84e3 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Tue, 27 Sep 2016 16:04:07 +0100 Subject: [PATCH 09/10] Move set_account_s preconditions to verify_open --- src/budy/models/order.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/budy/models/order.py b/src/budy/models/order.py index c80eb36c..858a3cf6 100644 --- a/src/budy/models/order.py +++ b/src/budy/models/order.py @@ -322,10 +322,9 @@ def build_discount(self): if self.discount_voucher > base_discount: return self.discount_voucher return base_discount - def set_account_s(self, account, force = False): - if not force: - self.verify_base() - appier.verify(self.status == "created") + def set_account_s(self, account): + self.verify_base() + self.verify_open() self.account = account self.store = self.account.store self.save() @@ -505,6 +504,15 @@ def verify_base(self): appier.verify(len(self.lines) > 0) + def verify_open(self): + """ + Verifies that the current order is considered open, + meaning that the it is still under the checkout stage. + """ + + appier.verify(self.status == "created") + appier.verify(self.paid == False) + def verify_shippable(self): appier.verify(not self.shipping_address == None) appier.verify(not self.billing_address == None) From 74211d6af3ce1bbb94a399ba321c190596244720 Mon Sep 17 00:00:00 2001 From: Gabriel Candal Date: Tue, 27 Sep 2016 16:32:58 +0100 Subject: [PATCH 10/10] Change set account order logic (you can takeover any open order) --- src/budy/controllers/api/order.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/budy/controllers/api/order.py b/src/budy/controllers/api/order.py index bc66cab5..339a894a 100644 --- a/src/budy/controllers/api/order.py +++ b/src/budy/controllers/api/order.py @@ -370,13 +370,9 @@ def set_meta(self, key): @appier.route("/api/orders//account", "PUT", json = True) @appier.ensure(token = "user") def set_account(self, key): - data = appier.request_json() - username = data["username"] order = budy.Order.get(key = key, rules = False) account = budy.BudyAccount.from_session() - order.verify_account(account) - new_account = budy.BudyAccount.get(username = username) - order.set_account_s(new_account) + order.set_account_s(account) order = order.reload(map = True) return order