1 # -*- coding: utf-8 -*-
2 ############################################################
3 # Copyright © 2009 Benoît PIN <pinbe@luxia.fr> #
4 # Cliché - http://luxia.fr #
6 # This program is free software; you can redistribute it #
7 # and/or modify it under the terms of the Creative Commons #
8 # "Attribution-Noncommercial 2.0 Generic" #
9 # http://creativecommons.org/licenses/by-nc/2.0/ #
10 ############################################################
12 Photo print tool. Used to link photo to print orders.
18 from AccessControl
import ClassSecurityInfo
19 from AccessControl
.requestmethod
import postonly
20 from Acquisition
import aq_base
, aq_inner
21 from Globals
import InitializeClass
22 from OFS
.OrderedFolder
import OrderedFolder
23 from Products
.CMFCore
.utils
import UniqueObject
, getToolByName
24 from permissions
import ManagePrintOrderTemplate
25 from price
import Price
26 from utils
import Message
as _
27 from Products
.Plinn
.utils
import makeValidId
28 from zope
.component
import getUtility
29 from zope
.component
.interfaces
import IFactory
30 from DateTime
import DateTime
31 from Products
.Plinn
.utils
import _sudo
34 PRINTING_OPTIONS_ID
= 'printingOptions'
35 COPIES_COUNTERS
= '_copies_counters'
39 class PhotoPrintTool(UniqueObject
, OrderedFolder
) :
41 Provide utilities to configure possible printing works
42 over photo of the portal.
45 id = 'portal_photo_print'
46 meta_type
= 'Photo print tool'
48 security
= ClassSecurityInfo()
50 incomingOrderPath
= 'commandes'
51 no_shipping_threshold
= 150
56 _transaction_id_counter
= 0
58 _properties
= OrderedFolder
._properties
+ (
59 {'id' : 'incomingOrderPath', 'type' : 'string', 'mode' : 'w'},
60 {'id' : 'no_shipping_threshold', 'type' : 'int', 'mode' : 'w'},
61 {'id' : 'shipping', 'type' : 'float', 'mode' : 'w'},
62 {'id' : 'shipping_vat', 'type' : 'float', 'mode' : 'w'},
63 {'id' : 'store_name', 'type' : 'string', 'mode' : 'w'}
67 security
.declarePublic('getPrintingOptionsFor')
68 def getPrintingOptionsFor(self
, ob
) :
69 "returns printing options for the given ob."
70 optionsContainer
= getattr(aq_inner(ob
), PRINTING_OPTIONS_ID
, None)
71 if optionsContainer
is None :
74 counters
= self
.getCountersFor(ob
)
75 if counters
.get(SOLD_OUT
) :
79 for o
in optionsContainer
.objectValues() :
80 if o
.maxCopies
== 0 or \
81 counters
.get(o
.productReference
, 0) < o
.maxCopies
:
86 security
.declarePublic('getPrintingOptionsContainerFor')
87 def getPrintingOptionsContainerFor(self
, ob
):
88 """getPrintingOptionsContainerFor
90 return getattr(ob
, PRINTING_OPTIONS_ID
, None)
92 security
.declarePrivate('getCountersFor')
93 def getCountersFor(self
, ob
):
94 if hasattr(ob
.aq_self
, COPIES_COUNTERS
) :
95 return getattr(ob
, COPIES_COUNTERS
)
100 security
.declareProtected(ManagePrintOrderTemplate
, 'createPrintingOptionsContainer')
101 def createPrintingOptionsContainer(self
, ob
):
102 container
= PrintingOptionsContainer()
103 setattr(ob
, PRINTING_OPTIONS_ID
, container
)
104 return getattr(ob
, PRINTING_OPTIONS_ID
)
106 security
.declareProtected(ManagePrintOrderTemplate
, 'deletePrintingOptionsContainer')
107 def deletePrintingOptionsContainer(self
, ob
):
108 if not self
.hasPrintingOptions(ob
) :
109 raise ValueError( _('No printing options found at %r') % ob
.absolute_url() )
111 delattr(ob
, PRINTING_OPTIONS_ID
)
113 security
.declareProtected(ManagePrintOrderTemplate
, 'hasPrintingOptions')
114 def hasPrintingOptions(self
, ob
):
115 """ return boolean that instruct if there's printing
116 options especially defined on ob
118 return hasattr(aq_base(ob
), PRINTING_OPTIONS_ID
)
121 security
.declareProtected(ManagePrintOrderTemplate
, 'getPrintingOptionsSrc')
122 def getPrintingOptionsSrc(self
, ob
) :
123 optionsContainer
= getattr(ob
, PRINTING_OPTIONS_ID
, None)
124 if optionsContainer
is None :
126 src
= optionsContainer
.aq_inner
.aq_parent
129 security
.declareProtected(ManagePrintOrderTemplate
, 'getPrintOrderOptionsContainerFor')
130 def getPrintOrderOptionsContainerFor(self
, ob
) :
132 returns the printing options container or None.
134 if hasattr(aq_base(ob
), PRINTING_OPTIONS_ID
) :
135 return getattr(ob
, PRINTING_OPTIONS_ID
)
137 security
.declareProtected(ManagePrintOrderTemplate
, 'addPrintOrderTemplate')
139 def addPrintOrderTemplate(self
143 , productReference
=''
149 title
, maxCopies
, price
, VATRate
= PhotoPrintTool
._ckeckTemplateParams
(title
, maxCopies
, price
, VATRate
)
151 container
= getattr(ob
, PRINTING_OPTIONS_ID
)
153 id = makeValidId(container
, title
)
155 factory
= getUtility(IFactory
, 'photoprint.order_template')
156 orderTemplate
= factory( id
158 , description
=description
159 , productReference
=productReference
160 , maxCopies
=maxCopies
164 container
._setObject
(id, orderTemplate
)
165 return orderTemplate
.__of
__(container
)
168 security
.declareProtected(ManagePrintOrderTemplate
, 'editPrintOrderTemplate')
170 def editPrintOrderTemplate(self
, ob
, id, REQUEST
=None, **kw
):
171 container
= self
.getPrintingOptionsContainerFor(ob
)
172 orderTemplate
= getattr(container
, id)
175 title
, description
, productReference
, maxCopies
, price
, VATRate
= \
176 g('title', ''), g('description', ''), g('productReference'), g('maxCopies',0), g('price',0), g('VATRate', 0)
177 title
, maxCopies
, price
, VATRate
= PhotoPrintTool
._ckeckTemplateParams
(title
, maxCopies
, price
, VATRate
)
179 orderTemplate
.edit( title
=title
180 , description
=description
181 , productReference
=productReference
182 , maxCopies
= maxCopies
189 def _ckeckTemplateParams(title
, maxCopies
, price
, VATRate
) :
190 title
= title
.strip()
193 raise ValueError(_(u
'You must enter a title.'))
195 maxCopies
= int(maxCopies
)
197 raise ValueError(_(u
'You must enter an integer number\nfor the maximum number of copies.'))
199 raise ValueError(_(u
'You must enter a positive value\nfor the maximum number of copies.'))
201 price
= float(price
.replace(',', '.'))
203 raise ValueError(_(u
'You must enter a numeric value for the price.'))
206 VATRate
= float(VATRate
.replace(',', '.')) / 100
208 raise ValueError(_(u
'You must enter a numeric value for the VAT rate.'))
210 return title
, maxCopies
, price
, VATRate
212 security
.declarePublic('addPrintOrder')
213 def addPrintOrder(self
, cart
):
214 utool
= getToolByName(self
, 'portal_url')
215 portal
= utool
.getPortalObject()
216 ttool
= getToolByName(portal
, 'portal_types')
218 baseContainer
= portal
.unrestrictedTraverse(self
.getProperty('incomingOrderPath'), None)
219 if baseContainer
is None:
220 parts
= self
.getProperty('incomingOrderPath').split('/')
221 baseContainer
= portal
223 if not hasattr(baseContainer
.aq_base
, id) :
224 id = _sudo(lambda:ttool
.constructContent('Order Folder', baseContainer
, id))
225 baseContainer
= getattr(baseContainer
, id)
228 monthId
= now
.strftime('%Y-%m')
229 if not hasattr(baseContainer
.aq_base
, monthId
) :
230 monthId
= _sudo(lambda:ttool
.constructContent('Order Folder', baseContainer
, monthId
))
232 container
= getattr(baseContainer
, monthId
)
234 self
._order
_counter
+= 1
235 id = '%s-%d' % (monthId
, self
._order
_counter
)
236 id = container
.invokeFactory('Order', id)
237 ob
= getattr(container
,id)
241 security
.declarePublic('getShippingFeesFor')
242 def getShippingFeesFor(self
, shippable
=None, price
=None):
245 # for the moment, shippable objet must provide a 'price' attribute
247 if shippable
and price
:
248 raise AttributeError("'shippable' and 'price' are mutually exclusive.")
251 amount
= shippable
.price
.getValues()['taxed']
253 amount
= price
.getValues()['taxed']
255 threshold
= self
.getProperty('no_shipping_threshold')
257 if amount
< threshold
:
258 fees
= Price(self
.getProperty('shipping')
259 , self
.getProperty('shipping_vat'))
264 security
.declarePrivate('getNextTransactionId')
265 def getNextTransactionId(self
):
266 trid
= self
._transaction
_id
_counter
267 trid
= (trid
+ 1) % 1000000
268 self
._transaction
_id
_counter
= trid
269 trid
= str(trid
).zfill(6)
273 InitializeClass(PhotoPrintTool
)
276 class PrintingOptionsContainer(OrderedFolder
) :
277 meta_type
= 'Printing options container'
278 security
= ClassSecurityInfo()
281 self
.id = PRINTING_OPTIONS_ID
283 def __getitem__(self
, k
) :
284 sd
= context
.session_data_manager
.getSessionData(create
= 1)