Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rely on fields when validating submitted values on sample creation #2307

Merged
merged 12 commits into from
May 11, 2023
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
2.5.0 (unreleased)
------------------

- #2307 Validate non-future and non-past dates on add sample
- #2305 Add support for dates in ANSI X3.30 and ANSI X3.43.3 formats
- #2304 Fix dynamic sample specification not applied for new samples
- #2303 Fix managed permission of analysis workflow for lab roles
Expand Down
56 changes: 56 additions & 0 deletions src/bika/lims/browser/analysisrequest/add2.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import json
from collections import OrderedDict
from datetime import datetime
from dateutil import relativedelta

import six

Expand Down Expand Up @@ -1557,6 +1558,40 @@ def ajax_recalculate_prices(self):

return prices

def is_true(self, val):
"""Returns whether val evaluates to True
"""
val = str(val).strip().lower()
return val in ["y", "yes", "1", "true", "on"]

def is_date_field(self, field):
"""Returns whether the field is for storing a date or datetime
"""
return field.type in ["date", "datetime", "datetime_ng"]

def get_min_dt(self, field, default=None):
"""Returns the minimum datetime supported for the given field
"""
# find-out from the widget
widget = getattr(field, "widget", None)
no_past = getattr(widget, "datepicker_nopast", False)
if self.is_true(no_past):
return datetime.now()
return default

def get_max_dt(self, field, default=None):
"""Returns the maximum datetime supported for the given field
"""
# find-out from the widget
widget = getattr(field, "widget", None)
no_future = getattr(widget, "datepicker_nofuture", False)
if self.is_true(no_future):
return datetime.now()
two_months = getattr(widget, "datepicker_2months", False)
if self.is_true(two_months):
return datetime.now() + relativedelta(months=2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do we have this in place? Not sure if this is possible at all with the current native HTML 5 date widget.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was present in 1.3.x, not in 2.x. Just removed this with 9823c4c

return default

def check_confirmation(self):
"""Returns a dict when user confirmation is required for the creation of
samples. Returns None otherwise
Expand Down Expand Up @@ -1672,6 +1707,27 @@ def ajax_submit(self):
})
fielderrors["NumSamples"] = self.context.translate(msg)

# Validate non-past and non-future dates
for field in filter(self.is_date_field, fields):
field_name = field.getName()
dt_value = api.to_date(record.get(field_name))
if not dt_value:
# required fields are handled later
continue

max_dt = self.get_max_dt(field)
if max_dt and dt_value > DateTime(max_dt):
fielderrors[field_name] = _(
"{}: in the future or earlier than expected"
).format(field_name)
continue

min_dt = self.get_min_dt(field)
if min_dt and dt_value < DateTime(min_dt):
fielderrors[field_name] = _(
"{}: in the past or older than expected"
).format(field_name)

# Missing required fields
missing = [f for f in required_fields if not record.get(f, None)]

Expand Down