--- /dev/null
+# -*- coding: utf-8 -*-
+#######################################################################################
+# Copyright © 2009 Benoît Pin <pin@cri.ensmp.fr> #
+# Plinn - http://plinn.org #
+# #
+# #
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
+#######################################################################################
+"""
+Photo print tool. Used to link photo to print orders.
+
+
+
+"""
+
+from AccessControl import ClassSecurityInfo
+from AccessControl.requestmethod import postonly
+from Acquisition import aq_base, aq_inner
+from Globals import InitializeClass
+from OFS.OrderedFolder import OrderedFolder
+from Products.CMFCore.utils import UniqueObject, getToolByName
+from permissions import ManagePrintOrderTemplate
+from price import Price
+from utils import Message as _
+from Products.Plinn.utils import makeValidId
+from zope.component import getUtility
+from zope.component.interfaces import IFactory
+from DateTime import DateTime
+from Products.Plinn.utils import _sudo
+
+
+PRINTING_OPTIONS_ID = 'printingOptions'
+COPIES_COUNTERS = '_copies_counters'
+SOLD_OUT = 'SOLD_OUT'
+
+
+class PhotoPrintTool(UniqueObject, OrderedFolder) :
+ """
+ Provide utilities to configure possible printing works
+ over photo of the portal.
+ """
+
+ id = 'portal_photo_print'
+ meta_type = 'Photo print tool'
+
+ security = ClassSecurityInfo()
+
+ incomingOrderPath = 'commandes'
+ no_shipping_threshold = 150
+ shipping = 6.0
+ shipping_vat = 0.196
+ store_name = ''
+ _order_counter = 0
+ _transaction_id_counter = 0
+
+ _properties = OrderedFolder._properties + (
+ {'id' : 'incomingOrderPath', 'type' : 'string', 'mode' : 'w'},
+ {'id' : 'no_shipping_threshold', 'type' : 'int', 'mode' : 'w'},
+ {'id' : 'shipping', 'type' : 'float', 'mode' : 'w'},
+ {'id' : 'shipping_vat', 'type' : 'float', 'mode' : 'w'},
+ {'id' : 'store_name', 'type' : 'string', 'mode' : 'w'}
+ )
+
+
+ security.declarePublic('getPrintingOptionsFor')
+ def getPrintingOptionsFor(self, ob) :
+ "returns printing options for the given ob."
+ optionsContainer = getattr(aq_inner(ob), PRINTING_OPTIONS_ID, None)
+ if optionsContainer is None :
+ return None
+
+ counters = self.getCountersFor(ob)
+ if counters.get(SOLD_OUT) :
+ return None
+
+ options = []
+ for o in optionsContainer.objectValues() :
+ if o.maxCopies == 0 or \
+ counters.get(o.productReference, 0) < o.maxCopies :
+ options.append(o)
+
+ return options
+
+ security.declarePublic('getPrintingOptionsContainerFor')
+ def getPrintingOptionsContainerFor(self, ob):
+ """getPrintingOptionsContainerFor
+ """
+ return getattr(ob, PRINTING_OPTIONS_ID, None)
+
+ security.declarePrivate('getCountersFor')
+ def getCountersFor(self, ob):
+ if hasattr(ob.aq_self, COPIES_COUNTERS) :
+ return getattr(ob, COPIES_COUNTERS)
+ else :
+ return {}
+
+
+ security.declareProtected(ManagePrintOrderTemplate, 'createPrintingOptionsContainer')
+ def createPrintingOptionsContainer(self, ob):
+ container = PrintingOptionsContainer()
+ setattr(ob, PRINTING_OPTIONS_ID, container)
+ return getattr(ob, PRINTING_OPTIONS_ID)
+
+ security.declareProtected(ManagePrintOrderTemplate, 'deletePrintingOptionsContainer')
+ def deletePrintingOptionsContainer(self, ob):
+ if not self.hasPrintingOptions(ob) :
+ raise ValueError( _('No printing options found at %r') % ob.absolute_url() )
+ else :
+ delattr(ob, PRINTING_OPTIONS_ID)
+
+ security.declareProtected(ManagePrintOrderTemplate, 'hasPrintingOptions')
+ def hasPrintingOptions(self, ob):
+ """ return boolean that instruct if there's printing
+ options especially defined on ob
+ """
+ return hasattr(aq_base(ob), PRINTING_OPTIONS_ID)
+
+
+ security.declareProtected(ManagePrintOrderTemplate, 'getPrintingOptionsSrc')
+ def getPrintingOptionsSrc(self, ob) :
+ optionsContainer = getattr(ob, PRINTING_OPTIONS_ID, None)
+ if optionsContainer is None :
+ return None
+ src = optionsContainer.aq_inner.aq_parent
+ return src
+
+ security.declareProtected(ManagePrintOrderTemplate, 'getPrintOrderOptionsContainerFor')
+ def getPrintOrderOptionsContainerFor(self, ob) :
+ """
+ returns the printing options container or None.
+ """
+ if hasattr(aq_base(ob), PRINTING_OPTIONS_ID) :
+ return getattr(ob, PRINTING_OPTIONS_ID)
+
+ security.declareProtected(ManagePrintOrderTemplate, 'addPrintOrderTemplate')
+ @postonly
+ def addPrintOrderTemplate(self
+ , ob
+ , title=''
+ , description=''
+ , productReference=''
+ , maxCopies=0
+ , price=0
+ , VATRate=0
+ , REQUEST=None):
+
+ title, maxCopies, price, VATRate = PhotoPrintTool._ckeckTemplateParams(title, maxCopies, price, VATRate)
+
+ container = getattr(ob, PRINTING_OPTIONS_ID)
+
+ id = makeValidId(container, title)
+
+ factory = getUtility(IFactory, 'photoprint.order_template')
+ orderTemplate = factory( id
+ , title=title
+ , description=description
+ , productReference=productReference
+ , maxCopies=maxCopies
+ , price=price
+ , VATRate=VATRate
+ )
+ container._setObject(id, orderTemplate)
+ return orderTemplate.__of__(container)
+
+
+ security.declareProtected(ManagePrintOrderTemplate, 'editPrintOrderTemplate')
+ @postonly
+ def editPrintOrderTemplate(self, ob, id, REQUEST=None, **kw):
+ container = self.getPrintingOptionsContainerFor(ob)
+ orderTemplate = getattr(container, id)
+
+ g = kw.get
+ title, description, productReference, maxCopies, price, VATRate = \
+ g('title', ''), g('description', ''), g('productReference'), g('maxCopies',0), g('price',0), g('VATRate', 0)
+ title, maxCopies, price, VATRate = PhotoPrintTool._ckeckTemplateParams(title, maxCopies, price, VATRate)
+
+ orderTemplate.edit( title=title
+ , description=description
+ , productReference=productReference
+ , maxCopies = maxCopies
+ , price=price
+ , VATRate=VATRate)
+
+ return orderTemplate
+
+ @staticmethod
+ def _ckeckTemplateParams(title, maxCopies, price, VATRate) :
+ title = title.strip()
+
+ if not title :
+ raise ValueError(_(u'You must enter a title.'))
+ try :
+ maxCopies = int(maxCopies)
+ except ValueError :
+ raise ValueError(_(u'You must enter an integer number\nfor the maximum number of copies.'))
+ if maxCopies < 0 :
+ raise ValueError(_(u'You must enter a positive value\nfor the maximum number of copies.'))
+ try :
+ price = float(price.replace(',', '.'))
+ except ValueError :
+ raise ValueError(_(u'You must enter a numeric value for the price.'))
+
+ try :
+ VATRate = float(VATRate.replace(',', '.')) / 100
+ except ValueError :
+ raise ValueError(_(u'You must enter a numeric value for the VAT rate.'))
+
+ return title, maxCopies, price, VATRate
+
+ security.declarePublic('addPrintOrder')
+ def addPrintOrder(self, cart):
+ utool = getToolByName(self, 'portal_url')
+ portal = utool.getPortalObject()
+ ttool = getToolByName(portal, 'portal_types')
+
+ baseContainer = portal.unrestrictedTraverse(self.getProperty('incomingOrderPath'), None)
+ if baseContainer is None:
+ parts = self.getProperty('incomingOrderPath').split('/')
+ baseContainer = portal
+ for id in parts :
+ if not hasattr(baseContainer.aq_base, id) :
+ id = _sudo(lambda:ttool.constructContent('Order Folder', baseContainer, id))
+ baseContainer = getattr(baseContainer, id)
+
+ now = DateTime()
+ monthId = now.strftime('%Y-%m')
+ if not hasattr(baseContainer.aq_base, monthId) :
+ monthId = _sudo(lambda:ttool.constructContent('Order Folder', baseContainer, monthId))
+
+ container = getattr(baseContainer, monthId)
+
+ self._order_counter += 1
+ id = '%s-%d' % (monthId, self._order_counter)
+ id = container.invokeFactory('Order', id)
+ ob = getattr(container,id)
+ ob.loadCart(cart)
+ return ob
+
+ security.declarePublic('getShippingFeesFor')
+ def getShippingFeesFor(self, shippable=None, price=None):
+ # returns Fees
+ # TODO: use adapters
+ # for the moment, shippable objet must provide a 'price' attribute
+
+ if shippable and price :
+ raise AttributeError("'shippable' and 'price' are mutually exclusive.")
+
+ if shippable :
+ amount = shippable.price.getValues()['taxed']
+ else :
+ amount = price.getValues()['taxed']
+
+ threshold = self.getProperty('no_shipping_threshold')
+
+ if amount < threshold :
+ fees = Price(self.getProperty('shipping')
+ , self.getProperty('shipping_vat'))
+ else :
+ fees = Price(0,0)
+ return fees
+
+ security.declarePrivate('getNextTransactionId')
+ def getNextTransactionId(self):
+ trid = self._transaction_id_counter
+ trid = (trid + 1) % 1000000
+ self._transaction_id_counter = trid
+ trid = str(trid).zfill(6)
+ return trid
+
+
+InitializeClass(PhotoPrintTool)
+
+
+class PrintingOptionsContainer(OrderedFolder) :
+ meta_type = 'Printing options container'
+ security = ClassSecurityInfo()
+
+ def __init__(self) :
+ self.id = PRINTING_OPTIONS_ID
+
+ def __getitem__(self, k) :
+ sd = context.session_data_manager.getSessionData(create = 1)