Пример #1
0
 def test_autoslugfield_allow_unicode_kwargs_precedence(self):
     from oscar.models.fields import AutoSlugField
     with override_settings(OSCAR_SLUG_ALLOW_UNICODE=True):
         autoslug_field = AutoSlugField(populate_from='title', allow_unicode=False)
         self.assertFalse(autoslug_field.allow_unicode)
         autoslug_field = AutoSlugField(populate_from='title')
         self.assertTrue(autoslug_field.allow_unicode)
    def test_migration(self):
        """
        Tests making migrations with Django 1.7+'s migration framework
        """

        import oscar
        from django.db import migrations
        from django.db.migrations.writer import MigrationWriter
        from oscar.models.fields import AutoSlugField
        fields = {
            'autoslugfield': AutoSlugField(populate_from='otherfield'),
        }

        migration = type(
            str("Migration"), (migrations.Migration, ), {
                "operations": [
                    migrations.CreateModel("MyModel", tuple(
                        fields.items()), {'populate_from': 'otherfield'},
                                           (models.Model, )),
                ],
            })
        writer = MigrationWriter(migration)
        output = writer.as_string()

        if isinstance(output, str):
            output = output.encode('utf-8')

        # We don't test the output formatting - that's too fragile.
        # Just make sure it runs for now, and that things look alright.
        context = {
            'migrations': migrations,
            'oscar': oscar,
        }
        result = self.safe_exec(output, context=context)
        self.assertIn("Migration", result)
Пример #3
0
class AbstractBase(models.Model):
    """
    Implements the interface declared by shipping.base.Base
    """
    code = AutoSlugField(_("Slug"),
                         max_length=128,
                         unique=True,
                         populate_from='name')
    name = models.CharField(_("Name"), max_length=128, unique=True)
    description = models.TextField(_("Description"), blank=True)

    # We allow shipping methods to be linked to a specific set of countries
    countries = models.ManyToManyField('address.Country',
                                       blank=True,
                                       verbose_name=_("Countries"))

    # We need this to mimic the interface of the Base shipping method
    is_discounted = False

    class Meta:
        abstract = True
        app_label = 'shipping'
        ordering = ['name']
        verbose_name = _("Shipping Method")
        verbose_name_plural = _("Shipping Methods")

    def __str__(self):
        return self.name
Пример #4
0
class AbstractBase(models.Model):
    """
    Implements the interface declared by shipping.base.Base
    """
    code = AutoSlugField(_("Slug"), max_length=128, unique=True,
                         populate_from='name')
    name = models.CharField(_("Name"), max_length=128, unique=True)
    description = models.TextField(_("Description"), blank=True)

    # We allow shipping methods to be linked to a specific set of countries
    countries = models.ManyToManyField('address.Country', null=True,
                                       blank=True, verbose_name=_("Countries"))

    is_discounted = False
    discount = D('0.00')
    _basket = None

    class Meta:
        abstract = True
        verbose_name = _("Shipping Method")
        verbose_name_plural = _("Shipping Methods")
        ordering = ['name']

    def __unicode__(self):
        return self.name

    def set_basket(self, basket):
        self._basket = basket
Пример #5
0
class AbstractShippingEventType(models.Model):
    """
    A type of shipping/fulfillment event

    Eg: 'Shipped', 'Cancelled', 'Returned'

    一种运输/履行事件
    例如:'已发货','已取消','已退回'
    """
    # Name is the friendly description of an event
    # 名称是对事件的友好描述
    name = models.CharField(_("Name"), max_length=255, unique=True)
    # Code is used in forms
    # 代码用于表单
    code = AutoSlugField(_("Code"),
                         max_length=128,
                         unique=True,
                         populate_from='name')

    class Meta:
        abstract = True
        app_label = 'order'
        verbose_name = _("Shipping Event Type")
        verbose_name_plural = _("Shipping Event Types")
        ordering = ('name', )

    def __str__(self):
        return self.name
Пример #6
0
    def test_17_migration(self):
        """
        Tests making migrations with Django 1.7+'s migration framework
        """
        from django.db import migrations
        from django.db.migrations.writer import MigrationWriter
        from django.utils import six
        from oscar.models.fields import AutoSlugField

        fields = {
            'autoslugfield': AutoSlugField(),
        }

        migration = type(
            str("Migration"), (migrations.Migration, ), {
                "operations": [
                    migrations.CreateModel("MyModel", tuple(
                        fields.items()), {'populate_from': 'otherfield'},
                                           (models.Model, )),
                ],
            })
        writer = MigrationWriter(migration)
        output = writer.as_string()
        # It should NOT be unicode.
        self.assertIsInstance(output, six.binary_type,
                              "Migration as_string returned unicode")
        # We don't test the output formatting - that's too fragile.
        # Just make sure it runs for now, and that things look alright.
        result = self.safe_exec(output)
        self.assertIn("Migration", result)
Пример #7
0
class ShippingMethod(models.Model):
    # Fields from shipping.base.ShippingMethod must be added here manually.
    code = AutoSlugField(_("Slug"),
                         max_length=128,
                         unique=True,
                         populate_from='name')
    name = models.CharField(_("Name"), max_length=128, unique=True)
    description = models.TextField(_("Description"), blank=True)

    # We allow shipping methods to be linked to a specific set of countries
    countries = models.ManyToManyField('address.Country',
                                       null=True,
                                       blank=True,
                                       verbose_name=_("Countries"))

    is_discounted = False
    discount = D('0.00')
    _basket = None

    class Meta:
        abstract = True
        verbose_name = _("Shipping Method")
        verbose_name_plural = _("Shipping Methods")

    def __unicode__(self):
        return self.name

    def set_basket(self, basket):
        self._basket = basket
Пример #8
0
class AbstractOption(models.Model):
    """
    An option that can be selected for a particular item when the product
    is added to the basket.

    For example,  a list ID for an SMS message send, or a personalised message
    to print on a T-shirt.

    This is not the same as an 'attribute' as options do not have a fixed value
    for a particular item.  Instead, option need to be specified by a customer
    when they add the item to their basket.
    """
    name = models.CharField(_("Name"), max_length=128)
    code = AutoSlugField(_("Code"), max_length=128, unique=True,
                         populate_from='name')

    REQUIRED, OPTIONAL = ('Required', 'Optional')
    TYPE_CHOICES = (
        (REQUIRED, _("Required - a value for this option must be specified")),
        (OPTIONAL, _("Optional - a value for this option can be omitted")),
    )
    type = models.CharField(_("Status"), max_length=128, default=REQUIRED,
                            choices=TYPE_CHOICES)

    class Meta:
        abstract = True
        verbose_name = _("Option")
        verbose_name_plural = _("Options")

    def __unicode__(self):
        return self.name

    @property
    def is_required(self):
        return self.type == self.REQUIRED
class NewAbstractPartner(models.Model):
    code = AutoSlugField(_("Code"), max_length=128, unique=True, populate_from='name')

    name = models.CharField(pgettext_lazy(u"Partner's name", u"Name"), max_length=128, blank=True)
    
    user = models.OneToOneField(
        AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='partner',
        null=True,
        verbose_name=_("User"))

    is_active = models.BooleanField(
        _("Is Active?"), default=False)

    @property
    def display_name(self):
        return self.name or self.code

    @property
    def primary_address(self):
        """
        Returns a partners primary address. Usually that will be the
        headquarters or similar.

        This is a rudimentary implementation that raises an error if there's
        more than one address. If you actually want to support multiple
        addresses, you will likely need to extend PartnerAddress to have some
        field or flag to base your decision on.
        """
        addresses = self.addresses.all()
        if len(addresses) == 0:  # intentionally using len() to save queries
            return None
        elif len(addresses) == 1:
            return addresses[0]
        else:
            raise NotImplementedError(
                "Oscar's default implementation of primary_address only "
                "supports one PartnerAddress.  You need to override the "
                "primary_address to look up the right address")

    def get_address_for_stockrecord(self, stockrecord):
        """
        Stock might be coming from different warehouses. Overriding this
        function allows selecting the correct PartnerAddress for the record.
        That can be useful when determining tax.
        """
        return self.primary_address

    class Meta:
        abstract = True
        app_label = 'partner'
        ordering = ('name', 'code')
        permissions = (('dashboard_access', 'Can access dashboard'), )
        verbose_name = _('Fulfillment partner')
        verbose_name_plural = _('Fulfillment partners')

    def __str__(self):
        return self.display_name
class AbstractOption(models.Model):
    """
    An option that can be selected for a particular item when the product
    is added to the basket.

    For example,  a list ID for an SMS message send, or a personalised message
    to print on a T-shirt.

    This is not the same as an 'attribute' as options do not have a fixed value
    for a particular item.  Instead, option need to be specified by a customer
    when they add the item to their basket.

    The `type` of the option determines the form input that will be used to
    collect the information from the customer, and the `required` attribute
    determines whether a value must be supplied in order to add the item to the basket.
    """

    # Option types
    TEXT = "text"
    INTEGER = "integer"
    FLOAT = "float"
    BOOLEAN = "boolean"
    DATE = "date"

    TYPE_CHOICES = (
        (TEXT, _("Text")),
        (INTEGER, _("Integer")),
        (BOOLEAN, _("True / False")),
        (FLOAT, _("Float")),
        (DATE, _("Date")),
    )

    name = models.CharField(_("Name"), max_length=128, db_index=True)
    code = AutoSlugField(_("Code"),
                         max_length=128,
                         unique=True,
                         populate_from='name')
    type = models.CharField(_("Type"),
                            max_length=255,
                            default=TEXT,
                            choices=TYPE_CHOICES)
    required = models.BooleanField(_("Is this option required?"),
                                   default=False)

    class Meta:
        abstract = True
        app_label = 'catalogue'
        ordering = ['name']
        verbose_name = _("Option")
        verbose_name_plural = _("Options")

    def __str__(self):
        return self.name

    @property
    @deprecated
    def is_required(self):
        return self.required
class OfferGroup(models.Model):
    """
    Ordered group of Offers
    """
    name = models.CharField(max_length=64, null=False)
    slug = AutoSlugField(populate_from='name', unique=True, null=True, default=None)
    priority = models.IntegerField(null=False, unique=True)
    is_system_group = models.BooleanField(default=False, editable=False)

    class Meta:
        verbose_name = _('OfferGroup')
        ordering = ('-priority', )

    def __str__(self):
        return '{} (priority {})'.format(self.name, self.priority)
Пример #12
0
class AbstractCategory(Timestamp):

    name = models.CharField(max_length=200)
    slug = AutoSlugField(max_length=128, populate_from='name')

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

    @property
    def get_number_posts(self):
        return AbstractCategory.objects.filter(
            name=self.name,
            abstractpost__post_date__lte=datetime.date.today()).count()
Пример #13
0
class AbstractProductClass(models.Model):
    """
    Used for defining options and attributes for a subset of products.
    E.g. Books, DVDs and Toys. A product can only belong to one product class.

    At least one product class must be created when setting up a new
    Oscar deployment.

    Not necessarily equivalent to top-level categories but usually will be.
    """
    name = models.CharField(_('Name'), max_length=128)
    slug = AutoSlugField(_('Slug'),
                         max_length=128,
                         unique=True,
                         populate_from='name')

    #: Some product type don't require shipping (e.g. digital products) - we use
    #: this field to take some shortcuts in the checkout.
    requires_shipping = models.BooleanField(_("Requires shipping?"),
                                            default=True)

    #: Digital products generally don't require their stock levels to be
    #: tracked.
    track_stock = models.BooleanField(_("Track stock levels?"), default=True)

    #: These are the options (set by the user when they add to basket) for this
    #: item class.  For instance, a product class of "SMS message" would always
    #: require a message to be specified before it could be bought.
    #: Note that you can also set options on a per-product level.
    options = models.ManyToManyField('catalogue.Option',
                                     blank=True,
                                     verbose_name=_("Options"))

    class Meta:
        abstract = True
        app_label = 'catalogue'
        ordering = ['name']
        verbose_name = _("Product class")
        verbose_name_plural = _("Product classes")

    def __str__(self):
        return self.name

    @property
    def has_attributes(self):
        return self.attributes.exists()
Пример #14
0
class AbstractPaymentEventType(models.Model):
    """
    Payment event types are things like 'Paid', 'Failed', 'Refunded'.

    These are effectively the transaction types.
    """
    name = models.CharField(_("Name"), max_length=128, unique=True)
    code = AutoSlugField(_("Code"), max_length=128, unique=True,
                         populate_from='name')

    class Meta:
        abstract = True
        verbose_name = _("Payment Event Type")
        verbose_name_plural = _("Payment Event Types")
        ordering = ('name', )

    def __unicode__(self):
        return self.name
Пример #15
0
class AbstractSourceType(models.Model):
    """
    A type of payment source.

    This could be an external partner like PayPal or DataCash,
    or an internal source such as a managed account.
    """
    name = models.CharField(_("Name"), max_length=128)
    code = AutoSlugField(
        _("Code"), max_length=128, populate_from='name', unique=True,
        help_text=_("This is used within forms to identify this source type"))

    class Meta:
        abstract = True
        app_label = 'payment'
        verbose_name = _("Source Type")
        verbose_name_plural = _("Source Types")

    def __str__(self):
        return self.name
Пример #16
0
class AbstractShippingEventType(models.Model):
    """
    A type of shipping/fulfillment event

    Eg: 'Shipped', 'Cancelled', 'Returned'
    """
    # Name is the friendly description of an event
    name = models.CharField(_("Name"), max_length=255, unique=True)
    # Code is used in forms
    code = AutoSlugField(_("Code"), max_length=128, unique=True,
                         populate_from='name')

    class Meta:
        abstract = True
        verbose_name = _("Shipping Event Type")
        verbose_name_plural = _("Shipping Event Types")
        ordering = ('name', )

    def __unicode__(self):
        return self.name
Пример #17
0
class AbstractBase(models.Model):
    """
    Implements the interface declared by shipping.base.Base
    实现shipping.base.Base声明的接口
    """
    code = AutoSlugField(_("Slug"),
                         max_length=128,
                         unique=True,
                         populate_from='name')
    name = models.CharField(_("Name"), max_length=128, unique=True)
    description = models.TextField(_("Description"), blank=True)

    # We allow shipping methods to be linked to a specific set of countries
    # 我们允许将运输方法链接到特定的一组国家。
    countries = models.ManyToManyField('address.Country',
                                       blank=True,
                                       verbose_name=_("Countries"))

    # We need this to mimic the interface of the Base shipping method
    # 我们需要这个来模仿Base运输方法的界面
    is_discounted = False

    class Meta:
        abstract = True
        app_label = 'shipping'
        ordering = ['name']
        verbose_name = _("Shipping Method")
        verbose_name_plural = _("Shipping Methods")

    def __str__(self):
        return self.name

    def discount(self, basket):
        """
        Return the discount on the standard shipping charge
        退还标准运费的折扣
        """
        # This method is identical to the Base.discount().
        return D('0.00')
Пример #18
0
class AbstractPost(Timestamp):

    title = models.CharField(max_length=200)
    content = models.TextField(blank=True)
    featured_image = models.ImageField(_("Featured Image"), upload_to='image')
    post_date = models.DateField(default=timezone.now)
    author = models.ForeignKey(AUTH_USER_MODEL,
                               blank=True,
                               null=True,
                               on_delete=models.CASCADE)
    category = models.ManyToManyField(AbstractCategory,
                                      blank=True,
                                      through='AbstractCategoryGroup',
                                      verbose_name=_("Category"))
    excerpt = models.TextField(blank=True)
    slug = AutoSlugField(max_length=128, populate_from='title')

    class Meta:
        ordering = ['-post_date', 'title']

    def __str__(self):
        return self.title
Пример #19
0
class AbstractPaymentEventType(models.Model):
    """
    支払いイベント管理テーブル
    支払いイベントの種類:「支払い済み」、「失敗」、「払い戻し」など

    These are effectively the transaction types.
    """
    name = models.CharField(_("Name"), max_length=128, unique=True)
    code = AutoSlugField(_("Code"),
                         max_length=128,
                         unique=True,
                         populate_from='name')

    class Meta:
        abstract = True
        app_label = 'order'
        verbose_name = _("Payment Event Type")
        verbose_name_plural = _("支払イベント種類管理(支払済/失敗/払戻)")
        ordering = ('name', )

    def __str__(self):
        return self.name
Пример #20
0
class AbstractCommunicationEventType(models.Model):
    """
    A 'type' of communication.  Like an order confirmation email.
    """

    #: Code used for looking up this event programmatically.
    # e.g. PASSWORD_RESET. AutoSlugField uppercases the code for us because
    # it's a useful convention that's been enforced in previous Oscar versions
    code = AutoSlugField(
        _('Code'), max_length=128, unique=True, populate_from='name',
        separator=six.u("_"), uppercase=True, editable=True,
        validators=[
            RegexValidator(
                regex=r'^[a-zA-Z_][0-9a-zA-Z_]*$',
                message=_(
                    "Code can only contain the letters a-z, A-Z, digits, "
                    "and underscores, and can't start with a digit."))],
        help_text=_("Code used for looking up this event programmatically"))

    #: Name is the friendly description of an event for use in the admin
    name = models.CharField(
        _('Name'), max_length=255,
        help_text=_("This is just used for organisational purposes"))

    # We allow communication types to be categorised
    # For backwards-compatibility, the choice values are quite verbose
    ORDER_RELATED = 'Order related'
    USER_RELATED = 'User related'
    CATEGORY_CHOICES = (
        (ORDER_RELATED, _('Order related')),
        (USER_RELATED, _('User related'))
    )

    category = models.CharField(
        _('Category'), max_length=255, default=ORDER_RELATED,
        choices=CATEGORY_CHOICES)

    # Template content for emails
    # NOTE: There's an intentional distinction between None and ''. None
    # instructs Oscar to look for a file-based template, '' is just an empty
    # template.
    email_subject_template = models.CharField(
        _('Email Subject Template'), max_length=255, blank=True, null=True)
    email_body_template = models.TextField(
        _('Email Body Template'), blank=True, null=True)
    email_body_html_template = models.TextField(
        _('Email Body HTML Template'), blank=True, null=True,
        help_text=_("HTML template"))

    # Template content for SMS messages
    sms_template = models.CharField(_('SMS Template'), max_length=170,
                                    blank=True, null=True,
                                    help_text=_("SMS template"))

    date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)
    date_updated = models.DateTimeField(_("Date Updated"), auto_now=True)

    objects = CommunicationTypeManager()

    # File templates
    email_subject_template_file = 'customer/emails/commtype_%s_subject.txt'
    email_body_template_file = 'customer/emails/commtype_%s_body.txt'
    email_body_html_template_file = 'customer/emails/commtype_%s_body.html'
    sms_template_file = 'customer/sms/commtype_%s_body.txt'

    class Meta:
        abstract = True
        app_label = 'customer'
        verbose_name = _("Communication event type")
        verbose_name_plural = _("Communication event types")

    def get_messages(self, ctx=None):
        """
        Return a dict of templates with the context merged in

        We look first at the field templates but fail over to
        a set of file templates that follow a conventional path.
        """
        code = self.code.lower()

        # Build a dict of message name to Template instances
        templates = {'subject': 'email_subject_template',
                     'body': 'email_body_template',
                     'html': 'email_body_html_template',
                     'sms': 'sms_template'}
        for name, attr_name in templates.items():
            field = getattr(self, attr_name, None)
            if field is not None:
                # Template content is in a model field
                templates[name] = Template(field)
            else:
                # Model field is empty - look for a file template
                template_name = getattr(self, "%s_file" % attr_name) % code
                try:
                    templates[name] = get_template(template_name)
                except TemplateDoesNotExist:
                    templates[name] = None

        # Pass base URL for serving images within HTML emails
        if ctx is None:
            ctx = {}
        ctx['static_base_url'] = getattr(
            settings, 'OSCAR_STATIC_BASE_URL', None)

        messages = {}
        for name, template in templates.items():
            messages[name] = template.render(Context(ctx)) if template else ''

        # Ensure the email subject doesn't contain any newlines
        messages['subject'] = messages['subject'].replace("\n", "")
        messages['subject'] = messages['subject'].replace("\r", "")

        return messages

    def __str__(self):
        return self.name

    def is_order_related(self):
        return self.category == self.ORDER_RELATED

    def is_user_related(self):
        return self.category == self.USER_RELATED
class AbstractPartner(models.Model):
    """
    A fulfilment partner. An individual or company who can fulfil products.
    E.g. for physical goods, somebody with a warehouse and means of delivery.

    Creating one or more instances of the Partner model is a required step in
    setting up an Oscar deployment. Many Oscar deployments will only have one
    fulfilment partner.
    """
    code = AutoSlugField(_("Code"),
                         max_length=128,
                         unique=True,
                         db_index=True,
                         populate_from='name')
    name = models.CharField(pgettext_lazy("Partner's name", "Name"),
                            max_length=128,
                            blank=True,
                            db_index=True)

    #: A partner can have users assigned to it. This is used
    #: for access modelling in the permission-based dashboard
    users = models.ManyToManyField(AUTH_USER_MODEL,
                                   related_name="partners",
                                   blank=True,
                                   verbose_name=_("Users"))

    @property
    def display_name(self):
        return self.name or self.code

    @property
    def primary_address(self):
        """
        Returns a partners primary address. Usually that will be the
        headquarters or similar.

        This is a rudimentary implementation that raises an error if there's
        more than one address. If you actually want to support multiple
        addresses, you will likely need to extend PartnerAddress to have some
        field or flag to base your decision on.
        """
        addresses = self.addresses.all()
        if len(addresses) == 0:  # intentionally using len() to save queries
            return None
        elif len(addresses) == 1:
            return addresses[0]
        else:
            raise NotImplementedError(
                "Oscar's default implementation of primary_address only "
                "supports one PartnerAddress.  You need to override the "
                "primary_address to look up the right address")

    def get_address_for_stockrecord(self, stockrecord):
        """
        Stock might be coming from different warehouses. Overriding this
        function allows selecting the correct PartnerAddress for the record.
        That can be useful when determining tax.
        """
        return self.primary_address

    class Meta:
        abstract = True
        app_label = 'partner'
        ordering = ('name', 'code')
        permissions = (('dashboard_access', 'Can access dashboard'), )
        verbose_name = _('Fulfillment partner')
        verbose_name_plural = _('Fulfillment partners')

    def __str__(self):
        return self.display_name
Пример #22
0
class AbstractHook(models.Model):
    name = models.CharField(_('Name'), max_length=128)

    error_report_email = models.EmailField(_('Error Report Email'),
                                           blank=False,
                                           default='')

    slug = AutoSlugField(_('Slug'),
                         max_length=255,
                         unique=False,
                         populate_from='name')

    description = models.TextField(_('Description'), blank=True, null=True)

    product_class = models.ForeignKey(
        ProductClass,
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        verbose_name=_('Product type'),
        related_name="Product.product_class+",
        help_text=_("Choose what type of product this is"))

    user = models.ForeignKey(AUTH_USER_MODEL,
                             related_name="hookClasses",
                             blank=True,
                             verbose_name=_("User"),
                             null=True)

    date_created = models.DateTimeField(_("Date created"),
                                        null=True,
                                        default=datetime.now)

    # This field is used by Haystack to reindex search
    date_updated = models.DateTimeField(_("Date updated"),
                                        db_index=True,
                                        null=True,
                                        default=datetime.now)

    event_count = models.PositiveSmallIntegerField(_("HookEvent Count"),
                                                   default=0)

    class Meta:
        abstract = True
        app_label = 'hooks'
        ordering = ['-date_created']
        verbose_name = _("Hook")
        verbose_name_plural = _("Hook")

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        return super(AbstractHook, self).save(*args, **kwargs)

    def as_dict(self):
        return NotImplementedError

    def get_product_class(self):
        """
        :return: a hook's item class.
        """
        return self.product_class
Пример #23
0
class SluggedTestModel(models.Model):
    title = models.CharField(max_length=42)
    slug = AutoSlugField(populate_from='title')
Пример #24
0
class CustomSluggedTestModel(models.Model):
    title = models.CharField(max_length=42)
    slug = AutoSlugField(populate_from='title', separator="_", uppercase=True)
Пример #25
0
class AbstractPartner(models.Model):
    """
    A fulfillment partner. An individual or company who can fulfil products.
    E.g. for physical goods, somebody with a warehouse and means of delivery.

    Creating one or more instances of the Partner model is a required step in
    setting up an Oscar deployment. Many Oscar deployments will only have one
    fulfillment partner.

    履行合作伙伴。 可以履行产品的个人或公司。
    例如。 对于实物商品,有仓库和交货方式的人。

    创建合作伙伴模型的一个或多个实例是设置Oscar部署的必要步骤。 许多奥斯卡
    部署只会有一个履行合作伙伴。
    """
    code = AutoSlugField(_("Code"),
                         max_length=128,
                         unique=True,
                         populate_from='name')
    name = models.CharField(pgettext_lazy("Partner's name", "Name"),
                            max_length=128,
                            blank=True)

    #: A partner can have users assigned to it. This is used
    #: for access modelling in the permission-based dashboard
    # 合作伙伴可以为其分配用户。 这用于基于权限的仪表板中的访问建模
    users = models.ManyToManyField(AUTH_USER_MODEL,
                                   related_name="partners",
                                   blank=True,
                                   verbose_name=_("Users"))

    @property
    def display_name(self):
        return self.name or self.code

    @property
    def primary_address(self):
        """
        Returns a partners primary address. Usually that will be the
        headquarters or similar.

        This is a rudimentary implementation that raises an error if there's
        more than one address. If you actually want to support multiple
        addresses, you will likely need to extend PartnerAddress to have some
        field or flag to base your decision on.

        返回合作伙伴主要地址。 通常那将是总部或类似的。
        这是一个基本实现,如果有多个地址,则会引发错误。 如果您确实希望支持
        多个地址,则可能需要扩展PartnerAddress以使某些字段或标记作为您的决策依据。
        """
        addresses = self.addresses.all()
        if len(
                addresses
        ) == 0:  # intentionally using len() to save queries 故意使用len()来保存查询
            return None
        elif len(addresses) == 1:
            return addresses[0]
        else:
            raise NotImplementedError(
                "Oscar's default implementation of primary_address only "
                "supports one PartnerAddress.  You need to override the "
                "primary_address to look up the right address")

    def get_address_for_stockrecord(self, stockrecord):
        """
        Stock might be coming from different warehouses. Overriding this
        function allows selecting the correct PartnerAddress for the record.
        That can be useful when determining tax.
        库存可能来自不同的仓库。 覆盖此功能允许为记录选择正确的PartnerAddress。
        这在确定税时很有用。
        """
        return self.primary_address

    class Meta:
        abstract = True
        app_label = 'partner'
        ordering = ('name', 'code')
        permissions = (('dashboard_access', 'Can access dashboard'), )
        verbose_name = _('Fulfillment partner')
        verbose_name_plural = _('Fulfillment partners')

    def __str__(self):
        return self.display_name
Пример #26
0
class AbstractPartner(models.Model):
    """
    A fulfillment partner. An individual or company who can fulfil products.
    E.g. for physical goods, somebody with a warehouse and means of delivery.

    Creating one or more instances of the Partner model is a required step in
    setting up an Oscar deployment. Many Oscar deployments will only have one
    fulfillment partner.
    """
    code = AutoSlugField(_("Code"),
                         max_length=128,
                         unique=True,
                         populate_from='name')
    name = models.CharField(pgettext_lazy(u"Partner's name", u"Name"),
                            max_length=128,
                            blank=True)

    #: A partner can have users assigned to it. This is used
    #: for access modelling in the permission-based dashboard
    users = models.ManyToManyField(AUTH_USER_MODEL,
                                   related_name="partners",
                                   blank=True,
                                   verbose_name=_("Users"))

    @property
    def display_name(self):
        return self.name or self.code

    @property
    def primary_address(self):
        """
        Returns a partners primary address. Usually that will be the
        headquarters or similar.

        This is a rudimentary implementation that raises an error if there's
        more than one address. If you actually want to support multiple
        addresses, you will likely need to extend PartnerAddress to have some
        field or flag to base your decision on.
        """
        addresses = self.addresses.all()
        if len(addresses) == 0:  # intentionally using len() to save queries
            return None
        elif len(addresses) == 1:
            return addresses[0]
        else:
            raise NotImplementedError(
                "Oscar's default implementation of primary_address only "
                "supports one PartnerAddress.  You need to override the "
                "primary_address to look up the right address")

    def get_address_for_stockrecord(self, stockrecord):
        """
        Stock might be coming from different warehouses. Overriding this
        function allows selecting the correct PartnerAddress for the record.
        That can be useful when determining tax.
        """
        return self.primary_address

    class Meta:
        abstract = True
        app_label = 'partner'
        ordering = ('name', 'code')
        permissions = (('dashboard_access', 'Can access dashboard'), )
        verbose_name = _('Fulfillment partner')
        verbose_name_plural = _('Fulfillment partners')

    def __str__(self):
        return self.display_name

    def get_absolute_url(self):
        """
        Our URL scheme means we have to look up the category's ancestors. As
        that is a bit more expensive, we cache the generated URL. That is
        safe even for a stale cache, as the default implementation of
        ProductCategoryView does the lookup via primary key anyway. But if
        you change that logic, you'll have to reconsider the caching
        approach.
        """
        current_locale = get_language()
        cache_key = 'CATEGORY_URL_%s_%s' % (current_locale, self.pk)
        url = cache.get(cache_key)
        url = None
        if not url:
            url = reverse('partner:detail',
                          kwargs={
                              'product_slug': self.code,
                              'pk': self.pk
                          })
            cache.set(cache_key, url)
        return url
Пример #27
0
class AbstractCommunicationEventType(models.Model):
    """
    A 'type' of communication.  Like a order confirmation email.
    """

    # Code used for looking up this event programmatically.
    # eg. PASSWORD_RESET
    code = AutoSlugField(_('Code'),
                         max_length=128,
                         unique=True,
                         populate_from='name')

    #: Name is the friendly description of an event for use in the admin
    name = models.CharField(
        _('Name'),
        max_length=255,
        help_text=_("This is just used for organisational purposes"))

    # We allow communication types to be categorised
    ORDER_RELATED = _('Order related')
    USER_RELATED = _('User related')
    category = models.CharField(_('Category'),
                                max_length=255,
                                default=ORDER_RELATED)

    # Template content for emails
    email_subject_template = models.CharField(_('Email Subject Template'),
                                              max_length=255,
                                              blank=True,
                                              null=True)
    email_body_template = models.TextField(_('Email Body Template'),
                                           blank=True,
                                           null=True)
    email_body_html_template = models.TextField(_('Email Body HTML Template'),
                                                blank=True,
                                                null=True,
                                                help_text=_("HTML template"))

    # Template content for SMS messages
    sms_template = models.CharField(_('SMS Template'),
                                    max_length=170,
                                    blank=True,
                                    null=True,
                                    help_text=_("SMS template"))

    date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)
    date_updated = models.DateTimeField(_("Date Updated"), auto_now=True)

    objects = CommunicationTypeManager()

    # File templates
    email_subject_template_file = 'customer/emails/commtype_%s_subject.txt'
    email_body_template_file = 'customer/emails/commtype_%s_body.txt'
    email_body_html_template_file = 'customer/emails/commtype_%s_body.html'
    sms_template_file = 'customer/sms/commtype_%s_body.txt'

    class Meta:
        abstract = True
        verbose_name = _("Communication event type")
        verbose_name_plural = _("Communication event types")

    def get_messages(self, ctx=None):
        """
        Return a dict of templates with the context merged in

        We look first at the field templates but fail over to
        a set of file templates that follow a conventional path.
        """
        code = self.code.lower()

        # Build a dict of message name to Template instances
        templates = {
            'subject': 'email_subject_template',
            'body': 'email_body_template',
            'html': 'email_body_html_template',
            'sms': 'sms_template'
        }
        for name, attr_name in templates.items():
            field = getattr(self, attr_name, None)
            if field is not None:
                # Template content is in a model field
                templates[name] = Template(field)
            else:
                # Model field is empty - look for a file template
                template_name = getattr(self, "%s_file" % attr_name) % code
                try:
                    templates[name] = get_template(template_name)
                except TemplateDoesNotExist:
                    templates[name] = None

        # Pass base URL for serving images within HTML emails
        if ctx is None:
            ctx = {}
        ctx['static_base_url'] = getattr(settings, 'OSCAR_STATIC_BASE_URL',
                                         None)

        messages = {}
        for name, template in templates.items():
            messages[name] = template.render(Context(ctx)) if template else ''

        # Ensure the email subject doesn't contain any newlines
        messages['subject'] = messages['subject'].replace("\n", "")
        messages['subject'] = messages['subject'].replace("\r", "")

        return messages

    def __unicode__(self):
        return self.name

    def is_order_related(self):
        return self.category == self.ORDER_RELATED

    def is_user_related(self):
        return self.category == self.USER_RELATED
Пример #28
0
class AbstractCommunicationEventType(models.Model):
    """
    A 'type' of communication.  Like an order confirmation email.
    沟通的“类型”。 像订单确认电子邮件。
    """

    #: Code used for looking up this event programmatically.
    # e.g. PASSWORD_RESET. AutoSlugField uppercases the code for us because
    # it's a useful convention that's been enforced in previous Oscar versions
    # 用于以编程方式查找此事件的代码。
    # 例如 重设密码。 AutoSlugField为我们提供了代码,因为它是一个在以前
    # 的Oscar版本中强制执行的有用约定

    code = AutoSlugField(
        _('Code'),
        max_length=128,
        unique=True,
        populate_from='name',
        separator="_",
        uppercase=True,
        editable=True,
        validators=[
            RegexValidator(
                regex=r'^[a-zA-Z_][0-9a-zA-Z_]*$',
                # 代码只能包含字母a-z,A-Z,数字和下划线,并且不能以数字开头。
                message=_(
                    "Code can only contain the letters a-z, A-Z, digits, "
                    "and underscores, and can't start with a digit."))
        ],
        # 用于以编程方式查找此事件的代码
        help_text=_("Code used for looking up this event programmatically"))

    #: Name is the friendly description of an event for use in the admin
    #: 名称是在管理员中使用的事件的友好描述
    name = models.CharField(
        _('Name'),
        max_length=255,
        # 这仅用于组织目的
        help_text=_("This is just used for organisational purposes"))

    # We allow communication types to be categorised
    # For backwards-compatibility, the choice values are quite verbose
    # 我们允许对通信类型进行分类为了向后兼容,选择值非常详细
    ORDER_RELATED = 'Order related'
    USER_RELATED = 'User related'
    CATEGORY_CHOICES = ((ORDER_RELATED, _('Order related')),
                        (USER_RELATED, _('User related')))

    category = models.CharField(_('Category'),
                                max_length=255,
                                default=ORDER_RELATED,
                                choices=CATEGORY_CHOICES)

    # Template content for emails
    # NOTE: There's an intentional distinction between None and ''. None
    # instructs Oscar to look for a file-based template, '' is just an empty
    # template.
    # 电子邮件的模板内容
    # 注意:None和''之间存在故意的区别。 没有指示Oscar寻找基于文件的
    # 模板,''只是一个空模板。
    email_subject_template = models.CharField(_('Email Subject Template'),
                                              max_length=255,
                                              blank=True,
                                              null=True)
    email_body_template = models.TextField(_('Email Body Template'),
                                           blank=True,
                                           null=True)
    email_body_html_template = models.TextField(_('Email Body HTML Template'),
                                                blank=True,
                                                null=True,
                                                help_text=_("HTML template"))

    # Template content for SMS messages
    # SMS消息的模板内容
    sms_template = models.CharField(_('SMS Template'),
                                    max_length=170,
                                    blank=True,
                                    null=True,
                                    help_text=_("SMS template"))

    date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)
    date_updated = models.DateTimeField(_("Date Updated"), auto_now=True)

    objects = CommunicationTypeManager()

    # File templates
    # 文件模板
    email_subject_template_file = 'customer/emails/commtype_%s_subject.txt'
    email_body_template_file = 'customer/emails/commtype_%s_body.txt'
    email_body_html_template_file = 'customer/emails/commtype_%s_body.html'
    sms_template_file = 'customer/sms/commtype_%s_body.txt'

    class Meta:
        abstract = True
        app_label = 'customer'
        verbose_name = _("Communication event type")
        verbose_name_plural = _("Communication event types")

    def get_messages(self, ctx=None):
        """
        Return a dict of templates with the context merged in

        We look first at the field templates but fail over to
        a set of file templates that follow a conventional path.

        返回合并上下文的模板的dict

        我们首先查看字段模板,但故障转移到遵循传统路径的一组文件模板。
        """
        code = self.code.lower()

        # Build a dict of message name to Template instances
        # 为Template实例构建消息名称的dict
        templates = {
            'subject': 'email_subject_template',
            'body': 'email_body_template',
            'html': 'email_body_html_template',
            'sms': 'sms_template'
        }
        for name, attr_name in templates.items():
            field = getattr(self, attr_name, None)
            if field is not None:
                # Template content is in a model field
                # 模板内容位于模型字段中
                templates[name] = engines['django'].from_string(field)
            else:
                # Model field is empty - look for a file template
                # 模型字段为空 - 查找文件模板
                template_name = getattr(self, "%s_file" % attr_name) % code
                try:
                    templates[name] = get_template(template_name)
                except TemplateDoesNotExist:
                    templates[name] = None

        # Pass base URL for serving images within HTML emails
        # 传递用于在HTML电子邮件中提供图像的基本URL
        if ctx is None:
            ctx = {}
        ctx['static_base_url'] = getattr(settings, 'OSCAR_STATIC_BASE_URL',
                                         None)

        messages = {}
        for name, template in templates.items():
            messages[name] = template.render(ctx) if template else ''

        # Ensure the email subject doesn't contain any newlines
        # 确保电子邮件主题不包含任何换行符
        messages['subject'] = messages['subject'].replace("\n", "")
        messages['subject'] = messages['subject'].replace("\r", "")

        return messages

    def __str__(self):
        return self.name

    def is_order_related(self):
        return self.category == self.ORDER_RELATED

    def is_user_related(self):
        return self.category == self.USER_RELATED
Пример #29
0
class News(models.Model):
    """
    News
    """
    author = models.ForeignKey(AUTH_USER_MODEL,
                               verbose_name=_('author'),
                               null=True,
                               blank=True,
                               related_name='news_author')
    category = models.ForeignKey("NewsCategory",
                                 verbose_name=_('category'),
                                 related_name="news_category",
                                 null=True,
                                 blank=True,
                                 default=None)

    meta_description = models.TextField(
        verbose_name=_('news meta description'), blank=True, default='')
    meta_keywords = models.TextField(verbose_name=_('news meta keywords'),
                                     blank=True,
                                     default='')
    meta_title = models.CharField(
        verbose_name=_('news meta title'),
        help_text=_('used in title tag and social sharing'),
        max_length=255,
        blank=True,
        default='')

    title = models.CharField(_('Title'), max_length=255)
    slug = AutoSlugField(_('Slug'),
                         max_length=128,
                         unique=True,
                         editable=True,
                         populate_from='title',
                         help_text=_('A slug is a short name which uniquely'
                                     ' identifies the news item'))
    description = HTMLField(_('Description'),
                            blank=True,
                            configuration='CKEDITOR_SETTINGS_NEWS')
    content = PlaceholderField('news_content', related_name='news_content')

    publish = models.BooleanField(_('Published'), default=False)

    date_created = models.DateTimeField(_('created'), auto_now_add=True)
    date_modified = models.DateTimeField(_('last modified'), auto_now=True)
    date_published = models.DateTimeField(_('published since'),
                                          default=timezone.now)
    date_published_end = models.DateTimeField(_('published until'),
                                              null=True,
                                              blank=True)

    enable_comments = models.BooleanField(
        verbose_name=_('enable comments on post'), default=ENABLE_COMMENTS)

    images = models.ManyToManyField(
        'filer.Image',
        through='NewsImages',
        verbose_name=_("News images"),
    )

    # Oscar links
    linked_products = models.ManyToManyField(
        'catalogue.Product',
        blank=True,
        verbose_name=_("Linked products"),
        help_text=_(
            "These are products that can be shown with news post "
            "or news post can be shown on the specific product's page."))
    linked_categories = models.ManyToManyField(
        'catalogue.Category',
        blank=True,
        verbose_name=_("Linked product's categories"),
        help_text=_("Show news for that categories "
                    "or display news on the category page"))
    linked_classes = models.ManyToManyField(
        'catalogue.ProductClass',
        blank=True,
        verbose_name=_("Linked product's classes"),
        help_text=_("Show news for that classes "
                    "or display news on the specific class product's pages"))

    sites = models.ManyToManyField(
        'sites.Site',
        verbose_name=_('Site(s)'),
        blank=True,
        help_text=_('Select sites in which to show the post. '
                    'If none is set it will be '
                    'visible in all the configured sites.'))

    tags = TaggableManager(blank=True, related_name='news_tags')

    objects = NewsManager()
    published = PublishedNewsManager()

    class Meta:
        app_label = 'oscar_news'
        verbose_name = _('News')
        verbose_name_plural = _('News')
        ordering = ('-date_published', )

    def __unicode__(self):
        return self.title

    @property
    def is_published(self):
        """
        Checks whether the news entry is *really* published by checking publishing dates too
        """
        return (self.publish and
                (self.date_published and self.date_published <= timezone.now())
                and (self.date_published_end is None
                     or self.date_published_end > timezone.now()))

    def _set_default_author(self, current_user):
        if not self.author_id:
            if AUTHOR_DEFAULT is True:
                user = current_user
            else:
                user = get_user_model().objects.get(username=AUTHOR_DEFAULT)
            self.author = user

    def get_absolute_url(self):
        """
        method below inherited and slightly customized
        """
        cache_key = 'NEWS_ENTRY_URL_%s' % self.pk
        url = cache.get(cache_key)
        if not url:
            # temporarily use link to news detail
            url = reverse('oscar_news:entry-detail',
                          kwargs={'slug': self.slug})
            cache.set(cache_key, url)
        return url

    def get_tags(self, queryset=None):
        """
        :return: the list of object's tags annotated with counters.
        Tags are limited by published news.
        """
        queryset = queryset or News.published.get_queryset()
        return get_tag_cloud(self.__class__, queryset, set(self.tags.all()))

    def get_all_tags(self):
        """
        :return: List of all object's tags including unpublished
        """
        return self.get_tags(News.objects.all())

    def _get_next_or_previous_published(self, is_next):
        if not self.pk:
            raise ValueError(
                "get_next/get_previous cannot be used on unsaved objects.")
        op = 'gt' if is_next else 'lt'
        order = '' if is_next else '-'
        field = 'date_published'
        param = force_text(getattr(self, field))
        q = Q(**{'%s__%s' % (field, op): param})
        q = q | Q(**{field: param, 'pk__%s' % op: self.pk})
        qs = self.__class__.published.using(self._state.db).filter(q).order_by(
            '%s%s' % (order, field), '%spk' % order)
        try:
            return qs[0]
        except IndexError:
            raise self.DoesNotExist("%s matching query does not exist." %
                                    self.__class__._meta.object_name)

    def get_next_published(self):
        return self._get_next_or_previous_published(is_next=True)

    def get_previous_published(self):
        return self._get_next_or_previous_published(is_next=False)
Пример #30
0
class AbstractPerson(models.Model):
    
    # As it said in wiki there are too many number formats
    # but no longer than 15 chars (hello, Canada :)
    # see https://en.wikipedia.org/wiki/VAT_identification_number
    # for explanation
    # django-internationalflavor seems to be the right solution
    # for VAT and BIC fields validation

    (ACTIVE, LIQUIDATING, LIQUIDATED) = range(1, 4)
    PERSON_STATUSES = {
            ACTIVE: _("Active"),
            LIQUIDATING: _("Liquidating process started"),
            LIQUIDATED: _("Liquidated"),
        }
    vatin = VATNumberField(countries=countries, 
                           verbose_name=_("VAT number"), 
                           help_text=_("VAT or tax payer ID"))
    reason_code = models.CharField(
        _("Code for Reason of registration, e.g. KPP in Russia"), 
        null=True, 
        blank=True, max_length=9)
    name = models.CharField(_('Name'), max_length=200)
    full_name = models.CharField(_('Full Name'), max_length=254, blank=True,
                              null=True, )
    
    slug = AutoSlugField(_("Code"), max_length=200, unique=True,
                         populate_from='name')
    logo = models.ImageField(_('Logo'), upload_to='juristic/logos', blank=True,
                              null=True, max_length=255)
    image = models.ImageField(_('Image'), upload_to='juristic/images', blank=True,
                              null=True, max_length=255)
    
    partner = models.ForeignKey(
        'partner.Partner',
        verbose_name=_("Fulfillment partner"),
        related_name="juristic_persons", blank=True, null=True,)
    
    users = models.ManyToManyField(
        AUTH_USER_MODEL, related_name="related_persons",
        blank=True, verbose_name=_("Users"))
    
    
    # Contact details
    manager_name = models.CharField(
        _('Manager name'), max_length=200, blank=True, null=True)
    chief_name = models.CharField(
        _('GM or Director name'), max_length=200, blank=True, null=True)
    chief_title = models.CharField(
        _('Title for GM or Director'), max_length=200, blank=True, null=True)
    accountant_name = models.CharField(
        _('Main Accountant name'), max_length=200, blank=True, null=True)
    
    phone = models.CharField(_('Phone'), max_length=64, blank=True, null=True)
    email = models.EmailField(_('Email'), max_length=254, blank=True, null=True)
    website = models.URLField(_('Web-site'), blank=True, null=True)
    
    reference = models.CharField(
        _("Reference"),
        max_length=32,
        unique=True,
        null=True,
        blank=True,
        help_text=_("A reference number that uniquely identifies this person"))

    description = models.TextField(
        _("Description"),
        max_length=2000,
        blank=True, null=True)

    group = models.ForeignKey(
        'oscar_ficta.PersonGroup',
        related_name='persons',
        verbose_name=_("Group"),
        null=True,
        blank=True)
    
    is_active = models.BooleanField(_("Is active"), default=True)
    
    status_choices = [(k, v) for k, v in PERSON_STATUSES.items()]
    status = models.PositiveIntegerField(
        _("State status"),
        choices=status_choices,
        default=ACTIVE)
    
    # Date information
    date_created = models.DateTimeField(
        _("Date created"),
        auto_now_add=True)
    date_updated = models.DateTimeField(
        _("Date updated"),
        auto_now=True,
        db_index=True)
    date_registration = models.DateTimeField(
        _("Date of registration"),
        blank=True, null=True)
    date_liquidation = models.DateTimeField(
        _("Date of liquidation"),
        blank=True, null=True)
    
    # due to https://docs.djangoproject.com/en/1.8/topics/db/managers/#default-managers
    # default manager should be placed first
    objects = models.Manager()
    browsable = BrowsablePersonManager()

    def __str__(self):
        return u"%s" % self.name
    
    def __unicode__(self):
        return self.name
    
    def current_status(self):
        return self.PERSON_STATUSES[self.status]
    
    class Meta:
        abstract = True
        verbose_name = _("Juristic person")
        verbose_name_plural = _("Juristic persons")
        unique_together = ("vatin", "reason_code")