修复返工bug

This commit is contained in:
jinling.yang
2024-07-19 17:25:51 +08:00
parent 9d3c4f8163
commit 8a0c968b10
6 changed files with 134 additions and 60 deletions

View File

@@ -53,7 +53,6 @@ class MrpProduction(models.Model):
check_status = fields.Boolean(string='启用状态', default=False, readonly=True) check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
active = fields.Boolean(string='已归档', default=True) active = fields.Boolean(string='已归档', default=True)
programming_no = fields.Char('编程单号') programming_no = fields.Char('编程单号')
reprogramming_num = fields.Integer('重新编程次数', default=0)
work_state = fields.Char('业务状态') work_state = fields.Char('业务状态')
programming_state = fields.Selection( programming_state = fields.Selection(
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')], [('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')],
@@ -131,13 +130,15 @@ class MrpProduction(models.Model):
if all(wo_state not in ('progress', 'done', 'rework') for wo_state in if all(wo_state not in ('progress', 'done', 'rework') for wo_state in
production.workorder_ids.mapped('state')): production.workorder_ids.mapped('state')):
production.state = 'pending_cam' production.state = 'pending_cam'
if production.is_rework is True:
production.state = 'rework'
# if production.state == 'pending_cam': # if production.state == 'pending_cam':
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')): # if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
# production.state = 'done' # production.state = 'done'
if any( if any(
( (
wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [ wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
'已编程', '已下发']) or ( '已编程']) or (
wo.state == 'rework' and production.programming_state == '编程中') or ( wo.state == 'rework' and production.programming_state == '编程中') or (
wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中', wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
'已编程']) '已编程'])
@@ -179,11 +180,12 @@ class MrpProduction(models.Model):
try: try:
if not self: if not self:
reproduction = self.env['mrp.production'].search( reproduction = self.env['mrp.production'].search(
[('state', '=', 'rework'), ('programming_state', '=', '编程中')]) [('state', '=', 'rework'), ('programming_state', '=', '编程中'), ('is_rework', '=', True)])
else: else:
reproduction = self reproduction = self
if reproduction: if reproduction:
programming_no = [item.programming_no for item in reproduction] programming_no_set = set([str(item.programming_no) for item in reproduction])
programming_no = list(programming_no_set)
programming_no_str = ','.join(programming_no) programming_no_str = ','.join(programming_no)
res = {'programming_no': programming_no_str} res = {'programming_no': programming_no_str}
logging.info('res=%s:' % res) logging.info('res=%s:' % res)
@@ -199,7 +201,8 @@ class MrpProduction(models.Model):
for item in result['programming_list']: for item in result['programming_list']:
if not self: if not self:
production = self.env['mrp.production'].search( production = self.env['mrp.production'].search(
[('state', '=', 'rework'), ('programming_no', '=', item['programming_no'])]) [('state', '=', 'rework'),
('programming_no', '=', item['programming_no'], ('is_rework', '=', True))])
if production: if production:
production.write({'programming_state': '已编程未下发' if item[ production.write({'programming_state': '已编程未下发' if item[
'programming_state'] == '已编程' else '编程中'}) 'programming_state'] == '已编程' else '编程中'})
@@ -746,17 +749,10 @@ class MrpProduction(models.Model):
# 返工 # 返工
def button_rework(self): def button_rework(self):
cloud_programming = None cloud_programming = None
is_reprogramming = False if self.programming_state in ['已编程']:
if self.programming_state == '已编程' and self.reprogramming_num >= 0:
cloud_programming = self._cron_get_programming_state() cloud_programming = self._cron_get_programming_state()
if self.reprogramming_num == 0: logging.info('cloud_programming_state:%s' % cloud_programming['programming_state'])
self.reprogramming_num = cloud_programming['reprogramming_num']
logging.info('cloud_programming_state:%s' % cloud_programming['programming_state'])
logging.info('programming_state:%s' % self.programming_state) logging.info('programming_state:%s' % self.programming_state)
if cloud_programming is None:
if self.reprogramming_num > 1:
is_reprogramming = False
return { return {
'name': _('返工'), 'name': _('返工'),
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
@@ -765,9 +761,8 @@ class MrpProduction(models.Model):
'target': 'new', 'target': 'new',
'context': { 'context': {
'default_production_id': self.id, 'default_production_id': self.id,
'default_reprogramming_num': self.reprogramming_num, 'default_reprogramming_num': cloud_programming['reprogramming_num'],
'default_programming_state': self.programming_state if cloud_programming is None else 'default_programming_state': cloud_programming['programming_state'],
cloud_programming['programming_state'],
'default_is_reprogramming': False if (cloud_programming['programming_state'] in ['编程中', 'default_is_reprogramming': False if (cloud_programming['programming_state'] in ['编程中',
'待编程'] and self.programming_state in [ '待编程'] and self.programming_state in [
'编程中']) '编程中'])
@@ -805,7 +800,7 @@ class MrpProduction(models.Model):
'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type == 'CNC加工') 'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type == 'CNC加工')
if not pending_workorder.cnc_ids: if not pending_workorder.cnc_ids:
production.get_new_program(rework_item.processing_panel) production.get_new_program(rework_item.processing_panel)
production.write({'state': 'progress', 'programming_state': '已编程'}) production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
# 从cloud获取重新编程过的最新程序 # 从cloud获取重新编程过的最新程序
def get_new_program(self, processing_panel): def get_new_program(self, processing_panel):
@@ -819,31 +814,32 @@ class MrpProduction(models.Model):
r = r.json() r = r.json()
result = json.loads(r['result']) result = json.loads(r['result'])
if result['status'] == 1: if result['status'] == 1:
program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel) # program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel)
if os.path.exists(program_path_tmp_panel): # if os.path.exists(program_path_tmp_panel):
files_r = os.listdir(program_path_tmp_panel) # files_r = os.listdir(program_path_tmp_panel)
if files_r: # if files_r:
for file_name in files_r: # for file_name in files_r:
file_path = os.path.join(program_path_tmp_panel, file_name) # file_path = os.path.join(program_path_tmp_panel, file_name)
os.remove(file_path) # os.remove(file_path)
download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'], # download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'],
processing_panel) # processing_panel)
if download_state is False: # if download_state is False:
raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no)) # raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no))
productions = self.env['mrp.production'].search( productions = self.env['mrp.production'].search(
[('programming_no', '=', self.programming_no), ('state', 'not in', ['cancel,done'])]) [('programming_no', '=', self.programming_no), ('state', 'not in', ('cancel', 'done'))])
if productions: if productions:
panel_workorder = productions.workorder_ids.filtered( panel_workorder = productions.workorder_ids.filtered(
lambda lambda
ac: ac.processing_panel == processing_panel and ac.routing_type == 'CNC加工' and ac.state != 'rework') ac: ac.processing_panel == processing_panel and ac.routing_type == 'CNC加工' and ac.state not in (
'rework', 'done'))
if panel_workorder: if panel_workorder:
if panel_workorder.cnc_ids: if panel_workorder.cnc_ids:
panel_workorder.cmm_ids.sudo().unlink() panel_workorder.cmm_ids.sudo().unlink()
panel_workorder.cnc_ids.sudo().unlink() panel_workorder.cnc_ids.sudo().unlink()
self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan( self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
productions) productions)
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
# processing_panel) processing_panel)
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel) logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
files_panel = os.listdir(program_path_tmp_panel) files_panel = os.listdir(program_path_tmp_panel)
if files_panel: if files_panel:

View File

@@ -738,7 +738,10 @@ class ResMrpWorkOrder(models.Model):
if re_work: if re_work:
if workorder.routing_type == '装夹预调' and workorder.state not in ['done', 'rework', if workorder.routing_type == '装夹预调' and workorder.state not in ['done', 'rework',
'cancel'] and workorder.is_rework is False: 'cancel'] and workorder.is_rework is False:
workorder.state = 'waiting' if workorder.production_id.programming_state == '已编程':
workorder.state = 'ready'
else:
workorder.state = 'waiting'
if workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'rework', 'cancel']: if workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'rework', 'cancel']:
pre_workorder = self.env['mrp.workorder'].search( pre_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id), [('production_id', '=', workorder.production_id.id),
@@ -750,7 +753,8 @@ class ResMrpWorkOrder(models.Model):
cnc_workorder = self.env['mrp.workorder'].search( cnc_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id), [('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel), ('processing_panel', '=', workorder.processing_panel),
('routing_type', '=', 'CNC加工'), ('state', '=', 'done')]) ('routing_type', '=', 'CNC加工'), ('state', '=', 'done'),
('test_results', '=', '返工')])
if cnc_workorder: if cnc_workorder:
workorder.state = 'waiting' workorder.state = 'waiting'
elif workorder.production_id.state == 'progress': elif workorder.production_id.state == 'progress':
@@ -759,14 +763,13 @@ class ResMrpWorkOrder(models.Model):
logging.info('状态:%s' % workorder.state) logging.info('状态:%s' % workorder.state)
logging.info('is_rework:%s' % workorder.is_rework) logging.info('is_rework:%s' % workorder.is_rework)
logging.info('面:%s' % workorder.processing_panel) logging.info('面:%s' % workorder.processing_panel)
logging.info('reprogramming_num:%s' % workorder.production_id.reprogramming_num)
logging.info('编程状态:%s' % workorder.production_id.programming_state) logging.info('编程状态:%s' % workorder.production_id.programming_state)
logging.info('制造状态:%s' % workorder.production_id.state) logging.info('制造状态:%s' % workorder.production_id.state)
if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已编程' and \ if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已编程' and \
workorder.is_rework is False and workorder.state not in [ workorder.is_rework is False and workorder.state not in [
'done', 'rework', 'done', 'rework',
'cancel']: 'cancel']:
if re_work: if re_work and workorder.production_id.is_rework is False:
workorder.state = 'ready' workorder.state = 'ready'
# else: # else:
# if workorder.state not in ['cancel', 'rework']: # if workorder.state not in ['cancel', 'rework']:
@@ -916,16 +919,15 @@ class ResMrpWorkOrder(models.Model):
record.process_state = '待解除装夹' record.process_state = '待解除装夹'
# record.write({'process_state': '待加工'}) # record.write({'process_state': '待加工'})
record.production_id.process_state = '待解除装夹' record.production_id.process_state = '待解除装夹'
if record.test_results in ['返工', '报废']: record.production_id.write({'detection_result_ids': [(0, 0, {
record.production_id.write({'detection_result_ids': [(0, 0, { 'rework_reason': record.reason,
'rework_reason': record.reason, 'detailed_reason': record.detailed_reason,
'detailed_reason': record.detailed_reason, 'processing_panel': record.processing_panel,
'processing_panel': record.processing_panel, 'routing_type': record.routing_type,
'routing_type': record.routing_type, 'handle_result': '待处理' if record.test_results == '返工' or record.is_rework is True else '',
'handle_result': '待处理' if record.test_results == '返工' or record.is_rework is True else '', 'test_results': record.test_results,
'test_results': record.test_results, 'test_report': record.detection_report})],
'test_report': record.detection_report})], 'is_scrap': True if record.test_results == '报废' else False})
'is_scrap': True if record.test_results == '报废' else False})
if record.routing_type == '解除装夹': if record.routing_type == '解除装夹':
''' '''
记录结束时间 记录结束时间
@@ -980,10 +982,12 @@ class ResMrpWorkOrder(models.Model):
# record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code}) # record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code})
# if record.is_remanufacture is True: # if record.is_remanufacture is True:
# record.recreateManufacturingOrWorkerOrder() # record.recreateManufacturingOrWorkerOrder()
is_production_id = True is_production_id = False
for workorder in record.production_id.workorder_ids: rework_workorder = record.production_id.workorder_ids.filtered(lambda p: p.state == 'rework')
if workorder.state != 'done': done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state == 'done')
is_production_id = False if (len(rework_workorder) + len(done_workorder) == len(record.production_id.workorder_ids)) or (
len(done_workorder) == len(record.production_id.workorder_ids)):
is_production_id = True
if record.routing_type in ['解除装夹'] or ( if record.routing_type in ['解除装夹'] or (
record.is_rework is True and record.routing_type in ['装夹预调']) or ( record.is_rework is True and record.routing_type in ['装夹预调']) or (
record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']): record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']):

View File

@@ -124,7 +124,7 @@
confirm="是否确认更新程序" confirm="是否确认更新程序"
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/> attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>
<button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user" <button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': ['|',('state', '!=', 'rework') ,'&amp;',('programming_state', '!=', '已编程'),('is_rework', '=', True)]}"/> attrs="{'invisible': ['|',('state', '!=', 'rework') ,('programming_state', '!=', '已编程')]}"/>
<button name="%(sf_manufacturing.action_sf_production_wizard)d" string="报废" type="action" <button name="%(sf_manufacturing.action_sf_production_wizard)d" string="报废" type="action"
groups="sf_base.group_sf_mrp_user" groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': [('is_scrap', '=', False)]}"/> attrs="{'invisible': [('is_scrap', '=', False)]}"/>

View File

@@ -189,7 +189,7 @@
<xpath expr="//label[1]" position="before"> <xpath expr="//label[1]" position="before">
<field name='routing_type' readonly="1"/> <field name='routing_type' readonly="1"/>
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> <field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
<field name="rfid_code" force_save="1" readonly="1" cache="True" <field name="rfid_code" force_save="1" readonly="0" cache="True"
attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/> attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/>
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/> <field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
</xpath> </xpath>
@@ -496,7 +496,7 @@
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/> attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>--> <!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}' <field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
widget="pdf_viewer"/> widget="pdf_viewer" readonly="1"/>
</group> </group>
<!-- <div class="col-12 col-lg-6 o_setting_box">--> <!-- <div class="col-12 col-lg-6 o_setting_box">-->
<!-- <button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"--> <!-- <button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"-->

View File

@@ -44,6 +44,25 @@ class ReworkWizard(models.TransientModel):
self.workorder_id.button_finish() self.workorder_id.button_finish()
else: else:
if self.production_id.workorder_ids: if self.production_id.workorder_ids:
handle_result = self.production_id.detection_result_ids.filtered(
lambda dr: dr.handle_result == '待处理')
if handle_result:
processing_panels_to_handle = set(handle_item.processing_panel for handle_item in handle_result)
processing_panels_choice = set(dr_panel.name for dr_panel in self.processing_panel_id)
# 使用集合的差集操作找出那些待处理结果中有但实际可用加工面中没有的加工面
processing_panels_missing = processing_panels_to_handle - processing_panels_choice
# 存在不一致的加工面
if processing_panels_missing:
processing_panels_str = ','.join(processing_panels_missing)
raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str)
# processing_panels = set()
# for handle_item in handle_result:
# for dr_panel in self.processing_panel_id:
# if dr_panel.name == handle_item.processing_panel:
# processing_panels.add(dr_panel.name)
# if len(processing_panels) != len(handle_result):
# processing_panels_str = ','.join(processing_panels)
# return UserError(f'您还有待处理的检测结果中为{processing_panels_str}的加工面未选择')
for panel in self.processing_panel_id: for panel in self.processing_panel_id:
panel_workorder = self.production_id.workorder_ids.filtered( panel_workorder = self.production_id.workorder_ids.filtered(
lambda ap: ap.processing_panel == panel.name and ap.state != 'rework') lambda ap: ap.processing_panel == panel.name and ap.state != 'rework')
@@ -60,19 +79,74 @@ class ReworkWizard(models.TransientModel):
self.env['mrp.workorder'].json_workorder_str(panel.name, self.env['mrp.workorder'].json_workorder_str(panel.name,
self.production_id, route, False)) self.production_id, route, False))
if workorders_values: if workorders_values:
self.production_id.write( self.production_id.write({'workorder_ids': workorders_values, 'is_rework': True})
{'workorder_ids': workorders_values, 'programming_state': '编程中', 'work_state': '编程中',
'reprogramming_num': self.production_id.reprogramming_num + 1})
self.production_id._reset_work_order_sequence() self.production_id._reset_work_order_sequence()
self.production_id.detection_result_ids.filtered( self.production_id.detection_result_ids.filtered(
lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write( lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write(
{'handle_result': '已处理'}) {'handle_result': '已处理'})
if self.is_reprogramming is False: if self.is_reprogramming is False:
self.production_id.get_new_program(panel) if self.reprogramming_num >= 1:
self.production_id.get_new_program(panel)
else:
ret = {'programming_list': []}
cnc_rework = self.production_id.workorder_ids.filtered(
lambda crw: crw.processing_panel == panel.name and crw.state in (
'rework') and crw.routing_type == 'CNC加工')
if cnc_rework.cnc_ids:
for item_line in cnc_rework.cnc_ids:
vals = {
'processing_panel': item_line.processing_panel,
'sequence_number': item_line.sequence_number,
'program_name': item_line.program_name,
'cutting_tool_name': item_line.cutting_tool_name,
'cutting_tool_no': item_line.cutting_tool_no,
'processing_type': item_line.processing_type,
'margin_x_y': item_line.margin_x_y,
'margin_z': item_line.margin_z,
'depth_of_processing_z': item_line.depth_of_processing_z,
'cutting_tool_extension_length': item_line.cutting_tool_extension_length,
'estimated_processing_time': item_line.estimated_processing_time,
'cutting_tool_handle_type': item_line.cutting_tool_handle_type,
'ftp_path': item_line.ftp_path,
'program_create_date': datetime.strftime(item_line.program_create_date,
'%Y-%m-%d %H:%M:%S'),
'remark': item_line.remark
}
ret['programming_list'].append(vals)
for cmm_line in cnc_rework.cmm_ids:
vals = {
'sequence_number': cmm_line.sequence_number,
'program_name': cmm_line.program_name,
'ftp_path': cmm_line.ftp_path,
'program_create_date': datetime.strftime(cmm_line.program_create_date,
'%Y-%m-%d %H:%M:%S')
}
ret['programming_list'].append(vals)
new_cnc_workorder = self.production_id.workorder_ids.filtered(
lambda ap1: ap1.processing_panel == panel.name and ap1.state not in (
'rework', 'done') and ap1.routing_type == 'CNC加工')
if not new_cnc_workorder.cnc_ids:
new_cnc_workorder.write({
'cnc_ids': new_cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel.name,
ret),
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name, ret),
'cnc_worksheet': new_cnc_workorder.cnc_worksheet})
new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
'rework', 'done'))
if new_pre_workorder:
pre_rework = self.production_id.workorder_ids.filtered(
lambda pr: pr.processing_panel == panel.name and pr.state in (
'rework') and crw.routing_type == '装夹预调')
new_pre_workorder.write(
{'processing_drawing': pre_rework.processing_drawing})
self.production_id.write({'state': 'progress'})
if self.is_reprogramming is True: if self.is_reprogramming is True:
self.production_id.update_programming_state() self.production_id.update_programming_state()
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中'})
if self.production_id.state == 'progress': if self.production_id.state == 'progress':
self.production_id.write({'programming_state': '已编程'}) self.production_id.write({'programming_state': '已编程', 'work_state': '编程中'})
@api.onchange('production_id') @api.onchange('production_id')
def onchange_processing_panel_id(self): def onchange_processing_panel_id(self):

View File

@@ -44,7 +44,7 @@ class ReSaleOrder(models.Model):
store=True, readonly=False, precompute=True, check_company=True, tracking=True, store=True, readonly=False, precompute=True, check_company=True, tracking=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]") domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
remark = fields.Text('备注') remark = fields.Text('备注')
translated_display_name = fields.Char('交货人电话号码')
validity_date = fields.Date( validity_date = fields.Date(
string="Expiration", string="Expiration",
compute='_compute_validity_date', compute='_compute_validity_date',