pengyuthon wechat
This commit is contained in:
@@ -53,16 +53,19 @@
|
||||
|
||||
'assets': {
|
||||
'web.assets_backend':[
|
||||
'ks_dashboard_ninja/static/lib/css/Chart.css',
|
||||
# 'ks_dashboard_ninja/static/lib/css/Chart.css',
|
||||
'ks_dashboard_ninja/static/lib/css/gridstack.min.css',
|
||||
'ks_dashboard_ninja/static/lib/js/gridstack-h5.js',
|
||||
'ks_dashboard_ninja/static/lib/js/Chart.bundle.min.js',
|
||||
# 'ks_dashboard_ninja/static/lib/js/Chart.bundle.min.js',
|
||||
'ks_dashboard_ninja/static/lib/js/chart.umd.min.js',
|
||||
'ks_dashboard_ninja/static/lib/js/exporting.js',
|
||||
'ks_dashboard_ninja/static/lib/js/pdfmake.min.js',
|
||||
'ks_dashboard_ninja/static/src/css/*',
|
||||
'ks_dashboard_ninja/static/src/scss/*',
|
||||
'ks_dashboard_ninja/static/src/dashboard/*',
|
||||
'ks_dashboard_ninja/static/src/items/*',
|
||||
'ks_dashboard_ninja/static/src/items/**/*',
|
||||
'ks_dashboard_ninja/static/src/preview/**/*',
|
||||
'ks_dashboard_ninja/static/src/preview/*',
|
||||
'ks_dashboard_ninja/static/src/tools/*',
|
||||
# 'ks_dashboard_ninja/static/src/**/**/*',
|
||||
],
|
||||
|
||||
@@ -11,6 +11,18 @@ from odoo.tools.safe_eval import safe_eval
|
||||
import locale
|
||||
from dateutil.parser import parse
|
||||
|
||||
def calculate_time(func):
|
||||
import time
|
||||
"""
|
||||
计算函数执行时间的装饰器
|
||||
"""
|
||||
def wrapper(*args, **kwargs):
|
||||
start_time = time.time() # 记录开始时间
|
||||
result = func(*args, **kwargs) # 执行函数
|
||||
end_time = time.time() # 记录结束时间
|
||||
print(f"{func.__name__} 函数执行时间: {end_time - start_time} 秒")
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
class KsDashboardNinjaBoard(models.Model):
|
||||
_name = 'ks_dashboard_ninja.board'
|
||||
@@ -217,6 +229,7 @@ class KsDashboardNinjaBoard(models.Model):
|
||||
return default_grid_id
|
||||
|
||||
@api.model
|
||||
@calculate_time
|
||||
def ks_fetch_dashboard_data(self, ks_dashboard_id, ks_item_domain=False):
|
||||
"""
|
||||
Return Dictionary of Dashboard Data.
|
||||
@@ -297,6 +310,7 @@ class KsDashboardNinjaBoard(models.Model):
|
||||
return dashboard_data
|
||||
|
||||
@api.model
|
||||
@calculate_time
|
||||
def ks_fetch_item(self, item_list, ks_dashboard_id, params={}):
|
||||
"""
|
||||
:rtype: object
|
||||
@@ -394,8 +408,6 @@ class KsDashboardNinjaBoard(models.Model):
|
||||
'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.replace('"%UID"', str(
|
||||
# self.env.user.id)) if rec.ks_domain and "%UID" in rec.ks_domain else rec.ks_domain,
|
||||
'ks_domain': rec.ks_convert_into_proper_domain(rec.ks_domain, rec, item_domain1),
|
||||
'ks_dashboard_id': rec.ks_dashboard_ninja_board_id.id,
|
||||
'ks_icon': rec.ks_icon,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,93 +19,55 @@ class KsDashboardNinjaItems(models.Model):
|
||||
ks_to_do_data = rec._ksGetToDOData()
|
||||
rec.ks_to_do_data = ks_to_do_data
|
||||
|
||||
def getToDoData(self):
|
||||
return self._ksGetToDOData()
|
||||
|
||||
def _ksGetToDOData(self):
|
||||
""" @return
|
||||
ks_to_do_data = {
|
||||
'label': [],
|
||||
'ks_link': [],
|
||||
'ks_href_id': [],
|
||||
'ks_section_id': [],
|
||||
'ks_content': {},
|
||||
'ks_content_record_id': {},
|
||||
'ks_content_active': {}
|
||||
"items":[
|
||||
{"item_id": item_id1, "label": label1, "lines": [{"id": id1, "content": content1, "active": active},{"id": id2, "content": content1, "active": active}] ,
|
||||
{"item_id": item_id2,"label": label2,"lines": [{"id": id3, "content": content2, "active": active},...],
|
||||
]
|
||||
}
|
||||
|
||||
if self.ks_dn_header_lines:
|
||||
for ks_dn_header_line in self.ks_dn_header_lines:
|
||||
ks_to_do_header_label = ks_dn_header_line.ks_to_do_header[:]
|
||||
ks_to_do_data['label'].append(ks_to_do_header_label)
|
||||
ks_dn_header_line_id = str(ks_dn_header_line.id)
|
||||
if type(ks_dn_header_line.id).__name__ != 'int' and ks_dn_header_line.id.ref != None:
|
||||
ks_dn_header_line_id = ks_dn_header_line.id.ref
|
||||
if ' ' in ks_dn_header_line.ks_to_do_header:
|
||||
ks_temp = ks_dn_header_line.ks_to_do_header.replace(" ", "")
|
||||
ks_to_do_data['ks_link'].append('#' + ks_temp + ks_dn_header_line_id)
|
||||
ks_to_do_data['ks_href_id'].append(ks_temp + str(ks_dn_header_line.id))
|
||||
"""
|
||||
|
||||
elif ks_dn_header_line.ks_to_do_header[0].isdigit():
|
||||
ks_temp = ks_dn_header_line.ks_to_do_header.replace(
|
||||
ks_dn_header_line.ks_to_do_header[0], 'z')
|
||||
ks_to_do_data['ks_link'].append('#' + ks_temp + ks_dn_header_line_id)
|
||||
ks_to_do_data['ks_href_id'].append(ks_temp + str(ks_dn_header_line.id))
|
||||
if not self.ks_dn_header_lines:
|
||||
return False
|
||||
ks_to_do_data = {
|
||||
'items': [],
|
||||
}
|
||||
items = []
|
||||
for record in self.ks_dn_header_lines:
|
||||
lines = []
|
||||
for line in record.ks_to_do_description_lines:
|
||||
if isinstance(line.id, int):
|
||||
line_id = line.id
|
||||
else:
|
||||
ks_to_do_data['ks_link'].append('#' + ks_dn_header_line.ks_to_do_header + ks_dn_header_line_id)
|
||||
ks_to_do_data['ks_href_id'].append(ks_dn_header_line.ks_to_do_header + str(ks_dn_header_line.id))
|
||||
ks_to_do_data['ks_section_id'].append(str(ks_dn_header_line.id))
|
||||
if len(ks_dn_header_line.ks_to_do_description_lines):
|
||||
for ks_to_do_description_line in ks_dn_header_line.ks_to_do_description_lines:
|
||||
if ' ' in ks_dn_header_line.ks_to_do_header or ks_dn_header_line.ks_to_do_header[0].isdigit():
|
||||
if ks_to_do_data['ks_content'].get(ks_temp +
|
||||
str(ks_dn_header_line.id), False):
|
||||
|
||||
ks_to_do_data['ks_content'][ks_temp +
|
||||
str(ks_dn_header_line.id)].append(
|
||||
ks_to_do_description_line.ks_description)
|
||||
ks_to_do_data['ks_content_record_id'][ks_temp +
|
||||
str(ks_dn_header_line.id)].append(
|
||||
str(ks_to_do_description_line.id))
|
||||
ks_to_do_data['ks_content_active'][ks_temp +
|
||||
str(ks_dn_header_line.id)].append(
|
||||
str(ks_to_do_description_line.ks_active))
|
||||
else:
|
||||
ks_to_do_data['ks_content'][ks_temp +
|
||||
str(ks_dn_header_line.id)] = [
|
||||
ks_to_do_description_line.ks_description]
|
||||
ks_to_do_data['ks_content_record_id'][ks_temp +
|
||||
str(ks_dn_header_line.id)] = [
|
||||
str(ks_to_do_description_line.id)]
|
||||
ks_to_do_data['ks_content_active'][ks_temp +
|
||||
str(ks_dn_header_line.id)] = [
|
||||
str(ks_to_do_description_line.ks_active)]
|
||||
else:
|
||||
if ks_to_do_data['ks_content'].get(ks_dn_header_line.ks_to_do_header +
|
||||
str(ks_dn_header_line.id), False):
|
||||
|
||||
ks_to_do_data['ks_content'][ks_dn_header_line.ks_to_do_header +
|
||||
str(ks_dn_header_line.id)].append(
|
||||
ks_to_do_description_line.ks_description)
|
||||
ks_to_do_data['ks_content_record_id'][ks_dn_header_line.ks_to_do_header +
|
||||
str(ks_dn_header_line.id)].append(
|
||||
str(ks_to_do_description_line.id))
|
||||
ks_to_do_data['ks_content_active'][ks_dn_header_line.ks_to_do_header +
|
||||
str(ks_dn_header_line.id)].append(
|
||||
str(ks_to_do_description_line.ks_active))
|
||||
else:
|
||||
ks_to_do_data['ks_content'][ks_dn_header_line.ks_to_do_header +
|
||||
str(ks_dn_header_line.id)] = [
|
||||
ks_to_do_description_line.ks_description]
|
||||
ks_to_do_data['ks_content_record_id'][ks_dn_header_line.ks_to_do_header +
|
||||
str(ks_dn_header_line.id)] = [
|
||||
str(ks_to_do_description_line.id)]
|
||||
ks_to_do_data['ks_content_active'][ks_dn_header_line.ks_to_do_header +
|
||||
str(ks_dn_header_line.id)] = [
|
||||
str(ks_to_do_description_line.ks_active)]
|
||||
|
||||
ks_to_do_data = json.dumps(ks_to_do_data)
|
||||
else:
|
||||
ks_to_do_data = False
|
||||
return ks_to_do_data
|
||||
|
||||
|
||||
try:
|
||||
line_id = line.ids[0]
|
||||
except IndexError:
|
||||
line_id = 0
|
||||
lines.append({
|
||||
"id": line_id,
|
||||
"ks_description": line.ks_description,
|
||||
"ks_active": line.ks_active
|
||||
})
|
||||
if isinstance(record.id, int):
|
||||
record_id = record.id
|
||||
else:
|
||||
try:
|
||||
record_id = record.ids[0]
|
||||
except IndexError:
|
||||
record_id = 0
|
||||
items.append({
|
||||
"item_id": record_id,
|
||||
"label": record.ks_to_do_header,
|
||||
"lines": lines
|
||||
})
|
||||
ks_to_do_data['items'] = items
|
||||
return json.dumps(ks_to_do_data)
|
||||
|
||||
|
||||
class KsToDoheaders(models.Model):
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -86,6 +86,7 @@
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
/*max-height: 24rem;*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="ks_dashboard_ninja.DashboardAction" >
|
||||
<style>
|
||||
.modal-lg, .modal-xl {
|
||||
max-width: 96%;
|
||||
padding: 2% 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="o_action ks_dashboard_ninja d-flex flex-column">
|
||||
<t t-if="ref.dashboardData">
|
||||
<KsDashboard dashboardName="session.name"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { itemProps } from "../item_props";
|
||||
import { get_rgba_format, mainBodyStyle } from "@ks_dashboard_ninja/dashboard/utils";
|
||||
@@ -28,6 +27,36 @@ export class KsKpi extends Component {
|
||||
this.count_2 = this.kpi_data[1] ? this.kpi_data[1]?.record_data : undefined;
|
||||
this.target_1 = this.kpi_data[0]?.target;
|
||||
|
||||
this.deviation = Math.round((this.difference / this.target_1) * 100)
|
||||
if (this.deviation !== Infinity) {
|
||||
this.deviation = this.deviation ? this.deviation + '%' : 0 + '%';
|
||||
}
|
||||
|
||||
this.previous_period_data = this.kpi_data[0].previous_period;
|
||||
this.pre_diffrence = (this.count_1 - this.previous_period_data);
|
||||
const { ks_multiplier_active, ks_multiplier } = this.props.record
|
||||
if (ks_multiplier_active){
|
||||
this.previous_period_data = this.kpi_data[0].previous_period * ks_multiplier;
|
||||
this.pre_diffrence = (this.count_1 * ks_multiplier - this.previous_period_data);
|
||||
}
|
||||
|
||||
this.pre_achieve = this.pre_diffrence > 0;
|
||||
this.pre_diffrence = Math.abs(this.pre_diffrence);
|
||||
this.pre_deviation = this.previous_period_data ? parseInt((this.pre_diffrence / this.previous_period_data) * 100) + '%' : "100%"
|
||||
}
|
||||
|
||||
onClickAction() {
|
||||
const { name, ks_model_name, ks_domain } = this.props.record;
|
||||
const action = {
|
||||
name: name,
|
||||
type: 'ir.actions.act_window',
|
||||
res_model:ks_model_name,
|
||||
domain: ks_domain,
|
||||
views: [[false, 'tree']],
|
||||
view_mode: 'tree',
|
||||
target: 'current',
|
||||
};
|
||||
this.action.doAction(action);
|
||||
}
|
||||
|
||||
get difference() {
|
||||
@@ -44,14 +73,24 @@ export class KsKpi extends Component {
|
||||
return this.difference >= 0;
|
||||
}
|
||||
|
||||
get target_arrow() {
|
||||
get targetArrow() {
|
||||
if (this.deviation === Infinity) {
|
||||
return ;
|
||||
}
|
||||
return this.achieve ? 'up' : 'down';
|
||||
}
|
||||
|
||||
get pre_deviation() {
|
||||
|
||||
get preArrow() {
|
||||
return this.pre_achieve ? 'up' : 'down';
|
||||
}
|
||||
|
||||
get preDeviationColor() {
|
||||
return this.pre_achieve ? 'color: green' : 'color: red'
|
||||
}
|
||||
|
||||
get targetDeviationColor() {
|
||||
return this.achieve ? 'color: green' : 'color: red'
|
||||
}
|
||||
|
||||
get container_class() {
|
||||
return "grid-stack-item ui-draggable-disabled ui-resizable-disabled"
|
||||
@@ -82,19 +121,7 @@ export class KsKpi extends Component {
|
||||
return mainBodyStyle(rgba_background_color, rgba_font_color, this.props.record).background_style;
|
||||
}
|
||||
|
||||
onClickAction() {
|
||||
const action = {
|
||||
name: this.props.record.ks_model_display_name,
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: this.props.record.ks_model_name,
|
||||
// res_id: this.props.dashboard_id,
|
||||
// context: {'create':false},
|
||||
domain: this.props.record.ks_domain,
|
||||
views: [[false, 'tree']],
|
||||
view_mode: 'tree',
|
||||
target: 'current',
|
||||
};
|
||||
this.action.doAction(action);
|
||||
get ks_icon_url() {
|
||||
return `data:image/png;base64,${this.props.record.ks_icon}`;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,10 +35,10 @@
|
||||
<span>vs Target</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="font-size : medium;">
|
||||
<t t-esc="item.deviation"/>
|
||||
<span style="font-size : medium;" t-att-style="targetDeviationColor">
|
||||
<t t-esc="deviation"/>
|
||||
<t t-if="achieve">
|
||||
<i t-att-class="'fa fa-arrow-'+ target_arrow"/>
|
||||
<i t-att-class="'fa fa-arrow-'+ targetArrow"/>
|
||||
</t>
|
||||
</span>
|
||||
</div>
|
||||
@@ -48,9 +48,10 @@
|
||||
<span>vs Prev</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="font-size : medium;">
|
||||
<span style="font-size : medium;"
|
||||
t-att-style="preDeviationColor">
|
||||
<t t-esc="pre_deviation"/>
|
||||
<i t-att-class="'fa fa-arrow-'+ pre_arrow"/>
|
||||
<i t-att-class="'fa fa-arrow-'+ preArrow"/>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { itemProps } from "../item_props";
|
||||
import { get_rgba_format, mainBodyStyle } from "@ks_dashboard_ninja/dashboard/utils";
|
||||
import { KsToolBar } from "../tool_bar";
|
||||
|
||||
const { Component, onWillStart, onWillUpdateProps, useRef, onMounted, useState, onWillUnmount } = owl;
|
||||
const { Component } = owl;
|
||||
|
||||
export class KsTile extends Component {
|
||||
|
||||
@@ -25,6 +23,18 @@ export class KsTile extends Component {
|
||||
this.action = useService('action');
|
||||
}
|
||||
|
||||
onClickAction() {
|
||||
const { name, ks_model_name, ks_domain } = this.props.record;
|
||||
this.action.doAction({
|
||||
name: name,
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: ks_model_name,
|
||||
domain: ks_domain,
|
||||
views: [[false, 'tree']],
|
||||
view_mode: 'tree',
|
||||
target: 'current',
|
||||
});
|
||||
}
|
||||
|
||||
get container_class() {
|
||||
return "grid-stack-item ui-draggable-disabled ui-resizable-disabled"
|
||||
@@ -55,18 +65,5 @@ export class KsTile extends Component {
|
||||
return mainBodyStyle(rgba_background_color, rgba_font_color, this.props.record).background_style;
|
||||
}
|
||||
|
||||
onClickAction() {
|
||||
this.action.doAction({
|
||||
name: this.props.record.ks_model_display_name,
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: this.props.record.ks_model_name,
|
||||
// res_id: this.props.dashboard_id,
|
||||
// context: {'create':false},
|
||||
domain: this.props.record.ks_domain,
|
||||
views: [[false, 'tree']],
|
||||
view_mode: 'tree',
|
||||
target: 'current',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { itemProps } from "../item_props";
|
||||
import { get_rgba_format, mainBodyStyle } from "@ks_dashboard_ninja/dashboard/utils";
|
||||
import { KsToolBar } from "../tool_bar";
|
||||
import {useService} from "@web/core/utils/hooks"
|
||||
import {ConfirmationDialog} from "@web/core/confirmation_dialog/confirmation_dialog";
|
||||
import {_t} from "@web/core/l10n/translation";
|
||||
import {itemProps} from "../item_props";
|
||||
import {get_rgba_format, mainBodyStyle} from "@ks_dashboard_ninja/dashboard/utils";
|
||||
import {KsToolBar} from "../tool_bar";
|
||||
import {TodoTaskDialog} from "./todo_task_dialog";
|
||||
|
||||
const { Component, onWillStart, onWillUpdateProps, useRef, onMounted, useState, onWillUnmount } = owl;
|
||||
|
||||
@@ -18,34 +20,142 @@ export class KsTODO extends Component {
|
||||
};
|
||||
|
||||
static components = {
|
||||
KsToolBar
|
||||
KsToolBar,
|
||||
TodoTaskDialog
|
||||
}
|
||||
|
||||
setup() {
|
||||
this.action = useService('action');
|
||||
this.orm = useService("orm");
|
||||
this.action = useService("action");
|
||||
this.notification = useService("notification");
|
||||
this.dialog = useService("dialog");
|
||||
this.state = useState({
|
||||
tab: 0,
|
||||
labels: null,
|
||||
tableData: null,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
this.prepareTableData();
|
||||
});
|
||||
}
|
||||
|
||||
get todoData() {
|
||||
return JSON.parse(this.props.record.ks_to_do_data);
|
||||
}
|
||||
|
||||
set todoData(data) {
|
||||
this.props.record.ks_to_do_data = data;
|
||||
}
|
||||
|
||||
prepareTableData() {
|
||||
const { items } = this.todoData;
|
||||
this.state.labels = items.map(item => [item.item_id, item.label]);
|
||||
this.state.tableData = items[this.state.tab];
|
||||
}
|
||||
|
||||
onTabClick(index) {
|
||||
this.state.tab = index;
|
||||
this.prepareTableData();
|
||||
}
|
||||
|
||||
createContent() {
|
||||
this.dialog.add(TodoTaskDialog, {
|
||||
title: _t('Create Task'),
|
||||
body: _t('Create Task'),
|
||||
mode: 'create',
|
||||
cancel: () => {},
|
||||
confirm: (content) => this.doCreate(content),
|
||||
});
|
||||
}
|
||||
|
||||
async doCreate(content) {
|
||||
const result = await this.orm.create('ks_to.do.description', [{ks_to_do_header_id: this.state.tableData.item_id, ks_description: content}])
|
||||
const newItem = {
|
||||
id: result[0],
|
||||
ks_description: content,
|
||||
ks_active: true,
|
||||
}
|
||||
this.state.tableData.lines.push(newItem);
|
||||
this.todoData = await this.loadTodoData();
|
||||
}
|
||||
|
||||
editContent(id) {
|
||||
this.dialog.add(TodoTaskDialog, {
|
||||
title: _t('Edit Task'),
|
||||
body: _t('Edit Task'),
|
||||
mode: 'edit',
|
||||
record: this.state.tableData.lines.filter(line => line.id === id),
|
||||
cancel: () => {},
|
||||
confirm: (content) => this.doEdit(id, content),
|
||||
});
|
||||
}
|
||||
|
||||
async doEdit(id, content) {
|
||||
await this.orm.write('ks_to.do.description', [id], { ks_description: content });
|
||||
// document.getElementById(`todoTask_${id}`).innerText = content;
|
||||
this.todoData = await this.loadTodoData();
|
||||
this.prepareTableData();
|
||||
}
|
||||
|
||||
deleteContent(id) {
|
||||
this.dialog.add(ConfirmationDialog, {
|
||||
body: _t('Do you really want to delete this task?'),
|
||||
cancel: () => {},
|
||||
confirm: () => this.doDelete(id),
|
||||
});
|
||||
}
|
||||
|
||||
async doDelete(id) {
|
||||
const res = await this.orm.unlink('ks_to.do.description', [id])
|
||||
this.state.tableData.lines = this.state.tableData.lines.filter(line => line.id !== id)
|
||||
this.todoData = await this.loadTodoData()
|
||||
if (res) {
|
||||
this.notification.add(
|
||||
_t('Selected task is deleted.'),
|
||||
{
|
||||
title: _t("Task Deleted"),
|
||||
type: "success",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async loadTodoData() {
|
||||
return await this.orm.call('ks_dashboard_ninja.item', 'getToDoData', [this.props.record.id]);
|
||||
}
|
||||
|
||||
async doCheckBox(id, active) {
|
||||
await this.orm.write('ks_to.do.description', [id], { ks_active: !active });
|
||||
this.todoData = await this.loadTodoData();
|
||||
this.prepareTableData();
|
||||
}
|
||||
|
||||
get container_class() {
|
||||
return "grid-stack-item ui-draggable-disabled ui-resizable-disabled"
|
||||
return "grid-stack-item ui-draggable-disabled ui-resizable-disabled";
|
||||
}
|
||||
|
||||
get inner_container_class() {
|
||||
return "grid-stack-item-content"
|
||||
return "grid-stack-item-content";
|
||||
}
|
||||
|
||||
get rgba_button_color() {
|
||||
return get_rgba_format(this.props.record.ks_button_color)
|
||||
return get_rgba_format(this.props.record.ks_button_color);
|
||||
}
|
||||
|
||||
get rgba_background_color() {
|
||||
return get_rgba_format(this.props.record.ks_background_color)
|
||||
return get_rgba_format(this.props.record.ks_background_color);
|
||||
}
|
||||
|
||||
get header_bg_color() {
|
||||
return get_rgba_format(this.props.record.ks_header_bg_color);
|
||||
}
|
||||
|
||||
get rgba_font_color() {
|
||||
return get_rgba_format(this.props.record.ks_font_color)
|
||||
return get_rgba_format(this.props.record.ks_font_color);
|
||||
}
|
||||
get rgba_default_icon_color() {
|
||||
return get_rgba_format(this.props.record.ks_default_icon_color)
|
||||
return get_rgba_format(this.props.record.ks_default_icon_color);
|
||||
}
|
||||
|
||||
get style_main_body() {
|
||||
@@ -54,18 +164,4 @@ export class KsTODO extends Component {
|
||||
return mainBodyStyle(rgba_background_color, rgba_font_color, this.props.record).background_style;
|
||||
}
|
||||
|
||||
onClickAction() {
|
||||
this.action.doAction({
|
||||
name: this.props.record.ks_model_display_name,
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: this.props.record.ks_model_name,
|
||||
// res_id: this.props.dashboard_id,
|
||||
// context: {'create':false},
|
||||
domain: this.props.record.ks_domain,
|
||||
views: [[false, 'tree']],
|
||||
view_mode: 'tree',
|
||||
target: 'current',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,43 +4,130 @@
|
||||
|
||||
<t t-name="ks_dashboard_ninja.todo" >
|
||||
<t t-set="item" t-value="props?.record"/>
|
||||
<!-- <div >-->
|
||||
<div t-att-id="item.id" t-on-click="onClickAction"
|
||||
t-att-class="'ks_dashboarditem_id ks_dashboard_item ks_dashboard_item_hover ks_item_click ' + inner_container_class"
|
||||
t-att-style="style_main_body"
|
||||
t-att-title="item.ks_info">
|
||||
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header ks_dashboard_item_header_hover"
|
||||
t-att-data-item_id="item.id">
|
||||
<KsToolBar item="props?.record"
|
||||
dashboardList="props.dashboardList"
|
||||
doRefresh.bind="props.doRefresh"/>
|
||||
</div>
|
||||
<!-- <div class="grid-stack-item" t-att-id="item.id">-->
|
||||
<div class="grid-stack-item-content ks_list_view_container ks_dashboard_item_hover card shadow"
|
||||
t-att-title="item.ks_info" t-att-id="item.id">
|
||||
<div class="ks_card_header" t-att-style=" 'background-color:' + header_bg_color ">
|
||||
<div class="p-3 py-3 d-flex flex-row align-items-center justify-content-between ">
|
||||
<div class="d-flex align-items-center w-50">
|
||||
<h6 class="m-0 font-weight-bold h3 mr-3 ks_list_view_heading" t-att-title="item.name">
|
||||
<t t-esc="item.name"/>
|
||||
</h6>
|
||||
</div>
|
||||
|
||||
<div class="ks_dashboard_item_main_body">
|
||||
<div class="ks_dashboard_icon">
|
||||
<t t-if="item.ks_icon_select=='Custom'">
|
||||
<t t-if="ks_icon_url">
|
||||
<img t-att-src="ks_icon_url"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-elif="item.ks_icon_select=='Default'">
|
||||
<span t-att-style="'color:'+ ks_rgba_default_icon_color + ';'"
|
||||
t-att-class="'fa fa-' + item.ks_default_icon + ' fa-4x'"/>
|
||||
</t>
|
||||
<!--<img t-att-src="ks_icon_url"/>-->
|
||||
</div>
|
||||
<div class="ks_dashboard_item_info">
|
||||
<div class="ks_dashboard_item_name" t-att-title="item.name">
|
||||
<t t-esc="item.name"/>
|
||||
</div>
|
||||
<div class="ks_dashboard_item_domain_count" t-att-title="count">
|
||||
<t t-esc="item.ks_record_count"/>
|
||||
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header ks_dashboard_item_header_hover"
|
||||
t-att-data-item_id="item.id">
|
||||
<KsToolBar item="props?.record"
|
||||
dashboardList="props.dashboardList"
|
||||
doRefresh.bind="props.doRefresh"/>
|
||||
</div>
|
||||
</div>
|
||||
<t t-call="todo_card_header" />
|
||||
</div>
|
||||
<div class="ks_to_do_card_body card-body table-responsive">
|
||||
<t t-call="ks_to_do_dashboard_inner_container" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- </div>-->
|
||||
</t>
|
||||
|
||||
|
||||
<t t-name="todo_card_header">
|
||||
<div class="card-header">
|
||||
<div class="nav-tabs-navigation">
|
||||
<div class="nav-tabs-wrapper">
|
||||
<t t-if="state.labels">
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" style="overflow:overlay">
|
||||
<t t-foreach="state.labels" t-as="label" t-key="label_index">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
t-att-style="'color:' + rgba_font_color"
|
||||
t-attf-class="{{ label_index == state.tab ? 'active': '' }}"
|
||||
t-att-data-item-id="item.id"
|
||||
t-att-data-section-id="label[label_index]"
|
||||
data-toggle="pill"
|
||||
t-att-href="label[1]"
|
||||
t-on-click.prevent.stop="() => this.onTabClick(label_index)"
|
||||
>
|
||||
<t t-esc="label[1]"/>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
<button class="header_add_btn"
|
||||
t-att-data-item-id="item.id"
|
||||
t-att-style="'color:'+ rgba_button_color + ' !important' + ';'"
|
||||
t-on-click="createContent"
|
||||
>
|
||||
<span class="fa fa-lg fa-plus-circle"/>
|
||||
</button>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<ul class="nav nav-tabs" data-tabs="tabs">
|
||||
<li class="nav-item">
|
||||
No Section Available.
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="ks_to_do_dashboard_inner_container">
|
||||
<div class="container-fluid p-0">
|
||||
<div class="card">
|
||||
<t t-if="state.tableData?.lines.length > 0">
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<table class="table">
|
||||
<t t-foreach="state.tableData?.lines" t-as="data" t-key="data_index">
|
||||
<tr>
|
||||
<td class="ks_custom_check">
|
||||
<div class="form-check" t-on-click="() => this.doCheckBox(data.id, data.ks_active)">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" t-att-checked="!data.ks_active"/>
|
||||
<span class="form-check-sign ks_do_item_active_handler"
|
||||
t-att-data-item-id="data.id">
|
||||
<span class="check"/>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
<td class="ks_description"
|
||||
t-att-id="'todoTask_' + data.id"
|
||||
t-attf-class="{{ data.ks_active ? '': 'ks_do_item_line_through' }}"
|
||||
t-esc="data.ks_description" />
|
||||
<td class="td-actions" style="text-align:right; padding-right:0">
|
||||
<button type="button" rel="tooltip"
|
||||
t-att-data-item-id="data.id"
|
||||
class="btn-link btn-sm ks_edit_content"
|
||||
t-on-click="() => this.editContent(data.id)"
|
||||
data-original-title="Edit Task">
|
||||
<span class="fa fa-lg fa-pencil"/>
|
||||
</button>
|
||||
</td>
|
||||
<td class="td-actions" style="text-align:left; padding-left:0">
|
||||
<button type="button"
|
||||
t-att-data-item-id="data.id"
|
||||
class="btn btn-danger btn-link btn-sm ks_delete_content"
|
||||
t-on-click="() => this.deleteContent(data.id)"
|
||||
data-original-title="Remove">
|
||||
<i class="fa fa-trash-o"/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="" >
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<span class="nav-tabs-title">No Tasks Available</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,40 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
|
||||
const { useState, useRef } = owl;
|
||||
export class TodoTaskDialog extends ConfirmationDialog {
|
||||
static template = "ks_dashboard_ninja.todoTaskDialog";
|
||||
static props = {
|
||||
...ConfirmationDialog.props,
|
||||
record: { type: Object, optional: true },
|
||||
mode: { type: String }
|
||||
}
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.todoInput = useRef('todoInput');
|
||||
this.todoTask = useState({
|
||||
id: '',
|
||||
content: ''
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
confirm() {
|
||||
this.props.confirm();
|
||||
this.props.close();
|
||||
}
|
||||
|
||||
editConfirm() {
|
||||
const content = this.todoInput.el.value;
|
||||
this.props.confirm(content);
|
||||
this.props.close();
|
||||
}
|
||||
|
||||
createConfirm() {
|
||||
const content = this.todoInput.el.value;
|
||||
this.props.confirm(content);
|
||||
this.props.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="ks_dashboard_ninja.todoTaskDialog">
|
||||
<Dialog size="'md'" title="props.title" modalRef="modalRef">
|
||||
<div class="mb-3">
|
||||
<input type="text" class="form-control"
|
||||
t-if="props.mode === 'create'"
|
||||
t-ref="todoInput"
|
||||
/>
|
||||
<input type="text" class="form-control"
|
||||
t-att-value="props.record[0].ks_description"
|
||||
t-if="props.mode === 'edit'"
|
||||
t-ref="todoInput"/>
|
||||
</div>
|
||||
<t t-set-slot="footer">
|
||||
<div class="btn btn-primary"
|
||||
t-if="props.mode === 'edit'"
|
||||
t-on-click="() => this.editConfirm()">Confirm</div>
|
||||
<div class="btn btn-primary"
|
||||
t-if="props.mode === 'create'"
|
||||
t-on-click="() => this.createConfirm()">Confirm</div>
|
||||
<div class="btn btn-secondary" t-on-click="_cancel">Cancel</div>
|
||||
</t>
|
||||
</Dialog>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -6,13 +6,14 @@ const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsAreaChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
this.type = 'line';
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,58 +6,16 @@ const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsBarChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.barChart';
|
||||
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.type = 'bar';
|
||||
|
||||
onWillStart(() => {
|
||||
this.options = {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.data = {
|
||||
labels: ['1', '2', '3', '4', '5', '6', '7'],
|
||||
datasets: [{
|
||||
label: 'My First Dataset',
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(255, 159, 64, 0.2)',
|
||||
'rgba(255, 205, 86, 0.2)',
|
||||
'rgba(75, 192, 192, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
'rgba(153, 102, 255, 0.2)',
|
||||
'rgba(201, 203, 207, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgb(255, 99, 132)',
|
||||
'rgb(255, 159, 64)',
|
||||
'rgb(255, 205, 86)',
|
||||
'rgb(75, 192, 192)',
|
||||
'rgb(54, 162, 235)',
|
||||
'rgb(153, 102, 255)',
|
||||
'rgb(201, 203, 207)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const ctx = document.getElementById(`barChart_${this.props.record.id}`);
|
||||
new Chart(ctx, {
|
||||
type: this.type,
|
||||
data: this.data,
|
||||
options: this.options
|
||||
})
|
||||
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,13 @@ const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsBulletChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,61 +8,20 @@
|
||||
<div class="d-flex align-items-center w-50">
|
||||
<h6 t-esc="props?.record.name"/>
|
||||
</div>
|
||||
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header ks_dashboard_item_header_hover chart_button_container d-md-flex d-none">
|
||||
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header
|
||||
ks_dashboard_item_header_hover chart_button_container d-md-flex d-none">
|
||||
<KsToolBar item="props?.record"
|
||||
dashboardList="props.dashboardList"
|
||||
doRefresh.bind="props.doRefresh"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card-body ks_chart_card_body">
|
||||
<div class="card-body ks_chart_card_body d-flex flex-row align-items-center justify-content-center">
|
||||
<canvas t-att-id=" props.record.ks_dashboard_item_type + '_' + props.record.id" >
|
||||
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="ks_dashboard_ninja.barChart" t-inherit="ks_dashboard_ninja.chartBase" t-inherit-mode="primary">
|
||||
<xpath expr="//div[hasclass('card-body')]" position="inside">
|
||||
<canvas t-att-id="'barChart_' + props.record.id">
|
||||
|
||||
</canvas>
|
||||
</xpath>
|
||||
|
||||
</t>
|
||||
|
||||
<t t-name="ks_dashboard_ninja.lineChart" t-inherit="ks_dashboard_ninja.chartBase" t-inherit-mode="primary">
|
||||
<xpath expr="//div[hasclass('card-body')]" position="inside">
|
||||
<canvas t-att-id="'lineChart_' + props.record.id">
|
||||
|
||||
</canvas>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="ks_dashboard_ninja.pieChart" t-inherit="ks_dashboard_ninja.chartBase" t-inherit-mode="primary">
|
||||
<xpath expr="//div[hasclass('card-body')]" position="inside">
|
||||
<canvas t-att-id="'pieChart_' + props.record.id">
|
||||
|
||||
</canvas>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="ks_dashboard_ninja.scatterChart" t-inherit="ks_dashboard_ninja.chartBase" t-inherit-mode="primary">
|
||||
<xpath expr="//div[hasclass('card-body')]" position="inside">
|
||||
<canvas t-att-id="'scatterChart_' + props.record.id">
|
||||
|
||||
</canvas>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="ks_dashboard_ninja.doughnutChart" t-inherit="ks_dashboard_ninja.chartBase" t-inherit-mode="primary">
|
||||
<xpath expr="//div[hasclass('card-body')]" position="inside">
|
||||
<!-- <div class="chart-canvas">-->
|
||||
<canvas t-att-id="'doughnutChart_' + props.record.id" class="canvas-box">
|
||||
|
||||
</canvas>
|
||||
<!-- </div>-->
|
||||
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
import { itemProps } from "../item_props";
|
||||
import { KsToolBar } from "../tool_bar";
|
||||
|
||||
const { Component } = owl;
|
||||
const { Component, onWillUnmount } = owl;
|
||||
|
||||
export class KsChartBase extends Component {
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
|
||||
static props = {
|
||||
...itemProps,
|
||||
doRefresh: { type: Function, optional: true },
|
||||
@@ -16,90 +17,36 @@ export class KsChartBase extends Component {
|
||||
|
||||
setup() {
|
||||
this.chartData = JSON.parse(this.props.record.ks_chart_data);
|
||||
this.chartIns = null;
|
||||
this.type = '';
|
||||
this.data = {};
|
||||
this.options = {};
|
||||
this.data = {
|
||||
labels: this.chartData.labels ?? [],
|
||||
datasets: this.chartData.datasets ?? '',
|
||||
}
|
||||
this.options = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
}
|
||||
}
|
||||
this.plugins = [];
|
||||
}
|
||||
|
||||
renderGraph(item) {
|
||||
let self = this;
|
||||
let chart_data = JSON.parse(item.ks_chart_data);
|
||||
let isDrill = item.isDrill ? item.isDrill : false;
|
||||
let chart_id = item.id;
|
||||
let chart_title = item.name;
|
||||
let chart_type = item.ks_dashboard_item_type.split('_')[1];
|
||||
let chart_family;
|
||||
switch (chart_type) {
|
||||
case "pie":
|
||||
case "doughnut":
|
||||
case "polarArea":
|
||||
chart_family = "circle";
|
||||
break;
|
||||
case "radar":
|
||||
chart_family = "circle";
|
||||
break;
|
||||
case "bar":
|
||||
case "horizontalBar":
|
||||
case "line":
|
||||
case "area":
|
||||
chart_family = "square"
|
||||
break;
|
||||
case "scatter":
|
||||
chart_family = "square"
|
||||
break;
|
||||
default:
|
||||
chart_family = "none";
|
||||
break;
|
||||
}
|
||||
let ks_description
|
||||
if (item.ks_info){
|
||||
ks_description = item.ks_info.split('\n');
|
||||
ks_description = ks_description.filter(element => element !== '')
|
||||
}else {
|
||||
ks_description = false;
|
||||
}
|
||||
|
||||
let container_data = this.ks_container_option(chart_title, self.ks_dashboard_data.ks_dashboard_manager, true, self.ks_dashboard_data.ks_dashboard_list, chart_id, chart_family, chart_type, this.ksChartColorOptions, ks_description, item.ks_company, item.ks_dashboard_item_type);
|
||||
|
||||
let $ks_gridstack_container = $(QWeb.render('ks_gridstack_container', container_data)).addClass('ks_dashboarditem_id');
|
||||
self.ks_set_selected_color_pallet($ks_gridstack_container, item);
|
||||
|
||||
let ksLayoutGridId = $(self.$el[0]).find('.ks_layout_selected').attr('data-ks_layout_id')
|
||||
if(ksLayoutGridId && ksLayoutGridId != 'ks_default'){
|
||||
self.gridstackConfig = JSON.parse(self.ks_dashboard_data.ks_child_boards[parseInt(ksLayoutGridId)][1])
|
||||
}
|
||||
parseInt($(self.$el[0]).find('.ks_layout_selected').attr('data-ks_layout_id'))
|
||||
if (chart_id in self.gridstackConfig) {
|
||||
if (config.device.isMobile){
|
||||
self.grid.addWidget($ks_gridstack_container[0], {x:self.gridstackConfig[chart_id].x, y:self.gridstackConfig[chart_id].y, w:self.gridstackConfig[chart_id].w, h:self.gridstackConfig[chart_id].h, autoPosition:true,minW:4,maxW:null,minH:3,maxH:null,id :chart_id});
|
||||
onWillUnmount(() => {
|
||||
if (this.chartIns) {
|
||||
this.chartIns.destroy();
|
||||
}
|
||||
else{
|
||||
self.grid.addWidget($ks_gridstack_container[0], {x:self.gridstackConfig[chart_id].x, y:self.gridstackConfig[chart_id].y, w:self.gridstackConfig[chart_id].w, h:self.gridstackConfig[chart_id].h, autoPosition:false,minW:4,maxW:null,minH:3,maxH:null,id :chart_id});
|
||||
}
|
||||
} else {
|
||||
self.grid.addWidget($ks_gridstack_container[0], {x:0, y:0, w:5, h:4,autoPosition:true,minW:4,maxW:null,minH:3,maxH:null, id :chart_id});
|
||||
}
|
||||
self._renderChart($ks_gridstack_container, item);
|
||||
})
|
||||
}
|
||||
|
||||
ks_set_selected_color_pallet($ks_gridstack_container, item){
|
||||
$ks_gridstack_container.find('.ks_li_' + item.ks_chart_item_color).addClass('ks_date_filter_selected');
|
||||
renderChart() {
|
||||
const ctx = document.getElementById(`${this.props.record.ks_dashboard_item_type}_${this.props.record.id}`);
|
||||
this.chartIns = new Chart(ctx, {
|
||||
type: this.type,
|
||||
data: this.data,
|
||||
options: this.options
|
||||
})
|
||||
}
|
||||
|
||||
ks_container_option(chart_title, ksIsDashboardManager, ksIsUser, ks_dashboard_list, chart_id, chart_family, chart_type, ksChartColorOptions, ks_info, ks_company,ks_dashboard_item_type){
|
||||
return {
|
||||
ks_chart_title: chart_title,
|
||||
ksIsDashboardManager: ksIsDashboardManager,
|
||||
ksIsUser: ksIsUser,
|
||||
ks_dashboard_list: ks_dashboard_list,
|
||||
chart_id: chart_id,
|
||||
chart_family: chart_family,
|
||||
chart_type: chart_type,
|
||||
ksChartColorOptions: ksChartColorOptions,
|
||||
ks_info: ks_info,
|
||||
ks_company: ks_company,
|
||||
ks_dashboard_item_type: ks_dashboard_item_type
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -6,50 +6,16 @@ const { onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsDoughnutChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.doughnutChart';
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
this.type = 'doughnut';
|
||||
|
||||
onWillStart(() => {
|
||||
this.options = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Chart.js Doughnut Chart'
|
||||
}
|
||||
}
|
||||
};
|
||||
this.data = {
|
||||
labels: [
|
||||
'Red',
|
||||
'Blue',
|
||||
'Yellow'
|
||||
],
|
||||
datasets: [{
|
||||
label: 'My First Dataset',
|
||||
data: [300, 50, 100],
|
||||
backgroundColor: [
|
||||
'rgb(255, 99, 132)',
|
||||
'rgb(54, 162, 235)',
|
||||
'rgb(255, 205, 86)'
|
||||
],
|
||||
hoverOffset: 4
|
||||
}]
|
||||
};
|
||||
|
||||
})
|
||||
onMounted(() => {
|
||||
const ctx = document.getElementById(`doughnutChart_${this.props.record.id}`);
|
||||
new Chart(ctx, {
|
||||
type: this.type,
|
||||
data: this.data,
|
||||
options: this.options
|
||||
})
|
||||
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,13 @@ const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsFlower extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,12 @@ const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsFunnelChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
// this.type = 'doughnut';
|
||||
onMounted(() => {
|
||||
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,26 @@
|
||||
|
||||
import { KsChartBase } from "./chart_base"
|
||||
|
||||
const { Component, onWillStart, onMounted } = owl;
|
||||
const { Component, onWillStart, onMounted, onWillUnmount } = owl;
|
||||
|
||||
export class KsHorizontalBarChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
this.type = 'bar';
|
||||
this.options = {
|
||||
...this.options,
|
||||
...{indexAxis: 'y'},
|
||||
// ...{scales: {
|
||||
// x: {
|
||||
// beginAtZero: true
|
||||
// }
|
||||
// }}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,13 @@ import { KsChartBase } from "./chart_base"
|
||||
const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsLineChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.lineChart';
|
||||
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
this.type = 'line';
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,7 @@ import { KsChartBase } from "./chart_base"
|
||||
const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsMap extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
|
||||
@@ -6,18 +6,18 @@ import { KsChartBase } from "./chart_base"
|
||||
const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsPieChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.pieChart';
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
this.type = 'pie';
|
||||
|
||||
onWillStart(() => {
|
||||
// this.chartData = JSON.parse(this.props.record.ks_chart_data);
|
||||
})
|
||||
onMounted(() => {
|
||||
const ctx = document.getElementById(`pieChart_${this.props.record.id}`);
|
||||
|
||||
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,13 @@ import { KsChartBase } from "./chart_base"
|
||||
const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsPolarAreaChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
this.type = 'polarArea';
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,13 @@ import { KsChartBase } from "./chart_base"
|
||||
const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsRadar extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
this.type = 'radar';
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,13 @@ import { KsChartBase } from "./chart_base"
|
||||
const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsRadialBarChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
this.type = 'radar';
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,13 @@ import { KsChartBase } from "./chart_base"
|
||||
const { Component, onWillStart, onMounted } = owl;
|
||||
|
||||
export class KsScatterChart extends KsChartBase {
|
||||
|
||||
static template = 'ks_dashboard_ninja.scatterChart';
|
||||
|
||||
|
||||
static template = 'ks_dashboard_ninja.chartBase';
|
||||
setup() {
|
||||
super.setup();
|
||||
onMounted(() => {
|
||||
this.type = 'scatter';
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,8 @@
|
||||
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { itemProps } from "../item_props";
|
||||
|
||||
import { KsToolBar } from "../tool_bar";
|
||||
|
||||
const { Component, onWillStart, onWillUpdateProps, useRef, onMounted, useState, onWillUnmount } = owl;
|
||||
const { Component, onWillStart, useState } = owl;
|
||||
|
||||
export class KsList extends Component {
|
||||
|
||||
@@ -19,12 +17,17 @@ export class KsList extends Component {
|
||||
|
||||
setup() {
|
||||
this.action = useService('action');
|
||||
this.orm = useService('orm');
|
||||
this.ref = useState({
|
||||
listData: null
|
||||
listData: null,
|
||||
prevOffset: false,
|
||||
nextOffset: false
|
||||
})
|
||||
|
||||
onWillStart(() => {
|
||||
this.ref.listData = JSON.parse(this.props.record.ks_list_view_data)
|
||||
this.ref.listData = JSON.parse(this.props.record.ks_list_view_data);
|
||||
this.ref.prevOffset = 1;
|
||||
this.ref.nextOffset = this.ref.listData?.data_rows?.length ?? false;
|
||||
})
|
||||
|
||||
}
|
||||
@@ -43,4 +46,42 @@ export class KsList extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
loadPreviousRecords() {
|
||||
const {id, ks_pagination_limit, ks_dashboard_id} = this.props.record
|
||||
const ksOffset = parseInt(this.ref.prevOffset) - (ks_pagination_limit + 1)
|
||||
this.orm.call('ks_dashboard_ninja.board', 'ks_get_list_view_data_offset', [id, {
|
||||
ks_intial_count: this.ref.nextOffset,
|
||||
offset: ksOffset,
|
||||
}, parseInt(ks_dashboard_id), {} ]).then(result => {
|
||||
this.ref.listData = JSON.parse(result.ks_list_view_data);
|
||||
this.ref.prevOffset = result.offset;
|
||||
this.ref.nextOffset = result.next_offset;
|
||||
})
|
||||
}
|
||||
|
||||
loadMoreRecords() {
|
||||
const {id, ks_dashboard_id} = this.props.record
|
||||
this.orm.call('ks_dashboard_ninja.board', 'ks_get_list_view_data_offset', [id, {
|
||||
ks_intial_count: this.ref.prevOffset,
|
||||
offset: this.ref.nextOffset,
|
||||
}, parseInt(ks_dashboard_id), {} ]).then(result => {
|
||||
this.ref.listData = JSON.parse(result.ks_list_view_data);
|
||||
this.ref.prevOffset = result.offset;
|
||||
this.ref.nextOffset = result.next_offset;
|
||||
})
|
||||
}
|
||||
|
||||
get prevBtnClass() {
|
||||
if (this.ref.prevOffset === 1) {
|
||||
return 'ks_event_offer_list'
|
||||
}
|
||||
}
|
||||
|
||||
get nextBtnClass() {
|
||||
const { ks_pagination_limit, ks_record_count } = this.props.record
|
||||
if (ks_pagination_limit < this.ref.nextOffset || ks_pagination_limit >= ks_record_count) {
|
||||
return 'ks_event_offer_list'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,23 +8,27 @@
|
||||
<div class="d-flex align-items-center w-50">
|
||||
<h6 t-esc="props?.record.name"/>
|
||||
</div>
|
||||
<!-- <div class="ks_pager_name">-->
|
||||
<!-- <div class="ks_pager">-->
|
||||
<!-- <span class="ks_counter">-->
|
||||
<!-- <span class="ks_value">-->
|
||||
<!-- <t t-esc="props.record.ks_record_count"/>-->
|
||||
<!-- </span>-->
|
||||
<!-- </span>-->
|
||||
<!-- <span class="btn-group" aria-atomic="true" t-att-data-next_offset="intial_count"-->
|
||||
<!-- t-att-data-prev-offset="offset">-->
|
||||
<!-- <button type="button"-->
|
||||
<!-- class="fa fa-chevron-left btn ks_load_previous ks_event_offer_list"-->
|
||||
<!-- t-att-data-item-id="props.record.id" title="Previous"/>-->
|
||||
<!-- <button type="button" class="fa fa-chevron-right btn ks_load_next"-->
|
||||
<!-- t-att-data-item-id="props.record.id" title="Next"/>-->
|
||||
<!-- </span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="ks_pager_name">
|
||||
<div class="ks_pager">
|
||||
<span class="ks_counter">
|
||||
<span class="ks_value">
|
||||
<t t-esc="ref.prevOffset"/>-<t t-esc="ref.nextOffset"/>
|
||||
</span>
|
||||
</span>
|
||||
<span class="btn-group" aria-atomic="true"
|
||||
t-att-data-next-offset="ref.prevOffset"
|
||||
t-att-data-prev-offset="ref.nextOffset">
|
||||
<button type="button" class="fa fa-chevron-left btn"
|
||||
t-on-click="loadPreviousRecords"
|
||||
t-att-class="prevBtnClass"
|
||||
t-att-data-item-id="props.record.id" title="Previous"/>
|
||||
<button type="button" class="fa fa-chevron-right btn"
|
||||
t-att-class="nextBtnClass"
|
||||
t-on-click="loadMoreRecords"
|
||||
t-att-data-item-id="props.record.id" title="Next"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header ks_dashboard_item_header_hover chart_button_container d-md-flex d-none">
|
||||
<KsToolBar item="props?.record"
|
||||
dashboardList="props.dashboardList"
|
||||
@@ -33,58 +37,56 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body table-responsive">
|
||||
<t t-if="ref.listData">
|
||||
<table id="ListViewTable" class="table table-hover list_view_layout_1"
|
||||
t-att-data-model="ref.listData.model">
|
||||
<thead>
|
||||
<tr>
|
||||
<t t-foreach="ref.listData['label']" t-as="header" t-key="header_index">
|
||||
<th>
|
||||
<t t-esc="header"/>
|
||||
</th>
|
||||
</t>
|
||||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table_body">
|
||||
<t t-foreach="ref.listData.data_rows" t-as="row" t-key="row_index">
|
||||
<tr class="tr" t-att-data-record-id="row['id']" t-att-data-domain="row['domain']"
|
||||
t-att-data-item-Id="props.record.id"
|
||||
t-att-data-sequence="row['sequence']" t-att-data-last_seq="row['last_seq']">
|
||||
<t t-set="rec_count" t-value="0" />
|
||||
<t t-foreach="row['data']" t-as="data" t-key="data_index">
|
||||
<t t-if="row['ks_column_type'][rec_count]=='html'">
|
||||
<td class="list_canvas_click">
|
||||
<t t-out="data"/>
|
||||
</td>
|
||||
<t t-set="data" t-value="rec_count + 1"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="list_canvas_click">
|
||||
<t t-esc="data"/>
|
||||
</td>
|
||||
<t t-set="rec_count" t-value="rec_count + 1"/>
|
||||
</t>
|
||||
</t>
|
||||
<td class="info">
|
||||
<i id="item_info" t-att-data-model="ref.listData.model"
|
||||
t-att-data-list-type="ref.listData.list_view_type"
|
||||
t-att-data-groupby="ref.listData.groupby"
|
||||
t-att-data-record-id="row['id']" t-att-data-item-id="props.record.id"
|
||||
t-att-data-list-view-type="list_type"
|
||||
t-att-data-domain="row['domain']"
|
||||
t-on-click="() => this.onClickAction(row)"
|
||||
t-att-class="'ks_item_click '"
|
||||
class="fa fa-pencil"/>
|
||||
</td>
|
||||
</tr>
|
||||
<t t-if="ref.listData">
|
||||
<table id="ListViewTable" class="table table-hover list_view_layout_1" t-att-data-model="ref.listData.model">
|
||||
<thead>
|
||||
<tr>
|
||||
<t t-foreach="ref.listData['label']" t-as="header" t-key="header_index">
|
||||
<th>
|
||||
<t t-esc="header"/>
|
||||
</th>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">No Data Present</t>
|
||||
</div>
|
||||
|
||||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table_body">
|
||||
<t t-foreach="ref.listData.data_rows" t-as="row" t-key="row_index">
|
||||
<tr class="tr" t-att-data-record-id="row['id']" t-att-data-domain="row['domain']"
|
||||
t-att-data-item-Id="props.record.id"
|
||||
t-att-data-sequence="row['sequence']" t-att-data-last_seq="row['last_seq']">
|
||||
<t t-set="rec_count" t-value="0" />
|
||||
<t t-foreach="row['data']" t-as="data" t-key="data_index">
|
||||
<t t-if="row['ks_column_type'][rec_count]=='html'">
|
||||
<td class="list_canvas_click">
|
||||
<t t-out="data"/>
|
||||
</td>
|
||||
<t t-set="data" t-value="rec_count + 1"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="list_canvas_click">
|
||||
<t t-esc="data"/>
|
||||
</td>
|
||||
<t t-set="rec_count" t-value="rec_count + 1"/>
|
||||
</t>
|
||||
</t>
|
||||
<td class="info">
|
||||
<i id="item_info" t-att-data-model="ref.listData.model"
|
||||
t-att-data-list-type="ref.listData.list_view_type"
|
||||
t-att-data-groupby="ref.listData.groupby"
|
||||
t-att-data-record-id="row['id']" t-att-data-item-id="props.record.id"
|
||||
t-att-data-list-view-type="list_type"
|
||||
t-att-data-domain="row['domain']"
|
||||
t-on-click="() => this.onClickAction(row)"
|
||||
t-att-class="'ks_item_click '"
|
||||
class="fa fa-pencil"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">No Data Present</t>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useService } from "@web/core/utils/hooks";
|
||||
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
|
||||
import { download } from "@web/core/network/download";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { loadCSS } from "@web/core/assets";
|
||||
const { Component, onWillStart, onWillUpdateProps, useRef, onMounted, useState } = owl;
|
||||
|
||||
export class KsToolBar extends Component {
|
||||
@@ -26,11 +27,36 @@ export class KsToolBar extends Component {
|
||||
targetId: null,
|
||||
targetName: null,
|
||||
});
|
||||
|
||||
this.chartTypeMap = new Map([
|
||||
['ks_pie_chart', 'pie'],
|
||||
['ks_bar_chart', 'bar'],
|
||||
['ks_line_chart', 'line'],
|
||||
['ks_horizontalBar_chart', 'bar'],
|
||||
['ks_area_chart', 'line'],
|
||||
['ks_doughnut_chart', 'doughnut'],
|
||||
['ks_polarArea_chart', 'polarArea'],
|
||||
['ks_radialBar_chart', 'radar'],
|
||||
['ks_scatter_chart', 'scatter'],
|
||||
['ks_funnel_chart', 'bar'],
|
||||
['ks_bullet_chart', 'bar'],
|
||||
['ks_radar_view', 'radar'],
|
||||
['ks_flower_view', 'flower'],
|
||||
])
|
||||
this.chartColorOptions = {
|
||||
default: 'default',
|
||||
cool: 'cool',
|
||||
warm: 'warm',
|
||||
neon: 'neon',
|
||||
}
|
||||
|
||||
onWillStart(async () => {
|
||||
// await loadCSS('ks_dashboard_ninja/static/src/items/css/dialog.css');
|
||||
})
|
||||
}
|
||||
|
||||
changeDashboard(ev) {
|
||||
this.state.targetId = ev.target.value
|
||||
// console.log(ev.target)
|
||||
}
|
||||
|
||||
async duplicateItem() {
|
||||
@@ -46,8 +72,6 @@ export class KsToolBar extends Component {
|
||||
type: "success",
|
||||
}
|
||||
);
|
||||
|
||||
// this.refreshAction()
|
||||
}
|
||||
|
||||
async moveItem() {
|
||||
@@ -62,8 +86,6 @@ export class KsToolBar extends Component {
|
||||
type: "success",
|
||||
}
|
||||
);
|
||||
|
||||
// this.refreshAction()
|
||||
}
|
||||
|
||||
customizeItem() {
|
||||
@@ -81,7 +103,7 @@ export class KsToolBar extends Component {
|
||||
|
||||
removeItem() {
|
||||
this.dialog.add(ConfirmationDialog, {
|
||||
body: _t('确定删除这个图表吗?'),
|
||||
body: _t('确定删除这个图表吗'),
|
||||
cancel: () => {},
|
||||
confirm: () => this.doDelete(),
|
||||
});
|
||||
@@ -100,12 +122,144 @@ export class KsToolBar extends Component {
|
||||
|
||||
}
|
||||
|
||||
exportItem() {
|
||||
console.log(`id`)
|
||||
onClickMoreInfo() {
|
||||
const {
|
||||
name,
|
||||
ks_model_name,
|
||||
ks_chart_data,
|
||||
ks_chart_groupby_type,
|
||||
ks_chart_relation_groupby_name,
|
||||
ks_chart_date_groupby,
|
||||
ks_show_records,
|
||||
action,
|
||||
ks_is_client_action,
|
||||
ks_domain
|
||||
} = this.props.item;
|
||||
debugger
|
||||
const domain = JSON.parse(ks_chart_data).previous_domain
|
||||
// const groupBy = ks_chart_groupby_type === 'relational_type' ? ks_chart_relation_groupby_name : ks_chart_relation_groupby_name + ':' + ks_chart_date_groupby;
|
||||
const groupBy = ks_chart_relation_groupby_name;
|
||||
if (ks_show_records) {
|
||||
let newAction;
|
||||
if (action) {
|
||||
if (!ks_is_client_action) {
|
||||
newAction = Object.assign({}, action);
|
||||
if (newAction.view_mode.includes('tree')) {
|
||||
newAction.view_mode = newAction.view_mode.replace('tree', 'list');
|
||||
}
|
||||
for (let i = 0; i < newAction.views.length; i++) {
|
||||
newAction.views[i][1].includes('tree') ? newAction.views[i][1] = newAction.views[i][1].replace('tree', 'list') : newAction.views[i][1]
|
||||
}
|
||||
newAction['domain'] = ks_domain || [];
|
||||
newAction['search_view_id'] = [newAction.search_view_id, 'search']
|
||||
} else {
|
||||
newAction = Object.assign({}, action[0]);
|
||||
if (newAction.params) {
|
||||
newAction.params.default_active_id || 'mailbox_inbox';
|
||||
} else {
|
||||
newAction.params = {
|
||||
'default_active_id': 'mailbox_inbox'
|
||||
}
|
||||
newAction.context = {}
|
||||
newAction.context.params = {
|
||||
'active_model': false
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newAction = {
|
||||
name: name,
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: ks_model_name,
|
||||
domain: domain || [],
|
||||
context: {
|
||||
'group_by': groupBy ? groupBy : false ,
|
||||
},
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
view_mode: 'list',
|
||||
target: 'current',
|
||||
}
|
||||
}
|
||||
this.action.doAction(newAction);
|
||||
}
|
||||
}
|
||||
|
||||
// refreshAction() {
|
||||
// const { action, context, params} = this.action.currentController
|
||||
// // this.action.doAction(action)
|
||||
// }
|
||||
onClickColorOption(color) {
|
||||
if (color === this.props.item.ks_chart_item_color) {
|
||||
return;
|
||||
}
|
||||
this.orm.write('ks_dashboard_ninja.item', [this.props.item.id], {ks_chart_item_color: color});
|
||||
}
|
||||
async exportExcelOrCsv(type) {
|
||||
const { id, name, ks_dashboard_item_type, ks_chart_data, ks_list_view_data } = this.props.item;
|
||||
const fmt = ks_dashboard_item_type === 'ks_list_view'? `list_${type}` : `chart_${type}`;
|
||||
const context = this.__owl__.parent.parent.parent.parent.props.getContext();
|
||||
let data = {
|
||||
header: name,
|
||||
chart_data: ks_dashboard_item_type === 'ks_list_view' ? ks_list_view_data : ks_chart_data,
|
||||
};
|
||||
|
||||
if (ks_dashboard_item_type === 'ks_list_view') {
|
||||
data = {
|
||||
...data,
|
||||
ks_export_boolean: true,
|
||||
context: context,
|
||||
params: {},
|
||||
ks_item_id: id,
|
||||
};
|
||||
}
|
||||
download({
|
||||
data: {
|
||||
data: JSON.stringify(data),
|
||||
},
|
||||
url: `/ks_dashboard_ninja/export/${fmt}`,
|
||||
});
|
||||
}
|
||||
|
||||
async exportPdf() {
|
||||
const { id, name, ks_dashboard_item_type } = this.props.item;
|
||||
const { chartIns } = this.__owl__.parent.component
|
||||
const base64Image = chartIns.toBase64Image();
|
||||
const canvas = document.getElementById(`${ks_dashboard_item_type}_${id}`);
|
||||
const ImgDef = {
|
||||
content: [{
|
||||
image: canvas.toDataURL("image/png"),
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
}],
|
||||
images: {
|
||||
bee: base64Image,
|
||||
}
|
||||
}
|
||||
pdfMake.createPdf(ImgDef).download(name + '.pdf');
|
||||
}
|
||||
|
||||
async exportImg() {
|
||||
const name = this.props.item.name;
|
||||
const { chartIns } = this.__owl__.parent.component;
|
||||
const base64Image = chartIns.toBase64Image();
|
||||
const link = document.createElement('a');
|
||||
link.href = base64Image;
|
||||
link.download = name;
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
async exportItem() {
|
||||
const { id, name} = this.props.item;
|
||||
const data = {
|
||||
header: name,
|
||||
item_id: id,
|
||||
}
|
||||
download({
|
||||
data: {
|
||||
data: JSON.stringify(data),
|
||||
},
|
||||
url: "/ks_dashboard_ninja/export/item_json",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -4,51 +4,49 @@
|
||||
|
||||
<t t-name="ks_dashboard_ninja.toolBar">
|
||||
|
||||
<button class="ks_dashboard_item_chart_info" title="More Information" type="button" t-att-data-item-id="props.item.id">
|
||||
<button class="ks_dashboard_item_chart_info" title="More Info" type="button"
|
||||
t-att-data-item-id="props.item.id"
|
||||
t-on-click.stop="onClickMoreInfo"
|
||||
t-if="!['ks_tile', 'ks_kpi', 'ks_to_do', 'ks_list_view'].includes(props.item.ks_dashboard_item_type)" >
|
||||
<i class="fa fa-info-circle"/>
|
||||
</button>
|
||||
|
||||
<div class="ks_chart_inner_buttons">
|
||||
<button title="调色板" data-bs-toggle="dropdown" class="ks_dashboard_item_action ks_dashboard_color_option btn dropdown-toggle btn-xs o-no-caret show" type="button" aria-expanded="true">
|
||||
<div class="ks_chart_inner_buttons"
|
||||
t-if="!['ks_tile', 'ks_kpi', 'ks_to_do', 'ks_list_view'].includes(props.item.ks_dashboard_item_type)">
|
||||
<button title="Color Palette"
|
||||
data-bs-toggle="dropdown"
|
||||
class="ks_dashboard_item_action ks_dashboard_color_option btn dropdown-toggle btn-xs o-no-caret"
|
||||
type="button"
|
||||
aria-expanded="true">
|
||||
<i class="fa fa-paint-brush"/>
|
||||
</button>
|
||||
<ul role="menu" class="dropdown-menu dropdown-menu-right ks_color_pallate " t-att-data-item-id="props.item.id" data-chart-type="doughnut" data-chart-family="circle" style="position: absolute; inset: 0px auto auto 0px; margin: 0px; transform: translate(403px, 45px);" data-popper-placement="bottom-start">
|
||||
<li class="ks_li_default ks_date_filter_selected">
|
||||
<span class="default ks_chart_color_options" data-chart-color="default">
|
||||
default
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="ks_li_cool">
|
||||
<span class="cool ks_chart_color_options" data-chart-color="cool">
|
||||
cool
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="ks_li_warm">
|
||||
<span class="warm ks_chart_color_options" data-chart-color="warm">
|
||||
warm
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="ks_li_neon">
|
||||
<span class="neon ks_chart_color_options" data-chart-color="neon">
|
||||
neon
|
||||
</span>
|
||||
</li>
|
||||
<ul role="menu" class="dropdown-menu dropdown-menu-right ks_color_pallate"
|
||||
t-on-click.stop=""
|
||||
t-att-data-item-id="props.item.id"
|
||||
t-att-data-chart-type="this.chartTypeMap.get(props.item.ks_dashboard_item_type)"
|
||||
data-chart-family="circle">
|
||||
|
||||
<t t-foreach="chartColorOptions" t-as="color" t-key="color_index">
|
||||
<li t-attf-class="{{ color === props.item.ks_chart_item_color ? 'ks_date_filter_selected': ''}}"
|
||||
t-on-click="() => this.onClickColorOption(color)">
|
||||
<span class="ks_chart_color_options" t-att-data-chart-color="color">
|
||||
<t t-esc="color"/>
|
||||
</span>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Dashboard Item Move/Copy Feature -->
|
||||
<button title="Move/Duplicate" data-bs-toggle="dropdown"
|
||||
class="dashboard_item_action btn dropdown-toggle btn-xs d-md-block d-none"
|
||||
type="button"
|
||||
t-on-click.stop=""
|
||||
t-att-style="'color:'+ rgba_button_color + ';'"
|
||||
aria-expanded="true">
|
||||
<i class="fa fa-files-o"/>
|
||||
</button>
|
||||
|
||||
<ul role="menu" class="dashboard_menu_container dropdown-menu dropdown-menu-right">
|
||||
<ul role="menu" class="dashboard_menu_container dropdown-menu dropdown-menu-right" t-on-click.stop="">
|
||||
<li class="md_heading">
|
||||
<span>Select Dashboard</span>
|
||||
</li>
|
||||
@@ -63,20 +61,20 @@
|
||||
</select>
|
||||
</li>
|
||||
<li class="mt-3">
|
||||
<button class="btn btn-primary o_apply_group o_add_group duplicate_item"
|
||||
t-on-click="(ev) => this.duplicateItem(ev)"
|
||||
<button class="btn btn-primary o_apply_group o_add_group"
|
||||
t-on-click.stop="(ev) => this.duplicateItem(ev)"
|
||||
tabindex="-1" type="button">Duplicate
|
||||
</button>
|
||||
<button class="btn btn-primary o_apply_group o_add_group move_item" tabindex="-1"
|
||||
t-on-click="(ev) => this.moveItem(ev)"
|
||||
<button class="btn btn-primary o_apply_group o_add_group mx-1" tabindex="-1"
|
||||
t-on-click.stop="(ev) => this.moveItem(ev)"
|
||||
type="button">Move
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button title="Quick Customize"
|
||||
<button title="Edit"
|
||||
class="dashboard_quick_edit_action_popup d-md-block d-none "
|
||||
t-on-click="customizeItem"
|
||||
t-on-click.stop="customizeItem"
|
||||
type="button" t-att-data-item-id="props.item.id">
|
||||
<i class="fa fa-pencil"/>
|
||||
</button>
|
||||
@@ -89,57 +87,55 @@
|
||||
|
||||
<button type="button" title="Remove Item"
|
||||
class="dashboard_item_delete d-md-block d-none"
|
||||
t-on-click="removeItem">
|
||||
t-on-click.stop="removeItem">
|
||||
<i class="fa fa-times"/>
|
||||
</button>
|
||||
|
||||
<!-- <div class="chart_inner_buttons dropdown d-md-none dn-setting-panel">-->
|
||||
<!-- <button data-bs-toggle="dropdown"-->
|
||||
<!-- class="dashboard_item_action btn dropdown-toggle btn-xs o-no-caret btn"-->
|
||||
<!-- type="button"-->
|
||||
<!-- t-att-style="'color:'+ rgba_button_color + ';'"-->
|
||||
<!-- aria-expanded="true">-->
|
||||
<!-- <i class="fa fa-cog"/>-->
|
||||
<!-- </button>-->
|
||||
<!-- <div role="menu" class="dropdown-menu dropdown-menu-right chart_inner_min_width">-->
|
||||
<!-- <!–Dynamic Rendering–>-->
|
||||
<!-- <div class="chart_export_menu">-->
|
||||
<!-- <div class="chart_export_menu_item"-->
|
||||
<!-- t-on-click="customizeItem"-->
|
||||
<!-- t-att-data-item-id="props.item.id"-->
|
||||
<!-- data-format="chart_xls">-->
|
||||
<!-- <i class="fa fa-pencil"/>-->
|
||||
<!-- <span>Customize Item</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="chart_export_menu_item"-->
|
||||
<!-- t-on-click="removeItem"-->
|
||||
<!-- t-att-data-item-id="props.item.id"-->
|
||||
<!-- data-format="chart_xls">-->
|
||||
<!-- <i class="fa fa-times"/>-->
|
||||
<!-- <span>Remove Item</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<div class="chart_inner_buttons d-md-block d-none">
|
||||
<div class="ks_chart_inner_buttons d-md-block d-none">
|
||||
<button title="Export Item" data-bs-toggle="dropdown"
|
||||
class="dashboard_item_action btn dropdown-toggle btn-xs o-no-caret btn"
|
||||
type="button"
|
||||
t-on-click.stop=""
|
||||
t-att-style="'color:'+ rgba_button_color + ';'"
|
||||
aria-expanded="true">
|
||||
<i class="fa fa-ellipsis-v"/>
|
||||
</button>
|
||||
<div role="menu" class="dropdown-menu dropdown-menu-right">
|
||||
<!--Dynamic Rendering-->
|
||||
<div class="chart_export_menu">
|
||||
<div class="chart_export_menu_header">
|
||||
<div class="ks_chart_export_menu">
|
||||
<div class="ks_chart_export_menu_header">
|
||||
<span>Export</span>
|
||||
</div>
|
||||
<div class="chart_json_export chart_export_menu_item"
|
||||
<div class=" ks_chart_export_menu_item"
|
||||
t-on-click.stop="() => this.exportExcelOrCsv('xls')"
|
||||
t-if="!['ks_tile', 'ks_kpi', 'ks_to_do'].includes(props.item.ks_dashboard_item_type)"
|
||||
t-att-data-chart-id="props.item.id">
|
||||
<i class="fa fa-file-excel-o"/>
|
||||
<span>Export to Excel</span>
|
||||
</div>
|
||||
<div class="ks_chart_export_menu_item"
|
||||
t-on-click.stop="() => this.exportExcelOrCsv('csv')"
|
||||
t-if="!['ks_tile', 'ks_kpi', 'ks_to_do'].includes(props.item.ks_dashboard_item_type)"
|
||||
t-att-data-chart-id="props.item.id">
|
||||
<i class="fa fa-file-code-o"/>
|
||||
<span>Export to CSV</span>
|
||||
</div>
|
||||
<div t-att-data-chart-id="props.item.id"
|
||||
t-on-click.stop="exportPdf"
|
||||
t-if="!['ks_tile', 'ks_kpi', 'ks_to_do', 'ks_list_view'].includes(props.item.ks_dashboard_item_type)"
|
||||
class="ks_chart_export_menu_item">
|
||||
<i class="fa fa-file-pdf-o"/>
|
||||
<span>Save as PDF</span>
|
||||
</div>
|
||||
<div class="ks_chart_export_menu_item"
|
||||
t-att-data-chart-id="props.item.id"
|
||||
t-if="!['ks_tile', 'ks_kpi', 'ks_to_do', 'ks_list_view'].includes(props.item.ks_dashboard_item_type)"
|
||||
t-on-click.stop="exportImg">
|
||||
<i class="fa fa-file-image-o"/>
|
||||
<span>Save as Image</span>
|
||||
</div>
|
||||
<div class="ks_chart_export_menu_item"
|
||||
t-att-data-item-id="props.item.id"
|
||||
t-on-click="exportItem"
|
||||
data-format="chart_xls">
|
||||
t-on-click.stop="exportItem">
|
||||
<i class="fa fa-file-code-o"/>
|
||||
<span>Export Item</span>
|
||||
</div>
|
||||
@@ -147,33 +143,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart_inner_buttons dropdown ">
|
||||
<div class="ks_chart_inner_buttons dropdown ">
|
||||
<button title="Info" data-bs-toggle="dropdown"
|
||||
class="item_description btn dropdown-toggle btn-xs o-no-caret btn"
|
||||
type="button"
|
||||
t-on-click.stop=""
|
||||
t-att-style="'color:'+ rgba_button_color + ';'"
|
||||
aria-expanded="true">
|
||||
<i class="fa fa-book"/>
|
||||
</button>
|
||||
<div role="menu" class="dropdown-menu dropdown-menu-right" style="width:20rem">
|
||||
<div role="menu" class="dropdown-menu dropdown-menu-right"
|
||||
style="width: 20rem; position: absolute; inset: auto auto 0px 0px; margin: 0px; transform: translate(0px, -33px);">
|
||||
<!--Dynamic Rendering-->
|
||||
<div class="chart_export_menu">
|
||||
<div class="chart_export_menu_header" style="margin-left:-10px">
|
||||
<div class="ks_chart_export_menu">
|
||||
<div class="chart_export_menu_header" style="margin-left:10px">
|
||||
<span>Info</span>
|
||||
</div>
|
||||
<div class="info" style="margin-left:10px">
|
||||
<div class="ks_info" style="margin-left:10px">
|
||||
<span>Company:
|
||||
<t t-esc="props.item?.ks_company"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info" style="margin-left:10px">
|
||||
<div class="ks_info" style="margin-left:10px">
|
||||
<t t-if="props.item.ks_info">
|
||||
<t t-foreach="props.item.ks_info" t-as="ks" t-key="ks_index">
|
||||
<span>
|
||||
<t t-esc="ks"/>
|
||||
</span>
|
||||
<br/>
|
||||
</t>
|
||||
<span>Description:
|
||||
<t t-esc="props.item.ks_info"/>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,6 +26,10 @@ export class BasePreview extends Component {
|
||||
return get_rgba_format(this.props.record.data.ks_button_color)
|
||||
}
|
||||
|
||||
get header_bg_color() {
|
||||
return get_rgba_format(this.props.record.data.ks_header_bg_color);
|
||||
}
|
||||
|
||||
get rgba_background_color() {
|
||||
return get_rgba_format(this.props.record.data.ks_background_color)
|
||||
}
|
||||
@@ -43,4 +47,8 @@ export class BasePreview extends Component {
|
||||
return mainBodyStyle(rgba_background_color, rgba_font_color, this.props.record.data).background_style;
|
||||
}
|
||||
|
||||
get ks_icon_url() {
|
||||
return `/web/image?model=ks_dashboard_ninja.item&id=${this.props.record.resId}&field=ks_icon`
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { BasePreview } from "./base_preview";
|
||||
import { standardWidgetProps } from "@web/views/widgets/standard_widget_props";
|
||||
|
||||
const { Component, onMounted, onWillUnmount, onPatched, useRef} = owl;
|
||||
|
||||
export class ChartPreview extends Component {
|
||||
static template = 'ks_dashboard_ninja.chartPreview';
|
||||
static props = {...standardWidgetProps};
|
||||
|
||||
setup() {
|
||||
this.chartIns = null;
|
||||
this.chartCtx = useRef('chartCtx');
|
||||
this.chartTypeMap = new Map([
|
||||
['ks_pie_chart', 'pie'],
|
||||
['ks_bar_chart', 'bar'],
|
||||
['ks_line_chart', 'line'],
|
||||
['ks_horizontalBar_chart', 'bar'],
|
||||
['ks_area_chart', 'line'],
|
||||
['ks_doughnut_chart', 'doughnut'],
|
||||
['ks_polarArea_chart', 'polarArea'],
|
||||
['ks_radialBar_chart', 'radar'],
|
||||
['ks_scatter_chart', 'scatter'],
|
||||
['ks_funnel_chart', 'bar'],
|
||||
['ks_bullet_chart', 'bar'],
|
||||
// ['ks_radar_view', 'radar'],
|
||||
// ['ks_flower_view', 'flower'],
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
this.renderChart(this.chartCtx.el)
|
||||
})
|
||||
|
||||
onPatched(() => {
|
||||
if (this.chartIns){
|
||||
this.chartIns.destroy();
|
||||
}
|
||||
this.renderChart(this.chartCtx.el);
|
||||
})
|
||||
|
||||
onWillUnmount(() => {
|
||||
if (this.chartIns) {
|
||||
this.chartIns.destroy();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setChartData() {
|
||||
this.chartData = JSON.parse(this.props.record.data.ks_chart_data);
|
||||
this.data = {
|
||||
labels: this.chartData.labels ?? [],
|
||||
datasets: this.chartData.datasets ?? '',
|
||||
}
|
||||
this.options = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
},
|
||||
indexAxis: 'x',
|
||||
}
|
||||
if (this.ks_type === 'ks_horizontalBar_chart') {
|
||||
this.options.indexAxis ='y';
|
||||
}
|
||||
if (this.props.record.data.ks_semi_circle_chart && ["pie", "doughnut"].includes(this.ks_type)) {
|
||||
this.options.rotation = Math.PI;
|
||||
this.options.circumference = Math.PI;
|
||||
}
|
||||
}
|
||||
|
||||
renderChart(ctx) {
|
||||
this.ks_type = this.props.record.data.ks_dashboard_item_type;
|
||||
this.setChartData();
|
||||
this.chartIns = new Chart(ctx, {
|
||||
type: this.chartTypeMap.get(this.ks_type),
|
||||
data: this.data,
|
||||
options: this.options
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registry.category("view_widgets").add("chart_preview", {
|
||||
component: ChartPreview,
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="ks_dashboard_ninja.chartPreview" >
|
||||
<div class="grid-stack-item-content ks_dashboarditem_chart_container ks_dashboard_item_hover card shadow" style="min-height: 20rem">
|
||||
<div class="p-3 py-3 d-flex flex-row align-items-center justify-content-between ">
|
||||
<div class="d-flex align-items-center w-50">
|
||||
<h6 t-esc="props?.record.data.name"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body ks_chart_card_body d-flex flex-row align-items-center justify-content-center">
|
||||
<canvas t-ref="chartCtx">
|
||||
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -11,10 +11,27 @@ export class KpiPreview extends BasePreview {
|
||||
static template = 'ks_dashboard_ninja.kpi_preview';
|
||||
|
||||
setup() {
|
||||
this.kpi_data = this.props.record.data.ks_kpi_data;
|
||||
this.kpi_data = JSON.parse(this.props.record.data.ks_kpi_data);
|
||||
this.count_1 = this.kpi_data[0] ? this.kpi_data[0]?.record_data : undefined;
|
||||
this.count_2 = this.kpi_data[1] ? this.kpi_data[1]?.record_data : undefined;
|
||||
this.target_1 = this.kpi_data[0]?.target;
|
||||
|
||||
this.deviation = Math.round((this.difference / this.target_1) * 100)
|
||||
if (this.deviation !== Infinity) {
|
||||
this.deviation = this.deviation ? this.deviation + '%' : 0 + '%';
|
||||
}
|
||||
|
||||
this.previous_period_data = this.kpi_data[0].previous_period;
|
||||
this.pre_diffrence = (this.count_1 - this.previous_period_data);
|
||||
const { ks_multiplier_active, ks_multiplier } = this.props.record.data
|
||||
if (ks_multiplier_active){
|
||||
this.previous_period_data = this.kpi_data[0].previous_period * ks_multiplier;
|
||||
this.pre_diffrence = (this.count_1 * ks_multiplier - this.previous_period_data);
|
||||
}
|
||||
|
||||
this.pre_achieve = this.pre_diffrence > 0;
|
||||
this.pre_diffrence = Math.abs(this.pre_diffrence);
|
||||
this.pre_deviation = this.previous_period_data ? parseInt((this.pre_diffrence / this.previous_period_data) * 100) + '%' : "100%"
|
||||
}
|
||||
|
||||
get difference() {
|
||||
@@ -31,12 +48,19 @@ export class KpiPreview extends BasePreview {
|
||||
return this.difference >= 0;
|
||||
}
|
||||
|
||||
get target_arrow() {
|
||||
get targetArrow() {
|
||||
if (this.deviation === Infinity) {
|
||||
return ;
|
||||
}
|
||||
return this.achieve ? 'up' : 'down';
|
||||
}
|
||||
|
||||
get pre_deviation() {
|
||||
get preDeviationColor() {
|
||||
return this.pre_achieve ? 'color: green' : 'color: red'
|
||||
}
|
||||
|
||||
get targetDeviationColor() {
|
||||
return this.achieve ? 'color: green' : 'color: red'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<t t-set="item" t-value="props?.record?.data" />
|
||||
<div class="ks_dashboard_kpi ks_dashboard_custom_srollbar ks_db_kpi_preview"
|
||||
t-att-class="'ks_dashboarditem_id ks_dashboard_item_hover ' + inner_container_class"
|
||||
t-att-style="style_main_body">
|
||||
t-att-style="'min-height: 9rem!important;' + style_main_body">
|
||||
<div class="ks_dashboard_icon_l5 ">
|
||||
<t t-if="item.ks_icon_select=='Custom'">
|
||||
<t t-if="ks_icon_url">
|
||||
@@ -34,10 +34,10 @@
|
||||
<span>vs Target</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="font-size : medium;">
|
||||
<t t-esc="item.deviation"/>
|
||||
<span style="font-size : medium;" t-att-style="targetDeviationColor">
|
||||
<t t-esc="deviation"/>
|
||||
<t t-if="achieve">
|
||||
<i t-att-class="'fa fa-arrow-'+ target_arrow"/>
|
||||
<i t-att-class="'fa fa-arrow-'+ targetArrow"/>
|
||||
</t>
|
||||
</span>
|
||||
</div>
|
||||
@@ -47,9 +47,9 @@
|
||||
<span>vs Prev</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="font-size : medium;">
|
||||
<span style="font-size : medium;" t-att-style="preDeviationColor">
|
||||
<t t-esc="pre_deviation"/>
|
||||
<i t-att-class="'fa fa-arrow-'+ pre_arrow"/>
|
||||
<i t-att-class="'fa fa-arrow-'+ preArrow"/>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { BasePreview } from "./base_preview";
|
||||
|
||||
const { useState, onMounted, onPatched } = owl;
|
||||
|
||||
export class ListPreview extends BasePreview {
|
||||
|
||||
static template = 'ks_dashboard_ninja.listPreview';
|
||||
|
||||
setup() {
|
||||
this.state = useState({
|
||||
listData: null,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
this.loadListData();
|
||||
|
||||
});
|
||||
onPatched(() => {
|
||||
this.loadListData();
|
||||
})
|
||||
}
|
||||
|
||||
get listData() {
|
||||
const toParse = this.props.record.data.ks_list_view_data;
|
||||
return toParse === "" ? "" : JSON.parse(toParse);
|
||||
}
|
||||
|
||||
loadListData() {
|
||||
this.state.listData = this.listData !== "" ? this.listData : null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
registry.category("view_widgets").add("list_preview", {
|
||||
component: ListPreview,
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="ks_dashboard_ninja.listPreview" >
|
||||
<div class="grid-stack-item-content ks_list_view_container ks_dashboard_item_hover card shadow">
|
||||
<div class="p-3 py-3 d-flex flex-row align-items-center justify-content-between ">
|
||||
<div class="d-flex align-items-center w-50">
|
||||
<h6 t-esc="props?.record.data.name"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body table-responsive">
|
||||
<t t-if="state.listData">
|
||||
<table id="ListViewTable" class="table table-hover list_view_layout_1"
|
||||
t-att-data-model="state.listData.model">
|
||||
<thead>
|
||||
<tr>
|
||||
<t t-foreach="state.listData['label']" t-as="header" t-key="header_index">
|
||||
<th>
|
||||
<t t-esc="header"/>
|
||||
</th>
|
||||
</t>
|
||||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table_body">
|
||||
<t t-foreach="state.listData.data_rows" t-as="row" t-key="row_index">
|
||||
<tr t-if="row_index < 5" class="tr" t-att-data-record-id="row['id']" t-att-data-domain="row['domain']"
|
||||
t-att-data-item-Id="props.record.id"
|
||||
t-att-data-sequence="row['sequence']" t-att-data-last_seq="row['last_seq']">
|
||||
<t t-set="rec_count" t-value="0" />
|
||||
<t t-foreach="row['data']" t-as="data" t-key="data_index">
|
||||
<t t-if="row['ks_column_type'][rec_count]=='html'">
|
||||
<td class="list_canvas_click">
|
||||
<t t-out="data"/>
|
||||
</td>
|
||||
<t t-set="data" t-value="rec_count + 1"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="list_canvas_click">
|
||||
<t t-esc="data"/>
|
||||
</td>
|
||||
<t t-set="rec_count" t-value="rec_count + 1"/>
|
||||
</t>
|
||||
</t>
|
||||
<td class="ks_info">
|
||||
<i id="item_info" t-att-data-model="state.listData.model"
|
||||
t-att-data-list-type="state.listData.list_view_type"
|
||||
t-att-data-groupby="state.listData.groupby"
|
||||
t-att-data-record-id="row['id']" t-att-data-item-id="props.record.id"
|
||||
t-att-data-list-view-type="list_type"
|
||||
t-att-data-domain="row['domain']"
|
||||
t-on-click="() => this.onClickAction(row)"
|
||||
t-att-class="'ks_item_click '"
|
||||
class="fa fa-pencil"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">No Data Present</t>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,50 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { BasePreview } from "./base_preview";
|
||||
|
||||
const { useState, onMounted, onPatched } = owl;
|
||||
|
||||
export class TodoPreview extends BasePreview {
|
||||
|
||||
static template = 'ks_dashboard_ninja.todoPreview';
|
||||
|
||||
setup() {
|
||||
this.state = useState({
|
||||
tab: 0,
|
||||
labels: null,
|
||||
tableData: null,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
this.prepareTableData();
|
||||
});
|
||||
onPatched(() => {
|
||||
this.prepareTableData();
|
||||
})
|
||||
}
|
||||
|
||||
get todoData() {
|
||||
const toParse = this.props.record.data.ks_to_do_data;
|
||||
return toParse === "" ? "" : JSON.parse(toParse);
|
||||
}
|
||||
|
||||
prepareTableData() {
|
||||
const items = this.todoData?.items ?? "";
|
||||
if (items === "") {
|
||||
return;
|
||||
}
|
||||
this.state.labels = items.map(item => [item.item_id, item.label]);
|
||||
this.state.tableData = items[this.state.tab];
|
||||
}
|
||||
|
||||
onTabClick(index) {
|
||||
this.state.tab = index;
|
||||
this.prepareTableData();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registry.category("view_widgets").add("todo_preview", {
|
||||
component: TodoPreview,
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="ks_dashboard_ninja.todoPreview" >
|
||||
<t t-set="item" t-value="props?.record.data"/>
|
||||
<div class="grid-stack-item" t-att-id="item.id">
|
||||
<div class="grid-stack-item-content ks_list_view_container ks_dashboard_item_hover card shadow"
|
||||
t-att-title="item.ks_info">
|
||||
<div class="ks_card_header" t-att-style=" 'background-color:' + header_bg_color ">
|
||||
<div class="p-3 py-3 d-flex flex-row align-items-center justify-content-between ">
|
||||
<div class="d-flex align-items-center w-50">
|
||||
<h6 class="m-0 font-weight-bold h3 mr-3 ks_list_view_heading" t-att-title="item.name">
|
||||
<t t-esc="item.name"/>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
<t t-call="ks_dashboard_ninja.todoPreviewHeader" />
|
||||
</div>
|
||||
<div class="ks_to_do_card_body card-body table-responsive">
|
||||
<t t-call="ks_dashboard_ninja.todoPreviewInnerContainer" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="ks_dashboard_ninja.todoPreviewHeader">
|
||||
<div class="card-header">
|
||||
<div class="nav-tabs-navigation">
|
||||
<div class="nav-tabs-wrapper">
|
||||
<t t-if="state.labels">
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" style="overflow:overlay">
|
||||
<t t-foreach="state.labels" t-as="label" t-key="label_index">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
t-att-style="'color:' + rgba_font_color"
|
||||
t-attf-class="{{ label_index == state.tab ? 'active': '' }}"
|
||||
t-att-data-item-id="item.id"
|
||||
t-att-data-section-id="label[label_index]"
|
||||
data-toggle="pill"
|
||||
t-att-href="label[1]"
|
||||
t-on-click.prevent.stop="() => this.onTabClick(label_index)"
|
||||
>
|
||||
<t t-esc="label[1]"/>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<ul class="nav nav-tabs" data-tabs="tabs">
|
||||
<li class="nav-item">
|
||||
No Section Available.
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="ks_dashboard_ninja.todoPreviewInnerContainer">
|
||||
<div class="container-fluid p-0">
|
||||
<div class="card">
|
||||
<t t-if="state.tableData?.lines.length > 0">
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<table class="table">
|
||||
<t t-foreach="state.tableData?.lines" t-as="data" t-key="data_index">
|
||||
<tr>
|
||||
<td class="ks_custom_check">
|
||||
<div class="form-check" >
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" t-att-checked="!data.ks_active"/>
|
||||
<span class="form-check-sign ks_do_item_active_handler"
|
||||
t-att-data-item-id="data.id">
|
||||
<span class="check"/>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
<td class="ks_description"
|
||||
t-att-id="'todoTask_' + data.id"
|
||||
t-attf-class="{{ data.ks_active ? '': 'ks_do_item_line_through' }}"
|
||||
t-esc="data.ks_description" />
|
||||
</tr>
|
||||
</t>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="" >
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<span class="nav-tabs-title">No Tasks Available</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -163,6 +163,7 @@
|
||||
<field context="{'current_id': id}" name="ks_dashboard_item_type" string="Type" />
|
||||
<field name="name" required="1"/>
|
||||
<field invisible="ks_data_calculation_type" name="data_source"/>
|
||||
<field name="ks_model_id" invisible="model_bool"/>
|
||||
<field name="ks_company_id" options="{'no_create': True, 'no_create_edit':True, 'no_open': True}"/>
|
||||
<field invisible="1" name="name_seq"/>
|
||||
<field invisible="1" name="ks_partners_map"/>
|
||||
@@ -205,6 +206,9 @@
|
||||
<div>
|
||||
<widget name="tile_preview" invisible="ks_dashboard_item_type != 'ks_tile'" />
|
||||
<widget name="kpi_preview" invisible="ks_dashboard_item_type != 'ks_kpi'" />
|
||||
<widget name="todo_preview" invisible="ks_dashboard_item_type != 'ks_to_do'" />
|
||||
<widget name="list_preview" invisible="ks_dashboard_item_type != 'ks_list_view'" />
|
||||
<widget name="chart_preview" invisible="not ks_model_id or ks_dashboard_item_type not in ['ks_bar_chart', 'ks_line_chart', 'ks_scatter_chart', 'ks_area_chart', 'ks_horizontalBar_chart', 'ks_pie_chart', 'ks_doughnut_chart', 'ks_polarArea_chart', 'ks_funnel_chart', 'ks_bullet_chart', 'ks_radialBar_chart']" />
|
||||
</div>
|
||||
<notebook>
|
||||
<page name="data_sets" string="Data">
|
||||
|
||||
@@ -16,21 +16,22 @@
|
||||
<field name="ks_sequence"/>
|
||||
</group>
|
||||
</group>
|
||||
<h3 class="oe_grey" invisible="not ks_template and not ks_template and not ks_template and not ks_template and not ks_template and not ks_template and not ks_template">
|
||||
<h3 class="oe_grey" invisible="ks_template not in [2, 3, 4, 5, 6, 7 ,8]">
|
||||
Preview
|
||||
</h3>
|
||||
<img alt="#" class="img img-fluid" invisible="not ks_template" src="/ks_dashboard_ninja/static/description/templates/ks_template1.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="not ks_template" src="/ks_dashboard_ninja/static/description/templates/ks_template2.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="not ks_template" src="/ks_dashboard_ninja/static/description/templates/ks_template3.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="not ks_template" src="/ks_dashboard_ninja/static/description/templates/ks_account_template.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="not ks_template" src="/ks_dashboard_ninja/static/description/templates/ks_crm_template.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="not ks_template" src="/ks_dashboard_ninja/static/description/templates/ks_inventory_template.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="not ks_template" src="/ks_dashboard_ninja/static/description/templates/ks_sale_template.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="ks_template != 2" src="/ks_dashboard_ninja/static/description/templates/ks_template1.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="ks_template != 3" src="/ks_dashboard_ninja/static/description/templates/ks_template2.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="ks_template != 4" src="/ks_dashboard_ninja/static/description/templates/ks_template3.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="ks_template != 5" src="/ks_dashboard_ninja/static/description/templates/ks_account_template.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="ks_template != 6" src="/ks_dashboard_ninja/static/description/templates/ks_crm_template.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="ks_template != 7" src="/ks_dashboard_ninja/static/description/templates/ks_inventory_template.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<img alt="#" class="img img-fluid" invisible="ks_template != 8" src="/ks_dashboard_ninja/static/description/templates/ks_sale_template.png" style="padding: 10px; border: 2px solid;"/>
|
||||
<footer>
|
||||
<button class="btn-primary" name="ks_create_record" string="Save" type="object"/>
|
||||
<button class="oe_link" special="cancel" string="Cancel"/>
|
||||
</footer>
|
||||
</form></field>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="ks_create_dashboard_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Create Dashboard</field>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
'license': 'LGPL-3',
|
||||
'depends': ['base', 'soong_workflow', 'yuthon_issue', 'hr', 'product'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/sps_approval_code_data.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/inherit_hr_employee_views.xml',
|
||||
'views/yuthon_office_supplies_views.xml',
|
||||
'views/yuthon_office_supplies_setting_views.xml',
|
||||
@@ -17,6 +17,4 @@
|
||||
'installable': True,
|
||||
'application': True,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import yuthon_office_supplies
|
||||
from . import yuthon_office_supplies_setting
|
||||
from . import inherit_hr_employee
|
||||
@@ -5,11 +5,11 @@ from odoo import api, fields, models
|
||||
class YuthonHrEmployee(models.Model):
|
||||
_inherit = "hr.employee"
|
||||
|
||||
office_supplies_ids = fields.One2many('yuthon.office.supplies.line', 'employee_id', string="办公用品明细行")
|
||||
office_supplies_ids = fields.One2many('yuthon.office.supplies.employee.line', 'employee_id', string="办公用品明细行")
|
||||
|
||||
|
||||
class YuthonOfficeSuppliesLine(models.Model):
|
||||
_name = 'yuthon.office.supplies.line'
|
||||
class YuthonOfficeSuppliesEmployeeLine(models.Model):
|
||||
_name = 'yuthon.office.supplies.employee.line'
|
||||
_description = "办公用品明细行"
|
||||
|
||||
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||
|
||||
@@ -14,11 +14,11 @@ class YuthonOfficeSupplies(models.Model):
|
||||
reason_application = fields.Html(string="事由")
|
||||
date = fields.Date(string="申请日期", default=fields.Date.today())
|
||||
related_issues_id = fields.Many2one('issue.library', string="关联议题")
|
||||
yuthon_company = fields.Selection(related='users_id.employee_id.yuthon_company', string="公司")
|
||||
yuthon_office_supplies_line_ids = fields.One2many('yuthon.office.supplies.line', 'supplies_id', string="关联办公用品申请明细")
|
||||
yuthon_office_borrow_line_ids = fields.One2many('yuthon.office.borrow.line', 'supplies_id', string="关联办公用品借用明细")
|
||||
yuthon_company = fields.Selection(related='users_id.employee_id.yuthon_company', string="所属公司")
|
||||
office_supplies_ids = fields.One2many('yuthon.office.supplies.line', 'supplies_id', string="关联办公用品申请明细")
|
||||
office_borrow_ids = fields.One2many('yuthon.office.borrow.line', 'supplies_id', string="关联办公用品借用明细")
|
||||
active = fields.Boolean('Active', default=True, tracking=True)
|
||||
office_supplies_id = fields.Many2one('yuthon.office.supplies.setting',string="办公用品")
|
||||
office_supplies_id = fields.Many2one('yuthon.office.supplies.setting', string="套餐")
|
||||
|
||||
@api.onchange('office_supplies_id')
|
||||
def _onchange_office_supplies(self):
|
||||
@@ -47,6 +47,7 @@ class YuthonOfficeSupplies(models.Model):
|
||||
}))
|
||||
self.yuthon_office_borrow_line_ids = new_borrow_lines
|
||||
|
||||
|
||||
class YuthonOfficeSuppliesLine(models.Model):
|
||||
_name = 'yuthon.office.supplies.line'
|
||||
_description = '办公用品申请明细'
|
||||
@@ -57,6 +58,7 @@ class YuthonOfficeSuppliesLine(models.Model):
|
||||
price = fields.Float(string="单价")
|
||||
amount = fields.Float(string="金额")
|
||||
|
||||
|
||||
class YuthonOfficeBorrowLine(models.Model):
|
||||
_name = 'yuthon.office.borrow.line'
|
||||
_description = '办公用品借用明细'
|
||||
|
||||
@@ -9,6 +9,7 @@ class YuthonOfficeSuppliesSetting(models.Model):
|
||||
supplies_setting_line_ids = fields.One2many('office.supplies.setting.line', 'supplies_setting_id', string='关联明细')
|
||||
supplies_borrow_line_ids = fields.One2many('office.supplies.borrow.line', 'supplies_setting_id', string='关联明细')
|
||||
|
||||
|
||||
class OfficeSuppliesSettingLine(models.Model):
|
||||
_name = 'office.supplies.setting.line'
|
||||
_description = '办公用品申用套餐配置明细'
|
||||
@@ -19,6 +20,7 @@ class OfficeSuppliesSettingLine(models.Model):
|
||||
price = fields.Float(string="单价")
|
||||
amount = fields.Float(string="金额")
|
||||
|
||||
|
||||
class OfficeSuppliesBorrowLine(models.Model):
|
||||
_name = 'office.supplies.borrow.line'
|
||||
_description = '办公用品申用明细'
|
||||
|
||||
@@ -5,3 +5,4 @@ access_yuthon_office_borrow_line,yuthon_office_borrow_line,model_yuthon_office_b
|
||||
access_yuthon_office_supplies_setting,yuthon_office_supplies_setting,model_yuthon_office_supplies_setting,base.group_user,1,1,1,1
|
||||
access_office_supplies_setting_line,office_supplies_setting_line,model_office_supplies_setting_line,base.group_user,1,1,1,1
|
||||
access_office_supplies_borrow_line,office_supplies_borrow_line,model_office_supplies_borrow_line,base.group_user,1,1,1,1
|
||||
access_yuthon_office_supplies_employee_line,yuthon_office_supplies_employee_line,model_yuthon_office_supplies_employee_line,base.group_user,1,1,1,1
|
||||
|
@@ -4,8 +4,8 @@
|
||||
<field name="name">hr.employee.inherit.sps.form</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||
<xpath expr="//page[@name='education']" position="after">
|
||||
<page string="办公用品">
|
||||
<xpath expr="//page[@name='hr_settings']" position="after">
|
||||
<page string="固定资产">
|
||||
<field name="office_supplies_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="employee_id" column_invisible="1"/>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_yuthon_office_supplies_form" model="ir.ui.view">
|
||||
<field name="name">yuthon.office.supplies.form</field>
|
||||
<field name="model">yuthon.office.supplies</field>
|
||||
@@ -23,7 +24,8 @@
|
||||
<group>
|
||||
<field name="users_id"/>
|
||||
<field name="code"/>
|
||||
<field name="is_issues" invisible="1"/>
|
||||
<field name="is_issues"/>
|
||||
<field name="related_issues_id" invisible="not is_issues"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="department_id"/>
|
||||
@@ -40,7 +42,7 @@
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="办公用品申请明细">
|
||||
<field name="yuthon_office_supplies_line_ids">
|
||||
<field name="office_supplies_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="supplies_id" column_invisible="1"/>
|
||||
<field name="product_id"/>
|
||||
@@ -51,7 +53,7 @@
|
||||
</field>
|
||||
</page>
|
||||
<page string="办公用品借用明细">
|
||||
<field name="yuthon_office_borrow_line_ids">
|
||||
<field name="office_borrow_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="supplies_id" column_invisible="1"/>
|
||||
<field name="product_id"/>
|
||||
@@ -63,9 +65,6 @@
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="关联议题" invisible="not is_issues">
|
||||
<field name="related_issues_id"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user