Browse Source

Fixes #5027 - OTRS import: When importing OTRS fields where the attribute key contains two colons „::“ in it, these attributes are removed in the Zammad object.

Dominik Klein 1 year ago
parent
commit
b10530b0ba

+ 6 - 2
lib/import/otrs/dynamic_field/dropdown.rb

@@ -4,13 +4,17 @@ module Import
   module OTRS
     class DynamicField
       class Dropdown < Import::OTRS::DynamicField
+        include Import::OTRS::DynamicField::Mixin::HasOptions
+
         def init_callback(dynamic_field)
+          tree_select = dynamic_field['Config']['TreeView'] == '1'
+
           @attribute_config.merge!(
-            data_type:   'select',
+            data_type:   tree_select ? 'tree_select' : 'select',
             data_option: {
               default:    '',
               multiple:   false,
-              options:    dynamic_field['Config']['PossibleValues'],
+              options:    option_list(dynamic_field['Config']['PossibleValues'], tree_select),
               nulloption: dynamic_field['Config']['PossibleNone'] == '1',
               null:       true,
               translate:  dynamic_field['Config']['TranslatableValues'] == '1',

+ 46 - 0
lib/import/otrs/dynamic_field/mixin/has_options.rb

@@ -0,0 +1,46 @@
+# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+module Import::OTRS::DynamicField::Mixin::HasOptions
+  extend ActiveSupport::Concern
+
+  # Method to build nested structure
+  def build_options_tree_structure(data)
+    # Transform data into a hierarchical structure
+    hierarchy = {}
+    data.each do |path, name|
+      current_level = hierarchy
+      segments = path.split('::')
+
+      segments.each_with_index do |segment, index|
+        current_level[segment] ||= {}
+
+        # Assign name to the last segment
+        if index == segments.size - 1
+          current_level[segment][:name] = name
+        end
+        current_level = current_level[segment]
+      end
+    end
+
+    # Recursively build the array structure from the hierarchy.
+    build_options_array_structure(hierarchy)
+  end
+
+  def build_options_array_structure(hierarchy)
+    hierarchy.filter_map do |key, value|
+      if value.is_a?(Hash) && value.key?(:name)
+        # Using except to exclude the :name key
+        children = value.except(:name)
+        node = { 'value' => key, 'name' => value[:name] }
+        node['children'] = build_options_array_structure(children) if children.any?
+        node
+      end
+    end
+  end
+
+  def option_list(possible_values, tree_select)
+    return possible_values if !tree_select
+
+    build_options_tree_structure(possible_values)
+  end
+end

+ 6 - 2
lib/import/otrs/dynamic_field/multiselect.rb

@@ -4,13 +4,17 @@ module Import
   module OTRS
     class DynamicField
       class Multiselect < Import::OTRS::DynamicField
+        include Import::OTRS::DynamicField::Mixin::HasOptions
+
         def init_callback(dynamic_field)
+          tree_select = dynamic_field['Config']['TreeView'] == '1'
+
           @attribute_config.merge!(
-            data_type:   'multiselect',
+            data_type:   tree_select ? 'multi_tree_select' : 'multiselect',
             data_option: {
               default:    '',
               multiple:   true,
-              options:    dynamic_field['Config']['PossibleValues'],
+              options:    option_list(dynamic_field['Config']['PossibleValues'], tree_select),
               nulloption: dynamic_field['Config']['PossibleNone'] == '1',
               null:       true,
               translate:  dynamic_field['Config']['TranslatableValues'] == '1',

+ 35 - 0
spec/fixtures/files/import/otrs/dynamic_field/dropdown/treeselect.json

@@ -0,0 +1,35 @@
+{
+  "ID": "40",
+  "ChangeTime": "2016-05-25 11:14:06",
+  "InternalField": "0",
+  "ValidID": "1",
+  "CreateTime": "2014-08-21 14:54:15",
+  "Label": "Treeselect Example",
+  "FieldOrder": "30",
+  "Config": {
+    "TranslatableValues": "0",
+    "PossibleValues": {
+      "Level1": "Level 1",
+      "Level1::SubLevel1": "SubLevel 1",
+      "Level1::SubLevel2": "SubLevel 2",
+      "Level2": "Level 2",
+      "Level2::SubLevel1": "SubLevel 1",
+      "Level2::SubLevel2": "SubLevel 2",
+      "Support": "Support",
+      "Support::Level1": "Level 1",
+      "Support::Level2": "Level 2",
+      "Support::Level3": "Level 3",
+      "Finance": "Finance",
+      "Finance::Invoice": "Invoice",
+      "Finance::Invoice::Germany": "Germany",
+      "Finance::Invoice::Germany::Monthly": "Monthly"
+    },
+    "TreeView": "1",
+    "DefaultValue": "",
+    "Link": "",
+    "PossibleNone": "1"
+  },
+  "FieldType": "Dropdown",
+  "Name": "TreeselectExample",
+  "ObjectType": "Ticket"
+}

+ 2 - 2
spec/fixtures/files/import/otrs/dynamic_field/multiselect/default.json

@@ -4,7 +4,7 @@
   "InternalField": "0",
   "ValidID": "1",
   "CreateTime": "2014-05-29 20:11:41",
-  "Label": "Multiselec tExample",
+  "Label": "Multiselect Example",
   "FieldOrder": "4",
   "Config": {
     "TranslatableValues": "0",
@@ -21,4 +21,4 @@
   "FieldType": "Multiselect",
   "Name": "MultiselectExample",
   "ObjectType": "Ticket"
-}
+}

+ 34 - 0
spec/fixtures/files/import/otrs/dynamic_field/multiselect/multi_treeselect.json

@@ -0,0 +1,34 @@
+{
+  "ID": "4",
+  "ChangeTime": "2014-09-12 09:31:58",
+  "InternalField": "0",
+  "ValidID": "1",
+  "CreateTime": "2014-05-29 20:11:41",
+  "Label": "Multitreeselect Example",
+  "FieldOrder": "4",
+  "Config": {
+    "TranslatableValues": "0",
+    "PossibleValues": {
+      "Level1": "Level 1",
+      "Level1::SubLevel1": "SubLevel 1",
+      "Level1::SubLevel2": "SubLevel 2",
+      "Level2": "Level 2",
+      "Level2::SubLevel1": "SubLevel 1",
+      "Level2::SubLevel2": "SubLevel 2",
+      "Support": "Support",
+      "Support::Level1": "Level 1",
+      "Support::Level2": "Level 2",
+      "Support::Level3": "Level 3",
+      "Finance": "Finance",
+      "Finance::Invoice": "Invoice",
+      "Finance::Invoice::Germany": "Germany",
+      "Finance::Invoice::Germany::Monthly": "Monthly"
+    },
+    "TreeView": "1",
+    "DefaultValue": "",
+    "PossibleNone": "0"
+  },
+  "FieldType": "Multiselect",
+  "Name": "MultitreeselectExample",
+  "ObjectType": "Ticket"
+}

+ 82 - 1
spec/lib/import/otrs/dynamic_field/dropdown_spec.rb

@@ -7,7 +7,6 @@ RSpec.describe Import::OTRS::DynamicField::Dropdown do
   it_behaves_like 'Import::OTRS::DynamicField'
 
   it 'imports an OTRS Dropdown DynamicField' do
-
     zammad_structure = {
       object:        'Ticket',
       name:          'dropdown_example',
@@ -43,6 +42,88 @@ RSpec.describe Import::OTRS::DynamicField::Dropdown do
     dynamic_field_from_json('dropdown/default', zammad_structure)
   end
 
+  it 'imports an OTRS Dropdown DynamicField with tree mode' do
+    zammad_structure = {
+      object:        'Ticket',
+      name:          'treeselect_example',
+      display:       'Treeselect Example',
+      screens:       {
+        view: {
+          '-all-' => {
+            shown: true
+          }
+        }
+      },
+      active:        true,
+      editable:      true,
+      position:      '30',
+      created_by_id: 1,
+      updated_by_id: 1,
+      data_type:     'tree_select',
+      data_option:   {
+        default:    '',
+        multiple:   false,
+        options:    [
+          {
+            'value'    => 'Level1',
+            'name'     => 'Level 1',
+            'children' => [
+              { 'value' => 'SubLevel1', 'name' => 'SubLevel 1' },
+              { 'value' => 'SubLevel2', 'name' => 'SubLevel 2' },
+            ],
+          },
+          {
+            'value'    => 'Level2',
+            'name'     => 'Level 2',
+            'children' => [
+              { 'value' => 'SubLevel1', 'name' => 'SubLevel 1' },
+              { 'value' => 'SubLevel2', 'name' => 'SubLevel 2' },
+            ],
+          },
+          {
+            'value'    => 'Support',
+            'name'     => 'Support',
+            'children' => [
+              {
+                'value' => 'Level1', 'name' => 'Level 1'
+              },
+              {
+                'value' => 'Level2', 'name' => 'Level 2'
+              },
+              {
+                'value' => 'Level3', 'name' => 'Level 3'
+              }
+            ]
+          },
+          {
+            'value'    => 'Finance',
+            'name'     => 'Finance',
+            'children' => [
+              {
+                'value'    => 'Invoice',
+                'name'     => 'Invoice',
+                'children' => [
+                  {
+                    'value'    => 'Germany',
+                    'name'     => 'Germany',
+                    'children' => [
+                      { 'value' => 'Monthly', 'name' => 'Monthly' }
+                    ],
+                  },
+                ]
+              }
+            ]
+          }
+        ],
+        nulloption: true,
+        null:       true,
+        translate:  false
+      }
+    }
+
+    dynamic_field_from_json('dropdown/treeselect', zammad_structure)
+  end
+
   context 'without possible values' do
     it 'imports no field without possible value' do
       allow(ObjectManager::Attribute).to receive(:add)

+ 83 - 1
spec/lib/import/otrs/dynamic_field/multiselect_spec.rb

@@ -11,7 +11,7 @@ RSpec.describe Import::OTRS::DynamicField::Multiselect, mariadb: true do
     zammad_structure = {
       object:        'Ticket',
       name:          'multiselect_example',
-      display:       'Multiselec tExample',
+      display:       'Multiselect Example',
       screens:       {
         view: {
           '-all-' => {
@@ -43,6 +43,88 @@ RSpec.describe Import::OTRS::DynamicField::Multiselect, mariadb: true do
     dynamic_field_from_json('multiselect/default', zammad_structure)
   end
 
+  it 'imports an OTRS Multiselect DynamicField with tree mode' do
+    zammad_structure = {
+      object:        'Ticket',
+      name:          'multitreeselect_example',
+      display:       'Multitreeselect Example',
+      screens:       {
+        view: {
+          '-all-' => {
+            shown: true
+          }
+        }
+      },
+      active:        true,
+      editable:      true,
+      position:      '4',
+      created_by_id: 1,
+      updated_by_id: 1,
+      data_type:     'multi_tree_select',
+      data_option:   {
+        default:    '',
+        multiple:   true,
+        options:    [
+          {
+            'value'    => 'Level1',
+            'name'     => 'Level 1',
+            'children' => [
+              { 'value' => 'SubLevel1', 'name' => 'SubLevel 1' },
+              { 'value' => 'SubLevel2', 'name' => 'SubLevel 2' },
+            ],
+          },
+          {
+            'value'    => 'Level2',
+            'name'     => 'Level 2',
+            'children' => [
+              { 'value' => 'SubLevel1', 'name' => 'SubLevel 1' },
+              { 'value' => 'SubLevel2', 'name' => 'SubLevel 2' },
+            ],
+          },
+          {
+            'value'    => 'Support',
+            'name'     => 'Support',
+            'children' => [
+              {
+                'value' => 'Level1', 'name' => 'Level 1'
+              },
+              {
+                'value' => 'Level2', 'name' => 'Level 2'
+              },
+              {
+                'value' => 'Level3', 'name' => 'Level 3'
+              }
+            ]
+          },
+          {
+            'value'    => 'Finance',
+            'name'     => 'Finance',
+            'children' => [
+              {
+                'value'    => 'Invoice',
+                'name'     => 'Invoice',
+                'children' => [
+                  {
+                    'value'    => 'Germany',
+                    'name'     => 'Germany',
+                    'children' => [
+                      { 'value' => 'Monthly', 'name' => 'Monthly' }
+                    ],
+                  },
+                ]
+              }
+            ]
+          }
+        ],
+        nulloption: false,
+        null:       true,
+        translate:  false
+      }
+    }
+
+    dynamic_field_from_json('multiselect/multi_treeselect', zammad_structure)
+  end
+
   context 'without possible values' do
     it 'imports no field without possible value' do
       allow(ObjectManager::Attribute).to receive(:add)