コード例 #1
0
ファイル: apps.py プロジェクト: thelabnyc/django-oscar-wfrs
class CreditApplicationApplicant(models.Model):
    first_name = models.CharField(_("First Name"), max_length=15)
    last_name = models.CharField(_("Last Name"), max_length=20)
    middle_initial = NullCharField(_("Middle Initial"), max_length=1)
    date_of_birth = DateOfBirthField(_("Date of Birth"))
    ssn = USSocialSecurityNumberField(_("Social Security Number"))
    annual_income = models.IntegerField(
        _("Annual Income"), validators=[MinValueValidator(0), MaxValueValidator(999999)]
    )
    email_address = models.EmailField(
        _("Email"),
        max_length=50,
        null=True,
        blank=True,
        validators=[MinLengthValidator(7)],
    )
    home_phone = PhoneNumberField(_("Home Phone"))
    mobile_phone = PhoneNumberField(_("Mobile Phone"), null=True, blank=True)
    work_phone = PhoneNumberField(_("Work Phone"), null=True, blank=True)
    employer_name = NullCharField(_("Employer Name"), max_length=30)
    housing_status = NullCharField(
        _("Housing Status"),
        max_length=_max_len(HOUSING_STATUSES),
        choices=HOUSING_STATUSES,
    )
    address = models.ForeignKey(
        "wellsfargo.CreditApplicationAddress",
        verbose_name=_("Address"),
        related_name="+",
        on_delete=models.CASCADE,
    )
コード例 #2
0
    def test_raises_exception_for_invalid_null_blank_combo(self):
        with self.assertRaises(ImproperlyConfigured):
            NullCharField(null=True, blank=False)

        with self.assertRaises(ImproperlyConfigured):
            NullCharField(null=False, blank=True)

        with self.assertRaises(ImproperlyConfigured):
            NullCharField(null=False, blank=False)
コード例 #3
0
class AccountInquiryResult(AccountNumberMixin, models.Model):
    credit_app_source = models.ForeignKey(
        "wellsfargo.CreditApplication",
        verbose_name=_("Credit Application Source"),
        related_name="inquiries",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
    )

    prequal_response_source = models.ForeignKey(
        "wellsfargo.PreQualificationResponse",
        verbose_name=_("Pre-Qualification Source"),
        related_name="inquiries",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    main_applicant_full_name = NullCharField(_("Main Applicant Name"),
                                             max_length=50)
    joint_applicant_full_name = NullCharField(_("Joint Applicant Name"),
                                              max_length=50)

    main_applicant_address = models.ForeignKey(
        "wellsfargo.CreditApplicationAddress",
        verbose_name=_("Main Applicant Address"),
        related_name="+",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    joint_applicant_address = models.ForeignKey(
        "wellsfargo.CreditApplicationAddress",
        verbose_name=_("Joint Applicant Address"),
        related_name="+",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    credit_limit = models.DecimalField(_("Account Credit Limit"),
                                       decimal_places=2,
                                       max_digits=12)
    available_credit = models.DecimalField(_("Current Available Credit"),
                                           decimal_places=2,
                                           max_digits=12)

    created_datetime = models.DateTimeField(auto_now_add=True)
    modified_datetime = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ("-created_datetime", "-id")
        verbose_name = _("Account Inquiry Result")
        verbose_name_plural = _("Account Inquiry Results")
コード例 #4
0
ファイル: apps.py プロジェクト: thelabnyc/django-oscar-wfrs
class CreditApplicationAddress(models.Model):
    address_line_1 = models.CharField(_("Address Line 1"), max_length=26)
    address_line_2 = NullCharField(_("Address Line 2"), max_length=26)
    city = models.CharField(_("City"), max_length=18)
    state_code = USStateField(_("State"))
    postal_code = USZipCodeField(
        _("Postcode"), validators=[MinLengthValidator(5), MaxLengthValidator(5)]
    )
コード例 #5
0
class AbstractProduct(models.Model):
    """
    The base product object

    There's three kinds of products; they're distinguished by the structure
    field.

    - A stand alone product. Regular product that lives by itself.
    - A child product. All child products have a parent product. They're a
      specific version of the parent.
    - A parent product. It essentially represents a set of products.

    An example could be a yoga course, which is a parent product. The different
    times/locations of the courses would be associated with the child products.
    """
    STANDALONE, PARENT, CHILD = 'standalone', 'parent', 'child'
    STRUCTURE_CHOICES = ((STANDALONE, _('Stand-alone product')),
                         (PARENT, _('Parent product')), (CHILD,
                                                         _('Child product')))
    structure = models.CharField(_("Product structure"),
                                 max_length=10,
                                 choices=STRUCTURE_CHOICES,
                                 default=STANDALONE)

    is_public = models.BooleanField(
        _('Is public'),
        default=True,
        db_index=True,
        help_text=_(
            "Show this product in search results and catalogue listings."))

    upc = NullCharField(
        _("UPC"),
        max_length=64,
        blank=True,
        null=True,
        unique=True,
        help_text=_("Universal Product Code (UPC) is an identifier for "
                    "a product which is not specific to a particular "
                    " supplier. Eg an ISBN for a book."))

    parent = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        related_name='children',
        verbose_name=_("Parent product"),
        help_text=_("Only choose a parent product if you're creating a child "
                    "product.  For example if this is a size "
                    "4 of a particular t-shirt.  Leave blank if this is a "
                    "stand-alone product (i.e. there is only one version of"
                    " this product)."))

    # Title is mandatory for canonical products but optional for child products
    title = models.CharField(pgettext_lazy('Product title', 'Title'),
                             max_length=255,
                             blank=True)
    slug = models.SlugField(_('Slug'), max_length=255, unique=False)
    description = models.TextField(_('Description'), blank=True)

    #: "Kind" of product, e.g. T-Shirt, Book, etc.
    #: None for child products, they inherit their parent's product class
    product_class = models.ForeignKey(
        'catalogue.ProductClass',
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        verbose_name=_('Product type'),
        related_name="products",
        help_text=_("Choose what type of product this is"))
    attributes = models.ManyToManyField(
        'catalogue.ProductAttribute',
        through='ProductAttributeValue',
        verbose_name=_("Attributes"),
        help_text=_("A product attribute is something that this product may "
                    "have, such as a size, as specified by its class"))
    #: It's possible to have options product class-wide, and per product.
    product_options = models.ManyToManyField(
        'catalogue.Option',
        blank=True,
        verbose_name=_("Product options"),
        help_text=_("Options are values that can be associated with a item "
                    "when it is added to a customer's basket.  This could be "
                    "something like a personalised message to be printed on "
                    "a T-shirt."))

    recommended_products = models.ManyToManyField(
        'catalogue.Product',
        through='ProductRecommendation',
        blank=True,
        verbose_name=_("Recommended products"),
        help_text=_("These are products that are recommended to accompany the "
                    "main product."))

    # Denormalised product rating - used by reviews app.
    # Product has no ratings if rating is None
    rating = models.FloatField(_('Rating'), null=True, editable=False)

    date_created = models.DateTimeField(_("Date created"),
                                        auto_now_add=True,
                                        db_index=True)

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

    categories = models.ManyToManyField('catalogue.Category',
                                        through='ProductCategory',
                                        verbose_name=_("Categories"))

    #: Determines if a product may be used in an offer. It is illegal to
    #: discount some types of product (e.g. ebooks) and this field helps
    #: merchants from avoiding discounting such products
    #: Note that this flag is ignored for child products; they inherit from
    #: the parent product.
    is_discountable = models.BooleanField(
        _("Is discountable?"),
        default=True,
        help_text=_(
            "This flag indicates if this product can be used in an offer "
            "or not"))

    objects = ProductQuerySet.as_manager()
    # browsable property is deprecated and will be removed in Oscar 2.1
    # Use Product.objects.browsable() instead.
    browsable = BrowsableProductManager()

    class Meta:
        abstract = True
        app_label = 'catalogue'
        ordering = ['-date_created']
        verbose_name = _('Product')
        verbose_name_plural = _('Products')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.attr = ProductAttributesContainer(product=self)

    def __str__(self):
        if self.title:
            return self.title
        if self.attribute_summary:
            return "%s (%s)" % (self.get_title(), self.attribute_summary)
        else:
            return self.get_title()

    def get_absolute_url(self):
        """
        Return a product's absolute URL
        """
        return reverse('catalogue:detail',
                       kwargs={
                           'product_slug': self.slug,
                           'pk': self.id
                       })

    def clean(self):
        """
        Validate a product. Those are the rules:

        +---------------+-------------+--------------+--------------+
        |               | stand alone | parent       | child        |
        +---------------+-------------+--------------+--------------+
        | title         | required    | required     | optional     |
        +---------------+-------------+--------------+--------------+
        | product class | required    | required     | must be None |
        +---------------+-------------+--------------+--------------+
        | parent        | forbidden   | forbidden    | required     |
        +---------------+-------------+--------------+--------------+
        | stockrecords  | 0 or more   | forbidden    | 0 or more    |
        +---------------+-------------+--------------+--------------+
        | categories    | 1 or more   | 1 or more    | forbidden    |
        +---------------+-------------+--------------+--------------+
        | attributes    | optional    | optional     | optional     |
        +---------------+-------------+--------------+--------------+
        | rec. products | optional    | optional     | unsupported  |
        +---------------+-------------+--------------+--------------+
        | options       | optional    | optional     | forbidden    |
        +---------------+-------------+--------------+--------------+

        Because the validation logic is quite complex, validation is delegated
        to the sub method appropriate for the product's structure.
        """
        getattr(self, '_clean_%s' % self.structure)()
        if not self.is_parent:
            self.attr.validate_attributes()

    def _clean_standalone(self):
        """
        Validates a stand-alone product
        """
        if not self.title:
            raise ValidationError(_("Your product must have a title."))
        if not self.product_class:
            raise ValidationError(_("Your product must have a product class."))
        if self.parent_id:
            raise ValidationError(_("Only child products can have a parent."))

    def _clean_child(self):
        """
        Validates a child product
        """
        if not self.parent_id:
            raise ValidationError(_("A child product needs a parent."))
        if self.parent_id and not self.parent.is_parent:
            raise ValidationError(
                _("You can only assign child products to parent products."))
        if self.product_class:
            raise ValidationError(
                _("A child product can't have a product class."))
        if self.pk and self.categories.exists():
            raise ValidationError(
                _("A child product can't have a category assigned."))
        # Note that we only forbid options on product level
        if self.pk and self.product_options.exists():
            raise ValidationError(_("A child product can't have options."))

    def _clean_parent(self):
        """
        Validates a parent product.
        """
        self._clean_standalone()
        if self.has_stockrecords:
            raise ValidationError(
                _("A parent product can't have stockrecords."))

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.get_title())
        super().save(*args, **kwargs)
        self.attr.save()

    # Properties

    @property
    def is_standalone(self):
        return self.structure == self.STANDALONE

    @property
    def is_parent(self):
        return self.structure == self.PARENT

    @property
    def is_child(self):
        return self.structure == self.CHILD

    def can_be_parent(self, give_reason=False):
        """
        Helps decide if a the product can be turned into a parent product.
        """
        reason = None
        if self.is_child:
            reason = _('The specified parent product is a child product.')
        if self.has_stockrecords:
            reason = _("One can't add a child product to a product with stock"
                       " records.")
        is_valid = reason is None
        if give_reason:
            return is_valid, reason
        else:
            return is_valid

    @property
    def options(self):
        """
        Returns a set of all valid options for this product.
        It's possible to have options product class-wide, and per product.
        """
        pclass_options = self.get_product_class().options.all()
        return pclass_options | self.product_options.all()

    @cached_property
    def has_options(self):
        # Extracting annotated value with number of product class options
        # from product list queryset.
        has_product_class_options = getattr(self, 'has_product_class_options',
                                            None)
        has_product_options = getattr(self, 'has_product_options', None)
        if has_product_class_options is not None and has_product_options is not None:
            return has_product_class_options or has_product_options
        return self.options.exists()

    @property
    def is_shipping_required(self):
        return self.get_product_class().requires_shipping

    @property
    def has_stockrecords(self):
        """
        Test if this product has any stockrecords
        """
        return self.stockrecords.exists()

    @property
    def num_stockrecords(self):
        return self.stockrecords.count()

    @property
    def attribute_summary(self):
        """
        Return a string of all of a product's attributes
        """
        attributes = self.attribute_values.all()
        pairs = [attribute.summary() for attribute in attributes]
        return ", ".join(pairs)

    def get_title(self):
        """
        Return a product's title or it's parent's title if it has no title
        """
        title = self.title
        if not title and self.parent_id:
            title = self.parent.title
        return title

    get_title.short_description = pgettext_lazy("Product title", "Title")

    def get_product_class(self):
        """
        Return a product's item class. Child products inherit their parent's.
        """
        if self.is_child:
            return self.parent.product_class
        else:
            return self.product_class

    get_product_class.short_description = _("Product class")

    def get_is_discountable(self):
        """
        At the moment, :py:attr:`.is_discountable` can't be set individually for child
        products; they inherit it from their parent.
        """
        if self.is_child:
            return self.parent.is_discountable
        else:
            return self.is_discountable

    def get_categories(self):
        """
        Return a product's categories or parent's if there is a parent product.
        """
        if self.is_child:
            return self.parent.categories
        else:
            return self.categories

    get_categories.short_description = _("Categories")

    # Images

    def get_missing_image(self):
        """
        Returns a missing image object.
        """
        # This class should have a 'name' property so it mimics the Django file
        # field.
        return MissingProductImage()

    def get_all_images(self):
        if self.is_child and not self.images.exists(
        ) and self.parent_id is not None:
            return self.parent.images.all()
        return self.images.all()

    def primary_image(self):
        """
        Returns the primary image for a product. Usually used when one can
        only display one product image, e.g. in a list of products.
        """
        images = self.get_all_images()
        ordering = self.images.model.Meta.ordering
        if not ordering or ordering[0] != 'display_order':
            # Only apply order_by() if a custom model doesn't use default
            # ordering. Applying order_by() busts the prefetch cache of
            # the ProductManager
            images = images.order_by('display_order')
        try:
            return images[0]
        except IndexError:
            # We return a dict with fields that mirror the key properties of
            # the ProductImage class so this missing image can be used
            # interchangeably in templates.  Strategy pattern ftw!
            missing_image = self.get_missing_image()
            return {
                'original': missing_image.name,
                'caption': '',
                'is_missing': True
            }

    # Updating methods

    def update_rating(self):
        """
        Recalculate rating field
        """
        self.rating = self.calculate_rating()
        self.save()

    update_rating.alters_data = True

    def calculate_rating(self):
        """
        Calculate rating value
        """
        result = self.reviews.filter(
            status=self.reviews.model.APPROVED).aggregate(sum=Sum('score'),
                                                          count=Count('id'))
        reviews_sum = result['sum'] or 0
        reviews_count = result['count'] or 0
        rating = None
        if reviews_count > 0:
            rating = float(reviews_sum) / reviews_count
        return rating

    def has_review_by(self, user):
        if user.is_anonymous:
            return False
        return self.reviews.filter(user=user).exists()

    def is_review_permitted(self, user):
        """
        Determines whether a user may add a review on this product.

        Default implementation respects OSCAR_ALLOW_ANON_REVIEWS and only
        allows leaving one review per user and product.

        Override this if you want to alter the default behaviour; e.g. enforce
        that a user purchased the product to be allowed to leave a review.
        """
        if user.is_authenticated or settings.OSCAR_ALLOW_ANON_REVIEWS:
            return not self.has_review_by(user)
        else:
            return False

    @cached_property
    def num_approved_reviews(self):
        return self.reviews.approved().count()

    @property
    def sorted_recommended_products(self):
        """Keeping order by recommendation ranking."""
        return [
            r.recommendation
            for r in self.primary_recommendations.select_related(
                'recommendation').all()
        ]
コード例 #6
0
class AbstractProduct(models.Model):
    """
    ベースとなる製品モデルオブジェクト。
    製品には3種類ある。これらは構造フィールドによって区別される。
    - stand alone 製品        : 単独で存在するレギュラー製品。
    - child 製品(子製品)    :すべての子製品には必ず親製品が存在する。 それらは親の特定のバージョンになる。
    - parent 製品(親製品)    :基本的に、製品のセットを表す。
    【例】ヨガコース
        ・コースそのもの:親製品
        ・コースの時間/場所:子製品
    """
    STANDALONE, PARENT, CHILD = 'standalone', 'parent', 'child'
    STRUCTURE_CHOICES = ((STANDALONE, _('Stand-alone product')),
                         (PARENT, _('Parent product')), (CHILD,
                                                         _('Child product')))
    # カラム定義
    structure = models.CharField(_("製品構成"),
                                 max_length=10,
                                 choices=STRUCTURE_CHOICES,
                                 default=STANDALONE)

    is_public = models.BooleanField(
        _('Is public'),
        default=True,
        help_text=_(
            "Show this product in search results and catalogue listings."))

    upc = NullCharField(
        _("UPC"),
        max_length=64,
        blank=True,
        null=True,
        unique=True,
        help_text=_("Universal Product Code (UPC) is an identifier for "
                    "a product which is not specific to a particular "
                    " supplier. Eg an ISBN for a book."))

    parent = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        related_name='children',
        verbose_name=_("親製品"),
        help_text=_("Only choose a parent product if you're creating a child "
                    "product.  For example if this is a size "
                    "4 of a particular t-shirt.  Leave blank if this is a "
                    "stand-alone product (i.e. there is only one version of"
                    " this product)."))

    # # タイトルは標準製品には必須になるが、子製品に対してはオプションとなる
    title = models.CharField(pgettext_lazy(u'製品タイトル', u'タイトル'),
                             max_length=255,
                             blank=True)
    slug = models.SlugField(_('スラッグ'), max_length=255, unique=False)
    description = models.TextField(_('詳細説明'), blank=True)

    #: "Kind" of product, e.g. T-Shirt, Book, etc.
    #: None for child products, they inherit their parent's product class
    product_class = models.ForeignKey(
        'catalogue.ProductClass',
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        verbose_name=_('製品タイプ'),
        related_name="products",
        help_text=_("Choose what type of product this is"))
    attributes = models.ManyToManyField(
        'catalogue.ProductAttribute',
        through='ProductAttributeValue',
        verbose_name=_("Attributes"),
        help_text=_("A product attribute is something that this product may "
                    "have, such as a size, as specified by its class"))
    #: It's possible to have options product class-wide, and per product.
    product_options = models.ManyToManyField(
        'catalogue.Option',
        blank=True,
        verbose_name=_("製品オプション"),
        help_text=_("Options are values that can be associated with a item "
                    "when it is added to a customer's basket.  This could be "
                    "something like a personalised message to be printed on "
                    "a T-shirt."))

    recommended_products = models.ManyToManyField(
        'catalogue.Product',
        through='ProductRecommendation',
        blank=True,
        verbose_name=_("オススメ商品"),
        help_text=_("These are products that are recommended to accompany the "
                    "main product."))

    # 非正規化された製品評価-レビューアプリで使用される
    # 評価「なし」の場合、製品には評価項目がなくなる
    rating = models.FloatField(
        _('Rating'), null=True,
        editable=False)  # editable=False:adminページに表示させない

    date_created = models.DateTimeField(_("Date created"),
                                        auto_now_add=True,
                                        db_index=True)

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

    categories = models.ManyToManyField('catalogue.Category',
                                        through='ProductCategory',
                                        verbose_name=_("Categories"))

    #: Determines if a product may be used in an offer. It is illegal to
    #: discount some types of product (e.g. ebooks) and this field helps
    #: merchants from avoiding discounting such products
    #: Note that this flag is ignored for child products; they inherit from
    #: the parent product.
    is_discountable = models.BooleanField(
        _("割引可能か?"),
        default=True,
        help_text=_("このフラグは、この製品をオファーで使用できるかどうかを示す。"))

    objects = ProductQuerySet.as_manager()
    # browsable property is deprecated and will be removed in Oscar 2.1
    # Use Product.objects.browsable() instead.
    browsable = BrowsableProductManager()

    class Meta:
        abstract = True
        app_label = 'catalogue'
        ordering = ['-date_created']
        verbose_name = _('Product')
        verbose_name_plural = _('Products')

    def __init__(self, *args, **kwargs):
        """初期化メソッド"""
        super().__init__(*args, **kwargs)
        self.attr = ProductAttributesContainer(product=self)

    def __str__(self):
        """adminページへの表示仕様を決定するメソッド"""
        if self.title:
            return self.title
        if self.attribute_summary:
            return "%s (%s)" % (self.get_title(), self.attribute_summary)
        else:
            return self.get_title()

    def get_absolute_url(self):
        """製品の絶対URLを返却するメソッド"""
        return reverse('catalogue:detail',
                       kwargs={
                           'product_slug': self.slug,
                           'pk': self.id
                       })

    def clean(self):
        """
        以下ルールにしたがって製品を判定する

        +---------------+-------------+--------------+--------------+
        |               | stand alone | parent       | child        |
        +---------------+-------------+--------------+--------------+
        | title         | required    | required     | optional     |
        +---------------+-------------+--------------+--------------+
        | product class | required    | required     | must be None |
        +---------------+-------------+--------------+--------------+
        | parent        | forbidden   | forbidden    | required     |
        +---------------+-------------+--------------+--------------+
        | stockrecords  | 0 or more   | forbidden    | 0 or more    |
        +---------------+-------------+--------------+--------------+
        | categories    | 1 or more   | 1 or more    | forbidden    |
        +---------------+-------------+--------------+--------------+
        | attributes    | optional    | optional     | optional     |
        +---------------+-------------+--------------+--------------+
        | rec. products | optional    | optional     | unsupported  |
        +---------------+-------------+--------------+--------------+
        | options       | optional    | optional     | forbidden    |
        +---------------+-------------+--------------+--------------+

        検証ロジックは非常に複雑なため、検証は製品の構造に適したサブメソッドに委任される。
        """
        getattr(self, '_clean_%s' % self.structure)()
        if not self.is_parent:
            self.attr.validate_attributes()

    def _clean_standalone(self):
        """スタンドアロン商品をバリデート(判定)する"""
        if not self.title:
            raise ValidationError(_("Your product must have a title."))
        if not self.product_class:
            raise ValidationError(_("Your product must have a product class."))
        if self.parent_id:
            raise ValidationError(_("Only child products can have a parent."))

    def _clean_child(self):
        """子製品をバリデート(判定)する"""
        if not self.parent_id:
            raise ValidationError(_("A child product needs a parent."))
        if self.parent_id and not self.parent.is_parent:
            raise ValidationError(
                _("You can only assign child products to parent products."))
        if self.product_class:
            raise ValidationError(
                _("A child product can't have a product class."))
        if self.pk and self.categories.exists():
            raise ValidationError(
                _("A child product can't have a category assigned."))
        # Note that we only forbid options on product level
        if self.pk and self.product_options.exists():
            raise ValidationError(_("A child product can't have options."))

    def _clean_parent(self):
        """親製品をバリデート(判定)する"""
        self._clean_standalone()
        if self.has_stockrecords:
            raise ValidationError(
                _("A parent product can't have stockrecords."))

    def save(self, *args, **kwargs):
        """編集を保存するメソッド。adminページにて「保存する」ボタンを押下したときに呼び出される"""
        if not self.slug:
            self.slug = slugify(self.get_title())
        super().save(*args, **kwargs)
        self.attr.save()

    # Properties

    @property
    def is_standalone(self):
        """「structure」カラムに'standalone'を設定するメソッド"""

        return self.structure == self.STANDALONE

    @property
    def is_parent(self):
        """「structure」カラムに'parent'を設定するメソッド"""

        return self.structure == self.PARENT

    @property
    def is_child(self):
        """「structure」カラムに'child'を設定するメソッド"""

        return self.structure == self.CHILD

    def can_be_parent(self, give_reason=False):
        """製品を親製品に変えることができるかどうかを判断するのに役立つメソッド"""

        reason = None
        if self.is_child:
            reason = _('The specified parent product is a child product.')
        if self.has_stockrecords:
            reason = _("One can't add a child product to a product with stock"
                       " records.")
        is_valid = reason is None
        if give_reason:
            return is_valid, reason
        else:
            return is_valid

    @property
    def options(self):
        """
        該当製品に対して全ての有効なオプションのセットを返却するメソッド
        オプション製品はクラス全体で、製品ごとに持つことができる。
        """
        pclass_options = self.get_product_class().options.all()
        return pclass_options | self.product_options.all()

    @cached_property
    def has_options(self):
        # 製品リストクエリセットから多数の製品クラスオプションを使用して注釈付きの値を抽出する。
        num_product_class_options = getattr(self, 'num_product_class_options',
                                            None)
        num_product_options = getattr(self, 'num_product_options', None)
        if num_product_class_options is not None and num_product_options is not None:
            return num_product_class_options > 0 or num_product_options > 0
        return self.get_product_class().options.exists(
        ) or self.product_options.exists()

    @property
    def is_shipping_required(self):
        return self.get_product_class().requires_shipping

    @property
    def has_stockrecords(self):
        """この製品に在庫記録があるかどうかテストするメソッド"""

        return self.stockrecords.exists()

    @property
    def num_stockrecords(self):
        """該当製品の在庫数を返却するメソッド"""

        return self.stockrecords.count()

    @property
    def attribute_summary(self):
        """
        製品のすべての属性の文字列を返却するメソッド
        """
        attributes = self.attribute_values.all()
        pairs = [attribute.summary() for attribute in attributes]
        return ", ".join(pairs)

    def get_title(self):
        """
        製品のタイトルを取得し返却する。タイトルがない場合は親のタイトルを返す
        """
        title = self.title
        if not title and self.parent_id:
            title = self.parent.title
        return title

    get_title.short_description = pgettext_lazy("Product title", "Title")

    def get_product_class(self):
        """
        該当製品の「製品タイプ」カラム部分の値を返却するメソッド
        ※子製品は親製品を継承する
        """
        if self.is_child:
            return self.parent.product_class
        else:
            return self.product_class

    get_product_class.short_description = _("Product class")

    def get_is_discountable(self):
        """
        現時点では、子製品に対してis_discountableを個別に設定することはできず、親から継承する。
        """
        if self.is_child:
            return self.parent.is_discountable
        else:
            return self.is_discountable

    def get_categories(self):
        """
        親製品がある場合、製品のカテゴリまたは親のカテゴリを返すメソッド
        """
        if self.is_child:
            return self.parent.categories
        else:
            return self.categories

    get_categories.short_description = _("Categories")

    # Images

    def get_missing_image(self):
        """
        不足している画像オブジェクトを返すメソッド。
        """
        # This class should have a 'name' property so it mimics the Django file
        # field.
        return MissingProductImage()

    def get_all_images(self):
        if self.is_child and not self.images.exists():
            return self.parent.images.all()
        return self.images.all()

    def primary_image(self):
        """
        Returns the primary image for a product. Usually used when one can
        only display one product image, e.g. in a list of products.
        """
        images = self.get_all_images()
        ordering = self.images.model.Meta.ordering
        if not ordering or ordering[0] != 'display_order':
            # Only apply order_by() if a custom model doesn't use default
            # ordering. Applying order_by() busts the prefetch cache of
            # the ProductManager
            images = images.order_by('display_order')
        try:
            return images[0]
        except IndexError:
            # We return a dict with fields that mirror the key properties of
            # the ProductImage class so this missing image can be used
            # interchangeably in templates.  Strategy pattern ftw!
            missing_image = self.get_missing_image()
            return {
                'original': missing_image.name,
                'caption': '',
                'is_missing': True
            }

    # Updating methods

    def update_rating(self):
        """
        ratingフィールドを再計算し、更新するメソッド
        """
        self.rating = self.calculate_rating()
        self.save()

    update_rating.alters_data = True

    def calculate_rating(self):
        """割引額を計算するメソッド"""
        result = self.reviews.filter(
            status=self.reviews.model.APPROVED).aggregate(sum=Sum('score'),
                                                          count=Count('id'))
        reviews_sum = result['sum'] or 0
        reviews_count = result['count'] or 0
        rating = None
        if reviews_count > 0:
            rating = float(reviews_sum) / reviews_count
        return rating

    def has_review_by(self, user):
        """
        ・レビュー内容を返却する?
        ・ゲストユーザの場合、Falseを返す
        """
        if user.is_anonymous:
            return False
        return self.reviews.filter(user=user).exists()

    def is_review_permitted(self, user):
        """
        ユーザーがこの製品にレビューを追加できるかどうかを決定するメソッド。
        ■デフォルトでの実装
            「OSCAR_ALLOW_ANON_REVIEWS」を尊重
            →ユーザーと製品ごとに1つのレビューのみ投稿可能
        ※デフォルトの動作を変更する場合は、これをオーバーライドする必要がある
         例えば レビューを残すことを許可するために、ユーザーが製品を購入したことを強制する
        """
        if user.is_authenticated or settings.OSCAR_ALLOW_ANON_REVIEWS:
            return not self.has_review_by(user)
        else:
            return False

    @cached_property
    def num_approved_reviews(self):
        """承認されたレビューの数を返却するメソッド?"""
        return self.reviews.approved().count()

    @property
    def sorted_recommended_products(self):
        """推奨ランキングによる順序を維持して返却するメソッド"""
        return [
            r.recommendation
            for r in self.primary_recommendations.select_related(
                'recommendation').all()
        ]
コード例 #7
0
class AbstractProduct(models.Model):
    """
    The base product object

    If an item has no parent, then it is the "canonical" or abstract version
    of a product which essentially represents a set of products.  If a
    product has a parent then it is a specific version of a catalogue.

    For example, a canonical product would have a title like "Green fleece"
    while its children would be "Green fleece - size L".
    """

    upc = NullCharField(
        _("UPC"),
        max_length=64,
        blank=True,
        null=True,
        unique=True,
        help_text=_("Universal Product Code (UPC) is an identifier for "
                    "a product which is not specific to a particular "
                    " supplier. Eg an ISBN for a book."))

    # No canonical product should have a stock record as they cannot be bought.
    parent = models.ForeignKey(
        'self',
        null=True,
        blank=True,
        related_name='variants',
        verbose_name=_("Parent"),
        help_text=_("Only choose a parent product if this is a 'variant' of "
                    "a canonical catalogue.  For example if this is a size "
                    "4 of a particular t-shirt.  Leave blank if this is a "
                    "CANONICAL PRODUCT (ie there is only one version of this "
                    "product)."))

    # Title is mandatory for canonical products but optional for child products
    title = models.CharField(_('Product title'), max_length=255, blank=True)
    slug = models.SlugField(_('Slug'), max_length=255, unique=False)
    description = models.TextField(_('Description'), blank=True)

    #: "Type" of product.
    #: None for Product variants, they inherit their parent's product class
    product_class = models.ForeignKey(
        'catalogue.ProductClass',
        null=True,
        on_delete=models.PROTECT,
        verbose_name=_('Product Type'),
        related_name="products",
        help_text=_("Choose what type of product this is"))
    attributes = models.ManyToManyField(
        'catalogue.ProductAttribute',
        through='ProductAttributeValue',
        verbose_name=_("Attributes"),
        help_text=_("A product attribute is something that this product MUST "
                    "have, such as a size, as specified by its class"))
    product_options = models.ManyToManyField(
        'catalogue.Option',
        blank=True,
        verbose_name=_("Product Options"),
        help_text=_("Options are values that can be associated with a item "
                    "when it is added to a customer's basket.  This could be "
                    "something like a personalised message to be printed on "
                    "a T-shirt."))

    recommended_products = models.ManyToManyField(
        'catalogue.Product',
        through='ProductRecommendation',
        blank=True,
        verbose_name=_("Recommended Products"),
        help_text=_("These are products that are recommended to accompany the "
                    "main product."))

    # Denormalised product rating - used by reviews app.
    # Product has no ratings if rating is None
    rating = models.FloatField(_('Rating'), null=True, editable=False)

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

    # This field is used by Haystack to reindex search
    date_updated = models.DateTimeField(_("Date Updated"),
                                        auto_now=True,
                                        db_index=True)

    categories = models.ManyToManyField('catalogue.Category',
                                        through='ProductCategory',
                                        verbose_name=_("Categories"))

    #: Determines if a product may be used in an offer. It is illegal to
    #: discount some types of product (e.g. ebooks) and this field helps
    #: merchants from avoiding discounting such products
    is_discountable = models.BooleanField(
        _("Is discountable?"),
        default=True,
        help_text=_(
            "This flag indicates if this product can be used in an offer "
            "or not"))

    objects = ProductManager()
    browsable = BrowsableProductManager()

    class Meta:
        abstract = True
        ordering = ['-date_created']
        verbose_name = _('Product')
        verbose_name_plural = _('Products')

    def __init__(self, *args, **kwargs):
        super(AbstractProduct, self).__init__(*args, **kwargs)
        self.attr = ProductAttributesContainer(product=self)

    def __unicode__(self):
        if self.is_variant:
            return u"%s (%s)" % (self.get_title(), self.attribute_summary)
        return self.get_title()

    def get_absolute_url(self):
        """
        Return a product's absolute url
        """
        return reverse('catalogue:detail',
                       kwargs={
                           'product_slug': self.slug,
                           'pk': self.id
                       })

    def clean(self):
        if self.is_top_level and not self.title:
            raise ValidationError(_("Canonical products must have a title"))
        if self.is_top_level and not self.product_class:
            raise ValidationError(
                _("Canonical products must have a product class"))
        if not self.is_group:
            self.attr.validate_attributes()

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.get_title())
        super(AbstractProduct, self).save(*args, **kwargs)
        self.attr.save()

    # Properties

    @property
    def options(self):
        pclass = self.get_product_class()
        if pclass:
            return list(
                chain(self.product_options.all(),
                      self.get_product_class().options.all()))
        return self.product_options.all()

    @property
    def is_top_level(self):
        """
        Test if this product is a parent (who may or may not have children)
        """
        return self.parent_id is None

    @cached_property
    def is_group(self):
        """
        Test if this is a top level product and has more than 0 variants
        """
        return self.is_top_level and self.variants.exists()

    @property
    def is_variant(self):
        """Return True if a product is not a top level product"""
        return not self.is_top_level

    @property
    def is_shipping_required(self):
        return self.get_product_class().requires_shipping

    @property
    def has_stockrecords(self):
        """
        Test if this product has any stockrecords
        """
        return self.num_stockrecords > 0

    @property
    def num_stockrecords(self):
        return self.stockrecords.all().count()

    @property
    def attribute_summary(self):
        """
        Return a string of all of a product's attributes
        """
        pairs = []
        for value in self.attribute_values.select_related().all():
            pairs.append(value.summary())
        return ", ".join(pairs)

    @property
    def min_variant_price_incl_tax(self):
        """
        Return minimum variant price including tax
        """
        return self._min_variant_price('price_incl_tax')

    @property
    def min_variant_price_excl_tax(self):
        """
        Return minimum variant price excluding tax
        """
        return self._min_variant_price('price_excl_tax')

    def _min_variant_price(self, property):
        """
        Return minimum variant price
        """
        prices = []
        for variant in self.variants.all():
            if variant.has_stockrecords:
                prices.append(getattr(variant.stockrecord, property))
        if not prices:
            return None
        prices.sort()
        return prices[0]

    # Wrappers

    def get_title(self):
        """
        Return a product's title or it's parent's title if it has no title
        """
        title = self.title
        if not title and self.parent_id:
            title = self.parent.title
        return title

    get_title.short_description = pgettext_lazy(u"Product title", u"Title")

    def get_product_class(self):
        """
        Return a product's item class
        """
        if self.product_class_id or self.product_class:
            return self.product_class
        if self.parent and self.parent.product_class:
            return self.parent.product_class
        return None

    get_product_class.short_description = _("Product class")

    # Images

    def get_missing_image(self):
        """
        Returns a missing image object.
        """
        # This class should have a 'name' property so it mimics the Django file
        # field.
        return MissingProductImage()

    def primary_image(self):
        """
        Returns the primary image for a product. Usually used when one can
        only display one product image, e.g. in a list of products.
        """
        images = self.images.all()
        ordering = self.images.model.Meta.ordering
        if not ordering or ordering[0] != 'display_order':
            # Only apply order_by() if a custom model doesn't use default
            # ordering. Applying order_by() busts the prefetch cache of
            # the ProductManager
            images = images.order_by('display_order')
        try:
            return images[0]
        except IndexError:
            # We return a dict with fields that mirror the key properties of
            # the ProductImage class so this missing image can be used
            # interchangeably in templates.  Strategy pattern ftw!
            return {
                'original': self.get_missing_image(),
                'caption': '',
                'is_missing': True
            }

    # Updating methods

    def update_rating(self):
        """
        Recalculate rating field
        """
        self.rating = self.calculate_rating()
        self.save()

    update_rating.alters_data = True

    def calculate_rating(self):
        """
        Calculate rating value
        """
        result = self.reviews.filter(
            status=self.reviews.model.APPROVED).aggregate(sum=Sum('score'),
                                                          count=Count('id'))
        reviews_sum = result['sum'] or 0
        reviews_count = result['count'] or 0
        rating = None
        if reviews_count > 0:
            rating = float(reviews_sum) / reviews_count
        return rating

    def has_review_by(self, user):
        if user.is_anonymous():
            return False
        return self.reviews.filter(user=user).exists()

    def is_review_permitted(self, user):
        """
        Determines whether a user may add a review on this product.

        Default implementation respects OSCAR_ALLOW_ANON_REVIEWS and only
        allows leaving one review per user and product.

        Override this if you want to alter the default behaviour; e.g. enforce
        that a user purchased the product to be allowed to leave a review.
        """
        if user.is_authenticated() or settings.OSCAR_ALLOW_ANON_REVIEWS:
            return not self.has_review_by(user)
        else:
            return False

    @cached_property
    def num_approved_reviews(self):
        return self.reviews.filter(status=self.reviews.model.APPROVED).count()
コード例 #8
0
class TransferMetadata(AccountNumberMixin, models.Model):
    """
    Store WFRS specific metadata about a transfer
    """

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        verbose_name=_("Requesting User"),
        related_name="wfrs_transfers",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
    )
    merchant_name = NullCharField(_("Merchant Name"), max_length=200)
    merchant_num = NullCharField(_("Merchant Number"), max_length=200)
    merchant_reference = models.CharField(max_length=128, null=True)
    amount = models.DecimalField(decimal_places=2, max_digits=12)
    type_code = models.CharField(_("Transaction Type"),
                                 choices=TRANS_TYPES,
                                 max_length=2)
    ticket_number = models.CharField(_("Ticket Number"),
                                     null=True,
                                     blank=True,
                                     max_length=12)
    financing_plan = models.ForeignKey(
        "wellsfargo.FinancingPlan",
        verbose_name=_("Plan Number"),
        null=True,
        blank=False,
        on_delete=models.SET_NULL,
    )
    auth_number = models.CharField(_("Authorization Number"),
                                   null=True,
                                   blank=True,
                                   max_length=6,
                                   default="000000")
    status = models.CharField(_("Status"),
                              choices=TRANS_STATUSES,
                              max_length=2)
    message = models.TextField(_("Message"))
    disclosure = models.TextField(_("Disclosure"))
    created_datetime = models.DateTimeField(_("Created"), auto_now_add=True)
    modified_datetime = models.DateTimeField(_("Modified"), auto_now=True)

    @classmethod
    def get_by_oscar_transaction(cls, transaction, type_code=TRANS_TYPE_AUTH):
        return (cls.objects.filter(
            merchant_reference=transaction.reference).filter(
                type_code=type_code).order_by("-created_datetime").first())

    @property
    def type_name(self):
        return dict(TRANS_TYPES).get(self.type_code)

    @property
    def status_name(self):
        return dict(TRANS_STATUSES).get(self.status)

    @property
    def financing_plan_number(self):
        return self.financing_plan.plan_number if self.financing_plan else None

    @cached_property
    def order(self):
        return self.get_order()

    def get_oscar_transaction(self):
        Transaction = get_model("payment", "Transaction")
        try:
            return Transaction.objects.get(reference=self.merchant_reference)
        except Transaction.DoesNotExist:
            return None

    def get_order(self):
        transaction = self.get_oscar_transaction()
        if not transaction:
            return None
        return transaction.source.order
コード例 #9
0
 def test_from_db_value_converts_null_to_string(self):
     field = NullCharField()
     self.assertEqual('', field.from_db_value(None, expression=None, connection=None, context=None))
コード例 #10
0
 def test_get_prep_value_converts_empty_string_to_null(self):
     field = NullCharField()
     self.assertEqual(None, field.get_prep_value(''))
コード例 #11
0
ファイル: apps.py プロジェクト: thelabnyc/django-oscar-wfrs
class CreditApplication(MaybeAccountNumberMixin, models.Model):
    transaction_code = models.CharField(
        _("Transaction Code"),
        max_length=_max_len(CREDIT_APP_TRANS_CODES),
        choices=CREDIT_APP_TRANS_CODES,
        default=CREDIT_APP_TRANS_CODE_CREDIT_APPLICATION,
        help_text=_("Indicates where the transaction takes place."),
    )
    reservation_number = NullCharField(
        _("Reservation Number"),
        max_length=20,
        help_text=_("The unique code that correlates with the user’s reservation."),
    )
    application_id = NullCharField(
        _("Prequalified Application ID"),
        max_length=8,
        help_text=_("An 8-character alphanumeric ID identifying the application."),
    )
    requested_credit_limit = models.IntegerField(
        _("Requested Credit Limit"),
        null=True,
        blank=True,
        validators=[MinValueValidator(0), MaxValueValidator(99999)],
        help_text=_(
            "This denotes the total price value of the items that the applicant’s shopping cart."
        ),
    )
    language_preference = models.CharField(
        _("Language Preference"),
        max_length=_max_len(LANGUAGES),
        choices=LANGUAGES,
        default=ENGLISH,
        help_text=_("The main applicant’s language preference values"),
    )
    salesperson = NullCharField(
        _("Sales Person ID"),
        max_length=10,
        help_text=_("Alphanumeric value associated with the salesperson."),
    )

    # Main applicant data
    main_applicant = models.ForeignKey(
        "wellsfargo.CreditApplicationApplicant",
        verbose_name=_("Main Applicant"),
        related_name="+",
        on_delete=models.CASCADE,
        help_text=_("The main applicant’s personal details."),
    )
    joint_applicant = models.ForeignKey(
        "wellsfargo.CreditApplicationApplicant",
        verbose_name=_("Joint Applicant"),
        null=True,
        blank=True,
        related_name="+",
        on_delete=models.CASCADE,
        help_text=_("The joint applicant’s details."),
    )

    # Submit Status
    status = models.CharField(
        _("Application Status"),
        max_length=_max_len(CREDIT_APP_STATUSES),
        choices=CREDIT_APP_STATUSES,
        default="",
        help_text=_("Application Status"),
    )

    # Internal Metadata
    merchant_name = NullCharField(_("Merchant Name"), max_length=200)
    merchant_num = NullCharField(_("Merchant Number"), max_length=200)
    application_source = models.CharField(
        _("Application Source"),
        default=_("Website"),
        max_length=25,
        help_text=_(
            "Where/how is user applying? E.g. Website, Call Center, In-Store, etc."
        ),
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        verbose_name=_("Owner"),
        help_text=_(
            "Select the user user who is applying and who will own (be the primary user of) this account."
        ),
        related_name="+",
        on_delete=models.SET_NULL,
    )
    ip_address = models.GenericIPAddressField(
        _("Submitting User's IP Address"),
        null=True,
        blank=True,
        help_text=_("Submitting User's IP Address"),
    )
    submitting_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        verbose_name=_("Submitting User"),
        help_text=_(
            "Select the user who filled out and submitted the credit application (not always the same as the user who is applying for credit)."
        ),
        related_name="+",
        on_delete=models.SET_NULL,
    )
    created_datetime = models.DateTimeField(_("Created Date/Time"), auto_now_add=True)
    modified_datetime = models.DateTimeField(_("Modified Date/Time"), auto_now=True)

    class Meta:
        verbose_name = _("Wells Fargo Credit Application")
        verbose_name_plural = _("Wells Fargo Credit Applications")

    @property
    def is_joint(self):
        return False

    @property
    def full_name(self):
        return "%s %s" % (self.main_applicant.first_name, self.main_applicant.last_name)

    def get_inquiries(self):
        return self.inquiries.order_by("-created_datetime").all()

    def get_credit_limit(self):
        inquiry = self.get_inquiries().first()
        if not inquiry:
            return None
        return inquiry.credit_limit

    def get_orders(self):
        """
        Find orders that were probably placed using the account that resulted from this application. It's
        not foolproof since we don't store the full account number.
        """
        if not hasattr(self, "_orders_cache"):
            Order = get_model("order", "Order")
            # all transfers made with last 4 digits
            reference_uuids = set(
                TransferMetadata.objects.filter(
                    last4_account_number=self.last4_account_number
                )
                .values_list("merchant_reference", flat=True)
                .distinct()
                .all()
            )
            # all orders made by app.email that contain ref above UUIDs
            emails = [self.main_applicant.email_address]
            if self.joint_applicant:
                emails.append(self.joint_applicant.email_address)
            orders = (
                Order.objects.filter(
                    Q(guest_email__in=emails) | Q(user__email__in=emails)
                )
                .filter(sources__transactions__reference__in=reference_uuids)
                .filter(date_placed__gte=self.created_datetime)
                .order_by("date_placed")
                .all()
            )
            self._orders_cache = orders
        return self._orders_cache

    def get_first_order(self):
        if not hasattr(self, "_first_order_cache"):
            self._first_order_cache = self.get_orders().first()
        return self._first_order_cache

    def get_first_order_merchant_name(self):
        Transaction = get_model("payment", "Transaction")
        order = self.get_first_order()
        if not order:
            return None
        transfers = []
        for source in order.sources.filter(source_type__name="Wells Fargo").all():
            for transaction in source.transactions.filter(
                txn_type=Transaction.AUTHORISE
            ).all():
                transfer = TransferMetadata.get_by_oscar_transaction(transaction)
                if transfer:
                    transfers.append(transfer)
        if len(transfers) <= 0:
            return None
        return transfers[0].merchant_name
コード例 #12
0
class PreQualificationRequest(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    entry_point = models.CharField(
        _("Entry Point"),
        max_length=_max_len(PREQUAL_ENTRY_POINT_CHOICES),
        choices=PREQUAL_ENTRY_POINT_CHOICES,
        default=PREQUAL_ENTRY_POINT_WEB,
    )
    customer_initiated = models.BooleanField(
        _("Check was deliberately initiated by customer action"),
        default=False)
    email = models.EmailField(_("Email"), null=True, blank=True)
    first_name = models.CharField(_("First Name"), max_length=15)
    middle_initial = models.CharField(_("Middle Initial"),
                                      null=True,
                                      blank=True,
                                      max_length=1)
    last_name = models.CharField(_("Last Name"), max_length=20)
    line1 = models.CharField(_("Address Line 1"), max_length=26)
    line2 = models.CharField(_("Address Line 2"),
                             max_length=26,
                             null=True,
                             blank=True)
    city = models.CharField(_("City"), max_length=15)
    state = USStateField(_("State"))
    postcode = USZipCodeField(_("Postcode"))
    phone = PhoneNumberField(_("Phone"))
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    merchant_name = NullCharField(_("Merchant Name"), max_length=200)
    merchant_num = NullCharField(_("Merchant Number"), max_length=200)
    created_datetime = models.DateTimeField(auto_now_add=True)
    modified_datetime = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = _("Pre-Qualification Request")
        verbose_name_plural = _("Pre-Qualification Requests")
        ordering = ("-created_datetime", "-id")
        indexes = [
            models.Index(fields=["-created_datetime", "-id"]),
        ]

    @property
    def entry_point_name(self):
        return dict(PREQUAL_ENTRY_POINT_CHOICES).get(self.entry_point,
                                                     self.entry_point)

    @property
    def status(self):
        response = getattr(self, "response", None)
        if response:
            return response.status
        return PREQUAL_TRANS_STATUS_REJECTED

    @property
    def status_name(self):
        response = getattr(self, "response", None)
        if response:
            return response.status_name
        return get_prequal_trans_status_name(PREQUAL_TRANS_STATUS_REJECTED,
                                             self.customer_initiated)

    @property
    def credit_limit(self):
        resp = getattr(self, "response", None)
        if resp is None:
            return None
        return resp.credit_limit

    @property
    def customer_response(self):
        resp = getattr(self, "response", None)
        if resp is None:
            return None
        return resp.customer_response

    @property
    def sdk_application_result(self):
        resp = getattr(self, "response", None)
        if resp is None:
            return None
        app_result = getattr(resp, "sdk_application_result", None)
        if app_result is None:
            return None
        return app_result.application_status

    @cached_property
    def resulting_order(self):
        resp = getattr(self, "response", None)
        if resp and resp.customer_order:
            return resp.customer_order
        # Look for other orders which might have been placed by this customer
        Order = get_model("order", "Order")
        email_matches = Q(guest_email=self.email) | Q(user__email=self.email)
        date_matches = Q(date_placed__gt=self.created_datetime)
        order = (Order.objects.filter(email_matches & date_matches).order_by(
            "date_placed").first())
        return order

    @property
    def order_total(self):
        return self.resulting_order.total_incl_tax if self.resulting_order else None

    @property
    def order_date_placed(self):
        return self.resulting_order.date_placed if self.resulting_order else None

    @cached_property
    def order_merchant_name(self):
        Transaction = get_model("payment", "Transaction")
        order = self.resulting_order
        if not order:
            return None
        transfers = []
        for source in order.sources.filter(
                source_type__name="Wells Fargo").all():
            for transaction in source.transactions.filter(
                    txn_type=Transaction.AUTHORISE).all():
                transfer = TransferMetadata.get_by_oscar_transaction(
                    transaction)
                if transfer:
                    transfers.append(transfer)
        if len(transfers) <= 0:
            return None
        return transfers[0].merchant_name

    @property
    def response_reported_datetime(self):
        resp = getattr(self, "response", None)
        if resp is None:
            return None
        return resp.reported_datetime

    def get_signed_id(self):
        return signing.Signer().sign(self.pk)

    def get_resume_offer_url(self, next_url="/"):
        url = reverse("wfrs-api-prequal-resume", args=[self.get_signed_id()])
        qs = urllib.parse.urlencode({
            "next": next_url,
        })
        return "{}?{}".format(url, qs)