WesternFriend/WF-website

View on GitHub
orders/models.py

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
from decimal import Decimal
from django.db import models
from modelcluster.fields import ParentalKey  # type: ignore
from modelcluster.models import ClusterableModel  # type: ignore
from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel
from wagtail.models import Orderable


class Order(ClusterableModel):
    purchaser_given_name = models.CharField(
        max_length=255,
        default="",
        help_text="Enter the given name for the purchaser.",
        blank=True,
    )
    purchaser_family_name = models.CharField(
        max_length=255,
        blank=True,
        default="",
        help_text="Enter the family name for the purchaser.",
    )
    purchaser_meeting_or_organization = models.CharField(
        max_length=255,
        blank=True,
        default="",
        help_text="Enter the meeting or organization name, if this purchaser is a meeting or organization.",  # noqa: E501
    )
    purchaser_email = models.EmailField(
        help_text="Provide an email, so we can communicate any issues regarding this order.",  # noqa: E501
    )
    recipient_name = models.CharField(
        max_length=255,
        default="",
        help_text="Enter the recipient name (as it should appear on shipping label).",
    )
    recipient_street_address = models.CharField(
        max_length=255,
        blank=True,
        default="",
        help_text="Reicipient street address and/or PO box",
    )
    recipient_postal_code = models.CharField(
        max_length=16,
        help_text="Postal code for the shipping address.",
    )
    recipient_address_locality = models.CharField(
        max_length=255,
        help_text="City for the shipping address.",
    )
    recipient_address_region = models.CharField(
        max_length=255,
        help_text="State for the shipping address.",
        blank=True,
        default="",
    )
    recipient_address_country = models.CharField(
        max_length=255,
        default="United States",
        help_text="Country for shipping.",
    )
    shipping_cost = models.DecimalField(
        max_digits=10,
        decimal_places=2,
    )
    paid = models.BooleanField(default=False)
    paypal_order_id = models.CharField(
        max_length=255,
        blank=True,
        default="",
    )
    paypal_payment_id = models.CharField(
        max_length=255,
        blank=True,
        default="",
    )
    paypal_transaction_id = models.CharField(
        max_length=255,
        blank=True,
        default="",
    )
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        indexes = [
            models.Index(
                fields=["paypal_order_id"],
            ),
        ]

    panels = [
        MultiFieldPanel(
            [
                FieldPanel("purchaser_given_name"),
                FieldPanel("purchaser_family_name"),
                FieldPanel("purchaser_meeting_or_organization"),
                FieldPanel("purchaser_email"),
            ],
            heading="Purchaser",
        ),
        MultiFieldPanel(
            [
                FieldPanel("recipient_name"),
                FieldPanel("recipient_street_address"),
                FieldPanel("recipient_postal_code"),
                FieldPanel("recipient_address_locality"),
                FieldPanel("recipient_address_region"),
                FieldPanel("recipient_address_country"),
            ],
            heading="Recipient",
        ),
        MultiFieldPanel(
            [
                FieldPanel(
                    "paypal_transaction_id",
                    read_only=True,
                ),
                FieldPanel("paid"),
                FieldPanel("shipping_cost"),
            ],
            heading="PayPal Payment Info",
        ),
        InlinePanel("items", label="Order items"),
    ]

    def __str__(self) -> str:
        return f"Order {self.id}"  # type: ignore

    def get_total_items_cost(self) -> Decimal:
        """Return the sum of all order items' costs."""
        # order.items is of type list[OrderItem]

        items_cost = sum(
            [item.get_cost() for item in self.items.all()],
        )

        return Decimal(items_cost).quantize(Decimal("0.01"))

    def get_total_cost(self) -> Decimal:
        """Return the sum of all order items' costs, plus shipping cost."""
        return self.get_total_items_cost() + Decimal(self.shipping_cost)

    @property
    def purchaser_full_name(self) -> str:
        full_name = ""

        if self.purchaser_given_name:
            full_name += self.purchaser_given_name + " "
        if self.purchaser_family_name:
            full_name += self.purchaser_family_name + " "
        if self.purchaser_meeting_or_organization:
            full_name += self.purchaser_meeting_or_organization
        # Combine any available name data, removing leading or trailing whitespace
        return full_name.rstrip()


class OrderItem(Orderable):
    order = ParentalKey(
        Order,
        related_name="items",
        on_delete=models.CASCADE,
        blank=False,
    )
    product_title = models.CharField(
        max_length=255,
    )
    product_id = models.PositiveIntegerField(
        default=1,
    )
    price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
    )
    quantity = models.PositiveIntegerField(
        default=1,
    )

    panels = [
        FieldPanel("product_title"),
        FieldPanel("price"),
        FieldPanel("quantity"),
    ]

    def __str__(self) -> str:
        return f"{self.quantity}x {self.product_title} @ {round(self.price, 2)}/each"  # noqa: E501

    def get_cost(self) -> Decimal:
        """Return the total cost for this order item."""
        total_cost = self.price * self.quantity

        return Decimal(total_cost).quantize(Decimal("0.01"))