Merge pull request !5157 from 李鹏宇/dev
This commit is contained in:
李鹏宇
2025-09-16 03:18:57 +00:00
committed by Gitee
28 changed files with 201 additions and 26 deletions
+28 -19
View File
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from datetime import datetime, time
from re import search
from odoo import models, fields, api, exceptions, _
from datetime import timedelta
from dateutil.relativedelta import relativedelta
@@ -111,27 +113,34 @@ class HrAttendance(models.Model):
# 判断是否为上月
record.is_last_month = (first_day_last_month <= record.current_data <= last_day_last_month)
def compute_company_id(self):
employee_date_records = {}
for att in self.env["hr.attendance"].browse(self._context.get('active_ids', self._context.get('active_id'))):
employee_id = att.employee_id.id
# 创建唯一分组键 (员工ID, 日期)
group_key = (employee_id, att.current_data)
# 将记录按员工+日期分组
if group_key not in employee_date_records:
employee_date_records[group_key] = []
employee_date_records[group_key].append(att.id)
def update_att(self):
# 定义要更新的员工姓名列表
target_employees = ["简梓豪", "林紫珊"]
# 定义日期范围:9月1日至9月15日
start_date = fields.Date.from_string("2025-09-01")
end_date = fields.Date.from_string("2025-09-15")
# 处理重复记录:每个员工每天只保留第一条记录
for (employee_id, date), record_ids in employee_date_records.items():
if len(record_ids) > 1:
# 保留第一条记录,其余标记为删除
ids_to_keep = record_ids[0]
ids_to_delete = record_ids[1:]
# 查找目标员工
employees = self.env["hr.employee"].search([
("name", "in", target_employees)
])
# 执行删除操作
if ids_to_delete:
self.env["hr.attendance"].browse(ids_to_delete).unlink()
if not employees:
raise UserError("未找到指定的员工记录")
# 生成日期范围内的所有日期
current_date = start_date
while current_date <= end_date:
for employee in employees:
# 执行考勤详情计算方法
self.env["hr.attendance.detail"].compute_attendance_detail(
employee,
current_date,
is_manual=True
)
current_date = current_date + timedelta(days=1)
return True
Binary file not shown.
@@ -17,7 +17,6 @@ class PropertyMeterArchives(models.Model):
lessee_id = fields.Many2one('property.tenant.information', string='承租方', store=True, compute='_compute_lessee_id')
employee_ids = fields.Many2many('hr.employee', string='抄表人')
epitope_values = fields.Char(string='表位置')
meter_type = fields.Selection([('electric', '电表'), ('water', '水表'), ('smart', '智能电表')], string='仪表类型')
other = fields.Char(string='其他')
meter_date = fields.Date(string='任务日期')
last_dosage = fields.Float(string='上次用量')
@@ -15,7 +15,6 @@ class PropertyMeterWater(models.Model):
maximum_count = fields.Integer(string='最大位数')
lessee_id = fields.Many2one('property.tenant.information', store=True, compute='_compute_lessee_id', string="承租方")
employee_ids = fields.Many2many('hr.employee', string='抄表人')
meter_type = fields.Selection([('electric', '电表'), ('water', '水表')], string='仪表类型')
epitope_values = fields.Char(string='表位置')
other = fields.Char(string='其他')
meter_date = fields.Date(string='任务日期')
@@ -58,7 +58,6 @@
<field name="lessee_id"/>
<field name="employee_ids" widget="many2many_tags"/>
<field name="epitope_values"/>
<field name="meter_type" invisible="1"/>
</group>
<group>
<field name="other"/>
@@ -133,6 +132,7 @@
<field name="name">电表库</field>
<field name="res_model">property.meter.archives</field>
<field name="view_mode">tree,form,search</field>
<field name="domain">[('is_push', '=', False)]</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_archives_tree')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_archives_form')})]"/>
@@ -60,7 +60,6 @@
<field name="lessee_id"/>
<field name="employee_ids" widget="many2many_tags"/>
<field name="epitope_values"/>
<field name="meter_type" invisible="1"/>
</group>
<group>
<field name="other"/>
@@ -131,6 +130,7 @@
<field name="name">水表库</field>
<field name="res_model">property.meter.water</field>
<field name="view_mode">tree,form,search</field>
<field name="domain">[('is_push', '=', False)]</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_water_tree')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_water_form')})]"/>
@@ -32,7 +32,6 @@ class PropertyMeterWizard(models.Model):
epitope_values = row[0].value
name = row[3].value
type_dict = {'水表': 'water', '电表': 'electric', '电表(电子表)': 'smart', '': ''}
meter_type = type_dict.get(row[4].value)
unit_number = row[1].value
installation_location = row[2].value
if row[5].value:
@@ -63,7 +62,6 @@ class PropertyMeterWizard(models.Model):
'unit_number': unit_number,
'installation_location': installation_location,
'type':type,
'meter_type': meter_type,
'gauge_magnification': gauge_magnification,
'meter_date': meter_date,
'current_read': current_read,
@@ -65,7 +65,6 @@ class PropertyWaterWizard(models.Model):
'unit_number': unit_number,
'installation_location': installation_location,
'type': type,
'meter_type': meter_type,
'gauge_magnification': gauge_magnification,
'meter_date': meter_date,
'current_read': current_read,
+3
View File
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models
from . import controllers
+35
View File
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
{
"name": "Yuthon Web Notice",
"summary": "通知公告模块",
"description": "通知公告模块",
"author": "Yuthon",
"website": "#",
"category": "Yuthon/Yuthon Web Notice",
"version": "17.0.0.1",
"depends": ["base", "web","hr"],
"data": [
"views/templates.xml",
],
"assets": {
"yuthon_web_notice.assets_announcement": [
("include", "web._assets_helpers"),
"web/static/src/scss/pre_variables.scss",
"web/static/lib/bootstrap/scss/_variables.scss",
("include", "web._assets_bootstrap_backend"),
# 字体图标
"web/static/src/libs/fontawesome/css/font-awesome.css",
# include base files from framework
("include", "web._assets_backend"),
("include", "web._assets_core"),
"web/static/src/core/utils/functions.js",
"web/static/src/core/browser/browser.js",
"web/static/src/core/registry.js",
"web/static/src/core/assets.js",
"yuthon_web_notice/static/src/**/*",
],
},
"installable": True,
"application": True,
"license": "AGPL-3",
}
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import controllers
@@ -0,0 +1,10 @@
from odoo import http
from odoo.http import request, route
class OwlPlayground(http.Controller):
@http.route(['/web_notice'], type='http', auth='public')
def show_playground(self):
"""
Renders the owl playground page
"""
return request.render('yuthon_web_notice.Announcement')
+1
View File
@@ -0,0 +1 @@
from . import hr_employee
+16
View File
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class HrEmployee(models.Model):
_inherit = "hr.employee"
def get_full_employee_info(self, employee_id):
"""返回员工完整信息(所有字段)"""
employee = self.browse(employee_id)
if not employee.exists():
return {}
# 读取所有字段
fields = employee.fields_get().keys() # 获取所有字段名
return employee.sudo().read(fields)[0] # 返回字典
@@ -0,0 +1,32 @@
/** @odoo-module **/
import { Component, useState, onWillUnmount, } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
export class Announcement extends Component {
static template = "yuthon_web_notice.Announcement";
static props = ["*"];
setup() {
console.log(useService);
debugger;
this.rpc = useService("rpc");
this.employee = useState({});
this.id = this.props.id || "";
onWillUnmount(async () => {
if (this.id) {
await this.loadEmployee(this.id);
}
});
}
async loadEmployee(employeeId) {
try {
this.employee = await this.rpc({
model: "hr.employee",
method: "get_full_employee_info",
args: [parseInt(employeeId)],
});
} catch (err) {
console.error("获取员工信息失败:", err);
}
}
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="yuthon_web_notice.Announcement">
<h1>Hello OWL Web</h1>
</t>
</templates>
+51
View File
@@ -0,0 +1,51 @@
/** @odoo-module **/
import { browser } from "@web/core/browser/browser";
import { makeEnv } from "@web/env"; // 引入环境构建工具
import { mount, whenReady } from "@odoo/owl";
import { Announcement } from "./announcement";
import { templates } from "@web/core/assets";
// 当document.body准备好时,挂载 Announcement 组件
whenReady(() => {
const env = makeEnv(); // 创建标准 Odoo 环境
mount(Announcement, document.body, {
templates,
dev: true,
name: "Web Notice",
env,
props: {
id: new URLSearchParams(window.location.search).get("id"),
},
});
});
/**
* 错误日志捕获函数
* 遍历 error 对象的 cause 属性,输出错误堆栈信息(包含链式错误原因)
* @param {Event} ev 浏览器事件对象
*/
function logError(ev) {
ev.preventDefault();
let error = ev?.error || ev.reason;
if (error.seen) {
// 如果错误已经标记过 seen,就不再重复打印
// 场景:mount 失败时,Owl 会抛出错误,如果不处理就会打印两次
return;
}
error.seen = true;
let errorMessage = error.stack;
while (error.cause) {
errorMessage += "\nCaused by: "
errorMessage += error.cause.stack;
error = error.cause;
}
console.error(errorMessage);
}
// 监听全局错误事件(比如未捕获的异常)
browser.addEventListener("error", (ev) => { logError(ev) });
// 监听未捕获的 Promise 错误(unhandledrejection)
browser.addEventListener("unhandledrejection", (ev) => { logError(ev) });
+14
View File
@@ -0,0 +1,14 @@
<odoo>
<data>
<template id="yuthon_web_notice.Announcement" name="Yuthon Web Notice">
<html>
<head>
<link type="image/x-icon" rel="shortcut icon" href="/web/static/img/favicon.ico"/>
<t t-call-assets="yuthon_web_notice.assets_announcement"/>
</head>
<body>
</body>
</html>
</template>
</data>
</odoo>