work in progress: orders and order items

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3111 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2009-01-03 14:29:48 +00:00
parent e7fb4cd238
commit 1f72392904
7 changed files with 173 additions and 16 deletions

View file

@ -78,15 +78,31 @@ Customers
Carts and Orders Carts and Orders
================ ================
>>> from cybertools.commerce.order import OrderItems A cart is just a collection of order items belonging to a certain customer
>>> component.provideAdapter(OrderItems) (or some other kind of party).
>>> orderItems = manager.orderItems >>> orderItems = manager.orderItems
>>> orderItems.add(p001, c001, quantity=3) >>> orderItems.add(p001, c001, shop=shop1, quantity=3)
<Track [..., 1, ..., '... ...']: {'quantity': 3}> <OrderItem [2, 1, 7, '... ...', '???']: {'shop': 0, 'quantity': 3}>
>>> orderItems.getCart(c001)
[<OrderItem [2, 1, 7, '... ...', '???']: {'shop': 0, 'quantity': 3}>]
Orders Orders
------ ------
The items in a shopping cart may be included in an order.
>>> ord001 = manager.orders.create(u'001', shop=shop1, customer=c001) >>> ord001 = manager.orders.create(u'001', shop=shop1, customer=c001)
>>> for item in orderItems.getCart(c001):
... item.setOrder(ord001)
Now the default cart is empty; we now have to supply the order for
retrieving the order items.
>>> orderItems.getCart(c001)
[]
>>> orderItems.getCart(c001, ord001)
[<OrderItem [2, 1, 7, '... ...', 11]: {'shop': 0, 'quantity': 3}>]

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 # 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 # it under the terms of the GNU General Public License as published by
@ -22,6 +22,9 @@ Common functionality.
$Id$ $Id$
""" """
from zope.app.intid.interfaces import IIntIds
from zope import component
class ContainerAttribute(object): class ContainerAttribute(object):
@ -37,9 +40,11 @@ class ContainerAttribute(object):
for k, v in kw.items(): for k, v in kw.items():
setattr(obj, k, v) setattr(obj, k, v)
self.data[id] = obj self.data[id] = obj
component.getUtility(IIntIds).register(obj)
return obj return obj
def remove(self, id): def remove(self, id):
component.getUtility(IIntIds).unregister(self.data[id])
del self.data[id] del self.data[id]
def get(self, id, default=None): def get(self, id, default=None):
@ -110,3 +115,16 @@ class BaseObject(object):
collection = RelationSet collection = RelationSet
# utility functions
def getUidForObject(obj, intIds=None):
if intIds is None:
intIds = component.getUtility(IIntIds)
return intIds.getId(obj)
def getObjectForUid(uid, intIds=None):
if intIds is None:
intIds = component.getUtility(IIntIds)
return intIds.getObject(uid)

View file

@ -40,7 +40,7 @@ class Customer(BaseObject):
self.orders = self.collection(self, 'customer') self.orders = self.collection(self, 'customer')
class Address(object): class Address(BaseObject):
implements(IAddress) implements(IAddress)

View file

@ -335,14 +335,42 @@ class IOrderItem(Interface):
default=1, default=1,
required=True) required=True)
order = Attribute(u'The order this order item belongs to.')
product = Attribute(u'The product represented by this order item.') product = Attribute(u'The product represented by this order item.')
party = Attribute(u'The party (person, customer, session, ...) '
u'that is ordering the product.')
shop = Attribute(u'The shop from which the product is ordered.')
order = Attribute(u'The order this order item belongs to.')
unitPrice = Attribute(u'The basic unit price for one of the product ' unitPrice = Attribute(u'The basic unit price for one of the product '
u'items ordered.') u'items ordered.')
fullPrice = Attribute(u'The full price for the quantity ordered.') fullPrice = Attribute(u'The full price for the quantity ordered.')
def setOrder(order):
""" Assign the order given to the order item.
"""
class IOrderItems(Interface): class IOrderItems(Interface):
""" A collection of order items. """ A collection of order items.
""" """
def __getitem__(key):
""" Return the order item identified by the key given.
"""
def __iter__():
""" Return an iterator of all order items.
"""
def query(**criteria):
""" Search for order items. Possible criteria are:
product, party, order, run, timeFrom, timeTo.
"""
def add(product, party, shop, order='???', run=0, **kw):
""" Create and register an order item; return it. Additional properties
may be specified via keyword arguments.
"""
def getCart(party, order='???', shop=None, run=0, **kw):
""" Return a collection of order items.
"""

View file

@ -22,16 +22,20 @@ Order and order item classes.
$Id$ $Id$
""" """
from zope.app.intid.interfaces import IIntIds
from zope.cachedescriptors.property import Lazy
from zope import component
from zope.component import adapts from zope.component import adapts
from zope.interface import implements, Interface from zope.interface import implements, Interface
from cybertools.commerce.common import getUidForObject, getObjectForUid
from cybertools.commerce.common import Relation, BaseObject from cybertools.commerce.common import Relation, BaseObject
from cybertools.commerce.interfaces import IOrder, IOrderItem, IOrderItems from cybertools.commerce.interfaces import IOrder, IOrderItem, IOrderItems
from cybertools.tracking.btree import Track from cybertools.tracking.btree import Track
from cybertools.tracking.interfaces import ITrackingStorage from cybertools.tracking.interfaces import ITrackingStorage
class Order(object): class Order(BaseObject):
implements(IOrder) implements(IOrder)
@ -47,11 +51,34 @@ class OrderItem(Track):
implements(IOrderItem) implements(IOrderItem)
metadata_attributes = Track.metadata_attributes + ('order',)
index_attributes = metadata_attributes
typeName = 'OrderItem'
def __getattr__(self, attr): def __getattr__(self, attr):
if attr not in IOrderItem: if attr not in IOrderItem:
raise AttributeError(attr) raise AttributeError(attr)
return self.data.get(attr) return self.data.get(attr)
def getParent(self):
return IOrderItems(self.__parent__)
def getObject(self, ref):
if isinstance(ref, int):
return getObjectForUid(ref)
if isinstance(ref, basestring):
if ref.isdigit:
return getObjectForUid(int(ref))
if ':' in ref:
tp, id = ref.split(':', 1)
return (tp, id)
return ref
def setOrder(self, order):
parent = self.getParent()
self.order = parent.getUid(order)
parent.context.indexTrack(0, self, 'order')
class OrderItems(object): class OrderItems(object):
""" A tracking storage adapter managing order items. """ A tracking storage adapter managing order items.
@ -71,14 +98,40 @@ class OrderItems(object):
def query(self, **criteria): def query(self, **criteria):
if 'product' in criteria: if 'product' in criteria:
criteria['taskId'] = criteria.pop('product') criteria['taskId'] = self.getUid(criteria.pop('product'))
if 'person' in criteria: if 'party' in criteria:
criteria['userName'] = criteria.pop('person') criteria['userName'] = self.getUid(criteria.pop('party'))
if 'order' in criteria:
criteria['order'] = self.getUid(criteria.pop('order'))
if 'run' in criteria: if 'run' in criteria:
criteria['runId'] = criteria.pop('run') criteria['runId'] = criteria.pop('run')
return self.context.query(**criteria) return self.context.query(**criteria)
def add(self, product, person, run=0, **kw): def add(self, product, party, shop, order='???', run=0, **kw):
trackId = self.context.saveUserTrack(product, run, person, kw) kw['shop'] = self.getUid(shop)
trackId = self.context.saveUserTrack(self.getUid(product), run,
self.getUid(party), kw)
track = self[trackId] track = self[trackId]
track.order = self.getUid(order)
self.context.indexTrack(0, track, 'order')
return track return track
def getCart(self, party, order='???', shop=None, run=None, **kw):
if run:
kw['run'] = run
result = self.query(party=party, order=order, **kw)
if shop is None:
return list(result)
return [item for item in result if item.shop == shop]
# utility methods
@Lazy
def intIds(self):
return component.getUtility(IIntIds)
def getUid(self, obj):
if isinstance(obj, BaseObject):
return getUidForObject(obj, self.intIds)
return obj

View file

@ -24,11 +24,11 @@ $Id$
from zope.interface import implements from zope.interface import implements
from cybertools.commerce.common import RelationSet from cybertools.commerce.common import RelationSet, BaseObject
from cybertools.commerce.interfaces import IShop from cybertools.commerce.interfaces import IShop
class Shop(object): class Shop(BaseObject):
implements(IShop) implements(IShop)

View file

@ -8,8 +8,49 @@ $Id$
import unittest, doctest import unittest, doctest
from zope.testing.doctestunit import DocFileSuite from zope.testing.doctestunit import DocFileSuite
from zope.app.intid.interfaces import IIntIds
from zope import component
from zope.interface import implements
from cybertools.commerce.order import OrderItems
from cybertools.commerce.product import Product from cybertools.commerce.product import Product
class IntIdsStub(object):
"""A testing stub (mock utility) for IntIds."""
implements(IIntIds)
def __init__(self):
self.objs = []
def getObject(self, uid):
return self.objs[uid]
def register(self, ob):
if ob not in self.objs:
self.objs.append(ob)
return self.objs.index(ob)
getId = register
queryId = getId
def unregister(self, ob):
id = self.getId(ob)
self.objs[id] = None
def __iter__(self):
return iter(xrange(len(self.objs)))
def setUp(testCase):
component.provideUtility(IntIdsStub())
component.provideAdapter(OrderItems)
def tearDown(testCase):
pass
class Test(unittest.TestCase): class Test(unittest.TestCase):
"Basic tests." "Basic tests."
@ -21,7 +62,8 @@ def test_suite():
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return unittest.TestSuite(( return unittest.TestSuite((
unittest.makeSuite(Test), unittest.makeSuite(Test),
DocFileSuite('README.txt', optionflags=flags), DocFileSuite('README.txt', optionflags=flags,
setUp=setUp, tearDown=tearDown),
)) ))
if __name__ == '__main__': if __name__ == '__main__':