예제 #1
0
    def test_FinanceAdviceFoundInUpdateDelayAwareAdvices(self):
        """Check that every states of finance advice WF will be managed
           by @@update-delay-aware-advices."""
        '''Test the finances advice workflow.'''
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        self._configureFinancesAdvice(cfg)
        # put users in finances group
        self._setupFinancesGroup()
        update_advices_view = self.portal.restrictedTraverse(
            '@@update-delay-aware-advices')
        self.changeUser('pmCreator1')
        item = self.create('MeetingItem', title='The first item')
        # ask finances advice
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
        self.proposeItem(item)
        self.changeUser('pmReviewer1')
        self.do(item, 'wait_advices_from_prevalidated')

        # now act as the finances users
        self.changeUser('pmFinController')
        # set completeness
        item.setCompleteness('completeness_complete')
        item.at_post_edit_script()
        # add advice
        advice = createContentInContainer(
            item, 'meetingadvicefinances', **{
                'advice_group': finance_group_uid(),
                'advice_type': u'negative_finance',
                'advice_comment': RichTextValue(u'My comment finances'),
                'advice_category': u'acquisitions'
            })

        query = update_advices_view._computeQuery()
        catalog = api.portal.get_tool('portal_catalog')
        self.assertTrue(
            item.UID() in [brain.UID for brain in catalog(**query)])

        # send advice to finances editor
        self.do(advice, 'proposeToFinancialEditor')
        self.assertTrue(
            item.UID() in [brain.UID for brain in catalog(**query)])
        # send advice to finances reviewer
        self.changeUser('pmFinEditor')
        self.do(advice, 'proposeToFinancialReviewer')
        self.assertTrue(
            item.UID() in [brain.UID for brain in catalog(**query)])
        # send advice to finances Manager
        self.changeUser('pmFinReviewer')
        self.do(advice, 'proposeToFinancialManager')
        self.assertTrue(
            item.UID() in [brain.UID for brain in catalog(**query)])
        # sign the advice
        self.changeUser('pmFinManager')
        self.do(advice, 'signFinancialAdvice')
        # no more found now that advice is signed, at has been moved to 'advice_given'
        self.assertTrue(advice.queryState() in ADVICE_STATES_ENDED)
        self.assertFalse(
            item.UID() in [brain.UID for brain in catalog(**query)])
예제 #2
0
 def _add_finance_advice(item, newItem, last_transition):
     if item['toDiscuss'] and \
        item['category'] in ['affaires-juridiques', 'remboursement'] and \
        last_transition == 'prevalidate':
         finance_group = finance_group_uid()
         if finance_group:
             finance_advice_id = '{0}__rowid__unique_id_002'.format(
                 finance_group)
             optional_advisers = get_vocab(
                 newItem,
                 'Products.PloneMeeting.vocabularies.itemoptionaladvicesvocabulary'
             )
             if finance_advice_id not in optional_advisers:
                 _memos.clear()
                 finance_group = finance_group_uid()
                 finance_advice_id = '{0}__rowid__unique_id_002'.format(
                     finance_group)
             newItem.setOptionalAdvisers((finance_advice_id, ))
             newItem.updateLocalRoles()
             wfTool.doActionFor(newItem, 'wait_advices_from_prevalidated')
             newItem.setCompleteness('completeness_complete')
             newItem.updateLocalRoles()
             with api.env.adopt_user('dfin'):
                 advice = createContentInContainer(
                     newItem, 'meetingadvicefinances', **{
                         'advice_group': finance_group,
                         'advice_type': u'positive_finance',
                         'advice_comment': RichTextValue(u'Mon commentaire')
                     })
                 wfTool.doActionFor(advice, 'proposeToFinancialEditor')
                 wfTool.doActionFor(advice, 'proposeToFinancialReviewer')
                 wfTool.doActionFor(advice, 'proposeToFinancialManager')
                 wfTool.doActionFor(advice, 'signFinancialAdvice')
             return True
     return False
예제 #3
0
 def showFinancesAdvice(self, advice_data=None):
     """Finances advice is only shown :
        - if given (at worst it will be 'not_given_finance');
        - if advice_type is not not_required_finance;
        - in any case if item is in Council;
        - if item is 'validated' to everybody;
        - if it is 'prevalidated_waiting_advices', to finances advisers."""
     adviceHolder = self._advice_holder(advice_data)
     adviceObj = adviceHolder.getAdviceObj(finance_group_uid())
     # check advice state
     if not adviceObj or adviceObj.advice_type == 'not_required_finance':
         return False
     # check item state
     item_state = self.real_context.queryState()
     if item_state == 'validated' or adviceHolder.hasMeeting():
         return True
     # check user access (administrators and advisers from finance director service)
     tool = api.portal.get_tool('portal_plonemeeting')
     if tool.isManager(self.real_context, realManagers=True) \
             or (item_state == 'prevalidated_waiting_advices' and
                 tool.get_orgs_for_user(suffixes=['advisers'],
                                        using_groups=[finance_group_uid()],
                                        the_objects=False)):
         return True
     return False
예제 #4
0
    def test_AdviceCategoryCorrectlyIndexed(self):
        """When an advice_category is defined, it is correctly indexed
           it's parent after add/modify/delete."""
        catalog = self.portal.portal_catalog
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        self._configureFinancesAdvice(cfg)
        # put users in finances group
        self._setupFinancesGroup()
        self.changeUser('pmCreator1')
        item = self.create('MeetingItem', title='The first item')
        # ask finances advice
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
        self.proposeItem(item)
        self.changeUser('pmReviewer1')
        self.do(item, 'wait_advices_from_prevalidated')

        # now act as the finances users
        # advice may be added/edit when item is considered 'complete'
        self.changeUser('pmFinController')
        changeCompleteness = item.restrictedTraverse(
            '@@change-item-completeness')
        self.request.set('new_completeness_value', 'completeness_complete')
        self.request.form['form.submitted'] = True
        changeCompleteness()
        # add advice
        advice = createContentInContainer(
            item, 'meetingadvicefinances', **{
                'advice_group': finance_group_uid(),
                'advice_type': u'positive_finance',
                'advice_comment': RichTextValue(u'My comment finances'),
                'advice_category': u'acquisitions'
            })
        # reindexed when advice added
        itemUID = item.UID()
        self.assertEqual(
            catalog(meta_type='MeetingItem',
                    financesAdviceCategory=advice.advice_category)[0].UID,
            itemUID)
        # reindexed when advice edited
        advice.advice_category = u'attributions'
        # notify modified
        notify(ObjectModifiedEvent(advice))
        self.assertEqual(
            catalog(meta_type='MeetingItem',
                    financesAdviceCategory=advice.advice_category)[0].UID,
            itemUID)
        # reindexed when advice deleted
        self.portal.restrictedTraverse('@@delete_givenuid')(advice.UID())
        self.assertEqual(
            len(
                catalog(meta_type='MeetingItem',
                        financesAdviceCategory=advice.advice_category)), 0)
예제 #5
0
    def test_CollegePostPoneNextMeetingWithGivenAdvices(self):
        '''Check that postpone_next_meeting will work, especially because if finances advice
           is asked, it must be mandatorily given for the item to be validated.'''
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        self._configureFinancesAdvice(cfg)

        self.changeUser('pmCreator1')
        item = self.create('MeetingItem', title='The first item')
        # ask finances advice
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        self.proposeItem(item)

        # finances advice
        self.changeUser('pmReviewer1')
        self.do(item, 'wait_advices_from_prevalidated')
        self.changeUser('pmFinController')
        item.setCompleteness('completeness_complete')
        item._update_after_edit()
        # give advice positive with remarks
        # finances advice WF is tested in test_CollegeFinancesAdviceWF
        advice = createContentInContainer(
            item, 'meetingadvicefinances', **{
                'advice_group': finance_group_uid(),
                'advice_type': u'positive_finance',
                'advice_comment': RichTextValue(u'My comment finances'),
                'advice_category': u'acquisitions'
            })
        self.do(advice, 'proposeToFinancialEditor')
        self.changeUser('pmFinEditor')
        self.do(advice, 'proposeToFinancialReviewer')
        self.changeUser('pmFinReviewer')
        self.do(advice, 'proposeToFinancialManager')
        self.changeUser('pmFinManager')
        self.do(advice, 'signFinancialAdvice')

        # item was automatically validated, present it into a meeting
        self.changeUser('pmManager')
        meeting = self.create('Meeting', date=DateTime('2016/12/11'))
        self.presentItem(item)
        self.decideMeeting(meeting)
        self.do(item, 'postpone_next_meeting')
        # item was postponed, cloned item is validated and advices are inherited
        self.assertEqual(item.queryState(), 'postponed_next_meeting')
        cloneItem = item.getBRefs('ItemPredecessor')[0]
        for adviceInfo in cloneItem.adviceIndex.values():
            self.assertTrue(adviceInfo['inherited'])
예제 #6
0
def onAdvicesUpdated(item, event):
    '''
      If item is 'backToProposedToInternalReviewer', we need to reinitialize finances advice delay.
    '''
    for groupId, adviceInfo in item.adviceIndex.items():
        # special behaviour for finances advice
        if groupId != finance_group_uid():
            continue

        # when a finance advice is just timed out, we will send the item back to the refadmin
        if adviceInfo['delay_infos']['delay_status'] == 'timed_out' and \
           'delay_infos' in event.old_adviceIndex[groupId] and not \
           event.old_adviceIndex[groupId]['delay_infos']['delay_status'] == 'timed_out':
            if item.queryState() == 'prevalidated_waiting_advices':
                wfTool = api.portal.get_tool('portal_workflow')
                item.adviceIndex[groupId]['delay_stopped_on'] = datetime.now()
                item.REQUEST.set(
                    'maybackTo_proposed_to_refadmin_from_waiting_advices',
                    True)
                wfTool.doActionFor(
                    item,
                    'backTo_proposed_to_refadmin_from_waiting_advices',
                    comment='item_wf_changed_finance_advice_timed_out')
                item.REQUEST.set(
                    'maybackTo_proposed_to_refadmin_from_waiting_advices',
                    False)
예제 #7
0
    def _configureFinancesAdvice(self, cfg):
        """ """
        # add finances group
        self._createFinancesGroup()
        # put users in finances group
        self._setupFinancesGroup()
        # configure customAdvisers for 'meeting-config-college'
        # turn FINANCE_GROUP_ID into relevant org UID
        customAdvisers = deepcopy(charleroi_import_data.collegeMeeting.customAdvisers)
        for customAdviser in customAdvisers:
            customAdviser['org'] = finance_group_uid()
        cfg.setCustomAdvisers(customAdvisers)

        cfg.setTransitionsReinitializingDelays(
            charleroi_import_data.collegeMeeting.transitionsReinitializingDelays)
        # configure usedAdviceTypes
        cfg.setUsedAdviceTypes(('asked_again',
                                'positive',
                                'positive_with_remarks',
                                'negative',
                                'nil',
                                'positive_finance',
                                'positive_with_remarks_finance',
                                'negative_finance',
                                'not_given_finance'))
        # finances advice can be given when item in state 'prevalidated_waiting_advices'
        cfg.setKeepAccessToItemWhenAdviceIsGiven(True)
예제 #8
0
def financesAdviceCategory(item):
    """
      Indexes the 'advice_category' field defined on the contained 'meetingadvicefinances'.
    """
    # finance group could not be present at portal creation (necessary in tests)
    advice = item.getAdviceObj(finance_group_uid())
    if advice and advice.advice_category:
        return advice.advice_category
    return _marker
예제 #9
0
 def _setItemToWaitingAdvices(self, item, transition=None):
     """We need to ask finances advice to be able to do the transition."""
     originalMember = self.member.getId()
     self.changeUser('siteadmin')
     self._configureFinancesAdvice(self.meetingConfig)
     self.changeUser(originalMember)
     item.setOptionalAdvisers(item.getOptionalAdvisers() + (
         '{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
     item.at_post_edit_script()
     if transition:
         self.do(item, transition)
예제 #10
0
 def _setupFinancesGroup(self):
     '''Configure finances group.'''
     groupsTool = api.portal.get_tool('portal_groups')
     # add finances users to relevant groups
     # _advisers
     groupsTool.addPrincipalToGroup('pmFinController', '{0}_advisers'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('pmFinEditor', '{0}_advisers'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('pmFinReviewer', '{0}_advisers'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('pmFinManager', '{0}_advisers'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('dfin', '{0}_advisers'.format(finance_group_uid()))
     # respective _financesXXX groups
     groupsTool.addPrincipalToGroup('pmFinController', '{0}_financialcontrollers'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('pmFinEditor', '{0}_financialeditors'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('pmFinReviewer', '{0}_financialreviewers'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('pmFinManager', '{0}_financialmanagers'.format(finance_group_uid()))
     # dfin is member of every finances groups
     groupsTool.addPrincipalToGroup('dfin', '{0}_financialcontrollers'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('dfin', '{0}_financialeditors'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('dfin', '{0}_financialreviewers'.format(finance_group_uid()))
     groupsTool.addPrincipalToGroup('dfin', '{0}_financialmanagers'.format(finance_group_uid()))
예제 #11
0
    def _financeAdviceData(self, advice_data=None):
        """ """
        # if item is in Council, get the adviceData from it's predecessor
        if not advice_data:
            advice_data = self.real_context.getAdviceDataFor(
                self.real_context, finance_group_uid())

        adviceTypeTranslated = advice_data['type_translated'].replace(
            'Avis finances ', '')
        advice_data['advice_type_translated'] = adviceTypeTranslated

        return advice_data
예제 #12
0
 def printFinancesAdvice(self):
     """Print the legal text regarding Finances advice."""
     advice_data = self.real_context.getAdviceDataFor(
         self.real_context, finance_group_uid())
     if not self.showFinancesAdvice(advice_data=advice_data):
         return ''
     adviceData = self._financeAdviceData(advice_data=advice_data)
     delayStartedOnLocalized = adviceData['delay_infos'][
         'delay_started_on_localized']
     adviceGivenOnLocalized = adviceData['advice_given_on_localized']
     adviceTypeTranslated = safe_unicode(
         adviceData['advice_type_translated'])
     return FIN_ADVICE_LINE1.format(delayStartedOnLocalized) + \
         FIN_ADVICE_LINE2.format(adviceTypeTranslated, adviceGivenOnLocalized)
예제 #13
0
    def test_FinancesAdviserOnlyMayEvaluateCompleteness(self):
        '''Only finances adviser may evaluate completeness when item is 'waiting_advices'.'''
        self.changeUser('admin')
        self._configureFinancesAdvice(self.meetingConfig)
        # completeness widget is disabled for items of the config
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        recurringItem = cfg.getRecurringItems()[0]
        templateItem = cfg.getItemTemplates(as_brains=False)[0]
        self.assertFalse(recurringItem.adapted().mayEvaluateCompleteness())
        self.assertFalse(templateItem.adapted().mayEvaluateCompleteness())
        self.assertFalse(recurringItem.adapted().mayAskCompletenessEvalAgain())
        self.assertFalse(templateItem.adapted().mayAskCompletenessEvalAgain())

        # creator may not evaluate
        self.changeUser('pmCreator1')
        item = self.create('MeetingItem')
        self.assertTrue(
            item.getCompleteness() == 'completeness_not_yet_evaluated')
        self.assertTrue(self.hasPermission(ModifyPortalContent, item))
        self.assertFalse(item.adapted().mayEvaluateCompleteness())

        # reviewer may not evaluate
        self.proposeItem(item)
        self.changeUser('pmReviewer1')
        self.assertTrue(self.hasPermission(ModifyPortalContent, item))
        self.assertFalse(item.adapted().mayEvaluateCompleteness())

        # not sendable to 'waiting_advices' if finances advice not asked
        self.assertFalse('wait_advices' in self.transitions(item))
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        self.do(item, 'wait_advices_from_prevalidated')
        self.assertFalse(item.adapted().mayEvaluateCompleteness())

        # finances controller is able to evaluate
        self.changeUser('pmFinController')
        self.assertTrue(self.hasPermission(View, item))
        self.assertTrue(item.adapted().mayEvaluateCompleteness())
        itemCompletenessView = item.restrictedTraverse('item-completeness')
        # and even able to change it
        self.assertTrue(itemCompletenessView.listSelectableCompleteness())
예제 #14
0
    def test_MayChangeDelayTo(self):
        """Method MeetingItem.mayChangeDelayTo is made to control the 'available_on'
           of customAdvisers used for finance advice (5, 10 or 20 days).
           - 10 days is the only advice selectable thru the item edit form;
           - the delay '5' must be changed using the change delay widdget;
           - the delay '20' is reserved to finance advisers."""
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        self._configureFinancesAdvice(cfg)
        # put users in finances group
        self._setupFinancesGroup()
        self.changeUser('pmCreator1')
        item = self.create('MeetingItem')
        # by default, only the 10 days delay is selectable
        optional_advisers = get_vocab(
            item,
            'Products.PloneMeeting.vocabularies.itemoptionaladvicesvocabulary')
        self.assertEqual([term.token for term in optional_advisers], [
            'not_selectable_value_delay_aware_optional_advisers',
            '{0}__rowid__unique_id_002'.format(finance_group_uid()),
            'not_selectable_value_non_delay_aware_optional_advisers',
            self.developers_uid, self.vendors_uid
        ])
        # select the 10 days delay
        item.setOptionalAdvisers(
            ('%s__rowid__unique_id_002' % finance_group_uid(), ))
        item.at_post_edit_script()
        self.assertEqual(item.adviceIndex[finance_group_uid()]['delay'], '10')
        # Managers, are also required to use change delay widget for 5/20 delays
        self.changeUser('pmManager')
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))

        # user having 'Modify portal content' may select 10 but not others
        self.changeUser('pmCreator1')
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))
        # may select 5 if using change delay widget
        # aka 'managing_available_delays' is found in the REQUEST
        self.request.set('managing_available_delays', True)
        self.assertTrue(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))
        # change to 5 days
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_003'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        self.assertEqual(item.adviceIndex[finance_group_uid()]['delay'], '5')
        # could back to 10 days
        self.assertTrue(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))
        # Managers have bypass when using change delay widget
        self.changeUser('pmManager')
        self.assertTrue(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertTrue(item.adapted().mayChangeDelayTo(20))

        # now, when item is 'wait_advices_from_prevalidated', finance advisers
        # may select the 20 days delay
        self.changeUser('pmReviewer1')
        self.proposeItem(item)
        self.do(item, 'wait_advices_from_prevalidated')
        self.changeUser('pmFinController')
        # may change to 20
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertFalse(item.adapted().mayChangeDelayTo(10))
        self.assertTrue(item.adapted().mayChangeDelayTo(20))
        # change to 20 days
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_004'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        self.assertEqual(item.adviceIndex[finance_group_uid()]['delay'], '20')
        # once to 20, may back to 10
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertTrue(item.adapted().mayChangeDelayTo(20))

        # if item no more waiting finances advice, finances may not
        # change delay anymore
        self.do(item, 'backTo_proposed_to_refadmin_from_waiting_advices')
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertFalse(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))

        # if advice delay is set to 20, user have edit rights may not change it anymore
        self.changeUser('pmRefAdmin1')
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertFalse(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))

        # only a Manager will be able to change that delay now
        self.changeUser('pmManager')
        self.assertTrue(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertTrue(item.adapted().mayChangeDelayTo(20))
예제 #15
0
    def test_CollegeProcessWithFinancesAdvice(self):
        '''How does the process behave when the 'finances' advice is asked.'''
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        self._configureFinancesAdvice(cfg)

        self.changeUser('pmCreator1')
        item = self.create('MeetingItem', title='The first item')
        # ask finances advice
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        # not askable for now
        self.assertEqual(self.transitions(item), [
            'propose',
        ])

        # only directors may ask finances advice, item must be 'prevalidated'
        self.assertEqual(self.transitions(item), ['propose'])
        self.do(item, 'propose')
        self.assertFalse(self.transitions(item))
        self.changeUser('pmServiceHead1')
        self.assertEqual(self.transitions(item),
                         ['backToItemCreated', 'proposeToRefAdmin'])
        self.do(item, 'proposeToRefAdmin')
        self.assertFalse(self.transitions(item))
        self.changeUser('pmRefAdmin1')
        self.assertEqual(self.transitions(item),
                         ['backToProposed', 'prevalidate'])
        self.do(item, 'prevalidate')
        self.assertFalse(self.transitions(item))
        self.changeUser('pmReviewer1')
        # may only validate if no finances advice or finances advice is given
        self.assertEqual(self.transitions(item), [
            'backToItemCreatedFromPrevalidated',
            'backToProposedFromPrevalidated', 'backToProposedToRefAdmin',
            'wait_advices_from_prevalidated'
        ])
        # remove fact that finances advice was asked
        item.setOptionalAdvisers(())
        self.assertEqual(self.transitions(item), [
            'backToItemCreatedFromPrevalidated',
            'backToProposedFromPrevalidated', 'backToProposedToRefAdmin',
            'wait_advices_from_prevalidated'
        ])
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))

        # ask finances advice
        self.do(item, 'wait_advices_from_prevalidated')
        # when item is sent to finances, even the reviewer may not change it's state
        self.assertFalse(self.transitions(item))
        # item may be returned to RefAdmin if completeness not evaluated/incomplete
        self.changeUser('pmFinController')
        self.assertEqual(item.getCompleteness(),
                         'completeness_not_yet_evaluated')
        self.assertEqual(self.transitions(item),
                         ['backTo_proposed_to_refadmin_from_waiting_advices'])
        changeCompleteness = item.restrictedTraverse(
            '@@change-item-completeness')
        self.request.set('new_completeness_value', 'completeness_incomplete')
        self.request.form['form.submitted'] = True
        changeCompleteness()
        self.assertEqual(self.transitions(item),
                         ['backTo_proposed_to_refadmin_from_waiting_advices'])
        # once 'complete' item may not be returned to refAdmin anymore
        self.request.set('new_completeness_value', 'completeness_complete')
        changeCompleteness()
        self.assertEqual(self.transitions(item), [])

        # give advice positive with remarks
        # finances advice WF is tested in test_CollegeFinancesAdviceWF
        self.changeUser('pmFinController')
        advice = createContentInContainer(
            item, 'meetingadvicefinances', **{
                'advice_group': finance_group_uid(),
                'advice_type': u'negative_finance',
                'advice_comment': RichTextValue(u'My comment finances'),
                'advice_category': u'acquisitions'
            })
        # MeetingManager may not change item state when sent to finance, but Manager may
        self.do(advice, 'proposeToFinancialEditor')
        self.changeUser('pmManager')
        self.assertFalse(self.transitions(item))
        self.changeUser('siteadmin')
        self.assertEqual(self.transitions(item),
                         ['backTo_prevalidated_from_waiting_advices'])

        self.changeUser('pmFinEditor')
        self.do(advice, 'proposeToFinancialReviewer')
        self.changeUser('pmManager')
        self.assertFalse(self.transitions(item))
        self.changeUser('siteadmin')
        self.assertEqual(self.transitions(item),
                         ['backTo_prevalidated_from_waiting_advices'])

        self.changeUser('pmFinReviewer')
        self.do(advice, 'proposeToFinancialManager')
        self.changeUser('pmManager')
        self.assertFalse(self.transitions(item))
        self.changeUser('siteadmin')
        self.assertEqual(self.transitions(item),
                         ['backTo_prevalidated_from_waiting_advices'])

        self.changeUser('pmFinManager')
        self.do(advice, 'signFinancialAdvice')

        # item was sent back to administrative referent
        self.assertEqual(item.queryState(), 'proposed_to_refadmin')
        # now if it goes to director, director is able to validate the item
        self.changeUser('pmRefAdmin1')
        self.do(item, 'prevalidate')
        self.changeUser('pmReviewer1')
        # now item may be validated but finances advice may not be asked
        # anymore as it was already given
        self.assertEqual(self.transitions(item), [
            'backToItemCreatedFromPrevalidated',
            'backToProposedFromPrevalidated', 'backToProposedToRefAdmin',
            'validate'
        ])
        # if finances advice is 'asked_again', it is giveable again
        changeView = advice.restrictedTraverse('@@change-advice-asked-again')
        changeView()
        self.assertEqual(advice.advice_type, 'asked_again')
        self.assertEqual(self.transitions(item), [
            'backToItemCreatedFromPrevalidated',
            'backToProposedFromPrevalidated', 'backToProposedToRefAdmin',
            'wait_advices_from_prevalidated'
        ])
예제 #16
0
def onAdviceAfterTransition(advice, event):
    '''Called whenever a transition has been fired on an advice.'''

    if advice != event.object:
        return

    # pass if we are pasting items as advices are not kept
    if advice.REQUEST.get('currentlyPastingItems', False):
        return

    # manage finance workflow, just consider relevant transitions
    # if it is not a finance wf transition, return
    if advice.advice_group != finance_group_uid():
        return

    item = advice.getParentNode()
    itemState = item.queryState()

    oldStateId = event.old_state.id
    newStateId = event.new_state.id

    # initial_state or going back from 'advice_given', we set automatically
    # advice_hide_during_redaction to True
    if not event.transition or \
       newStateId == 'proposed_to_financial_controller' and oldStateId == 'advice_given':
        advice.advice_hide_during_redaction = True

    if newStateId == 'financial_advice_signed':
        plone_utils = api.portal.get_tool('plone_utils')
        # final state of the wf, make sure advice is no more hidden during redaction
        advice.advice_hide_during_redaction = False
        # if item was still in state 'prevalidated_waiting_advices',
        # it is automatically validated if advice is 'positive_finance'
        # otherwise it is sent back to the refadmin
        if itemState == 'prevalidated_waiting_advices':
            wfTool = api.portal.get_tool('portal_workflow')
            if advice.advice_type == 'positive_finance':
                item.REQUEST.set('mayValidate', True)
                wfTool.doActionFor(
                    item,
                    'backTo_validated_from_waiting_advices',
                    comment='item_wf_changed_finance_advice_positive')
                item.REQUEST.set('mayValidate', False)
                msg = _AP('backTo_validated_from_waiting_advices_done_descr')
            else:
                item.REQUEST.set(
                    'maybackTo_proposed_to_refadmin_from_waiting_advices',
                    True)
                wfTool.doActionFor(
                    item,
                    'backTo_proposed_to_refadmin_from_waiting_advices',
                    comment='item_wf_changed_finance_advice_not_positive')
                item.REQUEST.set(
                    'maybackTo_proposed_to_refadmin_from_waiting_advices',
                    False)
                sendMailIfRelevant(
                    item,
                    'sentBackToRefAdminWhileSigningNotPositiveFinancesAdvice',
                    'MeetingReviewer',
                    isRole=True)
                msg = _AP(
                    'backTo_proposed_to_refadmin_from_waiting_advices_done_descr'
                )
            plone_utils.addPortalMessage(msg)

    # in some corner case, we could be here and we are actually already updating advices,
    # this is the case if we validate an item and it triggers the fact that advice delay is exceeded
    # this should never be the case as advice delay should have been updated during nightly cron...
    # but if we are in a '_updateAdvices', do not _updateAdvices again...
    # also bypass if we are creating the advice as onAdviceAdded is called after onAdviceTransition
    if event.transition and not item.REQUEST.get('currentlyUpdatingAdvice',
                                                 False):
        item.updateLocalRoles()
    return
예제 #17
0
    def test_CollegeFinancesAdviceWF(self):
        '''Test the finances advice workflow.'''
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        self._configureFinancesAdvice(cfg)
        # put users in finances group
        self._setupFinancesGroup()
        self.changeUser('pmCreator1')
        item = self.create('MeetingItem', title='The first item')
        # ask finances advice
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
        self.proposeItem(item)
        self.changeUser('pmReviewer1')
        self.do(item, 'wait_advices_from_prevalidated')

        # now act as the finances users
        # advice may be added/edit when item is considered 'complete'
        self.changeUser('pmFinController')
        self.assertEqual(self.transitions(item),
                         ['backTo_proposed_to_refadmin_from_waiting_advices'])
        toAdd, toEdit = item.getAdvicesGroupsInfosForUser()
        self.assertFalse(toAdd or toEdit)

        # set item as "incomplete" using itemcompleteness view
        # this way, it checks that current user may actually evaluate completeness
        # and item is updated (at_post_edit_script is called)
        self.assertEqual(item.getCompleteness(),
                         'completeness_not_yet_evaluated')
        changeCompleteness = item.restrictedTraverse(
            '@@change-item-completeness')
        self.request.set('new_completeness_value', 'completeness_incomplete')
        self.request.form['form.submitted'] = True
        changeCompleteness()
        self.assertEqual(item.getCompleteness(), 'completeness_incomplete')
        # can be sent back even if considered incomplete
        self.assertEqual(self.transitions(item),
                         ['backTo_proposed_to_refadmin_from_waiting_advices'])
        toAdd, toEdit = item.getAdvicesGroupsInfosForUser()
        self.assertFalse(toAdd or toEdit)
        # back to refadmin
        self.do(item, 'backTo_proposed_to_refadmin_from_waiting_advices')

        # now do item complete
        self.changeUser('pmRefAdmin1')
        self.do(item, 'prevalidate')
        self.changeUser('pmReviewer1')
        self.do(item, 'wait_advices_from_prevalidated')
        self.changeUser('pmFinController')
        # delay is not started
        self.assertIsNone(
            item.adviceIndex[finance_group_uid()]['delay_started_on'])
        self.assertEqual(item.getCompleteness(),
                         'completeness_evaluation_asked_again')
        self.request.set('new_completeness_value', 'completeness_complete')
        changeCompleteness()
        self.assertEqual(item.getCompleteness(), 'completeness_complete')
        self.assertTrue(
            item.adviceIndex[finance_group_uid()]['delay_started_on'])
        # advice may be added
        toAdd, toEdit = item.getAdvicesGroupsInfosForUser()
        self.assertEqual(toAdd,
                         [(finance_group_uid(), u'Directeur Financier')])
        self.assertFalse(toEdit)
        # add advice
        advice = createContentInContainer(
            item, 'meetingadvicefinances', **{
                'advice_group': finance_group_uid(),
                'advice_type': u'negative_finance',
                'advice_comment': RichTextValue(u'My comment finances'),
                'advice_category': u'acquisitions'
            })

        # a meetingadvicefinances will be automatically hidden during redaction
        self.assertFalse(cfg.getDefaultAdviceHiddenDuringRedaction())
        self.assertTrue(advice.advice_hide_during_redaction)
        # editable
        self.assertTrue(item.adapted()._adviceIsEditableByCurrentUser(
            finance_group_uid()))

        # send advice to finances editor
        self.assertEqual(self.transitions(advice),
                         ['proposeToFinancialEditor'])
        self.do(advice, 'proposeToFinancialEditor')
        # send advice to finances reviewer
        self.changeUser('pmFinEditor')
        # editable
        self.assertTrue(item.adapted()._adviceIsEditableByCurrentUser(
            finance_group_uid()))
        self.assertEqual(self.transitions(advice), [
            'backToProposedToFinancialController', 'proposeToFinancialReviewer'
        ])
        self.do(advice, 'proposeToFinancialReviewer')
        # send advice to finances Manager
        self.changeUser('pmFinReviewer')
        # editable
        self.assertTrue(item.adapted()._adviceIsEditableByCurrentUser(
            finance_group_uid()))
        self.assertEqual(self.transitions(advice), [
            'backToProposedToFinancialController',
            'backToProposedToFinancialEditor', 'proposeToFinancialManager'
        ])
        self.do(advice, 'proposeToFinancialManager')
        # sign the advice
        self.changeUser('pmFinManager')
        # editable
        self.assertTrue(item.adapted()._adviceIsEditableByCurrentUser(
            finance_group_uid()))
        self.assertEqual(self.transitions(advice), [
            'backToProposedToFinancialController',
            'backToProposedToFinancialReviewer', 'signFinancialAdvice'
        ])

        # sign the advice, as it is 'negative_finance', aka not 'positive_finances'
        # it will be automatically sent back to the refadmin
        self.do(advice, 'signFinancialAdvice')
        self.assertEqual(item.queryState(), 'proposed_to_refadmin')
        # advice was automatically shown
        self.assertFalse(advice.advice_hide_during_redaction)

        # sign the advice as 'positive_finance' it will be automatically 'validated'
        self.changeUser('pmReviewer1')
        self.do(item, 'prevalidate')
        # as advice was already given, user needs to ask advice again
        self.assertFalse(
            'wait_advices_from_prevalidated' in self.transitions(item))
        changeView = advice.restrictedTraverse('@@change-advice-asked-again')
        changeView()
        self.assertEqual(advice.advice_type, 'asked_again')
        self.do(item, 'wait_advices_from_prevalidated')
        self.changeUser('pmFinController')
        # advice was automatically hidden
        self.assertTrue(advice.advice_hide_during_redaction)
        # delay is not started
        self.assertIsNone(
            item.adviceIndex[finance_group_uid()]['delay_started_on'])
        self.assertEqual(item.getCompleteness(),
                         'completeness_evaluation_asked_again')
        self.request.set('new_completeness_value', 'completeness_complete')
        changeCompleteness()
        self.assertTrue(
            item.adviceIndex[finance_group_uid()]['delay_started_on'])
        self.do(advice, 'proposeToFinancialEditor')
        self.changeUser('pmFinEditor')
        self.do(advice, 'proposeToFinancialReviewer')
        self.changeUser('pmFinReviewer')
        # advice may not be sent to financial manager if it is still asked_again
        # change advice_type to 'positive_finance'
        self.assertFalse(
            'proposeToFinancialManager' in self.transitions(advice))
        advice.advice_type = u'positive_finance'
        self.assertTrue(
            'proposeToFinancialManager' in self.transitions(advice))
        self.do(advice, 'proposeToFinancialManager')
        self.changeUser('pmFinManager')
        self.do(advice, 'signFinancialAdvice')
        self.assertEqual(item.queryState(), 'validated')
        # advice was automatically shown
        self.assertFalse(advice.advice_hide_during_redaction)
        # advice is no more editable/deletable by finances
        self.assertFalse(self.hasPermission(ModifyPortalContent, advice))
        self.assertFalse(self.hasPermission(DeleteObjects, advice))
예제 #18
0
    def test_ItemWithTimedOutAdviceIsAutomaticallySentBackToRefAdmin(self):
        '''When an item is 'prevalidated_waiting_advices', if delay is exceeded
           the 'backTo_proposed_to_refadmin_from_waiting_advices' is automatically
           triggered.
           If advice was already given but still not signed, 'advice_hide_during_redaction'
           is set to True so advice is considered 'not_given' as if it was never added.'''
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        self._configureFinancesAdvice(cfg)

        # first case, delay exceeded and advice was never given, the item is sent back to refadmin
        self.changeUser('pmCreator1')
        item = self.create('MeetingItem', title='The first item')
        # ask finances advice
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        self.proposeItem(item)
        # finances advice
        self.changeUser('pmReviewer1')
        self.do(item, 'wait_advices_from_prevalidated')
        self.changeUser('pmFinController')
        item.setCompleteness('completeness_complete')
        item.updateLocalRoles()

        # now does advice timed out
        item.adviceIndex[
            finance_group_uid()]['delay_started_on'] = datetime.datetime(
                2014, 1, 1)
        item.updateLocalRoles()
        # advice is timed out
        self.assertEqual(
            item.adviceIndex[finance_group_uid()]['delay_infos']
            ['delay_status'], 'no_more_giveable')
        # item has been automatically sent back to refadmin
        self.assertEqual(item.queryState(), 'proposed_to_refadmin')
        # advice delay is kept
        self.assertEqual(
            item.adviceIndex[finance_group_uid()]['delay_started_on'],
            datetime.datetime(2014, 1, 1))

        # second case, delay exceeded and advice exists but not signed
        # 'advice_hide_during_redaction' is set to True on the advice
        # and item is sent back to refadmin
        self.changeUser('pmCreator1')
        item = self.create('MeetingItem', title='The second item')
        # ask finances advice
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_002'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        self.proposeItem(item)
        # finances advice
        self.changeUser('pmReviewer1')
        self.do(item, 'wait_advices_from_prevalidated')
        self.changeUser('pmFinController')
        item.setCompleteness('completeness_complete')
        item._update_after_edit()
        # give advice positive with remarks
        advice = createContentInContainer(
            item, 'meetingadvicefinances', **{
                'advice_group': finance_group_uid(),
                'advice_type': u'positive_finance',
                'advice_comment': RichTextValue(u'My comment finances'),
                'advice_category': u'acquisitions'
            })
        self.do(advice, 'proposeToFinancialEditor')
        self.assertTrue(
            item.adviceIndex[finance_group_uid()]['hidden_during_redaction'])
        # now does advice timed out
        item.adviceIndex[
            finance_group_uid()]['delay_started_on'] = datetime.datetime(
                2014, 1, 1)
        item.updateLocalRoles()
        # advice is timed out
        self.assertEqual(
            item.adviceIndex[finance_group_uid()]['delay_infos']
            ['delay_status'], 'no_more_giveable')
        # item has been automatically sent back to refadmin
        self.assertTrue(item.queryState() == 'proposed_to_refadmin')
        # advice is still 'hidden_during_redaction'
        self.assertTrue(
            item.adviceIndex[finance_group_uid()]['hidden_during_redaction'])
예제 #19
0
 def _advice_holder(self, advice_data=None):
     if not advice_data:
         advice_data = self.real_context.getAdviceDataFor(
             self.real_context, finance_group_uid())
     return advice_data.get('adviceHolder', self.real_context)