diff --git a/openslides/motions/static/js/motions/site.js b/openslides/motions/static/js/motions/site.js
index e40e8e174..9fa12aca8 100644
--- a/openslides/motions/static/js/motions/site.js
+++ b/openslides/motions/static/js/motions/site.js
@@ -1428,6 +1428,7 @@ angular.module('OpenSlidesApp.motions.site', [
Workflow.bindAll({}, $scope, 'workflows');
$scope.model = {};
+ $scope.alert = {};
// Check whether this is a new amendment.
var isAmendment = $scope.$parent.motion && $scope.$parent.motion.id;
@@ -1464,6 +1465,13 @@ angular.module('OpenSlidesApp.motions.site', [
$state.go('motions.motion.detail', {id: success.id});
}
$scope.closeThisDialog();
+ },
+ function (error) {
+ var message = '';
+ for (var e in error.data) {
+ message += e + ': ' + error.data[e] + ' ';
+ }
+ $scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
diff --git a/openslides/motions/static/templates/motions/motion-list.html b/openslides/motions/static/templates/motions/motion-list.html
index 92cb9363e..2b0bb6260 100644
--- a/openslides/motions/static/templates/motions/motion-list.html
+++ b/openslides/motions/static/templates/motions/motion-list.html
@@ -590,7 +590,7 @@
-
+
{{ motion.motionBlock.title }}
diff --git a/openslides/motions/views.py b/openslides/motions/views.py
index ba95772f2..d8613ff38 100644
--- a/openslides/motions/views.py
+++ b/openslides/motions/views.py
@@ -85,14 +85,32 @@ class MotionViewSet(ModelViewSet):
"""
Customized view endpoint to create a new motion.
"""
+ # Check if parent motion exists
+ parent_motion = None
+ if 'parent_id' in request.data:
+ try:
+ parent_motion = Motion.objects.get(pk=request.data['parent_id'])
+ except Motion.DoesNotExist:
+ raise ValidationError({'detail': _('The parent motion does not exist.')})
+
# Check permission to send some data.
if not has_perm(request.user, 'motions.can_manage'):
- whitelist = (
+ whitelist = [
'title',
'text',
'reason',
'comments', # This is checked later.
- )
+ ]
+ if parent_motion: # For creating amendments.
+ whitelist.extend([
+ 'parent_id',
+ 'category_id', # This will be set to the matching
+ 'motion_block_id', # values from parent_motion.
+ ])
+ request.data['category_id'] = (
+ parent_motion.category.id if parent_motion.category else None)
+ request.data['motion_block_id'] = (
+ parent_motion.motion_block.id if parent_motion.motion_block else None)
for key in request.data.keys():
if key not in whitelist:
# Non-staff users are allowed to send only some data.
diff --git a/tests/integration/motions/test_viewset.py b/tests/integration/motions/test_viewset.py
index ab3e3014e..5769acdbe 100644
--- a/tests/integration/motions/test_viewset.py
+++ b/tests/integration/motions/test_viewset.py
@@ -264,8 +264,8 @@ class CreateMotion(TestCase):
Test to create a motion by a delegate, non staff user.
"""
self.admin = get_user_model().objects.get(username='admin')
- self.admin.groups.add(3)
- self.admin.groups.remove(4)
+ self.admin.groups.add(2)
+ self.admin.groups.remove(3)
response = self.client.post(
reverse('motion-list'),
@@ -274,6 +274,71 @@ class CreateMotion(TestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ def test_amendment_motion(self):
+ """
+ Test to create a motion with a parent motion as staff user.
+ """
+ parent_motion = self.create_parent_motion()
+ response = self.client.post(
+ reverse('motion-list'),
+ {'title': 'test_title_doe93Jsjd2sW20dkSl20',
+ 'text': 'test_text_feS20SksD8D25skmwD25',
+ 'parent_id': parent_motion.id})
+ created_motion = Motion.objects.get(pk=int(response.data['id']))
+
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ self.assertEqual(created_motion.parent, parent_motion)
+
+ def test_amendment_motion_parent_not_exist(self):
+ """
+ Test to create an amendment motion with a non existing parent.
+ """
+ response = self.client.post(
+ reverse('motion-list'),
+ {'title': 'test_title_gEjdkW93Wj23KS2s8dSe',
+ 'text': 'test_text_lfwLIC&AjfsaoijOEusa',
+ 'parent_id': 100})
+
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+ self.assertEqual(response.data, {'detail': 'The parent motion does not exist.'})
+
+ def test_amendment_motion_non_admin(self):
+ """
+ Test to create an amendment motion by a delegate. The parents
+ category should be also set on the new motion.
+ """
+ parent_motion = self.create_parent_motion()
+ category = Category.objects.create(
+ name='test_category_name_Dslk3Fj8s8Ps36S3Kskw',
+ prefix='TEST_PREFIX_L23skfmlq3kslamslS39')
+ parent_motion.category = category
+ parent_motion.save()
+
+ self.admin = get_user_model().objects.get(username='admin')
+ self.admin.groups.add(2)
+ self.admin.groups.remove(3)
+
+ response = self.client.post(
+ reverse('motion-list'),
+ {'title': 'test_title_fk3a0slalms47KSewnWG',
+ 'text': 'test_text_al3FMwSCNM31WOmw9ezx',
+ 'parent_id': parent_motion.id})
+ created_motion = Motion.objects.get(pk=int(response.data['id']))
+
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ self.assertEqual(created_motion.parent, parent_motion)
+ self.assertEqual(created_motion.category, category)
+
+ def create_parent_motion(self):
+ """
+ Returns a new created motion used for testing amendments.
+ """
+ response = self.client.post(
+ reverse('motion-list'),
+ {'title': 'test_title_3leoeo2qac7830c92j9s',
+ 'text': 'test_text_9dm3ks9gDuW20Al38L9w'})
+ return Motion.objects.get(pk=int(response.data['id']))
+
class RetrieveMotion(TestCase):
"""