## $Id: workflow.py 12526 2015-01-30 14:02:53Z henrik $ ## ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """Workflow for customers. """ import grok from datetime import datetime from zope.component import getUtility from hurry.workflow.workflow import Transition, WorkflowState, NullCondition from hurry.workflow.interfaces import ( IWorkflowState, IWorkflowTransitionEvent, InvalidTransitionError) from waeup.ikoba.interfaces import ( IObjectHistory, IIkobaWorkflowInfo, IIkobaUtils, STARTED, CREATED, REQUESTED, PROVISIONALLY, APPROVED, SUBMITTED, VERIFIED, REJECTED, EXPIRED) from waeup.ikoba.interfaces import MessageFactory as _ from waeup.ikoba.workflow import ( IkobaWorkflow, IkobaWorkflowInfo) from waeup.ikoba.customers.interfaces import ( ICustomer, ICustomersUtils, IContract, ICustomerDocument) from waeup.ikoba.utils.helpers import get_current_principal # Customer workflow IMPORTABLE_REGISTRATION_STATES = (STARTED, REQUESTED, APPROVED) REGISTRATION_TRANSITIONS = ( Transition( transition_id = 'create', title = _('Create customer'), source = None, condition = NullCondition, msg = _('Customer created'), destination = CREATED), Transition( transition_id = 'start', title = _('Start registration'), source = CREATED, condition = NullCondition, msg = _('Customer registration started'), destination = STARTED), Transition( transition_id = 'request', title = _('Request registration'), msg = _('Customer registration requested'), source = STARTED, destination = REQUESTED), Transition( transition_id = 'approve_provisionally', title = _('Approve customer provisionally'), msg = _('Customer registration provisionally approved'), source = REQUESTED, destination = PROVISIONALLY), Transition( transition_id = 'approve_finally', title = _('Approve finally'), msg = _('Customer registration finally approved'), source = PROVISIONALLY, destination = APPROVED), Transition( transition_id = 'approve', title = _('Approve customer'), msg = _('Customer registration approved'), source = REQUESTED, destination = APPROVED), Transition( transition_id = 'reject', title = _('Reject customer'), msg = _('Customer registration rejected'), source = REQUESTED, destination = STARTED), Transition( transition_id = 'reset1', title = _('Reset customer'), msg = _('Reset to initial customer state'), source = APPROVED, destination = STARTED), Transition( transition_id = 'reset2', title = _('Reset to requested'), msg = _("Reset to 'requested'"), source = APPROVED, destination = REQUESTED), Transition( transition_id = 'reset3', title = _('Reset customer'), msg = _("Reset to initial state"), source = REQUESTED, destination = STARTED), Transition( transition_id = 'reset4', title = _('Reset customer'), msg = _('Reset to initial customer state'), source = PROVISIONALLY, destination = STARTED), Transition( transition_id = 'reset5', title = _('Reset to requested'), msg = _("Reset to 'requested'"), source = PROVISIONALLY, destination = REQUESTED), ) IMPORTABLE_REGISTRATION_TRANSITIONS = [i.transition_id for i in REGISTRATION_TRANSITIONS] registration_workflow = IkobaWorkflow(REGISTRATION_TRANSITIONS) class RegistrationWorkflowState(WorkflowState, grok.Adapter): """An adapter to adapt Customer objects to workflow states. """ grok.context(ICustomer) grok.provides(IWorkflowState) state_key = 'wf.registration.state' state_id = 'wf.registration.id' class RegistrationWorkflowInfo(IkobaWorkflowInfo, grok.Adapter): """Adapter to adapt Customer objects to workflow info objects. """ grok.context(ICustomer) grok.provides(IIkobaWorkflowInfo) def __init__(self, context): self.context = context self.wf = registration_workflow @grok.subscribe(ICustomer, IWorkflowTransitionEvent) def handle_customer_transition_event(obj, event): """Append message to customer history and log file when transition happened. """ msg = event.transition.user_data['msg'] history = IObjectHistory(obj) history.addMessage(msg) try: customers_container = grok.getSite()['customers'] customers_container.logger.info('%s - %s' % (obj.customer_id,msg)) except (TypeError, AttributeError): pass return # Contract workflow CONTRACT_TRANSITIONS = ( Transition( transition_id = 'create', title = _('Create contract'), source = None, condition = NullCondition, msg = _('Contract created'), destination = CREATED), Transition( transition_id = 'submit', title = _('Submit for approval'), msg = _('Submitted for approval'), source = CREATED, destination = SUBMITTED), Transition( transition_id = 'approve', title = _('Approve'), msg = _('Approved'), source = SUBMITTED, destination = APPROVED), Transition( transition_id = 'reject', title = _('Reject'), msg = _('REJECTED'), source = SUBMITTED, destination = REJECTED), Transition( transition_id = 'reset1', title = _('Reset to initial state'), msg = _('Reset to initial state'), source = REJECTED, destination = CREATED), Transition( transition_id = 'reset2', title = _('Reset to initial state'), msg = _('Reset to initial state'), source = APPROVED, destination = CREATED), Transition( transition_id = 'reset3', title = _('Reset to initial state'), msg = _('Reset to initial state'), source = SUBMITTED, destination = CREATED), Transition( transition_id = 'expire', title = _('Set to expired'), msg = _('Set to expired'), source = APPROVED, destination = EXPIRED), Transition( transition_id = 'reset4', title = _('Reset to initial state'), msg = _('Reset to initial state'), source = EXPIRED, destination = CREATED), ) contract_workflow = IkobaWorkflow(CONTRACT_TRANSITIONS) class ContractWorkflowState(WorkflowState, grok.Adapter): """An adapter to adapt Contract objects to workflow states. """ grok.context(IContract) grok.provides(IWorkflowState) state_key = 'wf.contract.state' state_id = 'wf.contract.id' class ContractWorkflowInfo(IkobaWorkflowInfo, grok.Adapter): """Adapter to adapt Contract objects to workflow info objects. """ grok.context(IContract) grok.provides(IIkobaWorkflowInfo) def __init__(self, context): self.context = context self.wf = contract_workflow @grok.subscribe(IContract, IWorkflowTransitionEvent) def handle_contract_transition_event(obj, event): """Append message to contract history and log file. Undo the approval of contract and raise an exception if contract does not meet the requirements for approval. """ if event.transition.destination == APPROVED: approvable, error = obj.is_approvable if not approvable: # Undo transition and raise an exception. IWorkflowState(obj).setState(event.transition.source) raise InvalidTransitionError(error) msg = event.transition.user_data['msg'] history = IObjectHistory(obj) history.addMessage(msg) try: customers_container = grok.getSite()['customers'] customers_container.logger.info( '%s - %s - %s' % (obj.customer.customer_id, obj.contract_id, msg)) except (TypeError, AttributeError): pass return # Customer document workflow VERIFICATION_TRANSITIONS = ( Transition( transition_id = 'create', title = _('Create document'), source = None, condition = NullCondition, msg = _('Document created'), destination = CREATED), Transition( transition_id = 'submit', title = _('Submit for verification'), msg = _('Submitted for verification'), source = CREATED, destination = SUBMITTED), Transition( transition_id = 'verify', title = _('Verify'), msg = _('Verified'), source = SUBMITTED, destination = VERIFIED), Transition( transition_id = 'reject', title = _('Reject'), msg = _('REJECTED'), source = SUBMITTED, destination = REJECTED), Transition( transition_id = 'reset1', title = _('Reset to initial state'), msg = _('Reset to initial state'), source = REJECTED, destination = CREATED), Transition( transition_id = 'reset2', title = _('Reset to initial state'), msg = _('Reset to initial state'), source = VERIFIED, destination = CREATED), Transition( transition_id = 'reset3', title = _('Reset to initial state'), msg = _('Reset to initial state'), source = SUBMITTED, destination = CREATED), Transition( transition_id = 'expire', title = _('Set to expired'), msg = _('Set to expired'), source = VERIFIED, destination = EXPIRED), Transition( transition_id = 'reset4', title = _('Reset to initial state'), msg = _('Reset to initial state'), source = EXPIRED, destination = CREATED), ) verification_workflow = IkobaWorkflow(VERIFICATION_TRANSITIONS) class VerificationWorkflowState(WorkflowState, grok.Adapter): """An adapter to adapt CustomerDocument objects to workflow states. """ grok.context(ICustomerDocument) grok.provides(IWorkflowState) state_key = 'wf.verification.state' state_id = 'wf.verification.id' class VerificationWorkflowInfo(IkobaWorkflowInfo, grok.Adapter): """Adapter to adapt CustomerDocument objects to workflow info objects. """ grok.context(ICustomerDocument) grok.provides(IIkobaWorkflowInfo) def __init__(self, context): self.context = context self.wf = verification_workflow @grok.subscribe(ICustomerDocument, IWorkflowTransitionEvent) def handle_customer_document_transition_event(obj, event): """Append message to document history and log file. Undo the verification of document and raise an exception if document does not meet the requirements for verification. """ if event.transition.destination == VERIFIED: verifiable, error = obj.is_verifiable if not verifiable: # Undo transition and raise an exception. IWorkflowState(obj).setState(event.transition.source) raise InvalidTransitionError(error) if event.transition.transition_id == 'verify': obj.setMD5() msg = event.transition.user_data['msg'] history = IObjectHistory(obj) history.addMessage(msg) try: customers_container = grok.getSite()['customers'] customers_container.logger.info( '%s - %s - %s' % (obj.customer.customer_id, obj.document_id, msg)) except (TypeError, AttributeError): pass return