From 7a5c7b42a22882289c6954a96e7dd7a5f3cbe92a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?=
<9664676+pengyuthon@user.noreply.gitee.com>
Date: Fri, 5 Dec 2025 18:18:41 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yuthon_catering/__init__.py | 1 +
yuthon_catering/__manifest__.py | 13 +--
yuthon_catering/data/catering_time.xml | 18 ++++
yuthon_catering/models/__init__.py | 1 +
yuthon_catering/models/yuthon_catering.py | 66 +++++++++++++++
.../models/yuthon_catering_setting.py | 31 +++++++
yuthon_catering/security/ir.model.access.csv | 3 +-
.../static/description/catering.png | Bin 0 -> 4057 bytes
.../static/src/js/catering_create_button.js | 28 +++++++
.../src/xml/catering_create_button_views.xml | 10 +++
yuthon_catering/views/root_menu.xml | 4 +-
yuthon_catering/views/yuthon_catering.xml | 3 +-
.../views/yuthon_catering_conf.xml | 2 +-
.../views/yuthon_catering_setting_views.xml | 48 +++++++++++
yuthon_catering/wizard/__init__.py | 1 +
.../wizard/catering_reinstated_wizard.py | 78 ++++++++++++++++++
.../catering_reinstated_wizard_views.xml | 20 +++++
17 files changed, 318 insertions(+), 9 deletions(-)
create mode 100644 yuthon_catering/data/catering_time.xml
create mode 100644 yuthon_catering/models/yuthon_catering_setting.py
create mode 100644 yuthon_catering/static/description/catering.png
create mode 100644 yuthon_catering/static/src/js/catering_create_button.js
create mode 100644 yuthon_catering/static/src/xml/catering_create_button_views.xml
create mode 100644 yuthon_catering/views/yuthon_catering_setting_views.xml
create mode 100644 yuthon_catering/wizard/__init__.py
create mode 100644 yuthon_catering/wizard/catering_reinstated_wizard.py
create mode 100644 yuthon_catering/wizard/catering_reinstated_wizard_views.xml
diff --git a/yuthon_catering/__init__.py b/yuthon_catering/__init__.py
index 9e5827f90..cec04a5b0 100644
--- a/yuthon_catering/__init__.py
+++ b/yuthon_catering/__init__.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import controllers
from . import models
+from . import wizard
diff --git a/yuthon_catering/__manifest__.py b/yuthon_catering/__manifest__.py
index 7b4de9300..dced2d36e 100644
--- a/yuthon_catering/__manifest__.py
+++ b/yuthon_catering/__manifest__.py
@@ -13,12 +13,15 @@
'views/root_menu.xml',
'views/yuthon_catering.xml',
'views/yuthon_catering_conf.xml',
+ 'views/yuthon_catering_setting_views.xml',
+ 'wizard/catering_reinstated_wizard_views.xml',
'templates/templates.xml',
],
- # 'assets': {
- # 'web.assets_backend': [
- # 'base_dynamic_columns/static/src/**/*',
- # ],
- # },
+ 'assets': {
+ 'web.assets_backend': [
+ 'yuthon_catering/static/src/js/catering_create_button.js',
+ 'yuthon_catering/static/src/xml/catering_create_button_views.xml',
+ ],
+ },
}
diff --git a/yuthon_catering/data/catering_time.xml b/yuthon_catering/data/catering_time.xml
new file mode 100644
index 000000000..69c1d6ecd
--- /dev/null
+++ b/yuthon_catering/data/catering_time.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ 定时任务:餐饮收集任务
+
+ code
+ model.catering_task()
+
+
+ 1
+ weeks
+
+ -1
+
+
+
+
\ No newline at end of file
diff --git a/yuthon_catering/models/__init__.py b/yuthon_catering/models/__init__.py
index 5b6692f79..4bfbd4d62 100644
--- a/yuthon_catering/models/__init__.py
+++ b/yuthon_catering/models/__init__.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import yuthon_catering
from . import yuthon_catering_conf
+from . import yuthon_catering_setting
diff --git a/yuthon_catering/models/yuthon_catering.py b/yuthon_catering/models/yuthon_catering.py
index 3da727d34..499a6259f 100644
--- a/yuthon_catering/models/yuthon_catering.py
+++ b/yuthon_catering/models/yuthon_catering.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models
+from datetime import datetime, timedelta
class YuthonCatering(models.Model):
@@ -9,9 +10,74 @@ class YuthonCatering(models.Model):
employee_id = fields.Many2one(string='员工', comodel_name='hr.employee', required=True)
breakfast = fields.Boolean(string='早餐', default=False)
lunch = fields.Boolean(string='午餐', default=False)
+ menu_day_id = fields.Many2one('yuthon.menu.setting', string='菜单')
amount = fields.Float(string='结算费用')
note = fields.Text(string='备注')
+ def catering_task(self):
+ conf_records = self.env['yuthon.catering.conf'].search([])
+ employee_ids = conf_records.mapped('employee_ids')
+
+ today = fields.Date.today()
+ date_list = [today + timedelta(days=i) for i in range(7)]
+
+ create_vals = []
+ for employee in employee_ids:
+ for target_date in date_list:
+ if not self._process_shift_days(target_date):
+ continue
+ existing = self.search([
+ ('employee_id', '=', employee.id),
+ ('date', '=', target_date)
+ ], limit=1)
+
+ menu_record = self.env['yuthon.menu.setting'].search([
+ ('menu_date', '=', target_date)
+ ], limit=1)
+
+ if not existing:
+ create_vals.append({
+ 'menu_record': menu_record.id if menu_record else False,
+ 'date': target_date,
+ 'employee_id': employee.id,
+ })
+
+ if create_vals:
+ self.create(create_vals)
+
+ def _process_shift_days(self, current_data):
+ """
+ 判断是否是工作日 调休日 双休
+ """
+ calendar_leaves = self.env['resource.calendar.leaves'].search([("calendar_id", "=", "行政考勤工作日")])
+ holidays_list = []
+ shift_days_list = []
+ # 提取节假日和调休日
+ for leave in calendar_leaves:
+ if leave.holidays:
+ holidays_list.extend(leave.holidays.split(','))
+ if leave.alternate_rest_day:
+ holidays_list.extend(leave.alternate_rest_day.split(','))
+ if leave.shift_replacement_day:
+ shift_days_list.extend(leave.shift_replacement_day.split(','))
+
+ holidays_list = [self.remove_leading_zero(date) for date in holidays_list]
+ shift_days_list = [self.remove_leading_zero(date) for date in shift_days_list]
+ result = f"{current_data.month}月{current_data.day}日"
+ if result in shift_days_list:
+ return True
+ elif (result in holidays_list) or (current_data.weekday() in [5, 6]):
+ return False
+ else:
+ return True
+
+ def remove_leading_zero(self, date_str):
+ month, day = date_str.split('月')
+ month = month.lstrip('0')
+ day = day.rstrip('日')
+ day = day.lstrip('0')
+ return f'{month}月{day}日'
+
def cron_send_message(self):
url = f'/catering/portal/'
diff --git a/yuthon_catering/models/yuthon_catering_setting.py b/yuthon_catering/models/yuthon_catering_setting.py
new file mode 100644
index 000000000..600d8fa90
--- /dev/null
+++ b/yuthon_catering/models/yuthon_catering_setting.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+from odoo import api, fields, models
+
+class YuthonMenuSetting(models.Model):
+ _name = 'yuthon.menu.setting'
+ _description = '菜单配置'
+
+ breakfast_ids = fields.Many2many(
+ 'yuthon.dishes.setting',
+ 'yuthon_menu_breakfast_rel',
+ 'menu_id',
+ 'dish_id',
+ string='早餐'
+ )
+ lunch_ids = fields.Many2many(
+ 'yuthon.dishes.setting',
+ 'yuthon_menu_lunch_rel',
+ 'menu_id',
+ 'dish_id',
+ string='午餐'
+ )
+ menu_date = fields.Date(string='日期')
+
+
+class YuthonDishesSetting(models.Model):
+ _name = 'yuthon.dishes.setting'
+ _description = '菜品配置'
+
+ name = fields.Char(string='菜品名称')
+ dishes_type = fields.Selection([('breakfast', '早餐'), ('lunch', '午餐')], string='菜品类型')
+
diff --git a/yuthon_catering/security/ir.model.access.csv b/yuthon_catering/security/ir.model.access.csv
index 70698e012..9f46bc0f4 100644
--- a/yuthon_catering/security/ir.model.access.csv
+++ b/yuthon_catering/security/ir.model.access.csv
@@ -1,6 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_yuthon_catering,yuthon_catering,model_yuthon_catering,,1,1,1,1
access_yuthon_catering_conf,yuthon_catering_conf,model_yuthon_catering_conf,,1,1,1,1
-
+access_yuthon_menu_setting,yuthon_menu_setting,model_yuthon_menu_setting,,1,1,1,1
+access_yuthon_dishes_setting,yuthon_dishes_setting,model_yuthon_dishes_setting,,1,1,1,1
diff --git a/yuthon_catering/static/description/catering.png b/yuthon_catering/static/description/catering.png
new file mode 100644
index 0000000000000000000000000000000000000000..df91e0b7a590afd6b826966d34e33676e2b5d71c
GIT binary patch
literal 4057
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
+
+
+
+
+
+
+
diff --git a/yuthon_catering/views/root_menu.xml b/yuthon_catering/views/root_menu.xml
index f8e1397ae..90058bb5d 100644
--- a/yuthon_catering/views/root_menu.xml
+++ b/yuthon_catering/views/root_menu.xml
@@ -1,7 +1,9 @@
-
diff --git a/yuthon_catering/views/yuthon_catering.xml b/yuthon_catering/views/yuthon_catering.xml
index 81414145c..9a008e1ee 100644
--- a/yuthon_catering/views/yuthon_catering.xml
+++ b/yuthon_catering/views/yuthon_catering.xml
@@ -6,12 +6,13 @@
yuthon.catering.tree
yuthon.catering
-
+
+
diff --git a/yuthon_catering/views/yuthon_catering_conf.xml b/yuthon_catering/views/yuthon_catering_conf.xml
index 51718b2e2..57fdcbe36 100644
--- a/yuthon_catering/views/yuthon_catering_conf.xml
+++ b/yuthon_catering/views/yuthon_catering_conf.xml
@@ -8,7 +8,7 @@
-
+
diff --git a/yuthon_catering/views/yuthon_catering_setting_views.xml b/yuthon_catering/views/yuthon_catering_setting_views.xml
new file mode 100644
index 000000000..913a424e6
--- /dev/null
+++ b/yuthon_catering/views/yuthon_catering_setting_views.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+ yuthon_dishes_setting_tree
+ yuthon.dishes.setting
+
+
+
+
+
+
+
+
+ 菜品配置
+ yuthon.dishes.setting
+ tree
+
+
+
+
+
+
diff --git a/yuthon_catering/wizard/__init__.py b/yuthon_catering/wizard/__init__.py
new file mode 100644
index 000000000..26d2cb010
--- /dev/null
+++ b/yuthon_catering/wizard/__init__.py
@@ -0,0 +1 @@
+from . import catering_reinstated_wizard
\ No newline at end of file
diff --git a/yuthon_catering/wizard/catering_reinstated_wizard.py b/yuthon_catering/wizard/catering_reinstated_wizard.py
new file mode 100644
index 000000000..e5a4efe18
--- /dev/null
+++ b/yuthon_catering/wizard/catering_reinstated_wizard.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+from odoo import api, fields, models
+from datetime import datetime, timedelta, time
+
+
+class CateringReinstatedWizard(models.TransientModel):
+ _name = 'catering.reinstated.wizard'
+ _description = '复用弹窗'
+
+ def confirm(self):
+ today = fields.Date.today()
+ date_list = []
+ for i in range(7):
+ target_date = today + timedelta(days=i)
+ if self._process_shift_days(target_date):
+ date_list.append(target_date)
+
+ for current_date in date_list:
+ # 上周对应日期
+ last_week_date = current_date - timedelta(days=7)
+
+ # 查找上周该日期的所有餐饮记录
+ last_week_records = self.env['yuthon.catering'].search([
+ ('date', '=', last_week_date)
+ ])
+ if not last_week_records:
+ continue
+
+ for last_record in last_week_records:
+ existing = self.env['yuthon.catering'].search([
+ ('employee_id', '=', last_record.employee_id.id),
+ ('date', '=', current_date)
+ ], limit=1)
+ if existing:
+ continue
+
+ self.env['yuthon.catering'].create({
+ 'date': current_date,
+ 'employee_id': last_record.employee_id.id,
+ 'breakfast': last_record.breakfast,
+ 'lunch': last_record.lunch,
+ 'menu_day_id': last_record.menu_day_id.id,
+ 'amount': last_record.amount,
+ 'note': last_record.note,
+ })
+
+ def _process_shift_days(self, current_data):
+ """
+ 判断是否是工作日 调休日 双休
+ """
+ calendar_leaves = self.env['resource.calendar.leaves'].search([("calendar_id", "=", "行政考勤工作日")])
+ holidays_list = []
+ shift_days_list = []
+ # 提取节假日和调休日
+ for leave in calendar_leaves:
+ if leave.holidays:
+ holidays_list.extend(leave.holidays.split(','))
+ if leave.alternate_rest_day:
+ holidays_list.extend(leave.alternate_rest_day.split(','))
+ if leave.shift_replacement_day:
+ shift_days_list.extend(leave.shift_replacement_day.split(','))
+
+ holidays_list = [self.remove_leading_zero(date) for date in holidays_list]
+ shift_days_list = [self.remove_leading_zero(date) for date in shift_days_list]
+ result = f"{current_data.month}月{current_data.day}日"
+ if result in shift_days_list:
+ return True
+ elif (result in holidays_list) or (current_data.weekday() in [5, 6]):
+ return False
+ else:
+ return True
+
+ def remove_leading_zero(self, date_str):
+ month, day = date_str.split('月')
+ month = month.lstrip('0')
+ day = day.rstrip('日')
+ day = day.lstrip('0')
+ return f'{month}月{day}日'
\ No newline at end of file
diff --git a/yuthon_catering/wizard/catering_reinstated_wizard_views.xml b/yuthon_catering/wizard/catering_reinstated_wizard_views.xml
new file mode 100644
index 000000000..335a0ce58
--- /dev/null
+++ b/yuthon_catering/wizard/catering_reinstated_wizard_views.xml
@@ -0,0 +1,20 @@
+
+
+
+ catering.reinstated.wizard.form
+ catering.reinstated.wizard
+
+
+
+
+
\ No newline at end of file