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$) ($Id$)
>>> from zope import component
>>> from cybertools.commerce.manager import Manager >>> from cybertools.commerce.manager import Manager
>>> manager = Manager() >>> manager = Manager()
@ -42,6 +44,13 @@ it belongs to.
>>> sorted((s.name, s.title) for s in p003.shops) >>> sorted((s.name, s.title) for s in p003.shops)
[(u'shop1', u'PC up Ltd'), (u'shop2', u'Video up Ltd')] [(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 Customers
========= =========
@ -66,6 +75,18 @@ Customers
[(u'shop1', u'PC up Ltd'), (u'shop2', u'Video up Ltd')] [(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): class RelationSet(object):
def __init__(self, parent, attributeName): def __init__(self, parent, attributeName=None):
self.parent = parent self.parent = parent
self.attributeName = attributeName self.attributeName = attributeName
self.data = {} self.data = {}
def add(self, related): def add(self, related):
self.data[related.name] = related self.data[related.name] = related
relatedData = getattr(related, self.attributeName).data if self.attributeName:
relatedData[self.parent.name] = self.parent 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): def remove(self, related):
name = related.name name = related.name
del self.data[name] del self.data[name]
relatedData = getattr(related, self.attributeName).data if self.attributeName:
del relatedData[self.parent.name] 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): def __iter__(self):
for obj in self.data.values(): for obj in self.data.values():
yield obj 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 # 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
@ -24,21 +24,20 @@ $Id$
from zope.interface import implements, Interface 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 from cybertools.commerce.interfaces import ICustomer, IAddress
class Customer(object): class Customer(BaseObject):
implements(ICustomer) implements(ICustomer)
collection = RelationSet
def __init__(self, customerId, title=None, client=None): def __init__(self, customerId, title=None, client=None):
self.name = self.customerId = customerId self.name = self.customerId = customerId
self.title = title or u'unknown' self.title = title or u'unknown'
self.client = client self.client = client
self.shops = self.collection(self, 'customers') self.shops = self.collection(self, 'customers')
self.orders = self.collection(self, 'customer')
class Address(object): 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 # 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
@ -29,6 +30,7 @@ from zope.i18nmessageid import MessageFactory
from cybertools.util.jeep import Jeep, Term from cybertools.util.jeep import Jeep, Term
from cybertools.organize.interfaces import IAddress as IBaseAddress from cybertools.organize.interfaces import IAddress as IBaseAddress
from cybertools.organize.interfaces import IPerson as IBasePerson
from loops import util from loops import util
_ = MessageFactory('cybertools.commerce') _ = MessageFactory('cybertools.commerce')
@ -41,12 +43,15 @@ class IManager(Interface):
all components of a commerce site. all components of a commerce site.
""" """
shops = Attribute('All shops in this commerce manager.') shops = Attribute(u'All shops in this commerce manager.')
products = Attribute('All products in this commerce manager.') products = Attribute(u'All products in this commerce manager.')
categories = Attribute('All product categories in this commerce manager.') categories = Attribute(u'All product categories in this commerce manager.')
manufacturers = Attribute('All manufacturers in this commerce manager.') manufacturers = Attribute(u'All manufacturers in this commerce manager.')
suppliers = Attribute('All suppliers in this commerce manager.') suppliers = Attribute(u'All suppliers in this commerce manager.')
customers = Attribute('All customers 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 # shops
@ -179,6 +184,11 @@ class ICategory(Interface):
""" A product category. """ 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 = schema.TextLine(
title=_(u'Title'), title=_(u'Title'),
description=_(u'Short title of the category.'), description=_(u'Short title of the category.'),
@ -203,6 +213,7 @@ class ICategory(Interface):
products = Attribute(u'The products belonging to this category.') products = Attribute(u'The products belonging to this category.')
subcategories = Attribute(u'The sub-categories 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.') shops = Attribute(u'The shops providing this category.')
accessories = Attribute(u'Accessories for this category.') accessories = Attribute(u'Accessories for this category.')
@ -210,8 +221,8 @@ class ICategory(Interface):
# customers # customers
class ICustomer(Interface): class ICustomer(Interface):
""" Typically a role of - for example - a person or institution (the ``client``) """ Typically a role instance of - for example - a person or institution
representing a customer of one or more shops. (the ``client``) representing a customer of one or more shops.
The client may be None in which case the customer is an object on The client may be None in which case the customer is an object on
its own. its own.
@ -229,16 +240,23 @@ class ICustomer(Interface):
required=True) required=True)
description = schema.Text( description = schema.Text(
title=_(u'Description'), title=_(u'Description'),
description=_(u'A medium-length description of the category.'), description=_(u'A medium-length description of the customer.'),
default=u'', default=u'',
missing_value=u'', missing_value=u'',
required=False) required=False)
shops = Attribute(u'The shops the client object is a customer of.') 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.') 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(( addressTypesVoc = util.KeywordVocabulary((
('standard', _(u'Standard Address')), ('standard', _(u'Standard Address')),
('invoice', _(u'Invoice Address')), ('invoice', _(u'Invoice Address')),
@ -256,9 +274,42 @@ class IAddress(IBaseAddress):
default='standard', default='standard',
required=False) required=False)
clients = Attribute(u'A collection of objects that this address belongs to.')
# orders # 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): class IOrder(Interface):
""" """
""" """
@ -266,10 +317,16 @@ class IOrder(Interface):
orderId = Attribute(u'Order Identifier') orderId = Attribute(u'Order Identifier')
shop = Attribute(u'The shop this order belongs to.') shop = Attribute(u'The shop this order belongs to.')
customer = Attribute(u'The customer issuing this order.') 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): class IOrderItem(Interface):
""" """ An individual order or cart item.
""" """
quantity = schema.Int( quantity = schema.Int(
@ -278,7 +335,14 @@ class IOrderItem(Interface):
default=1, default=1,
required=True) 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.') product = Attribute(u'The product represented by this order item.')
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.')
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.common import ContainerAttribute
from cybertools.commerce.customer import Customer from cybertools.commerce.customer import Customer
from cybertools.commerce.interfaces import IManager from cybertools.commerce.interfaces import IManager, IOrderItems
from cybertools.commerce.product import Product from cybertools.commerce.product import Product, Category, Manufacturer, Supplier
from cybertools.commerce.order import Order, OrderItem
from cybertools.commerce.shop import Shop from cybertools.commerce.shop import Shop
from cybertools.tracking.btree import TrackingStorage
class Manager(object): class Manager(object):
@ -38,4 +40,15 @@ class Manager(object):
def __init__(self): def __init__(self):
self.shops = ContainerAttribute(Shop) self.shops = ContainerAttribute(Shop)
self.products = ContainerAttribute(Product, 'productId') 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.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 # 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
@ -24,46 +24,53 @@ $Id$
from zope.interface import implements, Interface 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 IProduct, ICategory
from cybertools.commerce.interfaces import IManufacturer, ISupplier from cybertools.commerce.interfaces import IManufacturer, ISupplier
class Product(object): class Product(BaseObject):
implements(IProduct) implements(IProduct)
collection = RelationSet manufacturer = Relation('_manufacturer', 'products')
def __init__(self, productId, title=None): def __init__(self, productId, title=None):
self.name = self.productId = productId self.name = self.productId = productId
self.title = title or u'unknown' self.title = title or u'unknown'
self.shops = self.collection(self, 'products') 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) implements(ICategory)
collection = RelationSet def __init__(self, name, title=None):
self.name = name
def __init__(self, title=None):
self.title = title or u'unknown' self.title = title or u'unknown'
self.shops = self.collection(self, 'categories') 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) implements(IManufacturer)
def __init__(self, title=None): def __init__(self, name, title=None):
self.name = name
self.title = title or u'unknown' self.title = title or u'unknown'
self.products = self.collection(self, 'manufacturer')
class Supplier(object): class Supplier(BaseObject):
implements(ISupplier) implements(ISupplier)
def __init__(self, title=None): def __init__(self, name, title=None):
self.name = name
self.title = title or u'unknown' self.title = title or u'unknown'