source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/workflow.py @ 12711

Last change on this file since 12711 was 12663, checked in by Henrik Bettermann, 10 years ago

Extend contract workflow to integrate payment.

Prepare (empty) page to select payment method and finally create a payment object.

  • Property svn:keywords set to Id
File size: 13.2 KB
Line 
1## $Id: workflow.py 12663 2015-03-05 07:28:31Z henrik $
2##
3## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""Workflow for customers.
19"""
20import grok
21from datetime import datetime
22from zope.component import getUtility
23from zope.i18n import translate
24from hurry.workflow.workflow import Transition, WorkflowState, NullCondition
25from hurry.workflow.interfaces import (
26    IWorkflowState, IWorkflowTransitionEvent, InvalidTransitionError)
27from waeup.ikoba.interfaces import (
28    IObjectHistory, IIkobaWorkflowInfo, IIkobaUtils, IUserAccount,
29    STARTED, CREATED, REQUESTED, PROVISIONALLY, APPROVED,
30    SUBMITTED, VERIFIED, REJECTED, EXPIRED, AWAITING)
31from waeup.ikoba.interfaces import MessageFactory as _
32from waeup.ikoba.workflow import (
33    IkobaWorkflow, IkobaWorkflowInfo)
34from waeup.ikoba.customers.interfaces import (
35    ICustomer, ICustomersUtils,
36    IContract, ICustomerDocument)
37from waeup.ikoba.utils.helpers import get_current_principal
38
39# Customer workflow
40
41IMPORTABLE_REGISTRATION_STATES = (STARTED, REQUESTED, APPROVED)
42
43REGISTRATION_TRANSITIONS = (
44    Transition(
45        transition_id = 'create',
46        title = _('Create customer'),
47        source = None,
48        condition = NullCondition,
49        msg = _('Customer created'),
50        destination = CREATED),
51
52    Transition(
53        transition_id = 'start',
54        title = _('Start registration'),
55        source = CREATED,
56        condition = NullCondition,
57        msg = _('Customer registration started'),
58        destination = STARTED),
59
60    Transition(
61        transition_id = 'request',
62        title = _('Request registration'),
63        msg = _('Customer registration requested'),
64        source = STARTED,
65        destination = REQUESTED),
66
67    Transition(
68        transition_id = 'approve_provisionally',
69        title = _('Approve customer provisionally'),
70        msg = _('Customer registration provisionally approved'),
71        source = REQUESTED,
72        destination = PROVISIONALLY),
73
74    Transition(
75        transition_id = 'approve_finally',
76        title = _('Approve finally'),
77        msg = _('Customer registration finally approved'),
78        source = PROVISIONALLY,
79        destination = APPROVED),
80
81    Transition(
82        transition_id = 'approve',
83        title = _('Approve customer'),
84        msg = _('Customer registration approved'),
85        source = REQUESTED,
86        destination = APPROVED),
87
88    Transition(
89        transition_id = 'reject',
90        title = _('Reject customer'),
91        msg = _('Customer registration rejected'),
92        source = REQUESTED,
93        destination = STARTED),
94
95    Transition(
96        transition_id = 'reset1',
97        title = _('Reset customer'),
98        msg = _('Reset to initial customer state'),
99        source = APPROVED,
100        destination = STARTED),
101
102    Transition(
103        transition_id = 'reset2',
104        title = _('Reset to requested'),
105        msg = _("Reset to 'requested'"),
106        source = APPROVED,
107        destination = REQUESTED),
108
109    Transition(
110        transition_id = 'reset3',
111        title = _('Reset customer'),
112        msg = _("Reset to initial state"),
113        source = REQUESTED,
114        destination = STARTED),
115
116    Transition(
117        transition_id = 'reset4',
118        title = _('Reset customer'),
119        msg = _('Reset to initial customer state'),
120        source = PROVISIONALLY,
121        destination = STARTED),
122
123    Transition(
124        transition_id = 'reset5',
125        title = _('Reset to requested'),
126        msg = _("Reset to 'requested'"),
127        source = PROVISIONALLY,
128        destination = REQUESTED),
129
130    )
131
132
133IMPORTABLE_REGISTRATION_TRANSITIONS = [i.transition_id for i in REGISTRATION_TRANSITIONS]
134
135registration_workflow = IkobaWorkflow(REGISTRATION_TRANSITIONS)
136
137
138class RegistrationWorkflowState(WorkflowState, grok.Adapter):
139    """An adapter to adapt Customer objects to workflow states.
140    """
141    grok.context(ICustomer)
142    grok.provides(IWorkflowState)
143
144    state_key = 'wf.registration.state'
145    state_id = 'wf.registration.id'
146
147
148class RegistrationWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
149    """Adapter to adapt Customer objects to workflow info objects.
150    """
151    grok.context(ICustomer)
152    grok.provides(IIkobaWorkflowInfo)
153
154    def __init__(self, context):
155        self.context = context
156        self.wf = registration_workflow
157
158
159@grok.subscribe(ICustomer, IWorkflowTransitionEvent)
160def handle_customer_transition_event(obj, event):
161    """Append message to customer history and log file when transition happened.
162
163    Notify customer by email.
164    """
165
166    msg = event.transition.user_data['msg']
167    history = IObjectHistory(obj)
168    history.addMessage(msg)
169    if event.transition.transition_id not in ('create', 'start', 'request'):
170        ikoba_utils = getUtility(IIkobaUtils)
171        ikoba_utils.sendTransitionInfo(IUserAccount(obj), obj, msg)
172    try:
173        customers_container = grok.getSite()['customers']
174        customers_container.logger.info('%s - %s' % (obj.customer_id,msg))
175    except (TypeError, AttributeError):
176        pass
177    return
178
179
180# Contract workflow
181
182
183CONTRACT_TRANSITIONS = (
184    Transition(
185        transition_id = 'create',
186        title = _('Create contract'),
187        source = None,
188        condition = NullCondition,
189        msg = _('Contract created'),
190        destination = CREATED),
191
192    Transition(
193        transition_id = 'await',
194        title = _('Initiate payment'),
195        source = CREATED,
196        condition = NullCondition,
197        msg = _('Payment initiated'),
198        destination = AWAITING),
199
200    Transition(
201        transition_id = 'discard',
202        title = _('Discard payment'),
203        source = AWAITING,
204        condition = NullCondition,
205        msg = _('Payment discarded'),
206        destination = CREATED),
207
208    Transition(
209        transition_id = 'submit',
210        title = _('Submit for approval'),
211        msg = _('Submitted for approval'),
212        source = CREATED,
213        destination = SUBMITTED),
214
215    Transition(
216        transition_id = 'confirm',
217        title = _('Confirm payment'),
218        msg = _('Payment and submission confirmed'),
219        source = AWAITING,
220        destination = SUBMITTED),
221
222    Transition(
223        transition_id = 'approve',
224        title = _('Approve'),
225        msg = _('Approved'),
226        source = SUBMITTED,
227        destination = APPROVED),
228
229    Transition(
230        transition_id = 'reject',
231        title = _('Reject'),
232        msg = _('REJECTED'),
233        source = SUBMITTED,
234        destination = REJECTED),
235
236    Transition(
237        transition_id = 'reset1',
238        title = _('Reset to initial state'),
239        msg = _('Reset to initial state'),
240        source = REJECTED,
241        destination = CREATED),
242
243    Transition(
244        transition_id = 'reset2',
245        title = _('Reset to initial state'),
246        msg = _('Reset to initial state'),
247        source = APPROVED,
248        destination = CREATED),
249
250    Transition(
251        transition_id = 'reset3',
252        title = _('Reset to initial state'),
253        msg = _('Reset to initial state'),
254        source = SUBMITTED,
255        destination = CREATED),
256
257    Transition(
258        transition_id = 'expire',
259        title = _('Set to expired'),
260        msg = _('Set to expired'),
261        source = APPROVED,
262        destination = EXPIRED),
263
264    Transition(
265        transition_id = 'reset4',
266        title = _('Reset to initial state'),
267        msg = _('Reset to initial state'),
268        source = EXPIRED,
269        destination = CREATED),
270
271    )
272
273PAYMENT_TRANSITIONS = ['await', 'discard', 'confirm']
274
275contract_workflow = IkobaWorkflow(CONTRACT_TRANSITIONS)
276
277
278class ContractWorkflowState(WorkflowState, grok.Adapter):
279    """An adapter to adapt Contract objects to workflow states.
280    """
281    grok.context(IContract)
282    grok.provides(IWorkflowState)
283
284    state_key = 'wf.contract.state'
285    state_id = 'wf.contract.id'
286
287
288class ContractWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
289    """Adapter to adapt Contract objects to workflow info objects.
290    """
291    grok.context(IContract)
292    grok.provides(IIkobaWorkflowInfo)
293
294    def __init__(self, context):
295        self.context = context
296        self.wf = contract_workflow
297
298@grok.subscribe(IContract, IWorkflowTransitionEvent)
299def handle_contract_transition_event(obj, event):
300    """Append message to contract history and log file.
301
302    Undo the approval of contract and raise an exception if contract
303    does not meet the requirements for approval.
304    """
305    if event.transition.destination == APPROVED:
306        approvable, error = obj.is_approvable
307        if not approvable:
308            # Undo transition and raise an exception.
309            IWorkflowState(obj).setState(event.transition.source)
310            raise InvalidTransitionError(error)
311    msg = event.transition.user_data['msg']
312    history = IObjectHistory(obj)
313    history.addMessage(msg)
314    if event.transition.transition_id not in (
315        'create', 'submit', 'confirm', 'await') and obj.customer:
316        ikoba_utils = getUtility(IIkobaUtils)
317        ikoba_utils.sendTransitionInfo(IUserAccount(obj.customer), obj, msg)
318    try:
319        customers_container = grok.getSite()['customers']
320        customers_container.logger.info(
321            '%s - %s - %s' % (obj.customer.customer_id, obj.contract_id, msg))
322    except (TypeError, AttributeError):
323        pass
324    return
325
326# Customer document workflow
327
328VERIFICATION_TRANSITIONS = (
329    Transition(
330        transition_id = 'create',
331        title = _('Create document'),
332        source = None,
333        condition = NullCondition,
334        msg = _('Document created'),
335        destination = CREATED),
336
337    Transition(
338        transition_id = 'submit',
339        title = _('Submit for verification'),
340        msg = _('Submitted for verification'),
341        source = CREATED,
342        destination = SUBMITTED),
343
344    Transition(
345        transition_id = 'verify',
346        title = _('Verify'),
347        msg = _('Verified'),
348        source = SUBMITTED,
349        destination = VERIFIED),
350
351    Transition(
352        transition_id = 'reject',
353        title = _('Reject'),
354        msg = _('REJECTED'),
355        source = SUBMITTED,
356        destination = REJECTED),
357
358    Transition(
359        transition_id = 'reset1',
360        title = _('Reset to initial state'),
361        msg = _('Reset to initial state'),
362        source = REJECTED,
363        destination = CREATED),
364
365    Transition(
366        transition_id = 'reset2',
367        title = _('Reset to initial state'),
368        msg = _('Reset to initial state'),
369        source = VERIFIED,
370        destination = CREATED),
371
372    Transition(
373        transition_id = 'reset3',
374        title = _('Reset to initial state'),
375        msg = _('Reset to initial state'),
376        source = SUBMITTED,
377        destination = CREATED),
378
379    Transition(
380        transition_id = 'expire',
381        title = _('Set to expired'),
382        msg = _('Set to expired'),
383        source = VERIFIED,
384        destination = EXPIRED),
385
386    Transition(
387        transition_id = 'reset4',
388        title = _('Reset to initial state'),
389        msg = _('Reset to initial state'),
390        source = EXPIRED,
391        destination = CREATED),
392    )
393
394verification_workflow = IkobaWorkflow(VERIFICATION_TRANSITIONS)
395
396
397class VerificationWorkflowState(WorkflowState, grok.Adapter):
398    """An adapter to adapt CustomerDocument objects to workflow states.
399    """
400    grok.context(ICustomerDocument)
401    grok.provides(IWorkflowState)
402
403    state_key = 'wf.verification.state'
404    state_id = 'wf.verification.id'
405
406
407class VerificationWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
408    """Adapter to adapt CustomerDocument objects to workflow info objects.
409    """
410    grok.context(ICustomerDocument)
411    grok.provides(IIkobaWorkflowInfo)
412
413    def __init__(self, context):
414        self.context = context
415        self.wf = verification_workflow
416
417@grok.subscribe(ICustomerDocument, IWorkflowTransitionEvent)
418def handle_customer_document_transition_event(obj, event):
419    """Append message to document history and log file.
420
421    Undo the verification of document and raise an exception if document
422    does not meet the requirements for verification.
423    """
424    if event.transition.destination == VERIFIED:
425        verifiable, error = obj.is_verifiable
426        if not verifiable:
427            # Undo transition and raise an exception.
428            IWorkflowState(obj).setState(event.transition.source)
429            raise InvalidTransitionError(error)
430    if event.transition.transition_id == 'verify':
431        obj.setMD5()
432    msg = event.transition.user_data['msg']
433    history = IObjectHistory(obj)
434    history.addMessage(msg)
435    try:
436        customers_container = grok.getSite()['customers']
437        customers_container.logger.info(
438            '%s - %s - %s' % (obj.customer.customer_id, obj.document_id, msg))
439    except (TypeError, AttributeError):
440        pass
441    return
Note: See TracBrowser for help on using the repository browser.