diff --git a/addons/ks_dashboard_ninja/__init__.py b/addons/ks_dashboard_ninja/__init__.py
new file mode 100644
index 0000000..728ad5d
--- /dev/null
+++ b/addons/ks_dashboard_ninja/__init__.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+
+from . import models
+from . import controllers
+from . import common_lib
+from . import wizard
+
+from odoo.api import Environment, SUPERUSER_ID
+
+
+def uninstall_hook(env):
+ # env = Environment(cr, SUPERUSER_ID, {})
+ for rec in env['ks_dashboard_ninja.board'].search([]):
+ rec.ks_dashboard_client_action_id.unlink()
+ rec.ks_dashboard_menu_id.unlink()
diff --git a/addons/ks_dashboard_ninja/__manifest__.py b/addons/ks_dashboard_ninja/__manifest__.py
new file mode 100644
index 0000000..7cb159b
--- /dev/null
+++ b/addons/ks_dashboard_ninja/__manifest__.py
@@ -0,0 +1,191 @@
+# -*- coding: utf-8 -*-
+{
+ 'name': 'Dashboard Ninja with AI',
+
+ 'summary': """
+Ksolves Dashboard Ninja gives you a wide-angle view of your business that you might have missed. Get smart visual data with interactive and engaging dashboards for your Odoo ERP. Odoo Dashboard, CRM Dashboard, Inventory Dashboard, Sales Dashboard, Account Dashboard, Invoice Dashboard, Revamp Dashboard, Best Dashboard, Odoo Best Dashboard, Odoo Apps Dashboard, Best Ninja Dashboard, Analytic Dashboard, Pre-Configured Dashboard, Create Dashboard, Beautiful Dashboard, Customized Robust Dashboard, Predefined Dashboard, Multiple Dashboards, Advance Dashboard, Beautiful Powerful Dashboards, Chart Graphs Table View, All In One Dynamic Dashboard, Accounting Stock Dashboard, Pie Chart Dashboard, Modern Dashboard, Dashboard Studio, Dashboard Builder, Dashboard Designer, Odoo Studio. Revamp your Odoo Dashboard like never before! It is one of the best dashboard odoo apps in the market.
+""",
+
+ 'description': """
+Dashboard Ninja v18.0,
+ Odoo Dashboard,
+ Dashboard,
+ Dashboards,
+ Odoo apps,
+ Dashboard app,
+ HR Dashboard,
+ Sales Dashboard,
+ inventory Dashboard,
+ Lead Dashboard,
+ Opportunity Dashboard,
+ CRM Dashboard,
+ POS,
+ POS Dashboard,
+ Connectors,
+ Web Dynamic,
+ Report Import/Export,
+ Date Filter,
+ HR,
+ Sales,
+ Theme,
+ Tile Dashboard,
+ Dashboard Widgets,
+ Dashboard Manager,
+ Debranding,
+ Customize Dashboard,
+ Graph Dashboard,
+ Charts Dashboard,
+ Invoice Dashboard,
+ Project management,
+ ksolves,
+ ksolves apps,
+ Ksolves India Ltd.
+ Ksolves India Limited,
+ odoo dashboard apps
+ odoo dashboard app
+ odoo dashboard module
+ odoo modules
+ dashboards
+ powerful dashboards
+ beautiful odoo dashboard
+ odoo dynamic dashboard
+ all in one dashboard
+ multiple dashboard menu
+ odoo dashboard portal
+ beautiful odoo dashboard
+ odoo best dashboard
+ dashboard for management
+ Odoo custom dashboard
+ odoo dashboard management
+ odoo dashboard apps
+ create odoo dashboard
+ odoo dashboard extension
+ odoo dashboard module
+""",
+
+ 'author': 'Ksolves India Ltd.',
+
+ 'license': 'OPL-1',
+
+ 'currency': 'EUR',
+
+ 'price': '518.62',
+
+ 'website': 'https://store.ksolves.com/',
+
+ 'maintainer': 'Ksolves India Ltd.',
+
+ 'live_test_url': 'https://ksdndemo18.kappso.com/web/demo_login',
+
+ 'category': 'Services',
+ 'version': '18.0.1.1.7',
+
+ 'support': 'sales@ksolves.com',
+
+ 'images': ['static/description/output.gif'],
+
+ 'depends': ['base', 'web', 'base_setup', 'bus', 'base_geolocalize', 'mail'],
+
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'security/ks_security_groups.xml',
+ 'data/ks_default_data.xml',
+ 'data/ks_mail_cron.xml',
+ 'data/dn_data.xml',
+ 'data/sequence.xml',
+ 'views/res_settings.xml',
+ 'views/ks_dashboard_ninja_view.xml',
+ 'views/ks_dashboard_ninja_item_view.xml',
+ 'views/ks_dashboard_group_by.xml',
+ 'views/ks_dashboard_csv_group_by.xml',
+ 'views/ks_dashboard_action.xml',
+ 'views/ks_import_dashboard_view.xml',
+ 'wizard/ks_create_dashboard_wiz_view.xml',
+ 'wizard/ks_duplicate_dashboard_wiz_view.xml',
+ 'views/ks_ai_dashboard.xml',
+ 'views/ks_whole_ai_dashboard.xml',
+ 'views/ks_key_fetch.xml',
+ 'views/webExtend.xml'
+ ],
+
+ 'demo': ['demo/ks_dashboard_ninja_demo.xml'],
+
+ 'assets': {
+ 'ks_dashboard_ninja.ks_dashboard_lib': [
+ '/ks_dashboard_ninja/static/lib/css/gridstack.min.css',
+ '/ks_dashboard_ninja/static/lib/js/gridstack-h5.js',
+ '/ks_dashboard_ninja/static/lib/js/pdfmake.min.js',
+ '/ks_dashboard_ninja/static/lib/js/vfs_fonts.js',
+ 'ks_dashboard_ninja/static/lib/js/Animated.js',
+ 'ks_dashboard_ninja/static/lib/js/worldLow.js',
+ 'ks_dashboard_ninja/static/lib/js/map.js',
+ 'ks_dashboard_ninja/static/lib/js/index.js',
+ 'ks_dashboard_ninja/static/lib/js/pdfmake.js',
+ 'ks_dashboard_ninja/static/lib/js/percent.js',
+ 'ks_dashboard_ninja/static/lib/js/pdf.min.js',
+ 'ks_dashboard_ninja/static/lib/js/print.min.js',
+ 'ks_dashboard_ninja/static/lib/js/Dataviz.js',
+ 'ks_dashboard_ninja/static/lib/js/Material.js',
+ 'ks_dashboard_ninja/static/lib/js/Moonrise.js',
+ 'ks_dashboard_ninja/static/lib/js/xy.js',
+ 'ks_dashboard_ninja/static/lib/js/radar.js',
+ ],
+ 'web.assets_backend': [
+ 'web/static/lib/jquery/jquery.js',
+ 'ks_dashboard_ninja/static/src/scss/variable.scss',
+ 'ks_dashboard_ninja/static/src/css/ks_dashboard_ninja.scss',
+ 'ks_dashboard_ninja/static/src/css/ks_dashboard_ninja_item.css',
+ 'ks_dashboard_ninja/static/src/css/ks_icon_container_modal.css',
+ 'ks_dashboard_ninja/static/src/css/ks_dashboard_item_theme.css',
+ 'ks_dashboard_ninja/static/src/css/ks_input_bar.css',
+ 'ks_dashboard_ninja/static/src/css/ks_ai_dash.css',
+ 'ks_dashboard_ninja/static/src/css/ks_dn_filter.css',
+ 'ks_dashboard_ninja/static/src/css/ks_toggle_icon.css',
+ 'ks_dashboard_ninja/static/src/css/ks_flower_view.css',
+ 'ks_dashboard_ninja/static/src/css/ks_map_view.css',
+ 'ks_dashboard_ninja/static/src/css/ks_funnel_view.css',
+ 'ks_dashboard_ninja/static/src/css/ks_dashboard_options.css',
+ 'ks_dashboard_ninja/static/src/css/ks_dashboard_ninja_pro.css',
+ 'ks_dashboard_ninja/static/src/css/ks_to_do_item.css',
+ 'ks_dashboard_ninja/static/src/scss/common.scss',
+ '/ks_dashboard_ninja/static/src/scss/explainAi.scss',
+ '/ks_dashboard_ninja/static/src/scss/chat_with_ai.scss',
+ '/ks_dashboard_ninja/static/src/scss/Generate-ai.scss',
+ '/ks_dashboard_ninja/static/src/scss/ks_ai_dashboard.scss',
+ 'ks_dashboard_ninja/static/src/css/style.css',
+ 'ks_dashboard_ninja/static/src/js/ks_global_functions.js',
+ 'ks_dashboard_ninja/static/lib/js/index.js',
+ 'ks_dashboard_ninja/static/lib/js/pdfmake.js',
+ 'ks_dashboard_ninja/static/lib/js/percent.js',
+ 'ks_dashboard_ninja/static/lib/js/pdf.min.js',
+ 'ks_dashboard_ninja/static/lib/js/print.min.js',
+ 'ks_dashboard_ninja/static/lib/js/Dataviz.js',
+ 'ks_dashboard_ninja/static/lib/js/Material.js',
+ 'ks_dashboard_ninja/static/lib/js/Moonrise.js',
+ 'ks_dashboard_ninja/static/lib/js/exporting.js',
+ 'ks_dashboard_ninja/static/lib/js/pdfmake.js',
+ 'ks_dashboard_ninja/static/lib/js/percent.js',
+ 'ks_dashboard_ninja/static/src/js/ks_global_functions.js',
+ 'ks_dashboard_ninja/static/lib/js/xy.js',
+ 'ks_dashboard_ninja/static/lib/js/radar.js',
+ 'ks_dashboard_ninja/static/src/js/domainfix.js',
+ 'ks_dashboard_ninja/static/src/js/chart_buttons_patch.js',
+ 'ks_dashboard_ninja/static/src/xml/**/*',
+ 'ks_dashboard_ninja/static/src/css/ks_radial_chart.css',
+ 'ks_dashboard_ninja/static/src/js/ks_ai_dash_action.js',
+ 'ks_dashboard_ninja/static/src/components/**/*',
+ 'ks_dashboard_ninja/static/src/widgets/**/*',
+ 'ks_dashboard_ninja/static/src/js/charts_render_global_functions.js',
+ 'ks_dashboard_ninja/static/src/js/cookies.js',
+ 'ks_dashboard_ninja/static/src/scss/form_views.scss',
+ 'ks_dashboard_ninja/static/src/scss/modal.scss',
+ 'ks_dashboard_ninja/static/src/odoo_base_extend/**/*',
+ ],
+ },
+
+ 'external_dependencies': {
+ 'python': ['pandas', 'xlrd', 'openpyxl', 'gTTS', 'SQLAlchemy']
+ },
+
+ 'uninstall_hook': 'uninstall_hook',
+}
diff --git a/addons/ks_dashboard_ninja/common_lib/__init__.py b/addons/ks_dashboard_ninja/common_lib/__init__.py
new file mode 100644
index 0000000..2da4c12
--- /dev/null
+++ b/addons/ks_dashboard_ninja/common_lib/__init__.py
@@ -0,0 +1,2 @@
+from . import ks_date_filter_selections
+from . import filter_tools
diff --git a/addons/ks_dashboard_ninja/common_lib/filter_tools.py b/addons/ks_dashboard_ninja/common_lib/filter_tools.py
new file mode 100644
index 0000000..4510ae5
--- /dev/null
+++ b/addons/ks_dashboard_ninja/common_lib/filter_tools.py
@@ -0,0 +1,20 @@
+import json
+
+from odoo.tools.safe_eval import safe_eval
+
+
+def replace_company_domain(domain, company_id, company_ids):
+ domain = safe_eval(domain) if isinstance(domain, str) else domain
+ new_domain = []
+ for condition in domain:
+ if isinstance(condition, tuple) and len(condition) >= 3:
+ if condition[1] in ('in', 'not in') and isinstance(condition[2], list) and '%MYCOMPANY' in condition[2]:
+ new_condition = (condition[0], condition[1], [y for x in condition[2] for y in (company_ids if x == '%MYCOMPANY' else [x])])
+ elif condition[2] == '%MYCOMPANY':
+ new_condition = (condition[0], condition[1], company_id)
+ else:
+ new_condition = condition
+ new_domain.append(new_condition)
+ else:
+ new_domain.append(condition)
+ return json.dumps(new_domain)
\ No newline at end of file
diff --git a/addons/ks_dashboard_ninja/common_lib/ks_date_filter_selections.py b/addons/ks_dashboard_ninja/common_lib/ks_date_filter_selections.py
new file mode 100644
index 0000000..4ec7040
--- /dev/null
+++ b/addons/ks_dashboard_ninja/common_lib/ks_date_filter_selections.py
@@ -0,0 +1,343 @@
+# -*- coding: utf-8 -*-
+
+import json
+import os
+import os.path
+from datetime import timedelta
+
+import pytz
+from dateutil import rrule
+from dateutil.relativedelta import relativedelta
+from odoo import _
+from odoo.exceptions import ValidationError
+from odoo.fields import datetime
+from odoo.tools.safe_eval import safe_eval
+
+
+def ks_get_date(ks_date_filter_selection, self, type):
+ try:
+ timezone = self._context.get('tz')
+ except Exception as e:
+ timezone = self.env.user.tz
+
+ if not timezone:
+ ks_tzone = os.environ.get('TZ')
+ if ks_tzone:
+ timezone = ks_tzone
+ elif os.path.exists('/etc/timezone'):
+ ks_tzone = open('/etc/timezone').read()
+ timezone = ks_tzone[0:-1]
+ try:
+ datetime.now(pytz.timezone(timezone))
+ except Exception as e:
+ raise ValidationError(_("Please set the local timezone."))
+
+ else:
+ raise ValidationError(_("Please set the local timezone."))
+
+ series = ks_date_filter_selection
+ if ks_date_filter_selection in ['t_fiscal_year', 'n_fiscal_year', 'ls_fiscal_year']:
+ function_name = globals()["ks_date_series_" + series.split("_")[0]]
+ return function_name(series.split("_")[1], timezone, type,self)
+ else:
+ function_name = globals()["ks_date_series_" + series.split("_")[0]]
+ return function_name(series.split("_")[1],timezone, type,self)
+
+def ks_date_series_td(ks_date_selection, timezone, type, self=None):
+ ks_function_name = globals()["ks_get_date_range_from_td_" + ks_date_selection]
+ return ks_function_name(timezone, type, self)
+
+def ks_get_date_range_from_td_year(timezone, type,self):
+ ks_date_data = {}
+ date = datetime.now(pytz.timezone(timezone))
+ year = date.year
+ start_date = datetime(year, 1, 1)
+ end_date = date
+ if type == 'date':
+ ks_date_data["selected_start_date"] = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = datetime.strptime(end_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, timezone)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date, timezone)
+ return ks_date_data
+
+def ks_get_date_range_from_td_month(timezone, type,self):
+ ks_date_data = {}
+
+ date = datetime.now(pytz.timezone(timezone))
+ year = date.year
+ month = date.month
+ start_date = datetime(year, month, 1)
+ end_date = date
+ if type == 'date':
+ ks_date_data["selected_start_date"] = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = datetime.strptime(end_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, timezone)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date, timezone)
+ return ks_date_data
+def ks_get_date_range_from_td_week(timezone, type,self):
+ ks_date_data = {}
+ lang = self.env['res.lang']._lang_get(self.env.user.lang)
+ week_start = lang.week_start
+ start_Date = rrule.weekday(int(week_start) - 1)
+ start_date = datetime.today() + relativedelta(weekday=start_Date(-1))
+ end_date = datetime.now(pytz.timezone(timezone))
+ start_date = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ if type == 'date':
+ ks_date_data["selected_start_date"] = start_date
+ end_date = datetime.strptime(end_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = end_date
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, timezone)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date, timezone)
+ return ks_date_data
+def ks_get_date_range_from_td_quarter(timezone, type,self):
+ ks_date_data = {}
+ date = datetime.now(pytz.timezone(timezone))
+ year = date.year
+ quarter = int((date.month - 1) / 3) + 1
+ start_date = datetime(year, 3 * quarter - 2, 1)
+ end_date = date
+ if type == 'date':
+ ks_date_data["selected_start_date"] = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = datetime.strptime(end_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, timezone)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date, timezone)
+ return ks_date_data
+
+
+# Last Specific Days Ranges : 7, 30, 90, 365
+def ks_date_series_l(ks_date_selection, timezone, type, self=None):
+ ks_date_data = {}
+ date_filter_options = {
+ 'day': 0,
+ 'week': 7,
+ 'month': 30,
+ 'quarter': 90,
+ 'year': 365,
+ 'past': False,
+ 'future': False
+ }
+ end_time = datetime.strptime(datetime.now(pytz.timezone(timezone)).strftime("%Y-%m-%d 23:59:59"),
+ '%Y-%m-%d %H:%M:%S')
+ start_time = datetime.strptime((datetime.now(pytz.timezone(timezone)) - timedelta(
+ days=date_filter_options[ks_date_selection])).strftime("%Y-%m-%d 00:00:00"), '%Y-%m-%d %H:%M:%S')
+ if type == 'date':
+ ks_date_data["selected_end_date"] = datetime.strptime(end_time.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_start_date"] = datetime.strptime(start_time.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_time, timezone)
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_time, timezone)
+
+ return ks_date_data
+
+
+# Current Date Ranges : Week, Month, Quarter, year
+def ks_date_series_t(ks_date_selection, timezone, type, self=None):
+ ks_function_name = globals()["ks_get_date_range_from_" + ks_date_selection]
+ return ks_function_name("current", timezone, type,self)
+
+
+# Previous Date Ranges : Week, Month, Quarter, year
+def ks_date_series_ls(ks_date_selection, timezone, type,self=None):
+ ks_function_name = globals()["ks_get_date_range_from_" + ks_date_selection]
+ return ks_function_name("previous", timezone, type,self)
+
+
+# Next Date Ranges : Day, Week, Month, Quarter, year
+def ks_date_series_n(ks_date_selection, timezone, type,self=None):
+ ks_function_name = globals()["ks_get_date_range_from_" + ks_date_selection]
+ return ks_function_name("next", timezone, type, self)
+
+
+def ks_get_date_range_from_day(date_state, timezone, type,self):
+ ks_date_data = {}
+
+ date = datetime.now(pytz.timezone(timezone))
+
+ if date_state == "previous":
+ date = date - timedelta(days=1)
+ elif date_state == "next":
+ date = date + timedelta(days=1)
+ start_date = datetime(date.year, date.month, date.day)
+ end_date = datetime(date.year, date.month, date.day) + timedelta(days=1, seconds=-1)
+ if type == 'date':
+ ks_date_data["selected_start_date"] = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = datetime.strptime(end_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date,timezone)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date,timezone)
+ return ks_date_data
+
+
+def ks_get_date_range_from_week(date_state, timezone, type,self):
+ ks_date_data = {}
+
+ # date = datetime.now(pytz.timezone(timezone))
+ # ks_week = 0
+ lang = self.env['res.lang']._lang_get(self.env.user.lang)
+ week_start = lang.week_start
+ start_Date = rrule.weekday(int(week_start) - 1)
+ start_date = datetime.today() + relativedelta(weekday=start_Date(-1))
+ if date_state == "previous":
+ start_date = datetime.today() - relativedelta(weeks=1, weekday=start_Date(-1))
+ elif date_state == "next":
+ start_date = datetime.today() - relativedelta(weeks=-1, weekday=start_Date(-1))
+
+ start_date = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ if type == 'date':
+ ks_date_data["selected_start_date"] = start_date
+ end_date = start_date + timedelta(days=6, hours=23, minutes=59, seconds=59, milliseconds=59)
+ ks_date_data["selected_end_date"] = end_date
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, timezone)
+ end_date = start_date + timedelta(days=6, hours=23, minutes=59, seconds=59, milliseconds=59)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date, timezone)
+ return ks_date_data
+
+def ks_get_date_range_from_month(date_state, timezone, type,self):
+ ks_date_data = {}
+
+ date = datetime.now(pytz.timezone(timezone))
+ year = date.year
+ month = date.month
+
+ if date_state == "previous":
+ month -= 1
+ if month == 0:
+ month = 12
+ year -= 1
+ elif date_state == "next":
+ month += 1
+ if month == 13:
+ month = 1
+ year += 1
+
+ end_year = year
+ end_month = month
+ if month == 12:
+ end_year += 1
+ end_month = 1
+ else:
+ end_month += 1
+ start_date = datetime(year, month, 1)
+ end_date = datetime(end_year, end_month, 1) - timedelta(seconds=1)
+ if type == 'date':
+ ks_date_data["selected_start_date"] = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = datetime.strptime(end_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, timezone)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date, timezone)
+ return ks_date_data
+
+
+def ks_get_date_range_from_quarter(date_state, timezone, type,self):
+ ks_date_data = {}
+
+ date = datetime.now(pytz.timezone(timezone))
+ year = date.year
+ quarter = int((date.month - 1) / 3) + 1
+
+ if date_state == "previous":
+ quarter -= 1
+ if quarter == 0:
+ quarter = 4
+ year -= 1
+ elif date_state == "next":
+ quarter += 1
+ if quarter == 5:
+ quarter = 1
+ year += 1
+
+ start_date = datetime(year, 3 * quarter - 2, 1)
+
+ month = 3 * quarter
+ remaining = int(month / 12)
+ end_date = datetime(year + remaining, month % 12 + 1, 1) - timedelta(seconds=1)
+ if type == 'date':
+ ks_date_data["selected_start_date"] = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = datetime.strptime(end_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, timezone)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date, timezone)
+ return ks_date_data
+
+
+def ks_get_date_range_from_year(date_state, timezone, type,self):
+ ks_date_data = {}
+
+ date = datetime.now(pytz.timezone(timezone))
+ year = date.year
+
+ if date_state == "previous":
+ year -= 1
+ elif date_state == "next":
+ year += 1
+ start_date = datetime(year, 1, 1)
+ end_date = datetime(year + 1, 1, 1) - timedelta(seconds=1)
+ if type == 'date':
+ ks_date_data["selected_start_date"] = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = datetime.strptime(end_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, timezone)
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(end_date, timezone)
+ return ks_date_data
+
+def ks_get_date_range_from_past(date_state, self_tz, type, self):
+ ks_date_data = {}
+ date = datetime.now(pytz.timezone(self_tz))
+ if type == 'date':
+ ks_date_data["selected_end_date"] = datetime.strptime(date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(date, self_tz)
+ ks_date_data["selected_start_date"] = False
+ return ks_date_data
+
+
+def ks_get_date_range_from_pastwithout(date_state, self_tz, type,self):
+ ks_date_data = {}
+ date = datetime.now(pytz.timezone(self_tz))
+ hour = date.hour + 1
+ date = date - timedelta(hours=hour)
+ date = datetime.strptime(date.strftime("%Y-%m-%d 23:59:59"), '%Y-%m-%d %H:%M:%S')
+ ks_date_data["selected_start_date"] = False
+ if type == 'date':
+ ks_date_data["selected_end_date"] = datetime.strptime(date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_end_date"] = ks_convert_into_utc(date, self_tz)
+ return ks_date_data
+
+
+def ks_get_date_range_from_future(date_state, self_tz, type,self):
+ ks_date_data = {}
+ date = datetime.now(pytz.timezone(self_tz))
+ ks_date_data["selected_end_date"] = False
+ if type == 'date':
+ ks_date_data["selected_start_date"] = date.strptime(date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(date,self_tz)
+ return ks_date_data
+
+
+def ks_get_date_range_from_futurestarting(date_state, self_tz, type,self):
+ ks_date_data = {}
+ date = datetime.now(pytz.timezone(self_tz))
+ date = date + timedelta(days=1)
+ start_date = datetime.strptime(date.strftime("%Y-%m-%d 00:00:00"), '%Y-%m-%d %H:%M:%S')
+ if type == 'date':
+ ks_date_data["selected_start_date"] = datetime.strptime(start_date.strftime("%Y-%m-%d"), '%Y-%m-%d')
+ ks_date_data["selected_end_date"] = False
+ else:
+ ks_date_data["selected_start_date"] = ks_convert_into_utc(start_date, self_tz)
+ ks_date_data["selected_end_date"] = False
+ return ks_date_data
+
+def ks_convert_into_utc(datetime, timezone):
+ ks_tz = timezone and pytz.timezone(timezone) or pytz.UTC
+ return ks_tz.localize(datetime.replace(tzinfo=None), is_dst=False).astimezone(pytz.UTC).replace(tzinfo=None)
+
+def ks_convert_into_local(datetime, timezone):
+ ks_tz = timezone and pytz.timezone(timezone) or pytz.UTC
+ return pytz.UTC.localize(datetime.replace(tzinfo=None), is_dst=False).astimezone(ks_tz).replace(tzinfo=None)
\ No newline at end of file
diff --git a/addons/ks_dashboard_ninja/controllers/__init__.py b/addons/ks_dashboard_ninja/controllers/__init__.py
new file mode 100644
index 0000000..798bc50
--- /dev/null
+++ b/addons/ks_dashboard_ninja/controllers/__init__.py
@@ -0,0 +1,4 @@
+from . import ks_chart_export
+from . import ks_list_export
+from . import ks_dashboard_export
+from . import ks_domain_fix
\ No newline at end of file
diff --git a/addons/ks_dashboard_ninja/controllers/ks_chart_export.py b/addons/ks_dashboard_ninja/controllers/ks_chart_export.py
new file mode 100644
index 0000000..d72cc4f
--- /dev/null
+++ b/addons/ks_dashboard_ninja/controllers/ks_chart_export.py
@@ -0,0 +1,131 @@
+
+import re
+import datetime
+import io
+import json
+import operator
+import logging
+
+from odoo.addons.web.controllers.export import ExportXlsxWriter
+from odoo.tools.translate import _
+from werkzeug.exceptions import InternalServerError
+from odoo import http
+from odoo.http import content_disposition, request
+from odoo.tools.misc import xlwt
+from odoo.exceptions import UserError
+from odoo.tools import pycompat
+
+from odoo.exceptions import ValidationError
+
+_logger = logging.getLogger(__name__)
+
+class KsChartExport(http.Controller):
+
+ def base(self, data):
+ params = json.loads(data)
+ if not params.get('chart_data'):
+ raise ValidationError("Chart data not present")
+ header,chart_data = operator.itemgetter('header','chart_data')(params)
+ chart_data = json.loads(chart_data)
+
+ if isinstance(chart_data['labels'], list):
+ chart_data['labels'] = [str(label) for label in chart_data['labels']]
+
+ chart_data['labels'].insert(0,'Measure')
+ columns_headers = chart_data['labels']
+ import_data = []
+ excel_fields = []
+
+ for dataset in chart_data['datasets']:
+ dataset['data'].insert(0, dataset['label'])
+ import_data.append(dataset['data'])
+
+ for i in range(len(columns_headers)):
+ ks_type_obj = {}
+ if (len(import_data)):
+ if isinstance(import_data[0][i],float):
+ ks_type_obj['type'] = 'float'
+ else:
+ ks_type_obj['type'] = ''
+ excel_fields.append((ks_type_obj))
+
+ return request.make_response(self.from_data(excel_fields, columns_headers, import_data),
+ headers=[('Content-Disposition',
+ content_disposition(self.filename(header))),
+ ('Content-Type', self.content_type)],
+ # cookies={'fileToken': token}
+ )
+
+class KsChartExcelExport(KsChartExport, http.Controller):
+
+ # Excel needs raw data to correctly handle numbers and date values
+ raw_data = True
+
+ @http.route('/ks_dashboard_ninja/export/chart_xls', type='http', auth="user")
+ def index(self, data):
+ try:
+ return self.base(data)
+ except Exception as exc:
+ _logger.exception("Exception during request handling.")
+ payload = json.dumps({
+ 'code': 200,
+ 'message': "Odoo Server Error",
+ 'data': http.serialize_exception(exc)
+ })
+ raise InternalServerError(payload) from exc
+
+ @property
+ def content_type(self):
+ return 'application/vnd.ms-excel'
+
+ def filename(self, base):
+ return base + '.xlsx'
+
+ def from_data(self, fields, columns_headers, rows):
+ with ExportXlsxWriter(fields, columns_headers, len(rows)) as xlsx_writer:
+ for row_index, row in enumerate(rows):
+ for cell_index, cell_value in enumerate(row):
+ xlsx_writer.write_cell(row_index + 1, cell_index, cell_value)
+
+ return xlsx_writer.value
+
+
+class KsChartCsvExport(KsChartExport, http.Controller):
+
+ @http.route('/ks_dashboard_ninja/export/chart_csv', type='http', auth="user")
+ def index(self, data):
+ try:
+ return self.base(data)
+ except Exception as exc:
+ _logger.exception("Exception during request handling.")
+ payload = json.dumps({
+ 'code': 200,
+ 'message': "Odoo Server Error",
+ 'data': http.serialize_exception(exc)
+ })
+ raise InternalServerError(payload) from exc
+
+ @property
+ def content_type(self):
+ return 'text/csv;charset=utf8'
+
+ def filename(self, base):
+ return base + '.csv'
+
+ def from_data(self, fields,columns_headers, rows):
+ fp = io.BytesIO()
+ writer = pycompat.csv_writer(fp, quoting=1)
+
+ writer.writerow(columns_headers)
+
+ for data in rows:
+ row = []
+ for d in data:
+ # Spreadsheet apps tend to detect formulas on leading =, + and -
+ if isinstance(d, str) and d.startswith(('=', '-', '+')):
+ d = "'" + d
+
+ row.append(pycompat.to_text(d))
+ writer.writerow(row)
+
+ return fp.getvalue()
diff --git a/addons/ks_dashboard_ninja/controllers/ks_dashboard_export.py b/addons/ks_dashboard_ninja/controllers/ks_dashboard_export.py
new file mode 100644
index 0000000..7e879ce
--- /dev/null
+++ b/addons/ks_dashboard_ninja/controllers/ks_dashboard_export.py
@@ -0,0 +1,88 @@
+import io
+import json
+import operator
+import logging
+
+# from odoo.addons.web.controllers.main import ExportFormat
+from odoo.addons.web.controllers.export import ExportXlsxWriter
+
+from odoo import http
+from odoo.http import request
+from odoo.http import content_disposition,request
+from werkzeug.exceptions import InternalServerError
+_logger = logging.getLogger(__name__)
+
+
+class KsDashboardExport(http.Controller):
+
+ def base(self, data):
+ params = json.loads(data)
+ header, dashboard_data = operator.itemgetter('header', 'dashboard_data')(params)
+ return request.make_response(self.from_data(dashboard_data),
+ headers=[('Content-Disposition',
+ content_disposition(self.filename(header))),
+ ('Content-Type', self.content_type)],
+ # cookies={'fileToken': token}
+ )
+
+
+class KsDashboardJsonExport(KsDashboardExport, http.Controller):
+
+ @http.route('/ks_dashboard_ninja/export/dashboard_json', type='http', auth="user")
+ def index(self, data):
+ try:
+ return self.base(data)
+ except Exception as exc:
+ _logger.exception("Exception during request handling.")
+ payload = json.dumps({
+ 'code': 200,
+ 'message': "Odoo Server Error",
+ 'data': http.serialize_exception(exc)
+ })
+ raise InternalServerError(payload) from exc
+
+ @property
+ def content_type(self):
+ return 'text/csv;charset=utf8'
+
+ def filename(self, base):
+ return base + '.json'
+
+ def from_data(self, dashboard_data):
+ fp = io.StringIO()
+ fp.write(json.dumps(dashboard_data))
+
+ return fp.getvalue()
+
+class KsItemJsonExport(KsDashboardExport, http.Controller):
+
+ @http.route('/ks_dashboard_ninja/export/item_json', type='http', auth="user")
+ def index(self, data):
+ try:
+ data = json.loads(data)
+ item_id = data["item_id"]
+ data['dashboard_data'] = request.env['ks_dashboard_ninja.board'].ks_export_item(item_id)
+ data = json.dumps(data)
+ return self.base(data)
+ except Exception as exc:
+ _logger.exception("Exception during request handling.")
+ payload = json.dumps({
+ 'code': 200,
+ 'message': "Odoo Server Error",
+ 'data': http.serialize_exception(exc)
+ })
+ raise InternalServerError(payload) from exc
+
+
+ @property
+ def content_type(self):
+ return 'text/csv;charset=utf8'
+
+ def filename(self, base):
+ return base + '.json'
+
+ def from_data(self, dashboard_data):
+ fp = io.StringIO()
+ fp.write(json.dumps(dashboard_data))
+
+ return fp.getvalue()
diff --git a/addons/ks_dashboard_ninja/controllers/ks_domain_fix.py b/addons/ks_dashboard_ninja/controllers/ks_domain_fix.py
new file mode 100644
index 0000000..b4fb31d
--- /dev/null
+++ b/addons/ks_dashboard_ninja/controllers/ks_domain_fix.py
@@ -0,0 +1,21 @@
+from odoo.addons.web.controllers.domain import Domain
+
+from odoo import http, _
+from odoo.http import Controller, request
+from odoo.tools.safe_eval import safe_eval
+
+
+class ksdomainfix(Domain):
+ # to validate our uid and mycompany based domain
+ @http.route('/web/domain/validate', type='json', auth="user")
+ def validate(self, model, domain):
+ ks_uid_domain = str(domain)
+ if ks_uid_domain and "%UID" in ks_uid_domain:
+ ks_domain = ks_uid_domain.replace("%UID", str(request.env.user.id))
+ return super().validate(model,safe_eval(ks_domain))
+ elif ks_uid_domain and "%MYCOMPANY" in ks_uid_domain:
+ ks_domain = ks_uid_domain.replace("%MYCOMPANY", str(request.env.company.id))
+ return super().validate(model,safe_eval(ks_domain))
+ else:
+ return super().validate(model, domain)
+
diff --git a/addons/ks_dashboard_ninja/controllers/ks_list_export.py b/addons/ks_dashboard_ninja/controllers/ks_list_export.py
new file mode 100644
index 0000000..bb0fbd2
--- /dev/null
+++ b/addons/ks_dashboard_ninja/controllers/ks_list_export.py
@@ -0,0 +1,217 @@
+import datetime
+import io
+import json
+import logging
+import operator
+import os
+
+import pytz
+from dateutil.parser import parse
+from odoo.exceptions import ValidationError
+from odoo.http import content_disposition, request
+from odoo.tools import pycompat
+from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
+from werkzeug.exceptions import InternalServerError
+
+from odoo import http
+from odoo.addons.web.controllers.export import ExportXlsxWriter
+from ..common_lib.ks_date_filter_selections import ks_get_date, ks_convert_into_local
+
+_logger = logging.getLogger(__name__)
+
+
+class KsListExport(http.Controller):
+
+ def base(self, data):
+ params = json.loads(data)
+ # header,list_data = operator.itemgetter('header','chart_data')(params)
+ header, list_data, item_id, ks_export_boolean, context, params = operator.itemgetter('header', 'chart_data',
+ 'ks_item_id',
+ 'ks_export_boolean',
+ 'context', 'params')(
+ params)
+ list_data = json.loads(list_data)
+ if not list_data or not list_data.get('label', False):
+ raise ValidationError("List data not present")
+ if ks_export_boolean:
+ item = request.env['ks_dashboard_ninja.item'].browse(int(item_id))
+ ks_timezone = item._context.get('tz') or item.env.user.tz
+ if not ks_timezone:
+ ks_tzone = os.environ.get('TZ')
+ if ks_tzone:
+ ks_timezone = ks_tzone
+ elif os.path.exists('/etc/timezone'):
+ ks_tzone = open('/etc/timezone').read()
+ ks_timezone = ks_tzone[0:-1]
+ try:
+ datetime.now(pytz.timezone(ks_timezone))
+ except Exception as e:
+ _logger.info('Please set the local timezone')
+
+ else:
+ _logger.info('Please set the local timezone')
+ orderby = item.ks_sort_by_field.id
+ sort_order = item.ks_sort_by_order
+ ks_start_date = context.get('ksDateFilterStartDate', False)
+ ks_end_date = context.get('ksDateFilterEndDate', False)
+ ksDateFilterSelection = context.get('ksDateFilterSelection', False)
+ if context.get('allowed_company_ids', False):
+ item = item.with_context(allowed_company_ids=context.get('allowed_company_ids'))
+ if item.ks_data_calculation_type == 'query':
+ query_start_date = item.ks_query_start_date
+ query_end_date = item.ks_query_end_date
+ ks_query = str(item.ks_custom_query)
+ if ks_start_date and ks_end_date:
+ ks_start_date = parse(ks_start_date)
+ ks_end_date = parse(ks_end_date)
+ item = item.with_context(ksDateFilterStartDate=ks_start_date)
+ item = item.with_context(ksDateFilterEndDate=ks_end_date)
+ item = item.with_context(ksDateFilterSelection=ksDateFilterSelection)
+
+ if item._context.get('ksDateFilterSelection', False):
+ ks_date_filter_selection = item._context['ksDateFilterSelection']
+ if ks_date_filter_selection == 'l_custom':
+ item = item.with_context(ksDateFilterStartDate=ks_start_date)
+ item = item.with_context(ksDateFilterEndDate=ks_end_date)
+ item = item.with_context(ksIsDefultCustomDateFilter=False)
+
+ else:
+ ks_date_filter_selection = item.ks_dashboard_ninja_board_id.ks_date_filter_selection
+ item = item.with_context(ksDateFilterStartDate=item.ks_dashboard_ninja_board_id.ks_dashboard_start_date)
+ item = item.with_context(ksDateFilterEndDate=item.ks_dashboard_ninja_board_id.ks_dashboard_end_date)
+ item = item.with_context(ksDateFilterSelection=ks_date_filter_selection)
+ item = item.with_context(ksIsDefultCustomDateFilter=True)
+
+ if ks_date_filter_selection not in ['l_custom', 'l_none']:
+ ks_date_data = ks_get_date(ks_date_filter_selection, request, 'datetime')
+ item = item.with_context(ksDateFilterStartDate=ks_date_data["selected_start_date"])
+ item = item.with_context(ksDateFilterEndDate=ks_date_data["selected_end_date"])
+
+ item_domain = params.get('ks_domain_1', [])
+ ks_chart_domain = item.ks_convert_into_proper_domain(item.ks_domain, item,item_domain)
+ # list_data = item.ks_fetch_list_view_data(item,ks_chart_domain, ks_export_all=
+ if list_data['type'] == 'ungrouped':
+ list_data = item.ks_fetch_list_view_data(item, ks_chart_domain, ks_export_all=True)
+ elif list_data['type'] == 'grouped':
+ list_data = item.get_list_view_record(orderby, sort_order, ks_chart_domain, ks_export_all=True)
+ elif item.ks_data_calculation_type == 'query':
+ if ks_start_date or ks_end_date:
+ query_start_date = ks_start_date
+ query_end_date = ks_end_date
+ ks_query_result = item.ks_get_list_query_result(ks_query, query_start_date, query_end_date, ks_offset=0,
+ ks_export_all=True)
+ list_data = item.ks_format_query_result(ks_query_result)
+
+ # chart_data['labels'].insert(0,'Measure')
+ columns_headers = list_data['label']
+ import_data = []
+ excel_fields = []
+ for dataset in list_data['data_rows']:
+ if not list_data['type'] == 'grouped':
+ for count, index in enumerate(dataset['ks_column_type']):
+ if index == 'datetime':
+ ks_converted_date = False
+ date_string = dataset['data'][count]
+ if dataset['data'][count]:
+ ks_converted_date = ks_convert_into_local(datetime.datetime.strptime(date_string, '%m/%d/%y %H:%M:%S'),ks_timezone)
+ dataset['data'][count] = ks_converted_date
+ for ks_count, val in enumerate(dataset['data']):
+ if isinstance(val, (float, int)):
+ if val >= 0:
+ try:
+ ks_precision = item.sudo().env.ref('ks_dashboard_ninja.ks_dashboard_ninja_precision').digits
+ except Exception as e:
+ ks_precision = 2
+ dataset['data'][ks_count] = item.env['ir.qweb.field.float'].sudo().value_to_html(val,
+ {'precision': ks_precision})
+ import_data.append(dataset['data'])
+ for i in range(len(columns_headers)):
+ ks_type_obj = {}
+ if (len(import_data)):
+ if isinstance(import_data[0][i], float):
+ ks_type_obj['type'] = 'float'
+ else:
+ ks_type_obj['type'] = ''
+ excel_fields.append((ks_type_obj))
+
+ return request.make_response(self.from_data(excel_fields, columns_headers, import_data),
+ headers=[('Content-Disposition',
+ content_disposition(self.filename(header))),
+ ('Content-Type', self.content_type)],
+ # cookies={'fileToken': token}
+ )
+
+
+class KsListExcelExport(KsListExport, http.Controller):
+
+ # Excel needs raw data to correctly handle numbers and date values
+ raw_data = True
+
+ @http.route('/ks_dashboard_ninja/export/list_xls', type='http', auth="user")
+ def index(self, data):
+ try:
+ return self.base(data)
+ except Exception as exc:
+ _logger.exception("Exception during request handling.")
+ payload = json.dumps({
+ 'code': 200,
+ 'message': "Odoo Server Error",
+ 'data': http.serialize_exception(exc)
+ })
+ raise InternalServerError(payload) from exc
+
+ @property
+ def content_type(self):
+ return 'application/vnd.ms-excel'
+
+ def filename(self, base):
+ return base + '.xlsx'
+
+ def from_data(self, fields, columns_headers, rows):
+ with ExportXlsxWriter(fields, columns_headers, len(rows)) as xlsx_writer:
+ for row_index, row in enumerate(rows):
+ for cell_index, cell_value in enumerate(row):
+ xlsx_writer.write_cell(row_index + 1, cell_index, cell_value)
+
+ return xlsx_writer.value
+
+
+class KsListCsvExport(KsListExport, http.Controller):
+
+ @http.route('/ks_dashboard_ninja/export/list_csv', type='http', auth="user")
+ def index(self, data):
+ try:
+ return self.base(data)
+ except Exception as exc:
+ _logger.exception("Exception during request handling.")
+ payload = json.dumps({
+ 'code': 200,
+ 'message': "Odoo Server Error",
+ 'data': http.serialize_exception(exc)
+ })
+ raise InternalServerError(payload) from exc
+
+ @property
+ def content_type(self):
+ return 'text/csv;charset=utf8'
+
+ def filename(self, base):
+ return base + '.csv'
+
+ def from_data(self, fields, column_headers,rows):
+ fp = io.BytesIO()
+ writer = pycompat.csv_writer(fp, quoting=1)
+
+ writer.writerow(column_headers)
+
+ for data in rows:
+ row = []
+ for d in data:
+ # Spreadsheet apps tend to detect formulas on leading =, + and -
+ if isinstance(d, str) and d.startswith(('=', '-', '+')):
+ d = "'" + d
+
+ row.append(pycompat.to_text(d))
+ writer.writerow(row)
+
+ return fp.getvalue()
diff --git a/addons/ks_dashboard_ninja/data/dn_data.xml b/addons/ks_dashboard_ninja/data/dn_data.xml
new file mode 100644
index 0000000..da76cfd
--- /dev/null
+++ b/addons/ks_dashboard_ninja/data/dn_data.xml
@@ -0,0 +1,9 @@
+
+
\n" +" You can find all items related to Dashboard Here.
\n" +" " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__ks_action +msgid "Action" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_item_action_field +msgid "Action Group By" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_action_lines +msgid "Action Lines" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "Action field: {} cannot be aggregated by {}" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_actions +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Actions" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_active +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__ks_is_active +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Active" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__ks_active +msgid "Active Description" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Add" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Add Item" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Add a Line" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Add a Section" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#, python-format +msgid "Add a condition" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_form +msgid "Add a filter" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_form +msgid "Add a separator" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Add button" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Advance Configuration" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "" +"All Target Lines Changes Will be reflected on Chart after saving the record " +"and pagination will be ignore ." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__l_none +#, python-format +msgid "All Time" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#, python-format +msgid "All items can not be Imported" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#, python-format +msgid "Apply" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#, python-format +msgid "Archive" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#, python-format +msgid "Are you sure that you want to archive all the selected records?" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Are you sure you want to remove this item?" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_to_do_dashboard.js:0 +#, python-format +msgid "Are you sure you want to remove this task?" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_area_chart +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_chart_type__ks_area_chart +#, python-format +msgid "Area Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_sort_by_order__asc +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_sort_by_order__asc +msgid "Ascending" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Auto Update" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_auto_update_type +msgid "Auto Update Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_data_count_type__average +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_record_count_type_2__average +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_record_count_type__average +msgid "Average" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_background_color +msgid "Background Color" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_bar_chart +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_chart_type__ks_bar_chart +#, python-format +msgid "Bar Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_base +msgid "Base" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Below action will be performed at the end of the Drill Down Action" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.ks_dashboard_ninja_action +msgid "Cancel" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "" +"Cannot create target lines when Group By Date field is set to have " +"aggregation in Minute and Hour case." +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "" +"Cannot set aggregation having Date time (Hour, Minute) when target lines per" +" date are being used. To proceed this, first delete target lines" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "" +"Changing Layout midway will set the default icon colour and font colour for " +"selected layout." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_item_color +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Chart Color Palette" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_data +msgid "Chart Data in string form" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_show_live_pop_up +msgid "Checkbox to enable notification after every update. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_previous_period +msgid "" +"Checkbox to show comparison between the data of present day and the previous" +" selected period. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_icon_select +msgid "Choose the Icon option. " +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Clear" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_is_client_action +msgid "Client Action" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_client_action +msgid "Client Item Action" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_to_do_dashboard.js:0 +#: code:addons/ks_dashboard_ninja/static/src/js/ks_to_do_dashboard.js:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Close" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_data_format__colombian +msgid "Colombian Peso Format" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Color Palette" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Coming Soon in Future :)" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_company_id +msgid "Company" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.ui.menu,name:ks_dashboard_ninja.configuration_menu +msgid "Configuration" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_item_color__cool +msgid "Cool" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_data_count_type__count +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_record_count_type_2__count +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_record_count_type__count +msgid "Count" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Count..." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__create_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__create_uid +msgid "Created by" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__create_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__create_date +msgid "Created on" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_cumulative +msgid "Cumulative As Line" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_cumulative_field +msgid "Cumulative Field" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#, python-format +msgid "" +"Current Json File is not properly formatted according to Dashboard Ninja " +"Model." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board_template__ks_template_type__ks_custom +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_unit_selection__custom +msgid "Custom" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__l_custom +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__l_custom +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__l_custom +#, python-format +msgid "Custom Filter" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_form +msgid "Custom Filters" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_data_calculation_type__query +msgid "Custom Query" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Customize Dashboard" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "Customize Item" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__ks_dashboard_board_id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__ks_dashboard_board_id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__ks_dashboard_board_id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_dashboard_ninja_board_id +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.child_board_tree +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item +msgid "Dashboard" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_custom_filters_ids +msgid "Dashboard Custom Filters" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_defined_filters +msgid "Dashboard Defined Filter" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_dashboard_item_id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__ks_dashboard_item +msgid "Dashboard Item" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.ks_dashboard_ninja_action +msgid "Dashboard Item Action" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_date_groupby +msgid "Dashboard Item Chart Group By Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_date_sub_groupby +msgid "Dashboard Item Chart Sub Group By Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_dashboard_item_type +msgid "Dashboard Item Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#: model:ir.actions.act_window,name:ks_dashboard_ninja.item_action_window +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_items_ids +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__ks_dashboard_item_ids +#, python-format +msgid "Dashboard Items" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.child_board_tree +msgid "Dashboard Layout" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.ui.menu,name:ks_dashboard_ninja.dashboard_layout_menu +msgid "Dashboard Layouts" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.actions.act_window,name:ks_dashboard_ninja.board_form_tree_action_window +msgid "Dashboard Manager" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__name +msgid "Dashboard Name" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_dashboard_ninja_board +msgid "Dashboard Ninja" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_dashboard_ninja_child_board +msgid "Dashboard Ninja Child Board" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_dashboard_ninja_board_custom_filters +msgid "Dashboard Ninja Custom Filters" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_dashboard_ninja_board_defined_filters +msgid "Dashboard Ninja Defined Filters" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_ninja_dashboard_item_action +msgid "Dashboard Ninja Item Actions" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_dashboard_ninja_item_action +msgid "Dashboard Ninja Items Action Lines" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_dashboard_ninja_item_goal +msgid "Dashboard Ninja Items Goal Lines" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.module.category,name:ks_dashboard_ninja.ks_dashboard_ninja_security_groups +msgid "Dashboard Ninja Rights" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_dashboard_ninja_board_template +msgid "Dashboard Ninja Template" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_dashboard_ninja_item +msgid "Dashboard Ninja items" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_defined_filters_ids +msgid "Dashboard Predefined Filters" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.actions.act_window,name:ks_dashboard_ninja.template_tree_action_window +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_default_template +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_dashboard_board_template_id +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_template_form +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_template_tree +msgid "Dashboard Template" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.actions.act_window,name:ks_dashboard_ninja.layout_tree_action_window +msgid "Dashboard layout" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.ui.menu,name:ks_dashboard_ninja.dashboard_menu +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_form +msgid "Dashboards" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Data" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Data #2" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Data Calculation" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_data_calculation_type +msgid "Data Calculation Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_kpi_type__layout_2 +msgid "Data Comparison" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_measure_field_2 +msgid "Data Points displayed with a line in the graph. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_data_count_type +msgid "Data Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_cumulative_field +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_measure_field +msgid "Data points to be selected." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__ks_model_id +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__ks_model_id +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_model_id +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_model_id +msgid "" +"Data source to fetch and read the data for the creation of dashboard items. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__ks_goal_date +msgid "Date" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Date Filter" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_date_filter_field +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Date Filter Field" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_date_filter_selection +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Date Filter Selection" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_groupby__day +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_sub_groupby__day +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_item_action_date_groupby__day +msgid "Day" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_item_color__default +msgid "Default" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#, python-format +msgid "Default Dashboard can't be deleted." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_date_filter_selection +msgid "Default Date Filter" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_widget_toggle.xml:0 +#, python-format +msgid "Default Icons" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_data_calculation_type__custom +msgid "Default Query" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_set_interval +msgid "Default Update Interval" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_domain_extension +msgid "Define conditions for filter to write manually" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__ks_domain +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_domain +msgid "Define conditions for filter. " +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#, python-format +msgid "Delete" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_sort_by_order__desc +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_sort_by_order__desc +msgid "Descending" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__ks_description +msgid "Description" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Deviation Field" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_quick_edit_view.xml:0 +#, python-format +msgid "Discard" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Discard Changes" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Display" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__display_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__display_name +msgid "Display Name" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__display_type +msgid "Display Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_unit +msgid "Display the unit of the data." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__ks_domain +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_domain +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Domain" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_domain_extension +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Domain Extension" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__ks_domain_field_id +msgid "Domain Field" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__ks_domain_temp +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_domain_temp +msgid "Domain Substitute" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_filters.py:0 +#, python-format +msgid "Domain can not be empty" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_doughnut_chart +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_chart_type__ks_doughnut_chart +#, python-format +msgid "Doughnut Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Drill Up" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: model:ir.actions.server,name:ks_dashboard_ninja.ks_duplicate_dashboard +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_ninja_dashboard_item_action__ks_action__duplicate +#, python-format +msgid "Duplicate" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Edit Layout" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_to_do_dashboard.js:0 +#, python-format +msgid "Edit Task" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_goal_enable +msgid "Enable Target" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_end_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_item_end_date +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "End Date" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_data_format__global +msgid "English Format" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_unit +msgid "Enter Unit" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_data_formatting__exact +msgid "Exact" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_data_format__exact +msgid "Exact Value" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "Export" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_export_all_records +msgid "Export All Records" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Export Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#, python-format +msgid "Export Dashboard" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "Export Item" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "Export List" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Export to CSV" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Export to Excel" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Fields Required : Name, Model, Icon (Default or Custom Upload), Layout" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_list_view_fields +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Fields to show in list" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_fill_temporal +msgid "Fill Temporal Value" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +#, python-format +msgid "Filter" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__name +msgid "Filter Label" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Font Awesome 4.7.0" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_font_color +msgid "Font Color" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_data_formatting +msgid "Format" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_quick_edit_view.xml:0 +#, python-format +msgid "Full Settings" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__n_future_starting_now +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__n_future_starting_now +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__n_future_starting_now +#, python-format +msgid "Future Starting Now" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__n_futurestarting_tomorrow +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__n_futurestarting_tomorrow +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__n_futurestarting_tomorrow +#, python-format +msgid "Future Starting Tomorrow" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__sequence +msgid "" +"Gives the sequence order when displaying a list of payment terms lines." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_data_formatting__global +msgid "Global" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Goal Lines" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_graph_preview +msgid "Graph Preview" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_group_access +msgid "Group Access" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_relation_groupby +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Group By" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_item_action_date_groupby +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Group By Date" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "Groupby Field aggregation" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "Groupby field: {} cannot be aggregated by {}" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_list_view_type__grouped +msgid "Grouped" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Groups/Dimensions" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__ks_to_do_header +msgid "Header" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_header_bg_color +msgid "Header Background Color" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_hide_legend +msgid "Hide all legend from the chart item" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_horizontalbar_chart +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_chart_type__ks_horizontalbar_chart +#, python-format +msgid "Horizontal Bar Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_groupby__hour +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_sub_groupby__hour +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_item_action_date_groupby__hour +msgid "Hour" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__id +msgid "ID" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_default_icon +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Icon" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_default_icon_color +msgid "Icon Color" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_icon_select +msgid "Icon Option" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_import_list_view_template.xml:0 +#, python-format +msgid "Import Dashboard" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Import Item" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_compare_period +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Include Period" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_data_formatting__indian +msgid "Indian" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_data_format__indian +msgid "Indian Format" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Invalid Date" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__ks_active +msgid "Is Selected" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Item Action" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_gridstack_config +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__ks_gridstack_config +msgid "Item Configurations" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Item Duplicated" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Item Moved" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_chart_type +msgid "Item Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_update_items_data +msgid "Item Update Interval" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Item Update Interval.." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_kpi +#, python-format +msgid "KPI" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_kpi_data +msgid "KPI Data" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_domain_extension_2 +msgid "KPI Domain Extension" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_compare_period_2 +msgid "KPI Include Period" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_count_2 +msgid "KPI Record Count" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_year_period_2 +msgid "KPI Same Period Previous Years" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_kpi_type__layout_1 +msgid "KPI With Target" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_data_comparison +msgid "Kpi Data Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_date_filter_field_2 +msgid "Kpi Date Filter Field" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_date_filter_selection_2 +msgid "Kpi Date Filter Selection" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_domain_2 +msgid "Kpi Domain" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_domain_2_temp +msgid "Kpi Domain Substitute" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_item_end_date_2 +msgid "Kpi End Date" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_kpi_type +msgid "Kpi Layout" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_model_id_2 +msgid "Kpi Model" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_model_name_2 +msgid "Kpi Model Name" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_kpi_preview +msgid "Kpi Preview" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_field_2 +msgid "Kpi Record Field" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_count_type_2 +msgid "Kpi Record Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_item_start_date_2 +msgid "Kpi Start Date" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_groupby_type +msgid "Ks Chart Groupby Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_sub_groupby_type +msgid "Ks Chart Sub Groupby Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_child_dashboard_ids +msgid "Ks Child Dashboard" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_client_action_id +msgid "Ks Dashboard Client Action" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_menu_id +msgid "Ks Dashboard Menu" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_state +msgid "Ks Dashboard State" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_dn_header_lines +msgid "Ks Dn Header Lines" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__ks_dn_item_id +msgid "Ks Dn Item" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__ks_gridstack_config +msgid "Ks Gridstack Config" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_isDateFilterApplied +msgid "Ks Isdatefilterapplied" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_item_action_field_type +msgid "Ks Item Action Field Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__ks_item_count +msgid "Ks Item Count" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_many2many_field_ordering +msgid "Ks Many2Many Field Ordering" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__ks_to_do_description_lines +msgid "Ks To Do Description Lines" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__ks_to_do_header_id +msgid "Ks To Do Header" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__l_month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__l_month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__l_month +#, python-format +msgid "Last 30 days" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__l_year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__l_year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__l_year +#, python-format +msgid "Last 365 days" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__l_week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__l_week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__l_week +#, python-format +msgid "Last 7 days" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__l_quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__l_quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__l_quarter +#, python-format +msgid "Last 90 days" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__ls_day +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__ls_day +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__ls_day +#, python-format +msgid "Last Day" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description____last_update +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers____last_update +msgid "Last Modified on" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__ls_month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__ls_month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__ls_month +#, python-format +msgid "Last Month" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__ls_quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__ls_quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__ls_quarter +#, python-format +msgid "Last Quarter" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__write_uid +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_description__write_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_to_do_headers__write_date +msgid "Last Updated on" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__ls_week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__ls_week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__ls_week +#, python-format +msgid "Last Week" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__ls_year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__ls_year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__ls_year +#, python-format +msgid "Last Year" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_layout +msgid "Layout" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_layout__layout1 +msgid "Layout 1" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_layout__layout2 +msgid "Layout 2" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_layout__layout3 +msgid "Layout 3" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_layout__layout4 +msgid "Layout 4" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_layout__layout5 +msgid "Layout 5" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_layout__layout6 +msgid "Layout 6" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Layout Coming Soon" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Layout..." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_line_chart +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_chart_type__ks_line_chart +#, python-format +msgid "Line Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_measure_field_2 +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Line Measure" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_list_view +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_chart_type__ks_list_view +#, python-format +msgid "List View" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_list_view_data +msgid "List View Data in JSon" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_list_view_group_fields +msgid "List View Grouped Fields" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_list_view_preview +msgid "List View Preview" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_list_view_type +msgid "List View Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_unit +msgid "Maximum limit 5 characters, for ex: km, m" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_measure_field +msgid "Measure 1" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Measures" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_menu_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__ks_dashboard_menu_name +msgid "Menu Name" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_menu_sequence +msgid "Menu Sequence" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_groupby__minute +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_sub_groupby__minute +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_item_action_date_groupby__minute +msgid "Minute" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_custom_filters__ks_model_id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__ks_model_id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_model_id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_model_id +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Model" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__ks_model_name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_model_name +msgid "Model Name" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Model..." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_unit_selection__monetary +msgid "Monetary" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_groupby__month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_sub_groupby__month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_item_action_date_groupby__month +msgid "Month" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_groupby__month_year +msgid "Month-Year" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "More Info" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: model:ir.actions.server,name:ks_dashboard_ninja.ks_move_dashboard +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_ninja_dashboard_item_action__ks_action__move +#, python-format +msgid "Move" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "Move/Duplicate" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.actions.client,name:ks_dashboard_ninja.board_dashboard_action_window +#: model:ir.ui.menu,name:ks_dashboard_ninja.board_menu_root +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_tree +msgid "My Dashboard" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__name +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__name +msgid "Name" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_company_id +msgid "" +"Name of the company for which analytics will be displayed in the dashboard. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Name..." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_item_color__neon +msgid "Neon" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_to_do_dashboard.js:0 +#, python-format +msgid "New Task" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Next" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__n_day +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__n_day +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__n_day +#, python-format +msgid "Next Day" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__n_month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__n_month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__n_month +#, python-format +msgid "Next Month" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__n_quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__n_quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__n_quarter +#, python-format +msgid "Next Quarter" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__n_week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__n_week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__n_week +#, python-format +msgid "Next Week" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__n_year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__n_year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__n_year +#, python-format +msgid "Next Year" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "No Data Present" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "No Section Available." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "No Tasks Available" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_widget_toggle.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__l_none +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__l_none +#, python-format +msgid "None" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Note :" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Note:" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_widget_toggle.xml:0 +#, python-format +msgid "Number" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_data_format +msgid "Number System" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_pagination_limit +msgid "Pagination Limit" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "Pagination limit value cannot be Negative or Zero" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Past Excluding Today" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__ls_past_until_now +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__ls_past_until_now +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__ls_past_until_now +#, python-format +msgid "Past Till Now" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_widget_toggle.xml:0 +#, python-format +msgid "Percentage" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_pie_chart +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_chart_type__ks_pie_chart +#, python-format +msgid "Pie Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#, python-format +msgid "" +"Please Install the Module which contains the following Model : %s " +"ks_model_id" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "Please chose any Data Type!" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Please enter start date and end date" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/lib/ks_date_filter_selections.py:0 +#: code:addons/ks_dashboard_ninja/lib/ks_date_filter_selections.py:0 +#, python-format +msgid "Please set the local timezone." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "" +"Please use Font Awesome 4.7.0 icons only. E.g. 'fa-bell' or 'bell'.\n" +" For more information visit" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_polararea_chart +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_chart_type__ks_polararea_chart +#, python-format +msgid "Polar Area Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_form +msgid "Pre Defined Filters" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board_template__ks_template_type__ks_default +msgid "Predefined" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_preview +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Preview" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Previous" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Print" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_widget_toggle.xml:0 +#, python-format +msgid "Progress Bar" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_groupby__quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_sub_groupby__quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_item_action_date_groupby__quarter +msgid "Quarter" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "Quick Customize" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_widget_toggle.xml:0 +#, python-format +msgid "Ratio" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_count +msgid "Record Count" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_field +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Record Field" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Record Field..." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_data_limit +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_record_limit +msgid "Record Limit" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_data_limit_visibility +msgid "Record Limit Data Visibility" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_count_type +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Record Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_quick_edit_form_view +msgid "Record Value" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_actions +msgid "Redirects you to the selected view. " +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#, python-format +msgid "Remove" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#, python-format +msgid "Remove Item" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_year_period +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Same Period Previous Years" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_quick_edit_view.xml:0 +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.ks_dashboard_ninja_action +#, python-format +msgid "Save" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Save Changes" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Save Changes as a New Layout" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Save as Image" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Save as New Layout" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#, python-format +msgid "Save as PDF" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.ks_item_search_view +msgid "Search Items" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Search fa-icon.." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Search through site content" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board_defined_filters__display_type__line_section +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Section" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Sections" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Select" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_pro.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_to_do_template.xml:0 +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_child_board__ks_dashboard_ninja_id +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__ks_dashboard_ninja_id +#, python-format +msgid "Select Dashboard" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_ninja_dashboard_item_action__ks_dashboard_ninja_ids +msgid "Select Dashboards" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Select Icon" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Select Icons" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_unit_selection +msgid "Select Unit Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_date_filter_selection +msgid "Select interval of the records to be displayed. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_dashboard_item_theme +msgid "Select the color theme for the display. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_list_view_type +msgid "Select the desired list view type. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_sort_by_field +msgid "Select the desired sorting preference. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_item_color +msgid "Select the display preference. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_date_filter_field +msgid "Select the field for which Date Filter should be applicable." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_font_color +msgid "Select the font color. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_default_icon +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_default_icon_color +msgid "Select the icon to be displayed. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_dashboard_item_type +msgid "Select the required type of dashboard to display. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_chart_relation_sub_groupby +msgid "Select the second level of grouping. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_data_calculation_type +msgid "Select the type of calculation you want to perform on the data." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_unit_selection +msgid "Select the unit to be assigned to the value. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_auto_update_type +msgid "Select the update type." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Selected item is duplicated to ." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Selected item is moved to ." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_domain_fix.js:0 +#, python-format +msgid "Selected records" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_semi_circle_chart +msgid "Semi Circle Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_defined_filters +msgid "Separator Name (eg. Order States, Deadlines)" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__sequence +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__sequence +msgid "Sequence" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Set Current Layout" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Set Update Interval" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_unit +msgid "Show Custom Unit" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_show_data_value +msgid "Show Data Value" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:res.groups,name:ks_dashboard_ninja.ks_dashboard_ninja_group_manager +msgid "Show Full Dashboard Features" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_hide_legend +msgid "Show Legend" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_show_live_pop_up +msgid "Show Live Update Pop Up" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_show_records +msgid "Show Records" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_goal_bar_line +msgid "Show Target As Line" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_top_menu_id +msgid "Show Under Menu" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_standard_goal_value +msgid "Show the set target" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_goal_enable +msgid "Show the set target." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_menu_sequence +msgid "" +"Smallest sequence give high priority and Highest sequence give low priority" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#, python-format +msgid "Some Items can not be imported Need Dashboard Ninja pro " +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_filters.py:0 +#, python-format +msgid "" +"Something went wrong . Possibly it is due to wrong input type for domain" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_sort_by_field +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_sort_by_field +msgid "Sort By Field" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_sort_by_order +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_action__ks_sort_by_order +msgid "Sort Order" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_bar_chart_stacked +msgid "Stack the columns of the same record. " +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_bar_chart_stacked +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Stacked Bar Chart" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_standard_goal_value +msgid "Standard Target" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_dashboard_start_date +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_item_start_date +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Start Date" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#, python-format +msgid "Start date must be less than end date" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#, python-format +msgid "Start date should be less than end date" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Sub Group By Date" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "Sub Groupby field: {} cannot be aggregated by {}" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_widget_toggle.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_data_count_type__sum +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_record_count_type_2__sum +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_record_count_type__sum +#, python-format +msgid "Sum" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Target" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_goal_lines +msgid "Target Lines" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Task Lines" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Tasks" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_board_defined_filters__display_type +msgid "Technical field for UX purpose." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__ks_template_type +msgid "Template Format" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_board_template__ks_dashboard_item_ids +msgid "Template Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__name +msgid "The item will be represented by this unique name." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_dashboard_item_theme +msgid "Theme" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_client_action +msgid "This Action will be Performed at the end of Drill Down Action" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__t_month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__t_month +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__t_month +#, python-format +msgid "This Month" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__t_quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__t_quarter +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__t_quarter +#, python-format +msgid "This Quarter" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__t_week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__t_week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__t_week +#, python-format +msgid "This Week" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__t_year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__t_year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__t_year +#, python-format +msgid "This Year" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_show_records +msgid "" +"This field Enable the click on \n" +" Dashboard Items to view the Odoo \n" +" default view of records" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja.py:0 +#, python-format +msgid "This file is not supported" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_tile +#, python-format +msgid "Tile" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_data_format +msgid "To Change the number format showing in chart to given option" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_dashboard_item_type__ks_to_do +#, python-format +msgid "To Do" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_to_do_data +msgid "To Do Data in JSon" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_to_do_preview +msgid "To Do Preview" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "To add dashboard item, use" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_data_limit_visibility +msgid "To enable the record data limit field" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja.js:0 +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_board__ks_date_filter_selection__l_day +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection_2__l_day +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_date_filter_selection__l_day +#, python-format +msgid "Today" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_button_color +msgid "Top Button Color" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.item_form_view +msgid "Type" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_record_count_type +msgid "" +"Type of record how record will show as count,sum and average of the record" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_list_view_type__ungrouped +msgid "Un-Grouped" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#: code:addons/ks_dashboard_ninja/static/src/js/ks_import_dashboard.js:0 +#, python-format +msgid "Unarchive" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_board__ks_set_interval +msgid "Update Interval for new items only" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_auto_update_type__ks_live_update +msgid "Update at every instance." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_widget_toggle.xml:0 +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_icon +#, python-format +msgid "Upload Icon" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#, python-format +msgid "Uploading..." +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item_goal__ks_goal_value +msgid "Value" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_target_view +msgid "View" +msgstr "" + +#. module: ks_dashboard_ninja +#: model_terms:ir.ui.view,arch_db:ks_dashboard_ninja.board_tree +msgid "View Items" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_item_color__warm +msgid "Warm" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_groupby__week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_sub_groupby__week +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_item_action_date_groupby__week +msgid "Week" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_groupby__year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item__ks_chart_date_sub_groupby__year +#: model:ir.model.fields.selection,name:ks_dashboard_ninja.selection__ks_dashboard_ninja_item_action__ks_item_action_date_groupby__year +msgid "Year" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "Your personal dashboard is empty" +msgstr "" + +#. module: ks_dashboard_ninja +#: code:addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py:0 +#, python-format +msgid "if target lines is selected then cannot be set pagination value" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,field_description:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_list_target_deviation_field +msgid "list_field_id" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "on top right corner." +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dn_global_filter.xml:0 +#, python-format +msgid "or" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_to_do_description +msgid "to do description" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model,name:ks_dashboard_ninja.model_ks_to_do_headers +msgid "to do headers" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "vs Prev" +msgstr "" + +#. module: ks_dashboard_ninja +#. openerp-web +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_item_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#: code:addons/ks_dashboard_ninja/static/src/xml/ks_dashboard_ninja_templates.xml:0 +#, python-format +msgid "vs Target" +msgstr "" + +#. module: ks_dashboard_ninja +#: model:ir.model.fields,help:ks_dashboard_ninja.field_ks_dashboard_ninja_item__ks_export_all_records +msgid "" +"when click on boolean button, all the records will be downloaded which are " +"present in entire list" +msgstr "" diff --git a/addons/ks_dashboard_ninja/models/Kpi_mail.py b/addons/ks_dashboard_ninja/models/Kpi_mail.py new file mode 100644 index 0000000..d296b9d --- /dev/null +++ b/addons/ks_dashboard_ninja/models/Kpi_mail.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields + + +class KpSendMail(models.Model): + _name = 'ks_dashboard_ninja.kpi_mail' + _description = 'Dashboard Ninja Kpi mail' + + + name = fields.Char(string="Email To:") diff --git a/addons/ks_dashboard_ninja/models/__init__.py b/addons/ks_dashboard_ninja/models/__init__.py new file mode 100644 index 0000000..679c939 --- /dev/null +++ b/addons/ks_dashboard_ninja/models/__init__.py @@ -0,0 +1,17 @@ +from . import ks_dashboard_ninja +from . import ks_dashboard_ninja_items +from . import ks_item_action +from . import ks_child_dashboard +from . import ks_dashboard_filters +from . import ks_dashboard_templates +from . import ks_dn_to_do_item +from . import ks_import_dashboard +from . import Kpi_mail +from . import res_settings +from . import ks_ai_ninja_dashboard +from . import ks_ai_whole_dashboard +from . import ks_key_fetch +from . import ks_chat_channel +from . import base_model_extend + + diff --git a/addons/ks_dashboard_ninja/models/base_model_extend.py b/addons/ks_dashboard_ninja/models/base_model_extend.py new file mode 100644 index 0000000..d6e5780 --- /dev/null +++ b/addons/ks_dashboard_ninja/models/base_model_extend.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from odoo import models, api + + +class BaseExtend(models.AbstractModel): + _inherit = 'base' + + @api.model_create_multi + def create(self, vals): + recs = super(BaseExtend, self).create(vals) + if 'ir.' not in self._name and 'bus.' not in self._name and self.env.user.has_group('base.group_user'): + # items = self.env['ks_dashboard_ninja.item'].search( + # [['ks_model_id.model', '=', self._name]]) + # if items: + # online_partners = self.env["bus.presence"].sudo().search([('status', '=', 'online')]).mapped('user_id.partner_id').ids + # updates = [ for partner_id in online_partners] + self.env['bus.bus']._sendone('ks_notification', 'Update: Dashboard Items', {'model': self._name}) + return recs + + def write(self, vals): + recs = super(BaseExtend, self).write(vals) + if 'ir.' not in self._name and 'bus.' not in self._name and self.env.user.has_group('base.group_user') and 'res.partner' not in self._name: + # items = self.env['ks_dashboard_ninja.item'].search( + # [['ks_model_id.model', '=', self._name]]) + # if items: + # online_partner = self.env["bus.presence"].search([('status', '=', 'online')]).mapped('user_id.partner_id').ids + # updates = [[ + # (self._cr.dbname, 'res.partner', partner_id), + # {'type': 'ks_notification', 'model': self._name}, + # {'id': self.id} + # ] for partner_id in online_partner] + self.env['bus.bus']._sendone('ks_notification', 'Update: Dashboard Items', {'model': self._name}) + return recs \ No newline at end of file diff --git a/addons/ks_dashboard_ninja/models/ks_ai_ninja_dashboard.py b/addons/ks_dashboard_ninja/models/ks_ai_ninja_dashboard.py new file mode 100644 index 0000000..9d78983 --- /dev/null +++ b/addons/ks_dashboard_ninja/models/ks_ai_ninja_dashboard.py @@ -0,0 +1,402 @@ +# -*- coding: utf-8 -*- + +import base64 +import io +import json +import logging +from urllib.parse import quote + +import pandas as pd +import requests +from gtts import gTTS +from odoo.exceptions import ValidationError +from odoo.tools import config + +from odoo import api, fields, models, _ + +_logger = logging.getLogger(__name__) + + +class KsDashboardNInjaAI(models.TransientModel): + _name = 'ks_dashboard_ninja.arti_int' + _description = 'AI Dashboard' + + ks_type = fields.Selection([('ks_model', 'Model'), ('ks_keyword', 'Keywords')], + string="Ks AI Type", default='ks_model') + + ks_import_model_id = fields.Many2one('ir.model', string='Model ID', + domain="[('access_ids','!=',False),('transient','=',False)," + "('model','not ilike','base_import%'),'|',('model','not ilike','ir.%'),('model','=ilike','_%ir.%')," + "('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%')," + "('model','!=','mail.thread'),('model','not ilike','ks_dash%'),('model','not ilike','ks_to%')]", + help="Data source to fetch and read the data for the creation of dashboard items. ") + + ks_import_model = fields.Many2one('ir.model', string='Model', + domain="[('access_ids','!=',False),('transient','=',False)," + "('model','not ilike','base_import%'),('model','not ilike','ir.%')," + "('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%')," + "('model','!=','mail.thread'),('model','not ilike','ks_dash%'),('model','not ilike','ks_to%')]", + help="Data source to fetch and read the data for the creation of dashboard items. ") + ks_input_keywords = fields.Char("Ks Keywords") + ks_model_show = fields.Boolean(default = False, compute='_compute_show_model') + + @api.onchange('ks_input_keywords') + def _compute_show_model(self): + if self.ks_input_keywords and self.ks_type=="ks_keyword": + api_key = self.env['ir.config_parameter'].sudo().get_param('ks_dashboard_ninja.dn_api_key') + url = self.env['ir.config_parameter'].sudo().get_param('ks_dashboard_ninja.url') + if api_key and url: + json_data = {'name': api_key, + 'type': self.ks_type, + 'keyword': self.ks_input_keywords + } + url = url + "/api/v1/ks_dn_keyword_gen" + ks_response = requests.post(url, data=json_data) + if json.loads(ks_response.text) == False: + self.ks_model_show = True + else: + self.ks_model_show = False + else: + self.ks_model_show = False + else: + self.ks_model_show = False + + @api.model + def ks_get_keywords(self): + url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + if url: + url = url + "/api/v1/ks_dn_get_keyword" + ks_response = requests.post(url) + if ks_response.status_code == 200: + return json.loads(ks_response.text) + else: + return [] + + + def ks_do_action(self): + headers = {"Content-Type": "application/json", + "Accept": "application/json", + "Catch-Control": "no-cache", + } + + if self.ks_import_model_id: + ks_model_name = self.ks_import_model_id.model + ks_fields = self.env[ks_model_name].fields_get() + ks_filtered_fields = {key: val for key, val in ks_fields.items() if val['type'] not in ['many2many', 'one2many', 'binary'] and'name' in val and val['name'] != 'id' and val['name'] != 'sequence' and val['store'] == True} + ks_fields_name = {val['name']:val['type'] for val in ks_filtered_fields.values()} + question = ("columns: "+ f"{ks_fields_name}") + + api_key = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.dn_api_key') + url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + if api_key and url: + json_data = {'name': api_key, + 'question':question, + 'type': self.ks_type, + 'url': self.env['ir.config_parameter'].sudo().get_param('web.base.url'), + 'db_name': self.env.cr.dbname + } + url = url+"/api/v1/ks_dn_main_api" + ks_ai_response = requests.post(url, data=json_data) + if ks_ai_response.status_code == 200: + ks_ai_response = json.loads(ks_ai_response.text) + # create dummy dash to create items on the dashboard, later deleted it. + ks_create_record = self.env['ks_dashboard_ninja.board'].create({ + 'name': 'AI dashboard', + 'ks_dashboard_menu_name': 'AI menu', + 'ks_dashboard_default_template': self.env.ref('ks_dashboard_ninja.ks_blank', False).id, + 'ks_dashboard_top_menu_id': self.env['ir.ui.menu'].search([('name', '=', 'My Dashboards')])[0].id, + }) + ks_dash_id = ks_create_record.id + + ks_result = self.env['ks_dashboard_ninja.item'].create_ai_dash(ks_ai_response, ks_dash_id, + ks_model_name) + context = {'ks_dash_id': self._context['ks_dashboard_id'], + 'ks_dash_name': self.env['ks_dashboard_ninja.board'].search([ + ('id','=',self._context['ks_dashboard_id'])]).name,'ks_delete_dash_id':ks_dash_id } + + # return client action created through js for AI dashboard to render items on dummy dashboard + if (ks_result == "success"): + return { + 'type': 'ir.actions.client', + 'name': 'Generate items with AI', + 'params': {'ks_dashboard_id': ks_create_record.id, 'explain_ai_whole': True}, + 'tag': 'ks_ai_dashboard_ninja', + 'context': context, + 'target':'new' + } + else: + self.env['ks_dashboard_ninja.board'].browse(ks_dash_id).unlink() + raise ValidationError(_("Items didn't render because AI provides invalid response for this model.Please try again")) + else: + raise ValidationError(_("AI Responds with the following status:- %s") % ks_ai_response.text) + else: + raise ValidationError(_("Please enter URL and API Key in General Settings")) + else: + raise ValidationError(_("Please enter the Model")) + + + + def ks_generate_item(self): + if self.ks_input_keywords: + api_key = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.dn_api_key') + url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + if api_key and url: + json_data = {'name': api_key, + 'type': self.ks_type, + 'keyword':self.ks_input_keywords + } + url = url + "/api/v1/ks_dn_keyword_gen" + ks_response = requests.post(url, data=json_data) + else: + raise ValidationError(_("Please put API key and URL")) + if json.loads(ks_response.text) != False and ks_response.status_code==200 : + ks_ai_response = json.loads(ks_response.text) + ks_dash_id = self._context['ks_dashboard_id'] + ks_model_name = ks_ai_response[0]['model'] + ks_result = self.env['ks_dashboard_ninja.item'].create_ai_dash(ks_ai_response, ks_dash_id, + ks_model_name) + if ks_result == "success": + return{ + 'type': 'ir.actions.client', + 'tag': 'reload', + } + else: + raise ValidationError(_("Items didn't render, please try again!")) + else: + ks_model_name = self.ks_import_model.model + ks_fields = self.env[ks_model_name].fields_get() + ks_filtered_fields = {key: val for key, val in ks_fields.items() if + val['type'] not in ['many2many', 'one2many', 'binary'] and 'name' in val and val[ + 'name'] != 'id' and val['name'] != 'sequence' and val['store'] == True} + ks_fields_name = {val['name']: val['type'] for val in ks_filtered_fields.values()} + question = ("schema: " + f"{ks_fields_name}") + model =("model:" + f"{ks_model_name}") + api_key = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.dn_api_key') + url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + if api_key and url: + json_data = {'name': api_key, + 'question': self.ks_input_keywords, + 'type':self.ks_type, + 'schema':question, + 'model':model, + 'url': self.env['ir.config_parameter'].sudo().get_param('web.base.url'), + 'db_name': self.env.cr.dbname + } + url = url + "/api/v1/ks_dn_main_api" + ks_ai_response = requests.post(url, data=json_data) + if ks_ai_response.status_code == 200: + ks_ai_response = json.loads(ks_ai_response.text) + ks_dash_id = self._context['ks_dashboard_id'] + ks_model_name = (ks_ai_response[0]['model']).lower() + if self.env['ir.model'].search([('model','=',ks_model_name)]).id or self.env['ir.model'].search([('name','=',ks_model_name)]).id: + if self.env['ir.model'].search([('name','=',ks_model_name)]).id: + ks_model_name = self.env['ir.model'].search([('name','=',ks_model_name)]).model + else: + ks_model_name = (ks_ai_response[0]['model']).lower() + ks_result = self.env['ks_dashboard_ninja.item'].create_ai_dash(ks_ai_response, ks_dash_id,ks_model_name) + if ks_result == "success": + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } + else: + raise ValidationError(_("Items didn't render, please try again!")) + else: + raise ValidationError(_("%s model does not exist.Please install")% ks_model_name) + else: + raise ValidationError( + _("AI Responds with the following status:- %s") % ks_ai_response.text) + + else: + raise ValidationError(_("Please enter URL and API Key in General Settings")) + else: + raise ValidationError(_("Enter the input keywords to render the item")) + + @api.model + def ks_generate_analysis(self,ks_items_explain,ks_rest_items,dashboard_id): + if ks_items_explain: + result = [] + api_key = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.dn_api_key') + ks_url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + words = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.ks_analysis_word_length') + url = ks_url + "/api/v1/ks_dn_main_api" + for i in range(0,len(ks_items_explain)): + if api_key and url : + json_data = {'name': api_key, + 'items':json.dumps(ks_items_explain[i]), + 'type':'ks_ai_explain', + 'url': self.env['ir.config_parameter'].sudo().get_param('web.base.url'), + 'db_name': self.env.cr.dbname, + 'words': words if words else 100 + } + ks_response = requests.post(url, data=json_data) + if ks_response.status_code == 200 and json.loads(ks_response.text): + ks_ai_response = json.loads(ks_response.text) + item = ks_ai_response[0] + if item['analysis'] or item['insights']: + try: + self.env['ks_dashboard_ninja.item'].browse(item['id']).write({ + 'ks_ai_analysis': item['analysis']+'ks_gap'+item['insights'] + }) + result.append(True) + except: + result + else: + result + + else: + result + else: + raise ValidationError(_("Please put API key and URL")) + if len(result): #len(result) + if self.env.context.get('explain_items_with_ai', False): + self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({ + 'ks_ai_explain_dash': False + }) + else: + self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({ + 'ks_ai_explain_dash': True + }) + return True + else: + raise ValidationError(_("AI Responds with the wrong analysis. Please try again ")) + elif ks_rest_items: + if self.env.context.get('explain_items_with_ai', False): + self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({ + 'ks_ai_explain_dash': False + }) + else: + self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({ + 'ks_ai_explain_dash': True + }) + return True + else: + return False + + def get_ai_explain(self, item_id): + print(item_id) + res = self.env['ks_dashboard_ninja.item'].browse(item_id).ks_ai_analysis + return res + + @api.model + def ks_switch_default_dashboard(self,dashboard_id): + self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({ + 'ks_ai_explain_dash':False + }) + return True + @api.model + def ks_generatetext_to_speech(self,item_id): + if (item_id): + try: + ks_text = self.env['ks_dashboard_ninja.item'].browse(item_id).ks_ai_analysis + if ks_text: + language = 'en' + ks_myobj = gTTS(text=ks_text, lang=language, slow=False) + audio_data = io.BytesIO() + ks_myobj.write_to_fp(audio_data) + audio_data.seek(0) + binary_data = audio_data.read() + wav_file = base64.b64encode( binary_data).decode('UTF-8') + data = {"snd": wav_file} + return json.dumps(data) + else: + return False + except Exception as e: + _logger.error(e) + raise ValidationError(_("Some problem in audio generation.")) + + else: + return False + + @api.model + def ks_gen_chat_res(self,**kwargs): + ks_question = kwargs.get('ks_question') + url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + "/api/v1/get_sql_query" + data = { + "question": ks_question, + } + try: + ks_response = requests.post(url,data=data) + if (ks_response.status_code == 200): + ks_response = json.loads(ks_response.text)['response']['Query'] + return self.ks_gen_dataframe(ks_response,ks_question) + else: + _logger.error('Unexpected error occurs') + return False + except Exception as e: + _logger.error(e) + return False + + + + def ks_gen_dataframe(self,ks_query,question): + host = config.get('db_host', False) + user = quote(config.get('db_user', False)) + port = config.get('db_port', False) or 5432 + password = quote(config.get('db_password', False)) + db = config.get('db_name', False) or self.env.cr.dbname + if not all([host, user, port, password, db]): + _logger.error('some credentials are missing') + return False + else: + sql_uri = f"postgresql+psycopg2://{user}:{password}@{host}:{port}/{db}" + ks_fixed_url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + "/api/v1/get_fixed_query" + try: + df = pd.read_sql(ks_query, sql_uri) + except Exception as e: + ks_query_data = { + 'query':ks_query, + 'error':e + } + fixed_query = requests.post(ks_fixed_url, data=ks_query_data) + if fixed_query.status_code == 200: + ks_corrected_query = fixed_query.text + df = pd.read_sql(ks_corrected_query, sql_uri) + else: + _logger.error('Error in generating Dataframe') + return False + if any(df.dtypes == 'datetime64[ns]'): + datetime_columns = [col for col in df.columns if df[col].dtype == 'datetime64[ns]'] + df[datetime_columns] = df[datetime_columns].astype(str) + + # Convert DataFrame to JSON + if len(df) >= 100: + df = df.head(100) + partial_data = True + + df_json = df.to_json(orient='records') + + ans = "As dataframe having more data to analyse we are not showing dataframe summary" + # Generate answer + if len(df) < 13: + ks_ans_url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + "/api/v1/get_answer" + ks_ans_data = {'df':df.to_dict(orient='records'),'question':question} + ans = requests.post(ks_ans_url, json = ks_ans_data) + if ans.status_code == 200: + ans = ans.text + response_json = { + "Dataframe": df_json, + "Answer": ans, + } + else: + _logger.error('Error in generating answer') + return False + else: + response_json = { + "Dataframe": df_json, + "Answer": ans, + } + return response_json diff --git a/addons/ks_dashboard_ninja/models/ks_ai_whole_dashboard.py b/addons/ks_dashboard_ninja/models/ks_ai_whole_dashboard.py new file mode 100644 index 0000000..5ec2154 --- /dev/null +++ b/addons/ks_dashboard_ninja/models/ks_ai_whole_dashboard.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- + +import json +import logging + +import requests +from odoo.exceptions import ValidationError + +from odoo import fields, models, _ + +_logger = logging.getLogger(__name__) + + +class KsAIDashboardninja(models.TransientModel): + _name = 'ks_dashboard_ninja.ai_dashboard' + _description = 'AI Dashboard' + + ks_import_model_id = fields.Many2one('ir.model', string='Model', + domain="[('access_ids','!=',False),('transient','=',False)," + "('model','not ilike','base_import%'),('model','not ilike','ir.%')," + "('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%')," + "('model','!=','mail.thread'),('model','not ilike','ks_dash%'),('model','not ilike','ks_to%')]", + help="Data source to fetch and read the data for the creation of dashboard items. ", required=True) + + ks_dash_name = fields.Char(string="Dashboard Name", required=True, size=35) + ks_menu_name = fields.Char(string="Menu Name", required=True, size=35) + ks_top_menu_id = fields.Many2one('ir.ui.menu', + domain="[('parent_id','=',False)]", + string="Show Under Menu", required=True, + default=lambda self: self.env['ir.ui.menu'].search( + [('name', '=', 'My Dashboards')])[0]) + ks_template = fields.Many2one('ks_dashboard_ninja.board_template', + default=lambda self: self.env.ref('ks_dashboard_ninja.ks_blank', + False), + string="Dashboard Template") + + def ks_do_action(self): + headers = {"Content-Type": "application/json", + "Accept": "application/json", + "Catch-Control": "no-cache", + } + + if self.ks_import_model_id: + ks_model_name = self.ks_import_model_id.model + ks_fields = self.env[ks_model_name].fields_get() + ks_filtered_fields = {key: val for key, val in ks_fields.items() if val['type'] not in ['many2many', 'one2many', 'binary'] and'name' in val and val['name'] != 'id' and val['name'] != 'sequence' and val['store'] == True} + ks_fields_name = {val['name']:val['type'] for val in ks_filtered_fields.values()} + question = ("columns: "+ f"{ks_fields_name}") + + api_key = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.dn_api_key') + url = self.env['ir.config_parameter'].sudo().get_param( + 'ks_dashboard_ninja.url') + if api_key and url: + json_data = {'name': api_key, + 'question':question, + 'url':self.env['ir.config_parameter'].sudo().get_param('web.base.url'), + 'db_name':self.env.cr.dbname + } + url = url+"/api/v1/ks_dn_main_api" + ks_ai_response = requests.post(url, data=json_data) + if ks_ai_response.status_code == 200: + ks_ai_response = json.loads(ks_ai_response.text) + ks_create_record = self.env['ks_dashboard_ninja.board'].create({ + 'name': self.ks_dash_name, + 'ks_dashboard_menu_name': self.ks_menu_name, + 'ks_dashboard_default_template': self.ks_template.id, + 'ks_dashboard_top_menu_id': self.ks_top_menu_id.id, + }) + ks_dash_id = ks_create_record.id + + ks_result = self.env['ks_dashboard_ninja.item'].create_ai_dash(ks_ai_response, ks_dash_id, + ks_model_name) + + if (ks_result == "success"): + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } + else: + self.env['ks_dashboard_ninja.board'].browse(ks_dash_id).unlink() + raise ValidationError(_("Items didn't render, please try again!")) + else: + raise ValidationError(_("AI Responds with the following status:- %s") % ks_ai_response.text) + else: + raise ValidationError(_("Please enter URL and API Key in General Settings")) + else: + raise ValidationError(_("Please enter the Model")) + + + + diff --git a/addons/ks_dashboard_ninja/models/ks_chat_channel.py b/addons/ks_dashboard_ninja/models/ks_chat_channel.py new file mode 100644 index 0000000..564559b --- /dev/null +++ b/addons/ks_dashboard_ninja/models/ks_chat_channel.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +from markupsafe import Markup + +from odoo import models, fields, _ + + +class ChatChannel(models.Model): + _inherit = 'discuss.channel' + + ks_dashboard_board_id = fields.Many2one('ks_dashboard_ninja.board') + ks_dashboard_item_id = fields.Many2one('ks_dashboard_ninja.item') + + def ks_chat_wizard_channel_id(self, **kwargs): + item_id = kwargs.get('item_id') + dashboard_id = kwargs.get('dashboard_id') + item_name = kwargs.get('item_name') + dashboard_name = kwargs.get('dashboard_name') + + channel = self.search([('ks_dashboard_item_id', '=', item_id)], limit=1) + + if not channel: + users = self.env['res.users'].search([('groups_id', 'in', self.env.ref('base.group_user').ids)]).mapped('partner_id.id') + + channel = self.create({ + 'name': f"{dashboard_name} - {item_name}", + 'ks_dashboard_board_id': dashboard_id, + 'ks_dashboard_item_id': item_id, + 'channel_member_ids': [(0, 0, {'partner_id': partner_id}) for partner_id in users] + }) + + notification = Markup('+ You can find all items related to Dashboard Here.
+ '''), + + } + + def ks_export_item(self, item_id): + return { + 'ks_file_format': 'ks_dashboard_ninja_item_export', + 'item': self.ks_export_item_data(self.ks_dashboard_items_ids.browse(int(item_id))) + } + + # fetching Item info (Divided to make function inherit easily) + def ks_export_item_data(self, rec): + ks_timezone = self._context.get('tz') or self.env.user.tz + ks_chart_measure_field = [] + ks_chart_measure_field_2 = [] + if rec.ks_many2many_field_ordering: + ks_many2many_field_ordering = json.loads(rec.ks_many2many_field_ordering) + else: + ks_many2many_field_ordering = {} + if ks_many2many_field_ordering.get('ks_list_view_fields', False): + ks_list_view_fields_list = self.env['ir.model.fields'].search([('id', 'in', + ks_many2many_field_ordering.get('ks_list_view_fields', False))]) + if ks_many2many_field_ordering.get('ks_list_view_group_fields', False): + ks_list_view_group_fields_list = self.env['ir.model.fields'].search([('id', 'in', + ks_many2many_field_ordering.get('ks_list_view_group_fields', False))]) + if ks_many2many_field_ordering.get('ks_chart_measure_field', False): + ks_chart_measure_field_list = self.env['ir.model.fields'].search([('id', 'in', + ks_many2many_field_ordering.get('ks_chart_measure_field', False))]) + if ks_many2many_field_ordering.get('ks_chart_measure_field_2', False): + ks_chart_measure_field_2_list = self.env['ir.model.fields'].search([('id', 'in', + ks_many2many_field_ordering.get('ks_chart_measure_field_2', False))]) + + try: + for res in ks_chart_measure_field_list: + ks_chart_measure_field.append(res.name) + except Exception as E: + ks_chart_measure_field = [] + try: + for res in ks_chart_measure_field_2_list: + ks_chart_measure_field_2.append(res.name) + except Exception as E: + ks_chart_measure_field_2 = [] + ks_multiplier_fields = [] + ks_multiplier_value = [] + if rec.ks_multiplier_lines: + for ress in rec.ks_multiplier_lines.ks_multiplier_fields: + ks_multiplier_fields.append(ress.name) + for ks_val in rec.ks_multiplier_lines: + ks_multiplier_value.append(ks_val.ks_multiplier_value) + + ks_list_view_group_fields = [] + try: + for res in ks_list_view_group_fields_list: + ks_list_view_group_fields.append(res.name) + except Exception as e: + ks_list_view_group_fields = [] + ks_goal_lines = [] + for res in rec.ks_goal_lines: + goal_line = { + 'ks_goal_date': datetime.datetime.strftime(res.ks_goal_date, "%Y-%m-%d"), + 'ks_goal_value': res.ks_goal_value, + } + ks_goal_lines.append(goal_line) + ks_dn_header_lines = [] + for res in rec.ks_dn_header_lines: + ks_dn_header_line = { + 'ks_to_do_header': res.ks_to_do_header + } + + if res.ks_to_do_description_lines: + ks_to_do_description_lines = [] + for ks_description_line in res.ks_to_do_description_lines: + description_line = { + 'ks_description': ks_description_line.ks_description, + 'ks_active': ks_description_line.ks_active, + } + ks_to_do_description_lines.append(description_line) + ks_dn_header_line[res.ks_to_do_header] = ks_to_do_description_lines + ks_dn_header_lines.append(ks_dn_header_line) + + ks_action_lines = [] + for res in rec.ks_action_lines: + action_line = { + 'ks_item_action_field': res.ks_item_action_field.name, + 'ks_item_action_date_groupby': res.ks_item_action_date_groupby, + 'ks_chart_type': res.ks_chart_type, + 'ks_sort_by_field': res.ks_sort_by_field.name, + 'ks_sort_by_order': res.ks_sort_by_order, + 'ks_record_limit': res.ks_record_limit, + 'sequence': res.sequence, + 'ks_action_item_name': res.ks_action_item_name + } + ks_action_lines.append(action_line) + ks_multiplier_lines = [] + for res in rec.ks_multiplier_lines: + ks_multiplier_line = { + 'ks_multiplier_fields': res.ks_multiplier_fields.id, + 'ks_multiplier_value': res.ks_multiplier_value, + 'ks_dashboard_item_id': rec.id, + 'ks_model_id': rec.ks_model_id.id + } + ks_multiplier_lines.append(ks_multiplier_line) + + ks_list_view_field = [] + try: + for res in ks_list_view_fields_list: + ks_list_view_field.append(res.name) + except Exception as e: + ks_list_view_field = [] + val = str(rec.id) + keys_data = {} + selecred_rec = self.env['ks_dashboard_ninja.child_board'].search( + [['id', 'in', rec.ks_dashboard_ninja_board_id.ks_child_dashboard_ids.ids], ['ks_active', '=', True], + ['company_id', '=', self.env.company.id]], limit=1) + if rec.ks_dashboard_ninja_board_id.ks_gridstack_config: + keys_data = json.loads(rec.ks_dashboard_ninja_board_id.ks_gridstack_config) + elif selecred_rec and selecred_rec.ks_gridstack_config: + keys_data = json.loads(selecred_rec.ks_gridstack_config) + elif rec.ks_dashboard_ninja_board_id.ks_child_dashboard_ids[0].ks_gridstack_config: + keys_data = json.loads(rec.ks_dashboard_ninja_board_id.ks_child_dashboard_ids[0].ks_gridstack_config) + elif self._context.get('gridstack_config', False): + keys_data = self._context.get('gridstack_config', False) + else: + if rec.grid_corners: + keys_data = {rec.id: json.loads(rec.grid_corners.replace("\'", "\""))} + keys_list = keys_data.keys() + grid_corners = {} + if val in keys_list: + grid_corners = keys_data.get(str(val)) + + item = { + 'name': rec.name if rec.name else rec.ks_model_id.name if rec.ks_model_id else "Name", + 'ks_background_color': rec.ks_background_color, + 'ks_font_color': rec.ks_font_color, + 'ks_header_bg_color': rec.ks_header_bg_color, + 'ks_domain': rec.ks_domain, + 'ks_icon': str(rec.ks_icon) if rec.ks_icon else False, + 'ks_id': rec.id, + 'ks_model_id': rec.ks_model_name, + 'ks_record_count': rec.ks_record_count, + 'ks_layout': rec.ks_layout, + 'ks_icon_select': rec.ks_icon_select, + 'ks_default_icon': rec.ks_default_icon, + 'ks_default_icon_color': rec.ks_default_icon_color, + 'ks_record_count_type': rec.ks_record_count_type, + # Pro Fields + 'ks_dashboard_item_type': rec.ks_dashboard_item_type, + 'ks_chart_item_color': rec.ks_chart_item_color, + 'ks_chart_groupby_type': rec.ks_chart_groupby_type, + 'ks_chart_relation_groupby': rec.ks_chart_relation_groupby.name, + 'ks_chart_date_groupby': rec.ks_chart_date_groupby, + 'ks_record_field': rec.ks_record_field.name, + 'ks_chart_sub_groupby_type': rec.ks_chart_sub_groupby_type, + 'ks_chart_relation_sub_groupby': rec.ks_chart_relation_sub_groupby.name, + 'ks_chart_date_sub_groupby': rec.ks_chart_date_sub_groupby, + 'ks_chart_data_count_type': rec.ks_chart_data_count_type, + 'ks_chart_measure_field': ks_chart_measure_field, + 'ks_chart_measure_field_2': ks_chart_measure_field_2, + 'ks_list_view_fields': ks_list_view_field, + 'ks_list_view_group_fields': ks_list_view_group_fields, + 'ks_list_view_type': rec.ks_list_view_type, + 'ks_record_data_limit': rec.ks_record_data_limit, + 'ks_sort_by_order': rec.ks_sort_by_order, + 'ks_sort_by_field': rec.ks_sort_by_field.name, + 'ks_date_filter_field': rec.ks_date_filter_field.name, + 'ks_goal_enable': rec.ks_goal_enable, + 'ks_standard_goal_value': rec.ks_standard_goal_value, + 'ks_goal_liness': ks_goal_lines, + 'ks_date_filter_selection': rec.ks_date_filter_selection, + 'ks_item_start_date': rec.ks_item_start_date.strftime( + DEFAULT_SERVER_DATETIME_FORMAT) if rec.ks_item_start_date else False, + 'ks_item_end_date': rec.ks_item_end_date.strftime( + DEFAULT_SERVER_DATETIME_FORMAT) if rec.ks_item_end_date else False, + 'ks_date_filter_selection_2': rec.ks_date_filter_selection_2, + 'ks_item_start_date_2': rec.ks_item_start_date_2.strftime( + DEFAULT_SERVER_DATETIME_FORMAT) if rec.ks_item_start_date_2 else False, + 'ks_item_end_date_2': rec.ks_item_end_date_2.strftime( + DEFAULT_SERVER_DATETIME_FORMAT) if rec.ks_item_end_date_2 else False, + 'ks_previous_period': rec.ks_previous_period, + 'ks_target_view': rec.ks_target_view, + 'ks_data_comparison': rec.ks_data_comparison, + 'ks_record_count_type_2': rec.ks_record_count_type_2, + 'ks_record_field_2': rec.ks_record_field_2.name, + 'ks_model_id_2': rec.ks_model_id_2.model, + 'ks_date_filter_field_2': rec.ks_date_filter_field_2.name, + 'ks_action_liness': ks_action_lines, + 'ks_compare_period': rec.ks_compare_period, + 'ks_year_period': rec.ks_year_period, + 'ks_compare_period_2': rec.ks_compare_period_2, + 'ks_year_period_2': rec.ks_year_period_2, + 'ks_domain_2': rec.ks_domain_2, + 'ks_show_data_value': rec.ks_show_data_value, + 'ks_list_target_deviation_field': rec.ks_list_target_deviation_field.name, + 'ks_unit': rec.ks_unit, + 'ks_show_records': rec.ks_show_records, + 'ks_hide_legend': rec.ks_hide_legend, + 'ks_radial_legend': rec.ks_radial_legend, + 'ks_fill_temporal': rec.ks_fill_temporal, + 'ks_domain_extension': rec.ks_domain_extension, + 'ks_unit_selection': rec.ks_unit_selection, + 'ks_chart_unit': rec.ks_chart_unit, + 'ks_bar_chart_stacked': rec.ks_bar_chart_stacked, + 'ks_goal_bar_line': rec.ks_goal_bar_line, + 'ks_actions': rec.ks_actions.xml_id if rec.ks_actions else False, + 'ks_client_action': rec.ks_client_action.xml_id if rec.ks_client_action else False, + 'ks_is_client_action': rec.ks_is_client_action, + 'ks_export_all_records': rec.ks_export_all_records, + 'ks_record_data_limit_visibility': rec.ks_record_data_limit_visibility, + 'ks_data_format': rec.ks_data_format, + 'ks_pagination_limit': rec.ks_pagination_limit, + 'ks_chart_cumulative_field': rec.ks_chart_cumulative_field.ids, + 'ks_chart_cumulative': rec.ks_chart_cumulative, + 'ks_button_color': rec.ks_button_color, + 'ks_dn_header_line': ks_dn_header_lines, + 'ks_semi_circle_chart': rec.ks_semi_circle_chart, + 'ks_multiplier_active': rec.ks_multiplier_active, + 'ks_multiplier': rec.ks_multiplier, + 'ks_multiplier_lines': ks_multiplier_lines if ks_multiplier_lines else False, + 'ks_many2many_field_ordering': rec.ks_many2many_field_ordering, + 'ks_data_label_type': rec.ks_data_label_type, + 'ks_as_of_now': rec.ks_as_of_now, + 'ks_scatter_measure_x_id': rec.ks_chart_relation_groupby.name, + # 'ks_scatter_measure_y_id': ks_chart_measure_field, + 'ks_is_scatter_group': rec.ks_is_scatter_group, + 'ks_country_id': rec.ks_country_id.id, + 'ks_bounds': rec.ks_bounds, + 'ks_partners_map': rec.ks_partners_map, + + } + if grid_corners: + item.update({ + 'grid_corners': grid_corners, + }) + return item + + def ks_open_import(self, **kwargs): + action = self.env['ir.actions.act_window']._for_xml_id('ks_dashboard_ninja.ks_import_dashboard_action') + return action + + def ks_open_setting(self, **kwargs): + action = self.env['ir.actions.act_window']._for_xml_id('ks_dashboard_ninja.board_form_tree_action_window') + # action['res_id'] = self.id + # action['target'] = 'new' + # action['context'] = {'form_view_ref':'ks_dashboard_ninja.board_form'} + # action['view_mode']='form' + return action + + # def ks_delete_dashboard(self): + # if str(self.id) in self.ks_dashboard_default_template: + # raise ValidationError(_('You cannot delete any default template')) + # else: + # self.search([('id', '=', self.id)]).unlink() + # return { + # 'type': 'ir.actions.client', + # 'name': "My Dashboard", + # 'tag': 'dashboard_ninja', + # # 'id': {} + # } + def save_dashboard_image(self, *args, **kwargs): + self.ensure_one() + image = kwargs.get('image') + if image: + self.ks_dn_dashboard_image = image + + def ks_create_dashboard(self): + action = self.env['ir.actions.act_window']._for_xml_id('ks_dashboard_ninja.board_form_tree_action_window') + action['target'] = 'new' + return action + + def ks_import_item(self, dashboard_id, **kwargs): + try: + # ks_dashboard_data = json.loads(file) + file = kwargs.get('file', False) + ks_dashboard_file_read = json.loads(file) + except Exception: + raise ValidationError(_("This file is not supported")) + + if 'ks_file_format' in ks_dashboard_file_read and ks_dashboard_file_read[ + 'ks_file_format'] == 'ks_dashboard_ninja_item_export': + item = ks_dashboard_file_read['item'] + else: + raise ValidationError(_("Current Json File is not properly formatted according to Dashboard Ninja Model.")) + + item['ks_dashboard_ninja_board_id'] = int(dashboard_id) + item['ks_company_id'] = False + self.ks_create_item(item) + + return "Success" + + @api.model + def ks_dashboard_export(self, ks_dashboard_ids, **kwargs): + ks_dashboard_data = [] + ks_dashboard_export_data = {} + if kwargs.get('dashboard_id'): + ks_dashboard_ids = '['+str(ks_dashboard_ids)+']' + ks_dashboard_ids = json.loads(ks_dashboard_ids) + for ks_dashboard_id in ks_dashboard_ids: + dash = self.search([('id', '=', ks_dashboard_id)]) + selecred_rec = self.env['ks_dashboard_ninja.child_board'].search( + [['id', 'in', dash.ks_child_dashboard_ids.ids], ['ks_active', '=', True], + ['company_id', '=', self.env.company.id]], limit=1) + ks_dashboard_rec = self.browse(ks_dashboard_id) + if selecred_rec: + name = selecred_rec.name + grid_conf = selecred_rec.ks_gridstack_config + elif dash.ks_child_dashboard_ids: + name = dash.display_name + grid_conf = dash.ks_child_dashboard_ids[0].ks_gridstack_config + else: + name = dash.name + grid_conf = dash.ks_gridstack_config + dashboard_data = self.ks_prepare_export_data_vals(ks_dashboard_rec, grid_conf=grid_conf) + if selecred_rec: + dashboard_data['name'] = selecred_rec.name + dashboard_data['ks_gridstack_config'] = selecred_rec.ks_gridstack_config + elif len(ks_dashboard_rec.ks_child_dashboard_ids) > 1: + dashboard_data['name'] = ks_dashboard_rec.ks_child_dashboard_ids[0].name + dashboard_data['ks_gridstack_config'] = ks_dashboard_rec.ks_child_dashboard_ids[0].ks_gridstack_config + if dashboard_data['name'] == 'Default Board Layout': + dashboard_data['name'] = ks_dashboard_rec.ks_dashboard_menu_name + if len(ks_dashboard_rec.ks_dashboard_items_ids) < 1: + dashboard_data['ks_item_data'] = False + else: + items = [] + for rec in ks_dashboard_rec.ks_dashboard_items_ids: + item = self.ks_export_item_data(rec) + items.append(item) + + dashboard_data['ks_item_data'] = items + ks_dashboard_data.append(dashboard_data) + + ks_dashboard_export_data = { + 'ks_file_format': 'ks_dashboard_ninja_export_file', + 'ks_dashboard_data': ks_dashboard_data + } + return ks_dashboard_export_data + + def ks_prepare_export_data_vals(self, ks_dashboard_rec, grid_conf=None,): + dashboard_data = { + 'name': ks_dashboard_rec.name, + 'ks_dashboard_menu_name': ks_dashboard_rec.ks_dashboard_menu_name, + 'ks_gridstack_config': grid_conf if grid_conf else '{}', + 'ks_set_interval': ks_dashboard_rec.ks_set_interval, + 'ks_date_filter_selection': ks_dashboard_rec.ks_date_filter_selection, + 'ks_dashboard_start_date': ks_dashboard_rec.ks_dashboard_start_date, + 'ks_dashboard_end_date': ks_dashboard_rec.ks_dashboard_end_date, + 'ks_dashboard_top_menu_id': ks_dashboard_rec.ks_dashboard_top_menu_id.id, + 'ks_data_formatting': ks_dashboard_rec.ks_data_formatting, + } + return dashboard_data + + @api.model + def ks_import_dashboard(self, file, menu_id): + try: + # ks_dashboard_data = json.loads(file) + ks_dashboard_file_read = json.loads(file) + except Exception: + raise ValidationError(_("This file is not supported")) + + if 'ks_file_format' in ks_dashboard_file_read and ks_dashboard_file_read[ + 'ks_file_format'] == 'ks_dashboard_ninja_export_file': + ks_dashboard_data = ks_dashboard_file_read['ks_dashboard_data'] + for i in range(len(ks_dashboard_data)): + if 'ks_set_interval' in ks_dashboard_data[i].keys() and ks_dashboard_data[i].get('ks_item_data', False): + # del ks_dashboard_data[i]['ks_set_interval'] + for j in range(len(ks_dashboard_data[i].get('ks_item_data', False))): + if 'ks_update_items_data' in ks_dashboard_data[i].get('ks_item_data', False)[j].keys(): + del ks_dashboard_data[i].get('ks_item_data', False)[j]['ks_update_items_data'] + if 'ks_auto_update_type' in ks_dashboard_data[i].get('ks_item_data', False)[j].keys(): + del ks_dashboard_data[i].get('ks_item_data', False)[j]['ks_auto_update_type'] + if 'ks_show_live_pop_up' in ks_dashboard_data[i].get('ks_item_data', False)[j].keys(): + del ks_dashboard_data[i].get('ks_item_data', False)[j]['ks_show_live_pop_up'] + else: + raise ValidationError(_("Current Json File is not properly formatted according to Dashboard Ninja Model.")) + + ks_dashboard_key = ['name', 'ks_dashboard_menu_name', 'ks_gridstack_config'] + ks_dashboard_item_key = ['ks_model_id', 'ks_chart_measure_field', 'ks_list_view_fields', 'ks_record_field', + 'ks_chart_relation_groupby', 'ks_id'] + + # Fetching dashboard model info + for data in ks_dashboard_data: + if not all(key in data for key in ks_dashboard_key): + raise ValidationError( + _("Current Json File is not properly formatted according to Dashboard Ninja Model.")) + ks_dashboard_top_menu_id = data.get('ks_dashboard_top_menu_id', False) + if ks_dashboard_top_menu_id: + try: + self.env['ir.ui.menu'].browse(ks_dashboard_top_menu_id).name + ks_dashboard_top_menu_id = self.env['ir.ui.menu'].browse(ks_dashboard_top_menu_id) + except Exception: + ks_dashboard_top_menu_id = False + vals = self.ks_prepare_import_data_vals(data, menu_id) + # Creating Dashboard + dashboard_id = self.create(vals) + + if data['ks_gridstack_config']: + ks_gridstack_config = safe_eval(data['ks_gridstack_config']) + ks_grid_stack_config = {} + + item_ids = [] + item_new_ids = [] + ks_skiped = False + if data['ks_item_data']: + # Fetching dashboard item info + ks_skiped = 0 + for item in data['ks_item_data']: + item['ks_company_id'] = False + if not all(key in item for key in ks_dashboard_item_key): + raise ValidationError( + _("Current Json File is not properly formatted according to Dashboard Ninja Model.")) + + # Creating dashboard items + item['ks_dashboard_ninja_board_id'] = dashboard_id.id + item_ids.append(item['ks_id']) + del item['ks_id'] + + if 'ks_data_calculation_type' in item: + if item['ks_data_calculation_type'] == 'custom': + del item['ks_data_calculation_type'] + del item['ks_custom_query'] + del item['ks_xlabels'] + del item['ks_ylabels'] + del item['ks_list_view_layout'] + ks_item = self.ks_create_item(item) + item_new_ids.append(ks_item.id) + else: + ks_skiped += 1 + else: + ks_item = self.ks_create_item(item) + item_new_ids.append(ks_item.id) + + for id_index, id in enumerate(item_ids): + if data['ks_gridstack_config'] and str(id) in ks_gridstack_config: + ks_grid_stack_config[str(item_new_ids[id_index])] = ks_gridstack_config[str(id)] + # if id_index in item_new_ids: + + self.browse(dashboard_id.id).write({ + 'ks_gridstack_config': json.dumps(ks_grid_stack_config) + }) + + if ks_skiped: + return { + 'ks_skiped_items': ks_skiped, + } + + return "Success" + # separate function to make item for import + + def ks_prepare_import_data_vals(self, data, menu_id): + vals = { + 'name': data['name'], + 'ks_dashboard_menu_name': data['ks_dashboard_menu_name'], + 'ks_dashboard_top_menu_id': menu_id.id if menu_id else self.env.ref( + "ks_dashboard_ninja.board_menu_root").id, + 'ks_dashboard_active': True, + 'ks_gridstack_config': data['ks_gridstack_config'], + 'ks_dashboard_default_template': self.env.ref("ks_dashboard_ninja.ks_blank").id, + 'ks_dashboard_group_access': False, + 'ks_set_interval': data['ks_set_interval'], + 'ks_date_filter_selection': data['ks_date_filter_selection'], + 'ks_dashboard_start_date': data['ks_dashboard_start_date'], + 'ks_dashboard_end_date': data['ks_dashboard_end_date'], + } + return vals + + def ks_create_item(self, item): + model = self.env['ir.model'].search([('model', '=', item['ks_model_id'])]) + + if not model and not item['ks_dashboard_item_type'] == 'ks_to_do': + raise ValidationError(_( + "Please Install the Module which contains the following Model : %s " % item['ks_model_id'])) + + ks_model_name = item['ks_model_id'] + + ks_goal_lines = item['ks_goal_liness'].copy() if item.get('ks_goal_liness', False) else False + ks_action_lines = item['ks_action_liness'].copy() if item.get('ks_action_liness', False) else False + ks_multiplier_lines = item['ks_multiplier_lines'].copy() if item.get('ks_multiplier_lines', False) else False + ks_dn_header_line = item['ks_dn_header_line'].copy() if item.get('ks_dn_header_line', False) else False + + # Creating dashboard items + item = self.ks_prepare_item(item) + + if 'ks_goal_liness' in item: + del item['ks_goal_liness'] + if 'ks_id' in item: + del item['ks_id'] + if 'ks_action_liness' in item: + del item['ks_action_liness'] + if 'ks_icon' in item: + item['ks_icon_select'] = "Default" + item['ks_icon'] = False + if 'ks_dn_header_line' in item: + del item['ks_dn_header_line'] + if 'ks_multiplier_lines' in item: + del item['ks_multiplier_lines'] + + ks_item = self.env['ks_dashboard_ninja.item'].create(item) + + if ks_goal_lines and len(ks_goal_lines) != 0: + for line in ks_goal_lines: + line['ks_goal_date'] = datetime.datetime.strptime(line['ks_goal_date'].split(" ")[0], + '%Y-%m-%d') + line['ks_dashboard_item'] = ks_item.id + self.env['ks_dashboard_ninja.item_goal'].create(line) + + if ks_dn_header_line and len(ks_dn_header_line) != 0: + for line in ks_dn_header_line: + ks_line = {} + ks_line['ks_to_do_header'] = line.get('ks_to_do_header') + ks_line['ks_dn_item_id'] = ks_item.id + ks_dn_header_id = self.env['ks_to.do.headers'].create(ks_line) + if line.get(line.get('ks_to_do_header'), False): + for ks_task in line.get(line.get('ks_to_do_header')): + ks_task['ks_to_do_header_id'] = ks_dn_header_id.id + self.env['ks_to.do.description'].create(ks_task) + + if ks_action_lines and len(ks_action_lines) != 0: + + for line in ks_action_lines: + if line['ks_sort_by_field']: + ks_sort_by_field = line['ks_sort_by_field'] + ks_sort_record_id = self.env['ir.model.fields'].search( + [('model', '=', ks_model_name), ('name', '=', ks_sort_by_field)]) + if ks_sort_record_id: + line['ks_sort_by_field'] = ks_sort_record_id.id + else: + line['ks_sort_by_field'] = False + if line['ks_item_action_field']: + ks_item_action_field = line['ks_item_action_field'] + ks_record_id = self.env['ir.model.fields'].search( + [('model', '=', ks_model_name), ('name', '=', ks_item_action_field)]) + if ks_record_id: + line['ks_item_action_field'] = ks_record_id.id + line['ks_dashboard_item_id'] = ks_item.id + self.env['ks_dashboard_ninja.item_action'].create(line) + + if ks_multiplier_lines and len(ks_multiplier_lines) != 0: + for rec in ks_multiplier_lines: + ks_multiplier_field = rec['ks_multiplier_fields'] + ks_multiplier_field_id = self.env['ir.model.fields'].search( + [('model', '=', ks_model_name), ('id', '=', ks_multiplier_field)]) + if ks_multiplier_field: + rec['ks_multiplier_fields'] = ks_multiplier_field_id.id + rec['ks_dashboard_item_id'] = ks_item.id + self.env['ks_dashboard_item.multiplier'].create(rec) + + return ks_item + + def ks_prepare_item(self, item): + try: + ks_measure_field_ids = [] + ks_measure_field_2_ids = [] + ks_many2many_field_ordering = item['ks_many2many_field_ordering'] if item.get('ks_many2many_field_ordering', False) else False + ks_list_view_group_fields_name = False + ks_list_view_fields_name = False + ks_chart_measure_field_name = False + ks_chart_measure_field_2_name = False + if ks_many2many_field_ordering: + ks_many2many_field_ordering = json.loads(ks_many2many_field_ordering) + ks_list_view_group_fields_name = ks_many2many_field_ordering.get('ks_list_view_group_fields_name', False) + ks_list_view_fields_name = ks_many2many_field_ordering.get('ks_list_view_fields_name', False) + ks_chart_measure_field_name = ks_many2many_field_ordering.get('ks_chart_measure_field_name', False) + ks_chart_measure_field_2_name = ks_many2many_field_ordering.get('ks_chart_measure_field_2_name', False) + ks_chart_measure_field = item['ks_chart_measure_field'] + if ks_chart_measure_field_name and len(ks_chart_measure_field_name)>0: + ks_chart_measure_field = ks_chart_measure_field_name + for ks_measure in ks_chart_measure_field: + ks_measure_id = self.env['ir.model.fields'].search( + [('name', '=', ks_measure), ('model', '=', item['ks_model_id'])]) + if ks_measure_id: + ks_measure_field_ids.append(ks_measure_id.id) + item['ks_chart_measure_field'] = [(6, 0, ks_measure_field_ids)] + ks_chart_measure_field_2 = item['ks_chart_measure_field_2'] + if ks_chart_measure_field_name and len(ks_chart_measure_field_name) > 0: + ks_chart_measure_field_2 = ks_chart_measure_field_2_name + for ks_measure in ks_chart_measure_field_2: + ks_measure_id = self.env['ir.model.fields'].search( + [('name', '=', ks_measure), ('model', '=', item['ks_model_id'])]) + if ks_measure_id: + ks_measure_field_2_ids.append(ks_measure_id.id) + item['ks_chart_measure_field_2'] = [(6, 0, ks_measure_field_2_ids)] + + ks_list_view_group_fields_ids = [] + ks_list_view_group_fields = item['ks_list_view_group_fields'] + if ks_list_view_group_fields_name and len(ks_list_view_group_fields_name) > 0: + ks_list_view_group_fields = ks_list_view_group_fields_name + for ks_measure in ks_list_view_group_fields: + ks_measure_id = self.env['ir.model.fields'].search( + [('name', '=', ks_measure), ('model', '=', item['ks_model_id'])]) + + if ks_measure_id: + ks_list_view_group_fields_ids.append(ks_measure_id.id) + item['ks_list_view_group_fields'] = [(6, 0, ks_list_view_group_fields_ids)] + + ks_list_view_field_ids = [] + + ks_list_view_fields = item['ks_list_view_fields'] + if ks_list_view_fields_name and len(ks_list_view_fields_name) > 0: + ks_list_view_fields = ks_list_view_group_fields_name + for ks_list_field in ks_list_view_fields: + ks_list_field_id = self.env['ir.model.fields'].search( + [('name', '=', ks_list_field), ('model', '=', item['ks_model_id'])]) + if ks_list_field_id: + ks_list_view_field_ids.append(ks_list_field_id.id) + item['ks_list_view_fields'] = [(6, 0, ks_list_view_field_ids)] + + if item['ks_record_field']: + ks_record_field = item['ks_record_field'] + ks_record_id = self.env['ir.model.fields'].search( + [('name', '=', ks_record_field), ('model', '=', item['ks_model_id'])]) + if ks_record_id: + item['ks_record_field'] = ks_record_id.id + else: + item['ks_record_field'] = False + + if item['ks_date_filter_field']: + ks_date_filter_field = item['ks_date_filter_field'] + ks_record_id = self.env['ir.model.fields'].search( + [('name', '=', ks_date_filter_field), ('model', '=', item['ks_model_id'])]) + if ks_record_id: + item['ks_date_filter_field'] = ks_record_id.id + else: + item['ks_date_filter_field'] = False + + if item['ks_chart_relation_groupby']: + ks_group_by = item['ks_chart_relation_groupby'] + ks_record_id = self.env['ir.model.fields'].search( + [('name', '=', ks_group_by), ('model', '=', item['ks_model_id'])]) + if ks_record_id: + item['ks_chart_relation_groupby'] = ks_record_id.id + else: + item['ks_chart_relation_groupby'] = False + + if item['ks_chart_relation_sub_groupby']: + ks_group_by = item['ks_chart_relation_sub_groupby'] + ks_chart_relation_sub_groupby = self.env['ir.model.fields'].search( + [('name', '=', ks_group_by), ('model', '=', item['ks_model_id'])]) + if ks_chart_relation_sub_groupby: + item['ks_chart_relation_sub_groupby'] = ks_chart_relation_sub_groupby.id + else: + item['ks_chart_relation_sub_groupby'] = False + + if item['ks_dashboard_item_type'] == "ks_scatter_chart" and item[ + 'ks_scatter_measure_x_id']: + # ks_scatter_id = self.env['ir.model.fields'].search( + # [('name', '=', item['ks_scatter_measure_y_id'][0]), ('model', '=', item['ks_model_id'])]) + # if ks_scatter_id: + # item['ks_scatter_measure_y_id'] = ks_scatter_id.id + ks_scatter_group_by = item['ks_scatter_measure_x_id'] + ks_record_id = self.env['ir.model.fields'].search( + [('name', '=', ks_scatter_group_by), ('model', '=', item['ks_model_id'])]) + if ks_record_id: + item['ks_scatter_measure_x_id'] = ks_record_id.id + else: + item['ks_scatter_measure_x_id'] = False + if item["ks_dashboard_item_type"] != "ks_scatter_chart": + item['ks_scatter_measure_x_id'] = False + # item['ks_scatter_measure_y_id'] = False + + # Sort by field : Many2one Entery + if item['ks_sort_by_field']: + ks_group_by = item['ks_sort_by_field'] + ks_sort_by_field = self.env['ir.model.fields'].search( + [('name', '=', ks_group_by), ('model', '=', item['ks_model_id'])]) + if ks_sort_by_field: + item['ks_sort_by_field'] = ks_sort_by_field.id + else: + item['ks_sort_by_field'] = False + + if item['ks_list_target_deviation_field']: + ks_list_target_deviation_field = item['ks_list_target_deviation_field'] + record_id = self.env['ir.model.fields'].search( + [('name', '=', ks_list_target_deviation_field), ('model', '=', item['ks_model_id'])]) + if record_id: + item['ks_list_target_deviation_field'] = record_id.id + else: + item['ks_list_target_deviation_field'] = False + + ks_model_id = self.env['ir.model'].search([('model', '=', item['ks_model_id'])]).id + + if item.get("ks_actions"): + ks_action = self.env.ref(item["ks_actions"], False) + if ks_action: + item["ks_actions"] = ks_action.id + else: + item["ks_actions"] = False + if item.get("ks_client_action"): + ks_action = self.env.ref(item["ks_client_action"], False) + if ks_action: + item["ks_client_action"] = ks_action.id + else: + item["ks_client_action"] = False + + if (item['ks_model_id_2']): + ks_model_2 = item['ks_model_id_2'].replace(".", "_") + ks_model_id_2 = self.env['ir.model'].search([('model', '=', item['ks_model_id_2'])]).id + if item['ks_record_field_2']: + ks_record_field = item['ks_record_field_2'] + ks_record_id = self.env['ir.model.fields'].search( + [('model', '=', item['ks_model_id_2']), ('name', '=', ks_record_field)]) + + if ks_record_id: + item['ks_record_field_2'] = ks_record_id.id + else: + item['ks_record_field_2'] = False + if item['ks_date_filter_field_2']: + ks_record_id = self.env['ir.model.fields'].search( + [('model', '=', item['ks_model_id_2']), ('name', '=', item['ks_date_filter_field_2'])]) + + if ks_record_id: + item['ks_date_filter_field_2'] = ks_record_id.id + else: + item['ks_date_filter_field_2'] = False + + item['ks_model_id_2'] = ks_model_id_2 + else: + item['ks_date_filter_field_2'] = False + item['ks_record_field_2'] = False + + item['ks_model_id'] = ks_model_id + + item['ks_goal_liness'] = False + item['ks_item_start_date'] = item['ks_item_start_date'] if \ + item['ks_item_start_date'] else False + item['ks_item_end_date'] = item['ks_item_end_date'] if \ + item['ks_item_end_date'] else False + item['ks_item_start_date_2'] = item['ks_item_start_date_2'] if \ + item['ks_item_start_date_2'] else False + item['ks_item_end_date_2'] = item['ks_item_end_date_2'] if \ + item['ks_item_end_date_2'] else False + + return item + except Exception as e: + raise ValidationError('JSON file not supported.') + @api.model + def update_child_board(self, action, dashboard_id, data): + dashboard_id = self.browse(dashboard_id) + selecred_rec = self.env['ks_dashboard_ninja.child_board'].search( + [['id', 'in', dashboard_id.ks_child_dashboard_ids.ids], + ['company_id', '=', self.env.company.id], ['ks_active', '=', True]], limit=1) + if action == 'create': + dashboard_id.ks_child_dashboard_ids.write({'ks_active': False}) + result = self.env['ks_dashboard_ninja.child_board'].create(data) + result = result.id + elif action == 'update': + # result = dashboard_id.ks_child_dashboard_ids.search([['ks_active', '=', True]]).write({'ks_active': False}) + if data['ks_selected_board_id'] != 'ks_default': + selecred_rec.ks_active = False + result = dashboard_id.ks_child_dashboard_ids.browse(int(data['ks_selected_board_id'])).write( + {'ks_active': True}) + else: + result = dashboard_id.ks_child_dashboard_ids.search([['ks_active', '=', True]]).write( + {'ks_active': False}) + for i in dashboard_id.ks_child_dashboard_ids: + if i.name == 'Default Board Layout': + i.ks_active = True + return result + + def ks_prepare_dashboard_domain(self): + pre_defined_filter_ids = self.env['ks_dashboard_ninja.board_defined_filters'].search( + [['id', 'in', self.ks_dashboard_defined_filters_ids.ids], '|', ['ks_is_active', '=', True], + ['display_type', '=', 'line_section']], order='sequence') + data = {} + filter_model_ids = pre_defined_filter_ids.mapped('ks_model_id').ids + for model_id in filter_model_ids: + filter_ids = self.env['ks_dashboard_ninja.board_defined_filters'].search( + [['id', 'in', pre_defined_filter_ids.ids], '|', ['ks_model_id', '=', model_id], + ['display_type', '=', 'line_section']], + order='sequence') + connect_symbol = '|' + for rec in filter_ids: + if rec.display_type == 'line_section': + connect_symbol = '&' + + if data.get(rec.ks_model_id.model) and rec.ks_domain: + data[rec.ks_model_id.model]['domain'] = data[rec.ks_model_id.model]['domain'] + safe_eval( + rec.ks_domain) + data[rec.ks_model_id.model]['domain'].insert(0, connect_symbol) + elif rec.ks_model_id.model: + ks_domain = rec.ks_domain + if ks_domain and "%UID" in ks_domain: + ks_domain = ks_domain.replace('"%UID"', str(self.env.user.id)) + if ks_domain and "%MYCOMPANY" in ks_domain: + ks_domain = replace_company_domain(ks_domain, self.env.company.id, self.env.companies.ids) + data[rec.ks_model_id.model] = { + 'domain': safe_eval(ks_domain) if ks_domain else [], + 'ks_domain_index_data': [], + 'model_name': rec.ks_model_id.name, + 'item_ids': self.env['ks_dashboard_ninja.item'].search( + [['id', 'in', self.ks_dashboard_items_ids.ids], '|', + ['ks_model_id', '=', rec.ks_model_id.id], ['ks_model_id_2', '=', rec.ks_model_id.id]]).ids + } + + return data + + def ks_prepare_dashboard_pre_domain(self): + data = {} + pre_defined_filter_ids = self.env['ks_dashboard_ninja.board_defined_filters'].search( + [['id', 'in', self.ks_dashboard_defined_filters_ids.ids]], order='sequence') + categ_seq = 1 + for rec in pre_defined_filter_ids: + if rec.display_type == 'line_section': + categ_seq = categ_seq + 1 + ks_domain = rec.ks_domain + if ks_domain and "%UID" in ks_domain: + ks_domain = ks_domain.replace('"%UID"', str(self.env.user.id)) + if ks_domain and "%MYCOMPANY" in ks_domain: + ks_domain = replace_company_domain(ks_domain, self.env.company.id, self.env.companies.ids) + + data[rec['id']] = { + 'id': rec.id, + 'name': rec.name, + 'model': rec.ks_model_id.model, + 'model_name': rec.ks_model_id.name, + 'active': rec.ks_is_active, + 'categ': rec.ks_model_id.model + '_' + str(categ_seq) if rec.display_type != 'line_section' else 0, + 'type': 'filter' if rec.display_type != 'line_section' else 'separator', + 'domain': safe_eval(ks_domain) if ks_domain else [], + 'sequence': rec.sequence + } + return data + + def ks_prepare_dashboard_custom_domain(self): + custom_filter_ids = self.env['ks_dashboard_ninja.board_custom_filters'].search( + [['id', 'in', self.ks_dashboard_custom_filters_ids.ids]], order='name') + data = {} + for rec in custom_filter_ids: + data[str(rec.id)] = { + 'id': rec.id, + 'name': rec.name, + 'model': rec.ks_model_id.model, + 'model_name': rec.ks_model_id.name, + 'field_name': rec.ks_domain_field_id.name, + 'type': rec.ks_domain_field_id.ttype, + 'relation': rec.ks_domain_field_id.relation if rec.ks_domain_field_id.ttype in ['many2many', 'many2one', + 'one2many'] else False, + 'selection': [] + } + if rec.ks_domain_field_id.ttype == 'selection': + selection_list = self.env[rec.ks_model_id.model].fields_get(allfields=[rec.ks_domain_field_id.name])[ + rec.ks_domain_field_id.name]['selection'] + data[str(rec.id)]['selection'] = selection_list + return data + + def ks_prepare_dashboard_favourite_filter(self): + data = {} + ks_favourite_filter_ids = self.env['ks_dashboard_ninja.favourite_filters'].search( + [['id', 'in', self.ks_dashboard_favourite_filters_ids.ids], '|', ['ks_access_id', '=', self.env.user.id], + ['ks_access_id', '=', 0]], order='create_date') + for rec in ks_favourite_filter_ids: + data[rec.name] = {'id': rec.id, + 'ks_filter': json.loads(rec.ks_filter), + 'name': rec.name, + 'type': rec.ks_filter_type, + 'ks_access_id': True if rec.ks_access_id else False, + } + return data diff --git a/addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py b/addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py new file mode 100644 index 0000000..dc20e0d --- /dev/null +++ b/addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py @@ -0,0 +1,4632 @@ +# -*- coding: utf-8 -*- + +import ast +import binascii +import csv +import datetime as dt +import json +import logging +import os +import tempfile +from collections import defaultdict +from datetime import datetime +from datetime import timedelta + +import babel +import dateutil +import pandas as pd +from odoo.tools import sql, SQL +import pytz +from dateutil import relativedelta +from odoo import models, fields, api, _ +from odoo.addons.ks_dashboard_ninja.common_lib.ks_date_filter_selections import ks_get_date, ks_convert_into_utc, \ + ks_convert_into_local +from odoo.exceptions import ValidationError, UserError +from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT +from odoo.tools.safe_eval import safe_eval +from odoo.addons.ks_dashboard_ninja.common_lib.filter_tools import replace_company_domain + + +from .ks_country_bounds import get_country_code + +_logger = logging.getLogger("DS_NINJA") +# TODO : Check all imports if needed + + +read = fields.Many2one.read + + + +def ks_read(self, records): + if self.name == 'ks_list_view_fields' or self.name == 'ks_list_view_group_fields' or \ + self.name == 'ks_chart_measure_field' or self.name == 'ks_chart_measure_field_2': + comodel = records.env[self.comodel_name] + + # String domains are supposed to be dynamic and evaluated on client-side + # only (thus ignored here). + domain = self.domain if isinstance(self.domain, list) else [] + query = comodel._where_calc(domain) + comodel._apply_ir_rules(query, 'read') + query.order = comodel._order_to_sql(comodel._order, query) + + wquery = comodel._where_calc(domain) + comodel._apply_ir_rules(wquery, 'read') + sql_id1 = SQL.identifier(self.relation, self.column1) + sql_id2 = SQL.identifier(self.relation, self.column2) + query.add_join('JOIN', self.relation, None, SQL( + "%s = %s", sql_id2, SQL.identifier(comodel._table, 'id'), + )) + query.add_where(SQL("%s IN %s", sql_id1, tuple(records.ids))) + + group = defaultdict(list) + records.env.execute_query(query.select(sql_id1, sql_id2)) + # for id1, id2 in records.env.execute_query(query.select(sql_id1, sql_id2)): + # group[id1].append(id2) + + for record in records: + if self.name == 'ks_list_view_fields': + field = 'ks_list_view_fields' + elif self.name == 'ks_chart_measure_field': + field = 'ks_chart_measure_field' + elif self.name == 'ks_chart_measure_field_2': + field = 'ks_chart_measure_field_2' + else: + field = 'ks_list_view_group_fields' + order = False + if record.ks_many2many_field_ordering: + order = json.loads(record.ks_many2many_field_ordering).get(field, False) + + + rec_list = records._cr.fetchall() + if order: + for row in order: + group[record.id].append(row) + + else: + for id1, id2 in rec_list: + group[id1].append(id2) + + # store result in cache + # cache = records.env.cache + if order: + try: + group[record.id].sort(key=lambda x: order.index(x)) + except Exception as e: + pass + values = [tuple(group[id_]) for id_ in records._ids] + records.env.cache.insert_missing(records, self, values) + + + else: + context = {'active_test': False} + context.update(self.context) + comodel = records.env[self.comodel_name].with_context(**context) + + # make the query for the lines + domain = self.get_domain_list(records) + query = comodel._where_calc(domain) + comodel._apply_ir_rules(query, 'read') + query.order = comodel._order_to_sql(comodel._order, query) + + # join with many2many relation table + sql_id1 = SQL.identifier(self.relation, self.column1) + sql_id2 = SQL.identifier(self.relation, self.column2) + query.add_join('JOIN', self.relation, None, SQL( + "%s = %s", sql_id2, SQL.identifier(comodel._table, 'id'), + )) + query.add_where(SQL("%s IN %s", sql_id1, tuple(records.ids))) + + # retrieve pairs (record, line) and group by record + group = defaultdict(list) + for id1, id2 in records.env.execute_query(query.select(sql_id1, sql_id2)): + group[id1].append(id2) + + # store result in cache + values = [tuple(group[id_]) for id_ in records._ids] + records.env.cache.insert_missing(records, self, values) + + + +fields.Many2many.read = ks_read + +read_group = models.BaseModel._read_group_groupby + + +def ks_time_addition(self, gb, query): + """ + Overwriting default to add minutes to Helper method to collect important + information about groupbys: raw field name, type, time information, qualified name, ... + """ + split = gb.split(':') + field_type = self._fields[split[0]].type + gb_function = split[1] if len(split) == 2 else None + if gb_function == 'month_year': + gb_function = 'month' + temporal = field_type in ('date', 'datetime') + tz_convert = field_type == 'datetime' and self._context.get('tz') in pytz.all_timezones + qualified_field = self._inherits_join_calc(self._table, split[0], query) + if temporal: + lang = self.env['res.lang']._lang_get(self.env.user.lang).time_format + if '%H' in lang: + display_formats = { + 'minute': 'HH:mm dd MMM', + 'hour': 'HH:00 dd MMM', + 'day': 'dd MMM yyyy', # yyyy = normal year + 'week': "'W'w YYYY", # w YYYY = ISO week-year + 'month': 'MMMM yyyy', + 'quarter': 'QQQ yyyy', + 'year': 'yyyy', + } + else: + display_formats = { + 'minute': 'hh:mm dd MMM', + 'hour': 'hh:00 dd MMM', + 'day': 'dd MMM yyyy', # yyyy = normal year + 'week': "'W'w YYYY", # w YYYY = ISO week-year + 'month': 'MMMM yyyy', + 'quarter': 'QQQ yyyy', + 'year': 'yyyy', + } + time_intervals = { + 'minute': dateutil.relativedelta.relativedelta(minutes=1), + 'hour': dateutil.relativedelta.relativedelta(hours=1), + 'day': dateutil.relativedelta.relativedelta(days=1), + 'week': dt.timedelta(days=7), + 'month': dateutil.relativedelta.relativedelta(months=1), + 'quarter': dateutil.relativedelta.relativedelta(months=3), + 'year': dateutil.relativedelta.relativedelta(years=1) + } + if tz_convert: + qualified_field = "timezone('%s', timezone('UTC',%s))" % (self._context.get('tz', 'UTC'), qualified_field) + qualified_field = "date_trunc('%s', %s::timestamp)" % (gb_function or 'month', qualified_field) + if field_type == 'boolean': + qualified_field = "coalesce(%s,false)" % qualified_field + return { + 'field': split[0], + 'groupby': gb, + 'type': field_type, + 'display_format': display_formats[gb_function or 'month'] if temporal else None, + 'interval': time_intervals[gb_function or 'month'] if temporal else None, + 'tz_convert': tz_convert, + 'qualified_field': qualified_field, + 'granularity': gb_function or 'month' if temporal else None, + } + + +# models.BaseModel._read_group = ks_time_addition + + +class KsDashboardNinjaItems(models.Model): + _name = 'ks_dashboard_ninja.item' + _description = 'Dashboard Ninja items' + + name = fields.Char(string="Name", translate=True, help="The item will be represented by this unique name.") + ks_info = fields.Text(string="Item Description", translate=True) + ks_model_id = fields.Many2one('ir.model', string='Model', + domain="[('access_ids','!=',False),('transient','=',False)," + "('model','not ilike','base_import%'),'|',('model','not ilike','ir.%'),('model','=ilike','_%ir.%')," + "('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%')," + "('model','!=','mail.thread'),('model','not ilike','ks_dash%'),('model','not ilike','ks_to%')]", + help="Data source to fetch and read the data for the creation of dashboard items. ") + ks_dashboard_board_template_id = fields.Many2one('ks_dashboard_ninja.board_template', string="Dashboard Template") + ks_domain = fields.Char(string="Domain", help="Define conditions for filter. ") + + ks_model_id_2 = fields.Many2one('ir.model', string='Kpi Model', + domain="[('access_ids','!=',False),('transient','=',False)," + "('model','not ilike','base_import%'),('model','not ilike','ir.%')," + "('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%')," + "('model','!=','mail.thread'),('model','not ilike','ks_dash%'), ('model','not ilike','ks_to%')]") + + ks_model_name_2 = fields.Char(related='ks_model_id_2.model', string="Kpi Model Name") + + zoom_enabled = fields.Boolean(string="Zoom enabled?", compute="compute_zoom_enabled") + + def compute_zoom_enabled(self): + for rec in self: + rec.zoom_enabled = self.env['ir.config_parameter'].sudo().get_param('ks_dashboard_ninja.enable_chart_zoom') + + # This field main purpose is to store %UID as current user id. Mainly used in JS file as container. + ks_domain_temp = fields.Char(string="Domain Substitute") + grid_corners = fields.Char(string="grid corners") + ks_background_color = fields.Char(string="Background Color", + default="#DAEAF6,0.99", help=' Select the background color with transparency. ') + ks_icon = fields.Binary(string="Upload Icon", attachment=True) + ks_default_icon = fields.Char(string="Icon", default="bar-chart", help='Select the icon to be displayed. ') + ks_default_icon_color = fields.Char(default="#6789C6,0.99", string="Icon Color", + help='Select the icon to be displayed. ') + ks_icon_select = fields.Selection([("Default","Default"),("Custom","Custom"),],string="Icon Option", default=("Default"), help='Choose the Icon option. ') + ks_font_color = fields.Char(default="#000000,0.99", string="Font Color", help='Select the font color. ') + ks_dashboard_item_theme = fields.Char(string="Theme", default="white", + help='Select the color theme for the display. ') + ks_layout = fields.Selection([('layout1', 'Layout 1'), + ('layout2', 'Layout 2'), + ('layout3', 'Layout 3'), + ('layout4', 'Layout 4'), + ('layout5', 'Layout 5'), + ('layout6', 'Layout 6'), + ], default=('layout5'), required=True, string="Layout", + help=' Select the layout to display records. ') + ks_preview = fields.Integer(default=1, string="Preview") + ks_model_name = fields.Char(related='ks_model_id.model', string="Model Name") + + ks_record_count_type_2 = fields.Selection([('count', 'Count'), + ('sum', 'Sum'), + ('average', 'Average')], string="Kpi Record Type", default="sum") + ks_record_field_2 = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id_2),('name','!=','id'),('name','!=','sequence'),('store','=',True)," + "'|','|',('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Kpi Record Field") + ks_record_count_2 = fields.Float(string="KPI Record Count", readonly=True, compute='ks_get_record_count_2', + compute_sudo=False) + ks_record_count_type = fields.Selection([('count', 'Count'), + ('sum', 'Sum'), + ('average', 'Average')], string="Record Type", default="count", + help="Type of record how record will show as count,sum and average of the record") + ks_record_count = fields.Float(string="Record Count", compute='ks_get_record_count', readonly=True, + compute_sudo=False) + ks_record_field = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('store','=',True),'|'," + "'|',('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Record Field") + ks_send_mail = fields.Boolean(string="Send Mail") + ks_email_to_ids = fields.Many2many('ks_dashboard_ninja.kpi_mail',string="Email Address") + ks_stop_mail_cron = fields.Boolean(string="Stop mail cron") + ks_record_data_limit_visibility = fields.Boolean(string="Record Limit Data Visibility", + help="To enable the record data limit field") + + # Date Filter Fields + # Condition to tell if date filter is applied or not + ks_isDateFilterApplied = fields.Boolean(default=False) + + # ---------------------------- Date Filter Fields ------------------------------------------ + ks_date_filter_selection = fields.Selection([ + ('l_none', 'None'), + ('l_day', 'Today'), + ('t_week', 'This Week'), + ('t_month', 'This Month'), + ('t_quarter', 'This Quarter'), + ('t_year', 'This Year'), + ('td_week', 'Week to Date'), + ('td_month', 'Month to Date'), + ('td_quarter', 'Quarter to Date'), + ('td_year', 'Year to Date'), + ('n_day', 'Next Day'), + ('n_week', 'Next Week'), + ('n_month', 'Next Month'), + ('n_quarter', 'Next Quarter'), + ('n_year', 'Next Year'), + ('ls_day', 'Last Day'), + ('ls_week', 'Last Week'), + ('ls_month', 'Last Month'), + ('ls_quarter', 'Last Quarter'), + ('ls_year', 'Last Year'), + ('l_week', 'Last 7 days'), + ('l_month', 'Last 30 days'), + ('l_quarter', 'Last 90 days'), + ('l_year', 'Last 365 days'), + ('ls_past_until_now', 'Past Till Now'), + ('ls_pastwithout_now', ' Past Excluding Today'), + ('n_future_starting_now', 'Future Starting Now'), + ('n_futurestarting_tomorrow', 'Future Starting Tomorrow'), + ('l_custom', 'Custom Filter'), + ], string="Date Filter Selection", default="l_none", required=True, + help='Select interval of the records to be displayed. ') + ks_date_filter_field = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('store','=',True),'|',('ttype','=','date')," + "('ttype','=','datetime')]", + string="Date Filter Field", + help='Select the field for which Date Filter should be applicable.') + + ks_item_start_date = fields.Datetime(string="Start Date") + ks_item_end_date = fields.Datetime(string="End Date") + + ks_date_filter_field_2 = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id_2),('store','=',True),'|',('ttype','=','date')," + "('ttype','=','datetime')]", + string="Kpi Date Filter Field") + + ks_item_start_date_2 = fields.Datetime(string="Kpi Start Date") + ks_item_end_date_2 = fields.Datetime(string="Kpi End Date") + + ks_domain_2 = fields.Char(string="Kpi Domain") + ks_domain_2_temp = fields.Char(string="Kpi Domain Substitute") + + ks_date_filter_selection_2 = fields.Selection([ + ('l_none', "None"), + ('l_day', 'Today'), + ('t_week', 'This Week'), + ('t_month', 'This Month'), + ('t_quarter', 'This Quarter'), + ('t_year', 'This Year'), + ('td_week', 'Week to Date'), + ('td_month', 'Month to Date'), + ('td_quarter', 'Quarter to Date'), + ('td_year', 'Year to Date'), + ('n_day', 'Next Day'), + ('n_week', 'Next Week'), + ('n_month', 'Next Month'), + ('n_quarter', 'Next Quarter'), + ('n_year', 'Next Year'), + ('ls_day', 'Last Day'), + ('ls_week', 'Last Week'), + ('ls_month', 'Last Month'), + ('ls_quarter', 'Last Quarter'), + ('ls_year', 'Last Year'), + ('l_week', 'Last 7 days'), + ('l_month', 'Last 30 days'), + ('l_quarter', 'Last 90 days'), + ('l_year', 'Last 365 days'), + ('ls_past_until_now', 'Past Till Now'), + ('ls_pastwithout_now', ' Past Excluding Today'), + ('n_future_starting_now', 'Future Starting Now'), + ('n_futurestarting_tomorrow', 'Future Starting Tomorrow'), + ('l_custom', 'Custom Filter'), + ], string="Kpi Date Filter Selection", required=True, default='l_none') + + ks_previous_period = fields.Boolean(string=" Compare With Previous Period ", help='Checkbox to show comparison between the data of present day and the previous selected period. ') + + # ------------------------ Pro Fields -------------------- + ks_dashboard_ninja_board_id = fields.Many2one('ks_dashboard_ninja.board', string="Dashboard", + default=lambda self: self._context[ + 'ks_dashboard_id'] if 'ks_dashboard_id' in self._context + else False) + + # Chart related fields + ks_dashboard_item_type = fields.Selection([('ks_tile', 'Tile'), + ('ks_bar_chart', 'Bar Chart'), + ('ks_horizontalBar_chart', 'Horizontal Bar Chart'), + ('ks_line_chart', 'Line Chart'), + ('ks_area_chart', 'Area Chart'), + ('ks_pie_chart', 'Pie Chart'), + ('ks_doughnut_chart', 'Doughnut Chart'), + ('ks_polarArea_chart', 'Polar Area Chart'), + ('ks_radialBar_chart', 'Radial Bar Chart'), + ('ks_scatter_chart', 'Scatter Chart'), + ('ks_list_view', 'List View'), + ('ks_radar_view', 'Radar View'), + ('ks_flower_view', 'Flower View'), + ('ks_kpi', 'KPI'), + ('ks_to_do', 'To Do'), + ('ks_map_view', 'Map View'), + ('ks_funnel_chart', 'Funnel Chart'), + ('ks_bullet_chart', 'Bullet Chart') + ], default=lambda self: self._context.get('ks_dashboard_item_type', + 'ks_tile'), required=True, + string="Dashboard Item Type", + help="Select the required type of dashboard to display. ") + ks_chart_groupby_type = fields.Char(compute='get_chart_groupby_type', compute_sudo=False) + ks_chart_sub_groupby_type = fields.Char(compute='get_chart_sub_groupby_type', compute_sudo=False) + ks_chart_relation_groupby = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),('ttype','!=','binary')," + "('ttype','!=','many2many'), ('ttype','!=','one2many')]", + string="Group By", help=' Define the x-axis of the graph. ') + ks_chart_relation_sub_groupby = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),('ttype','!=','binary')," + "('ttype','!=','many2many'), ('ttype','!=','one2many')]", + string=" Sub Group By", + help='Select the second level of grouping. ') + ks_chart_date_groupby = fields.Selection([('minute', 'Minute'), + ('hour', 'Hour'), + ('day', 'Day'), + ('week', 'Week'), + ('month', 'Month'), + ('quarter', 'Quarter'), + ('year', 'Year'), + ('month_year', 'Month-Year') + ], string="Dashboard Item Chart Group By Type") + ks_chart_date_sub_groupby = fields.Selection([('minute', 'Minute'), + ('hour', 'Hour'), + ('day', 'Day'), + ('week', 'Week'), + ('month', 'Month'), + ('quarter', 'Quarter'), + ('year', 'Year'), + ], string="Dashboard Item Chart Sub Group By Type") + ks_graph_preview = fields.Char(string="Graph Preview", default="Graph Preview") + ks_chart_data = fields.Char(string="Chart Data in string form", compute='ks_get_chart_data', compute_sudo=False) + ks_chart_data_count_type = fields.Selection([('count', 'Count'), ('sum', 'Sum'), ('average', 'Average')], + string="Data Type", default="sum") + ks_chart_measure_field = fields.Many2many('ir.model.fields', 'ks_dn_measure_field_rel', 'measure_field_id', + 'field_id', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),'|','|'," + "('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Measure 1", help='Data points to be selected.') + ks_chart_is_cumulative = fields.Boolean('Is Cumulative') + ks_chart_cumulative_field = fields.Many2many('ir.model.fields', 'ks_dn_cumulative_measure_field_rel', + 'measure_cumulative_field_id', + 'cumulative_field_id', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name'," + "'!=','sequence'), " + "('store','=',True),'|','|'," + "('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Cumulative Fields", help='Data points to be selected.') + + ks_chart_cumulative = fields.Boolean("Cumulative As Line") + ks_chart_measure_field_2 = fields.Many2many('ir.model.fields', 'ks_dn_measure_field_rel_2', 'measure_field_id_2', + 'field_id', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),'|','|'," + "('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Line Measure", + help='Data Points displayed with a line in the graph. ') + + ks_bar_chart_stacked = fields.Boolean(string="Stacked Bar Chart", help='Stack the columns of the same record. ') + + ks_semi_circle_chart = fields.Boolean(string="Semi Circle Chart") + + ks_sort_by_field = fields.Many2one('ir.model.fields', + domain="[('model_id', '=', ks_model_id), ('name', '!=', 'id'), ('name', '!=', 'sequence'), ('store', '=', True), " + "('ttype', 'not in', ['one2many', 'binary', 'char', 'text', 'boolean', 'html'])]", + string="Sort By Field", help='Select the desired sorting preference. ') + ks_sort_by_order = fields.Selection([('ASC', 'Ascending'), ('DESC', 'Descending')], + string="Sort Order", help=' Select the order of the sorting. ') + ks_record_data_limit = fields.Integer(string="Record Limit", help=' Records to be displayed on the graph') + + ks_list_view_preview = fields.Char(string="List View Preview", default="List View Preview") + + ks_kpi_preview = fields.Char(string="Kpi Preview", default="KPI Preview") + + ks_kpi_type = fields.Selection([ + ('layout_1', 'KPI With Target'), + ('layout_2', 'Data Comparison'), + ], string="Kpi Layout", default="layout_1") + + ks_target_view = fields.Selection([("Number","Number"),("Progress Bar","Progress Bar"),],string="View", default="Number", help=' Select the view to compare target with data.') + + ks_data_comparison = fields.Selection([("None","None"),("Sum","Sum"),("Ratio","Ratio"),("Percentage","Percentage"),],string="Kpi Data Type", default="None") + + ks_kpi_data = fields.Char(string="KPI Data", compute="ks_get_kpi_data", compute_sudo=False) + + ks_chart_item_color = fields.Selection( + [('default', 'Default'), ('dark', 'Dark'), ('material', 'Material'), ('moonrise', 'Moonrise')], + string="Chart Color Palette", default="default", help='Select the display preference. ') + + # ------------------------ List View Fields ------------------------------ + + ks_list_view_type = fields.Selection([('ungrouped', 'Un-Grouped'), ('grouped', 'Grouped')], default="ungrouped", + string="List View Type", required=True, + help='Select the desired list view type. ') + ks_list_view_fields = fields.Many2many('ir.model.fields', 'ks_dn_list_field_rel', 'list_field_id', 'field_id', + domain="[('model_id','=',ks_model_id),('ttype','!=','one2many')," + "('ttype','!=','many2many'),('ttype','!=','binary')]", + string="Fields to show in list", + help=' Select the fields you want to display in the list. ') + + ks_export_all_records = fields.Boolean(string="Export All Records", default=True, + help="when click on boolean button, all the records will be downloaded which are present in entire list") + + ks_list_view_group_fields = fields.Many2many('ir.model.fields', 'ks_dn_list_group_field_rel', 'list_field_id', + 'field_id', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),'|','|'," + "('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="List View Grouped Fields") + + ks_list_view_data = fields.Char(string="List View Data in JSon", compute='ks_get_list_view_data', + compute_sudo=False) + + # -------------------- Multi Company Feature --------------------- + ks_company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id, + help='Name of the company for which analytics will be displayed in the dashboard. ') + + # -------------------- Target Company Feature --------------------- + ks_goal_enable = fields.Boolean(string="Enable Target", help='Show the set target.') + ks_goal_bar_line = fields.Boolean(string="Show Target As Line") + ks_standard_goal_value = fields.Float(string="Standard Target", help='Show the set target') + ks_goal_lines = fields.One2many('ks_dashboard_ninja.item_goal', 'ks_dashboard_item', string="Target Lines") + + ks_list_target_deviation_field = fields.Many2one('ir.model.fields', 'list_field_id', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),'|','|'," + "('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + ) + + ks_many2many_field_ordering = fields.Char() + + # TODO : Merge all these fields into one and show a widget to get output for these fields from JS + ks_show_data_value = fields.Boolean(string="Show Data Value", help=' Display value on the graph. . ') + + ks_action_lines = fields.One2many('ks_dashboard_ninja.item_action', 'ks_dashboard_item_id', string="Action Lines") + + ks_actions = fields.Many2one('ir.actions.act_window', domain="[('res_model','=',ks_model_name)]", + string="Actions", help="Redirects you to the selected view. ") + + ks_compare_period = fields.Integer(string="Include Period", + help=' Provide the number of Date Filter Selection you want to include while displaying the record.') + ks_year_period = fields.Integer(string="Same Period Previous Years", + help=' Display the record for the same Date field for the last year. ') + ks_compare_period_2 = fields.Integer(string="KPI Include Period") + ks_year_period_2 = fields.Integer(string="KPI Same Period Previous Years") + + ks_multiplier_active = fields.Boolean(string="Apply Multiplier", default=False, + help="Provides the visibility of multiplier field") + ks_multiplier = fields.Float(string="Multiplier",default=1, help="Provides the multiplication of record value") + + + + # User can select custom units for measure + ks_currency_id= fields.Many2one("res.currency",string="Currency", domain="['|', ('active', '=', False), ('active', '=', True)]", + default=lambda self: self.env.company.currency_id) + + ks_unit = fields.Boolean(string="Show Custom Unit", default=False, help='Display the unit of the data.') + ks_unit_selection = fields.Selection([ + ('monetary', 'Monetary'), + ('custom', 'Custom'), + ], string="Select Unit Type", help='Select the unit to be assigned to the value. ') + ks_chart_unit = fields.Char(string="Enter Unit", size=5, default="", + help="Maximum limit 5 characters, for ex: km, m") + + # User can stop propagation of the tile item + ks_show_records = fields.Boolean(string="Show Records", default=True, help="""This field Enable the click on + Dashboard Items to view the Odoo + default view of records""") + # Field for fill temp data + ks_fill_temporal = fields.Boolean('Fill Temporal Value') + # Domain Extension field + ks_domain_extension = fields.Char('Domain Extension', help="Define conditions for filter to write manually") + ks_domain_extension_2 = fields.Char('KPI Domain Extension') + # hide legend + ks_hide_legend = fields.Boolean('Show Legend', help="Hide all legend from the chart item", default=False) + ks_radial_legend = fields.Boolean('Show Radial Legend', help="Hide all legend from the chart item", default=False) + ks_data_calculation_type = fields.Selection([('custom', 'Default Query'), + ('query', 'Custom Query')], string="Data Calculation Type", + default="custom", + help='Select the type of calculation you want to perform on the data.') + + # to show the Global / Indian / Exact Number Format + ks_data_format = fields.Selection([ + ('global', 'English Format'), + ('indian', 'Indian Format'), + ('colombian', 'Colombian Peso Format'), + ('exact', 'Exact Value')], + string='Number System', + default='global', + help="To Change the number format showing in chart to given option") + ks_button_color = fields.Char(string="Top Button Color", + default="#000000,0.99") + + + ks_is_client_action = fields.Boolean('Client Action', default=False) + ks_client_action = fields.Many2one('ir.actions.client', + string="Client Item Action", + domain="[('name','!=','App Store'),('name','!=','Updates'),('res_model','not ilike','ks_dashboard_ninja.%'),('name','!=','Discuss')]", + help="This Action will be Performed at the end of Drill Down Action") + ks_pagination_limit = fields.Integer('Pagination Limit', default=15) + + ks_multiplier_lines = fields.One2many('ks_dashboard_item.multiplier', 'ks_dashboard_item_id', + + readonly=False, store=True, + string="Multiplier Lines") + + ks_precision_digits = fields.Integer('Digits', compute="_ks_compute_precision_digits", store=True, readonly=False) + + ks_scatter_measure_x_id = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),'|','|'," + "('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Measure X") + # ks_scatter_ungroup_measure_y_id = fields.Many2one('ir.model.fields', + # domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + # "('store','=',True),'|','|'," + # "('ttype','=','integer'),('ttype','=','float')," + # "('ttype','=','monetary')]", + # string="Measure Y") + ks_is_scatter_group = fields.Boolean(string="Scatter Group By") + ks_scatter_measure_y_id = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),'|','|'," + "('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Measure Y") + ks_scatter_field_id = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),('ttype','!=','binary')," + "('ttype','!=','many2many'), ('ttype','!=','one2many')]", + string="Scatter Points") + + ks_data_label_type = fields.Selection([('percent', 'Percent'), ('value', 'Value')], string='Show Data Value Type', + help='When "Show Data Value Type" selected this field enables to select label type in percent or value', + default='percent') + ks_as_of_now = fields.Boolean("Data Till Now", + help="Display the total sum of each legends as it grows with times") + ks_radial_preview = fields.Char(string="Radial Preview", default="Radial Preview") + ks_map_preview = fields.Char(string="Map Preview", default="Map Preview") + ks_partners_map = fields.Char(compute="_compute_map_partners") + ks_country_id = fields.Many2one('res.country', string="Country") + ks_country_code = fields.Char(related="ks_country_id.code", store=True) + ks_bounds = fields.Char(compute="_compute_bounds", store=True) + + ks_funnel_preview = fields.Char(string="Funnel Preview", default="Funnel Preview") + ks_funnel_record_field = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('store','=',True),'|'," + "'|',('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Funnel Record Field") + ks_map_record_field = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('store','=',True),'|'," + "'|',('ttype','=','integer'),('ttype','=','float')," + "('ttype','=','monetary')]", + string="Map Record Field") + + ks_map_chart_relation_groupby = fields.Many2one('ir.model.fields', + domain="[('model_id','=',ks_model_id),('name','!=','id'),('name','!=','sequence')," + "('store','=',True),('ttype','!=','binary')," + "('ttype','!=','many2many'), ('ttype','!=','one2many'),('relation', '=', 'res.partner')]", + string="Map Group By") + + + + ks_bullet_preview = fields.Char(string="Bullet Preview", default="Bullet Preview") + ks_flower_view_preview = fields.Char(string="Flower Preview", default="Flower Preview") + + + upload_excel = fields.Binary(string='Upload Excel File', attachment=False) + ks_csv_field = fields.Binary(string='Upload CSV File', attachment=False) + ks_group_by_lines = fields.One2many('ks.dashboard.group.by', 'ks_dashboard_group_by_id', string="Group By Lines") + ks_csv_group_by_lines = fields.One2many('ks.dashboard.csv.group.by', 'ks_dashboard_csv_group_by_id', string="CSV Group By Lines") + filename = fields.Char(string='Filename') + name_seq = fields.Char(help="Sequential Queue ID", copy=False) + excel_bool = fields.Boolean(string='Excel Bool') + model_bool = fields.Boolean(string='Model Bool') + csv_bool = fields.Boolean(string='CSV Bool') + ks_is_external_db = fields.Boolean() + ks_host = fields.Char() + ks_port = fields.Char() + ks_db_name = fields.Char() + ks_db_password = fields.Char() + ks_db_user = fields.Char() + data_source = fields.Selection( + [('odoo', 'Odoo'), ('excel', 'Excel'), ('csv', 'CSV')], + string="Data Source",default='odoo') + + ks_ai_analysis = fields.Char(string='AI Analysis') + + + + @api.model + def create_ai_dash(self, data, ks_dash_id, model): + try: + result= [] + for item in data: + ks_measure_field_ids = [] + value = {} + chart_switch = { + 'bar': "ks_bar_chart", + 'pie': 'ks_pie_chart', + 'donut': 'ks_doughnut_chart', + 'area': 'ks_area_chart', + 'line': 'ks_line_chart', + 'polar': 'ks_polarArea_chart', + 'horizontalbar': 'ks_horizontalBar_chart', + 'table': "ks_list_view" + } + if item["chart_type"].lower() in ['bar', 'line', 'pie', 'area', 'donut', 'polar', 'horizontalbar']: + ks_measure_id = self.env['ir.model.fields'].search( + [('name', '=', item["aggregations"][0]["field"]), ('model', '=', model)]) + if ks_measure_id and ks_measure_id['ttype'] in ['integer','float','monetary']: + ks_measure_field_ids.append(ks_measure_id.id) + value["ks_chart_measure_field"] = [(6, 0, ks_measure_field_ids)] + + + ks_record_id = self.env['ir.model.fields'].search( + [('name', '=', item["group_by_column"]), ('model', '=', model)]) + if ks_record_id: + value['ks_chart_relation_groupby'] = ks_record_id.id + if ks_record_id['ttype'] == "datetime" or ks_record_id['ttype'] == "date": + value['ks_chart_date_groupby'] = "month" + + value["name"] = item["chart_name"] + + ks_model_id = self.env['ir.model'].search([('model', '=', model)]).id + value['ks_model_id'] = ks_model_id + + if item["aggregations"][0]["type"] == 'avg': + value['ks_chart_data_count_type'] = 'average' + else: + value['ks_chart_data_count_type'] = item["aggregations"][0]["type"] + + value['ks_dashboard_item_type'] = chart_switch.get(item['chart_type'], False) + value['ks_dashboard_ninja_board_id'] = ks_dash_id + if ks_measure_field_ids and ks_record_id and ks_model_id: + try: + ks_result = self.create(value) + result.append(ks_result) + except Exception as e: + result + elif item["chart_type"].lower() == "table": + value["name"] = item["chart_name"] + value['ks_dashboard_ninja_board_id'] = ks_dash_id + value['ks_dashboard_item_type'] = chart_switch.get(item['chart_type'], False) + + ks_model_id = self.env['ir.model'].search([('model', '=', model)]).id + value['ks_model_id'] = ks_model_id + + ks_measure_id = self.env['ir.model.fields'].search( + [('name', '=', item["aggregations"][0]["field"]), ('model', '=', model)]) + if ks_measure_id and ks_measure_id['ttype'] in ['integer','float','monetary']: + ks_measure_field_ids.append(ks_measure_id.id) + value["ks_list_view_group_fields"] = [(6, 0, ks_measure_field_ids)] + # value["ks_list_view_fields"] = [(6, 0, ks_measure_field_ids)] + + ks_record_id = self.env['ir.model.fields'].search( + [('name', '=', item["group_by_column"]), ('model', '=', model)]) + if ks_record_id: + value['ks_chart_relation_groupby'] = ks_record_id.id + if ks_record_id['ttype'] == "datetime" or ks_record_id['ttype'] == "date": + value['ks_chart_date_groupby'] = "month" + value['ks_list_view_type'] = 'grouped' + if ks_measure_field_ids and ks_record_id and ks_model_id: + try: + ks_result = self.create(value) + result.append(ks_result) + except Exception as e: + result + elif item["chart_type"].lower() == "kpi": + value["name"] = item["chart_name"] + value['ks_dashboard_ninja_board_id'] = ks_dash_id + value['ks_dashboard_item_type'] = "ks_kpi" + + ks_model_id = self.env['ir.model'].search([('model', '=', model)]).id + value['ks_model_id'] = ks_model_id + + ks_measure_id = self.env['ir.model.fields'].search( + [('name', '=', item["aggregations"][0]["field"]), ('model', '=', model)]) + if ks_measure_id: + value["ks_record_field"] = ks_measure_id.id + + if item["aggregations"][0]["type"] == 'avg': + value['ks_record_count_type'] = 'average' + else: + value['ks_record_count_type'] = item["aggregations"][0]["type"] + value['ks_background_color'] = "#DAEAF6,0.99" + value['ks_default_icon_color'] = "#000000,0.99" + value['ks_font_color'] = "#000000,0.99" + value['ks_button_color'] = "#000000,0.99" + + if ks_measure_id and ks_model_id: + try: + ks_result = self.create(value) + result.append(ks_result) + except Exception as e: + result + else: + pass + if len(result): + return "success" + else: + return "Abort" + except: + raise ValidationError(_("Getting invalid response from AI, please try again")) + + + # Making model, csv and excel field invisible on condition. + @api.onchange('data_source') + def make_invisible(self): + if self.data_source == 'excel': + self.excel_bool = True + self.model_bool = False + self.csv_bool = False + elif self.data_source == 'odoo': + self.model_bool = True + self.excel_bool = False + self.csv_bool = False + elif self.data_source == 'csv': + self.csv_bool = True + self.model_bool = False + self.excel_bool = False + else: + self.model_bool = False + self.excel_bool = False + self.csv_bool = False + + + # Reading the Csv file + @api.onchange('ks_csv_field') + def read_csv(self): + if self.ks_csv_field: + if ' ' in self.filename or '_' in self.filename: + try: + fp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv") + fp.write(binascii.a2b_base64(self.ks_csv_field)) + fp.seek(0) + + with open(fp.name, 'r', encoding='utf-8') as csvfile: + csv_reader = csv.reader(csvfile) + fields = [] + values = {} + header_row = next(csv_reader) + + for row in header_row: + fields.append(row) + values[row] = None + del_group_by_field = """delete from ks_dashboard_csv_new;""" + self._cr.execute(del_group_by_field) + self.env['ks.dashboard.csv.new'].search([]) + for rec in fields: + self.env['ks.dashboard.csv.new'].create({ + 'name': rec, + }) + + for line in csv_reader: + for i, field in enumerate(fields): + values[field] = line[i] + + values = {} + for field in fields: + values[field] = None + except: + raise ValidationError(_("Invalid file!")) + else: + raise ValidationError('Please add filename which contain Spaces and Underscore in there name only.') + else: + if self.ks_model_id: + model = self.env['ir.model'].search([('id', '=', self.ks_model_id.id)]) + model.unlink() + if self.ks_csv_group_by_lines: + for rec in self.ks_csv_group_by_lines: + rec.unlink() + + # Reading the Excel file + @api.onchange('upload_excel') + def _read_xls(self): + if self.upload_excel: + if ' ' in self.filename or '_' in self.filename: + try: + fp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") + fp.write(binascii.a2b_base64(self.upload_excel)) + fp.seek(0) + file_type = os.path.splitext(self.filename) + if file_type[1] == ".xlsx": + df = pd.read_excel(fp.name, engine='openpyxl') + elif file_type[1] == ".xls": + df = pd.read_excel(fp.name, engine='xlrd') + else: + raise ValueError("Unsupported file format. Only XLSX and XLS are supported.") + except Exception as e: + raise ValidationError(_(str(e))) + + values = {} + fields = df.columns.tolist() + val = {} + del_group_by_field = """delete from ks_dashboard_new;""" + self._cr.execute(del_group_by_field) + self.env['ks.dashboard.new'].search([]) + for rec in fields: + self.env['ks.dashboard.new'].create({ + 'name': str(rec), + }) + for row_no in range(df.shape[0]): + line = list(df.iloc[row_no]) + value = 0 + for field in fields: + while value < len(line): + values.update({ + field: str(line[value]), + }) + value += 1 + break + else: + raise ValidationError('Please add filename which contain Spaces and Underscore in there name only.') + else: + if self.ks_group_by_lines: + for rec in self.ks_group_by_lines: + rec.unlink() + if self.ks_model_id: + model = self.env['ir.model'].search([('id', '=', self.ks_model_id.id)]) + model.unlink() + + # Syncing the data from table to page + def data_sync(self): + print(self.id) + data = self.env['ks.dashboard.new'].search([]) + for rec in data: + self.write({ + 'ks_group_by_lines': [(0, 0, { + 'name': rec.name + })] + }) + + def csv_data_sync(self): + data = self.env['ks.dashboard.csv.new'].search([]) + for rec in data: + self.write({ + 'ks_csv_group_by_lines': [(0, 0, { + 'name': rec.name + })] + }) + + # Creating table in ir model and adding column (fields) in it. + def create_table(self): + records = self.ks_group_by_lines + dict = [] + if records: + for rec in records: + values = {} + if not rec.ttype: + raise ValidationError('Please Enter the type under Column Data Type Tab') + values.update({ + 'name': rec.name.lower().replace(' ', '_'), + 'type': rec.ttype + }) + dict.append(values) + if '_' and '-' in self.filename: + split = self.filename.lower().split('_') + split_value = '' + for res in split: + split_value += res + final_split = split_value.split('-') + elif' ' in self.filename: + final_split = self.filename.lower().split(' ') + elif'_' in self.filename: + final_split = self.filename.lower().split('_') + else: + final_split = self.filename.lower().split('.') + tablemodel = 'x_'+final_split[0]+'_'+self.name_seq + tablename = final_split[0]+' '+self.name_seq + model_creation = self.env['ir.model'].create({ + 'name': tablename, + 'model': tablemodel, + 'order': 'x_name asc, id desc', # valid order + }) + for value in dict: + column_name = value.get('name') + column_type = value.get('type') + if '/' in column_name: + column_name = value.get('name').replace('/', '_') + if ' ' in column_name: + column_name = value.get('name').replace(' ', '_') + if '(' and ')' in column_name: + column_name = value.get('name').replace(')', '').replace('(', '') + if column_name == 'name': + column_name = column_name.replace('name', 'name1') + model_creation.write({ + 'field_id': [(0, 0, { + 'name': 'x_'+column_name, + 'ttype': column_type, + 'field_description': column_name.replace('_', ' ') + })] + }) + self.env['ir.model.access'].sudo().create({ + 'name': model_creation.name + ' all_user', + 'model_id': model_creation.id, + 'perm_read': True, + 'perm_write': False, + 'perm_create': False, + 'perm_unlink': False, + }) + self.ks_model_id = model_creation.id + try: + self.insert_data_into_table(tablemodel) + except Exception as e: + raise ValidationError("Found error while table creation Error {}".format(e)) + + # Inserting data into the ir model table. + def insert_data_into_table(self, tablemodel): + if self.upload_excel: + try: + fp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") + fp.write(binascii.a2b_base64(self.upload_excel)) + fp.seek(0) + file_type = os.path.splitext(self.filename) + if file_type[1] == ".xlsx": + df = pd.read_excel(fp.name, engine='openpyxl') + elif file_type[1] == ".xls": + df = pd.read_excel(fp.name, engine='xlrd') + else: + raise ValueError("Unsupported file format. Only XLSX and XLS are supported.") + except Exception as e: + raise ValidationError("Invalid file") + df = df.astype(str) + values ={} + fields = df.columns.tolist() + user_timezone_str = self.env.context.get('tz', 'UTC') + for row_no in range(df.shape[0]): + line = list(df.iloc[row_no]) + val = {} + value = 0 + for field in fields: + if '/' in field: + field = field.replace('/', ' ') + if ' ' in field: + field = field.replace(' ', '_') + if '(' and ')' in field: + field = field.replace(')', '').replace('(', '') + if field == 'Name': + field = field.replace('Name', 'name1') + if 'Date' in field or 'Deadline' in field: + if line[value] != 'NaT' and line[value] !=False: + if self.ks_group_by_lines[value].ttype == 'datetime': + user_datetime_str = line[value] + local_datetime = datetime.strptime(user_datetime_str, '%Y-%m-%d %H:%M:%S') + user_timezone = pytz.timezone(user_timezone_str) + localized_datetime = user_timezone.localize(local_datetime) + utc_datetime = localized_datetime.astimezone(pytz.utc) + formatted_utc_datetime = utc_datetime.strftime('%Y-%m-%d %H:%M:%S') + # final_date = pd.to_timedelta(float(line[value]), unit='D') + pd.to_datetime('1899-12-30') + while (value < len(line)): + values.update({ + field: formatted_utc_datetime, + }) + value = value + 1 + break + else: + while (value < len(line)): + values.update({ + field: line[value], + }) + value = value + 1 + break + else: + while (value < len(line)): + values.update({ + field: 'Null', + }) + value = value + 1 + break + else: + while (value < len(line)): + if line[value] != 'nan' and line[value] !=False: + if '.' in line[value]: + if ',' in line[value]: + if self.ks_group_by_lines[value].ttype == 'char': + split = line[value].split(',') + split_value = '' + for res in split: + split_value += res + final_split = split_value.split('.') + final_split_value = '' + for final_res in final_split: + final_split_value += final_res + values.update({ + field: final_split_value, + }) + else: + split = line[value].split(',') + split_value = split[0] + split[1] + final_value = float(split_value) + values.update({ + field: final_value, + }) + elif '@' in line[value]: + values.update({ + field: line[value], + }) + else: + # final_value = (line[value]) + if self.ks_group_by_lines[value].ttype == 'integer' and line[value]!='nan': + try: + values.update({ + field: int(float(line[value])), + }) + except: + values.update({ + field: 0, + }) + elif self.ks_group_by_lines[value].ttype == 'float'and line[value]!='nan': + try: + values.update({ + field: float(line[value]), + }) + except: + values.update({ + field: 0.00, + }) + elif line[value] == 'nan': + values.update({ + field: 'Null', + }) + + else: + values.update({ + field: line[value], + }) + elif "'" and '+' in line[value]: + split_value = line[value].split('+') + final_split = '+' + split_value[1] + values.update({ + field: final_split, + }) + elif "'" in line[value]: + split_value = line[value].split("'") + final_split_value = '' + for res in split_value: + final_split_value += res + values.update({ + field: final_split_value, + }) + elif "NaT" in line[value]: + values.update({ + field:'Null', + }) + elif self.ks_group_by_lines[value].ttype == 'integer' and line[value]!='nan': + try: + values.update({ + field: int(float(line[value])), + }) + except: + values.update({ + field: 0, + }) + elif self.ks_group_by_lines[value].ttype == 'float' and line[value] != 'nan': + try: + values.update({ + field: float(line[value]), + }) + except: + values.update({ + field: 0.00, + }) + else: + values.update({ + field: line[value], + }) + value = value + 1 + break + else: + values.update({ + field: 'Null', + }) + value = value + 1 + break + try: + if values.keys(): + data_values = dict([('x_' + key.lower().replace(' ', '_'), values[key]) for key in values if + values[key] != 'Null']) + self.env[tablemodel].sudo().create(data_values) + except Exception as e: + raise ValidationError("Found error while table creation {}".format(e)) + + def csv_create_table(self): + records = self.ks_csv_group_by_lines + dict = [] + if records: + for rec in records: + values = {} + if not rec.ttype: + raise ValidationError('Please Enter the type under Column Data Type Tab') + values.update({ + 'name': rec.name.lower().replace(' ', '_'), + 'type': rec.ttype + }) + dict.append(values) + if '_' and '-' in self.filename: + split = self.filename.lower().split('_') + split_value = '' + for res in split: + split_value += res + final_split = split_value.split('-') + elif ' ' in self.filename: + final_split = self.filename.lower().split(' ') + elif '_' in self.filename: + final_split = self.filename.lower().split('_') + else: + final_split = self.filename.lower().split('.') + tablemodel = 'x_'+final_split[0]+'_'+self.name_seq + tablename = final_split[0]+' '+self.name_seq + model_creation = self.env['ir.model'].create({ + 'name': tablename, + 'model': tablemodel, + 'order': 'x_name asc, id desc', # valid order + }) + for value in dict: + column_name = value.get('name') + column_type = value.get('type') + if '/' in column_name: + column_name = value.get('name').replace('/', '_') + if ' ' in column_name: + column_name = value.get('name').replace(' ', '_') + if '(' and ')' in column_name: + column_name = value.get('name').replace(')', '').replace('(', '') + if column_name == 'name': + column_name = column_name.replace('name', 'name1') + model_creation.write({ + 'field_id': [(0, 0, { + 'name': 'x_'+column_name, + 'ttype': column_type, + 'field_description': column_name.replace('_', ' ') + })] + }) + self.env['ir.model.access'].sudo().create({ + 'name': model_creation.name + ' all_user', + 'model_id': model_creation.id, + 'perm_read': True, + 'perm_write': False, + 'perm_create': False, + 'perm_unlink': False, + }) + self.ks_model_id = model_creation.id + try: + self.insert_data_into_csv_table(tablemodel) + except Exception as e: + raise ValidationError("Found error while table creation Error {}".format(e)) + + + def insert_data_into_csv_table(self, tablemodel): + if self.ks_csv_field: + fp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv") + fp.write(binascii.a2b_base64(self.ks_csv_field)) + fp.seek(0) + + with open(fp.name, 'r', encoding='utf-8') as csvfile: + csv_reader = csv.reader(csvfile) + fields = [] + values = {} + field_values = {} + header_row = next(csv_reader) + user_timezone_str = self.env.context.get('tz', 'UTC') + for row in header_row: + fields.append(row) + field_values[row] = None + for line in csv_reader: + value = 0 + for field in fields: + if ' ' in field: + field = field.replace(' ', '_') + if '/' in field: + field = field.replace('/', ' ') + if '(' and ')' in field: + field = field.replace(')', '').replace('(', '') + if field == 'Name': + field = field.replace('Name', 'name1') + if 'Date' in field or 'Deadline' in field: + if line[value]: + if self.ks_csv_group_by_lines[value].ttype == 'datetime': + user_datetime_str = line[value] + local_datetime = datetime.strptime(user_datetime_str, '%Y-%m-%d %H:%M:%S') + user_timezone = pytz.timezone(user_timezone_str) + localized_datetime = user_timezone.localize(local_datetime) + utc_datetime = localized_datetime.astimezone(pytz.utc) + formatted_utc_datetime = utc_datetime.strftime('%Y-%m-%d %H:%M:%S') + final_date = line[value].split(' ')[0] + while (value < len(line)): + values.update({ + field: formatted_utc_datetime, + }) + value = value + 1 + break + else: + while (value < len(line)): + values.update({ + field: line[value], + }) + value = value + 1 + break + else: + while (value < len(line)): + values.update({ + field: 'Null', + }) + value = value + 1 + break + else: + while (value < len(line)): + if line[value]: + if '$' in line[value]: + line[value] = line[value].replace('$', '') + if '-' in line[value]: + line[value] = line[value].replace('-', '') + if '(' and ')' in line[value]: + line[value] = line[value].replace(')', '').replace('(', '') + if '.' in line[value]: + if ',' in line[value]: + if self.ks_csv_group_by_lines[value].ttype == 'char': + split = line[value].split(',') + split_value = '' + for res in split: + split_value += res + final_split = split_value.split('.') + final_split_value = '' + for final_res in final_split: + final_split_value += final_res + values.update({ + field: final_split_value, + }) + else: + split = line[value].split(',') + split_value = split[0] + split[1] + if self.ks_csv_group_by_lines[value].ttype == 'float': + values.update({ + field: float(split_value), + }) + elif self.ks_csv_group_by_lines[value].ttype == 'integer': + values.update({ + field: int(float(split_value)), + }) + else: + values.update({ + field: split_value, + }) + elif '@' in line[value]: + values.update({ + field: line[value], + }) + else: + if self.ks_csv_group_by_lines[value].ttype == 'float': + values.update({ + field: float(line[value]), + }) + elif self.ks_csv_group_by_lines[value].ttype == 'integer': + values.update({ + field: int(float(line[value])), + }) + else: + values.update({ + field: line[value], + }) + elif "'" and '+' in line[value]: + split_value = line[value].split('+') + final_split = '+' + split_value[1] + values.update({ + field: final_split, + }) + elif "'" in line[value]: + split_value = line[value].split("'") + final_split_value = '' + for res in split_value: + final_split_value += res + values.update({ + field: final_split_value, + }) + elif line[value] == ' ': + values.update({ + field: 'Null', + }) + value = value + 1 + break + elif self.ks_csv_group_by_lines[value].ttype == 'float': + values.update({ + field: float(line[value]), + }) + elif self.ks_csv_group_by_lines[value].ttype == 'integer': + values.update({ + field: int(float(line[value])), + }) + else: + values.update({ + field: line[value], + }) + value = value + 1 + break + else: + values.update({ + field: 'Null', + }) + value = value + 1 + break + try: + if values.keys(): + data_values = dict([('x_' + key.lower().replace(' ', '_'), values[key]) for key in values if + values[key] != 'Null']) + self.env[tablemodel].sudo().create(data_values) + except Exception as e: + raise ValidationError("Found error while table creation Error {}".format(e)) + + def check_target(self): + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + sales_target = self.env['ks_dashboard_ninja.item'].search([ + ('ks_dashboard_item_type', '=', 'ks_kpi'), + ('ks_send_mail', '=', True), + ('ks_stop_mail_cron', '=' , False) + ]) + menu_record = self.env.ref('ks_dashboard_ninja.board_menu_root') + menu_id_1 = menu_record.id + recipient_emails = [] + email_from = self.env['res.company'].search([], limit=1) + for res in sales_target: + if res.ks_record_count >= res.ks_standard_goal_value: + dashboard_id = res.ks_dashboard_ninja_board_id.id + action_id = res.ks_dashboard_ninja_board_id.ks_dashboard_menu_id.action.id if res.ks_dashboard_ninja_board_id.ks_dashboard_menu_id.action and res.ks_dashboard_ninja_board_id.ks_dashboard_menu_id.action.id else menu_record.action.id + for partner in res.ks_email_to_ids: + recipient_emails.append(partner.name) + kpi_mail = self.env['mail.mail'].create({ + 'body_html': 'Congratulations! The Target of '+ str(res.ks_standard_goal_value) +' for '+ res.name +' is achieved!!
' + f"Click here to check the dashboard: " + f"Dashboard Link
", + 'subject' : 'Commendable Achievement: Meeting and Exceeding Sales Targets', + 'email_from' : email_from.email, + 'email_to': ','.join(recipient_emails), + }) + kpi_mail.sudo().send() + res.ks_stop_mail_cron = True + recipient_emails = [] + + def write(self, vals): + if vals.get('ks_standard_goal_value') or vals.get('ks_record_count_type'): + self.ks_stop_mail_cron = False + return super(KsDashboardNinjaItems, self).write(vals) + + + @api.onchange('ks_year_period', 'ks_year_period_2') + def ks_year_neg_val_not_allow(self): + for rec in self: + if rec.ks_year_period < 0 or rec.ks_year_period_2 < 0 : + raise ValidationError(_(" Negative periods are not allowed ")) + + @api.onchange('ks_item_start_date', 'ks_item_end_date') + def ks_item_date_validation(self): + for rec in self: + if rec.ks_item_start_date and rec.ks_item_end_date: + if rec.ks_item_start_date > rec.ks_item_end_date: + raise ValidationError(_('Start date must be less than end date')) + + @api.onchange('ks_item_start_date_2', 'ks_item_end_date_2') + def ks_item_date_validation_2(self): + for rec in self: + if rec.ks_item_start_date_2 and rec.ks_item_end_date_2: + if rec.ks_item_start_date_2 > rec.ks_item_end_date_2: + raise ValidationError(_('Start date must be less than end date')) + + @api.onchange('ks_dashboard_item_type') + def change_data_source_to_odoo(self): + if self.ks_dashboard_item_type == 'ks_scatter_chart': + self.ks_data_calculation_type = 'custom' + + @api.onchange('ks_dashboard_item_type') + def change_data_calculation_type_to_default(self): + if self.ks_dashboard_item_type == 'ks_map_view': + self.data_source = 'odoo' + + @api.depends('ks_dashboard_item_type') + def _ks_compute_precision_digits(self): + for rec in self: + try: + precision_digits = self.sudo().env.ref('ks_dashboard_ninja.ks_dashboard_ninja_precision') + ks_precision_digits = precision_digits.digits + if ks_precision_digits < 0: + ks_precision_digits = 2 + if ks_precision_digits > 100: + ks_precision_digits = 2 + + rec.ks_precision_digits = ks_precision_digits + except Exception as E: + rec.ks_precision_digits = 2 + # default = lambda self: self.sudo().env.ref('ks_dashboard_ninja.ks_dashboard_ninja_precision') + + @api.onchange('ks_multiplier_active', 'ks_chart_measure_field', + 'ks_chart_measure_field_2' ,'ks_list_view_group_fields') + def _ks_compute_multiplier_lines(self): + for rec in self: + rec.ks_multiplier_lines = [(5, 0, 0)] + ks_chart_measure_fields = rec.ks_chart_measure_field + if rec.ks_multiplier_active: + if rec.ks_dashboard_item_type == 'ks_list_view' and rec.ks_list_view_type == 'grouped': + ks_chart_measure_fields = rec.ks_list_view_group_fields + ks_temp_list = [] + ks_chart_measure_id = [] + for ks_chart_measure_field in ks_chart_measure_fields: + ks_dict = { + 'ks_dashboard_item_id': rec.id, + 'ks_multiplier_fields': ks_chart_measure_field.ids[0], + 'ks_multiplier_value': 1 + } + ks_chart_measure_id.append(ks_chart_measure_field.ids[0]) + ks_line = self.env['ks_dashboard_item.multiplier'].create(ks_dict) + ks_temp_list.append(ks_line.id) + + if rec.ks_chart_measure_field_2: + for ks_chart_measure_field in rec.ks_chart_measure_field_2: + if ks_chart_measure_field.ids[0] not in ks_chart_measure_id: + ks_dict = { + 'ks_dashboard_item_id': rec.id, + 'ks_multiplier_fields': ks_chart_measure_field.ids[0], + 'ks_multiplier_value': 1 + } + ks_line = self.env['ks_dashboard_item.multiplier'].create(ks_dict) + ks_temp_list.append(ks_line.id) + # rec.ks_multiplier_lines = [(6, 0, [])] + # rec.ks_multiplier_lines = [(6, 0, ks_temp_list)] + rec.ks_multiplier_lines = [(6, 0, [])] + rec.ks_multiplier_lines = [(6, 0, ks_temp_list)] + + if len(rec.ks_chart_measure_field) == 0: + rec.ks_chart_cumulative_field = False + + @api.onchange('ks_list_view_type') + def _ks_onchange_ks_list_view_type(self): + for rec in self: + if rec.ks_list_view_type == 'ungrouped': + rec.ks_multiplier_active = False + + @api.onchange('ks_data_calculation_type') + def _ks_onchange_ks_data_calculation_type(self): + for rec in self: + if rec.ks_data_calculation_type == 'query': + rec.ks_list_view_type = 'ungrouped' + rec.ks_multiplier_active = False + rec.ks_record_field = False + + @api.onchange('ks_goal_lines') + def ks_is_goal_lines(self): + for rec in self: + if rec.ks_goal_enable and rec.ks_goal_lines: + rec.ks_pagination_limit = 0 + elif rec.ks_goal_enable and not rec.ks_goal_lines: + rec.ks_pagination_limit = 15 + + + @api.onchange('ks_goal_enable') + def ks_is_goal_enable(self): + for rec in self: + if not rec.ks_goal_enable : + rec.ks_goal_lines = False + rec.ks_pagination_limit = 15 + elif rec.ks_goal_enable and not rec.ks_goal_lines: + rec.ks_pagination_limit = 15 + + + @api.onchange('ks_pagination_limit') + def ks_on_negativ_limit(self): + for rec in self: + if rec.ks_pagination_limit > 0: + rec.ks_pagination_limit = rec.ks_pagination_limit + elif not rec.ks_goal_lines and rec.ks_pagination_limit <= 0: + raise ValidationError(_("Pagination limit value cannot be Negative or Zero")) + if rec.ks_goal_lines and rec.ks_pagination_limit > 0 or rec.ks_pagination_limit < 0: + raise ValidationError(_("if target lines is selected then cannot be set pagination value")) + + @api.onchange('ks_is_client_action') + def ks_on_change_item_action_to_client(self): + for rec in self: + if rec.ks_is_client_action: + rec.ks_actions = False + + @api.onchange('ks_record_data_limit_visibility') + def ks_on_change_record_data_visibility(self): + for rec in self: + if not rec.ks_record_data_limit_visibility: + rec.ks_record_data_limit = 0 + + @api.onchange('ks_fill_temporal') + def ks_onchange_fill_temporal(self): + if self.ks_fill_temporal: + self.ks_sort_by_field = self.ks_chart_relation_groupby.id + self.ks_sort_by_order = 'ASC' + else: + self.ks_sort_by_field = False + self.ks_sort_by_order = False + + @api.onchange('ks_goal_lines') + def ks_date_target_line(self): + for rec in self: + if rec.ks_chart_date_groupby in ('minute', 'hour') or rec.ks_chart_date_sub_groupby in ('minute', 'hour'): + rec.ks_goal_lines = False + return {'warning': { + 'title': _('Groupby Field aggregation'), + 'message': _( + 'Cannot create target lines when Group By Date field is set to have aggregation in ' + 'Minute and Hour case.') + }} + + @api.onchange('ks_chart_date_groupby', 'ks_chart_date_sub_groupby') + def ks_date_target(self): + for rec in self: + if (rec.ks_chart_date_groupby in ('minute', 'hour') or rec.ks_chart_date_sub_groupby in ('minute', 'hour')) \ + and rec.ks_goal_lines: + raise ValidationError(_( + "Cannot set aggregation having Date time (Hour, Minute) when target lines per date are being used." + " To proceed this, first delete target lines")) + if rec.ks_chart_relation_groupby.ttype == 'date' and rec.ks_chart_date_groupby in ('minute', 'hour'): + raise ValidationError(_('Groupby field: {} cannot be aggregated by {}').format( + rec.ks_chart_relation_groupby.display_name, rec.ks_chart_date_groupby)) + if rec.ks_chart_relation_sub_groupby.ttype == 'date' and rec.ks_chart_date_sub_groupby in ( + 'minute', 'hour'): + raise ValidationError(_('Groupby field: {} cannot be aggregated by {}').format( + rec.ks_chart_relation_sub_groupby.display_name, rec.ks_chart_date_sub_groupby)) + + def copy_data(self, default=None): + if default is None: + default = {} + if 'ks_action_lines' not in default: + default['ks_action_lines'] = [(0, 0, line.copy_data()[0]) for line in self.ks_action_lines] + + if 'ks_goal_lines' not in default: + default['ks_goal_lines'] = [(0, 0, line.copy_data()[0]) for line in self.ks_goal_lines] + if 'ks_multiplier_lines' not in default: + default['ks_multiplier_lines'] = [(0, 0, line.copy_data()[0]) for line in self.ks_multiplier_lines] + ks_many2many_field_ordering = self.ks_many2many_field_ordering + ks_list_view_group_fields = [] + ks_list_view_fields = [] + ks_chart_measure_field = [] + ks_chart_measure_field_2 = [] + if ks_many2many_field_ordering: + ks_many2many_field_ordering = json.loads(ks_many2many_field_ordering) + ks_list_view_group_fields = ks_many2many_field_ordering.get('ks_list_view_group_fields', False) + ks_list_view_fields = ks_many2many_field_ordering.get('ks_list_view_fields', False) + ks_chart_measure_field = ks_many2many_field_ordering.get('ks_chart_measure_field', False) + ks_chart_measure_field_2 = ks_many2many_field_ordering.get('ks_chart_measure_field_2', False) + if 'ks_list_view_group_fields' not in default: + default['ks_list_view_group_fields'] = ks_list_view_group_fields + if 'ks_list_view_fields' not in default: + default['ks_list_view_fields'] = ks_list_view_fields + if 'ks_chart_measure_field' not in default: + default['ks_chart_measure_field'] = ks_chart_measure_field + if 'ks_chart_measure_field_2' not in default: + default['ks_chart_measure_field_2'] = ks_chart_measure_field_2 + return super(KsDashboardNinjaItems, self).copy_data(default) + + def copy(self, default=None): + default = default or {} + res = super(KsDashboardNinjaItems, self).copy(default) + + if self.ks_dn_header_lines: + for line in self.ks_dn_header_lines: + ks_line = {} + ks_line['ks_to_do_header'] = line.ks_to_do_header + ks_line['ks_dn_item_id'] = res.id + ks_dn_header_id = self.env['ks_to.do.headers'].create(ks_line) + if line.ks_to_do_description_lines: + for ks_task in line.ks_to_do_description_lines: + ks_task_line = { + 'ks_to_do_header_id': ks_dn_header_id.id, + 'ks_description': ks_task.ks_description, + 'ks_active': ks_task.ks_active + } + + self.env['ks_to.do.description'].create(ks_task_line) + return res + + def name_get(self): + res = [] + for rec in self: + name = rec.name + if not name: + name = rec.ks_model_id.name + res.append((rec.id, name)) + + return res + + @api.model_create_multi + def create(self, values): + """ Override to save list view fields ordering """ + for i in range(len(values)): + # if not values[i].get('ks_model_id', False): + # raise ValidationError(_("Enter or create model ")) + if not values[i].get('ks_many2many_field_ordering', False): + ks_list_view_group_fields_name = [] + ks_list_view_fields_name = [] + ks_chart_measure_field_name = [] + ks_chart_measure_field_2_name = [] + if values[i].get('ks_list_view_group_fields', False) and len(values[i]['ks_list_view_group_fields'][0][2]) > 0: + for measure in values[i]['ks_list_view_group_fields'][0][2]: + ks_measure_id = self.env['ir.model.fields'].search( + [('id', '=', measure)]) + ks_list_view_group_fields_name.append(ks_measure_id.name) + if values[i].get('ks_list_view_fields', False) and len(values[i]['ks_list_view_fields'][0][2]) > 0: + for measure in values[i]['ks_list_view_fields'][0][2]: + ks_measure_id = self.env['ir.model.fields'].search( + [('id', '=', measure)]) + ks_list_view_fields_name.append(ks_measure_id.name) + if values[i].get('ks_chart_measure_field', False) and len(values[i]['ks_chart_measure_field'][0][2]) > 0: + for measure in values[i]['ks_chart_measure_field'][0][2]: + ks_measure_id = self.env['ir.model.fields'].search( + [('id', '=', measure)]) + ks_chart_measure_field_name.append(ks_measure_id.name) + if values[i].get('ks_chart_measure_field_2', False) and len(values[i]['ks_chart_measure_field_2'][0][2]) > 0: + for measure in values[i]['ks_chart_measure_field_2'][0][2]: + ks_measure_id = self.env['ir.model.fields'].search( + [('id', '=', measure)]) + ks_chart_measure_field_2_name.append(ks_measure_id.name) + ks_many2many_field_ordering = { + 'ks_list_view_fields': values[i]['ks_list_view_fields'][0][2] if values[i].get('ks_list_view_fields', False) else [], + 'ks_list_view_fields_name': ks_list_view_fields_name, + 'ks_list_view_group_fields': values[i]['ks_list_view_group_fields'][0][2] if values[i].get('ks_list_view_group_fields', False) else [], + 'ks_list_view_group_fields_name': ks_list_view_group_fields_name , + 'ks_chart_measure_field': values[i]['ks_chart_measure_field'][0][2] if values[i].get('ks_chart_measure_field', False) else [], + 'ks_chart_measure_field_name': ks_chart_measure_field_name, + 'ks_chart_measure_field_2': values[i]['ks_chart_measure_field_2'][0][2] if values[i].get('ks_chart_measure_field_2', False) else [], + 'ks_chart_measure_field_2_name': ks_chart_measure_field_2_name, + } + values[i]['ks_many2many_field_ordering'] = json.dumps(ks_many2many_field_ordering) + seq = self.env['ir.sequence'].next_by_code('ks_dashboard_ninja.item') or 'New' + values[0]['name_seq'] = seq + return super(KsDashboardNinjaItems, self).create( + values) + + @api.onchange('ks_list_view_fields') + def ks_list_view_fields_onchange(self): + ks_many2many_field_ordering = {} + for rec in self: + if rec.ks_many2many_field_ordering: + ks_many2many_field_ordering = json.loads(rec.ks_many2many_field_ordering) + ks_many2many_field_ordering['ks_list_view_fields'] = rec.ks_list_view_fields.ids + ks_many2many_field_ordering['ks_list_view_fields_name'] = [x.name for x in rec.ks_list_view_fields] + + rec.ks_many2many_field_ordering = json.dumps(ks_many2many_field_ordering) + + @api.onchange('ks_list_view_group_fields') + def ks_list_view_group_fields_onchange(self): + ks_many2many_field_ordering = {} + for rec in self: + if rec.ks_many2many_field_ordering: + ks_many2many_field_ordering = json.loads(rec.ks_many2many_field_ordering) + ks_many2many_field_ordering['ks_list_view_group_fields'] = rec.ks_list_view_group_fields.ids + ks_many2many_field_ordering['ks_list_view_group_fields_name'] = [x.name for x in rec.ks_list_view_group_fields] + rec.ks_many2many_field_ordering = json.dumps(ks_many2many_field_ordering) + + @api.onchange('ks_chart_measure_field') + def ks_chart_measure_field_onchange(self): + for rec in self: + ks_many2many_field_ordering = {} + if rec.ks_many2many_field_ordering: + ks_many2many_field_ordering = json.loads(rec.ks_many2many_field_ordering) + ks_many2many_field_ordering['ks_chart_measure_field'] = rec.ks_chart_measure_field.ids + ks_many2many_field_ordering['ks_chart_measure_field_name'] = [x.name for x in + rec.ks_chart_measure_field] + rec.ks_many2many_field_ordering = json.dumps(ks_many2many_field_ordering) + + @api.onchange('ks_chart_measure_field_2') + def ks_chart_measure_field_2_onchange(self): + ks_many2many_field_ordering = {} + for rec in self: + if rec.ks_many2many_field_ordering: + ks_many2many_field_ordering = json.loads(rec.ks_many2many_field_ordering) + ks_many2many_field_ordering['ks_chart_measure_field_2'] = rec.ks_chart_measure_field_2.ids + ks_many2many_field_ordering['ks_chart_measure_field_2_name'] = [x.name for x in + rec.ks_chart_measure_field_2] + rec.ks_many2many_field_ordering = json.dumps(ks_many2many_field_ordering) + + + + @api.onchange('ks_layout','ks_dashboard_item_theme') + def layout_four_font_change(self): + if self.ks_dashboard_item_theme != "white": + if self.ks_layout == 'layout4' and self.ks_dashboard_item_theme in ['red','blue','yellow','green']: + self.ks_font_color = '#E7495E,0.99' + self.ks_default_icon_color = "#6789C6,0.99" + elif self.ks_layout == 'layout4' and self.ks_dashboard_item_theme not in ['red','blue','yellow','green']: + self.ks_font_color = '#000000,0.99' + if self.ks_background_color=="#DAEAF6,0.99": + self.ks_default_icon_color="#000000,0.99" + else: + self.ks_default_icon_color = "#000000,0.99" + elif self.ks_layout != 'layout4' and self.ks_dashboard_item_theme not in ['red', 'blue', 'yellow', 'green']: + self.ks_font_color = "#000000,0.99" + elif self.ks_layout == 'layout6': + self.ks_font_color = "#737791,0.99" + self.ks_default_icon_color = "#737791,0.99" + elif self.ks_layout == 'layout3': + self.ks_font_color = "#6789C6,0.99" + else: + self.ks_default_icon_color = "#6789C6,0.99" + self.ks_font_color = "#000000,0.99" + elif self.ks_dashboard_item_type == 'ks_tile' and self.ks_layout == 'layout6': + self.ks_font_color = "#737791,0.99" + self.ks_default_icon_color = "#737791,0.99" + else: + if self.ks_layout == 'layout4': + self.ks_background_color = "#DAEAF6,0.99" + self.ks_font_color = "#E7495E,0.99" + self.ks_default_icon_color = "#6789C6,0.99" + elif self.ks_layout == 'layout3': + self.ks_font_color = "#6789C6,0.99" + else: + self.ks_background_color = "#DAEAF6,0.99" + self.ks_font_color = "#000000,0.99" + self.ks_default_icon_color = "#6789C6,0.99" + + # To convert color into 10% darker. Percentage amount is hardcoded. Change amt if want to change percentage. + def ks_get_dark_color(self, color, opacity): + num = int(color[1:], 16) + amt = -25 + R = (num >> 16) + amt + R = (255 if R > 255 else 0 if R < 0 else R) * 0x10000 + G = (num >> 8 & 0x00FF) + amt + G = (255 if G > 255 else 0 if G < 0 else G) * 0x100 + B = (num & 0x0000FF) + amt + B = (255 if B > 255 else 0 if B < 0 else B) + return "#" + hex(0x1000000 + R + G + B).split('x')[1][1:] + "," + opacity + + @api.onchange('ks_model_id') + def make_record_field_empty(self): + for rec in self: + rec.ks_record_field = False + rec.ks_domain = False + rec.ks_date_filter_field = False + # To show "created on" by default on date filter field on model select. + if rec.ks_model_id: + datetime_field_list = rec.ks_date_filter_field.search( + [('model_id', '=', rec.ks_model_id.id), '|', ('ttype', '=', 'date'), + ('ttype', '=', 'datetime')]).read(['id', 'name']) + for field in datetime_field_list: + if field['name'] == 'create_date': + rec.ks_date_filter_field = field['id'] + else: + rec.ks_date_filter_field = False + # Pro + rec.ks_funnel_record_field = False + rec.ks_map_chart_relation_groupby = False + rec.ks_map_record_field = False + rec.ks_record_field = False + rec.ks_chart_measure_field = False + rec.ks_chart_measure_field_2 = False + rec.ks_chart_relation_sub_groupby = False + rec.ks_chart_relation_groupby = False + rec.ks_chart_date_sub_groupby = False + rec.ks_chart_date_groupby = False + rec.ks_sort_by_field = False + rec.ks_sort_by_order = False + rec.ks_record_data_limit = False + rec.ks_list_view_fields = False + rec.ks_list_view_group_fields = False + rec.ks_action_lines = False + rec.ks_actions = False + rec.ks_domain_extension = False + rec.ks_scatter_measure_x_id = False + rec.ks_scatter_measure_y_id = False + + @api.onchange('ks_record_count', 'ks_layout', 'name', 'ks_model_id', 'ks_domain', 'ks_icon_select', + 'ks_default_icon', 'ks_icon', + 'ks_background_color', 'ks_font_color', 'ks_default_icon_color') + def ks_preview_update(self): + self.ks_preview += 1 + + @api.onchange('ks_dashboard_item_theme') + def change_dashboard_item_theme(self): + if self.ks_dashboard_item_theme == "red": + self.ks_background_color = "#DCFCE7,0.99" + if self.ks_dashboard_item_type == 'ks_tile': + self.ks_default_icon_color = "#6789C6,0.99" + self.ks_font_color = "#000000,0.99" + elif self.ks_layout == 'layout3': + self.ks_font_color = "#6789C6,0.99" + else: + self.ks_default_icon_color = "#000000,0.99" + self.ks_font_color = "#000000,0.99" + self.ks_button_color = "#000000,0.99" + elif self.ks_dashboard_item_theme == "blue": + self.ks_background_color = "#FFF4DE,0.99" + if self.ks_dashboard_item_type == 'ks_tile': + self.ks_default_icon_color = "#6789C6,0.99" + self.ks_font_color = "#000000,0.99" + elif self.ks_layout == 'layout3': + self.ks_font_color = "#6789C6,0.99" + else: + self.ks_default_icon_color = "#000000,0.99" + self.ks_font_color = "#000000,0.99" + self.ks_button_color = "#000000,0.99" + elif self.ks_dashboard_item_theme == "yellow": + self.ks_background_color = "#F3E8FF,0.99" + if self.ks_dashboard_item_type == 'ks_tile': + self.ks_default_icon_color = "#6789C6,0.99" + self.ks_font_color = "##E7495E,0.99" + elif self.ks_layout == 'layout3': + self.ks_font_color = "#6789C6,0.99" + else: + self.ks_default_icon_color = "#000000,0.99" + self.ks_font_color = "#000000,0.99" + self.ks_button_color = "#000000,0.99" + elif self.ks_dashboard_item_theme == "green": + self.ks_background_color = "#FFE2E5,0.99" + if self.ks_dashboard_item_type == 'ks_tile': + self.ks_default_icon_color = "#6789C6,0.99" + self.ks_font_color = "#000000,0.99" + elif self.ks_layout == 'layout3': + self.ks_font_color = "#6789C6,0.99" + else: + self.ks_default_icon_color = "#000000,0.99" + self.ks_font_color = "#000000,0.99" + self.ks_button_color = "#000000,0.99" + elif self.ks_dashboard_item_theme == "white": + if self.ks_layout == 'layout4': + self.ks_background_color = "#DAEAF6,0.99" + self.ks_default_icon_color = "#6789C6,0.99" + self.ks_font_color = "#E7495E,0.99" + self.ks_button_color = "#6789C6,0.99" + elif self.ks_layout == 'layout3': + self.ks_font_color = "#6789C6,0.99" + self.ks_button_color = "#000000,0.99" + else: + self.ks_background_color = "#DAEAF6,0.99" + self.ks_default_icon_color = "#6789C6,0.99" + self.ks_font_color = "#000000,0.99" + self.ks_button_color = "#000000,0.99" + + if self.ks_layout == 'layout4': + self.ks_font_color = "#DAEAF6,0.99" + self.ks_button_color = "#000000,0.99" + + elif self.ks_dashboard_item_type == 'ks_tile' and self.ks_layout == 'layout6': + self.ks_default_icon_color = "#000000,0.99" + if self.ks_dashboard_item_theme == "white": + self.ks_default_icon_color = "#000000,0.99" + + @api.depends('ks_record_count_type', 'ks_model_id', 'ks_domain', 'ks_record_field', 'ks_date_filter_field', + 'ks_item_end_date', 'ks_item_start_date', 'ks_compare_period', 'ks_year_period', + 'ks_dashboard_item_type', 'ks_domain_extension', 'ks_data_format') + def ks_get_record_count(self): + for rec in self: + rec.ks_record_count = rec._ksGetRecordCount(domain=[]) + + def unlink(self): + channel = self.env['discuss.channel'].search([('ks_dashboard_item_id', 'in', self.ids)]) + if channel: + channel.unlink() + return super(KsDashboardNinjaItems, self).unlink() + + def _ksGetRecordCount(self, domain=[]): + rec = self + if rec.ks_record_count_type == 'count' or rec.ks_dashboard_item_type == 'ks_list_view': + ks_record_count = rec.ks_fetch_model_data(rec.ks_model_name, rec.ks_domain, 'search_count', rec, domain) + elif rec.ks_record_count_type in ['sum', + 'average'] and rec.ks_record_field and rec.ks_dashboard_item_type != 'ks_list_view': + ks_records_grouped_data = rec.ks_fetch_model_data(rec.ks_model_name, rec.ks_domain, 'read_group', rec, + domain) + if ks_records_grouped_data and len(ks_records_grouped_data) > 0: + ks_records_grouped_data = ks_records_grouped_data[0] + if rec.ks_record_count_type == 'sum' and ks_records_grouped_data.get('__count', False) and ( + ks_records_grouped_data.get(rec.ks_record_field.name)): + ks_record_count = ks_records_grouped_data.get(rec.ks_record_field.name, 0) + elif rec.ks_record_count_type == 'average' and ks_records_grouped_data.get( + '__count', False) and (ks_records_grouped_data.get(rec.ks_record_field.name)): + ks_record_count = ks_records_grouped_data.get(rec.ks_record_field.name, + 0) / ks_records_grouped_data.get('__count', + 1) + else: + ks_record_count = 0 + else: + ks_record_count = 0 + else: + ks_record_count = 0 + return ks_record_count + + # Writing separate function to fetch dashboard item data + def ks_fetch_model_data(self, ks_model_name, ks_domain, ks_func, rec, domain=[]): + data = 0 + try: + if ks_domain and ks_domain != '[]' and ks_model_name: + proper_domain = self.ks_convert_into_proper_domain(ks_domain, rec, domain) + if ks_func == 'search_count': + data = self.env[ks_model_name].search_count(proper_domain) + elif ks_func == 'read_group': + data = self.env[ks_model_name].read_group(proper_domain, [rec.ks_record_field.name], [], lazy=False) + elif ks_model_name: + # Have to put extra if condition here because on load,model giving False value + proper_domain = self.ks_convert_into_proper_domain(False, rec, domain) + if ks_func == 'search_count': + data = self.env[ks_model_name].search_count(proper_domain) + + elif ks_func == 'read_group': + data = self.env[ks_model_name].read_group(proper_domain, [rec.ks_record_field.name], [], lazy=False) + else: + return [] + except Exception as e: + return 0 + return data + + def ks_convert_into_proper_domain(self, ks_domain, rec, domain=[]): + if ks_domain and "%UID" in ks_domain: + ks_domain = ks_domain.replace('"%UID"', str(self.env.user.id)) + + if ks_domain and "%MYCOMPANY" in ks_domain: + ks_domain = replace_company_domain(ks_domain, self.env.company.id, self.env.companies.ids) + + ks_date_domain = False + if rec.ks_date_filter_field: + if not rec.ks_date_filter_selection or rec.ks_date_filter_selection == "l_none": + selected_start_date = self._context.get('ksDateFilterStartDate', False) + selected_end_date = self._context.get('ksDateFilterEndDate', False) + ks_is_def_custom_filter = self._context.get('ksIsDefultCustomDateFilter', False) + ks_timezone = self._context.get('tz') or self.env.user.tz + if selected_start_date and selected_end_date and rec.ks_date_filter_field.ttype == 'datetime' and not ks_is_def_custom_filter: + selected_start_date = ks_convert_into_utc(selected_start_date, ks_timezone) + selected_end_date = ks_convert_into_utc(selected_end_date, ks_timezone) + if selected_start_date and selected_end_date and rec.ks_date_filter_field.ttype == 'date' and ks_is_def_custom_filter: + selected_start_date = ks_convert_into_local(selected_start_date, ks_timezone) + selected_end_date = ks_convert_into_local(selected_end_date, ks_timezone) + + if self._context.get('ksDateFilterSelection', False) and self._context['ksDateFilterSelection'] not in [ + 'l_none', 'l_custom']: + ks_date_data = ks_get_date(self._context.get('ksDateFilterSelection'), self, + rec.ks_date_filter_field.ttype) + selected_start_date = ks_date_data["selected_start_date"] + selected_end_date = ks_date_data["selected_end_date"] + + if selected_end_date and not selected_start_date: + ks_date_domain = [ + (rec.ks_date_filter_field.name, "<=", + selected_end_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT))] + elif selected_start_date and not selected_end_date: + ks_date_domain = [ + (rec.ks_date_filter_field.name, ">=", + selected_start_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT))] + else: + if selected_end_date and selected_start_date: + ks_date_domain = [ + (rec.ks_date_filter_field.name, ">=", + selected_start_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)), + (rec.ks_date_filter_field.name, "<=", + selected_end_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT))] + + else: + if rec.ks_date_filter_selection and rec.ks_date_filter_selection != 'l_custom': + ks_date_data = ks_get_date(rec.ks_date_filter_selection, self, rec.ks_date_filter_field.ttype) + selected_start_date = ks_date_data["selected_start_date"] + selected_end_date = ks_date_data["selected_end_date"] + else: + selected_start_date = False + selected_end_date = False + if rec.ks_item_start_date or rec.ks_item_end_date: + selected_start_date = rec.ks_item_start_date + selected_end_date = rec.ks_item_end_date + if rec.ks_date_filter_field.ttype == 'date' and rec.ks_item_start_date and rec.ks_item_end_date: + ks_timezone = self._context.get('tz') or self.env.user.tz + selected_start_date = ks_convert_into_local(rec.ks_item_start_date, ks_timezone) + selected_end_date = ks_convert_into_local(rec.ks_item_end_date, ks_timezone) + + if selected_start_date and selected_end_date: + if rec.ks_compare_period: + ks_compare_period = abs(rec.ks_compare_period) + if ks_compare_period > 100: + ks_compare_period = 100 + if rec.ks_compare_period > 0: + selected_end_date = selected_end_date + ( + selected_end_date - selected_start_date) * ks_compare_period + if rec.ks_date_filter_field.ttype == "date" and rec.ks_date_filter_selection == 'l_day': + selected_end_date = selected_end_date + timedelta(days=ks_compare_period) + elif rec.ks_compare_period < 0: + selected_start_date = selected_start_date - ( + selected_end_date - selected_start_date) * ks_compare_period + if rec.ks_date_filter_field.ttype == "date" and rec.ks_date_filter_selection == 'l_day': + selected_start_date = selected_end_date - timedelta(days=ks_compare_period) + + if rec.ks_year_period and rec.ks_year_period != 0 and rec.ks_dashboard_item_type: + abs_year_period = abs(rec.ks_year_period) + sign_yp = rec.ks_year_period / abs_year_period + if abs_year_period > 100: + abs_year_period = 100 + date_field_name = rec.ks_date_filter_field.name + + ks_date_domain = ['&', (date_field_name, ">=", + fields.datetime.strftime(selected_start_date, + DEFAULT_SERVER_DATETIME_FORMAT)), + (date_field_name, "<=", + fields.datetime.strftime(selected_end_date, DEFAULT_SERVER_DATETIME_FORMAT))] + + for p in range(1, abs_year_period + 1): + ks_date_domain.insert(0, '|') + ks_date_domain.extend(['&', (date_field_name, ">=", fields.datetime.strftime( + selected_start_date - relativedelta.relativedelta(years=p) * sign_yp, + DEFAULT_SERVER_DATETIME_FORMAT)), + (date_field_name, "<=", fields.datetime.strftime( + selected_end_date - relativedelta.relativedelta(years=p) + * sign_yp, DEFAULT_SERVER_DATETIME_FORMAT))]) + else: + selected_start_date = fields.datetime.strftime(selected_start_date, + DEFAULT_SERVER_DATETIME_FORMAT) + selected_end_date = fields.datetime.strftime(selected_end_date, DEFAULT_SERVER_DATETIME_FORMAT) + ks_date_domain = [(rec.ks_date_filter_field.name, ">=", selected_start_date), + (rec.ks_date_filter_field.name, "<=", selected_end_date)] + elif selected_start_date and not selected_end_date: + selected_start_date = fields.datetime.strftime(selected_start_date, DEFAULT_SERVER_DATETIME_FORMAT) + ks_date_domain = [(rec.ks_date_filter_field.name, ">=", selected_start_date)] + elif selected_end_date and not selected_start_date: + selected_end_date = fields.datetime.strftime(selected_end_date, DEFAULT_SERVER_DATETIME_FORMAT) + ks_date_domain = [(rec.ks_date_filter_field.name, "<=", selected_end_date)] + else: + ks_date_domain = [] + + proper_domain = safe_eval(ks_domain) if ks_domain else [] + if ks_date_domain: + proper_domain.extend(ks_date_domain) + if rec.ks_domain_extension: + ks_domain_extension = rec.ks_convert_domain_extension(rec.ks_domain_extension, rec) + proper_domain.extend(ks_domain_extension) + if domain: + proper_domain.extend(domain) + + return proper_domain + + def ks_convert_domain_extension(self, ks_extensiom_domain, rec): + if ks_extensiom_domain and "%UID" in ks_extensiom_domain: + ks_extensiom_domain = ks_extensiom_domain.replace('"%UID"', str(self.env.user.id)) + if "%UID" in ks_extensiom_domain: + ks_extensiom_domain = ks_extensiom_domain.replace("'%UID'", str(self.env.user.id)) + print(ks_extensiom_domain) + + if ks_extensiom_domain and "%MYCOMPANY" in ks_extensiom_domain: + ks_extensiom_domain = replace_company_domain(ks_extensiom_domain, self.env.company.id, self.env.companies.ids) + if "%MYCOMPANY" in ks_extensiom_domain: + ks_extensiom_domain = replace_company_domain(ks_extensiom_domain, self.env.company.id, self.env.companies.ids) + + ks_domain = safe_eval(ks_extensiom_domain) + return ks_domain + + @api.onchange('ks_domain_extension') + def ks_onchange_domain_extension(self): + if self.ks_domain_extension: + proper_domain = [] + try: + ks_domain_extension = self.ks_domain_extension + if "%UID" in ks_domain_extension: + ks_domain_extension = ks_domain_extension.replace("%UID", str(self.env.user.id)) + if "%MYCOMPANY" in ks_domain_extension: + ks_domain_extension = replace_company_domain(ks_domain_extension, self.env.company.id, self.env.companies.ids) + self.env[self.ks_model_name].search_count(safe_eval(ks_domain_extension)) + except Exception: + raise ValidationError( + "Domain Extension Syntax is wrong. \nProper Syntax Example :[['
+
+ Create amazing reports with the powerful & smart Odoo
+ Dashboard Ninja app with a
+ refreshed, modern user interface for effortless navigation
+ and enhanced user
+ experience. Enjoy the simplified workflow that makes
+ everything easy, from
+ generating dashboards to collaborating with your team
+ members.
+
+
+ + 7th April, 2025 +
++ New Theme +
++ Extract Chart Insights with AI +
++ Bookmark Dashboard +
++ Internal Chat +
++ Dashboard Overview +
++ Chart Visualization +
+
+ Empower your business with Odoo Best Dashboard
+
+ tools—customized, insightful, and ready to scale.
+
+ We are available 24/7 for your service. Contact us today! +
+ +
+ + Supports 17 impressive Odoo dashboard item types for easy + business data interpretation. (Tiles, Line Chart, List View, + Bar Chart, Horizontal Bar Chart, Area Chart, To-do Item, + Polar Area Chart, Pie Chart, Doughnut Charts, Flower Chart, + Funnel Chart, Radial Chart, Radar Chart, Scatter Chart, + Bullet Chart & Map view) +
+ +
+
+ + Display key performance indicators (KIPs) in small + square-shaped visualizations for a quick glance +
+
+ + Visualize data trends, changes, or relationships + over time by connecting data points with straight + lines. +
+
+ + Present detailed data in a tabular format with + rows and columns for easy sorting and comparison. +
+
+ + Visualize categorical data with rectangular + vertical bars with the height proportionate to the + value they represent. +
+
+ + Represent categorical data with rectangular + horizontal bars, representing the difference + between data categories. +
+
+ + Organize all tasks or actions that need to be + completed in the form of a checklist, ensuring + easy tracking of progress. +
+
+ + Represent cyclical data using radial lines + separating segments, with each segment’s area + representing its value. +
+
+ + Display your data in a circular graph, with each + slice of a circle representing a fraction or + proportionate part of the whole. +
+
+ + Similar to a pie chart with a central hole, + providing a clean and modern look. +
+
+ + Visualize multivariate data wherein data points + are displayed as petals radiating from a central + point, with each petal’s size or color + representing its value. +
+
+ + Visualize data as it moves through different + stages of a process with decreasing segments. + Ideal to visualize conversion rates or sales + pipelines. +
+
+ + Represent multivariate data along concentric + circles, where each circle represents a different + variable or category. +
+
+ + A variation of a bar chart with a reference line + and markers to visualize performance against the + target or benchmark. +
+
+ + Visualize the relationship between two continuous + variables of a data set, plotted across the X-Y + axis. +
+
+ + Display multivariate data stacked at an axis with + the same central point, resembling a spider’s web. +
+
+ + Visualize spatial data by plotting it on a + geographical map for location-based analysis. +
+
+ + Display cumulative data by filling the area + beneath the line chart, illustrating the changes + over time or the magnitude of data. +
++ The ultimate time and money saving Odoo tool for your + Business. +
+
+ + Generate smart, interactive charts with a single click. +
+ ++ Upgrade to a modern, intuitive, and scalable interface. +
+ ++ Ksolves will provide FREE 90 days support for any doubt, + queries, and bug fixing (excluding data recovery) or any + type of issues related to this module. This is applicable + from the date of purchase. +
+
+
+ + You know us as the makers of the Odoo "Dashboard Ninja" . . +
++ You know us as the makers of the Odoo "Dashboard Ninja" . . +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Invest your trust like our elite clientele did! +
+
+
+
+
+
+
+
+
+
+
+
+
+ + Related Products You May Be Interested In! +
+
+ | + + | + +
+ |
+
+
+
+
+
+ Perfect Combo
+
+ |
+
+ Peerless, powerful, and personalized apps to streamline + every workflow. +
+ +
+ Take a look at our other Odoo apps designed to
+
+ make your life easier with a smooth workflow. Don’t
+
+ miss the chance to uplift your Odoo ERP experience
+
+ with our apps!
+
+ + Dashboard Ninja Advance breaks all the limitations of + productivity by simplifying complex data into attractive + charts... +
+ Learn More +
+ + An app which manages the List Views on the fly and + endeavor a quick and effortless way to view/manage the + desired data, where you’ve multifarious options to... +
+ Learn More +
+ + Connect multiple WooCommerce stores to the Odoo ERP and + reach more customers with our high-end WooCommerce + Connector. +
+ Learn More +
+ + Bring productivity and enthusiasm into your work by + applying the Odoo Arc Backend Theme. +
+ Learn More +
+ + Gantt View Base is the fundamental framework that creates + the groundwork for using Gantt View. It works as a + prerequisite for using... +
+ Learn More +
+ + ReportMate extracts all the needed information for all the + fields of any Odoo Model and displays complex data in a + single view... +
+ Learn More +
+ + With a comprehensive list of financial reports, Dynamic + Financial Report has carved out its niche among the elite + bookkeeping... +
+ Learn More +
+ + Keep hurdles away from the way of your business with a + highly effective eCommerce management solution... +
+ Learn More +
+ + Streamline your workflow and save valuable time and effort + by implementing Instant Access, a cutting-edge management + application... +
+ Learn More +
+ + Sync all your Google mails in your Odoo account without + any hassle by using the Ksolves Gmail Connector! This + integration allows you to manage emails... +
+ Learn More ++ Upscale your business with expert services from an Odoo Gold + Partner. +
+
+
+ Website Application
+ Development
+
+ + Module Development +
+
+ + Theme Development +
+
+ + Implementation +
+
+ + Customization +
+
+ + Integration +
+
+ + Migration +
+
+
+ Support &
+ Maintenance
+
+ + Consulting +
++ Our clients love us! Hear from those who have made + data-driven decisions with Dashboard Ninja. +
++ Exceptional application and outstanding customer care + indeed. My experience with them was truly remarkable, + and I highly recommend their services to anyone + looking for top-tier solutions. +
+
+ + They were very easy to schedule time with and when I + got on the call, they answered all my questions. They + also understood beyond my questions and showed me some + features that would help me. +
+
+ + Ksolves is 24/7 open to discuss your thoughts and queries! +
+
+
+ + Explore intuitive features that empower you to turn your + Odoo data into actionable insights. Track key performance + indicators and make informed, data-driven decisions. +
+
+ Generate multiple items for your Odoo Dashboard with AI.
+ Gain the flexibility to
+
+ choose from a range of charts and graphs and save them
+ with ease.
+
+
+
+ + Bookmark your key dashboards and easily access them for + faster data analysis. Effortlessly remove dashboards + from the bookmark when you are done with them. +
+
+ + Conveniently view your data over different devices such + as Mobile, Desktop, or Tablet for easy access. It offers + an optimized browsing experience to track business + performance on the go. +
+
+ + Interpret complex charts with single-click AI-generated + explanations! Identify hidden patterns & trends and + make informed data-backed business decisions. +
+
+ + Present data in a timely manner over a selected period + in the date filter option. Select from 20 predefined + date filters (Last 7 days, Last 30 days, Last 90 days, + & more) or choose a custom date. +
++ Collaborate with your team members with the chat feature + on every item of a dashboard. A dedicated channel is + created for conversations on each item, and all channels + are organized in a unified inbox. +
+
+
+ + Switch from one graph style to another with just a + single click, retaining the same data. Effortlessly + analyze your data with the desired data visualization + chart! +
++ Now just provide the keyword, and AI will create the + dashboard items for you within seconds. +
+
+
+ + Easily upload Excel or CSV files, and let the system + automatically synchronize the data. It will then process + the data and create charts. +
+
+ + Exported data items can be used for offline + presentations, seminars, or emails. +
++ Upgrades the data in real-time to give a 360 view of the + business performance. This feature will update the data + if any changes occur from the backend in real-time or + after a specific interval of time. +
+
+
+ + Modify the degree of details presented in a dashboard + entity. Drill-down to access detailed data or drill-up + to get a broader view of information. +
+
+ Save your time and effort! Generate a complete Odoo
+ Dashboard
+ a specific model within a few clicks with AI efficiency.
+
+
+
+ Export an entire Odoo Dashboard or Specific Odoo Dashboard
+ Item to a same system or
+ different Odoo instances for saving time and effort while
+ migrating from one system to another.
+
+
+ 5 Predefined Odoo dashboards are provided for different
+ verticals of a
+ company(Sales, CRM, Account, Inventory, & POS).
+ Customize them as per your need.
+
+ + Group By, Sort By, Limit, filter condition to get + desired data for any dashboard item as per your + requirement. +
+
+ + Get company-oriented details by filtering the logged-in + user data using this dynamic filter. +
+
+ + Filter logged-in user data using %UID in domain filter + to see the results related to the logged-in user. +
+
+ + Use the list-view item to view multi-value data sets in + a grouped or ungrouped manner. Example: To see the total + sales, average sales, total quotations of the top 10 + sales persons. +
+
+ Design your dashboard layouts by resizing, dragging,
+ repositioning the dashboard
+
+ items, and renaming the dashboard heading for arranging
+ all items as per your need.
+
+ + Beautiful, interactive, and outstanding multi-colored + themes can be added to your dashboard. +
+
+
+ Configure the dashboard name, menu, group access, choose
+ the sequence,
+ and more while creating interactive presentable dashboards
+ for your business.
+
+
+ + Control who can access the Odoo dashboard with a single + click for robust security. +
+
+
+ A user can edit the basic information of any dashboard
+ items instantly with
+ Quick Edit Mode without going to the detailed edit screen.
+
+
+ Odoo Dashboard Ninja gives you the freedom to work in
+ Right-to-Left
+ language-oriented interface for increasing productivity.
+
+
+
+ Sometimes you need a quick answer to your
+ question. You can get it right away here.
+
+
+ Every update brings new capabilities. Keep track of the latest
+ features and
+ enhancements designed to visualize and analyze your data like
+ a pro.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \n * \tThe destination magnification factor can also be specified when goto is a page number or a named destination. (see documentation below)\n * (set magFactor in options). XYZ is the default.\n *
\n *\n * Links, Text, Popup, and FreeText are supported.\n *
\n *\n * Options In PDF spec Not Implemented Yet\n *
If pageNumber is specified, top and zoom may also be specified
\n * @name link\n * @function\n * @param {number} x\n * @param {number} y\n * @param {number} w\n * @param {number} h\n * @param {Object} options\n */\n jsPDFAPI.link = function(x, y, w, h, options) {\n var pageInfo = this.internal.getCurrentPageInfo();\n var getHorizontalCoordinateString = this.internal.getCoordinateString;\n var getVerticalCoordinateString = this.internal.getVerticalCoordinateString;\n\n pageInfo.pageContext.annotations.push({\n finalBounds: {\n x: getHorizontalCoordinateString(x),\n y: getVerticalCoordinateString(y),\n w: getHorizontalCoordinateString(x + w),\n h: getVerticalCoordinateString(y + h)\n },\n options: options,\n type: \"link\"\n });\n };\n\n /**\n * Currently only supports single line text.\n * Returns the width of the text/link\n *\n * @name textWithLink\n * @function\n * @param {string} text\n * @param {number} x\n * @param {number} y\n * @param {Object} options\n * @returns {number} width the width of the text/link\n */\n jsPDFAPI.textWithLink = function(text, x, y, options) {\n var totalLineWidth = this.getTextWidth(text);\n var lineHeight = this.internal.getLineHeight() / this.internal.scaleFactor;\n var linkHeight, linkWidth;\n\n // Checking if maxWidth option is passed to determine lineWidth and number of lines for each line\n if (options.maxWidth !== undefined) {\n var { maxWidth } = options;\n linkWidth = maxWidth;\n var numOfLines = this.splitTextToSize(text, linkWidth).length;\n linkHeight = Math.ceil(lineHeight * numOfLines);\n } else {\n linkWidth = totalLineWidth;\n linkHeight = lineHeight;\n }\n\n this.text(text, x, y, options);\n\n //TODO We really need the text baseline height to do this correctly.\n // Or ability to draw text on top, bottom, center, or baseline.\n y += lineHeight * 0.2;\n //handle x position based on the align option\n if (options.align === \"center\") {\n x = x - totalLineWidth / 2; //since starting from center move the x position by half of text width\n }\n if (options.align === \"right\") {\n x = x - totalLineWidth;\n }\n this.link(x, y - lineHeight, linkWidth, linkHeight, options);\n return totalLineWidth;\n };\n\n //TODO move into external library\n /**\n * @name getTextWidth\n * @function\n * @param {string} text\n * @returns {number} txtWidth\n */\n jsPDFAPI.getTextWidth = function(text) {\n var fontSize = this.internal.getFontSize();\n var txtWidth =\n (this.getStringUnitWidth(text) * fontSize) / this.internal.scaleFactor;\n return txtWidth;\n };\n\n return this;\n})(jsPDF.API);\n","/**\n * @license\n * Copyright (c) 2017 Aras Abbasi\n *\n * Licensed under the MIT License.\n * http://opensource.org/licenses/mit-license\n */\n\nimport { jsPDF } from \"../jspdf.js\";\n\n/**\n * jsPDF arabic parser PlugIn\n *\n * @name arabic\n * @module\n */\n(function(jsPDFAPI) {\n \"use strict\";\n\n /**\n * Arabic shape substitutions: char code => (isolated, final, initial, medial).\n * Arabic Substition A\n */\n var arabicSubstitionA = {\n 0x0621: [0xfe80], // ARABIC LETTER HAMZA\n 0x0622: [0xfe81, 0xfe82], // ARABIC LETTER ALEF WITH MADDA ABOVE\n 0x0623: [0xfe83, 0xfe84], // ARABIC LETTER ALEF WITH HAMZA ABOVE\n 0x0624: [0xfe85, 0xfe86], // ARABIC LETTER WAW WITH HAMZA ABOVE\n 0x0625: [0xfe87, 0xfe88], // ARABIC LETTER ALEF WITH HAMZA BELOW\n 0x0626: [0xfe89, 0xfe8a, 0xfe8b, 0xfe8c], // ARABIC LETTER YEH WITH HAMZA ABOVE\n 0x0627: [0xfe8d, 0xfe8e], // ARABIC LETTER ALEF\n 0x0628: [0xfe8f, 0xfe90, 0xfe91, 0xfe92], // ARABIC LETTER BEH\n 0x0629: [0xfe93, 0xfe94], // ARABIC LETTER TEH MARBUTA\n 0x062a: [0xfe95, 0xfe96, 0xfe97, 0xfe98], // ARABIC LETTER TEH\n 0x062b: [0xfe99, 0xfe9a, 0xfe9b, 0xfe9c], // ARABIC LETTER THEH\n 0x062c: [0xfe9d, 0xfe9e, 0xfe9f, 0xfea0], // ARABIC LETTER JEEM\n 0x062d: [0xfea1, 0xfea2, 0xfea3, 0xfea4], // ARABIC LETTER HAH\n 0x062e: [0xfea5, 0xfea6, 0xfea7, 0xfea8], // ARABIC LETTER KHAH\n 0x062f: [0xfea9, 0xfeaa], // ARABIC LETTER DAL\n 0x0630: [0xfeab, 0xfeac], // ARABIC LETTER THAL\n 0x0631: [0xfead, 0xfeae], // ARABIC LETTER REH\n 0x0632: [0xfeaf, 0xfeb0], // ARABIC LETTER ZAIN\n 0x0633: [0xfeb1, 0xfeb2, 0xfeb3, 0xfeb4], // ARABIC LETTER SEEN\n 0x0634: [0xfeb5, 0xfeb6, 0xfeb7, 0xfeb8], // ARABIC LETTER SHEEN\n 0x0635: [0xfeb9, 0xfeba, 0xfebb, 0xfebc], // ARABIC LETTER SAD\n 0x0636: [0xfebd, 0xfebe, 0xfebf, 0xfec0], // ARABIC LETTER DAD\n 0x0637: [0xfec1, 0xfec2, 0xfec3, 0xfec4], // ARABIC LETTER TAH\n 0x0638: [0xfec5, 0xfec6, 0xfec7, 0xfec8], // ARABIC LETTER ZAH\n 0x0639: [0xfec9, 0xfeca, 0xfecb, 0xfecc], // ARABIC LETTER AIN\n 0x063a: [0xfecd, 0xfece, 0xfecf, 0xfed0], // ARABIC LETTER GHAIN\n 0x0641: [0xfed1, 0xfed2, 0xfed3, 0xfed4], // ARABIC LETTER FEH\n 0x0642: [0xfed5, 0xfed6, 0xfed7, 0xfed8], // ARABIC LETTER QAF\n 0x0643: [0xfed9, 0xfeda, 0xfedb, 0xfedc], // ARABIC LETTER KAF\n 0x0644: [0xfedd, 0xfede, 0xfedf, 0xfee0], // ARABIC LETTER LAM\n 0x0645: [0xfee1, 0xfee2, 0xfee3, 0xfee4], // ARABIC LETTER MEEM\n 0x0646: [0xfee5, 0xfee6, 0xfee7, 0xfee8], // ARABIC LETTER NOON\n 0x0647: [0xfee9, 0xfeea, 0xfeeb, 0xfeec], // ARABIC LETTER HEH\n 0x0648: [0xfeed, 0xfeee], // ARABIC LETTER WAW\n 0x0649: [0xfeef, 0xfef0, 64488, 64489], // ARABIC LETTER ALEF MAKSURA\n 0x064a: [0xfef1, 0xfef2, 0xfef3, 0xfef4], // ARABIC LETTER YEH\n 0x0671: [0xfb50, 0xfb51], // ARABIC LETTER ALEF WASLA\n 0x0677: [0xfbdd], // ARABIC LETTER U WITH HAMZA ABOVE\n 0x0679: [0xfb66, 0xfb67, 0xfb68, 0xfb69], // ARABIC LETTER TTEH\n 0x067a: [0xfb5e, 0xfb5f, 0xfb60, 0xfb61], // ARABIC LETTER TTEHEH\n 0x067b: [0xfb52, 0xfb53, 0xfb54, 0xfb55], // ARABIC LETTER BEEH\n 0x067e: [0xfb56, 0xfb57, 0xfb58, 0xfb59], // ARABIC LETTER PEH\n 0x067f: [0xfb62, 0xfb63, 0xfb64, 0xfb65], // ARABIC LETTER TEHEH\n 0x0680: [0xfb5a, 0xfb5b, 0xfb5c, 0xfb5d], // ARABIC LETTER BEHEH\n 0x0683: [0xfb76, 0xfb77, 0xfb78, 0xfb79], // ARABIC LETTER NYEH\n 0x0684: [0xfb72, 0xfb73, 0xfb74, 0xfb75], // ARABIC LETTER DYEH\n 0x0686: [0xfb7a, 0xfb7b, 0xfb7c, 0xfb7d], // ARABIC LETTER TCHEH\n 0x0687: [0xfb7e, 0xfb7f, 0xfb80, 0xfb81], // ARABIC LETTER TCHEHEH\n 0x0688: [0xfb88, 0xfb89], // ARABIC LETTER DDAL\n 0x068c: [0xfb84, 0xfb85], // ARABIC LETTER DAHAL\n 0x068d: [0xfb82, 0xfb83], // ARABIC LETTER DDAHAL\n 0x068e: [0xfb86, 0xfb87], // ARABIC LETTER DUL\n 0x0691: [0xfb8c, 0xfb8d], // ARABIC LETTER RREH\n 0x0698: [0xfb8a, 0xfb8b], // ARABIC LETTER JEH\n 0x06a4: [0xfb6a, 0xfb6b, 0xfb6c, 0xfb6d], // ARABIC LETTER VEH\n 0x06a6: [0xfb6e, 0xfb6f, 0xfb70, 0xfb71], // ARABIC LETTER PEHEH\n 0x06a9: [0xfb8e, 0xfb8f, 0xfb90, 0xfb91], // ARABIC LETTER KEHEH\n 0x06ad: [0xfbd3, 0xfbd4, 0xfbd5, 0xfbd6], // ARABIC LETTER NG\n 0x06af: [0xfb92, 0xfb93, 0xfb94, 0xfb95], // ARABIC LETTER GAF\n 0x06b1: [0xfb9a, 0xfb9b, 0xfb9c, 0xfb9d], // ARABIC LETTER NGOEH\n 0x06b3: [0xfb96, 0xfb97, 0xfb98, 0xfb99], // ARABIC LETTER GUEH\n 0x06ba: [0xfb9e, 0xfb9f], // ARABIC LETTER NOON GHUNNA\n 0x06bb: [0xfba0, 0xfba1, 0xfba2, 0xfba3], // ARABIC LETTER RNOON\n 0x06be: [0xfbaa, 0xfbab, 0xfbac, 0xfbad], // ARABIC LETTER HEH DOACHASHMEE\n 0x06c0: [0xfba4, 0xfba5], // ARABIC LETTER HEH WITH YEH ABOVE\n 0x06c1: [0xfba6, 0xfba7, 0xfba8, 0xfba9], // ARABIC LETTER HEH GOAL\n 0x06c5: [0xfbe0, 0xfbe1], // ARABIC LETTER KIRGHIZ OE\n 0x06c6: [0xfbd9, 0xfbda], // ARABIC LETTER OE\n 0x06c7: [0xfbd7, 0xfbd8], // ARABIC LETTER U\n 0x06c8: [0xfbdb, 0xfbdc], // ARABIC LETTER YU\n 0x06c9: [0xfbe2, 0xfbe3], // ARABIC LETTER KIRGHIZ YU\n 0x06cb: [0xfbde, 0xfbdf], // ARABIC LETTER VE\n 0x06cc: [0xfbfc, 0xfbfd, 0xfbfe, 0xfbff], // ARABIC LETTER FARSI YEH\n 0x06d0: [0xfbe4, 0xfbe5, 0xfbe6, 0xfbe7], //ARABIC LETTER E\n 0x06d2: [0xfbae, 0xfbaf], // ARABIC LETTER YEH BARREE\n 0x06d3: [0xfbb0, 0xfbb1] // ARABIC LETTER YEH BARREE WITH HAMZA ABOVE\n };\n\n /*\n var ligaturesSubstitutionA = {\n 0xFBEA: []// ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM\n };\n */\n\n var ligatures = {\n 0xfedf: {\n 0xfe82: 0xfef5, // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM\n 0xfe84: 0xfef7, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM\n 0xfe88: 0xfef9, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM\n 0xfe8e: 0xfefb // ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM\n },\n 0xfee0: {\n 0xfe82: 0xfef6, // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM\n 0xfe84: 0xfef8, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM\n 0xfe88: 0xfefa, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM\n 0xfe8e: 0xfefc // ARABIC LIGATURE LAM WITH ALEF FINAL FORM\n },\n 0xfe8d: { 0xfedf: { 0xfee0: { 0xfeea: 0xfdf2 } } }, // ALLAH\n 0x0651: {\n 0x064c: 0xfc5e, // Shadda + Dammatan\n 0x064d: 0xfc5f, // Shadda + Kasratan\n 0x064e: 0xfc60, // Shadda + Fatha\n 0x064f: 0xfc61, // Shadda + Damma\n 0x0650: 0xfc62 // Shadda + Kasra\n }\n };\n\n var arabic_diacritics = {\n 1612: 64606, // Shadda + Dammatan\n 1613: 64607, // Shadda + Kasratan\n 1614: 64608, // Shadda + Fatha\n 1615: 64609, // Shadda + Damma\n 1616: 64610 // Shadda + Kasra\n };\n\n var alfletter = [1570, 1571, 1573, 1575];\n\n var noChangeInForm = -1;\n var isolatedForm = 0;\n var finalForm = 1;\n var initialForm = 2;\n var medialForm = 3;\n\n jsPDFAPI.__arabicParser__ = {};\n\n //private\n var isInArabicSubstitutionA = (jsPDFAPI.__arabicParser__.isInArabicSubstitutionA = function(\n letter\n ) {\n return typeof arabicSubstitionA[letter.charCodeAt(0)] !== \"undefined\";\n });\n\n var isArabicLetter = (jsPDFAPI.__arabicParser__.isArabicLetter = function(\n letter\n ) {\n return (\n typeof letter === \"string\" &&\n /^[\\u0600-\\u06FF\\u0750-\\u077F\\u08A0-\\u08FF\\uFB50-\\uFDFF\\uFE70-\\uFEFF]+$/.test(\n letter\n )\n );\n });\n\n var isArabicEndLetter = (jsPDFAPI.__arabicParser__.isArabicEndLetter = function(\n letter\n ) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length <= 2\n );\n });\n\n var isArabicAlfLetter = (jsPDFAPI.__arabicParser__.isArabicAlfLetter = function(\n letter\n ) {\n return (\n isArabicLetter(letter) && alfletter.indexOf(letter.charCodeAt(0)) >= 0\n );\n });\n\n jsPDFAPI.__arabicParser__.arabicLetterHasIsolatedForm = function(letter) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length >= 1\n );\n };\n\n var arabicLetterHasFinalForm = (jsPDFAPI.__arabicParser__.arabicLetterHasFinalForm = function(\n letter\n ) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length >= 2\n );\n });\n\n jsPDFAPI.__arabicParser__.arabicLetterHasInitialForm = function(letter) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length >= 3\n );\n };\n\n var arabicLetterHasMedialForm = (jsPDFAPI.__arabicParser__.arabicLetterHasMedialForm = function(\n letter\n ) {\n return (\n isArabicLetter(letter) &&\n isInArabicSubstitutionA(letter) &&\n arabicSubstitionA[letter.charCodeAt(0)].length == 4\n );\n });\n\n var resolveLigatures = (jsPDFAPI.__arabicParser__.resolveLigatures = function(\n letters\n ) {\n var i = 0;\n var tmpLigatures = ligatures;\n var result = \"\";\n var effectedLetters = 0;\n\n for (i = 0; i < letters.length; i += 1) {\n if (typeof tmpLigatures[letters.charCodeAt(i)] !== \"undefined\") {\n effectedLetters++;\n tmpLigatures = tmpLigatures[letters.charCodeAt(i)];\n\n if (typeof tmpLigatures === \"number\") {\n result += String.fromCharCode(tmpLigatures);\n tmpLigatures = ligatures;\n effectedLetters = 0;\n }\n if (i === letters.length - 1) {\n tmpLigatures = ligatures;\n result += letters.charAt(i - (effectedLetters - 1));\n i = i - (effectedLetters - 1);\n effectedLetters = 0;\n }\n } else {\n tmpLigatures = ligatures;\n result += letters.charAt(i - effectedLetters);\n i = i - effectedLetters;\n effectedLetters = 0;\n }\n }\n\n return result;\n });\n\n jsPDFAPI.__arabicParser__.isArabicDiacritic = function(letter) {\n return (\n letter !== undefined &&\n arabic_diacritics[letter.charCodeAt(0)] !== undefined\n );\n };\n\n var getCorrectForm = (jsPDFAPI.__arabicParser__.getCorrectForm = function(\n currentChar,\n beforeChar,\n nextChar\n ) {\n if (!isArabicLetter(currentChar)) {\n return -1;\n }\n\n if (isInArabicSubstitutionA(currentChar) === false) {\n return noChangeInForm;\n }\n if (\n !arabicLetterHasFinalForm(currentChar) ||\n (!isArabicLetter(beforeChar) && !isArabicLetter(nextChar)) ||\n (!isArabicLetter(nextChar) && isArabicEndLetter(beforeChar)) ||\n (isArabicEndLetter(currentChar) && !isArabicLetter(beforeChar)) ||\n (isArabicEndLetter(currentChar) && isArabicAlfLetter(beforeChar)) ||\n (isArabicEndLetter(currentChar) && isArabicEndLetter(beforeChar))\n ) {\n return isolatedForm;\n }\n\n if (\n arabicLetterHasMedialForm(currentChar) &&\n isArabicLetter(beforeChar) &&\n !isArabicEndLetter(beforeChar) &&\n isArabicLetter(nextChar) &&\n arabicLetterHasFinalForm(nextChar)\n ) {\n return medialForm;\n }\n\n if (isArabicEndLetter(currentChar) || !isArabicLetter(nextChar)) {\n return finalForm;\n }\n return initialForm;\n });\n\n /**\n * @name processArabic\n * @function\n * @param {string} text\n * @returns {string}\n */\n var parseArabic = function(text) {\n text = text || \"\";\n\n var result = \"\";\n var i = 0;\n var j = 0;\n var position = 0;\n var currentLetter = \"\";\n var prevLetter = \"\";\n var nextLetter = \"\";\n\n var words = text.split(\"\\\\s+\");\n var newWords = [];\n for (i = 0; i < words.length; i += 1) {\n newWords.push(\"\");\n for (j = 0; j < words[i].length; j += 1) {\n currentLetter = words[i][j];\n prevLetter = words[i][j - 1];\n nextLetter = words[i][j + 1];\n if (isArabicLetter(currentLetter)) {\n position = getCorrectForm(currentLetter, prevLetter, nextLetter);\n if (position !== -1) {\n newWords[i] += String.fromCharCode(\n arabicSubstitionA[currentLetter.charCodeAt(0)][position]\n );\n } else {\n newWords[i] += currentLetter;\n }\n } else {\n newWords[i] += currentLetter;\n }\n }\n\n newWords[i] = resolveLigatures(newWords[i]);\n }\n result = newWords.join(\" \");\n\n return result;\n };\n\n var processArabic = (jsPDFAPI.__arabicParser__.processArabic = jsPDFAPI.processArabic = function() {\n var text =\n typeof arguments[0] === \"string\" ? arguments[0] : arguments[0].text;\n var tmpText = [];\n var result;\n\n if (Array.isArray(text)) {\n var i = 0;\n tmpText = [];\n for (i = 0; i < text.length; i += 1) {\n if (Array.isArray(text[i])) {\n tmpText.push([parseArabic(text[i][0]), text[i][1], text[i][2]]);\n } else {\n tmpText.push([parseArabic(text[i])]);\n }\n }\n result = tmpText;\n } else {\n result = parseArabic(text);\n }\n if (typeof arguments[0] === \"string\") {\n return result;\n } else {\n arguments[0].text = result;\n return arguments[0];\n }\n });\n\n jsPDFAPI.events.push([\"preProcessText\", processArabic]);\n})(jsPDF.API);\n","/** @license\n * jsPDF Autoprint Plugin\n *\n * Licensed under the MIT License.\n * http://opensource.org/licenses/mit-license\n */\n\nimport { jsPDF } from \"../jspdf.js\";\n\n/**\n * @name autoprint\n * @module\n */\n(function(jsPDFAPI) {\n \"use strict\";\n\n /**\n * Makes the PDF automatically open the print-Dialog when opened in a PDF-viewer.\n *\n * @name autoPrint\n * @function\n * @param {Object} options (optional) Set the attribute variant to 'non-conform' (default) or 'javascript' to activate different methods of automatic printing when opening in a PDF-viewer .\n * @returns {jsPDF}\n * @example\n * var doc = new jsPDF();\n * doc.text(10, 10, 'This is a test');\n * doc.autoPrint({variant: 'non-conform'});\n * doc.save('autoprint.pdf');\n */\n jsPDFAPI.autoPrint = function(options) {\n \"use strict\";\n var refAutoPrintTag;\n options = options || {};\n options.variant = options.variant || \"non-conform\";\n\n switch (options.variant) {\n case \"javascript\":\n //https://github.com/Rob--W/pdf.js/commit/c676ecb5a0f54677b9f3340c3ef2cf42225453bb\n this.addJS(\"print({});\");\n break;\n case \"non-conform\":\n default:\n this.internal.events.subscribe(\"postPutResources\", function() {\n refAutoPrintTag = this.internal.newObject();\n this.internal.out(\"<<\");\n this.internal.out(\"/S /Named\");\n this.internal.out(\"/Type /Action\");\n this.internal.out(\"/N /Print\");\n this.internal.out(\">>\");\n this.internal.out(\"endobj\");\n });\n\n this.internal.events.subscribe(\"putCatalog\", function() {\n this.internal.out(\"/OpenAction \" + refAutoPrintTag + \" 0 R\");\n });\n break;\n }\n return this;\n };\n})(jsPDF.API);\n","/**\n * @license\n * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv\n *\n * Licensed under the MIT License.\n * http://opensource.org/licenses/mit-license\n */\n\nimport { jsPDF } from \"../jspdf.js\";\n\n/**\n * jsPDF Canvas PlugIn\n * This plugin mimics the HTML5 Canvas\n *\n * The goal is to provide a way for current canvas users to print directly to a PDF.\n * @name canvas\n * @module\n */\n(function(jsPDFAPI) {\n \"use strict\";\n\n /**\n * @class Canvas\n * @classdesc A Canvas Wrapper for jsPDF\n */\n var Canvas = function() {\n var jsPdfInstance = undefined;\n Object.defineProperty(this, \"pdf\", {\n get: function() {\n return jsPdfInstance;\n },\n set: function(value) {\n jsPdfInstance = value;\n }\n });\n\n var _width = 150;\n /**\n * The height property is a positive integer reflecting the height HTML attribute of the