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

# Conflicts:
#	yuthon_property/models/property_meter_water.py
This commit is contained in:
李鹏宇
2025-10-12 22:14:53 +08:00
5 changed files with 434 additions and 300 deletions
@@ -1,11 +1,10 @@
<templates xml:space="preserve">
<t t-inherit="web.ControlPanel" t-inherit-mode="extension">
<!-- 替换页面头部左侧 -->
<xpath expr="//div[hasclass('o_control_panel_main')]" position="after">
<xpath expr="//div[hasclass('o_control_panel_main')]" position="after">
<!-- 这是header_search_panel模块 -->
<t t-if="hasHeaderSearch">
<HeaderSearchFilter />
<t t-if="hasHeaderSearch and this.env.config.viewType=='list'">
<HeaderSearchFilter />
</t>
</xpath>
</t>
@@ -6,25 +6,63 @@ from odoo import api, fields, models, tools, _
class HrAttendance(models.Model):
_inherit = "hr.attendance"
_order = 'current_data desc, check_in desc'
_order = "current_data desc, check_in desc"
userid = fields.Char(string='userid')
groupname = fields.Char(string='打卡规则名称')
current_data = fields.Date(string='打卡日期', tracking=True)
sch_check_in_time = fields.Datetime(string='上班正常时间')
sch_check_out_time = fields.Datetime(string='下班正常时间')
sch_check_noon_time = fields.Datetime(string='中午正常时间')
check_noon = fields.Datetime(string='中午打卡', tracking=True)
check_in = fields.Datetime(string="上班打卡", required=False, default=False, tracking=True)
userid = fields.Char(string="userid")
groupname = fields.Char(string="打卡规则名称")
current_data = fields.Date(string="打卡日期", tracking=True)
current_data_month = fields.Char(
string="打卡日期(月份)",
compute="_compute_current_data_month",
store=True,
index=True,
)
def _compute_current_data_month(self):
for rec in self:
if rec.current_data:
rec.current_data_month = rec.current_data.strftime("%Y-%m")
else:
rec.current_data_month = False
sch_check_in_time = fields.Datetime(string="上班正常时间")
sch_check_out_time = fields.Datetime(string="下班正常时间")
sch_check_noon_time = fields.Datetime(string="中午正常时间")
check_noon = fields.Datetime(string="中午打卡", tracking=True)
check_in = fields.Datetime(
string="上班打卡", required=False, default=False, tracking=True
)
check_out = fields.Datetime(string="下班打卡", tracking=True)
attendance_type = fields.Selection([('wx_work', '企业微信')], readonly=True, string='来源')
attendance_type = fields.Selection(
[("wx_work", "企业微信")], readonly=True, string="来源"
)
# field_work_check = fields.Datetime(string="外勤打卡")
attendance_detail_ids = fields.One2many(string="打卡详情", comodel_name='hr.attendance.detail', inverse_name='hr_attendance_id')
replacement_approval_ids = fields.One2many(string="补卡详情", comodel_name='approval2.request', compute='_compute_replacement_approval_ids')
go_out_approval_ids = fields.One2many(string="外出详情", comodel_name='approval2.request', compute='_compute_go_out_approval_ids')
work_overtime_approval_ids = fields.One2many(string="加班详情", comodel_name='approval2.request', compute='_compute_work_overtime_approval_ids')
hr_leave_approval_ids = fields.One2many(string="请假详情", comodel_name='hr.leave', compute='_compute_hr_leave_approval_ids')
attendance_detail_ids = fields.One2many(
string="打卡详情",
comodel_name="hr.attendance.detail",
inverse_name="hr_attendance_id",
)
replacement_approval_ids = fields.One2many(
string="补卡详情",
comodel_name="approval2.request",
compute="_compute_replacement_approval_ids",
)
go_out_approval_ids = fields.One2many(
string="外出详情",
comodel_name="approval2.request",
compute="_compute_go_out_approval_ids",
)
work_overtime_approval_ids = fields.One2many(
string="加班详情",
comodel_name="approval2.request",
compute="_compute_work_overtime_approval_ids",
)
hr_leave_approval_ids = fields.One2many(
string="请假详情",
comodel_name="hr.leave",
compute="_compute_hr_leave_approval_ids",
)
is_approval = fields.Boolean(string="是否审批修正", default=False)
def action_show_details(self):
@@ -40,17 +78,16 @@ class HrAttendance(models.Model):
"view_id": view.id,
"target": "current",
"res_id": self.id,
"domain": [('hr_attendance_id', '=', self.id)],
"context": {'hr_attendance_id': self.id}
"domain": [("hr_attendance_id", "=", self.id)],
"context": {"hr_attendance_id": self.id},
}
@api.constrains('check_in', 'check_out', 'employee_id')
@api.constrains("check_in", "check_out", "employee_id")
def _check_validity(self):
for attendance in self:
if attendance.attendance_type != 'wx_work':
if attendance.attendance_type != "wx_work":
super(HrAttendance, attendance)._check_validity()
def get_work_time(self, attendance_data, current_date, tz):
"""
当前时间的工作时间段
@@ -61,16 +98,28 @@ class HrAttendance(models.Model):
"""
work_time_list = []
for work_time in attendance_data:
work_time_list.append(self.time_to_utc_time(self.str_to_time(current_date, work_time.hour_from), tz))
work_time_list.append(self.time_to_utc_time(self.str_to_time(current_date, work_time.hour_to), tz))
work_time_list.append(
self.time_to_utc_time(
self.str_to_time(current_date, work_time.hour_from), tz
)
)
work_time_list.append(
self.time_to_utc_time(
self.str_to_time(current_date, work_time.hour_to), tz
)
)
work_time_sorted = sorted(work_time_list)
go_from, go_to, off_from, off_to = work_time_sorted
return go_from, go_to, off_from, off_to, current_date
@api.depends('check_in', 'sch_check_in_time')
@api.depends("check_in", "sch_check_in_time")
def is_come_late(self):
self.ensure_one()
if self.check_in and self.sch_check_in_time and self.check_in > self.sch_check_in_time:
if (
self.check_in
and self.sch_check_in_time
and self.check_in > self.sch_check_in_time
):
return True
return False
@@ -93,7 +142,10 @@ class HrAttendance(models.Model):
if not attendance_ids:
return False
# 是不是工作时间
attendance_data = resource_calendar.attendance_ids.filtered(lambda s: s.dayofweek == str(current_week) and s.day_period in ["morning", "afternoon"])
attendance_data = resource_calendar.attendance_ids.filtered(
lambda s: s.dayofweek == str(current_week)
and s.day_period in ["morning", "afternoon"]
)
if not attendance_data:
return False
# 获取工作时间
@@ -102,7 +154,9 @@ class HrAttendance(models.Model):
return False
go_from, go_to, off_from, off_to, current_date = work_time
# 早上和下午上班 判断当天 是不是公休日
if self.get_leave_day(global_leave, go_from, tz) and self.get_leave_day(global_leave, off_from, tz):
if self.get_leave_day(global_leave, go_from, tz) and self.get_leave_day(
global_leave, off_from, tz
):
return False
return go_from, go_to, off_from, off_to, current_date
@@ -117,7 +171,11 @@ class HrAttendance(models.Model):
if not global_leave:
return False
for leave in global_leave:
if self.utc_time_to_tz(tz, leave.date_from) <= current_time.strftime("%Y-%m-%d %H:%M:%S") <= self.utc_time_to_tz(tz, leave.date_to):
if (
self.utc_time_to_tz(tz, leave.date_from)
<= current_time.strftime("%Y-%m-%d %H:%M:%S")
<= self.utc_time_to_tz(tz, leave.date_to)
):
return True
return False
@@ -129,7 +187,7 @@ class HrAttendance(models.Model):
:param chin: 时间
:return: 转换时间
"""
time_timezone = pytz.timezone('utc').localize(chin, is_dst=False)
time_timezone = pytz.timezone("utc").localize(chin, is_dst=False)
time_to_utc = time_timezone.astimezone(pytz.timezone(tz))
utc_time_str = time_to_utc.strftime("%Y-%m-%d %H:%M:%S")
return utc_time_str
+143 -108
View File
@@ -5,56 +5,71 @@
<field name="model">hr.attendance</field>
<field name="arch" type="xml">
<tree string="出勤"
edit="false" delete="false" create="false" duplicate="false"
decoration-danger="is_workflow or is_approval"
decoration-muted = "is_week"
decoration-success="is_holiday"
editable="bottom">
edit="false" delete="false" create="false" duplicate="false"
decoration-danger="is_workflow or is_approval"
decoration-muted="is_week"
decoration-success="is_holiday"
editable="bottom"
default_order="current_data desc,current_data_month desc">
<header>
<!-- <button name="compute_company_id" string="公司更新" type="object" class="btn btn-primary"/>-->
<!-- <button name="button_all" string="班次更新" type="object" class="btn btn-primary"/>-->
<button name="compute_attendance_ids" string="重新计算" type="object" class="btn btn-primary"/>
<button name="state_fix" string="状态修正" type="object" class="btn btn-primary" groups="yuthon_hr_attendance.group_he_attendance_sign_manager"/>
<button name="unlink"
string="删除"
type="object"
icon="fa-trash"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager"
confirm="确定要删除选中的记录吗?"/>
<!-- <button name="compute_company_id" string="公司更新" type="object" class="btn
btn-primary"/>-->
<!-- <button name="button_all" string="班次更新" type="object" class="btn
btn-primary"/>-->
<button name="compute_attendance_ids" string="重新计算" type="object"
class="btn btn-primary" />
<button name="state_fix" string="状态修正" type="object" class="btn btn-primary"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager" />
<button name="unlink"
string="删除"
type="object"
icon="fa-trash"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager"
confirm="确定要删除选中的记录吗?" />
</header>
<field name="employee_id" class="list_current_data" options="{'no_create': True}"/>
<field name="current_data" optional="show" class="list_current_data"/>
<field name="week" optional="show" class="list_ids_width"/>
<field name="check_in" optional="show" widget="time" decoration-danger="is_check_in_red == 1"/>
<field name="check_out" optional="show" widget="time" class="list_current_data" decoration-danger="is_check_out_red == 1"/>
<field name="shift_id" optional="hidden" column_invisible="1"/>
<field name="shift_name" optional="show" class="list_current_data"/>
<field name="is_week" column_invisible="1"/>
<field name="is_holiday" column_invisible="1"/>
<field name="create_date" optional="hidden"/>
<field name="user_id" column_invisible="1"/>
<field name="is_clock_card" column_invisible="1"/>
<field name="department_id" column_invisible="1"/>
<field name="is_no_card" optional="show" class="list_ids_width"/>
<field name="is_early_card" optional="show" class="list_ids_width"/>
<field name="is_late_card" optional="show" class="list_ids_width"/>
<field name="absenteeism_card" optional="show" class="list_ids_width" string="旷工"/>
<field name="is_abnormal" optional="show" class="list_ids_width" string="异常"/>
<field name="abnormal" optional="show" column_invisible="1"/>
<field name="work_time_type" optional="hidden"/>
<field name="is_approval" optional="hidden"/>
<field name="is_check_in_red" column_invisible="1"/>
<field name="is_check_out_red" column_invisible="1"/>
<field name="is_holiday" optional="hidden"/>
<field name="correct_result" optional="hidden"/>
<field name="is_warning" optional="hidden"/>
<field name="is_workflow" optional="hidden"/>
<field name="workflow_id" optional="hidden"/>
<button name="button_controls_wizard" type="object" string="操作" class="oe_highlight" invisible="not (is_abnormal or is_early_card or is_late_card or absenteeism_card or is_no_card)"/>
<button name="button_correct_wizard" type="object" string="修正" class="btn btn-primary" invisible="not (is_abnormal or is_early_card or is_late_card or absenteeism_card or is_no_card)" groups="yuthon_hr_attendance.group_he_attendance_sign_manager"/>
<button name="revoke" type="object" string="撤销" class="btn btn-primary" invisible="not (is_check_out_red or is_check_in_red)" groups="yuthon_hr_attendance.group_he_attendance_sign_manager"/>
<button name="button_workflow" type="object" string="流程" class="oe_highlight" invisible="not is_workflow"/>
<button name="button_source" type="object" string="详情" class="oe_highlight" groups="yuthon_hr_attendance.group_he_attendance_sign_manager"/>
<field name="employee_id" class="list_current_data" options="{'no_create': True}" />
<field name="current_data" optional="show" class="list_current_data" />
<field name="week" optional="show" class="list_ids_width" />
<field name="check_in" optional="show" widget="time"
decoration-danger="is_check_in_red == 1" />
<field name="check_out" optional="show" widget="time" class="list_current_data"
decoration-danger="is_check_out_red == 1" />
<field name="shift_id" optional="hidden" column_invisible="1" />
<field name="shift_name" optional="show" class="list_current_data" />
<field name="is_week" column_invisible="1" />
<field name="is_holiday" column_invisible="1" />
<field name="create_date" optional="hidden" />
<field name="user_id" column_invisible="1" />
<field name="is_clock_card" column_invisible="1" />
<field name="department_id" column_invisible="1" />
<field name="is_no_card" optional="show" class="list_ids_width" />
<field name="is_early_card" optional="show" class="list_ids_width" />
<field name="is_late_card" optional="show" class="list_ids_width" />
<field name="absenteeism_card" optional="show" class="list_ids_width" string="旷工" />
<field name="is_abnormal" optional="show" class="list_ids_width" string="异常" />
<field name="abnormal" optional="show" column_invisible="1" />
<field name="work_time_type" optional="hidden" />
<field name="is_approval" optional="hidden" />
<field name="is_check_in_red" column_invisible="1" />
<field name="is_check_out_red" column_invisible="1" />
<field name="is_holiday" optional="hidden" />
<field name="correct_result" optional="hidden" />
<field name="is_warning" optional="hidden" />
<field name="is_workflow" optional="hidden" />
<field name="workflow_id" optional="hidden" />
<button name="button_controls_wizard" type="object" string="操作" class="oe_highlight"
invisible="not (is_abnormal or is_early_card or is_late_card or absenteeism_card or is_no_card)" />
<button name="button_correct_wizard" type="object" string="修正"
class="btn btn-primary"
invisible="not (is_abnormal or is_early_card or is_late_card or absenteeism_card or is_no_card)"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager" />
<button name="revoke" type="object" string="撤销" class="btn btn-primary"
invisible="not (is_check_out_red or is_check_in_red)"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager" />
<button name="button_workflow" type="object" string="流程" class="oe_highlight"
invisible="not is_workflow" />
<button name="button_source" type="object" string="详情" class="oe_highlight"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager" />
</tree>
</field>
</record>
@@ -63,38 +78,46 @@
<field name="model">hr.attendance</field>
<field name="arch" type="xml">
<tree string="出勤"
edit="false" delete="false" create="false" duplicate="false"
editable="bottom" default_order="current_data desc">
<!-- <header>-->
<!-- <button name="compute_company_id" string="公司更新" type="object" class="btn btn-primary"/>-->
<!-- </header>-->
<field name="current_data" optional="show" class="list_current_data"/>
<field name="week" optional="show" class="list_ids_width"/>
<field name="check_in" optional="show" widget="time"/>
<field name="check_out" optional="show" widget="time" class="list_current_data"/>
<field name="shift_id" optional="hidden" column_invisible="1"/>
<field name="shift_name" optional="show" class="list_current_data"/>
<field name="create_date" optional="hidden"/>
<field name="user_id" column_invisible="1"/>
<field name="is_clock_card" column_invisible="1"/>
<field name="department_id" column_invisible="1"/>
<field name="is_no_card" optional="show" class="list_ids_width"/>
<field name="is_early_card" optional="show" class="list_ids_width"/>
<field name="is_late_card" optional="show" class="list_ids_width"/>
<field name="absenteeism_card" optional="show" class="list_ids_width" string="旷工"/>
<field name="is_abnormal" optional="show" class="list_ids_width" string="异常"/>
<field name="abnormal" optional="show" column_invisible="1"/>
<field name="work_time_type" optional="hidden"/>
<field name="is_approval" optional="hidden"/>
<field name="is_month" optional="hidden"/>
<field name="is_last_month" optional="hidden"/>
<field name="is_warning" optional="hidden"/>
<field name="is_check_in_red" column_invisible="1"/>
<field name="is_check_out_red" column_invisible="1"/>
<button name="button_controls_wizard" type="object" string="操作" class="oe_highlight" invisible="not (is_abnormal or is_early_card or is_late_card or absenteeism_card or is_no_card)"/>
<button name="button_correct_wizard" type="object" string="修正" class="btn btn-primary" invisible="not (is_abnormal or is_early_card or is_late_card or absenteeism_card or is_no_card)" groups="yuthon_hr_attendance.group_he_attendance_sign_manager"/>
<button name="revoke" type="object" string="撤销" class="btn btn-primary" invisible="not (is_check_out_red or is_check_in_red)" groups="yuthon_hr_attendance.group_he_attendance_sign_manager"/>
<button name="button_source" type="object" string="详情" class="oe_highlight" groups="yuthon_hr_attendance.group_he_attendance_sign_manager"/>
edit="false" delete="false" create="false" duplicate="false"
editable="bottom" default_order="current_data desc,current_data_month desc">
<!-- <header>-->
<!-- <button name="compute_company_id" string="公司更新" type="object" class="btn
btn-primary"/>-->
<!-- </header>-->
<field name="current_data" optional="show" class="list_current_data" />
<field name="week" optional="show" class="list_ids_width" />
<field name="check_in" optional="show" widget="time" />
<field name="check_out" optional="show" widget="time" class="list_current_data" />
<field name="shift_id" optional="hidden" column_invisible="1" />
<field name="shift_name" optional="show" class="list_current_data" />
<field name="create_date" optional="hidden" />
<field name="user_id" column_invisible="1" />
<field name="is_clock_card" column_invisible="1" />
<field name="department_id" column_invisible="1" />
<field name="is_no_card" optional="show" class="list_ids_width" />
<field name="is_early_card" optional="show" class="list_ids_width" />
<field name="is_late_card" optional="show" class="list_ids_width" />
<field name="absenteeism_card" optional="show" class="list_ids_width" string="旷工" />
<field name="is_abnormal" optional="show" class="list_ids_width" string="异常" />
<field name="abnormal" optional="show" column_invisible="1" />
<field name="work_time_type" optional="hidden" />
<field name="is_approval" optional="hidden" />
<field name="is_month" optional="hidden" />
<field name="is_last_month" optional="hidden" />
<field name="is_warning" optional="hidden" />
<field name="is_check_in_red" column_invisible="1" />
<field name="is_check_out_red" column_invisible="1" />
<button name="button_controls_wizard" type="object" string="操作" class="oe_highlight"
invisible="not (is_abnormal or is_early_card or is_late_card or absenteeism_card or is_no_card)" />
<button name="button_correct_wizard" type="object" string="修正"
class="btn btn-primary"
invisible="not (is_abnormal or is_early_card or is_late_card or absenteeism_card or is_no_card)"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager" />
<button name="revoke" type="object" string="撤销" class="btn btn-primary"
invisible="not (is_check_out_red or is_check_in_red)"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager" />
<button name="button_source" type="object" string="详情" class="oe_highlight"
groups="yuthon_hr_attendance.group_he_attendance_sign_manager" />
</tree>
</field>
</record>
@@ -104,7 +127,8 @@
<field name="arch" type="xml">
<search>
<group expand="0" name="group_by" string="Group By">
<filter string="打卡日期" name="group_by_current_data" domain="[]" context="{'group_by' : 'current_data'}"/>
<filter string="打卡日期" name="group_by_current_data" domain="[]"
context="{'group_by' : 'current_data_month'}" />
</group>
</search>
</field>
@@ -114,12 +138,16 @@
<field name="model">hr.attendance</field>
<field name="arch" type="xml">
<search>
<field name="employee_id"/>
<field name="employee_id" />
<group expand="0" name="group_by" string="Group By">
<filter string="打卡日期" name="group_by_current_data" domain="[]" context="{'group_by' : 'current_data'}"/>
<filter string="公司" name="group_by_company_id" domain="[]" context="{'group_by' : 'company_id'}"/>
<filter string="部门" name="group_by_department_id" domain="[]" context="{'group_by' : 'department_id'}"/>
<filter string="员工" name="group_by_employee_id" domain="[]" context="{'group_by' : 'employee_id'}"/>
<filter string="打卡日期" name="group_by_current_data" domain="[]"
context="{'group_by' : 'current_data_month'}" />
<filter string="公司" name="group_by_company_id" domain="[]"
context="{'group_by' : 'company_id'}" />
<filter string="部门" name="group_by_department_id" domain="[]"
context="{'group_by' : 'department_id'}" />
<filter string="员工" name="group_by_employee_id" domain="[]"
context="{'group_by' : 'employee_id'}" />
</group>
</search>
</field>
@@ -140,58 +168,65 @@
<field name="res_model">hr.attendance</field>
<field name="view_mode">tree,search</field>
<field name="domain">[('employee_id.user_id', '=', uid)]</field>
<field name="search_view_id" ref="view_attendance_going_search"/>
<field name="search_view_id" ref="view_attendance_going_search" />
<field name="context">{'search_default_group_by_current_data': 1}</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_hr_attendance.view_attendance_going_tree3')})]"/>
<field name="view_ids"
eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_hr_attendance.view_attendance_going_tree3')})]" />
</record>
<record id="hr_attendance_my_abnormal" model="ir.actions.act_window">
<field name="name">我的异常</field>
<field name="res_model">hr.attendance</field>
<field name="view_mode">tree,form,search</field>
<field name="domain">[('employee_id.user_id', '=', uid), '|', '|', '|', '|', ('is_abnormal', '=', True), ('is_early_card', '=', True), ('is_late_card', '=', True), ('absenteeism_card', '=', True), ('is_no_card', '=', True)]</field>
<field name="search_view_id" ref="view_attendance_going_search"/>
<field name="domain">[('employee_id.user_id', '=', uid), '|', '|', '|', '|', ('is_abnormal',
'=', True), ('is_early_card', '=', True), ('is_late_card', '=', True),
('absenteeism_card', '=', True), ('is_no_card', '=', True)]</field>
<field name="search_view_id" ref="view_attendance_going_search" />
<field name="context">{'search_default_group_by_current_data': 1}</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_hr_attendance.view_attendance_going_tree3')})]"/>
<field name="view_ids"
eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_hr_attendance.view_attendance_going_tree3')})]" />
</record>
<record id="action_attendance_company_form" model="ir.actions.act_window">
<field name="name">考勤记录</field>
<field name="res_model">hr.attendance</field>
<field name="view_mode">tree</field>
<field name="context">{"search_default_employee": 1}</field>
<field name="search_view_id" ref="view_hr_attendance_search"/>
<field name="search_view_id" ref="view_hr_attendance_search" />
</record>
<record id="action_button_batch_card" model="ir.actions.server">
<field name="name">批量修正</field>
<field name="model_id" ref="yuthon_hr_attendance.model_hr_attendance"/>
<field name="binding_model_id" ref="yuthon_hr_attendance.model_hr_attendance"/>
<field name="model_id" ref="yuthon_hr_attendance.model_hr_attendance" />
<field name="binding_model_id" ref="yuthon_hr_attendance.model_hr_attendance" />
<field name="state">code</field>
<field name="code">action = model.button_batch_correct()</field>
<field name="groups_id" eval="[(4, ref('yuthon_hr_attendance.group_he_attendance_sign_manager'))]"/>
<field name="groups_id"
eval="[(4, ref('yuthon_hr_attendance.group_he_attendance_sign_manager'))]" />
</record>
<record id="action_update_shift" model="ir.actions.server">
<field name="name">批量更新班次</field>
<field name="model_id" ref="yuthon_hr_attendance.model_hr_attendance"/>
<field name="binding_model_id" ref="yuthon_hr_attendance.model_hr_attendance"/>
<field name="model_id" ref="yuthon_hr_attendance.model_hr_attendance" />
<field name="binding_model_id" ref="yuthon_hr_attendance.model_hr_attendance" />
<field name="state">code</field>
<field name="groups_id" eval="[(4, ref('yuthon_hr_attendance.group_he_attendance_sign_manager'))]"/>
<field name="groups_id"
eval="[(4, ref('yuthon_hr_attendance.group_he_attendance_sign_manager'))]" />
</record>
<record id="update_state" model="ir.actions.server">
<field name="name">初始化状态</field>
<field name="model_id" ref="yuthon_hr_attendance.model_hr_attendance"/>
<field name="binding_model_id" ref="yuthon_hr_attendance.model_hr_attendance"/>
<field name="model_id" ref="yuthon_hr_attendance.model_hr_attendance" />
<field name="binding_model_id" ref="yuthon_hr_attendance.model_hr_attendance" />
<field name="state">code</field>
<field name="code">action = model.update_state()</field>
<field name="groups_id" eval="[(4, ref('yuthon_hr_attendance.group_he_attendance_sign_manager'))]"/>
<field name="groups_id"
eval="[(4, ref('yuthon_hr_attendance.group_he_attendance_sign_manager'))]" />
</record>
<record id="action_update_abnormal_shift" model="ir.actions.server">
<field name="name">批量更新班次名称</field>
<field name="model_id" ref="yuthon_hr_attendance.model_hr_attendance"/>
<field name="binding_model_id" ref="yuthon_hr_attendance.model_hr_attendance"/>
<field name="model_id" ref="yuthon_hr_attendance.model_hr_attendance" />
<field name="binding_model_id" ref="yuthon_hr_attendance.model_hr_attendance" />
<field name="state">code</field>
<field name="code">action = model.update_abnormal()</field>
<field name="groups_id" eval="[(4, ref('yuthon_hr_attendance.group_he_attendance_sign_manager'))]"/>
<field name="groups_id"
eval="[(4, ref('yuthon_hr_attendance.group_he_attendance_sign_manager'))]" />
</record>
</odoo>
</odoo>
+122 -89
View File
@@ -4,74 +4,98 @@ from datetime import datetime
class PropertyElectricReading(models.Model):
_name = 'property.electric.reading'
_name = "property.electric.reading"
class PropertyMeterWater(models.Model):
_name = 'property.meter.water'
_description = '水表库'
_order = 'number asc'
_name = "property.meter.water"
_description = "水表库"
_order = "number asc"
name = fields.Char(string='仪表编号')
number = fields.Integer(related='epitope_values_id.sequence', string="序号", store=True)
sequence1 = fields.Integer(string='序号')
unit_number = fields.Char(string='单元编号/用途')
installation_location = fields.Char(string='安装位置')
is_state = fields.Boolean(string='是否启用')
type = fields.Selection([('public', '公共'), ('fees', '收费')], string='类型')
gauge_magnification = fields.Integer(string='仪表倍率', default=1)
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='抄表人')
epitope_values = fields.Char(string='表位置')
epitope_values_id = fields.Many2one('yuthon.epitope.values', string='表位置')
other = fields.Char(string='其他')
unit = fields.Float(string='当月单价', igits=(6, 4))
name = fields.Char(string="仪表编号")
number = fields.Integer(
related="epitope_values_id.sequence", string="序号", store=True
)
sequence1 = fields.Integer(string="序号")
unit_number = fields.Char(string="单元编号/用途")
installation_location = fields.Char(string="安装位置")
is_state = fields.Boolean(string="是否启用")
type = fields.Selection([("public", "公共"), ("fees", "收费")], string="类型")
gauge_magnification = fields.Integer(string="仪表倍率", default=1)
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="抄表人")
epitope_values = fields.Char(string="表位置")
epitope_values_id = fields.Many2one("yuthon.epitope.values", string="表位置")
other = fields.Char(string="其他")
unit = fields.Float(string="当月单价", igits=(6, 4))
start_time = fields.Date(string="安装日期")
end_time = fields.Date(string="拆除日期")
line_loss = fields.Float(string="线损", default=0.05)
last_reading_time = fields.Date(string="最后读数时间")
water_state = fields.Selection([('stop', '停用'), ('use', '使用'), ('rent', '出租')], string='状态', compute='_compute_water_state', store=True)
standards_id = fields.Many2one('yuthon.electricity.setting', string='标准')
yuthon_property_ids = fields.Many2many('yuthon.property', string='物业信息')
line_ids = fields.One2many('meter.water.line', 'water_id', string='水表明细行')
state = fields.Selection([('reading', '已抄'), ('unreading', '未抄')], string='抄表状态', default='unreading')
remarks = fields.Text(string='备注')
water_state = fields.Selection(
[("stop", "停用"), ("use", "使用"), ("rent", "出租")],
string="状态",
compute="_compute_water_state",
store=True,
)
standards_id = fields.Many2one("yuthon.electricity.setting", string="标准")
yuthon_property_ids = fields.Many2many("yuthon.property", string="物业信息")
line_ids = fields.One2many("meter.water.line", "water_id", string="水表明细行")
state = fields.Selection(
[("reading", "已抄"), ("unreading", "未抄")],
string="抄表状态",
default="unreading",
)
remarks = fields.Text(string="备注")
@api.model
def create(self, vals):
record = super().create(vals)
if 'name' in vals and vals['name']:
existing_records = self.search([('name', '=', vals['name'])])
if "name" in vals and vals["name"]:
existing_records = self.search([("name", "=", vals["name"])])
if existing_records:
raise ValidationError(f"仪表编号 '{vals['name']}' 已存在,请使用其他编号!")
record.archives_state = 'rent' if record.lessee_id else 'use'
raise ValidationError(
f"仪表编号 '{vals['name']}' 已存在,请使用其他编号!"
)
record.archives_state = "rent" if record.lessee_id else "use"
return record
def update_epitope_values(self):
meter_ids = self.env["property.meter.water"].browse(
self._context.get('active_ids', self._context.get('active_id')))
self._context.get("active_ids", self._context.get("active_id"))
)
for meter in meter_ids:
for property in meter.yuthon_property_ids:
existing_energy = self.env['yuthon.property.water'].search([
('yuthon_property_id', '=', property.id),
('property_meter_water_id', '=', meter.id)
], limit=1)
existing_energy = self.env["yuthon.property.water"].search(
[
("yuthon_property_id", "=", property.id),
("property_meter_water_id", "=", meter.id),
],
limit=1,
)
if not existing_energy:
self.env['yuthon.property.water'].create({
'property_meter_water_id': meter.id,
'yuthon_property_id': property.id,
})
self.env["yuthon.property.water"].create(
{
"property_meter_water_id": meter.id,
"yuthon_property_id": property.id,
}
)
def open_record(self):
return {
'type': 'ir.actions.act_window',
'res_model': 'property.meter.water',
'res_id': self.id,
'view_mode': 'form',
"type": "ir.actions.act_window",
"res_model": "property.meter.water",
"res_id": self.id,
"view_mode": "form",
}
@api.depends('yuthon_property_ids.lessee_id')
@api.depends("yuthon_property_ids.lessee_id")
def _compute_lessee_id(self):
for record in self:
if record.yuthon_property_ids:
@@ -79,27 +103,28 @@ class PropertyMeterWater(models.Model):
else:
record.lessee_id = False
@api.depends('lessee_id')
@api.depends("lessee_id")
def _compute_water_state(self):
for record in self:
if record.lessee_id:
record.water_state = 'rent'
record.water_state = "rent"
else:
record.water_state = 'use'
record.water_state = "use"
def button_water_state(self):
water_ids = self.env["property.meter.water"].browse(
self._context.get('active_ids', self._context.get('active_id')))
self._context.get("active_ids", self._context.get("active_id"))
)
for record in water_ids:
record.water_state = 'stop'
record.water_state = "stop"
@api.onchange('yuthon_property_ids')
@api.onchange("yuthon_property_ids")
def _onchange_property(self):
if not self.line_ids:
return
sync_vals = {}
if self.yuthon_property_ids:
sync_vals['yuthon_property_ids'] = [(6, 0, self.yuthon_property_ids.ids)]
sync_vals["yuthon_property_ids"] = [(6, 0, self.yuthon_property_ids.ids)]
if sync_vals:
self.line_ids.write(sync_vals)
self.update_epitope_values()
@@ -114,38 +139,49 @@ class PropertyMeterWater(models.Model):
}
def batch_reading_water_push(self):
active_ids = self._context.get('active_ids', [])
active_ids = self._context.get("active_ids", [])
return {
'name': '电水表推送',
'type': 'ir.actions.act_window',
'res_model': 'electric.water.wizard',
'view_mode': 'form',
'view_type': 'form',
'target': 'new',
'context': {
'default_water_ids': [(6, 0, active_ids)]
}
"name": "电水表推送",
"type": "ir.actions.act_window",
"res_model": "electric.water.wizard",
"view_mode": "form",
"view_type": "form",
"target": "new",
"context": {"default_water_ids": [(6, 0, active_ids)]},
}
class MeterWaterLine(models.Model):
_name = 'meter.water.line'
_description = '水表库明细行'
_order = 'number asc, sequence asc'
_name = "meter.water.line"
_description = "水表库明细行"
_order = "number asc, sequence asc"
# name = fields.Char(string='仪表编号')
water_id = fields.Many2one('property.meter.water', string='水表库', required=True, ondelete='cascade')
number = fields.Integer(related='epitope_values_id.sequence', string="序号", store=True)
sequence = fields.Integer(related='water_id.sequence1', string="表序号", store=True)
epitope_values_id = fields.Many2one('yuthon.epitope.values', string='表位置')
lessee_id = fields.Many2one('property.tenant.information', string='承租方')
yuthon_property_ids = fields.Many2many('yuthon.property', string='物业信息')
last_meter = fields.Date(string='上次抄表日期')
last_dosage = fields.Float(string='上次用量', store=True, group_operator=None)
last_read = fields.Float(string='上次读数', store=True, group_operator=None)
meter_date = fields.Date(string='任务日期')
water_id = fields.Many2one(
"property.meter.water", string="水表库", required=True, ondelete="cascade"
)
number = fields.Integer(
related="epitope_values_id.sequence", string="序号", store=True
)
sequence = fields.Integer(related="water_id.sequence1", string="表序号", store=True)
epitope_values_id = fields.Many2one("yuthon.epitope.values", string="表位置")
lessee_id = fields.Many2one("property.tenant.information", string="承租方")
yuthon_property_ids = fields.Many2many("yuthon.property", string="物业信息")
last_meter = fields.Date(string="上次抄表日期", compute="_compute_last")
last_dosage = fields.Float(
string="上次用量", store=True, group_operator=None, compute="_compute_last"
)
last_read = fields.Float(
string="上次读数", store=True, group_operator=None, compute="_compute_last"
)
meter_date = fields.Date(string="任务日期")
current_meter = fields.Float(string='本次用量', store=True, compute="compute_meter_values", group_operator=None)
meter_date_month = fields.Char(
string="任务日期月份",
compute="_compute_meter_date_month",
store=True,
index=True,
)
current_read = fields.Float(string='本次读数', group_operator=None)
unit_number = fields.Char(string='单元编号/用途')
@@ -153,7 +189,7 @@ class MeterWaterLine(models.Model):
receivables = fields.Float(string="应收", compute='_compute_receivables', store=True, group_operator=None, help="应收金额=本次用量*单价*(1+线损)")
gauge_magnification = fields.Integer(string='仪表倍率', readonly=True, default=1, group_operator=None)
last_reading_time = fields.Date(string="最后读数时间")
type = fields.Selection([('public', '公共'), ('fees', '收费')], string='类型')
type = fields.Selection([("public", "公共"), ("fees", "收费")], string="类型")
start_time = fields.Date(string="安装日期")
line_loss = fields.Float(string="线损", default=0.02)
end_time = fields.Date(string="拆除日期")
@@ -176,13 +212,13 @@ class MeterWaterLine(models.Model):
else:
rec.meter_date_month = False
@api.onchange('name')
@api.onchange("name")
def _onchange_line_ids(self):
if self.line_ids:
for line in self.line_ids:
line.name = self.name
@api.depends('line_loss', 'current_meter', 'unit')
@api.depends("line_loss", "current_meter", "unit")
def _compute_receivables(self):
for record in self:
if record.line_loss and record.current_meter and record.unit:
@@ -194,17 +230,13 @@ class MeterWaterLine(models.Model):
def update_meter(self):
current_year = datetime.now().year
target_date = datetime(current_year, 9, 1).date()
domain = [
('meter_date', '<', target_date) # 任务日期在9月之前
]
active_ids = self._context.get('active_ids', self._context.get('active_id'))
domain = [("meter_date", "<", target_date)] # 任务日期在9月之前
active_ids = self._context.get("active_ids", self._context.get("active_id"))
if active_ids:
domain.append(('id', 'in', active_ids))
unread_meter_lines = self.env['meter.water.line'].search(domain)
domain.append(("id", "in", active_ids))
unread_meter_lines = self.env["meter.water.line"].search(domain)
if unread_meter_lines:
unread_meter_lines.write({
'state': 'reading'
})
unread_meter_lines.write({"state": "reading"})
# @api.depends('water_id', 'last_dosage', 'last_read', 'last_meter', 'current_meter', 'current_read',
# 'maximum_count')
@@ -237,7 +269,7 @@ class MeterWaterLine(models.Model):
# i.last_meter = i.meter_date
# i.current_meter = 0
@api.onchange('yuthon_property_ids')
@api.onchange("yuthon_property_ids")
def onchange_lessee_id(self):
for record in self:
if record.yuthon_property_ids:
@@ -247,6 +279,7 @@ class MeterWaterLine(models.Model):
def update_date(self):
meter_ids = self.env["meter.water.line"].browse(
self._context.get('active_ids', self._context.get('active_id')))
self._context.get("active_ids", self._context.get("active_id"))
)
for record in meter_ids:
record.onchange_lessee_id()
record.onchange_lessee_id()
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_property_meter_water_tree" model="ir.ui.view">
<field name="name">property.meter.water.tree</field>
@@ -6,31 +6,33 @@
<field name="arch" type="xml">
<tree string="水表库" decoration-danger="water_state == 'stop'">
<header>
<button class="oe_highlight" name="batch_reading_water_push" string="任务生成" type="object"/>
<button class="oe_highlight" name="button_water_state" string="停用" type="object"/>
<button class="oe_highlight" name="update_epitope_values" string="更新" type="object"/>
<button class="oe_highlight" name="batch_reading_water_push" string="任务生成"
type="object" />
<button class="oe_highlight" name="button_water_state" string="停用" type="object" />
<button class="oe_highlight" name="update_epitope_values" string="更新"
type="object" />
</header>
<field name="sequence1"/>
<field name="number" column_invisible="1"/>
<field name="name" required="1"/>
<field name="epitope_values" column_invisible="1"/>
<field name="epitope_values_id" domain="[('type', '=', 'watermeter')]"/>
<field name="lessee_id" options="{'no_create': True}"/>
<field name="yuthon_property_ids" string="物业信息" widget="many2many_tags"/>
<field name="unit_number"/>
<field name="gauge_magnification" readonly="1"/>
<field name="type"/>
<field name="start_time"/>
<field name="end_time"/>
<field name="water_state"/>
<field name="remarks"/>
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="maximum_count"/>
<button name="open_record" string="详情" type="object" class="oe_highlight"/>
<field name="sequence1" />
<field name="number" column_invisible="1" />
<field name="name" required="1" />
<field name="epitope_values" column_invisible="1" />
<field name="epitope_values_id" domain="[('type', '=', 'watermeter')]" />
<field name="lessee_id" options="{'no_create': True}" />
<field name="yuthon_property_ids" string="物业信息" widget="many2many_tags" />
<field name="unit_number" />
<field name="gauge_magnification" readonly="1" />
<field name="type" />
<field name="start_time" />
<field name="end_time" />
<field name="water_state" />
<field name="remarks" />
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}" />
<field name="maximum_count" />
<button name="open_record" string="详情" type="object" class="oe_highlight" />
</tree>
</field>
</record>
<record id="property_meter_water_form" model="ir.ui.view">
<record id="property_meter_water_form" model="ir.ui.view">
<field name="name">property.meter.water.form</field>
<field name="model">property.meter.water</field>
<field name="arch" type="xml">
@@ -38,50 +40,54 @@
<sheet>
<group col="2">
<group>
<field name="name"/>
<field name="unit_number"/>
<field name="type"/>
<field name="gauge_magnification" readonly="1"/>
<field name="maximum_count"/>
<field name="lessee_id" options="{'no_create': True}"/>
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="epitope_values_id" domain="[('type', '=', 'watermeter')]"/>
<field name="yuthon_property_ids" string="物业信息" widget="many2many_tags"/>
<field name="name" />
<field name="unit_number" />
<field name="type" />
<field name="gauge_magnification" readonly="1" />
<field name="maximum_count" />
<field name="lessee_id" options="{'no_create': True}" />
<field name="employee_ids" widget="many2many_tags"
options="{'no_create': True}" />
<field name="epitope_values_id" domain="[('type', '=', 'watermeter')]" />
<field name="yuthon_property_ids" string="物业信息" widget="many2many_tags" />
</group>
<group>
<field name="start_time"/>
<field name="end_time"/>
<field name="water_state"/>
<field name="remarks"/>
<field name="start_time" />
<field name="end_time" />
<field name="water_state" />
<field name="remarks" />
</group>
</group>
<notebook>
<notebook>
<page string="抄表明细">
<field name="line_ids" string=" ">
<tree editable="bottom">
<field name="water_id"/>
<field name="epitope_values_id" domain="[('type', '=', 'watermeter')]"/>
<field name="lessee_id" options="{'no_create': True}"/>
<field name="yuthon_property_ids" string="物业信息" widget="many2many_tags"/>
<field name="water_id" />
<field name="epitope_values_id"
domain="[('type', '=', 'watermeter')]" />
<field name="lessee_id" options="{'no_create': True}" />
<field name="yuthon_property_ids" string="物业信息"
widget="many2many_tags" />
<field name="last_meter" />
<field name="last_dosage"/>
<field name="last_read" readonly="1"/>
<field name="current_meter" readonly="1"/>
<field name="current_read"/>
<field name="unit_number"/>
<field name="gauge_magnification" readonly="1"/>
<field name="last_reading_time"/>
<field name="type"/>
<field name="start_time"/>
<field name="end_time"/>
<field name="water_state"/>
<field name="remarks"/>
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="maximum_count"/>
<field name="last_dosage" />
<field name="last_read" readonly="1" />
<field name="current_meter" readonly="1" />
<field name="current_read" />
<field name="unit_number" />
<field name="gauge_magnification" readonly="1" />
<field name="last_reading_time" />
<field name="type" />
<field name="start_time" />
<field name="end_time" />
<field name="water_state" />
<field name="remarks" />
<field name="employee_ids" widget="many2many_tags"
options="{'no_create': True}" />
<field name="maximum_count" />
</tree>
</field>
</page>
</notebook>
</notebook>
</sheet>
</form>
</field>
@@ -118,10 +124,10 @@
<field name="model">property.meter.water</field>
<field name="arch" type="xml">
<search string="水表">
<field name="name"/>
<field name="epitope_values_id"/>
<field name="yuthon_property_ids"/>
<field name="lessee_id"/>
<field name="name" />
<field name="epitope_values_id" />
<field name="yuthon_property_ids" />
<field name="lessee_id" />
</search>
</field>
</record>
@@ -130,10 +136,10 @@
<field name="model">meter.water.line</field>
<field name="arch" type="xml">
<search string="水表">
<field name="water_id"/>
<field name="epitope_values_id"/>
<field name="yuthon_property_ids"/>
<field name="lessee_id"/>
<field name="water_id" />
<field name="epitope_values_id" />
<field name="yuthon_property_ids" />
<field name="lessee_id" />
<group expand="0" string="Group By">
<filter string="按月" name="group_by_meter_date_month" domain="[]" context="{'group_by': 'meter_date_month'}"/>
</group>
@@ -144,23 +150,26 @@
<field name="name">水表库</field>
<field name="res_model">property.meter.water</field>
<field name="view_mode">tree,form,search</field>
<field name="view_ids" eval="[(5, 0, 0),
<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')})]"/>
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_water_form')})]" />
</record>
<record id="action_property_meter_water1" model="ir.actions.act_window">
<field name="name">水表任务</field>
<field name="res_model">meter.water.line</field>
<field name="view_mode">tree,search</field>
<field name="context">{'search_default_group_by_meter_date_month': True, 'search_default_group_by_name': True}</field>
<field name="search_view_id" ref="yuthon_property.meter_water_line_search"/>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_meter_water_line_tree')})]"/>
<field name="context">{'search_default_group_by_meter_date_month': True,
'search_default_group_by_name': True}</field>
<field name="search_view_id" ref="yuthon_property.meter_water_line_search" />
<field name="view_ids"
eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_meter_water_line_tree')})]" />
</record>
<record id="all_data_meter_water_property" model="ir.actions.server">
<field name="name">导入水表</field>
<field name="model_id" ref="yuthon_property.model_property_meter_water"/>
<field name="binding_model_id" ref="yuthon_property.model_property_meter_water"/>
<field name="model_id" ref="yuthon_property.model_property_meter_water" />
<field name="binding_model_id" ref="yuthon_property.model_property_meter_water" />
<field name="state">code</field>
<field name="code">action = model.action_property_water_wizard()</field>
</record>