Example #1
0
def test_analog():
    PseudoPaymentProcessorLogEntry = define_log_model(PseudoPaymentProcessor)
    assert PseudoPaymentProcessorLogEntry.__module__ == PseudoPaymentProcessor.__module__
    assert PseudoPaymentProcessorLogEntry._meta.get_field("target").rel.to is PseudoPaymentProcessor
    assert PseudoPaymentProcessor.log_entries.rel.model is PseudoPaymentProcessor
    assert PseudoPaymentProcessor.log_entries.rel.related_model is PseudoPaymentProcessorLogEntry
    assert issubclass(PseudoPaymentProcessorLogEntry, BaseLogEntry)
    assert isinstance(PseudoPaymentProcessorLogEntry(), BaseLogEntry)
Example #2
0
def test_analog():
    FakeModelLogEntry = define_log_model(FakeModel)
    assert FakeModelLogEntry.__module__ == FakeModel.__module__
    assert FakeModelLogEntry._meta.get_field("target").rel.to is FakeModel
    assert FakeModel.log_entries.related.model is FakeModel
    assert FakeModel.log_entries.related.related_model is FakeModelLogEntry
    assert issubclass(FakeModelLogEntry, BaseLogEntry)
    assert isinstance(FakeModelLogEntry(), BaseLogEntry)
Example #3
0
def test_analog():
    PseudoPaymentProcessorLogEntry = define_log_model(PseudoPaymentProcessor)
    assert PseudoPaymentProcessorLogEntry.__module__ == PseudoPaymentProcessor.__module__

    related_field_name = "related"
    # Behavior changs in Django 1.9
    if VERSION >= (1, 9):
        related_field_name = "rel"

    relation_manager = getattr(PseudoPaymentProcessorLogEntry._meta.get_field("target"), related_field_name)
    assert relation_manager.to is PseudoPaymentProcessor

    relation_manager = getattr(PseudoPaymentProcessor.log_entries, related_field_name)
    assert relation_manager.model is PseudoPaymentProcessor
    assert relation_manager.related_model is PseudoPaymentProcessorLogEntry

    assert issubclass(PseudoPaymentProcessorLogEntry, BaseLogEntry)
    assert isinstance(PseudoPaymentProcessorLogEntry(), BaseLogEntry)
Example #4
0
    service_model = ShippingMethod

    def delete(self, *args, **kwargs):
        ShippingMethod.objects.filter(carrier=self).update(
            **{"enabled": False})
        super(Carrier, self).delete(*args, **kwargs)

    def _create_service(self, choice_identifier, **kwargs):
        labels = kwargs.pop("labels", None)
        service = ShippingMethod.objects.create(
            carrier=self, choice_identifier=choice_identifier, **kwargs)
        if labels:
            service.labels = labels
        return service


class CustomCarrier(Carrier):
    """
    Carrier without any integration or special processing.
    """
    class Meta:
        verbose_name = _("custom carrier")
        verbose_name_plural = _("custom carriers")

    def get_service_choices(self):
        return [ServiceChoice('manual', _("Manually processed shipment"))]


ShippingMethodLogEntry = define_log_model(ShippingMethod)
CarrierLogEntry = define_log_model(Carrier)
Example #5
0
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

from shuup.core.fields import InternalIdentifierField
from shuup.utils.analog import define_log_model

__all__ = ("Manufacturer",)


@python_2_unicode_compatible
class Manufacturer(models.Model):
    created_on = models.DateTimeField(auto_now_add=True, verbose_name=_('added'))
    identifier = InternalIdentifierField(unique=True)

    name = models.CharField(max_length=128, verbose_name=_('name'))
    url = models.CharField(null=True, blank=True, max_length=128, verbose_name=_('URL'))

    class Meta:
        verbose_name = _('manufacturer')
        verbose_name_plural = _('manufacturers')

    def __str__(self):  # pragma: no cover
        return u'%s' % (self.name)


ManufacturerLogEntry = define_log_model(Manufacturer)
Example #6
0
    objects = ShopManager()

    class Meta:
        verbose_name = _('shop')
        verbose_name_plural = _('shops')

    def __str__(self):
        return force_text(self.safe_translation_getter("name", default="Shop %d" % self.pk))

    def create_price(self, value):
        """
        Create a price with given value and settings of this shop.

        Takes the ``prices_include_tax`` and ``currency`` settings of
        this Shop into account.

        :type value: decimal.Decimal|int|str
        :rtype: shuup.core.pricing.Price
        """
        if self.prices_include_tax:
            return TaxfulPrice(value, self.currency)
        else:
            return TaxlessPrice(value, self.currency)

    def _are_changes_protected(self):
        return Order.objects.filter(shop=self).exists()


ShopLogEntry = define_log_model(Shop)
Example #7
0
                shop_product.categories.remove(self)
                shop_product.primary_category = None
                shop_product.save()
            for shop_product in self.shop_products.all():
                shop_product.categories.remove(self)
                shop_product.primary_category = None
                shop_product.save()
            for child in self.children.all():
                child.parent = None
                child.save()
            self.status = CategoryStatus.DELETED
            self.add_log_entry("Success! Deleted (soft).",
                               kind=LogEntryKind.DELETION,
                               user=user)
            self.save()
            category_deleted.send(sender=type(self), category=self)

    def save(self, *args, **kwargs):
        rv = super(Category, self).save(*args, **kwargs)
        generate_multilanguage_slugs(self, self._get_slug_name)

        # bump children cache
        from shuup.core import cache

        cache.bump_version("category_cached_children")

        return rv


CategoryLogEntry = define_log_model(Category)
Example #8
0
from shuup.core.fields import CurrencyField, MoneyValueField
from shuup.utils.analog import define_log_model
from shuup.utils.properties import MoneyProperty, MoneyPropped

__all__ = ("Payment",)


class Payment(MoneyPropped, models.Model):
    # TODO: Revise!!!
    order = models.ForeignKey("Order", related_name='payments', on_delete=models.PROTECT, verbose_name=_('order'))
    created_on = models.DateTimeField(auto_now_add=True, verbose_name=_('created on'))
    gateway_id = models.CharField(max_length=32, verbose_name=_('gateway ID'))  # TODO: do we need this?
    payment_identifier = models.CharField(max_length=96, unique=True, verbose_name=_('identifier'))

    amount = MoneyProperty('amount_value', 'order.currency')
    foreign_amount = MoneyProperty('foreign_amount_value', 'foreign_currency')

    amount_value = MoneyValueField(verbose_name=_('amount'))
    foreign_amount_value = MoneyValueField(default=None, blank=True, null=True, verbose_name=_('foreign amount'))
    foreign_currency = CurrencyField(default=None, blank=True, null=True, verbose_name=_('foreign amount currency'))

    description = models.CharField(max_length=256, blank=True, verbose_name=_('description'))

    class Meta:
        verbose_name = _('payment')
        verbose_name_plural = _('payments')


PaymentLogEntry = define_log_model(Payment)
Example #9
0
class CustomerTaxGroup(TranslatableShuupModel):
    identifier = InternalIdentifierField(unique=True)
    translations = TranslatedFields(
        name=models.CharField(max_length=100, verbose_name=_('name')),
    )
    enabled = models.BooleanField(default=True, verbose_name=_('enabled'))

    class Meta:
        verbose_name = _('customer tax group')
        verbose_name_plural = _('customer tax groups')

    @classmethod
    def get_default_person_group(cls):
        obj, c = CustomerTaxGroup.objects.get_or_create(identifier="default_person_customers", defaults={
            "name": _("Retail Customers")
        })
        return obj

    @classmethod
    def get_default_company_group(cls):
        obj, c = CustomerTaxGroup.objects.get_or_create(identifier="default_company_customers", defaults={
            "name": _("Company Customers")
        })
        return obj


TaxLogEntry = define_log_model(Tax)
TaxClassLogEntry = define_log_model(TaxClass)
CustomerTaxGroupLogEntry = define_log_model(CustomerTaxGroup)
Example #10
0
@python_2_unicode_compatible
class OrderLineTax(MoneyPropped, ShuupModel, LineTax):
    order_line = models.ForeignKey(
        OrderLine, related_name='taxes', on_delete=models.PROTECT,
        verbose_name=_('order line'))
    tax = models.ForeignKey(
        "Tax", related_name="order_line_taxes",
        on_delete=models.PROTECT, verbose_name=_('tax'))
    name = models.CharField(max_length=200, verbose_name=_('tax name'))

    amount = MoneyProperty('amount_value', 'order_line.order.currency')
    base_amount = MoneyProperty('base_amount_value', 'order_line.order.currency')

    amount_value = MoneyValueField(verbose_name=_('tax amount'))
    base_amount_value = MoneyValueField(
        verbose_name=_('base amount'),
        help_text=_('Amount that this tax is calculated from'))

    ordering = models.IntegerField(default=0, verbose_name=_('ordering'))

    class Meta:
        ordering = ["ordering"]

    def __str__(self):
        return "%s: %s on %s" % (self.name, self.amount, self.base_amount)


OrderLineLogEntry = define_log_model(OrderLine)
OrderLineTaxLogEntry = define_log_model(OrderLineTax)
Example #11
0
    def use(self, order):
        return CouponUsage.add_usage(order=order, coupon=self)

    def increase_customer_usage_limit_by(self, amount):
        if self.usage_limit_customer:
            new_limit = self.usage_limit_customer + amount
        else:
            new_limit = self.usages.count() + amount
        self.usage_limit_customer = new_limit

    def increase_usage_limit_by(self, amount):
        self.usage_limit = self.usage_limit + amount if self.usage_limit else (self.usages.count() + amount)

    def has_been_used(self, usage_count=1):
        """ See if code is used the times given """
        return CouponUsage.objects.filter(coupon=self).count() >= usage_count

    def save(self, **kwargs):
        if Coupon.objects.filter(code__iexact=self.code, active=True).exclude(pk=self.pk).exists():
            raise ValidationError(_("Cannot have two same codes active at the same time."))
        return super(Coupon, self).save(**kwargs)

    def __str__(self):
        return self.code


CatalogCampaignLogEntry = define_log_model(CatalogCampaign)
BasketCampaignLogEntry = define_log_model(BasketCampaign)
CouponLogEntry = define_log_model(Coupon)
CouponUsageLogEntry = define_log_model(CouponUsage)
Example #12
0
        if self.kind != ProductMediaKind.IMAGE:
            return None

        return get_thumbnailer(self.file)

    def get_thumbnail(self, **kwargs):
        """
        Get thumbnail for image

        This will return `None` if there is no file or kind is not `ProductMediaKind.IMAGE`

        :rtype: easy_thumbnails.files.ThumbnailFile|None
        """
        kwargs.setdefault("size", (64, 64))
        kwargs.setdefault("crop", True)  # sane defaults
        kwargs.setdefault("upscale", True)  # sane defaults

        if kwargs["size"] is (0, 0):
            return None

        thumbnailer = self.easy_thumbnails_thumbnailer

        if not thumbnailer:
            return None

        return thumbnailer.get_thumbnail(thumbnail_options=kwargs)


ProductMediaLogEntry = define_log_model(ProductMedia)
Example #13
0
@python_2_unicode_compatible
class OrderLineTax(MoneyPropped, ShuupModel, LineTax):
    order_line = models.ForeignKey(
        OrderLine, related_name='taxes', on_delete=models.PROTECT,
        verbose_name=_('order line'))
    tax = models.ForeignKey(
        "Tax", related_name="order_line_taxes",
        on_delete=models.PROTECT, verbose_name=_('tax'))
    name = models.CharField(max_length=200, verbose_name=_('tax name'))

    amount = MoneyProperty('amount_value', 'order_line.order.currency')
    base_amount = MoneyProperty('base_amount_value', 'order_line.order.currency')

    amount_value = MoneyValueField(verbose_name=_('tax amount'))
    base_amount_value = MoneyValueField(
        verbose_name=_('base amount'),
        help_text=_('Amount that this tax is calculated from'))

    ordering = models.IntegerField(default=0, verbose_name=_('ordering'))

    class Meta:
        ordering = ["ordering"]

    def __str__(self):
        return "%s: %s on %s" % (self.name, self.amount, self.base_amount)


OrderLineLogEntry = define_log_model(OrderLine)
OrderLineTaxLogEntry = define_log_model(OrderLineTax)
Example #14
0
    def __str__(self):
        return self.code


def get_currency_precision(currency):
    """
    Get precision by currency code.

    Precision values will be populated from the ``decimal_places``
    fields of the `Currency` objects in the database.

    :type currency: str
    :param currency: Currency code as 3-letter string (ISO-4217)

    :rtype: decimal.Decimal|None
    :return: Precision value for given currency code or None for unknown
    """
    cache_key = 'currency_precision:' + currency
    precision = cache.get(cache_key)
    if precision is None:
        currency_obj = Currency.objects.filter(code=currency).first()
        precision = (
            decimal.Decimal('0.1') ** currency_obj.decimal_places
            if currency_obj else None)
        cache.set(cache_key, precision)
    return precision


CurrencyLogEntry = define_log_model(Currency)
Example #15
0
    created_on = models.DateTimeField(auto_now_add=True, editable=False, db_index=True, verbose_name=_("created on"))
    modified_on = models.DateTimeField(auto_now=True, editable=False, db_index=True, verbose_name=_("modified on"))

    objects = TaskCommentQuerySet.as_manager()

    def reply(self, contact, body):
        comment = TaskComment(task=self.task, comment_author=contact, body=body)
        comment.full_clean()
        comment.save()
        return comment

    def as_html(self):
        return mark_safe(force_text(self.body))

    def can_see(self, user):
        is_admin = user.is_superuser
        is_staff = bool(user.is_staff and user in self.task.shop.staff_members.all())

        if not (is_admin or is_staff):
            return (self.visibility == TaskCommentVisibility.PUBLIC)
        elif not is_admin:
            return (
                self.visibility == TaskCommentVisibility.PUBLIC or
                self.visibility == TaskCommentVisibility.STAFF_ONLY
            )

        return True


TaskLogEntry = define_log_model(Task)
Example #16
0
from django.utils.translation import ugettext_lazy as _

from shuup.core.fields import QuantityField
from shuup.utils.analog import define_log_model


class SuppliedProduct(models.Model):
    supplier = models.ForeignKey("Supplier",
                                 on_delete=models.CASCADE,
                                 verbose_name=_("supplier"))
    product = models.ForeignKey("Product",
                                on_delete=models.CASCADE,
                                verbose_name=_("product"))
    sku = models.CharField(db_index=True,
                           max_length=128,
                           verbose_name=_('SKU'))
    alert_limit = models.IntegerField(default=0, verbose_name=_('alert limit'))
    physical_count = QuantityField(editable=False,
                                   verbose_name=_('physical stock count'))
    logical_count = QuantityField(editable=False,
                                  verbose_name=_('logical stock count'))

    class Meta:
        unique_together = ((
            "supplier",
            "product",
        ), )


SuppliedProductLogEntry = define_log_model(SuppliedProduct)
Example #17
0
    """
    owner = models.ForeignKey("Contact", on_delete=models.CASCADE, verbose_name=_("owner"))
    address = models.ForeignKey(
        MutableAddress, verbose_name=_('address'),
        related_name="saved_addresses", on_delete=models.CASCADE)
    role = EnumIntegerField(SavedAddressRole, verbose_name=_('role'), default=SavedAddressRole.SHIPPING)
    status = EnumIntegerField(SavedAddressStatus, default=SavedAddressStatus.ENABLED, verbose_name=_('status'))
    title = models.CharField(max_length=255, blank=True, verbose_name=_('title'))
    objects = SavedAddressManager()

    class Meta:
        verbose_name = _('saved address')
        verbose_name_plural = _('saved addresses')
        ordering = ("owner_id", "role", "title")

    def __str__(self):
        return u"%s" % self.get_title()

    def get_title(self):
        """
        Returns the display title for this `SavedAddress` instance. Defaults
        to a short representation of the address.

        This method should be used instead of accessing the `title` field
        directly when displaying `SavedAddress` objects.
        """
        return self.title.strip() if self.title else six.text_type(self.address)


SavedAddressLogEntry = define_log_model(SavedAddress)
Example #18
0
                            default=SavedAddressRole.SHIPPING)
    status = EnumIntegerField(SavedAddressStatus,
                              default=SavedAddressStatus.ENABLED,
                              verbose_name=_('status'))
    title = models.CharField(max_length=255,
                             blank=True,
                             verbose_name=_('title'))
    objects = SavedAddressManager()

    class Meta:
        verbose_name = _('saved address')
        verbose_name_plural = _('saved addresses')
        ordering = ("owner_id", "role", "title")

    def __str__(self):
        return u"%s" % self.get_title()

    def get_title(self):
        """
        Returns the display title for this `SavedAddress` instance. Defaults
        to a short representation of the address.

        This method should be used instead of accessing the `title` field
        directly when displaying `SavedAddress` objects.
        """
        return self.title.strip() if self.title else six.text_type(
            self.address)


SavedAddressLogEntry = define_log_model(SavedAddress)
Example #19
0
    class Meta:
        verbose_name = _("custom payment processor")
        verbose_name_plural = _("custom payment processors")

    def get_service_choices(self):
        return [
            ServiceChoice('manual', _("Manually processed payment")),
            ServiceChoice('cash', _("Cash payment"))
        ]

    def _create_service(self, choice_identifier, **kwargs):
        service = super(CustomPaymentProcessor, self)._create_service(
            choice_identifier, **kwargs)
        if choice_identifier == 'cash':
            service.behavior_components.add(
                StaffOnlyBehaviorComponent.objects.create())
        return service

    def process_payment_return_request(self, service, order, request):
        if service == 'cash':
            if not order.is_paid():
                order.create_payment(
                    order.taxful_total_price,
                    payment_identifier="Cash-%s" % now().isoformat(),
                    description="Cash Payment"
                )


PaymentMethodLogEntry = define_log_model(PaymentMethod)
PaymentProcessorLogEntry = define_log_model(PaymentProcessor)
Example #20
0
        return products

    def get_unshipped_products(self):
        return dict(
            (product, summary_datum)
            for product, summary_datum in self.get_product_summary().items()
            if summary_datum['unshipped']
        )

    def get_status_display(self):
        return force_text(self.status)

    def get_tracking_codes(self):
        return [shipment.tracking_code for shipment in self.shipments.all() if shipment.tracking_code]

    def can_edit(self):
        return (
            not self.has_refunds() and
            not self.is_canceled() and
            not self.is_complete() and
            self.shipping_status == ShippingStatus.NOT_SHIPPED and
            self.payment_status == PaymentStatus.NOT_PAID
        )


OrderLogEntry = define_log_model(Order)


def _round_price(value):
    return bankers_round(value, 2)  # TODO: To be fixed in SHUUP-1912
Example #21
0
        return self.module.get_stock_status(product_id)

    def get_suppliable_products(self, shop, customer):
        """
        :param shop: Shop to check for suppliability
        :type shop: shuup.core.models.Shop
        :param customer: Customer contact to check for suppliability
        :type customer: shuup.core.models.Contact
        :rtype: list[int]
        """
        return [
            shop_product.pk
            for shop_product
            in self.shop_products.filter(shop=shop)
            if shop_product.is_orderable(self, customer, shop_product.minimum_purchase_quantity)
        ]

    def adjust_stock(self, product_id, delta, created_by=None, type=None):
        from shuup.core.suppliers.base import StockAdjustmentType
        adjustment_type = type or StockAdjustmentType.INVENTORY
        return self.module.adjust_stock(product_id, delta, created_by=created_by, type=adjustment_type)

    def update_stock(self, product_id):
        return self.module.update_stock(product_id)

    def update_stocks(self, product_ids):
        return self.module.update_stocks(product_ids)


SupplierLogEntry = define_log_model(Supplier)
Example #22
0
    Note: `Carrier` objects should never be created on their own but
    rather through a concrete subclass.
    """

    service_model = ShippingMethod

    def delete(self, *args, **kwargs):
        ShippingMethod.objects.filter(carrier=self).update(**{"enabled": False})
        super(Carrier, self).delete(*args, **kwargs)

    def _create_service(self, choice_identifier, **kwargs):
        return ShippingMethod.objects.create(
            carrier=self, choice_identifier=choice_identifier, **kwargs)


class CustomCarrier(Carrier):
    """
    Carrier without any integration or special processing.
    """
    class Meta:
        verbose_name = _("custom carrier")
        verbose_name_plural = _("custom carriers")

    def get_service_choices(self):
        return [ServiceChoice('manual', _("Manually processed shipment"))]


ShippingMethodLogEntry = define_log_model(ShippingMethod)
CarrierLogEntry = define_log_model(Carrier)
Example #23
0
        attr = self.get_available_attribute_queryset().get(identifier=identifier)
        applied_attr = self.attributes.filter(attribute=attr).first()

        if not applied_attr:
            applied_attr = self.attributes.model(attribute=attr)
            setattr(applied_attr, applied_attr._applied_fk_field, self)
        else:
            self.clear_attribute_cache()

        if attr.is_translated:
            if not language:
                raise ValueError("`language` must be set for translated attribute %s" % attr)
            applied_attr.set_current_language(language)

        if not attr.is_translated and attr.is_null_value(value):
            # Trying to set a null value for an untranslated attribute,
            # so we can just get rid of the applied object altogether.
            # TODO: Do the same sort of cleanup for translated attributes.
            if applied_attr.pk:
                applied_attr.delete()
            return

        # Set the value and save the attribute (possibly new)
        applied_attr.value = value
        applied_attr.save()
        return applied_attr


AttributeLogEntry = define_log_model(Attribute)
Example #24
0
        Shipment, related_name='products', on_delete=models.PROTECT, verbose_name=_("shipment")
    )
    product = models.ForeignKey(
        "Product", related_name='shipments', on_delete=models.CASCADE, verbose_name=_("product")
    )
    quantity = QuantityField(verbose_name=_("quantity"))

    # volume is m^3, not mm^3, because mm^3 are tiny. like ants.
    unit_volume = MeasurementField(unit="m3", verbose_name=_("unit volume"))
    unit_weight = MeasurementField(unit="g", verbose_name=_("unit weight"))

    class Meta:
        verbose_name = _('sent product')
        verbose_name_plural = _('sent products')

    def __str__(self):  # pragma: no cover
        return "%(quantity)s of '%(product)s' in Shipment #%(shipment_pk)s" % {
            'product': self.product,
            'quantity': self.quantity,
            'shipment_pk': self.shipment_id,
        }

    def cache_values(self):
        prod = self.product
        self.unit_volume = (prod.width * prod.height * prod.depth) / CUBIC_MM_TO_CUBIC_METERS_DIVISOR
        self.unit_weight = prod.gross_weight


ShipmentLogEntry = define_log_model(Shipment)
ShipmentProductLogEntry = define_log_model(ShipmentProduct)
Example #25
0
        return get_thumbnailer(self.file)

    def get_thumbnail(self, **kwargs):
        """
        Get thumbnail for image.

        This will return `None` if there is no file or kind is not `ProductMediaKind.IMAGE`

        :rtype: easy_thumbnails.files.ThumbnailFile|None
        """
        kwargs.setdefault("size", (64, 64))
        kwargs.setdefault("crop", True)  # sane defaults
        kwargs.setdefault("upscale", True)  # sane defaults

        if kwargs["size"] == (0, 0):
            return None

        thumbnailer = self.easy_thumbnails_thumbnailer

        if not thumbnailer:
            return None

        try:
            return thumbnailer.get_thumbnail(thumbnail_options=kwargs)
        except InvalidImageFormatError:
            return None


ProductMediaLogEntry = define_log_model(ProductMedia)
Example #26
0
        shop__isnull=True,
        group__identifier__in=PROTECTED_CONTACT_GROUP_IDENTIFIERS).exclude(
            group__identifier__in=identifiers).exclude(pk__in=ids).values_list(
                "id", flat=True)
    return ContactGroupPriceDisplay.objects.filter(pk__in=list(ids) +
                                                   list(defaults))


def get_groups_for_price_display_create(shop):
    default_groups = ContactGroup.objects.filter(
        shop__isnull=True, identifier__in=PROTECTED_CONTACT_GROUP_IDENTIFIERS)
    used_in_shop = ContactGroupPriceDisplay.objects.filter(shop=shop)

    if not used_in_shop.exists():
        return default_groups

    used_groups = [pd.group for pd in used_in_shop]
    used_ids = [g.id for g in used_groups]
    used_identifiers = [g.identifier for g in used_groups]

    available = ContactGroup.objects.filter(shop=shop).exclude(
        id__in=used_ids).values_list("id", flat=True)
    defaults = default_groups.exclude(identifier__in=used_identifiers).exclude(
        pk__in=used_ids).values_list("id", flat=True)
    return ContactGroup.objects.filter(pk__in=list(available) + list(defaults))


CompanyContactLogEntry = define_log_model(CompanyContact)
PersonContactLogEntry = define_log_model(PersonContact)
ContactGroupLogEntry = define_log_model(ContactGroup)
Example #27
0
        """
        :param shop: Shop to check for suppliability
        :type shop: shuup.core.models.Shop
        :param customer: Customer contact to check for suppliability
        :type customer: shuup.core.models.Contact
        :rtype: list[int]
        """
        return [
            shop_product.pk
            for shop_product in self.shop_products.filter(shop=shop)
            if shop_product.is_orderable(
                self, customer, shop_product.minimum_purchase_quantity)
        ]

    def adjust_stock(self, product_id, delta, created_by=None, type=None):
        from shuup.core.suppliers.base import StockAdjustmentType
        adjustment_type = type or StockAdjustmentType.INVENTORY
        return self.module.adjust_stock(product_id,
                                        delta,
                                        created_by=created_by,
                                        type=adjustment_type)

    def update_stock(self, product_id):
        return self.module.update_stock(product_id)

    def update_stocks(self, product_ids):
        return self.module.update_stocks(product_ids)


SupplierLogEntry = define_log_model(Supplier)
Example #28
0
    def soft_delete(self, user=None):
        if not self.status == CategoryStatus.DELETED:
            for shop_product in self.primary_shop_products.all():
                shop_product.categories.remove(self)
                shop_product.primary_category = None
                shop_product.save()
            for shop_product in self.shop_products.all():
                shop_product.categories.remove(self)
                shop_product.primary_category = None
                shop_product.save()
            for child in self.children.all():
                child.parent = None
                child.save()
            self.status = CategoryStatus.DELETED
            self.add_log_entry("Deleted.", kind=LogEntryKind.DELETION, user=user)
            self.save()
            category_deleted.send(sender=type(self), category=self)

    def save(self, *args, **kwargs):
        rv = super(Category, self).save(*args, **kwargs)
        generate_multilanguage_slugs(self, self._get_slug_name)

        # bump children cache
        from shuup.core import cache
        cache.bump_version("category_cached_children")

        return rv


CategoryLogEntry = define_log_model(Category)
Example #29
0
from shuup.core.models import Contact, Shop
from shuup.utils.analog import define_log_model


class MailchimpBaseModel(models.Model):
    shop = models.ForeignKey(Shop, related_name="+", on_delete=models.CASCADE, verbose_name=_("shop"))
    updated = models.DateTimeField(auto_now=True, verbose_name=_("updated"))
    created = models.DateTimeField(auto_now_add=True, verbose_name=_("created"))
    sent_to_mailchimp = models.DateTimeField(null=True, verbose_name=_("sent to mailchimp"))

    class Meta:
        abstract = True


class MailchimpContact(MailchimpBaseModel):
    contact = models.ForeignKey(
        Contact,
        related_name="+",
        on_delete=models.CASCADE,
        verbose_name=_("contact"),
        blank=True,
        null=True,
    )
    email = models.EmailField(max_length=254, verbose_name=_('email'), unique=True)

    class Meta:
        abstract = False


MailchimpLogEntry = define_log_model(MailchimpContact)
Example #30
0
    image = FilerImageField(
        verbose_name=_("Image"),
        blank=True, null=True,
        on_delete=models.SET_NULL,
        help_text=_("The image of your object."), related_name="blog_meta_image"
    )
    og_type = EnumField(PageOpenGraphType, verbose_name=_("type"), default=PageOpenGraphType.Website)
    translations = TranslatedFields(
        title=models.CharField(max_length=100, blank=True, verbose_name=_("Title"), help_text=_(
            'The title of your object as it should appear within the graph, e.g. The Rock.'
        )),
        description=models.TextField(max_length=160, blank=True, verbose_name=_("Description"), help_text=_(
            "A one to two sentence description of your object."
        )),
        section=models.CharField(max_length=256, blank=True, verbose_name=_("Section"), help_text=_(
            "A high-level section name, e.g. Technology. Only applicable when type is Article."
        )),
        tags=models.CharField(max_length=256, blank=True, verbose_name=_("Tags"), help_text=_(
            "Tag words associated with this article. Only applicable when type is Article."
        )),
        article_author=models.CharField(max_length=100, blank=True, verbose_name=_("Article author"), help_text=_(
            'The name of the author for the article. Only applicable when type is Article.'
        ))
    )

    def __str__(self):
        return force_text(self.page)


PageLogEntry = define_log_model(Page)
Example #31
0
    identifier = InternalIdentifierField(unique=True)

    shops = models.ManyToManyField("shuup.Shop",
                                   blank=True,
                                   verbose_name=_("shops"))
    name = models.CharField(
        max_length=128,
        verbose_name=_('name'),
        help_text=_(
            "Enter the manufacturer’s name. "
            "Products can be filtered by the manufacturer and can be useful for inventory and stock management."
        ))
    url = models.CharField(
        null=True,
        blank=True,
        max_length=128,
        verbose_name=_('URL'),
        help_text=_(
            "Enter the URL of the product manufacturer if you would like customers to be able to visit the manufacturer's "
            "website."))

    class Meta:
        verbose_name = _('manufacturer')
        verbose_name_plural = _('manufacturers')

    def __str__(self):  # pragma: no cover
        return u'%s' % (self.name)


ManufacturerLogEntry = define_log_model(Manufacturer)
Example #32
0
            (product, summary_datum)
            for product, summary_datum in self.get_product_summary().items()
            if summary_datum['unshipped'])

    def get_status_display(self):
        return force_text(self.status)

    def get_tracking_codes(self):
        return [
            shipment.tracking_code
            for shipment in self.shipments.all_except_deleted()
            if shipment.tracking_code
        ]

    def can_edit(self):
        return (not self.has_refunds() and not self.is_canceled()
                and not self.is_complete()
                and self.shipping_status == ShippingStatus.NOT_SHIPPED
                and self.payment_status == PaymentStatus.NOT_PAID)

    def get_customer_name(self):
        name_attrs = [
            "customer", "billing_address", "orderer", "shipping_address"
        ]
        for attr in name_attrs:
            if getattr(self, "%s_id" % attr):
                return getattr(self, attr).name


OrderLogEntry = define_log_model(Order)
Example #33
0
# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2016, Shoop Ltd. All rights reserved.
#
# This source code is licensed under the AGPLv3 license found in the
# LICENSE file in the root directory of this source tree.
from django.db import models
from django.utils.translation import ugettext_lazy as _

from shuup.core.fields import QuantityField
from shuup.utils.analog import define_log_model


class SuppliedProduct(models.Model):
    supplier = models.ForeignKey("Supplier", on_delete=models.CASCADE, verbose_name=_("supplier"))
    product = models.ForeignKey("Product", on_delete=models.CASCADE, verbose_name=_("product"))
    sku = models.CharField(db_index=True, max_length=128, verbose_name=_('SKU'))
    alert_limit = models.IntegerField(default=0, verbose_name=_('alert limit'))
    physical_count = QuantityField(editable=False, verbose_name=_('physical stock count'))
    logical_count = QuantityField(editable=False, verbose_name=_('logical stock count'))

    class Meta:
        unique_together = (("supplier", "product", ), )


SuppliedProductLogEntry = define_log_model(SuppliedProduct)
Example #34
0
                                       verbose_name=_("modified on"))

    objects = TaskCommentQuerySet.as_manager()

    def reply(self, contact, body):
        comment = TaskComment(task=self.task,
                              comment_author=contact,
                              body=body)
        comment.full_clean()
        comment.save()
        return comment

    def as_html(self):
        return mark_safe(force_text(self.body))

    def can_see(self, user):
        is_admin = user.is_superuser
        is_staff = bool(user.is_staff
                        and user in self.task.shop.staff_members.all())

        if not (is_admin or is_staff):
            return (self.visibility == TaskCommentVisibility.PUBLIC)
        elif not is_admin:
            return (self.visibility == TaskCommentVisibility.PUBLIC
                    or self.visibility == TaskCommentVisibility.STAFF_ONLY)

        return True


TaskLogEntry = define_log_model(Task)
Example #35
0
    foreign_currency = CurrencyField(default=None,
                                     blank=True,
                                     null=True,
                                     verbose_name=_('foreign amount currency'))

    description = models.CharField(max_length=256,
                                   blank=True,
                                   verbose_name=_('description'))

    class Meta:
        abstract = True


# TODO (2.0): Rename this to OrderPayment
class Payment(AbstractPayment):
    order = models.ForeignKey("Order",
                              related_name='payments',
                              on_delete=models.PROTECT,
                              verbose_name=_('order'))

    class Meta:
        verbose_name = _('payment')
        verbose_name_plural = _('payments')

    @property
    def currency(self):
        return self.order.currency


PaymentLogEntry = define_log_model(Payment)
Example #36
0
    def get_serialized_steps(self):
        return [step.serialize() for step in self.get_steps()]

    def set_serialized_steps(self, serialized_data):
        self._steps = None
        self._step_data = serialized_data
        # Poor man's validation
        for step in self.get_steps():
            pass

    @property
    def event_class(self):
        return Event.class_for_identifier(self.event_identifier)

    def __str__(self):
        return self.name

    def execute(self, context):
        """
        Execute the script in the given context.

        :param context: Script context
        :type context: shuup.notify.script.Context
        """
        for step in self.get_steps():
            if step.execute(context) == StepNext.STOP:
                break


ScriptLogEntry = define_log_model(Script)
Example #37
0
    class Meta:
        verbose_name = _("custom payment processor")
        verbose_name_plural = _("custom payment processors")

    def get_service_choices(self):
        return [
            ServiceChoice('manual', _("Manually processed payment")),
            ServiceChoice('cash', _("Cash payment"))
        ]

    def _create_service(self, choice_identifier, **kwargs):
        service = super(CustomPaymentProcessor,
                        self)._create_service(choice_identifier, **kwargs)
        if choice_identifier == 'cash':
            service.behavior_components.add(
                StaffOnlyBehaviorComponent.objects.create())
        return service

    def process_payment_return_request(self, service, order, request):
        if service == 'cash':
            if not order.is_paid():
                order.create_payment(order.taxful_total_price,
                                     payment_identifier="Cash-%s" %
                                     now().isoformat(),
                                     description="Cash Payment")


PaymentMethodLogEntry = define_log_model(PaymentMethod)
PaymentProcessorLogEntry = define_log_model(PaymentProcessor)
Example #38
0
                     quantity=None,
                     shipping_address=None):
        supplier_strategy = cached_load(
            "SHUUP_SHOP_PRODUCT_SUPPLIERS_STRATEGY")
        kwargs = {
            "shop_product": self,
            "customer": customer,
            "quantity": quantity,
            "shipping_address": shipping_address
        }
        return supplier_strategy().get_supplier(**kwargs)

    def __str__(self):
        return self.get_name()

    def get_name(self):
        return self._safe_get_string("name")

    def get_description(self):
        return self._safe_get_string("description")

    def get_short_description(self):
        return self._safe_get_string("short_description")

    def _safe_get_string(self, key):
        return (self.safe_translation_getter(key, any_language=True) or
                self.product.safe_translation_getter(key, any_language=True))


ShopProductLogEntry = define_log_model(ShopProduct)
Example #39
0
            applied_attr.set_current_language(language)

        if not attr.is_translated and attr.is_null_value(value):
            # Trying to set a null value for an untranslated attribute,
            # so we can just get rid of the applied object altogether.
            # TODO: Do the same sort of cleanup for translated attributes.
            if applied_attr.pk:
                applied_attr.delete()
            return

        # Set the value and save the attribute (possibly new)
        applied_attr.value = value
        applied_attr.save()
        return applied_attr

    def clear_attribute_value(self, identifier, language=None):
        avail_attrs = self.get_available_attribute_queryset()
        attr = avail_attrs.get(identifier=identifier)
        attr_val = self.attributes.filter(attribute=attr).first()
        if not attr_val:
            return
        if language is None:  # Delete all translations
            attr_val.delete()
            return
        trans = attr_val.translations.filter(language_code=language).first()
        if trans:
            trans.delete()


AttributeLogEntry = define_log_model(Attribute)
Example #40
0
    translations = TranslatedFields(
        name=models.CharField(max_length=64, verbose_name=_("name")),
        public_name=models.CharField(max_length=64, verbose_name=_("public name")),
        maintenance_message=models.CharField(max_length=300, blank=True, verbose_name=_("maintenance message"))
    )

    def __str__(self):
        return self.safe_translation_getter("name", default="Shop %d" % self.pk)

    def create_price(self, value):
        """
        Create a price with given value and settings of this shop.

        Takes the ``prices_include_tax`` and ``currency`` settings of
        this Shop into account.

        :type value: decimal.Decimal|int|str
        :rtype: shuup.core.pricing.Price
        """
        if self.prices_include_tax:
            return TaxfulPrice(value, self.currency)
        else:
            return TaxlessPrice(value, self.currency)

    def _are_changes_protected(self):
        return Order.objects.filter(shop=self).exists()


ShopLogEntry = define_log_model(Shop)
Example #41
0
        verbose_name_plural = _("currencies")

    def __str__(self):
        return self.code


def get_currency_precision(currency):
    """
    Get precision by currency code.

    Precision values will be populated from the ``decimal_places``
    fields of the `Currency` objects in the database.

    :type currency: str
    :param currency: Currency code as 3-letter string (ISO-4217).

    :rtype: decimal.Decimal|None
    :return: Precision value for a given currency code or None for unknown.
    """
    cache_key = "currency_precision:" + currency
    precision = cache.get(cache_key)
    if precision is None:
        currency_obj = Currency.objects.filter(code=currency).first()
        precision = decimal.Decimal(
            "0.1")**currency_obj.decimal_places if currency_obj else None
        cache.set(cache_key, precision)
    return precision


CurrencyLogEntry = define_log_model(Currency)
Example #42
0
            ProductPackageLink.objects.filter(child=self).values_list("parent", flat=True)
        ))

    def get_all_package_children(self):
        return Product.objects.filter(pk__in=(
            ProductPackageLink.objects.filter(parent=self).values_list("child", flat=True)
        ))

    def get_public_media(self):
        return self.media.filter(enabled=True, public=True)

    def is_stocked(self):
        return (self.stock_behavior == StockBehavior.STOCKED)


ProductLogEntry = define_log_model(Product)


class ProductCrossSell(models.Model):
    product1 = models.ForeignKey(
        Product, related_name="cross_sell_1", on_delete=models.CASCADE, verbose_name=_("primary product"))
    product2 = models.ForeignKey(
        Product, related_name="cross_sell_2", on_delete=models.CASCADE, verbose_name=_("secondary product"))
    weight = models.IntegerField(default=0, verbose_name=_("weight"))
    type = EnumIntegerField(ProductCrossSellType, verbose_name=_("type"))

    class Meta:
        verbose_name = _('cross sell link')
        verbose_name_plural = _('cross sell links')

Example #43
0
                                on_delete=models.CASCADE,
                                verbose_name=_("product"))
    quantity = QuantityField(verbose_name=_("quantity"))

    unit_volume = MeasurementField(unit=get_shuup_volume_unit(),
                                   verbose_name=_("unit volume ({})".format(
                                       get_shuup_volume_unit())))
    unit_weight = MeasurementField(unit=settings.SHUUP_MASS_UNIT,
                                   verbose_name=_("unit weight ({})".format(
                                       settings.SHUUP_MASS_UNIT)))

    class Meta:
        verbose_name = _('sent product')
        verbose_name_plural = _('sent products')

    def __str__(self):  # pragma: no cover
        return "%(quantity)s of '%(product)s' in Shipment #%(shipment_pk)s" % {
            'product': self.product,
            'quantity': self.quantity,
            'shipment_pk': self.shipment_id,
        }

    def cache_values(self):
        prod = self.product
        self.unit_volume = prod.width * prod.height * prod.depth
        self.unit_weight = prod.gross_weight


ShipmentLogEntry = define_log_model(Shipment)
ShipmentProductLogEntry = define_log_model(ShipmentProduct)
Example #44
0
            if self.usages.filter(order__customer=customer, coupon=self).count() >= self.usage_limit_customer:
                return False

        return not self.exhausted

    def use(self, order):
        return CouponUsage.add_usage(order=order, coupon=self)

    def increase_customer_usage_limit_by(self, amount):
        if self.usage_limit_customer:
            new_limit = self.usage_limit_customer + amount
        else:
            new_limit = self.usages.count() + amount
        self.usage_limit_customer = new_limit

    def increase_usage_limit_by(self, amount):
        self.usage_limit = self.usage_limit + amount if self.usage_limit else (self.usages.count() + amount)

    def has_been_used(self, usage_count=1):
        """ See if code was already used the number of maximum times given """
        return CouponUsage.objects.filter(coupon=self).count() >= usage_count

    def __str__(self):
        return self.code


CatalogCampaignLogEntry = define_log_model(CatalogCampaign)
BasketCampaignLogEntry = define_log_model(BasketCampaign)
CouponLogEntry = define_log_model(Coupon)
CouponUsageLogEntry = define_log_model(CouponUsage)
Example #45
0
        name=models.CharField(max_length=100, verbose_name=_('name'), help_text=_(
                "The customer tax group name. "
                "Customer tax groups can be used to control how taxes are applied to a set of customers. "
            )
        ),
    )
    enabled = models.BooleanField(default=True, verbose_name=_('enabled'))

    class Meta:
        verbose_name = _('customer tax group')
        verbose_name_plural = _('customer tax groups')

    @classmethod
    def get_default_person_group(cls):
        obj, c = CustomerTaxGroup.objects.get_or_create(identifier="default_person_customers", defaults={
            "name": _("Retail Customers")
        })
        return obj

    @classmethod
    def get_default_company_group(cls):
        obj, c = CustomerTaxGroup.objects.get_or_create(identifier="default_company_customers", defaults={
            "name": _("Company Customers")
        })
        return obj


TaxLogEntry = define_log_model(Tax)
TaxClassLogEntry = define_log_model(TaxClass)
CustomerTaxGroupLogEntry = define_log_model(CustomerTaxGroup)
Example #46
0
    image = FilerImageField(
        verbose_name=_("Image"),
        blank=True, null=True,
        on_delete=models.SET_NULL,
        help_text=_("The image of your object."), related_name="blog_meta_image"
    )
    og_type = EnumField(PageOpenGraphType, verbose_name=_("type"), default=PageOpenGraphType.Website)
    translations = TranslatedFields(
        title=models.CharField(max_length=100, blank=True, verbose_name=_("Title"), help_text=_(
            'The title of your object as it should appear within the graph, e.g. The Rock.'
        )),
        description=models.TextField(max_length=160, blank=True, verbose_name=_("Description"), help_text=_(
            "A one to two sentence description of your object."
        )),
        section=models.CharField(max_length=256, blank=True, verbose_name=_("Section"), help_text=_(
            "A high-level section name, e.g. Technology. Only applicable when type is Article."
        )),
        tags=models.CharField(max_length=256, blank=True, verbose_name=_("Tags"), help_text=_(
            "Tag words associated with this article. Only applicable when type is Article."
        )),
        article_author=models.CharField(max_length=100, blank=True, verbose_name=_("Article author"), help_text=_(
            'The name of the author for the article. Only applicable when type is Article.'
        ))
    )

    def __str__(self):
        return force_text(self.page)


PageLogEntry = define_log_model(Page)
Example #47
0
        'is_active': user.is_active,
        'first_name': getattr(user, 'first_name', ''),
        'last_name': getattr(user, 'last_name', ''),
        'email': getattr(user, 'email', ''),
    }
    return PersonContact.objects.get_or_create(user=user, defaults=defaults)[0]


def get_company_contact(user):
    """
    Get preferred CompanyContact of given user.

    If user has associated PersonContact which is member of
    CompanyContact, return CompanyContact. Otherwise, return None.

    :param user: User object (or None) to get contact for
    :type user: django.contrib.auth.models.User|None
    :return:
      CompanyContact (or none) of which user's PersonContact is a member
    :rtype: CompanyContact|None
    """
    contact = get_person_contact(user)
    if not contact:
        return None
    return contact.company_memberships.filter(is_active=True).first()


CompanyContactLogEntry = define_log_model(CompanyContact)
PersonContactLogEntry = define_log_model(PersonContact)
ContactGroupLogEntry = define_log_model(ContactGroup)