Init Django-SHOP
This commit is contained in:
405
weirdlittleempire/models.py
Normal file
405
weirdlittleempire/models.py
Normal file
@@ -0,0 +1,405 @@
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user