cyberapps.commerce: Python3 fixes
This commit is contained in:
parent
3524df21a1
commit
c6c4fd56f2
27 changed files with 1554 additions and 0 deletions
7
cyberapps/bsm/README.txt
Normal file
7
cyberapps/bsm/README.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
========================
|
||||
Berlin School Management
|
||||
========================
|
||||
|
||||
School Information
|
||||
==================
|
||||
|
3
cyberapps/bsm/__init__.py
Normal file
3
cyberapps/bsm/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
$Id$
|
||||
"""
|
138
cyberapps/bsm/browser.py
Normal file
138
cyberapps/bsm/browser.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
#
|
||||
# Copyright (c) 2007 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
|
||||
#
|
||||
|
||||
"""
|
||||
View classes for the BSM (Berlin School Management) project.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope import interface, component
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
|
||||
from cybertools.composer.schema import Schema
|
||||
from cybertools.composer.schema import Field
|
||||
from cybertools.reporter.browser.report import DetailView, ListingView
|
||||
from cybertools.reporter.resultset import ResultSet, Cell
|
||||
from loops.browser.concept import ConceptView
|
||||
from loops.browser.node import NodeView
|
||||
from loops.common import adapted
|
||||
from loops.browser.common import conceptMacrosTemplate
|
||||
from loops import util
|
||||
|
||||
|
||||
bsmTemplate = ViewPageTemplateFile('macros.pt')
|
||||
|
||||
|
||||
class TitleCell(Cell):
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
view = self.row.resultSet.view
|
||||
if view is None:
|
||||
return u''
|
||||
return ('%s/.target%s' %
|
||||
(view.url, util.getUidForObject(self.row.context.context)))
|
||||
|
||||
|
||||
class UrlCell(Cell):
|
||||
|
||||
limit = None
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
value = self.value
|
||||
if not value:
|
||||
return ''
|
||||
if self.field.name == 'email':
|
||||
return 'mailto:' + value
|
||||
if value.startswith('http'):
|
||||
return value
|
||||
return 'http://' + value
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
text = super(UrlCell, self).text
|
||||
if self.limit and len(text) > self.limit:
|
||||
text = text[:self.limit-1] + '...'
|
||||
return text
|
||||
|
||||
|
||||
class UrlCellWithLimit(UrlCell):
|
||||
|
||||
limit = 20
|
||||
|
||||
|
||||
class SchoolDetails(DetailView):
|
||||
|
||||
conceptMacros = conceptMacrosTemplate
|
||||
|
||||
@property
|
||||
def macro(self):
|
||||
return bsmTemplate.macros['detail']
|
||||
|
||||
@Lazy
|
||||
def nodeView(self):
|
||||
return NodeView(self.context, self.request)
|
||||
|
||||
def resources(self):
|
||||
for obj in self.context.getResources():
|
||||
yield ConceptView(obj, self.request)
|
||||
|
||||
@Lazy
|
||||
def resultSet(self):
|
||||
result = ResultSet([adapted(self.context)])
|
||||
result.schema = Schema(
|
||||
Field(u'title', u'Name'),
|
||||
Field(u'address', u'Anschrift'),
|
||||
Field(u'headMaster', u'Rektor'),
|
||||
Field(u'telephone', u'Telefon'),
|
||||
Field(u'telefax', u'Telefax'),
|
||||
Field(u'email', u'E-Mail', renderFactory=UrlCell),
|
||||
Field(u'website', u'Web', renderFactory=UrlCell),
|
||||
)
|
||||
result.view = self.nodeView
|
||||
return result
|
||||
|
||||
|
||||
class SchoolListing(ListingView):
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
for obj in self.nodeView.virtualTargetObject.getChildren():
|
||||
yield adapted(obj)
|
||||
|
||||
@Lazy
|
||||
def nodeView(self):
|
||||
return NodeView(self.context, self.request)
|
||||
|
||||
@Lazy
|
||||
def resultSet(self):
|
||||
result = ResultSet(self.children)
|
||||
result.schema = Schema(
|
||||
Field(u'title', u'Name', renderFactory=TitleCell),
|
||||
Field(u'address', u'Anschrift'),
|
||||
Field(u'headMaster', u'Rektor'),
|
||||
Field(u'telephone', u'Telefon'),
|
||||
Field(u'telefax', u'Telefax'),
|
||||
Field(u'email', u'E-Mail', renderFactory=UrlCellWithLimit),
|
||||
Field(u'website', u'Web', renderFactory=UrlCellWithLimit),
|
||||
)
|
||||
result.view = self.nodeView
|
||||
return result
|
32
cyberapps/bsm/configure.zcml
Normal file
32
cyberapps/bsm/configure.zcml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!-- $Id$ -->
|
||||
|
||||
<configure
|
||||
xmlns:zope="http://namespaces.zope.org/zope"
|
||||
xmlns:browser="http://namespaces.zope.org/browser"
|
||||
i18n_domain="zope">
|
||||
|
||||
<zope:adapter factory="cyberapps.bsm.data.SchoolInfoAdapter"
|
||||
trusted="True" />
|
||||
|
||||
<zope:class class="cyberapps.bsm.data.SchoolInfoAdapter">
|
||||
<require permission="zope.View"
|
||||
interface="cyberapps.bsm.interfaces.ISchoolInfo" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cyberapps.bsm.interfaces.ISchoolInfo" />
|
||||
</zope:class>
|
||||
|
||||
<browser:page
|
||||
for="loops.interfaces.IConcept"
|
||||
name="bsm_school_detail.html"
|
||||
class="cyberapps.bsm.browser.SchoolDetails"
|
||||
permission="zope.View"
|
||||
/>
|
||||
|
||||
<browser:page
|
||||
for="loops.interfaces.INode"
|
||||
name="bsm_schools.html"
|
||||
class="cyberapps.bsm.browser.SchoolListing"
|
||||
permission="zope.View"
|
||||
/>
|
||||
|
||||
</configure>
|
42
cyberapps/bsm/data.py
Normal file
42
cyberapps/bsm/data.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
#
|
||||
# Copyright (c) 2007 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
|
||||
#
|
||||
|
||||
"""
|
||||
Classes for BSM School Informations.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.component import adapts
|
||||
from zope.interface import implements
|
||||
|
||||
from cyberapps.bsm.interfaces import ISchoolInfo
|
||||
from loops.common import AdapterBase
|
||||
from loops.interfaces import IConcept
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
||||
TypeInterfaceSourceList.typeInterfaces += (ISchoolInfo,)
|
||||
|
||||
|
||||
class SchoolInfoAdapter(AdapterBase):
|
||||
|
||||
implements(ISchoolInfo)
|
||||
|
||||
_contextAttributes = list(ISchoolInfo) + list(IConcept)
|
||||
|
62
cyberapps/bsm/interfaces.py
Normal file
62
cyberapps/bsm/interfaces.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
#
|
||||
# Copyright (c) 2007 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
|
||||
#
|
||||
|
||||
"""
|
||||
Interfaces for BSM (Berlin School Managment).
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.interface import Interface, Attribute
|
||||
from zope import schema
|
||||
|
||||
from loops.util import _
|
||||
|
||||
|
||||
class ISchoolInfo(Interface):
|
||||
""" Information about schools taking part in the Berlin School Management
|
||||
(BSM) project.
|
||||
|
||||
The name of the school is in the ``title`` attribute.
|
||||
"""
|
||||
|
||||
address = schema.TextLine(
|
||||
title=_(u'Address'),
|
||||
description=_(u'Postal address of the school'),
|
||||
required=False,)
|
||||
headMaster = schema.TextLine(
|
||||
title=_(u'Headmaster'),
|
||||
description=_(u'Name of the head master of the school'),
|
||||
required=False,)
|
||||
telephone = schema.TextLine(
|
||||
title=_(u'Telephone'),
|
||||
description=_(u'Telephone number of the headmaster'),
|
||||
required=False,)
|
||||
telefax = schema.TextLine(
|
||||
title=_(u'Telefax'),
|
||||
description=_(u'Telefax number of the headmaster'),
|
||||
required=False,)
|
||||
email = schema.TextLine(
|
||||
title=_(u'Email'),
|
||||
description=_(u'Email address of the headmaster'),
|
||||
required=False,)
|
||||
website = schema.TextLine(
|
||||
title=_(u'Website'),
|
||||
description=_(u'URL of the website of the school'),
|
||||
required=False,)
|
||||
|
30
cyberapps/bsm/macros.pt
Normal file
30
cyberapps/bsm/macros.pt
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!-- $Id$ -->
|
||||
|
||||
<metal:detail define-macro="detail"
|
||||
tal:define="result item/resultSet">
|
||||
|
||||
<h2 tal:content="item/title"
|
||||
tal:attributes="ondblclick item/openEditWindow">
|
||||
Something</h2><br />
|
||||
<table>
|
||||
<tr tal:repeat="cell item/cells">
|
||||
<td width="20%">
|
||||
<span tal:content="cell/field/title"
|
||||
i18n:translate="">Fieldname</span>:
|
||||
</td>
|
||||
<td>
|
||||
<a href="#"
|
||||
tal:omit-tag="not:cell/url"
|
||||
tal:attributes="href cell/url;
|
||||
title cell/urlTitle">
|
||||
<span tal:content="cell/text">Value</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table><br />
|
||||
|
||||
<div tal:attributes="class string:content-$level;">
|
||||
<metal:fields use-macro="item/conceptMacros/macros/conceptresources" />
|
||||
</div>
|
||||
|
||||
</metal:detail>
|
187
cyberapps/commerce/README.txt
Normal file
187
cyberapps/commerce/README.txt
Normal file
|
@ -0,0 +1,187 @@
|
|||
====================
|
||||
eCommerce with loops
|
||||
====================
|
||||
|
||||
Note: This package depends on loops.
|
||||
|
||||
Let's do some basic set up
|
||||
|
||||
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||
>>> site = placefulSetUp(True)
|
||||
|
||||
>>> from zope import component, interface
|
||||
|
||||
and setup a simple loops site with a concept manager and some concepts
|
||||
(with all the type machinery, what in real life is done via standard
|
||||
ZCML setup):
|
||||
|
||||
>>> from cyberapps.commerce.tests import TestSite
|
||||
>>> t = TestSite(site)
|
||||
>>> concepts, resources, views = t.setup()
|
||||
|
||||
We also collect here some basic imports we'll need later.
|
||||
|
||||
>>> from loops.concept import Concept
|
||||
>>> from loops.common import adapted
|
||||
>>> from loops.setup import addAndConfigureObject
|
||||
|
||||
We also use an adapter to the concept manager for accessing the commerce
|
||||
objects.
|
||||
|
||||
>>> from cyberapps.commerce.manager import Manager
|
||||
>>> manager = Manager(concepts)
|
||||
|
||||
|
||||
Shops and Products
|
||||
==================
|
||||
|
||||
Let's start with two shops:
|
||||
|
||||
>>> shop1 = manager.shops.create('shop1', title='PC up Ltd')
|
||||
>>> shop2 = manager.shops.create('shop2', title='Video up Ltd')
|
||||
|
||||
>>> len(list(manager.shops))
|
||||
2
|
||||
|
||||
>>> shop1.title
|
||||
'PC up Ltd'
|
||||
|
||||
Now we create a few products ...
|
||||
|
||||
>>> p001 = manager.products.create('001', title='Silent Case')
|
||||
>>> p002 = manager.products.create('002', title='Portable Projector')
|
||||
>>> p003 = manager.products.create('003', title='HD Flatscreen Monitor')
|
||||
>>> p004 = manager.products.create('004', title='Giga Mainboard')
|
||||
|
||||
>>> p001.title
|
||||
'Silent Case'
|
||||
>>> p001.fullDescription
|
||||
|
||||
... and add them to the shops.
|
||||
|
||||
>>> shop1.products.add(p001)
|
||||
>>> shop1.products.add(p003)
|
||||
>>> shop1.products.add(p004)
|
||||
>>> shop2.products.add(p002)
|
||||
>>> shop2.products.add(p003)
|
||||
|
||||
We can now list the products in a shop.
|
||||
|
||||
>>> sorted((p.productId, p.title) for p in shop1.products)
|
||||
[('001', 'Silent Case'), ('003', 'HD Flatscreen Monitor'),
|
||||
('004', 'Giga Mainboard')]
|
||||
|
||||
>>> sorted((s.name, s.title) for s in p003.shops)
|
||||
[('shop1', 'PC up Ltd'), ('shop2', 'Video up Ltd')]
|
||||
|
||||
Categories
|
||||
----------
|
||||
|
||||
>>> cat001 = manager.categories.create('001', title='Cases')
|
||||
>>> p001.categories.add(cat001)
|
||||
|
||||
|
||||
Customers
|
||||
=========
|
||||
|
||||
Now let's add a few customers.
|
||||
|
||||
>>> c001 = manager.customers.create('001', title='Your Local Computer Store')
|
||||
>>> c002 = manager.customers.create('002', title='Speedy Gonzales')
|
||||
>>> c003 = manager.customers.create('003', title='TeeVee')
|
||||
>>> c004 = manager.customers.create('004', title='MacVideo')
|
||||
|
||||
These are stored in a separate ConceptManager object.
|
||||
|
||||
>>> customers = concepts.getLoopsRoot()['customers']
|
||||
>>> len(customers)
|
||||
4
|
||||
|
||||
In the testing scenario we have to index all the customer objects.
|
||||
|
||||
>>> from zope.app.catalog.interfaces import ICatalog
|
||||
>>> catalog = component.getUtility(ICatalog)
|
||||
>>> from loops import util
|
||||
>>> for r in customers.values():
|
||||
... catalog.index_doc(int(util.getUidForObject(r)), r)
|
||||
|
||||
Now the customers can be accessed via the standard concept manager adapter.
|
||||
|
||||
>>> manager.customers.get('004')
|
||||
<cyberapps.commerce.customer.Customer object ...>
|
||||
|
||||
>>> shop1.customers.add(c001)
|
||||
>>> shop1.customers.add(c002)
|
||||
>>> shop1.customers.add(c004)
|
||||
>>> shop2.customers.add(c002)
|
||||
>>> shop2.customers.add(c003)
|
||||
>>> shop2.customers.add(c004)
|
||||
|
||||
>>> sorted((c.customerId, c.title) for c in shop1.customers)
|
||||
[('001', 'Your Local Computer Store'), ('002', 'Speedy Gonzales'),
|
||||
('004', 'MacVideo')]
|
||||
|
||||
>>> sorted((s.name, s.title) for s in c002.shops)
|
||||
[('shop1', 'PC up Ltd'), ('shop2', 'Video up Ltd')]
|
||||
|
||||
|
||||
Carts and Orders
|
||||
================
|
||||
|
||||
A cart is just a collection of order items belonging to a certain customer
|
||||
(or some other kind of party).
|
||||
|
||||
>>> orderItems = manager.orderItems
|
||||
|
||||
>>> orderItems.add(p001, c001, shop=shop1, quantity=3)
|
||||
<OrderItem ['44', 1, '60', '... ...', '???']: {'quantity': 3, 'shop': '40'}>
|
||||
|
||||
>>> orderItems.getCart(c001)
|
||||
[<OrderItem ['44', 1, '60', '... ...', '???']: {'quantity': 3, 'shop': '40'}>]
|
||||
|
||||
Orders
|
||||
------
|
||||
|
||||
The items in a shopping cart may be included in an order.
|
||||
|
||||
>>> ord001 = manager.orders.create('001', shop=shop1, customer=c001)
|
||||
|
||||
>>> for item in orderItems.getCart(c001):
|
||||
... item.setOrder(ord001)
|
||||
|
||||
Now the default cart is empty; we have to supply the order for
|
||||
retrieving the order items. But now we can omit the customer from the query.
|
||||
|
||||
>>> orderItems.getCart(c001)
|
||||
[]
|
||||
>>> orderItems.getCart(c001, ord001)
|
||||
[<OrderItem ['44', 1, '60', '... ...', '74']: {'quantity': 3, 'shop': '40'}>]
|
||||
>>> orderItems.getCart(order=ord001)
|
||||
[<OrderItem ['44', 1, '60', '... ...', '74']: {'quantity': 3, 'shop': '40'}>]
|
||||
|
||||
|
||||
Administrative Views and Forms
|
||||
==============================
|
||||
|
||||
>>> from zope.publisher.browser import TestRequest
|
||||
|
||||
Listings
|
||||
--------
|
||||
|
||||
>>> from cyberapps.commerce.browser.base import SimpleListing
|
||||
|
||||
Forms
|
||||
-----
|
||||
|
||||
>>> from loops.view import Node
|
||||
>>> from cyberapps.commerce.browser.product import CreateProductPage
|
||||
|
||||
>>> home = addAndConfigureObject(views, Node, 'home', target=cat001.context)
|
||||
|
||||
>>> form = CreateProductPage(home, TestRequest())
|
||||
|
||||
|
||||
Fin de partie
|
||||
=============
|
||||
|
||||
>>> placefulTearDown()
|
3
cyberapps/commerce/__init__.py
Normal file
3
cyberapps/commerce/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
$Id$
|
||||
"""
|
3
cyberapps/commerce/browser/__init__.py
Normal file
3
cyberapps/commerce/browser/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
$Id$
|
||||
"""
|
58
cyberapps/commerce/browser/action.py
Normal file
58
cyberapps/commerce/browser/action.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
Action definitions for loops-based eCommerce applications.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from cyberapps.commerce.util import _
|
||||
from cybertools.browser.action import actions
|
||||
from loops.browser.action import DialogAction, TargetAction
|
||||
|
||||
|
||||
actions.register('create_product', 'portlet', TargetAction,
|
||||
title=_(u'Create Product...'),
|
||||
description=_(u'Create a new product.'),
|
||||
viewName='create_product_page.html',
|
||||
)
|
||||
|
||||
actions.register('edit_product', 'portlet', TargetAction,
|
||||
title=_(u'Edit Product...'),
|
||||
description=_(u'Modify product data.'),
|
||||
viewName='edit_product_page.html',
|
||||
)
|
||||
|
||||
actions.register('create_category', 'portlet', TargetAction,
|
||||
title=_(u'Create Catgory...'),
|
||||
description=_(u'Create a new category.'),
|
||||
viewName='create_category_page.html',
|
||||
)
|
||||
|
||||
actions.register('edit_category', 'portlet', TargetAction,
|
||||
title=_(u'Edit Category...'),
|
||||
description=_(u'Modify category data.'),
|
||||
viewName='edit_category_page.html',
|
||||
)
|
||||
|
||||
actions.register('edit_customer', 'portlet', TargetAction,
|
||||
title=_(u'Edit Customer...'),
|
||||
description=_(u'Modify customer data.'),
|
||||
viewName='edit_concept_page.html',
|
||||
)
|
22
cyberapps/commerce/browser/base.pt
Normal file
22
cyberapps/commerce/browser/base.pt
Normal file
|
@ -0,0 +1,22 @@
|
|||
<!-- $Id$ -->
|
||||
|
||||
|
||||
<metal:block define-macro="simple_listing">
|
||||
<div>
|
||||
<metal:title use-macro="item/conceptMacros/concepttitle" />
|
||||
</div>
|
||||
<metal:listing define-macro="base_listing">
|
||||
<dl tal:repeat="related item/children">
|
||||
<dt>
|
||||
<a tal:content="related/title"
|
||||
tal:attributes="href python: view.getUrlForTarget(related);">Something</a>
|
||||
</dt>
|
||||
<dd class="description"
|
||||
tal:define="description related/description"
|
||||
tal:condition="description">
|
||||
<span tal:content="structure related/renderedDescription">Description</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</metal:listing>
|
||||
</metal:block>
|
||||
|
41
cyberapps/commerce/browser/base.py
Normal file
41
cyberapps/commerce/browser/base.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
Common base/common view classes for loops-based eCommerce applications.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope import component
|
||||
|
||||
from loops.browser.concept import ConceptView
|
||||
from loops.common import adapted
|
||||
from loops import util
|
||||
|
||||
|
||||
base_macros = ViewPageTemplateFile('base.pt')
|
||||
|
||||
|
||||
class SimpleListing(ConceptView):
|
||||
|
||||
@Lazy
|
||||
def macro(self):
|
||||
return base_macros.macros['simple_listing']
|
84
cyberapps/commerce/browser/configure.zcml
Normal file
84
cyberapps/commerce/browser/configure.zcml
Normal file
|
@ -0,0 +1,84 @@
|
|||
<!-- $Id$ -->
|
||||
|
||||
<configure
|
||||
xmlns:zope="http://namespaces.zope.org/zope"
|
||||
xmlns:browser="http://namespaces.zope.org/browser"
|
||||
i18n_domain="cyberapps.commerce">
|
||||
|
||||
<zope:module module="cyberapps.commerce.browser.action" />
|
||||
|
||||
<!-- products, categories, ... -->
|
||||
|
||||
<zope:adapter
|
||||
name="commerce_simple_listing.html"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="cyberapps.commerce.browser.base.SimpleListing"
|
||||
permission="zope.View" />
|
||||
|
||||
<zope:adapter
|
||||
name="commerce_product.html"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="cyberapps.commerce.browser.product.ProductView"
|
||||
permission="zope.View" />
|
||||
|
||||
<zope:adapter
|
||||
name="commerce_category.html"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="cyberapps.commerce.browser.product.CategoryView"
|
||||
permission="zope.View" />
|
||||
|
||||
<browser:page
|
||||
name="create_product_page.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="cyberapps.commerce.browser.product.CreateProductPage"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<browser:page
|
||||
name="edit_product_page.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="cyberapps.commerce.browser.product.EditProductPage"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<zope:adapter
|
||||
name="create_product"
|
||||
for="loops.browser.node.NodeView
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
factory="cyberapps.commerce.browser.product.CreateProduct"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<browser:page
|
||||
name="create_category_page.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="cyberapps.commerce.browser.product.CreateCategoryPage"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<browser:page
|
||||
name="edit_category_page.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="cyberapps.commerce.browser.product.EditCategoryPage"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<zope:adapter
|
||||
name="create_category"
|
||||
for="loops.browser.node.NodeView
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
factory="cyberapps.commerce.browser.product.CreateCategory"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<!-- customers, ... -->
|
||||
|
||||
<zope:adapter
|
||||
name="commerce_customer.html"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="cyberapps.commerce.browser.customer.CustomerView"
|
||||
permission="zope.View" />
|
||||
|
||||
</configure>
|
37
cyberapps/commerce/browser/customer.py
Normal file
37
cyberapps/commerce/browser/customer.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
View classes for customers and related objects.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
|
||||
from loops.browser.concept import ConceptView
|
||||
from loops.common import adapted
|
||||
from loops import util
|
||||
|
||||
|
||||
# customers
|
||||
|
||||
class CustomerView(ConceptView):
|
||||
|
||||
pass
|
43
cyberapps/commerce/browser/product.pt
Normal file
43
cyberapps/commerce/browser/product.pt
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!-- $Id$ -->
|
||||
|
||||
|
||||
<metal:block define-macro="category">
|
||||
|
||||
<div>
|
||||
<metal:title use-macro="item/conceptMacros/concepttitle" />
|
||||
</div>
|
||||
<h2 i18n:translate="">Subcategories</h2>
|
||||
<dl tal:repeat="related item/subcategories">
|
||||
<tal:item define="data related/data">
|
||||
<dt>
|
||||
<a tal:content="related/title"
|
||||
tal:attributes="href python: view.getUrlForTarget(related);">Category</a>
|
||||
</dt>
|
||||
<dd class="description"
|
||||
tal:define="description related/description"
|
||||
tal:condition="description">
|
||||
<span tal:content="structure related/renderedDescription">Description</span>
|
||||
</dd>
|
||||
</tal:item>
|
||||
</dl>
|
||||
|
||||
<h2 i18n:translate="">Products</h2>
|
||||
<dl tal:repeat="related item/products">
|
||||
<tal:item define="data related/data">
|
||||
<dt>
|
||||
<a tal:content="related/title"
|
||||
tal:attributes="href python: view.getUrlForTarget(related);">Product</a>
|
||||
</dt>
|
||||
<dd class="description"
|
||||
tal:define="description related/description"
|
||||
tal:condition="description">
|
||||
<span tal:content="structure related/renderedDescription">Description</span>
|
||||
</dd>
|
||||
</tal:item>
|
||||
</dl>
|
||||
|
||||
<metal:resources use-macro="view/concept_macros/conceptresources" />
|
||||
|
||||
</metal:block>
|
||||
|
||||
|
130
cyberapps/commerce/browser/product.py
Normal file
130
cyberapps/commerce/browser/product.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
View classes for loops-based eCommerce applications.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope import component
|
||||
|
||||
from cybertools.browser.action import actions
|
||||
from cybertools.composer.schema import Schema
|
||||
from cybertools.composer.schema import Field
|
||||
from loops.browser.action import DialogAction, TargetAction
|
||||
from loops.browser.concept import ConceptView
|
||||
from loops.browser.form import EditConceptPage, CreateConceptPage
|
||||
from loops.browser.form import EditConcept, CreateConcept
|
||||
from loops.browser.node import NodeView
|
||||
from loops.common import adapted
|
||||
from loops import util
|
||||
|
||||
|
||||
product_macros = ViewPageTemplateFile('product.pt')
|
||||
|
||||
|
||||
# products
|
||||
|
||||
class ProductView(ConceptView):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class EditProductPage(EditConceptPage):
|
||||
|
||||
showAssignments = False
|
||||
|
||||
def setupController(self):
|
||||
super(EditProductPage, self).setupController()
|
||||
self.registerDojoFormAllGrid()
|
||||
|
||||
|
||||
class CreateProductPage(CreateConceptPage):
|
||||
|
||||
showAssignments = False
|
||||
typeToken = '.loops/concepts/product'
|
||||
fixedType = True
|
||||
form_action = 'create_product'
|
||||
|
||||
def setupController(self):
|
||||
super(CreateProductPage, self).setupController()
|
||||
self.registerDojoFormAllGrid()
|
||||
|
||||
|
||||
class CreateProduct(CreateConcept):
|
||||
|
||||
def getNameFromData(self):
|
||||
id = self.request.form.get('productId')
|
||||
if id:
|
||||
return 'p' + id
|
||||
|
||||
|
||||
# categories
|
||||
|
||||
class CategoryView(ConceptView):
|
||||
|
||||
@Lazy
|
||||
def macro(self):
|
||||
return product_macros.macros['category']
|
||||
|
||||
@Lazy
|
||||
def subcategories(self):
|
||||
for c in self.adapted.subcategories:
|
||||
view = ConceptView(c.context, self.request)
|
||||
yield view
|
||||
|
||||
@Lazy
|
||||
def products(self):
|
||||
for c in self.adapted.products:
|
||||
view = ProductView(c.context, self.request)
|
||||
yield view
|
||||
|
||||
|
||||
class EditCategoryPage(EditConceptPage):
|
||||
|
||||
#showAssignments = False
|
||||
|
||||
def setupController(self):
|
||||
super(EditCategoryPage, self).setupController()
|
||||
self.registerDojoFormAllGrid()
|
||||
|
||||
|
||||
class CreateCategoryPage(CreateConceptPage):
|
||||
|
||||
#showAssignments = False
|
||||
typeToken = '.loops/concepts/category'
|
||||
fixedType = True
|
||||
form_action = 'create_category'
|
||||
|
||||
def setupController(self):
|
||||
super(CreateCategoryPage, self).setupController()
|
||||
self.registerDojoFormAllGrid()
|
||||
|
||||
|
||||
class CreateCategory(CreateConcept):
|
||||
|
||||
def getNameFromData(self):
|
||||
return super(CreateCategory, self).getNameFromData()
|
||||
|
||||
id = self.request.form.get('productId')
|
||||
if id:
|
||||
return 'p' + id
|
||||
|
95
cyberapps/commerce/configure.zcml
Normal file
95
cyberapps/commerce/configure.zcml
Normal file
|
@ -0,0 +1,95 @@
|
|||
<!-- $Id$ -->
|
||||
|
||||
<configure
|
||||
xmlns:zope="http://namespaces.zope.org/zope"
|
||||
xmlns:browser="http://namespaces.zope.org/browser"
|
||||
i18n_domain="cyberapps.commerce">
|
||||
|
||||
<include package="cybertools.commerce" />
|
||||
|
||||
<zope:adapter factory="cyberapps.commerce.shop.Shop"
|
||||
provides="cyberapps.commerce.interfaces.IShop"
|
||||
trusted="True" />
|
||||
<zope:class class="cyberapps.commerce.shop.Shop">
|
||||
<require permission="zope.View"
|
||||
interface="cyberapps.commerce.interfaces.IShop" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cyberapps.commerce.interfaces.IShop" />
|
||||
</zope:class>
|
||||
|
||||
<zope:adapter factory="cyberapps.commerce.product.Product"
|
||||
provides="cyberapps.commerce.interfaces.IProduct"
|
||||
trusted="True" />
|
||||
<zope:class class="cyberapps.commerce.product.Product">
|
||||
<require permission="zope.View"
|
||||
interface="cyberapps.commerce.interfaces.IProduct" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cyberapps.commerce.interfaces.IProduct" />
|
||||
</zope:class>
|
||||
|
||||
<zope:adapter factory="cyberapps.commerce.product.Category"
|
||||
provides="cyberapps.commerce.interfaces.ICategory"
|
||||
trusted="True" />
|
||||
<zope:class class="cyberapps.commerce.product.Category">
|
||||
<require permission="zope.View"
|
||||
interface="cyberapps.commerce.interfaces.ICategory" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cyberapps.commerce.interfaces.ICategory" />
|
||||
</zope:class>
|
||||
|
||||
<zope:adapter factory="cyberapps.commerce.product.Manufacturer"
|
||||
trusted="True" />
|
||||
<zope:class class="cyberapps.commerce.product.Manufacturer">
|
||||
<require permission="zope.View"
|
||||
interface="cybertools.commerce.interfaces.IManufacturer" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cybertools.commerce.interfaces.IManufacturer" />
|
||||
</zope:class>
|
||||
|
||||
<zope:adapter factory="cyberapps.commerce.customer.Customer"
|
||||
provides="cyberapps.commerce.interfaces.ICustomer"
|
||||
trusted="True" />
|
||||
<zope:class class="cyberapps.commerce.customer.Customer">
|
||||
<require permission="zope.View"
|
||||
interface="cyberapps.commerce.interfaces.ICustomer" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cyberapps.commerce.interfaces.ICustomer" />
|
||||
</zope:class>
|
||||
|
||||
<zope:adapter factory="cyberapps.commerce.customer.Address"
|
||||
provides="cyberapps.commerce.interfaces.IAddress"
|
||||
trusted="True" />
|
||||
<zope:class class="cyberapps.commerce.customer.Address">
|
||||
<require permission="zope.View"
|
||||
interface="cyberapps.commerce.interfaces.IAddress" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cyberapps.commerce.interfaces.IAddress" />
|
||||
</zope:class>
|
||||
|
||||
<zope:adapter factory="cyberapps.commerce.order.Order"
|
||||
provides="cyberapps.commerce.interfaces.IOrder"
|
||||
trusted="True" />
|
||||
<zope:class class="cyberapps.commerce.order.Order">
|
||||
<require permission="zope.View"
|
||||
interface="cyberapps.commerce.interfaces.IOrder" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cyberapps.commerce.interfaces.IOrder" />
|
||||
</zope:class>
|
||||
|
||||
<!--<zope:adapter factory="cyberapps.commerce.order.OrderItems" />-->
|
||||
|
||||
<zope:class class="cyberapps.commerce.order.OrderItem">
|
||||
<require permission="zope.View"
|
||||
interface="cybertools.commerce.interfaces.IOrderItem" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="cybertools.commerce.interfaces.IOrderItem" />
|
||||
</zope:class>
|
||||
|
||||
<!-- setup -->
|
||||
|
||||
<zope:adapter factory="cyberapps.commerce.setup.SetupManager"
|
||||
name="cyberapps.commerce" />
|
||||
|
||||
<include package=".browser" />
|
||||
|
||||
</configure>
|
39
cyberapps/commerce/customer.py
Normal file
39
cyberapps/commerce/customer.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# cyberapps.commerce.customer
|
||||
|
||||
""" Customer adapter and related classes.
|
||||
"""
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from cyberapps.commerce.interfaces import ICustomer, IAddress
|
||||
from cybertools.commerce.customer import Customer as BaseCustomer
|
||||
from cybertools.commerce.customer import Address as BaseAddress
|
||||
from loops.common import AdapterBase
|
||||
from loops.common import ChildRelationSetProperty, ParentRelationSetProperty
|
||||
from loops.interfaces import IConceptSchema
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
||||
TypeInterfaceSourceList.typeInterfaces += (ICustomer, IAddress)
|
||||
|
||||
|
||||
@implementer(ICustomer)
|
||||
class Customer(AdapterBase, BaseCustomer):
|
||||
|
||||
_adapterAttributes = ('context', '__parent__', 'shops')
|
||||
_contextAttributes = list(ICustomer)
|
||||
_noexportAttributes = ('shops', 'orders')
|
||||
|
||||
shops = ParentRelationSetProperty('shop.customer', noSecurityCheck=True)
|
||||
orders = ChildRelationSetProperty('customer.order')
|
||||
|
||||
@property
|
||||
def identifier(self):
|
||||
return self.customerId
|
||||
|
||||
|
||||
@implementer(IAddress)
|
||||
class Address(AdapterBase, BaseAddress):
|
||||
|
||||
_contextAttributes = list(IAddress)
|
||||
|
90
cyberapps/commerce/interfaces.py
Normal file
90
cyberapps/commerce/interfaces.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
Specific interfaces.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from cyberapps.commerce.util import _
|
||||
from cybertools.commerce.interfaces import IShop, IProduct, ICategory, IManufacturer
|
||||
from cybertools.commerce.interfaces import ICustomer, IAddress
|
||||
from cybertools.commerce.interfaces import IOrder, IOrderItem
|
||||
from loops.interfaces import ILoopsAdapter
|
||||
from loops.schema.base import Relation, RelationSet
|
||||
|
||||
|
||||
class IShop(ILoopsAdapter, IShop):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ICategory(ILoopsAdapter, ICategory):
|
||||
|
||||
accessorySubcategories = RelationSet(
|
||||
title=_(u'Accessory Subcategories'),
|
||||
description=_(u'A collection of categories that contain products '
|
||||
u'that may be used as accessories for this category.'),
|
||||
target_types=('category',),
|
||||
required=False)
|
||||
selectedProducts = RelationSet(
|
||||
title=_(u'Selected Products'),
|
||||
description=_(u'Selected products for this category.'),
|
||||
target_types=('product',),
|
||||
required=False)
|
||||
|
||||
|
||||
class IProduct(ILoopsAdapter, IProduct):
|
||||
|
||||
shops = RelationSet(
|
||||
title=_(u'Shops'),
|
||||
description=_(u'The shops providing this product..'),
|
||||
target_types=('shop',),
|
||||
required=False)
|
||||
categories = RelationSet(
|
||||
title=_(u'Categories'),
|
||||
description=_(u'The product categories this product belongs to.'),
|
||||
target_types=('category',),
|
||||
required=False)
|
||||
manufacturer = Relation(
|
||||
title=_(u'Manufacturer'),
|
||||
description=_(u'The manufacturer providing this product.'),
|
||||
target_types=('manufacturer',),
|
||||
required=False)
|
||||
recommendedAccessories = RelationSet(
|
||||
title=_(u'Recommended Accessories'),
|
||||
description=_(u'Accessories for this product.'),
|
||||
target_types=('product',),
|
||||
required=False)
|
||||
|
||||
|
||||
class ICustomer(ILoopsAdapter, ICustomer):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class IAddress(ILoopsAdapter, IAddress):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class IOrder(ILoopsAdapter, IOrder):
|
||||
|
||||
pass
|
||||
|
42
cyberapps/commerce/manager.py
Normal file
42
cyberapps/commerce/manager.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
# cyberapps.commerce.manager
|
||||
|
||||
""" The commerce manager (container, registry, ...).
|
||||
"""
|
||||
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.component import adapts
|
||||
from zope.interface import implementer
|
||||
|
||||
from cybertools.commerce.interfaces import IManager, IOrderItems
|
||||
from loops.common import TypeInstancesProperty
|
||||
from loops.concept import Concept
|
||||
from loops.interfaces import IConceptManager
|
||||
from loops.setup import addAndConfigureObject
|
||||
|
||||
|
||||
@implementer(IManager)
|
||||
class Manager(object):
|
||||
|
||||
adapts(IConceptManager)
|
||||
|
||||
langInfo = None
|
||||
|
||||
shops = TypeInstancesProperty('shop')
|
||||
products = TypeInstancesProperty('product', 'productId', 'p')
|
||||
categories = TypeInstancesProperty('category', 'name', 'cat')
|
||||
customers = TypeInstancesProperty('customer', 'customerId', 'c',
|
||||
container='customers')
|
||||
orders = TypeInstancesProperty('order', 'orderId', '',
|
||||
container='orders')
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
@Lazy
|
||||
def records(self):
|
||||
return self.context.getLoopsRoot().getRecordManager()
|
||||
|
||||
@Lazy
|
||||
def orderItems(self):
|
||||
return IOrderItems(self.records['orderitems'])
|
||||
|
59
cyberapps/commerce/order.py
Normal file
59
cyberapps/commerce/order.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# cyberapps.commerce.order
|
||||
|
||||
""" Order and order item classes.
|
||||
"""
|
||||
|
||||
from zope.app.intid.interfaces import IIntIds
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope import component
|
||||
from zope.component import adapts
|
||||
from zope.interface import implementer, Interface
|
||||
|
||||
from cyberapps.commerce.interfaces import IOrder
|
||||
from cybertools.commerce.order import Order as BaseOrder
|
||||
from cybertools.commerce.order import OrderItem as BaseOrderItem
|
||||
from cybertools.commerce.order import OrderItems as BaseOrderItems
|
||||
from loops.common import AdapterBase, ParentRelation
|
||||
from loops.interfaces import ILoopsObject
|
||||
from loops import util
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
||||
TypeInterfaceSourceList.typeInterfaces += (IOrder,)
|
||||
|
||||
|
||||
@implementer(IOrder)
|
||||
class Order(AdapterBase, BaseOrder):
|
||||
|
||||
_adapterAttributes = ('context', '__parent__', 'customer')
|
||||
_contextAttributes = list(IOrder)
|
||||
|
||||
#shop = ParentRelation('shop.order') # implemented as context attribute
|
||||
customer = ParentRelation('customer.order')
|
||||
|
||||
|
||||
class OrderItem(BaseOrderItem):
|
||||
|
||||
def getObject(self, ref):
|
||||
if isinstance(ref, int):
|
||||
return util.getObjectForUid(ref)
|
||||
if isinstance(ref, basestring):
|
||||
if ref.isdigit:
|
||||
return util.getObjectForUid(ref)
|
||||
if ':' in ref:
|
||||
tp, id = ref.split(':', 1)
|
||||
return (tp, id)
|
||||
return ref
|
||||
|
||||
|
||||
class OrderItems(BaseOrderItems):
|
||||
|
||||
# utility methods
|
||||
|
||||
def getUid(self, obj):
|
||||
if ILoopsObject.providedBy(obj):
|
||||
return util.getUidForObject(obj, self.intIds)
|
||||
elif isinstance(obj, AdapterBase):
|
||||
return util.getUidForObject(obj.context, self.intIds)
|
||||
return obj
|
||||
|
140
cyberapps/commerce/product.py
Normal file
140
cyberapps/commerce/product.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
# cyberapps.commerce.product
|
||||
|
||||
""" Product adapter.
|
||||
"""
|
||||
|
||||
from zope.app.intid.interfaces import IIntIds
|
||||
from zope import component
|
||||
from zope.interface import implementer
|
||||
|
||||
from cyberapps.commerce.interfaces import IProduct, ICategory
|
||||
from cybertools.commerce.interfaces import IManufacturer, ISupplier
|
||||
from cybertools.commerce.product import Product as BaseProduct, Category as BaseCategory
|
||||
from cybertools.commerce.product import Manufacturer, Supplier
|
||||
from cybertools.util.cache import cache
|
||||
from loops.common import adapted, AdapterBase, baseObject, ParentRelation
|
||||
from loops.common import ParentRelationSetProperty, ChildRelationSetProperty
|
||||
from loops.interfaces import IConceptSchema
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
from loops import util
|
||||
|
||||
|
||||
TypeInterfaceSourceList.typeInterfaces += (
|
||||
IProduct, ICategory, IManufacturer, ISupplier)
|
||||
|
||||
|
||||
@implementer(IProduct)
|
||||
class Product(AdapterBase, BaseProduct):
|
||||
|
||||
_adapterAttributes = ('context', '__parent__',
|
||||
'shops', 'manufacturer', 'suppliers', 'categories',
|
||||
'recommendedAccessories')
|
||||
_contextAttributes = list(IProduct)
|
||||
_noexportAttributes = ('shops', 'categories', 'manufacturer', 'suppliers',
|
||||
'recommendedAccessories')
|
||||
|
||||
shops = ParentRelationSetProperty('shop.product')
|
||||
categories = ParentRelationSetProperty('category.product')
|
||||
manufacturer = ParentRelation('manufacturer.product')
|
||||
suppliers = ParentRelationSetProperty('supplier.product')
|
||||
recommendedAccessories = ChildRelationSetProperty('product.accessory')
|
||||
|
||||
@property
|
||||
def identifier(self):
|
||||
return self.productId
|
||||
|
||||
|
||||
@implementer(ICategory)
|
||||
class Category(AdapterBase, BaseCategory):
|
||||
|
||||
_adapterAttributes = ('context', '__parent__',
|
||||
'shops', 'subcategories', 'products',
|
||||
'accessorySubcategories', 'selectedProducts')
|
||||
_contextAttributes = list(ICategory)
|
||||
_noexportAttributes = ('shops', 'products', 'parentCategories',
|
||||
'subcategories', 'accessorySubcategories',
|
||||
'selectedProducts')
|
||||
|
||||
shops = ParentRelationSetProperty('shop.category')
|
||||
products = ChildRelationSetProperty('category.product')
|
||||
parentCategories = ParentRelationSetProperty('standard', ICategory)
|
||||
subcategories = ChildRelationSetProperty('standard')
|
||||
accessorySubcategories = ChildRelationSetProperty('category.accessory')
|
||||
selectedProducts = ChildRelationSetProperty('category.selected')
|
||||
|
||||
def getLongTitle(self):
|
||||
parent = u''
|
||||
defaultPredicate = self.getLoopsRoot().getConceptManager().getDefaultPredicate()
|
||||
pr = self.context.getParentRelations([defaultPredicate])
|
||||
for r in pr:
|
||||
if r.relevance >= 0.8:
|
||||
parent = r.first.title
|
||||
if parent:
|
||||
parent = u' - ' + parent
|
||||
return self.title + parent
|
||||
|
||||
def getIsActiveCacheId(self, shop=None, *args, **kw):
|
||||
shopId = shop is None and 'None' or shop.uid
|
||||
filter = kw.get('filter') or ''
|
||||
return 'commerce.category.isActive.%s.%s.%s' % (self.uid, shopId, filter)
|
||||
|
||||
@cache(getIsActiveCacheId, lifetime=93600)
|
||||
def isActive(self, shop=None, filter=None):
|
||||
depth = 0
|
||||
for cat in self.subcategories:
|
||||
if ICategory.providedBy(cat):
|
||||
depth = max(depth, int(cat.isActive(shop, filter=filter)))
|
||||
if depth:
|
||||
return depth + 1
|
||||
for prod in self.products:
|
||||
if prod.isActive(shop, filter=filter):
|
||||
return True
|
||||
return False
|
||||
|
||||
def getAccessoryParents(self):
|
||||
pred = self.getLoopsRoot().getConceptManager()['category.accessory']
|
||||
for c in self.context.getParents([pred]):
|
||||
yield adapted(c)
|
||||
|
||||
def getAllProductsCacheId(self, shop=None, *args, **kw):
|
||||
shopId = shop is None and 'None' or shop.uid
|
||||
filter = kw.get('filter') or ''
|
||||
return 'commerce.category.allProducts.%s.%s.%s' % (self.uid, shopId, filter)
|
||||
|
||||
@cache(getAllProductsCacheId, lifetime=93600)
|
||||
def getAllProductsUids(self, shop=None, filter=None):
|
||||
intids = component.getUtility(IIntIds)
|
||||
result = [util.getUidForObject(baseObject(p), intids)
|
||||
for p in self.products
|
||||
if p.isActive(shop, filter)]
|
||||
for c in self.subcategories:
|
||||
result.extend(c.getAllProductsUids())
|
||||
return result
|
||||
|
||||
|
||||
class Manufacturer(AdapterBase, Manufacturer):
|
||||
|
||||
_contextAttributes = list(IManufacturer)
|
||||
_noexportAttributes = ('products',)
|
||||
|
||||
products = ChildRelationSetProperty('manufacturer.product')
|
||||
|
||||
def getIsActiveCacheId(self, shop, *args, **kw):
|
||||
shopId = shop is None and 'None' or shop.uid
|
||||
filter = kw.get('filter') or ''
|
||||
return 'commerce.manufacturer.isActive.%s.%s.%s' % (self.uid, shopId, filter)
|
||||
|
||||
@cache(getIsActiveCacheId, lifetime=72000)
|
||||
def isActive(self, shop=None, filter=None):
|
||||
for prod in self.products:
|
||||
if prod.isActive(shop, filter=filter):
|
||||
return True
|
||||
|
||||
|
||||
class Supplier(AdapterBase, Supplier):
|
||||
|
||||
_contextAttributes = list(ISupplier)
|
||||
_noexportAttributes = ('products',)
|
||||
|
||||
products = ChildRelationSetProperty('supplier.product')
|
||||
|
58
cyberapps/commerce/setup.py
Normal file
58
cyberapps/commerce/setup.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
# cyberapps.commerce.setup
|
||||
|
||||
""" Automatic setup of a loops site for the commerce package.
|
||||
"""
|
||||
|
||||
from zope.component import adapts
|
||||
|
||||
from cyberapps.commerce.order import OrderItem
|
||||
from cybertools.commerce.interfaces import IShop, IProduct, ICategory
|
||||
from cybertools.commerce.interfaces import ICustomer, IOrder
|
||||
from cybertools.tracking.btree import TrackingStorage
|
||||
from loops.concept import Concept, ConceptManager
|
||||
from loops.interfaces import ITypeConcept
|
||||
from loops.setup import SetupManager as BaseSetupManager
|
||||
|
||||
|
||||
class SetupManager(BaseSetupManager):
|
||||
|
||||
def setup(self):
|
||||
concepts = self.context.getConceptManager()
|
||||
type = concepts.getTypeConcept()
|
||||
predicate = concepts.getPredicateType()
|
||||
customers = self.addObject(self.context, ConceptManager, 'customers')
|
||||
orders = self.addObject(self.context, ConceptManager, 'orders')
|
||||
# type concepts:
|
||||
tShop = self.addAndConfigureObject(concepts, Concept, 'shop',
|
||||
title=u'Shop', conceptType=type, typeInterface=IShop)
|
||||
tProduct = self.addAndConfigureObject(concepts, Concept, 'product',
|
||||
title=u'Product', conceptType=type, typeInterface=IProduct)
|
||||
tCategory = self.addAndConfigureObject(concepts, Concept, 'category',
|
||||
title=u'Category', conceptType=type, typeInterface=ICategory)
|
||||
tCustomer = self.addAndConfigureObject(concepts, Concept, 'customer',
|
||||
title=u'Customer', conceptType=type, typeInterface=ICustomer)
|
||||
tOrder = self.addAndConfigureObject(concepts, Concept, 'order',
|
||||
title=u'Order', conceptType=type, typeInterface=IOrder)
|
||||
# predicates:
|
||||
category_product = self.addObject(concepts, Concept, 'category.product',
|
||||
title=u'category <- product', conceptType=predicate)
|
||||
category_accessory = self.addObject(concepts, Concept, 'category.accessory',
|
||||
title=u'category <- accessory subcategory', conceptType=predicate)
|
||||
#category_selected = self.addObject(concepts, Concept, 'category.selected',
|
||||
# title=u'category <- selected product', conceptType=predicate)
|
||||
product_accessory = self.addObject(concepts, Concept, 'product.accessory',
|
||||
title=u'product <- accessory', conceptType=predicate)
|
||||
shop_product = self.addObject(concepts, Concept, 'shop.product',
|
||||
title=u'shop <- product', conceptType=predicate)
|
||||
shop_customer = self.addObject(concepts, Concept, 'shop.customer',
|
||||
title=u'shop <- customer', conceptType=predicate)
|
||||
#shop_order = self.addObject(concepts, Concept, 'shop.order',
|
||||
# title=u'shop <- order', conceptType=predicate)
|
||||
manufacturer_product = self.addObject(concepts, Concept, 'manufacturer.product',
|
||||
title=u'manufacturer <- product', conceptType=predicate)
|
||||
customer_order = self.addObject(concepts, Concept, 'customer.order',
|
||||
title=u'customer <- order', conceptType=predicate)
|
||||
# records:
|
||||
records = self.context.getRecordManager()
|
||||
if 'orderitems' not in records:
|
||||
records['orderitems'] = TrackingStorage(trackFactory=OrderItem)
|
31
cyberapps/commerce/shop.py
Normal file
31
cyberapps/commerce/shop.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# cyberapps.commerce.shop
|
||||
|
||||
""" Shop adapter.
|
||||
"""
|
||||
|
||||
from zope.interface import implementer
|
||||
from zope.traversing.api import getName
|
||||
|
||||
from cyberapps.commerce.interfaces import IShop
|
||||
from cybertools.commerce.shop import Shop as BaseShop
|
||||
from loops.common import AdapterBase, ChildRelationSetProperty
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
||||
TypeInterfaceSourceList.typeInterfaces += (IShop,)
|
||||
|
||||
|
||||
@implementer(IShop)
|
||||
class Shop(AdapterBase, BaseShop):
|
||||
|
||||
_adapterAttributes = ('context', '__parent__',
|
||||
'products', 'categories', 'suppliers', 'customers')
|
||||
_contextAttributes = list(IShop)
|
||||
_noexportAttributes = ('products', 'customers')
|
||||
|
||||
products = ChildRelationSetProperty('shop.product')
|
||||
customers = ChildRelationSetProperty('shop.customer')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return getName(self.context)
|
55
cyberapps/commerce/tests.py
Executable file
55
cyberapps/commerce/tests.py
Executable file
|
@ -0,0 +1,55 @@
|
|||
# $Id$
|
||||
|
||||
import unittest, doctest
|
||||
from zope.testing.doctestunit import DocFileSuite
|
||||
from zope import component
|
||||
from zope.interface.verify import verifyClass
|
||||
|
||||
from cyberapps.commerce.customer import Customer, Address
|
||||
from cyberapps.commerce.interfaces import IShop, IProduct, ICategory
|
||||
from cyberapps.commerce.interfaces import ICustomer, IAddress, IOrder
|
||||
from cyberapps.commerce.order import Order, OrderItems
|
||||
from cyberapps.commerce.product import Product, Category
|
||||
from cyberapps.commerce.shop import Shop
|
||||
from cyberapps.commerce.setup import SetupManager
|
||||
from cybertools.commerce.interfaces import IProduct
|
||||
from loops.interfaces import ILoops
|
||||
from loops.setup import ISetupManager
|
||||
from loops.tests.setup import TestSite as BaseTestSite
|
||||
|
||||
|
||||
class TestSite(BaseTestSite):
|
||||
|
||||
def __init__(self, site):
|
||||
self.site = site
|
||||
|
||||
def setup(self):
|
||||
component.provideAdapter(SetupManager, (ILoops,), ISetupManager,
|
||||
name='cyberapps.commerce')
|
||||
component.provideAdapter(Shop, provides=IShop)
|
||||
component.provideAdapter(Product, provides=IProduct)
|
||||
component.provideAdapter(Category, provides=ICategory)
|
||||
component.provideAdapter(Customer, provides=ICustomer)
|
||||
component.provideAdapter(Address, provides=IAddress)
|
||||
component.provideAdapter(Order, provides=IOrder)
|
||||
component.provideAdapter(OrderItems)
|
||||
concepts, resources, views = self.baseSetup()
|
||||
return concepts, resources, views
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
"Basic tests for the cyberapps.commerce package."
|
||||
|
||||
def testSomething(self):
|
||||
pass
|
||||
|
||||
|
||||
def test_suite():
|
||||
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test),
|
||||
DocFileSuite('README.txt', optionflags=flags),
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='test_suite')
|
23
cyberapps/commerce/util.py
Normal file
23
cyberapps/commerce/util.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# cyberapps.commerce.util
|
||||
|
||||
""" Utility functions.
|
||||
"""
|
||||
|
||||
from email.mime.text import MIMEText
|
||||
from zope import component
|
||||
from zope.i18nmessageid import MessageFactory
|
||||
from zope.sendmail.interfaces import IMailDelivery
|
||||
|
||||
|
||||
_ = MessageFactory('cyberapps.commerce')
|
||||
|
||||
|
||||
def sendEMail(subject, message, sender, recipients):
|
||||
if isinstance(message, unicode):
|
||||
message = message.encode('UTF-8')
|
||||
msg = MIMEText(message, 'plain', 'utf-8')
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = sender
|
||||
msg['To'] = ', '.join(recipients)
|
||||
mailhost = component.getUtility(IMailDelivery, 'Mail')
|
||||
mailhost.send(sender, recipients, msg.as_string())
|
Loading…
Add table
Reference in a new issue