|
1
|
|
|
# -*- coding: utf-8 -*- |
|
2
|
|
|
# |
|
3
|
|
|
# This file is part of SENAITE.CORE |
|
4
|
|
|
# |
|
5
|
|
|
# Copyright 2018 by it's authors. |
|
6
|
|
|
# Some rights reserved. See LICENSE.rst, CONTRIBUTORS.rst. |
|
7
|
|
|
|
|
8
|
|
|
from AccessControl import getSecurityManager |
|
9
|
|
|
from DateTime import DateTime |
|
10
|
|
|
from plone.app.layout.globals.interfaces import IViewView |
|
11
|
|
|
from Products.Archetypes.config import REFERENCE_CATALOG |
|
12
|
|
|
from Products.CMFCore.utils import getToolByName |
|
13
|
|
|
from Products.CMFPlone.i18nl10n import ulocalized_time |
|
14
|
|
|
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile |
|
15
|
|
|
from zope.interface import implements |
|
16
|
|
|
from bika.lims.catalog import CATALOG_ANALYSIS_LISTING |
|
17
|
|
|
from bika.lims.config import PRIORITIES |
|
18
|
|
|
from bika.lims import bikaMessageFactory as _ |
|
19
|
|
|
from bika.lims import EditResults, EditWorksheet, ManageWorksheets |
|
20
|
|
|
from bika.lims import PMF, logger |
|
21
|
|
|
from bika.lims.browser.bika_listing import BikaListingView |
|
22
|
|
|
from bika.lims.browser.worksheet.tools import checkUserManage |
|
23
|
|
|
from bika.lims.browser.worksheet.tools import showRejectionMessage |
|
24
|
|
|
from bika.lims.utils import t |
|
25
|
|
|
from bika.lims.vocabularies import CatalogVocabulary |
|
26
|
|
|
|
|
27
|
|
|
|
|
28
|
|
|
class AddAnalysesView(BikaListingView): |
|
29
|
|
|
implements(IViewView) |
|
30
|
|
|
template = ViewPageTemplateFile("../templates/add_analyses.pt") |
|
31
|
|
|
|
|
32
|
|
|
def __init__(self, context, request): |
|
33
|
|
|
BikaListingView.__init__(self, context, request) |
|
34
|
|
|
self.icon = self.portal_url + "/++resource++bika.lims.images/worksheet_big.png" |
|
35
|
|
|
self.title = self.context.translate(_("Add Analyses")) |
|
36
|
|
|
self.description = "" |
|
37
|
|
|
self.catalog = CATALOG_ANALYSIS_LISTING |
|
38
|
|
|
self.context_actions = {} |
|
39
|
|
|
# initial review state for first form display of the worksheet |
|
40
|
|
|
# add_analyses search view - first batch of analyses, latest first. |
|
41
|
|
|
self.sort_on = 'Priority' |
|
42
|
|
|
self.contentFilter = {'portal_type': 'Analysis', |
|
43
|
|
|
'review_state':'sample_received', |
|
44
|
|
|
'worksheetanalysis_review_state':'unassigned', |
|
45
|
|
|
'sort_on': 'getPrioritySortkey', |
|
46
|
|
|
'cancellation_state':'active'} |
|
47
|
|
|
self.base_url = self.context.absolute_url() |
|
48
|
|
|
self.view_url = self.base_url + "/add_analyses" |
|
49
|
|
|
self.show_sort_column = False |
|
50
|
|
|
self.show_select_row = False |
|
51
|
|
|
self.show_select_column = True |
|
52
|
|
|
self.pagesize = 50 |
|
53
|
|
|
|
|
54
|
|
|
self.columns = { |
|
55
|
|
|
'Priority': { |
|
56
|
|
|
'title': '', |
|
57
|
|
|
'sortable': True, |
|
58
|
|
|
'index': 'getPrioritySortkey' }, |
|
59
|
|
|
'Client': { |
|
60
|
|
|
'title': _('Client'), |
|
61
|
|
|
'attr': 'getClientTitle', |
|
62
|
|
|
'replace_url': 'getClientURL', |
|
63
|
|
|
'index':'getClientTitle'}, |
|
64
|
|
|
'getClientOrderNumber': { |
|
65
|
|
|
'title': _('Order'), |
|
66
|
|
|
'index': 'getClientOrderNumber'}, |
|
67
|
|
|
'getRequestID': { |
|
68
|
|
|
'title': _('Request ID'), |
|
69
|
|
|
'attr': 'getRequestID', |
|
70
|
|
|
'replace_url': 'getRequestURL', |
|
71
|
|
|
'index': 'getRequestID'}, |
|
72
|
|
|
'CategoryTitle': { |
|
73
|
|
|
'title': _('Category'), |
|
74
|
|
|
'attr': 'getCategoryTitle', |
|
75
|
|
|
'sortable': False}, |
|
76
|
|
|
'Title': { |
|
77
|
|
|
'title': _('Analysis'), |
|
78
|
|
|
'index':'getId'}, |
|
79
|
|
|
'getDateReceived': { |
|
80
|
|
|
'title': _('Date Received'), |
|
81
|
|
|
'index': 'getDateReceived'}, |
|
82
|
|
|
'getDueDate': { |
|
83
|
|
|
'title': _('Due Date'), |
|
84
|
|
|
'index': 'getDueDate'}, |
|
85
|
|
|
} |
|
86
|
|
|
self.filter_indexes = ['Title',] |
|
87
|
|
|
self.review_states = [ |
|
88
|
|
|
{'id':'default', |
|
89
|
|
|
'title': _('All'), |
|
90
|
|
|
'contentFilter': {}, |
|
91
|
|
|
'transitions': [{'id':'assign'}, ], |
|
92
|
|
|
'columns':['Priority', |
|
93
|
|
|
'Client', |
|
94
|
|
|
'getClientOrderNumber', |
|
95
|
|
|
'getRequestID', |
|
96
|
|
|
'CategoryTitle', |
|
97
|
|
|
'Title', |
|
98
|
|
|
'getDateReceived', |
|
99
|
|
|
'getDueDate'], |
|
100
|
|
|
}, |
|
101
|
|
|
] |
|
102
|
|
|
|
|
103
|
|
|
def __call__(self): |
|
104
|
|
|
if not(getSecurityManager().checkPermission(EditWorksheet, self.context)): |
|
105
|
|
|
self.request.response.redirect(self.context.absolute_url()) |
|
106
|
|
|
return |
|
107
|
|
|
|
|
108
|
|
|
# Deny access to foreign analysts |
|
109
|
|
|
if checkUserManage(self.context, self.request) == False: |
|
110
|
|
|
return [] |
|
111
|
|
|
|
|
112
|
|
|
showRejectionMessage(self.context) |
|
113
|
|
|
|
|
114
|
|
|
form = self.request.form |
|
115
|
|
|
rc = getToolByName(self.context, REFERENCE_CATALOG) |
|
116
|
|
|
if 'submitted' in form: |
|
117
|
|
|
if 'getWorksheetTemplate' in form and form['getWorksheetTemplate']: |
|
118
|
|
|
layout = self.context.getLayout() |
|
119
|
|
|
wst = rc.lookupObject(form['getWorksheetTemplate']) |
|
120
|
|
|
self.request['context_uid'] = self.context.UID() |
|
121
|
|
|
self.context.applyWorksheetTemplate(wst) |
|
122
|
|
|
if len(self.context.getLayout()) != len(layout): |
|
123
|
|
|
self.context.plone_utils.addPortalMessage( |
|
124
|
|
|
PMF("Changes saved.")) |
|
125
|
|
|
self.request.RESPONSE.redirect(self.context.absolute_url() + |
|
126
|
|
|
"/manage_results") |
|
127
|
|
|
else: |
|
128
|
|
|
self.context.plone_utils.addPortalMessage( |
|
129
|
|
|
_("No analyses were added to this worksheet.")) |
|
130
|
|
|
self.request.RESPONSE.redirect(self.context.absolute_url() + |
|
131
|
|
|
"/add_analyses") |
|
132
|
|
|
|
|
133
|
|
|
filter_mapping = { |
|
134
|
|
|
"FilterByCategory": "getCategoryUID", |
|
135
|
|
|
"FilterByService": "getServiceUID", |
|
136
|
|
|
"FilterByClient": "getClientUID", |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
for filter_key, cat_index in filter_mapping.items(): |
|
140
|
|
|
form_key = "{}_{}".format(self.form_id, filter_key) |
|
141
|
|
|
value = form.get(form_key) |
|
142
|
|
|
if value is None: |
|
143
|
|
|
continue |
|
144
|
|
|
self.contentFilter[cat_index] = value |
|
145
|
|
|
|
|
146
|
|
|
self.update() |
|
147
|
|
|
|
|
148
|
|
|
if self.request.get('table_only', '') == self.form_id or \ |
|
149
|
|
|
self.request.get('rows_only', '') == self.form_id: |
|
150
|
|
|
return self.contents_table() |
|
151
|
|
|
else: |
|
152
|
|
|
return self.template() |
|
153
|
|
|
|
|
154
|
|
|
def isItemAllowed(self, obj): |
|
155
|
|
|
""" |
|
156
|
|
|
Checks if the passed in Analysis must be displayed in the list. If the |
|
157
|
|
|
'filtering by department' option is enabled in Bika Setup, this |
|
158
|
|
|
function checks if the Analysis Service associated to the Analysis |
|
159
|
|
|
is assigned to any of the currently selected departments (information |
|
160
|
|
|
stored in a cookie). |
|
161
|
|
|
If department filtering is disabled in bika_setup, returns True. |
|
162
|
|
|
If the obj is None or empty, returns False. |
|
163
|
|
|
If the obj has no department assined, returns True. |
|
164
|
|
|
|
|
165
|
|
|
:param obj: A single Analysis brain |
|
166
|
|
|
:type obj: CatalogBrain |
|
167
|
|
|
:returns: True if the item can be added to the list. |
|
168
|
|
|
:rtype: bool |
|
169
|
|
|
""" |
|
170
|
|
|
if not obj: |
|
171
|
|
|
return False |
|
172
|
|
|
|
|
173
|
|
|
if not self.isAllowDepartmentFiltering: |
|
174
|
|
|
return True |
|
175
|
|
|
|
|
176
|
|
|
# Department filtering is enabled. Check if the Analysis Service |
|
177
|
|
|
# associated to this Analysis is assigned to at least one of the |
|
178
|
|
|
# departments currently selected. |
|
179
|
|
|
depuid = obj.getDepartmentUID |
|
180
|
|
|
deps = self.request.get('filter_by_department_info', '') |
|
181
|
|
|
return not depuid or depuid in deps.split(',') |
|
182
|
|
|
|
|
183
|
|
|
def folderitems(self): |
|
184
|
|
|
self.isAllowDepartmentFiltering =\ |
|
185
|
|
|
self.context.bika_setup.getAllowDepartmentFiltering() |
|
186
|
|
|
# Check if mtool has been initialized |
|
187
|
|
|
self.mtool = self.mtool if self.mtool\ |
|
188
|
|
|
else getToolByName(self.context, 'portal_membership') |
|
189
|
|
|
# Getting the current user |
|
190
|
|
|
self.member = self.member if self.member\ |
|
191
|
|
|
else self.mtool.getAuthenticatedMember() |
|
192
|
|
|
self.roles = self.member.getRoles() |
|
193
|
|
|
self.hideclientlink = 'RegulatoryInspector' in self.roles \ |
|
194
|
|
|
and 'Manager' not in self.roles \ |
|
195
|
|
|
and 'LabManager' not in self.roles \ |
|
196
|
|
|
and 'LabClerk' not in self.roles |
|
197
|
|
|
items = BikaListingView.folderitems(self, classic=False) |
|
198
|
|
|
return items |
|
199
|
|
|
|
|
200
|
|
|
def folderitem(self, obj, item, index): |
|
201
|
|
|
""" |
|
202
|
|
|
:obj: is a brain |
|
203
|
|
|
""" |
|
204
|
|
|
# Call the folderitem method from the base class |
|
205
|
|
|
item = BikaListingView.folderitem(self, obj, item, index) |
|
206
|
|
|
item['getDateReceived'] = self.ulocalized_time(obj.getDateReceived) |
|
207
|
|
|
DueDate = obj.getDueDate |
|
208
|
|
|
item['getDueDate'] = self.ulocalized_time(DueDate) |
|
209
|
|
|
if DueDate and DueDate < DateTime(): |
|
210
|
|
|
item['after']['DueDate'] = '<img width="16" height="16"' |
|
211
|
|
|
' src="%s/++resource++bika.lims.images/late.png" title="%s"/>' % \ |
|
212
|
|
|
(self.context.absolute_url(), |
|
213
|
|
|
t(_("Late Analysis"))) |
|
214
|
|
|
if self.hideclientlink: |
|
215
|
|
|
del item['replace']['Client'] |
|
216
|
|
|
# Add Priority column |
|
217
|
|
|
priority_sort_key = obj.getPrioritySortkey |
|
218
|
|
|
if not priority_sort_key: |
|
219
|
|
|
# Default priority is Medium = 3. |
|
220
|
|
|
# The format of PrioritySortKey is <priority>.<created> |
|
221
|
|
|
priority_sort_key = '3.%s' % obj.created.ISO8601() |
|
222
|
|
|
priority = priority_sort_key.split('.')[0] |
|
223
|
|
|
priority_text = PRIORITIES.getValue(priority) |
|
224
|
|
|
priority_div = '<div class="priority-ico priority-%s"><span class="notext">%s</span><div>' |
|
225
|
|
|
item['replace']['Priority'] = priority_div % (priority, priority_text) |
|
226
|
|
|
return item |
|
227
|
|
|
|
|
228
|
|
|
def getServices(self): |
|
229
|
|
|
vocabulary = CatalogVocabulary(self) |
|
230
|
|
|
vocabulary.catalog = 'bika_setup_catalog' |
|
231
|
|
|
categoryUID = self.request.get('list_getCategoryUID', '') |
|
232
|
|
|
if categoryUID: |
|
233
|
|
|
return vocabulary( |
|
234
|
|
|
portal_type='AnalysisService', |
|
235
|
|
|
getCategoryUID=categoryUID, |
|
236
|
|
|
sort_on='sortable_title' |
|
237
|
|
|
) |
|
238
|
|
|
return vocabulary( |
|
239
|
|
|
portal_type='AnalysisService', |
|
240
|
|
|
sort_on='sortable_title' |
|
241
|
|
|
) |
|
242
|
|
|
|
|
243
|
|
|
def getClients(self): |
|
244
|
|
|
vocabulary = CatalogVocabulary(self) |
|
245
|
|
|
return vocabulary(portal_type='Client', sort_on='sortable_title') |
|
246
|
|
|
|
|
247
|
|
|
def getCategories(self): |
|
248
|
|
|
vocabulary = CatalogVocabulary(self) |
|
249
|
|
|
vocabulary.catalog = 'bika_setup_catalog' |
|
250
|
|
|
return vocabulary( |
|
251
|
|
|
portal_type='AnalysisCategory', sort_on='sortable_title') |
|
252
|
|
|
|
|
253
|
|
|
def getWorksheetTemplates(self): |
|
254
|
|
|
""" Return WS Templates """ |
|
255
|
|
|
vocabulary = CatalogVocabulary(self) |
|
256
|
|
|
vocabulary.catalog = 'bika_setup_catalog' |
|
257
|
|
|
return vocabulary( |
|
258
|
|
|
portal_type='WorksheetTemplate', sort_on='sortable_title') |
|
259
|
|
|
|