diff --git a/app/apps/api/serializers.py b/app/apps/api/serializers.py
index ed97f2b477a30f8a829186aca3f33b8ea56df20a..36122feb22fe940a8a51a1c8a4938094b19aea90 100644
--- a/app/apps/api/serializers.py
+++ b/app/apps/api/serializers.py
@@ -200,26 +200,39 @@ class LineSerializer(serializers.ModelSerializer):
         list_serializer_class = LineListSerializer
 
 
-class LineMoveSerializer(serializers.ModelSerializer):
-    index = serializers.IntegerField()
+class LineOrderListSerializer(serializers.ListSerializer):
+    def update(self, qs, validated_data):
+        # Maps for id->instance and id->data item.
+        line_mapping = {line.pk: line for line in qs}
+        data_mapping = {item['pk']: item for item in validated_data}
 
-    class Meta:
-        model = Line
-        fields = ('index',)
+        # we can only go down or up (not both)
+        first_ = qs[0]
+        down = first_.order < data_mapping[first_.pk]['order']
+        lines = list(data_mapping.items())
+        lines.sort(key=lambda l: l[1]['order'])
+        if down:
+            # reverse to avoid pushing up already moved lines
+            lines.reverse()
 
-    def __init__(self, *args, line=None, **kwargs):
-        self.line = line
-        super().__init__(*args, **kwargs)
+        for i, (line_id, data) in enumerate(lines):
+            line = line_mapping.get(line_id, None)
+            line.to(data['order'])
 
-    def move(self):
-        self.line.to(self.validated_data['index'])
-        self.line.save()
+        line.document_part.enforce_line_order()
+        # returns all new ordering for the whole page
+        data = self.child.__class__(line.document_part.lines.all(), many=True).data
+        return data
 
 
 class LineOrderSerializer(serializers.ModelSerializer):
+    pk = serializers.IntegerField()
+    order = serializers.IntegerField()
+
     class Meta:
         model = Line
         fields = ('pk', 'order')
+        list_serializer_class = LineOrderListSerializer
 
 
 class DetailedLineSerializer(LineSerializer):
diff --git a/app/apps/api/views.py b/app/apps/api/views.py
index ce58fd437769070df4ce720fae9c2727caf21b66..366475d0075557c62dd3d386c1235c77d5861a6d 100644
--- a/app/apps/api/views.py
+++ b/app/apps/api/views.py
@@ -23,7 +23,6 @@ from api.serializers import (UserOnboardingSerializer,
                              BlockTypeSerializer,
                              LineTypeSerializer,
                              DetailedLineSerializer,
-                             LineMoveSerializer,
                              LineOrderSerializer,
                              TranscriptionSerializer,
                              LineTranscriptionSerializer)
@@ -252,33 +251,14 @@ class LineViewSet(ModelViewSet):
 
     @action(detail=False, methods=['post'])
     def move(self, request, document_pk=None, part_pk=None, pk=None):
-        lines = request.data.get("lines")
-        response = []
-        errors = []
-
-        for line in lines:
-            l = get_object_or_404(Line, pk=line["pk"])
-            serializer = LineMoveSerializer(line=l, data=line)
-            if serializer.is_valid():
-                serializer.move()
-                response.append(serializer.data)
-            else:
-                errors.append(errors)
-
-        if errors:
-            return Response(errors,
-                            status=status.HTTP_400_BAD_REQUEST)
-        return Response(response)
-
-        # return Response(status=200, data=response)
-
-        # line = get_object_or_404(Line, pk=pk)
-        # serializer = LineMoveSerializer(line=line, data=request.data)
-        # if serializer.is_valid():
-        #     serializer.move()
-        #     return Response({'status': 'moved'})
-        # else:
-        #     return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+        data = request.data.get('lines')
+        qs = Line.objects.filter(pk__in=[l['pk'] for l in data])
+        serializer = LineOrderSerializer(qs, data=data, many=True)
+        if serializer.is_valid():
+            resp = serializer.save()
+            return Response(resp, status=200)
+        else:
+            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
 
 class LargeResultsSetPagination(PageNumberPagination):
diff --git a/app/apps/core/models.py b/app/apps/core/models.py
index 9d53131c50c21516270b718c6eaa968e92518dce..831f4ef470f556427953d531166eeb88bebf57de 100644
--- a/app/apps/core/models.py
+++ b/app/apps/core/models.py
@@ -763,6 +763,15 @@ class DocumentPart(OrderedModel):
 
         return to_calc
 
+    def enforce_line_order(self):
+        # django-ordered-model doesn't care about unicity and linearity...
+        lines = self.lines.order_by('order', 'pk')
+        for i, line in enumerate(lines):
+            if line.order != i:
+                logger.debug('Enforcing line order %d : %d' % (line.pk, i))
+                line.order = i
+                line.save()
+
 
 def validate_polygon(value):
     if value is None:
@@ -829,7 +838,7 @@ class Line(OrderedModel):  # Versioned,
     script = models.CharField(max_length=8, null=True, blank=True)  # choices ??
     # text direction
     order_with_respect_to = 'document_part'
-    version_ignore_fields = ('document_part', 'order')
+    # version_ignore_fields = ('document_part', 'order')
 
     typology = models.ForeignKey(LineType, null=True, blank=True,
                                  on_delete=models.SET_NULL)
diff --git a/app/apps/core/static/js/edit/components/diplo_line.js b/app/apps/core/static/js/edit/components/diplo_line.js
index 8fb919733abe7dd004875c20118b46ec0df8a69d..0a67afdafc8320a62e97b7709e0f92e1725165f6 100644
--- a/app/apps/core/static/js/edit/components/diplo_line.js
+++ b/app/apps/core/static/js/edit/components/diplo_line.js
@@ -23,17 +23,18 @@ var diploLine = LineBase.extend({
         this.getEl().remove();
     },
     watch: {
-        'line.order': function(n,o) {
-            // make sure it's at the right place,
-            // in case it was just created or the ordering got recalculated
-            let index = Array.from(this.$el.parentNode.children).indexOf(this.$el);
-            if (index != this.line.order) {
-                this.$el.parentNode.insertBefore(
-                    this.$el,
-                    this.$el.parentNode.children[this.line.order]);
-                this.setElContent(this.line.currentTrans.content);
-            }
-        },
+        /* 'line.order': function(n,o) {
+         *     // make sure it's at the right place,
+         *     // in case it was just created or the ordering got recalculated
+         *     let index = Array.from(this.$el.parentNode.children).indexOf(this.$el);
+         *     console.log(index, this.line.order);
+         *     if (index != this.line.order) {
+         *         this.$el.parentNode.insertBefore(
+         *             this.$el,
+         *             this.$el.parentNode.children[this.line.order]);
+         *         this.setElContent(this.line.currentTrans.content);
+         *     }
+         * }, */
         'line.currentTrans': function(n, o) {
             if (n!=undefined && n.content) {
                 this.setElContent(n.content);
diff --git a/app/apps/core/static/js/edit/components/diplo_panel.js b/app/apps/core/static/js/edit/components/diplo_panel.js
index 56c8e6ec22ab7b45dfcb40a5e42b29c340fad43f..fa8ba33fe6e87f474fbe61d67cbf5404db724df8 100644
--- a/app/apps/core/static/js/edit/components/diplo_panel.js
+++ b/app/apps/core/static/js/edit/components/diplo_panel.js
@@ -25,7 +25,7 @@ var DiploPanel = BasePanel.extend({
                 selectedClass: "selected",
                 animation: 150,
                 onEnd: function(evt) {
-                    vm.onDragginEnd(evt);
+                    vm.onDraggingEnd(evt);
                 }
             });
         }.bind(this));
@@ -97,21 +97,23 @@ var DiploPanel = BasePanel.extend({
             this.constrainLineNumber();
             this.save();
         },
-        onDragginEnd(ev) {
+        onDraggingEnd(ev) {
             /*
                Finish dragging lines, save new positions
              */
-            if(ev.newIndicies.length == 0 && ev.newIndex != ev.oldIndex){
+            if(ev.newIndicies.length == 0 && ev.newIndex != ev.oldIndex) {
+                let diploLine = this.$children.find(dl=>dl.line.order==ev.oldIndex);
                 this.movedLines.push({
-                    "pk": this.$children[ev.oldIndex].line.pk,
-                    "index": ev.newIndex
+                    "pk": diploLine.line.pk,
+                    "order": ev.newIndex
                 });
             } else {
-                for(let i=0; i< ev.newIndicies.length; i++){
-                    // TODO: doesn't appear to  work?!
+                for(let i=0; i< ev.newIndicies.length; i++) {
+
+                    let diploLine = this.$children.find(dl=>dl.line.order==ev.oldIndicies[i].index);
                     this.movedLines.push({
-                        "pk": this.$children[ev.oldIndicies[i].index].line.pk,
-                        "index": ev.newIndicies[i].index
+                        "pk": diploLine.line.pk,
+                        "order": ev.newIndicies[i].index
                     });
                 }
             }
@@ -119,7 +121,7 @@ var DiploPanel = BasePanel.extend({
         },
         moveLines() {
             if(this.movedLines.length != 0){
-                this.$parent.$emit('line:move', this.movedLines, function () {
+                this.$parent.$emit('move:line', this.movedLines, function () {
                     this.movedLines = [];
                 }.bind(this));
             }
@@ -213,7 +215,8 @@ var DiploPanel = BasePanel.extend({
             let target = ev.target.nodeType==Node.TEXT_NODE?ev.target.parentNode:ev.target;
             let index = Array.prototype.indexOf.call(target.parentNode.children, target);
             if (index > -1 && index < this.$children.length) {
-                this.$children[index].showOverlay();
+                let diploLine = this.$children.find(dl=>dl.line.order==index);
+                if (diploLine) diploLine.showOverlay();
             } else {
                 this.hideOverlay();
             }
@@ -248,8 +251,9 @@ var DiploPanel = BasePanel.extend({
              */
             for(let i=0; i<this.$children.length; i++) {
                 let currentLine = this.$children[i];
-                if(currentLine.line.currentTrans.content != currentLine.getEl().textContent){
-                    currentLine.line.currentTrans.content = currentLine.getEl().textContent;
+                let content = currentLine.getEl().textContent;
+                if(currentLine.line.currentTrans.content != content){
+                    currentLine.line.currentTrans.content = content;
                     if(currentLine.line.currentTrans.pk) {
                         this.addToUpdatedLines(currentLine.line.currentTrans);
                     } else {
diff --git a/app/apps/core/static/js/edit/main.js b/app/apps/core/static/js/edit/main.js
index 9fdc0c41263aeb3d700278c3dd5239addc23573c..441e63c1f56a441a79d9ff987e1e754354e3fac0 100644
--- a/app/apps/core/static/js/edit/main.js
+++ b/app/apps/core/static/js/edit/main.js
@@ -128,9 +128,10 @@ var partVM = new Vue({
             this.part.bulkUpdateLineTranscriptions(lines, cb);
         }.bind(this));
 
-        this.$on('line:move', function(movedLines, cb) {
+        this.$on('move:line', function(movedLines, cb) {
             this.part.move(movedLines, cb);
         }.bind(this));
+
         document.addEventListener('keydown', function(event) {
             if (this.blockShortcuts) return;
             if (event.keyCode == 33 ||  // page up
diff --git a/app/apps/core/static/js/edit/store/part.js b/app/apps/core/static/js/edit/store/part.js
index 6b112e437b131d0e5cc7955efdeb598b86edeb29..f87f83bf401567c0996576228d2a98e95e35d2e8 100644
--- a/app/apps/core/static/js/edit/store/part.js
+++ b/app/apps/core/static/js/edit/store/part.js
@@ -453,13 +453,23 @@ const partStore = {
                 console.log('couldnt update line', error)
             });
     },
-    move(movedLines,callback){
+    move(movedLines, callback){
         let uri = this.getApiPart()+ 'lines/move/';
         this.push(uri,{"lines": movedLines},method="post")
             .then((response) =>response.json())
             .then(function (data) {
+                for (let i=0; i<data.length; i++) {
+                    let lineData = data[i];
+                    let line = this.lines.find(function(l) {
+                        return l.pk==lineData.pk;
+                    });
+                    if (line) {
+                        if (line.order != lineData.order) console.log('change', line.pk, line.order, lineData.order);
+                        line.order = lineData.order;
+                    }
+                }
                 callback();
-            }).catch(function(error) {
+            }.bind(this)).catch(function(error) {
                 console.log('couldnt recalculate order of line', error)
             });
     },