work in progress: orders and order items

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3110 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2009-01-03 09:24:14 +00:00
parent 2eba0d27a3
commit e7fb4cd238
7 changed files with 266 additions and 38 deletions

View file

@ -4,6 +4,8 @@ Commerce: Shope, Products, Customers, Orders, ...
($Id$)
>>> from zope import component
>>> from cybertools.commerce.manager import Manager
>>> manager = Manager()
@ -42,6 +44,13 @@ it belongs to.
>>> sorted((s.name, s.title) for s in p003.shops)
[(u'shop1', u'PC up Ltd'), (u'shop2', u'Video up Ltd')]
We can also create a manufacturer and set it for a product.
>>> mf001 = manager.manufacturers.create(u'001', title=u'Global Electronics')
>>> p001.manufacturer = mf001
>>> [p.title for p in mf001.products]
[u'Silent Case']
Customers
=========
@ -66,6 +75,18 @@ Customers
[(u'shop1', u'PC up Ltd'), (u'shop2', u'Video up Ltd')]
Orders
======
Carts and Orders
================
>>> from cybertools.commerce.order import OrderItems
>>> component.provideAdapter(OrderItems)
>>> orderItems = manager.orderItems
>>> orderItems.add(p001, c001, quantity=3)
<Track [..., 1, ..., '... ...']: {'quantity': 3}>
Orders
------
>>> ord001 = manager.orders.create(u'001', shop=shop1, customer=c001)

View file

@ -51,22 +51,62 @@ class ContainerAttribute(object):
class RelationSet(object):
def __init__(self, parent, attributeName):
def __init__(self, parent, attributeName=None):
self.parent = parent
self.attributeName = attributeName
self.data = {}
def add(self, related):
self.data[related.name] = related
relatedData = getattr(related, self.attributeName).data
if self.attributeName:
value = getattr(related, self.attributeName)
if isinstance(value, RelationSet):
relatedData = value.data
relatedData[self.parent.name] = self.parent
else:
setattr(related, self.attributeName, self.parent)
def remove(self, related):
name = related.name
del self.data[name]
relatedData = getattr(related, self.attributeName).data
if self.attributeName:
value = getattr(related, self.attributeName)
if isinstance(value, RelationSet):
relatedData = value.data
del relatedData[self.parent.name]
else:
setattr(related, self.attributeName, None)
def __iter__(self):
for obj in self.data.values():
yield obj
class Relation(object):
def __init__(self, name, otherName):
self.name = name
self.otherName = otherName
def __get__(self, inst, class_=None):
if inst is None:
return self
return getattr(inst, self.name)
def __set__(self, inst, value):
existing = getattr(inst, self.name, None)
if existing is not None:
other = getattr(existing, self.otherName).data
for k, v in other.items():
if v != inst:
del other[k]
if value is not None:
other = getattr(value, self.otherName).data
other[inst.name] = inst
setattr(inst, self.name, value)
class BaseObject(object):
collection = RelationSet

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -24,21 +24,20 @@ $Id$
from zope.interface import implements, Interface
from cybertools.commerce.common import RelationSet
from cybertools.commerce.common import RelationSet, BaseObject
from cybertools.commerce.interfaces import ICustomer, IAddress
class Customer(object):
class Customer(BaseObject):
implements(ICustomer)
collection = RelationSet
def __init__(self, customerId, title=None, client=None):
self.name = self.customerId = customerId
self.title = title or u'unknown'
self.client = client
self.shops = self.collection(self, 'customers')
self.orders = self.collection(self, 'customer')
class Address(object):

View file

@ -1,5 +1,6 @@
#-*- coding: UTF-8 -*-
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -29,6 +30,7 @@ from zope.i18nmessageid import MessageFactory
from cybertools.util.jeep import Jeep, Term
from cybertools.organize.interfaces import IAddress as IBaseAddress
from cybertools.organize.interfaces import IPerson as IBasePerson
from loops import util
_ = MessageFactory('cybertools.commerce')
@ -41,12 +43,15 @@ class IManager(Interface):
all components of a commerce site.
"""
shops = Attribute('All shops in this commerce manager.')
products = Attribute('All products in this commerce manager.')
categories = Attribute('All product categories in this commerce manager.')
manufacturers = Attribute('All manufacturers in this commerce manager.')
suppliers = Attribute('All suppliers in this commerce manager.')
customers = Attribute('All customers in this commerce manager.')
shops = Attribute(u'All shops in this commerce manager.')
products = Attribute(u'All products in this commerce manager.')
categories = Attribute(u'All product categories in this commerce manager.')
manufacturers = Attribute(u'All manufacturers in this commerce manager.')
suppliers = Attribute(u'All suppliers in this commerce manager.')
customers = Attribute(u'All customers in this commerce manager.')
orders = Attribute(u'All orders in this commerce manager.')
orderItems = Attribute(u'All order items; may also be cart items without '
u'relation to an existing order.')
# shops
@ -179,6 +184,11 @@ class ICategory(Interface):
""" A product category.
"""
name = schema.ASCIILine(
title=_(u'Category Identifier'),
description=_(u'An internal name uniquely identifying the category.'),
default='',
required=True)
title = schema.TextLine(
title=_(u'Title'),
description=_(u'Short title of the category.'),
@ -203,6 +213,7 @@ class ICategory(Interface):
products = Attribute(u'The products belonging to this category.')
subcategories = Attribute(u'The sub-categories belonging to this category.')
parentCategories = Attribute(u'The parent categories belonging to this category.')
shops = Attribute(u'The shops providing this category.')
accessories = Attribute(u'Accessories for this category.')
@ -210,8 +221,8 @@ class ICategory(Interface):
# customers
class ICustomer(Interface):
""" Typically a role of - for example - a person or institution (the ``client``)
representing a customer of one or more shops.
""" Typically a role instance of - for example - a person or institution
(the ``client``) representing a customer of one or more shops.
The client may be None in which case the customer is an object on
its own.
@ -229,16 +240,23 @@ class ICustomer(Interface):
required=True)
description = schema.Text(
title=_(u'Description'),
description=_(u'A medium-length description of the category.'),
description=_(u'A medium-length description of the customer.'),
default=u'',
missing_value=u'',
required=False)
shops = Attribute(u'The shops the client object is a customer of.')
paymentTypes = Attribute(u'A collection of payment types supported.')
orders = Attribute(u'A collection of the customer\'s orders.')
client = Attribute(u'An optional (real) client object of the customer role.')
class IPerson(IBasePerson):
customer = Attribute(u'The customer the person belongs to.')
addressTypesVoc = util.KeywordVocabulary((
('standard', _(u'Standard Address')),
('invoice', _(u'Invoice Address')),
@ -256,9 +274,42 @@ class IAddress(IBaseAddress):
default='standard',
required=False)
clients = Attribute(u'A collection of objects that this address belongs to.')
# orders
valueTypesVoc = util.KeywordVocabulary((
('product', _(u'Product Prices')),
('shipping', _(u'Shipping Cost')),
))
vatRatesVoc = util.KeywordVocabulary((
('product', 0),
('reduced', 7),
('standard', 19),
))
currencyVoc = util.KeywordVocabulary((
('EUR', u''),
('USD', u'$'),
))
class IValue(Interface):
""" An order or item value with additional information about the type of
the value and e.g. the VAT rate.
"""
type = Attribute(u'The type of the value; see valueTypesVoc.')
value = Attribute(u'The value in the standard currency (EUR), '
u'stored as a Decimal value.')
currency = Attribute(u'The currency of the value.')
currencyRate = Attribute(u'The rate for converting the value to the '
u'standard currency, default is 1.')
vat = Attribute(u'The id of the VAT rate; see vatRatesVoc.')
class IOrder(Interface):
"""
"""
@ -266,10 +317,16 @@ class IOrder(Interface):
orderId = Attribute(u'Order Identifier')
shop = Attribute(u'The shop this order belongs to.')
customer = Attribute(u'The customer issuing this order.')
invoiceAddress = Attribute(u'The address the invoice should be sent to.')
shippingAddress = Attribute(u'The address the products should be sent to.')
paymentType = Attribute(u'The payment type to be used for the order.')
netValues = Attribute(u'A collection of net total values (IValue objects)'
u'of the order.')
total = Attribute(u'The total gross value (Decimal) of the order.')
class IOrderItem(Interface):
"""
""" An individual order or cart item.
"""
quantity = schema.Int(
@ -278,7 +335,14 @@ class IOrderItem(Interface):
default=1,
required=True)
order = Attribute(u'Order this order item belongs to.')
order = Attribute(u'The order this order item belongs to.')
product = Attribute(u'The product represented by this order item.')
unitPrice = Attribute(u'The basic unit price for one of the product '
u'items ordered.')
fullPrice = Attribute(u'The full price for the quantity ordered.')
class IOrderItems(Interface):
""" A collection of order items.
"""

View file

@ -26,9 +26,11 @@ from zope.interface import implements
from cybertools.commerce.common import ContainerAttribute
from cybertools.commerce.customer import Customer
from cybertools.commerce.interfaces import IManager
from cybertools.commerce.product import Product
from cybertools.commerce.interfaces import IManager, IOrderItems
from cybertools.commerce.product import Product, Category, Manufacturer, Supplier
from cybertools.commerce.order import Order, OrderItem
from cybertools.commerce.shop import Shop
from cybertools.tracking.btree import TrackingStorage
class Manager(object):
@ -38,4 +40,15 @@ class Manager(object):
def __init__(self):
self.shops = ContainerAttribute(Shop)
self.products = ContainerAttribute(Product, 'productId')
self.categories = ContainerAttribute(Category, 'name')
self.manufacturers = ContainerAttribute(Manufacturer, 'name')
self.suppliers = ContainerAttribute(Supplier, 'name')
self.customers = ContainerAttribute(Customer, 'customerId')
self.orders = ContainerAttribute(Order, 'orderId')
self._orderItems = TrackingStorage(trackFactory=OrderItem)
@property
def orderItems(self):
return IOrderItems(self._orderItems)

84
commerce/order.py Normal file
View file

@ -0,0 +1,84 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Order and order item classes.
$Id$
"""
from zope.component import adapts
from zope.interface import implements, Interface
from cybertools.commerce.common import Relation, BaseObject
from cybertools.commerce.interfaces import IOrder, IOrderItem, IOrderItems
from cybertools.tracking.btree import Track
from cybertools.tracking.interfaces import ITrackingStorage
class Order(object):
implements(IOrder)
customer = Relation('_customer', 'orders')
def __init__(self, orderId, shop=None, customer=None):
self.name = self.orderId = orderId
self.shop = shop
self.customer = customer
class OrderItem(Track):
implements(IOrderItem)
def __getattr__(self, attr):
if attr not in IOrderItem:
raise AttributeError(attr)
return self.data.get(attr)
class OrderItems(object):
""" A tracking storage adapter managing order items.
"""
implements(IOrderItems)
adapts(ITrackingStorage)
def __init__(self, context):
self.context = context
def __getitem__(self, key):
return self.context[key]
def __iter__(self):
return iter(self.context.values())
def query(self, **criteria):
if 'product' in criteria:
criteria['taskId'] = criteria.pop('product')
if 'person' in criteria:
criteria['userName'] = criteria.pop('person')
if 'run' in criteria:
criteria['runId'] = criteria.pop('run')
return self.context.query(**criteria)
def add(self, product, person, run=0, **kw):
trackId = self.context.saveUserTrack(product, run, person, kw)
track = self[trackId]
return track

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -24,46 +24,53 @@ $Id$
from zope.interface import implements, Interface
from cybertools.commerce.common import RelationSet
from cybertools.commerce.common import Relation, RelationSet, BaseObject
from cybertools.commerce.interfaces import IProduct, ICategory
from cybertools.commerce.interfaces import IManufacturer, ISupplier
class Product(object):
class Product(BaseObject):
implements(IProduct)
collection = RelationSet
manufacturer = Relation('_manufacturer', 'products')
def __init__(self, productId, title=None):
self.name = self.productId = productId
self.title = title or u'unknown'
self.shops = self.collection(self, 'products')
self.categories = self.collection(self, 'products')
self.suppliers = self.collection(self, 'products')
class Category(object):
class Category(BaseObject):
implements(ICategory)
collection = RelationSet
def __init__(self, title=None):
def __init__(self, name, title=None):
self.name = name
self.title = title or u'unknown'
self.shops = self.collection(self, 'categories')
self.products = self.collection(self, 'categories')
self.subcategories = self.collection(self, 'parentCategories')
self.parentCategories = self.collection(self, 'subCategories')
class Manufacturer(object):
class Manufacturer(BaseObject):
implements(IManufacturer)
def __init__(self, title=None):
def __init__(self, name, title=None):
self.name = name
self.title = title or u'unknown'
self.products = self.collection(self, 'manufacturer')
class Supplier(object):
class Supplier(BaseObject):
implements(ISupplier)
def __init__(self, title=None):
def __init__(self, name, title=None):
self.name = name
self.title = title or u'unknown'