Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
pengyuthon
2026-01-18 21:27:07 +08:00
35 changed files with 463 additions and 343 deletions
@@ -9,6 +9,7 @@ import { localization } from "@web/core/l10n/localization";
import {formatDate,formatDateTime} from "@web/core/l10n/dates";
import {parseDateTime,parseDate,} from "@web/core/l10n/dates";
import { renderToElement } from "@web/core/utils/render";
import { session } from "@web/session";
export class Ksdashboardgraph extends Component{
setup(){
@@ -16,6 +17,7 @@ export class Ksdashboardgraph extends Component{
this.actionService = useService("action");
this._rpc = useService("rpc");
this.state = useState({item_data:"",list_view_data:""})
this.isAdmin = session.is_system;
onMounted(() => this._update_view());
onPatched(() => this.update_list_view());
this.item = this.props.item
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="Ks_chart_list_container">
<t t-if="item.ks_dashboard_item_type == 'ks_list_view'">
<t t-call="ks_dashboard_ninja.Ksdashboardlistview"/>
@@ -9,50 +8,46 @@
<t t-call="Ks_gridstack_container"/>
</t>
</t>
<t t-name="Ks_gridstack_container">
<div class="grid-stack-item ks_chart_container ks_dashboarditem_id" t-att-id="chart_id" t-ref="ks_gridstack_container">
<div class="grid-stack-item-content ks_dashboarditem_chart_container ks_dashboard_item_hover card shadow"
t-att-title="ks_info">
<div class="p-3 py-3 d-flex flex-row align-items-center justify-content-between ">
<div class="d-flex align-items-center">
<h6 class="m-0 font-weight-bold h3 ks_chart_heading mr-3" t-att-title="ks_chart_title">
<t t-esc="ks_chart_title"/>
</h6>
<!-- <div >-->
<t t-if="ks_breadcrumb.length >= 1">
<nav class="ks_breadcrumb">
<t t-if="ks_breadcrumb.length >= 1">
<nav class="ks_breadcrumb">
<ul>
<li class="d-none" t-att-id="'item'+'_'+'-1'">
<span t-att-data-sequence = '-1' t-att-data-item-id="chart_id" t-on-click="ksOnDrillUp">
<t t-esc="ks_chart_title"/>
</span>
<span t-att-data-sequence = '-1' t-att-data-item-id="chart_id" t-on-click="ksOnDrillUp">
<t t-esc="ks_chart_title"/>
</span>
</li>
<t t-foreach="ks_breadcrumb" t-as="chart_breadcrumb" t-key="chart_breadcrumb_index">
<li class="d-none" t-att-id="chart_breadcrumb['name'] + '_' + chart_breadcrumb_index">
<li class="d-none" t-att-id="chart_breadcrumb['name'] + '_' + chart_breadcrumb_index">
<span t-att-data-sequence="chart_breadcrumb_index" t-att-data-item-id="chart_id" t-on-click="ksOnDrillUp">
<t t-esc="chart_breadcrumb['name']"/>
<t t-esc="chart_breadcrumb['name']"/>
</span>
</li>
</t>
</ul>
</nav>
</t>
</nav>
</t>
</div>
<!-- </div>-->
<img src="ks_dashboard_ninja/static/images/selected.svg" class="ks_img_display d-none"
width="30"/>
<!-- Dashboard Item Move/Copy Feature -->
<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"
t-att-data-item_id="chart_id">
<t t-if="ksIsDashboardManager">
<button class="ks_dashboard_item_chart_info" title="More Info" type="button"
t-att-data-item_id="chart_id" t-if="isAdmin">
<t t-if="ksIsDashboardManager and isAdmin">
<button class="ks_dashboard_item_chart_info" title="More Info" type="button"
t-att-data-item-id="chart_id">
<i class="fa fa-info-circle"/>
</button>
<i class="fa fa-info-circle"/>
</button>
<t t-if="ks_dashboard_item_type != 'ks_map_view'">
<div class="ks_chart_inner_buttons">
<button title="Color Palette" data-bs-toggle="dropdown"
@@ -76,8 +71,6 @@
</ul>
</div>
</t>
<div class="ks_chart_inner_buttons">
<button title="Move/Duplicate" data-bs-toggle="dropdown"
class="ks_dashboard_item_action btn dropdown-toggle btn-xs" type="button"
@@ -109,101 +102,103 @@
</li>
</ul>
</div>
<button title="Quick Customize"
class="ks_dashboard_quick_edit_action_popup btn d-sm-block d-none"
type="button" t-att-data-item-id="chart_id">
<i class="fa fa-pencil"/>
</button>
<button class="ks_dashboard_item_customize d-block d-sm-none" title="Customize Item"
type="button" t-att-data-item-id="chart_id">
<i class="fa fa-pencil"/>
</button>
<button class="ks_dashboard_item_delete" title="Remove Item" type="button">
<i class="fa fa-times"/>
</button>
</t>
<t t-if="ksIsUser">
<div class="ks_chart_inner_buttons ks_dashboard_more_action"
<div class="ks_chart_inner_buttons ks_dashboard_more_action"
t-att-data-item-id="chart_id">
<button title="Export Chart" data-bs-toggle="dropdown"
<button title="Export Chart" data-bs-toggle="dropdown"
class="ks_dashboard_item_action btn dropdown-toggle btn-xs o-no-caret btn"
type="button"
aria-expanded="true">
<i class="fa fa-ellipsis-v"/>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right">
<!--Dynamic Rendering-->
<t t-call="ksMoreChartOptions"/>
</div>
<i class="fa fa-ellipsis-v"/>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right">
<!--Dynamic Rendering-->
<t t-call="ksMoreChartOptions"/>
</div>
</div>
</t>
<div class="ks_chart_inner_buttons dropdown">
<button title="Info" data-bs-toggle="dropdown"
<button title="Info" data-bs-toggle="dropdown"
class="ks_item_description btn dropdown-toggle btn-xs o-no-caret btn"
type="button"
t-att-style="'color:'+ ks_rgba_button_color + ';'"
aria-expanded="true">
<i class="fa fa-book"/>
</button>
<i class="fa fa-book"/>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right" style="width:20rem">
<!--Dynamic Rendering-->
<!--Dynamic Rendering-->
<div class="ks_chart_export_menu">
<div class="ks_chart_export_menu_header" style="margin-left:-10px">
<span>Info</span>
</div>
<div class="ks_chart_export_menu_header" style="margin-left:-10px">
<span>Info</span>
</div>
<div class="ks_info" style="margin-left:10px">
<span>Company: <t t-esc="ks_company"/></span>
</div>
<span>Company:
<t t-esc="ks_company"/>
</span>
</div>
<div class="ks_info" style="margin-left:10px">
<t t-if="ks_info">
<t t-foreach="ks_info" t-as="ks_description" t-key="ks_description_index">
<span><t t-esc="ks_description"/></span>
<br></br>
</t>
</t>
<t t-if="ks_info">
<t t-foreach="ks_info" t-as="ks_description" t-key="ks_description_index">
<span>
<t t-esc="ks_description"/>
</span>
<br></br>
</t>
</t>
</div>
</div>
</div>
</div>
</div>
</div>
<t t-if="ksIsDashboardManager">
<div class="dropdown d-md-none dn-setting-panel">
<button class="btn btn-secondary border-0" type="button" id="dropdownSettingButton"
<button class="btn btn-secondary border-0" type="button" id="dropdownSettingButton"
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-cog"></i>
</button>
<i class="fa fa-cog"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownSettingButton">
<t t-if="ks_show_records &amp; ks_query_type=='custom'">
<a class="dropdown-item ks_dashboard_item_chart_info ks_info_display"
<t t-if="ks_show_records &amp; ks_query_type=='custom'">
<a class="dropdown-item ks_dashboard_item_chart_info ks_info_display"
t-att-data-item-id="chart_id">
<i class="fa fa-info-circle"/>
<i class="fa fa-info-circle"/>
More Info
</a>
</t>
</a>
</t>
<a class="dropdown-item ks_dashboard_item_customize" t-att-data-item-id="chart_id">
<i class="fa fa-pencil"></i>
<i class="fa fa-pencil"></i>
Customize Item
</a>
</a>
<a class="dropdown-item ks_dashboard_item_delete">
<i class="fa fa-times"></i>
<i class="fa fa-times"></i>
Remove Item
</a>
</div>
</div>
</a>
</div>
</div>
</t>
</div>
<t t-if="ks_ai_analysis">
<div class="ks_ai_explain_body">
<div class="card-body ks_chart_card_body"></div>
<div class="ks_ai_explanation">
<p><t t-esc="ks_ai_analysis_1"/></p>
<p><t t-esc="ks_ai_analysis_2"/></p>
<p>
<t t-esc="ks_ai_analysis_1"/>
</p>
<p>
<t t-esc="ks_ai_analysis_2"/>
</p>
</div>
<div class="ks_speaker" t-on-click="props.ks_speak">
<span class="fa fa-volume-up"/>
@@ -217,7 +212,6 @@
</div>
</div>
</t>
<t t-name="ksMoreChartOptions">
<div class="ks_chart_export_menu">
<div class="ks_chart_export_menu_header">
@@ -233,7 +227,6 @@
<i class="fa fa-file-code-o"/>
<span>Export to CSV</span>
</div>
<div t-att-data-chart-id="chart_id" class="ks_chart_pdf_export ks_chart_export_menu_item">
<i class="fa fa-file-pdf-o"/>
<span>Save as PDF</span>
@@ -6,7 +6,7 @@ import { formatInteger } from "@web/views/fields/formatters";
import { useService } from "@web/core/utils/hooks";
import { jsonrpc } from "@web/core/network/rpc_service";
import { _t } from "@web/core/l10n/translation";
import { session } from "@web/session";
export class Ksdashboardkpiview extends Component{
file_type_magic_word= {'/': 'jpg','R': 'gif','i': 'png','P': 'svg+xml'}
setup(){
@@ -15,6 +15,7 @@ export class Ksdashboardkpiview extends Component{
this.actionService = useService("action");
this.ks_kpi = useRef('ks_kpi')
this.state = useState({item_info_kpi1:{},item_info_kpi2:{},item_info_kpi3:{}})
this.isAdmin = session.is_system;
onMounted(() => this._update_view());
this.item = this.props.item
this.ks_dashboard_data = this.props.dashboard_data
@@ -113,7 +113,7 @@
<img src="ks_dashboard_ninja/static/images/selected.svg" class="ks_img_display d-none" width="30"/>
<!---->
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header_l6 ks_dashboard_item_header_hover"
t-att-data-item_id="state.item_info_kpi1.item.id">
t-att-data-item_id="state.item_info_kpi1.item.id" t-if="isAdmin">
<t t-if="state.item_info_kpi1.item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
<button title="Move/Duplicate" data-bs-toggle="dropdown"
@@ -305,7 +305,7 @@
</t>
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header_l6 ks_dashboard_item_header_hover"
t-att-data-item_id="state.item_info_kpi2.item.id">
t-att-data-item_id="state.item_info_kpi2.item.id" t-if="isAdmin">
<t t-if="state.item_info_kpi2.item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
<button title="Move/Duplicate" data-bs-toggle="dropdown"
@@ -489,7 +489,7 @@
</t>
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header_l6 ks_dashboard_item_header_hover"
t-att-data-item_id="state.item_info_kpi3.item.id">
t-att-data-item_id="state.item_info_kpi3.item.id" t-if="isAdmin">
<t t-if="state.item_info_kpi3.item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
<button title="Move/Duplicate" data-bs-toggle="dropdown"
@@ -37,7 +37,7 @@
<!-- -->
<!-- Dashboard Item Move/Copy Feature -->
<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"
t-att-data-item_id="item_id">
t-att-data-item_id="item_id" >
<t t-if="ksIsDashboardManager">
<button title="Move/Duplicate" data-bs-toggle="dropdown" t-att-data-item-id="item_id"
class="ks_dashboard_item_action btn dropdown-toggle btn-xs" type="button"
@@ -5,7 +5,7 @@ import { loadBundle } from "@web/core/assets";
import { useService } from "@web/core/utils/hooks";
import { jsonrpc } from "@web/core/network/rpc_service";
import { _t } from "@web/core/l10n/translation";
import { session } from "@web/session";
export class Ksdashboardtile extends Component{
file_type_magic_word= {'/': 'jpg','R': 'gif','i': 'png','P': 'svg+xml'}
@@ -17,6 +17,7 @@ export class Ksdashboardtile extends Component{
this.ks_container_class = 'grid-stack-item';
this.ks_inner_container_class = 'grid-stack-item-content';
this.state = useState({data_count:""})
this.isAdmin = session.is_system;
this.item = this.props.item
this.file_type_magic_word= {'/': 'jpg','R': 'gif','i': 'png','P': 'svg+xml'}
this.ks_dashboard_data = this.props.dashboard_data
@@ -33,7 +33,7 @@
t-att-title="item.ks_info"
t-att-id="item.id">
<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">
t-att-data-item_id="item.id" t-if="isAdmin">
<t t-if="item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
@@ -230,7 +230,7 @@
</div>
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header_l6 ks_dashboard_item_header_hover"
t-att-data-item_id="item.id">
t-att-data-item_id="item.id" t-if="isAdmin">
<t t-if="item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
<button title="Move/Duplicate" data-bs-toggle="dropdown"
@@ -383,7 +383,7 @@
t-att-title="item.ks_info" t-att-id="item.id">
<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">
t-att-data-item_id="item.id" t-if="isAdmin">
<t t-if="item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
@@ -581,7 +581,7 @@
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header_l6 ks_dashboard_item_header_hover"
t-att-data-item_id="item.id">
t-att-data-item_id="item.id" t-if="isAdmin">
<t t-if="item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
<button title="Move/Duplicate" data-bs-toggle="dropdown"
@@ -756,7 +756,7 @@
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header_l2 ks_dashboard_item_header_hover"
t-att-data-item_id="item.id">
t-att-data-item_id="item.id" t-if="isAdmin">
<t t-if="item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
@@ -933,7 +933,7 @@
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header_l6 ks_dashboard_item_header_hover"
t-att-data-item_id="item.id">
t-att-data-item_id="item.id" t-if="isAdmin">
<t t-if="item.ksIsDashboardManager">
<!-- Dashboard Item Move/Copy Feature -->
<button title="Move/Duplicate" data-bs-toggle="dropdown"
@@ -1074,7 +1074,7 @@
<t t-name="ks_dashboard_item_layout_default">
<div t-att-id="item.id" t-att-class="'ks_item_click ' + ks_container_class">
<div class="ks_dashboard_item ks_dashboard_item_hover"
t-att-style="style_main_body">
t-att-style="style_main_body" t-if="isAdmin">
<t t-if="item.ksIsDashboardManager">
<div class="ks_dashboard_item_header ks_dashboard_item_header_hover">
<button type="button" title="Customize Item" class="ks_dashboard_item_customize">
@@ -7,7 +7,7 @@ import { useService } from "@web/core/utils/hooks";
import {Todoeditdialog,addtododialog} from "@ks_dashboard_ninja/components/ks_dashboard_to_do_item/editdialog";
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { _t } from "@web/core/l10n/translation";
import { session } from "@web/session";
export class Ksdashboardtodo extends Component{
setup(){
super.setup();
@@ -17,6 +17,7 @@ export class Ksdashboardtodo extends Component{
this.item = this.props.item
this.ks_dashboard_data = this.props.dashboard_data
this.prepare_item();
this.isAdmin = session.is_system;
}
@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="Ksdashboardtodo">
<t t-name="Ksdashboardtodo">
<div class="grid-stack-item ks_dashboarditem_id" t-att-id="item_id">
<div class="grid-stack-item-content ks_list_view_container ks_dashboard_item_hover card shadow"
t-att-title="ks_info">
@@ -16,10 +13,9 @@
<t t-esc="ks_chart_title"/>
</h6>
</div>
<!-- Dashboard Item Move/Copy Feature -->
<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"
t-att-data-item_id="item_id">
t-att-data-item_id="item_id" t-if="isAdmin">
<t t-if="ksIsDashboardManager">
<button title="Move/Duplicate" data-bs-toggle="dropdown"
t-att-data-item-id="item_id"
@@ -39,7 +35,7 @@
<option t-att-value="ks_dashboard['id']">
<t t-esc="ks_dashboard['name']"/>
</option>
</t>
</t>
</select>
</li>
<li class="mt-3">
@@ -58,7 +54,6 @@
type="button" t-att-data-item-id="item_id">
<i class="fa fa-pencil"/>
</button>
<button class="ks_dashboard_item_customize d-block text-white d-sm-none"
title="Customize Item"
t-att-style="'color:'+ ks_rgba_button_color + ' !important' + ';'"
@@ -73,77 +68,78 @@
</t>
<t t-if="ksIsUser">
<div class="ks_chart_inner_buttons">
<button title="Export List" data-bs-toggle="dropdown"
<button title="Export List" data-bs-toggle="dropdown"
class="ks_dashboard_item_action_export text-white btn dropdown-toggle btn-xs o-no-caret btn"
t-att-style="'color:'+ ks_rgba_button_color + ' !important' + ';'"
type="button"
aria-expanded="true">
<i class="fa fa-ellipsis-v"/>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right">
<!--Dynamic Rendering-->
<div class="ks_chart_export_menu">
<div class="ks_chart_export_menu_header">
<span>Export</span>
</div>
<div class="ks_chart_json_export ks_chart_export_menu_item"
<i class="fa fa-ellipsis-v"/>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right">
<!--Dynamic Rendering-->
<div class="ks_chart_export_menu">
<div class="ks_chart_export_menu_header">
<span>Export</span>
</div>
<div class="ks_chart_json_export ks_chart_export_menu_item"
t-att-data-item-id="item_id"
data-format="chart_xls">
<i class="fa fa-file-code-o"/>
<span>Export Item</span>
<i class="fa fa-file-code-o"/>
<span>Export Item</span>
</div>
</div>
</div>
</div>
</div>
</t>
<div class="ks_chart_inner_buttons dropdown">
<button title="Item description" data-bs-toggle="dropdown"
<button title="Item description" data-bs-toggle="dropdown"
class="ks_item_description btn dropdown-toggle btn-xs o-no-caret btn"
type="button"
t-att-style="'color:'+ ks_rgba_button_color + ';'"
aria-expanded="true">
<i class="fa fa-book"/>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right" style="width:20rem">
<!--Dynamic Rendering-->
<div class="ks_chart_export_menu">
<div class="ks_chart_export_menu_header" style="margin-left:-10px">
<span>Info</span>
</div>
<div class="ks_info" style="margin-left:10px">
<span>Company: <t t-esc="ks_company"/></span>
</div>
<div class="ks_info" style="margin-left:10px">
<t t-if="ks_info">
<t t-foreach="ks_info" t-as="ks_description" t-key="ks_description_index">
<span><t t-esc="ks_description"/></span>
<br></br>
</t>
</t>
<i class="fa fa-book"/>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right" style="width:20rem">
<!--Dynamic Rendering-->
<div class="ks_chart_export_menu">
<div class="ks_chart_export_menu_header" style="margin-left:-10px">
<span>Info</span>
</div>
<div class="ks_info" style="margin-left:10px">
<span>Company:
<t t-esc="ks_company"/>
</span>
</div>
<div class="ks_info" style="margin-left:10px">
<t t-if="ks_info">
<t t-foreach="ks_info" t-as="ks_description" t-key="ks_description_index">
<span>
<t t-esc="ks_description"/>
</span>
<br></br>
</t>
</t>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<t t-if="ksIsDashboardManager">
<div class="dropdown d-md-none dn-setting-panel">
<button class="btn btn-secondary border-0" type="button" id="dropdownSettingButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" >
<i class="fa fa-cog"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownSettingButton">
<a class="dropdown-item ks_dashboard_item_customize" >
<i class="fa fa-pencil"></i> Customize Item
</a>
<a class="dropdown-item ks_dashboard_item_delete" >
<i class="fa fa-times"></i> Remove Item
</a>
</div>
</div>
<button class="btn btn-secondary border-0" type="button" id="dropdownSettingButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" >
<i class="fa fa-cog"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownSettingButton">
<a class="dropdown-item ks_dashboard_item_customize" >
<i class="fa fa-pencil"></i> Customize Item
</a>
<a class="dropdown-item ks_dashboard_item_delete" >
<i class="fa fa-times"></i> Remove Item
</a>
</div>
</div>
</t>
</div>
<div class="card-header">
<div class="nav-tabs-navigation">
<div class="nav-tabs-wrapper">
@@ -172,7 +168,6 @@
<t t-esc="table_header"/>
</a>
</t>
</li>
<t t-set="ks_rec_count" t-value="ks_rec_count+1"/>
</t>
@@ -191,7 +186,6 @@
</li>
</ul>
</t>
</div>
</div>
</div>
@@ -202,16 +196,12 @@
</div>
</div>
</t>
<t t-name="ks_to_do_dashboard_inner_body">
<div class="container-fluid p-0">
<div>
<div>
<div class="card">
<t t-if="state.to_do_view_data['label']">
<div class="card-body">
<div class="tab-content">
<t t-set="ks_section_count" t-value="0"/>
@@ -232,7 +222,6 @@
<td class="ks_custom_check">
<div class="form-check">
<label class="form-check-label">
<t t-if="state.to_do_view_data['ks_content_active'][ks_href_id][ks_temp_count]=='True'">
<input class="form-check-input"
type="checkbox"
@@ -243,7 +232,6 @@
type="checkbox" value=""
checked=""/>
</t>
<span class="form-check-sign ks_do_item_active_handler" t-on-click="_onKsActiveHandler"
t-att-data-item-id="item_id"
t-att-data-content-id="state.to_do_view_data['ks_content_record_id'][ks_href_id][ks_temp_count]"
@@ -262,7 +250,6 @@
<t t-esc="table_row"/>
</td>
</t>
<t t-else="">
<td class="ks_description ks_do_item_line_through"
t-att-value="table_row"
@@ -294,14 +281,12 @@
<t t-set="ks_temp_count" t-value="ks_temp_count+1"/>
</tr>
</t>
</t>
</table>
</t>
<t t-else="">
<span class="nav-tabs-title">No Tasks Available</span>
</t>
<t t-set="ks_section_count" t-value="ks_section_count+1"/>
</div>
</t>
@@ -350,7 +335,6 @@
<t t-esc="table_row"/>
</td>
</t>
<t t-else="">
<td class="ks_description ks_do_item_line_through"
t-att-value="table_row"
@@ -382,7 +366,6 @@
<t t-set="ks_temp_count" t-value="ks_temp_count+1"/>
</tr>
</t>
</t>
</table>
</t>
@@ -404,10 +387,8 @@
</div>
</t>
</div>
</div>
</div>
</div>
</t>
</templates>
@@ -59,7 +59,7 @@
<xpath expr="////div[@class='o_control_panel d-flex flex-column gap-3 gap-lg-1 px-3 pt-2 pb-3']" position="after">
<div class="o_control_panel th_custom_control_panel d-flex gap-1" t-if="env.isSmall">
<div class="th_custom_control_panel_buttons">
<!-- 用于放置按钮-->
<!-- 用于放置按钮-->
</div>
<!-- 返回箭头-->
@@ -72,8 +72,9 @@
<div t-if="collapsedBreadcrumbs.length || visiblePathBreadcrumbs.length" class="o_breadcrumb d-flex flex-row flex-md-column align-self-stretch justify-content-between min-w-0">
<t t-if="env.isSmall">
<t t-set="previousBreadcrumb" t-value="visiblePathBreadcrumbs.slice(-1)"/>
<button class="o_back_button btn btn-link px-1" t-on-click.prevent="() => this.onBreadcrumbClicked(previousBreadcrumb.jsId)">
<i class="oi oi-fw oi-arrow-left"/>
<button class="o_back_button btn btn-primary px-1" t-on-click.prevent="() => this.onBreadcrumbClicked(previousBreadcrumb.jsId)">
<!-- <i class="oi oi-fw oi-arrow-left"/>-->
返回
</button>
</t>
</div>
@@ -209,6 +209,31 @@ class SoongWorkflowMixin(models.AbstractModel):
soong_record_ids = fields.One2many(comodel_name="soong.workflow.line", inverse_name="res_id", string=" ",)
project_ids = fields.One2many(comodel_name="project.project.line", inverse_name="res_id", string=" ",)
def get_logs_for_print(self, applicant_first=False):
"""
返回用于打印的审批日志列表:
- 按与界面相同的顺序(sequence 升序)
- 同名节点只保留最新一条
- 可选:申请人置顶
"""
self.ensure_one()
logs = self.log_ids.sorted('sequence')
seen = set()
ordered = []
# 逆序去重,保留最新
for log in reversed(logs):
key = log.name or ''
if key in seen:
continue
seen.add(key)
ordered.append(log)
ordered.reverse() # 恢复原有顺序(去重后)
if applicant_first:
applicants = [l for l in ordered if (l.name or '') == '申请人']
others = [l for l in ordered if (l.name or '') != '申请人']
ordered = applicants + others
return ordered
# @api.model_create_multi
# def create(self, vals_list):
# res = super().create(vals_list)
+36 -4
View File
@@ -16,10 +16,7 @@ class SoongWorkflowRecord(models.Model):
def get_workflow_count(self):
domain = [
('soong_state', 'in', ['to_do', 'active']),
('employee_ids.user_id', '=', self.env.user.id),
('users_ids', 'in', self.env.user.id),
('reclaim_state', '=', False)
('is_to_do', '=', True),
]
return self.search_count(domain)
@@ -74,6 +71,41 @@ class SoongWorkflowRecord(models.Model):
# 人力资源流程的发生日期
workflow_happen_date = fields.Date(string="发生日期")
is_unlink = fields.Boolean(string="是否可删", compute='_compute_is_unlink')
is_to_do = fields.Boolean(
string="是否是待办流程",
compute='_compute_is_to_do',
search='_search_is_to_do',
)
@api.depends('employee_ids', 'soong_state', 'reclaim_state')
def _compute_is_to_do(self):
current_uid = self.env.user.id
for record in self:
record.is_to_do = False
condition1 = record.soong_state in ['to_do', 'to_active']
condition2 = not record.reclaim_state
condition3 = False
if record.employee_ids:
condition3 = current_uid in record.employee_ids.mapped('user_id.id')
if condition1 and condition2 and condition3:
record.is_to_do = True
def _search_is_to_do(self, operator, value):
current_uid = self.env.user.id
current_employee_ids = self.env['hr.employee'].search([
('user_id', '=', current_uid)
]).ids
base_domain = [
('soong_state', 'in', ['to_do', 'to_active']),
('reclaim_state', '=', False),
('employee_ids', 'in', current_employee_ids)
]
if (operator == '=' and value) or (operator == '!=' and not value):
domain = base_domain
elif (operator == '=' and not value) or (operator == '!=' and value):
domain = ['!'] + base_domain
else:
domain = []
return domain
@api.depends_context('uid', 'company')
@api.depends('department_id', 'employee_ids')
@@ -4,7 +4,7 @@
<notebook>
<page string="审批详情">
<field name="log_ids" style="width:100%; margin-top: 10px;" mode="kanban,tree" context="{'kanban_view_ref':'soong_workflow.soong_workflow_log_kanban'}">
<tree editable="bottom">
<tree editable="bottom" create="1" edit="1" delete="1">
<field name="sequence" string="" widget="handle" groups="soong_workflow.group_soong_workflow_manager"/>
<field name="name" string="" readonly="0"/>
<field name="comment" string=""/>
@@ -20,7 +20,7 @@
</page>
<page string="抄送/传阅记录" invisible="not log_ids2" mode="kanban,tree" context="{'kanban_view_ref':'soong_workflow.soong_workflow_log2_kanban'}">
<field name="log_ids2" style="width:100%; margin-top: 10px;">
<tree editable="bottom" >
<tree editable="bottom" create="1" edit="1" delete="1">
<field name="done_by" string=""/>
<field name="commit" string=""/>
<field name="lase_click_date" string=""/>
@@ -32,7 +32,7 @@
</page>
<page string="关联流程">
<field name="soong_record_ids" style="width:100%; margin-top: 10px;">
<tree edit="true" delete="False" editable="bottom" type="object">
<tree create="1" edit="1" delete="1" editable="bottom" type="object">
<field name="workflow_record_id" domain="[('users_ids', 'in', uid)]"
options="{'no_open': True}"/>
<field name="code" column_invisible="1"/>
@@ -76,7 +76,7 @@
<field name="arch" type="xml">
<tree string="流程查询" action="open_record" type="object"
decoration-danger="approve_state == 'end'" delete="false"
create="false" default_order="write_date2 desc, id desc" expand="1" limit="15">
create="false" default_order="write_date2 desc, id desc" expand="1" limit="10">
<header>
<button name="admin_end" type="object" string="管理员强制结束" class="oe_highlight" groups="soong_workflow.group_soong_workflow_manager"/>
<button name="delete_record" type="object" string="删除" class="oe_highlight" groups="soong_workflow.group_soong_workflow_manager"/>
@@ -162,7 +162,7 @@
<field name="arch" type="xml">
<tree string="流程查询" action="open_record" type="object"
decoration-danger="approve_state == 'end'" delete="false"
create="false" default_order="write_date2 desc, id desc" expand="1" limit="15">
create="false" default_order="write_date2 desc, id desc" expand="1" limit="10">
<header>
<button name="admin_end" type="object" string="管理员强制结束" class="oe_highlight" groups="soong_workflow.group_soong_workflow_manager"/>
<button name="delete_record" type="object" string="删除" class="oe_highlight" groups="soong_workflow.group_soong_workflow_manager"/>
@@ -191,7 +191,7 @@
<field name="arch" type="xml">
<tree string="流程查询" action="open_record" type="object"
decoration-danger="approve_state == 'end'" delete="false"
create="false" default_order="write_date2 desc, id desc" expand="1" limit="15">
create="false" default_order="write_date2 desc, id desc" expand="1" limit="10">
<field name="code"/>
<field name="workflow_id" string="审批类型"/>
<field name="name"/>
@@ -211,7 +211,7 @@
<field name="arch" type="xml">
<tree string="流程查询" action="open_record" type="object"
decoration-danger="approve_state == 'end'" delete="false"
create="false" default_order="write_date2 desc, id desc" expand="1" limit="15">
create="false" default_order="write_date2 desc, id desc" expand="1" limit="10">
<header>
<button name="admin_end" type="object" string="管理员强制结束" class="oe_highlight" groups="soong_workflow.group_soong_workflow_manager"/>
<button name="delete_record" type="object" string="删除" class="oe_highlight" groups="soong_workflow.group_soong_workflow_manager"/>
@@ -273,7 +273,7 @@
<field name="arch" type="xml">
<tree string="工作流回收站" action="open_record" type="object"
decoration-muted="approve_state == 'end'" decoration-warning="approve_state != 'end'"
create="false" default_order="write_date2 desc" limit="15">
create="false" default_order="write_date2 desc" limit="10">
<field name="name"/>
<field name="workflow_id" string="审批类型"/>
<field name="node_id" string="当前节点"/>
@@ -317,7 +317,7 @@
<field name="res_model">soong.workflow.record</field>
<field name="view_mode">tree,kanban,search</field>
<field name="context">{'header_search_default_approve_state': 1, 'header_search_default_to_do':1, 'check_employee': 1}</field>
<field name="domain">[('users_ids', 'in', uid), ('reclaim_state', '=', False), ('soong_state', 'in', ['to_do', 'to_active'])]</field>
<field name="domain">[('is_to_do', '=', True)]</field>
<field name="search_view_id" ref="soong_workflow_record_search1"/>
<field name="view_id" ref="soong_workflow_record_my_tree"/>
</record>
@@ -450,7 +450,7 @@ class SoongWorkflowCommentWizard(models.TransientModel):
for employee in self.employee_ids:
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
target_url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&active_id={action_id.id}&model={record_id._name}&view_type=form'
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, category='oa')
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, employee.user_id.id)
_logger.info(f"===================当前的url为{url}=============================")
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
@@ -695,7 +695,7 @@ class SoongWorkflowCommentWizard(models.TransientModel):
recipients = [emp for emp in employees]
for employee in recipients:
target_url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&active_id={action_id.id}&model={record_id._name}&view_type=form'
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, category='oa')
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, employee.user_id.id)
if self.res_model == 'purchase.order':
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
@@ -716,7 +716,6 @@ class SoongWorkflowCommentWizard(models.TransientModel):
workflow_record_id = self.env['soong.workflow.record'].search([('code', '=', record_id.code)])
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
target_url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={menu_id.id}&active_id={action_id.id}&model={record_id._name}&view_type=form'
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, category='oa')
description = "标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str(fields.Datetime.now() + timedelta(hours=8))
if self.employee_ids:
workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.employee_ids if employee.user_id]
@@ -731,6 +730,7 @@ class SoongWorkflowCommentWizard(models.TransientModel):
"done_by": self.employee_ids.user_id.id,
"node_btn_name": "0",
})
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, self.employee_ids.user_id.id)
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=self.employee_ids.wecom_userid, title=record_id._description, description=description, url=url, btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
@@ -747,6 +747,7 @@ class SoongWorkflowCommentWizard(models.TransientModel):
"done_by": emp.user_id.id,
"node_btn_name": "4",
})
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, emp.user_id.id)
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=emp.wecom_userid, title=record_id._description + "承办", description=description, url=url, btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
@@ -781,6 +782,7 @@ class SoongWorkflowCommentWizard(models.TransientModel):
"node_btn_id": self.node_btn_id.id,
"approval_state": "0",
})
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, employee.user_id.id)
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=employee.wecom_userid, title=record_id._description + "传阅/抄送",description=description, url=url, btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
@@ -839,7 +841,6 @@ class SoongWorkflowCommentWizard(models.TransientModel):
menu_id = self.env['ir.ui.menu'].search([('name', '=', '行政收文')])
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
target_url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={menu_id.id}&active_id={action_id.id}&model={record_id._name}&view_type=form'
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, category='oa')
description = "标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str(fields.Datetime.now() + timedelta(hours=8))
record = self.env[self.res_model].search([('id', '=', self.res_id)])
record.log_ids[-1].write({'is_active_operate': True,
@@ -875,6 +876,7 @@ class SoongWorkflowCommentWizard(models.TransientModel):
"node_btn_id": self.node_btn_id.id,
"approval_state": "0",
})
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, employee.user_id.id)
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
title=record_id._description + "传阅/抄送",
+26
View File
@@ -553,6 +553,32 @@ class HolidaysRequestBatch(models.Model):
'text': f"请假审批"
})
overtime_records = self.env['vacation.over.line'].search([
('employee_id', '=', self.users_id.employee_ids.id),
('remaining_quantity', '>', 0),
('is_void', '=', False),
('text', '!=', '上月继承额度'),
], order='start_time ASC')
remaining_deduct = hours
for record in overtime_records:
if remaining_deduct <= 0:
break
current_remaining = record.remaining_quantity
deduct_amount = min(remaining_deduct, current_remaining)
if remaining_deduct > current_remaining:
carry_state = 'partial'
else:
carry_state = 'completed'
record.write({
'carry_forward_date': fields.Date.to_string(period_start),
'carry_forward_quantity': deduct_amount,
'carry_forward_state': carry_state,
})
remaining_deduct -= deduct_amount
def _create_annual_leave_line(self, period_start, leave_data, hours, period):
"""更新年假记录,使用小时作为单位(增加重复校验,避免重复创建)"""
report_month = date(period_start.year, period_start.month, 1)
@@ -131,7 +131,8 @@
<tr>
<td class="section-cell" colspan="4">审批详情</td>
</tr>
<t t-foreach="doc.log_ids.sorted('sequence')" t-as="log">
<t t-set="logs_ordered" t-value="doc.get_logs_for_print()"/>
<t t-foreach="logs_ordered" t-as="log">
<tr>
<!-- 节点名称:保留center -->
<!-- 若节点名称带“(物管)”等后缀,这里只取左侧文字 -->
+1 -1
View File
@@ -65,7 +65,7 @@
'views/hr_attendance_portal_templates.xml',
'views/meal_approval.xml',
'views/applet_address_views.xml',
# 'views/yuthon_holiday_statement.xml',
'views/yuthon_holiday_statement.xml',
'views/all_attendance_menu.xml',
'views/overtime_approval_inherit_header.xml',
@@ -48,18 +48,6 @@ class OvertimeApprovals(models.Model):
work_end = fields.Boolean(string="结束")
is_over = fields.Boolean(string="超过", compute="_compute_duration")
carry_forward_date = fields.Date(string="结转日期")
carry_forward_duration = fields.Float(string="结转时长")
carry_forward_result = fields.Selection([('success', '成功'),('failed', '失败')], string='结转结果',)
validity = fields.Char(string="有效期")
carry_forward_state = fields.Selection([('pending', '待结转'),('completed', '已结转'), ('partial', '部分结转')
],string='结转状态',
default='pending',
)
remaining_quantity = fields.Float(string='剩余数')
def action_print_overtime(self):
self.ensure_one()
return self.env.ref('yuthon_hr_attendance.action_overtime_approval_print').report_action(self)
@@ -2,24 +2,45 @@ from odoo import api, fields, models, tools, _
from datetime import datetime, time, timedelta
from odoo.exceptions import UserError
from datetime import date, timedelta
class HolidayStatement(models.Model):
class YuthonOvertimeReport(models.Model):
_name = 'yuthon.holiday.statement'
class InheritHrLeave(models.Model):
_inherit = 'hr.leave'
_description = '请假情况'
leave_approval_type = fields.Char(string="审批类型")
gender = fields.Selection([('male', '男'),('female', '女'),], string='性别', required=True, default='male')
leave_title = fields.Char(string="标题")
leave_summary = fields.Char(string="摘要")
caption = fields.Char(string="标题", compute="_compute_application_reason")
employee_id = fields.Many2one('hr.employee', string="发起人")
leave_start_date = fields.Char(string="开始时间")
leave_end_date = fields.Char(string="结束时间")
leave_duration = fields.Char(string="请假时长")
leave_stats = fields.Char(string="请假统计")
application_reason = fields.Char(string="申请原因")
leave_start_time = fields.Datetime(string="发起时间")
leave_end_time = fields.Datetime(string="最后操作时间")
approval_state = fields.Selection([
('approved', '通过'),
('rejected', '未通过')
], string='审批状态')
leave_stats = fields.Char(string="请假统计", compute="_compute_leave_stats")
application_reason = fields.Char(string="申请原因", compute="_compute_application_reason")
@api.depends('batch_id', 'hour_batch_id')
def _compute_application_reason(self):
for rec in self:
if rec.batch_id:
rec.application_reason = rec.batch_id.text
rec.caption = rec.batch_id.name
elif rec.hour_batch_id:
rec.application_reason = rec.hour_batch_id.text
rec.caption = rec.hour_batch_id.name
else:
rec.application_reason = ''
rec.caption = ''
@api.depends('holiday_status_id', 'employee_id')
def _compute_leave_stats(self):
for record in self:
record.leave_stats = ""
current_year = datetime.now().year
start_of_year = f"{current_year}-01-01"
end_of_year = f"{current_year}-12-31"
leave_records = self.env['hr.leave'].search([
('employee_id', '=', record.employee_id.id),
('holiday_status_id', '=', record.holiday_status_id.id),
('date_from', '>=', start_of_year),
('date_from', '<=', end_of_year),
('state', '=', 'validate'),
])
total_days = sum(leave.number_of_days for leave in leave_records)
leave_type_name = record.holiday_status_id.name
record.leave_stats = f"本年内已请{leave_type_name}{total_days:.1f}天"
@@ -1,34 +0,0 @@
from odoo import api, fields, models, tools, _
from datetime import datetime, time, timedelta
from odoo.exceptions import UserError
from datetime import date, timedelta
class YuthonOvertimeReport(models.Model):
_name = 'yuthon.overtime.report'
_description = '加班情况'
employee_id = fields.Many2one('hr.employee', string="员工")
department_id = fields.Many2one('hr.department',string="部门")
overtime_date = fields.Char(string="加班时间")
carry_forward_date = fields.Date(string="结转日期")
carry_forward_duration = fields.Float(string="结转时长")
carry_forward_result = fields.Selection(
selection=[
('success', '成功'),
('failed', '失败')
],
string='结转结果',
)
validity = fields.Char(string="有效期")
carry_forward_state = fields.Selection(
selection=[
('pending', '待结转'),
('completed', '已结转'),
('partial', '部分结转')
],
string='结转状态',
default='pending',
)
remaining_quantity = fields.Float(string='剩余数')
@@ -86,7 +86,6 @@ class YuthonVacationReports(models.Model):
attendance_message_log_ids = fields.One2many('attendance.message.log', 'vacation_id')
admin_confirms = fields.Boolean(string="是否管理员确认")
vacation_state = fields.Selection(
selection=[
('not_sent', "未发送"),
@@ -931,7 +930,8 @@ class VacationOverline(models.Model):
_description = '加班明细'
vacation_id = fields.Many2one('yuthon.vacation.reports', string='关联假期额度记录')
employee_id = fields.Many2one('hr.employee', string='员工')
employee_id = fields.Many2one('hr.employee', string='员工', compute='_compute_employee_id', store=True)
department_id = fields.Many2one('hr.department', related='employee_id.department_id', string="部门")
workflow_record_id = fields.Many2one('soong.workflow.record', string='流程')
update_type= fields.Selection([('add', '增加'), ('reduce', '减少')], string='更新类型')
workday = fields.Float(string='工作日')
@@ -947,6 +947,57 @@ class VacationOverline(models.Model):
text = fields.Text(string='备注')
is_confirm = fields.Boolean(string="是否确认")
overtime_name = fields.Char(string="标题", compute="_compute_overtime", store=True)
start_time = fields.Datetime(string="开始时间", compute="_compute_overtime", store=True)
end_time = fields.Datetime(string="结束时间", compute="_compute_overtime", store=True)
carry_forward_date = fields.Date(string="结转日期")
duration = fields.Float(string="加班时长", compute="_compute_overtime", store=True)
carry_forward_quantity = fields.Float(string="结转时长")
validity = fields.Char(string="有效期")
carry_forward_state = fields.Selection([('pending', '待结转'),('completed', '已结转'), ('partial', '部分结转')
],string='结转状态',
default='pending',
)
remaining_quantity = fields.Float(string='剩余数', compute="_compute_remaining_quantity", store=True)
def update_line(self):
over_ids = self.env["vacation.over.line"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in over_ids:
rec._compute_remaining_quantity()
rec._compute_overtime()
rec._compute_employee_id()
@api.depends('duration', 'carry_forward_quantity')
def _compute_remaining_quantity(self):
for rec in self:
rec.remaining_quantity = rec.duration - rec.carry_forward_quantity
@api.depends('vacation_id')
def _compute_employee_id(self):
for rec in self:
if rec.vacation_id:
rec.employee_id = rec.vacation_id.employee_id.id
elif not rec.employee_id:
rec.employee_id = False
@api.depends('workflow_record_id')
def _compute_overtime(self):
for rec in self:
if rec.workflow_record_id and rec.update_type == 'add':
res_model = rec.workflow_record_id.res_model
res_id = rec.workflow_record_id.res_id
overtime_id = rec.env[res_model].search([('id', '=', res_id)])
rec.overtime_name = overtime_id.name
rec.start_time = overtime_id.start_time
rec.end_time = overtime_id.end_time
rec.duration = overtime_id.duration
else:
rec.overtime_name = ''
rec.start_time = ''
rec.end_time = ''
rec.duration = 0
def action_void(self):
self.is_void = True
@@ -969,6 +1020,8 @@ class VacationOverline(models.Model):
if vals.get('update_type') == 'reduce' and vals.get('vacation_id'):
vacation_main = self.env['yuthon.vacation.reports'].browse(vals['vacation_id'])
current_vacation_date = vacation_main.vacation_date
next_month_first_day = current_vacation_date + relativedelta(months=+1, day=1)
deduct_workday = vals.get('workday', 0.0)
deduct_public = vals.get('public_holidays', 0.0)
deduct_workday_convert = deduct_workday / 8
@@ -983,8 +1036,6 @@ class VacationOverline(models.Model):
is_cross_year = False
# 计算下月1号并判断是否跨年
if vacation_main.vacation_date:
current_vacation_date = vacation_main.vacation_date
next_month_first_day = current_vacation_date + relativedelta(months=+1, day=1)
is_cross_year = current_vacation_date.year != next_month_first_day.year
# 跨年场景
@@ -1006,31 +1057,52 @@ class VacationOverline(models.Model):
current_text = vals.get('text', '').strip()
if current_text != '上月额度继承':
vacation_main = self.env['yuthon.vacation.reports'].browse(vals['vacation_id'])
current_vacation_date = vacation_main.vacation_date
next_month_first_day = current_vacation_date + relativedelta(months=+1, day=1)
add_workday = vals.get('workday', 0.0)
add_public = vals.get('public_holidays', 0.0)
next_month_vacation = self.env['yuthon.vacation.reports'].search([
('employee_id', '=', vacation_main.employee_id.id),
('vacation_date', '=', next_month_first_day),
('vacation_state', 'in', ['not_sent', 'pending']),
], limit=1)
if vacation_main:
vacation_add_vals = {
'year_workday': vacation_main.year_workday + add_workday / 8,
'year_holiday': vacation_main.year_holiday + add_public / 8,
}
vacation_main.write(vacation_add_vals)
next_month_vacation = self.env['yuthon.vacation.reports'].search([
('employee_id', '=', vacation_main.employee_id.id),
('vacation_date', '=', next_month_first_day),
('vacation_state', 'in', ['not_sent', 'pending']),
], limit=1)
if next_month_vacation:
total_add_days = add_workday + add_public
inherit_vacation = self.env['vacation.over.line'].search([
('vacation_id', '=', next_month_vacation.id),
('text', '=', '上月额度继承'),
], limit=1)
if inherit_vacation:
inherit_vacation.write({
'year_workday': next_month_vacation.year_workday + add_workday / 8,
'year_holiday': next_month_vacation.year_holiday + add_public / 8,
'workday': inherit_vacation.workday + total_add_days
})
if vacation_main.vacation_date:
current_vacation_date = vacation_main.vacation_date
next_month_first_day = current_vacation_date + relativedelta(months=+1, day=1)
# 必须是跨年才写入上年工作日
is_cross_year = current_vacation_date.year != next_month_first_day.year
next_month_vacation = self.env['yuthon.vacation.reports'].search([
('employee_id', '=', vacation_main.employee_id.id),
('vacation_date', '=', next_month_first_day),
('vacation_state', 'in', ['not_sent', 'pending']),
], limit=1)
if is_cross_year:
next_month_deduct_vals = {
'last_workday': next_month_vacation.last_workday + add_workday / 8,
'last_holiday': next_month_vacation.last_holiday + add_public / 8,
}
next_month_vacation.write(next_month_deduct_vals)
next_month_vacation.previous_year = next_month_vacation.last_workday/8 + next_month_vacation.last_holiday / 8,
return super(VacationOverline, self).create(vals)
@@ -67,7 +67,8 @@
<!-- 审批详情 -->
<tr><td class="section" colspan="3">审批详情</td></tr>
<t t-foreach="doc.log_ids.sorted('sequence')" t-as="log">
<t t-set="logs_ordered" t-value="doc.get_logs_for_print()"/>
<t t-foreach="logs_ordered" t-as="log">
<tr>
<td class="cell-content approval-name approval-col1" style="text-align:center;"><span t-esc="log.name"/></td>
<td class="cell-content approval-col2 no-right-border approval-center"><span t-esc="log.comment"/></td>
@@ -15,7 +15,6 @@
<field name="model">overtime.approval</field>
<field name="arch" type="xml">
<form create="0">
<!-- <header>-->
<!-- <button name="put_hr_employee" type="object" string="Approve" class="oe_highlight"/>-->
<!-- </header>-->
@@ -127,29 +126,4 @@
<field name="domain">[('users_id', '=', uid)]</field>
<field name="view_mode">tree,form</field>
</record>
<record id="view_overtime_approval_report_tree" model="ir.ui.view">
<field name="name">overtime.approval.tree</field>
<field name="model">overtime.approval</field>
<field name="arch" type="xml">
<tree string="加班情况" editable="bottom">
<field name="name"/>
<field name="department_id"/>
<field name="start_time"/>
<field name="end_time"/>
<field name="carry_forward_date"/>
<field name="carry_forward_duration"/>
<field name="carry_forward_result"/>
<field name="carry_forward_state"/>
<field name="remaining_quantity"/>
<field name="validity"/>
</tree>
</field>
</record>
<record id="action_overtime_approval_report" model="ir.actions.act_window">
<field name="name">加班情况</field>
<field name="res_model">overtime.approval</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_overtime_approval_report_tree"/>
</record>
</odoo>
@@ -1,29 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_yuthon_holiday_statement_tree" model="ir.ui.view">
<field name="name">yuthon.holiday.statement.tree</field>
<field name="model">yuthon.holiday.statement</field>
<record id="view_hr_leave_statement_tree" model="ir.ui.view">
<field name="name">hr.leave.statement.tree</field>
<field name="model">hr.leave</field>
<field name="arch" type="xml">
<tree string="请假情况" editable="bottom">
<tree string="请假情况" editable="bottom" limit="15">
<field name="caption"/>
<field name="employee_id"/>
<field name="leave_approval_type"/>
<field name="gender"/>
<field name="leave_title"/>
<field name="leave_summary"/>
<field name="leave_start_date"/>
<field name="leave_end_date"/>
<field name="leave_duration"/>
<field name="leave_start_time"/>
<field name="leave_end_time"/>
<field name="employee_company_id"/>
<field name="holiday_status_id"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="number_of_days" string="请假时长(天)"/>
<field name="leave_stats"/>
<field name="application_reason"/>
<field name="approval_state"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="view_yuthon_holiday_statement_action" model="ir.actions.act_window">
<field name="name">请假情况</field>
<field name="res_model">yuthon.holiday.statement</field>
<field name="res_model">hr.leave</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_hr_leave_statement_tree"/>
</record>
</odoo>
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_yuthon_overtime_report_tree" model="ir.ui.view">
<field name="name">yuthon.overtime.report.tree</field>
<field name="model">yuthon.overtime.report</field>
<field name="arch" type="xml">
<tree string="加班情况" editable="bottom">
<field name="employee_id"/>
<field name="department_id"/>
<field name="overtime_date"/>
<field name="carry_forward_date"/>
<field name="carry_forward_duration"/>
<field name="carry_forward_result"/>
<field name="carry_forward_state"/>
<field name="remaining_quantity"/>
<field name="validity"/>
</tree>
</field>
</record>
<record id="action_yuthon_overtime_report" model="ir.actions.act_window">
<field name="name">加班情况</field>
<field name="res_model">yuthon.overtime.report</field>
<field name="view_mode">tree</field>
</record>
</odoo>
@@ -5,8 +5,8 @@
<field name="model">yuthon.vacation.reports</field>
<field name="arch" type="xml">
<tree string="误餐情况">
<field name="employee_id"/>
<field name="meal_fee"/>
<field name="employee_id"/>
<field name="meal_fee"/>
</tree>
</field>
</record>
@@ -286,7 +286,23 @@
<!-- </tree>-->
<!-- </field>-->
<!-- </page>-->
<page string="汇总" invisible="1">
<page string="管理员修改字段" groups="base.group_system">
<group col="2">
<group>
<field name="previous_year"/>
<field name="year_distribution"/>
<field name="used_this_year"/>
<field name="last_holiday"/>
</group>
<group>
<field name="last_workday"/>
<field name="year_holiday"/>
<field name="year_workday"/>
<field name="used_this_year1"/>
</group>
</group>
</page>
<page string="汇总" >
<group>
<field name="is_late_card"/>
<field name="is_early_card"/>
@@ -491,10 +507,57 @@
</form>
</field>
</record>
<record id="view_yuthon_vacation_overtime_tree" model="ir.ui.view">
<field name="name">vacation.over.line.overtime.tree</field>
<field name="model">vacation.over.line</field>
<field name="arch" type="xml">
<tree string="加班情况" editable="bottom">
<field name="overtime_name"/>
<field name="employee_id"/>
<field name="department_id"/>
<field name="start_time"/>
<field name="end_time"/>
<field name="duration"/>
<field name="carry_forward_quantity"/>
<field name="carry_forward_date"/>
<field name="carry_forward_state"/>
<field name="remaining_quantity"/>
<field name="validity"/>
</tree>
</field>
</record>
<record id="view_yuthon_vacation_overtime_search" model="ir.ui.view">
<field name="name">vacation.over.line.overtime.search</field>
<field name="model">vacation.over.line</field>
<field name="arch" type="xml">
<search string="加班情况">
<field name="overtime_name"/>
<field name="employee_id"/>
<field name="department_id"/>
<field name="start_time"/>
</search>
</field>
</record>
<record id="action_overtime_approval_report" model="ir.actions.act_window">
<field name="name">加班情况</field>
<field name="res_model">vacation.over.line</field>
<field name="view_mode">tree</field>
<field name="domain">[('workflow_record_id','!=', False), ('update_type', '=', 'add')]</field>
<field name="view_id" ref="view_yuthon_vacation_overtime_tree"/>
<field name="search_view_id" ref="view_yuthon_vacation_overtime_search"/>
</record>
<record id="update_vacation_over_line" model="ir.actions.server">
<field name="name">触发计算逻辑</field>
<field name="model_id" ref="yuthon_hr_attendance.model_vacation_over_line"/>
<field name="binding_model_id" ref="yuthon_hr_attendance.model_vacation_over_line"/>
<field name="state">code</field>
<field name="code">action = model.update_line()</field>
</record>
<record id="view_yuthon_vacation_meal_action" model="ir.actions.act_window">
<field name="name">误餐情况</field>
<field name="res_model">yuthon.vacation.reports</field>
<field name="view_mode">tree</field>
<field name="domain">[('meal_fee', '!=', 0)]</field>
<field name="context">{'search_default_group_by_employee_id': 1, 'search_default_group_by_vacation_month': True}</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_hr_attendance.view_yuthon_vacation_meal_tree')}),]"/>
@@ -82,7 +82,7 @@
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='department_id']" position="after">
<field name="allowed_user_ids" invisible="1"/>
<!-- <field name="allowed_user_ids" invisible="1"/>-->
<field name="number"/>
<field name="establishment_quotas"/>
<field name="no_of_employee" string="在编人数"/>
@@ -92,7 +92,8 @@
<tr>
<td colspan="4" style="background-color: #d4e6f1; text-align: center;"><strong>审批详情</strong></td>
</tr>
<t t-foreach="doc.log_ids.sorted('sequence')" t-as="log">
<t t-set="logs_ordered" t-value="doc.get_logs_for_print()"/>
<t t-foreach="logs_ordered" t-as="log">
<tr>
<td colspan="1"><span t-esc="log.name"/></td>
<!-- 核心修改:隐藏意见列右侧边框 -->
@@ -119,7 +119,8 @@
<td class="header-cell" colspan="5" style="text-align:center;"><strong>审批详情</strong></td>
</tr>
<t t-if="doc.log_ids">
<t t-foreach="doc.log_ids.sorted('sequence')" t-as="log">
<t t-set="logs_ordered" t-value="doc.get_logs_for_print()"/>
<t t-foreach="logs_ordered" t-as="log">
<tr>
<td class="approval-col1" style="text-align:center;"><span t-esc="log.name"/></td>
<!-- 仅修改此处colspan为3,让approval-col2和approval-col3相邻 -->
@@ -116,7 +116,8 @@
<div class="section">审批详情</div>
<table class="po">
<t t-if="doc.log_ids">
<t t-foreach="doc.log_ids.sorted('sequence')" t-as="log">
<t t-set="logs_ordered" t-value="doc.get_logs_for_print()"/>
<t t-foreach="logs_ordered" t-as="log">
<tr>
<td class="text-center"><span t-esc="log.name or ''"/></td>
<!-- 隐藏意见列右侧边框 + 内容居中 -->
@@ -42,18 +42,8 @@
<tr t-if="doc.is_chapter == 'yes'"><td class="hdr"><strong>用章范围</strong></td><td colspan="2" class="cell-content"><t t-foreach="doc.scope_chapter_ids" t-as="scope"><span t-esc="scope.name"/><t t-if="not scope_last">、</t></t></td></tr>
<tr><td class="hdr"><strong>附件清单</strong></td><td colspan="2" class="cell-content attachment-list"><t t-foreach="doc.document_ids" t-as="att"><div><t t-esc="att.name"/></div></t></td></tr>
<tr><td class="section" colspan="3"><strong>审批详情</strong></td></tr>
<!-- 按 sequence 逆序去重(同名节点保留最新) -->
<t t-set="logs_sorted" t-value="doc.log_ids.sorted('sequence', reverse=True)"/>
<t t-set="seen" t-value="set()"/>
<t t-set="logs_dedup" t-value="[]"/>
<t t-foreach="logs_sorted" t-as="log_tmp">
<t t-if="(log_tmp.name or '') not in seen">
<t t-set="seen" t-value="seen | {log_tmp.name or ''}"/>
<t t-set="logs_dedup" t-value="logs_dedup + [log_tmp]"/>
</t>
</t>
<t t-set="logs_final" t-value="logs_dedup"/>
<t t-foreach="logs_final" t-as="log">
<t t-set="logs_ordered" t-value="doc.get_logs_for_print(True)"/>
<t t-foreach="logs_ordered" t-as="log">
<tr>
<td class="cell-content approval-name approval-col1"><span t-esc="log.name"/></td>
<td class="cell-content approval-col2 no-right-border approval-center"><span t-esc="log.comment"/></td>
@@ -104,7 +104,8 @@
<td class="header-cell" colspan="7" style="text-align:center; background-color: #d4e6f1;"><strong>审批详情</strong></td>
</tr>
<t t-if="doc.log_ids">
<t t-foreach="doc.log_ids.sorted('sequence')" t-as="log">
<t t-set="logs_ordered" t-value="doc.get_logs_for_print()"/>
<t t-foreach="logs_ordered" t-as="log">
<tr>
<td class="approval-col1 approval-center" colspan="1" style="width:6em;"><span t-esc="log.name"/></td>
<td class="approval-col2 no-right-border approval-center" colspan="4"><span t-esc="log.comment"/></td>
+4 -2
View File
@@ -167,7 +167,8 @@ class YuthonWillTask(models.Model):
if minute1 == now.minute and hour1 == now.hour:
task.write({'task_state': 'done'})
will_id = task.yuthon_will_task_id
for employee_id in will_id.employee_ids:
will_id.task_state = 'done'
for employee_id in task.employee_ids:
url = f'https://phone.thtzjt.com/phone/Schedule/{task.yuthon_will_task_id.id}/{employee_id.id}'
data_date = '时间:' + (task.reminder_time + timedelta(hours=8)).strftime("%Y-%m-%d %H:%M:%S") + '\n'
data_name = "事项名称: " + task.yuthon_will_task_id.name + '\n'
@@ -290,4 +291,5 @@ class YuthonWillTask(models.Model):
'view_mode': 'tree',
'res_model': 'yuthon.dates.task',
'domain': [('yuthon_will_task_id', '=', self.id)],
}
}
+5 -5
View File
@@ -170,11 +170,11 @@
parent="hr_attendance_root_board_menu"
action="yuthon_hr_attendance.action_overtime_approval_report"
sequence="15"/>
<!-- <menuitem id="menu_yuthon_holiday_statement_board"-->
<!-- name="请假情况"-->
<!-- parent="hr_attendance_root_board_menu"-->
<!-- action="yuthon_hr_attendance.view_yuthon_holiday_statement_action"-->
<!-- sequence="16"/>-->
<menuitem id="menu_yuthon_holiday_statement_board"
name="请假情况"
parent="hr_attendance_root_board_menu"
action="yuthon_hr_attendance.view_yuthon_holiday_statement_action"
sequence="16"/>
<menuitem id="menu_yuthon_meal_case_board"
name="误餐情况"
parent="hr_attendance_root_board_menu"