Bugfix : reduce non homogène.
[photoprint.git] / cart.py
1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Copyright © 2009 Benoît Pin <pin@cri.ensmp.fr> #
4 # Plinn - http://plinn.org #
5 # #
6 # #
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. #
11 # #
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. #
16 # #
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 #######################################################################################
21 """ Cart definition used to store buyable prints
22
23 """
24 from Globals import InitializeClass, Persistent, PersistentMapping
25 from Acquisition import Implicit
26 from AccessControl import ModuleSecurityInfo
27 from Products.CMFCore.utils import getToolByName
28 from exceptions import SoldOutError, CartLockedError
29 from tool import COPIES_COUNTERS
30 from order import CopiesCounters
31
32 from logging import getLogger
33 console = getLogger('Products.photoprint.cart')
34
35 CART_ITEM_KEYS = ['cmf_uid', 'printing_template', 'quantity']
36
37 msecurity = ModuleSecurityInfo('Products.photoprint.cart')
38 msecurity.declarePublic('PrintCart')
39
40 class PrintCart(Persistent, Implicit) :
41 """
42 items are store like that:
43 {<uid>:
44 {<template>:quantity
45 ,...}
46 , ...
47 }
48 """
49
50 __allow_access_to_unprotected_subobjects__ = 1
51
52 def __init__(self) :
53 self._uids = PersistentMapping()
54 self._order = tuple() # products sequence order
55 self._shippingInfo = PersistentMapping()
56 self._confirmed = False
57 self.pendingOrderPath = ''
58
59 def setShippingInfo(self, **kw) :
60 self._shippingInfo.update(kw)
61
62 @property
63 def locked(self):
64 return self._confirmed
65
66 def append(self, context, item) :
67 if self.locked :
68 raise CartLockedError
69 assert isinstance(item, dict)
70 keys = item.keys()
71 keys.sort()
72 assert keys == CART_ITEM_KEYS
73
74 pptool = getToolByName(context, 'portal_photo_print')
75 uidh = getToolByName(context, 'portal_uidhandler')
76
77 uid = item['cmf_uid']
78 template = item['printing_template']
79 quantity = item['quantity']
80
81 photo = uidh.getObject(uid)
82 pOptions = pptool.getPrintingOptionsContainerFor(photo)
83 template = getattr(pOptions, template)
84 templateId = template.getId()
85
86 reference = template.productReference
87
88 # check / update counters
89 if template.maxCopies :
90 if not hasattr(photo.aq_base, COPIES_COUNTERS) :
91 setattr(photo, COPIES_COUNTERS, CopiesCounters())
92 counters = getattr(photo, COPIES_COUNTERS)
93 alreadySold = counters.get(reference)
94
95 if (alreadySold + quantity) > template.maxCopies :
96 raise SoldOutError(template.maxCopies - alreadySold)
97 else :
98 counters[reference] = alreadySold + quantity
99
100 if not self._uids.has_key(uid) :
101 self._uids[uid] = PersistentMapping()
102 self._order = self._order + (uid,)
103
104 if not self._uids[uid].has_key(templateId) :
105 self._uids[uid][templateId] = PersistentMapping()
106 self._uids[uid][templateId]['reference'] = reference
107 self._uids[uid][templateId]['quantity'] = 0
108
109 self._uids[uid][templateId]['quantity'] += quantity
110
111 def update(self, context, item) :
112 if self.locked :
113 raise CartLockedError
114 assert isinstance(item, dict)
115 keys = item.keys()
116 keys.sort()
117 assert keys == CART_ITEM_KEYS
118
119 pptool = getToolByName(context, 'portal_photo_print')
120 uidh = getToolByName(context, 'portal_uidhandler')
121
122 uid = item['cmf_uid']
123 template = item['printing_template']
124 quantity = item['quantity']
125
126 photo = uidh.getObject(uid)
127 pOptions = pptool.getPrintingOptionsContainerFor(photo)
128 template = getattr(pOptions, template)
129 templateId = template.getId()
130 reference = template.productReference
131
132 currentQuantity = self._uids[uid][templateId]['quantity']
133 delta = quantity - currentQuantity
134 if template.maxCopies :
135 counters = getattr(photo, COPIES_COUNTERS)
136 if delta > 0 :
137 already = counters[reference]
138 if (already + delta) > template.maxCopies :
139 raise SoldOutError(template.maxCopies - already)
140 counters[reference] += delta
141
142 self._uids[uid][templateId]['quantity'] += delta
143
144 def remove(self, context, uid, templateId) :
145 if self.locked :
146 raise CartLockedError
147 pptool = getToolByName(context, 'portal_photo_print')
148 uidh = getToolByName(context, 'portal_uidhandler')
149
150 photo = uidh.getObject(uid)
151 pOptions = pptool.getPrintingOptionsContainerFor(photo)
152 template = getattr(pOptions, templateId)
153 reference = template.productReference
154
155 quantity = self._uids[uid][templateId]['quantity']
156 if template.maxCopies :
157 counters = getattr(photo, COPIES_COUNTERS)
158 counters[reference] -= quantity
159
160 del self._uids[uid][templateId]
161 if not self._uids[uid] :
162 del self._uids[uid]
163 self._order = tuple([u for u in self._order if u != uid])
164
165
166 def __iter__(self) :
167 for uid in self._order :
168 item = {}
169 item['cmf_uid'] = uid
170 for templateId, rq in self._uids[uid].items() :
171 item['printing_template'] = templateId
172 item['quantity'] = rq['quantity']
173 yield item
174
175 def __nonzero__(self) :
176 return len(self._order) > 0
177