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:
@@ -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),
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user