chore(gzth): 天投云 Odoo 17 定制补丁汇总

本提交汇总生产环境长期定制改动(2024-11 ~ 2026-05):

- web 前端:list/calendar/form 状态指示/确认框/选择创建对话框等交互与中文化调整

- mail:mail_thread、models 定制(含多公司/权限相关处理)

- hr / hr_attendance:考勤与员工逻辑定制

- odoo/addons/base:ir_rule 记录规则调整

- bus/websocket、maintenance 视图、purchase_stock manifest、hr_contract zh_CN 翻译
This commit is contained in:
pengyuthon
2026-09-29 16:35:05 +08:00
parent e8f8c9b428
commit 6b5469a4f0
17 changed files with 41 additions and 42 deletions
+1
View File
@@ -834,6 +834,7 @@ class WebsocketConnectionHandler:
try:
response = cls._get_handshake_response(request.httprequest.headers)
socket = request.httprequest._HTTPRequest__environ['socket']
# socket = request.httprequest._HTTPRequest__environ['werkzeug.socket']
session, db, httprequest = request.session, request.db, request.httprequest
response.call_on_close(lambda: cls._serve_forever(
Websocket(socket, session),
+1 -1
View File
@@ -80,7 +80,7 @@ class HrEmployeePrivate(models.Model):
birthday = fields.Date('Date of Birth', groups="hr.group_hr_user", tracking=True)
ssnid = fields.Char('SSN No', help='Social Security Number', groups="hr.group_hr_user", tracking=True)
sinid = fields.Char('SIN No', help='Social Insurance Number', groups="hr.group_hr_user", tracking=True)
identification_id = fields.Char(string='Identification No', groups="hr.group_hr_user", tracking=True)
identification_id = fields.Char(string='Identification No', tracking=True)
passport_id = fields.Char('Passport No', groups="hr.group_hr_user", tracking=True)
bank_account_id = fields.Many2one(
'res.partner.bank', 'Bank Account Number',
+1 -1
View File
@@ -32,7 +32,7 @@ class HrAttendance(models.Model):
employee_id = fields.Many2one('hr.employee', string="Employee", default=_default_employee, required=True, ondelete='cascade', index=True)
department_id = fields.Many2one('hr.department', string="Department", related="employee_id.department_id",
readonly=True)
check_in = fields.Datetime(string="Check In", default=fields.Datetime.now, required=True, tracking=True)
check_in = fields.Datetime(string="Check In", tracking=True)
check_out = fields.Datetime(string="Check Out", tracking=True)
worked_hours = fields.Float(string='Worked Hours', compute='_compute_worked_hours', store=True, readonly=True)
color = fields.Integer(compute='_compute_color')
+7 -7
View File
@@ -98,13 +98,13 @@ class HrEmployee(models.Model):
end_tz = now_tz
end_naive = end_tz.astimezone(pytz.utc).replace(tzinfo=None)
hours = sum(
att.worked_hours or 0
for att in employee.attendance_ids.filtered(
lambda att: att.check_in >= start_naive and att.check_out and att.check_out <= end_naive
)
)
# hours = sum(
# att.worked_hours or 0
# for att in employee.attendance_ids.filtered(
# lambda att: att.check_in >= start_naive and att.check_out and att.check_out <= end_naive
# )
# )
hours = 0
employee.hours_last_month = round(hours, 2)
employee.hours_last_month_display = "%g" % employee.hours_last_month
+1 -1
View File
@@ -246,7 +246,7 @@ msgstr "日历不匹配: 员工日历与合约日历不匹配。可能会导
#: model:ir.model.fields.selection,name:hr_contract.selection__hr_contract__state__cancel
#: model:ir.model.fields.selection,name:hr_contract.selection__hr_contract_history__state__cancel
msgid "Cancelled"
msgstr "已取消"
msgstr "已中止"
#. module: hr_contract
#: model:ir.model,name:hr_contract.model_res_company
+1 -1
View File
@@ -4422,7 +4422,7 @@ class MailThread(models.AbstractModel):
payload = self._notify_by_web_push_prepare_payload(message, msg_vals=msg_vals)
payload = self._truncate_payload(payload)
if len(devices) < MAX_DIRECT_PUSH:
if False:
session = Session()
devices_to_unlink = set()
for device in devices:
+9 -7
View File
@@ -176,13 +176,15 @@ class BaseModel(models.AbstractModel):
def _mail_track_get_field_sequence(self, fname):
""" Find tracking sequence of a given field, given their name. Current
parameter 'tracking' should be an integer, but attributes with True
are still supported; old naming 'track_sequence' also. """
sequence = getattr(
self._fields[fname], 'tracking',
getattr(self._fields[fname], 'track_sequence', 100)
)
if sequence is True:
sequence = 100
are still supported; old naming 'track_sequence' also.
此文件因物业水电表日期字段报keyerror做屏蔽暂时解决
"""
# sequence = getattr(
# self._fields[fname], 'tracking',
# getattr(self._fields[fname], 'track_sequence', 100)
# )
# if sequence is True:
sequence = 100
return sequence
def _message_get_default_recipients(self):
@@ -569,8 +569,7 @@
<field name="arch" type="xml">
<search string="Search">
<field string="Equipment" name="name" filter_domain="[
'|', '|', '|',
('name', 'ilike', self), ('model', 'ilike', self), ('serial_no', 'ilike', self), ('partner_ref', 'ilike', self)]"/>
'|', '|', '|', ('name', 'ilike', self), ('model', 'ilike', self), ('serial_no', 'ilike', self), ('partner_ref', 'ilike', self)]"/>
<field string="Category" name="category_id"/>
<field name="owner_user_id"/>
<filter string="My Equipment" name="my" domain="[('owner_user_id', '=', uid)]"/>
@@ -599,9 +598,9 @@
</search>
</field>
</record>
<record id="hr_equipment_action" model="ir.actions.act_window">
<field name="name">Equipment</field>
<field name="name">固定资产</field>
<field name="res_model">maintenance.equipment</field>
<field name="view_mode">kanban,tree,form</field>
<field name="view_id" ref="hr_equipment_view_kanban"/>
+1 -1
View File
@@ -10,7 +10,7 @@
'depends': ['stock_account', 'purchase'],
'data': [
'security/ir.model.access.csv',
'data/purchase_stock_data.xml',
# 'data/purchase_stock_data.xml',
'data/mail_templates.xml',
'report/vendor_delay_report.xml',
'views/purchase_views.xml',
@@ -7,10 +7,7 @@ import { useChildRef } from "@web/core/utils/hooks";
import { Component } from "@odoo/owl";
export const deleteConfirmationMessage = _t(
`Ready to make your record disappear into thin air? Are you sure?
It will be gone forever!
Think twice before you click that 'Delete' button!`
`你确定要删除吗?`
);
export class ConfirmationDialog extends Component {
@@ -323,7 +323,7 @@ export class CalendarController extends Component {
}
deleteRecord(record) {
this.displayDialog(ConfirmationDialog, {
title: _t("Bye-bye, record!"),
title: _t(" "),
body: deleteConfirmationMessage,
confirm: () => {
this.model.unlinkRecord(record.id);
@@ -10,20 +10,22 @@
data-hotkey="s"
t-on-click.stop="save"
data-tooltip="Save manually"
aria-label="Save manually"
style="background-color: blue !important;color: white !important;"
aria-label="Save manually"
t-ref="save">
<i class="fa fa-cloud-upload fa-fw" />
<i class="fa fa-cloud-upload fa-fw"/>
</button>
<button
type="button"
class="o_form_button_cancel btn btn-light px-1 py-0 lh-sm"
data-hotkey="j"
t-on-click.stop="discard"
data-tooltip="Discard changes"
data-tooltip="Discard changes"
style="background-color: red !important;color: white !important;"
aria-label="Discard changes">
<i class="fa fa-undo fa-fw" />
</button>
</div>
<i class="fa fa-undo fa-fw"/>
</button>
</div>
<span
t-if="!props.model.root.isNew and indicatorMode === 'invalid'"
class="text-danger small ms-2"
@@ -536,7 +536,7 @@ export class ListController extends Component {
body = _t("Are you sure you want to delete these records?");
}
return {
title: _t("Bye-bye, record!"),
title: _t(" "),
body,
confirmLabel: _t("Delete"),
confirm: () => this.model.root.deleteRecords(),
@@ -983,7 +983,7 @@ export class ListRenderer extends Component {
}
get getEmptyRowIds() {
let nbEmptyRow = Math.max(0, 4 - this.props.list.records.length);
let nbEmptyRow = Math.max(0, 1 - this.props.list.records.length);
if (nbEmptyRow > 0 && this.displayRowCreates) {
nbEmptyRow -= 1;
}
@@ -21,7 +21,7 @@ export class SelectCreateDialog extends Component {
const translatedText = _t("No records found!");
this.busy = false; // flag used to ensure we only call once the onSelected/onUnselect props
this.baseViewProps = {
display: { searchPanel: false },
display: { searchPanel: true },
editable: false, // readonly
noBreadcrumbs: true,
noContentHelp: markup(`<p>${escape(translatedText)}</p>`),
@@ -939,7 +939,7 @@ QUnit.module("Views", ({ beforeEach }) => {
await click(target, ".o_cw_popover_delete");
assert.strictEqual(
target.querySelector(".modal-title").textContent,
"Bye-bye, record!",
" ",
"should display the confirm message"
);
+2 -4
View File
@@ -212,12 +212,10 @@ class IrRule(models.Model):
'unlink': _("unlink"),
}
user_description = f"{self.env.user.name} (id={self.env.user.id})"
operation_error = _("Uh-oh! Looks like you have stumbled upon some top-secret records.\n\n" \
"Sorry, %s doesn't have '%s' access to:", user_description, operations[operation])
operation_error = "对不起,你没有访问的权限: %s", user_description
failing_model = _("- %s (%s)", description, model)
resolution_info = _("If you really, really need access, perhaps you can win over your friendly administrator with a batch of freshly baked cookies.")
resolution_info = ""
if not self.user_has_groups('base.group_no_one') or not self.env.user.has_group('base.group_user'):
records.invalidate_recordset()
return AccessError(f"{operation_error}\n{failing_model}\n\n{resolution_info}")