diff --git a/.gitignore b/.gitignore index aa3cb3c5..1665cf12 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ easypay.shelve* /.vscode/settings.json +/.venv + /dist /build /src/budy.egg-info diff --git a/CHANGELOG.md b/CHANGELOG.md index 99918c14..d139a001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -* +* Made code compliant with black ### Fixed diff --git a/setup.py b/setup.py index 69658784..f0db731b 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,41 +32,35 @@ import setuptools setuptools.setup( - name = "budy", - version = "0.8.10", - author = "Hive Solutions Lda.", - author_email = "development@hive.pt", - description = "Budy E-commerce System", - license = "Apache License, Version 2.0", - keywords = "budy e-commerce engine web json", - url = "http://budy.hive.pt", - zip_safe = False, - packages = [ + name="budy", + version="0.8.10", + author="Hive Solutions Lda.", + author_email="development@hive.pt", + description="Budy E-commerce System", + license="Apache License, Version 2.0", + keywords="budy e-commerce engine web json", + url="http://budy.hive.pt", + zip_safe=False, + packages=[ "budy", "budy.controllers", "budy.controllers.api", "budy.controllers.web", "budy.models", - "budy.test" + "budy.test", ], - test_suite = "budy.test", - package_dir = { - "" : os.path.normpath("src") - }, - package_data = { - "budy" : [ + test_suite="budy.test", + package_dir={"": os.path.normpath("src")}, + package_data={ + "budy": [ "static/js/*.js", "templates/*.tpl", "templates/order/*.tpl", - "templates/partials/*.tpl" + "templates/partials/*.tpl", ] }, - install_requires = [ - "appier", - "appier-extras", - "commons-py" - ], - classifiers = [ + install_requires=["appier", "appier-extras", "commons-py"], + classifiers=[ "Development Status :: 3 - Alpha", "Topic :: Utilities", "License :: OSI Approved :: Apache Software License", @@ -81,8 +75,10 @@ "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7" + "Programming Language :: Python :: 3.7", ], - long_description = open(os.path.join(os.path.dirname(__file__), "README.md"), "rb").read().decode("utf-8"), - long_description_content_type = "text/markdown" + long_description=open(os.path.join(os.path.dirname(__file__), "README.md"), "rb") + .read() + .decode("utf-8"), + long_description_content_type="text/markdown", ) diff --git a/src/budy/__init__.py b/src/budy/__init__.py index 5cfea0e8..5ca564c2 100644 --- a/src/budy/__init__.py +++ b/src/budy/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -19,7 +19,7 @@ # You should have received a copy of the Apache License along with # Hive Budy. If not, see . -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" diff --git a/src/budy/bots/__init__.py b/src/budy/bots/__init__.py index 8638c0ef..5ab70b3f 100644 --- a/src/budy/bots/__init__.py +++ b/src/budy/bots/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -19,7 +19,7 @@ # You should have received a copy of the Apache License along with # Hive Budy. If not, see . -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" diff --git a/src/budy/bots/base.py b/src/budy/bots/base.py index e3580fea..4354613c 100644 --- a/src/budy/bots/base.py +++ b/src/budy/bots/base.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,12 +30,14 @@ import logging -class Bot(object): +class Bot(object): def __init__(self, *args, **kwargs): self.owner = kwargs.get("owner", None) @property def logger(self): - if self.owner: return self.owner.logger - else: return logging.getLogger() + if self.owner: + return self.owner.logger + else: + return logging.getLogger() diff --git a/src/budy/bots/omni_bot.py b/src/budy/bots/omni_bot.py index 3fed6847..70b32f1c 100644 --- a/src/budy/bots/omni_bot.py +++ b/src/budy/bots/omni_bot.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -40,12 +40,12 @@ """ The default value for the number of records that are going to be retrieved per each HTTP request """ -class OmniBot(base.Bot): +class OmniBot(base.Bot): def __init__(self, *args, **kwargs): base.Bot.__init__(self, *args, **kwargs) - self.enabled = appier.conf("OMNI_BOT_ENABLED", False, cast = bool) - self.store = appier.conf("OMNI_BOT_STORE", None, cast = int) + self.enabled = appier.conf("OMNI_BOT_ENABLED", False, cast=bool) + self.store = appier.conf("OMNI_BOT_STORE", None, cast=int) self.records = appier.conf("OMNI_BOT_RECORDS", RECORDS) self.enabled = kwargs.get("enabled", self.enabled) self.store = kwargs.get("store", self.store) @@ -53,7 +53,8 @@ def __init__(self, *args, **kwargs): self.api = None def tick(self): - if not self.enabled: return + if not self.enabled: + return self.sync_products() self.fix_products() self.gc_products() @@ -84,18 +85,14 @@ def sync_products_store(self): while True: kwargs = { - "filter_string" : "", - "start_record" : offset, - "number_records" : self.records, - "filters[]" : [ - "sellable:equals:2" - ] + "filter_string": "", + "start_record": offset, + "number_records": self.records, + "filters[]": ["sellable:equals:2"], } - merchandise = api.list_store_merchandise( - store_id = self.store, - **kwargs - ) - if not merchandise: break + merchandise = api.list_store_merchandise(store_id=self.store, **kwargs) + if not merchandise: + break offset += len(merchandise) for merchandise in merchandise: @@ -103,41 +100,42 @@ def sync_products_store(self): is_product = _class in ("Product",) is_sub_product = _class in ("SubProduct",) is_valid = is_product or is_sub_product - if not is_valid: continue + if not is_valid: + continue inventory_lines = api.list_inventory_lines( - store_id = self.store, - number_records = 1, + store_id=self.store, + number_records=1, **{ - "filter_string" : "", - "filters[]" : [ + "filter_string": "", + "filters[]": [ "functional_unit:equals:%d" % self.store, - "merchandise:equals:%d" % merchandise["object_id"] - ] + "merchandise:equals:%d" % merchandise["object_id"], + ], } ) inventory_line = inventory_lines[0] inventory_lines = api.list_inventory_lines( - store_id = self.store, - number_records = -1, + store_id=self.store, + number_records=-1, **{ - "filter_string" : "", - "filters[]" : [ + "filter_string": "", + "filters[]": [ "functional_unit:not_equals:%d" % self.store, - "merchandise:equals:%d" % merchandise["object_id"] - ] + "merchandise:equals:%d" % merchandise["object_id"], + ], } ) if is_product: self.sync_product_safe( merchandise, - inventory_line = inventory_line, - inventory_lines = inventory_lines + inventory_line=inventory_line, + inventory_lines=inventory_lines, ) else: self.sync_sub_product_safe( merchandise, - inventory_line = inventory_line, - inventory_lines = inventory_lines + inventory_line=inventory_line, + inventory_lines=inventory_lines, ) self.logger.info("Ended syncing of products from store") @@ -148,16 +146,18 @@ def sync_products_db(self): api = self.get_api() products = budy.Product.find() - self.logger.info( - "Syncing %d products in database ..." % len(products) - ) + self.logger.info("Syncing %d products in database ..." % len(products)) for product in products: object_id = product.meta.get("object_id", None) - if not object_id: continue - try: merchandise = api.get_product(object_id) - except self.get_exception(): continue - if not merchandise: continue + if not object_id: + continue + try: + merchandise = api.get_product(object_id) + except self.get_exception(): + continue + if not merchandise: + continue merchandise.pop("stock_on_hand", None) merchandise.pop("retail_price", None) merchandise.pop("price", None) @@ -171,16 +171,18 @@ def sync_measurements_db(self): api = self.get_api() measurements = budy.Measurement.find() - self.logger.info( - "Syncing %d measurements in database ..." % len(measurements) - ) + self.logger.info("Syncing %d measurements in database ..." % len(measurements)) for measurement in measurements: object_id = measurement.meta.get("object_id", None) - if not object_id: continue - try: merchandise = api.get_sub_product(object_id) - except self.get_exception(): continue - if not merchandise: continue + if not object_id: + continue + try: + merchandise = api.get_sub_product(object_id) + except self.get_exception(): + continue + if not merchandise: + continue merchandise.pop("stock_on_hand", None) merchandise.pop("retail_price", None) merchandise.pop("price", None) @@ -191,20 +193,18 @@ def sync_measurements_db(self): def fix_products_db(self): products = budy.Product.find() - self.logger.info( - "Fixing %d products in database ..." % len(products) - ) + self.logger.info("Fixing %d products in database ..." % len(products)) - for product in products: product.fix_s() + for product in products: + product.fix_s() def fix_measurements_db(self): measurements = budy.Measurement.find() - self.logger.info( - "Fixing %d measurements in database ..." % len(measurements) - ) + self.logger.info("Fixing %d measurements in database ..." % len(measurements)) - for measurement in measurements: measurement.fix_s() + for measurement in measurements: + measurement.fix_s() def gc_measurements_db(self): measurements = budy.Measurement.find() @@ -216,10 +216,7 @@ def gc_measurements_db(self): # sorts the measurements (product dimensions) according to the # modified data, from latest to oldest to be able to iterate # over them to remove possible duplication from the database - measurements.sort( - key = lambda v: v.meta.get("modify_date", 0), - reverse = True - ) + measurements.sort(key=lambda v: v.meta.get("modify_date", 0), reverse=True) # creates the list that is going to be used to detect possible # duplicated sub product object ids @@ -238,21 +235,19 @@ def gc_measurements_db(self): object_ids.append(object_id) def sync_product_safe(self, merchandise, *args, **kwargs): - try: self.sync_product(merchandise, *args, **kwargs) + try: + self.sync_product(merchandise, *args, **kwargs) except Exception as exception: object_id = merchandise["object_id"] self.logger.warn( "Problem syncing product %d - %s ..." % (object_id, exception) ) lines = traceback.format_exc().splitlines() - for line in lines: self.logger.info(line) + for line in lines: + self.logger.info(line) def sync_product( - self, - merchandise, - inventory_line = None, - inventory_lines = None, - force = False + self, merchandise, inventory_line=None, inventory_lines=None, force=False ): # retrieves the reference to the API object that is # going to be used for API based operations @@ -267,9 +262,9 @@ def sync_product( # information that has just been retrieved product = budy.Product.from_omni( merchandise, - inventory_line = inventory_line, - inventory_lines = inventory_lines, - force = force + inventory_line=inventory_line, + inventory_lines=inventory_lines, + force=force, ) product.save() product.images = [] @@ -277,9 +272,7 @@ def sync_product( # retrieves the media information associated with the # current merchandise to be able to sync it by either # creating new local medias or re-using existing ones - media = api.info_media_entity( - object_id, dimensions = "original" - ) + media = api.info_media_entity(object_id, dimensions="original") # iterates over the complete set of media associated with # the current product to try to create/update its media @@ -289,7 +282,7 @@ def sync_product( # value tries to retrieve a possible already existing # and equivalent media (avoids duplication) unique = "%d-%d" % (item["object_id"], item["modify_date"]) - _media = budy.Media.get(unique = unique, raise_e = False) + _media = budy.Media.get(unique=unique, raise_e=False) # in case the media does not exist, tries to retrieve the # new remote data from the source and create a new media @@ -297,12 +290,12 @@ def sync_product( media_url = api.get_media_url(item["secret"]) data = appier.get(media_url) _media = budy.Media( - description = item["dimensions"], - label = item["label"], - order = item["position"] or 1, - size = item["dimensions"], - unique = unique, - file = appier.File((item["label"], None, data)) + description=item["dimensions"], + label=item["label"], + order=item["position"] or 1, + size=item["dimensions"], + unique=unique, + file=appier.File((item["label"], None, data)), ) _media.save() @@ -313,12 +306,12 @@ def sync_product( ("thumbnail", 260), ("thumbnail_2x", 540), ("large", 540), - ("large_2x", 1080) + ("large_2x", 1080), ): resized_unique = "%s-%s" % (unique, suffix) - resized = budy.Media.get(unique = resized_unique, raise_e = False) + resized = budy.Media.get(unique=resized_unique, raise_e=False) if not resized: - resized = _media.thumbnail_s(width = size, suffix = suffix) + resized = _media.thumbnail_s(width=size, suffix=suffix) resized.save() product.images.append(resized) @@ -326,21 +319,19 @@ def sync_product( product.save() def sync_sub_product_safe(self, merchandise, *args, **kwargs): - try: self.sync_sub_product(merchandise, *args, **kwargs) + try: + self.sync_sub_product(merchandise, *args, **kwargs) except Exception as exception: object_id = merchandise["object_id"] self.logger.warn( "Problem syncing sub product %d - %s ..." % (object_id, exception) ) lines = traceback.format_exc().splitlines() - for line in lines: self.logger.info(line) + for line in lines: + self.logger.info(line) def sync_sub_product( - self, - merchandise, - inventory_line = None, - inventory_lines = None, - force = False + self, merchandise, inventory_line=None, inventory_lines=None, force=False ): api = self.get_api() @@ -355,23 +346,24 @@ def sync_sub_product( # the proper associated (parent) product is created measurement = budy.Measurement.from_omni( merchandise, - sub_product = sub_product, - inventory_line = inventory_line, - inventory_lines = inventory_lines, - force = force + sub_product=sub_product, + inventory_line=inventory_line, + inventory_lines=inventory_lines, + force=force, ) if not measurement: product = api.get_product(product["object_id"]) - self.sync_product(product, force = True) + self.sync_product(product, force=True) measurement = budy.Measurement.from_omni( merchandise, - sub_product = sub_product, - inventory_line = inventory_line, - inventory_lines = inventory_lines, - force = force + sub_product=sub_product, + inventory_line=inventory_line, + inventory_lines=inventory_lines, + force=force, ) - if not measurement: return + if not measurement: + return measurement.save() @@ -383,10 +375,13 @@ def sync_sub_product( def get_api(self): import omni - if self.api: return self.api + + if self.api: + return self.api self.api = omni.API() return self.api def get_exception(self): import omni + return omni.OmniError diff --git a/src/budy/bots/scheduler.py b/src/budy/bots/scheduler.py index 5a6c906f..aae33d23 100644 --- a/src/budy/bots/scheduler.py +++ b/src/budy/bots/scheduler.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -39,24 +39,12 @@ to spend many resources or to high to create a long set of time between external interactions """ -class Scheduler(appier.Scheduler): +class Scheduler(appier.Scheduler): def __init__(self, owner, *args, **kwargs): - appier.Scheduler.__init__( - self, - owner, - timeout = LOOP_TIMEOUT, - *args, - **kwargs - ) - self.omni_bot = omni_bot.OmniBot( - owner = owner, - **kwargs - ) - self.tracking_bot = tracking_bot.TrackingBot( - owner = owner, - **kwargs - ) + appier.Scheduler.__init__(self, owner, timeout=LOOP_TIMEOUT, *args, **kwargs) + self.omni_bot = omni_bot.OmniBot(owner=owner, **kwargs) + self.tracking_bot = tracking_bot.TrackingBot(owner=owner, **kwargs) def tick(self): appier.Scheduler.tick(self) diff --git a/src/budy/bots/tracking_bot.py b/src/budy/bots/tracking_bot.py index 76fb2d39..0b860d6b 100644 --- a/src/budy/bots/tracking_bot.py +++ b/src/budy/bots/tracking_bot.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -36,33 +36,35 @@ from . import base -class TrackingBot(base.Bot): +class TrackingBot(base.Bot): def __init__(self, *args, **kwargs): base.Bot.__init__(self, *args, **kwargs) - self.enabled = appier.conf("TRACKING_BOT_ENABLED", False, cast = bool) - self.window = appier.conf("TRACKING_BOT_WINDOW", 14 * 86400, cast = int) + self.enabled = appier.conf("TRACKING_BOT_ENABLED", False, cast=bool) + self.window = appier.conf("TRACKING_BOT_WINDOW", 14 * 86400, cast=int) self.enabled = kwargs.get("enabled", self.enabled) self.window = kwargs.get("window", self.window) self.api = None def tick(self): - if not self.enabled: return + if not self.enabled: + return self.sync_sent() def sync_sent(self): orders = budy.Order.find_e( - status = "sent", - created = {"$gt" : time.time() - self.window} + status="sent", created={"$gt": time.time() - self.window} ) for order in orders: - if not order.tracking_number: continue + if not order.tracking_number: + continue try: result = appier.get( "https://cttpie.stage.hive.pt", - params = dict(tracking = order.tracking_number) + params=dict(tracking=order.tracking_number), ) - if not result["status"] == "Entregue": continue + if not result["status"] == "Entregue": + continue order.mark_received_s() except Exception as exception: self.logger.warn( diff --git a/src/budy/controllers/__init__.py b/src/budy/controllers/__init__.py index 69106a1b..0a38d3cf 100644 --- a/src/budy/controllers/__init__.py +++ b/src/budy/controllers/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -19,7 +19,7 @@ # You should have received a copy of the Apache License along with # Hive Budy. If not, see . -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" diff --git a/src/budy/controllers/api/__init__.py b/src/budy/controllers/api/__init__.py index b1ee88d5..6013802b 100644 --- a/src/budy/controllers/api/__init__.py +++ b/src/budy/controllers/api/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -19,7 +19,7 @@ # You should have received a copy of the Apache License along with # Hive Budy. If not, see . -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" diff --git a/src/budy/controllers/api/account.py b/src/budy/controllers/api/account.py index b6f0f33c..b18d42bc 100644 --- a/src/budy/controllers/api/account.py +++ b/src/budy/controllers/api/account.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,50 +34,50 @@ from . import root -class AccountAPIController(root.RootAPIController): - @appier.route("/api/accounts", "GET", json = True) - @appier.ensure(token = "admin") +class AccountAPIController(root.RootAPIController): + @appier.route("/api/accounts", "GET", json=True) + @appier.ensure(token="admin") def list(self): - object = appier.get_object(alias = True, find = True) - accounts = budy.BudyAccount.find(map = True, **object) + object = appier.get_object(alias=True, find=True) + accounts = budy.BudyAccount.find(map=True, **object) return accounts - @appier.route("/api/accounts", "POST", json = True) + @appier.route("/api/accounts", "POST", json=True) def create(self): - pre_enabled = self.field("pre_enabled", False, cast = bool) + pre_enabled = self.field("pre_enabled", False, cast=bool) account = budy.BudyAccount.new() account.type = budy.BudyAccount.USER_TYPE account.password_confirm = account.password account.enabled = pre_enabled account.save() - account = account.reload(rules = True, map = True) + account = account.reload(rules=True, map=True) return account - @appier.route("/api/accounts/confirm/", "GET", json = True) + @appier.route("/api/accounts/confirm/", "GET", json=True) def confirm(self, token): - account = budy.BudyAccount.get(confirmation_token = token) + account = budy.BudyAccount.get(confirmation_token=token) account.confirm_s() return account - @appier.route("/api/accounts/recover/", "GET", json = True) + @appier.route("/api/accounts/recover/", "GET", json=True) def recover(self, identifier): budy.BudyAccount.recover(identifier) - @appier.route("/api/accounts/reset", "POST", json = True) + @appier.route("/api/accounts/reset", "POST", json=True) def reset(self): - token = self.field("token", mandatory = True) - password = self.field("password", mandatory = True) + token = self.field("token", mandatory=True) + password = self.field("password", mandatory=True) budy.BudyAccount.reset(token, password, password) - @appier.route("/api/accounts/me", "GET", json = True) - @appier.ensure(token = "user") + @appier.route("/api/accounts/me", "GET", json=True) + @appier.ensure(token="user") def me(self): - account = budy.BudyAccount.from_session(map = True) + account = budy.BudyAccount.from_session(map=True) return account - @appier.route("/api/accounts/me", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/accounts/me", "PUT", json=True) + @appier.ensure(token="user") def update_me(self): account = budy.BudyAccount.from_session() account.apply() @@ -85,84 +85,65 @@ def update_me(self): account = account.map() return account - @appier.route("/api/accounts/me/avatar", "GET", json = True) - @appier.ensure(token = "user") + @appier.route("/api/accounts/me/avatar", "GET", json=True) + @appier.ensure(token="user") def avatar_me(self): - account = budy.BudyAccount.from_session(rules = False) + account = budy.BudyAccount.from_session(rules=False) return account._send_avatar() - @appier.route("/api/accounts/me/orders", "GET", json = True) - @appier.ensure(token = "user") + @appier.route("/api/accounts/me/orders", "GET", json=True) + @appier.ensure(token="user") def orders_me(self): - object = appier.get_object(alias = True, find = True, sort = [("id", -1)]) + object = appier.get_object(alias=True, find=True, sort=[("id", -1)]) account = budy.BudyAccount.from_session() orders = budy.Order.find( - account = account.id, - paid = True, - eager = ("lines",), - map = True, - **object + account=account.id, paid=True, eager=("lines",), map=True, **object ) return orders - @appier.route("/api/accounts/me/addresses", "GET", json = True) - @appier.ensure(token = "user") + @appier.route("/api/accounts/me/addresses", "GET", json=True) + @appier.ensure(token="user") def addresses_me(self): - account = budy.BudyAccount.from_session( - eager = ("addresses",), - map = True - ) + account = budy.BudyAccount.from_session(eager=("addresses",), map=True) return account["addresses"] - @appier.route("/api/accounts/me/addresses", "POST", json = True) - @appier.ensure(token = "user") + @appier.route("/api/accounts/me/addresses", "POST", json=True) + @appier.ensure(token="user") def create_address_me(self): address = budy.Address.new() address.save() - account = budy.BudyAccount.from_session(rules = False) + account = budy.BudyAccount.from_session(rules=False) account.addresses.append(address) account.save() address = address.map() return address - @appier.route("/api/accounts/me/addresses/", "DELETE", json = True) - @appier.ensure(token = "user") + @appier.route("/api/accounts/me/addresses/", "DELETE", json=True) + @appier.ensure(token="user") def delete_address_me(self, key): - address = budy.Address.get(key = key) - account = budy.BudyAccount.from_session(rules = False) + address = budy.Address.get(key=key) + account = budy.BudyAccount.from_session(rules=False) account.addresses.remove(address.id) account.save() address.delete() @appier.route("/api/accounts/simple.csv", "GET") - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def simple_csv(self): - object = appier.get_object( - alias = True, - find = True, - limit = 0 - ) - accounts = budy.BudyAccount.find( - **object - ) + object = appier.get_object(alias=True, find=True, limit=0) + accounts = budy.BudyAccount.find(**object) - accounts_s = [( - "username", - "email", - "first_name", - "last_name", - "birth_date" - )] + accounts_s = [("username", "email", "first_name", "last_name", "birth_date")] for account in accounts: account_s = ( account.username, account.email, account.first_name, account.last_name, - account.birth_date + account.birth_date, ) accounts_s.append(account_s) - result = appier.serialize_csv(accounts_s, delimiter = ",") + result = appier.serialize_csv(accounts_s, delimiter=",") self.content_type("text/csv") return result diff --git a/src/budy/controllers/api/address.py b/src/budy/controllers/api/address.py index db7c1f6c..1b9ba382 100644 --- a/src/budy/controllers/api/address.py +++ b/src/budy/controllers/api/address.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,39 +34,39 @@ from . import root -class AddressAPIController(root.RootAPIController): - @appier.route("/api/addresses", "GET", json = True) - @appier.ensure(token = "admin") +class AddressAPIController(root.RootAPIController): + @appier.route("/api/addresses", "GET", json=True) + @appier.ensure(token="admin") def list(self): - object = appier.get_object(alias = True, find = True) - addresses = budy.Address.find(map = True, **object) + object = appier.get_object(alias=True, find=True) + addresses = budy.Address.find(map=True, **object) return addresses - @appier.route("/api/addresses", "POST", json = True) - @appier.ensure(token = "user") + @appier.route("/api/addresses", "POST", json=True) + @appier.ensure(token="user") def create(self): address = budy.Address.new() address.save() address = address.map() return address - @appier.route("/api/addresses/", "GET", json = True) - @appier.ensure(token = "user") + @appier.route("/api/addresses/", "GET", json=True) + @appier.ensure(token="user") def show(self, key): - address = budy.Address.get(key = key, map = True) + address = budy.Address.get(key=key, map=True) return address - @appier.route("/api/addresses/", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/addresses/", "PUT", json=True) + @appier.ensure(token="user") def update(self, key): - address = budy.Address.get(key = key, rules = False) + address = budy.Address.get(key=key, rules=False) address.apply() address.save() address = address.map() return address - @appier.route("/api/addresses/", "DELETE", json = True) + @appier.route("/api/addresses/", "DELETE", json=True) def delete(self, key): - address = budy.Address.get(key = key) + address = budy.Address.get(key=key) address.delete() diff --git a/src/budy/controllers/api/bag.py b/src/budy/controllers/api/bag.py index 62281077..f01a7ffb 100644 --- a/src/budy/controllers/api/bag.py +++ b/src/budy/controllers/api/bag.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,91 +34,89 @@ from . import root -class BagAPIController(root.RootAPIController): - @appier.route("/api/bags", "GET", json = True) - @appier.ensure(token = "admin") +class BagAPIController(root.RootAPIController): + @appier.route("/api/bags", "GET", json=True) + @appier.ensure(token="admin") def list(self): - object = appier.get_object(alias = True, find = True) - bags = budy.Bag.find(eager_l = True, map = True, **object) + object = appier.get_object(alias=True, find=True) + bags = budy.Bag.find(eager_l=True, map=True, **object) return bags - @appier.route("/api/bags", "POST", json = True) + @appier.route("/api/bags", "POST", json=True) def create(self): bag = budy.Bag.new() bag.save() bag = bag.map() return bag - @appier.route("/api/bags/key", "GET", json = True) + @appier.route("/api/bags/key", "GET", json=True) def key(self): bag = budy.Bag.from_session() - return dict(key = bag.key) + return dict(key=bag.key) - @appier.route("/api/bags/", "GET", json = True) + @appier.route("/api/bags/", "GET", json=True) def show(self, key): - ensure = self.field("ensure", True, cast = bool) - try_valid = self.field("try_valid", True, cast = bool) - bag = budy.Bag.get(key = key, raise_e = not ensure) - if not bag: bag = budy.Bag.ensure_s(key = key) - bag.refresh_s(currency = self.currency, country = self.country) - if try_valid: bag.try_valid_s() + ensure = self.field("ensure", True, cast=bool) + try_valid = self.field("try_valid", True, cast=bool) + bag = budy.Bag.get(key=key, raise_e=not ensure) + if not bag: + bag = budy.Bag.ensure_s(key=key) + bag.refresh_s(currency=self.currency, country=self.country) + if try_valid: + bag.try_valid_s() bag = bag.reload( - eager = ( - "lines.product.images", - "lines.product.brand" - ), - map = True + eager=("lines.product.images", "lines.product.brand"), map=True ) return bag - @appier.route("/api/bags//merge/", "PUT", json = True) + @appier.route("/api/bags//merge/", "PUT", json=True) def merge(self, key, target): - increment = self.field("increment", False, cast = bool) - bag = budy.Bag.get(key = key) - target = budy.Bag.get(key = target) - bag.merge_s(target.id, increment = increment) - bag = bag.reload(map = True) + increment = self.field("increment", False, cast=bool) + bag = budy.Bag.get(key=key) + target = budy.Bag.get(key=target) + bag.merge_s(target.id, increment=increment) + bag = bag.reload(map=True) return bag - @appier.route("/api/bags//lines", "POST", json = True) + @appier.route("/api/bags//lines", "POST", json=True) def add_line(self, key): line = budy.BagLine.new() line.ensure_size_s() line.save() - bag = budy.Bag.get(key = key) + bag = budy.Bag.get(key=key) bag.lines.append(line) bag.save() - line = line.reload(map = True) + line = line.reload(map=True) return line - @appier.route("/api/bags//lines/", "DELETE", json = True) + @appier.route("/api/bags//lines/", "DELETE", json=True) def remove_line(self, key, line_id): - bag = budy.Bag.get(key = key) + bag = budy.Bag.get(key=key) bag.remove_line_s(line_id) - bag = bag.reload(map = True) + bag = bag.reload(map=True) return bag - @appier.route("/api/bags//lines/add_update", "POST", json = True) + @appier.route("/api/bags//lines/add_update", "POST", json=True) def add_update_line(self, key): - increment = self.field("increment", True, cast = bool) + increment = self.field("increment", True, cast=bool) line = budy.BagLine.new() line.ensure_size_s() - bag = budy.Bag.get(key = key) - line = bag.add_update_line_s(line, increment = increment) - line = line.reload(map = True) + bag = budy.Bag.get(key=key) + line = bag.add_update_line_s(line, increment=increment) + line = line.reload(map=True) return line - @appier.route("/api/bags//empty", "GET", json = True) + @appier.route("/api/bags//empty", "GET", json=True) def empty(self, key): - bag = budy.Bag.get(key = key) + bag = budy.Bag.get(key=key) bag.empty_s() - bag = bag.reload(map = True) + bag = bag.reload(map=True) return bag - @appier.route("/api/bags//order", "GET", json = True) + @appier.route("/api/bags//order", "GET", json=True) def order(self, key): - bag = budy.Bag.get(key = key) + bag = budy.Bag.get(key=key) order = bag.to_order_s() - order = order.reload(map = True) + order = order.reload(map=True) return order diff --git a/src/budy/controllers/api/base.py b/src/budy/controllers/api/base.py index c776126f..219ffa83 100644 --- a/src/budy/controllers/api/base.py +++ b/src/budy/controllers/api/base.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,18 +34,13 @@ from . import root -class BaseAPIController(root.RootAPIController): - @appier.route("/api/login", ("GET", "POST"), json = True) +class BaseAPIController(root.RootAPIController): + @appier.route("/api/login", ("GET", "POST"), json=True) def login(self): username = self.field("username") password = self.field("password") account = budy.BudyAccount.login(username, password) account._set_account() sid = self.session.sid - return dict( - sid = sid, - session_id = sid, - username = username, - tokens = account.tokens() - ) + return dict(sid=sid, session_id=sid, username=username, tokens=account.tokens()) diff --git a/src/budy/controllers/api/brand.py b/src/budy/controllers/api/brand.py index 4593f084..861552f8 100644 --- a/src/budy/controllers/api/brand.py +++ b/src/budy/controllers/api/brand.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,24 +34,20 @@ from . import root -class BrandAPIController(root.RootAPIController): - @appier.route("/api/brands", "GET", json = True) +class BrandAPIController(root.RootAPIController): + @appier.route("/api/brands", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - brands = budy.Brand.find_e( - eager = ("images",), - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + brands = budy.Brand.find_e(eager=("images",), map=True, **object) return brands - @appier.route("/api/brands/", "GET", json = True) + @appier.route("/api/brands/", "GET", json=True) def show(self, id): - brand = budy.Brand.get_e(id = id, map = True) + brand = budy.Brand.get_e(id=id, map=True) return brand - @appier.route("/api/brands/slug/", "GET", json = True) + @appier.route("/api/brands/slug/", "GET", json=True) def slug(self, slug): - brand = budy.Brand.get_e(slug = slug, map = True) + brand = budy.Brand.get_e(slug=slug, map=True) return brand diff --git a/src/budy/controllers/api/category.py b/src/budy/controllers/api/category.py index 6920954d..8b202146 100644 --- a/src/budy/controllers/api/category.py +++ b/src/budy/controllers/api/category.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,24 +34,20 @@ from . import root -class CategoryAPIController(root.RootAPIController): - @appier.route("/api/categories", "GET", json = True) +class CategoryAPIController(root.RootAPIController): + @appier.route("/api/categories", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - categories = budy.Category.find_e( - eager = ("images",), - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + categories = budy.Category.find_e(eager=("images",), map=True, **object) return categories - @appier.route("/api/categories/", "GET", json = True) + @appier.route("/api/categories/", "GET", json=True) def show(self, id): - category = budy.Category.get_e(id = id, map = True) + category = budy.Category.get_e(id=id, map=True) return category - @appier.route("/api/categories/slug/", "GET", json = True) + @appier.route("/api/categories/slug/", "GET", json=True) def slug(self, slug): - category = budy.Category.get_e(slug = slug, map = True) + category = budy.Category.get_e(slug=slug, map=True) return category diff --git a/src/budy/controllers/api/collection.py b/src/budy/controllers/api/collection.py index 6f1e89a4..f24d5b2e 100644 --- a/src/budy/controllers/api/collection.py +++ b/src/budy/controllers/api/collection.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "Tiago Silva " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,24 +34,20 @@ from . import root -class CollectionAPIController(root.RootAPIController): - @appier.route("/api/collections", "GET", json = True) +class CollectionAPIController(root.RootAPIController): + @appier.route("/api/collections", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - collections = budy.Collection.find_e( - eager = ("images",), - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + collections = budy.Collection.find_e(eager=("images",), map=True, **object) return collections - @appier.route("/api/collections/", "GET", json = True) + @appier.route("/api/collections/", "GET", json=True) def show(self, id): - collection = budy.Collection.get_e(id = id, map = True) + collection = budy.Collection.get_e(id=id, map=True) return collection - @appier.route("/api/collections/slug/", "GET", json = True) + @appier.route("/api/collections/slug/", "GET", json=True) def slug(self, slug): - collection = budy.Collection.get_e(slug = slug, map = True) + collection = budy.Collection.get_e(slug=slug, map=True) return collection diff --git a/src/budy/controllers/api/color.py b/src/budy/controllers/api/color.py index 3bb5bf62..b1f00511 100644 --- a/src/budy/controllers/api/color.py +++ b/src/budy/controllers/api/color.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,24 +34,20 @@ from . import root -class ColorAPIController(root.RootAPIController): - @appier.route("/api/colors", "GET", json = True) +class ColorAPIController(root.RootAPIController): + @appier.route("/api/colors", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - colors = budy.Color.find_e( - eager = ("images",), - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + colors = budy.Color.find_e(eager=("images",), map=True, **object) return colors - @appier.route("/api/colors/", "GET", json = True) + @appier.route("/api/colors/", "GET", json=True) def show(self, id): - color = budy.Color.get_e(id = id, map = True) + color = budy.Color.get_e(id=id, map=True) return color - @appier.route("/api/colors/slug/", "GET", json = True) + @appier.route("/api/colors/slug/", "GET", json=True) def slug(self, slug): - color = budy.Color.get_e(slug = slug, map = True) + color = budy.Color.get_e(slug=slug, map=True) return color diff --git a/src/budy/controllers/api/country.py b/src/budy/controllers/api/country.py index 68f81a21..1ecc6c1f 100644 --- a/src/budy/controllers/api/country.py +++ b/src/budy/controllers/api/country.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,44 +34,30 @@ from . import root -class CountryAPIController(root.RootAPIController): - @appier.route("/api/countries", "GET", json = True) +class CountryAPIController(root.RootAPIController): + @appier.route("/api/countries", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - countries = budy.Country.find( - find_i = True, - find_t = "right", - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + countries = budy.Country.find(find_i=True, find_t="right", map=True, **object) return countries @appier.route("/api/countries/simple.csv", "GET") - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def simple_csv(self): - object = appier.get_object( - alias = True, - find = True, - limit = 0 - ) + object = appier.get_object(alias=True, find=True, limit=0) countries = budy.Country.find(**object) - countries_s = [( - "name", - "country_code", - "currency_code", - "locale" - )] + countries_s = [("name", "country_code", "currency_code", "locale")] for country in countries: country_s = ( country.name, country.country_code, country.currency_code, - country.locale + country.locale, ) countries_s.append(country_s) - result = appier.serialize_csv(countries_s, delimiter = ",") + result = appier.serialize_csv(countries_s, delimiter=",") self.content_type("text/csv") return result diff --git a/src/budy/controllers/api/currency.py b/src/budy/controllers/api/currency.py index 6c718bd6..19134cbc 100644 --- a/src/budy/controllers/api/currency.py +++ b/src/budy/controllers/api/currency.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,27 +34,18 @@ from . import root -class CurrencyAPIController(root.RootAPIController): - @appier.route("/api/currencies", "GET", json = True) +class CurrencyAPIController(root.RootAPIController): + @appier.route("/api/currencies", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - currencies = budy.Currency.find( - find_i = True, - find_t = "right", - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + currencies = budy.Currency.find(find_i=True, find_t="right", map=True, **object) return currencies @appier.route("/api/currencies/simple.csv", "GET") - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def simple_csv(self): - object = appier.get_object( - alias = True, - find = True, - limit = 0 - ) + object = appier.get_object(alias=True, find=True, limit=0) currencies = budy.Currency.find(**object) currencies_s = [("iso", "decimal_places")] @@ -62,6 +53,6 @@ def simple_csv(self): currency_s = (currency.iso, currency.decimal_places) currencies_s.append(currency_s) - result = appier.serialize_csv(currencies_s, delimiter = ",") + result = appier.serialize_csv(currencies_s, delimiter=",") self.content_type("text/csv") return result diff --git a/src/budy/controllers/api/easypay.py b/src/budy/controllers/api/easypay.py index 3eefd774..2c4ff5d3 100644 --- a/src/budy/controllers/api/easypay.py +++ b/src/budy/controllers/api/easypay.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,9 +34,9 @@ from . import root -class EasypayAPIController(root.RootAPIController): - @appier.route("/api/easypay/notification", "GET", json = True) +class EasypayAPIController(root.RootAPIController): + @appier.route("/api/easypay/notification", "GET", json=True) def notification(self): cin = self.field("ep_cin") username = self.field("ep_user") @@ -47,21 +47,21 @@ def notification(self): return result @appier.route("/api/easypay/cancel", ("GET", "POST")) - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def cancel(self): - identifier = self.field("identifier", mandatory = True) + identifier = self.field("identifier", mandatory=True) api = budy.Order._get_api_easypay() return api.cancel_mb(identifier) @appier.route("/api/easypay/delete", ("GET", "POST")) - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def delete(self): - identifier = self.field("identifier", mandatory = True) + identifier = self.field("identifier", mandatory=True) api = budy.Order._get_api_easypay() return api.del_reference(identifier) @appier.route("/api/easypay/diagnostics", "GET") - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def diagnostics(self): api = budy.Order._get_api_easypay() return api.diagnostics() diff --git a/src/budy/controllers/api/media.py b/src/budy/controllers/api/media.py index 24c985b0..cb82b098 100644 --- a/src/budy/controllers/api/media.py +++ b/src/budy/controllers/api/media.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -36,65 +36,53 @@ from . import root -class MediaAPIController(root.RootAPIController): - @appier.route("/api/media", "GET", json = True) +class MediaAPIController(root.RootAPIController): + @appier.route("/api/media", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - media = budy.Media.find(map = True, **object) + object = appier.get_object(alias=True, find=True) + media = budy.Media.find(map=True, **object) return media - @appier.route("/api/media//data", "GET", json = True) + @appier.route("/api/media//data", "GET", json=True) def data(self, id): - media = budy.Media.get( - id = id, - fields = ("file",), - rules = False - ) + media = budy.Media.get(id=id, fields=("file",), rules=False) file = media.file - if not file: raise appier.NotFoundError( - message = "File not found for media '%d'" % id - ) + if not file: + raise appier.NotFoundError(message="File not found for media '%d'" % id) return self.send_file( file.data, - name = "%d" % id, - content_type = file.mime, - etag = file.etag, - cache = True + name="%d" % id, + content_type=file.mime, + etag=file.etag, + cache=True, ) - @appier.route("/api/media//data.", "GET", json = True) + @appier.route("/api/media//data.", "GET", json=True) def data_format(self, id, format): background = self.field("background", None) - quality = self.field("quality", 90, cast = int) - media = budy.Media.get( - id = id, - fields = ("file",), - rules = False - ) + quality = self.field("quality", 90, cast=int) + media = budy.Media.get(id=id, fields=("file",), rules=False) file = media.file - if not file: raise appier.NotFoundError( - message = "File not found for media '%d'" % id - ) + if not file: + raise appier.NotFoundError(message="File not found for media '%d'" % id) extension = mimetypes.guess_extension(file.mime or "") mime, _encoding = mimetypes.guess_type("data." + format) if extension and extension[1:] == format: return self.send_file( file.data, - name = "%d.%s" % (id, format), - content_type = file.mime, - etag = file.etag, - cache = True + name="%d.%s" % (id, format), + content_type=file.mime, + etag=file.etag, + cache=True, ) buffer = media.convert_image( - format, - background = background, - **dict(quality = quality) + format, background=background, **dict(quality=quality) ) return self.send_file( buffer.getvalue(), - name = "%d.%s" % (id, format), - content_type = mime, - etag = file.etag, - cache = True + name="%d.%s" % (id, format), + content_type=mime, + etag=file.etag, + cache=True, ) diff --git a/src/budy/controllers/api/order.py b/src/budy/controllers/api/order.py index 053d3c3e..7441cebb 100644 --- a/src/budy/controllers/api/order.py +++ b/src/budy/controllers/api/order.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,67 +34,69 @@ from . import root -class OrderAPIController(root.RootAPIController): - @appier.route("/api/orders", "GET", json = True) - @appier.ensure(token = "admin") +class OrderAPIController(root.RootAPIController): + @appier.route("/api/orders", "GET", json=True) + @appier.ensure(token="admin") def list(self): - object = appier.get_object(alias = True, find = True, sort = [("id", -1)]) - orders = budy.Order.find(eager_l = True, map = True, **object) + object = appier.get_object(alias=True, find=True, sort=[("id", -1)]) + orders = budy.Order.find(eager_l=True, map=True, **object) return orders @appier.route("/api/orders/complex.csv", "GET") - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def complex_csv(self): - start = self.field("start", cast = int) - end = self.field("end", cast = int) - paid = self.field("paid", True, cast = bool) - object = appier.get_object( - alias = True, - find = True, - limit = 0, - sort = [("id", -1)] - ) + start = self.field("start", cast=int) + end = self.field("end", cast=int) + paid = self.field("paid", True, cast=bool) + object = appier.get_object(alias=True, find=True, limit=0, sort=[("id", -1)]) id = object.get("id", {}) - if start: id["$gte"] = start - if end: id["$lte"] = end - if start or end: object["id"] = id - if paid: object["paid"] = True + if start: + id["$gte"] = start + if end: + id["$lte"] = end + if start or end: + object["id"] = id + if paid: + object["paid"] = True orders = budy.Order.find(**object) - orders_s = [( - "id", - "reference", - "date", - "status", - "email", - "account", - "store", - "product", - "gender", - "size", - "quantity", - "total", - "taxes", - "currency", - "first_name", - "last_name", - "billing_address", - "billing_city", - "billing_state", - "billing_postal_code", - "billing_country", - "billing_phone", - "shipping_address", - "shipping_city", - "shipping_state", - "shipping_postal_code", - "shipping_country", - "shipping_phone", - "shipping_cost" - )] + orders_s = [ + ( + "id", + "reference", + "date", + "status", + "email", + "account", + "store", + "product", + "gender", + "size", + "quantity", + "total", + "taxes", + "currency", + "first_name", + "last_name", + "billing_address", + "billing_city", + "billing_state", + "billing_postal_code", + "billing_country", + "billing_phone", + "shipping_address", + "shipping_city", + "shipping_state", + "shipping_postal_code", + "shipping_country", + "shipping_phone", + "shipping_cost", + ) + ] for order in orders: for line in order.lines: - if not line.product: continue + if not line.product: + continue account = order.account shipping_address = order.shipping_address billing_address = order.billing_address @@ -128,75 +130,79 @@ def complex_csv(self): shipping_address and shipping_address.postal_code, shipping_address and shipping_address.country, shipping_address and shipping_address.phone_number, - shipping_cost + shipping_cost, ) orders_s.append(order_s) - result = appier.serialize_csv(orders_s, delimiter = ",") + result = appier.serialize_csv(orders_s, delimiter=",") self.content_type("text/csv") return result @appier.route("/api/orders/ctt.csv", "GET") - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def ctt_csv(self): - start = self.field("start", cast = int) - end = self.field("end", cast = int) - paid = self.field("paid", True, cast = bool) - sms = self.field("sms", False, cast = bool) - quantity = self.field("quantity", 1, cast = int) - weight = self.field("weight", 100, cast = int) - object = appier.get_object( - alias = True, - find = True, - limit = 0, - sort = [("id", -1)] - ) + start = self.field("start", cast=int) + end = self.field("end", cast=int) + paid = self.field("paid", True, cast=bool) + sms = self.field("sms", False, cast=bool) + quantity = self.field("quantity", 1, cast=int) + weight = self.field("weight", 100, cast=int) + object = appier.get_object(alias=True, find=True, limit=0, sort=[("id", -1)]) id = object.get("id", {}) - if start: id["$gte"] = start - if end: id["$lte"] = end - if start or end: object["id"] = id - if paid: object["paid"] = True + if start: + id["$gte"] = start + if end: + id["$lte"] = end + if start or end: + object["id"] = id + if paid: + object["paid"] = True orders = self.admin_part._find_view(budy.Order, **object) orders_s = [] for order in orders: shipping_address = order.shipping_address postal_code = shipping_address.postal_code or "" - if not "-" in postal_code: postal_code += "-" + if not "-" in postal_code: + postal_code += "-" weight_s = "%.2f" % (order.quantity * weight) weight_s = weight_s.replace(".", ",") line = dict( - reference = order.reference, - quantity = int(order.quantity) if quantity == None else quantity, - weight = weight_s, - price = "0ue", - destiny = shipping_address.full_name[:60], - title = order.account.title[:20], - name = shipping_address.full_name[:60], - address = shipping_address.address[:60], - town = shipping_address.city[:50], - zip_code_4 = postal_code.split("-", 1)[0][:4], - zip_code_3 = postal_code.split("-", 1)[1][:3], - not_applicable_1 = "", - observations = "", - back = 0, - document_code = "", - phone_number = (shipping_address.phone_number or "").replace("+", "00")[:15], - saturday = 0, - email = (order.email or "")[:200], - country = order.country, - fragile = 0, - not_applicable_2 = "", - document_collection = "", - code_email = "", - mobile_phone = (shipping_address.phone_number or "").replace("+", "00")[:15], - second_delivery = 0, - delivery_date = "", - return_signed_document = 0, - expeditor_instructions = 0, - sms = 1 if sms else 0, - not_applicable_3 = "", - printer = "", - ticket_machine = "", - at_code = "" + reference=order.reference, + quantity=int(order.quantity) if quantity == None else quantity, + weight=weight_s, + price="0ue", + destiny=shipping_address.full_name[:60], + title=order.account.title[:20], + name=shipping_address.full_name[:60], + address=shipping_address.address[:60], + town=shipping_address.city[:50], + zip_code_4=postal_code.split("-", 1)[0][:4], + zip_code_3=postal_code.split("-", 1)[1][:3], + not_applicable_1="", + observations="", + back=0, + document_code="", + phone_number=(shipping_address.phone_number or "").replace("+", "00")[ + :15 + ], + saturday=0, + email=(order.email or "")[:200], + country=order.country, + fragile=0, + not_applicable_2="", + document_collection="", + code_email="", + mobile_phone=(shipping_address.phone_number or "").replace("+", "00")[ + :15 + ], + second_delivery=0, + delivery_date="", + return_signed_document=0, + expeditor_instructions=0, + sms=1 if sms else 0, + not_applicable_3="", + printer="", + ticket_machine="", + at_code="", ) order_s = ( line["reference"], @@ -231,268 +237,252 @@ def ctt_csv(self): line["not_applicable_3"], line["printer"], line["ticket_machine"], - line["at_code"] + line["at_code"], ) orders_s.append(order_s) result = appier.serialize_csv( - orders_s, - encoding = "Cp1252", - errors = "ignore", - delimiter = "+" - ) - result = appier.legacy.bytes( - result, - encoding = "Cp1252", - errors = "ignore" + orders_s, encoding="Cp1252", errors="ignore", delimiter="+" ) + result = appier.legacy.bytes(result, encoding="Cp1252", errors="ignore") self.content_type("text/csv") return result - @appier.route("/api/orders/", "GET", json = True) + @appier.route("/api/orders/", "GET", json=True) def show(self, key): - order = budy.Order.get(key = key) + order = budy.Order.get(key=key) order.refresh_s( - currency = order.payment_currency or self.currency, - country = order.shipping_country or self.country + currency=order.payment_currency or self.currency, + country=order.shipping_country or self.country, ) order = order.reload( - eager = ( - "lines.product.images", - "lines.product.brand" - ), - map = True + eager=("lines.product.images", "lines.product.brand"), map=True ) return order - @appier.route("/api/orders//store", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//store", "PUT", json=True) + @appier.ensure(token="user") def set_store(self, key): data = appier.request_json() store_id = data["store_id"] - store = budy.Store.get(id = store_id) - order = budy.Order.get(key = key, rules = False) + store = budy.Store.get(id=store_id) + order = budy.Order.get(key=key, rules=False) order.store = store order.save() - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//shipping_address", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//shipping_address", "PUT", json=True) + @appier.ensure(token="user") def set_shipping_address(self, key): data = appier.request_json() unset_store = data.get("unset_store", True) address = budy.Address.new() address.save() - order = budy.Order.get(key = key, rules = False) + order = budy.Order.get(key=key, rules=False) order.shipping_address = address - if unset_store: order.store = None + if unset_store: + order.store = None order.save() - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//billing_address", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//billing_address", "PUT", json=True) + @appier.ensure(token="user") def set_billing_address(self, key): data = appier.request_json() unset_store = data.get("unset_store", False) address = budy.Address.new() address.save() - order = budy.Order.get(key = key, rules = False) + order = budy.Order.get(key=key, rules=False) order.billing_address = address - if unset_store: order.store = None + if unset_store: + order.store = None order.save() - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//store_shipping", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//store_shipping", "PUT", json=True) + @appier.ensure(token="user") def set_store_shipping(self, key): - order = budy.Order.get(key = key, rules = False) + order = budy.Order.get(key=key, rules=False) order.verify_store() store = order.store address = store.address.clone() address.save() order.shipping_address = address order.save() - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//store_billing", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//store_billing", "PUT", json=True) + @appier.ensure(token="user") def set_store_billing(self, key): - order = budy.Order.get(key = key, rules = False) + order = budy.Order.get(key=key, rules=False) order.verify_store() store = order.store address = store.address.clone() address.save() order.billing_address = address order.save() - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//ip_address", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//ip_address", "PUT", json=True) + @appier.ensure(token="user") def set_ip_address(self, key): data = appier.request_json() ip_address = data["ip_address"] - order = budy.Order.get(key = key, rules = False) + order = budy.Order.get(key=key, rules=False) order.set_ip_address_s(ip_address) - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//email", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//email", "PUT", json=True) + @appier.ensure(token="user") def set_email(self, key): data = appier.request_json() email = data["email"] - order = budy.Order.get(key = key, rules = False) + order = budy.Order.get(key=key, rules=False) order.email = email order.save() - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//gift_wrap", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//gift_wrap", "PUT", json=True) + @appier.ensure(token="user") def set_gift_wrap(self, key): data = appier.request_json() gift_wrap = data["gift_wrap"] - order = budy.Order.get(key = key, rules = False) + order = budy.Order.get(key=key, rules=False) order.gift_wrap = gift_wrap order.save() - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//referral", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//referral", "PUT", json=True) + @appier.ensure(token="user") def set_referral(self, key): data = appier.request_json() referral_name = data["name"] - order = budy.Order.get(key = key, rules = False) - referral = budy.Referral.get(name = referral_name) + order = budy.Order.get(key=key, rules=False) + referral = budy.Referral.get(name=referral_name) order.set_referral_s(referral) - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//voucher", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//voucher", "PUT", json=True) + @appier.ensure(token="user") def set_voucher(self, key): - strict = self.field("strict", False, cast = bool) - empty_vouchers = self.field("empty_vouchers", True, cast = bool) + strict = self.field("strict", False, cast=bool) + empty_vouchers = self.field("empty_vouchers", True, cast=bool) data = appier.request_json() voucher_key = data["key"] - order = budy.Order.get(key = key, rules = False) - if empty_vouchers: order.empty_vouchers_s() - voucher = budy.Voucher.get( - key = voucher_key, - raise_e = strict - ) - if not voucher: return + order = budy.Order.get(key=key, rules=False) + if empty_vouchers: + order.empty_vouchers_s() + voucher = budy.Voucher.get(key=voucher_key, raise_e=strict) + if not voucher: + return order.set_voucher_s(voucher) - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//meta", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//meta", "PUT", json=True) + @appier.ensure(token="user") def set_meta(self, key): - name = self.field("name", mandatory = True, not_empty = True) - value = self.field("value", mandatory = True) - order = budy.Order.get(key = key, rules = False) + name = self.field("name", mandatory=True, not_empty=True) + value = self.field("value", mandatory=True) + order = budy.Order.get(key=key, rules=False) order.set_meta_s(name, value) - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//account", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//account", "PUT", json=True) + @appier.ensure(token="user") def set_account(self, key): - order = budy.Order.get(key = key, rules = False) + order = budy.Order.get(key=key, rules=False) account = budy.BudyAccount.from_session() order.set_account_s(account) - order = order.reload(map = True) + order = order.reload(map=True) return order - @appier.route("/api/orders//wait_payment", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//wait_payment", "PUT", json=True) + @appier.ensure(token="user") def wait_payment(self, key): - empty_bag = self.field("empty_bag", True, cast = bool) - order = budy.Order.get(key = key, rules = False) - result = order.wait_payment_s(notify = True) + empty_bag = self.field("empty_bag", True, cast=bool) + order = budy.Order.get(key=key, rules=False) + result = order.wait_payment_s(notify=True) bag = budy.Bag.from_session() - if empty_bag and bag: bag.empty_s() - order = order.reload(map = True) + if empty_bag and bag: + bag.empty_s() + order = order.reload(map=True) redirect_url = result if appier.legacy.is_string(result) else None - return dict( - redirect_url = redirect_url, - order = order - ) + return dict(redirect_url=redirect_url, order=order) - @appier.route("/api/orders//pay", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//pay", "PUT", json=True) + @appier.ensure(token="user") def pay(self, key): data = appier.request_json() - empty_bag = self.field("empty_bag", True, cast = bool) - order = budy.Order.get(key = key, rules = False) - result = order.pay_s(payment_data = data, notify = True) + empty_bag = self.field("empty_bag", True, cast=bool) + order = budy.Order.get(key=key, rules=False) + result = order.pay_s(payment_data=data, notify=True) bag = budy.Bag.from_session() - if empty_bag and bag: bag.empty_s() - order = order.reload(map = True) + if empty_bag and bag: + bag.empty_s() + order = order.reload(map=True) redirect_url = result if appier.legacy.is_string(result) else None - return dict( - redirect_url = redirect_url, - order = order - ) + return dict(redirect_url=redirect_url, order=order) - @appier.route("/api/orders//end_pay", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//end_pay", "PUT", json=True) + @appier.ensure(token="user") def end_pay(self, key): data = appier.request_json() - empty_bag = self.field("empty_bag", True, cast = bool) - order = budy.Order.get(key = key, rules = False) - result = order.end_pay_s(payment_data = data, strict = True, notify = True) + empty_bag = self.field("empty_bag", True, cast=bool) + order = budy.Order.get(key=key, rules=False) + result = order.end_pay_s(payment_data=data, strict=True, notify=True) bag = budy.Bag.from_session() - if empty_bag and bag: bag.empty_s() - order = order.reload(map = True) + if empty_bag and bag: + bag.empty_s() + order = order.reload(map=True) redirect_url = result if appier.legacy.is_string(result) else None - return dict( - redirect_url = redirect_url, - order = order - ) + return dict(redirect_url=redirect_url, order=order) - @appier.route("/api/orders//cancel", "PUT", json = True) - @appier.ensure(token = "user") + @appier.route("/api/orders//cancel", "PUT", json=True) + @appier.ensure(token="user") def cancel(self, key): data = appier.request_json() - order = budy.Order.get(key = key, rules = False) - result = order.cancel_s(data, notify = True) - order = order.reload(map = True) + order = budy.Order.get(key=key, rules=False) + result = order.cancel_s(data, notify=True) + order = order.reload(map=True) redirect_url = result if appier.legacy.is_string(result) else None - return dict( - redirect_url = redirect_url, - order = order - ) + return dict(redirect_url=redirect_url, order=order) @appier.route("/api/orders//lines.csv", "GET") - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def lines_csv(self, key): - order = budy.Order.get(key = key) - lines_s = [( - "id", - "product", - "product_id", - "quantity", - "size", - "scale", - "gender", - "line_total", - "currency", - "order_id", - "order_reference", - "order_total", - "order_status", - "account", - "date", - )] + order = budy.Order.get(key=key) + lines_s = [ + ( + "id", + "product", + "product_id", + "quantity", + "size", + "scale", + "gender", + "line_total", + "currency", + "order_id", + "order_reference", + "order_total", + "order_status", + "account", + "date", + ) + ] for line in order.lines: - if not line.product: continue + if not line.product: + continue line_s = ( line.id, line.product.id, @@ -512,6 +502,6 @@ def lines_csv(self, key): ) lines_s.append(line_s) - result = appier.serialize_csv(lines_s, delimiter = ",") + result = appier.serialize_csv(lines_s, delimiter=",") self.content_type("text/csv") return result diff --git a/src/budy/controllers/api/product.py b/src/budy/controllers/api/product.py index 94778efd..655cb4a7 100644 --- a/src/budy/controllers/api/product.py +++ b/src/budy/controllers/api/product.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,85 +34,69 @@ from . import root -class ProductAPIController(root.RootAPIController): - @appier.route("/api/products", "GET", json = True) +class ProductAPIController(root.RootAPIController): + @appier.route("/api/products", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) + object = appier.get_object(alias=True, find=True) products = budy.Product.find_e( - find_t = "right", - eager = ("images", "brand"), - map = True, - **object + find_t="right", eager=("images", "brand"), map=True, **object ) return products - @appier.route("/api/products/", "GET", json = True) + @appier.route("/api/products/", "GET", json=True) def show(self, id): product = budy.Product.get_e( - id = id, - eager = ("images", "brand", "measurements"), - map = True + id=id, eager=("images", "brand", "measurements"), map=True ) return product - @appier.route("/api/products/search", "GET", json = True) + @appier.route("/api/products/search", "GET", json=True) def search(self): - object = appier.get_object(alias = True, find = True) + object = appier.get_object(alias=True, find=True) products = budy.Product.find_se( - find_t = "both", - find_n = "tokens", - eager = ("images", "brand"), - map = True, + find_t="both", + find_n="tokens", + eager=("images", "brand"), + map=True, **object ) return products - @appier.route("/api/products//related", "GET", json = True) + @appier.route("/api/products//related", "GET", json=True) def related(self, id): - limit = self.field("limit", 10, cast = int) - available = self.field("available", True, cast = bool) - product = budy.Product.get_e(id = id) - products = product.related( - limit = limit, - available = available, - enabled = True - ) + limit = self.field("limit", 10, cast=int) + available = self.field("available", True, cast=bool) + product = budy.Product.get_e(id=id) + products = product.related(limit=limit, available=available, enabled=True) return products - @appier.route("/api/products//share", "GET", json = True) + @appier.route("/api/products//share", "GET", json=True) def share(self, id): - sender = self.field("sender", mandatory = True, not_empty = True) - email = self.field("email", mandatory = True, not_empty = True) - product = budy.Product.get_e(id = id) - share = product.share(sender = sender, email = email) + sender = self.field("sender", mandatory=True, not_empty=True) + email = self.field("email", mandatory=True, not_empty=True) + product = budy.Product.get_e(id=id) + share = product.share(sender=sender, email=email) return share - @appier.route("/api/products//quote", "GET", json = True) + @appier.route("/api/products//quote", "GET", json=True) def quote(self, id): - requester = self.field("requester", mandatory = True, not_empty = True) - email = self.field("email", mandatory = True, not_empty = True) + requester = self.field("requester", mandatory=True, not_empty=True) + email = self.field("email", mandatory=True, not_empty=True) phone = self.field("phone") observations = self.field("observations") - product = budy.Product.get_e(id = id) + product = budy.Product.get_e(id=id) quote = product.quote( - requester = requester, - email = email, - phone = phone, - observations = observations + requester=requester, email=email, phone=phone, observations=observations ) return quote @appier.route("/api/products/simple.csv", "GET") - @appier.ensure(token = "admin") + @appier.ensure(token="admin") def simple_csv(self): - object = appier.get_object( - alias = True, - find = True, - limit = 0 - ) + object = appier.get_object(alias=True, find=True, limit=0) products = budy.Product.find_e( - eager = ( + eager=( "colors", "categories", "collections", @@ -120,34 +104,36 @@ def simple_csv(self): "brand", "season", "measurements", - "compositions" + "compositions", ), **object ) - products_s = [( - "description", - "short_description", - "product_id", - "gender", - "price", - "order", - "tag", - "tag_description", - "farfetch_url", - "farfetch_male_url", - "farfetch_female_url", - "colors", - "categories", - "collections", - "variants", - "brand", - "season", - "measurements", - "compositions", - "price_provider", - "price_url" - )] + products_s = [ + ( + "description", + "short_description", + "product_id", + "gender", + "price", + "order", + "tag", + "tag_description", + "farfetch_url", + "farfetch_male_url", + "farfetch_female_url", + "colors", + "categories", + "collections", + "variants", + "brand", + "season", + "measurements", + "compositions", + "price_provider", + "price_url", + ) + ] for product in products: product_s = ( product.description, @@ -170,10 +156,10 @@ def simple_csv(self): ";".join([measurement.name for measurement in product.measurements]), ";".join([composition.name for composition in product.compositions]), product.price_provider, - product.price_url + product.price_url, ) products_s.append(product_s) - result = appier.serialize_csv(products_s, delimiter = ",") + result = appier.serialize_csv(products_s, delimiter=",") self.content_type("text/csv") return result diff --git a/src/budy/controllers/api/referral.py b/src/budy/controllers/api/referral.py index 6eefdf3f..013169f1 100644 --- a/src/budy/controllers/api/referral.py +++ b/src/budy/controllers/api/referral.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,10 +34,10 @@ from . import root -class ReferralAPIController(root.RootAPIController): - @appier.route("/api/referrals", "GET", json = True) +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) + object = appier.get_object(alias=True, find=True) + referrals = budy.Referral.find(map=True, **object) return referrals diff --git a/src/budy/controllers/api/root.py b/src/budy/controllers/api/root.py index 36f4bf13..a143d770 100644 --- a/src/budy/controllers/api/root.py +++ b/src/budy/controllers/api/root.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,8 +30,8 @@ import appier -class RootAPIController(appier.Controller): +class RootAPIController(appier.Controller): @property def country(self): return self.request.get_header("X-Budy-Country", None) diff --git a/src/budy/controllers/api/season.py b/src/budy/controllers/api/season.py index cf38f2e5..7e9529a4 100644 --- a/src/budy/controllers/api/season.py +++ b/src/budy/controllers/api/season.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,24 +34,20 @@ from . import root -class SeasonAPIController(root.RootAPIController): - @appier.route("/api/seasons", "GET", json = True) +class SeasonAPIController(root.RootAPIController): + @appier.route("/api/seasons", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - seasons = budy.Season.find_e( - eager = ("images",), - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + seasons = budy.Season.find_e(eager=("images",), map=True, **object) return seasons - @appier.route("/api/seasons/", "GET", json = True) + @appier.route("/api/seasons/", "GET", json=True) def show(self, id): - season = budy.Season.get_e(id = id, map = True) + season = budy.Season.get_e(id=id, map=True) return season - @appier.route("/api/seasons/slug/", "GET", json = True) + @appier.route("/api/seasons/slug/", "GET", json=True) def slug(self, slug): - season = budy.Season.get_e(slug = slug, map = True) + season = budy.Season.get_e(slug=slug, map=True) return season diff --git a/src/budy/controllers/api/section.py b/src/budy/controllers/api/section.py index 19bbf32b..de4dcfdc 100644 --- a/src/budy/controllers/api/section.py +++ b/src/budy/controllers/api/section.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,24 +34,20 @@ from . import root -class SectionAPIController(root.RootAPIController): - @appier.route("/api/sections", "GET", json = True) +class SectionAPIController(root.RootAPIController): + @appier.route("/api/sections", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - sections = budy.Section.find_e( - eager = ("images",), - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + sections = budy.Section.find_e(eager=("images",), map=True, **object) return sections - @appier.route("/api/sections/", "GET", json = True) + @appier.route("/api/sections/", "GET", json=True) def show(self, id): - section = budy.Section.get_e(id = id, map = True) + section = budy.Section.get_e(id=id, map=True) return section - @appier.route("/api/sections/slug/", "GET", json = True) + @appier.route("/api/sections/slug/", "GET", json=True) def slug(self, slug): - section = budy.Section.get_e(slug = slug, map = True) + section = budy.Section.get_e(slug=slug, map=True) return section diff --git a/src/budy/controllers/api/seeplus.py b/src/budy/controllers/api/seeplus.py index 2d9d2f80..a731fd32 100644 --- a/src/budy/controllers/api/seeplus.py +++ b/src/budy/controllers/api/seeplus.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -36,19 +36,16 @@ from . import root -class SeeplusAPIController(root.RootAPIController): - @appier.route("/api/seeplus/update", "POST", json = True) +class SeeplusAPIController(root.RootAPIController): + @appier.route("/api/seeplus/update", "POST", json=True) def update(self): key = self.field("token", None) key = self.field("key", key) key = self.request.get_header("X-Seeplus-Key", key) _key = appier.conf("SEEPLUS_KEY", None) if _key and not _key == key: - raise appier.SecurityError( - message = "Mismatch in Seeplus key", - code = 401 - ) + raise appier.SecurityError(message="Mismatch in Seeplus key", code=401) object = appier.get_object() event = object.get("event", "OrderManagement.StatusChanged") data = object.get("data", {}) @@ -56,31 +53,26 @@ def update(self): if event == "OrderManagement.StatusChanged": self._status_change_s(data) result = "Handled" - return dict(result = result) + return dict(result=result) def _status_change_s(self, data): reference = data["code"] status = data["status"] - order = budy.Order.get(reference = reference) + order = budy.Order.get(reference=reference) seeplus_status = order.meta.get("seeplus_status", None) seeplus_timestamp = order.meta.get("seeplus_timestamp", None) seeplus_updates = order.meta.get("seeplus_updates", []) - if not seeplus_timestamp: raise appier.OperationalError( - message = "Order not imported in Seeplus" - ) - if status == seeplus_status: raise appier.OperationalError( - message = "Order already in status '%s'" % status - ) - status_timestamp = time.time() - seeplus_updates.append( - dict( - status = status, - timestamp = status_timestamp + if not seeplus_timestamp: + raise appier.OperationalError(message="Order not imported in Seeplus") + if status == seeplus_status: + raise appier.OperationalError( + message="Order already in status '%s'" % status ) - ) + status_timestamp = time.time() + seeplus_updates.append(dict(status=status, timestamp=status_timestamp)) order.meta.update( - seeplus_status = status, - seeplus_update = status_timestamp, - seeplus_updates = seeplus_updates + seeplus_status=status, + seeplus_update=status_timestamp, + seeplus_updates=seeplus_updates, ) order.save() diff --git a/src/budy/controllers/api/store.py b/src/budy/controllers/api/store.py index 10f24c03..fc950cfd 100644 --- a/src/budy/controllers/api/store.py +++ b/src/budy/controllers/api/store.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,19 +34,15 @@ from . import root -class StoreAPIController(root.RootAPIController): - @appier.route("/api/stores", "GET", json = True) +class StoreAPIController(root.RootAPIController): + @appier.route("/api/stores", "GET", json=True) def list(self): - object = appier.get_object(alias = True, find = True) - stores = budy.Store.find_e( - eager = ("address",), - map = True, - **object - ) + object = appier.get_object(alias=True, find=True) + stores = budy.Store.find_e(eager=("address",), map=True, **object) return stores - @appier.route("/api/colors/", "GET", json = True) + @appier.route("/api/colors/", "GET", json=True) def show(self, id): - store = budy.Color.get_e(id = id, map = True) + store = budy.Color.get_e(id=id, map=True) return store diff --git a/src/budy/controllers/api/subscription.py b/src/budy/controllers/api/subscription.py index 8029a66e..a66038ac 100644 --- a/src/budy/controllers/api/subscription.py +++ b/src/budy/controllers/api/subscription.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,9 +34,9 @@ from . import root -class SubscriptionAPIController(root.RootAPIController): - @appier.route("/api/subscriptions", "POST", json = True) +class SubscriptionAPIController(root.RootAPIController): + @appier.route("/api/subscriptions", "POST", json=True) def create(self): subscription = budy.Subscription.new() subscription.save() diff --git a/src/budy/controllers/api/voucher.py b/src/budy/controllers/api/voucher.py index f1ff73db..43a47a48 100644 --- a/src/budy/controllers/api/voucher.py +++ b/src/budy/controllers/api/voucher.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,11 +34,11 @@ from . import root -class VoucherAPIController(root.RootAPIController): - @appier.route("/api/vouchers", "GET", json = True) - @appier.ensure(token = "admin") +class VoucherAPIController(root.RootAPIController): + @appier.route("/api/vouchers", "GET", json=True) + @appier.ensure(token="admin") def list(self): - object = appier.get_object(alias = True, find = True, sort = [("id", -1)]) - vouchers = budy.Voucher.find(map = True, **object) + object = appier.get_object(alias=True, find=True, sort=[("id", -1)]) + vouchers = budy.Voucher.find(map=True, **object) return vouchers diff --git a/src/budy/controllers/web/__init__.py b/src/budy/controllers/web/__init__.py index b89adf13..bef4eb5f 100644 --- a/src/budy/controllers/web/__init__.py +++ b/src/budy/controllers/web/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -19,7 +19,7 @@ # You should have received a copy of the Apache License along with # Hive Budy. If not, see . -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" diff --git a/src/budy/controllers/web/_stripe.py b/src/budy/controllers/web/_stripe.py index c0135e75..a8066a9a 100644 --- a/src/budy/controllers/web/_stripe.py +++ b/src/budy/controllers/web/_stripe.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,12 +30,9 @@ import appier -class StripeController(appier.Controller): +class StripeController(appier.Controller): @appier.route("/stripe/redirect", "GET") def redirect(self): - redirect_url = self.field("redirect_url", mandatory = True) - return self.template( - "stripe/redirect.html.tpl", - redirect_url = redirect_url - ) + redirect_url = self.field("redirect_url", mandatory=True) + return self.template("stripe/redirect.html.tpl", redirect_url=redirect_url) diff --git a/src/budy/controllers/web/admin.py b/src/budy/controllers/web/admin.py index 92fe12a7..ff784269 100644 --- a/src/budy/controllers/web/admin.py +++ b/src/budy/controllers/web/admin.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,14 +30,11 @@ import appier -class AdminController(appier.Controller): +class AdminController(appier.Controller): @appier.route("/admin/force_scheduler", ("GET", "POST")) def force_scheduler(self): self.scheduler.awake() return self.redirect( - self.url_for( - "admin.operations", - message = "Forced scheduler to run" - ) + self.url_for("admin.operations", message="Forced scheduler to run") ) diff --git a/src/budy/controllers/web/base.py b/src/budy/controllers/web/base.py index 50206905..6eb313a6 100644 --- a/src/budy/controllers/web/base.py +++ b/src/budy/controllers/web/base.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,54 +32,40 @@ import budy -class BaseController(appier.Controller): +class BaseController(appier.Controller): @appier.route("/", "GET") def index(self): - return self.redirect( - self.url_for("admin.index") - ) + return self.redirect(self.url_for("admin.index")) @appier.route("/index_store", "GET") def index_store(self): - return self.redirect( - self.url_for("order.me") - ) + return self.redirect(self.url_for("order.me")) @appier.route("/signin", "GET") def signin(self): next = self.field("next") error = self.field("error") - return self.template( - "signin.html.tpl", - next = next, - error = error - ) + return self.template("signin.html.tpl", next=next, error=error) @appier.route("/signin", "POST") def login(self): username = self.field("username") password = self.field("password") next = self.field("next") - try: account = budy.BudyAccount.login(username, password) + try: + account = budy.BudyAccount.login(username, password) except appier.AppierException as error: return self.template( - "signin.html.tpl", - next = next, - username = username, - error = error.message + "signin.html.tpl", next=next, username=username, error=error.message ) account._set_account() - return self.redirect( - next or self.url_for(self.login_redirect) - ) + return self.redirect(next or self.url_for(self.login_redirect)) @appier.route("/signout", "GET") def signout(self): next = self.field("next") budy.BudyAccount._unset_account() - return self.redirect( - next or self.url_for(self.logout_redirect) - ) + return self.redirect(next or self.url_for(self.logout_redirect)) diff --git a/src/budy/controllers/web/order.py b/src/budy/controllers/web/order.py index 1105be16..776e03e6 100644 --- a/src/budy/controllers/web/order.py +++ b/src/budy/controllers/web/order.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,27 +32,22 @@ import budy -class OrderController(appier.Controller): +class OrderController(appier.Controller): @appier.route("/orders/me", "GET") @appier.route("/orders/me/", "GET") - @appier.ensure(token = "user") - def me(self, status = "waiting_payment"): + @appier.ensure(token="user") + def me(self, status="waiting_payment"): account = budy.BudyAccount.from_session() - orders = account.get_store_orders(status = status) - return self.template( - "order/me.html.tpl", - orders = orders - ) + orders = account.get_store_orders(status=status) + return self.template("order/me.html.tpl", orders=orders) @appier.route("/orders//mark_paid", "GET") - @appier.ensure(token = "user") + @appier.ensure(token="user") def mark_paid(self, key): - order = budy.Order.get(key = key) + order = budy.Order.get(key=key) account = budy.BudyAccount.from_session() order.verify_account(account) order.mark_paid_s() order.notify_s() - return self.redirect( - self.url_for("order.me") - ) + return self.redirect(self.url_for("order.me")) diff --git a/src/budy/main.py b/src/budy/main.py index 8e61bc8c..192843ff 100644 --- a/src/budy/main.py +++ b/src/budy/main.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -33,16 +33,11 @@ from budy import bots -class BudyApp(appier.WebApp): +class BudyApp(appier.WebApp): def __init__(self, *args, **kwargs): appier.WebApp.__init__( - self, - name = "budy", - parts = ( - appier_extras.AdminPart, - ), - *args, **kwargs + self, name="budy", parts=(appier_extras.AdminPart,), *args, **kwargs ) self.login_route = "base.signin" self.login_redirect = "base.index_store" @@ -55,28 +50,36 @@ def start(self): appier.WebApp.start(self) self.scheduler.start() self.admin_part.add_operation( - "force_scheduler", "admin.force_scheduler", - description = "Force scheduler", - message = "Are you really sure you want to force scheduler?", - note = "Forcing scheduler may consume computer resources", - level = 3 + "force_scheduler", + "admin.force_scheduler", + description="Force scheduler", + message="Are you really sure you want to force scheduler?", + note="Forcing scheduler may consume computer resources", + level=3, ) def stop(self): - try: import easypay - except ImportError: easypay = None - if easypay: easypay.ShelveAPI.cleanup() + try: + import easypay + except ImportError: + easypay = None + if easypay: + easypay.ShelveAPI.cleanup() appier.WebApp.stop(self) def get_omni_api(self): import omni - if self.omni_api: return self.omni_api + + if self.omni_api: + return self.omni_api self.omni_api = omni.API() return self.omni_api def get_seeplus_api(self): import seeplus - if self.seeplus_api: return self.seeplus_api + + if self.seeplus_api: + return self.seeplus_api self.seeplus_api = seeplus.API() return self.seeplus_api @@ -89,6 +92,7 @@ def _description(self): def _observations(self): return "Simple and easy to use E-commerce engine" + if __name__ == "__main__": app = BudyApp() app.serve() diff --git a/src/budy/models/__init__.py b/src/budy/models/__init__.py index 3db26829..12fa198c 100644 --- a/src/budy/models/__init__.py +++ b/src/budy/models/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -19,7 +19,7 @@ # You should have received a copy of the Apache License along with # Hive Budy. If not, see . -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" diff --git a/src/budy/models/account.py b/src/budy/models/account.py index bf37dc23..cdb1fc22 100644 --- a/src/budy/models/account.py +++ b/src/budy/models/account.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,61 +34,29 @@ from . import bag from . import order + class BudyAccount(appier_extras.admin.Account): + PREFIXES = appier_extras.admin.Account.PREFIXES + ["budy."] - PREFIXES = appier_extras.admin.Account.PREFIXES + [ - "budy." - ] + GENDER_S = dict(Male="Male", Female="Female") - GENDER_S = dict( - Male = "Male", - Female = "Female" - ) + first_name = appier.field(index=True) - first_name = appier.field( - index = True - ) + last_name = appier.field(index=True) - last_name = appier.field( - index = True - ) + gender = appier.field(meta="enum", enum=GENDER_S) - gender = appier.field( - meta = "enum", - enum = GENDER_S - ) + birth_date = appier.field(type=int, index=True, meta="date") - birth_date = appier.field( - type = int, - index = True, - meta = "date" - ) - - country = appier.field( - meta = "country" - ) + country = appier.field(meta="country") phone_number = appier.field() - receive_newsletters = appier.field( - type = bool, - initial = False - ) + receive_newsletters = appier.field(type=bool, initial=False) - store = appier.field( - type = appier.reference( - "Store", - name = "id" - ), - eager = True - ) + store = appier.field(type=appier.reference("Store", name="id"), eager=True) - addresses = appier.field( - type = appier.references( - "Address", - name = "id" - ) - ) + addresses = appier.field(type=appier.references("Address", name="id")) @classmethod def _build(cls, model, map): @@ -98,14 +66,18 @@ def _build(cls, model, map): last_name = model.get("last_name", None) full_name = cls._build_full_name(first_name, last_name) short_name = cls._build_short_name(first_name, last_name) - if id: model["bag_key"] = cls._get_bag_key(id) - if full_name: model["full_name"] = full_name - if short_name: model["short_name"] = short_name + if id: + model["bag_key"] = cls._get_bag_key(id) + if full_name: + model["full_name"] = full_name + if short_name: + model["short_name"] = short_name @classmethod def _get_bag_key(cls, id): - _bag = bag.Bag.get(account = id, raise_e = False) - if not _bag: return None + _bag = bag.Bag.get(account=id, raise_e=False) + if not _bag: + return None return _bag.key @classmethod @@ -115,43 +87,40 @@ def _build_full_name(cls, first_name, last_name): return first_name + (" " + last_name if last_name else "") @classmethod - def _build_short_name(cls, first_name, last_name, limit = 16): + def _build_short_name(cls, first_name, last_name, limit=16): first_name = first_name or "" last_name = last_name or "" first = first_name.split(" ")[0].strip() last = last_name.split(" ")[-1].strip() short_name = first + (" " + last if last else "") - if len(short_name) <= limit: return short_name + if len(short_name) <= limit: + return short_name last = last_name[0] + "." if last_name else "" return first + (" " + last if last else "") @classmethod @appier.operation( - name = "Import Social CSV", - parameters = ( - ("CSV File", "file", "file"), - ("Strict", "strict", bool, False) - ) + name="Import Social CSV", + parameters=(("CSV File", "file", "file"), ("Strict", "strict", bool, False)), ) def import_social_csv_s(cls, file, strict): - def callback(line): username, facebook_id, google_id = line - account = cls.get(username = username, raise_e = strict) - if not account: return - if facebook_id: account.facebook_id = facebook_id - if google_id: account.google_id = google_id + account = cls.get(username=username, raise_e=strict) + if not account: + return + if facebook_id: + account.facebook_id = facebook_id + if google_id: + account.google_id = google_id account.save() cls._csv_import(file, callback) @classmethod - @appier.link(name = "Export Simple") - def simple_csv_url(cls, absolute = False): - return appier.get_app().url_for( - "account_api.simple_csv", - absolute = absolute - ) + @appier.link(name="Export Simple") + def simple_csv_url(cls, absolute=False): + return appier.get_app().url_for("account_api.simple_csv", absolute=absolute) def pre_create(self): appier_extras.admin.Account.pre_create(self) @@ -163,102 +132,90 @@ def post_create(self): self.ensure_bag_s() self.notify() - def recover_s(self, send_email = False): - result = appier_extras.admin.Account.recover_s( - self, - send_email = send_email - ) - self.notify(name = "account.recover") + def recover_s(self, send_email=False): + result = appier_extras.admin.Account.recover_s(self, send_email=send_email) + self.notify(name="account.recover") return result - def ensure_bag_s(self, key = None): + def ensure_bag_s(self, key=None): _bag = self.get_bag() - if _bag: return _bag - _bag = bag.Bag(key = key) + if _bag: + return _bag + _bag = bag.Bag(key=key) _bag.account = self _bag.save() return _bag def get_bag(self): - return bag.Bag.get(account = self.id, raise_e = False) + return bag.Bag.get(account=self.id, raise_e=False) - def get_store_orders(self, status = "waiting_payment"): - if not self.store: raise appier.OperationalError( - message = "No store associated with user", - code = 403 - ) + def get_store_orders(self, status="waiting_payment"): + if not self.store: + raise appier.OperationalError( + message="No store associated with user", code=403 + ) kwargs = dict() - if self.store.is_restricted: kwargs["account"] = self.id + if self.store.is_restricted: + kwargs["account"] = self.id orders = order.Order.find( - status = status, - store = self.store.id, - sort = [("id", -1)], - **kwargs + status=status, store=self.store.id, sort=[("id", -1)], **kwargs ) return orders @appier.operation( - name = "Set Store", - parameters = ( - ( - "Store", - "store", - appier.reference("Store", name = "id") - ), - ) + name="Set Store", + parameters=(("Store", "store", appier.reference("Store", name="id")),), ) def set_store_s(self, store): - if not store: return + if not store: + return self.store = store self.save() @appier.operation( - name = "Set Metadata", - parameters = (("Metadata","meta", "longmap"),) + name="Set Metadata", parameters=(("Metadata", "meta", "longmap"),) ) def set_meta_s(self, meta): - if not meta: return + if not meta: + return self.meta = meta self.save() - @appier.operation(name = "Notify") - def notify(self, name = None, *args, **kwargs): + @appier.operation(name="Notify") + def notify(self, name=None, *args, **kwargs): name = name or "account.new" - account = self.reload(rules = False, map = True) + account = self.reload(rules=False, map=True) receiver = account.get("email", None) receiver = kwargs.get("email", receiver) appier_extras.admin.Event.notify_g( name, - arguments = dict( - params = dict( - payload = account, - account = account, - receiver = receiver, - extra = kwargs + arguments=dict( + params=dict( + payload=account, account=account, receiver=receiver, extra=kwargs ) - ) + ), ) - @appier.view(name = "Orders") + @appier.view(name="Orders") def orders_v(self, *args, **kwargs): kwargs["sort"] = kwargs.get("sort", [("created", -1)]) - kwargs.update(account = self.id) + kwargs.update(account=self.id) return appier.lazy_dict( - model = order.Order, - kwargs = kwargs, - entities = appier.lazy(lambda: order.Order.find(*args, **kwargs)), - page = appier.lazy(lambda: order.Order.paginate(*args, **kwargs)), - names = ["reference", "created", "total", "currency", "status"] + model=order.Order, + kwargs=kwargs, + entities=appier.lazy(lambda: order.Order.find(*args, **kwargs)), + page=appier.lazy(lambda: order.Order.paginate(*args, **kwargs)), + names=["reference", "created", "total", "currency", "status"], ) - @appier.view(name = "Addresses") + @appier.view(name="Addresses") def addresses_v(self, *args, **kwargs): return appier.lazy_dict( - model = self.addresses._target, - kwargs = kwargs, - entities = appier.lazy(lambda: self.addresses.find(*args, **kwargs)), - page = appier.lazy(lambda: self.addresses.paginate(*args, **kwargs)), - names = ["first_name", "last_name", "address", "country"] + model=self.addresses._target, + kwargs=kwargs, + entities=appier.lazy(lambda: self.addresses.find(*args, **kwargs)), + page=appier.lazy(lambda: self.addresses.paginate(*args, **kwargs)), + names=["first_name", "last_name", "address", "country"], ) @property diff --git a/src/budy/models/address.py b/src/budy/models/address.py index 9f59bb91..ca376b6a 100644 --- a/src/budy/models/address.py +++ b/src/budy/models/address.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,25 +32,15 @@ from . import base -class Address(base.BudyBase): - key = appier.field( - index = True, - safe = True, - immutable = True - ) +class Address(base.BudyBase): + key = appier.field(index=True, safe=True, immutable=True) - first_name = appier.field( - index = True - ) + first_name = appier.field(index=True) - last_name = appier.field( - index = True - ) + last_name = appier.field(index=True) - tax_number = appier.field( - index = True - ) + tax_number = appier.field(index=True) address = appier.field() @@ -62,15 +52,11 @@ class Address(base.BudyBase): state = appier.field() - country = appier.field( - meta = "country" - ) + country = appier.field(meta="country") phone_number = appier.field() - vat_number = appier.field( - description = "VAT Number" - ) + vat_number = appier.field(description="VAT Number") neighborhood = appier.field() @@ -79,17 +65,13 @@ def validate(cls): return super(Address, cls).validate() + [ appier.not_null("first_name"), appier.not_empty("first_name"), - appier.not_null("last_name"), appier.not_empty("last_name"), - appier.not_null("address"), appier.not_empty("address"), - appier.not_null("city"), appier.not_empty("city"), - - appier.string_eq("country", 2) + appier.string_eq("country", 2), ] @classmethod @@ -116,5 +98,6 @@ def pre_create(self): @property def full_name(self): - if not self.last_name: return self.first_name + if not self.last_name: + return self.first_name return self.first_name + " " + self.last_name diff --git a/src/budy/models/bag.py b/src/budy/models/bag.py index bf98db20..43796172 100644 --- a/src/budy/models/bag.py +++ b/src/budy/models/bag.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -35,23 +35,12 @@ from . import bundle from . import bag_line -class Bag(bundle.Bundle): - lines = appier.field( - type = appier.references( - "BagLine", - name = "id" - ), - eager = True - ) +class Bag(bundle.Bundle): + lines = appier.field(type=appier.references("BagLine", name="id"), eager=True) account = appier.field( - type = appier.reference( - "BudyAccount", - name = "id" - ), - index = "hashed", - eager = True + type=appier.reference("BudyAccount", name="id"), index="hashed", eager=True ) @classmethod @@ -63,26 +52,32 @@ def line_cls(cls): return bag_line.BagLine @classmethod - def from_session(cls, ensure = True, raise_e = False): + def from_session(cls, ensure=True, raise_e=False): from . import BudyAccount - account = BudyAccount.from_session(raise_e = raise_e) - if account: return account.ensure_bag_s() + + account = BudyAccount.from_session(raise_e=raise_e) + if account: + return account.ensure_bag_s() session = appier.get_session() key = session.get("bag", None) - bag = cls.get(key = key, raise_e = raise_e) if key else None - if bag: return bag - if not ensure: return None + bag = cls.get(key=key, raise_e=raise_e) if key else None + if bag: + return bag + if not ensure: + return None bag = cls() bag.save() session["bag"] = bag.key return bag @classmethod - def ensure_s(cls, key = None): + def ensure_s(cls, key=None): from . import BudyAccount - account = BudyAccount.from_session(raise_e = False) - if account: return account.ensure_bag_s(key = key) - bag = cls(key = key) + + account = BudyAccount.from_session(raise_e=False) + if account: + return account.ensure_bag_s(key=key) + bag = cls(key=key) bag.save() return bag @@ -92,28 +87,31 @@ def pre_validate(self): def pre_delete(self): bundle.Bundle.pre_delete(self) - for line in self.lines: line.delete() + for line in self.lines: + line.delete() def add_line_s(self, line): line.bag = self return bundle.Bundle.add_line_s(self, line) - def to_order_s(self, verify = True, try_valid = True): + def to_order_s(self, verify=True, try_valid=True): self.refresh_s() - if try_valid: self.try_valid_s() - if verify: self.verify_order() + if try_valid: + self.try_valid_s() + if verify: + self.verify_order() _order = order.Order( - currency = self.currency, - country = self.country, - sub_total = self.sub_total, - discounted_sub_total = self.discounted_sub_total, - undiscounted_sub_total = self.undiscounted_sub_total, - discount = self.discount, - shipping_cost = self.shipping_cost, - taxes = self.taxes, - total = self.total, - account = self.account, - store = self.account and self.account.store + currency=self.currency, + country=self.country, + sub_total=self.sub_total, + discounted_sub_total=self.discounted_sub_total, + undiscounted_sub_total=self.undiscounted_sub_total, + discount=self.discount, + shipping_cost=self.shipping_cost, + taxes=self.taxes, + total=self.total, + account=self.account, + store=self.account and self.account.store, ) _order.save() _order.lines = [] @@ -121,7 +119,8 @@ def to_order_s(self, verify = True, try_valid = True): order_line = line.to_order_line_s(_order) _order.lines.append(order_line) _order.save() - if verify: _order.verify_base() + if verify: + _order.verify_base() return _order def verify_order(self): @@ -134,47 +133,42 @@ def verify_order(self): for line in self.lines: appier.verify(line.is_valid()) - @appier.operation(name = "Garbage Collect") + @appier.operation(name="Garbage Collect") def collect_s(self): self.delete() - @appier.operation(name = "Fix Orphans") + @appier.operation(name="Fix Orphans") def fix_orphans_s(self): for line in self.lines: line.bag = self line.save() - @appier.operation(name = "Notify") - def notify(self, name = None, *args, **kwargs): + @appier.operation(name="Notify") + def notify(self, name=None, *args, **kwargs): name = name or "bag.new" - bag = self.reload(map = True) + bag = self.reload(map=True) account = bag.get("account", {}) account = kwargs.get("email", account) receiver = account.get("email", None) receiver = kwargs.get("email", receiver) appier_extras.admin.Event.notify_g( name, - arguments = dict( - params = dict( - payload = bag, - bag = bag, - receiver = receiver, - extra = kwargs - ) - ) + arguments=dict( + params=dict(payload=bag, bag=bag, receiver=receiver, extra=kwargs) + ), ) - @appier.operation(name = "Remind") + @appier.operation(name="Remind") def remind(self, *args, **kwargs): - self.notify(name = "bag.remind", *args, **kwargs) + self.notify(name="bag.remind", *args, **kwargs) - @appier.view(name = "Lines") + @appier.view(name="Lines") def lines_v(self, *args, **kwargs): kwargs["sort"] = kwargs.get("sort", [("created", -1)]) return appier.lazy_dict( - model = self.lines._target, - kwargs = kwargs, - entities = appier.lazy(lambda: self.lines.find(*args, **kwargs)), - page = appier.lazy(lambda: self.lines.paginate(*args, **kwargs)), - names = ["product", "quantity", "total", "currency"] + model=self.lines._target, + kwargs=kwargs, + entities=appier.lazy(lambda: self.lines.find(*args, **kwargs)), + page=appier.lazy(lambda: self.lines.paginate(*args, **kwargs)), + names=["product", "quantity", "total", "currency"], ) diff --git a/src/budy/models/bag_line.py b/src/budy/models/bag_line.py index 885bb983..4e07eecd 100644 --- a/src/budy/models/bag_line.py +++ b/src/budy/models/bag_line.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -33,14 +33,9 @@ from . import order_line from . import bundle_line -class BagLine(bundle_line.BundleLine): - bag = appier.field( - type = appier.reference( - "Bag", - name = "id" - ) - ) +class BagLine(bundle_line.BundleLine): + bag = appier.field(type=appier.reference("Bag", name="id")) @classmethod def list_names(cls): @@ -54,25 +49,26 @@ def pre_validate(self): bundle_line.BundleLine.pre_validate(self) self.try_valid() - def to_order_line_s(self, order = None): + def to_order_line_s(self, order=None): _order_line = order_line.OrderLine( - price = self.price, - currency = self.currency, - country = self.country, - quantity = self.quantity, - total = self.total, - size = self.size, - size_s = self.size_s, - scale = self.scale, - discounted = self.discounted, - attributes = self.attributes, - product = self.product, - order = order + price=self.price, + currency=self.currency, + country=self.country, + quantity=self.quantity, + total=self.total, + size=self.size, + size_s=self.size_s, + scale=self.scale, + discounted=self.discounted, + attributes=self.attributes, + product=self.product, + order=order, ) _order_line.save() return _order_line - @appier.operation(name = "Garbage Collect") + @appier.operation(name="Garbage Collect") def collect_s(self): - if appier.is_unset(self.bag): return + if appier.is_unset(self.bag): + return self.delete() diff --git a/src/budy/models/base.py b/src/budy/models/base.py index c1e353ec..1a582c37 100644 --- a/src/budy/models/base.py +++ b/src/budy/models/base.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -31,23 +31,13 @@ import appier import appier_extras -class BudyBase(appier_extras.admin.Base): - slug = appier.field( - index = "hashed", - safe = True - ) +class BudyBase(appier_extras.admin.Base): + slug = appier.field(index="hashed", safe=True) - slug_id = appier.field( - index = "hashed", - safe = True, - description = "Slug ID" - ) + slug_id = appier.field(index="hashed", safe=True, description="Slug ID") - tokens = appier.field( - index = "hashed", - safe = True - ) + tokens = appier.field(index="hashed", safe=True) @classmethod def is_abstract(cls): @@ -60,13 +50,15 @@ def token_names(cls): @classmethod def find_s(cls, *args, **kwargs): find_s = kwargs.get("find_s", None) - if find_s: kwargs["find_s"] = cls._simplify(find_s) + if find_s: + kwargs["find_s"] = cls._simplify(find_s) return cls.find(*args, **kwargs) @classmethod def find_se(cls, *args, **kwargs): find_s = kwargs.get("find_s", None) - if find_s: kwargs["find_s"] = cls._simplify(find_s) + if find_s: + kwargs["find_s"] = cls._simplify(find_s) return cls.find_e(*args, **kwargs) @classmethod @@ -86,12 +78,12 @@ def post_save(self): appier_extras.admin.Base.post_save(self) self._update_slug_s() - @appier.operation(name = "Update Slug") + @appier.operation(name="Update Slug") def update_slug_s(self): self._update_slug() self.save() - @appier.operation(name = "Update Tokens") + @appier.operation(name="Update Tokens") def update_tokens_s(self): self._update_tokens() self.save() @@ -101,7 +93,8 @@ def _update_slug_s(self): slug_id = self.slug_id if hasattr(self, "slug_id") else None self._update_slug() has_changed = not slug == self.slug or not slug_id == self.slug_id - if not has_changed: return + if not has_changed: + return self.save() def _update_slug(self): @@ -118,13 +111,13 @@ def _update_tokens(self): cls = self.__class__ tokens = [] for name, pluralize in cls.token_names(): - field_tokens = self._get_tokens(name, pluralize = pluralize) + field_tokens = self._get_tokens(name, pluralize=pluralize) tokens.extend(field_tokens) tokens = list(set(tokens)) tokens.sort() self.tokens = "|".join(tokens) - def _get_tokens(self, name, pluralize = False): + def _get_tokens(self, name, pluralize=False): cls = self.__class__ tokens = [] @@ -132,24 +125,29 @@ def _get_tokens(self, name, pluralize = False): names = name.split(".") name = names[0] names = names[1:] if len(names) > 1 else [] - if not hasattr(self, name): return [] + if not hasattr(self, name): + return [] value = getattr(self, name) - if not value: return [] + if not value: + return [] is_iterable = hasattr(value, "__iter__") - is_iterable = is_iterable and not appier.legacy.is_string(value, all = True) + is_iterable = is_iterable and not appier.legacy.is_string(value, all=True) values = value if is_iterable else [value] for value in values: for _name in names: - if not value: break + if not value: + break value = getattr(value, _name) - if not value: continue + if not value: + continue token = value token = cls._simplify(token) token_p = cls._pluralize(token) tokens.append(token) - if pluralize: tokens.append(token_p) + if pluralize: + tokens.append(token_p) return tokens diff --git a/src/budy/models/brand.py b/src/budy/models/brand.py index 7c437153..ce3dc2f1 100644 --- a/src/budy/models/brand.py +++ b/src/budy/models/brand.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,5 +30,6 @@ from . import group + class Brand(group.Group): pass diff --git a/src/budy/models/bundle.py b/src/budy/models/bundle.py index c806e128..6880d236 100644 --- a/src/budy/models/bundle.py +++ b/src/budy/models/bundle.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -35,201 +35,155 @@ from . import base from . import bundle_line + class Bundle(base.BudyBase): + key = appier.field(index="hashed", safe=True, immutable=True) - key = appier.field( - index = "hashed", - safe = True, - immutable = True - ) + currency = appier.field(index="hashed") - currency = appier.field( - index = "hashed" - ) - - country = appier.field( - index = "hashed" - ) + country = appier.field(index="hashed") quantity = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True + type=commons.Decimal, index=True, initial=commons.Decimal(0.0), safe=True ) sub_total = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True, - observations = """The total amount for the bundle resulting + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + safe=True, + observations="""The total amount for the bundle resulting from the sum of the line values, this value should not include - the discount and the shipping costs """ + the discount and the shipping costs """, ) discounted_sub_total = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True, - observations = """The sub total amount resolved from the bundle lines - for which a line discount has been applied (lines that are discounted)""" + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + safe=True, + observations="""The sub total amount resolved from the bundle lines + for which a line discount has been applied (lines that are discounted)""", ) undiscounted_sub_total = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True, - observations = """The sub total amount resolved from the bundle lines for - which no line discount has been applied (lines that are not discounted)""" + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + safe=True, + observations="""The sub total amount resolved from the bundle lines for + which no line discount has been applied (lines that are not discounted)""", ) discountable_sub_total = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True, - observations = """The total amount of the sub total that is still discountable - under the global discount approach""" + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + safe=True, + observations="""The total amount of the sub total that is still discountable + under the global discount approach""", ) discount = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True, - observations = """The total amount for the global discount applied to the - current bundle, does not include the line discount values""" + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + safe=True, + observations="""The total amount for the global discount applied to the + current bundle, does not include the line discount values""", ) discount_fixed = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True + type=commons.Decimal, index=True, initial=commons.Decimal(0.0), safe=True ) discount_dynamic = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True + type=commons.Decimal, index=True, initial=commons.Decimal(0.0), safe=True ) shipping_cost = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True + type=commons.Decimal, index=True, initial=commons.Decimal(0.0), safe=True ) shipping_fixed = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True + type=commons.Decimal, index=True, initial=commons.Decimal(0.0), safe=True ) shipping_dynamic = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True + type=commons.Decimal, index=True, initial=commons.Decimal(0.0), safe=True ) taxes = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True + type=commons.Decimal, index=True, initial=commons.Decimal(0.0), safe=True ) total = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True + type=commons.Decimal, index=True, initial=commons.Decimal(0.0), safe=True ) discountable_full = appier.field( - type = bool, - index = True, - initial = False, - safe = True, - observations = """If the bundle should be fully discountable, + type=bool, + index=True, + initial=False, + safe=True, + observations="""If the bundle should be fully discountable, meaning that the sub total value to be used as the discountable base is going to include lines with line level discount and that the shipping costs may also be affected by discount, in effect - making them considered as discountable""" + making them considered as discountable""", ) ip_address = appier.field( - index = "hashed", - safe = True, - description = "IP Address", - observations = """The IP address of the device used to perform - the creation of the bundle""" + index="hashed", + safe=True, + description="IP Address", + observations="""The IP address of the device used to perform + the creation of the bundle""", ) ip_country = appier.field( - index = "hashed", - safe = True, - meta = "country", - description = "IP Country", - observations = """Country obtained through GEO IP operation - may be used for statistics and tracing""" + index="hashed", + safe=True, + meta="country", + description="IP Country", + observations="""Country obtained through GEO IP operation + may be used for statistics and tracing""", ) referrals = appier.field( - type = appier.references( - "Referral", - name = "name" - ), - eager = True + type=appier.references("Referral", name="name"), eager=True ) - def __init__(self, model = None, **kwargs): - base.BudyBase.__init__(self, model = model, **kwargs) + def __init__(self, model=None, **kwargs): + base.BudyBase.__init__(self, model=model, **kwargs) if not hasattr(self, "discountable_full"): self.discountable_full = appier.conf( - "BUDY_FULL_DISCOUNTABLE", False, cast = bool + "BUDY_FULL_DISCOUNTABLE", False, cast=bool ) @classmethod def validate(cls): return super(Bundle, cls).validate() + [ appier.not_duplicate("key", cls._name()), - appier.not_null("quantity"), appier.gte("quantity", 0.0), - appier.not_null("sub_total"), appier.gte("sub_total", 0.0), - appier.not_null("discounted_sub_total"), appier.gte("discounted_sub_total", 0.0), - appier.not_null("undiscounted_sub_total"), appier.gte("undiscounted_sub_total", 0.0), - appier.not_null("discountable_sub_total"), appier.gte("discountable_sub_total", 0.0), - appier.not_null("discount"), appier.gte("discount", 0.0), - appier.not_null("shipping_cost"), appier.gte("shipping_cost", 0.0), - appier.not_null("taxes"), appier.gte("taxes", 0.0), - appier.not_null("total"), appier.gte("total", 0.0), - - appier.string_eq("ip_country", 2) + appier.string_eq("ip_country", 2), ] @classmethod @@ -251,21 +205,24 @@ def line_cls(cls): @classmethod def eval_discount(cls, *args, **kwargs): discount = appier.conf("BUDY_DISCOUNT", None) - if not discount: return 0.0 + if not discount: + return 0.0 discount = eval(discount) return discount(*args, **kwargs) @classmethod def eval_shipping(cls, *args, **kwargs): shipping = appier.conf("BUDY_SHIPPING", None) - if not shipping: return 0.0 + if not shipping: + return 0.0 shipping = eval(shipping) return shipping(*args, **kwargs) @classmethod def eval_taxes(cls, *args, **kwargs): taxes = appier.conf("BUDY_TAXES", None) - if not taxes: return 0.0 + if not taxes: + return 0.0 taxes = eval(taxes) return taxes(*args, **kwargs) @@ -301,10 +258,12 @@ def add_line_s(self, line): def remove_line_s(self, line_id): match = None for line in self.lines: - if not line.id == line_id: continue + if not line.id == line_id: + continue match = line break - if not match: return + if not match: + return self.lines.remove(match) self.save() match.delete() @@ -312,20 +271,19 @@ def remove_line_s(self, line_id): def add_product_s( self, product, - quantity = 1.0, - size = None, - size_s = None, - scale = None, - attributes = None, - increment = True + quantity=1.0, + size=None, + size_s=None, + scale=None, + attributes=None, + increment=True, ): cls = self.__class__ _line = None - if not product: raise appier.OperationalError( - message = "No product defined" - ) + if not product: + raise appier.OperationalError(message="No product defined") for line in self.lines: is_same = line.product.id == product.id @@ -333,61 +291,63 @@ def add_product_s( is_same &= line.size_s == size_s is_same &= line.scale == scale is_same &= line.attributes == attributes - if not is_same: continue + if not is_same: + continue _line = line if _line: - if not increment: return _line + if not increment: + return _line _line.quantity += quantity _line.save() self.save() return _line _line = cls.line_cls()( - product = product, - quantity = quantity, - size = size, - size_s = size_s, - scale = scale, - attributes = attributes + product=product, + quantity=quantity, + size=size, + size_s=size_s, + scale=scale, + attributes=attributes, ) self.add_line_s(_line) return _line - def add_update_line_s(self, line, increment = True): + def add_update_line_s(self, line, increment=True): return self.add_product_s( line.product, - quantity = line.quantity, - size = line.size, - size_s = line.size_s, - scale = line.scale, - attributes = line.attributes, - increment = increment + quantity=line.quantity, + size=line.size, + size_s=line.size_s, + scale=line.scale, + attributes=line.attributes, + increment=increment, ) - def merge_s(self, bag_id, increment = False): + def merge_s(self, bag_id, increment=False): cls = self.__class__ - if bag_id == self.id: return - bag = cls.get(id = bag_id) + if bag_id == self.id: + return + bag = cls.get(id=bag_id) for line in bag.lines: line = line.clone() - self.add_update_line_s(line, increment = increment) + self.add_update_line_s(line, increment=increment) self.refresh_s() - def refresh_s(self, currency = None, country = None, force = False): + def refresh_s(self, currency=None, country=None, force=False): currency = currency or self.currency country = country or self.country - is_dirty = self.is_dirty(currency = currency, country = country) - if not is_dirty and not force: return False + is_dirty = self.is_dirty(currency=currency, country=country) + if not is_dirty and not force: + return False lines = self.lines if hasattr(self, "lines") else [] for line in lines: - is_dirty = line.is_dirty( - currency = currency, - country = country - ) - if not is_dirty: continue - line.calculate(currency = currency, country = country) + is_dirty = line.is_dirty(currency=currency, country=country) + if not is_dirty: + continue + line.calculate(currency=currency, country=country) line.save() self.currency = currency self.country = country @@ -411,8 +371,14 @@ def calculate(self): self.quantity = sum(line.quantity for line in lines) self.sub_total = sum(line.total for line in lines) self.discounted_sub_total = sum(line.total for line in lines if line.discounted) - self.undiscounted_sub_total = sum(line.total for line in lines if not line.discounted) - self.discountable_sub_total = sum(line.total for line in lines if line.is_discountable(strict = not self.discountable_full)) + self.undiscounted_sub_total = sum( + line.total for line in lines if not line.discounted + ) + self.discountable_sub_total = sum( + line.total + for line in lines + if line.is_discountable(strict=not self.discountable_full) + ) self.taxes = self.calculate_taxes() self.shipping_cost = self.calculate_shipping() self.discount = self.calculate_discount() @@ -431,12 +397,9 @@ def calculate_shipping(self): def build_discount(self): discount = 0.0 - join_discount = appier.conf("BUDY_JOIN_DISCOUNT", True, cast = bool) + join_discount = appier.conf("BUDY_JOIN_DISCOUNT", True, cast=bool) self.discount_dynamic = self.__class__.eval_discount( - self.discountable, - self.taxes, - self.quantity, - self + self.discountable, self.taxes, self.quantity, self ) if join_discount: discount += self.discount_dynamic @@ -447,12 +410,9 @@ def build_discount(self): def build_taxes(self): taxes = 0.0 - join_taxes = appier.conf("BUDY_JOIN_TAXES", True, cast = bool) + join_taxes = appier.conf("BUDY_JOIN_TAXES", True, cast=bool) taxes_dynamic = self.__class__.eval_taxes( - self.sub_total, - self.taxes, - self.quantity, - self + self.sub_total, self.taxes, self.quantity, self ) taxes_lines = sum(line.total_taxes for line in self.lines) if join_taxes: @@ -464,12 +424,9 @@ def build_taxes(self): def build_shipping(self): shipping_cost = 0.0 - join_shipping = appier.conf("BUDY_JOIN_SHIPPING", True, cast = bool) + join_shipping = appier.conf("BUDY_JOIN_SHIPPING", True, cast=bool) self.shipping_dynamic = self.__class__.eval_shipping( - self.sub_total, - self.taxes, - self.quantity, - self + self.sub_total, self.taxes, self.quantity, self ) if join_shipping: shipping_cost += self.shipping_dynamic @@ -482,8 +439,10 @@ def collect_empty(self): empty = [] for line in self.lines: is_empty = line.is_empty() - if is_empty: empty.append(line) - for line in empty: self.lines.remove(line) + if is_empty: + empty.append(line) + for line in empty: + self.lines.remove(line) def try_valid(self): # unsets the fixed flag meaning that by default no @@ -493,8 +452,10 @@ def try_valid(self): # iterates over the complete set of bundle lines # to try to fix them and make them valid in case # none of them has to be fixed returns immediately - for line in self.lines: fixed |= line.try_valid_s() - if not fixed: return fixed + for line in self.lines: + fixed |= line.try_valid_s() + if not fixed: + return fixed # otherwise we need to collect the possible empty # lines and to re-calculate the total values as @@ -508,61 +469,67 @@ def try_valid(self): def try_valid_s(self): fixed = self.try_valid() - if not fixed: return fixed + if not fixed: + return fixed self.save() return fixed def ensure_valid(self): appier.verify(self.is_valid()) - def is_dirty(self, currency = None, country = None): + def is_dirty(self, currency=None, country=None): dirty = False lines = self.lines if hasattr(self, "lines") else [] - for line in lines: dirty |= line.is_dirty( - currency = currency, - country = country - ) + for line in lines: + dirty |= line.is_dirty(currency=currency, country=country) return dirty def is_valid(self): is_valid = True - for line in self.lines: is_valid &= line.is_valid() + for line in self.lines: + is_valid &= line.is_valid() return is_valid - @appier.operation(name = "Empty") + @appier.operation(name="Empty") def empty_s(self): - for line in self.lines: line.delete() + for line in self.lines: + line.delete() self.lines = [] self.save() - @appier.operation(name = "Fix Lines") + @appier.operation(name="Fix Lines") def fix_lines_s(self): lines = self.lines lines_f = [] for line in lines: - if not line.is_resolvable(): continue + if not line.is_resolvable(): + continue line.resolve() lines_f.append(line) self.lines = lines_f self.save() - @appier.operation(name = "Fix Sub Total") + @appier.operation(name="Fix Sub Total") def fix_sub_total_s(self): - if self.sub_total: return + if self.sub_total: + return self.sub_total = self.total self.discounted_sub_total = 0.0 self.undiscounted_sub_total = self.sub_total self.save() - @appier.operation(name = "Calculate") + @appier.operation(name="Calculate") def calculate_s(self): self.calculate() self.save() @property def discountable(self): - return self.sub_total + self.shipping_cost if\ - self.discountable_full else self.discountable_sub_total + return ( + self.sub_total + self.shipping_cost + if self.discountable_full + else self.discountable_sub_total + ) @property def discount_base(self): diff --git a/src/budy/models/bundle_line.py b/src/budy/models/bundle_line.py index 46cbb614..e04d0eb6 100644 --- a/src/budy/models/bundle_line.py +++ b/src/budy/models/bundle_line.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -35,87 +35,71 @@ from . import base -class BundleLine(base.BudyBase): +class BundleLine(base.BudyBase): price = appier.field( - type = commons.Decimal, - observations = """The unit price for the item described by the - current line, the total should be calculated using this value""" + type=commons.Decimal, + observations="""The unit price for the item described by the + current line, the total should be calculated using this value""", ) taxes = appier.field( - type = commons.Decimal, - observations = """The amount of taxes paid per each item in the + type=commons.Decimal, + observations="""The amount of taxes paid per each item in the current line, this value is already included in the price value - of the line""" + of the line""", ) currency = appier.field( - observations = """The currency in use for the line all the price + observations="""The currency in use for the line all the price related values for the line are considered to be represented under this given currency""" ) country = appier.field() - quantity = appier.field( - type = commons.Decimal, - initial = commons.Decimal(0.0) - ) + quantity = appier.field(type=commons.Decimal, initial=commons.Decimal(0.0)) - total = appier.field( - type = commons.Decimal, - initial = commons.Decimal(0.0) - ) + total = appier.field(type=commons.Decimal, initial=commons.Decimal(0.0)) - total_taxes = appier.field( - type = commons.Decimal, - initial = commons.Decimal(0.0) - ) + total_taxes = appier.field(type=commons.Decimal, initial=commons.Decimal(0.0)) - size = appier.field( - type = int - ) + size = appier.field(type=int) size_s = appier.field() - scale = appier.field( - type = int - ) + scale = appier.field(type=int) discounted = appier.field( - type = bool, - initial = False, - index = True, - observations = """If the product associated with the + type=bool, + initial=False, + index=True, + observations="""If the product associated with the current line is a discounted one, meaning that the product - price is affected by some discount""" + price is affected by some discount""", ) closed = appier.field( - type = bool, - initial = False, - index = True, - observations = """Simple flag that control if the line is + type=bool, + initial=False, + index=True, + observations="""Simple flag that control if the line is closed meaning that calculated values (eg: totals) can no - longer be re-calculated as the line is now frozen""" + longer be re-calculated as the line is now frozen""", ) attributes = appier.field( - observations = """Additional metadata that define the given + observations="""Additional metadata that define the given (bundle) line, this is typically used to store a JSON serialized string with extra fields""" ) product = appier.field( - type = appier.reference( - "Product", - name = "id" - ), - eager = True, - observations = """The product that this bundle line is + type=appier.reference("Product", name="id"), + eager=True, + observations="""The product that this bundle line is associated with, this is the product that is going to - be used for the calculation of the price""" + be used for the calculation of the price""", ) @classmethod @@ -136,67 +120,62 @@ def pre_save(self): self.measure() self.ensure_valid() - def calculate(self, currency = None, country = None, force = False): - if self.closed: return + def calculate(self, currency=None, country=None, force=False): + if self.closed: + return currency = currency or self.currency country = country or self.country self.total_taxes = self.quantity * self.get_taxes( - currency = currency, - country = country, - force = force + currency=currency, country=country, force=force ) self.total = self.quantity * self.get_price( - currency = currency, - country = country, - force = force + currency=currency, country=country, force=force ) self.discounted = self.merchandise.is_discounted - def measure(self, currency = None, country = None, force = False): - if self.closed: return - if self.size and self.scale and not force: return + def measure(self, currency=None, country=None, force=False): + if self.closed: + return + if self.size and self.scale and not force: + return self.size, self.scale = self.get_size( - currency = currency, - country = country, - force = force + currency=currency, country=country, force=force ) def close_s(self): self.closed = True self.save() - 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 + 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 self.price = self.merchandise.get_price( - currency = currency, - country = country, - attributes = self.attributes + currency=currency, country=country, attributes=self.attributes ) self.taxes = self.merchandise.get_taxes( - currency = currency, - country = country, - attributes = self.attributes + currency=currency, country=country, attributes=self.attributes ) - self.currency = self.merchandise.get_currency(currency = currency) + self.currency = self.merchandise.get_currency(currency=currency) self.country = country return self.price - def get_taxes(self, currency = None, country = None, force = False): - self.get_price(currency = currency, country = country, force = force) + 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): - if not self.product: return None, None + def get_size(self, currency=None, country=None, force=False): + if not self.product: + return None, None return self.product.get_size( - currency = currency, - country = country, - attributes = self.attributes + currency=currency, country=country, attributes=self.attributes ) def ensure_size_s(self): - if not self.size: return - if not hasattr(self.merchandise, "value_s"): return + if not self.size: + return + if not hasattr(self.merchandise, "value_s"): + return self.size_s = self.merchandise.value_s def ensure_valid(self): @@ -210,34 +189,41 @@ def try_valid(self): def try_valid_s(self): fixed = self.try_valid() - if not fixed: return fixed + if not fixed: + return fixed self.save() return fixed def try_valid_quantity(self): fixed = False - if self.quantity <= 0.0: self.quantity = 0.0 - if self.merchandise.quantity_hand == None: return fixed - if self.quantity <= self.merchandise.quantity_hand: return fixed + if self.quantity <= 0.0: + self.quantity = 0.0 + if self.merchandise.quantity_hand == None: + return fixed + if self.quantity <= self.merchandise.quantity_hand: + return fixed self.quantity = min(self.quantity, self.merchandise.quantity_hand) - self.calculate(force = True) + self.calculate(force=True) fixed |= True return fixed def try_valid_price(self): fixed = False - if self.merchandise.is_price_provided: return fixed - if self.merchandise.price == None: return fixed - if self.merchandise.price == self.price: return fixed + if self.merchandise.is_price_provided: + return fixed + if self.merchandise.price == None: + return fixed + if self.merchandise.price == self.price: + return fixed self.price = self.merchandise.price - self.calculate(force = True) + self.calculate(force=True) fixed |= True return fixed def is_empty(self): return self.quantity == 0.0 - def is_dirty(self, currency = None, country = None): + 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 @@ -250,65 +236,88 @@ def is_valid(self): is_valid &= self.is_valid_size() return is_valid - def is_discountable(self, strict = False): - if not self.merchandise.is_discountable: return False - if strict and self.discounted: return False + def is_discountable(self, strict=False): + if not self.merchandise.is_discountable: + return False + if strict and self.discounted: + return False return True - def is_valid_quantity(self, reload = True): - if self.quantity < 0: return False - merchandise = self.merchandise and self.merchandise.reload() if\ - reload else self.merchandise - if not merchandise.quantity_hand == None and\ - self.quantity > merchandise.quantity_hand: return False + def is_valid_quantity(self, reload=True): + if self.quantity < 0: + return False + merchandise = ( + self.merchandise and self.merchandise.reload() + if reload + else self.merchandise + ) + if ( + not merchandise.quantity_hand == None + and self.quantity > merchandise.quantity_hand + ): + return False return True - def is_valid_price(self, reload = True): - merchandise = self.merchandise and self.merchandise.reload() if\ - reload else self.merchandise - if not merchandise.is_price_provided and\ - not merchandise.price == None and\ - not self.price == merchandise.price: return False + def is_valid_price(self, reload=True): + merchandise = ( + self.merchandise and self.merchandise.reload() + if reload + else self.merchandise + ) + if ( + not merchandise.is_price_provided + and not merchandise.price == None + and not self.price == merchandise.price + ): + return False return True - def is_valid_size(self, reload = True): - if not self.product.is_parent: return True + def is_valid_size(self, reload=True): + if not self.product.is_parent: + return True return True if self.size else False - @appier.operation(name = "Calculate") + @appier.operation(name="Calculate") def calculate_s(self): - self.calculate(force = True) + self.calculate(force=True) self.save() - @appier.operation(name = "Measure") + @appier.operation(name="Measure") def measure_s(self): - self.measure(force = True) + self.measure(force=True) self.save() - @appier.operation(name = "Recover product") + @appier.operation(name="Recover product") def recover_s(self): from . import product self.product.resolve() - if self.product.is_resolved(): return + if self.product.is_resolved(): + return - if not self.attributes: return + if not self.attributes: + return attributes = json.loads(self.attributes) product_id = attributes.get("product_id", None) - if not product_id: return + if not product_id: + return product_id_s = str(product_id) - _product = product.Product.get(product_id = product_id_s) - if not _product: return + _product = product.Product.get(product_id=product_id_s) + if not _product: + return self.product = _product - self.save(validate = False, pre_save = False) + self.save(validate=False, pre_save=False) @property - def merchandise(self, name = "size", strict = False): - if not self.size: return self.product - if not hasattr(self.product, "get_measurement"): return self.product - measurement = self.product.get_measurement(self.size, name = name) - if measurement or strict: return measurement + def merchandise(self, name="size", strict=False): + if not self.size: + return self.product + if not hasattr(self.product, "get_measurement"): + return self.product + measurement = self.product.get_measurement(self.size, name=name) + if measurement or strict: + return measurement return self.product diff --git a/src/budy/models/category.py b/src/budy/models/category.py index bb5e1ac6..70843c0e 100644 --- a/src/budy/models/category.py +++ b/src/budy/models/category.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,8 +30,8 @@ from . import group -class Category(group.Group): +class Category(group.Group): @classmethod def _plural(cls): return "Categories" diff --git a/src/budy/models/collection.py b/src/budy/models/collection.py index 6f1bef0c..19e8e5cb 100644 --- a/src/budy/models/collection.py +++ b/src/budy/models/collection.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,5 +30,6 @@ from . import group + class Collection(group.Group): pass diff --git a/src/budy/models/color.py b/src/budy/models/color.py index b0eddee3..24c88902 100644 --- a/src/budy/models/color.py +++ b/src/budy/models/color.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,5 +30,6 @@ from . import group + class Color(group.Group): pass diff --git a/src/budy/models/composition.py b/src/budy/models/composition.py index 20b57ac1..a2ba44ca 100644 --- a/src/budy/models/composition.py +++ b/src/budy/models/composition.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,36 +34,26 @@ from . import base -class Composition(base.BudyBase): +class Composition(base.BudyBase): name = appier.field() - part = appier.field( - index = True, - default = True - ) + part = appier.field(index=True, default=True) - material = appier.field( - index = True - ) + material = appier.field(index=True) - value = appier.field( - type = commons.Decimal - ) + value = appier.field(type=commons.Decimal) @classmethod def validate(cls): return super(Composition, cls).validate() + [ appier.not_null("name"), appier.not_empty("name"), - appier.not_null("part"), appier.not_empty("part"), - appier.not_null("material"), appier.not_empty("material"), - - appier.not_null("value") + appier.not_null("value"), ] @classmethod diff --git a/src/budy/models/country.py b/src/budy/models/country.py index f0057a42..9623206f 100644 --- a/src/budy/models/country.py +++ b/src/budy/models/country.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,39 +32,27 @@ from . import base + class Country(base.BudyBase): + name = appier.field(index=True, default=True) - name = appier.field( - index = True, - default = True - ) + country_code = appier.field(index=True) - country_code = appier.field( - index = True - ) - - currency_code = appier.field( - index = True - ) + currency_code = appier.field(index=True) - locale = appier.field( - index = True - ) + locale = appier.field(index=True) @classmethod def validate(cls): return super(Country, cls).validate() + [ appier.not_null("name"), appier.not_empty("name"), - appier.not_null("country_code"), appier.not_empty("country_code"), appier.string_eq("country_code", 2), - appier.not_null("currency_code"), appier.not_empty("currency_code"), appier.string_eq("currency_code", 3), - appier.not_null("locale"), appier.not_empty("locale"), appier.string_eq("locale", 5), @@ -76,18 +64,17 @@ def list_names(cls): @classmethod def get_by_code(cls, country_code, *args, **kwargs): - return cls.get(country_code = country_code, *args, **kwargs) + return cls.get(country_code=country_code, *args, **kwargs) @classmethod @appier.operation( - name = "Import CSV", - parameters = ( + name="Import CSV", + parameters=( ("CSV File", "file", "file"), - ("Empty source", "empty", bool, False) - ) + ("Empty source", "empty", bool, False), + ), ) def import_csv_s(cls, file, empty): - def callback(line): name, country_code, currency_code, locale = line name = name or None @@ -95,23 +82,21 @@ def callback(line): currency_code = currency_code or None locale = locale or None country = cls( - name = name, - country_code = country_code, - currency_code = currency_code, - locale = locale + name=name, + country_code=country_code, + currency_code=currency_code, + locale=locale, ) country.save() - if empty: cls.delete_c() + if empty: + cls.delete_c() cls._csv_import(file, callback) @classmethod - @appier.link(name = "Export Simple") - def simple_csv_url(cls, absolute = False): - return appier.get_app().url_for( - "country_api.simple_csv", - absolute = absolute - ) + @appier.link(name="Export Simple") + def simple_csv_url(cls, absolute=False): + return appier.get_app().url_for("country_api.simple_csv", absolute=absolute) @classmethod def _plural(cls): diff --git a/src/budy/models/currency.py b/src/budy/models/currency.py index 77d11a16..11e3d8fd 100644 --- a/src/budy/models/currency.py +++ b/src/budy/models/currency.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,18 +32,11 @@ from . import base -class Currency(base.BudyBase): - iso = appier.field( - default = True, - index = True, - description = "ISO" - ) +class Currency(base.BudyBase): + iso = appier.field(default=True, index=True, description="ISO") - decimal_places = appier.field( - type = int, - index = True - ) + decimal_places = appier.field(type=int, index=True) @classmethod def teardown(cls): @@ -56,9 +49,8 @@ def validate(cls): appier.not_null("iso"), appier.not_empty("iso"), appier.string_eq("iso", 3), - appier.not_null("decimal_places"), - appier.gte("decimal_places", 0) + appier.gte("decimal_places", 0), ] @classmethod @@ -66,20 +58,21 @@ def list_names(cls): return ["iso", "decimal_places"] @classmethod - def create_s(cls, iso, decimal_places, invalidate = True): - if invalidate: cls.invalidate() - currency = cls(iso = iso, decimal_places = decimal_places) + def create_s(cls, iso, decimal_places, invalidate=True): + if invalidate: + cls.invalidate() + currency = cls(iso=iso, decimal_places=decimal_places) currency.save() @classmethod - def round(cls, value, currency, rounder = round, decimal_places = 5): + def round(cls, value, currency, rounder=round, decimal_places=5): currencies = cls.get_currencies() currency = currencies.get(currency, {}) decimal_places = currency.get("decimal_places", decimal_places) return rounder(value, decimal_places) @classmethod - def format(cls, value, currency, decimal_places = 2): + def format(cls, value, currency, decimal_places=2): currencies = cls.get_currencies() currency = currencies.get(currency, {}) decimal_places = currency.get("decimal_places", decimal_places) @@ -87,48 +80,44 @@ def format(cls, value, currency, decimal_places = 2): return format % value @classmethod - def get_currencies(cls, app = None): + def get_currencies(cls, app=None): app = app or appier.get_app() - if hasattr(app, "_currencies"): return app._currencies - currencies = cls.find(map = True) + if hasattr(app, "_currencies"): + return app._currencies + currencies = cls.find(map=True) app._currencies = dict([(value["iso"], value) for value in currencies]) return app._currencies @classmethod - def invalidate(cls, app = None): + def invalidate(cls, app=None): app = app or appier.get_app() - if not hasattr(app, "_currencies"): return + if not hasattr(app, "_currencies"): + return delattr(app, "_currencies") @classmethod @appier.operation( - name = "Import CSV", - parameters = ( + name="Import CSV", + parameters=( ("CSV File", "file", "file"), - ("Empty source", "empty", bool, False) - ) + ("Empty source", "empty", bool, False), + ), ) def import_csv_s(cls, file, empty): - def callback(line): iso, decimal_places = line decimal_places = int(decimal_places) - currency = cls( - iso = iso, - decimal_places = decimal_places - ) + currency = cls(iso=iso, decimal_places=decimal_places) currency.save() - if empty: cls.delete_c() + if empty: + cls.delete_c() cls._csv_import(file, callback) @classmethod - @appier.link(name = "Export Simple") - def simple_csv_url(cls, absolute = False): - return appier.get_app().url_for( - "currency_api.simple_csv", - absolute = absolute - ) + @appier.link(name="Export Simple") + def simple_csv_url(cls, absolute=False): + return appier.get_app().url_for("currency_api.simple_csv", absolute=absolute) @classmethod def _plural(cls): diff --git a/src/budy/models/exchange_rate.py b/src/budy/models/exchange_rate.py index 97d92520..9cec905e 100644 --- a/src/budy/models/exchange_rate.py +++ b/src/budy/models/exchange_rate.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,36 +34,25 @@ from . import base -class ExchangeRate(base.BudyBase): - name = appier.field( - default = True - ) +class ExchangeRate(base.BudyBase): + name = appier.field(default=True) - base = appier.field( - index = True - ) + base = appier.field(index=True) - target = appier.field( - index = True - ) + target = appier.field(index=True) - rate = appier.field( - type = commons.Decimal, - index = True - ) + rate = appier.field(type=commons.Decimal, index=True) @classmethod def validate(cls): return super(ExchangeRate, cls).validate() + [ appier.not_null("base"), appier.not_empty("base"), - appier.not_null("target"), appier.not_empty("target"), - appier.not_null("rate"), - appier.gte("rate", 0.0) + appier.gte("rate", 0.0), ] @classmethod @@ -72,11 +61,7 @@ def list_names(cls): @classmethod def create_s(cls, base, target, rate): - exchange_rate = cls( - base = base, - target = target, - rate = rate - ) + exchange_rate = cls(base=base, target=target, rate=rate) exchange_rate.save() @classmethod @@ -86,50 +71,47 @@ def create_both_s(cls, base, target, rate): cls.create_s(target, base, rate_r) @classmethod - def convert(cls, value, base, target, reversed = False, rounder = round): + def convert(cls, value, base, target, reversed=False, rounder=round): from . import currency - if reversed: return cls.reverse(value, base, target, rounder = rounder) - exchange_rate = cls.get(base = base, target = target) + + if reversed: + return cls.reverse(value, base, target, rounder=rounder) + exchange_rate = cls.get(base=base, target=target) result = commons.Decimal(value) * exchange_rate.rate - return currency.Currency.round(result, target, rounder = rounder) + return currency.Currency.round(result, target, rounder=rounder) @classmethod - def reverse(cls, value, base, target, rounder = round): + def reverse(cls, value, base, target, rounder=round): from . import currency - exchange_rate = cls.get(base = target, target = base) + + exchange_rate = cls.get(base=target, target=base) result = commons.Decimal(value) * (commons.Decimal(1.0) / exchange_rate.rate) - return currency.Currency.round(result, target, rounder = rounder) + return currency.Currency.round(result, target, rounder=rounder) @classmethod def has_rate(cls, base, target): - exchange_rate = cls.get( - base = base, - target = target, - raise_e = False - ) - if not exchange_rate: return False - if not exchange_rate.rate: return False + exchange_rate = cls.get(base=base, target=target, raise_e=False) + if not exchange_rate: + return False + if not exchange_rate.rate: + return False return True @classmethod @appier.operation( - name = "Import CSV", - parameters = ( + name="Import CSV", + parameters=( ("CSV File", "file", "file"), - ("Empty source", "empty", bool, False) - ) + ("Empty source", "empty", bool, False), + ), ) def import_csv_s(cls, file, empty): - def callback(line): base, target, rate = line rate = float(rate) - exchange_rate = cls( - base = base, - target = target, - rate = rate - ) + exchange_rate = cls(base=base, target=target, rate=rate) exchange_rate.save() - if empty: cls.delete_c() + if empty: + cls.delete_c() cls._csv_import(file, callback) diff --git a/src/budy/models/group.py b/src/budy/models/group.py index 6c62cfd1..3fad6bb7 100644 --- a/src/budy/models/group.py +++ b/src/budy/models/group.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,56 +32,38 @@ from . import base -class Group(base.BudyBase): +class Group(base.BudyBase): name = appier.field( - index = True, - default = True, - observations = """The primary identifier of the group, can - be used as the textual representation of it""" + index=True, + default=True, + observations="""The primary identifier of the group, can + be used as the textual representation of it""", ) title = appier.field( - index = True, - observations = """Text value to be used in the representation - of the group, replaces the name visually""" + index=True, + observations="""Text value to be used in the representation + of the group, replaces the name visually""", ) - order = appier.field( - type = int, - index = True - ) + order = appier.field(type=int, index=True) - labels = appier.field( - type = list - ) + labels = appier.field(type=list) - image_url = appier.field( - index = True, - meta = "image_url", - description = "Image URL" - ) + image_url = appier.field(index=True, meta="image_url", description="Image URL") - new_in = appier.field( - type = bool - ) + new_in = appier.field(type=bool) - exclusive = appier.field( - type = bool - ) + exclusive = appier.field(type=bool) - images = appier.field( - type = appier.references( - "Media", - name = "id" - ) - ) + images = appier.field(type=appier.references("Media", name="id")) @classmethod def validate(cls): return super(Group, cls).validate() + [ appier.not_null("name"), - appier.not_empty("name") + appier.not_empty("name"), ] @classmethod @@ -94,9 +76,10 @@ def is_abstract(cls): @classmethod def ensure_s(cls, name): - group = cls.get(name = name, raise_e = False) - if group: return group - group = cls(name = name) + group = cls.get(name=name, raise_e=False) + if group: + return group + group = cls(name=name) group.save() return group @@ -110,94 +93,93 @@ def pre_save(self): self.update_label(self.exclusive, "exclusive") def build_images(self): - thumbnail = self.get_image(size = "thumbnail", order = 1) - thumbnail = thumbnail or self.get_image(size = "thumbnail") - image = self.get_image(size = "large", order = 1) - image = image or self.get_image(size = "large") + thumbnail = self.get_image(size="thumbnail", order=1) + thumbnail = thumbnail or self.get_image(size="thumbnail") + image = self.get_image(size="large", order=1) + image = image or self.get_image(size="large") self.thumbnail_url = thumbnail.get_url() if thumbnail else None self.image_url = image.get_url() if image else None - def get_image(self, size = None, order = None): + def get_image(self, size=None, order=None): for image in self.images: is_size = size == None or image.size == size - if not is_size: continue + if not is_size: + continue is_order = order == None or image.order == order - if not is_order: continue + if not is_order: + continue return image return None def update_label(self, value, name): if value: - if name in self.labels: return + if name in self.labels: + return self.labels.append(name) else: - if not name in self.labels: return + if not name in self.labels: + return self.labels.remove(name) @appier.operation( - name = "Add Image", - parameters = ( - ( - "Image", - "image", - appier.reference("Media", name = "id") - ), - ) + name="Add Image", + parameters=(("Image", "image", appier.reference("Media", name="id")),), ) def add_image_s(self, image): - if not image: return - if image in self.images: return + if not image: + return + if image in self.images: + return self.images.append(image) self.save() @appier.operation( - name = "Remove Image", - parameters = ( - ( - "Image", - "image", - appier.reference("Media", name = "id") - ), - ) + name="Remove Image", + parameters=(("Image", "image", appier.reference("Media", name="id")),), ) def remove_image_s(self, image): - if not image: return - if not image in self.images: return + if not image: + return + if not image in self.images: + return self.images.remove(image) self.save() @appier.operation( - name = "Upload Image", - parameters = ( + name="Upload Image", + parameters=( ("File", "file", "file"), ("Label", "label", str, "main"), - ("Size", "size", str, "large") - ) + ("Size", "size", str, "large"), + ), ) def upload_image_s(self, file, label, size): from . import media - if not file: return + + if not file: + return media = media.Media( - description = self.name, - label = label, - order = 1, - size = size, - file = appier.File(file) + description=self.name, + label=label, + order=1, + size=size, + file=appier.File(file), ) media.save() self.images.append(media) self.save() - @appier.view(name = "Products") + @appier.view(name="Products") def orders_v(self, *args, **kwargs): from . import product + cls = self.__class__ - plural = cls._underscore(plural = True) + plural = cls._underscore(plural=True) kwargs["sort"] = kwargs.get("sort", [("created", -1)]) - kwargs.update({plural: {"$all" : [self.id]}}) + kwargs.update({plural: {"$all": [self.id]}}) return appier.lazy_dict( - model = product.Product, - kwargs = kwargs, - entities = appier.lazy(lambda: product.Product.find(*args, **kwargs)), - page = appier.lazy(lambda: product.Product.paginate(*args, **kwargs)) + model=product.Product, + kwargs=kwargs, + entities=appier.lazy(lambda: product.Product.find(*args, **kwargs)), + page=appier.lazy(lambda: product.Product.paginate(*args, **kwargs)), ) diff --git a/src/budy/models/live_model.py b/src/budy/models/live_model.py index f99a18eb..df7b62f8 100644 --- a/src/budy/models/live_model.py +++ b/src/budy/models/live_model.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,8 +30,8 @@ from . import base -class LiveModel(base.BudyBase): +class LiveModel(base.BudyBase): @classmethod def list_names(cls): return ["id", "part", "material", "value"] diff --git a/src/budy/models/measurement.py b/src/budy/models/measurement.py index a9e9f966..aa2ba132 100644 --- a/src/budy/models/measurement.py +++ b/src/budy/models/measurement.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -35,85 +35,56 @@ from . import base from . import currency as _currency + class Measurement(base.BudyBase): + name = appier.field(index=True, default=True) - name = appier.field( - index = True, - default = True - ) + value = appier.field(type=int, index=True) - value = appier.field( - type = int, - index = True - ) - - value_s = appier.field( - index = True - ) + value_s = appier.field(index=True) weight = appier.field( - type = commons.Decimal, - index = True, - observations = """The weight of the current measurement in - a unit defined by convention (defined before-hand)""" + type=commons.Decimal, + index=True, + observations="""The weight of the current measurement in + a unit defined by convention (defined before-hand)""", ) - quantity_hand = appier.field( - type = commons.Decimal, - index = True - ) + quantity_hand = appier.field(type=commons.Decimal, index=True) - quantity_reserved = appier.field( - type = commons.Decimal, - index = True - ) + quantity_reserved = appier.field(type=commons.Decimal, index=True) price = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - observations = """Main retail price to be used for - a possible sale transaction of the measurement (includes taxes)""" + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + observations="""Main retail price to be used for + a possible sale transaction of the measurement (includes taxes)""", ) price_compare = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - observations = """The price that is going to be used - as the base for discount calculation purposes""" + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + observations="""The price that is going to be used + as the base for discount calculation purposes""", ) - taxes = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0) - ) + taxes = appier.field(type=commons.Decimal, index=True, initial=commons.Decimal(0.0)) - currency = appier.field( - index = True - ) + currency = appier.field(index=True) - product = appier.field( - type = appier.reference( - "Product", - name = "id" - ) - ) + product = appier.field(type=appier.reference("Product", name="id")) @classmethod def validate(cls): return super(Measurement, cls).validate() + [ appier.not_null("name"), appier.not_empty("name"), - appier.not_null("value"), - appier.gte("price", 0.0), - appier.gte("taxes", 0.0), - - appier.not_null("product") + appier.not_null("product"), ] @classmethod @@ -128,14 +99,14 @@ def order_name(cls): def from_omni( cls, merchandise, - sub_product = None, - inventory_line = None, - inventory_lines = None, - name = "size", - currency = "EUR", - strip = True, - path = True, - force = False + sub_product=None, + inventory_line=None, + inventory_lines=None, + name="size", + currency="EUR", + strip=True, + path=True, + force=False, ): from . import product @@ -157,20 +128,22 @@ def from_omni( # taking into account also the modification date of its inventory line if inventory_line: modify_date_line = inventory_line["modify_date"] - if modify_date_line > modify_date: modify_date = modify_date_line + if modify_date_line > modify_date: + modify_date = modify_date_line # tries to retrieve the parent product for this measurement using the # associated company product code as reference if there's no such parent # product then it's not possible to continue with the import operation _product = product.Product.get( - product_id = parent["company_product_code"], - raise_e = False + product_id=parent["company_product_code"], raise_e=False ) - if not _product: return None + if not _product: + return None # in case the discount at a merchandise level is not defined # then tries to use the one coming from the (parent) product - if discount == None: discount = _product.meta.get("discount", None) + if discount == None: + discount = _product.meta.get("discount", None) # creates the stocks list in case there are valid inventory lines being # passed on the current measurement update/creation @@ -187,15 +160,16 @@ def from_omni( functional_unit = inventory_line.get("functional_unit", None) is_valid = functional_unit and functional_unit.get("status") == 1 - if not is_valid: continue + if not is_valid: + continue stock_m = dict( - store_id = functional_unit["object_id"], - store_name = functional_unit["name"], - stock_on_hand = stock_on_hand, - stock_reserved = stock_reserved, - stock_in_transit = stock_in_transit, - retail_price = retail_price + store_id=functional_unit["object_id"], + store_name=functional_unit["name"], + stock_on_hand=stock_on_hand, + stock_reserved=stock_reserved, + stock_in_transit=stock_in_transit, + retail_price=retail_price, ) stocks.append(stock_m) @@ -211,19 +185,19 @@ def from_omni( # tries converts the value into an integer value, falling back # to the absolute hash value of it in case there's an error - try: value = int(value) - except ValueError: value = cls._hash(value) + try: + value = int(value) + except ValueError: + value = cls._hash(value) # tries to retrieve a measurement that is considered to be equivalent # to the one described by the associated subproduct in case it does # not exists creates a new instance that is going to be populate measurement = cls.get( - product = _product.id, - name = name, - value = value, - raise_e = False + product=_product.id, name=name, value=value, raise_e=False ) - if not measurement: measurement = cls() + if not measurement: + measurement = cls() measurement.name = name measurement.value = value @@ -234,14 +208,17 @@ def from_omni( measurement.product = _product meta = dict( - object_id = object_id, - company_product_code = company_product_code, - modify_date = modify_date, - discount = discount + object_id=object_id, + company_product_code=company_product_code, + modify_date=modify_date, + discount=discount, ) - if hasattr(measurement, "meta") and measurement.meta: measurement.meta.update(meta) - else: measurement.meta = meta - if not stocks == None: measurement.meta["stocks"] = stocks + if hasattr(measurement, "meta") and measurement.meta: + measurement.meta.update(meta) + else: + measurement.meta = meta + if not stocks == None: + measurement.meta["stocks"] = stocks if "stock_on_hand" in merchandise or force: measurement.quantity_hand = merchandise.get("stock_on_hand", 0.0) @@ -266,14 +243,22 @@ def from_omni( # in case all of the required "original" financial information (prices) # is available then the price, taxes and price compare are calculated if "retail_price" in measurement.meta and "untaxed_price" in measurement.meta: - untaxed_price = _currency.Currency.round( - measurement.meta["untaxed_price"] * ((100.0 - discount) / 100.0), - currency - ) if discount else measurement.meta["untaxed_price"] - measurement.price = _currency.Currency.round( - measurement.meta["retail_price"] * ((100.0 - discount) / 100.0), - currency - ) if discount else measurement.meta["retail_price"] + untaxed_price = ( + _currency.Currency.round( + measurement.meta["untaxed_price"] * ((100.0 - discount) / 100.0), + currency, + ) + if discount + else measurement.meta["untaxed_price"] + ) + measurement.price = ( + _currency.Currency.round( + measurement.meta["retail_price"] * ((100.0 - discount) / 100.0), + currency, + ) + if discount + else measurement.meta["retail_price"] + ) measurement.taxes = measurement.price - untaxed_price if not measurement.price_compare and discount: measurement.price_compare = measurement.meta["retail_price"] @@ -283,49 +268,44 @@ def from_omni( return measurement @classmethod - def _hash(cls, value, max_size = 8): + def _hash(cls, value, max_size=8): counter = 0 for index in range(len(value)): value_i = appier.legacy.ord(value[index]) counter += value_i * pow(256, index) - if not max_size: return counter + if not max_size: + return counter modulus = pow(256, max_size) counter = counter % modulus return counter def pre_delete(self): base.BudyBase.pre_delete(self) - if not self.product: return - if not hasattr(self.product, "measurements") : return - if not self in self.product.measurements: return + if not self.product: + return + if not hasattr(self.product, "measurements"): + return + if not self in self.product.measurements: + return self.product.measurements.remove(self) self.product.save() - def get_price( - self, - currency = None, - country = None, - attributes = None - ): + def get_price(self, currency=None, country=None, attributes=None): return self.price - def get_taxes( - self, - currency = None, - country = None, - attributes = None - ): + def get_taxes(self, currency=None, country=None, attributes=None): return self.taxes - def get_currency(self, currency = None): + def get_currency(self, currency=None): return currency - def get_size(self, currency = None, country = None, attributes = None): + def get_size(self, currency=None, country=None, attributes=None): return None, None @property def product_id(self): - if self._product_id_meta: return self._product_id_meta + if self._product_id_meta: + return self._product_id_meta return None @property @@ -338,13 +318,16 @@ def quantity(self): @property def discount(self): - if not self.price: return commons.Decimal(0.0) - if not self.price_compare: return commons.Decimal(0.0) + if not self.price: + return commons.Decimal(0.0) + if not self.price_compare: + return commons.Decimal(0.0) return self.price_compare - self.price @property def discount_percent(self): - if not self.discount: return commons.Decimal(0.0) + if not self.discount: + return commons.Decimal(0.0) return self.discount / self.price_compare * commons.Decimal(100.0) @property @@ -359,23 +342,24 @@ def is_discountable(self): def is_price_provided(self): return False - @appier.operation(name = "Fix") + @appier.operation(name="Fix") def fix_s(self): - if not self.exists(): return + if not self.exists(): + return self._fix_value_s() - @appier.operation(name = "Duplicate", factory = True) + @appier.operation(name="Duplicate", factory=True) def duplicate_s(self): cls = self.__class__ measurement = cls( - product = self.product.id, - name = self.name, - value = self.value, - value_s = self.value_s, - quantity_hand = self.quantity_hand, - price = self.price, - currency = self.currency, - meta = self.meta + product=self.product.id, + name=self.name, + value=self.value, + value_s=self.value_s, + quantity_hand=self.quantity_hand, + price=self.price, + currency=self.currency, + meta=self.meta, ) measurement.save() self.product.measurements.append(measurement) @@ -384,16 +368,20 @@ def duplicate_s(self): @property def _product_id_meta(self): - if not self.meta: return None + if not self.meta: + return None return self.meta.get("company_product_code", None) def _fix_value_s(self): cls = self.__class__ - try: self.value = int(self.value) - except ValueError: self.value = self._hash(self.value) + try: + self.value = int(self.value) + except ValueError: + self.value = self._hash(self.value) self.save() def _fix_invalid_s(self): is_valid = hasattr(self, "parent") and not self.parent == None - if is_valid: return + if is_valid: + return self.delete() diff --git a/src/budy/models/media.py b/src/budy/models/media.py index 87386d57..0502d148 100644 --- a/src/budy/models/media.py +++ b/src/budy/models/media.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -40,44 +40,28 @@ BASE_URL = "http://localhost:8080/" -class Media(base.BudyBase): - label = appier.field( - index = True - ) +class Media(base.BudyBase): + label = appier.field(index=True) - order = appier.field( - type = int, - index = True - ) + order = appier.field(type=int, index=True) - size = appier.field( - index = True - ) + size = appier.field(index=True) - unique = appier.field( - index = True, - safe = True - ) + unique = appier.field(index=True, safe=True) - file = appier.field( - type = appier.File, - private = True - ) + file = appier.field(type=appier.File, private=True) @classmethod def validate(cls): return super(Media, cls).validate() + [ appier.not_null("description"), appier.not_empty("description"), - appier.not_null("label"), appier.not_empty("label"), - appier.not_null("order"), - appier.not_null("file"), - appier.not_empty("file") + appier.not_empty("file"), ] @classmethod @@ -94,38 +78,43 @@ def is_visible(cls): @classmethod @appier.operation( - name = "Import Media", - parameters = ( + name="Import Media", + parameters=( ("Media File", "file", "file"), - ("Empty source", "empty", bool, False) - ) + ("Empty source", "empty", bool, False), + ), ) - def import_media_s(cls, file, empty, strict = False): + def import_media_s(cls, file, empty, strict=False): _file_name, mime_type, data = file is_zip = mime_type in ("application/zip", "application/octet-stream") if not is_zip and strict: - raise appier.OperationalError( - message = "Invalid MIME type '%s'" % mime_type - ) + raise appier.OperationalError(message="Invalid MIME type '%s'" % mime_type) buffer = appier.legacy.BytesIO(data) - file = zipfile.ZipFile(buffer, mode = "r") + file = zipfile.ZipFile(buffer, mode="r") target = tempfile.mkdtemp() - try: file.extractall(target) - finally: file.close() + try: + file.extractall(target) + finally: + file.close() - if empty: cls.delete_c() + if empty: + cls.delete_c() names = os.listdir(target) for name in names: path = os.path.join(target, name) is_dir = os.path.isdir(path) - if is_dir: image_names = os.listdir(path) - else: path = target; image_names = [name] + if is_dir: + image_names = os.listdir(path) + else: + path = target + image_names = [name] for image_name in image_names: base, extension = os.path.splitext(image_name) - if not extension in (".png", ".jpeg", ".jpg"): continue + if not extension in (".png", ".jpeg", ".jpg"): + continue - content_type, _encoding = mimetypes.guess_type(image_name, strict = False) + content_type, _encoding = mimetypes.guess_type(image_name, strict=False) product_id = base label = "undefined" @@ -133,29 +122,36 @@ def import_media_s(cls, file, empty, strict = False): size = "large" base_s = base.split("_") - if len(base_s) >= 1: product_id = base_s[0] - if len(base_s) >= 2: order = int(base_s[1]) - if len(base_s) >= 3: label = base_s[2] - if len(base_s) >= 4: size = base_s[3] + if len(base_s) >= 1: + product_id = base_s[0] + if len(base_s) >= 2: + order = int(base_s[1]) + if len(base_s) >= 3: + label = base_s[2] + if len(base_s) >= 4: + size = base_s[3] description = "%s_%s_%s" % (product_id, label, size) image_path = os.path.join(path, image_name) image_file = open(image_path, "rb") - try: image_data = image_file.read() - finally: image_file.close() + try: + image_data = image_file.read() + finally: + image_file.close() media = Media( - description = description, - label = label, - order = order, - size = size, - file = appier.File((product_id, content_type, image_data)) + description=description, + label=label, + order=order, + size=size, + file=appier.File((product_id, content_type, image_data)), ) media.save() - _product = product.Product.get(product_id = product_id, raise_e = False) - if not _product: continue + _product = product.Product.get(product_id=product_id, raise_e=False) + if not _product: + continue _product.images.append(media) _product.save() @@ -167,96 +163,82 @@ def _build(cls, model, map): if id: model["url"] = cls._get_url(id) for format in ("png", "jpeg", "webp"): - model["url_" + format] = cls._get_url(id, format = format) + model["url_" + format] = cls._get_url(id, format=format) @classmethod def _plural(cls): return "Media" @classmethod - def _get_url(cls, id, format = None, absolute = True): + def _get_url(cls, id, format=None, absolute=True): app = appier.get_app() if format: return app.url_for( "media_api.data_format", - id = id, - format = format, - prefix = "/", - absolute = absolute + id=id, + format=format, + prefix="/", + absolute=absolute, ) else: - return app.url_for( - "media_api.data", - id = id, - prefix = "/", - absolute = absolute - ) + return app.url_for("media_api.data", id=id, prefix="/", absolute=absolute) - def get_url(self, format = None): - return self.__class__._get_url(self.id, format = format) + def get_url(self, format=None): + return self.__class__._get_url(self.id, format=format) - def convert_image(self, format, background = None, **kwargs): + def convert_image(self, format, background=None, **kwargs): import PIL.Image + kwargs = dict(kwargs) buffer = appier.legacy.BytesIO() image = PIL.Image.open(appier.legacy.BytesIO(self.file.data)) - if format in ("jpeg",) and not background: background = "ffffff" + if format in ("jpeg",) and not background: + background = "ffffff" if background: image_background = PIL.Image.new( - "RGB", - (image.width, image.height), - color = "#" + background + "RGB", (image.width, image.height), color="#" + background ) - if image.mode == "RGBA": image_background.paste(image, mask = image) - else: image_background.paste(image) + if image.mode == "RGBA": + image_background.paste(image, mask=image) + else: + image_background.paste(image) image = image_background - image.save(buffer, format = format, **kwargs) + image.save(buffer, format=format, **kwargs) return buffer @appier.operation( - name = "Generate Thumbnail", - parameters = ( + name="Generate Thumbnail", + parameters=( ("Width", "width", int), ("Height", "height", int), ("Format", "format", str, "png"), - ("Suffix", "suffix", str, "thumbnail") + ("Suffix", "suffix", str, "thumbnail"), ), - factory = True + factory=True, ) - def thumbnail_s( - self, - width = None, - height = None, - format = "png", - suffix = "thumbnail" - ): + def thumbnail_s(self, width=None, height=None, format="png", suffix="thumbnail"): cls = self.__class__ - media = self.reload(rules = False) + media = self.reload(rules=False) builder = appier.image( - width = width or height, - height = height or width, - format = format + width=width or height, height=height or width, format=format ) image = builder(media.file) data = image.resize() name = "%s.%s" % (suffix, format) - mime, _encoding = mimetypes.guess_type(name, strict = False) + mime, _encoding = mimetypes.guess_type(name, strict=False) thumbnail = cls( - description = media.description, - label = suffix, - order = media.order, - size = suffix, - unique = "%s-%s" % (media.unique, suffix), - file = appier.File((name, mime, data)) + description=media.description, + label=suffix, + order=media.order, + size=suffix, + unique="%s-%s" % (media.unique, suffix), + file=appier.File((name, mime, data)), ) thumbnail.save() return thumbnail - @appier.link(name = "View") - def view_url(self, absolute = False): + @appier.link(name="View") + def view_url(self, absolute=False): return self.owner.url_for( - "media_api.data", - id = self.id, - prefix = "/", - absolute = absolute + "media_api.data", id=self.id, prefix="/", absolute=absolute ) diff --git a/src/budy/models/order.py b/src/budy/models/order.py index 281ada5a..ff2144b8 100644 --- a/src/budy/models/order.py +++ b/src/budy/models/order.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -45,203 +45,127 @@ from . import currency from . import order_line -COUNTRIES_MAP = dict( - ES = "Spain", - PT = "Portugal" -) +COUNTRIES_MAP = dict(ES="Spain", PT="Portugal") """ Map that associates ISO alpha-2 country codes with their corresponding full text version to be used in Omni """ -GENDERS_MAP = dict( - Male = 1, - Female = 2 -) +GENDERS_MAP = dict(Male=1, Female=2) """ Map that associates the internal string based gender enumeration with the Omni's numeric one """ -class Order(bundle.Bundle): +class Order(bundle.Bundle): STATUS_S = dict( - created = "created", - waiting_payment = "waiting_payment", - paid = "paid", - invoiced = "invoiced", - sent = "sent", - received = "received", - returned = "returned", - canceled = "canceled", - completed = "completed" + created="created", + waiting_payment="waiting_payment", + paid="paid", + invoiced="invoiced", + sent="sent", + received="received", + returned="returned", + canceled="canceled", + completed="completed", ) STATUS_C = dict( - created = "grey", - waiting_payment = "orange", - paid = "purple", - invoiced = "orange", - sent = "blue", - received = "green", - returned = "red", - canceled = "red", - completed = "green" + created="grey", + waiting_payment="orange", + paid="purple", + invoiced="orange", + sent="blue", + received="green", + returned="red", + canceled="red", + completed="green", ) status = appier.field( - initial = "created", - index = True, - safe = True, - meta = "enum", - enum = STATUS_S, - colors = STATUS_C + initial="created", + index=True, + safe=True, + meta="enum", + enum=STATUS_S, + colors=STATUS_C, ) - reference = appier.field( - index = "hashed", - default = True, - safe = True - ) + reference = appier.field(index="hashed", default=True, safe=True) - reference_f = appier.field( - index = "hashed", - safe = True - ) + reference_f = appier.field(index="hashed", safe=True) - paid = appier.field( - type = bool, - index = "hashed", - initial = False, - safe = True - ) + paid = appier.field(type=bool, index="hashed", initial=False, safe=True) - date = appier.field( - type = int, - index = True, - safe = True, - meta = "datetime" - ) + date = appier.field(type=int, index=True, safe=True, meta="datetime") - delivery_date = appier.field( - type = int, - index = True, - safe = True, - meta = "datetime" - ) + delivery_date = appier.field(type=int, index=True, safe=True, meta="datetime") email = appier.field( - index = "hashed", - safe = True, - observations = """The email of the entity owner of the current - order, typically a personal email address of the buyer""" + index="hashed", + safe=True, + observations="""The email of the entity owner of the current + order, typically a personal email address of the buyer""", ) - gift_wrap = appier.field( - type = bool, - index = "hashed", - initial = False, - safe = True - ) + gift_wrap = appier.field(type=bool, index="hashed", initial=False, safe=True) tracking_number = appier.field( - index = "hashed", + index="hashed", ) - tracking_url = appier.field( - index = "hashed", - meta = "url", - description = "Tracking URL" - ) + tracking_url = appier.field(index="hashed", meta="url", description="Tracking URL") - payment_data = appier.field( - type = dict - ) + payment_data = appier.field(type=dict) - cancel_data = appier.field( - type = dict - ) + cancel_data = appier.field(type=dict) - discount_data = appier.field( - type = dict - ) + discount_data = appier.field(type=dict) - notifications = appier.field( - type = list, - initial = [], - safe = True - ) + notifications = appier.field(type=list, initial=[], safe=True) discount_voucher = appier.field( - type = commons.Decimal, - index = "hashed", - initial = commons.Decimal(0.0), - safe = True, - observations = """The discount value that is related with + type=commons.Decimal, + index="hashed", + initial=commons.Decimal(0.0), + safe=True, + observations="""The discount value that is related with the vouchers to be redeemed (or already redeemed) for the - current order""" + current order""", ) discount_used = appier.field( - type = commons.Decimal, - index = "hashed", - initial = commons.Decimal(0.0), - safe = True, - observations = """The total value already used via the - redeeming of the vouchers associated with the current order""" + type=commons.Decimal, + index="hashed", + initial=commons.Decimal(0.0), + safe=True, + observations="""The total value already used via the + redeeming of the vouchers associated with the current order""", ) inventory_decremented = appier.field( - type = bool, - observations = """Controls if the inventory stock levels - have been decrement according to current order's lines""" + type=bool, + observations="""Controls if the inventory stock levels + have been decrement according to current order's lines""", ) - lines = appier.field( - type = appier.references( - "OrderLine", - name = "id" - ), - eager = True - ) + lines = appier.field(type=appier.references("OrderLine", name="id"), eager=True) - vouchers = appier.field( - type = appier.references( - "Voucher", - name = "id" - ), - eager = True - ) + vouchers = appier.field(type=appier.references("Voucher", name="id"), eager=True) - account = appier.field( - type = appier.reference( - "BudyAccount", - name = "id" - ), - eager = True - ) + account = appier.field(type=appier.reference("BudyAccount", name="id"), eager=True) store = appier.field( - type = appier.reference( - "Store", - name = "id" - ), - eager = True, - observations = """If set defines the store in which + type=appier.reference("Store", name="id"), + eager=True, + observations="""If set defines the store in which the order should be delivered, in principle the shipping - address should be set to the address of the store""" + address should be set to the address of the store""", ) shipping_address = appier.field( - type = appier.reference( - "Address", - name = "id" - ), - eager = True + type=appier.reference("Address", name="id"), eager=True ) billing_address = appier.field( - type = appier.reference( - "Address", - name = "id" - ), - eager = True + type=appier.reference("Address", name="id"), eager=True ) @classmethod @@ -263,122 +187,106 @@ def is_snapshot(cls): @classmethod @appier.link( - name = "Export Complex", - parameters = ( - ("Start ID", "start", int), - ("End ID", "end", int) - ) + name="Export Complex", + parameters=(("Start ID", "start", int), ("End ID", "end", int)), ) - def complex_csv_url(cls, start = None, end = None, absolute = False): + def complex_csv_url(cls, start=None, end=None, absolute=False): return appier.get_app().url_for( - "order_api.complex_csv", - start = start, - end = end, - absolute = absolute + "order_api.complex_csv", start=start, end=end, absolute=absolute ) @classmethod @appier.link( - name = "Export CTT", - parameters = ( - ("Start ID", "start", int), - ("End ID", "end", int) - ) + name="Export CTT", + parameters=(("Start ID", "start", int), ("End ID", "end", int)), ) - def ctt_csv_url(cls, start = None, end = None, absolute = False): + def ctt_csv_url(cls, start=None, end=None, absolute=False): return appier.get_app().url_for( - "order_api.ctt_csv", - start = start, - end = end, - absolute = absolute + "order_api.ctt_csv", start=start, end=end, absolute=absolute ) @classmethod - @appier.link(name = "Export CTT Context", context = True) - def ctt_csv_context_url(cls, view = None, context = None, absolute = False): + @appier.link(name="Export CTT Context", context=True) + def ctt_csv_context_url(cls, view=None, context=None, absolute=False): return appier.get_app().url_for( - "order_api.ctt_csv", - view = view, - context = context, - absolute = absolute + "order_api.ctt_csv", view=view, context=context, absolute=absolute ) @classmethod @appier.operation( - name = "Import CTT", - parameters = ( + name="Import CTT", + parameters=( ("CSV File", "file", "file"), - ("Base URL", "base_url", str, "http://www.ctt.pt/feapl_2/app/open/cttexpresso/objectSearch/objectSearch.jspx?objects=%s"), - ("Force", "force", bool, False) - ) + ( + "Base URL", + "base_url", + str, + "http://www.ctt.pt/feapl_2/app/open/cttexpresso/objectSearch/objectSearch.jspx?objects=%s", + ), + ("Force", "force", bool, False), + ), ) def import_ctt_csv_s(cls, file, base_url, force): - def callback(line): - _date,\ - _ctt_ref,\ - _ctt_id,\ - reference,\ - tracking_number,\ - _quantity,\ - _customer,\ - _weight,\ - _price = line - - order = cls.get(reference = reference, raise_e = False) - if not order: return - if not force and order.tracking_number: return - if not force and order.tracking_url: return - - order.set_tracking_s( + ( + _date, + _ctt_ref, + _ctt_id, + reference, tracking_number, - base_url % tracking_number - ) + _quantity, + _customer, + _weight, + _price, + ) = line - cls._csv_import( - file, - callback, - header = False, - delimiter = "+", - encoding = "Cp1252" - ) + order = cls.get(reference=reference, raise_e=False) + if not order: + return + if not force and order.tracking_number: + return + if not force and order.tracking_url: + return + + order.set_tracking_s(tracking_number, base_url % tracking_number) + + cls._csv_import(file, callback, header=False, delimiter="+", encoding="Cp1252") @classmethod @appier.operation( - name = "Generate Dummy", - parameters = ( + name="Generate Dummy", + parameters=( ("Product Description", "short_description", str, "product"), ("Product Gender", "gender", str, "Male"), ("Product Price", "price", float, 10.0), ("Quantity", "quantity", float, 1.0), - ("Set Account", "set_account", bool, True) + ("Set Account", "set_account", bool, True), ), - devel = True + devel=True, ) def generate_dummy_s( cls, - short_description = "product", - gender = "Male", - price = 10.0, - quantity = 1.0, - set_account = True + short_description="product", + gender="Male", + price=10.0, + quantity=1.0, + set_account=True, ): _product = product.Product( - short_description = short_description, - gender = gender, - price = price + short_description=short_description, gender=gender, price=price ) _product.save() order = cls() order.save() - _order_line = order_line.OrderLine(quantity = quantity) + _order_line = order_line.OrderLine(quantity=quantity) _order_line.product = _product _order_line.save() order.add_line_s(_order_line) - if not set_account: return + if not set_account: + return from . import account @@ -386,10 +294,10 @@ def generate_dummy_s( email = "%s@account.com" % username _account = account.BudyAccount( - username = username, - email = email, - password = "password", - password_confirm = "password" + username=username, + email=email, + password="password", + password_confirm="password", ) _account.save() @@ -397,68 +305,78 @@ def generate_dummy_s( @classmethod @appier.operation( - name = "Multiple Dummy", - parameters = ( - ("Quantity", "quantity", int, 10), - ), - devel = True + name="Multiple Dummy", + parameters=(("Quantity", "quantity", int, 10),), + devel=True, ) def multiple_dummy_s(cls, quantity): - for _index in range(quantity): cls.generate_dummy_s() + for _index in range(quantity): + cls.generate_dummy_s() @classmethod - @appier.view(name = "Paid") + @appier.view(name="Paid") def paid_v(cls, *args, **kwargs): kwargs["sort"] = kwargs.get("sort", [("id", -1)]) - kwargs.update(paid = True) + kwargs.update(paid=True) return appier.lazy_dict( - model = cls, - kwargs = kwargs, - entities = appier.lazy(lambda: cls.find(*args, **kwargs)), - page = appier.lazy(lambda: cls.paginate(*args, **kwargs)) + model=cls, + kwargs=kwargs, + entities=appier.lazy(lambda: cls.find(*args, **kwargs)), + page=appier.lazy(lambda: cls.paginate(*args, **kwargs)), ) @classmethod - @appier.view( - name = "Status", - parameters = ( - ("Status", "status", str, "paid"), - ) - ) + @appier.view(name="Status", parameters=(("Status", "status", str, "paid"),)) def status_v(cls, status, *args, **kwargs): kwargs["sort"] = kwargs.get("sort", [("id", -1)]) - kwargs.update(status = status) + kwargs.update(status=status) return appier.lazy_dict( - model = cls, - kwargs = kwargs, - entities = appier.lazy(lambda: cls.find(*args, **kwargs)), - page = appier.lazy(lambda: cls.paginate(*args, **kwargs)) + model=cls, + kwargs=kwargs, + entities=appier.lazy(lambda: cls.find(*args, **kwargs)), + page=appier.lazy(lambda: cls.paginate(*args, **kwargs)), ) @classmethod - @appier.view(name = "To Store") + @appier.view(name="To Store") def to_store_v(cls, *args, **kwargs): kwargs["sort"] = kwargs.get("sort", [("id", -1)]) - kwargs.update(store = {"$ne" : None}) + kwargs.update(store={"$ne": None}) return appier.lazy_dict( - model = cls, - kwargs = kwargs, - entities = appier.lazy(lambda: cls.find(*args, **kwargs)), - page = appier.lazy(lambda: cls.paginate(*args, **kwargs)), - names = ["reference", "store", "total", "currency", "email", "created", "status"] + model=cls, + kwargs=kwargs, + entities=appier.lazy(lambda: cls.find(*args, **kwargs)), + page=appier.lazy(lambda: cls.paginate(*args, **kwargs)), + names=[ + "reference", + "store", + "total", + "currency", + "email", + "created", + "status", + ], ) @classmethod - @appier.view(name = "To Store Paid") + @appier.view(name="To Store Paid") def to_store_paid_v(cls, *args, **kwargs): kwargs["sort"] = kwargs.get("sort", [("id", -1)]) - kwargs.update(store = {"$ne" : None}, paid = True) + kwargs.update(store={"$ne": None}, paid=True) return appier.lazy_dict( - model = cls, - kwargs = kwargs, - entities = appier.lazy(lambda: cls.find(*args, **kwargs)), - page = appier.lazy(lambda: cls.paginate(*args, **kwargs)), - names = ["reference", "store", "total", "currency", "email", "created", "status"] + model=cls, + kwargs=kwargs, + entities=appier.lazy(lambda: cls.find(*args, **kwargs)), + page=appier.lazy(lambda: cls.paginate(*args, **kwargs)), + names=[ + "reference", + "store", + "total", + "currency", + "email", + "created", + "status", + ], ) @classmethod @@ -472,11 +390,7 @@ def _pmethods(cls): @classmethod def _pmethods_stripe(cls): - return ( - "visa", - "mastercard", - "american_express" - ) + return ("visa", "mastercard", "american_express") @classmethod def _pmethods_easypay(cls): @@ -492,20 +406,26 @@ def _pmethods_stripe_sca(cls): @classmethod def _get_api_stripe(cls): - try: import stripe - except ImportError: return None + try: + import stripe + except ImportError: + return None return stripe.API.singleton() @classmethod def _get_api_easypay(cls): - try: import easypay - except ImportError: return None - return easypay.ShelveAPI.singleton(scallback = cls._on_api_easypay) + try: + import easypay + except ImportError: + return None + return easypay.ShelveAPI.singleton(scallback=cls._on_api_easypay) @classmethod def _get_api_paypal(cls): - try: import paypal - except ImportError: return None + try: + import paypal + except ImportError: + return None return paypal.API.singleton() @classmethod @@ -517,22 +437,25 @@ def _on_api_easypay(cls, api): @classmethod def _on_paid_easypay(cls, reference, details): identifier = reference["identifier"] - order = cls.get(key = identifier, raise_e = False) - if order.is_payable(): order.end_pay_s(notify = True) + order = cls.get(key=identifier, raise_e=False) + if order.is_payable(): + order.end_pay_s(notify=True) @classmethod def _on_canceled_easypay(cls, reference): identifier = reference["identifier"] - order = cls.get(key = identifier, raise_e = False) - order.cancel_s(notify = True) + order = cls.get(key=identifier, raise_e=False) + order.cancel_s(notify=True) def pre_validate(self): bundle.Bundle.pre_validate(self) - if self.is_open(): self.try_valid() + if self.is_open(): + self.try_valid() def pre_delete(self): bundle.Bundle.pre_delete(self) - for line in self.lines: line.delete() + for line in self.lines: + line.delete() def post_create(self): bundle.Bundle.post_create(self) @@ -546,7 +469,8 @@ def is_valid(self): # in case the current status of the current order is the # first one (created) the order is considered to be open # and so the validation process must occur - if self.is_open(): return bundle.Bundle.is_valid(self) + if self.is_open(): + return bundle.Bundle.is_valid(self) # returns the true value on all other cases as the order lines # are considered to be valid at all times and don't require @@ -554,10 +478,12 @@ def is_valid(self): return True def build_discount(self): - join = appier.conf("BUDY_JOIN_DISCOUNT", True, cast = bool) + join = appier.conf("BUDY_JOIN_DISCOUNT", True, cast=bool) base_discount = bundle.Bundle.build_discount(self) - if join: return base_discount + self.discount_voucher - if self.discount_voucher > base_discount: return self.discount_voucher + if join: + return base_discount + self.discount_voucher + if self.discount_voucher > base_discount: + return self.discount_voucher return base_discount def set_account_s(self, account): @@ -568,17 +494,18 @@ def set_account_s(self, account): self.save() def add_voucher_s(self, voucher): - join = appier.conf("BUDY_JOIN_DISCOUNT", True, cast = bool) - appier.verify(voucher.is_valid(currency = self.currency)) - discount = voucher.discount(self.discountable, currency = self.currency) + join = appier.conf("BUDY_JOIN_DISCOUNT", True, cast=bool) + appier.verify(voucher.is_valid(currency=self.currency)) + discount = voucher.discount(self.discountable, currency=self.currency) base_discount = bundle.Bundle.build_discount(self) - if not join and discount <= base_discount: return + if not join and discount <= base_discount: + return self.discount_voucher += discount self.vouchers.append(voucher) self.save() def set_voucher_s(self, voucher): - appier.verify(voucher.is_valid(currency = self.currency)) + appier.verify(voucher.is_valid(currency=self.currency)) self.empty_vouchers_s() self.add_voucher_s(voucher) @@ -590,52 +517,54 @@ def empty_vouchers_s(self): def refresh_vouchers_s(self): vouchers = self.vouchers self.empty_vouchers_s() - for voucher in vouchers: self.add_voucher_s(voucher) + for voucher in vouchers: + self.add_voucher_s(voucher) def set_meta_s(self, name, value): self.meta[name] = value self.save() def refresh_s(self, *args, **kwargs): - if self.paid: return + if self.paid: + return refreshed = bundle.Bundle.refresh_s(self, *args, **kwargs) - if refreshed: self.refresh_vouchers_s() + if refreshed: + self.refresh_vouchers_s() - def wait_payment_s(self, notify = False): + def wait_payment_s(self, notify=False): self.verify_waiting_payment() self.mark_waiting_payment_s() - if notify: self.notify_s() + if notify: + self.notify_s() def pay_s( self, - payment_data = None, - payment_function = None, - vouchers = True, - strict = True, - notify = False, - ensure_waiting = True + payment_data=None, + payment_function=None, + vouchers=True, + strict=True, + notify=False, + ensure_waiting=True, ): payment_data = payment_data or dict() - if ensure_waiting: self.ensure_waiting_s() + if ensure_waiting: + self.ensure_waiting_s() self.verify_paid() result = self._pay( - payment_data, - payment_function = payment_function, - strict = strict + payment_data, payment_function=payment_function, strict=strict ) confirmed = result == True self.save() - if vouchers: self.use_vouchers_s() - if confirmed: self.end_pay_s() - if notify: self.notify_s() + if vouchers: + self.use_vouchers_s() + if confirmed: + self.end_pay_s() + if notify: + self.notify_s() return result def end_pay_s( - self, - payment_data = None, - payment_function = None, - strict = False, - notify = False + self, payment_data=None, payment_function=None, strict=False, notify=False ): # joins the payment data passed as parameter and then # one currently stored as part of the order, these @@ -648,15 +577,14 @@ def end_pay_s( # a concrete operation has been executed or an invalid # value in case nothing has been done result = self._end_pay( - payment_data, - payment_function = payment_function, - strict = strict + payment_data, payment_function=payment_function, strict=strict ) # marks the current order as completely paid and in case # the notify flag is set notifies the event handlers self.mark_paid_s() - if notify: self.notify_s() + if notify: + self.notify_s() # returns the result value from the end pay operation to # the caller method (for post-processing) @@ -664,25 +592,25 @@ def end_pay_s( def cancel_s( self, - cancel_data = None, - cancel_function = None, - vouchers = True, - strict = False, - notify = False + cancel_data=None, + cancel_function=None, + vouchers=True, + strict=False, + notify=False, ): cancel_data = cancel_data or dict() cancel_data.update(self.cancel_data) result = self._cancel( - cancel_data, - cancel_function = cancel_function, - strict = strict + cancel_data, cancel_function=cancel_function, strict=strict ) self.mark_canceled_s() - if vouchers: self.disuse_vouchers_s() - if notify: self.notify_s() + if vouchers: + self.disuse_vouchers_s() + if notify: + self.notify_s() return result - def use_vouchers_s(self, reset = True): + def use_vouchers_s(self, reset=True): """ Runs the use/redeem operation on the vouchers currently associated with the order, note that in case the reset flag is set the values @@ -707,7 +635,8 @@ def use_vouchers_s(self, reset = True): # value is already zero discount = self.calculate_discount() pending = discount - self.discount_base - self.discount_used - if pending <= 0.0: return + if pending <= 0.0: + return # iterates over the complete set of vouchers currently associated # with the order (both value and percentage) to try to retrieve @@ -715,16 +644,14 @@ def use_vouchers_s(self, reset = True): for voucher in self.vouchers: # in case there's no more discount pending/allowed to be # applied breaks the current loop (no more usage allowed) - if pending == 0.0: break + if pending == 0.0: + break # retrieves the "possible" discount value from the voucher # according to the order's currency (this value may be calculated # as a percentage of the discountable value or a fixed value # depending on the nature of the voucher, value vs percentage) - discount = voucher.discount( - self.discountable, - currency = self.currency - ) + discount = voucher.discount(self.discountable, currency=self.currency) # determines the concrete amount of discount to be used by # comparing it with the pending value (lowest wins) @@ -733,7 +660,7 @@ def use_vouchers_s(self, reset = True): # runs the concrete voucher usage operation taking into account # the "target" discount amount and the currency and decrements # the pending value by the amount one - voucher.use_s(amount, currency = self.currency) + voucher.use_s(amount, currency=self.currency) pending -= commons.Decimal(amount) # updates the discount used and the discount data value according @@ -746,7 +673,7 @@ def use_vouchers_s(self, reset = True): appier.verify(pending == 0.0) self.save() - def disuse_vouchers_s(self, force = False): + def disuse_vouchers_s(self, force=False): """ Disuses the complete set of voucher associated with the current order, note that only vouchers set in the discount data will be @@ -761,86 +688,97 @@ def disuse_vouchers_s(self, force = False): # in case the order is already paid and the force flag is not # set the disuse of the voucher is skipped, otherwise it would # "return vouchers" for an already paid order - if self.paid and not force: return + if self.paid and not force: + return # in case the discount data is not valid there's no voucher # value to be reverted/discounted, should return immediately - if not self.discount_data: return + if not self.discount_data: + return # iterates over the complete set of vouchers and associated amount # to disuse the associated amount in the related vouchers for voucher_id, amount in appier.legacy.items(self.discount_data): voucher_id = int(voucher_id) - _voucher = voucher.Voucher.get(id = voucher_id) - _voucher.disuse_s(amount, currency = self.currency) + _voucher = voucher.Voucher.get(id=voucher_id) + _voucher.disuse_s(amount, currency=self.currency) def ensure_waiting_s(self): - if not self.status == "created": return + if not self.status == "created": + return self.mark_waiting_payment_s() def close_lines_s(self): - for line in self.lines: line.close_s() + for line in self.lines: + line.close_s() - def get_paypal(self, return_url = None, cancel_url = None): + def get_paypal(self, return_url=None, cancel_url=None): items = [] for line in self.lines: items.append( dict( - name = line.product.short_description, - price = currency.Currency.format(line.price, line.currency), - currency = line.currency, - quantity = line.quantity + name=line.product.short_description, + price=currency.Currency.format(line.price, line.currency), + currency=line.currency, + quantity=line.quantity, ) ) - if self.discount: items.append( - dict( - name = "Discount", - price = currency.Currency.format(self.discount * -1, self.currency), - currency = self.currency, - quantity = 1 + if self.discount: + items.append( + dict( + name="Discount", + price=currency.Currency.format(self.discount * -1, self.currency), + currency=self.currency, + quantity=1, + ) ) - ) transaction = dict( - item_list = dict( - items = items, - shipping_address = dict( - recipient_name = self.shipping_address.full_name, - line1 = self.shipping_address.address, - line2 = self.shipping_address.address_extra, - city = self.shipping_address.city, - country_code = self.shipping_address.country, - postal_code = self.shipping_address.postal_code, - state = self.shipping_address.state - ) + item_list=dict( + items=items, + shipping_address=dict( + recipient_name=self.shipping_address.full_name, + line1=self.shipping_address.address, + line2=self.shipping_address.address_extra, + city=self.shipping_address.city, + country_code=self.shipping_address.country, + postal_code=self.shipping_address.postal_code, + state=self.shipping_address.state, + ), ), - amount = dict( - total = currency.Currency.format(self.total, self.currency), - currency = self.currency, - details = dict( - subtotal = currency.Currency.format(self.sub_total - self.discount, self.currency), - shipping = currency.Currency.format(self.shipping_cost, self.currency) - ) + amount=dict( + total=currency.Currency.format(self.total, self.currency), + currency=self.currency, + details=dict( + subtotal=currency.Currency.format( + self.sub_total - self.discount, self.currency + ), + shipping=currency.Currency.format( + self.shipping_cost, self.currency + ), + ), ), - soft_descriptor = self.reference + soft_descriptor=self.reference, ) return dict( - payer = dict(payment_method = "paypal"), - transactions = [transaction], - redirect_urls = dict( - return_url = return_url, - cancel_url = cancel_url - ) + payer=dict(payment_method="paypal"), + transactions=[transaction], + redirect_urls=dict(return_url=return_url, cancel_url=cancel_url), ) def is_payable(self): - if not self.status in ("waiting_payment",): return False - if self.paid: return False - if self.date: return False + if not self.status in ("waiting_payment",): + return False + if self.paid: + return False + if self.date: + return False return True def is_open(self): - if not self.status in ("created",): return False - if self.paid: return False + if not self.status in ("created",): + return False + if self.paid: + return False return True def is_closed(self): @@ -909,7 +847,9 @@ def verify_received(self): def verify_canceled(self): self.verify_base() - appier.verify(not self.status in ("created", "canceled", "received", "completed")) + appier.verify( + not self.status in ("created", "canceled", "received", "completed") + ) def verify_completed(self): self.verify_base() @@ -918,17 +858,17 @@ def verify_completed(self): def verify_vouchers(self): discount = self.calculate_discount() pending = discount - self.discount_base - self.discount_used - if pending <= 0.0: return + if pending <= 0.0: + return for voucher in self.vouchers: - if pending == 0.0: break - open_amount = voucher.open_amount_r(currency = self.currency) + if pending == 0.0: + break + open_amount = voucher.open_amount_r(currency=self.currency) overflows = open_amount > pending amount = pending if overflows else pending - result = voucher.is_valid( - amount = amount, - currency = self.currency - ) - if not result: continue + result = voucher.is_valid(amount=amount, currency=self.currency) + if not result: + continue pending -= commons.Decimal(amount) appier.verify(pending == 0.0) @@ -945,44 +885,37 @@ def verify_store(self): The raised exception should contain proper english messages. """ - appier.verify( - not self.store == None, - message = "No store is set for order" - ) + appier.verify(not self.store == None, message="No store is set for order") appier.verify( not self.store.address == None, - message = "Address is not set for order's store" + message="Address is not set for order's store", ) - @appier.operation(name = "Notify") - def notify_s(self, name = None, *args, **kwargs): + @appier.operation(name="Notify") + def notify_s(self, name=None, *args, **kwargs): name = name or "order.%s" % self.status - order = self.reload(map = True) + order = self.reload(map=True) receiver = order.get("email", None) receiver = kwargs.get("email", receiver) appier_extras.admin.Event.notify_g( name, - arguments = dict( - params = dict( - payload = order, - order = order, - receiver = receiver, - extra = kwargs - ) - ) + arguments=dict( + params=dict(payload=order, order=order, receiver=receiver, extra=kwargs) + ), ) exists = name in self.notifications - if not exists: self.notifications.append(name) + if not exists: + self.notifications.append(name) self.save() - @appier.operation(name = "Mark Waiting Payment") + @appier.operation(name="Mark Waiting Payment") def mark_waiting_payment_s(self): self.verify_waiting_payment() self.status = "waiting_payment" self.set_reference_f_s() self.save() - @appier.operation(name = "Mark Paid") + @appier.operation(name="Mark Paid") def mark_paid_s(self): self.verify_paid() self.status = "paid" @@ -992,204 +925,211 @@ def mark_paid_s(self): self.decrement_inventory_s() self.save() - @appier.operation(name = "Unmark Paid") + @appier.operation(name="Unmark Paid") def unmark_paid_s(self): self.status = "waiting_payment" self.paid = False self.increment_inventory_s() self.save() - @appier.operation(name = "Mark Invoiced") + @appier.operation(name="Mark Invoiced") def mark_invoiced_s(self): self.verify_invoiced() self.status = "invoiced" self.save() - @appier.operation(name = "Mark Sent") + @appier.operation(name="Mark Sent") def mark_sent_s(self): self.verify_sent() self.status = "sent" self.save() - @appier.operation(name = "Mark Received") + @appier.operation(name="Mark Received") def mark_received_s(self): self.verify_received() self.status = "received" self.save() - @appier.operation(name = "Mark Canceled") + @appier.operation(name="Mark Canceled") def mark_canceled_s(self): self.verify_canceled() self.status = "canceled" self.save() - @appier.operation(name = "Mark Completed") + @appier.operation(name="Mark Completed") def mark_completed_s(self): self.verify_completed() self.status = "completed" self.save() - @appier.operation(name = "Garbage Collect") + @appier.operation(name="Garbage Collect") def collect_s(self): - if self.paid: return + if self.paid: + return self.delete() @appier.operation( - name = "Set Tracking", - parameters = ( + name="Set Tracking", + parameters=( ("Tracking Number", "tracking_number", str), - ("Tracking URL", "tracking_url", str) - ) + ("Tracking URL", "tracking_url", str), + ), ) def set_tracking_s(self, tracking_number, tracking_url): - if not tracking_number and not tracking_url: return + if not tracking_number and not tracking_url: + return self.tracking_number = tracking_number self.tracking_url = tracking_url self.save() - @appier.operation(name = "Set Reference") - def set_reference_s(self, force = False): - if self.reference and not force: return + @appier.operation(name="Set Reference") + def set_reference_s(self, force=False): + if self.reference and not force: + return prefix = appier.conf("BUDY_ORDER_REF", "BD-%06d") self.reference = prefix % self.id self.save() - @appier.operation(name = "Set Reference Final") - def set_reference_f_s(self, force = False): - if self.reference_f and not force: return + @appier.operation(name="Set Reference Final") + def set_reference_f_s(self, force=False): + if self.reference_f and not force: + return cls = self.__class__ prefix = appier.conf("BUDY_ORDER_REF_F", "BDF-%06d") self.reference_f = prefix % cls._increment("reference_f") self.save() - @appier.operation(name = "Fix Orphans") + @appier.operation(name="Fix Orphans") def fix_orphans_s(self): for line in self.lines: line.order = self line.save() - @appier.operation(name = "Fix Shipping") + @appier.operation(name="Fix Shipping") def fix_shipping_s(self): - if self.shipping_address: return - if not self.store: return - if not self.store.address: return + if self.shipping_address: + return + if not self.store: + return + if not self.store.address: + return address = self.store.address.clone() address.save() self.shipping_address = address self.save() - @appier.operation(name = "Fix Store") + @appier.operation(name="Fix Store") def fix_store_s(self): - if self.store: return - if not self.account: return + if self.store: + return + if not self.account: + return self.store = self.account.store self.save() - @appier.operation(name = "Fix Closed Lines") + @appier.operation(name="Fix Closed Lines") def fix_closed_s(self): - if not self.is_closed(): return + if not self.is_closed(): + return self.close_lines_s() @appier.operation( - name = "Decrement Inventory", - description = """Decrements the inventory stock levels + name="Decrement Inventory", + description="""Decrements the inventory stock levels according to order lines""", - parameters = (("Force", "force", bool, False),), - level = 2 + parameters=(("Force", "force", bool, False),), + level=2, ) - def decrement_inventory_s(self, force = False): - if self.inventory_decremented and not force: return + def decrement_inventory_s(self, force=False): + if self.inventory_decremented and not force: + return for line in self.lines: - if line.merchandise.quantity_hand == None: continue - line.merchandise.quantity_hand =\ - max(line.merchandise.quantity_hand - line.quantity, 0) + if line.merchandise.quantity_hand == None: + continue + line.merchandise.quantity_hand = max( + line.merchandise.quantity_hand - line.quantity, 0 + ) line.merchandise.save() self.inventory_decremented = True self.save() @appier.operation( - name = "Increment Inventory", - description = """Increments the inventory stock levels + name="Increment Inventory", + description="""Increments the inventory stock levels according to order lines (reverses the decrement stock operation, restoring the stock back)""", - parameters = (("Force", "force", bool, False),), - level = 2 + parameters=(("Force", "force", bool, False),), + level=2, ) - def increment_inventory_s(self, force = False): - if not self.inventory_decremented and not force: return + def increment_inventory_s(self, force=False): + if not self.inventory_decremented and not force: + return for line in self.lines: - if line.merchandise.quantity_hand == None: continue + if line.merchandise.quantity_hand == None: + continue line.merchandise.quantity_hand += line.quantity line.merchandise.save() self.inventory_decremented = False self.save() @appier.operation( - name = "Import Omni", - parameters = ( + name="Import Omni", + parameters=( ("Invoice", "invoice", bool, True), ("Strict", "strict", bool, True), - ("Sync Prices", "sync_prices", bool, True) + ("Sync Prices", "sync_prices", bool, True), ), - level = 2 + level=2, ) def import_omni_s( - self, - invoice = False, - strict = True, - sync_prices = True, - use_discount = True + self, invoice=False, strict=True, sync_prices=True, use_discount=True ): api = self.owner.get_omni_api() - appier.verify( - self.paid, - message = "Order is not yet paid" - ) + appier.verify(self.paid, message="Order is not yet paid") # verifies if the current order is already "marked" with the # Omni import timestamp, if that's the case and the strict mode # is enabled then an operation error is raised omni_timestamp = self.meta.get("omni_timestamp", None) - if strict and omni_timestamp: raise appier.OperationalError( - message = "Order already imported in Omni" - ) + if strict and omni_timestamp: + raise appier.OperationalError(message="Order already imported in Omni") # retrieves via configuration the store from which the # sale is going to be performed (affects inventory) - store_id = appier.conf("OMNI_BOT_STORE", None, cast = int) + store_id = appier.conf("OMNI_BOT_STORE", None, cast=int) # gather using configuration the object ID of the services # that represents the presence of the shipping and gift wrap - shipping_id = appier.conf("OMNI_BOT_SHIPPING", None, cast = int) - gift_wrap_id = appier.conf("OMNI_BOT_GIFT_WRAP", None, cast = int) + shipping_id = appier.conf("OMNI_BOT_SHIPPING", None, cast=int) + gift_wrap_id = appier.conf("OMNI_BOT_GIFT_WRAP", None, cast=int) # builds the "unique" description of the order from which # a duplicates are going to be avoided by explicit checking description = "budy:order:%s" % self.reference orders = api.list_sales( - number_records = 1, - **{ - "filters[]" : [ - "description:equals:%s" % description - ] - } - ) - if strict and orders: raise appier.OperationalError( - message = "Duplicated order '%s' in Omni" % description + number_records=1, **{"filters[]": ["description:equals:%s" % description]} ) + if strict and orders: + raise appier.OperationalError( + message="Duplicated order '%s' in Omni" % description + ) # tries to obtain at least one of the customers that match # the email associated with the order in Omni's data source # in case a match then this customer is going to be used as # the owner of the sale otherwise a fallback must be performed - customers = api.list_customers( - number_records = 1, - **{ - "filters[]" : [ - "primary_contact_information.email:equals:%s" % self.email - ] - } - ) if self.email else [] + customers = ( + api.list_customers( + number_records=1, + **{ + "filters[]": [ + "primary_contact_information.email:equals:%s" % self.email + ] + } + ) + if self.email + else [] + ) if customers: # "gathers" the first customer of the sequence as the @@ -1200,24 +1140,21 @@ def import_omni_s( # its VAT number with the new one, this way a possible # invoice for the new sale will be "printed" with the # expected new VAT number - if self.billing_address.vat_number and\ - not existing["tax_number"] == self.billing_address.vat_number: + if ( + self.billing_address.vat_number + and not existing["tax_number"] == self.billing_address.vat_number + ): api.update_person( existing["object_id"], dict( - customer_person = dict( - tax_number = self.billing_address.vat_number - ) - ) + customer_person=dict(tax_number=self.billing_address.vat_number) + ), ) # sets the customer as an existing one by referencing its # object ID and marking parameters as existing customer = dict( - object_id = existing["object_id"], - _parameters = dict( - type = "existing" - ) + object_id=existing["object_id"], _parameters=dict(type="existing") ) elif self.account and self.billing_address and self.email: # determines if the account name was auto generated, probably @@ -1236,32 +1173,25 @@ def import_omni_s( # from the appropriate data sources related with the order and # the associated account information customer = dict( - name = name, - surname = surname, - gender = GENDERS_MAP.get(self.account.gender, None), - birth_date = self.account.birth_date, - primary_contact_information = dict( - phone_number = self.billing_address.phone_number, - email = self.email + name=name, + surname=surname, + gender=GENDERS_MAP.get(self.account.gender, None), + birth_date=self.account.birth_date, + primary_contact_information=dict( + phone_number=self.billing_address.phone_number, email=self.email ), - primary_address = dict( - street_name = self.billing_address.address, - zip_code = self.billing_address.postal_code, - zip_code_name = self.billing_address.city, - country = COUNTRIES_MAP.get(self.billing_address.country, None) + primary_address=dict( + street_name=self.billing_address.address, + zip_code=self.billing_address.postal_code, + zip_code_name=self.billing_address.city, + country=COUNTRIES_MAP.get(self.billing_address.country, None), ), - tax_number = self.billing_address.vat_number, - observations = "created by Budy", - _parameters = dict( - type = "new" - ) + tax_number=self.billing_address.vat_number, + observations="created by Budy", + _parameters=dict(type="new"), ) else: - customer = dict( - _parameters = dict( - type = "anonymous" - ) - ) + customer = dict(_parameters=dict(type="anonymous")) # allocates space for both the list that will contain the # complete set of sales lines and to the map that will associate @@ -1279,7 +1209,7 @@ def import_omni_s( # issue and an exception should be raised appier.verify( "object_id" in line.merchandise.meta, - message = "Product was not imported from Omni, no object ID" + message="Product was not imported from Omni, no object ID", ) # obtains the object ID of the product that identifies @@ -1297,33 +1227,36 @@ def import_omni_s( sale_line["quantity"] += line.quantity if line.attributes: metadata = sale_line.get("metadata", {}) - try: attributes = json.loads(line.attributes) - except ValueError: attributes = dict() - except TypeError: attributes = dict() - - def merger(first, second, separator = " | "): - if appier.legacy.is_string(first) and\ - appier.legacy.is_string(second): + try: + attributes = json.loads(line.attributes) + except ValueError: + attributes = dict() + except TypeError: + attributes = dict() + + def merger(first, second, separator=" | "): + if appier.legacy.is_string(first) and appier.legacy.is_string( + second + ): return "%s%s%s" % (first, separator, second) return second sale_line["metadata"] = appier.dict_merge( - metadata, - attributes, - recursive = True, - callback = merger + metadata, attributes, recursive=True, callback=merger ) else: # creates the standard sale line structure for the sale line using # merchandise object ID (from Omni) and the associated quantity sale_line = dict( - merchandise = dict(object_id = line_object_id), - quantity = line.quantity + merchandise=dict(object_id=line_object_id), quantity=line.quantity ) if line.attributes: - try: attributes = json.loads(line.attributes) - except ValueError: attributes = dict() - except TypeError: attributes = dict() + try: + attributes = json.loads(line.attributes) + except ValueError: + attributes = dict() + except TypeError: + attributes = dict() sale_line["metadata"] = attributes sale_lines.append(sale_line) sale_lines_m[line_object_id] = sale_line @@ -1338,7 +1271,7 @@ def merger(first, second, separator = " | "): store_merchandise = api.list_store_merchandise( store_id, **{ - "filters[]" : [ + "filters[]": [ "object_id:equals:%d" % line.merchandise.meta["object_id"] ] } @@ -1346,13 +1279,13 @@ def merger(first, second, separator = " | "): appier.verify( len(store_merchandise) > 0, - message = "Inventory line not found in Omni" + message="Inventory line not found in Omni", ) store_merchandise = store_merchandise[0] appier.verify( line.price <= store_merchandise["retail_price"], - message = "Trying to sell item at higher value" + message="Trying to sell item at higher value", ) # verifies if the price for which we're trying to sell the @@ -1364,53 +1297,50 @@ def merger(first, second, separator = " | "): # line is changed to reflect the delta between the price stored # in Omni and the price for which we're trying to sell the product if use_discount: - sale_line["unit_discount_vat"] = store_merchandise["retail_price"] - line.price + sale_line["unit_discount_vat"] = ( + store_merchandise["retail_price"] - line.price + ) # otherwise other strategy is used where the price of the product # is effectively changed for the store in question (e-commerce store) else: - api.prices_merchandise([ - dict( - object_id = line.merchandise.meta["object_id"], - retail_price = line.price, - functional_units = [store_id] - ) - ]) + api.prices_merchandise( + [ + dict( + object_id=line.merchandise.meta["object_id"], + retail_price=line.price, + functional_units=[store_id], + ) + ] + ) if self.shipping_cost > 0.0 and shipping_id: - sale_line = dict( - merchandise = dict(object_id = shipping_id), - quantity = 1 - ) + sale_line = dict(merchandise=dict(object_id=shipping_id), quantity=1) sale_lines.append(sale_line) if self.gift_wrap and gift_wrap_id: - sale_line = dict( - merchandise = dict(object_id = gift_wrap_id), - quantity = 1 - ) + sale_line = dict(merchandise=dict(object_id=gift_wrap_id), quantity=1) sale_lines.append(sale_line) primary_payment = dict( - payment_lines = [ + payment_lines=[ dict( - payment_method = dict(_class = "CardPayment"), - amount = dict(value = self.payable) + payment_method=dict(_class="CardPayment"), + amount=dict(value=self.payable), ) ] ) transaction = dict( - description = description, - sale_lines = sale_lines, - primary_payment = primary_payment + description=description, + sale_lines=sale_lines, + primary_payment=primary_payment, ) - if store_id: transaction["owner"] = dict(object_id = store_id) - if self.discount: transaction["discount_vat"] = self.discount + if store_id: + transaction["owner"] = dict(object_id=store_id) + if self.discount: + transaction["discount_vat"] = self.discount - payload = dict( - transaction = transaction, - customer = customer - ) + payload = dict(transaction=transaction, customer=customer) try: sale = api.create_sale(payload) @@ -1424,13 +1354,13 @@ def merger(first, second, separator = " | "): if invoice: # verifies that the current order is "invoiceable" so that # no inconsistent information is going to be "created" - if strict: self.verify_invoiced() + if strict: + self.verify_invoiced() # issues the money sale slip in the Omni system, this is # an expensive server-to-server operation api.issue_money_sale_slip_sale( - sale["object_id"], - metadata = dict(notes = self._build_notes()) + sale["object_id"], metadata=dict(notes=self._build_notes()) ) # "marks" the current order as invoiced, properly avoiding @@ -1441,29 +1371,25 @@ def merger(first, second, separator = " | "): # import operation so that the order is properly "marked" and # the linking can be properly "explorer" self.meta.update( - omni_timestamp = time.time(), - omni_sale = sale["object_id"], - omni_sale_identifier = sale["identifier"], - omni_url = api.base_url + "omni_sam/sales/%s" % sale["object_id"] + omni_timestamp=time.time(), + omni_sale=sale["object_id"], + omni_sale_identifier=sale["identifier"], + omni_url=api.base_url + "omni_sam/sales/%s" % sale["object_id"], ) self.save() @appier.operation( - name = "Import Seeplus", - parameters = ( + name="Import Seeplus", + parameters=( ("Fulfilment", "fulfilment", str), ("Delivery", "delivery", str), ("Origin", "origin", str), - ("Strict", "strict", bool, True) + ("Strict", "strict", bool, True), ), - level = 2 + level=2, ) def import_seeplus_s( - self, - fulfilment = None, - delivery = None, - origin = None, - strict = True + self, fulfilment=None, delivery=None, origin=None, strict=True ): # defaults the origin value to the one present in the global # configuration, expected default behaviour @@ -1472,61 +1398,41 @@ def import_seeplus_s( # in case there's no fulfilment defined then the default # store is used to retrieve the fulfilment identifier if not fulfilment: - fulfilment_id = appier.conf("SEEPLUS_FULFILMENT_ID", None, cast = int) + fulfilment_id = appier.conf("SEEPLUS_FULFILMENT_ID", None, cast=int) appier.verify( - not fulfilment_id == None, - message = "No default fulfilment is defined" + not fulfilment_id == None, message="No default fulfilment is defined" ) - fulfilment_store = store.Store.get(id = fulfilment_id) + fulfilment_store = store.Store.get(id=fulfilment_id) fulfilment = fulfilment_store.meta.get("seeplus_id", None) # if no explicit delivery is defined then the store for the # delivery is used to obtain the Seeplus identifier if not delivery: - appier.verify( - self.store, - message = "No store defined for order" - ) + appier.verify(self.store, message="No store defined for order") delivery = self.store.meta.get("seeplus_id", None) # ensures that the complete set of required parameters for the # Seeplus import are defined and valid, otherwise an exception # is raised indicating the problem - appier.verify( - fulfilment, - message = "No fulfilment is set for order" - ) - appier.verify( - delivery, - message = "No delivery is set for order" - ) - appier.verify( - origin, - message = "No origin is set for order" - ) + appier.verify(fulfilment, message="No fulfilment is set for order") + appier.verify(delivery, message="No delivery is set for order") + appier.verify(origin, message="No origin is set for order") # obtains the reference to the Seeplus API instance and # validates that the order is ready to be imported api = self.owner.get_seeplus_api() - appier.verify( - self.paid, - message = "Order is not yet paid" - ) + appier.verify(self.paid, message="Order is not yet paid") # verifies if the current order is already "marked" with the # Seeplus import timestamp, if that's the case and the strict mode # is enabled then an operation error is raised seeplus_timestamp = self.meta.get("seeplus_timestamp", None) - if strict and seeplus_timestamp: raise appier.OperationalError( - message = "Order already imported in Seeplus" - ) + if strict and seeplus_timestamp: + raise appier.OperationalError(message="Order already imported in Seeplus") customer = dict() if self.account: - customer = dict( - code = str(self.account.id), - name = self.account.full_name - ) + customer = dict(code=str(self.account.id), name=self.account.full_name) if self.account.email: customer["email"] = self.account.email if self.account.phone_number: @@ -1539,27 +1445,29 @@ def import_seeplus_s( for line in self.lines: products.append( dict( - product = line.merchandise.product_id, - qty = int(line.quantity), - comment = line.description or "-" + product=line.merchandise.product_id, + qty=int(line.quantity), + comment=line.description or "-", ) ) order_payload = dict( - origin = origin, - code = self.reference, - orderDate = datetime.datetime.utcfromtimestamp(self.created).strftime("%Y-%m-%dT%H:%M:%SZ"), - customer = customer, - products = products, - fulfilment = dict( - location = fulfilment, - expectedAt = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"), + origin=origin, + code=self.reference, + orderDate=datetime.datetime.utcfromtimestamp(self.created).strftime( + "%Y-%m-%dT%H:%M:%SZ" ), - deliveryPickup = dict( - location = delivery, - scheduledAt = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") + customer=customer, + products=products, + fulfilment=dict( + location=fulfilment, + expectedAt=datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"), ), - comment = self.description or "-" + deliveryPickup=dict( + location=delivery, + scheduledAt=datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"), + ), + comment=self.description or "-", ) # imports the order into the Seeplus infrastructure and obtains @@ -1572,65 +1480,55 @@ def import_seeplus_s( # sets the initial Seeplus updates sequence with the initial # status update operation, this will be used as a log - seeplus_updates = [ - dict( - status = order["status"], - timestamp = status_timestamp - ) - ] + seeplus_updates = [dict(status=order["status"], timestamp=status_timestamp)] # updates the complete set of metadata related with the Seeplus # import operation so that the order is properly "marked" and # the linking can be properly "explorer" self.meta.update( - seeplus_id = order["id"], - seeplus_status = order["status"], - seeplus_timestamp = status_timestamp, - seeplus_update = status_timestamp, - seeplus_updates = seeplus_updates + seeplus_id=order["id"], + seeplus_status=order["status"], + seeplus_timestamp=status_timestamp, + seeplus_update=status_timestamp, + seeplus_updates=seeplus_updates, ) self.save() - @appier.link(name = "Export Lines CSV") - def lines_csv_url(self, absolute = False): + @appier.link(name="Export Lines CSV") + def lines_csv_url(self, absolute=False): return appier.get_app().url_for( - "order_api.lines_csv", - key = self.key, - absolute = absolute + "order_api.lines_csv", key=self.key, absolute=absolute ) - @appier.view(name = "Lines") + @appier.view(name="Lines") def lines_v(self, *args, **kwargs): return appier.lazy_dict( - model = self.lines._target, - kwargs = kwargs, - entities = appier.lazy(lambda: self.lines.find(*args, **kwargs)), - page = appier.lazy(lambda: self.lines.paginate(*args, **kwargs)), - names = [ + model=self.lines._target, + kwargs=kwargs, + entities=appier.lazy(lambda: self.lines.find(*args, **kwargs)), + page=appier.lazy(lambda: self.lines.paginate(*args, **kwargs)), + names=[ "id", "product.product_id", "product", "size_s", "quantity", "total", - "currency" - ] + "currency", + ], ) @appier.view( - name = "Lines Currency", - parameters = ( - ("Currency", "currency", str, "EUR"), - ) + name="Lines Currency", parameters=(("Currency", "currency", str, "EUR"),) ) def lines_currency_v(self, currency, *args, **kwargs): - kwargs.update(currency = currency) + kwargs.update(currency=currency) return appier.lazy_dict( - model = self.lines._target, - kwargs = kwargs, - entities = appier.lazy(lambda: self.lines.find(*args, **kwargs)), - page = appier.lazy(lambda: self.lines.paginate(*args, **kwargs)), - names = ["id", "product", "quantity", "total", "currency"] + model=self.lines._target, + kwargs=kwargs, + entities=appier.lazy(lambda: self.lines.find(*args, **kwargs)), + page=appier.lazy(lambda: self.lines.paginate(*args, **kwargs)), + names=["id", "product", "quantity", "total", "currency"], ) @property @@ -1640,43 +1538,48 @@ def payable(self): @property def shipping_country(self): has_shipping = hasattr(self, "shipping_address") - if not has_shipping: return None - if not self.shipping_address: return None + if not has_shipping: + return None + if not self.shipping_address: + return None return self.shipping_address.country @property def shipping_currency(self): currency = appier.conf("BUDY_CURRENCY", None) - if currency: return currency - if not self.shipping_country: return None + if currency: + return currency + if not self.shipping_country: + return None shipping_country = country.Country.get_by_code(self.shipping_country) return shipping_country.currency_code @property def payment_currency(self): currency = appier.conf("BUDY_CURRENCY", None) - if currency: return currency + if currency: + return currency has_store_currency = self.store and self.store.currency_code - if has_store_currency: return self.store.currency_code + if has_store_currency: + return self.store.currency_code return self.shipping_currency - def _pay(self, payment_data, payment_function = None, strict = True): + def _pay(self, payment_data, payment_function=None, strict=True): cls = self.__class__ - if self.payable == 0.0: return True + if self.payable == 0.0: + return True methods = cls._pmethods() type = payment_data.get("type", None) method = methods.get(type, type) - if not method: raise appier.SecurityError( - message = "No payment method defined" - ) + if not method: + raise appier.SecurityError(message="No payment method defined") has_function = bool(payment_function) or hasattr(self, "_pay_" + method) - if not has_function and not strict: return - if not has_function: raise appier.SecurityError( - message = "Invalid payment method" - ) - if self.payment_data and strict: raise appier.SecurityError( - message = "Payment data has already been issued" - ) + if not has_function and not strict: + return + if not has_function: + raise appier.SecurityError(message="Invalid payment method") + if self.payment_data and strict: + raise appier.SecurityError(message="Payment data has already been issued") function = payment_function or getattr(self, "_pay_" + method) return function(payment_data) @@ -1684,9 +1587,9 @@ def _pay_stripe(self, payment_data): cls = self.__class__ api = cls._get_api_stripe() - stripe_legacy = appier.conf("BUDY_STRIPE_LEGACY", False, cast = bool) - three_d_enable = appier.conf("BUDY_3D_SECURE", False, cast = bool) - three_d_ensure = appier.conf("BUDY_3D_ENSURE", False, cast = bool) + stripe_legacy = appier.conf("BUDY_STRIPE_LEGACY", False, cast=bool) + three_d_enable = appier.conf("BUDY_3D_SECURE", False, cast=bool) + three_d_ensure = appier.conf("BUDY_3D_ENSURE", False, cast=bool) type = payment_data["type"] number = payment_data["card_number"] @@ -1696,21 +1599,24 @@ def _pay_stripe(self, payment_data): name = payment_data.get("card_name", None) return_url = payment_data.get("return_url", None) return_url = payment_data.get("stripe_return_url", return_url) - if number: number = number.replace(" ", "") - if cvc: cvc = cvc.replace(" ", "") - if name: name = name.strip() + if number: + number = number.replace(" ", "") + if cvc: + cvc = cvc.replace(" ", "") + if name: + name = name.strip() token = api.create_token( exp_month, exp_year, number, - cvc = cvc, - name = name, - address_country = self.shipping_address.country, - address_city = self.shipping_address.city, - address_zip = self.shipping_address.postal_code, - address_line1 = self.shipping_address.address, - address_line2 = self.shipping_address.address_extra + cvc=cvc, + name=name, + address_country=self.shipping_address.country, + address_city=self.shipping_address.city, + address_zip=self.shipping_address.postal_code, + address_line1=self.shipping_address.address, + address_line2=self.shipping_address.address_extra, ) token_id = token["id"] @@ -1724,10 +1630,7 @@ def _pay_stripe(self, payment_data): if use_secure: if stripe_legacy: secure = api.create_3d_secure( - int(self.payable * 100), - self.currency, - return_url, - token_id + int(self.payable * 100), self.currency, return_url, token_id ) redirect = secure.get("redirect_url", None) else: @@ -1735,19 +1638,19 @@ def _pay_stripe(self, payment_data): exp_month, exp_year, number, - cvc = cvc, - name = name, - address_country = self.shipping_address.country, - address_city = self.shipping_address.city, - address_zip = self.shipping_address.postal_code, - address_line1 = self.shipping_address.address, - address_line2 = self.shipping_address.address_extra + cvc=cvc, + name=name, + address_country=self.shipping_address.country, + address_city=self.shipping_address.city, + address_zip=self.shipping_address.postal_code, + address_line1=self.shipping_address.address, + address_line2=self.shipping_address.address_extra, ) source = api.create_3d_secure_source( int(self.payable * 100), self.currency, return_url, - card = source["id"] + card=source["id"], ) redirect = source.get("redirect", {}) redirect_u = redirect.get("url", None) @@ -1760,30 +1663,29 @@ def _pay_stripe(self, payment_data): if use_secure: if stripe_legacy: redirect_url = appier.get_app().url_for( - "stripe.redirect", - redirect_url = redirect, - absolute = True + "stripe.redirect", redirect_url=redirect, absolute=True ) else: redirect_url = redirect self.payment_data = dict( - engine = "stripe", - type = type, - number = "*" * 12 + number[-4:], - exp_month = exp_month, - exp_year = exp_year, - token = token_id, - redirect = redirect, - redirect_url = redirect_url, - secure = True + engine="stripe", + type=type, + number="*" * 12 + number[-4:], + exp_month=exp_month, + exp_year=exp_year, + token=token_id, + redirect=redirect, + redirect_url=redirect_url, + secure=True, ) return redirect_url else: - if three_d_ensure: raise appier.SecurityError( - message = "No 3-D Secure enabled for provided card" - ) + if three_d_ensure: + raise appier.SecurityError( + message="No 3-D Secure enabled for provided card" + ) api.create_charge( int(self.payable * 100), @@ -1791,45 +1693,45 @@ def _pay_stripe(self, payment_data): exp_month, exp_year, number, - cvc = cvc, - name = name, - description = self.reference, - address_country = self.shipping_address.country, - address_city = self.shipping_address.city, - address_zip = self.shipping_address.postal_code, - address_line1 = self.shipping_address.address, - address_line2 = self.shipping_address.address_extra, - metadata = dict( - order = self.reference, - email = self.email, - ip_address = self.ip_address, - ip_country = self.ip_country, - first_name = self.shipping_address.first_name, - last_name = self.shipping_address.last_name - ) + cvc=cvc, + name=name, + description=self.reference, + address_country=self.shipping_address.country, + address_city=self.shipping_address.city, + address_zip=self.shipping_address.postal_code, + address_line1=self.shipping_address.address, + address_line2=self.shipping_address.address_extra, + metadata=dict( + order=self.reference, + email=self.email, + ip_address=self.ip_address, + ip_country=self.ip_country, + first_name=self.shipping_address.first_name, + last_name=self.shipping_address.last_name, + ), ) self.payment_data = dict( - engine = "stripe", - type = type, - number = "*" * 12 + number[-4:], - exp_month = exp_month, - exp_year = exp_year, - token = token_id, - secure = False + engine="stripe", + type=type, + number="*" * 12 + number[-4:], + exp_month=exp_month, + exp_year=exp_year, + token=token_id, + secure=False, ) return True - def _pay_easypay(self, payment_data, warning_d = 172800, cancel_d = 259200): + def _pay_easypay(self, payment_data, warning_d=172800, cancel_d=259200): cls = self.__class__ api = cls._get_api_easypay() type = payment_data["type"] mb = api.generate_mb( self.payable, - key = self.key, - warning = int(time.time() + warning_d) if warning_d else None, - cancel = int(time.time() + cancel_d) if cancel_d else None + key=self.key, + warning=int(time.time() + warning_d) if warning_d else None, + cancel=int(time.time() + cancel_d) if cancel_d else None, ) entity = mb["entity"] reference = mb["reference"] @@ -1838,14 +1740,14 @@ def _pay_easypay(self, payment_data, warning_d = 172800, cancel_d = 259200): warning = mb["warning"] cancel = mb["cancel"] self.payment_data = dict( - engine = "easypay", - type = type, - entity = entity, - reference = reference, - cin = cin, - identifier = identifier, - warning = warning, - cancel = cancel + engine="easypay", + type=type, + entity=entity, + reference=reference, + cin=cin, + identifier=identifier, + warning=warning, + cancel=cancel, ) return False @@ -1856,18 +1758,15 @@ def _pay_paypal(self, payment_data): return_url = payment_data.get("paypal_return_url", return_url) cancel_url = payment_data.get("cancel_url", None) cancel_url = payment_data.get("paypal_cancel_url", cancel_url) - paypal_order = self.get_paypal( - return_url = return_url, - cancel_url = cancel_url - ) + paypal_order = self.get_paypal(return_url=return_url, cancel_url=cancel_url) payment = api.create_payment(**paypal_order) payment_id = payment["id"] approval_url = api.get_url(payment["links"], "approval_url") self.payment_data = dict( - engine = "paypal", - type = "paypal", - payment_id = payment_id, - approval_url = approval_url + engine="paypal", + type="paypal", + payment_id=payment_id, + approval_url=approval_url, ) return approval_url @@ -1881,49 +1780,49 @@ def _pay_stripe_sca(self, payment_data): intent = api.create_intent( int(self.payable * 100), self.currency, - description = self.reference, - metadata = dict( - order = self.reference, - email = self.email, - ip_address = self.ip_address, - ip_country = self.ip_country, - first_name = self.shipping_address.first_name, - last_name = self.shipping_address.last_name - ) + description=self.reference, + metadata=dict( + order=self.reference, + email=self.email, + ip_address=self.ip_address, + ip_country=self.ip_country, + first_name=self.shipping_address.first_name, + last_name=self.shipping_address.last_name, + ), ) identifier = intent["id"] secret = intent["client_secret"] query = "secret=%s&return_url=%s" % ( appier.quote(secret), - appier.quote(return_url) + appier.quote(return_url), ) pay_secret_url = pay_url + ("&" if "?" in pay_url else "?") + query self.payment_data = dict( - engine = "stripe_sca", - type = "stripe_sca", - identifier = identifier, - secret = secret, - pay_url = pay_url, - return_url = return_url, - pay_secret_url = pay_secret_url + engine="stripe_sca", + type="stripe_sca", + identifier=identifier, + secret=secret, + pay_url=pay_url, + return_url=return_url, + pay_secret_url=pay_secret_url, ) return pay_secret_url - def _end_pay(self, payment_data, payment_function = None, strict = False): + def _end_pay(self, payment_data, payment_function=None, strict=False): cls = self.__class__ - if self.payable == 0.0: return + if self.payable == 0.0: + return methods = cls._pmethods() type = payment_data.get("engine", None) type = payment_data.get("type", type) method = methods.get(type, type) - if not method: raise appier.SecurityError( - message = "No payment method defined" - ) + if not method: + raise appier.SecurityError(message="No payment method defined") has_function = payment_function or hasattr(self, "_end_pay_" + method) - if not has_function and not strict: return - if not has_function: raise appier.SecurityError( - message = "Invalid payment method" - ) + if not has_function and not strict: + return + if not has_function: + raise appier.SecurityError(message="Invalid payment method") function = payment_function or getattr(self, "_end_pay_" + method) return function(payment_data) @@ -1941,7 +1840,8 @@ def _end_pay_stripe(self, payment_data): # in case the current payment stream is not the (3D) secure # one then returns immediately - if not secure: return + if not secure: + return # tries to obtain the source associated with the return # so that the proper course of action may be taken @@ -1952,10 +1852,8 @@ def _end_pay_stripe(self, payment_data): # the cancel operation is run instead to cancel the current # order and a security exception is raised for the failure if status == "failed": - self.cancel_s(notify = True) - raise appier.SecurityError( - message = "Security verification failed" - ) + self.cancel_s(notify=True) + raise appier.SecurityError(message="Security verification failed") # (otherwise) runs the charging operation using the token # for the source as the source is considered to be valid @@ -1963,15 +1861,15 @@ def _end_pay_stripe(self, payment_data): int(self.payable * 100), self.currency, token_return, - description = self.reference, - metadata = dict( - order = self.reference, - email = self.email, - ip_address = self.ip_address, - ip_country = self.ip_country, - first_name = self.shipping_address.first_name, - last_name = self.shipping_address.last_name - ) + description=self.reference, + metadata=dict( + order=self.reference, + email=self.email, + ip_address=self.ip_address, + ip_country=self.ip_country, + first_name=self.shipping_address.first_name, + last_name=self.shipping_address.last_name, + ), ) # returns a valid value to the caller method indicating that @@ -1996,73 +1894,75 @@ def _end_pay_stripe_sca(self, payment_data): int(self.payable * 100), self.currency, token, - description = self.reference, - metadata = dict( - order = self.reference, - email = self.email, - ip_address = self.ip_address, - ip_country = self.ip_country, - first_name = self.shipping_address.first_name, - last_name = self.shipping_address.last_name - ) + description=self.reference, + metadata=dict( + order=self.reference, + email=self.email, + ip_address=self.ip_address, + ip_country=self.ip_country, + first_name=self.shipping_address.first_name, + last_name=self.shipping_address.last_name, + ), ) else: - charges = api.list_charges( - payment_intent = identifier, - limit = 1 - ) + charges = api.list_charges(payment_intent=identifier, limit=1) items = charges.get("data", []) - appier.verify( - items, - message = "No valid charge found" - ) + appier.verify(items, message="No valid charge found") charge = items[0] appier.verify( - charge.get("status", "succeeded"), - message = "Charge was not successful" + charge.get("status", "succeeded"), message="Charge was not successful" ) appier.verify( - charge.get("captured", False), - message = "Charge was not captured" + charge.get("captured", False), message="Charge was not captured" ) return True - def _cancel(self, cancel_data, cancel_function = None, strict = False): + def _cancel(self, cancel_data, cancel_function=None, strict=False): cls = self.__class__ - if self.payable == 0.0: return + if self.payable == 0.0: + return methods = cls._pmethods() type = cancel_data.get("engine", None) type = cancel_data.get("type", type) method = methods.get(type, type) - if not method: return + if not method: + return has_function = cancel_function or hasattr(self, "_cancel_" + method) - if not has_function and not strict: return + if not has_function and not strict: + return function = cancel_function or getattr(self, "_cancel_" + method) return function(cancel_data) - def _build_notes(self, split = "|", separator = "="): + def _build_notes(self, split="|", separator="="): # creates the list of notes that are going to be representing # the current order in a text fashion, this should include the product # attributes for every single order line notes_l = [] notes_l.append("Budy order - %s" % self.reference) for line in self.lines: - if not line.product: continue - if not line.attributes: continue - try: attributes = json.loads(line.attributes) - except ValueError: continue - except TypeError: continue - if not isinstance(attributes, dict): continue + if not line.product: + continue + if not line.attributes: + continue + try: + attributes = json.loads(line.attributes) + except ValueError: + continue + except TypeError: + continue + if not isinstance(attributes, dict): + continue attributes_l = appier.legacy.items(attributes) attributes_l.sort() for key, value in attributes_l: notes_l.append( - "Order line - %s %s %s %s %s" % ( + "Order line - %s %s %s %s %s" + % ( line.product.product_id or line.product.short_description, split, key, separator, - str(value) + str(value), ) ) notes = "\n".join(notes_l) diff --git a/src/budy/models/order_line.py b/src/budy/models/order_line.py index bbd8dabf..b0c9e33d 100644 --- a/src/budy/models/order_line.py +++ b/src/budy/models/order_line.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,14 +32,9 @@ from . import bundle_line -class OrderLine(bundle_line.BundleLine): - order = appier.field( - type = appier.reference( - "Order", - name = "id" - ) - ) +class OrderLine(bundle_line.BundleLine): + order = appier.field(type=appier.reference("Order", name="id")) @classmethod def list_names(cls): @@ -49,17 +44,20 @@ def list_names(cls): def is_visible(cls): return False - def is_valid_quantity(self, reload = True): + def is_valid_quantity(self, reload=True): order = self.order and self.order.reload() if reload else self.order - if order and order.is_closed(): return True - return bundle_line.BundleLine.is_valid_quantity(self, reload = reload) + if order and order.is_closed(): + return True + return bundle_line.BundleLine.is_valid_quantity(self, reload=reload) - def is_valid_price(self, reload = True): + def is_valid_price(self, reload=True): order = self.order and self.order.reload() if reload else self.order - if order and order.is_closed(): return True - return bundle_line.BundleLine.is_valid_price(self, reload = reload) + if order and order.is_closed(): + return True + return bundle_line.BundleLine.is_valid_price(self, reload=reload) - @appier.operation(name = "Garbage Collect") + @appier.operation(name="Garbage Collect") def collect_s(self): - if appier.is_unset(self.bag): return + if appier.is_unset(self.bag): + return self.delete() diff --git a/src/budy/models/product.py b/src/budy/models/product.py index 3660dad9..247ba0be 100644 --- a/src/budy/models/product.py +++ b/src/budy/models/product.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -38,325 +38,233 @@ from . import bundle from . import currency as _currency -class Product(base.BudyBase): - GENDER_S = { - "Male" : "Male", - "Female" : "Female", - "Child" : "Child", - "Both" : "Both" - } +class Product(base.BudyBase): + GENDER_S = {"Male": "Male", "Female": "Female", "Child": "Child", "Both": "Both"} """ The dictionary that maps the multiple gender enumeration values with their string representation """ short_description = appier.field( - index = "hashed", - default = True, - observations = """A short description on the product - that should be used as the title for the product""" + index="hashed", + default=True, + observations="""A short description on the product + that should be used as the title for the product""", ) product_id = appier.field( - index = "hashed", - description = "Product ID", - observations = """The primary identifier of the - product, should be globally unique""" + index="hashed", + description="Product ID", + observations="""The primary identifier of the + product, should be globally unique""", ) supplier_code = appier.field( - index = "hashed", - observations = """The identifier used by the + index="hashed", + observations="""The identifier used by the (back) supplier of the product, should be considered - a more unique way of identifying a product""" + a more unique way of identifying a product""", ) sku = appier.field( - index = "hashed", - description = "SKU", - observations = """The main identifier to be used for + index="hashed", + description="SKU", + observations="""The main identifier to be used for the keeping of the internal reference SKU (Stock Keeping Unit), should be considered the public way of representing - the product""" + the product""", ) upc = appier.field( - index = "hashed", - description = "UPC", - observations = """The standard Universal Product Code - (UPC) value for this product""" + index="hashed", + description="UPC", + observations="""The standard Universal Product Code + (UPC) value for this product""", ) ean = appier.field( - index = "hashed", - description = "EAN", - observations = """The standard European Article Number - (EAN) value for this product""" + index="hashed", + description="EAN", + observations="""The standard European Article Number + (EAN) value for this product""", ) - gender = appier.field( - index = "hashed", - meta = "enum", - enum = GENDER_S - ) + gender = appier.field(index="hashed", meta="enum", enum=GENDER_S) weight = appier.field( - type = commons.Decimal, - index = True, - observations = """The weight of the current product in - a unit defined by convention (defined before-hand)""" + type=commons.Decimal, + index=True, + observations="""The weight of the current product in + a unit defined by convention (defined before-hand)""", ) quantity_hand = appier.field( - type = commons.Decimal, - index = True, - observations = """The total quantity that is currently + type=commons.Decimal, + index=True, + observations="""The total quantity that is currently available (on hand) for the current product, if not set (none) the product is considered to have unlimited quantity - (inventory is not controlled)""" + (inventory is not controlled)""", ) quantity_reserved = appier.field( - type = commons.Decimal, - index = True, - observations = """The total quantity that is currently + type=commons.Decimal, + index=True, + observations="""The total quantity that is currently reserved (not available) for the current product, if not set (none) the product is considered to not have reservation - control disabled (no reservations are possible)""" + control disabled (no reservations are possible)""", ) price = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - observations = """Main retail price to be used for - a possible sale transaction of the product (includes taxes)""" + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + observations="""Main retail price to be used for + a possible sale transaction of the product (includes taxes)""", ) price_compare = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - observations = """The price that is going to be used - as the base for discount calculation purposes""" + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + observations="""The price that is going to be used + as the base for discount calculation purposes""", ) taxes = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - observations = """The amount of taxes that are associated + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + observations="""The amount of taxes that are associated with the product this value can include the VAT value but - it's not limited to it and more tax values can be included""" + it's not limited to it and more tax values can be included""", ) - currency = appier.field( - index = "hashed" - ) + currency = appier.field(index="hashed") - order = appier.field( - type = int, - index = "hashed" - ) + order = appier.field(type=int, index="hashed") tag = appier.field() tag_description = appier.field() - price_provider = appier.field( - index = True - ) + price_provider = appier.field(index=True) discountable = appier.field( - type = bool, - initial = True, - observations = """Flag that indicates if the product is - eligible for any kind of global discount""" + type=bool, + initial=True, + observations="""Flag that indicates if the product is + eligible for any kind of global discount""", ) orderable = appier.field( - type = bool, - observations = """Flag that controls if the product is only - meant to be used for pre-ordering and not direct sales""" + type=bool, + observations="""Flag that controls if the product is only + meant to be used for pre-ordering and not direct sales""", ) - price_url = appier.field( - index = "hashed", - meta = "url", - description = "Price URL" - ) + price_url = appier.field(index="hashed", meta="url", description="Price URL") - farfetch_url = appier.field( - index = "hashed", - meta = "url", - description = "Farfetch URL" - ) + farfetch_url = appier.field(index="hashed", meta="url", description="Farfetch URL") farfetch_male_url = appier.field( - index = "hashed", - meta = "url", - description = "Farfetch Male URL" + index="hashed", meta="url", description="Farfetch Male URL" ) farfetch_female_url = appier.field( - index = "hashed", - meta = "url", - description = "Farfetch Female URL" + index="hashed", meta="url", description="Farfetch Female URL" ) - image_url = appier.field( - index = "hashed", - meta = "image_url", - description = "Image URL" - ) + image_url = appier.field(index="hashed", meta="image_url", description="Image URL") thumbnail_url = appier.field( - index = "hashed", - meta = "image_url", - description = "Thumbnail URL", - observations = """The URL to the thumbnail image (low resolution) - to be used in listing contexts""" + index="hashed", + meta="image_url", + description="Thumbnail URL", + observations="""The URL to the thumbnail image (low resolution) + to be used in listing contexts""", ) characteristics = appier.field( - type = list, - observations = """The sequence of characteristics that - define the product under unstructured language""" + type=list, + observations="""The sequence of characteristics that + define the product under unstructured language""", ) features = appier.field( - type = dict, - observations = """Dictionary that maps a set of optional + type=dict, + observations="""Dictionary that maps a set of optional features with their corresponding configuration, for example the `initials` feature can be associated with the rules - that control those same initials""" + that control those same initials""", ) labels = appier.field( - type = list, - observations = """Set of simple tag labels that define the + type=list, + observations="""Set of simple tag labels that define the product behavior, this value is a calculated one based on - the labels provided by the complete set of collections""" + the labels provided by the complete set of collections""", ) - brand_s = appier.field( - index = "hashed" - ) + brand_s = appier.field(index="hashed") - season_s = appier.field( - index = "hashed" - ) + season_s = appier.field(index="hashed") - color_s = appier.field( - index = "hashed" - ) + color_s = appier.field(index="hashed") - category_s = appier.field( - index = "hashed" - ) + category_s = appier.field(index="hashed") - collection_s = appier.field( - index = "hashed" - ) + collection_s = appier.field(index="hashed") - colors = appier.field( - type = appier.references( - "Color", - name = "id" - ) - ) + colors = appier.field(type=appier.references("Color", name="id")) - categories = appier.field( - type = appier.references( - "Category", - name = "id" - ) - ) + categories = appier.field(type=appier.references("Category", name="id")) - collections = appier.field( - type = appier.references( - "Collection", - name = "id" - ) - ) + collections = appier.field(type=appier.references("Collection", name="id")) - variants = appier.field( - type = appier.references( - "Product", - name = "id" - ) - ) + variants = appier.field(type=appier.references("Product", name="id")) - images = appier.field( - type = appier.references( - "Media", - name = "id" - ) - ) + images = appier.field(type=appier.references("Media", name="id")) - brand = appier.field( - type = appier.reference( - "Brand", - name = "id" - ) - ) + brand = appier.field(type=appier.reference("Brand", name="id")) - season = appier.field( - type = appier.reference( - "Season", - name = "id" - ) - ) + season = appier.field(type=appier.reference("Season", name="id")) - measurements = appier.field( - type = appier.references( - "Measurement", - name = "id" - ) - ) + measurements = appier.field(type=appier.references("Measurement", name="id")) - compositions = appier.field( - type = appier.references( - "Composition", - name = "id" - ) - ) + compositions = appier.field(type=appier.references("Composition", name="id")) - live_model = appier.field( - type = appier.references( - "LiveModel", - name = "id" - ) - ) + live_model = appier.field(type=appier.references("LiveModel", name="id")) @classmethod def validate(cls): return super(Product, cls).validate() + [ appier.not_null("short_description"), appier.not_empty("short_description"), - appier.not_null("gender"), appier.not_empty("gender"), - appier.not_null("price"), appier.gte("price", 0.0), - appier.not_null("taxes"), appier.gte("taxes", 0.0), - appier.not_empty("brand_s"), - appier.not_empty("season_s"), - appier.not_empty("color_s"), - appier.not_empty("category_s"), - - appier.not_empty("collection_s") + appier.not_empty("collection_s"), ] @classmethod def list_names(cls): - return ["id", "product_id", "short_description", "enabled", "gender", "tag", "image_url"] + return [ + "id", + "product_id", + "short_description", + "enabled", + "gender", + "tag", + "image_url", + ] @classmethod def order_name(cls): @@ -382,19 +290,19 @@ def token_names(cls): ("colors.name", True), ("categories.name", True), ("collections.name", True), - ("compositions.name", True) + ("compositions.name", True), ] @classmethod def from_omni( cls, merchandise, - inventory_line = None, - inventory_lines = None, - gender = "Both", - currency = "EUR", - patch = True, - force = False + inventory_line=None, + inventory_lines=None, + gender="Both", + currency="EUR", + patch=True, + force=False, ): from . import brand from . import color @@ -429,7 +337,8 @@ def from_omni( # taking into account also the modification date of its inventory line if inventory_line: modify_date_line = inventory_line["modify_date"] - if modify_date_line > modify_date: modify_date = modify_date_line + if modify_date_line > modify_date: + modify_date = modify_date_line # creates the stocks list in case there are valid inventory lines being # passed on the current product update/creation @@ -446,15 +355,16 @@ def from_omni( functional_unit = inventory_line.get("functional_unit", None) is_valid = functional_unit and functional_unit.get("status") == 1 - if not is_valid: continue + if not is_valid: + continue stock_m = dict( - store_id = functional_unit["object_id"], - store_name = functional_unit["name"], - stock_on_hand = stock_on_hand, - stock_reserved = stock_reserved, - stock_in_transit = stock_in_transit, - retail_price = retail_price + store_id=functional_unit["object_id"], + store_name=functional_unit["name"], + stock_on_hand=stock_on_hand, + stock_reserved=stock_reserved, + stock_in_transit=stock_in_transit, + retail_price=retail_price, ) stocks.append(stock_m) @@ -463,11 +373,16 @@ def from_omni( collections = _collection if isinstance(_collection, list) else [_collection] colors = [color.Color.ensure_s(_color) for _color in colors] categories = [category.Category.ensure_s(_category) for _category in categories] - collections = [collection.Collection.ensure_s(_collection) for _collection in collections] - if _brand: _brand = brand.Brand.ensure_s(_brand) - if _season: _season = season.Season.ensure_s(_season) - product = cls.get(product_id = company_product_code, raise_e = False) - if not product: product = cls() + collections = [ + collection.Collection.ensure_s(_collection) for _collection in collections + ] + if _brand: + _brand = brand.Brand.ensure_s(_brand) + if _season: + _season = season.Season.ensure_s(_season) + product = cls.get(product_id=company_product_code, raise_e=False) + if not product: + product = cls() # in case the weight contains the special 1.0 value then # it must be set to invalid as this is considered to be @@ -476,7 +391,8 @@ def from_omni( # the patch mode is set (to enable automatic data correction) # this is considered a hack to reverse the invalid data source # values and should be used with proper care - if patch and weight == 1.0 and _season: weight = None + if patch and weight == 1.0 and _season: + weight = None product.product_id = company_product_code product.supplier_code = upc @@ -501,14 +417,17 @@ def from_omni( product.season = _season meta = dict( - object_id = object_id, - company_product_code = company_product_code, - modify_date = modify_date, - discount = discount + object_id=object_id, + company_product_code=company_product_code, + modify_date=modify_date, + discount=discount, ) - if hasattr(product, "meta") and product.meta: product.meta.update(meta) - else: product.meta = meta - if not stocks == None: product.meta["stocks"] = stocks + if hasattr(product, "meta") and product.meta: + product.meta.update(meta) + else: + product.meta = meta + if not stocks == None: + product.meta["stocks"] = stocks if "stock_on_hand" in merchandise or force: product.quantity_hand = merchandise.get("stock_on_hand", 0.0) @@ -532,14 +451,22 @@ def from_omni( # in case all of the required "original" financial information (prices) # is available then the price, taxes and price compare are calculated if "retail_price" in product.meta and "untaxed_price" in product.meta: - untaxed_price = _currency.Currency.round( - product.meta["untaxed_price"] * ((100.0 - discount) / 100.0), - currency - ) if discount else product.meta["untaxed_price"] - product.price = _currency.Currency.round( - product.meta["retail_price"] * ((100.0 - discount) / 100.0), - currency - ) if discount else product.meta["retail_price"] + untaxed_price = ( + _currency.Currency.round( + product.meta["untaxed_price"] * ((100.0 - discount) / 100.0), + currency, + ) + if discount + else product.meta["untaxed_price"] + ) + product.price = ( + _currency.Currency.round( + product.meta["retail_price"] * ((100.0 - discount) / 100.0), + currency, + ) + if discount + else product.meta["retail_price"] + ) product.taxes = product.price - untaxed_price if not product.price_compare and discount: product.price_compare = product.meta["retail_price"] @@ -550,13 +477,11 @@ def from_omni( @classmethod @appier.operation( - name = "Import Omni", - parameters = ( - ("Product", "product", "longtext"), - ), - factory = True + name="Import Omni", + parameters=(("Product", "product", "longtext"),), + factory=True, ) - def import_omni_s(cls, product, safe = True): + def import_omni_s(cls, product, safe=True): product = json.loads(product) product = cls.from_omni(product) product.save() @@ -564,14 +489,13 @@ def import_omni_s(cls, product, safe = True): @classmethod @appier.operation( - name = "Import CSV", - parameters = ( + name="Import CSV", + parameters=( ("CSV File", "file", "file"), - ("Empty source", "empty", bool, False) - ) + ("Empty source", "empty", bool, False), + ), ) def import_csv_s(cls, file, empty): - def callback(line): from . import color from . import brand @@ -581,27 +505,29 @@ def callback(line): from . import composition from . import measurement - description,\ - short_description,\ - product_id,\ - gender,\ - price,\ - order,\ - tag,\ - tag_description,\ - farfetch_url,\ - farfetch_male_url,\ - farfetch_female_url,\ - colors,\ - categories,\ - collections, \ - variants,\ - _brand,\ - _season,\ - measurements,\ - compositions, \ - price_provider, \ - price_url = line + ( + description, + short_description, + product_id, + gender, + price, + order, + tag, + tag_description, + farfetch_url, + farfetch_male_url, + farfetch_female_url, + colors, + categories, + collections, + variants, + _brand, + _season, + measurements, + compositions, + price_provider, + price_url, + ) = line product_id = product_id or None price = float(price) if price else None @@ -615,61 +541,59 @@ def callback(line): price_url = price_url or None colors = colors.split(";") if colors else [] - colors = color.Color.find(name = {"$in" : colors}) + colors = color.Color.find(name={"$in": colors}) categories = categories.split(";") if categories else [] - categories = category.Category.find(name = {"$in" : categories}) + categories = category.Category.find(name={"$in": categories}) collections = collections.split(";") if collections else [] - collections = collection.Collection.find(name = {"$in" : collections}) + collections = collection.Collection.find(name={"$in": collections}) variants = variants.split(";") if variants else [] - variants = Product.find(product_id = {"$in" : variants}) + variants = Product.find(product_id={"$in": variants}) - _brand = brand.Brand.find(name = _brand) if _brand else None - _season = season.Season.find(name = _season) if _season else None + _brand = brand.Brand.find(name=_brand) if _brand else None + _season = season.Season.find(name=_season) if _season else None measurements = measurements.split(";") if measurements else [] - measurements = measurement.Measurement.find(name = {"$in" : measurements}) + measurements = measurement.Measurement.find(name={"$in": measurements}) compositions = compositions.split(";") if compositions else [] - compositions = composition.Composition.find(name = {"$in" : compositions}) + compositions = composition.Composition.find(name={"$in": compositions}) product = cls( - description = description, - short_description = short_description, - product_id = product_id, - gender = gender, - price = price, - order = order, - tag = tag, - tag_description = tag_description, - farfetch_url = farfetch_url, - farfetch_male_url = farfetch_male_url, - farfetch_female_url = farfetch_female_url, - colors = colors, - categories = categories, - collections = collections, - variants = variants, - brand = _brand, - season = _season, - measurements = measurements, - compositions = compositions, - price_provider = price_provider, - price_url = price_url + description=description, + short_description=short_description, + product_id=product_id, + gender=gender, + price=price, + order=order, + tag=tag, + tag_description=tag_description, + farfetch_url=farfetch_url, + farfetch_male_url=farfetch_male_url, + farfetch_female_url=farfetch_female_url, + colors=colors, + categories=categories, + collections=collections, + variants=variants, + brand=_brand, + season=_season, + measurements=measurements, + compositions=compositions, + price_provider=price_provider, + price_url=price_url, ) product.save() - if empty: cls.delete_c() + if empty: + cls.delete_c() cls._csv_import(file, callback) @classmethod - @appier.link(name = "Export Simple") - def simple_csv_url(cls, absolute = False): - return appier.get_app().url_for( - "product_api.simple_csv", - absolute = absolute - ) + @appier.link(name="Export Simple") + def simple_csv_url(cls, absolute=False): + return appier.get_app().url_for("product_api.simple_csv", absolute=absolute) @classmethod def _build(cls, model, map): @@ -687,8 +611,10 @@ def _build(cls, model, map): # calculates the shipping costs taking into account if the price # is currently defined for the product defaulting to none otherwise - if price == None: shipping_cost = None - else: shipping_cost = bundle.Bundle.eval_shipping(price, 0.0, 1.0, None) + if price == None: + shipping_cost = None + else: + shipping_cost = bundle.Bundle.eval_shipping(price, 0.0, 1.0, None) # sets the multiple attributes of the product that describe it through # calculated attributes (as expected by model retriever) @@ -707,19 +633,30 @@ def pre_validate(self): def pre_save(self): base.BudyBase.pre_save(self) - if not self.measurements: return - quantities_hand = [measurement.quantity_hand or 0.0 for measurement in\ - self.measurements if hasattr(measurement, "quantity_hand") and\ - not measurement.quantity_hand == None] - prices = [measurement.price or 0.0 for measurement in\ - self.measurements if hasattr(measurement, "price") and\ - not measurement.price == None] - taxes = [measurement.taxes or 0.0 for measurement in\ - self.measurements if hasattr(measurement, "taxes") and\ - not measurement.taxes == None] - prices_compare = [measurement.price_compare or 0.0 for measurement in\ - self.measurements if hasattr(measurement, "price_compare") and\ - not measurement.price_compare == None] + if not self.measurements: + return + quantities_hand = [ + measurement.quantity_hand or 0.0 + for measurement in self.measurements + if hasattr(measurement, "quantity_hand") + and not measurement.quantity_hand == None + ] + prices = [ + measurement.price or 0.0 + for measurement in self.measurements + if hasattr(measurement, "price") and not measurement.price == None + ] + taxes = [ + measurement.taxes or 0.0 + for measurement in self.measurements + if hasattr(measurement, "taxes") and not measurement.taxes == None + ] + prices_compare = [ + measurement.price_compare or 0.0 + for measurement in self.measurements + if hasattr(measurement, "price_compare") + and not measurement.price_compare == None + ] self.quantity_hand = sum(quantities_hand) if quantities_hand else None self.price = max(prices) if prices else 0.0 self.taxes = max(taxes) if taxes else 0.0 @@ -733,10 +670,10 @@ def pre_save(self): self.quantity_hand = 1 def build_images(self): - thumbnail = self.get_image(size = "thumbnail", order = 1) - thumbnail = thumbnail or self.get_image(size = "thumbnail") - image = self.get_image(size = "large", order = 1) - image = image or self.get_image(size = "large") + thumbnail = self.get_image(size="thumbnail", order=1) + thumbnail = thumbnail or self.get_image(size="thumbnail") + image = self.get_image(size="large", order=1) + image = image or self.get_image(size="large") self.thumbnail_url = thumbnail.get_url() if thumbnail else None self.image_url = image.get_url() if image else None @@ -755,109 +692,84 @@ def build_labels(self): self._build_labels(self.categories) self._build_labels(self.collections) - def related(self, limit = 6, available = True, enabled = True): + def related(self, limit=6, available=True, enabled=True): cls = self.__class__ kwargs = dict() - if available: kwargs["quantity_hand"] = {"$gt" : 0} - if self.collections: kwargs["collections"] = {"$in" : [self.collections[0].id]} - elif self.categories: kwargs["categories"] = {"$in" : [self.categories[0].id]} - elif self.colors: kwargs["colors"] = {"$in" : [self.colors[0].id]} - elif self.brand: kwargs["brand"] = {"$in" : [self.brand.id]} - elif self.season: kwargs["season"] = {"$in" : [self.season.id]} - kwargs["id"] = {"$nin" : [self.id]} + if available: + kwargs["quantity_hand"] = {"$gt": 0} + if self.collections: + kwargs["collections"] = {"$in": [self.collections[0].id]} + elif self.categories: + kwargs["categories"] = {"$in": [self.categories[0].id]} + elif self.colors: + kwargs["colors"] = {"$in": [self.colors[0].id]} + elif self.brand: + kwargs["brand"] = {"$in": [self.brand.id]} + elif self.season: + kwargs["season"] = {"$in": [self.season.id]} + kwargs["id"] = {"$nin": [self.id]} kwargs["sort"] = [("id", 1)] count = cls.count(**kwargs) - skip = self._get_offset(count, limit, kwargs = kwargs) + skip = self._get_offset(count, limit, kwargs=kwargs) delta = skip + limit - count - if delta > 0: skip = count - skip - delta - if skip < 0: skip = 0 + if delta > 0: + skip = count - skip - delta + if skip < 0: + skip = 0 find = cls.find_e if enabled else cls.find - products = find( - eager = ("images",), - skip = skip, - limit = limit, - map = True, - **kwargs - ) + products = find(eager=("images",), skip=skip, limit=limit, map=True, **kwargs) return products - def get_measurement(self, value, name = None): + def get_measurement(self, value, name=None): for measurement in self.measurements: - if not measurement: continue - if not hasattr(measurement, "value"): continue - if not hasattr(measurement, "name"): continue - if not measurement.value == value: continue - if not measurement.name == name: continue + if not measurement: + continue + if not hasattr(measurement, "value"): + continue + if not hasattr(measurement, "name"): + continue + if not measurement.value == value: + continue + if not measurement.name == name: + continue return measurement return None - def get_price( - self, - currency = None, - country = None, - attributes = None - ): - if not self.price_provider: return self.price + def get_price(self, currency=None, country=None, attributes=None): + if not self.price_provider: + return self.price method = getattr(self, "get_price_%s" % self.price_provider) - return method( - currency = currency, - country = country, - attributes = attributes - ) + return method(currency=currency, country=country, attributes=attributes) - def get_taxes( - self, - currency = None, - country = None, - attributes = None - ): - if not self.price_provider: return self.taxes + 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 - ) + if not method: + return self.taxes + return method(currency=currency, country=country, attributes=attributes) - def get_price_ripe( - self, - currency = None, - country = None, - attributes = None - ): - if not self.price_url: return self.price + def get_price_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 + 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 + 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 + 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 - ): + def get_availability_ripe(self, currency=None, country=None, attributes=None): attributes_m = json.loads(attributes) p = [] parts = attributes_m.get("parts", {}) @@ -870,184 +782,168 @@ def get_availability_ripe( triplet = "%s:%s:%s" % (key, material, color) p.append(triplet) - params = dict( - product_id = self.product_id, - p = p - ) - if currency: params["currency"] = currency - if country: params["country"] = country - if embossing: params["embossing"] = embossing - if letters: params["letters"] = letters - - result = appier.get( - self.price_url, - params = params - ) + params = dict(product_id=self.product_id, p=p) + if currency: + params["currency"] = currency + if country: + params["country"] = country + if embossing: + params["embossing"] = embossing + if letters: + params["letters"] = letters + + result = appier.get(self.price_url, params=params) return result - def get_currency(self, currency = None): - if not self.price_provider: return self.currency or currency + def get_currency(self, currency=None): + if not self.price_provider: + return self.currency or currency method = getattr(self, "get_currency_%s" % self.price_provider) - return method(currency = currency) + return method(currency=currency) - def get_currency_ripe(self, currency = None): + def get_currency_ripe(self, currency=None): return self.currency or currency - def get_image(self, size = None, order = None): + def get_image(self, size=None, order=None): for image in self.images: - if not image: continue - if not hasattr(image, "size"): continue - if not hasattr(image, "order"): continue + if not image: + continue + if not hasattr(image, "size"): + continue + if not hasattr(image, "order"): + continue is_size = size == None or image.size == size - if not is_size: continue + if not is_size: + continue is_order = order == None or image.order == order - if not is_order: continue + if not is_order: + continue return image return None - def get_size(self, currency = None, country = None, attributes = None): - if not self.price_provider: return None, None + def get_size(self, currency=None, country=None, attributes=None): + if not self.price_provider: + return None, None method = getattr(self, "get_size_%s" % self.price_provider) - return method( - country = country, - attributes = attributes - ) + return method(country=country, attributes=attributes) - def get_size_ripe( - self, - currency = None, - country = None, - attributes = None - ): + def get_size_ripe(self, currency=None, country=None, attributes=None): attributes_m = json.loads(attributes) size = attributes_m["size"] scale = attributes_m["scale"] gender = attributes_m["gender"] - if gender == "male": converter = lambda native: ((native - 17) / 2) + 36 - else: converter = lambda native: ((native - 17) / 2) + 34 + if gender == "male": + converter = lambda native: ((native - 17) / 2) + 36 + else: + converter = lambda native: ((native - 17) / 2) + 34 return converter(size), scale @appier.operation( - name = "Add Collection", - parameters = ( - ( - "Collection", - "collection", - appier.reference("Collection", name = "id") - ), - ) + name="Add Collection", + parameters=( + ("Collection", "collection", appier.reference("Collection", name="id")), + ), ) def add_collection_s(self, collection): - if not collection: return - if collection in self.collections: return + if not collection: + return + if collection in self.collections: + return self.collections.append(collection) self.save() @appier.operation( - name = "Remove Collection", - parameters = ( - ( - "Collection", - "collection", - appier.reference("Collection", name = "id") - ), - ) + name="Remove Collection", + parameters=( + ("Collection", "collection", appier.reference("Collection", name="id")), + ), ) def remove_collection_s(self, collection): - if not collection: return - if not collection in self.collections: return + if not collection: + return + if not collection in self.collections: + return self.collections.remove(collection) self.save() @appier.operation( - name = "Add Image", - parameters = ( - ( - "Image", - "image", - appier.reference("Media", name = "id") - ), - ) + name="Add Image", + parameters=(("Image", "image", appier.reference("Media", name="id")),), ) def add_image_s(self, image): - if not image: return - if image in self.images: return + if not image: + return + if image in self.images: + return self.images.append(image) self.save() @appier.operation( - name = "Remove Image", - parameters = ( - ( - "Image", - "image", - appier.reference("Media", name = "id") - ), - ) + name="Remove Image", + parameters=(("Image", "image", appier.reference("Media", name="id")),), ) def remove_image_s(self, image): - if not image: return - if not image in self.images: return + if not image: + return + if not image in self.images: + return self.images.remove(image) self.save() - @appier.operation(name = "Fix") + @appier.operation(name="Fix") def fix_s(self): - if not self.exists(): return + if not self.exists(): + return self.save() - @appier.operation(name = "Ensure Quantity", level = 2, devel = True) - def ensure_quantity_s(self, quantity = 1.0): - if self.quantity_hand: return + @appier.operation(name="Ensure Quantity", level=2, devel=True) + def ensure_quantity_s(self, quantity=1.0): + if self.quantity_hand: + return self.quantity_hand = quantity self.save() - @appier.operation(name = "Ensure Price", level = 2, devel = True) - def ensure_price_s(self, price = 100.0): - if self.price: return + @appier.operation(name="Ensure Price", level=2, devel=True) + def ensure_price_s(self, price=100.0): + if self.price: + return self.price = price self.save() - @appier.operation( - name = "Notify", - parameters = (("Email", "email", str),) - ) - def notify(self, name = None, *args, **kwargs): + @appier.operation(name="Notify", parameters=(("Email", "email", str),)) + def notify(self, name=None, *args, **kwargs): name = name or "product.new" - product = self.reload(map = True) + product = self.reload(map=True) receiver = kwargs.get("email", None) appier_extras.admin.Event.notify_g( name, - arguments = dict( - params = dict( - payload = product, - product = product, - receiver = receiver, - extra = kwargs + arguments=dict( + params=dict( + payload=product, product=product, receiver=receiver, extra=kwargs ) - ) + ), ) @appier.operation( - name = "Share", - parameters = ( + name="Share", + parameters=( ("Sender", "sender", appier.legacy.UNICODE), - ("Email", "email", str) - ) + ("Email", "email", str), + ), ) def share(self, *args, **kwargs): - self.notify(name = "product.share", *args, **kwargs) + self.notify(name="product.share", *args, **kwargs) @appier.operation( - name = "Quote", - parameters = ( + name="Quote", + parameters=( ("Requester", "requester", appier.legacy.UNICODE), ("Email", "email", str), ("Phone", "phone", str), - ("Observations", "observations", str) - ) + ("Observations", "observations", str), + ), ) def quote(self, *args, **kwargs): # patches the email attribute for the quote call so @@ -1059,17 +955,17 @@ def quote(self, *args, **kwargs): # runs the notification process for the product quote making # sure that all of the notification names are used - self.notify(name = "product.quote", *args, **kwargs_quote) - self.notify(name = "product.quote.confirmation", *args, **kwargs) + self.notify(name="product.quote", *args, **kwargs_quote) + self.notify(name="product.quote.confirmation", *args, **kwargs) - @appier.view(name = "Measurements") + @appier.view(name="Measurements") def measurements_v(self, *args, **kwargs): kwargs["sort"] = kwargs.get("sort", [("id", 1)]) return appier.lazy_dict( - model = self.measurements._target, - kwargs = kwargs, - entities = appier.lazy(lambda: self.measurements.find(*args, **kwargs)), - page = appier.lazy(lambda: self.measurements.paginate(*args, **kwargs)) + model=self.measurements._target, + kwargs=kwargs, + entities=appier.lazy(lambda: self.measurements.find(*args, **kwargs)), + page=appier.lazy(lambda: self.measurements.paginate(*args, **kwargs)), ) @property @@ -1078,19 +974,24 @@ def quantity(self): @property def discount(self): - if not self.price: return commons.Decimal(0.0) - if not self.price_compare: return commons.Decimal(0.0) + if not self.price: + return commons.Decimal(0.0) + if not self.price_compare: + return commons.Decimal(0.0) return self.price_compare - self.price @property def discount_percent(self): - if not self.discount: return commons.Decimal(0.0) + if not self.discount: + return commons.Decimal(0.0) return self.discount / self.price_compare * commons.Decimal(100.0) @property def is_parent(self): - if not hasattr(self, "measurements"): return False - if not self.measurements: return False + if not hasattr(self, "measurements"): + return False + if not self.measurements: + return False return len(self.measurements) > 0 @property @@ -1113,7 +1014,8 @@ def _build_labels(self, groups): groups = (groups,) if groups else [] for group in groups: for label in group.labels: - if label in self.labels: continue + if label in self.labels: + continue self.labels.append(label) def _get_offset(self, count, limit, kwargs): @@ -1125,6 +1027,6 @@ def _get_offset_simple(self, count, limit, kwargs): def _get_offset_offset(self, count, limit, kwargs): cls = self.__class__ kwargs = dict(kwargs) - kwargs["id"] = {"$lt" : self.id} + kwargs["id"] = {"$lt": self.id} offset = cls.count(**kwargs) return offset diff --git a/src/budy/models/referral.py b/src/budy/models/referral.py index 4dc7c971..54471200 100644 --- a/src/budy/models/referral.py +++ b/src/budy/models/referral.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,12 +32,9 @@ from . import base -class Referral(base.BudyBase): - name = appier.field( - index = True, - default = True - ) +class Referral(base.BudyBase): + name = appier.field(index=True, default=True) @classmethod def validate(cls): @@ -46,7 +43,7 @@ def validate(cls): appier.not_empty("name"), appier.string_gt("name", 3), appier.string_lt("name", 64), - appier.not_duplicate("name", cls._name()) + appier.not_duplicate("name", cls._name()), ] @classmethod @@ -59,20 +56,19 @@ def order_name(cls): @classmethod @appier.operation( - name = "Import CSV", - parameters = ( + name="Import CSV", + parameters=( ("CSV File", "file", "file"), - ("Empty source", "empty", bool, False) - ) + ("Empty source", "empty", bool, False), + ), ) 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 = cls(name=composed_name) referral.save() - if empty: cls.delete_c() + if empty: + cls.delete_c() cls._csv_import(file, callback) diff --git a/src/budy/models/season.py b/src/budy/models/season.py index eda9904a..dfb1eae7 100644 --- a/src/budy/models/season.py +++ b/src/budy/models/season.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -30,5 +30,6 @@ from . import group + class Season(group.Group): pass diff --git a/src/budy/models/section.py b/src/budy/models/section.py index 65dadc79..9f459153 100644 --- a/src/budy/models/section.py +++ b/src/budy/models/section.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,6 +32,7 @@ from . import group + class Section(group.Group): """ The section entity that represents an aggregation of groups @@ -43,11 +44,11 @@ class Section(group.Group): """ context_fields = appier.field( - type = list, - observations = """The name of the fields that are + type=list, + observations="""The name of the fields that are considered to define the context of the section and that as such are going to be used in the filtering - process of the associated products""" + process of the associated products""", ) """ Field that can be used to control the context filtering for the section by listing the names of the fields that are @@ -55,59 +56,32 @@ class Section(group.Group): all of the non empty groups are going to be used in the "view" """ invisible_fields = appier.field( - type = list, - observations = """The name of the fields (groups) that + type=list, + observations="""The name of the fields (groups) that although being used as part of the context selection for the section should not be displayed in the "normal" listing - UI for the section""" + UI for the section""", ) """ Field that controls the groups that are going to be hidden from the UI although being used for the context filtering""" - genders = appier.field( - type = list - ) + genders = appier.field(type=list) - collections = appier.field( - type = appier.references( - "Collection", - name = "id" - ) - ) + collections = appier.field(type=appier.references("Collection", name="id")) - categories = appier.field( - type = appier.references( - "Category", - name = "id" - ) - ) + categories = appier.field(type=appier.references("Category", name="id")) - colors = appier.field( - type = appier.references( - "Color", - name = "id" - ) - ) + colors = appier.field(type=appier.references("Color", name="id")) - brands = appier.field( - type = appier.references( - "Brand", - name = "id" - ) - ) + brands = appier.field(type=appier.references("Brand", name="id")) - seasons = appier.field( - type = appier.references( - "Season", - name = "id" - ) - ) + seasons = appier.field(type=appier.references("Season", name="id")) @classmethod def validate(cls): return super(Section, cls).validate() + [ appier.not_null("name"), - appier.not_empty("name") + appier.not_empty("name"), ] @classmethod @@ -115,36 +89,30 @@ def list_names(cls): return ["id", "name"] @appier.operation( - name = "Add Collection", - parameters = ( - ( - "Collection", - "collection", - appier.reference("Collection", name = "id") - ), - ) + name="Add Collection", + parameters=( + ("Collection", "collection", appier.reference("Collection", name="id")), + ), ) def add_collection_s(self, collection): - if not collection: return + if not collection: + return self.collections.append(collection) self.save() @appier.operation( - name = "Remove Collection", - parameters = ( - ( - "Collection", - "collection", - appier.reference("Collection", name = "id") - ), - ) + name="Remove Collection", + parameters=( + ("Collection", "collection", appier.reference("Collection", name="id")), + ), ) def remove_collection_s(self, collection): - if not collection: return + if not collection: + return self.collections.remove(collection) self.save() - @appier.operation(name = "Clear Groups", level = 2) + @appier.operation(name="Clear Groups", level=2) def clear_groups_s(self): self.collections = [] self.categories = [] diff --git a/src/budy/models/store.py b/src/budy/models/store.py index 79448a1c..5bf74ade 100644 --- a/src/budy/models/store.py +++ b/src/budy/models/store.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,6 +32,7 @@ from . import base + class Store(base.BudyBase): """ Represents a physical store entity that can be used @@ -39,40 +40,22 @@ class Store(base.BudyBase): the checkout process. """ - name = appier.field( - index = True, - default = True - ) - - address = appier.field( - type = appier.reference( - "Address", - name = "id" - ), - eager = True - ) - - currency_code = appier.field( - index = True - ) - - checkout_mode = appier.field( - index = True - ) - - restrict_mode = appier.field( - type = bool, - index = True, - initial = True - ) + name = appier.field(index=True, default=True) + + address = appier.field(type=appier.reference("Address", name="id"), eager=True) + + currency_code = appier.field(index=True) + + checkout_mode = appier.field(index=True) + + restrict_mode = appier.field(type=bool, index=True, initial=True) @classmethod def validate(cls): return super(Store, cls).validate() + [ appier.not_null("name"), appier.not_empty("name"), - - appier.string_eq("currency_code", 3) + appier.string_eq("currency_code", 3), ] @classmethod diff --git a/src/budy/models/subscription.py b/src/budy/models/subscription.py index 2a369613..ea1a0445 100644 --- a/src/budy/models/subscription.py +++ b/src/budy/models/subscription.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,12 +32,9 @@ from . import base -class Subscription(base.BudyBase): - email = appier.field( - index = True, - default = True - ) +class Subscription(base.BudyBase): + email = appier.field(index=True, default=True) @classmethod def validate(cls): @@ -45,7 +42,7 @@ def validate(cls): appier.not_null("email"), appier.not_empty("email"), appier.is_email("email"), - appier.not_duplicate("email", cls._name()) + appier.not_duplicate("email", cls._name()), ] @classmethod @@ -55,4 +52,5 @@ def list_names(cls): def pre_validate(self): base.BudyBase.pre_validate(self) - if hasattr(self, "email"): self.email = self.email.lower() + if hasattr(self, "email"): + self.email = self.email.lower() diff --git a/src/budy/models/voucher.py b/src/budy/models/voucher.py index 94210f78..b3d7c33b 100644 --- a/src/budy/models/voucher.py +++ b/src/budy/models/voucher.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -36,99 +36,82 @@ from . import base -class Voucher(base.BudyBase): - key = appier.field( - index = True, - safe = True, - immutable = True - ) +class Voucher(base.BudyBase): + key = appier.field(index=True, safe=True, immutable=True) amount = appier.field( - type = commons.Decimal, - index = True, - initial = commons.Decimal(0.0), - safe = True, - immutable = True + type=commons.Decimal, + index=True, + initial=commons.Decimal(0.0), + safe=True, + immutable=True, ) used_amount = appier.field( - type = commons.Decimal, - initial = commons.Decimal(0.0), - index = True, - safe = True + type=commons.Decimal, initial=commons.Decimal(0.0), index=True, safe=True ) percentage = appier.field( - type = commons.Decimal, - initial = commons.Decimal(0.0), - index = True, - safe = True, - immutable = True + type=commons.Decimal, + initial=commons.Decimal(0.0), + index=True, + safe=True, + immutable=True, ) currency = appier.field( - index = True, - safe = True, - observations = """The base currency to be used for + index=True, + safe=True, + observations="""The base currency to be used for the voucher, usage of the voucher under other currency - will be performed using proper exchange rate conversion""" + will be performed using proper exchange rate conversion""", ) - start = appier.field( - type = int, - index = True, - safe = True, - meta = "datetime" - ) + start = appier.field(type=int, index=True, safe=True, meta="datetime") - expiration = appier.field( - type = int, - index = True, - safe = True, - meta = "datetime" - ) + expiration = appier.field(type=int, index=True, safe=True, meta="datetime") usage_count = appier.field( - type = int, - initial = 0, - index = True, - safe = True, - observations = """The number of time the voucher has + type=int, + initial=0, + index=True, + safe=True, + observations="""The number of time the voucher has already been used, if this value reached the usage limit the voucher is marked as used and no further usages are - possible""" + possible""", ) usage_limit = appier.field( - type = int, - initial = 0, - index = True, - safe = True, - observations = """The maximum number of usages of the + type=int, + initial=0, + index=True, + safe=True, + observations="""The maximum number of usages of the voucher before it's considered invalid, if the number is set to zero the voucher is considered to have no - limit in the number of usages""" + limit in the number of usages""", ) unlimited = appier.field( - type = bool, - initial = False, - index = True, - safe = True, - observations = """Flag that indicated if the value based + type=bool, + initial=False, + index=True, + safe=True, + observations="""Flag that indicated if the value based voucher should not have its used amount deducted and instead - be considered an unlimited voucher""" + be considered an unlimited voucher""", ) used = appier.field( - type = bool, - initial = False, - index = True, - safe = True, - observations = """Calculated value used to determine if + type=bool, + initial=False, + index=True, + safe=True, + observations="""Calculated value used to determine if the voucher is already used and should not be used anymore - under any circumstance""" + under any circumstance""", ) @classmethod @@ -136,26 +119,19 @@ def validate(cls): return super(Voucher, cls).validate() + [ appier.not_empty("key"), appier.not_duplicate("key", cls._name()), - appier.not_null("amount"), appier.gte("amount", 0.0), - appier.not_null("used_amount"), appier.gte("used_amount", 0.0), - appier.not_null("percentage"), appier.gte("percentage", 0.0), appier.lte("percentage", 100.0), - appier.not_null("usage_count"), appier.gte("usage_count", 0), - appier.not_null("usage_limit"), appier.gte("usage_limit", 0), - appier.not_null("unlimited"), - - appier.not_null("used") + appier.not_null("used"), ] @classmethod @@ -167,7 +143,7 @@ def list_names(cls): "percentage", "start", "expiration", - "used" + "used", ] @classmethod @@ -180,80 +156,66 @@ def is_snapshot(cls): @classmethod @appier.operation( - name = "Create Value", - parameters = ( + name="Create Value", + parameters=( ("Key", "key", str), ("Amount", "amount", commons.Decimal), ("Currency", "currency", str), - ("Unlimited", "unlimited", bool, False) + ("Unlimited", "unlimited", bool, False), ), - factory = True + factory=True, ) def create_value_s(cls, key, amount, currency, unlimited): - voucher = cls( - key = key, - amount = amount, - currency = currency, - unlimited = unlimited - ) + voucher = cls(key=key, amount=amount, currency=currency, unlimited=unlimited) voucher.save() return voucher @classmethod @appier.operation( - name = "Create Value Multiple", - parameters = ( + name="Create Value Multiple", + parameters=( ("Key", "key", str), ("Amount", "amount", commons.Decimal), ("Currency", "currency", str), ("Unlimited", "unlimited", bool, False), - ("Count", "count", int, 1) - ) + ("Count", "count", int, 1), + ), ) def create_value_multiple_s(cls, key, amount, currency, unlimited, count): key = key or cls.secret_g() for index in appier.legacy.xrange(count): voucher = cls( - key = key + "-" + str(index), - amount = amount, - currency = currency, - unlimited = unlimited + key=key + "-" + str(index), + amount=amount, + currency=currency, + unlimited=unlimited, ) voucher.save() @classmethod @appier.operation( - name = "Create Percentage", - parameters = ( - ("Key", "key", str), - ("Percentage", "percentage", commons.Decimal) - ), - factory = True + name="Create Percentage", + parameters=(("Key", "key", str), ("Percentage", "percentage", commons.Decimal)), + factory=True, ) def create_percentage_s(cls, key, percentage): - voucher = cls( - key = key, - percentage = percentage - ) + voucher = cls(key=key, percentage=percentage) voucher.save() return voucher @classmethod @appier.operation( - name = "Create Percentage Multiple", - parameters = ( + name="Create Percentage Multiple", + parameters=( ("Key", "key", str), ("Percentage", "percentage", commons.Decimal), - ("Count", "count", int, 1) - ) + ("Count", "count", int, 1), + ), ) def create_percentage_multiple_s(cls, key, percentage, count): key = key or cls.secret_g() for index in appier.legacy.xrange(count): - voucher = cls( - key = key + "-" + str(index), - percentage = percentage - ) + voucher = cls(key=key + "-" + str(index), percentage=percentage) voucher.save() def pre_create(self): @@ -264,18 +226,20 @@ def pre_create(self): def pre_update(self): base.BudyBase.pre_update(self) - if not self.used and self.is_used(): self.used = True - if self.used and not self.is_used(): self.used = False + if not self.used and self.is_used(): + self.used = True + if self.used and not self.is_used(): + self.used = False - def use_s(self, amount, currency = None): + def use_s(self, amount, currency=None): amount_l = self.to_local(amount, currency) - appier.verify(self.is_valid(amount = amount, currency = currency)) + appier.verify(self.is_valid(amount=amount, currency=currency)) if self.is_value and not self.unlimited: self.used_amount += commons.Decimal(amount_l) self.usage_count += 1 self.save() - def disuse_s(self, amount, currency = None): + def disuse_s(self, amount, currency=None): appier.verify(self.usage_count > 0) amount_l = self.to_local(amount, currency) if self.is_value and not self.unlimited: @@ -284,131 +248,151 @@ def disuse_s(self, amount, currency = None): self.usage_count -= 1 self.save() - def discount(self, amount, currency = None): - if self.is_percent: return self.open_amount_p(amount, currency = currency) - else: return self.open_amount_r(currency = currency) + def discount(self, amount, currency=None): + if self.is_percent: + return self.open_amount_p(amount, currency=currency) + else: + return self.open_amount_r(currency=currency) - def to_local(self, amount, currency, reversed = False): + def to_local(self, amount, currency, reversed=False): from . import exchange_rate - if not amount: return amount - if not currency: return amount - if not self.currency: return amount - if currency == self.currency: return amount + + if not amount: + return amount + if not currency: + return amount + if not self.currency: + return amount + if currency == self.currency: + return amount return exchange_rate.ExchangeRate.convert( - amount, - currency, - self.currency, - reversed = reversed, - rounder = commons.floor + amount, currency, self.currency, reversed=reversed, rounder=commons.floor ) - def to_remote(self, amount, currency, reversed = True): + def to_remote(self, amount, currency, reversed=True): from . import exchange_rate - if not amount: return amount - if not currency: return amount - if not self.currency: return amount - if currency == self.currency: return amount + + if not amount: + return amount + if not currency: + return amount + if not self.currency: + return amount + if currency == self.currency: + return amount return exchange_rate.ExchangeRate.convert( - amount, - self.currency, - currency, - reversed = reversed, - rounder = commons.floor + amount, self.currency, currency, reversed=reversed, rounder=commons.floor ) def is_used(self): - if self.usage_limit and self.usage_count >= self.usage_limit: return True - if self.amount and commons.Decimal(self.used_amount) >= commons.Decimal(self.amount): return True + if self.usage_limit and self.usage_count >= self.usage_limit: + return True + if self.amount and commons.Decimal(self.used_amount) >= commons.Decimal( + self.amount + ): + return True return False - def is_valid(self, amount = None, currency = None): + def is_valid(self, amount=None, currency=None): current = time.time() amount_l = self.to_local(amount, currency) - if self.is_used(): return False - if self.used: return False - if not self.enabled: return False - if self.start and current < self.start: return False - if self.expiration and current > self.expiration: return False - if self.amount and amount_l and commons.Decimal(amount_l) > commons.Decimal(self.open_amount): return False - if currency and not self.is_valid_currency(currency): return False - if not self.amount and not self.percentage: return False + if self.is_used(): + return False + if self.used: + return False + if not self.enabled: + return False + if self.start and current < self.start: + return False + if self.expiration and current > self.expiration: + return False + if ( + self.amount + and amount_l + and commons.Decimal(amount_l) > commons.Decimal(self.open_amount) + ): + return False + if currency and not self.is_valid_currency(currency): + return False + if not self.amount and not self.percentage: + return False return True def is_valid_currency(self, currency): from . import exchange_rate - if not currency: return True - if not self.currency: return True - if currency == self.currency: return True + + if not currency: + return True + if not self.currency: + return True + if currency == self.currency: + return True has_to_remote = exchange_rate.ExchangeRate.has_rate(self.currency, currency) has_to_local = exchange_rate.ExchangeRate.has_rate(currency, self.currency) - if has_to_remote and has_to_local: return True + if has_to_remote and has_to_local: + return True return False - def open_amount_r(self, currency = None): + def open_amount_r(self, currency=None): open_amount = commons.Decimal(self.amount) - commons.Decimal(self.used_amount) return self.to_remote(open_amount, currency) - def open_amount_p(self, amount, currency = None): + def open_amount_p(self, amount, currency=None): from . import currency as _currency + decimal = self.percentage / 100.0 open_amount = commons.Decimal(amount) * decimal - if not currency: return open_amount + if not currency: + return open_amount return _currency.Currency.round(open_amount, currency) - @appier.operation( - name = "Notify", - parameters = (("Email", "email", str),) - ) - def notify(self, name = None, *args, **kwargs): + @appier.operation(name="Notify", parameters=(("Email", "email", str),)) + def notify(self, name=None, *args, **kwargs): name = name or "voucher.new" - voucher = self.reload(map = True) + voucher = self.reload(map=True) receiver = kwargs.get("email", None) appier_extras.admin.Event.notify_g( name, - arguments = dict( - params = dict( - payload = voucher, - voucher = voucher, - receiver = receiver, - extra = kwargs + arguments=dict( + params=dict( + payload=voucher, voucher=voucher, receiver=receiver, extra=kwargs ) - ) + ), ) - @appier.operation( - name = "Remind", - parameters = (("Email", "email", str),) - ) + @appier.operation(name="Remind", parameters=(("Email", "email", str),)) def remind(self, *args, **kwargs): - self.notify(name = "voucher.remind", *args, **kwargs) + self.notify(name="voucher.remind", *args, **kwargs) - @appier.view(name = "All Orders") + @appier.view(name="All Orders") def all_orders_v(self, *args, **kwargs): from . import order + kwargs["sort"] = kwargs.get("sort", [("created", -1)]) - kwargs.update(vouchers = {"$in" : (self.id,)}) + kwargs.update(vouchers={"$in": (self.id,)}) return appier.lazy_dict( - model = order.Order, - kwargs = kwargs, - entities = appier.lazy(lambda: order.Order.find(*args, **kwargs)), - page = appier.lazy(lambda: order.Order.paginate(*args, **kwargs)), - names = ["reference", "created", "total", "currency", "status"] + model=order.Order, + kwargs=kwargs, + entities=appier.lazy(lambda: order.Order.find(*args, **kwargs)), + page=appier.lazy(lambda: order.Order.paginate(*args, **kwargs)), + names=["reference", "created", "total", "currency", "status"], ) - @appier.view(name = "Paid Orders") + @appier.view(name="Paid Orders") def paid_orders_v(self, *args, **kwargs): from . import order + kwargs["sort"] = kwargs.get("sort", [("created", -1)]) kwargs.update( - vouchers = {"$in" : (self.id,)}, - paid = True, + vouchers={"$in": (self.id,)}, + paid=True, ) return appier.lazy_dict( - model = order.Order, - kwargs = kwargs, - entities = appier.lazy(lambda: order.Order.find(*args, **kwargs)), - page = appier.lazy(lambda: order.Order.paginate(*args, **kwargs)), - names = ["reference", "created", "total", "currency", "status"] + model=order.Order, + kwargs=kwargs, + entities=appier.lazy(lambda: order.Order.find(*args, **kwargs)), + page=appier.lazy(lambda: order.Order.paginate(*args, **kwargs)), + names=["reference", "created", "total", "currency", "status"], ) @property @@ -417,11 +401,11 @@ def open_amount(self): @property def is_percent(self): - if self.amount: return False - if self.percentage: return True - raise appier.OperationalError( - message = "No amount or percentage defined" - ) + if self.amount: + return False + if self.percentage: + return True + raise appier.OperationalError(message="No amount or percentage defined") @property def is_value(self): diff --git a/src/budy/models/wishlist.py b/src/budy/models/wishlist.py index 53d31279..031d63d8 100644 --- a/src/budy/models/wishlist.py +++ b/src/budy/models/wishlist.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -34,23 +34,11 @@ from . import bundle from . import wishlist_line + class Wishlist(bundle.Bundle): + lines = appier.field(type=appier.references("WishlistLine", name="id"), eager=True) - lines = appier.field( - type = appier.references( - "WishlistLine", - name = "id" - ), - eager = True - ) - - account = appier.field( - type = appier.reference( - "BudyAccount", - name = "id" - ), - eager = True - ) + account = appier.field(type=appier.reference("BudyAccount", name="id"), eager=True) @classmethod def list_names(cls): @@ -61,26 +49,32 @@ def line_cls(cls): return wishlist_line.WishlistLine @classmethod - def from_session(cls, ensure = True, raise_e = False): + def from_session(cls, ensure=True, raise_e=False): from . import BudyAccount - account = BudyAccount.from_session(raise_e = raise_e) - if account: return account.ensure_wishlist_s() + + account = BudyAccount.from_session(raise_e=raise_e) + if account: + return account.ensure_wishlist_s() session = appier.get_session() key = session.get("wishlist", None) - wishlist = cls.get(key = key, raise_e = raise_e) if key else None - if wishlist: return wishlist - if not ensure: return None + wishlist = cls.get(key=key, raise_e=raise_e) if key else None + if wishlist: + return wishlist + if not ensure: + return None wishlist = cls() wishlist.save() session["wishlist"] = wishlist.key return wishlist @classmethod - def ensure_s(cls, key = None): + def ensure_s(cls, key=None): from . import BudyAccount - account = BudyAccount.from_session(raise_e = False) - if account: return account.ensure_wishlist_s(key = key) - wishlist = cls(key = key) + + account = BudyAccount.from_session(raise_e=False) + if account: + return account.ensure_wishlist_s(key=key) + wishlist = cls(key=key) wishlist.save() return wishlist @@ -90,42 +84,40 @@ def pre_validate(self): def pre_delete(self): bundle.Bundle.pre_delete(self) - for line in self.lines: line.delete() + for line in self.lines: + line.delete() def add_line_s(self, line): line.wishlist = self return bundle.Bundle.add_line_s(self, line) - @appier.operation(name = "Garbage Collect") + @appier.operation(name="Garbage Collect") def collect_s(self): self.delete() - @appier.operation(name = "Fix Orphans") + @appier.operation(name="Fix Orphans") def fix_orphans_s(self): for line in self.lines: line.wishlist = self line.save() - @appier.operation(name = "Notify") - def notify(self, name = None, *args, **kwargs): + @appier.operation(name="Notify") + def notify(self, name=None, *args, **kwargs): name = name or "wishlist.new" - wishlist = self.reload(map = True) + wishlist = self.reload(map=True) account = wishlist.get("account", {}) account = kwargs.get("account", account) receiver = account.get("email", None) receiver = kwargs.get("email", receiver) appier_extras.admin.Event.notify_g( name, - arguments = dict( - params = dict( - payload = wishlist, - wishlist = wishlist, - receiver = receiver, - extra = kwargs + arguments=dict( + params=dict( + payload=wishlist, wishlist=wishlist, receiver=receiver, extra=kwargs ) - ) + ), ) - @appier.operation(name = "Remind") + @appier.operation(name="Remind") def remind(self): - self.notify(name = "wishlist.remind") + self.notify(name="wishlist.remind") diff --git a/src/budy/models/wishlist_line.py b/src/budy/models/wishlist_line.py index 44e76cf5..21e44bde 100644 --- a/src/budy/models/wishlist_line.py +++ b/src/budy/models/wishlist_line.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Hive Budy -# Copyright (c) 2008-2020 Hive Solutions Lda. +# Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Budy. # @@ -22,7 +22,7 @@ __author__ = "João Magalhães " """ The author(s) of the module """ -__copyright__ = "Copyright (c) 2008-2020 Hive Solutions Lda." +__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" @@ -32,14 +32,9 @@ from . import bundle_line -class WishlistLine(bundle_line.BundleLine): - wishlist = appier.field( - type = appier.reference( - "Wishlist", - name = "id" - ) - ) +class WishlistLine(bundle_line.BundleLine): + wishlist = appier.field(type=appier.reference("Wishlist", name="id")) @classmethod def list_names(cls): @@ -53,7 +48,8 @@ def pre_validate(self): bundle_line.BundleLine.pre_validate(self) self.try_valid() - @appier.operation(name = "Garbage Collect") + @appier.operation(name="Garbage Collect") def collect_s(self): - if appier.is_unset(self.bag): return + if appier.is_unset(self.bag): + return self.delete() diff --git a/src/budy/templates/partials/internal.html.tpl b/src/budy/templates/partials/internal.html.tpl index a8897881..dfbbecc2 100644 --- a/src/budy/templates/partials/internal.html.tpl +++ b/src/budy/templates/partials/internal.html.tpl @@ -9,7 +9,7 @@ {% block footer %}