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

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

Notify customer by email after customer and contract transitions.

  • Property svn:keywords set to Id
File size: 12.5 KB
Line 
1## $Id: workflow.py 12553 2015-02-03 16:54:56Z 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)
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 = 'submit',
194        title = _('Submit for approval'),
195        msg = _('Submitted for approval'),
196        source = CREATED,
197        destination = SUBMITTED),
198
199    Transition(
200        transition_id = 'approve',
201        title = _('Approve'),
202        msg = _('Approved'),
203        source = SUBMITTED,
204        destination = APPROVED),
205
206    Transition(
207        transition_id = 'reject',
208        title = _('Reject'),
209        msg = _('REJECTED'),
210        source = SUBMITTED,
211        destination = REJECTED),
212
213    Transition(
214        transition_id = 'reset1',
215        title = _('Reset to initial state'),
216        msg = _('Reset to initial state'),
217        source = REJECTED,
218        destination = CREATED),
219
220    Transition(
221        transition_id = 'reset2',
222        title = _('Reset to initial state'),
223        msg = _('Reset to initial state'),
224        source = APPROVED,
225        destination = CREATED),
226
227    Transition(
228        transition_id = 'reset3',
229        title = _('Reset to initial state'),
230        msg = _('Reset to initial state'),
231        source = SUBMITTED,
232        destination = CREATED),
233
234    Transition(
235        transition_id = 'expire',
236        title = _('Set to expired'),
237        msg = _('Set to expired'),
238        source = APPROVED,
239        destination = EXPIRED),
240
241    Transition(
242        transition_id = 'reset4',
243        title = _('Reset to initial state'),
244        msg = _('Reset to initial state'),
245        source = EXPIRED,
246        destination = CREATED),
247    )
248
249contract_workflow = IkobaWorkflow(CONTRACT_TRANSITIONS)
250
251
252class ContractWorkflowState(WorkflowState, grok.Adapter):
253    """An adapter to adapt Contract objects to workflow states.
254    """
255    grok.context(IContract)
256    grok.provides(IWorkflowState)
257
258    state_key = 'wf.contract.state'
259    state_id = 'wf.contract.id'
260
261
262class ContractWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
263    """Adapter to adapt Contract objects to workflow info objects.
264    """
265    grok.context(IContract)
266    grok.provides(IIkobaWorkflowInfo)
267
268    def __init__(self, context):
269        self.context = context
270        self.wf = contract_workflow
271
272@grok.subscribe(IContract, IWorkflowTransitionEvent)
273def handle_contract_transition_event(obj, event):
274    """Append message to contract history and log file.
275
276    Undo the approval of contract and raise an exception if contract
277    does not meet the requirements for approval.
278    """
279    if event.transition.destination == APPROVED:
280        approvable, error = obj.is_approvable
281        if not approvable:
282            # Undo transition and raise an exception.
283            IWorkflowState(obj).setState(event.transition.source)
284            raise InvalidTransitionError(error)
285    msg = event.transition.user_data['msg']
286    history = IObjectHistory(obj)
287    history.addMessage(msg)
288    if event.transition.transition_id not in ('create', 'submit') \
289        and obj.customer:
290        ikoba_utils = getUtility(IIkobaUtils)
291        ikoba_utils.sendTransitionInfo(IUserAccount(obj.customer), obj, msg)
292    try:
293        customers_container = grok.getSite()['customers']
294        customers_container.logger.info(
295            '%s - %s - %s' % (obj.customer.customer_id, obj.contract_id, msg))
296    except (TypeError, AttributeError):
297        pass
298    return
299
300# Customer document workflow
301
302VERIFICATION_TRANSITIONS = (
303    Transition(
304        transition_id = 'create',
305        title = _('Create document'),
306        source = None,
307        condition = NullCondition,
308        msg = _('Document created'),
309        destination = CREATED),
310
311    Transition(
312        transition_id = 'submit',
313        title = _('Submit for verification'),
314        msg = _('Submitted for verification'),
315        source = CREATED,
316        destination = SUBMITTED),
317
318    Transition(
319        transition_id = 'verify',
320        title = _('Verify'),
321        msg = _('Verified'),
322        source = SUBMITTED,
323        destination = VERIFIED),
324
325    Transition(
326        transition_id = 'reject',
327        title = _('Reject'),
328        msg = _('REJECTED'),
329        source = SUBMITTED,
330        destination = REJECTED),
331
332    Transition(
333        transition_id = 'reset1',
334        title = _('Reset to initial state'),
335        msg = _('Reset to initial state'),
336        source = REJECTED,
337        destination = CREATED),
338
339    Transition(
340        transition_id = 'reset2',
341        title = _('Reset to initial state'),
342        msg = _('Reset to initial state'),
343        source = VERIFIED,
344        destination = CREATED),
345
346    Transition(
347        transition_id = 'reset3',
348        title = _('Reset to initial state'),
349        msg = _('Reset to initial state'),
350        source = SUBMITTED,
351        destination = CREATED),
352
353    Transition(
354        transition_id = 'expire',
355        title = _('Set to expired'),
356        msg = _('Set to expired'),
357        source = VERIFIED,
358        destination = EXPIRED),
359
360    Transition(
361        transition_id = 'reset4',
362        title = _('Reset to initial state'),
363        msg = _('Reset to initial state'),
364        source = EXPIRED,
365        destination = CREATED),
366    )
367
368verification_workflow = IkobaWorkflow(VERIFICATION_TRANSITIONS)
369
370
371class VerificationWorkflowState(WorkflowState, grok.Adapter):
372    """An adapter to adapt CustomerDocument objects to workflow states.
373    """
374    grok.context(ICustomerDocument)
375    grok.provides(IWorkflowState)
376
377    state_key = 'wf.verification.state'
378    state_id = 'wf.verification.id'
379
380
381class VerificationWorkflowInfo(IkobaWorkflowInfo, grok.Adapter):
382    """Adapter to adapt CustomerDocument objects to workflow info objects.
383    """
384    grok.context(ICustomerDocument)
385    grok.provides(IIkobaWorkflowInfo)
386
387    def __init__(self, context):
388        self.context = context
389        self.wf = verification_workflow
390
391@grok.subscribe(ICustomerDocument, IWorkflowTransitionEvent)
392def handle_customer_document_transition_event(obj, event):
393    """Append message to document history and log file.
394
395    Undo the verification of document and raise an exception if document
396    does not meet the requirements for verification.
397    """
398    if event.transition.destination == VERIFIED:
399        verifiable, error = obj.is_verifiable
400        if not verifiable:
401            # Undo transition and raise an exception.
402            IWorkflowState(obj).setState(event.transition.source)
403            raise InvalidTransitionError(error)
404    if event.transition.transition_id == 'verify':
405        obj.setMD5()
406    msg = event.transition.user_data['msg']
407    history = IObjectHistory(obj)
408    history.addMessage(msg)
409    try:
410        customers_container = grok.getSite()['customers']
411        customers_container.logger.info(
412            '%s - %s - %s' % (obj.customer.customer_id, obj.document_id, msg))
413    except (TypeError, AttributeError):
414        pass
415    return
Note: See TracBrowser for help on using the repository browser.