Merge pull request #651 from normanjaeckel/Workflow_Adjustablility_2
Workflow adjustablility (new feature)
This commit is contained in:
commit
9a881eceeb
@ -17,7 +17,7 @@ from openslides.config.api import config
|
|||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
from openslides.utils.forms import CleanHtmlFormMixin
|
from openslides.utils.forms import CleanHtmlFormMixin
|
||||||
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
||||||
from .models import Motion, Category
|
from .models import Motion, Category, Workflow
|
||||||
|
|
||||||
|
|
||||||
class BaseMotionForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm):
|
class BaseMotionForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm):
|
||||||
@ -137,6 +137,19 @@ class MotionIdentifierMixin(forms.ModelForm):
|
|||||||
fields = ('identifier',)
|
fields = ('identifier',)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionSetWorkflowMixin(forms.ModelForm):
|
||||||
|
"""
|
||||||
|
Mixin to let the user change the workflow of the motion. When he does
|
||||||
|
so, the motion's state is reset.
|
||||||
|
"""
|
||||||
|
|
||||||
|
set_workflow = forms.ModelChoiceField(
|
||||||
|
queryset=Workflow.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=ugettext_lazy('Workflow'),
|
||||||
|
help_text=ugettext_lazy('Set a specific workflow to switch to it. If you do so, the state of the motion will be reset.'))
|
||||||
|
|
||||||
|
|
||||||
class MotionImportForm(CssClassMixin, forms.Form):
|
class MotionImportForm(CssClassMixin, forms.Form):
|
||||||
"""
|
"""
|
||||||
Form for motion import via csv file.
|
Form for motion import via csv file.
|
||||||
|
@ -421,13 +421,16 @@ class Motion(SlideMixin, models.Model):
|
|||||||
self.set_identifier()
|
self.set_identifier()
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
def reset_state(self):
|
def reset_state(self, workflow=None):
|
||||||
"""
|
"""
|
||||||
Set the state to the default state.
|
Set the state to the default state.
|
||||||
|
|
||||||
If the motion is new, it chooses the default workflow from config.
|
If the motion is new and workflow is None, it chooses the default
|
||||||
|
workflow from config.
|
||||||
"""
|
"""
|
||||||
if self.state:
|
if workflow:
|
||||||
|
new_state = workflow.first_state
|
||||||
|
elif self.state:
|
||||||
new_state = self.state.workflow.first_state
|
new_state = self.state.workflow.first_state
|
||||||
else:
|
else:
|
||||||
new_state = (Workflow.objects.get(pk=config['motion_workflow']).first_state or
|
new_state = (Workflow.objects.get(pk=config['motion_workflow']).first_state or
|
||||||
|
@ -187,10 +187,11 @@ def create_builtin_workflows(sender, **kwargs):
|
|||||||
versioning=True)
|
versioning=True)
|
||||||
state_2_9 = State.objects.create(name=ugettext_noop('needs review'),
|
state_2_9 = State.objects.create(name=ugettext_noop('needs review'),
|
||||||
workflow=workflow_2,
|
workflow=workflow_2,
|
||||||
|
action_word=ugettext_noop('Needs review'),
|
||||||
versioning=True)
|
versioning=True)
|
||||||
state_2_10 = State.objects.create(name=ugettext_noop('rejected (not authorized)'),
|
state_2_10 = State.objects.create(name=ugettext_noop('rejected (not authorized)'),
|
||||||
workflow=workflow_2,
|
workflow=workflow_2,
|
||||||
action_word=ugettext_noop('reject (not authorized)'),
|
action_word=ugettext_noop('Reject (not authorized)'),
|
||||||
versioning=True)
|
versioning=True)
|
||||||
state_2_1.next_states.add(state_2_2, state_2_5, state_2_10)
|
state_2_1.next_states.add(state_2_2, state_2_5, state_2_10)
|
||||||
state_2_2.next_states.add(state_2_3, state_2_4, state_2_5, state_2_6, state_2_7, state_2_8, state_2_9)
|
state_2_2.next_states.add(state_2_3, state_2_4, state_2_5, state_2_6, state_2_7, state_2_8, state_2_9)
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span8">
|
<div class="span8">
|
||||||
{# TODO: show only for complex workflow #}
|
{# TODO: show only for workflow with versioning #}
|
||||||
{% if motion.version.version_number < motion.last_version.version_number %}
|
{% if motion.version.version_number < motion.last_version.version_number %}
|
||||||
<span class="label label-warning">
|
<span class="label label-warning">
|
||||||
<i class="icon-warning-sign icon-white"></i> {% trans "This is not the newest version." %}
|
<i class="icon-warning-sign icon-white"></i> {% trans "This is not the newest version." %}
|
||||||
@ -289,7 +289,7 @@
|
|||||||
<h4>{% trans "Manage motion" %}</h4>
|
<h4>{% trans "Manage motion" %}</h4>
|
||||||
<div class="btn-group btn-group-vertical">
|
<div class="btn-group btn-group-vertical">
|
||||||
{% for state in motion.state.next_states.all %}
|
{% for state in motion.state.next_states.all %}
|
||||||
<a href="{% url 'motion_set_state' motion.pk state.pk %}" class="btn btn-small">{% trans state.name %}</a>
|
<a href="{% url 'motion_set_state' motion.pk state.pk %}" class="btn btn-small">{% trans state.get_action_word %}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<p></p>
|
<p></p>
|
||||||
|
@ -38,7 +38,7 @@ from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
|
|||||||
MotionVersion, State, WorkflowError, Category)
|
MotionVersion, State, WorkflowError, Category)
|
||||||
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
||||||
MotionDisableVersioningMixin, MotionCategoryMixin,
|
MotionDisableVersioningMixin, MotionCategoryMixin,
|
||||||
MotionIdentifierMixin, MotionImportForm)
|
MotionIdentifierMixin, MotionSetWorkflowMixin, MotionImportForm)
|
||||||
from .pdf import motions_to_pdf, motion_to_pdf, motion_poll_to_pdf
|
from .pdf import motions_to_pdf, motion_to_pdf, motion_poll_to_pdf
|
||||||
from .csv_import import import_motions
|
from .csv_import import import_motions
|
||||||
|
|
||||||
@ -91,8 +91,9 @@ class MotionMixin(object):
|
|||||||
|
|
||||||
def manipulate_object(self, form):
|
def manipulate_object(self, form):
|
||||||
"""
|
"""
|
||||||
Save the version data into the motion object before it is saved in
|
Saves the version data into the motion object before it is saved in
|
||||||
the Database.
|
the Database. Does also set category, identifier and new workflow
|
||||||
|
if given.
|
||||||
"""
|
"""
|
||||||
super(MotionMixin, self).manipulate_object(form)
|
super(MotionMixin, self).manipulate_object(form)
|
||||||
for attr in ['title', 'text', 'reason']:
|
for attr in ['title', 'text', 'reason']:
|
||||||
@ -120,6 +121,10 @@ class MotionMixin(object):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
workflow = form.cleaned_data.get('set_workflow', None)
|
||||||
|
if workflow:
|
||||||
|
self.object.reset_state(workflow)
|
||||||
|
|
||||||
def post_save(self, form):
|
def post_save(self, form):
|
||||||
"""
|
"""
|
||||||
Save the submitter an the supporter so the motion.
|
Save the submitter an the supporter so the motion.
|
||||||
@ -161,6 +166,8 @@ class MotionMixin(object):
|
|||||||
if self.object:
|
if self.object:
|
||||||
if config['motion_allow_disable_versioning'] and self.object.state.versioning:
|
if config['motion_allow_disable_versioning'] and self.object.state.versioning:
|
||||||
form_classes.append(MotionDisableVersioningMixin)
|
form_classes.append(MotionDisableVersioningMixin)
|
||||||
|
if self.request.user.has_perm('motion.can_manage_motion'):
|
||||||
|
form_classes.append(MotionSetWorkflowMixin)
|
||||||
return type('MotionForm', tuple(form_classes), {})
|
return type('MotionForm', tuple(form_classes), {})
|
||||||
|
|
||||||
|
|
||||||
|
@ -219,6 +219,19 @@ class TestMotionUpdateView(MotionViewTestCase):
|
|||||||
motion = Motion.objects.get(pk=self.motion1.pk)
|
motion = Motion.objects.get(pk=self.motion1.pk)
|
||||||
self.assertEqual(motion.versions.count(), 1)
|
self.assertEqual(motion.versions.count(), 1)
|
||||||
|
|
||||||
|
def test_set_another_workflow(self):
|
||||||
|
self.assertEqual(self.motion1.state.workflow.pk, 1)
|
||||||
|
response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2',
|
||||||
|
'text': 'eequei1Tee1aegeNgee0',
|
||||||
|
'submitter': self.admin})
|
||||||
|
self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 1)
|
||||||
|
response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2',
|
||||||
|
'text': 'eequei1Tee1aegeNgee0',
|
||||||
|
'submitter': self.admin,
|
||||||
|
'set_workflow': 2})
|
||||||
|
self.assertRedirects(response, '/motion/1/')
|
||||||
|
self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 2)
|
||||||
|
|
||||||
|
|
||||||
class TestMotionDeleteView(MotionViewTestCase):
|
class TestMotionDeleteView(MotionViewTestCase):
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user