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

Allow to detach a partition from its primary sample #1420

Merged
merged 4 commits into from
Aug 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Changelog

**Added**

- #1420 Allow to detach a partition from its primary sample
- #1410 Email API


Expand Down
7 changes: 7 additions & 0 deletions bika/lims/browser/viewlets/analysisrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,10 @@ class RejectedAnalysisRequestViewlet(ViewletBase):
"""Current ANalysis Request was rejected. Display the reasons
"""
template = ViewPageTemplateFile("templates/rejected_ar_viewlet.pt")


class DetachedPartitionViewlet(ViewletBase):
"""Prints a viewlet that displays the Primary Sample the sample was
detached from
"""
template = ViewPageTemplateFile("templates/detached_partition_viewlet.pt")
11 changes: 11 additions & 0 deletions bika/lims/browser/viewlets/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,17 @@
layer="bika.lims.interfaces.IBikaLIMS"
/>

<!-- Dettached partition viewlet -->
<browser:viewlet
for="bika.lims.interfaces.IAnalysisRequest"
name="bika.lims.detached_partition_viewlet"
class=".analysisrequest.DetachedPartitionViewlet"
manager="plone.app.layout.viewlets.interfaces.IAboveContent"
template="templates/detached_partition_viewlet.pt"
permission="zope2.View"
layer="bika.lims.interfaces.IBikaLIMS"
/>

<!-- Secondary Analysis Request viewlet -->
<browser:viewlet
for="bika.lims.interfaces.IAnalysisRequestSecondary"
Expand Down
21 changes: 21 additions & 0 deletions bika/lims/browser/viewlets/templates/detached_partition_viewlet.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div tal:omit-tag=""
tal:define="primary python: view.context.getDetachedFrom()"
tal:condition="python: primary"
i18n:domain="senaite.core">

<div class="visualClear"></div>

<div id="portal-alert">
<div class="portlet-alert-item alert alert-info alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong i18n:translate="">Info</strong>
<p class="title">
<span i18n:translate="">This is a detached partition from Sample </span>
<a tal:attributes="href python:primary.absolute_url()"
tal:content="python:primary.getId()"></a>
</p>
</div>
</div>
</div>
14 changes: 14 additions & 0 deletions bika/lims/content/analysisrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,20 @@
),
),

# The Primary Sample the current sample was detached from
ReferenceField(
"DetachedFrom",
allowed_types=("AnalysisRequest",),
relationship="AnalysisRequestDetachedFrom",
referenceClass=HoldingReference,
mode="rw",
read_permission=View,
write_permission=ModifyPortalContent,
widget=ReferenceWidget(
visible=False,
)
),

# The Analysis Request the current Analysis Request comes from because of
# an invalidation of the former
ReferenceField(
Expand Down
6 changes: 6 additions & 0 deletions bika/lims/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,12 @@ class IReceived(Interface):
"""Marker interface for received objects
"""


class IInternalUse(Interface):
"""Marker interface for objects only lab personnel must have access
"""


class IDetachedPartition(Interface):
"""Marker interface for samples that have been detached from its primary
"""
1 change: 1 addition & 0 deletions bika/lims/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@

# Transition permissions (Analysis Request)
TransitionCancelAnalysisRequest = "senaite.core: Transition: Cancel Analysis Request"
TransitionDetachSamplePartition = "senaite.core: Transition: Detach Sample Partition"
TransitionReinstateAnalysisRequest = "senaite.core: Transition: Reinstate Analysis Request"
TransitionInvalidate = "senaite.core: Transition Invalidate"
TransitionPreserveSample = "senaite.core: Transition: Preserve Sample"
Expand Down
1 change: 1 addition & 0 deletions bika/lims/permissions.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@

# Transition Permissions (Analysis Request)
<permission id="senaite.core.permissions.TransitionCancelAnalysisRequest" title="senaite.core: Transition: Cancel Analysis Request"/>
<permission id="senaite.core.permissions.TransitionDetachSamplePartition" title="senaite.core: Transition: Detach Sample Partition" />
<permission id="senaite.core.permissions.TransitionReinstateAnalysisRequest" title="senaite.core: Transition: Reinstate Analysis Request"/>
<permission id="senaite.core.permissions.TransitionInvalidate" title="senaite.core: Transition: Invalidate"/>
<permission id="senaite.core.permissions.TransitionPreserveSample" title="senaite.core: Transition: Preserve Sample"/>
Expand Down
5 changes: 5 additions & 0 deletions bika/lims/profiles/default/rolemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,11 @@
<role name="Manager"/>
<role name="Owner"/>
</permission>
<permission name="senaite.core: Transition: Detach Sample Partition" acquire="False">
<role name="LabManager"/>
<role name="Manager"/>
<role name="LabClerk"/>
</permission>
<permission name="senaite.core: Transition: Reinstate Analysis Request" acquire="False">
<role name="LabClerk"/>
<role name="LabManager"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<!-- *** MANAGED PERMISSIONS *** -->
<!-- Transitions (governs guards) -->
<permission>senaite.core: Transition: Cancel Analysis Request</permission>
<permission>senaite.core: Transition: Detach Sample Partition</permission>
<permission>senaite.core: Transition: Reinstate Analysis Request</permission>
<permission>senaite.core: Transition: Invalidate</permission>
<permission>senaite.core: Transition: Preserve Sample</permission>
Expand Down Expand Up @@ -108,6 +109,7 @@
<!-- MANAGED PERMISSIONS -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="False"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -194,6 +196,7 @@
<!-- MANAGED PERMISSIONS -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="True"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="False"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -292,6 +295,7 @@
<!-- MANAGED PERMISSIONS -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="True"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="False"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -367,6 +371,7 @@

<!-- TRANSITIONS -->
<exit-transition transition_id="cancel"/>
<exit-transition transition_id="detach" />
<exit-transition transition_id="receive"/>
<exit-transition transition_id="submit"/>
<exit-transition transition_id="reject" />
Expand All @@ -385,6 +390,7 @@
<!-- MANAGED PERMISSIONS -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="True"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="True"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -454,6 +460,7 @@

<!-- TRANSITIONS -->
<exit-transition transition_id="cancel"/>
<exit-transition transition_id="detach"/>
<exit-transition transition_id="prepublish"/>
<exit-transition transition_id="submit"/>
<exit-transition transition_id="reject" />
Expand All @@ -471,6 +478,7 @@
<!-- MANAGED PERMISSIONS -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="True"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="True"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -626,6 +634,7 @@
<exit-transition transition_id="retract" />
<exit-transition transition_id="reject" />
<exit-transition transition_id="rollback_to_receive" />
<exit-transition transition_id="detach" />
<!-- /TRANSITIONS -->

<permission-map name="Delete objects" acquired="False">
Expand All @@ -638,6 +647,7 @@
<!-- MANAGED PERMISSIONS -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="True"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -714,6 +724,7 @@
<exit-transition transition_id="cancel"/>
<exit-transition transition_id="preserve"/>
<exit-transition transition_id="reject" />
<exit-transition transition_id="detach" />
<!-- /TRANSITIONS -->

<permission-map name="Delete objects" acquired="False">
Expand All @@ -726,6 +737,7 @@
<!-- MANAGED PERMISSIONS -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="True"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="True"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="True"/>
Expand Down Expand Up @@ -796,6 +808,7 @@
<exit-transition transition_id="publish" />
<exit-transition transition_id="invalidate" />
<exit-transition transition_id="rollback_to_receive" />
<exit-transition transition_id="detach" />
<!-- /TRANSITIONS -->

<permission-map name="Delete objects" acquired="False">
Expand All @@ -808,6 +821,7 @@
<!-- MANAGED PERMISSIONS (partially readonly) -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="True"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="True"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -887,6 +901,7 @@
<!-- MANAGED PERMISSIONS (partially readonly) -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="False"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="True"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -962,6 +977,7 @@
<!-- MANAGED PERMISSIONS (partially readonly) -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="False"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -1036,6 +1052,7 @@
<!-- MANAGED PERMISSIONS (partially readonly) -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="False"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -1115,6 +1132,7 @@
<!-- MANAGED PERMISSIONS (partially readonly) -->
<!-- Permissions for Transitions (must match with exit transitions) -->
<permission-map name="senaite.core: Transition: Cancel Analysis Request" acquired="False"/>
<permission-map name="senaite.core: Transition: Detach Sample Partition" acquired="False"/>
<permission-map name="senaite.core: Transition: Reinstate Analysis Request" acquired="True"/>
<permission-map name="senaite.core: Transition: Invalidate" acquired="False"/>
<permission-map name="senaite.core: Transition: Preserve Sample" acquired="False"/>
Expand Down Expand Up @@ -1428,6 +1446,23 @@
</guard>
</transition>

<!-- Transition: Detach
This transition only applies to Sample partitions (governed by the guard)
and allows the user to detach a given partition from its primary sample.
Thus, a detached partition behaves like a Primary Sample and the original
primary sample no longer follows the status of the detached partition.

Note this transition does not have a new state defined, so this
transition does not change the current state of the object.
-->
<transition transition_id="detach" title="Detach" new_state="" trigger="USER"
before_script="" after_script="" i18n:attributes="title">
<action url="" category="workflow" icon="">Detach</action>
<guard>
<guard-expression>python:here.guard_handler("detach")</guard-expression>
<guard-permission>senaite.core: Transition: Detach Sample Partition</guard-permission>
</guard>
</transition>

<!-- *** VARIABLES *** -->

Expand Down
43 changes: 41 additions & 2 deletions bika/lims/upgrade/v01_03_002.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
# Copyright 2018-2019 by it's authors.
# Some rights reserved, see README and LICENSE.

from bika.lims import api
from bika.lims import logger
from bika.lims.catalog.analysisrequest_catalog import \
CATALOG_ANALYSIS_REQUEST_LISTING
from bika.lims.config import PROJECTNAME as product
from bika.lims.upgrade import upgradestep
from bika.lims.upgrade.utils import UpgradeUtils
Expand All @@ -43,9 +46,45 @@ def upgrade(tool):

# -------- ADD YOUR STUFF BELOW --------

# Mixed permissions for transitions in client workflow
# https://github.com/senaite/senaite.core/pull/1419
# Allow to detach a partition from its primary sample (#1420)
setup.runImportStepFromProfile(profile, "rolemap")

# Mixed permissions for transitions in client workflow (#1419)
# Allow to detach a partition from its primary sample (#1420)
setup.runImportStepFromProfile(profile, "workflow")

# Allow to detach a partition from its primary sample (#1420)
update_partitions_role_mappings(portal)

logger.info("{0} upgraded to version {1}".format(product, version))
return True

def update_partitions_role_mappings(portal):
"""Updates the rolemappings for existing partitions that are in a suitable
state, so they can be detached from the primary sample they belong to
"""
logger.info("Updating role mappings of partitions ...")
wf_tool = api.get_tool("portal_workflow")
workflow = wf_tool.getWorkflowById("bika_ar_workflow")

# States that allow detach transition as defined in workflow definition in
# order to query and update role mappings of objects that matter
allowed_states = [
"to_be_preserved", "sample_due", "sample_received", "to_be_verified",
"verified"
]
query = dict(portal_type="AnalysisRequest",
isRootAncestor=False,
review_state=allowed_states)

brains = api.search(query, CATALOG_ANALYSIS_REQUEST_LISTING)
total = len(brains)
for num, brain in enumerate(brains):
if num and num % 100 == 0:
logger.info("Updating role mappings of partitions: {}/{}"
.format(num, total))
partition = api.get_object(brain)
workflow.updateRoleMappingsFor(partition)
partition.reindexObjectSecurity()

logger.info("Updating role mappings of partitions [DONE]")
1 change: 1 addition & 0 deletions bika/lims/utils/analysisrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ def create_partition(analysis_request, request, analyses, sample_type=None,
"Analyses",
"Attachment",
"Client",
"DetachedFrom",
"Profile",
"Profiles",
"RejectionReasons",
Expand Down
24 changes: 23 additions & 1 deletion bika/lims/workflow/analysisrequest/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

from DateTime import DateTime
from bika.lims import api
from bika.lims.interfaces import IReceived, IVerified
from bika.lims.interfaces import IDetachedPartition
from bika.lims.interfaces import IReceived, IVerified, IAnalysisRequestPartition
from bika.lims.utils import changeWorkflowState
from bika.lims.utils.analysisrequest import create_retest
from bika.lims.workflow import get_prev_status_from_history
Expand Down Expand Up @@ -153,3 +154,24 @@ def after_rollback_to_receive(analysis_request):
"""
if IVerified.providedBy(analysis_request):
noLongerProvides(analysis_request, IVerified)


def after_detach(analysis_request):
"""Function triggered after "detach" transition is performed
"""
# Unbind the sample from its parent (the primary)
parent = analysis_request.getParentAnalysisRequest()
analysis_request.setParentAnalysisRequest(None)

# Assign the primary from which the sample has been detached
analysis_request.setDetachedFrom(parent)

# This sample is no longer a partition
noLongerProvides(analysis_request, IAnalysisRequestPartition)

# And we mark the sample with IDetachedPartition
alsoProvides(analysis_request, IDetachedPartition)

# Reindex both the parent and the detached one
analysis_request.reindexObject()
parent.reindexObject()
Loading