1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Copyright © 2009 Benoît Pin <pin@cri.ensmp.fr> #
4 # Plinn - http://plinn.org #
7 # This program is free software; you can redistribute it and/or #
8 # modify it under the terms of the GNU General Public License #
9 # as published by the Free Software Foundation; either version 2 #
10 # of the License, or (at your option) any later version. #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program; if not, write to the Free Software #
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
20 #######################################################################################
22 Photo print tool. Used to link photo to print orders.
28 from AccessControl
import ClassSecurityInfo
29 from AccessControl
.requestmethod
import postonly
30 from Acquisition
import aq_base
, aq_inner
31 from Globals
import InitializeClass
32 from OFS
.OrderedFolder
import OrderedFolder
33 from Products
.CMFCore
.utils
import UniqueObject
, getToolByName
34 from permissions
import ManagePrintOrderTemplate
35 from price
import Price
36 from utils
import Message
as _
37 from Products
.Plinn
.utils
import makeValidId
38 from zope
.component
import getUtility
39 from zope
.component
.interfaces
import IFactory
40 from DateTime
import DateTime
41 from Products
.Plinn
.utils
import _sudo
44 PRINTING_OPTIONS_ID
= 'printingOptions'
45 COPIES_COUNTERS
= '_copies_counters'
49 class PhotoPrintTool(UniqueObject
, OrderedFolder
) :
51 Provide utilities to configure possible printing works
52 over photo of the portal.
55 id = 'portal_photo_print'
56 meta_type
= 'Photo print tool'
58 security
= ClassSecurityInfo()
60 incomingOrderPath
= 'commandes'
61 no_shipping_threshold
= 150
66 _transaction_id_counter
= 0
68 _properties
= OrderedFolder
._properties
+ (
69 {'id' : 'incomingOrderPath', 'type' : 'string', 'mode' : 'w'},
70 {'id' : 'no_shipping_threshold', 'type' : 'int', 'mode' : 'w'},
71 {'id' : 'shipping', 'type' : 'float', 'mode' : 'w'},
72 {'id' : 'shipping_vat', 'type' : 'float', 'mode' : 'w'},
73 {'id' : 'store_name', 'type' : 'string', 'mode' : 'w'}
77 security
.declarePublic('getPrintingOptionsFor')
78 def getPrintingOptionsFor(self
, ob
) :
79 "returns printing options for the given ob."
80 optionsContainer
= getattr(aq_inner(ob
), PRINTING_OPTIONS_ID
, None)
81 if optionsContainer
is None :
84 counters
= self
.getCountersFor(ob
)
85 if counters
.get(SOLD_OUT
) :
89 for o
in optionsContainer
.objectValues() :
90 if o
.maxCopies
== 0 or \
91 counters
.get(o
.productReference
, 0) < o
.maxCopies
:
96 security
.declarePublic('getPrintingOptionsContainerFor')
97 def getPrintingOptionsContainerFor(self
, ob
):
98 """getPrintingOptionsContainerFor
100 return getattr(ob
, PRINTING_OPTIONS_ID
, None)
102 security
.declarePrivate('getCountersFor')
103 def getCountersFor(self
, ob
):
104 if hasattr(ob
.aq_self
, COPIES_COUNTERS
) :
105 return getattr(ob
, COPIES_COUNTERS
)
110 security
.declareProtected(ManagePrintOrderTemplate
, 'createPrintingOptionsContainer')
111 def createPrintingOptionsContainer(self
, ob
):
112 container
= PrintingOptionsContainer()
113 setattr(ob
, PRINTING_OPTIONS_ID
, container
)
114 return getattr(ob
, PRINTING_OPTIONS_ID
)
116 security
.declareProtected(ManagePrintOrderTemplate
, 'deletePrintingOptionsContainer')
117 def deletePrintingOptionsContainer(self
, ob
):
118 if not self
.hasPrintingOptions(ob
) :
119 raise ValueError( _('No printing options found at %r') % ob
.absolute_url() )
121 delattr(ob
, PRINTING_OPTIONS_ID
)
123 security
.declareProtected(ManagePrintOrderTemplate
, 'hasPrintingOptions')
124 def hasPrintingOptions(self
, ob
):
125 """ return boolean that instruct if there's printing
126 options especially defined on ob
128 return hasattr(aq_base(ob
), PRINTING_OPTIONS_ID
)
131 security
.declareProtected(ManagePrintOrderTemplate
, 'getPrintingOptionsSrc')
132 def getPrintingOptionsSrc(self
, ob
) :
133 optionsContainer
= getattr(ob
, PRINTING_OPTIONS_ID
, None)
134 if optionsContainer
is None :
136 src
= optionsContainer
.aq_inner
.aq_parent
139 security
.declareProtected(ManagePrintOrderTemplate
, 'getPrintOrderOptionsContainerFor')
140 def getPrintOrderOptionsContainerFor(self
, ob
) :
142 returns the printing options container or None.
144 if hasattr(aq_base(ob
), PRINTING_OPTIONS_ID
) :
145 return getattr(ob
, PRINTING_OPTIONS_ID
)
147 security
.declareProtected(ManagePrintOrderTemplate
, 'addPrintOrderTemplate')
149 def addPrintOrderTemplate(self
153 , productReference
=''
159 title
, maxCopies
, price
, VATRate
= PhotoPrintTool
._ckeckTemplateParams
(title
, maxCopies
, price
, VATRate
)
161 container
= getattr(ob
, PRINTING_OPTIONS_ID
)
163 id = makeValidId(container
, title
)
165 factory
= getUtility(IFactory
, 'photoprint.order_template')
166 orderTemplate
= factory( id
168 , description
=description
169 , productReference
=productReference
170 , maxCopies
=maxCopies
174 container
._setObject
(id, orderTemplate
)
175 return orderTemplate
.__of
__(container
)
178 security
.declareProtected(ManagePrintOrderTemplate
, 'editPrintOrderTemplate')
180 def editPrintOrderTemplate(self
, ob
, id, REQUEST
=None, **kw
):
181 container
= self
.getPrintingOptionsContainerFor(ob
)
182 orderTemplate
= getattr(container
, id)
185 title
, description
, productReference
, maxCopies
, price
, VATRate
= \
186 g('title', ''), g('description', ''), g('productReference'), g('maxCopies',0), g('price',0), g('VATRate', 0)
187 title
, maxCopies
, price
, VATRate
= PhotoPrintTool
._ckeckTemplateParams
(title
, maxCopies
, price
, VATRate
)
189 orderTemplate
.edit( title
=title
190 , description
=description
191 , productReference
=productReference
192 , maxCopies
= maxCopies
199 def _ckeckTemplateParams(title
, maxCopies
, price
, VATRate
) :
200 title
= title
.strip()
203 raise ValueError(_(u
'You must enter a title.'))
205 maxCopies
= int(maxCopies
)
207 raise ValueError(_(u
'You must enter an integer number\nfor the maximum number of copies.'))
209 raise ValueError(_(u
'You must enter a positive value\nfor the maximum number of copies.'))
211 price
= float(price
.replace(',', '.'))
213 raise ValueError(_(u
'You must enter a numeric value for the price.'))
216 VATRate
= float(VATRate
.replace(',', '.')) / 100
218 raise ValueError(_(u
'You must enter a numeric value for the VAT rate.'))
220 return title
, maxCopies
, price
, VATRate
222 security
.declarePublic('addPrintOrder')
223 def addPrintOrder(self
, cart
):
224 utool
= getToolByName(self
, 'portal_url')
225 portal
= utool
.getPortalObject()
226 ttool
= getToolByName(portal
, 'portal_types')
228 baseContainer
= portal
.unrestrictedTraverse(self
.getProperty('incomingOrderPath'), None)
229 if baseContainer
is None:
230 parts
= self
.getProperty('incomingOrderPath').split('/')
231 baseContainer
= portal
233 if not hasattr(baseContainer
.aq_base
, id) :
234 id = _sudo(lambda:ttool
.constructContent('Order Folder', baseContainer
, id))
235 baseContainer
= getattr(baseContainer
, id)
238 monthId
= now
.strftime('%Y-%m')
239 if not hasattr(baseContainer
.aq_base
, monthId
) :
240 monthId
= _sudo(lambda:ttool
.constructContent('Order Folder', baseContainer
, monthId
))
242 container
= getattr(baseContainer
, monthId
)
244 self
._order
_counter
+= 1
245 id = '%s-%d' % (monthId
, self
._order
_counter
)
246 id = container
.invokeFactory('Order', id)
247 ob
= getattr(container
,id)
251 security
.declarePublic('getShippingFeesFor')
252 def getShippingFeesFor(self
, shippable
=None, price
=None):
255 # for the moment, shippable objet must provide a 'price' attribute
257 if shippable
and price
:
258 raise AttributeError("'shippable' and 'price' are mutually exclusive.")
261 amount
= shippable
.price
.getValues()['taxed']
263 amount
= price
.getValues()['taxed']
265 threshold
= self
.getProperty('no_shipping_threshold')
267 if amount
< threshold
:
268 fees
= Price(self
.getProperty('shipping')
269 , self
.getProperty('shipping_vat'))
274 security
.declarePrivate('getNextTransactionId')
275 def getNextTransactionId(self
):
276 trid
= self
._transaction
_id
_counter
277 trid
= (trid
+ 1) % 1000000
278 self
._transaction
_id
_counter
= trid
279 trid
= str(trid
).zfill(6)
283 InitializeClass(PhotoPrintTool
)
286 class PrintingOptionsContainer(OrderedFolder
) :
287 meta_type
= 'Printing options container'
288 security
= ClassSecurityInfo()
291 self
.id = PRINTING_OPTIONS_ID
293 def __getitem__(self
, k
) :
294 sd
= context
.session_data_manager
.getSessionData(create
= 1)