Beispiel #1
0
class UserDecimal(UserMember):

    instantiable = True
    groups_order = UserMember.groups_order
    member_class = schema.Decimal

    member_min = schema.Decimal(listed_by_default=False,
                                member_group="constraints")

    member_max = schema.Decimal(min=member_min,
                                listed_by_default=False,
                                member_group="constraints")
Beispiel #2
0
class PercentageDiscount(Discount):

    instantiable = True

    percentage = schema.Decimal(
        required = True
    )

    def apply(self, item, costs):
        costs["price"]["percentage"] -= self.percentage
Beispiel #3
0
class PriceOverride(Discount):

    instantiable = True

    price = schema.Decimal(
        required = True
    )

    def apply(self, item, costs):
        costs["price"]["cost"] = self.price
Beispiel #4
0
class PercentageTax(Tax):

    instantiable = True
        
    percentage = schema.Decimal(
        required = True
    )

    def apply(self, item, costs):
        costs["tax"]["percentage"] += self.percentage
Beispiel #5
0
class CumulativeTax(Tax):

    instantiable = True

    cost = schema.Decimal(
        required = True
    )

    def apply(self, item, costs):
        costs["tax"]["cost"] += self.cost
Beispiel #6
0
class CumulativeShippingCost(ShippingCost):

    instantiable = True

    cost = schema.Decimal(
        required = True
    )

    def apply(self, item, costs):
        costs["shipping"] += self.cost
Beispiel #7
0
class ShippingCostOverride(ShippingCost):
    
    instantiable = True
    
    cost = schema.Decimal(
        required = True
    )
    
    def apply(self, item, costs):
        costs["shipping"] = self.cost
Beispiel #8
0
class RelativeDiscount(Discount):

    instantiable = True
    
    amount = schema.Decimal(
        required = True
    )
    
    def apply(self, item, costs):
        costs["price"]["cost"] -= self.amount
Beispiel #9
0
 def schema(self):
     return schema.Schema(
         "ShopOrderCostFilter",
         members=[
             schema.String(
                 "operator",
                 required=True,
                 default="eq",
                 enumeration=self.operators,
                 text_search=False,
                 translate_value=lambda value, language=None, **kwargs: ""
                 if not value else translations(
                     "cocktail.html.UserFilterEntry operator " + value,
                     language, **kwargs)),
             schema.Boolean("include_shipping", default=False),
             schema.Boolean("include_taxes", default=False),
             schema.Boolean("include_discounts", default=False),
             schema.Decimal("value", required=True)
         ])
Beispiel #10
0
class Product(Publishable):

    instantiable = False
    view_class = None

    members_order = [
        "price",
        "categories",
        "entries"
    ]

    default_controller = schema.DynamicDefault(
        lambda: Controller.get_instance(qname = "woost.product_controller")
    )

    price = schema.Decimal(
        required = True,
        default = Decimal("0")
    )
    
    categories = schema.Collection(
        items = "woost.extensions.shop.productcategory.ProductCategory",
        bidirectional = True
    )

    entries = schema.Collection(
        items = "woost.extensions.shop.shoporderentry.ShopOrderEntry",
        bidirectional = True,
        visible = False,
        block_delete = True
    )

    def discounts(self):
        """Returns the discounts that can be applied to the product.        
        @rtype: L{Product<woost.extensions.shop.product.Product>}
        """
        from woost.extensions.shop import ShopExtension
        return [discount
                for discount in ShopExtension.instance.discounts
                if discount.applies_to(self)]
Beispiel #11
0
class ShopOrderEntry(Item):

    listed_from_root = False

    members_order = [
        "shop_order",
        "product",
        "quantity"
    ]

    shop_order = schema.Reference(
        type = "woost.extensions.shop.shoporder.ShopOrder",
        bidirectional = True,
        required = True
    )

    product = schema.Reference(
        type = "woost.extensions.shop.product.Product",
        bidirectional = True,
        required = True
    )

    quantity = schema.Integer(
        required = True,
        min = 1
    )

    cost = schema.Decimal(
        required = True,
        default = Decimal("0"),
        editable = False
    )

    def __translate__(self, language, **kwargs):
        return "%s (%d)" % (
            translations(self.product, language),
            self.quantity
        )
Beispiel #12
0
class ECommerceProduct(Publishable):

    instantiable = False

    members_order = [
        "description",
        "price",
        "weight",
        "attachments",
        "purchase_model",
        "purchases",
        "template"
    ]

    default_controller = schema.DynamicDefault(
        lambda: Controller.get_instance(
            qname = "woost.extensions.ecommerce.product_controller"
        )
    )

    description = schema.String(
        translated = True,
        edit_control = "woost.views.RichTextEditor",
        member_group = "product_data"
    )

    price = schema.Decimal(
        required = True,
        default = Decimal("0"),
        member_group = "product_data"
    )
 
    weight = schema.Decimal(
        translate_value = lambda value, language = None, **kwargs:
            "" if not value else "%s Kg" % translations(value, language),
        member_group = "product_data"
    )

    attachments = schema.Collection(
        items = schema.Reference(type = File),
        related_end = schema.Collection(),
        member_group = "product_data"
    )

    purchase_model = schema.Reference(
        class_family = "woost.extensions.ecommerce.ecommercepurchase."
                       "ECommercePurchase",
        default = schema.DynamicDefault(
            lambda: ECommerceProduct.purchase_model.class_family
        ),
        required = True,
        searchable = False,
        member_group = "product_data"
    )

    purchases = schema.Collection(
        items = "woost.extensions.ecommerce.ecommercepurchase."
                "ECommercePurchase",
        bidirectional = True,
        visible = False,
        member_group = "product_data"
    )

    template = schema.Reference(
        type = Template,
        related_end = schema.Collection(),
        default = schema.DynamicDefault(
            lambda: Template.get_instance(
                qname = "woost.extensions.ecommerce.product_template"
            )
        ),
        member_group = "presentation"
    )

    def get_image(self):
        for attachment in self.attachments:
            if attachment.resource_type == "image" \
            and attachment.is_accessible():
                return attachment

    def offers(self):
        from woost.extensions.ecommerce import ECommerceExtension
        for pricing in ECommerceExtension.instance.pricing:
            if not pricing.hidden and pricing.applies_to(self):
                yield pricing

    @getter
    def inherited_resources(self):

        if self.inherit_resources and self.parent is None:
            catalog = Document.get_instance(
                qname = "woost.extensions.ecommerce.catalog_page"
            )

            if catalog:
                for resource in catalog.inherited_resources:
                    yield resource

                for resource in catalog.branch_resources:
                    yield resource        
        else:
            for resource in Publishable.inherited_resources.__get__(self):
                yield resource
Beispiel #13
0
                                      required=True,
                                      default=True,
                                      indexed=True,
                                      member_group="sitemap",
                                      listed_by_default=False),
                       append=True)

URI.default_sitemap_indexable = False

Publishable.add_member(schema.String(
    "sitemap_change_frequency",
    enumeration=[
        "always", "hourly", "daily", "weekly", "monthly", "yearly", "never"
    ],
    translate_value=lambda value, language=None, **kwargs: ""
    if not value else translations(
        "woost.extensions.sitemap.change_frequency " + value, language, **
        kwargs),
    member_group="sitemap",
    text_search=False,
    listed_by_default=False),
                       append=True)

Publishable.add_member(schema.Decimal("sitemap_priority",
                                      default=Decimal("0.5"),
                                      min=0,
                                      max=1,
                                      listed_by_default=False,
                                      member_group="sitemap"),
                       append=True)
Beispiel #14
0
class ECommerceOrder(Item):

    payment_types_completed_status = {
        "payment_gateway": "accepted",
        "transfer": "payment_pending",
        "cash_on_delivery": "payment_pending"
    }

    incoming = Event(doc="""
        An event triggered when a new order is received.
        """)

    completed = Event(doc="""
        An event triggered when an order is completed.
        """)

    groups_order = ["shipping_info", "billing"]

    members_order = [
        "customer", "address", "town", "region", "country", "postal_code",
        "language", "status", "purchases", "payment_type", "total_price",
        "pricing", "total_shipping_costs", "shipping_costs", "total_taxes",
        "taxes", "total"
    ]

    customer = schema.Reference(
        type=User,
        related_end=schema.Collection(),
        required=True,
        default=schema.DynamicDefault(get_current_user))

    address = schema.String(member_group="shipping_info",
                            required=True,
                            listed_by_default=False)

    town = schema.String(member_group="shipping_info",
                         required=True,
                         listed_by_default=False)

    region = schema.String(member_group="shipping_info",
                           required=True,
                           listed_by_default=False)

    country = schema.Reference(
        member_group="shipping_info",
        type=Location,
        relation_constraints=[Location.location_type.equal("country")],
        default_order="location_name",
        related_end=schema.Collection(),
        required=True,
        listed_by_default=False,
        user_filter="cocktail.controllers.userfilter.MultipleChoiceFilter")

    postal_code = schema.String(member_group="shipping_info",
                                required=True,
                                listed_by_default=False)

    language = schema.String(
        required=True,
        format="^[a-z]{2}$",
        editable=False,
        default=schema.DynamicDefault(get_language),
        text_search=False,
        translate_value=lambda value, language=None, **kwargs: u""
        if not value else translations(value, language, **kwargs))

    status = schema.String(
        required=True,
        indexed=True,
        enumeration=[
            "shopping", "payment_pending", "accepted", "failed", "refund"
        ],
        default="shopping",
        text_search=False,
        translate_value=lambda value, language=None, **kwargs: u""
        if not value else translations("ECommerceOrder.status-" + value,
                                       language, **kwargs))

    purchases = schema.Collection(
        items="woost.extensions.ecommerce.ecommercepurchase."
        "ECommercePurchase",
        integral=True,
        bidirectional=True,
        min=1)

    payment_type = schema.String(
        member_group="billing",
        required=True,
        translate_value=lambda value, language=None, **kwargs: translations(
            "ECommerceOrder.payment_type-%s" % value, language=language),
        default=schema.DynamicDefault(_get_default_payment_type),
        text_search=False,
        edit_control="cocktail.html.RadioSelector",
        listed_by_default=False)

    total_price = schema.Decimal(member_group="billing",
                                 editable=False,
                                 listed_by_default=False,
                                 translate_value=_translate_amount)

    pricing = schema.Collection(
        member_group="billing",
        items=schema.Reference(type=ECommerceBillingConcept),
        related_end=schema.Collection(block_delete=True),
        editable=False)

    total_shipping_costs = schema.Decimal(member_group="billing",
                                          editable=False,
                                          listed_by_default=False,
                                          translate_value=_translate_amount)

    shipping_costs = schema.Collection(
        member_group="billing",
        items=schema.Reference(type=ECommerceBillingConcept),
        related_end=schema.Collection(block_delete=True),
        editable=False)

    total_taxes = schema.Decimal(member_group="billing",
                                 editable=False,
                                 listed_by_default=False,
                                 translate_value=_translate_amount)

    taxes = schema.Collection(
        member_group="billing",
        items=schema.Reference(type=ECommerceBillingConcept),
        related_end=schema.Collection(block_delete=True),
        editable=False)

    total = schema.Decimal(member_group="billing",
                           editable=False,
                           translate_value=_translate_amount)

    def calculate_cost(self,
                       apply_pricing=True,
                       apply_shipping_costs=True,
                       apply_taxes=True):
        """Calculates the costs for the order.
        :rtype: dict
        """
        from woost.extensions.ecommerce import ECommerceExtension
        extension = ECommerceExtension.instance

        order_costs = {
            "price": {
                "cost": Decimal("0.00"),
                "percentage": Decimal("0.00"),
                "concepts": []
            },
            "shipping_costs": {
                "cost": Decimal("0.00"),
                "percentage": Decimal("0.00"),
                "concepts": []
            },
            "taxes": {
                "cost": Decimal("0.00"),
                "percentage": Decimal("0.00"),
                "concepts": []
            },
            "purchases": {}
        }

        # Per purchase costs:
        for purchase in self.purchases:
            purchase_costs = purchase.calculate_costs(
                apply_pricing=apply_pricing,
                apply_shipping_costs=apply_shipping_costs,
                apply_taxes=apply_taxes)
            order_costs["purchases"][purchase] = purchase_costs

            order_costs["price"]["cost"] += purchase_costs["price"]["total"]
            order_costs["shipping_costs"]["cost"] += \
                purchase_costs["shipping_costs"]["total"]
            order_costs["taxes"]["cost"] += purchase_costs["taxes"]["total"]

        # Order price
        order_price = order_costs["price"]

        if apply_pricing:
            for pricing in extension.pricing:
                if pricing.applies_to(self):
                    pricing.apply(self, order_price)

        order_price["cost"] += \
            order_price["cost"] * order_price["percentage"] / 100

        order_price["total"] = order_price["cost"]

        # Order shipping costs
        order_shipping_costs = order_costs["shipping_costs"]

        if apply_shipping_costs:
            for shipping_cost in extension.shipping_costs:
                if shipping_cost.applies_to(self):
                    shipping_cost.apply(self, order_shipping_costs)

        order_shipping_costs["total"] = (
            order_shipping_costs["cost"] +
            order_price["total"] * order_shipping_costs["percentage"] / 100)

        # Order taxes
        order_taxes = order_costs["taxes"]

        if apply_taxes:
            for tax in extension.taxes:
                if tax.applies_to(self):
                    tax.apply(self, order_taxes)

        order_taxes["total"] = (
            order_taxes["cost"] +
            order_price["total"] * order_taxes["percentage"] / 100)

        # Total
        order_costs["total"] = (order_price["total"] +
                                order_shipping_costs["total"] +
                                order_taxes["total"])

        return order_costs

    def update_cost(self,
                    apply_pricing=True,
                    apply_shipping_costs=True,
                    apply_taxes=True):
        costs = self.calculate_cost(apply_pricing=apply_pricing,
                                    apply_shipping_costs=apply_shipping_costs,
                                    apply_taxes=apply_taxes)

        self.total_price = costs["price"]["total"]
        self.pricing = list(costs["price"]["concepts"])

        self.total_shipping_costs = costs["shipping_costs"]["total"]
        self.shipping_costs = list(costs["shipping_costs"]["concepts"])

        self.total_taxes = costs["taxes"]["total"]
        self.taxes = list(costs["taxes"]["concepts"])

        self.total = costs["total"]

        for purchase, purchase_costs in costs["purchases"].iteritems():
            purchase.total_price = purchase_costs["price"]["total"]
            purchase.pricing = list(purchase_costs["price"]["concepts"])
            self.pricing.extend(purchase.pricing)

            purchase.total_shipping_costs = \
                purchase_costs["shipping_costs"]["total"]
            purchase.shipping_costs = \
                list(purchase_costs["shipping_costs"]["concepts"])
            self.shipping_costs.extend(purchase.shipping_costs)

            purchase.total_taxes = purchase_costs["taxes"]["total"]
            purchase.taxes = list(purchase_costs["taxes"]["concepts"])
            self.taxes.extend(purchase.taxes)

            purchase.total = purchase_costs["total"]

    def count_units(self):
        return sum(purchase.quantity for purchase in self.purchases)

    def get_weight(self):
        return sum(purchase.get_weight() for purchase in self.purchases)

    def add_purchase(self, purchase):
        for order_purchase in self.purchases:
            if order_purchase.__class__ is purchase.__class__ \
            and order_purchase.product is purchase.product \
            and all(
                order_purchase.get(option) == purchase.get(option)
                for option in purchase.get_options()
                if option.name != "quantity"
            ):
                order_purchase.quantity += purchase.quantity
                purchase.product = None
                if purchase.is_inserted:
                    purchase.delete()
                break
        else:
            self.purchases.append(purchase)

    @classmethod
    def get_public_schema(cls):
        public_schema = schema.Schema("OrderCheckoutSummary")
        cls.get_public_adapter().export_schema(cls, public_schema)

        payment_type = public_schema.get_member("payment_type")
        if payment_type:
            payments = PaymentsExtension.instance

            if payments.enabled and payments.payment_gateway:

                translate_value = payment_type.translate_value

                def payment_type_translate_value(value,
                                                 language=None,
                                                 **kwargs):
                    if value == "payment_gateway":
                        return payments.payment_gateway.label
                    else:
                        return translate_value(value,
                                               language=language,
                                               **kwargs)

                payment_type.translate_value = payment_type_translate_value

        return public_schema

    @classmethod
    def get_public_adapter(cls):
        from woost.extensions.ecommerce import ECommerceExtension

        user = get_current_user()
        adapter = schema.Adapter()
        adapter.exclude(["customer", "status", "purchases"])
        adapter.exclude([
            member.name for member in cls.members().itervalues()
            if not member.visible or not member.editable
            or not issubclass(member.schema, ECommerceOrder)
            or not user.has_permission(ModifyMemberPermission, member=member)
        ])
        if len(ECommerceExtension.instance.payment_types) == 1:
            adapter.exclude(["payment_type"])
        return adapter

    @property
    def is_completed(self):
        return self.status \
        and self.status == self.payment_types_completed_status.get(
            self.payment_type
        )

    @event_handler
    def handle_changed(cls, event):

        item = event.source
        member = event.member

        if member.name == "status":

            if event.previous_value == "shopping" \
            and event.value in ("payment_pending", "accepted"):
                item.incoming()

            if item.is_completed:
                item.completed()

    def get_description_for_gateway(self):
        site_name = Configuration.instance.get_setting("site_name")
        if site_name:
            return translations(
                "woost.extensions.ECommerceOrder description for gateway"
            ) % site_name
        else:
            return translations(self)
@author:		Martí Congost
@contact:		[email protected]
@organization:	Whads/Accent SL
@since:			February 2010
"""
from decimal import Decimal
from cocktail.translations import translations
from cocktail import schema
from woost.models import Publishable, URI

translations.load_bundle("woost.extensions.sitemap.publishable")

URI.default_sitemap_indexable = False

Publishable.add_member(schema.String("x_sitemap_change_frequency",
                                     enumeration=[
                                         "always", "hourly", "daily", "weekly",
                                         "monthly", "yearly", "never"
                                     ],
                                     member_group="meta.robots",
                                     text_search=False,
                                     listed_by_default=False),
                       append=True)

Publishable.add_member(schema.Decimal("x_sitemap_priority",
                                      min=0,
                                      max=1,
                                      listed_by_default=False,
                                      member_group="meta.robots"),
                       append=True)
Beispiel #16
0
class ShopOrder(Item):

    members_order = [
        "address", "town", "region", "country", "postal_code", "cost",
        "entries"
    ]

    address = schema.String(member_group="shipping_info",
                            required=True,
                            listed_by_default=False)

    town = schema.String(member_group="shipping_info",
                         required=True,
                         listed_by_default=False)

    region = schema.String(member_group="shipping_info",
                           required=True,
                           listed_by_default=False)

    country = schema.Reference(
        member_group="shipping_info",
        type=Country,
        related_end=schema.Collection(),
        required=True,
        listed_by_default=False,
        user_filter="cocktail.controllers.userfilter.MultipleChoiceFilter")

    postal_code = schema.String(member_group="shipping_info",
                                required=True,
                                listed_by_default=False)

    cost = schema.Decimal(required=True, default=Decimal("0"), editable=False)

    language = schema.String(
        required=True,
        format="^[a-z]{2}$",
        editable=False,
        default=schema.DynamicDefault(get_language),
        text_search=False,
        translate_value=lambda value, language=None, **kwargs: u""
        if not value else translations(value, language, **kwargs))

    status = schema.String(
        required=True,
        indexed=True,
        enumeration=["pending", "accepted", "failed"],
        default="pending",
        text_search=False,
        translate_value=lambda value, language=None, **kwargs: u""
        if not value else translations(
            "woost.extensions.shop.ShopOrder.status " + value, language, **
            kwargs))

    entries = schema.Collection(
        items="woost.extensions.shop.shoporderentry.ShopOrderEntry",
        integral=True,
        bidirectional=True,
        min=1)

    def calculate_cost(self,
                       include_shipping=True,
                       include_taxes=True,
                       include_discounts=True):
        """Calculates the costs for the order.
        @rtype: dict
        """
        costs = {
            "pricing_policies": [],
            "price": {
                "cost": 0,
                "percentage": 0,
                "total": None
            },
            "shipping":
            0,
            "tax": {
                "cost": 0,
                "percentage": 0
            },
            "entries": [{
                "pricing_policies": [],
                "quantity": entry.quantity,
                "paid_quantity": entry.quantity,
                "price": {
                    "cost": entry.product.price,
                    "percentage": 0
                },
                "shipping": 0,
                "tax": {
                    "cost": 0,
                    "percentage": 0
                }
            } for entry in self.entries]
        }

        from woost.extensions.shop import ShopExtension
        shop_ext = ShopExtension.instance

        policies = list()

        if include_discounts:
            policies.extend(shop_ext.discounts)

        if include_shipping:
            policies.extend(shop_ext.shipping_costs)

        if include_taxes:
            policies.extend(shop_ext.taxes)

        for pricing_policy in policies:
            matching_items = pricing_policy.select_matching_items()

            if issubclass(matching_items.type, ShopOrder):
                if pricing_policy.applies_to(self):
                    pricing_policy.apply(self, costs)
                    costs["pricing_policies"].append(pricing_policy)
            else:
                for entry, entry_costs in zip(self.entries, costs["entries"]):
                    if pricing_policy.applies_to(entry.product):
                        pricing_policy.apply(entry.product, entry_costs)
                        entry_costs["pricing_policies"].append(pricing_policy)

        # Total price
        def apply_percentage(costs):
            cost = costs["cost"]
            percentage = costs["percentage"]
            if percentage:
                cost += cost * percentage / 100
            costs["total"] = cost
            return cost

        total_price = apply_percentage(costs["price"])

        for entry_costs in costs["entries"]:
            entry_price = apply_percentage(entry_costs["price"])
            entry_total_price = entry_price * entry_costs["paid_quantity"]
            entry_costs["total_price"] = entry_total_price
            total_price += entry_total_price

        costs["total_price"] = total_price

        # Total taxes
        total_taxes = costs["tax"]["cost"] \
                    + total_price * costs["tax"]["percentage"] / 100

        for entry_costs in costs["entries"]:
            quantity = entry_costs["paid_quantity"]
            entry_price = entry_costs["price"]["total"] * quantity
            entry_taxes = entry_costs["tax"]["cost"] * quantity \
                        + entry_price * entry_costs["tax"]["percentage"] / 100
            total_taxes += entry_taxes
            entry_costs["tax"]["total"] = entry_taxes

        costs["total_taxes"] = total_taxes

        # Total shipping costs
        total_shipping_costs = costs["shipping"] \
                             + sum(entry_costs["shipping"] * entry_costs["quantity"]
                                   for entry_costs in costs["entries"])
        costs["total_shipping_costs"] = total_shipping_costs

        # Grand total
        costs["total"] = total_price + total_taxes + total_shipping_costs

        return costs

    def count_items(self):
        """Gets the number of purchased product units in the order.
        @rtype: int
        """
        return sum(entry.quantity for entry in self.entries)

    def get_product_entry(self, product):
        """Gets the entry in the order for the given product.

        @param product: The product to obtain the entry for.
        @type product: L{Product<woost.extensions.shop.product.Product>}

        @return: The matching entry, or None if the order doesn't contain an
            entry for the indicated product.
        @rtype: L{ShopOrderEntry
                  <woost.extensions.shop.shoporderentry.ShopOrderEntry>}
        """
        for entry in self.entries:
            if entry.product is product:
                return entry

    def set_product_quantity(self, product, quantity):
        """Updates the quantity of ordered units for the indicated product.

        If an entry for the given product already exists, its quantity will be
        updated to the indicated value. If the indicated quantity is zero, the
        entry will be removed from the order. If no matching entry exists, a
        new entry for the product will be created with the specified amount of
        units.

        @param product: The product to set the quantity for.
        @type product: L{Product<woost.extensions.shop.product.Product>}

        @param quantity: The number of units of the product to order.
        @type quantity: int
        """
        entry = self.get_product_entry(product)

        if entry is None:
            if quantity:
                entry = ShopOrderEntry(product=product, quantity=quantity)
                self.entries.append(entry)
                if self.is_inserted:
                    entry.insert()
        else:
            if quantity:
                entry.quantity = quantity
            else:
                if entry.is_inserted:
                    entry.delete()
                else:
                    self.entries.remove(entry)
Beispiel #17
0
class ECommercePurchase(Item):

    listed_from_root = False

    members_order = [
        "order", "product", "quantity", "total_price", "pricing",
        "total_shipping_costs", "shipping_costs", "total_taxes", "taxes",
        "total"
    ]

    order = schema.Reference(
        type="woost.extensions.ecommerce.ecommerceorder.ECommerceOrder",
        bidirectional=True,
        required=True)

    product = schema.Reference(
        type="woost.extensions.ecommerce.ecommerceproduct.ECommerceProduct",
        bidirectional=True,
        required=True)

    quantity = schema.Integer(required=True, min=1, default=1)

    total_price = schema.Decimal(member_group="billing",
                                 editable=False,
                                 listed_by_default=False)

    pricing = schema.Collection(
        member_group="billing",
        items=schema.Reference(type=ECommerceBillingConcept),
        related_end=schema.Collection(block_delete=True),
        editable=False)

    total_shipping_costs = schema.Decimal(member_group="billing",
                                          editable=False,
                                          listed_by_default=False)

    shipping_costs = schema.Collection(
        member_group="billing",
        items=schema.Reference(type=ECommerceBillingConcept),
        related_end=schema.Collection(block_delete=True),
        editable=False)

    total_taxes = schema.Decimal(member_group="billing",
                                 editable=False,
                                 listed_by_default=False)

    taxes = schema.Collection(
        member_group="billing",
        items=schema.Reference(type=ECommerceBillingConcept),
        related_end=schema.Collection(block_delete=True),
        editable=False)

    total = schema.Decimal(member_group="billing", editable=False)

    def __translate__(self, language, **kwargs):

        desc = u"%d x %s" % (self.quantity, translations(
            self.product, language))

        options = []
        for member in self.get_options():
            if member is ECommercePurchase.quantity:
                continue
            options.append(
                "%s: %s" %
                (translations(member, language),
                 member.translate_value(self.get(member), language)))

        if options:
            desc += u" (%s)" % u", ".join(options)

        return desc

    def calculate_costs(self,
                        apply_pricing=True,
                        apply_shipping_costs=True,
                        apply_taxes=True):
        from woost.extensions.ecommerce import ECommerceExtension
        extension = ECommerceExtension.instance

        purchase_costs = {
            "price": {
                "cost": self.get_unit_price(),
                "paid_units": self.quantity,
                "percentage": Decimal("0.00"),
                "concepts": []
            },
            "shipping_costs": {
                "cost": Decimal("0.00"),
                "paid_units": self.quantity,
                "percentage": Decimal("0.00"),
                "concepts": []
            },
            "taxes": {
                "cost": Decimal("0.00"),
                "paid_units": self.quantity,
                "percentage": Decimal("0.00"),
                "concepts": []
            }
        }

        # Price
        purchase_price = purchase_costs["price"]

        if apply_pricing:
            for pricing in extension.pricing:
                if pricing.applies_to(self, purchase_costs):
                    pricing.apply(self, purchase_price)

        purchase_price["cost"] += \
            purchase_price["cost"] * purchase_price["percentage"] / 100

        purchase_price["total"] = \
            purchase_price["cost"] * purchase_price["paid_units"]

        # Shipping costs
        purchase_shipping_costs = purchase_costs["shipping_costs"]

        if apply_shipping_costs:
            for shipping_cost in extension.shipping_costs:
                if shipping_cost.applies_to(self, purchase_costs):
                    shipping_cost.apply(self, purchase_shipping_costs)

        purchase_shipping_costs["cost"] += \
            purchase_price["cost"] * purchase_shipping_costs["percentage"] / 100

        purchase_shipping_costs["total"] = \
            purchase_shipping_costs["cost"] * purchase_shipping_costs["paid_units"]

        # Taxes
        purchase_taxes = purchase_costs["taxes"]

        if apply_taxes:
            for tax in extension.taxes:
                if tax.applies_to(self, purchase_costs):
                    tax.apply(self, purchase_taxes)

        purchase_taxes["cost"] += \
            purchase_price["cost"] * purchase_taxes["percentage"] / 100

        purchase_taxes["total"] = \
            purchase_taxes["cost"] * purchase_taxes["paid_units"]

        # Total
        purchase_costs["total"] = (purchase_price["total"] +
                                   purchase_shipping_costs["total"] +
                                   purchase_taxes["total"])
        return purchase_costs

    def get_unit_price(self):
        return self.product.price

    def get_weight(self):
        if self.product is None or self.product.weight is None:
            return 0
        else:
            return self.quantity * self.product.weight

    @classmethod
    def get_options(cls):
        for member in cls.members().itervalues():
            if (member is not cls.product and member is not cls.order
                    and member.visible and member.editable
                    and issubclass(member.schema, ECommercePurchase)
                    and get_current_user().has_permission(
                        ModifyMemberPermission, member=member)):
                yield member