Add "DISABLED_COMMERCE" feature to disable buying functionality
Introduce a global config flag `DISABLED_COMMERCE` to toggle buy functionality availability. Raise specific `DisabledCommerceError` when buying is disabled, ensuring end-users are informed. Additionally, reformat code for readability and consistency, improving overall maintainability.
This commit is contained in:
parent
96c3fe23b0
commit
85a49c4e8b
3 changed files with 92 additions and 69 deletions
|
|
@ -1,5 +1,9 @@
|
||||||
from django.core.exceptions import BadRequest
|
from django.core.exceptions import BadRequest, ImproperlyConfigured
|
||||||
|
|
||||||
|
|
||||||
class NotEnoughMoneyError(BadRequest):
|
class NotEnoughMoneyError(BadRequest):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DisabledCommerceError(ImproperlyConfigured):
|
||||||
|
pass
|
||||||
|
|
|
||||||
154
core/models.py
154
core/models.py
|
|
@ -39,7 +39,7 @@ from mptt.models import MPTTModel
|
||||||
|
|
||||||
from core.abstract import NiceModel
|
from core.abstract import NiceModel
|
||||||
from core.choices import ORDER_PRODUCT_STATUS_CHOICES, ORDER_STATUS_CHOICES
|
from core.choices import ORDER_PRODUCT_STATUS_CHOICES, ORDER_STATUS_CHOICES
|
||||||
from core.errors import NotEnoughMoneyError
|
from core.errors import NotEnoughMoneyError, DisabledCommerceError
|
||||||
from core.utils import get_product_uuid_as_path, get_random_code
|
from core.utils import get_product_uuid_as_path, get_random_code
|
||||||
from core.utils.lists import FAILED_STATUSES
|
from core.utils.lists import FAILED_STATUSES
|
||||||
from core.validators import validate_category_image_dimensions
|
from core.validators import validate_category_image_dimensions
|
||||||
|
|
@ -219,20 +219,22 @@ class Brand(NiceModel):
|
||||||
verbose_name=_("brand name"),
|
verbose_name=_("brand name"),
|
||||||
unique=True,
|
unique=True,
|
||||||
)
|
)
|
||||||
small_logo = ImageField(upload_to="brands/",
|
small_logo = ImageField(
|
||||||
blank=True,
|
upload_to="brands/",
|
||||||
null=True,
|
blank=True,
|
||||||
help_text=_("upload a logo representing this brand"),
|
null=True,
|
||||||
validators=[validate_category_image_dimensions],
|
help_text=_("upload a logo representing this brand"),
|
||||||
verbose_name=_("brand small image"),
|
validators=[validate_category_image_dimensions],
|
||||||
)
|
verbose_name=_("brand small image"),
|
||||||
big_logo = ImageField(upload_to="brands/",
|
)
|
||||||
blank=True,
|
big_logo = ImageField(
|
||||||
null=True,
|
upload_to="brands/",
|
||||||
help_text=_("upload a big logo representing this brand"),
|
blank=True,
|
||||||
validators=[validate_category_image_dimensions],
|
null=True,
|
||||||
verbose_name=_("brand big image"),
|
help_text=_("upload a big logo representing this brand"),
|
||||||
)
|
validators=[validate_category_image_dimensions],
|
||||||
|
verbose_name=_("brand big image"),
|
||||||
|
)
|
||||||
description = TextField( # noqa: DJ001
|
description = TextField( # noqa: DJ001
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
|
@ -330,7 +332,7 @@ class Product(NiceModel):
|
||||||
|
|
||||||
@rating.setter
|
@rating.setter
|
||||||
def rating(self, value):
|
def rating(self, value):
|
||||||
self.__dict__['rating'] = value
|
self.__dict__["rating"] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def feedbacks_count(self):
|
def feedbacks_count(self):
|
||||||
|
|
@ -502,16 +504,16 @@ class Order(NiceModel):
|
||||||
@property
|
@property
|
||||||
def total_price(self) -> float:
|
def total_price(self) -> float:
|
||||||
return (
|
return (
|
||||||
round(
|
round(
|
||||||
sum(
|
sum(
|
||||||
order_product.buy_price * order_product.quantity
|
order_product.buy_price * order_product.quantity
|
||||||
if order_product.status not in FAILED_STATUSES and order_product.buy_price is not None
|
if order_product.status not in FAILED_STATUSES and order_product.buy_price is not None
|
||||||
else 0.0
|
else 0.0
|
||||||
for order_product in self.order_products.all()
|
for order_product in self.order_products.all()
|
||||||
),
|
),
|
||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
or 0.0
|
or 0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -606,10 +608,11 @@ class Order(NiceModel):
|
||||||
raise Http404(_("promocode does not exist"))
|
raise Http404(_("promocode does not exist"))
|
||||||
return promocode.use(self)
|
return promocode.use(self)
|
||||||
|
|
||||||
def buy(self,
|
def buy(
|
||||||
force_balance: bool = False,
|
self, force_balance: bool = False, force_payment: bool = False, promocode_uuid: str | None = None
|
||||||
force_payment: bool = False,
|
) -> Self | Transaction | None:
|
||||||
promocode_uuid: str | None = None) -> Self | Transaction | None:
|
if config.DISABLED_COMMERCE:
|
||||||
|
raise DisabledCommerceError(_("you can not buy at this moment, please try again in a few minutes"))
|
||||||
|
|
||||||
if (not force_balance and not force_payment) or (force_balance and force_payment):
|
if (not force_balance and not force_payment) or (force_balance and force_payment):
|
||||||
raise ValueError(_("invalid force value"))
|
raise ValueError(_("invalid force value"))
|
||||||
|
|
@ -647,8 +650,11 @@ class Order(NiceModel):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def buy_without_registration(self, products: list, promocode_uuid: str, **kwargs) -> Transaction | None:
|
def buy_without_registration(self, products: list, promocode_uuid: str, **kwargs) -> Transaction | None:
|
||||||
|
if config.DISABLED_COMMERCE:
|
||||||
|
raise DisabledCommerceError(_("you can not buy at this moment, please try again in a few minutes"))
|
||||||
|
|
||||||
if len(products) < 1:
|
if len(products) < 1:
|
||||||
raise ValueError(_("you cannot purchase without registration an empty order!"))
|
raise ValueError(_("you cannot purchase an empty order!"))
|
||||||
|
|
||||||
customer_name = kwargs.pop("customer_name")
|
customer_name = kwargs.pop("customer_name")
|
||||||
customer_email = kwargs.pop("customer_email")
|
customer_email = kwargs.pop("customer_email")
|
||||||
|
|
@ -656,8 +662,11 @@ class Order(NiceModel):
|
||||||
|
|
||||||
if not all([customer_name, customer_email, customer_phone_number]):
|
if not all([customer_name, customer_email, customer_phone_number]):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
_("you cannot buy without registration, please provide the following information:"
|
_(
|
||||||
" customer name, customer email, customer phone number"))
|
"you cannot buy without registration, please provide the following information:"
|
||||||
|
" customer name, customer email, customer phone number"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
payment_method = kwargs.get("payment_method")
|
payment_method = kwargs.get("payment_method")
|
||||||
|
|
||||||
|
|
@ -670,17 +679,24 @@ class Order(NiceModel):
|
||||||
billing_customer_postal_code = billing_customer_address.pop("customer_postal_code")
|
billing_customer_postal_code = billing_customer_address.pop("customer_postal_code")
|
||||||
billing_customer_address_line = billing_customer_address.pop("customer_address_line")
|
billing_customer_address_line = billing_customer_address.pop("customer_address_line")
|
||||||
|
|
||||||
if not all([billing_customer_city, billing_customer_country, billing_customer_postal_code,
|
if not all(
|
||||||
billing_customer_address_line]):
|
[
|
||||||
|
billing_customer_city,
|
||||||
|
billing_customer_country,
|
||||||
|
billing_customer_postal_code,
|
||||||
|
billing_customer_address_line,
|
||||||
|
]
|
||||||
|
):
|
||||||
raise ValueError(_("you cannot create a momental order without providing a billing address"))
|
raise ValueError(_("you cannot create a momental order without providing a billing address"))
|
||||||
|
|
||||||
billing_address = Address.objects.get_or_create(user=None,
|
billing_address = Address.objects.get_or_create(
|
||||||
country=Country.objects.get(code=billing_customer_country),
|
user=None,
|
||||||
region=Region.objects.get(code=billing_customer_city),
|
country=Country.objects.get(code=billing_customer_country),
|
||||||
city=City.objects.get(name=billing_customer_city),
|
region=Region.objects.get(code=billing_customer_city),
|
||||||
postal_code=PostalCode.objects.get(
|
city=City.objects.get(name=billing_customer_city),
|
||||||
code=billing_customer_postal_code),
|
postal_code=PostalCode.objects.get(code=billing_customer_postal_code),
|
||||||
street=billing_customer_address_line)
|
street=billing_customer_address_line,
|
||||||
|
)
|
||||||
|
|
||||||
shipping_customer_address = kwargs.pop("shipping_customer_address")
|
shipping_customer_address = kwargs.pop("shipping_customer_address")
|
||||||
shipping_customer_city = shipping_customer_address.pop("customer_city")
|
shipping_customer_city = shipping_customer_address.pop("customer_city")
|
||||||
|
|
@ -692,14 +708,14 @@ class Order(NiceModel):
|
||||||
shipping_address = billing_address
|
shipping_address = billing_address
|
||||||
|
|
||||||
else:
|
else:
|
||||||
shipping_address = Address.objects.get_or_create(user=None,
|
shipping_address = Address.objects.get_or_create(
|
||||||
country=Country.objects.get(
|
user=None,
|
||||||
code=shipping_customer_country),
|
country=Country.objects.get(code=shipping_customer_country),
|
||||||
region=Region.objects.get(code=shipping_customer_city),
|
region=Region.objects.get(code=shipping_customer_city),
|
||||||
city=City.objects.get(name=billing_customer_city),
|
city=City.objects.get(name=billing_customer_city),
|
||||||
postal_code=PostalCode.objects.get(
|
postal_code=PostalCode.objects.get(code=shipping_customer_postal_code),
|
||||||
code=shipping_customer_postal_code),
|
street=shipping_customer_address_line,
|
||||||
street=shipping_customer_address_line)
|
)
|
||||||
|
|
||||||
for product_uuid in products:
|
for product_uuid in products:
|
||||||
self.add_product(product_uuid)
|
self.add_product(product_uuid)
|
||||||
|
|
@ -709,9 +725,13 @@ class Order(NiceModel):
|
||||||
self.status = "CREATED"
|
self.status = "CREATED"
|
||||||
self.shipping_address = shipping_address
|
self.shipping_address = shipping_address
|
||||||
self.billing_address = billing_address
|
self.billing_address = billing_address
|
||||||
self.attributes.update({"customer_name": customer_name,
|
self.attributes.update(
|
||||||
"customer_email": customer_email,
|
{
|
||||||
"customer_phone_number": customer_phone_number})
|
"customer_name": customer_name,
|
||||||
|
"customer_email": customer_email,
|
||||||
|
"customer_phone_number": customer_phone_number,
|
||||||
|
}
|
||||||
|
)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
return Transaction.objects.create(
|
return Transaction.objects.create(
|
||||||
|
|
@ -723,16 +743,16 @@ class Order(NiceModel):
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
if (
|
if (
|
||||||
self.order_products.filter(
|
self.order_products.filter(
|
||||||
status__in=[
|
status__in=[
|
||||||
"ACCEPTED",
|
"ACCEPTED",
|
||||||
"FAILED",
|
"FAILED",
|
||||||
"RETURNED",
|
"RETURNED",
|
||||||
"CANCELED",
|
"CANCELED",
|
||||||
"FINISHED",
|
"FINISHED",
|
||||||
]
|
]
|
||||||
).count()
|
).count()
|
||||||
== self.order_products.count()
|
== self.order_products.count()
|
||||||
):
|
):
|
||||||
self.status = "FINISHED"
|
self.status = "FINISHED"
|
||||||
self.save()
|
self.save()
|
||||||
|
|
@ -968,7 +988,7 @@ class PromoCode(NiceModel):
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
if (self.discount_amount is not None and self.discount_percent is not None) or (
|
if (self.discount_amount is not None and self.discount_percent is not None) or (
|
||||||
self.discount_amount is None and self.discount_percent is None
|
self.discount_amount is None and self.discount_percent is None
|
||||||
):
|
):
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("only one type of discount should be defined (amount or percent), but not both or neither.")
|
_("only one type of discount should be defined (amount or percent), but not both or neither.")
|
||||||
|
|
@ -991,13 +1011,11 @@ class PromoCode(NiceModel):
|
||||||
match self.discount_type:
|
match self.discount_type:
|
||||||
case "percent":
|
case "percent":
|
||||||
amount -= round(amount * (self.discount_percent / 100), 2)
|
amount -= round(amount * (self.discount_percent / 100), 2)
|
||||||
order.attributes.update({"promocode": str(self.uuid),
|
order.attributes.update({"promocode": str(self.uuid), "final_price": amount})
|
||||||
"final_price": amount})
|
|
||||||
order.save()
|
order.save()
|
||||||
case "amount":
|
case "amount":
|
||||||
amount -= round(float(self.discount_amount), 2)
|
amount -= round(float(self.discount_amount), 2)
|
||||||
order.attributes.update({"promocode": str(self.uuid),
|
order.attributes.update({"promocode": str(self.uuid), "final_price": amount})
|
||||||
"final_price": amount})
|
|
||||||
order.save()
|
order.save()
|
||||||
case _:
|
case _:
|
||||||
raise ValueError(_(f"invalid discount type for promocode {self.uuid}"))
|
raise ValueError(_(f"invalid discount type for promocode {self.uuid}"))
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ CONSTANCE_CONFIG = {
|
||||||
"Abstract API Key, if empty - no Abstract features provided",
|
"Abstract API Key, if empty - no Abstract features provided",
|
||||||
),
|
),
|
||||||
"HTTP_PROXY": (getenv("DJANGO_HTTP_PROXY", "http://username:password@proxy_address:port"), "HTTP Proxy"),
|
"HTTP_PROXY": (getenv("DJANGO_HTTP_PROXY", "http://username:password@proxy_address:port"), "HTTP Proxy"),
|
||||||
|
"DISABLED_COMMERCE": (getenv("DISABLED_COMMERCE", False), "Disable buy functionality"),
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPOSABLE_KEYS = [
|
EXPOSABLE_KEYS = [
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue