diff --git a/package-lock.json b/package-lock.json
index 6f350ba12..a955d32b9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -43,7 +43,8 @@
"vue-flatpickr-component": "^8.1.3",
"vue-router": "^3.1.3",
"vue2-editor": "^2.10.3",
- "vue2-transitions": "^0.3.0"
+ "vue2-transitions": "^0.3.0",
+ "vuedraggable": "^2.24.3"
},
"devDependencies": {
"@babel/core": "^7.15.5",
@@ -23415,6 +23416,11 @@
"node": ">=0.10.0"
}
},
+ "node_modules/sortablejs": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
+ "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
+ },
"node_modules/source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -25861,6 +25867,14 @@
"resolved": "https://registry.npmjs.org/vue2-transitions/-/vue2-transitions-0.3.0.tgz",
"integrity": "sha512-m1ad8K8kufqiEhj5gXHkkqOioI5sW0FaMbRiO0Tv2WFfGbO2eIKrfkFiO3HPQtMJboimaLCN4p/zL81clLbG4w=="
},
+ "node_modules/vuedraggable": {
+ "version": "2.24.3",
+ "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
+ "integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==",
+ "dependencies": {
+ "sortablejs": "1.10.2"
+ }
+ },
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@@ -46042,6 +46056,11 @@
"is-plain-obj": "^1.0.0"
}
},
+ "sortablejs": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
+ "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
+ },
"source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -47994,6 +48013,14 @@
"resolved": "https://registry.npmjs.org/vue2-transitions/-/vue2-transitions-0.3.0.tgz",
"integrity": "sha512-m1ad8K8kufqiEhj5gXHkkqOioI5sW0FaMbRiO0Tv2WFfGbO2eIKrfkFiO3HPQtMJboimaLCN4p/zL81clLbG4w=="
},
+ "vuedraggable": {
+ "version": "2.24.3",
+ "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
+ "integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==",
+ "requires": {
+ "sortablejs": "1.10.2"
+ }
+ },
"watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
diff --git a/package.json b/package.json
index 72877b2a9..7bd769dc1 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,8 @@
"vue-flatpickr-component": "^8.1.3",
"vue-router": "^3.1.3",
"vue2-editor": "^2.10.3",
- "vue2-transitions": "^0.3.0"
+ "vue2-transitions": "^0.3.0",
+ "vuedraggable": "^2.24.3"
},
"devDependencies": {
"@babel/core": "^7.15.5",
diff --git a/resources/assets/js/views/common/documents.js b/resources/assets/js/views/common/documents.js
index 3b68c573d..f98d2045f 100644
--- a/resources/assets/js/views/common/documents.js
+++ b/resources/assets/js/views/common/documents.js
@@ -14,6 +14,7 @@ import Global from './../../mixins/global';
import Form from './../../plugins/form';
import BulkAction from './../../plugins/bulk-action';
+import draggable from 'vuedraggable';
// plugin setup
Vue.use(DashboardPlugin);
@@ -25,6 +26,10 @@ const app = new Vue({
Global
],
+ components: {
+ draggable
+ },
+
data: function () {
return {
form: new Form('document'),
@@ -77,6 +82,7 @@ const app = new Vue({
],
email_template: false,
send_to: false,
+ drag: false
}
},
@@ -111,6 +117,16 @@ const app = new Vue({
},
methods: {
+ onEnd() {
+ this.dragging = false;
+ },
+
+ onUpdate(event) {
+ let fromIndex = this.form.items.indexOf(this.form.items[event.oldIndex]);
+ let element = this.form.items.splice(fromIndex, 1)[0];
+ this.form.items.splice(event.newIndex, 0, element);
+ },
+
onRefFocus(ref) {
let index = this.form.items.length - 1;
diff --git a/resources/views/components/documents/form/line-item.blade.php b/resources/views/components/documents/form/line-item.blade.php
index 2180995ab..1c57e1b9b 100644
--- a/resources/views/components/documents/form/line-item.blade.php
+++ b/resources/views/components/documents/form/line-item.blade.php
@@ -1,427 +1,430 @@
-
- @stack('name_td_start')
+
+
+ @stack('name_td_start')
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
- @stack('move_td_start')
-
- list
- |
- @stack('move_td_end')
-
- @stack('items_td_start')
-
- @if (! $hideItems || (! $hideItemName && ! $hideItemDescription))
- @stack('name_td_start')
-
-
- @if (! $hideItemName)
-
-
-
-
-
- @stack('name_input_start')
-
-
-
-
-
- @stack('name_input_end')
-
- @endif
+ |
+
+ @stack('move_td_start')
+
+
+ list
+
|
+ @stack('move_td_end')
- @stack('name_td_end')
+ @stack('items_td_start')
- @stack('description_td_start')
+ @if (! $hideItems || (! $hideItemName && ! $hideItemDescription))
+ @stack('name_td_start')
+
+ @if (! $hideItemName)
+
+
+
- |
- @if (! $hideItemDescription)
-
- @endif
- |
+
+ @stack('name_input_start')
- @stack('description_td_end')
- @endif
+
- @stack('items_td_end')
+
- @stack('quantity_td_start')
-
-
- @if (! $hideItemQuantity)
-
- @stack('quantity_input_start')
-
-
-
-
-
-
- @stack('quantity_input_end')
-
- @endif
- |
-
- @stack('quantity_td_end')
-
- @stack('price_td_start')
-
-
- @if (! $hideItemPrice)
-
- @stack('price_input_start')
-
-
-
- @stack('price_input_end')
-
- @endif
- |
-
- @stack('price_td_end')
-
- @stack('total_td_start')
-
-
- @if (! $hideItemAmount)
-
-
-
- @endif
- |
-
- @stack('total_td_end')
-
- @stack('delete_td_start')
-
-
-
- |
-
- @stack('delete_td_end')
-
-
-
-
- @stack('item_custom_fields')
- |
-
-
-
-
- @if (! $hideDiscount && in_array(setting('localisation.discount_location'), ['item', 'both']))
-
-
-
- {{ trans('general.title.add', ['type' => trans('invoices.discount')]) }}
-
-
+ @stack('name_input_end')
@endif
+ |
-
-
-
- {{ trans('general.title.add', ['type' => trans_choice('general.taxes', 1)]) }}
-
-
-
-
-
+ @stack('name_td_end')
-
- @stack('discount_input_start')
+ @stack('description_td_start')
-
-
-
-
+
+ @if (! $hideItemDescription)
+
+ @endif
+ |
-
-
+ @stack('description_td_end')
+ @endif
-
+ @if (! $hideItemQuantity)
+
+ @stack('quantity_input_start')
+
+
+ v-if="form.errors.has('items.' + index + '.quantity')"
+ v-html="form.errors.get('items.' + index + '.quantity')">
+
+ @stack('quantity_input_end')
-
+ @endif
+
- @stack('discount_input_end')
+ @stack('quantity_td_end')
+
+ @stack('price_td_start')
+
+
+ @if (! $hideItemPrice)
+
+ @stack('price_input_start')
-
-
+
+ @stack('price_input_end')
+ @endif
+ |
-
-
-
-
-
+ @stack('price_td_end')
-
- {{ trans_choice('general.taxes', 1) }}
+ @stack('total_td_start')
-
- @stack('taxes_input_start')
-
-
-
- @stack('taxes_input_end')
-
-
-
-
+
+ @if (! $hideItemAmount)
+
+ @endif
+ |
-
-
+ @stack('total_td_end')
+
+ @stack('delete_td_start')
+
+
+
+ |
+
+ @stack('delete_td_end')
+
+
+
+
+ @stack('item_custom_fields')
+ |
+
+
+
+
+ @if (! $hideDiscount && in_array(setting('localisation.discount_location'), ['item', 'both']))
+
+
+
+ {{ trans('general.title.add', ['type' => trans('invoices.discount')]) }}
+
+
+
+ @endif
+
+
+
+
+ {{ trans('general.title.add', ['type' => trans_choice('general.taxes', 1)]) }}
+
+
+
-
-
- {{ trans_choice('general.taxes', 1) }}
+
+ @stack('discount_input_start')
-
- @stack('taxes_input_start')
+
+
+
+
-
+
+
- @stack('taxes_input_end')
-
+
-
-
-
-
+ @stack('discount_input_end')
+
+
-
- |
-
-
-
- |
- @stack('name_td_end')
-
+
+ {{ trans_choice('general.taxes', 1) }}
+
+
+ @stack('taxes_input_start')
+
+
+
+ @stack('taxes_input_end')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ trans_choice('general.taxes', 1) }}
+
+
+ @stack('taxes_input_start')
+
+
+
+ @stack('taxes_input_end')
+
+
+
+
+
+
+
+
+ |
+
+ @stack('name_td_end')
+
+