def test_deconstruct():
    f1 = MoneyField(currency='EUR', default=EUR(0))
    name, path, args, kwargs = f1.deconstruct()
    f2 = MoneyField(*args, **kwargs)
    assert f1.currency_code == f2.currency_code
    assert f1.decimal_places == f2.decimal_places
    assert f1.default == f2.default
def test_get_default():
    OneEuro = EUR(1)
    f = MoneyField(currency='EUR', null=True)
    assert f.get_default() is None
    f = MoneyField(currency='EUR', null=True, default=EUR())
    assert f.get_default() == EUR()
    f = MoneyField(currency='EUR', null=False, default=OneEuro)
    assert f.get_default() == OneEuro
Пример #3
0
class SmartCard(Product):
    # common product fields
    unit_price = MoneyField(_("Unit price"), decimal_places=3,
        help_text=_("Net price for this product"))

    # product properties
    CARD_TYPE = (2 * ('{}{}'.format(s, t),)
                 for t in ('SD', 'SDXC', 'SDHC', 'SDHC II') for s in ('', 'micro '))
    card_type = models.CharField(_("Card Type"), choices=CARD_TYPE, max_length=15)
    SPEED = ((str(s), "{} MB/s".format(s)) for s in (4, 20, 30, 40, 48, 80, 95, 280))
    speed = models.CharField(_("Transfer Speed"), choices=SPEED, 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"))
    multilingual = TranslatedFields(description=HTMLField(verbose_name=_("Description"),
        configuration='CKEDITOR_SETTINGS_DESCRIPTION',
        help_text=_("Full description used in the catalog's detail view of Smart Cards.")))

    class Meta:
        verbose_name = _("Smart Card")
        verbose_name_plural = _("Smart Cards")

    def get_price(self, request):
        return self.unit_price

    def get_product_variant(self, extra):
        """
        SmartCards do not have flavors, they are the product.
        """
        return self
Пример #4
0
class SmartCard(CMSPageReferenceMixin, TranslatableModelMixin, BaseProduct):
    product_name = models.CharField(max_length=255,
                                    verbose_name=_("Product Name"))
    slug = models.SlugField(verbose_name=_("Slug"))
    unit_price = MoneyField(_("Unit price"),
                            decimal_places=3,
                            help_text=_("Net price for this product"))
    description = TranslatedField()

    # product properties
    manufacturer = models.ForeignKey(Manufacturer,
                                     verbose_name=_("Manufacturer"))
    CARD_TYPE = (2 * ('{}{}'.format(s, t), )
                 for t in ('SD', 'SDXC', 'SDHC', 'SDHC II')
                 for s in ('', 'micro '))
    card_type = models.CharField(_("Card Type"),
                                 choices=CARD_TYPE,
                                 max_length=15)
    SPEED = ((str(s), "{} MB/s".format(s))
             for s in (4, 20, 30, 40, 48, 80, 95, 280))
    speed = models.CharField(_("Transfer Speed"), choices=SPEED, 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"))

    # controlling the catalog
    order = models.PositiveIntegerField(verbose_name=_("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)

    objects = ProductManager()

    # filter expression used to lookup for a product item using the Select2 widget
    lookup_fields = (
        'product_code__startswith',
        'product_name__icontains',
    )

    class Meta:
        verbose_name = _("Smart Card")
        verbose_name_plural = _("Smart Cards")
        ordering = ('order', )

    objects = ProductManager()

    def __str__(self):
        return self.product_name

    @property
    def sample_image(self):
        return self.images.first()

    def get_price(self, request):
        return self.unit_price
Пример #5
0
class OrderPayment(with_metaclass(deferred.ForeignKeyBuilder, models.Model)):
    """
    A model to hold received payments for a given order.
    """
    order = deferred.ForeignKey(
        BaseOrder,
        verbose_name=_("Order"),
    )

    amount = MoneyField(
        _("Amount paid"),
        help_text=_("How much was paid with this particular transfer."),
    )

    transaction_id = models.CharField(
        _("Transaction ID"),
        max_length=255,
        help_text=_("The transaction processor's reference"),
    )

    created_at = models.DateTimeField(
        _("Received at"),
        auto_now_add=True,
    )

    payment_method = models.CharField(
        _("Payment method"),
        max_length=50,
        help_text=_("The payment backend used to process the purchase"),
    )

    class Meta:
        verbose_name = pgettext_lazy('order_models', "Order payment")
        verbose_name_plural = pgettext_lazy('order_models', "Order payments")
Пример #6
0
class KhimageVariant(models.Model):
    product = models.ForeignKey(
        KhimageModel,
        verbose_name=_("Khimage 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 MB"),
    )

    def get_price(self, request):
        return self.unit_price
Пример #7
0
class Commodity(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,
    )

    # controlling the catalog
    placeholder = PlaceholderField("Commodity Details")
    show_breadcrumb = True  # hard coded to always show the product's breadcrumb

    default_manager = TranslatableManager()

    class Meta:
        verbose_name = _("Commodity")
        verbose_name_plural = _("Commodities")

    def get_price(self, request):
        return self.unit_price
Пример #8
0
class Membership(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,
    )

    signup_date = models.DateField(
        _("Signed Up"),
        default=timezone.now,
    )

    profile = models.ForeignKey(User, null=True, blank=True)

    # controlling the catalog
    placeholder = PlaceholderField("Membership Details")
    show_breadcrumb = True  # hard coded to always show the product's breadcrumb

    class Meta:
        verbose_name = _("Membership")

    def get_price(self, request):
        return self.unit_price
Пример #9
0
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")

    def get_price(self, request):
        return self.unit_price
Пример #10
0
class SmartCard(Product):
    # common product fields
    unit_price = MoneyField(_("Unit price"), decimal_places=3,
        help_text=_("Net price for this product"))

    # product properties
    CARD_TYPE = (2 * ('{}{}'.format(s, t),)
                 for t in ('SD', 'SDXC', 'SDHC', 'SDHC II') for s in ('', 'micro '))
    card_type = models.CharField(_("Card Type"), choices=CARD_TYPE, max_length=15)
    SPEED = ((str(s), "{} MB/s".format(s)) for s in (4, 20, 30, 40, 48, 80, 95, 280))
    speed = models.CharField(_("Transfer Speed"), choices=SPEED, 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"))

    class Meta:
        verbose_name = _("Smart Card")
        verbose_name_plural = _("Smart Cards")

    def get_price(self, request):
        return self.unit_price

    def get_product_markedness(self, extra):
        """
        SmartCards do not have a markedness, they are the product.
        """
        return self
Пример #11
0
class SmartCard(AvailableProductMixin, Product):
    multilingual = TranslatedFields(
        description=HTMLField(
            verbose_name=_("Description"),
            configuration='CKEDITOR_SETTINGS_DESCRIPTION',
            help_text=_(
                "Full description used in the catalog's detail view of Smart Cards."),
        ),
    )
    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"),
    )

    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()
def test_to_python():
    f = MoneyField(currency='EUR', null=True)
    assert f.to_python(3) == EUR('3')
    assert f.to_python('3.14') == EUR('3.14')
    assert f.to_python(None) == EUR()
    assert f.to_python(EUR(3)) == EUR('3')
    with pytest.raises(ValidationError):
        f.to_python('abc')
Пример #13
0
    class Commodity(CMSPageReferenceMixin, TranslatableModelMixin,
                    BaseProduct):
        """
        Generic Product Commodity to be used whenever the merchant does not require product specific
        attributes and just required a placeholder field to add arbitrary data.
        """
        # common product fields
        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"))

        # controlling the catalog
        order = models.PositiveIntegerField(verbose_name=_("Sort by"),
                                            db_index=True)
        cms_pages = models.ManyToManyField(
            'cms.Page',
            through=ProductPage,
            help_text=_("Choose list view this product shall appear on."))
        sample_image = image.FilerImageField(
            verbose_name=_("Sample Image"),
            blank=True,
            null=True,
            help_text=_("Sample image used in the catalog's list view."))
        show_breadcrumb = models.BooleanField(
            _("Show Breadcrumb"),
            default=True,
            help_text=_(
                "Shall the detail page show the product's breadcrumb."))
        placeholder = PlaceholderField("Commodity Details")

        # translatable fields for the catalog's list- and detail views
        product_name = TranslatedField()
        slug = TranslatedField()
        caption = TranslatedField()

        # filter expression used to search for a product item using the Select2 widget
        lookup_fields = (
            'product_code__startswith',
            'product_name__icontains',
        )

        objects = ProductManager()

        class Meta:
            app_label = app_settings.APP_LABEL
            ordering = ('order', )
            verbose_name = _("Commodity")
            verbose_name_plural = _("Commodities")

        def __str__(self):
            return self.product_code

        def get_price(self, request):
            return self.unit_price
Пример #14
0
class SmartCard(Product):
    # common product fields
    unit_price = MoneyField(
        _("Unit price"),
        decimal_places=3,
        help_text=_("Net price for this product"),
    )

    # product properties
    CARD_TYPE = (2 * ('{}{}'.format(s, t), )
                 for t in ('SD', 'SDXC', 'SDHC', 'SDHC II')
                 for s in ('', 'micro '))
    card_type = models.CharField(
        _("Card Type"),
        choices=CARD_TYPE,
        max_length=15,
    )

    SPEED = [(str(s), "{} MB/s".format(s))
             for s in (4, 20, 30, 40, 48, 80, 95, 280)]
    speed = models.CharField(
        _("Transfer Speed"),
        choices=SPEED,
        max_length=8,
    )

    product_code = models.CharField(
        _("Product code"),
        max_length=255,
        unique=True,
    )
    manufacturer = models.ForeignKey(
        Manufacturer,
        default=1,
        verbose_name=_("Manufacturer"),
    )
    storage = models.PositiveIntegerField(
        _("Storage Capacity"),
        help_text=_("Storage capacity in GB"),
    )

    description = HTMLField(
        verbose_name=_("Description"),
        configuration='CKEDITOR_SETTINGS_DESCRIPTION',
        help_text=_(
            "Full description used in the catalog's detail view of Smart Cards."
        ),
    )

    default_manager = BaseProductManager()

    class Meta:
        verbose_name = _("Smart Card")
        verbose_name_plural = _("Smart Cards")

    def get_price(self, request):
        return self.unit_price
Пример #15
0
class SmartPhone(models.Model):
    product = models.ForeignKey(SmartPhoneModel,
        verbose_name=_("Smart-Phone Model"))
    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 MB"))

    def get_price(self, request):
        return self.unit_price
Пример #16
0
class ShippingDestination(models.Model):
    shipping_method = models.ForeignKey(
        ShippingMethod,
        related_name='destinations',
    )

    country = models.CharField(max_length=3)

    price = MoneyField(currency='EUR')

    class Meta:
        verbose_name = _("Shipping Destination")
        verbose_name_plural = _("Shipping Destination")
        unique_together = ['country', 'shipping_method']
Пример #17
0
class ScooterVariant(models.Model):
    product = models.ForeignKey(
        ScooterModel,
        verbose_name=_("Scooter 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 MB"),
    #~ )

    #~ battery_capacity = models.DecimalField(
    #~ _("Battery capacity"),
    #~ max_digits=3,
    #~ decimal_places=1,
    #~ help_text=_("Battery capacity in Ah"),
    #~ )

    COLORS = (
        ("white", "white"),
        ("black", "black"),
    )

    color = models.CharField(
        _("Color"),
        max_length=255,
        choices=COLORS,
        default='white',
    )

    def get_price(self, request):
        return self.unit_price
Пример #18
0
class Commodity(Product):
    # common product fields
    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)

    # controlling the catalog
    placeholder = PlaceholderField("Commodity Details")

    class Meta:
        verbose_name = _("Commodity")
        verbose_name_plural = _("Commodities")

    def get_price(self, request):
        return self.unit_price
Пример #19
0
class Commodity(AvailableProductMixin, Product, TranslatableModelMixin):
    """
    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.ForeignKey(
        ProductList,
        on_delete=models.CASCADE,
    )
    multilingual = TranslatedFields(
        description=HTMLField(
            verbose_name=_("Description"),
            configuration='CKEDITOR_SETTINGS_DESCRIPTION',
            blank=True,
            help_text=
            _("Full description used in the catalog's detail view of Smart Cards."
              ),
        ),
        caption=HTMLField(
            verbose_name=_("Caption"),
            configuration='CKEDITOR_SETTINGS_DESCRIPTION',
            blank=True,
            help_text=
            _("Full description used in the catalog's detail view of Smart Cards."
              ),
        ),
    )
    # controlling the catalog
    placeholder = PlaceholderField("Commodity Details")
    show_breadcrumb = True  # hard coded to always show the product's breadcrumb

    default_manager = TranslatableManager()

    class Meta:
        verbose_name = _("Commodity")
        verbose_name_plural = _("Commodities")

    def get_price(self, request):
        return self.unit_price
Пример #20
0
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
Пример #21
0
class Scoop(Product):
    # common product fields
    unit_price = MoneyField(
        _("Unit price"),
        decimal_places=3,
        help_text=_("Net price for this product"),
    )

    # product properties

    product_code = models.CharField(
        _("Product code"),
        max_length=255,
        unique=True,
    )

    time_duration = models.PositiveIntegerField(
        _("time duration"),
        help_text=_("time in days"),
    )

    description = HTMLField(
        verbose_name=_("Description"),
        configuration='CKEDITOR_SETTINGS_DESCRIPTION',
        help_text=_(
            "Full description used in the catalog's detail view of Scoop."),
    )

    default_manager = BaseProductManager()

    class Meta:
        verbose_name = _("Scoop")
        verbose_name_plural = _("Scoop")

    def get_price(self, request):
        return self.unit_price
Пример #22
0
class SofaVariant(models.Model):
    class Meta:
        ordering = ('unit_price', )

    product_model = models.ForeignKey(
        SofaModel,
        related_name='variants',
        on_delete=models.CASCADE,
    )
    product_code = models.ForeignKey(
        ProductList,
        on_delete=models.CASCADE,
        blank=True,
    )
    images = models.ManyToManyField(
        'filer.Image',
        through='VariantImage',
    )

    unit_price = MoneyField(_("Unit price"), blank=True)

    fabric = models.ForeignKey(
        Fabric,
        on_delete=models.CASCADE,
        blank=True,
    )

    def get_availability(self, request, **kwargs):
        return True

    def __str__(self):
        return self.fabric.fabric_name

    def delete(self, using=None, keep_parents=False):
        ProductList.objects.filter(product_code=self.product_code).delete()
        super(SofaVariant, self).delete()
Пример #23
0
class Fabric(Product):

    fabric_name = models.CharField(
        _("Fabric name"),
        max_length=150,
        blank=True,
    )
    # common product fields
    unit_price = MoneyField(
        _("Price per meter"),
        decimal_places=2,
        help_text=_("Net price for this product by meter"),
    )
    # product properties
    FABRIC_TYPE = [
        ('leath', 'leather'),
        ('velv', 'velvet'),
        ('wool', 'wool'),
    ]
    fabric_type = models.CharField(
        _("Fabric type"),
        choices=FABRIC_TYPE,
        max_length=15,
    )

    product_code = models.ForeignKey(
        ProductList,
        on_delete=models.CASCADE,
        blank=True,
    )
    composition = models.CharField(
        _("Comosition of fabric"),
        max_length=255,
        unique=False,
    )
    care = models.CharField(
        _("Recommended wash care"),
        max_length=255,
        unique=False,
    )
    multilingual = TranslatedFields(
        description=HTMLField(
            verbose_name=_("Description"),
            configuration='CKEDITOR_SETTINGS_DESCRIPTION',
            blank=True,
            help_text=
            _("Full description used in the catalog's detail view of Smart Cards."
              ),
        ),
        caption=HTMLField(
            verbose_name=_("Caption"),
            configuration='CKEDITOR_SETTINGS_DESCRIPTION',
            blank=True,
            help_text=
            _("Full description used in the catalog's detail view of Smart Cards."
              ),
        ),
    )

    default_manager = ProductManager()

    class Meta:
        verbose_name = _("Fabric")
        verbose_name_plural = _("Fabrics")

    def get_price(self, request):
        return self.unit_price
Пример #24
0
class WeltladenProduct(CMSPageReferenceMixin, TranslatableModelMixin,
                       BaseProduct):
    product_name = models.CharField(
        max_length=255,
        verbose_name=_("Product Name"),
    )

    slug = models.SlugField(verbose_name=_("Slug"))

    caption = TranslatedField()
    short_description = TranslatedField()
    description = TranslatedField()
    ingredients = TranslatedField()

    # product properties
    manufacturer = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
        verbose_name=_("Manufacturer"),
        blank=True,
        null=True,
    )

    additional_manufacturers = models.ManyToManyField(
        Manufacturer,
        blank=True,
        verbose_name=_("Additional Manufacturers"),
        related_name="additional_manufacturers",
    )

    display_manufacturer_as_raw_material_supplier = models.BooleanField(
        _("Display manufacturer as raw material supplier"), default=False)

    supplier = models.ForeignKey(Supplier,
                                 verbose_name=_("Supplier"),
                                 on_delete=models.CASCADE)

    quality_labels = models.ManyToManyField(QualityLabel,
                                            blank=True,
                                            verbose_name=_("Quality labels"),
                                            related_name="quality_labels")

    origin_countries = CountryField(
        verbose_name=_("Origin countries"),
        blank_label=_('Select one or many'),
        multiple=True,
        blank=True,
    )

    # 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,
    )

    unit_price = MoneyField(
        _("Unit price"),
        decimal_places=3,
        help_text=_("Gross price for this product"),
    )

    vegan = models.BooleanField(_("Vegan"), default=False)

    lactose_free = models.BooleanField(_("Lactose free"), default=False)

    gluten_free = models.BooleanField(_("Gluten free"), default=False)

    tax_switch = models.BooleanField(
        _("Switch Tax"),
        default=True,
        help_text=_(
            "If switched on, then 20% tax item, if off then 10% tax item"))

    product_code = models.CharField(
        _("Product code"),
        max_length=255,
        unique=True,
    )

    instagram_category = models.ForeignKey(InstagramCategory,
                                           on_delete=models.CASCADE,
                                           null=True,
                                           blank=True)

    weight = models.DecimalField(
        _("Weight"),
        help_text=_("Weight in kilograms (kg). max 99.99kg"),
        decimal_places=2,
        max_digits=4,
        default=0.0)

    class Meta:
        verbose_name = _("Product")
        verbose_name_plural = _("Products")
        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

    objects = ProductManager()

    def __str__(self):
        return self.product_name

    @property
    def ordered_quality_labels(self):
        return self.quality_labels.all().order_by('ordering')

    @property
    def sample_image(self):
        return self.images.first()

    def get_weight(self):
        return self.weight

    def invalidate_cache(self):
        """
        Method ``ProductCommonSerializer.render_html()`` caches the rendered HTML snippets.
        Invalidate this HTML snippet after changing relevant parts of the product.
        """
        shop_app = apps.get_app_config('shop')
        if shop_app.cache_supporting_wildcard:
            cache.delete('product:{}|*'.format(self.id))

    def clean_fields(self, exclude=None):
        super().clean_fields(exclude=exclude)
        if WeltladenProduct.objects.filter(slug=self.slug).exclude(
                id=self.id).exists():
            raise ValidationError(_('Product slug already exits'),
                                  code='invalid')
Пример #25
0
class Product(BaseProduct, TranslatableModel):
    """
    Product model.
    """
    SINGLE, GROUP, VARIANT = range(3)

    KINDS = (
        (SINGLE, _('Single')),
        (GROUP, _('Group')),
        (VARIANT, _('Variant')),
    )

    translations = TranslatedFields(
        name=models.CharField(
            _('Name'),
            max_length=128,
        ),
        slug=models.SlugField(
            _('Slug'),
            db_index=True,
            help_text=
            _("Part that's used in url to display this product. Needs to be unique."
              ),
        ),
        _caption=models.TextField(
            _('Caption'),
            max_length=255,
            blank=True,
            help_text=
            _("Short product caption, usually used in catalog's list view of products."
              ),
        ),
        _description=models.TextField(
            _('Description'),
            blank=True,
            help_text=
            _("Description of a product, usually used as lead text in product's detail view."
              ),
        ),
        meta={
            'unique_together': [('language_code', 'slug')],
        },
    )

    code = models.CharField(
        _('Code'),
        max_length=64,
        unique=True,
        help_text=_('Unique identifier for a product.'),
    )

    # Categorization
    _category = TreeForeignKey(
        Category,
        models.CASCADE,
        blank=True,
        null=True,
        verbose_name=_('Category'),
    )

    _brand = TreeForeignKey(
        Brand,
        models.CASCADE,
        blank=True,
        null=True,
        verbose_name=_('Brand'),
    )

    _manufacturer = TreeForeignKey(
        Manufacturer,
        models.CASCADE,
        blank=True,
        null=True,
        verbose_name=_('Manufacturer'),
    )

    # Pricing
    _unit_price = MoneyField(
        _('Unit price'),
        default=0,
        help_text=_("For variants leave empty to use the Group price."),
    )

    _discount = models.DecimalField(
        _('Discount %'),
        blank=True,
        null=True,
        max_digits=4,
        decimal_places=2,
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("For variants leave empty to use Group discount."),
    )

    _tax = models.ForeignKey(
        Tax,
        models.SET_NULL,
        blank=True,
        null=True,
        verbose_name=_('Tax'),
        help_text=_(
            "Tax to be applied to this product. Variants inherit tax percentage from their Group, and should "
            "leave this field empty."),
    )

    # Settings
    kind = models.PositiveSmallIntegerField(
        _('Kind'),
        choices=KINDS,
        default=SINGLE,
        help_text=
        _('Choose a product type. Single products are products without variations. Group products are base products '
          'that hold variants and their common info, they cannot be added to cart. Variants are variations of a '
          'product that must select a Group product, and set their unique set of attributes. '
          '(See "Variant" section below)'),
    )

    discountable = models.BooleanField(
        _('Discountable'),
        default=True,
        help_text=_('Can this product be used in an offer?'),
    )

    modifiers = models.ManyToManyField(
        Modifier,
        blank=True,
        verbose_name=_('Modifiers'),
        limit_choices_to={'kind__in': [Modifier.STANDARD, Modifier.DISCOUNT]},
    )

    flags = models.ManyToManyField(
        Flag,
        blank=True,
        verbose_name=_('Flags'),
        help_text=_('Check flags for this product.'),
    )

    # Measurements
    _width = MeasurementField(
        _('Width'),
        blank=True,
        null=True,
        measurement=Distance,
    )

    _height = MeasurementField(
        _('Height'),
        blank=True,
        null=True,
        measurement=Distance,
    )

    _depth = MeasurementField(
        _('Depth'),
        blank=True,
        null=True,
        measurement=Distance,
    )

    _weight = MeasurementField(
        _('Weight'),
        blank=True,
        null=True,
        measurement=Mass,
    )

    # Group
    available_attributes = models.ManyToManyField(
        'Attribute',
        blank=True,
        related_name='products_available_attributes',
        verbose_name=_('Attributes'),
        help_text=_(
            'Select attributes that can be used in a Variant for this product.'
        ),
    )

    # Variant
    group = models.ForeignKey(
        'self',
        models.CASCADE,
        blank=True,
        null=True,
        related_name='variants',
        verbose_name=_('Group'),
        help_text=_('Select a Group product for this variation.'),
    )

    attributes = models.ManyToManyField(
        'Attribute',
        through='AttributeValue',
        verbose_name=_('Attributes'),
    )

    published = models.DateTimeField(
        _('Published'),
        default=timezone.now,
    )

    quantity = models.IntegerField(
        _('Quantity'),
        blank=True,
        null=True,
        help_text=_(
            'Number of available products to ship. Leave empty if product is always available, or set to 0 if product '
            'is not available.'),
    )

    order = models.BigIntegerField(
        _('Sort'),
        default=0,
    )

    content = PlaceholderField('shopit_product_content')

    objects = ProductManager()

    lookup_fields = ['code__startswith', 'translations__name__icontains']

    class Meta:
        db_table = 'shopit_products'
        verbose_name = _('Product')
        verbose_name_plural = _('Products')
        ordering = ['-order']

    def __str__(self):
        return self.product_name

    def save(self, *args, **kwargs):
        """
        Clean and clear product.
        Set unique ordering value for product and it's variants based on
        published timestamp. Force Single groups to a Group kind.
        """
        self.clean()
        self.clear()
        if self.is_variant:
            self.group.clear('_variants', '_invalid_variants', '_variations',
                             '_attribute_choices', '_combinations')
            self.order = self.group.order
            if self.group.is_single:
                self.group.kind = Product.GROUP
                self.group.save(update_fields=['kind'])
            super(Product, self).save(*args, **kwargs)
        else:
            # Don't generate timestamp if published hasn't changed.
            if self.pk is not None:
                original = Product.objects.get(
                    pk=self.pk).published.strftime('%s')
                if original == self.published.strftime('%s'):
                    super(Product, self).save(*args, **kwargs)
                    return
            timestamp = int(self.published.strftime('%s'))
            same = Product.objects.filter(order__startswith=timestamp).first()
            timestamp = same.order + 1 if same else timestamp * 1000
            self.order = timestamp
            super(Product, self).save(*args, **kwargs)
            if self.is_group:
                for variant in self.get_variants():
                    variant.clear()
                    variant.order = timestamp
                    variant.save(update_fields=['order'])

    def get_absolute_url(self, language=None):
        if not language:
            language = get_current_language()

        with switch_language(self, language):
            try:
                return reverse('shopit-product-detail',
                               args=[self.safe_translation_getter('slug')])
            except NoReverseMatch:  # pragma: no cover
                pass

    @property
    def product_name(self):
        return self.safe_translation_getter(
            'name', any_language=True) if self.pk else ''

    @property
    def product_code(self):
        return self.code

    @property
    def is_single(self):
        return self.kind == self.SINGLE

    @property
    def is_group(self):
        return self.kind == self.GROUP

    @property
    def is_variant(self):
        return self.kind == self.VARIANT

    @property
    def caption(self):
        return self.get_attr('_caption', '', translated=True)

    @caption.setter
    def caption(self, value):
        self._caption = value

    @property
    def description(self):
        return self.get_attr('_description', '', translated=True)

    @description.setter
    def description(self, value):
        self._description = value

    @property
    def category(self):
        return self.get_attr('_category')

    @category.setter
    def category(self, value):
        self._category = value

    @property
    def brand(self):
        return self.get_attr('_brand')

    @brand.setter
    def brand(self, value):
        self._brand = value

    @property
    def manufacturer(self):
        return self.get_attr('_manufacturer')

    @manufacturer.setter
    def manufacturer(self, value):
        self._manufacturer = value

    @property
    def unit_price(self):
        return self.get_attr('_unit_price', 0)

    @unit_price.setter
    def unit_price(self, value):
        self._unit_price = value

    @property
    def discount(self):
        return self.get_attr('_discount')

    @discount.setter
    def discount(self, value):
        self._discount = value

    @property
    def tax(self):
        return self.get_attr('_tax') or getattr(self.category, 'tax', None)

    @tax.setter
    def tax(self, value):
        self._tax = value

    @property
    def width(self):
        return self.get_attr('_width')

    @width.setter
    def width(self, value):
        self._width = value if isinstance(value, Distance) else Distance(
            m=value)

    @property
    def height(self):
        return self.get_attr('_height')

    @height.setter
    def height(self, value):
        self._height = value if isinstance(value, Distance) else Distance(
            m=value)

    @property
    def depth(self):
        return self.get_attr('_depth')

    @depth.setter
    def depth(self, value):
        self._depth = value if isinstance(value, Distance) else Distance(
            m=value)

    @property
    def weight(self):
        return self.get_attr('_weight')

    @weight.setter
    def weight(self, value):
        self._weight = value if isinstance(value, Mass) else Mass(g=value)

    @property
    def price(self):
        return self.get_price()

    @property
    def is_discounted(self):
        return bool(self.discount_percent)

    @property
    def is_taxed(self):
        return bool(self.tax_percent)

    @property
    def discount_percent(self):
        return self.discount or Decimal('0.00')

    @property
    def tax_percent(self):
        return getattr(self.tax, 'percent', Decimal('0.00'))

    @property
    def discount_amount(self):
        return self.unit_price * self.discount_percent / 100

    @property
    def tax_amount(self):
        return (self.unit_price -
                self.discount_amount) * self.tax_percent / 100

    @property
    def primary_image(self):
        return self.images.first()

    @cached_property
    def images(self):
        images = self.attachments.filter(kind=Attachment.IMAGE)
        return images or (self.group.images if self.is_variant else images)

    @cached_property
    def videos(self):
        videos = self.attachments.filter(kind=Attachment.VIDEO)
        return videos or (self.group.videos if self.is_variant else videos)

    @cached_property
    def files(self):
        files = self.attachments.filter(kind=Attachment.FILE)
        return files or (self.group.files if self.is_variant else files)

    def get_price(self, request=None):
        """
        Returns price with discount and tax calculated.
        """
        return self.unit_price - self.discount_amount + self.tax_amount

    def get_availability(self, request=None):
        """
        Returns product availibility as list of tuples `(quantity, until)`
        Method is not yet implemented in django-shop.
        """
        availability = self.quantity if self.quantity is not None else True
        if self.is_group:
            availability = 0
        elif self.is_variant and self not in self.group.get_variants():
            availability = 0
        return [(availability, datetime.max)]

    def is_available(self, quantity=1, request=None):
        """
        Returns if product is available for the given quantity. If request
        is passed in, count items already in cart.
        """
        if request:
            cart = Cart.objects.get_or_create_from_request(request)
            cart_item = self.is_in_cart(cart)
            quantity += cart_item.quantity if cart_item else 0

        now = timezone.now().replace(tzinfo=None)
        number, until = self.get_availability(request)[0]
        number = 100000 if number is True else number
        available = number >= quantity and now < until
        return available, int(number - quantity)

    def get_modifiers(self, distinct=True):
        """
        Returns all modifiers for this product.
        Collects categorization and group modifiers.
        """
        mods = getattr(self, '_mods', None)
        if mods is None:
            mods = self.modifiers.active()
            if self.is_variant:
                mods = mods | self.group.get_modifiers(distinct=False)
            else:
                if self.category:
                    mods = mods | self.category.get_modifiers(distinct=False)
                if self.brand:
                    mods = mods | self.brand.get_modifiers(distinct=False)
                if self.manufacturer:
                    mods = mods | self.manufacturer.get_modifiers(
                        distinct=False)
            self.cache('_mods', mods)
        return mods.distinct() if distinct else mods

    def get_flags(self, distinct=True):
        """
        Returns all flags for this product.
        Collects categorization and group flags.
        """
        flags = getattr(self, '_flags', None)
        if flags is None:
            flags = self.flags.active()
            if self.is_variant:
                flags = flags | self.group.get_flags(distinct=False)
            if self.category:
                flags = flags | self.category.get_flags(distinct=False)
            if self.brand:
                flags = flags | self.brand.get_flags(distinct=False)
            if self.manufacturer:
                flags = flags | self.manufacturer.get_flags(distinct=False)
            self.cache('_flags', flags)
        return flags.distinct() if distinct else flags

    def get_available_attributes(self):
        """
        Returns list of available attributes for Group and
        Variant products.
        """
        if self.is_group:
            if not hasattr(self, '_available_attributes'):
                self.cache('_available_attributes',
                           self.available_attributes.active())
            return getattr(self, '_available_attributes')

    def get_attributes(self):
        """
        Returns a dictionary containing Variant attributes.
        """
        if self.is_variant:
            attrs = getattr(self, '_attributes', OrderedDict())
            if not attrs:
                for value in self.attribute_values.select_related('attribute'):
                    attrs[value.attribute.key] = value.as_dict
                self.cache('_attributes', attrs)
            return attrs

    def get_variants(self):
        """
        Returns valid variants of a Group product.
        """
        if self.is_group:
            variants = getattr(self, '_variants', None)
            if variants is None:
                variants = self.variants.all()
                invalid = [x.pk for x in self.get_invalid_variants()]
                variants = self.variants.exclude(pk__in=invalid)
                self.cache('_variants', variants)
            return variants

    def get_invalid_variants(self):
        """
        Returns variants that whose attributes don't match available
        attributes and they need to be re-configured or deleted.
        """
        if self.is_group:
            if not hasattr(self, '_invalid_variants'):
                invalid = []
                valid_attrs = [
                ]  # Keep track of valid attrs to check for duplicates.
                codes = sorted(self.get_available_attributes().values_list(
                    'code', flat=True))
                for variant in self.variants.all():
                    attrs = variant.get_attributes()
                    if (attrs in valid_attrs
                            or sorted(x['code']
                                      for x in attrs.values()) != codes
                            or True in [
                                not x['nullable'] and x['value'] == ''
                                for x in attrs.values()
                            ]):
                        invalid.append(variant)
                    else:
                        valid_attrs.append(attrs)
                self.cache('_invalid_variants', invalid)
            return getattr(self, '_invalid_variants')

    def get_variations(self):
        """
        Returns a list of tuples containing a variant id and it's attributes.
        """
        if self.is_group:
            if not hasattr(self, '_variations'):
                variations = [(x.pk, x.get_attributes())
                              for x in self.get_variants()]
                self.cache('_variations', variations)
            return getattr(self, '_variations')

    def get_attribute_choices(self):
        """
        Returns available attribute choices for a group product, filtering
        only the used ones. Used to display dropdown fields on a group
        product to select a variant.
        """
        if self.is_group:
            if not hasattr(self, '_attribute_choices'):
                used = [
                    tuple([y['code'], y['value']])
                    for x in self.get_variations() for y in x[1].values()
                ]
                attrs = OrderedDict()
                for attr in self.get_available_attributes():
                    data = attr.as_dict
                    data['choices'] = [
                        x.as_dict for x in attr.get_choices()
                        if (x.attribute.code, x.value) in used
                    ]
                    if data['choices']:
                        attrs[attr.code] = data
                self.cache('_attribute_choices', attrs)
            return getattr(self, '_attribute_choices')

    def get_combinations(self):
        """
        Returns all available Variant combinations for a Group product
        based on `Available attributes` field, replacing the existant
        variants with actual variant data. Variants with attributes
        missing or not specified in `Available attributes` will not be
        included. This is used to show possible combinations in admin,
        as well as creating them automatically.
        """
        if self.is_group:
            if not hasattr(self, '_combinations'):
                values = []
                for attr in self.get_available_attributes():
                    vals = [
                        AttributeValue(attribute=attr, choice=x)
                        for x in attr.get_choices()
                    ]
                    if vals:
                        values.append(vals)
                combinations = []
                if values:
                    for combo in itertools.product(*values):
                        attrs = OrderedDict([(x.attribute.code, x.value)
                                             for x in combo])
                        name = self.safe_translation_getter('name',
                                                            any_language=True)
                        name = '%s %s' % (name, ' '.join(
                            [x.label for x in combo if x.label != '-']))
                        name = name.rstrip()
                        slug = slugify(name)
                        languages = []
                        variant = self.get_variant(attrs)
                        if variant:
                            name = variant.safe_translation_getter(
                                'name', name)
                            slug = variant.safe_translation_getter(
                                'slug', slug)
                            languages = variant.get_available_languages()
                        combinations.append({
                            'pk':
                            variant.pk if variant else None,
                            'name':
                            name,
                            'slug':
                            slug,
                            'code':
                            variant.code if variant else None,
                            'price':
                            variant.get_price() if variant else None,
                            'quantity':
                            variant.quantity if variant else None,
                            'languages':
                            languages,
                            'attributes':
                            OrderedDict([(x.attribute.code, x.as_dict)
                                         for x in combo])
                        })
                self.cache('_combinations', combinations)
            return getattr(self, '_combinations')

    def get_attachments(self):
        """
        Returns all attachments as a dictionary.
        If Product is a Variant and has not attachments itself,
        group attachemts are inherited.
        """
        attachments = getattr(self, '_attachments', None)
        if attachments is None:
            attachments = {
                'images': [x.as_dict for x in self.images] or None,
                'videos': [x.as_dict for x in self.videos] or None,
                'files': [x.as_dict for x in self.files] or None,
            }
            self.cache('_attachments', attachments)
        return attachments

    def get_relations(self):
        """
        Returns relations for a Single or Group product.
        """
        if not self.is_variant:
            if not hasattr(self, '_relations'):
                self.cache('_relations', self.relations.all())
            return getattr(self, '_relations')

    def get_related_products(self, kind=None):
        """
        Returns related products with the given kind. Variants inherit their
        related products from a Group.
        """
        if self.is_variant:
            return self.group.get_related_products(kind)
        relations = self.get_relations()
        if kind is not None:
            relations = relations.filter(kind=kind)
        return [x.product for x in relations]

    def get_reviews(self, language=None, include_inactive=False):
        """
        Returns reviews for this product, uses the group product for varaints.
        """
        if not self.is_variant:
            reviews = getattr(self, '_reviews', None)
            if include_inactive:
                reviews = self.reviews.all()
            if reviews is None:
                reviews = self.reviews.active()
                self.cache('_reviews', reviews)
            if language is not None:
                return reviews.filter(language=language)
            return reviews

    def get_variant(self, attrs):
        """
        Returns a Variant with the given attribute values for this Group.
        eg. attrs = {'code': 'value', 'code2': 'value2'}
        """
        if self.is_group:
            for variant in self.get_variants():
                current = [(x['code'], x['value'])
                           for x in variant.get_attributes().values()]
                if (sorted(attrs.items()) == sorted(current)):
                    return variant

    def filter_variants(self, attrs):
        """
        Returns a list of Variant products for this Group that contain
        attribute values passed in as `attrs`.

        eg. attrs = {'code': 'value', 'code2': 'value2'}
        """
        if self.is_group:
            variants = []
            for variant in self.get_variants():
                valid = True
                current = [(x['code'], x['value'])
                           for x in variant.get_attributes().values()]
                for attr in attrs.items():
                    if attr not in current:
                        valid = False
                        break
                if valid:
                    variants.append(variant)
            return variants

    def create_variant(self, combo, language=None):
        """
        Create a variant with the given `combo` object from
        the `get_combinations` method.
        """
        if self.is_group:
            if not language:
                language = get_current_language()
            slug = combo['slug']
            code = combo['code'] or Product.objects.latest('pk').pk + 1
            num = 0
            while Product.objects.translated(slug=slug).exists():
                num = num + 1
                slug = '%s-%d' % (combo['slug'], num)
            while Product.objects.filter(
                    code=code, translations__language_code=language).exists():
                code = int(code) + 1
            variant, created = Product.objects.get_or_create(
                code=code, kind=Product.VARIANT, group=self)
            variant.set_current_language(language)
            variant.name = combo['name']
            variant.slug = slug
            variant.save()
            if created:
                for attr_value in combo['attributes'].values():
                    attr = Attribute.objects.get(code=attr_value['code'])
                    if attr_value['value'] == '' and attr.nullable:
                        choice = None
                    else:
                        choice = attr.choices.get(pk=attr_value['choice'])
                    AttributeValue.objects.create(attribute=attr,
                                                  product=variant,
                                                  choice=choice)
            return variant

    @method_decorator(transaction.atomic)
    def create_all_variants(self, language=None):
        """
        Creates all missing variants for the group.
        """
        if self.is_group:
            variants = []
            if not language:
                language = get_current_language()
            for combo in self.get_combinations():
                if not combo['pk'] or language not in combo['languages']:
                    variants.append(
                        self.create_variant(combo, language=language))
            return variants

    def get_attr(self, name, case=None, translated=False):
Пример #26
0
    class Commodity(CMSPageReferenceMixin, CommodityMixin, BaseProduct):
        """
        Generic Product Commodity to be used whenever the merchant does not require product specific
        attributes and just required a placeholder field to add arbitrary data.
        """
        # common product fields
        product_name = models.CharField(
            max_length=255,
            verbose_name=_("Product Name"),
        )

        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"),
        )

        # controlling the catalog
        order = models.PositiveIntegerField(
            verbose_name=_("Sort by"),
            db_index=True,
        )

        cms_pages = models.ManyToManyField(
            'cms.Page',
            through=ProductPage,
            help_text=_("Choose list view this product shall appear on."),
        )

        sample_image = image.FilerImageField(
            verbose_name=_("Sample Image"),
            blank=True,
            null=True,
            default=None,
            on_delete=models.SET_DEFAULT,
            help_text=_("Sample image used in the catalog's list view."),
        )

        show_breadcrumb = models.BooleanField(
            _("Show Breadcrumb"),
            default=True,
            help_text=_(
                "Shall the detail page show the product's breadcrumb."),
        )

        placeholder = PlaceholderField("Commodity Details")

        quantity = models.PositiveIntegerField(
            _("Quantity"),
            default=0,
            validators=[MinValueValidator(0)],
            help_text=_("Available quantity in stock"))

        # common fields for the catalog's list- and detail views
        slug = models.SlugField(verbose_name=_("Slug"))

        caption = HTMLField(
            verbose_name=_("Caption"),
            blank=True,
            null=True,
            help_text=_("Short description for the catalog list view."),
        )

        # filter expression used to search for a product item using the Select2 widget
        lookup_fields = ['product_code__startswith', 'product_name__icontains']

        objects = BaseProductManager()

        class Meta:
            app_label = app_settings.APP_LABEL
            ordering = ('order', )
            verbose_name = _("Commodity")
            verbose_name_plural = _("Commodities")

        def __str__(self):
            return self.product_code
Пример #27
0
class LamellaFix(Product):
    # common product fields
    unit_price = MoneyField(
        _("Unit price"),
        decimal_places=3,
        help_text=_(" per unit"),
    )
    LAM_WIDTH = (('38', '38 mm'), ('53', '53 mm'), ('63', '63 mm'), ('68', '68 mm'))

    lamella_width = models.CharField(
        _('width'),
        default=53,
        max_length=6,
        choices=LAM_WIDTH,
        help_text=_("Lammelas width")
    )

    length = models.CharField(
        _("Lamella's length"),
        max_length=25,
        blank=True,
    )

    depth = models.CharField(
        _("Lamella's depth"),
        max_length=25,
        blank=True,
    )

    weight = models.CharField(
        _('weight'),
        default=0,
        max_length=6,
        help_text=_("Weight of item, kg")
    )
    is_lamella = models.BooleanField(
        _("Lamella"),
        default=True,
        help_text=_("Is this lamella (for calculating weight)."),
    )

    weight_by_hand = models.BooleanField(
        _("Enter weight by hand"),
        default=False,
        help_text=_("For enter lamella weight by hand"),
    )

    discont_scheme = models.ForeignKey(Discount, blank=True, on_delete=models.CASCADE)

    product_code = models.CharField(
        _("Product code"),
        max_length=255,
        blank=True,
    )

    description = HTMLField(
        verbose_name=_("Description"),
        configuration='CKEDITOR_SETTINGS_DESCRIPTION',
        help_text=_("Full description used in the catalog's detail view of Smart Cards."),
    )

    default_manager = BaseProductManager()

    class Meta:
        verbose_name = _("Lamella")
        verbose_name_plural = _("Lamellas")

    def is_unique_scu(self, scu):
        scu = str(scu)
        try:
            LamellaFix.objects.get(product_code=scu)
            return False
        except:
            return True

    def set_num_scu(self, scu, n):
        scu = str(scu)
        while len(scu) <= int(n):
            scu = '0'+ scu
        return scu

    def get_max_scu(self):
        codes = LamellaFix.objects.all()
        max_scu = 0
        for code in codes:
            code = int(code.product_code)
            if code > max_scu:
                max_scu = code
        return max_scu

    def save(self, *args, **kwargs):
        if not self.product_code or not self.is_unique_scu(self.product_code):
            max_scu = int(self.get_max_scu())
            while True:
                new_scu = max_scu + 1
                if self.is_unique_scu(new_scu):
                    self.product_code = self.set_num_scu(new_scu, 4)
                    break

        # TODO: unique product_code

        if(self.is_lamella and not self.weight_by_hand):
            m = 0.00075  # calculated empiric method
            vol = float(self.length) * float(self.lamella_width) * float(self.depth)
            self.weight = round((vol * m / 1000), 3)
        super(LamellaFix, self).save(*args, **kwargs)


    def get_price(self, request):
        return self.unit_price

    def get_rebate(self, x):
        some_str = self.discont_scheme.discont_scheme.split("\r\n")
        x = int(x)
        temp_discont = 0
        for i in some_str:
            discont = i.split(":")
            num = int(discont[0]) #get first element of tuple for get quantity in db
            if x>=num:
                temp_discont = int(discont[1])
            elif x<=num:
                return temp_discont
        return temp_discont
def test_from_db_value():
    f = MoneyField(currency='EUR', null=True)
    assert f.from_db_value(Decimal('3'), None, None) == EUR('3')
    assert f.from_db_value(3.45, None, None) == EUR('3.45')
    assert f.from_db_value(None, None, None) is None
def test_get_prep_value():
    f = MoneyField(currency='EUR', null=True)
    assert f.get_prep_value(EUR('3')) == Decimal('3')
Пример #30
0
class Modifier(TranslatableModel):
    STANDARD = 'standard'
    DISCOUNT = 'discount'
    CART = 'cart'

    KINDS = (
        (STANDARD, _('Standard')),
        (DISCOUNT, _('Discount')),
        (CART, _('Cart')),
    )

    translations = TranslatedFields(name=models.CharField(
        _('Name'),
        max_length=128,
    ), )

    code = models.SlugField(
        _('Code'),
        unique=True,
        help_text=_('Unique identifier for this modifier.'),
    )

    amount = MoneyField(
        _('Amount'),
        default=0,
        help_text=('Amount that should be added. Can be negative.'),
    )

    percent = models.DecimalField(
        _('Percent'),
        blank=True,
        null=True,
        max_digits=4,
        decimal_places=2,
        help_text=
        _('Percent that should be added, overrides the amount. Can be negative.'
          ),
    )

    kind = models.CharField(
        _('Kind'),
        max_length=16,
        choices=KINDS,
        default=STANDARD,
        help_text=_(
            'Standard affects the product regardles, Discount checks for a "Discountable" flag on a product and '
            'should be negative, Cart will affect an entire cart.'),
    )

    active = models.BooleanField(
        _('Active'),
        default=True,
        help_text=_('Is this modifier publicly visible.'),
    )

    created_at = models.DateTimeField(
        _('Created at'),
        auto_now_add=True,
    )

    updated_at = models.DateTimeField(
        _('Updated at'),
        auto_now=True,
    )

    order = models.PositiveIntegerField(
        _('Sort'),
        default=0,
    )

    objects = ModifierQuerySet.as_manager()

    class Meta:
        db_table = 'shopit_modifiers'
        verbose_name = _('Modifier')
        verbose_name_plural = _('Modifiers')
        ordering = ['order']

    def __str__(self):
        return self.label

    def save(self, *args, **kwargs):
        self.clean()
        super(Modifier, self).save(*args, **kwargs)

    @property
    def label(self):
        return self.safe_translation_getter('name', any_language=True)

    @property
    def requires_code(self):
        return self.discount_codes.active().exists()

    @property
    def is_filtering_enabled(self):
        return Modifier.objects.filtering_enabled().active().filter(
            id=self.id).exists()

    def get_conditions(self):
        if not hasattr(self, '_conditions'):
            setattr(self, '_conditions', list(self.conditions.all()))
        return getattr(self, '_conditions')

    def get_discount_codes(self, include_added=False):
        key = '_discount_codes_added' if include_added else '_discount_codes'
        if not hasattr(self, key):
            setattr(
                self, key,
                list(self.discount_codes.valid(include_added=include_added)))
        return getattr(self, key)

    def get_added_amount(self, price, quantity=1):
        return self.percent * price / 100 if self.percent else self.amount * quantity

    def can_be_applied(self, request, cart_item=None, cart=None):
        """
        Returns if a modifier can be applied to the given cart or cart item.
        Either `cart_item` or `cart` must be passed in.
        """
        if cart_item is None and cart is None:
            return False

        if cart_item and not self.is_eligible_product(cart_item.product):
            return False

        for condition in self.get_conditions():
            if not condition.is_met(request, cart_item, cart):
                return False

        if self.requires_code and not self.is_code_applied(
                cart_item.cart_id if cart_item else cart.id):
            return False

        return self.active  # Should never happen to be False up to this point, but just in case.

    def is_eligible_product(self, product):
        """
        Returns if modifier can be applied to the given product.
        """
        if self.kind == self.DISCOUNT:
            return product.discountable
        return self.kind == self.STANDARD

    def is_code_applied(self, cart_id):
        """
        Make sure that at least one code is applied to the given cart.
        """
        cart_codes = CartDiscountCode.objects.filter(
            cart_id=cart_id).values_list('code', flat=True)
        for code in self.get_discount_codes(include_added=True):
            if code.code in cart_codes:
                return True
        return False

    def clean(self):
        if self.kind == self.DISCOUNT:
            if self.percent and self.percent >= 0 or not self.percent and self.amount >= 0:
                raise ValidationError(em('discount_not_negative'))

    @classmethod
    def get_cart_modifiers(cls):
        return cls.objects.filter(kind=cls.CART)