## $Id: workflow.py 12168 2014-12-08 06:17:30Z 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, 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)
from waeup.ikoba.utils.helpers import get_current_principal
from waeup.ikoba.documents.workflow import VERIFICATION_TRANSITIONS

# Customer workflow

IMPORTABLE_REGISTRATION_STATES = (STARTED, REQUESTED, APPROVED)

REGISTRATION_TRANSITIONS = (
    Transition(
        transition_id = 'create',
        title = _('Create customer'),
        source = None,
        condition = NullCondition,
        msg = _('Customer record 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',
        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),

    )


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 (the same as verification workflow)

IMPORTABLE_CONTRACT_STATES = (CREATED, SUBMITTED, APPROVED, REJECTED, EXPIRED)

CONTRACT_TRANSITIONS = (
    Transition(
        transition_id = 'create',
        title = _('Create contract record'),
        source = None,
        condition = NullCondition,
        msg = _('Contract record 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),
    )


IMPORTABLE_CONTRACT_TRANSITIONS = [
    i.transition_id for i in REGISTRATION_TRANSITIONS]

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, also update
    last_transition_date when transition happened.

    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)
    obj.last_transition_date = datetime.utcnow()
    try:
        customers_container = grok.getSite()['customers']
        customers_container.logger.info('%s - %s' % (obj.customer_id,msg))
    except (TypeError, AttributeError):
        pass
    return