def proceed(workflow_object, field, user, next_state=None, god_mod=False): def process(workflow_object, field, user, action, next_state=None, god_mod=False): current_state = getattr(workflow_object, field) proceedings = ProceedingService.get_available_proceedings(workflow_object, field, [current_state], user=user, god_mod=god_mod) c = proceedings.count() if c == 0: raise RiverException(ErrorCode.NO_AVAILABLE_NEXT_STATE_FOR_USER, "There is no available state for destination for the user.") if c > 1: if next_state: proceedings = proceedings.filter(meta__transition__destination_state=next_state) if proceedings.count() == 0: available_states = StateService.get_available_states(workflow_object, field, user) raise RiverException(ErrorCode.INVALID_NEXT_STATE_FOR_USER, "Invalid state is given(%s). Valid states is(are) %s" % ( next_state.__unicode__(), ','.join([ast.__unicode__() for ast in available_states]))) else: raise RiverException(ErrorCode.NEXT_STATE_IS_REQUIRED, "State must be given when there are multiple states for destination") proceeding = proceedings[0] proceeding.status = action proceeding.transactioner = user proceeding.transaction_date = datetime.now() if workflow_object.proceeding: proceeding.previous = workflow_object.proceeding proceeding.save() return proceeding proceeding = process(workflow_object, field, user, APPROVED, next_state, god_mod) current_state = getattr(workflow_object, field) # Any other proceeding is left? required_proceedings = ProceedingService.get_available_proceedings(workflow_object, field, [current_state], destination_state=next_state, god_mod=god_mod) transition_status = False if required_proceedings.count() == 0: setattr(workflow_object, field, proceeding.meta.transition.destination_state) transition_status = True # Next states should be PENDING back again if there is circle. ProceedingService.cycle_proceedings(workflow_object, field) # ProceedingService.get_next_proceedings(workflow_object, field).update(status=PENDING) with ProceedingSignal(workflow_object, field, proceeding), TransitionSignal(transition_status, workflow_object, field, proceeding), FinalSignal( workflow_object, field): workflow_object.save() LOGGER.debug("Workflow object %s for field %s is proceeded for next transition. Transition: %s -> %s" % ( workflow_object, field, current_state.label, getattr(workflow_object, field).label))
def proceed(workflow_object, user, next_state=None, god_mod=False): def process(workflow_object, user, action, next_state=None, god_mod=False): proceedings = ProceedingService.get_available_proceedings( workflow_object, [workflow_object.get_state()], user=user, god_mod=god_mod) c = proceedings.count() if c == 0: raise RiverException(ErrorCode.NO_AVAILABLE_NEXT_STATE_FOR_USER, "There is no available state for destination for the user.") if c > 1: if next_state: proceedings = proceedings.filter(meta__transition__destination_state=next_state) if proceedings.count() == 0: available_states = StateService.get_available_states(workflow_object, user) raise RiverException(ErrorCode.INVALID_NEXT_STATE_FOR_USER, "Invalid state is given(%s). Valid states is(are) %s" % ( next_state.__str__(), ','.join([ast.__str__() for ast in available_states]))) else: raise RiverException(ErrorCode.NEXT_STATE_IS_REQUIRED, "State must be given when there are multiple states for destination") proceeding = proceedings[0] proceeding.status = action proceeding.transactioner = user proceeding.transaction_date = timezone.now() if workflow_object.proceeding: proceeding.previous = workflow_object.proceeding proceeding.save() return proceeding proceeding = process(workflow_object, user, APPROVED, next_state, god_mod) # Any other proceeding is left? required_proceedings = ProceedingService.get_available_proceedings(workflow_object, [workflow_object.get_state()], destination_state=next_state, god_mod=god_mod) transition_status = False if required_proceedings.count() == 0: workflow_object.set_state(proceeding.meta.transition.destination_state) transition_status = True # Next states should be PENDING back again if there is circle. ProceedingService.cycle_proceedings(workflow_object) # ProceedingService.get_next_proceedings(workflow_object).update(status=PENDING) with ProceedingSignal(workflow_object, proceeding), TransitionSignal(transition_status, workflow_object, proceeding), FinalSignal(workflow_object): workflow_object.save() LOGGER.debug("Workflow object %s is proceeded for next transition. Transition: %s -> %s" % ( workflow_object, workflow_object.get_state().label, workflow_object.get_state()))
def test_cycle_proceedings(self): self.initialize_circular_scenario() ObjectService.register_object(self.objects[0], self.field) # No Cycle self.assertFalse(ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.objects[0].proceed(user=self.user1, next_state=self.in_progress_state, god_mod=True) self.assertEqual(5, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) # No Cycle self.assertFalse(ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.objects[0].proceed(user=self.user2, next_state=self.resolved_state, god_mod=True) self.assertEqual(5, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) # State is re-opened and cycle is detected. Transition in-progress to resolved proceeding is cloned self.assertFalse(ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.objects[0].proceed(user=self.user3, next_state=self.re_opened_state, god_mod=True) self.assertEqual(6, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual(ProceedingMeta.objects.get(transition__source_state=self.in_progress_state, transition__destination_state=self.resolved_state), Proceeding.objects.filter(object_id=self.objects[0].pk).latest('date_created').meta) # There will be no cycling even if the method is invoked. Because cycling is done in proceeding. self.assertFalse(ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.assertEqual(6, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) # State is in-progress and cycle is detected. Transition resolved to re-opened proceeding is cloned self.objects[0].proceed(user=self.user3, next_state=self.in_progress_state, god_mod=True) self.assertEqual(7, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual(ProceedingMeta.objects.get(transition__source_state=self.resolved_state, transition__destination_state=self.re_opened_state), Proceeding.objects.filter(object_id=self.objects[0].pk).latest('date_created').meta) # State is resolved and cycle is detected. Transition re-opened to in-progress proceeding is cloned self.objects[0].proceed(user=self.user3, next_state=self.resolved_state, god_mod=True) self.assertEqual(8, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual(ProceedingMeta.objects.get(transition__source_state=self.re_opened_state, transition__destination_state=self.in_progress_state), Proceeding.objects.filter(object_id=self.objects[0].pk).latest('date_created').meta) # State is re-opened and cycle is detected. Transition in-progress to resolved proceeding is cloned self.assertFalse(ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.objects[0].proceed(user=self.user3, next_state=self.re_opened_state, god_mod=True) self.assertEqual(9, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual(ProceedingMeta.objects.get(transition__source_state=self.in_progress_state, transition__destination_state=self.resolved_state), Proceeding.objects.filter(object_id=self.objects[0].pk).latest('date_created').meta) # State is in-progress and cycle is detected. Transition resolved to re-opened proceeding is cloned self.objects[0].proceed(user=self.user3, next_state=self.in_progress_state, god_mod=True) self.assertEqual(10, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual(ProceedingMeta.objects.get(transition__source_state=self.resolved_state, transition__destination_state=self.re_opened_state), Proceeding.objects.filter(object_id=self.objects[0].pk).latest('date_created').meta) # State is resolved and cycle is detected. Transition re-opened to in-progress proceeding is cloned self.objects[0].proceed(user=self.user3, next_state=self.resolved_state, god_mod=True) self.assertEqual(11, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual(ProceedingMeta.objects.get(transition__source_state=self.re_opened_state, transition__destination_state=self.in_progress_state), Proceeding.objects.filter(object_id=self.objects[0].pk).latest('date_created').meta) # No Cycle for closed state. self.objects[0].proceed(user=self.user4, next_state=self.closed_state, god_mod=True) self.assertEqual(11, Proceeding.objects.filter(object_id=self.objects[0].pk).count())
def test_cycle_proceedings(self): self.initialize_circular_scenario() ObjectService.register_object(self.objects[0], self.field) # No Cycle self.assertFalse( ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.objects[0].proceed(user=self.user1, next_state=self.in_progress_state, god_mod=True) self.assertEqual( 5, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) # No Cycle self.assertFalse( ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.objects[0].proceed(user=self.user2, next_state=self.resolved_state, god_mod=True) self.assertEqual( 5, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) # State is re-opened and cycle is detected. Transition in-progress to resolved proceeding is cloned self.assertFalse( ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.objects[0].proceed(user=self.user3, next_state=self.re_opened_state, god_mod=True) self.assertEqual( 6, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual( ProceedingMeta.objects.get( transition__source_state=self.in_progress_state, transition__destination_state=self.resolved_state), Proceeding.objects.filter( object_id=self.objects[0].pk).latest('date_created').meta) # There will be no cycling even if the method is invoked. Because cycling is done in proceeding. self.assertFalse( ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.assertEqual( 6, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) # State is in-progress and cycle is detected. Transition resolved to re-opened proceeding is cloned self.objects[0].proceed(user=self.user3, next_state=self.in_progress_state, god_mod=True) self.assertEqual( 7, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual( ProceedingMeta.objects.get( transition__source_state=self.resolved_state, transition__destination_state=self.re_opened_state), Proceeding.objects.filter( object_id=self.objects[0].pk).latest('date_created').meta) # State is resolved and cycle is detected. Transition re-opened to in-progress proceeding is cloned self.objects[0].proceed(user=self.user3, next_state=self.resolved_state, god_mod=True) self.assertEqual( 8, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual( ProceedingMeta.objects.get( transition__source_state=self.re_opened_state, transition__destination_state=self.in_progress_state), Proceeding.objects.filter( object_id=self.objects[0].pk).latest('date_created').meta) # State is re-opened and cycle is detected. Transition in-progress to resolved proceeding is cloned self.assertFalse( ProceedingService.cycle_proceedings(self.objects[0], self.field)) self.objects[0].proceed(user=self.user3, next_state=self.re_opened_state, god_mod=True) self.assertEqual( 9, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual( ProceedingMeta.objects.get( transition__source_state=self.in_progress_state, transition__destination_state=self.resolved_state), Proceeding.objects.filter( object_id=self.objects[0].pk).latest('date_created').meta) # State is in-progress and cycle is detected. Transition resolved to re-opened proceeding is cloned self.objects[0].proceed(user=self.user3, next_state=self.in_progress_state, god_mod=True) self.assertEqual( 10, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual( ProceedingMeta.objects.get( transition__source_state=self.resolved_state, transition__destination_state=self.re_opened_state), Proceeding.objects.filter( object_id=self.objects[0].pk).latest('date_created').meta) # State is resolved and cycle is detected. Transition re-opened to in-progress proceeding is cloned self.objects[0].proceed(user=self.user3, next_state=self.resolved_state, god_mod=True) self.assertEqual( 11, Proceeding.objects.filter(object_id=self.objects[0].pk).count()) self.assertEqual( ProceedingMeta.objects.get( transition__source_state=self.re_opened_state, transition__destination_state=self.in_progress_state), Proceeding.objects.filter( object_id=self.objects[0].pk).latest('date_created').meta) # No Cycle for closed state. self.objects[0].proceed(user=self.user4, next_state=self.closed_state, god_mod=True) self.assertEqual( 11, Proceeding.objects.filter(object_id=self.objects[0].pk).count())