预约单子模块数据来自预约单,指定教练和预约日,列出该教练当日所有预约信息。关键字是教练编号+年月日,这个子模块,采用按钮生成方式,在窗口放两个按钮。
一个是基于教练生成,即选择教练和日期后,点击按钮,则自动生成该教练和该日所有预约单数据。
一个是基于日期生成,即选择日期后,点击按钮,则自动生成所有教练该日所有预约单数据。
预约情况 |
学员姓名: | 王小二 | 手机: | 1862323 |
日期: | 2020/8/1 | 预约时间: | 7:00:00 | 至 | 9:59:59 |
排档类: | 手动挡 | 金额: | 220 | 自备车: | 否 |
上车地点: | 上海浦东大道1111号 |
下车地点: | 上海浦东大道22号 |
教练员 |
工号: | 001 | 姓名: | 顾总 |
车辆信息 |
车牌: | 沪A7868 | 类型: | 桑塔纳3000型 |
实际情况 |
学员姓名: | 王小二 | 手机: | 1862323 |
日期: | 2020/8/1 | 陪驾时间: | 7:00:00 | 至 | 9:59:59 |
排档类: | 手动挡 | 金额: | 220 | 自备车: | 否 |
上车地点: | 上海浦东大道1111号 |
下车地点: | 上海浦东大道22号 |
教练员 |
工号: | 001 | 姓名: | 顾总 |
车辆信息 |
车牌: | 沪99900A | 类型: | 桑塔纳3000型 |
新增数据模型
在models目录下新增teacher_appointment.py数据模型文件,代码如下:
from datetime import datetime, timedeltafrom odoo import fields, models, apiclass NebulaTeacherAppointment(models.Model): _name = "nebula.teacher.appointment" _description = "教练预约单" _order = 'name' active = fields.Boolean('是否有效', default=True) name = fields.Char('教练预约简情', size=30) appointment_date = fields.Date('预约日期', default=datetime.now()) teacher_name = fields.Many2one('nebula.teacher', string='教练工号', size=8, required=True, domain=[('active', '=', True)]) teacher_full_name = fields.Char('教练姓名', related='teacher_name.full_name', store=True) teacher_mobile = fields.Char('教练手机', related='teacher_name.mobile') date_docket = fields.Datetime('单据日期', required=True, readonly=True, index=True, default=fields.Datetime.now) user_id = fields.Many2one('res.users', string='创建人', index=True, readonly=True, default=lambda self: self.env.user) order_line = fields.One2many('nebula.teacher.appointment.line', 'order_id', string='预约明细', auto_join=True) _sql_constraints = [('check_uniq_name', 'unique(appointment_date, name)', '一个教练一个日期预约单')]class TeacherAppointmentLine(models.Model): _name = 'nebula.teacher.appointment.line' _description = '预约明细' @api.model def _select_car_type(self): records = self.env['nebula.car.type'].search([('active', '=', True)]) return [(r.name, r.name_type) for r in records] def _select_auto_type(self): records = self.env['nebula.auto.type'].search([]) return [(r.name, r.name_type) for r in records] @api.depends('complete_begin_date', 'complete_hours') def _calc_complete_end_date(self): for order in self: if order.complete_begin_date: begin_date = order.complete_begin_date add_hours = order.complete_hours begin_date += timedelta(hours=add_hours) begin_date -= timedelta(seconds=1) order.complete_end_date = begin_date order_id = fields.Many2one('nebula.teacher.appointment', string='预约明细行', required=True, ondelete='cascade', index=True, copy=False) appointment_name = fields.Many2one('nebula.appointment', string='预约单', index=True, size=8) auto_type_name = fields.Selection(selection='_select_auto_type', string='预约类型', related='appointment_name.auto_type_name') student_name = fields.Many2one('nebula.student', string='学员姓名', index=True, size=8, related='appointment_name.student_name')student_mobile = fields.Char('学员手机', related='student_name.mobile')appointment_begin_date = fields.Datetime('预约起始时间', related='appointment_name.appointment_begin_date') appointment_hours = fields.Integer('预约小时数', related='appointment_name.appointment_hours') appointment_end_date = fields.Datetime('预约结束时间', related='appointment_name.appointment_end_date') complete_begin_date = fields.Datetime('实际起始时间') complete_hours = fields.Integer('实际小时数') complete_end_date = fields.Datetime('实际结束时间', compute='_calc_complete_end_date', store=True) car_name = fields.Many2one('nebula.car', string='车辆编号', size=8, related='appointment_name.car_name', domain=[('active', '=', True)]) license_plate = fields.Char('车辆牌照', related='car_name.license_plate') car_type_name = fields.Selection(selection='_select_car_type', string='车型', related='car_name.car_type_name') car_auto_type_name = fields.Selection(selection='_select_auto_type', string='排挡类', related='car_name.auto_type_name') work_charge_hour = fields.Integer('每小时收费', related='appointment_name.work_charge_hour') deposit = fields.Integer('学员缴费') begin_address = fields.Text('上车地点', related='appointment_name.begin_address') end_address = fields.Text('下车地点', related='appointment_name.end_address')note = fields.Text('备注', related='appointment_name.note')
将teacher_appointment.py添加到 models目录下__init__.py文件中import部分,以逗号分割文件名。
设置访问权限
在peijia->security目录下的ir.model.access.csv新增权限的记录。
access_nebula_teacher_appointment,nebula.teacher.appointment,model_nebula_teacher_appointment,group_peijia_manager,1,1,1,1access_nebula_teacher_appointment_line,nebula.teacher.appointment.line,model_nebula_teacher_appointment_line,group_peijia_manager,1,1,1,1
创建视图层
在views目录新增teacher_appointment_views.xml文件,暂时以默认方式展示即可,内容如下:
<?xml version="1.0" encoding="utf-8"?><odoo> <act_windowid="peijia.action_teacher_appointment_views"name="教练预约单" res_model="nebula.teacher.appointment" view_mode="tree,form" /></odoo>
修改peijia目录下__manifest__.py文件中data段,在views/car_views.xml前新增views/teacher_appointment_views文件名。
在views目录下car_views.xml文件,教练管理下添加菜单指向该子模块,代码:
<menuitem id="menu_teacher_appointment_views" name="教练预约单" parent="menu_peijia_teacher_data" sequence="105" action="peijia.action_teacher_appointment_views"/>
全部修改完成后,重启odoo服务。
name自动赋值
name设计为教练编号+日期,当教练编号和日期变化后,name自动变化。
我们在数据模型NebulaTeacherAppointment添加以下代码。
@api.onchange('teacher_name', 'appointment_date')def _onchange_name_date(self): if self.appointment_date and self.teacher_name: self.name = _make_name(self.appointment_date, self.teacher_name.name, self.teacher_name.full_name)
公用函数:
def _make_name(appointment_date, teacher_name, teacher_full_name): # 传入参数,生成默认name order_date = appointment_date + timedelta(hours=8) order_text = datetime.strftime(order_date, "%Y-%m-%d") record_name = '(' + teacher_name + ')' record_name += teacher_full_name + '^' record_name += order_text return record_name
效果见下图,当教练工号和预约日期变化时自动更新教练预约简情字段。
完善视图
将teacher_appointment_views.xml文件中act_window段删除,参考费用管理子模块修改,请读者自行完成,如果完成不了,最好从头再学!
另外为子表在views目录下新建视图文件teacher_appointment_lines_views.xml,并将该文件名添加到__manifest__.py的data段里面。该代码也请参考费用管理子模块修改。
子模块完整代码会在本节未贴出。
另外,演示了如何在视图中屏蔽教练、学员、陪驾单号的新建、修改等功能。关键点是添加options参数,如下例:
<fieldname="teacher_name"options="{'no_open': True, 'no_create': True}"/>
生成预约单按钮事件
我们在teacher_appointment_views.xml视图文件中添加按钮。
<form string="表单"> <header> <button name="button_teacher_time" type="object" string="指定教练本日预约" class="oe_highlight"/> <button name="button_all_time" type="object" string="所有教练本日预约" class="oe_highlight"/> </header>
在teacher_appointment.py数据模型文件中NebulaTeacherAppointment类最末段添加按钮事件。
def button_teacher_time(self): """ 功能: 生成指定教练本日预约。 从传入的self中得到指定的教练和日期,删除旧子表,再创建新子表 1.调插入子表程序 """ # todo : 应该有时间判断,大于等于今天才能操作,未写 _insert_line_table(self, self.id, self.appointment_date, self.teacher_name.id)def button_all_time(self): # 所有教练本日预约 # todo : 应该有时间判断,大于等于今天才能操作,未写 """ 功能: 生成所有教练本日预约。 从传入的self中得到指定的教练和日期,生成所有教练本日预约单。 1. 删除除了传入的教练外所有本日主表,子表会自动删除删除。 2.查找本日预约表中所有教练,循环生成除了本教练外主表, 3.查找教练预约表本日所有记录,遍历生成子表 """ # 1. 删除除了传入的教练外所有本日主表,子表会自动删除删除。 appointment_date = self.appointment_date domain = [('teacher_name', '!=', self.teacher_name.id), # 教练排除 ('active', '=', True), ('appointment_date', '=', appointment_date)] self.env['nebula.teacher.appointment'].search(domain).unlink() # 2. 查找本日预约表中所有教练,生成主表 begin_time = datetime.strftime(appointment_date, "%Y-%m-%d 00:00:00") end_time = datetime.strftime(appointment_date, "%Y-%m-%d 23:59:59") domain = [('active', '=', True), ('appointment_begin_date', '>=', begin_time), ('appointment_begin_date', '<=', end_time), ('teacher_name', '!=', self.teacher_name.id), ('complete_begin_date', '=', None)] field_names = ['teacher_name', 'teacher_full_name'] records = self.env['nebula.appointment'].search_read(domain, field_names, order='teacher_name desc') old_name = '' for record in records: record_name = _make_name(self.appointment_date, record['teacher_name'][1], record['teacher_full_name']) if old_name != record_name: # 不是重名 old_name = record_name new_record = { 'teacher_name': record['teacher_name'][0], 'appointment_date': self.appointment_date, 'name': record_name, } self.env['nebula.teacher.appointment'].create(new_record) # 3.查找教练预约表本日所有记录,遍历生成子表 domain = [('active', '=', True), ('appointment_date', '=', appointment_date)] field_names = ['teacher_name'] records = self.env['nebula.teacher.appointment'].search_read(domain, field_names) for record in records: order_id = record['id'] teacher_id = record['teacher_name'][0] _insert_line_table(self, order_id, appointment_date, teacher_id)
公用函数:
def _insert_line_table(self, order_id, appointment_date, teacher_id): """ 功能:传入主表,然后生成子表 1.传入主表 2.删除主表对应的子表 3.查询预约表本教练所有预约情况 4. 逐条插入到子表 """ domain = [('order_id', '=', order_id)] self.env['nebula.teacher.appointment.line'].search(domain).unlink() begin_time = datetime.strftime(appointment_date, "%Y-%m-%d 00:00:00") end_time = datetime.strftime(appointment_date, "%Y-%m-%d 23:59:59") domain = [('active', '=', True), ('appointment_begin_date', '>=', begin_time), ('appointment_begin_date', '<=', end_time), ('teacher_name', '=', teacher_id), ('complete_begin_date', '=', None)] field_names = ['name'] items = self.env['nebula.appointment'].search_read(domain, field_names) for item in items: new_record = { 'order_id': order_id, # 主表ID 'appointment_name': item['id'], # 子表预约单ID } self.env['nebula.teacher.appointment.line'].create(new_record)
界面展示
列表效果:
表单效果: