clinicedc 2.0.34__py3-none-any.whl → 2.0.36__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of clinicedc might be problematic. Click here for more details.
- {clinicedc-2.0.34.dist-info → clinicedc-2.0.36.dist-info}/METADATA +1 -1
- {clinicedc-2.0.34.dist-info → clinicedc-2.0.36.dist-info}/RECORD +155 -165
- {clinicedc-2.0.34.dist-info → clinicedc-2.0.36.dist-info}/WHEEL +1 -1
- edc_action_item/templatetags/action_item_extras.py +1 -1
- edc_adverse_event/form_validator_mixins/death_report_form_validator.py +1 -1
- edc_adverse_event/form_validator_mixins/requires_death_report_form_validator_mixin.py +5 -3
- edc_adverse_event/form_validators/death_report_tmg.py +2 -2
- edc_adverse_event/modeladmin_mixins/ae_tmg_admin_mixin.py +1 -1
- edc_adverse_event/modeladmin_mixins/utils.py +1 -1
- edc_adverse_event/utils.py +1 -1
- edc_appointment/admin/appointment_admin.py +24 -24
- edc_appointment/admin/list_filters.py +5 -5
- edc_appointment/appointment_reason_updater.py +6 -5
- edc_appointment/appointment_status_updater.py +2 -2
- edc_appointment/context_processors.py +1 -2
- edc_appointment/creators/appointment_creator.py +9 -8
- edc_appointment/creators/unscheduled_appointment_creator.py +31 -24
- edc_appointment/creators/utils.py +35 -37
- edc_appointment/exceptions.py +3 -3
- edc_appointment/form_runners.py +2 -2
- edc_appointment/form_validator_mixins/next_appointment_crf_form_validator_mixin.py +5 -5
- edc_appointment/form_validator_mixins/window_period_form_validator_mixin.py +7 -7
- edc_appointment/form_validators/appointment_form_validator.py +16 -23
- edc_appointment/management/commands/close_appointments.py +3 -3
- edc_appointment/management/commands/reset_visit_code_sequences.py +1 -1
- edc_appointment/management/commands/update_appointment_status.py +2 -2
- edc_appointment/management/commands/update_skipped_appointments.py +7 -7
- edc_appointment/managers.py +6 -7
- edc_appointment/model_mixins/appointment_model_mixin.py +3 -4
- edc_appointment/modeladmin_mixins/next_appointment_crf_modeladmin_mixin.py +2 -2
- edc_appointment/modelform_mixins/next_appointment_crf_modelform_mixins.py +25 -24
- edc_appointment/models/signals.py +1 -1
- edc_appointment/skip_appointments.py +10 -29
- edc_appointment/utils.py +43 -47
- edc_appointment/view_mixins/appointment_view_mixin.py +11 -13
- edc_appointment/views/unscheduled_appointment_view.py +5 -6
- edc_consent/consent_definition.py +5 -13
- edc_consent/consent_definition_extension.py +0 -2
- edc_consent/form_validators/consent_definition_form_validator_mixin.py +25 -30
- edc_consent/form_validators/subject_consent_form_validator.py +3 -3
- edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py +1 -1
- edc_consent/modelform_mixins/requires_consent_modelform_mixin.py +4 -5
- edc_consent/site_consents.py +13 -14
- edc_crf/crf_form_validator.py +13 -19
- edc_crf/crf_form_validator_mixins.py +2 -17
- edc_data_manager/admin/actions.py +1 -1
- edc_data_manager/admin/data_query_admin.py +1 -1
- edc_dx/form_validators/result_form_validator_mixin.py +1 -1
- edc_dx_review/medical_date.py +1 -1
- edc_egfr/egfr.py +4 -8
- edc_egfr/form_validator_mixins/egfr_form_validator_mixins.py +2 -2
- edc_facility/facility.py +7 -11
- edc_facility/holidays.py +3 -3
- edc_facility/models/holiday.py +1 -1
- edc_form_runners/form_runner.py +15 -16
- edc_form_validators/base_form_validator.py +5 -1
- edc_form_validators/date_range_validator.py +49 -61
- edc_form_validators/date_validator.py +1 -1
- edc_form_validators/extra_mixins/study_day_form_validator.py +1 -1
- edc_lab/form_validators/crf_requisition_form_validator_mixin.py +21 -15
- edc_lab/form_validators/requisition_form_validator_mixin.py +3 -3
- edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +1 -1
- edc_ltfu/modelform_mixins.py +1 -1
- edc_metadata/metadata/metadata.py +20 -7
- edc_metadata/metadata_rules/logic.py +5 -4
- edc_metadata/metadata_rules/predicate.py +22 -24
- edc_model_form/mixins/report_datetime_modelform_mixin.py +1 -5
- edc_offstudy/model_mixins/offstudy_model_mixin.py +1 -1
- edc_offstudy/modelform_mixins/crf/offstudy_crf_modelform_mixin.py +2 -2
- edc_offstudy/utils.py +4 -4
- edc_pdf_reports/crf_pdf_report.py +2 -1
- edc_pdutils/helper.py +3 -3
- edc_pdutils/utils/convert_dates_from_model.py +4 -3
- edc_pharmacy/admin/actions/confirm_stock.py +3 -3
- edc_pharmacy/admin/actions/delete_items_for_stock_request.py +4 -3
- edc_pharmacy/admin/actions/delete_order_items.py +7 -3
- edc_pharmacy/admin/actions/delete_receive_items.py +5 -3
- edc_pharmacy/admin/actions/process_repack_request.py +7 -11
- edc_pharmacy/admin/autocomplete_admin.py +1 -1
- edc_pharmacy/admin/list_filters.py +48 -46
- edc_pharmacy/admin/medication/assignment_admin.py +4 -4
- edc_pharmacy/admin/medication/dosage_guideline_admin.py +3 -3
- edc_pharmacy/admin/medication/formulation_admin.py +5 -5
- edc_pharmacy/admin/medication/medication_admin.py +3 -3
- edc_pharmacy/admin/prescription/rx_admin.py +2 -2
- edc_pharmacy/admin/prescription/rx_refill_admin.py +11 -17
- edc_pharmacy/admin/remove_fields_for_blinded_users.py +1 -1
- edc_pharmacy/admin/reports/stock_availability_admin.py +12 -8
- edc_pharmacy/admin/stock/allocation_admin.py +13 -17
- edc_pharmacy/admin/stock/allocation_proxy_admin.py +1 -2
- edc_pharmacy/admin/stock/confirmation_admin.py +4 -8
- edc_pharmacy/admin/stock/confirmation_at_site_item_admin.py +1 -1
- edc_pharmacy/admin/stock/container_admin.py +3 -3
- edc_pharmacy/admin/stock/location_admin.py +3 -6
- edc_pharmacy/admin/stock/lot_admin.py +9 -12
- edc_pharmacy/admin/stock/order_admin.py +2 -5
- edc_pharmacy/admin/stock/order_item_admin.py +14 -22
- edc_pharmacy/admin/stock/product_admin.py +6 -9
- edc_pharmacy/admin/stock/receive_admin.py +2 -2
- edc_pharmacy/admin/stock/receive_item_admin.py +3 -3
- edc_pharmacy/admin/stock/repack_request_admin.py +7 -10
- edc_pharmacy/admin/stock/stock_adjustment_admin.py +1 -1
- edc_pharmacy/admin/stock/stock_admin.py +17 -28
- edc_pharmacy/admin/stock/stock_proxy_admin.py +3 -4
- edc_pharmacy/admin/stock/stock_request_admin.py +6 -10
- edc_pharmacy/admin/stock/stock_request_item_admin.py +9 -18
- edc_pharmacy/admin/stock/stock_transfer_admin.py +5 -5
- edc_pharmacy/admin/stock/stock_transfer_item_admin.py +3 -3
- edc_pharmacy/admin/stock/storage_bin_admin.py +1 -1
- edc_pharmacy/admin/stock/storage_bin_item_admin.py +2 -2
- edc_pharmacy/form_validators/crf/study_medication_form_validator.py +27 -27
- edc_pharmacy/forms/stock/confirmation_form.py +2 -2
- edc_pharmacy/forms/stock/dispense_form.py +2 -2
- edc_pharmacy/forms/stock/lot_form.py +6 -6
- edc_pharmacy/forms/stock/order_form.py +4 -6
- edc_pharmacy/forms/stock/product_form.py +2 -3
- edc_pharmacy/forms/stock/receive_form.py +4 -4
- edc_pharmacy/forms/stock/receive_item_form.py +7 -5
- edc_pharmacy/forms/stock/repack_request_form.py +10 -8
- edc_pharmacy/forms/stock/stock_form.py +2 -3
- edc_pharmacy/forms/stock/stock_request_form.py +10 -8
- edc_pharmacy/forms/stock/stock_request_item_form.py +6 -5
- edc_pharmacy/forms/stock/stock_transfer_form.py +16 -14
- edc_pharmacy/forms/stock/supplier_form.py +2 -2
- edc_pharmacy/labels/label_data.py +2 -3
- edc_pharmacy/model_mixins/study_medication_crf_model_mixin.py +1 -1
- edc_pharmacy/models/medication/dosage_guideline.py +3 -3
- edc_pharmacy/models/model_mixins.py +12 -10
- edc_pharmacy/models/prescription/rx.py +1 -1
- edc_pharmacy/models/prescription/rx_refill.py +1 -1
- edc_pharmacy/models/signals.py +2 -3
- edc_pharmacy/models/storage/utils.py +4 -4
- edc_pharmacy/pdf_reports/manifest_pdf_report.py +3 -6
- edc_pharmacy/pdf_reports/stock_pdf_report.py +1 -3
- edc_pharmacy/refill/refill_creator.py +3 -4
- edc_protocol/validators.py +10 -16
- edc_reportable/forms/reportables_form_validator_mixin.py +1 -1
- edc_screening/form_validator_mixins.py +2 -1
- edc_transfer/form_validators.py +1 -1
- edc_transfer/model_mixins.py +1 -1
- edc_utils/age.py +17 -15
- edc_utils/date.py +7 -7
- edc_utils/text.py +7 -6
- edc_visit_schedule/exceptions.py +8 -0
- edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +1 -1
- edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +1 -1
- edc_visit_schedule/modelform_mixins/off_schedule_modelform_mixin.py +1 -3
- edc_visit_schedule/schedule/visit_collection.py +1 -1
- edc_visit_schedule/schedule/window.py +0 -2
- edc_visit_schedule/subject_schedule.py +1 -1
- edc_visit_schedule/utils.py +4 -4
- edc_visit_schedule/visit/visit.py +9 -3
- edc_visit_schedule/visit/window_period.py +1 -1
- edc_visit_tracking/form_validators/visit_form_validator.py +1 -1
- edc_metadata/metadata_wrappers/__init__.py +0 -5
- edc_metadata/metadata_wrappers/crf_metadata_wrapper.py +0 -5
- edc_metadata/metadata_wrappers/crf_metadata_wrappers.py +0 -8
- edc_metadata/metadata_wrappers/metadata_wrapper.py +0 -74
- edc_metadata/metadata_wrappers/metadata_wrappers.py +0 -33
- edc_metadata/metadata_wrappers/requisition_metadata_wrapper.py +0 -26
- edc_metadata/metadata_wrappers/requisition_metadata_wrappers.py +0 -10
- edc_pharmacy/management/__init__.py +0 -0
- edc_pharmacy/management/commands/__init__.py +0 -0
- edc_pharmacy/management/commands/update_initial_pharmacy_data.py +0 -10
- {clinicedc-2.0.34.dist-info → clinicedc-2.0.36.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
|
|
1
3
|
from django.contrib import admin
|
|
2
4
|
from django.template.loader import render_to_string
|
|
3
5
|
from django.urls import reverse
|
|
@@ -48,19 +50,19 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
48
50
|
|
|
49
51
|
show_form_tools = True
|
|
50
52
|
show_history_label = True
|
|
51
|
-
autocomplete_fields =
|
|
53
|
+
autocomplete_fields = ("container",)
|
|
52
54
|
|
|
53
55
|
change_list_note = (
|
|
54
56
|
"C=Confirmed, A=Allocated, T=Transferred to site, "
|
|
55
57
|
"S=Confirmed at site, B=Stored at site, D=Dispensed"
|
|
56
58
|
)
|
|
57
59
|
|
|
58
|
-
actions =
|
|
60
|
+
actions = (
|
|
59
61
|
print_labels,
|
|
60
62
|
confirm_stock_from_queryset,
|
|
61
63
|
go_to_add_repack_request_action,
|
|
62
64
|
print_stock_report_action,
|
|
63
|
-
|
|
65
|
+
)
|
|
64
66
|
|
|
65
67
|
form = StockForm
|
|
66
68
|
|
|
@@ -184,29 +186,24 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
184
186
|
|
|
185
187
|
def get_list_display(self, request):
|
|
186
188
|
fields = super().get_list_display(request)
|
|
187
|
-
|
|
188
|
-
return fields
|
|
189
|
+
return remove_fields_for_blinded_users(request, fields)
|
|
189
190
|
|
|
190
191
|
def get_list_display_links(self, request, list_display):
|
|
191
192
|
display_links = super().get_list_display_links(request, list_display)
|
|
192
193
|
if not request.user.userprofile.roles.filter(
|
|
193
194
|
name__in=[PHARMACIST_ROLE, PHARMACY_SUPER_ROLE]
|
|
194
195
|
).exists():
|
|
195
|
-
|
|
196
|
+
with contextlib.suppress(ValueError):
|
|
196
197
|
display_links.remove("formatted_code")
|
|
197
|
-
except ValueError:
|
|
198
|
-
pass
|
|
199
198
|
return display_links
|
|
200
199
|
|
|
201
200
|
def get_list_filter(self, request):
|
|
202
201
|
fields = super().get_list_filter(request)
|
|
203
|
-
|
|
204
|
-
return fields
|
|
202
|
+
return remove_fields_for_blinded_users(request, fields)
|
|
205
203
|
|
|
206
204
|
def get_search_fields(self, request):
|
|
207
205
|
fields = super().get_search_fields(request)
|
|
208
|
-
|
|
209
|
-
return fields
|
|
206
|
+
return remove_fields_for_blinded_users(request, fields)
|
|
210
207
|
|
|
211
208
|
def get_fieldsets(self, request, obj=None):
|
|
212
209
|
fieldsets = super().get_fieldsets(request, obj)
|
|
@@ -228,12 +225,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
228
225
|
mark_safe('<div style="color:red;">ERROR!</div>'), # nosec B703, B308
|
|
229
226
|
)
|
|
230
227
|
except AllocationError:
|
|
231
|
-
return
|
|
232
|
-
"{}",
|
|
233
|
-
mark_safe(
|
|
234
|
-
'<div style="color:red;">Allocation<BR>ERROR!</div>'
|
|
235
|
-
), # nosec B703, B308
|
|
236
|
-
)
|
|
228
|
+
return mark_safe('<div style="color:red;">Allocation<BR>ERROR!</div>')
|
|
237
229
|
return obj.lot.assignment
|
|
238
230
|
|
|
239
231
|
@admin.display(description="Lot #", ordering="lot__lot_no")
|
|
@@ -259,7 +251,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
259
251
|
@admin.display(description="C", boolean=True)
|
|
260
252
|
def formatted_confirmation(self, obj):
|
|
261
253
|
if obj:
|
|
262
|
-
return
|
|
254
|
+
return bool(get_related_or_none(obj, "confirmation"))
|
|
263
255
|
return None
|
|
264
256
|
|
|
265
257
|
@admin.display(description="A", boolean=True)
|
|
@@ -269,7 +261,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
269
261
|
and get_related_or_none(obj, "confirmation")
|
|
270
262
|
and get_related_or_none(obj, "from_stock")
|
|
271
263
|
):
|
|
272
|
-
return
|
|
264
|
+
return bool(get_related_or_none(obj, "allocation"))
|
|
273
265
|
return None
|
|
274
266
|
|
|
275
267
|
@admin.display(description="T", boolean=True)
|
|
@@ -279,7 +271,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
279
271
|
and get_related_or_none(obj, "confirmation")
|
|
280
272
|
and get_related_or_none(obj, "from_stock")
|
|
281
273
|
):
|
|
282
|
-
return
|
|
274
|
+
return bool(get_related_or_none(obj, "stocktransferitem"))
|
|
283
275
|
return None
|
|
284
276
|
|
|
285
277
|
@admin.display(description="S", boolean=True)
|
|
@@ -289,7 +281,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
289
281
|
and get_related_or_none(obj, "confirmation")
|
|
290
282
|
and get_related_or_none(obj, "from_stock")
|
|
291
283
|
):
|
|
292
|
-
return
|
|
284
|
+
return bool(get_related_or_none(obj, "confirmationatsiteitem"))
|
|
293
285
|
return None
|
|
294
286
|
|
|
295
287
|
@admin.display(description="B", boolean=True)
|
|
@@ -301,7 +293,7 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
301
293
|
and get_related_or_none(obj, "confirmationatsiteitem")
|
|
302
294
|
and not get_related_or_none(obj, "dispenseitem")
|
|
303
295
|
):
|
|
304
|
-
return
|
|
296
|
+
return bool(get_related_or_none(obj, "storagebinitem"))
|
|
305
297
|
return None
|
|
306
298
|
|
|
307
299
|
@admin.display(description="D", boolean=True)
|
|
@@ -312,15 +304,12 @@ class StockAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
312
304
|
and get_related_or_none(obj, "from_stock")
|
|
313
305
|
and get_related_or_none(obj, "confirmationatsiteitem")
|
|
314
306
|
):
|
|
315
|
-
return
|
|
307
|
+
return bool(get_related_or_none(obj, "dispenseitem"))
|
|
316
308
|
return None
|
|
317
309
|
|
|
318
310
|
@admin.display(description="Container", ordering="container__name")
|
|
319
311
|
def container_str(self, obj):
|
|
320
|
-
return
|
|
321
|
-
"{}",
|
|
322
|
-
mark_safe("<BR>".join(str(obj.container).split(" "))), # nosec B703, B308
|
|
323
|
-
)
|
|
312
|
+
return mark_safe("<BR>".join(str(obj.container).split(" "))) # noqa: S308
|
|
324
313
|
|
|
325
314
|
@admin.display(description="formulation", ordering="product__formulation__name")
|
|
326
315
|
def formulation(self, obj):
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
|
|
1
3
|
from django.contrib import admin
|
|
2
4
|
from django_audit_fields import audit_fieldset_tuple
|
|
3
5
|
|
|
@@ -15,7 +17,6 @@ from .stock_admin import StockAdmin
|
|
|
15
17
|
|
|
16
18
|
@admin.register(StockProxy, site=edc_pharmacy_admin)
|
|
17
19
|
class StockProxyAdmin(StockAdmin):
|
|
18
|
-
|
|
19
20
|
fieldsets = (
|
|
20
21
|
(
|
|
21
22
|
"Stock item",
|
|
@@ -106,10 +107,8 @@ class StockProxyAdmin(StockAdmin):
|
|
|
106
107
|
if not request.user.userprofile.roles.filter(
|
|
107
108
|
name__in=[PHARMACIST_ROLE, PHARMACY_SUPER_ROLE]
|
|
108
109
|
).exists():
|
|
109
|
-
|
|
110
|
+
with contextlib.suppress(ValueError):
|
|
110
111
|
display_links.remove("formatted_code")
|
|
111
|
-
except ValueError:
|
|
112
|
-
pass
|
|
113
112
|
return display_links
|
|
114
113
|
|
|
115
114
|
def get_view_only_site_ids_for_user(self, request) -> list[int]:
|
|
@@ -3,7 +3,6 @@ from celery.states import SUCCESS
|
|
|
3
3
|
from django.contrib import admin
|
|
4
4
|
from django.template.loader import render_to_string
|
|
5
5
|
from django.urls import reverse
|
|
6
|
-
from django.utils.html import format_html
|
|
7
6
|
from django.utils.safestring import mark_safe
|
|
8
7
|
from django.utils.translation import gettext as _
|
|
9
8
|
from django_audit_fields import audit_fieldset_tuple
|
|
@@ -26,7 +25,7 @@ class StatusListFilter(admin.SimpleListFilter):
|
|
|
26
25
|
title = _("Status")
|
|
27
26
|
parameter_name = "status"
|
|
28
27
|
|
|
29
|
-
def lookups(self, request, model_admin):
|
|
28
|
+
def lookups(self, request, model_admin): # noqa: ARG002
|
|
30
29
|
return (
|
|
31
30
|
(PENDING, _("Pending")),
|
|
32
31
|
(COMPLETE, _("Complete")),
|
|
@@ -34,7 +33,7 @@ class StatusListFilter(admin.SimpleListFilter):
|
|
|
34
33
|
("ZERO", _("Zero total")),
|
|
35
34
|
)
|
|
36
35
|
|
|
37
|
-
def queryset(self, request, queryset):
|
|
36
|
+
def queryset(self, request, queryset): # noqa: ARG002
|
|
38
37
|
if self.value():
|
|
39
38
|
if self.value() == PENDING:
|
|
40
39
|
return (
|
|
@@ -67,15 +66,15 @@ class StockRequestAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
67
66
|
list_per_page = 20
|
|
68
67
|
ordering = ("-request_identifier",)
|
|
69
68
|
|
|
70
|
-
autocomplete_fields =
|
|
69
|
+
autocomplete_fields = ("container", "formulation", "location")
|
|
71
70
|
form = StockRequestForm
|
|
72
71
|
|
|
73
|
-
actions =
|
|
72
|
+
actions = (
|
|
74
73
|
prepare_stock_request_items_action,
|
|
75
74
|
allocate_stock_to_subject,
|
|
76
75
|
print_labels_from_stock_request_by_code,
|
|
77
76
|
"delete_selected",
|
|
78
|
-
|
|
77
|
+
)
|
|
79
78
|
|
|
80
79
|
fieldsets = (
|
|
81
80
|
(
|
|
@@ -194,10 +193,7 @@ class StockRequestAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
194
193
|
|
|
195
194
|
@admin.display(description="Container", ordering="container_name")
|
|
196
195
|
def container_str(self, obj):
|
|
197
|
-
return
|
|
198
|
-
"{}",
|
|
199
|
-
mark_safe("<BR>".join(str(obj.container).split(" "))), # nosec B703, B308
|
|
200
|
-
)
|
|
196
|
+
return mark_safe("<BR>".join(str(obj.container).split(" "))) # noqa: S308
|
|
201
197
|
|
|
202
198
|
@admin.display(description="Task")
|
|
203
199
|
def task_status(self, obj):
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from django.contrib import admin
|
|
2
2
|
from django.template.loader import render_to_string
|
|
3
3
|
from django.urls import reverse
|
|
4
|
-
from django.utils.html import format_html
|
|
5
4
|
from django.utils.safestring import mark_safe
|
|
6
5
|
from django_audit_fields import audit_fieldset_tuple
|
|
7
6
|
from rangefilter.filters import DateRangeFilterBuilder
|
|
@@ -39,8 +38,8 @@ class StockRequestItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
39
38
|
show_cancel = True
|
|
40
39
|
list_per_page = 20
|
|
41
40
|
form = StockRequestItemForm
|
|
42
|
-
autocomplete_fields =
|
|
43
|
-
actions =
|
|
41
|
+
autocomplete_fields = ("rx",)
|
|
42
|
+
actions = (print_labels_from_stock_request_item, "delete_selected")
|
|
44
43
|
|
|
45
44
|
fieldsets = (
|
|
46
45
|
(
|
|
@@ -98,27 +97,19 @@ class StockRequestItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
98
97
|
|
|
99
98
|
def get_list_display(self, request):
|
|
100
99
|
fields = super().get_list_display(request)
|
|
101
|
-
|
|
102
|
-
return fields
|
|
100
|
+
return remove_fields_for_blinded_users(request, fields)
|
|
103
101
|
|
|
104
102
|
def get_list_filter(self, request):
|
|
105
103
|
fields = super().get_list_filter(request)
|
|
106
|
-
|
|
107
|
-
return fields
|
|
104
|
+
return remove_fields_for_blinded_users(request, fields)
|
|
108
105
|
|
|
109
106
|
def get_search_fields(self, request):
|
|
110
107
|
fields = super().get_search_fields(request)
|
|
111
|
-
|
|
112
|
-
return fields
|
|
108
|
+
return remove_fields_for_blinded_users(request, fields)
|
|
113
109
|
|
|
114
110
|
@admin.display(description="Product")
|
|
115
111
|
def formulation(self, obj):
|
|
116
|
-
return
|
|
117
|
-
"{}",
|
|
118
|
-
mark_safe(
|
|
119
|
-
f"{obj.stock_request.formulation}<BR>{obj.stock_request.container}"
|
|
120
|
-
), # nosec B703, B308
|
|
121
|
-
)
|
|
112
|
+
return mark_safe(f"{obj.stock_request.formulation}<BR>{obj.stock_request.container}") # noqa: S308
|
|
122
113
|
|
|
123
114
|
@admin.display(description="Date", ordering="request_item_datetime")
|
|
124
115
|
def item_date(self, obj):
|
|
@@ -147,13 +138,13 @@ class StockRequestItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
147
138
|
@admin.display(description="A", boolean=True)
|
|
148
139
|
def allocated(self, obj):
|
|
149
140
|
if obj:
|
|
150
|
-
return
|
|
141
|
+
return bool(getattr(obj, "allocation", None))
|
|
151
142
|
return None
|
|
152
143
|
|
|
153
144
|
@admin.display(description="T", boolean=True)
|
|
154
145
|
def transferred(self, obj):
|
|
155
146
|
if obj and getattr(obj, "allocation", None):
|
|
156
|
-
return
|
|
147
|
+
return bool(obj.allocation.stock.stocktransferitem)
|
|
157
148
|
return False
|
|
158
149
|
|
|
159
150
|
@admin.display(description="Subject", ordering="appt_datetime")
|
|
@@ -211,7 +202,7 @@ class StockRequestItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
211
202
|
|
|
212
203
|
def get_readonly_fields(self, request, obj=None):
|
|
213
204
|
if obj:
|
|
214
|
-
return self.readonly_fields
|
|
205
|
+
return tuple({*self.readonly_fields, "stock_request"})
|
|
215
206
|
return self.readonly_fields
|
|
216
207
|
|
|
217
208
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
|
@@ -21,10 +21,10 @@ class ConfirmedAtSiteListFilter(SimpleListFilter):
|
|
|
21
21
|
title = "Confirmed at site"
|
|
22
22
|
parameter_name = "confirmed_at_site"
|
|
23
23
|
|
|
24
|
-
def lookups(self, request, model_admin):
|
|
24
|
+
def lookups(self, request, model_admin): # noqa: ARG002
|
|
25
25
|
return (YES, YES), (PARTIAL, "Partial"), (NO, NO)
|
|
26
26
|
|
|
27
|
-
def queryset(self, request, queryset):
|
|
27
|
+
def queryset(self, request, queryset): # noqa: ARG002
|
|
28
28
|
qs = None
|
|
29
29
|
if self.value():
|
|
30
30
|
if self.value() == YES:
|
|
@@ -60,10 +60,10 @@ class StockTransferAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
60
60
|
show_object_tools = True
|
|
61
61
|
show_cancel = True
|
|
62
62
|
list_per_page = 20
|
|
63
|
-
ordering =
|
|
63
|
+
ordering = ("-transfer_identifier",)
|
|
64
64
|
|
|
65
|
-
autocomplete_fields =
|
|
66
|
-
actions =
|
|
65
|
+
autocomplete_fields = ("from_location", "to_location")
|
|
66
|
+
actions = (transfer_stock_action, print_transfer_stock_manifest_action)
|
|
67
67
|
|
|
68
68
|
form = StockTransferForm
|
|
69
69
|
|
|
@@ -19,10 +19,10 @@ class ConfirmedAtSiteFilter(SimpleListFilter):
|
|
|
19
19
|
title = "Confirmed at site"
|
|
20
20
|
parameter_name = "confirmed_at_site"
|
|
21
21
|
|
|
22
|
-
def lookups(self, request, model_admin):
|
|
22
|
+
def lookups(self, request, model_admin): # noqa: ARG002
|
|
23
23
|
return (YES, YES), (NO, NO)
|
|
24
24
|
|
|
25
|
-
def queryset(self, request, queryset):
|
|
25
|
+
def queryset(self, request, queryset): # noqa: ARG002
|
|
26
26
|
qs = None
|
|
27
27
|
if self.value():
|
|
28
28
|
opts = dict(
|
|
@@ -53,7 +53,7 @@ class StockTransferItemAdmin(ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
53
53
|
show_cancel = True
|
|
54
54
|
list_per_page = 20
|
|
55
55
|
|
|
56
|
-
autocomplete_fields =
|
|
56
|
+
autocomplete_fields = ("stock",)
|
|
57
57
|
|
|
58
58
|
fieldsets = (
|
|
59
59
|
(
|
|
@@ -27,7 +27,7 @@ class StorageBinAdmin(SiteModelAdminMixin, ModelAdminMixin, SimpleHistoryAdmin):
|
|
|
27
27
|
|
|
28
28
|
form = StorageBinForm
|
|
29
29
|
|
|
30
|
-
actions =
|
|
30
|
+
actions = (add_to_storage_bin_action, move_to_storage_bin_action)
|
|
31
31
|
|
|
32
32
|
fieldsets = (
|
|
33
33
|
(
|
|
@@ -23,8 +23,8 @@ class StorageBinItemAdmin(SiteModelAdminMixin, ModelAdminMixin, SimpleHistoryAdm
|
|
|
23
23
|
show_cancel = True
|
|
24
24
|
list_per_page = 20
|
|
25
25
|
|
|
26
|
-
autocomplete_fields =
|
|
27
|
-
actions =
|
|
26
|
+
autocomplete_fields = ("stock",)
|
|
27
|
+
actions = ("delete_selected",)
|
|
28
28
|
|
|
29
29
|
change_list_note = "Once an item is dispensed it is automatically removed from storage."
|
|
30
30
|
|
|
@@ -2,14 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
|
-
from zoneinfo import ZoneInfo
|
|
6
5
|
|
|
7
6
|
from django.core.exceptions import ObjectDoesNotExist
|
|
8
7
|
|
|
9
8
|
from edc_constants.constants import NO, YES
|
|
10
9
|
from edc_crf.crf_form_validator import CrfFormValidator
|
|
11
10
|
from edc_form_validators import INVALID_ERROR
|
|
12
|
-
from edc_utils import formatted_datetime
|
|
11
|
+
from edc_utils.text import formatted_datetime
|
|
13
12
|
|
|
14
13
|
from ...utils import get_rx_model_cls, get_rxrefill_model_cls
|
|
15
14
|
|
|
@@ -28,20 +27,18 @@ class StudyMedicationFormValidator(CrfFormValidator):
|
|
|
28
27
|
if self.related_visit.appointment.relative_next:
|
|
29
28
|
next_appt_datetime = self.related_visit.appointment.relative_next.appt_datetime
|
|
30
29
|
|
|
31
|
-
if
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
if (
|
|
31
|
+
next_appt_datetime
|
|
32
|
+
and self.refill_start_datetime
|
|
33
|
+
and self.refill_start_datetime > next_appt_datetime
|
|
34
|
+
):
|
|
35
|
+
local_dte = formatted_datetime(next_appt_datetime)
|
|
36
|
+
error_msg = (
|
|
37
|
+
"Refill start date cannot be after next appointmnent date. "
|
|
38
|
+
f"Next appointment date is {local_dte}."
|
|
39
|
+
)
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
{"refill_start_datetime": error_msg}, INVALID_ERROR
|
|
44
|
-
)
|
|
41
|
+
self.raise_validation_error({"refill_start_datetime": error_msg}, INVALID_ERROR)
|
|
45
42
|
|
|
46
43
|
self.validate_refill_start_date_against_offschedule_date()
|
|
47
44
|
|
|
@@ -66,14 +63,14 @@ class StudyMedicationFormValidator(CrfFormValidator):
|
|
|
66
63
|
@property
|
|
67
64
|
def refill_start_datetime(self) -> datetime | None:
|
|
68
65
|
if refill_start_datetime := self.cleaned_data.get("refill_start_datetime"):
|
|
69
|
-
return refill_start_datetime
|
|
70
|
-
return
|
|
66
|
+
return refill_start_datetime
|
|
67
|
+
return None
|
|
71
68
|
|
|
72
69
|
@property
|
|
73
70
|
def refill_end_datetime(self) -> datetime | None:
|
|
74
71
|
if refill_end_datetime := self.cleaned_data.get("refill_end_datetime"):
|
|
75
|
-
return refill_end_datetime
|
|
76
|
-
return
|
|
72
|
+
return refill_end_datetime
|
|
73
|
+
return None
|
|
77
74
|
|
|
78
75
|
@property
|
|
79
76
|
def next_refill(self) -> RxRefill | None:
|
|
@@ -93,14 +90,15 @@ class StudyMedicationFormValidator(CrfFormValidator):
|
|
|
93
90
|
@property
|
|
94
91
|
def rx(self) -> Rx:
|
|
95
92
|
try:
|
|
96
|
-
|
|
93
|
+
obj = get_rx_model_cls().objects.get(
|
|
97
94
|
subject_identifier=self.subject_identifier,
|
|
98
95
|
medications__in=[self.medication],
|
|
99
96
|
)
|
|
100
|
-
except ObjectDoesNotExist:
|
|
97
|
+
except ObjectDoesNotExist as e:
|
|
101
98
|
self.raise_validation_error(
|
|
102
|
-
{"__all__": "Prescription does not exist"}, INVALID_ERROR
|
|
99
|
+
{"__all__": "Prescription does not exist"}, INVALID_ERROR, exc=e
|
|
103
100
|
)
|
|
101
|
+
return obj
|
|
104
102
|
|
|
105
103
|
@property
|
|
106
104
|
def formulation(self) -> Formulation | None:
|
|
@@ -109,11 +107,13 @@ class StudyMedicationFormValidator(CrfFormValidator):
|
|
|
109
107
|
@property
|
|
110
108
|
def medication(self) -> Medication:
|
|
111
109
|
if self.formulation:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
medication = self.formulation.medication
|
|
111
|
+
else:
|
|
112
|
+
self.raise_validation_error(
|
|
113
|
+
{"__all__": "Need the formulation to look up the prescription."},
|
|
114
|
+
INVALID_ERROR,
|
|
115
|
+
)
|
|
116
|
+
return medication
|
|
117
117
|
|
|
118
118
|
@property
|
|
119
119
|
def next_appointment(self) -> Appointment | None:
|
|
@@ -7,7 +7,7 @@ class ConfirmationForm(forms.ModelForm):
|
|
|
7
7
|
class Meta:
|
|
8
8
|
model = Confirmation
|
|
9
9
|
fields = "__all__"
|
|
10
|
-
help_text = {"confirmation_identifier": "(read-only)"}
|
|
11
|
-
widgets = {
|
|
10
|
+
help_text = {"confirmation_identifier": "(read-only)"} # noqa: RUF012
|
|
11
|
+
widgets = { # noqa: RUF012
|
|
12
12
|
"confirmation_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
13
13
|
}
|
|
@@ -7,7 +7,7 @@ class DispenseForm(forms.ModelForm):
|
|
|
7
7
|
class Meta:
|
|
8
8
|
model = Dispense
|
|
9
9
|
fields = "__all__"
|
|
10
|
-
help_text = {"dispense_identifier": "(read-only)"}
|
|
11
|
-
widgets = {
|
|
10
|
+
help_text = {"dispense_identifier": "(read-only)"} # noqa: RUF012
|
|
11
|
+
widgets = { # noqa: RUF012
|
|
12
12
|
"dispense_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
13
13
|
}
|
|
@@ -4,14 +4,14 @@ from ...models import Lot
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class LotForm(forms.ModelForm):
|
|
7
|
-
|
|
8
7
|
def clean(self):
|
|
9
8
|
cleaned_data = super().clean()
|
|
10
|
-
if
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
if (
|
|
10
|
+
cleaned_data.get("product")
|
|
11
|
+
and cleaned_data.get("assignment")
|
|
12
|
+
and cleaned_data.get("product").assignment != cleaned_data.get("assignment")
|
|
13
|
+
):
|
|
14
|
+
raise forms.ValidationError({"assignment": "Assignment does not match product"})
|
|
15
15
|
|
|
16
16
|
return cleaned_data
|
|
17
17
|
|
|
@@ -4,7 +4,6 @@ from ...models import Order, OrderItem
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class OrderForm(forms.ModelForm):
|
|
7
|
-
|
|
8
7
|
def clean(self):
|
|
9
8
|
cleaned_data = super().clean()
|
|
10
9
|
if (
|
|
@@ -21,18 +20,17 @@ class OrderForm(forms.ModelForm):
|
|
|
21
20
|
class Meta:
|
|
22
21
|
model = Order
|
|
23
22
|
fields = "__all__"
|
|
24
|
-
help_text = {"order_identifier": "(read-only)"}
|
|
25
|
-
widgets = {
|
|
23
|
+
help_text = {"order_identifier": "(read-only)"} # noqa: RUF012
|
|
24
|
+
widgets = { # noqa: RUF012
|
|
26
25
|
"order_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
class OrderFormSuper(forms.ModelForm):
|
|
31
|
-
|
|
32
30
|
class Meta:
|
|
33
31
|
model = Order
|
|
34
32
|
fields = "__all__"
|
|
35
|
-
help_text = {"order_identifier": "(read-only)"}
|
|
36
|
-
widgets = {
|
|
33
|
+
help_text = {"order_identifier": "(read-only)"} # noqa: RUF012
|
|
34
|
+
widgets = { # noqa: RUF012
|
|
37
35
|
"order_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
38
36
|
}
|
|
@@ -4,7 +4,6 @@ from ...models import Product, Stock
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class ProductForm(forms.ModelForm):
|
|
7
|
-
|
|
8
7
|
def clean(self):
|
|
9
8
|
cleaned_data = super().clean()
|
|
10
9
|
if (
|
|
@@ -17,7 +16,7 @@ class ProductForm(forms.ModelForm):
|
|
|
17
16
|
class Meta:
|
|
18
17
|
model = Product
|
|
19
18
|
fields = "__all__"
|
|
20
|
-
help_text = {"product_identifier": "(read-only)"}
|
|
21
|
-
widgets = {
|
|
19
|
+
help_text = {"product_identifier": "(read-only)"} # noqa: RUF012
|
|
20
|
+
widgets = { # noqa: RUF012
|
|
22
21
|
"product_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
23
22
|
}
|
|
@@ -16,8 +16,8 @@ class ReceiveForm(forms.ModelForm):
|
|
|
16
16
|
class Meta:
|
|
17
17
|
model = Receive
|
|
18
18
|
fields = "__all__"
|
|
19
|
-
help_text = {"receive_identifier": "(read-only)"}
|
|
20
|
-
widgets = {
|
|
19
|
+
help_text = {"receive_identifier": "(read-only)"} # noqa: RUF012
|
|
20
|
+
widgets = { # noqa: RUF012
|
|
21
21
|
"receive_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -26,7 +26,7 @@ class ReceiveFormSuper(forms.ModelForm):
|
|
|
26
26
|
class Meta:
|
|
27
27
|
model = Receive
|
|
28
28
|
fields = "__all__"
|
|
29
|
-
help_text = {"receive_identifier": "(read-only)"}
|
|
30
|
-
widgets = {
|
|
29
|
+
help_text = {"receive_identifier": "(read-only)"} # noqa: RUF012
|
|
30
|
+
widgets = { # noqa: RUF012
|
|
31
31
|
"receive_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
32
32
|
}
|
|
@@ -7,7 +7,6 @@ from ...models import ReceiveItem
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class ReceiveItemForm(forms.ModelForm):
|
|
10
|
-
|
|
11
10
|
def clean(self):
|
|
12
11
|
cleaned_data = super().clean()
|
|
13
12
|
if not self.instance.id:
|
|
@@ -18,12 +17,15 @@ class ReceiveItemForm(forms.ModelForm):
|
|
|
18
17
|
if not cleaned_data.get("container"):
|
|
19
18
|
raise forms.ValidationError({"container": "This field is required"})
|
|
20
19
|
|
|
21
|
-
if
|
|
22
|
-
|
|
20
|
+
if (
|
|
21
|
+
cleaned_data.get("order_item")
|
|
22
|
+
and cleaned_data.get("lot")
|
|
23
|
+
and (
|
|
23
24
|
cleaned_data.get("order_item").product.assignment
|
|
24
25
|
!= cleaned_data.get("lot").assignment
|
|
25
|
-
)
|
|
26
|
-
|
|
26
|
+
)
|
|
27
|
+
):
|
|
28
|
+
raise forms.ValidationError({"lot": "Lot assignment does not match product"})
|
|
27
29
|
|
|
28
30
|
# in unit_qty's
|
|
29
31
|
if cleaned_data.get("qty"):
|
|
@@ -4,7 +4,6 @@ from ...models import RepackRequest
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class RepackRequestForm(forms.ModelForm):
|
|
7
|
-
|
|
8
7
|
def clean(self):
|
|
9
8
|
cleaned_data = super().clean()
|
|
10
9
|
if cleaned_data.get("from_stock") and not getattr(
|
|
@@ -31,11 +30,14 @@ class RepackRequestForm(forms.ModelForm):
|
|
|
31
30
|
> cleaned_data.get("from_stock").container.qty
|
|
32
31
|
):
|
|
33
32
|
raise forms.ValidationError({"container": "Cannot pack into larger container."})
|
|
34
|
-
if
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
if (
|
|
34
|
+
cleaned_data.get("requested_qty")
|
|
35
|
+
and self.instance.processed_qty
|
|
36
|
+
and cleaned_data.get("requested_qty") < self.instance.processed_qty
|
|
37
|
+
):
|
|
38
|
+
raise forms.ValidationError(
|
|
39
|
+
{"requested_qty": "Cannot be less than the number of containers processed"}
|
|
40
|
+
)
|
|
39
41
|
if (
|
|
40
42
|
cleaned_data.get("requested_qty") * cleaned_data.get("container").qty
|
|
41
43
|
> cleaned_data.get("from_stock").unit_qty
|
|
@@ -56,9 +58,9 @@ class RepackRequestForm(forms.ModelForm):
|
|
|
56
58
|
class Meta:
|
|
57
59
|
model = RepackRequest
|
|
58
60
|
fields = "__all__"
|
|
59
|
-
help_text = {
|
|
61
|
+
help_text = { # noqa: RUF012
|
|
60
62
|
"repack_identifier": "(read-only)",
|
|
61
63
|
}
|
|
62
|
-
widgets = {
|
|
64
|
+
widgets = { # noqa: RUF012
|
|
63
65
|
"repack_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
64
66
|
}
|