diff --git a/CHANGES.rst b/CHANGES.rst index 8a3a0834be..92e1f4973f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,7 @@ Changelog 2.3.0 (unreleased) ------------------ +- #2114 Allow Multi Analysis Results Entry - #2111 Replace header table with customizable sample header viewlet - #2110 Add a more descriptive message for "Reject" action inside a Worksheet - #2104 Fix result formatting when result is below LDL or above UDL diff --git a/src/bika/lims/browser/analysisrequest/add2.py b/src/bika/lims/browser/analysisrequest/add2.py index 059120c5b5..84c0c0d1b5 100644 --- a/src/bika/lims/browser/analysisrequest/add2.py +++ b/src/bika/lims/browser/analysisrequest/add2.py @@ -1680,11 +1680,21 @@ def ajax_submit(self): # Automatic label printing setup = api.get_setup() auto_print = setup.getAutoPrintStickers() - if 'register' in auto_print and ARs: - return { - 'success': message, - 'stickers': ARs.values(), - 'stickertemplate': setup.getAutoStickerTemplate() - } - else: - return {'success': message} + immediate_results_entry = setup.getImmediateResultsEntry() + redirect_to = self.context.absolute_url() + sample_uids = ARs.values() + if "register" in auto_print and sample_uids: + redirect_to = "{}/sticker?autoprint=1&template={}&items={}".format( + self.context.absolute_url(), + setup.getAutoStickerTemplate(), + ",".join(sample_uids) + ) + elif immediate_results_entry and sample_uids: + redirect_to = "{}/multi_results?uids={}".format( + self.context.absolute_url(), + ",".join(sample_uids) + ) + return { + "success": message, + "redirect_to": redirect_to, + } diff --git a/src/bika/lims/browser/stickers.py b/src/bika/lims/browser/stickers.py index a49f2dc1f1..7bff8661d0 100644 --- a/src/bika/lims/browser/stickers.py +++ b/src/bika/lims/browser/stickers.py @@ -109,6 +109,22 @@ def __call__(self): return self.template() + def get_back_url(self): + """Calculate the Back URL + """ + url = api.get_url(self.context) + portal_type = api.get_portal_type(self.context) + redirect_contexts = ['Client', 'AnalysisRequest', 'Samples', 'Batch'] + if portal_type not in redirect_contexts: + parent = api.get_parent(self.context) + url = api.get_url(parent) + # redirect to direct results entry + setup = api.get_setup() + if setup.getImmediateResultsEntry(): + url = "{}/multi_results?uids={}".format( + url, ",".join(self.get_uids())) + return url + def get_items(self): """Returns a list of SuperModel items """ diff --git a/src/bika/lims/browser/templates/stickers_preview.pt b/src/bika/lims/browser/templates/stickers_preview.pt index b95ac8ea3a..b4b558ff63 100644 --- a/src/bika/lims/browser/templates/stickers_preview.pt +++ b/src/bika/lims/browser/templates/stickers_preview.pt @@ -6,13 +6,7 @@ tal:attributes="lang default_language|default; xml:lang default_language|default;" i18n:domain="senaite.core" - tal:define="portal_state context/@@plone_portal_state; - portal_url portal_state/portal_url; - plone_view context/@@plone; - portal portal_state/portal; - portal_type python:context.portal_type; - anchor_self python:('Client','AnalysisRequest', 'Samples', 'Batch'); - goback_url python:context.absolute_url() if portal_type in anchor_self else context.aq_parent.absolute_url();"> + tal:define="goback_url python:view.get_back_url();">
diff --git a/src/bika/lims/content/bikasetup.py b/src/bika/lims/content/bikasetup.py index 65803138f3..a0ab106eee 100644 --- a/src/bika/lims/content/bikasetup.py +++ b/src/bika/lims/content/bikasetup.py @@ -338,6 +338,22 @@ def getCounterTypes(self, instance=None): "configured in individual Analysis Services."), ) ), + BooleanField( + "ImmediateResultsEntry", + schemata="Analyses", + default=False, + widget=BooleanWidget( + label=_("label_bikasetup_immediateresultsentry", + default=u"Immediate results entry"), + description=_( + "description_bikasetup_immediateresultsentry", + default=u"Allow the user to directly enter results after " + "sample creation, e.g. to enter field results immediately, or " + "lab results, when the automatic sample reception is " + "activated." + ), + ), + ), BooleanField( 'EnableAnalysisRemarks', schemata="Analyses", @@ -1032,5 +1048,22 @@ def setEnableGlobalAuditlog(self, value): if setup: setup.setEnableGlobalAuditlog(value) + def getImmediateResultsEntry(self): + """Get the value from the senaite setup + """ + setup = api.get_senaite_setup() + # setup is `None` during initial site content structure installation + if setup: + return setup.getImmediateResultsEntry() + return False + + def setImmediateResultsEntry(self, value): + """Set the value in the senaite setup + """ + setup = api.get_senaite_setup() + # setup is `None` during initial site content structure installation + if setup: + setup.setImmediateResultsEntry(value) + registerType(BikaSetup, PROJECTNAME) diff --git a/src/bika/lims/permissions.py b/src/bika/lims/permissions.py index 9ac523396d..9bbbd882fe 100644 --- a/src/bika/lims/permissions.py +++ b/src/bika/lims/permissions.py @@ -114,6 +114,7 @@ TransitionDispatchSample = "senaite.core: Transition: Dispatch Sample" TransitionRestoreSample = "senaite.core: Transition: Restore Sample" TransitionCreatePartitions = "senaite.core: Transition: Create Partitions" +TransitionMultiResults = "senaite.core: Transition: Multi Results" # Type-specific permissions diff --git a/src/bika/lims/permissions.zcml b/src/bika/lims/permissions.zcml index c77365d7bb..9c360c3f7a 100644 --- a/src/bika/lims/permissions.zcml +++ b/src/bika/lims/permissions.zcml @@ -86,6 +86,7 @@ + # Object-specific permissions # --------------------------- diff --git a/src/bika/lims/workflow/analysisrequest/guards.py b/src/bika/lims/workflow/analysisrequest/guards.py index 91922cd17e..9f10575da1 100644 --- a/src/bika/lims/workflow/analysisrequest/guards.py +++ b/src/bika/lims/workflow/analysisrequest/guards.py @@ -241,3 +241,9 @@ def guard_restore(sample): """Checks if the restore transition is allowed """ return True + + +def guard_multi_results(sample): + """Checks if the multi results action is allowed + """ + return True diff --git a/src/senaite/core/adapters/configure.zcml b/src/senaite/core/adapters/configure.zcml index c73e2ffe15..c6d259cd6a 100644 --- a/src/senaite/core/adapters/configure.zcml +++ b/src/senaite/core/adapters/configure.zcml @@ -23,6 +23,18 @@ provides="bika.lims.interfaces.IWorkflowActionAdapter" permission="zope.Public" /> + + + diff --git a/src/senaite/core/adapters/sample.py b/src/senaite/core/adapters/sample.py index aa8b3bc633..49178e5467 100644 --- a/src/senaite/core/adapters/sample.py +++ b/src/senaite/core/adapters/sample.py @@ -17,3 +17,16 @@ def __call__(self, action, uids): url = "{}/dispatch_samples?uids={}".format( api.get_url(self.context), ",".join(uids)) return self.redirect(redirect_url=url) + + +@implementer(IWorkflowActionUIDsAdapter) +class WorkflowActionMultiResultsAdapter(RequestContextAware): + """Redirects to multi results view + """ + + def __call__(self, action, uids): + """Redirects the user to the multi results form + """ + url = "{}/multi_results?uids={}".format( + api.get_url(self.context), ",".join(uids)) + return self.redirect(redirect_url=url) diff --git a/src/senaite/core/browser/contentmenu/view.py b/src/senaite/core/browser/contentmenu/view.py index c01f5f17b9..910df3add1 100644 --- a/src/senaite/core/browser/contentmenu/view.py +++ b/src/senaite/core/browser/contentmenu/view.py @@ -24,6 +24,10 @@ from zope.browsermenu.interfaces import IBrowserMenu from zope.component import getUtility +HIDE_WF_ITEMS = [ + "workflow-transition-advanced" +] + class ContentMenuProvider(Base): """Content menu provider for the "view" tab: displays the menu @@ -41,12 +45,20 @@ def fiddle_menu_item(self, item): Unfortunately, this can not be done more elegant w/o overrides.zcml. https://stackoverflow.com/questions/11904155/disable-advanced-in-workflow-status-menu-in-plone """ + def is_wf_item_visible(item): + """Checks if the WF items is visible or not + """ + extra = item.get("extra", {}) + wfid = extra.get("id") + if wfid in HIDE_WF_ITEMS: + return False + return True + action = item.get("action") if action.endswith("content_status_history"): # remove the "Advanced ..." submenu - submenu = filter( - lambda m: m.get("title") != "label_advanced", - item.get("submenu", []) or []) + submenu = item.get("submenu", []) or [] + submenu = filter(is_wf_item_visible, submenu) item["submenu"] = submenu return item diff --git a/src/senaite/core/browser/samples/configure.zcml b/src/senaite/core/browser/samples/configure.zcml index 74b8d9fbae..dc58d16523 100644 --- a/src/senaite/core/browser/samples/configure.zcml +++ b/src/senaite/core/browser/samples/configure.zcml @@ -19,6 +19,13 @@ permission="senaite.core.permissions.TransitionDispatchSample" layer="senaite.core.interfaces.ISenaiteCore" /> + +