Files
empire/weirdlittleempire/models.py
2021-02-23 23:00:01 +00:00

406 lines
11 KiB
Python

from decimal import Decimal
from django.core.exceptions import ObjectDoesNotExist
from django.core.validators import MinValueValidator
from django.db import models
from django.utils.translation import ugettext_lazy as _
from djangocms_text_ckeditor.fields import HTMLField
from cms.models.fields import PlaceholderField
from shop.money import Money, MoneyMaker
from shop.money.fields import MoneyField
from shop.models.product import BaseProduct, BaseProductManager, AvailableProductMixin, CMSPageReferenceMixin
from shop.models.defaults.cart import Cart
from shop.models.defaults.cart_item import CartItem
from shop.models.order import BaseOrderItem
from shop.models.defaults.delivery import Delivery
from shop.models.defaults.delivery_item import DeliveryItem
from shop.models.defaults.order import Order
from shop.models.defaults.mapping import ProductPage, ProductImage
from shop.models.defaults.address import BillingAddress, ShippingAddress
from shop.models.defaults.customer import Customer
__all__ = ['Cart', 'CartItem', 'Order', 'Delivery', 'DeliveryItem',
'BillingAddress', 'ShippingAddress', 'Customer', ]
class OrderItem(BaseOrderItem):
quantity = models.PositiveIntegerField(_("Ordered quantity"))
canceled = models.BooleanField(_("Item canceled "), default=False)
def populate_from_cart_item(self, cart_item, request):
super().populate_from_cart_item(cart_item, request)
# the product's unit_price must be fetched from the product's variant
try:
variant = cart_item.product.get_product_variant(
product_code=cart_item.product_code)
self._unit_price = Decimal(variant.unit_price)
except (KeyError, ObjectDoesNotExist) as e:
raise CartItem.DoesNotExist(e)
class Manufacturer(models.Model):
name = models.CharField(
_("Name"),
max_length=50,
unique=True,
)
def __str__(self):
return self.name
class ProductManager(BaseProductManager):
pass
class Product(CMSPageReferenceMixin, BaseProduct):
"""
Base class to describe a polymorphic product. Here we declare common fields available in all of
our different product types. These common fields are also used to build up the view displaying
a list of all products.
"""
product_name = models.CharField(
_("Product Name"),
max_length=255,
)
slug = models.SlugField(
_("Slug"),
unique=True,
)
caption = HTMLField(
verbose_name=_("Caption"),
blank=True,
null=True,
configuration='CKEDITOR_SETTINGS_CAPTION',
help_text=_(
"Short description used in the catalog's list view of products."),
)
# common product properties
manufacturer = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
verbose_name=_("Manufacturer"),
)
# controlling the catalog
order = models.PositiveIntegerField(
_("Sort by"),
db_index=True,
)
cms_pages = models.ManyToManyField(
'cms.Page',
through=ProductPage,
help_text=_("Choose list view this product shall appear on."),
)
images = models.ManyToManyField(
'filer.Image',
through=ProductImage,
)
class Meta:
ordering = ('order',)
verbose_name = _("Product")
verbose_name_plural = _("Products")
objects = ProductManager()
# filter expression used to lookup for a product item using the Select2 widget
lookup_fields = ['product_name__icontains']
def __str__(self):
return self.product_name
@property
def sample_image(self):
return self.images.first()
class Commodity(AvailableProductMixin, Product):
"""
This Commodity model inherits from polymorphic Product, and therefore has to be redefined.
"""
unit_price = MoneyField(
_("Unit price"),
decimal_places=3,
help_text=_("Net price for this product"),
)
product_code = models.CharField(
_("Product code"),
max_length=255,
unique=True,
)
quantity = models.PositiveIntegerField(
_("Quantity"),
default=0,
validators=[MinValueValidator(0)],
help_text=_("Available quantity in stock")
)
# controlling the catalog
placeholder = PlaceholderField("Commodity Details")
show_breadcrumb = True # hard coded to always show the product's breadcrumb
class Meta:
verbose_name = _("Commodity")
verbose_name_plural = _("Commodities")
default_manager = ProductManager()
def get_price(self, request):
return self.unit_price
class SmartCard(AvailableProductMixin, Product):
unit_price = MoneyField(
_("Unit price"),
decimal_places=3,
help_text=_("Net price for this product"),
)
card_type = models.CharField(
_("Card Type"),
choices=[2 * ('{}{}'.format(s, t),)
for t in ['SD', 'SDXC', 'SDHC', 'SDHC II'] for s in ['', 'micro ']],
max_length=15,
)
speed = models.CharField(
_("Transfer Speed"),
choices=[(str(s), "{} MB/s".format(s))
for s in [4, 20, 30, 40, 48, 80, 95, 280]],
max_length=8,
)
product_code = models.CharField(
_("Product code"),
max_length=255,
unique=True,
)
storage = models.PositiveIntegerField(
_("Storage Capacity"),
help_text=_("Storage capacity in GB"),
)
description = HTMLField(
_("Description"),
configuration='CKEDITOR_SETTINGS_DESCRIPTION',
help_text=_("Long description for the detail view of this product."),
)
quantity = models.PositiveIntegerField(
_("Quantity"),
default=0,
validators=[MinValueValidator(0)],
help_text=_("Available quantity in stock")
)
class Meta:
verbose_name = _("Smart Card")
verbose_name_plural = _("Smart Cards")
ordering = ['order']
# filter expression used to lookup for a product item using the Select2 widget
lookup_fields = ['product_code__startswith', 'product_name__icontains']
def get_price(self, request):
return self.unit_price
default_manager = ProductManager()
class OperatingSystem(models.Model):
name = models.CharField(
_("Name"),
max_length=50,
unique=True,
)
def __str__(self):
return self.name
class SmartPhoneModel(Product):
"""
A generic smart phone model, which must be concretized by a `SmartPhoneVariant` - see below.
"""
BATTERY_TYPES = [
(1, "Lithium Polymer (Li-Poly)"),
(2, "Lithium Ion (Li-Ion)"),
]
WIFI_CONNECTIVITY = [
(1, "802.11 b/g/n"),
]
BLUETOOTH_CONNECTIVITY = [
(1, "Bluetooth 4.0"),
(2, "Bluetooth 3.0"),
(3, "Bluetooth 2.1"),
]
battery_type = models.PositiveSmallIntegerField(
_("Battery type"),
choices=BATTERY_TYPES,
)
battery_capacity = models.PositiveIntegerField(
_("Capacity"),
help_text=_("Battery capacity in mAh"),
)
ram_storage = models.PositiveIntegerField(
_("RAM"),
help_text=_("RAM storage in MB"),
)
wifi_connectivity = models.PositiveIntegerField(
_("WiFi"),
choices=WIFI_CONNECTIVITY,
help_text=_("WiFi Connectivity"),
)
bluetooth = models.PositiveIntegerField(
_("Bluetooth"),
choices=BLUETOOTH_CONNECTIVITY,
help_text=_("Bluetooth Connectivity"),
)
gps = models.BooleanField(
_("GPS"),
default=False,
help_text=_("GPS integrated"),
)
operating_system = models.ForeignKey(
OperatingSystem,
on_delete=models.CASCADE,
verbose_name=_("Operating System"),
)
width = models.DecimalField(
_("Width"),
max_digits=4,
decimal_places=1,
help_text=_("Width in mm"),
)
height = models.DecimalField(
_("Height"),
max_digits=4,
decimal_places=1,
help_text=_("Height in mm"),
)
weight = models.DecimalField(
_("Weight"),
max_digits=5,
decimal_places=1,
help_text=_("Weight in gram"),
)
screen_size = models.DecimalField(
_("Screen size"),
max_digits=4,
decimal_places=2,
help_text=_("Diagonal screen size in inch"),
)
description = HTMLField(
verbose_name=_("Description"),
configuration='CKEDITOR_SETTINGS_DESCRIPTION',
help_text=_(
"Full description used in the catalog's detail view of Smart Phones."),
)
class Meta:
verbose_name = _("Smart Phone")
verbose_name_plural = _("Smart Phones")
default_manager = ProductManager()
def get_price(self, request):
"""
Return the starting price for instances of this smart phone model.
"""
if not hasattr(self, '_price'):
if self.variants.exists():
currency = self.variants.first().unit_price.currency
aggr = self.variants.aggregate(models.Min('unit_price'))
self._price = MoneyMaker(currency)(aggr['unit_price__min'])
else:
self._price = Money()
return self._price
def get_availability(self, request, **kwargs):
variant = self.get_product_variant(**kwargs)
return variant.get_availability(request)
def deduct_from_stock(self, quantity, **kwargs):
variant = self.get_product_variant(**kwargs)
variant.deduct_from_stock(quantity)
def is_in_cart(self, cart, watched=False, **kwargs):
try:
product_code = kwargs['product_code']
except KeyError:
return
cart_item_qs = CartItem.objects.filter(cart=cart, product=self)
for cart_item in cart_item_qs:
if cart_item.product_code == product_code:
return cart_item
def get_product_variant(self, **kwargs):
try:
product_code = kwargs.get('product_code')
return self.variants.get(product_code=product_code)
except SmartPhoneVariant.DoesNotExist as e:
raise SmartPhoneModel.DoesNotExist(e)
def get_product_variants(self):
return self.variants.all()
class SmartPhoneVariant(AvailableProductMixin, models.Model):
product = models.ForeignKey(
SmartPhoneModel,
on_delete=models.CASCADE,
verbose_name=_("Smartphone Model"),
related_name='variants',
)
product_code = models.CharField(
_("Product code"),
max_length=255,
unique=True,
)
unit_price = MoneyField(
_("Unit price"),
decimal_places=3,
help_text=_("Net price for this product"),
)
storage = models.PositiveIntegerField(
_("Internal Storage"),
help_text=_("Internal storage in GB"),
)
quantity = models.PositiveIntegerField(
_("Quantity"),
default=0,
validators=[MinValueValidator(0)],
help_text=_("Available quantity in stock")
)
def __str__(self):
return _("{product} with {storage} GB").format(product=self.product, storage=self.storage)
def get_price(self, request):
return self.unit_price