X-Git-Url: https://scm.cri.ensmp.fr/git/photoprint.git/blobdiff_plain/ccaba0f54cbd41250107d36a49154353425da8b3..b521f0db73266a7bd6b69e6a9891046a03c482ee:/Products/photoprint/tool.py diff --git a/Products/photoprint/tool.py b/Products/photoprint/tool.py new file mode 100755 index 0000000..ce244b5 --- /dev/null +++ b/Products/photoprint/tool.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- +####################################################################################### +# Copyright © 2009 Benoît Pin # +# 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)