diff --git a/docs/changelog.rst b/docs/changelog.rst
index 103d87e..71aaf43 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,8 @@ Changelog
1.5.0 (unreleased)
------------------
+- #98 Allow to flag patients as deceased
+
1.4.0 (2023-01-10)
------------------
diff --git a/src/senaite/patient/browser/patientfolder.py b/src/senaite/patient/browser/patientfolder.py
index 3187658..1bcde47 100644
--- a/src/senaite/patient/browser/patientfolder.py
+++ b/src/senaite/patient/browser/patientfolder.py
@@ -20,14 +20,13 @@
import collections
from bika.lims import api
-from bika.lims import senaiteMessageFactory as _
from bika.lims.interfaces import IClient
from bika.lims.utils import get_email_link
from bika.lims.utils import get_image
from bika.lims.utils import get_link
from senaite.app.listing.view import ListingView
from senaite.core.api import dtime
-from senaite.patient import messageFactory as _sp
+from senaite.patient import messageFactory as _
from senaite.patient.api import to_identifier_type_name
from senaite.patient.api import tuplify_identifiers
from senaite.patient.catalog import PATIENT_CATALOG
@@ -67,7 +66,7 @@ def __init__(self, context, request):
self.icon = "{}/{}".format(
self.portal_url, "senaite_theme/icon/patientfolder")
- self.title = _sp("Patients")
+ self.title = _("Patients")
self.description = self.context.Description()
self.show_select_column = True
self.pagesize = 25
@@ -110,6 +109,11 @@ def __init__(self, context, request):
"title": _("Inactive"),
"contentFilter": {'is_active': False},
"columns": self.columns.keys(),
+ }, {
+ "id": "deceased",
+ "title": _("Deceased"),
+ "contentFilter": {'patient_deceased': True},
+ "columns": self.columns.keys(),
}, {
"id": "all",
"title": _("All"),
@@ -159,7 +163,7 @@ def folderitem(self, obj, item, index):
mrn = obj.getMRN()
if not mrn:
item["before"]["mrn"] = get_image("info", width=16)
- mrn = t(_sp("mrn_not_defined", default="Not defined"))
+ mrn = t(_("mrn_not_defined", default="Not defined"))
item["mrn"] = self.to_utf8(mrn)
item["replace"]["mrn"] = get_link(url, value=mrn)
@@ -170,11 +174,19 @@ def folderitem(self, obj, item, index):
self.get_identifier_tags(identifiers))
# Fullname
- fullname = obj.getFullname()
- if fullname:
- fullname = api.safe_unicode(fullname).encode("utf8")
- item["fullname"] = fullname
- item["replace"]["fullname"] = get_link(url, value=fullname)
+ fullname_nd = t(_("fullname_not_defined", default="Not defined"))
+ fullname = obj.getFullname() or fullname_nd
+ fullname = api.safe_unicode(fullname).encode("utf8")
+ item["fullname"] = fullname
+
+ # Death dagger
+ if obj.getDeceased():
+ fullname = t(_(
+ "patient_fullname_deceased_html",
+ default="${fullname} †",
+ mapping={"fullname": fullname}
+ ))
+ item["replace"]["fullname"] = get_link(url, value=fullname)
# Email
email = obj.getEmail()
diff --git a/src/senaite/patient/catalog/indexer/configure.zcml b/src/senaite/patient/catalog/indexer/configure.zcml
index 1d2e26d..becf3b3 100644
--- a/src/senaite/patient/catalog/indexer/configure.zcml
+++ b/src/senaite/patient/catalog/indexer/configure.zcml
@@ -22,5 +22,6 @@
+
diff --git a/src/senaite/patient/catalog/indexer/patient.py b/src/senaite/patient/catalog/indexer/patient.py
index f08f13e..5f1497d 100644
--- a/src/senaite/patient/catalog/indexer/patient.py
+++ b/src/senaite/patient/catalog/indexer/patient.py
@@ -100,6 +100,13 @@ def patient_birthdate(instance):
return birthdate
+@indexer(IPatient)
+def patient_deceased(instance):
+ """Index deceased
+ """
+ return instance.getDeceased()
+
+
@indexer(IPatient)
def patient_searchable_text(instance):
"""Index for searchable text queries
diff --git a/src/senaite/patient/catalog/patient_catalog.py b/src/senaite/patient/catalog/patient_catalog.py
index 48d1bc7..2a3128b 100644
--- a/src/senaite/patient/catalog/patient_catalog.py
+++ b/src/senaite/patient/catalog/patient_catalog.py
@@ -42,6 +42,7 @@
("patient_birthdate", "", "DateIndex"),
("patient_searchable_text", "", "ZCTextIndex"),
("patient_searchable_mrn", "", "ZCTextIndex"),
+ ("patient_deceased", "", "BooleanIndex"),
]
COLUMNS = BASE_COLUMNS + [
diff --git a/src/senaite/patient/content/patient.py b/src/senaite/patient/content/patient.py
index 5e1fa40..642f4e3 100644
--- a/src/senaite/patient/content/patient.py
+++ b/src/senaite/patient/content/patient.py
@@ -304,6 +304,17 @@ class IPatientSchema(model.Schema):
required=False,
)
+ deceased = schema.Bool(
+ title=_(
+ u"label_patient_deceased",
+ default=u"Deceased"),
+ description=_(
+ u"description_patient_deceased",
+ default=u"Select this option if the patient is deceased"),
+ required=False,
+ default=False,
+ )
+
@invariant
def validate_mrn(data):
"""Checks if the patient MRN # is unique
@@ -732,3 +743,17 @@ def getFormattedAddress(self, atype=PHYSICAL_ADDRESS):
output = Template(address_format).safe_substitute(record)
output = filter(None, output.split(", "))
return ", ".join(output)
+
+ @security.protected(permissions.View)
+ def getDeceased(self):
+ """Returns whether the patient is deceased
+ """
+ accessor = self.accessor("deceased")
+ return accessor(self)
+
+ @security.protected(permissions.ModifyPortalContent)
+ def setDeceased(self, value):
+ """Set if the patient deceased
+ """
+ mutator = self.mutator("deceased")
+ return mutator(self, value)
diff --git a/src/senaite/patient/i18n.py b/src/senaite/patient/i18n.py
index c8c2359..1b90ba7 100644
--- a/src/senaite/patient/i18n.py
+++ b/src/senaite/patient/i18n.py
@@ -18,22 +18,11 @@
# Copyright 2020-2024 by it's authors.
# Some rights reserved, see README and LICENSE.
-from bika.lims import api
+from senaite.core.i18n import translate as core_translate
def translate(msgid, **kwargs):
"""Translate any zope i18n msgid returned from MessageFactory
"""
- msgid = api.safe_unicode(msgid)
-
- # XX: If the msgid is from type `Message`, Zope's i18n translate tool gives
- # priority `Message.domain` over the domain passed through kwargs
domain = kwargs.pop("domain", "senaite.patient")
- params = {
- "domain": getattr(msgid, "domain", domain),
- "context": api.get_request(),
- }
- params.update(kwargs)
-
- ts = api.get_tool("translation_service")
- return ts.translate(msgid, **params)
+ return core_translate(msgid, domain=domain, **kwargs)
diff --git a/src/senaite/patient/profiles/default/metadata.xml b/src/senaite/patient/profiles/default/metadata.xml
index c85d7a1..f6fbb24 100644
--- a/src/senaite/patient/profiles/default/metadata.xml
+++ b/src/senaite/patient/profiles/default/metadata.xml
@@ -1,6 +1,6 @@
- 1500
+ 1501
profile-senaite.lims:default
diff --git a/src/senaite/patient/upgrade/v01_05_000.py b/src/senaite/patient/upgrade/v01_05_000.py
index a719638..5bdfaa1 100644
--- a/src/senaite/patient/upgrade/v01_05_000.py
+++ b/src/senaite/patient/upgrade/v01_05_000.py
@@ -22,6 +22,7 @@
from senaite.core.upgrade.utils import UpgradeUtils
from senaite.patient import logger
from senaite.patient.config import PRODUCT_NAME
+from senaite.patient.setuphandlers import setup_catalogs
version = "1.5.0"
profile = "profile-{0}:default".format(PRODUCT_NAME)
@@ -46,3 +47,15 @@ def upgrade(tool):
logger.info("{0} upgraded to version {1}".format(PRODUCT_NAME, version))
return True
+
+
+def upgrade_catalog_indexes(tool):
+ """Reinstall controlpanel registry
+
+ :param tool: portal_setup tool
+ """
+ logger.info("Upgrade catalog indexes ...")
+ portal = tool.aq_inner.aq_parent
+ # setup patient catalog to add new indexes
+ setup_catalogs(portal)
+ logger.info("Upgrade catalog indexes [DONE]")
diff --git a/src/senaite/patient/upgrade/v01_05_000.zcml b/src/senaite/patient/upgrade/v01_05_000.zcml
index e8eae53..2c5af6f 100644
--- a/src/senaite/patient/upgrade/v01_05_000.zcml
+++ b/src/senaite/patient/upgrade/v01_05_000.zcml
@@ -2,6 +2,18 @@
xmlns="http://namespaces.zope.org/zope"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
+
+
+