X-Git-Url: https://svn.cri.ensmp.fr/git/photoprint.git/blobdiff_plain/205a98c2f6e0548ced213fc330c1a0704e9c3c64..f4c99bcdc9425cde32f0323811cf737e0d567070:/order.py diff --git a/order.py b/order.py index 7b87275..6966ff3 100755 --- a/order.py +++ b/order.py @@ -30,6 +30,7 @@ from AccessControl import ClassSecurityInfo from AccessControl.requestmethod import postonly from zope.interface import implements from zope.component.factory import Factory +from persistent.list import PersistentList from OFS.SimpleItem import SimpleItem from ZTUtils import make_query from DateTime import DateTime @@ -41,12 +42,14 @@ from Products.Plinn.utils import getPreferredLanguages from interfaces import IPrintOrderTemplate, IPrintOrder from permissions import ManagePrintOrderTemplate, ManagePrintOrders from price import Price -from utils import Message as _ -from utils import translate from xml.dom.minidom import Document from tool import COPIES_COUNTERS from App.config import getConfiguration -from paypal.interface import PayPalInterface +try : + from paypal.interface import PayPalInterface + paypalAvailable = True +except ImportError : + paypalAvailable = False from logging import getLogger console = getLogger('Products.photoprint.order') @@ -180,16 +183,18 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : self.id = id self.items = [] self.quantity = 0 + self.discount = 0 # discount ratio in percent self.price = Price(0, 0) # billing and shipping addresses self.billing = PersistentMapping() self.shipping = PersistentMapping() self.shippingFees = Price(0,0) - self._paymentResponse = PersistentMapping() + self._paypalLog = PersistentList() @property def amountWithFees(self) : - return self.price + self.shippingFees + coeff = (100 - self.discount) / 100. + return self.price * coeff + self.shippingFees security.declareProtected(ModifyPortalContent, 'editBilling') @@ -220,6 +225,7 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : pptool = getToolByName(self, 'portal_photo_print') uidh = getToolByName(self, 'portal_uidhandler') mtool = getToolByName(self, 'portal_membership') + utool = getToolByName(self, 'portal_url') items = [] for item in cart : @@ -248,6 +254,9 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : counters.confirm(reference, quantity) self.items = tuple(items) + discount_script = getattr(utool.getPortalObject(), 'photoprint_discount', None) + if discount_script : + self.discount = discount_script(self.price, self.quantity) member = mtool.getAuthenticatedMember() mg = lambda name : member.getProperty(name, '') @@ -312,12 +321,10 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : portal = utool.getPortalObject() member = mtool.getAuthenticatedMember() - options = {#'PAYMENTREQUEST_0_AMT' : '99.55', # todo - 'PAYMENTREQUEST_0_CURRENCYCODE' : 'EUR', + options = {'PAYMENTREQUEST_0_CURRENCYCODE' : 'EUR', 'PAYMENTREQUEST_0_PAYMENTACTION' : 'Sale', 'RETURNURL' : '%s/photoprint_order_confirm' % self.absolute_url(), 'CANCELURL' : '%s/photoprint_order_cancel' % self.absolute_url(), - # 'CALLBACK' : TODO 'ALLOWNOTE' : 0, # The buyer is unable to enter a note to the merchant. 'HDRIMG' : '%s/logo.gif' % portal_url, 'EMAIL' : member.getProperty('email'), @@ -328,8 +335,6 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : 'GIFTRECEIPTENABLE' : 0, 'BUYEREMAILOPTINENABLE' : 0, # Do not enable buyer to provide email address. 'NOSHIPPING' : 1, # PayPal does not display shipping address fields whatsoever. - # 'PAYMENTREQUEST_0_NOTIFYURL' : TODO - 'PAYMENTREQUEST_0_SHIPTONAME' : self.billing['name'], 'PAYMENTREQUEST_0_SHIPTOSTREET' : self.billing['address'], 'PAYMENTREQUEST_0_SHIPTOCITY' : self.billing['city'], @@ -337,24 +342,25 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : 'PAYMENTREQUEST_0_SHIPTOPHONENUM' : self.billing['phone'], } - quantitySum = reduce(lambda a, b : a['quantity'] + b['quantity'], self.items) - priceSum = reduce(lambda a, b : a['unit_price'] * a['quantity'] + b['unit_price'] * b['quantity'], self.items) - priceValues = priceSum.getValues() - total = round(priceValues['taxed'], 2) - basePrice = round(priceValues['value'], 2) - tax = round(total - basePrice, 2) + if len(self.items) > 1 : + quantitySum = reduce(lambda a, b : a + b, [item['quantity'] for item in self.items]) + else : + quantitySum = self.items[0]['quantity'] + total = round(self.amountWithFees.getValues()['taxed'], 2) - options['L_PAYMENTREQUEST_0_NAME0'] = 'Commande realis photo ref. %s' % self.getId() - options['L_PAYMENTREQUEST_0_DESC0'] = 'Commande de %d tirages photo' % quantitySum + options['L_PAYMENTREQUEST_0_NAME0'] = 'Commande photo ref. %s' % self.getId() + if quantitySum == 1 : + options['L_PAYMENTREQUEST_0_DESC0'] = "Commande d'un tirage photographique" + else : + options['L_PAYMENTREQUEST_0_DESC0'] = 'Commande de %d tirages photographiques' % quantitySum options['L_PAYMENTREQUEST_0_AMT0'] = total - # options['L_PAYMENTREQUEST_0_TAXAMT0'] = tax - # options['L_PAYMENTREQUEST_0_QTY%d' % n] = 1 + options['PAYMENTINFO_0_SHIPPINGAMT'] = round(self.shippingFees.getValues()['taxed'], 2) options['PAYMENTREQUEST_0_AMT'] = total ppi = self._initPayPalInterface() response = ppi.set_express_checkout(**options) response = PrintOrder.recordifyPPResp(response) - # self._paypalLog.append(response) + self._paypalLog.append(response) response['url'] = ppi.generate_express_checkout_redirect_url(response['TOKEN']) console.info(options) console.info(response) @@ -364,7 +370,7 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : def ppGetExpressCheckoutDetails(self, token) : ppi = self._initPayPalInterface() response = ppi.get_express_checkout_details(TOKEN=token) - response = Registration.recordifyPPResp(response) + response = PrintOrder.recordifyPPResp(response) self._paypalLog.append(response) return response @@ -376,7 +382,7 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : PAYMENTREQUEST_0_CURRENCYCODE='EUR', TOKEN=token, PAYERID=payerid) - response = Registration.recordifyPPResp(response) + response = PrintOrder.recordifyPPResp(response) self._paypalLog.append(response) return response @@ -384,7 +390,12 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : def ppPay(self, token, payerid): # assure le paiement paypal en une passe : # récupération des détails et validation de la transaction. - if not self.paid : + + wtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IWorkflowTool') + wfstate = wtool.getInfoFor(self, 'review_state', 'order_workflow') + paid = wfstate == 'paid' + + if not paid : details = self.ppGetExpressCheckoutDetails(token) if payerid != details['PAYERID'] : @@ -401,7 +412,7 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : wtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IWorkflowTool') wtool.doActionFor( self , 'paypal_pay' - , wf_id='jma_registration_workflow' + , wf_id='order_workflow' , comments='Paiement par PayPal') return True return False @@ -415,153 +426,7 @@ class PrintOrder(PortalContent, DefaultDublinCoreImpl) : security.declareProtected(ManagePortal, 'getPPLog') def getPPLog(self) : return self._paypalLog - - - - # security.declareProtected(View, 'getPaymentRequest') - # def getPaymentRequest(self) : - # config = _getCyberplusConfig() - # requester = CyberplusRequester(config) - # hereurl = self.absolute_url() - # amount = self.price + self.shippingFees - # amount = amount.getValues()['taxed'] - # amount = amount * 100 - # amount = str(int(round(amount, 0))) - # pptool = getToolByName(self, 'portal_photo_print') - # transaction_id = pptool.getNextTransactionId() - # - # userLanguages = getPreferredLanguages(self) - # for pref in userLanguages : - # lang = pref.split('-')[0] - # if lang in CYBERPLUS_LANGUAGES : - # break - # else : - # lang = 'en' - # - # options = { 'amount': amount - # ,'cancel_return_url' : '%s/paymentCancelHandler' % hereurl - # ,'normal_return_url' : '%s/paymentManualResponseHandler' % hereurl - # ,'automatic_response_url' :'%s/paymentAutoResponseHandler' % hereurl - # ,'transaction_id' : transaction_id - # ,'order_id' : self.getId() - # ,'language' : lang - # } - # req = requester.generateRequest(options) - # return req - # - # def _decodeCyberplusResponse(self, form) : - # config = _getCyberplusConfig() - # responder = CyberplusResponder(config) - # response = responder.getResponse(form) - # return response - # - # def _compareWithAutoResponse(self, manu) : - # keys = manu.keys() - # auto = self._paymentResponse - # autoKeys = auto.keys() - # if len(keys) != len(autoKeys) : - # console.warn('Manual has not the same keys.\nauto: %r\nmanual: %r' % \ - # (sorted(autoKeys), sorted(keys))) - # else : - # for k, v in manu.items() : - # if not auto.has_key(k) : - # console.warn('%r field only found in manual response.' % k) - # else : - # if v != auto[k] : - # console.warn('data mismatch for %r\nauto: %r\nmanual: %r' % (k, auto[k], v)) - # - # def _checkOrderId(self, response) : - # expected = self.getId() - # assert expected == response['order_id'], \ - # "Cyberplus response transaction_id doesn't match the order object:\n" \ - # "expected: %s\n" \ - # "found: %s" % (expected, response['transaction_id']) - - # def _executeOrderWfTransition(self, response) : - # if CyberplusResponder.transactionAccepted(response) : - # wfaction = 'auto_accept_payment' - # elif CyberplusResponder.transactionRefused(response) : - # self.resetCopiesCounters() - # wfaction = 'auto_refuse_payment' - # elif CyberplusResponder.transactionCanceled(response) : - # wfaction = 'auto_cancel_order' - # else : - # # transaction failed - # wfaction = 'auto_transaction_failed' - # - # wtool = getToolByName(self, 'portal_workflow') - # wf = wtool.getWorkflowById('order_workflow') - # tdef = wf.transitions.get(wfaction) - # wf._changeStateOf(self, tdef) - # wtool._reindexWorkflowVariables(self) - - # security.declarePublic('paymentAutoResponseHandler') - # @postonly - # def paymentAutoResponseHandler(self, REQUEST) : - # """\ - # Handle cyberplus payment auto response. - # """ - # response = self._decodeCyberplusResponse(REQUEST.form) - # self._checkOrderId(response) - # self._paymentResponse.update(response) - # self._executeOrderWfTransition(response) - # - # @postonly - # def paymentManualResponseHandler(self, REQUEST) : - # """\ - # Handle cyberplus payment manual response. - # """ - # response = self._decodeCyberplusResponse(REQUEST.form) - # self._checkOrderId(response) - # - # autoResponse = self._paymentResponse - # if not autoResponse : - # console.warn('Manual response handled before auto response at %s' % '/'.join(self.getPhysicalPath())) - # self._paymentResponse.update(response) - # self._executeOrderWfTransition(response) - # else : - # self._compareWithAutoResponse(response) - # - # url = '%s?%s' % (self.absolute_url(), - # make_query(portal_status_message=translate('Your payment is complete.', self).encode('utf-8')) - # ) - # return REQUEST.RESPONSE.redirect(url) - # - # @postonly - # def paymentCancelHandler(self, REQUEST) : - # """\ - # Handle cyberplus cancel response. - # This handler can be invoqued in two cases: - # - the user cancel the payment form - # - the payment transaction has been refused - # """ - # response = self._decodeCyberplusResponse(REQUEST.form) - # self._checkOrderId(response) - # - # if self._paymentResponse : - # # normaly, it happens when the transaction is refused by cyberplus. - # self._compareWithAutoResponse(response) - # - # - # if CyberplusResponder.transactionRefused(response) : - # if not self._paymentResponse : - # console.warn('Manual response handled before auto response at %s' % '/'.join(self.getPhysicalPath())) - # self._paymentResponse.update(response) - # self._executeOrderWfTransition(response) - # - # msg = 'Your payment has been refused.' - # - # else : - # self._executeOrderWfTransition(response) - # msg = 'Your payment has been canceled. You will be able to pay later.' - # - # url = '%s?%s' % (self.absolute_url(), - # make_query(portal_status_message= \ - # translate(msg, self).encode('utf-8')) - # ) - # return REQUEST.RESPONSE.redirect(url) - def getCustomerSummary(self) : ' ' return {'quantity':self.quantity,