Fixed motion states, handled workflow field.
This commit is contained in:
parent
5b37a21c87
commit
c379544e97
@ -79,7 +79,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
|
|
||||||
.controller('AssignmentDetailCtrl', function($scope, Assignment, assignment) {
|
.controller('AssignmentDetailCtrl', function($scope, Assignment, assignment) {
|
||||||
Assignment.bindOne(assignment.id, $scope, 'assignment');
|
Assignment.bindOne(assignment.id, $scope, 'assignment');
|
||||||
Assignment.loadRelations(assignment);
|
Assignment.loadRelations(assignment, 'agenda_item');
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('AssignmentCreateCtrl', function($scope, $state, Assignment) {
|
.controller('AssignmentCreateCtrl', function($scope, $state, Assignment) {
|
||||||
|
@ -111,7 +111,7 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
// Load the global data on startup
|
// Load the global data on startup
|
||||||
.run([
|
.run([
|
||||||
'loadGlobalData',
|
'loadGlobalData',
|
||||||
function(loadGlobalData, operator) {
|
function(loadGlobalData) {
|
||||||
loadGlobalData();
|
loadGlobalData();
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
@ -646,7 +646,7 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
|
|
||||||
.controller('CustomslideDetailCtrl', function($scope, Customslide, customslide) {
|
.controller('CustomslideDetailCtrl', function($scope, Customslide, customslide) {
|
||||||
Customslide.bindOne(customslide.id, $scope, 'customslide');
|
Customslide.bindOne(customslide.id, $scope, 'customslide');
|
||||||
Customslide.loadRelations(customslide);
|
Customslide.loadRelations(customslide, 'agenda_item');
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('CustomslideCreateCtrl', function($scope, $state, Customslide) {
|
.controller('CustomslideCreateCtrl', function($scope, $state, Customslide) {
|
||||||
|
@ -409,6 +409,7 @@ class Motion(RESTModelMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
Returns the id of the workflow of the motion.
|
Returns the id of the workflow of the motion.
|
||||||
"""
|
"""
|
||||||
|
# TODO: Rename to workflow_id
|
||||||
return self.state.workflow.pk
|
return self.state.workflow.pk
|
||||||
|
|
||||||
def set_state(self, state):
|
def set_state(self, state):
|
||||||
@ -442,7 +443,7 @@ class Motion(RESTModelMixin, models.Model):
|
|||||||
new_state = self.state.workflow.first_state
|
new_state = self.state.workflow.first_state
|
||||||
else:
|
else:
|
||||||
new_state = (Workflow.objects.get(pk=config['motions_workflow']).first_state or
|
new_state = (Workflow.objects.get(pk=config['motions_workflow']).first_state or
|
||||||
Workflow.objects.get(pk=config['motions_workflow']).state_set.all()[0])
|
Workflow.objects.get(pk=config['motions_workflow']).states.all()[0])
|
||||||
self.set_state(new_state)
|
self.set_state(new_state)
|
||||||
|
|
||||||
def get_agenda_title(self):
|
def get_agenda_title(self):
|
||||||
@ -729,7 +730,7 @@ class State(RESTModelMixin, models.Model):
|
|||||||
action_word = models.CharField(max_length=255)
|
action_word = models.CharField(max_length=255)
|
||||||
"""An alternative string to be used for a button to switch to this state."""
|
"""An alternative string to be used for a button to switch to this state."""
|
||||||
|
|
||||||
workflow = models.ForeignKey('Workflow')
|
workflow = models.ForeignKey('Workflow', related_name='states')
|
||||||
"""A many-to-one relation to a workflow."""
|
"""A many-to-one relation to a workflow."""
|
||||||
|
|
||||||
next_states = models.ManyToManyField('self', symmetrical=False)
|
next_states = models.ManyToManyField('self', symmetrical=False)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides.core.config import config
|
|
||||||
from openslides.utils.rest_api import (
|
from openslides.utils.rest_api import (
|
||||||
CharField,
|
CharField,
|
||||||
DictField,
|
DictField,
|
||||||
@ -58,19 +57,20 @@ class StateSerializer(ModelSerializer):
|
|||||||
'versioning',
|
'versioning',
|
||||||
'leave_old_version_active',
|
'leave_old_version_active',
|
||||||
'dont_set_identifier',
|
'dont_set_identifier',
|
||||||
'next_states',)
|
'next_states',
|
||||||
|
'workflow')
|
||||||
|
|
||||||
|
|
||||||
class WorkflowSerializer(ModelSerializer):
|
class WorkflowSerializer(ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for motion.models.Workflow objects.
|
Serializer for motion.models.Workflow objects.
|
||||||
"""
|
"""
|
||||||
state_set = StateSerializer(many=True, read_only=True)
|
states = StateSerializer(many=True, read_only=True)
|
||||||
first_state = PrimaryKeyRelatedField(read_only=True)
|
first_state = PrimaryKeyRelatedField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Workflow
|
model = Workflow
|
||||||
fields = ('id', 'name', 'state_set', 'first_state',)
|
fields = ('id', 'name', 'states', 'first_state',)
|
||||||
|
|
||||||
|
|
||||||
class MotionLogSerializer(ModelSerializer):
|
class MotionLogSerializer(ModelSerializer):
|
||||||
@ -180,11 +180,14 @@ class MotionSerializer(ModelSerializer):
|
|||||||
log_messages = MotionLogSerializer(many=True, read_only=True)
|
log_messages = MotionLogSerializer(many=True, read_only=True)
|
||||||
polls = MotionPollSerializer(many=True, read_only=True)
|
polls = MotionPollSerializer(many=True, read_only=True)
|
||||||
reason = CharField(allow_blank=True, required=False, write_only=True)
|
reason = CharField(allow_blank=True, required=False, write_only=True)
|
||||||
state = StateSerializer(read_only=True)
|
|
||||||
text = CharField(write_only=True)
|
text = CharField(write_only=True)
|
||||||
title = CharField(max_length=255, write_only=True)
|
title = CharField(max_length=255, write_only=True)
|
||||||
versions = MotionVersionSerializer(many=True, read_only=True)
|
versions = MotionVersionSerializer(many=True, read_only=True)
|
||||||
workflow = IntegerField(min_value=1, required=False, validators=[validate_workflow_field])
|
workflow_id = IntegerField(
|
||||||
|
min_value=1,
|
||||||
|
required=False,
|
||||||
|
validators=[validate_workflow_field],
|
||||||
|
write_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Motion
|
model = Motion
|
||||||
@ -201,13 +204,13 @@ class MotionSerializer(ModelSerializer):
|
|||||||
'submitters',
|
'submitters',
|
||||||
'supporters',
|
'supporters',
|
||||||
'state',
|
'state',
|
||||||
'workflow',
|
'workflow_id',
|
||||||
'tags',
|
'tags',
|
||||||
'attachments',
|
'attachments',
|
||||||
'polls',
|
'polls',
|
||||||
'agenda_item_id',
|
'agenda_item_id',
|
||||||
'log_messages',)
|
'log_messages',)
|
||||||
read_only_fields = ('parent',) # Some other fields are also read_only. See definitions above.
|
read_only_fields = ('parent', 'state') # Some other fields are also read_only. See definitions above.
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
@ -220,7 +223,7 @@ class MotionSerializer(ModelSerializer):
|
|||||||
motion.reason = validated_data.get('reason', '')
|
motion.reason = validated_data.get('reason', '')
|
||||||
motion.identifier = validated_data.get('identifier')
|
motion.identifier = validated_data.get('identifier')
|
||||||
motion.category = validated_data.get('category')
|
motion.category = validated_data.get('category')
|
||||||
motion.reset_state(validated_data.get('workflow', int(config['motions_workflow'])))
|
motion.reset_state(validated_data.get('workflow_id'))
|
||||||
motion.save()
|
motion.save()
|
||||||
if validated_data.get('submitters'):
|
if validated_data.get('submitters'):
|
||||||
motion.submitters.add(*validated_data['submitters'])
|
motion.submitters.add(*validated_data['submitters'])
|
||||||
@ -242,9 +245,9 @@ class MotionSerializer(ModelSerializer):
|
|||||||
setattr(motion, key, validated_data[key])
|
setattr(motion, key, validated_data[key])
|
||||||
|
|
||||||
# Workflow.
|
# Workflow.
|
||||||
workflow = validated_data.get('workflow')
|
workflow_id = validated_data.get('workflow_id')
|
||||||
if workflow is not None and workflow != motion.workflow:
|
if workflow_id is not None and workflow_id != motion.workflow:
|
||||||
motion.reset_state(workflow)
|
motion.reset_state(workflow_id)
|
||||||
|
|
||||||
# Decide if a new version is saved to the database.
|
# Decide if a new version is saved to the database.
|
||||||
if (motion.state.versioning and
|
if (motion.state.versioning and
|
||||||
|
@ -2,14 +2,58 @@
|
|||||||
|
|
||||||
angular.module('OpenSlidesApp.motions', [])
|
angular.module('OpenSlidesApp.motions', [])
|
||||||
|
|
||||||
|
.factory('WorkflowState', [
|
||||||
|
'DS',
|
||||||
|
function (DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'motions/workflowstate',
|
||||||
|
methods: {
|
||||||
|
getNextStates: function () {
|
||||||
|
var states = [];
|
||||||
|
_.forEach(this.next_states_id, function (stateId) {
|
||||||
|
states.push(DS.get('motions/workflowstate', stateId));
|
||||||
|
})
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.factory('Workflow', [
|
||||||
|
'DS',
|
||||||
|
'jsDataModel',
|
||||||
|
'WorkflowState',
|
||||||
|
function (DS, jsDataModel, WorkflowState) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'motions/workflow',
|
||||||
|
useClass: jsDataModel,
|
||||||
|
relations: {
|
||||||
|
hasMany: {
|
||||||
|
'motions/workflowstate': {
|
||||||
|
localField: 'states',
|
||||||
|
foreignKey: 'workflow_id',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// Load all MotionWorkflows at stateup
|
||||||
|
.run([
|
||||||
|
'Workflow',
|
||||||
|
function (Workflow) {
|
||||||
|
Workflow.findAll();
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
.factory('MotionPoll', [
|
.factory('MotionPoll', [
|
||||||
'DS',
|
'DS',
|
||||||
'Config',
|
'Config',
|
||||||
'jsDataModel',
|
function (DS, Config) {
|
||||||
function(DS, Config, jsDataModel) {
|
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: 'motions/motionpoll',
|
name: 'motions/poll',
|
||||||
useClass: jsDataModel,
|
|
||||||
relations: {
|
relations: {
|
||||||
belongsTo: {
|
belongsTo: {
|
||||||
'motions/motion': {
|
'motions/motion': {
|
||||||
@ -149,10 +193,16 @@ angular.module('OpenSlidesApp.motions', [])
|
|||||||
localKeys: 'supporters_id',
|
localKeys: 'supporters_id',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'motions/motionpoll': {
|
'motions/poll': {
|
||||||
localField: 'polls',
|
localField: 'polls',
|
||||||
foreignKey: 'motion_id',
|
foreignKey: 'motion_id',
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
hasOne: {
|
||||||
|
'motions/workflowstate': {
|
||||||
|
localField: 'state',
|
||||||
|
localKey: 'state_id',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -165,13 +215,7 @@ angular.module('OpenSlidesApp.motions', [])
|
|||||||
});
|
});
|
||||||
}])
|
}])
|
||||||
|
|
||||||
.factory('Workflow', ['DS', function(DS) {
|
.run(['Motion', 'Category', function(Motion, Category) {}]);
|
||||||
return DS.defineResource({
|
|
||||||
name: 'motions/workflow',
|
|
||||||
});
|
|
||||||
}])
|
|
||||||
|
|
||||||
.run(['Motion', 'Category', 'Workflow', function(Motion, Category, Workflow) {}]);
|
|
||||||
|
|
||||||
|
|
||||||
angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||||
@ -221,9 +265,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
categories: function(Category) {
|
categories: function(Category) {
|
||||||
return Category.findAll();
|
return Category.findAll();
|
||||||
},
|
},
|
||||||
workflows: function(Workflow) {
|
|
||||||
return Workflow.findAll();
|
|
||||||
},
|
|
||||||
tags: function(Tag) {
|
tags: function(Tag) {
|
||||||
return Tag.findAll();
|
return Tag.findAll();
|
||||||
},
|
},
|
||||||
@ -262,9 +303,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
categories: function(Category) {
|
categories: function(Category) {
|
||||||
return Category.findAll();
|
return Category.findAll();
|
||||||
},
|
},
|
||||||
workflows: function(Workflow) {
|
|
||||||
return Workflow.findAll();
|
|
||||||
},
|
|
||||||
tags: function(Tag) {
|
tags: function(Tag) {
|
||||||
return Tag.findAll();
|
return Tag.findAll();
|
||||||
},
|
},
|
||||||
@ -405,7 +443,9 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
Category.bindAll({}, $scope, 'categories');
|
Category.bindAll({}, $scope, 'categories');
|
||||||
Workflow.bindAll({}, $scope, 'workflows');
|
Workflow.bindAll({}, $scope, 'workflows');
|
||||||
User.bindAll({}, $scope, 'users');
|
User.bindAll({}, $scope, 'users');
|
||||||
Motion.loadRelations(motion);
|
Motion.loadRelations(motion, 'agenda_item');
|
||||||
|
var state = motion.state;
|
||||||
|
state.getNextStates()
|
||||||
$scope.alert = {}; // TODO: show alert in template
|
$scope.alert = {}; // TODO: show alert in template
|
||||||
|
|
||||||
$scope.update_state = function (state_id) {
|
$scope.update_state = function (state_id) {
|
||||||
|
@ -6,7 +6,11 @@
|
|||||||
</small>
|
</small>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{{ motion.agenda_item }}
|
agenda_id: {{ motion.agenda_item }}
|
||||||
|
|
||||||
|
<br><br>state: {{ motion.state }}
|
||||||
|
|
||||||
|
<br><br>next states: {{ motion.state.getNextStates() }}
|
||||||
|
|
||||||
<div id="submenu">
|
<div id="submenu">
|
||||||
<a ui-sref="motions.motion.list" class="btn btn-sm btn-default">
|
<a ui-sref="motions.motion.list" class="btn btn-sm btn-default">
|
||||||
|
@ -108,15 +108,17 @@ class CreateMotion(TestCase):
|
|||||||
self.assertEqual(motion.tags.get().name, 'test_tag_iRee3kiecoos4rorohth')
|
self.assertEqual(motion.tags.get().name, 'test_tag_iRee3kiecoos4rorohth')
|
||||||
|
|
||||||
def test_with_workflow(self):
|
def test_with_workflow(self):
|
||||||
self.assertEqual(config['motions_workflow'], '1')
|
"""
|
||||||
|
Test to create a motion with a specific workflow.
|
||||||
|
"""
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse('motion-list'),
|
reverse('motion-list'),
|
||||||
{'title': 'test_title_eemuR5hoo4ru2ahgh5EJ',
|
{'title': 'test_title_eemuR5hoo4ru2ahgh5EJ',
|
||||||
'text': 'test_text_ohviePopahPhoili7yee',
|
'text': 'test_text_ohviePopahPhoili7yee',
|
||||||
'workflow': '2'})
|
'workflow_id': '2'})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
motion = Motion.objects.get()
|
self.assertEqual(Motion.objects.get().state.workflow_id, 2)
|
||||||
self.assertEqual(motion.state.workflow.pk, 2)
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateMotion(TestCase):
|
class UpdateMotion(TestCase):
|
||||||
@ -141,12 +143,15 @@ class UpdateMotion(TestCase):
|
|||||||
self.assertEqual(motion.identifier, 'test_identifier_jieseghohj7OoSah1Ko9')
|
self.assertEqual(motion.identifier, 'test_identifier_jieseghohj7OoSah1Ko9')
|
||||||
|
|
||||||
def test_patch_workflow(self):
|
def test_patch_workflow(self):
|
||||||
self.assertEqual(config['motions_workflow'], '1')
|
"""
|
||||||
|
Tests to only update the workflow of a motion.
|
||||||
|
"""
|
||||||
response = self.client.patch(
|
response = self.client.patch(
|
||||||
reverse('motion-detail', args=[self.motion.pk]),
|
reverse('motion-detail', args=[self.motion.pk]),
|
||||||
{'workflow': '2'})
|
{'workflow_id': '2'})
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
motion = Motion.objects.get()
|
motion = Motion.objects.get()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(motion.title, 'test_title_aeng7ahChie3waiR8xoh')
|
self.assertEqual(motion.title, 'test_title_aeng7ahChie3waiR8xoh')
|
||||||
self.assertEqual(motion.workflow, 2)
|
self.assertEqual(motion.workflow, 2)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user