Source code for shoop.core.order_creator._creator

# -*- coding: utf-8 -*-
# This file is part of Shoop.
#
# Copyright (c) 2012-2016, Shoop Ltd. All rights reserved.
#
# This source code is licensed under the AGPLv3 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

from decimal import Decimal

import six
from django.utils.encoding import force_text

from shoop.core.models import Order, OrderLine, OrderLineType
from shoop.core.shortcuts import update_order_line_from_product
from shoop.core.utils.users import real_user_or_none
from shoop.front.signals import order_creator_finished
from shoop.utils.numbers import bankers_round


[docs]class OrderCreator(object): def __init__(self, request): self.request = request # TODO: Get rid of `request`?
[docs] def source_line_to_order_lines(self, order, source_line): """ Convert a SourceLine into one or more OrderLines (yield them) :param order: The order :param source_line: The SourceLine """ order_line = OrderLine(order=order) product = source_line.product quantity = Decimal(source_line.quantity) if product: order_line.product = product if product.sales_unit: quantized_quantity = bankers_round(quantity, product.sales_unit.decimals) if quantized_quantity != quantity: raise ValueError("Sales unit decimal conversion causes precision loss!") else: order_line.product = None def text(value): return force_text(value) if value is not None else "" order_line.quantity = quantity order_line.supplier = source_line.supplier order_line.sku = text(source_line.sku) order_line.text = (text(source_line.text))[:192] if source_line.base_unit_price: order_line.base_unit_price = source_line.base_unit_price if source_line.discount_amount: order_line.discount_amount = source_line.discount_amount order_line.type = (source_line.type if source_line.type is not None else OrderLineType.OTHER) order_line.accounting_identifier = text(source_line.accounting_identifier) order_line.require_verification = bool(source_line.require_verification) order_line.verified = False order_line.source_line = source_line self._check_orderability(order_line) yield order_line for child_order_line in self.create_package_children(order_line): yield child_order_line
[docs] def create_package_children(self, order_line): order = order_line.order parent_product = order_line.product # :type parent_product: shoop.core.models.Product if not (parent_product and parent_product.is_package_parent()): return for child_product, child_quantity in six.iteritems(parent_product.get_package_child_to_quantity_map()): child_order_line = OrderLine(order=order, parent_line=order_line) update_order_line_from_product( pricing_context=self.request, order_line=child_order_line, product=child_product, quantity=(order_line.quantity * child_quantity), ) # Package children are free assert child_order_line.base_unit_price.value == 0 child_order_line.source_line = order_line.source_line child_order_line.supplier = order_line.supplier self._check_orderability(child_order_line) yield child_order_line
def _check_orderability(self, order_line): if not order_line.product: return if not order_line.supplier: raise ValueError("Order line has no supplier") order = order_line.order shop_product = order_line.product.get_shop_instance(order.shop) shop_product.raise_if_not_orderable( supplier=order_line.supplier, quantity=order_line.quantity, customer=order.customer )
[docs] def process_saved_order_line(self, order, order_line): """ Called in sequence for all order lines to be saved into the order. These have all been saved, so they have PKs. :type order: Order :type order_line: OrderLine """ pass
[docs] def add_lines_into_order(self, order, lines): source_line_id_to_order_line_map = {} for index, order_line in enumerate(lines): order_line.order = order order_line.ordering = index if not order_line.require_verification: order_line.verified = True order_line.save() source_line = getattr(order_line, "source_line", None) if source_line: # Stash which source line corresponds to which order line for the parentage pass below. line_id = source_line.line_id if line_id: source_line_id_to_order_line_map[line_id] = order_line # One more pass to save parent relations from the source lines for order_line in lines: source_line = getattr(order_line, "source_line", None) if source_line: parent_source_line_id = source_line.parent_line_id parent_order_line = source_line_id_to_order_line_map.get(parent_source_line_id) if parent_order_line: order_line.parent_line = parent_order_line order_line.save() self.add_line_taxes(lines) # And one last pass to call the subclass hook. for order_line in lines: self.process_saved_order_line(order=order, order_line=order_line)
[docs] def add_line_taxes(self, lines): for line in lines: for (index, line_tax) in enumerate(line.source_line.taxes, 1): line.taxes.create( tax=line_tax.tax, name=line_tax.tax.name, amount_value=line_tax.amount.value, base_amount_value=line_tax.base_amount.value, ordering=index, )
[docs] def get_source_order_lines(self, source, order): """ :type source: shoop.core.order_creator.OrderSource :type order: shoop.core.models.Order """ lines = [] source.update_from_order(order) # Since we just updated `order_provision`, we need to uncache # the processed lines. source.uncache() for line in source.get_final_lines(with_taxes=True): lines.extend(self.source_line_to_order_lines(order, line)) return lines
[docs] def create_order(self, order_source): # order_provision.target_user = self._maybe_create_user( # user=order_provision.target_user, # billing_address=order_provision.billing_address, # shipping_address=order_provision.shipping_address # ) order = Order( shop=order_source.shop, currency=order_source.currency, prices_include_tax=order_source.prices_include_tax, shipping_method=order_source.shipping_method, payment_method=order_source.payment_method, customer_comment=order_source.customer_comment, marketing_permission=bool(order_source.marketing_permission), ip_address=(self.request.META.get("REMOTE_ADDR") if self.request else None), creator=real_user_or_none(order_source.creator), orderer=(order_source.orderer or None), customer=(order_source.customer or None), billing_address=(order_source.billing_address.to_immutable() if order_source.billing_address else None), shipping_address=(order_source.shipping_address.to_immutable() if order_source.shipping_address else None), order_date=order_source.order_date, status=order_source.status, payment_data=order_source.payment_data, shipping_data=order_source.shipping_data, extra_data=order_source.extra_data ) order.save() self.process_order_before_lines(source=order_source, order=order) lines = self.get_source_order_lines(source=order_source, order=order) self.add_lines_into_order(order, lines) if any(line.require_verification for line in order.lines.all()): order.require_verification = True order.all_verified = False else: order.all_verified = True order.cache_prices() order.save() order_creator_finished.send(OrderCreator, order=order, source=order_source, request=self.request) order.save() self.process_order_after_lines(source=order_source, order=order) # Then do all the caching one more time! order.cache_prices() order.save() return order # # def assign_campaign_usages(self, order, campaign_to_code): # from shoop.shop.models.campaigns import OrderCampaignUsage # # for campaign, campaign_code in campaign_to_code.iteritems(): # OrderCampaignUsage.objects.create( # order=order, # campaign=campaign, # code=campaign_code # )
[docs] def process_order_before_lines(self, source, order): # Subclass hook pass
[docs] def process_order_after_lines(self, source, order): # Subclass hook pass