75
75
"website.controller.page" ,
76
76
]
77
77
78
- models_with_user_id = [
78
+ MODELS_WITH_USER_ID = [
79
79
'crm.lead' ,
80
80
'event.event' ,
81
81
'knowledge.article.favorite' ,
@@ -141,6 +141,7 @@ def _check_files_in_path(self, module):
141
141
self ._check_static_files_usage_in_xml (tree , in_use_files )
142
142
self ._check_fields (tree , file_name )
143
143
self ._check_change_theme_method (tree , file_name )
144
+ self ._check_dates_are_relative (tree , file_name )
144
145
if root .split ('/' )[- 1 ] == 'data' :
145
146
self ._check_view_active (tree , file_name )
146
147
self ._check_is_published_false (tree , file_name )
@@ -201,14 +202,14 @@ def _check_manifest(self, s, need_studio):
201
202
_logger .warning ("'web_studio' should not be in the dependencies." )
202
203
203
204
def _check_studio (self , root , file_name ):
204
- models_for_studio = [
205
+ MODELS_FOR_STUDIO = [
205
206
"ir.actions.act_window" ,
206
207
"ir.actions.server" ,
207
208
"ir.model" ,
208
209
"ir.model.fields" ,
209
210
"ir.ui.menu" ,
210
211
]
211
- for model in models_for_studio :
212
+ for model in MODELS_FOR_STUDIO :
212
213
if root .xpath (f"//record[@model='{ model } ']" ):
213
214
_logger .info ("%s found in %s, needs studio" , model , file_name )
214
215
return True
@@ -221,18 +222,18 @@ def _check_studio(self, root, file_name):
221
222
return False
222
223
223
224
def _check_xml_style (self , s , root , module , file_name ):
224
- starts_with = [
225
+ STARTS_WITH = [
225
226
"<?xml version='1.0' encoding='UTF-8'?>" ,
226
227
"<?xml version='1.0' encoding=\" UTF-8\" ?>" ,
227
228
"<?xml version=\" 1.0\" encoding=\" UTF-8\" ?>" ,
228
229
"<?xml version=\" 1.0\" encoding='UTF-8'?>" ,
229
230
"<?xml version=\" 1.0\" encoding=\" utf-8\" ?>" ,
230
231
]
231
232
first_line = s .split ('\n ' )[0 ]
232
- if not any (first_line == start_line for start_line in starts_with ):
233
+ if not any (first_line == start_line for start_line in STARTS_WITH ):
233
234
_logger .warning (
234
235
"XML files should begin with the following line: %s, but %s starts with %s" ,
235
- starts_with [0 ],
236
+ STARTS_WITH [0 ],
236
237
file_name ,
237
238
first_line ,
238
239
)
@@ -422,7 +423,7 @@ def _check_view_active(self, root, file_name):
422
423
423
424
def _check_user_is_set (self , root , previous_records ):
424
425
records = previous_records
425
- for model in models_with_user_id :
426
+ for model in MODELS_WITH_USER_ID :
426
427
for record in root .xpath (f"//record[@model='{ model } ']" ):
427
428
record_id = record .get ('id' )
428
429
user_field = record .xpath (".//field[@name='user_id']" )
@@ -441,3 +442,31 @@ def _check_change_theme_method(self, root, file_name):
441
442
"You should use button_choose_theme instead of button_immediate_install in %s." ,
442
443
file_name ,
443
444
)
445
+
446
+ def _check_dates_are_relative (self , root , file_name ):
447
+ RELATIVE_DATES = [
448
+ 'today()' ,
449
+ 'now()' ,
450
+ ]
451
+ for record in root .xpath ("//record" ):
452
+ model_name = record .get ('model' )
453
+ if not model_name :
454
+ continue
455
+ model = self .env .get (model_name )
456
+ fields_set_in_record = {
457
+ field for field in record .xpath ('.//field' )
458
+ if field .getparent ().get ('id' , False ) == record .get ('id' ) # nested record definitions
459
+ }
460
+ for field in fields_set_in_record :
461
+ field_name = field .get ('name' )
462
+ field_type = model ._fields .get (field_name ).type
463
+ if field_type not in ('date' , 'datetime' ):
464
+ continue
465
+ field_eval = field .get ('eval' )
466
+ if not field_eval or not any (date in field_eval for date in RELATIVE_DATES ):
467
+ _logger .warning (
468
+ "Date field '%s' in model '%s' is hard coded (file: %s). " ,
469
+ field_name ,
470
+ model_name ,
471
+ file_name ,
472
+ )
0 commit comments