diff --git a/msp/fixtures/print_format.json b/msp/fixtures/print_format.json
new file mode 100644
index 0000000..f0ab8cc
--- /dev/null
+++ b/msp/fixtures/print_format.json
@@ -0,0 +1,29 @@
+[
+ {
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "css": null,
+ "custom_format": 0,
+ "default_print_language": null,
+ "disabled": 0,
+ "doc_type": "MSP Documentation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Default",
+ "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\r\\n\\t
\\r\\n\\t\\t
\\r\\n\\t\\t\\t

\\r\\n\\t\\t
\\r\\n\\t
\\r\\n\\t
\\r\\n\\t\\t
\\r\\nIT Documentation\\r\\n\\t\\t
{{ doc.name }}
\\r\\n\\t
\\r\\n\\t
\\r\\n\\t\\t

\\r\\n\\t
\\r\\n
\\r\\n\\r\\n\\t
\\r\\n\\t\\t
\\r\\n\\t\\r\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"customer\", \"print_hide\": 0, \"label\": \"Customer\"}, {\"fieldname\": \"customer_name\", \"print_hide\": 0, \"label\": \"Customer Name\"}, {\"fieldname\": \"generation_date\", \"print_hide\": 0, \"label\": \"Generation Date\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"landscape\", \"print_hide\": 0, \"label\": \"Landscape\"}, {\"fieldname\": \"tactical_rmm_tenant_caption\", \"print_hide\": 0, \"label\": \"Tactical RMM Tenant Caption\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"Introduction
\\n{{ frappe.utils.md_to_html(doc.introduction) }}\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Systems from RMM\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"Server Systems
\\n{{ frappe.utils.md_to_html(doc.server_list) }}\\n
\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"Workstation Systems
\\n{{ frappe.utils.md_to_html(doc.workstation_list) }}\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Backups\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"backup\", \"print_hide\": 0, \"label\": \"Backup\"}, {\"fieldname\": \"aditional_data\", \"print_hide\": 0, \"label\": \"Aditional Data\"}]",
+ "html": null,
+ "line_breaks": 0,
+ "modified": "2023-06-09 07:34:10.957905",
+ "module": "MSP",
+ "name": "Standard IT Landscape Documentation",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "print_format_builder": 1,
+ "print_format_type": "Jinja",
+ "raw_commands": null,
+ "raw_printing": 0,
+ "show_section_headings": 1,
+ "standard": "No"
+ }
+]
\ No newline at end of file
diff --git a/msp/hooked_methods.py b/msp/hooked_methods.py
new file mode 100644
index 0000000..8b1c77b
--- /dev/null
+++ b/msp/hooked_methods.py
@@ -0,0 +1,27 @@
+import frappe
+from frappe.utils import cstr
+
+def build_full_location_path(doctype, method=None):
+ parent_location_name = doctype.parent_location
+ full_path = ''
+ html_full_path = ''
+ has_parent_location = True if parent_location_name else False
+
+ while has_parent_location:
+ result = frappe.db.get_value('Location', {'name': parent_location_name}, ['name', 'location_name', 'parent_location'], as_dict=True)
+ if not result:
+ has_parent_location = False
+ continue
+
+ full_path = f"{result['location_name']} --> {full_path}"
+ html_full_path = f"{result['location_name']} --> {html_full_path}"
+ parent_location_name = result['parent_location']
+
+ if not parent_location_name:
+ has_parent_location = False
+
+ full_path = f"{full_path} {doctype.location_name}" if full_path != '' else doctype.location_name
+ html_full_path = f"{html_full_path} {doctype.location_name}" if html_full_path != '' else f"{doctype.location_name}"
+
+ doctype.full_path = full_path
+ doctype.html_full_path = html_full_path
\ No newline at end of file
diff --git a/msp/hooks.py b/msp/hooks.py
index 9fdebce..48b68d9 100644
--- a/msp/hooks.py
+++ b/msp/hooks.py
@@ -91,7 +91,7 @@ doctype_js = {"Location" : "public/js/location.js"}
doc_events = {
"Location": {
- "before_save": "msp.tools.hooks_methods.build_full_location_path"
+ "before_save": "msp.hooked_methods.build_full_location_path"
}
}
@@ -138,3 +138,7 @@ doc_events = {
override_doctype_class = {
"Location": "msp.overrides.location.CustomLocation.CustomLocation"
}
+
+fixtures = [
+ {"doctype": "Print Format", "filters": {"name": "Standard IT Landscape Documentation"}}
+]
diff --git a/msp/msp/doctype/it_landscape/it_landscape.js b/msp/msp/doctype/it_landscape/it_landscape.js
index edfc078..c86a399 100644
--- a/msp/msp/doctype/it_landscape/it_landscape.js
+++ b/msp/msp/doctype/it_landscape/it_landscape.js
@@ -9,6 +9,9 @@ frappe.ui.form.on('IT Landscape', {
};
if (frm.doc.monitoring_link) {
frm.add_custom_button('Open Monitoring', () => frm.trigger('open_monitoring'), 'Actions');
+ };
+ if (frm.doc.rmm_instance) {
+ frm.add_custom_button('Get Agents From RMM', () => frm.trigger('rmm_get_agents'), 'RMM');
}
},
open_ticket_system: function(frm) {
@@ -17,6 +20,21 @@ frappe.ui.form.on('IT Landscape', {
open_monitoring: function(frm) {
window.open(frm.doc.monitoring_link, '_blank').focus();
},
+ rmm_get_agents: function(frm) {
+ frappe.call({
+ "method": "msp.tactical-rmm.get_agents",
+ args: {
+ "it_landscape": frm.doc.name,
+ "rmm_instance": frm.doc.rmm_instance,
+ "tactical_rmm_tenant_caption": frm.doc.tactical_rmm_tenant_caption
+ },
+ callback: (response) => {
+ frappe.msgprint(__(response.message));
+ }
+
+ })
+ },
+
copy_ssh_keys: function(frm) {
frappe.call({
diff --git a/msp/msp/doctype/it_landscape/it_landscape.json b/msp/msp/doctype/it_landscape/it_landscape.json
index 77526a1..180470b 100644
--- a/msp/msp/doctype/it_landscape/it_landscape.json
+++ b/msp/msp/doctype/it_landscape/it_landscape.json
@@ -21,7 +21,8 @@
"landscape_image",
"monitoring_link",
"ticket_system_link",
- "tactical_rmm_tenant_caption"
+ "tactical_rmm_tenant_caption",
+ "rmm_instance"
],
"fields": [
{
@@ -112,11 +113,17 @@
"fieldtype": "Link",
"label": "IT Contract",
"options": "IT Contract"
+ },
+ {
+ "fieldname": "rmm_instance",
+ "fieldtype": "Link",
+ "label": "RMM Instance",
+ "options": "RMM Instance"
}
],
"image_field": "landscape_image",
"links": [],
- "modified": "2023-02-22 20:02:46.515700",
+ "modified": "2023-06-08 23:20:35.777651",
"modified_by": "Administrator",
"module": "MSP",
"name": "IT Landscape",
diff --git a/msp/msp/doctype/it_object/it_object.json b/msp/msp/doctype/it_object/it_object.json
index 58f9c63..6381bb7 100644
--- a/msp/msp/doctype/it_object/it_object.json
+++ b/msp/msp/doctype/it_object/it_object.json
@@ -28,10 +28,15 @@
"linked_objects",
"network_config_section",
"ip_adresses",
+ "rmm_data_section",
+ "rmm_specs",
+ "rmm_software",
"external_links_section",
"admin_interface_link",
"monitoring_link",
- "oitc_host_uuid"
+ "oitc_host_uuid",
+ "rmm_agent_id",
+ "rmm_instance"
],
"fields": [
{
@@ -183,11 +188,38 @@
"fieldname": "serial_number",
"fieldtype": "Data",
"label": "Serial Number"
+ },
+ {
+ "fieldname": "rmm_agent_id",
+ "fieldtype": "Data",
+ "label": "RMM Agent ID"
+ },
+ {
+ "fieldname": "rmm_instance",
+ "fieldtype": "Link",
+ "label": "RMM Instance",
+ "options": "RMM Instance"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "rmm_data_section",
+ "fieldtype": "Section Break",
+ "label": "RMM Data"
+ },
+ {
+ "fieldname": "rmm_specs",
+ "fieldtype": "Markdown Editor",
+ "label": "RMM Specs"
+ },
+ {
+ "fieldname": "rmm_software",
+ "fieldtype": "Markdown Editor",
+ "label": "RMM Software"
}
],
"image_field": "image",
"links": [],
- "modified": "2023-03-02 10:10:07.173113",
+ "modified": "2023-06-08 23:00:31.338542",
"modified_by": "Administrator",
"module": "MSP",
"name": "IT Object",
diff --git a/msp/msp/doctype/it_software_matching/__init__.py b/msp/msp/doctype/it_software_matching/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/msp/msp/doctype/it_software_matching/it_software_matching.js b/msp/msp/doctype/it_software_matching/it_software_matching.js
new file mode 100644
index 0000000..c2bf0f9
--- /dev/null
+++ b/msp/msp/doctype/it_software_matching/it_software_matching.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2023, itsdave GmbH and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('IT Software Matching', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/msp/msp/doctype/it_software_matching/it_software_matching.json b/msp/msp/doctype/it_software_matching/it_software_matching.json
new file mode 100644
index 0000000..a148241
--- /dev/null
+++ b/msp/msp/doctype/it_software_matching/it_software_matching.json
@@ -0,0 +1,70 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "ITSWMATCH-.#####",
+ "creation": "2023-06-08 22:10:40.703139",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "search_regex",
+ "software_name",
+ "category",
+ "active"
+ ],
+ "fields": [
+ {
+ "fieldname": "search_regex",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Search Regex"
+ },
+ {
+ "fieldname": "software_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Software Name"
+ },
+ {
+ "fieldname": "category",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Category",
+ "options": "\nSoftware\nMS Office"
+ },
+ {
+ "default": "0",
+ "fieldname": "active",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Active"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2023-06-08 22:10:59.456228",
+ "modified_by": "Administrator",
+ "module": "MSP",
+ "name": "IT Software Matching",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/msp/msp/doctype/it_software_matching/it_software_matching.py b/msp/msp/doctype/it_software_matching/it_software_matching.py
new file mode 100644
index 0000000..980eb79
--- /dev/null
+++ b/msp/msp/doctype/it_software_matching/it_software_matching.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2023, itsdave GmbH and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class ITSoftwareMatching(Document):
+ pass
diff --git a/msp/msp/doctype/it_software_matching/test_it_software_matching.py b/msp/msp/doctype/it_software_matching/test_it_software_matching.py
new file mode 100644
index 0000000..127dff4
--- /dev/null
+++ b/msp/msp/doctype/it_software_matching/test_it_software_matching.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2023, itsdave GmbH and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestITSoftwareMatching(unittest.TestCase):
+ pass
diff --git a/msp/msp/doctype/msp_documentation/msp_documentation.js b/msp/msp/doctype/msp_documentation/msp_documentation.js
index e1ba6c0..313c602 100644
--- a/msp/msp/doctype/msp_documentation/msp_documentation.js
+++ b/msp/msp/doctype/msp_documentation/msp_documentation.js
@@ -14,6 +14,16 @@ frappe.ui.form.on('MSP Documentation', {
}
});
}, 'Workflow');
+ frm.add_custom_button('2. office suche', function(){
+ frappe.call({
+ method: 'msp.tactical-rmm.search_office',
+ args: { documentation: frm.doc.name },
+ callback:function(r){
+ console.log(r.message)
+ frm.reload_doc()
+ }
+ });
+ }, 'Workflow');
}
});
diff --git a/msp/msp/doctype/msp_documentation/msp_documentation.json b/msp/msp/doctype/msp_documentation/msp_documentation.json
index 7809efb..5aaab09 100644
--- a/msp/msp/doctype/msp_documentation/msp_documentation.json
+++ b/msp/msp/doctype/msp_documentation/msp_documentation.json
@@ -9,9 +9,12 @@
"field_order": [
"landscape",
"customer",
+ "customer_name",
"tactical_rmm_tenant_caption",
"generation_date",
"introduction",
+ "server_list",
+ "workstation_list",
"system_list",
"backup",
"aditional_data"
@@ -24,10 +27,12 @@
"options": "IT Landscape"
},
{
+ "fetch_from": "landscape.customer",
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
- "options": "Customer"
+ "options": "Customer",
+ "read_only": 1
},
{
"fieldname": "introduction",
@@ -59,11 +64,28 @@
"fieldname": "generation_date",
"fieldtype": "Date",
"label": "Generation Date"
+ },
+ {
+ "fieldname": "server_list",
+ "fieldtype": "Markdown Editor",
+ "label": "Server List"
+ },
+ {
+ "fieldname": "workstation_list",
+ "fieldtype": "Markdown Editor",
+ "label": "Workstation List"
+ },
+ {
+ "fetch_from": "customer.customer_name",
+ "fieldname": "customer_name",
+ "fieldtype": "Data",
+ "label": "Customer Name",
+ "read_only": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-02-02 01:08:47.664035",
+ "modified": "2023-06-09 07:14:25.256192",
"modified_by": "Administrator",
"module": "MSP",
"name": "MSP Documentation",
diff --git a/msp/msp/doctype/msp_functions/__init__.py b/msp/msp/doctype/msp_functions/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/msp/msp/doctype/msp_functions/msp_functions.js b/msp/msp/doctype/msp_functions/msp_functions.js
new file mode 100644
index 0000000..418b0b1
--- /dev/null
+++ b/msp/msp/doctype/msp_functions/msp_functions.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2023, itsdave GmbH and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('MSP Functions', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/msp/msp/doctype/msp_functions/msp_functions.json b/msp/msp/doctype/msp_functions/msp_functions.json
new file mode 100644
index 0000000..11abc23
--- /dev/null
+++ b/msp/msp/doctype/msp_functions/msp_functions.json
@@ -0,0 +1,41 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2023-06-08 21:34:09.516160",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "get_office_versions"
+ ],
+ "fields": [
+ {
+ "fieldname": "get_office_versions",
+ "fieldtype": "Button",
+ "label": "Get Office Versions",
+ "options": "msp.tactical-rmm.search_office"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2023-06-08 21:34:09.516160",
+ "modified_by": "Administrator",
+ "module": "MSP",
+ "name": "MSP Functions",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/msp/msp/doctype/msp_functions/msp_functions.py b/msp/msp/doctype/msp_functions/msp_functions.py
new file mode 100644
index 0000000..d3ccc12
--- /dev/null
+++ b/msp/msp/doctype/msp_functions/msp_functions.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2023, itsdave GmbH and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class MSPFunctions(Document):
+ pass
diff --git a/msp/msp/doctype/msp_functions/test_msp_functions.py b/msp/msp/doctype/msp_functions/test_msp_functions.py
new file mode 100644
index 0000000..a72b6ed
--- /dev/null
+++ b/msp/msp/doctype/msp_functions/test_msp_functions.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2023, itsdave GmbH and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestMSPFunctions(unittest.TestCase):
+ pass
diff --git a/msp/msp/doctype/rmm_instance/__init__.py b/msp/msp/doctype/rmm_instance/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/msp/msp/doctype/rmm_instance/rmm_instance.js b/msp/msp/doctype/rmm_instance/rmm_instance.js
new file mode 100644
index 0000000..a2da5c2
--- /dev/null
+++ b/msp/msp/doctype/rmm_instance/rmm_instance.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2023, itsdave GmbH and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('RMM Instance', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/msp/msp/doctype/rmm_instance/rmm_instance.json b/msp/msp/doctype/rmm_instance/rmm_instance.json
new file mode 100644
index 0000000..aed07e1
--- /dev/null
+++ b/msp/msp/doctype/rmm_instance/rmm_instance.json
@@ -0,0 +1,76 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "format:RMMINST-{caption}",
+ "creation": "2023-06-08 22:46:28.727092",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "caption",
+ "type",
+ "api_url",
+ "user",
+ "key"
+ ],
+ "fields": [
+ {
+ "fieldname": "type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Type",
+ "options": "Tactical RMM",
+ "reqd": 1
+ },
+ {
+ "fieldname": "api_url",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "API URL",
+ "reqd": 1
+ },
+ {
+ "fieldname": "caption",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Caption",
+ "reqd": 1
+ },
+ {
+ "fieldname": "user",
+ "fieldtype": "Data",
+ "label": "User"
+ },
+ {
+ "fieldname": "key",
+ "fieldtype": "Password",
+ "label": "Key"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2023-06-08 22:48:17.914520",
+ "modified_by": "Administrator",
+ "module": "MSP",
+ "name": "RMM Instance",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/msp/msp/doctype/rmm_instance/rmm_instance.py b/msp/msp/doctype/rmm_instance/rmm_instance.py
new file mode 100644
index 0000000..52848c3
--- /dev/null
+++ b/msp/msp/doctype/rmm_instance/rmm_instance.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2023, itsdave GmbH and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class RMMInstance(Document):
+ pass
diff --git a/msp/msp/doctype/rmm_instance/test_rmm_instance.py b/msp/msp/doctype/rmm_instance/test_rmm_instance.py
new file mode 100644
index 0000000..bbe8c34
--- /dev/null
+++ b/msp/msp/doctype/rmm_instance/test_rmm_instance.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2023, itsdave GmbH and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestRMMInstance(unittest.TestCase):
+ pass
diff --git a/msp/quotation_tools.py b/msp/quotation_tools.py
new file mode 100644
index 0000000..84d2bcb
--- /dev/null
+++ b/msp/quotation_tools.py
@@ -0,0 +1,59 @@
+
+import frappe
+
+@frappe.whitelist()
+def copy_attachments(source_doctype, source_docname, target_doctype, target_docname):
+ attachments = frappe.get_all("File", filters={"attached_to_doctype": source_doctype, "attached_to_name": source_docname})
+
+ for attachment in attachments:
+ # Überprüfen Sie den Datei-URL-Wert
+ file_url = frappe.db.get_value("File", attachment.name, "file_url")
+
+ # Überprüfen, ob ein Anhang mit derselben URL bereits für das Ziel-Dokument existiert
+ exists = frappe.db.exists({
+ "doctype": "File",
+ "file_url": file_url,
+ "attached_to_doctype": target_doctype,
+ "attached_to_name": target_docname
+ })
+
+ if not exists:
+ # Wenn nicht existiert, dann Anhang zum Ziel-Dokument kopieren
+ attach = frappe.get_doc({
+ "doctype": "File",
+ "file_url": file_url,
+ "attached_to_doctype": target_doctype,
+ "attached_to_name": target_docname
+ })
+ attach.insert(ignore_permissions=True)
+
+""" Benötigt das folgende Client Script für Quotation Form
+
+frappe.ui.form.on('Quotation', {
+ refresh: function(frm) {
+ frm.add_custom_button(__('Anhänge hinzufügen'), function() {
+ attach_item_attachments_to_quotation(frm);
+ }, "Aktionen");
+ }
+});
+
+function attach_item_attachments_to_quotation(frm) {
+ frm.doc.items.forEach(item_row => {
+ frappe.call({
+ method: 'msp.quotation_tools.copy_attachments',
+ args: {
+ 'source_doctype': 'Item',
+ 'source_docname': item_row.item_code, // Verwenden Sie item_code, um den Anhang vom Artikel zu kopieren
+ 'target_doctype': 'Quotation',
+ 'target_docname': frm.doc.name
+ },
+ callback: function(response) {
+ if (!response.exc) {
+ frm.reload_doc();
+ }
+ }
+ });
+ });
+ frappe.msgprint(__('Anhänge wurden hinzugefügt.'));
+}
+ """
\ No newline at end of file
diff --git a/msp/tactical-rmm.py b/msp/tactical-rmm.py
index a7294ee..de743bc 100644
--- a/msp/tactical-rmm.py
+++ b/msp/tactical-rmm.py
@@ -4,6 +4,84 @@ from frappe.utils.password import get_decrypted_password
import requests
import json
from pprint import pprint
+import re
+
+@frappe.whitelist()
+def get_agents(it_landscape, rmm_instance = None, tactical_rmm_tenant_caption = None):
+ pass
+
+
+@frappe.whitelist()
+def get_relevant_software_for_agent(agent_id):
+ found_software = []
+ software_for_agent = get_software_for_agent(agent_id)
+ if "software" in software_for_agent:
+ for s in software_for_agent["software"]:
+ result = _match_software(s)
+ if result:
+ found_software.append(result)
+
+ return(found_software)
+
+def _match_software(rmm_software_elememt):
+ sw_match_list = frappe.get_all("IT Software Matching", fields=["search_regex", "software_name", "category"], filters={"active": 1})
+ for el in sw_match_list:
+ result = re.match(el["search_regex"])
+ if result:
+ return {el["software_name"], el["category"]}
+ return None
+
+@frappe.whitelist()
+def search_office(agents = None):
+ points_for_found_strings = {}
+ agents_with_office = []
+
+ if not agents:
+ agents = get_all_agents()
+ todo = len(agents)
+ count = 0
+ for agent in agents:
+ found_office = False
+ count = count + 1
+ print("verarbeite agent " + str(count) + " von " + str(todo))
+ software_for_agent = get_software_for_agent(agent["agent_id"])
+ if "software" in software_for_agent:
+ for s in software_for_agent["software"]:
+ if (s["name"].lower().startswith("Microsoft Office".lower())):
+ found_string = s["name"]
+ found_office = True
+
+ if found_string in points_for_found_strings.keys():
+ points_for_found_strings[found_string] = points_for_found_strings[found_string] + 1
+ else:
+ points_for_found_strings[found_string] = 1
+ if found_office:
+ agents_with_office.append(agent)
+ else:
+ pass
+
+
+
+ print(points_for_found_strings)
+ #print(len(agents_with_office))
+ search_for_office_patches(agents_with_office)
+ print(points_for_found_strings)
+
+def search_for_office_patches(agents_with_office):
+ count = 0
+ for agent in agents_with_office:
+ found_patches_for_office = False
+ patches = get_patches_for_agent(agent["agent_id"])
+ for patch in patches:
+ if "office".lower() in patch["title"].lower():
+ found_patches_for_office = True
+ if found_patches_for_office == False:
+ print("kein patch für agent mit office gefunden: " + agent["hostname"] + " | " + agent["client_name"])
+ count = count + 1
+ print("Anzahl Clients: " + str(count))
+
+
+
@frappe.whitelist()
def get_agents_pretty(documentation):
@@ -15,15 +93,30 @@ def get_agents_pretty(documentation):
client_name = documentation_doc.tactical_rmm_tenant_caption
agents = get_all_agents()
-
+ print(agents)
agent_list = []
+ workstation_list = []
+ server_list = []
+ #if not agent_list:
+ # frappe.throw("API Abfrage hat keine Agents geliefert.")
+
+
for agent in agents:
if agent["client_name"] == client_name:
agent_list.append(agent)
+ if agent["monitoring_type"] == "workstation":
+ workstation_list.append(agent)
+ if agent["monitoring_type"] == "server":
+ server_list.append(agent)
+ pprint(agent)
output = make_agent_md_output(agent_list)
+ output_workstation = make_agent_md_output(workstation_list)
+ output_server = make_agent_md_output(server_list)
documentation_doc.system_list = output
+ documentation_doc.workstation_list = output_workstation
+ documentation_doc.server_list = output_server
documentation_doc.save()
return agent_list
@@ -42,9 +135,48 @@ def get_all_agents():
}
agents = requests.get(f"{API}/agents/?detail=true", headers=HEADERS)
-
return agents.json()
+def get_software_for_agent(agent_id=None):
+ settings = frappe.get_single("MSP Settings")
+ if not settings.api_key:
+ frappe.throw("API Key is missing")
+ if not settings.api_url:
+ frappe.throw("API URL is missing")
+
+ API = settings.api_url
+ HEADERS = {
+ "Content-Type": "application/json",
+ "X-API-KEY": get_decrypted_password("MSP Settings", "MSP Settings", "api_key", raise_exception=True),
+ }
+
+ if not agent_id:
+ frappe.throw("Agent ID fehlt")
+
+ software_for_agent = requests.get(f"{API}/software/{agent_id}/", headers=HEADERS)
+ return software_for_agent.json()
+
+def get_patches_for_agent(agent_id=None):
+ settings = frappe.get_single("MSP Settings")
+ if not settings.api_key:
+ frappe.throw("API Key is missing")
+ if not settings.api_url:
+ frappe.throw("API URL is missing")
+
+ API = settings.api_url
+ HEADERS = {
+ "Content-Type": "application/json",
+ "X-API-KEY": get_decrypted_password("MSP Settings", "MSP Settings", "api_key", raise_exception=True),
+ }
+
+ if not agent_id:
+ frappe.throw("Agent ID fehlt")
+
+ patches_for_agent = requests.get(f"{API}/winupdate/{agent_id}/", headers=HEADERS)
+ return patches_for_agent.json()
+
+
+
def make_agent_md_output(agents):
md_output = ""
@@ -56,8 +188,12 @@ def make_agent_md_output(agents):
- GPU: {agent["graphics"]}
- Disks: {agent["physical_disks"]}
- Model: {render_model(agent["make_model"])}
+- Serial Number: {agent["serial_number"]}
- Type: {agent["monitoring_type"]}
- Site: {agent["site_name"]}
+- Local IPs: {agent["local_ips"]}
+- Public IP: {agent["public_ip"]}
+- Last Seen: {agent["last_seen"]}
- Last User: {agent["logged_username"]}
'''
if agent["description"]: