Shop

class plata.shop.models.BillingShippingAddress(*args, **kwargs)[source]

Abstract base class for all models storing a billing and a shipping address

addresses()[source]

Return a dict containing a billing and a shipping address, taking into account the value of the shipping_same_as_billing flag

class plata.shop.models.Order(*args, **kwargs)[source]

The main order model. Used for carts and orders alike.

CART = 10

Order object is a cart.

CHECKOUT = 20

Checkout process has started.

COMPLETED = 50

Order has been completed. Plata itself never sets this state, it is only meant for use by the shop owners.

CONFIRMED = 30

Order has been confirmed, but it not (completely) paid for yet.

exception DoesNotExist
exception MultipleObjectsReturned
PAID = 40

Order has been completely paid for.

PENDING = 35

For invoice payment methods, when waiting for the money

VALIDATE_ALL = 100

This should not be used while registering a validator, it’s mostly useful as an argument to validate() when you want to run all validators.

VALIDATE_BASE = 10

This validator is always called; basic consistency checks such as whether the currencies in the order match should be added here.

VALIDATE_CART = 20

A cart which fails the criteria added to the VALIDATE_CART group isn’t considered a valid cart and the user cannot proceed to the checkout form. Stuff such as stock checking, minimal order total checking, or maximal items checking might be added here.

balance_remaining

Returns the balance which needs to be paid by the customer to fully pay this order. This value is not necessarily the same as the order total, because there can be more than one order payment in principle.

discount

Returns the discount total.

discount_remaining

Remaining discount amount excl. tax

is_confirmed()[source]

Returns True if this order has already been confirmed and therefore cannot be modified anymore.

items_in_order()[source]

Returns the item count in the order

This is different from order.items.count() because it counts items, not distinct products.

modify_item(product, relative=None, absolute=None, recalculate=True, data=None, item=None, force_new=False)[source]

Updates order with the given product

  • relative or absolute: Add/subtract or define order item amount exactly
  • recalculate: Recalculate order after cart modification (defaults to True)
  • data: Additional data for the order item; replaces the contents of the JSON field if it is not None. Pass an empty dictionary if you want to reset the contents.
  • item: The order item which should be modified. Will be automatically detected using the product if unspecified.
  • force_new: Force the creation of a new order item, even if the product exists already in the cart (especially useful if the product is configurable).

Returns the OrderItem instance; if quantity is zero, the order item instance is deleted, the pk attribute set to None but the order item is returned anyway.

order_id

Returns _order_id (if it has been set) or a generic ID for this order.

recalculate_total(save=True)[source]

Recalculates totals, discounts, taxes.

classmethod register_validator(validator, group)[source]

Registers another order validator in a validation group

A validator is a callable accepting an order (and only an order).

There are several types of order validators:

  • Base validators are always called
  • Cart validators: Need to validate for a valid cart
  • Checkout validators: Need to validate in the checkout process
reload()[source]

Return this order instance, reloaded from the database

Used f.e. inside the payment processors when adding new payment records etc.

save(*args, **kwargs)[source]

Sequential order IDs for completed orders.

shipping

Returns the shipping cost, with or without tax depending on this order’s price_includes_tax field.

subtotal

Returns the order subtotal.

tax

Returns the tax total for this order, meaning tax on order items and tax on shipping.

update_status(status, notes)[source]

Update the order status

validate(group)[source]

Validates this order

The argument determines which order validators are called:

  • Order.VALIDATE_BASE
  • Order.VALIDATE_CART
  • Order.VALIDATE_CHECKOUT
  • Order.VALIDATE_ALL
class plata.shop.models.OrderItem(*args, **kwargs)[source]

Single order line item

exception DoesNotExist
exception MultipleObjectsReturned
class plata.shop.models.OrderPayment(*args, **kwargs)[source]

Order payment

Stores additional data from the payment interface for analysis and accountability.

exception DoesNotExist
exception MultipleObjectsReturned
class plata.shop.models.OrderStatus(*args, **kwargs)[source]

Order status

Stored in separate model so that the order status changes stay visible for analysis after the fact.

exception DoesNotExist
exception MultipleObjectsReturned
class plata.shop.models.PriceBase(*args, **kwargs)[source]

Price for a given product, currency, tax class and time period

Prices should not be changed or deleted but replaced by more recent prices. (Deleting old prices does not hurt, but the price history cannot be reconstructed anymore if you’d need it.)

The concrete implementation needs to provide a foreign key to the product model.

handle_order_item(item)[source]

Set price data on the OrderItem passed

class plata.shop.models.TaxClass(*args, **kwargs)[source]

Tax class, storing a tax rate

TODO informational / advisory currency or country fields?

exception DoesNotExist
exception MultipleObjectsReturned
plata.shop.models.validate_order_currencies(order)[source]

Check whether order contains more than one or an invalid currency

Order processors

class plata.shop.processors.ApplyRemainingDiscountToShippingProcessor(shared_state)[source]

Apply the remaining discount to the shipping (if shipping is non-zero and there are any remaining discounts left)

class plata.shop.processors.DiscountProcessor(shared_state)[source]

Apply all discounts which do not act as a means of payment but instead act on the subtotal

class plata.shop.processors.FixedAmountShippingProcessor(shared_state)[source]

Set shipping costs to a fixed value. Uses PLATA_SHIPPING_FIXEDAMOUNT. If you have differing needs you should probably implement your own shipping processor (and propose it for inclusion if you like) instead of extending this one.

PLATA_SHIPPING_FIXEDAMOUNT = {
    'cost': Decimal('8.00'),
    'tax': Decimal('19.6'),
    }
class plata.shop.processors.InitializeOrderProcessor(shared_state)[source]

Zero out all relevant order values and calculate line item prices excl. tax.

class plata.shop.processors.ItemSummationProcessor(shared_state)[source]

Sum up line item prices, discounts and taxes.

class plata.shop.processors.MeansOfPaymentDiscountProcessor(shared_state)[source]

Apply all discounts which act as a means of payment.

class plata.shop.processors.OrderSummationProcessor(shared_state)[source]

Sum up order total by adding up items and shipping totals.

process(order, items)[source]

The value must be quantized here, because otherwise f.e. the payment modules will be susceptible to rounding errors giving f.e. missing payments of 0.01 units.

class plata.shop.processors.ProcessorBase(shared_state)[source]

Order processor class base. Offers helper methods for order total aggregation and tax calculation.

add_tax_details(tax_details, tax_rate, price, discount, tax_amount)[source]

Add tax details grouped by tax_rate. Especially useful if orders potentially use more than one tax class. These values are not used for the order total calculation – they are only needed to show the tax amount for different tax rates if this is necessary for your invoices.

  • tax_details: The tax details dict, most often stored as order.data['tax_details'] = tax_details.items()
  • tax_rate: The tax rate of the current entry
  • price: The price excl. tax
  • discount: The discount amount (will be subtracted from the price before applying the tax)
  • tax_amount: The exact amount; a bit redundant because this could be calculated using the values above as well

See the taxes documentation or the standard invoice PDF generation code if you need to know more about the use of these values.

process(order, items)[source]

This is the method which must be implemented in order processor classes.

split_cost(cost_incl_tax, tax_rate)[source]

Split a cost incl. tax into the part excl. tax and the tax

class plata.shop.processors.TaxProcessor(shared_state)[source]

Calculate taxes for every line item and aggregate tax details.

class plata.shop.processors.ZeroShippingProcessor(shared_state)[source]

Set shipping costs to zero.

Template tags

plata.shop.templatetags.plata_tags.form_errors(parser, token)[source]

Show all form and formset errors:

{% form_errors form formset1 formset2 %}

Silently ignores non-existant variables.

plata.shop.templatetags.plata_tags.form_item(item, additional_classes=None)[source]

Helper for easy displaying of form items:

{% for field in form %}{% form_item field %}{% endfor %}
plata.shop.templatetags.plata_tags.form_item_plain(item, additional_classes=None)[source]

Helper for easy displaying of form items without any additional tags (table cells or paragraphs) or labels:

{% form_item_plain field %}
plata.shop.templatetags.plata_tags.form_items(form)[source]

Render all form items:

{% form_items form %}
plata.shop.templatetags.plata_tags.load_plata_context(context)[source]

Conditionally run plata’s context processor using {% load_plata_context %}

Rather than having the overheads involved in globally adding it to TEMPLATE_CONTEXT_PROCESSORS.

plata.shop.templatetags.plata_tags.quantity_ordered(product, order)[source]

e.g. {% if product|quantity_ordered:plata.order > 0 %} … {% endif %}

Signals

plata.shop.signals.contact_created = <django.dispatch.dispatcher.Signal object>

Emitted upon contact creation. Receives the user and contact instance and the new password in cleartext.

plata.shop.signals.order_confirmed = <django.dispatch.dispatcher.Signal object>

Emitted upon order confirmation. Receives an order instance.

plata.shop.signals.order_paid = <django.dispatch.dispatcher.Signal object>

Emitted when an order has been completely paid for. Receives the order and payment instances and the remaining discount amount excl. tax, if there is any.

Notifications

Even though these shop signal handlers might be useful, you might be better off writing your own handlers for the three important signals:

  • contact_created: A new contact has been created during the checkout process
  • order_confirmed: The order has been confirmed, a payment method has been selected
  • order_paid: The order is fully paid

A real-world example follows:

from django.utils.translation import activate

from plata.shop import notifications, signals as shop_signals


class EmailHandler(notifications.BaseHandler):
    ALWAYS = ['shopadmin@example.com']
    SHIPPING = ['warehouse@example.com']

    def __call__(self, sender, order, **kwargs):
        cash_on_delivery = False
        try:
            if (order.payments.all()[0].payment_module_key == 'cod'):
                cash_on_delivery = True
        except:
            pass

        if order.language_code:
            activate(order.language_code)

        invoice_message = self.create_email_message(
            'plata/notifications/order_paid.txt',
            order=order,
            **kwargs)
        invoice_message.attach(order.order_id + '.pdf',
            self.invoice_pdf(order), 'application/pdf')
        invoice_message.to.append(order.email)
        invoice_message.bcc.extend(self.ALWAYS)

        packing_slip_message = self.create_email_message(
            'plata/notifications/packing_slip.txt',
            order=order,
            **kwargs)
        packing_slip_message.attach(
            order.order_id + '-LS.pdf',
            self.packing_slip_pdf(order),
            'application/pdf')
        packing_slip_message.to.extend(self.ALWAYS)

        if cash_on_delivery:
            invoice_message.bcc.extend(self.SHIPPING)
        else:
            packing_slip_message.to.extend(self.SHIPPING)

        invoice_message.send()
        packing_slip_message.send()

shop_signals.contact_created.connect(
    notifications.ContactCreatedHandler(),
    weak=False)
shop_signals.order_paid.connect(
    EmailHandler(),
    weak=False)
class plata.shop.notifications.ContactCreatedHandler(always_to=None, always_bcc=None)[source]

Send an e-mail message to a newly created contact, optionally BCC’ing the addresses passed as always_bcc upon handler initialization.

Usage:

signals.contact_created.connect(
    ContactCreatedHandler(),
    weak=False)

or:

signals.contact_created.connect(
    ContactCreatedHandler(always_bcc=['owner@example.com']),
    weak=False)
class plata.shop.notifications.SendInvoiceHandler(always_to=None, always_bcc=None)[source]

Send an e-mail with attached invoice to the customer after successful order completion, optionally BCC’ing the addresses passed as always_bcc to the handler upon initialization.

Usage:

signals.order_paid.connect(
    SendInvoiceHandler(always_bcc=['owner@example.com']),
    weak=False)
class plata.shop.notifications.SendPackingSlipHandler(always_to=None, always_bcc=None)[source]

Send an e-mail with attached packing slip to the addresses specified upon handler initialization. You should pass at least one address in either the always_to or the always_bcc argument, or else the e-mail will go nowhere.

Usage:

signals.order_paid.connect(
    SendPackingSlipHandler(always_to=['warehouse@example.com']),
    weak=False)