1 namespace('kivi.Part', function(ns) {
4 ns.open_history_popup = function() {
5 var id = $("#part_id").val();
7 url: 'controller.pl?action=Part/history&part.id=' + id,
8 dialog: { title: kivi.t8('History') },
12 ns.save = function() {
13 var data = $('#ic').serializeArray();
14 data.push({ name: 'action', value: 'Part/save' });
16 $.post("controller.pl", data, kivi.eval_json_result);
19 ns.use_as_new = function() {
20 var oldid = $("#part_id").val();
21 $('#ic').attr('action', 'controller.pl?action=Part/use_as_new&old_id=' + oldid);
25 ns.print_from_showdetail = function(part_id) {
26 var data = $('#print_options_form').serializeArray();
27 data.push({ name: 'action', value: 'Part/print_label' });
28 data.push({ name: 'part.id', value: part_id });
29 $.download("controller.pl", data);
32 ns.delete = function() {
33 var data = $('#ic').serializeArray();
34 data.push({ name: 'action', value: 'Part/delete' });
36 $.post("controller.pl", data, kivi.eval_json_result);
39 ns.reformat_number = function(event) {
40 $(event.target).val(kivi.format_amount(kivi.parse_amount($(event.target).val()), -2));
43 ns.set_tab_active_by_index = function (index) {
44 $("#ic_tabs").tabs({active: index});
47 ns.set_tab_active_by_name= function (name) {
48 var index = $('#ic_tabs a[href=#' + name + ']').parent().index();
49 ns.set_tab_active_by_index(index);
52 ns.reorder_items = function(order_by) {
53 var dir = $('#' + order_by + '_header_id a img').attr("data-sort-dir");
54 var part_type = $("#part_part_type").val();
57 if (part_type === 'assortment') {
58 $('#assortment thead a img').remove();
59 data = $('#assortment :input').serializeArray();
60 } else if ( part_type === 'assembly') {
61 $('#assembly thead a img').remove();
62 data = $('#assembly :input').serializeArray();
71 src = "image/down.png";
74 $('#' + order_by + '_header_id a').append('<img border=0 data-sort-dir=' + dir + ' src=' + src + ' alt="' + kivi.t8('sort items') + '">');
77 { name: 'action', value: 'Part/reorder_items' },
78 { name: 'order_by', value: order_by },
79 { name: 'part_type', value: part_type },
80 { name: 'sort_dir', value: dir }
83 $.post("controller.pl", data, kivi.eval_json_result);
86 ns.assortment_recalc = function() {
87 var data = $('#assortment :input').serializeArray();
89 { name: 'action', value: 'Part/update_item_totals' },
90 { name: 'part_type', value: 'assortment' }
93 $.post("controller.pl", data, kivi.eval_json_result);
96 ns.assembly_recalc = function() {
97 var data = $('#assembly :input').serializeArray();
99 { name: 'action', value: 'Part/update_item_totals' },
100 { name: 'part_type', value: 'assembly' }
103 $.post("controller.pl", data, kivi.eval_json_result);
106 ns.set_assortment_sellprice = function() {
107 $("#part_sellprice_as_number").val($("#items_sellprice_sum").html());
108 // ns.set_tab_active_by_name('basic_data');
109 // $("#part_sellprice_as_number").focus();
112 ns.set_assortment_lsg_sellprice = function() {
113 $("#items_lsg_sellprice_sum_basic").closest('td').find('input').val($("#items_lsg_sellprice_sum").html());
116 ns.set_assortment_douglas_sellprice = function() {
117 $("#items_douglas_sellprice_sum_basic").closest('td').find('input').val($("#items_douglas_sellprice_sum").html());
120 ns.set_assortment_lastcost = function() {
121 $("#part_lastcost_as_number").val($("#items_lastcost_sum").html());
122 // ns.set_tab_active_by_name('basic_data');
123 // $("#part_lastcost_as_number").focus();
126 ns.set_assembly_sellprice = function() {
127 $("#part_sellprice_as_number").val($("#items_sellprice_sum").html());
128 // ns.set_tab_active_by_name('basic_data');
129 // $("#part_sellprice_as_number").focus();
132 ns.renumber_positions = function() {
133 var part_type = $("#part_part_type").val();
135 if (part_type === 'assortment') {
136 rows = $('.assortment_item_row [name="position"]');
137 } else if ( part_type === 'assembly') {
138 rows = $('.assembly_item_row [name="position"]');
140 $(rows).each(function(idx, elt) {
142 var row = $(elt).closest('tr');
143 if ( idx % 2 === 0 ) {
144 if ( row.hasClass('listrow1') ) {
145 row.removeClass('listrow1');
146 row.addClass('listrow0');
149 if ( row.hasClass('listrow0') ) {
150 row.removeClass('listrow0');
151 row.addClass('listrow1');
157 ns.delete_item_row = function(clicked) {
158 var row = $(clicked).closest('tr');
160 var part_type = $("#part_part_type").val();
161 ns.renumber_positions();
162 if (part_type === 'assortment') {
163 ns.assortment_recalc();
164 } else if ( part_type === 'assembly') {
165 ns.assembly_recalc();
169 ns.add_assortment_item = function() {
170 if ($('#assortment_picker').val() === '') return;
172 $('#row_table_id thead a img').remove();
174 var data = $('#assortment :input').serializeArray();
176 { name: 'action', value: 'Part/add_assortment_item' },
177 { name: 'part.id', value: $('#part_id').val() },
178 { name: 'part.part_type', value: 'assortment' }
180 $('#assortment_picker').data('part_picker').clear();
182 $.post("controller.pl", data, kivi.eval_json_result);
185 ns.add_assembly_item = function() {
186 if ($('#assembly_picker').val() === '') return;
188 var data = $('#assembly :input').serializeArray();
190 { name: 'action', value: 'Part/add_assembly_item' },
191 { name: 'part.id', value: $("#part_id").val() },
192 { name: 'part.part_type', value: 'assembly' }
194 $('#assembly_picker').data('part_picker').clear();
196 $.post("controller.pl", data, kivi.eval_json_result);
199 ns.set_multi_assembly_items = function(data) {
200 data.push({ name: 'part.id', value: $('#part_id').val() });
201 data.push({ name: 'part.part_type', value: $('#part_part_type').val() });
202 $.post("controller.pl?action=Part/add_multi_assembly_items", data, kivi.eval_json_result);
205 ns.set_multi_assortment_items = function(data) {
206 data.push({ name: 'part.id', value: $('#part_id').val() });
207 data.push({ name: 'part.part_type', value: $('#part_part_type').val() });
208 $.post("controller.pl?action=Part/add_multi_assortment_items", data, kivi.eval_json_result);
211 ns.close_picker_dialogs = function() {
212 $('.part_autocomplete').each(function(_, e) {
213 var picker = $(e).data('part_picker');
214 if (picker && picker.dialog) picker.close_dialog();
218 ns.redisplay_items = function(data) {
220 var part_type = $("#part_part_type").val();
221 if (part_type === 'assortment') {
222 old_rows = $('.assortment_item_row').detach();
223 } else if ( part_type === 'assembly') {
224 old_rows = $('.assembly_item_row').detach();
227 $(data).each(function(idx, elt) {
228 new_rows.push(old_rows[elt.old_pos - 1]);
230 if (part_type === 'assortment') {
231 $(new_rows).appendTo($('#assortment_items'));
232 } else if ( part_type === 'assembly') {
233 $(new_rows).appendTo($('#assembly_items'));
235 ns.renumber_positions();
238 ns.focus_last_assortment_input = function () {
239 $("#assortment_items tr:last").find('input[type=text]').filter(':visible:first').focus();
242 ns.focus_last_assembly_input = function () {
243 $("#assembly_rows tr:last").find('input[type=text]').filter(':visible:first').focus();
247 ns.makemodel_renumber_positions = function() {
248 $('.makemodel_row [name="position"]').each(function(idx, elt) {
253 ns.delete_makemodel_row = function(clicked) {
254 var row = $(clicked).closest('tr');
257 ns.makemodel_renumber_positions();
260 ns.add_makemodel_row = function() {
261 if ($('#add_makemodel').val() === '') return;
263 var data = $('#makemodel_table :input').serializeArray();
264 data.push({ name: 'action', value: 'Part/add_makemodel_row' });
265 $('#add_makemodel').data('customer_vendor_picker').clear();
267 $.post("controller.pl", data, kivi.eval_json_result);
270 ns.focus_last_makemodel_input = function () {
271 $("#makemodel_rows tr:last").find('input[type=text]').filter(':visible:first').focus();
275 ns.businessmodel_renumber_positions = function() {
276 $('.businessmodel_row [name="position"]').each(function(idx, elt) {
281 ns.delete_businessmodel_row = function(clicked) {
282 var row = $(clicked).closest('tr');
285 ns.businessmodel_renumber_positions();
288 ns.add_businessmodel_row = function() {
289 if ($('#add_businessmodel').val() === '') return;
291 var data = $('#businessmodel_table :input').serializeArray();
292 data.push({ name: 'action', value: 'Part/add_businessmodel_row' });
294 $.post("controller.pl", data, kivi.eval_json_result);
297 ns.focus_last_businessmodel_input = function () {
298 $("#businessmodel_rows tr:last").find('input[type=text]').filter(':visible:first').focus();
302 ns.customerprice_renumber_positions = function() {
303 $('.customerprice_row [name="position"]').each(function(idx, elt) {
308 ns.delete_customerprice_row = function(clicked) {
309 var row = $(clicked).closest('tr');
312 ns.customerprice_renumber_positions();
315 ns.add_customerprice_row = function() {
316 if ($('#add_customerprice').val() === '') return;
318 var data = $('#customerprice_table :input').serializeArray();
319 data.push({ name: 'action', value: 'Part/add_customerprice_row' });
320 $('#add_customerprice').data('customer_vendor_picker').clear();
322 $.post("controller.pl", data, kivi.eval_json_result);
325 ns.focus_last_customerprice_input = function () {
326 $("#customerprice_rows tr:last").find('input[type=text]').filter(':visible:first').focus();
330 ns.reload_bin_selection = function() {
331 $.post("controller.pl", { action: 'Part/warehouse_changed', warehouse_id: function(){ return $('#part_warehouse_id').val(); } }, kivi.eval_json_result);
349 ns.Picker = function($real, options) {
351 this.o = $.extend(true, {
355 commit_none: function(){ },
356 commit_one: function(){ $('#update_button').click(); },
357 commit_many: function(){ self.open_dialog(); }
360 }, $real.data('part-picker-data'), options);
362 this.real_id = $real.attr('id');
363 this.last_real = $real.val();
364 this.$dummy = $($real.siblings()[0]);
365 this.autocomplete_open = false;
366 this.menu_above = false;
367 this.state = this.STATES.PICKED;
368 this.last_dummy = this.$dummy.val();
369 this.timer = undefined;
370 this.dialog = undefined;
371 this.multiple_default = this.o.multiple;
376 ns.Picker.prototype = {
378 PICKED: 'partpicker-picked',
379 UNDEFINED: 'partpicker-undefined',
381 ajax_data: function(term) {
383 current: this.$real.val(),
386 if (this.o.part_type)
387 data['filter.part_type'] = this.o.part_type.split(',');
390 if (this.o.status == 'active') data['filter.obsolete'] = 0;
391 if (this.o.status == 'obsolete') data['filter.obsolete'] = 1;
393 data['filter.obsolete'] = 0;
395 if (this.o.classification_id)
396 data['filter.classification_id:any'] = this.o.classification_id.replaceAll(',', ' ');
399 data['filter.unit'] = this.o.unit.split(',');
401 if (this.o.convertible_unit)
402 data['filter.unit_obj.convertible_to'] = this.o.convertible_unit;
404 if (this.o.order_locked)
405 data['filter.order_locked'] = 1;
406 if (this.o.not_order_locked)
407 data['filter.order_locked'] = 0;
409 var filter_name = 'all';
410 if (this.o.with_makemodel) {
411 filter_name = 'all_with_makemodel';
413 if (this.o.with_customer_partnumber) {
414 filter_name = 'all_with_customer_partnumber';
416 data['filter.' + filter_name + ':substr:multi::ilike'] = term;
420 set_item: function(item) {
423 this.$real.val(item.id);
424 // autocomplete ui has name, use the value for ajax items, which contains displayable_name
425 this.$dummy.val(item.name ? item.name : item.value);
430 this.state = this.STATES.PICKED;
431 this.last_real = this.$real.val();
432 this.last_dummy = this.$dummy.val();
433 this.$real.trigger('change');
435 if (this.o.fat_set_item && item.id) {
437 url: 'controller.pl?action=Part/show.json',
438 data: { 'part.id': item.id },
439 success: function(rsp) {
440 self.$real.trigger('set_item:PartPicker', rsp);
444 this.$real.trigger('set_item:PartPicker', item);
446 this.annotate_state();
448 set_multi_items: function(data) {
449 this.run_action(this.o.action.set_multi_items, [ data ]);
451 make_defined_state: function() {
452 if (this.state == this.STATES.PICKED) {
453 this.annotate_state();
455 } else if (this.state == this.STATES.UNDEFINED && this.$dummy.val() === '') {
458 this.set_item({ id: this.last_real, name: this.last_dummy });
460 this.annotate_state();
462 annotate_state: function() {
463 if (this.state == this.STATES.PICKED)
464 this.$dummy.removeClass(this.STATES.UNDEFINED).addClass(this.STATES.PICKED);
465 else if (this.state == this.STATES.UNDEFINED && this.$dummy.val() === '')
466 this.$dummy.removeClass(this.STATES.UNDEFINED).addClass(this.STATES.PICKED);
468 this.$dummy.addClass(this.STATES.UNDEFINED).removeClass(this.STATES.PICKED);
471 handle_changed_text: function(callbacks) {
474 url: 'controller.pl?action=Part/ajax_autocomplete',
476 data: $.extend( self.ajax_data(self.$dummy.val()), { prefer_exact: 1 } ),
477 success: function (data) {
478 if (data.length == 1) {
479 self.set_item(data[0]);
480 if (callbacks && callbacks.match_one) self.run_action(callbacks.match_one, [ data[0] ]);
481 } else if (data.length > 1) {
482 self.state = self.STATES.UNDEFINED;
483 if (callbacks && callbacks.match_many) self.run_action(callbacks.match_many, [ data ]);
485 self.state = self.STATES.UNDEFINED;
486 if (callbacks && callbacks.match_none) self.run_action(callbacks.match_none, [ self, self.$dummy.val() ]);
488 self.annotate_state();
492 /* In case users are impatient and want to skip ahead:
493 * Capture <enter> key events and check if it's a unique hit.
494 * If it is, go ahead and assume it was selected. If it wasn't don't do
495 * anything so that autocompletion kicks in. For <tab> don't prevent
496 * propagation. It would be nice to catch it, but javascript is too stupid
497 * to fire a tab event later on, so we'd have to reimplement the "find
498 * next active element in tabindex order and focus it".
501 * event.which does not contain tab events in keypressed in firefox but will report 0
502 * chrome does not fire keypressed at all on tab or escape
504 handle_keydown: function(event) {
506 if (event.which == KEY.ENTER || event.which == KEY.TAB) {
507 self.$dummy.autocomplete('close');
509 // if string is empty assume they want to delete
510 if (self.$dummy.val() === '') {
513 } else if (self.state == self.STATES.PICKED) {
514 if (self.o.action.commit_one) {
515 self.run_action(self.o.action.commit_one);
519 if (event.which == KEY.TAB) {
520 event.preventDefault();
521 self.handle_changed_text();
523 if (event.which == KEY.ENTER) {
524 event.preventDefault();
525 self.handle_changed_text({
526 match_none: self.o.action.commit_none,
527 match_one: self.o.action.commit_one,
528 match_many: self.o.action.commit_many
532 } else if (event.which == KEY.DOWN && !self.autocomplete_open) {
533 var old_options = self.$dummy.autocomplete('option');
534 self.$dummy.autocomplete('option', 'minLength', 0);
535 self.$dummy.autocomplete('search', self.$dummy.val());
536 self.$dummy.autocomplete('option', 'minLength', old_options.minLength);
537 } else if ((event.which != KEY.SHIFT) && (event.which != KEY.CTRL) && (event.which != KEY.ALT)) {
538 self.state = self.STATES.UNDEFINED;
541 open_dialog: function() {
542 if (this.o.multiple) {
543 this.o.multiple = this.multiple_default;
544 this.dialog = new ns.PickerMultiPopup(this);
546 this.dialog = new ns.PickerPopup(this);
549 close_dialog: function() {
550 this.dialog.close_dialog();
551 this.dialog = undefined;
555 this.$dummy.autocomplete({
556 source: function(req, rsp) {
557 $.ajax($.extend(self.o, {
558 url: 'controller.pl?action=Part/ajax_autocomplete',
560 data: self.ajax_data(req.term),
561 success: function (data){ rsp(data); }
564 select: function(event, ui) {
565 self.set_item(ui.item);
566 if (self.o.action.commit_one) {
567 self.run_action(self.o.action.commit_one);
570 search: function(event) {
571 if ((event.which == KEY.SHIFT) || (event.which == KEY.CTRL) || (event.which == KEY.ALT))
572 event.preventDefault();
575 const $widget = self.$dummy.autocomplete('widget');
576 const input_bound = self.$dummy[0].getBoundingClientRect();
578 // safe inner viewport height without scrollbars
579 const viewport_height = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
582 // if the widget was already open, keep position
583 // if fits above but not below: render above
584 // else: render below
585 if (!self.autocomplete_open) {
586 if (input_bound.top > $widget.height() && input_bound.bottom + $widget.height() > viewport_height) {
587 $widget.position({ my: "left bottom", at: "left top", of: self.$dummy });
588 self.menu_above = true;
590 $widget.position({ my: "left top", at: "left bottom", of: self.$dummy });
591 self.menu_above = false;
594 if (self.menu_above) {
595 $widget.position({ my: "left bottom", at: "left top", of: self.$dummy });
597 $widget.position({ my: "left top", at: "left bottom", of: self.$dummy });
601 self.autocomplete_open = true;
604 self.autocomplete_open = false;
607 this.$dummy.keydown(function(event){ self.handle_keydown(event); });
608 this.$dummy.on('paste', function(){
609 setTimeout(function() {
610 self.handle_changed_text();
613 this.$dummy.blur(function(){
614 window.clearTimeout(self.timer);
615 self.timer = window.setTimeout(function() { self.annotate_state(); }, 100);
618 var popup_button = $('<span>').addClass('ppp_popup_button');
619 this.$dummy.after(popup_button);
620 popup_button.click(function() { self.open_dialog(); });
622 run_action: function(code, args) {
623 if (typeof code === 'function')
624 code.apply(this, args);
626 kivi.run(code, args);
632 ns.Picker.prototype.STATES = {
633 PICKED: ns.Picker.prototype.CLASSES.PICKED,
634 UNDEFINED: ns.Picker.prototype.CLASSES.UNDEFINED
637 ns.PickerPopup = function(pp) {
638 this.timer = undefined;
643 ns.PickerPopup.prototype = {
644 open_dialog: function() {
647 url: 'controller.pl?action=Part/part_picker_search',
649 real_id: self.pp.real_id,
650 }, self.pp.ajax_data(this.pp.$dummy.val())),
651 id: 'part_selection',
653 title: kivi.t8('Part picker'),
657 load: function() { self.init_search(); }
659 window.clearTimeout(this.timer);
662 init_search: function() {
664 $('#part_picker_filter').keypress(function(e) { self.result_timer(e); }).focus();
665 $('#no_paginate').change(function() { self.update_results(); });
666 this.update_results();
668 update_results: function() {
671 url: 'controller.pl?action=Part/part_picker_result',
673 no_paginate: $('#no_paginate').prop('checked') ? 1 : 0,
674 }, self.pp.ajax_data(function(){
675 var val = $('#part_picker_filter').val();
676 return val === undefined ? '' : val;
678 success: function(data){
679 $('#part_picker_result').html(data);
684 init_results: function() {
686 $('div.part_picker_part').each(function(){
687 $(this).click(function(){
689 id: $(this).children('input.part_picker_id').val(),
690 name: $(this).children('input.part_picker_description').val(),
691 classification_id: $(this).children('input.part_picker_classification_id').val(),
692 ean: $(this).children('input.part_picker_ean').val(),
693 unit: $(this).children('input.part_picker_unit').val(),
694 partnumber: $(this).children('input.part_picker_partnumber').val(),
695 description: $(this).children('input.part_picker_description').val(),
698 self.pp.$dummy.focus();
702 $('#part_selection').keydown(function(e){
703 if (e.which == KEY.ESCAPE) {
705 self.pp.$dummy.focus();
709 result_timer: function(event) {
711 if (!$('no_paginate').prop('checked')) {
712 if (event.keyCode == KEY.PAGE_UP) {
713 $('#part_picker_result a.paginate-prev').click();
716 if (event.keyCode == KEY.PAGE_DOWN) {
717 $('#part_picker_result a.paginate-next').click();
721 window.clearTimeout(this.timer);
722 if (event.which == KEY.ENTER) {
723 self.update_results();
725 this.timer = window.setTimeout(function() { self.update_results(); }, 100);
728 close_dialog: function() {
729 $('#part_selection').dialog('close');
733 ns.PickerMultiPopup = function(pp) {
735 this.callback = 'Part/add_multi_' + this.pp.o.part_type + '_items';
739 ns.PickerMultiPopup.prototype = {
740 open_dialog: function() {
742 $('#row_table_id thead a img').remove();
745 url: 'controller.pl?action=Part/show_multi_items_dialog',
747 real_id: self.pp.real_id,
748 show_pos_input: self.pp.o.multiple_pos_input,
749 }, self.pp.ajax_data(this.pp.$dummy.val())),
750 id: 'jq_multi_items_dialog',
752 title: kivi.t8('Add multiple items'),
762 init_search: function() {
764 $('#multi_items_filter_table input, #multi_items_filter_table select').keydown(function(event) {
765 if(event.which == KEY.ENTER) {
766 event.preventDefault();
767 self.update_results();
772 $('#multi_items_filter_all_substr_multi_ilike').focus();
773 $('#multi_items_filter_button').click(function(){ self.update_results(); });
774 $('#multi_items_filter_reset').click(function(){ $("#multi_items_form").resetForm(); });
775 $('#continue_button').click(function(){ self.add_multi_items(); });
777 update_results: function() {
779 var data = $('#multi_items_form').serializeArray();
780 data.push({ name: 'type', value: self.pp.type });
781 data.push({ name: 'limit', value: self.pp.o.multiple_limit });
782 var ppdata = self.pp.ajax_data(function(){
783 var val = $('#multi_items_filter').val();
784 return val === undefined ? '' : val;
786 $.each(Object.keys(ppdata), function() {data.push({ name: 'multi_items.' + this, value: ppdata[this]});});
788 url: 'controller.pl?action=Part/multi_items_update_result',
791 success: function(data){
792 $('#multi_items_result').html(data);
794 self.enable_continue();
798 set_qty_to_one: function(clicked) {
799 if ($(clicked).val() === '') {
800 $(clicked).val(kivi.format_amount(1.00, -2));
804 init_results: function() {
806 $('#multi_items_all_qty').change(function(event){
807 $('.multi_items_qty').val($(event.target).val());
809 $('.multi_items_qty').click(function(){ self.set_qty_to_one(this); });
811 result_timer: function() {
813 close_dialog: function() {
814 $('#jq_multi_items_dialog').dialog('close');
816 disable_continue: function() {
817 $('#multi_items_result input, #multi_items_position').off("keydown");
818 $('#continue_button').prop('disabled', true);
820 enable_continue: function() {
822 $('#multi_items_result input, #multi_items_position').keydown(function(event) {
823 if(event.keyCode == KEY.ENTER) {
824 event.preventDefault();
825 self.add_multi_items();
829 $('#continue_button').prop('disabled', false);
831 add_multi_items: function() {
833 var n_rows = $('.multi_items_qty').length;
834 if ( n_rows === 0) { return; }
837 n_rows = $('.multi_items_qty').filter(function() {
838 return $(this).val().length > 0;
840 if (n_rows === 0) { return; }
842 this.disable_continue();
844 var data = $('#multi_items_form').serializeArray();
845 this.pp.set_multi_items(data);
849 ns.reinit_widgets = function() {
850 kivi.run_once_for('input.part_autocomplete', 'part_picker', function(elt) {
851 if (!$(elt).data('part_picker'))
852 $(elt).data('part_picker', new kivi.Part.Picker($(elt)));
855 kivi.run_once_for('#customerprice_rows', 'customerprice_row_sort_renumber', function(elt) {
856 $(elt).on('sortstop', kivi.Part.customerprice_renumber_positions);
859 kivi.run_once_for('#makemodel_rows', 'makemodel_row_sort_renumber', function(elt) {
860 $(elt).on('sortstop', kivi.Part.makemodel_renumber_positions);
863 kivi.run_once_for('#businessmodel_rows', 'businessmodel_row_sort_renumber', function(elt) {
864 $(elt).on('sortstop', kivi.Part.businessmodel_renumber_positions);
868 ns.init = function() {
872 ns.add_to_basket = function() {
873 var data = $('#ic').serializeArray();
874 data.push({ name: 'action', value: 'Part/add_to_basket' });
876 $.post("controller.pl", data, kivi.eval_json_result);
880 $('#ic').on('focusout', '.reformat_number', function(event) {
881 ns.reformat_number(event);
884 $('#part_warehouse_id').change(kivi.Part.reload_bin_selection);