diff --git a/modules/order.py b/modules/order.py index d68e4a3..053b6f5 100644 --- a/modules/order.py +++ b/modules/order.py @@ -51,27 +51,31 @@ class OrderSkel( Skeleton ): "shiptype": ["shipping_type", "payment_type"] } -class SkipStepException( Exception ): +class SkipStepException(Exception): """Raise this Exception to skip the current step""" pass -class ReturnHtmlException( Exception ): + +class ReturnHtmlException(Exception): """Raise this Exception to force the return of the given HTML inside a pre/post handler""" - def __init__(self, html ): - super( ReturnHtmlException, self ).__init__() + + def __init__(self, html): + super(ReturnHtmlException, self).__init__() self.html = html + from server.modules.order_paypal import PayPal from server.modules.order_sofort import Sofort -class Order( List ): + +class Order(List): """ Provides an unified ordering process with payment-handling. This is encoded as a state machine. """ - archiveDelay = timedelta( days=31 ) # Archive completed orders after 31 Days - paymentProviders = [PayPal,Sofort] + archiveDelay = timedelta(days=31) # Archive completed orders after 31 Days + paymentProviders = [PayPal, Sofort] states = [ "complete", # The user completed all steps of the process, he might got redirected to a paymentprovider @@ -179,20 +183,19 @@ class Order( List ): ] } - def __init__(self, *args, **kwargs): - super( Order, self ).__init__( *args, **kwargs ) + super(Order, self).__init__(*args, **kwargs) # Initialize the payment-providers self.initializedPaymentProviders = {} for p in self.paymentProviders: - pInstance = p( self ) - self.initializedPaymentProviders[ pInstance.__class__.__name__.lower() ] = pInstance - #Also put it as an object into self, sothat any exposed function is reachable - setattr( self, "pp_%s" % pInstance.__class__.__name__.lower(), pInstance ) + pInstance = p(self) + self.initializedPaymentProviders[pInstance.__class__.__name__.lower()] = pInstance + # Also put it as an object into self, sothat any exposed function is reachable + setattr(self, "pp_%s" % pInstance.__class__.__name__.lower(), pInstance) - def setState(self, orderKey, state, removeState = False): + def setState(self, orderKey, state, removeState=False): """ Set a status on the given order. @@ -205,16 +208,17 @@ def setState(self, orderKey, state, removeState = False): :param removeState: Should the state be removed instead of set :type removeState: bool """ - def txn( orderKey, state, removeState ): - dbObj = db.Get( db.Key( orderKey ) ) + + def txn(orderKey, state, removeState): + dbObj = db.Get(db.Key(orderKey)) if not dbObj: return - dbObj[ "state_%s" % state ] = "1" if not removeState else "0" + dbObj["state_%s" % state] = "1" if not removeState else "0" dbObj["changedate"] = datetime.now() - db.Put( dbObj ) + db.Put(dbObj) - db.RunInTransaction( txn, orderKey, state, removeState ) + db.RunInTransaction(txn, orderKey, state, removeState) def getStates(self, orderKey): """ @@ -226,7 +230,7 @@ def getStates(self, orderKey): :returns: States of the given order. :rtype: list of str """ - dbObj = db.Get( db.Key( orderKey ) ) + dbObj = db.Get(db.Key(orderKey)) res = [] if not dbObj: @@ -235,12 +239,12 @@ def getStates(self, orderKey): for state in self.states: stateName = "state_%s" % state - if stateName in dbObj and str( dbObj[ stateName ] )=="1": - res.append( state ) + if stateName in dbObj and str(dbObj[stateName]) == "1": + res.append(state) return res - def setComplete( self, orderKey ): + def setComplete(self, orderKey): """ Mark an order as complete. May be overridden to hook this event. @@ -253,18 +257,18 @@ def setComplete( self, orderKey ): """ order = self.editSkel() - if not order.fromDB( str(orderKey) ): + if not order.fromDB(str(orderKey)): return False - self.setState( orderKey, "complete") + self.setState(orderKey, "complete") - if order[ "payment_type" ].value == "pod": - states = self.getStates( orderKey ) + if order["payment_type"] == "pod": + states = self.getStates(orderKey) - if not any( [ x in states for x in ["canceled", "rts", "send"] ] ): - self.setRTS( orderKey ) + if not any([x in states for x in ["canceled", "rts", "send"]]): + self.setRTS(orderKey) - self.assignBillSequence( str(orderKey) ) + self.assignBillSequence(str(orderKey)) return True def setRTS(self, orderKey): @@ -275,7 +279,7 @@ def setRTS(self, orderKey): :param orderKey: Order to mark :type orderKey: str """ - self.setState( orderKey, "rts") + self.setState(orderKey, "rts") def setPayed(self, orderKey): """ @@ -285,14 +289,14 @@ def setPayed(self, orderKey): :param orderKey: Order to mark :type orderKey: str """ - self.setState( orderKey, "payed") - states = self.getStates( orderKey ) + self.setState(orderKey, "payed") + states = self.getStates(orderKey) - if not any( [ x in states for x in ["rts", "send", "canceled", "closed"] ] ): - self.setState( orderKey,"rts") + if not any([x in states for x in ["rts", "send", "canceled", "closed"]]): + self.setState(orderKey, "rts") - self.sendOrderPayedEMail( orderKey ) - return( True ) + self.sendOrderPayedEMail(orderKey) + return True def setSend(self, orderKey): """ @@ -302,10 +306,10 @@ def setSend(self, orderKey): :param orderKey: Order to mark :type orderKey: str """ - self.setState( orderKey, "send" ) - self.setState( orderKey, "rts", True ) - self.sendOrderShippedEMail( orderKey ) - return( True ) + self.setState(orderKey, "send") + self.setState(orderKey, "rts", True) + self.sendOrderShippedEMail(orderKey) + return True def setCanceled(self, orderKey): """ @@ -315,12 +319,12 @@ def setCanceled(self, orderKey): :param orderKey: Order to mark :type orderKey: str """ - self.setState( orderKey, "canceled" ) - self.setState( orderKey, "rts", True ) - self.sendOrderCanceledEMail( orderKey ) - return( True ) + self.setState(orderKey, "canceled") + self.setState(orderKey, "rts", True) + self.sendOrderCanceledEMail(orderKey) + return True - def setArchived(self, orderKey ): + def setArchived(self, orderKey): """ Marks an order as archived. May be overridden to hook this event. @@ -328,10 +332,10 @@ def setArchived(self, orderKey ): :param orderKey: Order to mark :type orderKey: str """ - self.setState( orderKey, "archived" ) - self.setState( orderKey, "rts", True ) - self.sendOrderArchivedEMail( orderKey ) - return( True ) + self.setState(orderKey, "archived") + self.setState(orderKey, "rts", True) + self.sendOrderArchivedEMail(orderKey) + return True def sendOrderShippedEMail(self, orderKey): """ @@ -379,24 +383,24 @@ def sendOrderArchivedEMail(self, orderKey): pass @exposed - def markPayed( self, key, skey, *args, **kwargs ): + def markPayed(self, key, skey, *args, **kwargs): """ Exposed function for marking an order as paid. :param key: The key of the order to be marked. :type key: str """ - if not self.canEdit( key ): + if not self.canEdit(key): raise errors.Unauthorized() - if not securitykey.validate( skey ): + if not securitykey.validate(skey): raise errors.PreconditionFailed() self.setPayed(key) return "OKAY" @exposed - def markSend( self, key, skey, *args, **kwargs ): + def markSend(self, key, skey, *args, **kwargs): """ Exposed function for marking an order as sent. @@ -404,33 +408,41 @@ def markSend( self, key, skey, *args, **kwargs ): :type key: str """ - if not self.canEdit( key ): + skel = self.editSkel() + if not skel.fromDB(key): + raise errors.NotFound() + + if not self.canEdit(skel): raise errors.Unauthorized() - if not securitykey.validate( skey ): + if not securitykey.validate(skey): raise errors.PreconditionFailed() self.setSend(key) return "OKAY" @exposed - def markCanceled( self, key, skey, *args, **kwargs ): + def markCanceled(self, key, skey, *args, **kwargs): """ Exposed function for marking an order as cancelled. :param key: The key of the order to be marked. :type key: str """ - if not self.canEdit( key ): + skel = self.editSkel() + if not skel.fromDB(key): + raise errors.NotFound() + + if not self.canEdit(skel): raise errors.Unauthorized() - if not securitykey.validate( skey ): + if not securitykey.validate(skey): raise errors.PreconditionFailed() - self.setCanceled( key ) + self.setCanceled(key) return "OKAY" - def checkSkipShippingAddress( self, step, orderKey, *args, **kwargs ): + def orderStep_checkSkipShippingAddress(self, step, orderKey, *args, **kwargs): """ This step updates the current order and copies the values from billAddressSkel to shippingAddressSkel if extrashippingaddress is @@ -443,19 +455,19 @@ def checkSkipShippingAddress( self, step, orderKey, *args, **kwargs ): :type orderKey: str """ billSkel = self.editSkel() - billSkel.fromDB( orderKey ) + billSkel.fromDB(orderKey) - if not billSkel[ "extrashippingaddress" ].value: + if not billSkel["extrashippingaddress"]: for name, bone in billSkel.items(): - if name.startswith( "bill_" ): - name = name.replace( "bill_", "shipping_" ) - if name in billSkel: - billSkel[ name ].value = bone.value + if name.startswith("bill_"): + shipname = name.replace("bill_", "shipping_") + if shipname in billSkel: + billSkel[shipname] = billSkel[name] billSkel.toDB() raise SkipStepException() - def calculateOrderSum( self, step, orderKey, *args, **kwargs ): + def orderStep_calculateOrderSum(self, step, orderKey, *args, **kwargs): """ Calculates the final price for this order. This function *must* be called before any attempt is made to start a payment process. @@ -467,12 +479,12 @@ def calculateOrderSum( self, step, orderKey, *args, **kwargs ): :param orderKey: order to calculate the price for :type orderKey: str """ - price = sum( [x[3] for x in self.getBillItems( orderKey ) ] ) - orderObj = db.Get( db.Key( str( orderKey ) ) ) + price = sum([x[3] for x in self.getBillItems(orderKey)]) + orderObj = db.Get(db.Key(str(orderKey))) orderObj["price"] = price - db.Put( orderObj ) + db.Put(orderObj) - def startPayment( self, step, orderKey, *args, **kwargs ): + def orderStep_startPayment(self, step, orderKey, *args, **kwargs): """ Starts the payment processing for this order. The order is marked completed, so no further changes can be made. @@ -484,24 +496,24 @@ def startPayment( self, step, orderKey, *args, **kwargs ): :type orderKey: str """ order = self.editSkel() - order.fromDB( orderKey ) + order.fromDB(orderKey) if not str(order["state_complete"]) == "1": - session.current["order_"+order.kindName] = None - session.current.markChanged() #Fixme - self.setComplete( orderKey ) + session.current["order_" + order.kindName] = None + session.current.markChanged() # Fixme + self.setComplete(orderKey) if order["payment_type"] in self.initializedPaymentProviders.keys(): pp = self.initializedPaymentProviders[order["payment_type"]] - pp.startProcessing( step, orderKey ) - #getattr( self, "paymentProvider_%s" % order.payment_type.value )( step, orderKey ) + pp.startProcessing(step, orderKey) + # getattr( self, "paymentProvider_%s" % order.payment_type.value )( step, orderKey ) - def paymentProvider_pod( self, step, orderKey ): + def paymentProvider_pod(self, step, orderKey): """ If Pay-On-Delivery is choosen, immediately mark this order as ready to ship """ - self.setRTS( orderKey ) + self.setRTS(orderKey) @internalExposed - def getBillItems(self, orderKey ): + def getBillItems(self, orderKey): """ Returns all items for the given Order. Must be overridden. @@ -514,30 +526,30 @@ def getBillItems(self, orderKey ): """ return [] - def billSequenceAvailable( self, orderKey ): - self.sendOrderCompleteEMail( orderKey ) + def billSequenceAvailable(self, orderKey): + self.sendOrderCompleteEMail(orderKey) @callDeferred - def assignBillSequence( self, orderKey ): + def assignBillSequence(self, orderKey): """ Assigns an unique order-order to the given order. """ - def getKeyTxn( kindName, orderKey ): + def getKeyTxn(kindName, orderKey): """Generates and returns a new, unique Key""" seqObj = db.GetOrInsert(kindName, "viur_bill_sequences", count=1000) idx = seqObj["count"] seqObj["count"] += 1 - db.Put( seqObj ) + db.Put(seqObj) return str(idx) - def setKeyTxn( orderKey, idx ): + def setKeyTxn(orderKey, idx): """Assigns the new order to the given order""" - dbObj = db.Get( db.Key( orderKey ) ) + dbObj = db.Get(db.Key(orderKey)) if not dbObj: return - dbObj[ "idx" ] = idx - db.Put( dbObj ) + dbObj["idx"] = idx + db.Put(dbObj) dbObj = db.Get(db.Key(orderKey)) if not dbObj: @@ -545,9 +557,9 @@ def setKeyTxn( orderKey, idx ): idx = db.RunInTransaction(getKeyTxn, self.viewSkel().kindName, orderKey) db.RunInTransaction(setKeyTxn, orderKey, idx) - self.billSequenceAvailable( orderKey ) + self.billSequenceAvailable(orderKey) - def rebuildSearchIndex(self, step, orderKey, *args, **kwargs ): + def orderStep_rebuildSearchIndex(self, step, orderKey, *args, **kwargs): """ This rewrites the order after its completion. @@ -558,12 +570,12 @@ def rebuildSearchIndex(self, step, orderKey, *args, **kwargs ): Not a transaction, do not defer! """ skel = self.viewSkel() - if not skel.fromDB( orderKey ): + if not skel.fromDB(orderKey): raise AssertionError() skel.toDB() - def resetCart(self, step, orderKey, *args, **kwargs ): + def orderStep_resetCart(self, step, orderKey, *args, **kwargs): """ Clears the cart (if any) after the checkout process is finished. @@ -580,74 +592,65 @@ def getSkelByName(self, name, orderKey): return self.viewSkel().subSkel(name) - def tmpSkipCheck(self, *args, **kwargs): + def orderStep_Skip(self, *args, **kwargs): raise SkipStepException() - steps = [ - { - "preHandler": tmpSkipCheck, - "mainHandler": { - "action": "function", - "function": tmpSkipCheck - } - }, - { - "mainHandler": { - "action": "edit", - "skeleton": "billaddress", - "template": "order_billaddress", - "descr":u"Billinginformation" - } - }, - { - "preHandler": checkSkipShippingAddress, - "mainHandler": { - "action": "edit", - "skeleton": "shippingaddress", - "template": "order_shipaddress", - "descr":u"Shippinginformation" - } - }, - { - "mainHandler": { - "action": "edit", - "skeleton": "shiptype", - "template": "order_payment", - "descr":u"Payment" - }, - "postHandler": calculateOrderSum - }, - { - "mainHandler": { - "action": "view", - "skeleton": "", - "template": "order_verify", - "descr":u"Overview" - } - }, - { - "preHandler": [rebuildSearchIndex,resetCart,startPayment], - "mainHandler": { - "action": "view", - "skeleton": "", - "template": "order_complete", - "descr":u"Order completed" - } - } - ] - @internalExposed def getSteps(self): - steps = [] - - for step in self.steps[:]: - step = step.copy() - step["mainHandler"] = step["mainHandler"].copy() - if "descr" in step["mainHandler"]: - step["mainHandler"].update({"descr": _(step["mainHandler"]["descr"])}) - steps.append( step ) - - return steps + return [ + { + "preHandler": self.orderStep_Skip, + "mainHandler": { + "action": "function", + "function": self.orderStep_Skip + } + }, + { + "mainHandler": { + "action": "edit", + "skeleton": "billaddress", + "template": "order_billaddress", + "descr": _(u"Billinginformation") + } + }, + { + "preHandler": self.orderStep_checkSkipShippingAddress, + "mainHandler": { + "action": "edit", + "skeleton": "shippingaddress", + "template": "order_shipaddress", + "descr": _(u"Shippinginformation") + } + }, + { + "mainHandler": { + "action": "edit", + "skeleton": "shiptype", + "template": "order_payment", + "descr": _(u"Payment") + }, + "postHandler": self.orderStep_calculateOrderSum + }, + { + "mainHandler": { + "action": "view", + "skeleton": "", + "template": "order_verify", + "descr": _(u"Overview") + } + }, + { + "preHandler": [self.orderStep_rebuildSearchIndex, + self.orderStep_resetCart, + self.orderStep_startPayment], + "mainHandler": { + "action": "view", + "skeleton": "", + "template": "order_complete", + "descr": _(u"Order completed") + } + } + ] def getBillPdf(self, orderKey): """ @@ -661,7 +664,7 @@ def getBillPdf(self, orderKey): """ return None - def getDeliveryNotePdf(self, orderKey ): + def getDeliveryNotePdf(self, orderKey): """ Should be overridden to return the delivery note (as pdf) for the given order. @@ -680,24 +683,24 @@ def getBill(self, key, *args, **kwargs): """ skel = self.viewSkel() - if "canView" in dir( self ): - if not self.canView( key ): + if "canView" in dir(self): + if not self.canView(key): raise errors.Unauthorized() else: - queryObj = self.viewSkel().all().mergeExternalFilter( {"key": key} ) - queryObj = self.listFilter( queryObj ) #Access control + queryObj = self.viewSkel().all().mergeExternalFilter({"key": key}) + queryObj = self.listFilter(queryObj) # Access control if queryObj is None: raise errors.Unauthorized() - bill = self.getBillPdf( key ) + bill = self.getBillPdf(key) if not bill: raise errors.NotFound() request.current.get().response.headers['Content-Type'] = "application/pdf" - return( bill ) + return bill @exposed def getDeliveryNote(self, key, *args, **kwargs): @@ -706,28 +709,39 @@ def getDeliveryNote(self, key, *args, **kwargs): """ skel = self.viewSkel() - if "canView" in dir( self ): - if not self.canView( key ): + if "canView" in dir(self): + if not self.canView(key): raise errors.Unauthorized() else: - queryObj = self.viewSkel().all().mergeExternalFilter( {"key": key} ) - queryObj = self.listFilter( queryObj ) #Access control + queryObj = self.viewSkel().all().mergeExternalFilter({"key": key}) + queryObj = self.listFilter(queryObj) # Access control if queryObj is None: raise errors.Unauthorized() - bill = self.getDeliveryNotePdf( key ) + bill = self.getDeliveryNotePdf(key) if not bill: raise errors.NotFound() request.current.get().response.headers['Content-Type'] = "application/pdf" - return( bill ) + return bill + + def getCheckoutUrl(self, step, key, style): + + url = "?step=%d&key=%s" % (step, key) + + if style: + url += "&style=%s" % style + + logging.info("url = %s", url) + + return url @exposed - def checkout( self, step=None, key=None, skey=None, *args, **kwargs ): + def checkout(self, step=None, key=None, style=None, skey=None, *args, **kwargs): """ - Performs the checkout process trough the state machine provided by self.steps. + Performs the checkout process trough the state machine provided by self.getSteps(). :param step: The current step index, None for beginning a new checkout :param key: Key of the current checkout @@ -736,118 +750,120 @@ def checkout( self, step=None, key=None, skey=None, *args, **kwargs ): :return: Returns the rendered template or throws redirection exceptions. """ myKindName = self.viewSkel().kindName + steps = self.getSteps() + #logging.debug(steps) + if step is None: logging.info("Starting new checkout process") - billObj = db.Entity( myKindName ) + billObj = db.Entity(myKindName) billObj["idx"] = "0000000" for state in self.states: - billObj[ "state_%s" % state ] = "0" - db.Put( billObj ) - key = str( billObj.key() ) + billObj["state_%s" % state] = "0" + db.Put(billObj) + key = str(billObj.key()) - #Copy the Cart - if "amountSkel" in dir ( self ): + # Copy the Cart + if "amountSkel" in dir(self): cart = session.current.get("cart_products") or {} s = self.amountSkel() products = [] for prod, atts in cart.items(): - for i in range( 0, atts[ "amount" ] ): - products.append( str(prod) ) + for i in range(0, atts["amount"]): + products.append(str(prod)) - s.fromClient( {"product": products} ) + s.fromClient({"product": products}) s.toDB() - session.current["order_"+myKindName] = {"key": str( key ), "completedSteps": [] } + session.current["order_" + myKindName] = {"key": str(key), "completedSteps": []} session.current.markChanged() - raise errors.Redirect( "?step=0&key=%s" % str( key ) ) + raise errors.Redirect(self.getCheckoutUrl(0, key, style)) elif key: try: - orderKey = db.Key( key ) - step = int( step ) - assert( step>=0 ) - assert( step < len( self.steps ) ) + orderKey = db.Key(key) + step = int(step) + assert (step >= 0) + assert (step < len(steps)) except: raise errors.NotAcceptable() - sessionInfo = session.current.get("order_"+myKindName) + sessionInfo = session.current.get("order_" + myKindName) - if not sessionInfo or not sessionInfo.get("key") == str( orderKey ): + if not sessionInfo or not sessionInfo.get("key") == str(orderKey): raise errors.Unauthorized() if step in sessionInfo["completedSteps"]: - session.current["order_"+myKindName]["completedSteps"] = [ x for x in sessionInfo["completedSteps"] if x