diff --git a/README.md b/README.md index e2081ea..03bc92e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Bika Health LIS Bika Laboratory Information Management System customised for use in Health laboratories. -Last stable version: **v3.1.6** · released: 2015-02-27 +Last stable version: **v3.1.7** · released: 2015-06-09 [**Naringenin**](http://en.wikipedia.org/wiki/Naringenin) is a flavanone, a typeof flavonoid, that is considered to have a bioactive effect on human health as antioxidant, free radical scavenger, anti-inflammatory, carbohydrate metabolism promoter, and immune system modulator. It is the predominant flavanone in grapefruit. @@ -12,7 +12,7 @@ Installation and upgrades Bika Health can be installed on Windows, Mac OS X, Linux, BSD and other platforms: https://github.com/bikalabs/bika.health/wiki/Installing-Bika-Health-Extension -**Bika Health LIS 3.1.6 is only compatible with Bika LIMS 3.1.7**. Follow the instructions [Installing Bika Health Extension](https://github.com/bikalabs/bika.health/wiki/Installing-Bika-Health-Extension). +**Bika Health LIS 3.1.7 is only compatible with Bika LIMS 3.1.8**. Follow the instructions [Installing Bika Health Extension](https://github.com/bikalabs/bika.health/wiki/Installing-Bika-Health-Extension). You might also find useful the following recipes: - Installing Bika LIMS: https://github.com/bikalabs/Bika-LIMS/wiki/Bika-LIMS-Installation diff --git a/bika/health/__init__.py b/bika/health/__init__.py index 2acc32f..58dd9ba 100644 --- a/bika/health/__init__.py +++ b/bika/health/__init__.py @@ -35,6 +35,7 @@ def initialize(context): from content.aetiologicagent import AetiologicAgent from content.caseoutcome import CaseOutcome from content.casestatus import CaseStatus + from content.ethnicity import Ethnicity from content.casesyndromicclassification import CaseSyndromicClassification from content.disease import Disease from content.doctor import Doctor @@ -64,6 +65,7 @@ def initialize(context): from controlpanel.bika_immunizations import Immunizations from controlpanel.bika_treatments import Treatments from controlpanel.bika_insurancecompanies import InsuranceCompanies + from controlpanel.bika_ethnicities import Ethnicities from controlpanel.bika_vaccinationcenters import VaccinationCenters content_types, constructors, ftis = process_types( diff --git a/bika/health/browser/admin.py b/bika/health/browser/admin.py deleted file mode 100644 index 706c419..0000000 --- a/bika/health/browser/admin.py +++ /dev/null @@ -1,16 +0,0 @@ -from Products.CMFPlone.browser.admin import Overview as OverviewBase -from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile - - - -class Overview(OverviewBase): - """ Customized plone overview. """ - - index = ViewPageTemplateFile('plone-addsiteTemplates/plone-overview.pt') - - def __call__(self): - """ Redirect to bika.health instance if single instance is found. """ - sites = self.sites() - if len(sites) == 1 and not self.outdated(sites[0]): - return self.request.response.redirect(sites[0].absolute_url()) - return self.index() \ No newline at end of file diff --git a/bika/health/browser/analysisrequest/add.py b/bika/health/browser/analysisrequest/add.py new file mode 100644 index 0000000..fbf8c69 --- /dev/null +++ b/bika/health/browser/analysisrequest/add.py @@ -0,0 +1,78 @@ +from bika.lims.browser.analysisrequest.add import AnalysisRequestAddView as AnalysisRequestAddViewLIMS +from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile +import json +from Products.CMFCore.utils import getToolByName +from bika.lims.browser.widgets import AddressWidget + + +class AnalysisRequestAddView(AnalysisRequestAddViewLIMS): + """ + The main AR Add form adapted for health usage + """ + health_template = ViewPageTemplateFile("templates/ar_add_health_standard.pt") + patient_template = ViewPageTemplateFile("templates/ar_addpatient.pt") + doctor_referrer_template = ViewPageTemplateFile("templates/ar_add_doctor_referrer.pt") + insurance_template = ViewPageTemplateFile("templates/ar_insurance.pt") + analyses_template = ViewPageTemplateFile("templates/ar_analyses.pt") + prices_template = ViewPageTemplateFile("templates/ar_prices.pt") + + def __init__(self, context, request): + AnalysisRequestAddViewLIMS.__init__(self, context, request) + self.templatename = self.request.get('tpl','') + self.w = AddressWidget() + + def __call__(self): + if self.templatename == 'classic': + return AnalysisRequestAddViewLIMS.__call__(self) + + # Getting the checkbox value + enable_bika_request_field = self.context.bika_setup.Schema().getField('EnableBikaAnalysisRequestRequestForm') + enable_bika_request = enable_bika_request_field.get(self.context.bika_setup) + if enable_bika_request: + # Use the template defined on BikaLIMS + return AnalysisRequestAddViewLIMS.__call__(self) + else: + # Use the Health's template + self.col_count = 1 + self.request.set('disable_border', 1) + return self.health_template() + + def get_json_format(self, d): + """ + Given some data, it gets its json format. + :param d: Data to be formatted. + :return: The formatted data in JSON. + """ + return json.dumps(d) + + def getDepartments(self): + """ + It obtains the different departments + :return: A list with the department names and UID's [(UID,Name),....] + """ + dep = getToolByName(self.context, 'bika_setup_catalog')(portal_type='Department') + l = [] + for i in dep: + l.append((i.UID, i.Title)) + return l + + def categoryInDepartment(self, category, department): + """ + It checks if the category belongs to the department + :param category: The category UID + :param department: The department UID + :return: True or False + """ + return getToolByName(self.context, 'bika_setup_catalog')(UID=category)[0]\ + .getObject().getDepartment().UID() == department + + def getAvailableServices(self, categoryuid): + """ Return a list of services brains + """ + bsc = getToolByName(self.context, 'bika_setup_catalog') + services = bsc(portal_type="AnalysisService", + sort_on='sortable_title', + inactive_state='active', + getCategoryUID=categoryuid) + return services + diff --git a/bika/health/browser/analysisrequest/overrides.zcml b/bika/health/browser/analysisrequest/overrides.zcml index b50de0f..5e9ee18 100644 --- a/bika/health/browser/analysisrequest/overrides.zcml +++ b/bika/health/browser/analysisrequest/overrides.zcml @@ -76,4 +76,12 @@ permission="bika.lims.ManageInvoices" layer="bika.lims.interfaces.IBikaLIMS" /> + + diff --git a/bika/health/browser/analysisrequest/templates/ar_add_doctor_referrer.pt b/bika/health/browser/analysisrequest/templates/ar_add_doctor_referrer.pt new file mode 100644 index 0000000..2b71e5b --- /dev/null +++ b/bika/health/browser/analysisrequest/templates/ar_add_doctor_referrer.pt @@ -0,0 +1,67 @@ +
+ +

Doctor and Referral Institution

+ +
+
+ + +
+ +
+
+ +
+ + +
+ +
+ +
+ + + +
+ + +
+ +
+ +
+
+
+ diff --git a/bika/health/browser/analysisrequest/templates/ar_add_health_standard.pt b/bika/health/browser/analysisrequest/templates/ar_add_health_standard.pt new file mode 100644 index 0000000..55f155d --- /dev/null +++ b/bika/health/browser/analysisrequest/templates/ar_add_health_standard.pt @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+

Analysis Request details

+ + +
+
+ + +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+ + +
+
+ + + + + + + + + + + +
+
+ + +
+ +
+
+ + +
+
+ + +
+ + +
+
+ +
+ +
+ + +
+ + +
+
+
+ +
+ + + + + + + + + + + + + + +
+ + diff --git a/bika/health/browser/analysisrequest/templates/ar_addpatient.pt b/bika/health/browser/analysisrequest/templates/ar_addpatient.pt new file mode 100644 index 0000000..de9ed48 --- /dev/null +++ b/bika/health/browser/analysisrequest/templates/ar_addpatient.pt @@ -0,0 +1,194 @@ +
+

Patient Details

+ + +
+
+ + + +
+
+ + +
+
+ + + + + + +
+
+ + +
+
+ + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+ + +
+
+ + +
+ +
+
+ + +
+
+ + +
+ +
+ +
+ + + +
+
+ +
+
+ + +
+ +
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+
+ diff --git a/bika/health/browser/analysisrequest/templates/ar_analyses.pt b/bika/health/browser/analysisrequest/templates/ar_analyses.pt new file mode 100644 index 0000000..43c8043 --- /dev/null +++ b/bika/health/browser/analysisrequest/templates/ar_analyses.pt @@ -0,0 +1,63 @@ +
+ + + + +
+

+ + + +
+

+
    +
  • + + + + + + + + +
    + + + +
    +
    +
  • +
+
+
+
+
+
+
diff --git a/bika/health/browser/analysisrequest/templates/ar_insurance.pt b/bika/health/browser/analysisrequest/templates/ar_insurance.pt new file mode 100644 index 0000000..de7d0ec --- /dev/null +++ b/bika/health/browser/analysisrequest/templates/ar_insurance.pt @@ -0,0 +1,174 @@ +
+

Account to

+ + +
+
+ + + +
+
+ + + +
+
+ + The ID number (Insurance Number) from the person whose contract cover the current patient. + +
+
+ + +
+ +
+ + +
+ + + +
+ +
+ + + +
+ + +
+ +
+ +
+ + +
+ +
+ +
+ + +
+ +
+ +
+ + +
+ +
+ +
+ + +
+ +
+ + + +
+ +

Postal Address

+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + diff --git a/bika/health/browser/analysisrequest/templates/ar_prices.pt b/bika/health/browser/analysisrequest/templates/ar_prices.pt new file mode 100644 index 0000000..bbb79d7 --- /dev/null +++ b/bika/health/browser/analysisrequest/templates/ar_prices.pt @@ -0,0 +1,79 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ Discount + (%) + + + + +
Subtotal + + + +
VAT + + + + +
Total + + + + +
+
\ No newline at end of file diff --git a/bika/health/browser/overrides.zcml b/bika/health/browser/overrides.zcml deleted file mode 100644 index 37d955a..0000000 --- a/bika/health/browser/overrides.zcml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/bika/health/browser/plone-addsiteTemplates/plone-addsite.pt b/bika/health/browser/plone-addsiteTemplates/plone-addsite.pt deleted file mode 100644 index a4f38ee..0000000 --- a/bika/health/browser/plone-addsiteTemplates/plone-addsite.pt +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - Install Bika Health - - - - - - - - - - -

Install Bika health

- -
- -
- - -
- The id of the site. This ends up as part of the URL.
- No special characters are allowed. -
- -
- IMPORTANT: Don't use "Bika" or "BIKA" as the instance name, this is a - reserved namespace. If you get "Site Error" citing "AttributeError: adapters", - this is the most likely cause. -
- - -
- -
- - -
- A short title for the site. This will be shown in the title of the - browser window on each page. -
- - -
- -
- - -
- The main language of the site. -
- - -
- -
- - -
- - - - - Should the default example content be added to the site? - -
- - - - - -
- - -
- You normally don't need to change anything here unless you have - specific reasons and know what you are doing. -
- -
- -
- - -
-
- Profile description -
-
-
-
-
- - - - -
- - Add-ons - - -
- Select any add-ons you want to activate immediately. You can - also activate add-ons after the site has been created using the - Add-ons control panel. -
- -
- - - -
- - -
-
- profile description -
-
- - - -
-
-
-
-
-
- - - - - - - - - - - - - -
- - -
- - - - \ No newline at end of file diff --git a/bika/health/browser/plone-addsiteTemplates/plone-overview.pt b/bika/health/browser/plone-addsiteTemplates/plone-overview.pt deleted file mode 100644 index a6bdb70..0000000 --- a/bika/health/browser/plone-addsiteTemplates/plone-overview.pt +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - Bika health - Professional Open Source LIMS for Health Labs - - - - - - - -
- Bika health Logo -
-

- Professional Open Source LIMS for Health Labs -

-
    - - -
  • - - - View your Bika health installation - -
    - - This site configuration is outdated and needs to be - upgraded: -
    - - -
    -
    -
  • -
    - -
  • - - You have multiple installations of Bika health: - -
      -
    • - - - - Site title - - - -
      - - This site configuration is outdated and - needs to be upgraded: - -
      - - -
      - -
      -
    • -
    -
  • -
    -
    -
  • - - BikaHealth has not been installed yet: - -
    - - -
    -
  • -
  • - Zope Management Interface - - — low-level technical configuration. - -
  • -
  • - - For documentation, add-ons, support, community, visit - - http://www.bikalims.org. -
  • -
  • - - For the pyhton egg, please visit - - https://pypi.python.org -
  • - - Please log issues, feature requests, or bug reports in the - - issue tracker. -
  • -
  • -
    The lab is open.
    -
  • -
-
-
- - diff --git a/bika/health/config.py b/bika/health/config.py index 361e020..2152689 100644 --- a/bika/health/config.py +++ b/bika/health/config.py @@ -6,6 +6,12 @@ PROJECTNAME = "bika.health" +GENDERS = DisplayList(( + ('male', _('Male')), + ('female', _('Female')), + ('dk', _("Don't Know")), + )) + ETHNICITIES = DisplayList(( ('Native American', _('Native American')), ('Asian', _('Asian')), @@ -15,12 +21,6 @@ ('Hispanic or Latino', _('Hispanic or Latino')), )) -GENDERS = DisplayList(( - ('male', _('Male')), - ('female', _('Female')), - ('dk', _("Don't Know")), - )) - GENDERS_APPLY = DisplayList(( ('male', _('Male')), ('female', _('Female')), diff --git a/bika/health/content/bikasetup.py b/bika/health/content/bikasetup.py index 789bc4f..7edf992 100644 --- a/bika/health/content/bikasetup.py +++ b/bika/health/content/bikasetup.py @@ -95,6 +95,17 @@ class BikaSetupSchemaExtender(object): "preferences' tab.") ) ), + ExtBooleanField('EnableBikaAnalysisRequestRequestForm', + schemata="Analyses", + default=False, + widget=BooleanWidget( + label=_("Enable Bika's analysis request form."), + description=_("It enables the secondary analysis request form. This request has some characteristic " + "features as allowing you to register more than one analysis request at the same time. " + "It's useful if you are supposed to register a big amount of analysis request at the same " + "time.") + ) + ), ] def __init__(self, context): diff --git a/bika/health/content/ethnicity.py b/bika/health/content/ethnicity.py new file mode 100644 index 0000000..97d0c4f --- /dev/null +++ b/bika/health/content/ethnicity.py @@ -0,0 +1,23 @@ +from zope.interface import implements +from Products.Archetypes import atapi +from Products.Archetypes.public import BaseContent +from bika.health.interfaces import IEthnicity +from bika.lims.content.bikaschema import BikaSchema +from bika.health import config + + +schema = BikaSchema.copy() + atapi.Schema(( + +)) + +schema['description'].widget.visible = True +schema['description'].schemata = 'default' + + +class Ethnicity(BaseContent): + # It implements the IEthnicity interface + implements(IEthnicity) + schema = schema + +# Activating the content type in Archetypes' internal types registry +atapi.registerType(Ethnicity, config.PROJECTNAME) diff --git a/bika/health/content/patient.py b/bika/health/content/patient.py index ea7a0fc..1f719ca 100644 --- a/bika/health/content/patient.py +++ b/bika/health/content/patient.py @@ -20,6 +20,7 @@ from bika.health.widgets import ReadonlyStringWidget from datetime import datetime from zope.interface import implements +from Products.Archetypes.references import HoldingReference from bika.health.widgets.patientmenstrualstatuswidget import PatientMenstrualStatusWidget schema = Person.schema.copy() + Schema(( @@ -304,10 +305,24 @@ label=_('Birth place'), ), ), + # TODO This field will be removed on release 319. We maintain this field on release 318 + # because of the transference between string field and content type data. StringField('Ethnicity', schemata='Personal', - index='FieldIndex', - vocabulary=ETHNICITIES, - widget=ReferenceWidget( + index='FieldIndex', + vocabulary=ETHNICITIES, + widget=ReferenceWidget( + label=_('Ethnicity'), + description=_("Ethnicity eg. Asian, African, etc."), + visible=False, + ), + ), + # TODO This field will change its name on v319 and it'll be called Ethnicity + ReferenceField('Ethnicity_Obj', schemata='Personal', + vocabulary='getEthnicitiesVocabulary', + allowed_types = ('Ethnicity',), + relationship = 'PatientEthnicity', + widget=SelectionWidget( + format='select', label=_('Ethnicity'), description=_("Ethnicity eg. Asian, African, etc."), ), @@ -322,6 +337,11 @@ label=_('Mothers name'), ), ), + StringField('FathersName', schemata='Personal', + widget=StringWidget( + label=_('Fathers name'), + ), + ), StringField('CivilStatus', schemata='Personal', widget=StringWidget( label=_('Civil status'), @@ -415,6 +435,64 @@ description=_("If it is checked the invoices will be send to the insurance company." " In this case the insurance number will be mandatory.")) ), + BooleanField('PatientAsGuarantor', + schemata = 'Insurance', + default=True, + widget=BooleanWidget( + label=_("The patient is the guarantor."), + description=_("The patient and the guarantor are the same.")) + ), + StringField('GuarantorID', + searchable=1, + schemata = 'Insurance', + required=0, + widget=StringWidget( + label=_('Guarantor ID'), + description=_("The ID number (Insurance Number) from the person whose contract cover the current patient.") + ), + ), + StringField('GuarantorSurname', + searchable=1, + schemata = 'Insurance', + required=0, + widget=StringWidget( + label=_("Guarantor's Surname"), + ), + ), + StringField('GuarantorFirstname', + searchable=1, + schemata = 'Insurance', + required=0, + widget=StringWidget( + label=_("Guarantor's First Name"), + ), + ), + StringField('GuarantorPostalAddress', + searchable=1, + schemata = 'Insurance', + required=0, + widget=AddressWidget( + label=_("Guarantor's postal address"), + ), + ), + StringField('GuarantorBusinessPhone', + schemata = 'Insurance', + widget = StringWidget( + label=_("Guarantor's Phone (business)"), + ), + ), + StringField('GuarantorHomePhone', + schemata = 'Insurance', + widget = StringWidget( + label=_("Guarantor's Phone (home)"), + ), + ), + StringField('GuarantorMobilePhone', + schemata = 'Insurance', + widget = StringWidget( + label=_("Guarantor's Phone (mobile)"), + ), + ), )) schema['JobTitle'].widget.visible = False @@ -591,5 +669,89 @@ def getCountryState(self): if self.getField('CountryState').get(self) \ else self.getPhysicalAddress() + def getGuarantorID(self): + """ + If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as + the current patient fields. + :return: The guarantor ID (insurance number) from + """ + return self.getInsuranceNumber() if self.getPatientAsGuarantor() else self.getField('GuarantorID').get(self) + + def getGuarantorSurname(self): + """ + If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as + the current patient fields. + """ + return self.getSurname() if self.getPatientAsGuarantor() else self.getField('GuarantorSurname').get(self) + + def getGuarantorFirstname(self): + """ + If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as + the current patient fields. + """ + return self.getFirstname() if self.getPatientAsGuarantor() else self.getField('GuarantorFirstname').get(self) + + def getGuarantorPostalAddress(self): + """ + If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as + the current patient fields. + """ + return self.getPostalAddress() \ + if self.getPatientAsGuarantor() \ + else self.getField('GuarantorPostalAddress').get(self) + + def getGuarantorBusinessPhone(self): + """ + If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as + the current patient fields. + """ + return self.getBusinessPhone() \ + if self.getPatientAsGuarantor() \ + else self.getField('GuarantorBusinessPhone').get(self) + + def getGuarantorHomePhone(self): + """ + If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as + the current patient fields. + """ + return self.getHomePhone() if self.getPatientAsGuarantor() else self.getField('GuarantorHomePhone').get(self) + + def getGuarantorMobilePhone(self): + """ + If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as + the current patient fields. + """ + return self.getMobilePhone() \ + if self.getPatientAsGuarantor() \ + else self.getField('GuarantorMobilePhone').get(self) + + def getEthnicitiesVocabulary(self, instance=None): + """ + Obtain all the ethnicities registered in the system and returns them as a list + """ + bsc = getToolByName(self, 'bika_setup_catalog') + items = [(c.UID, c.Title) \ + for c in bsc(portal_type='Ethnicity', + inactive_state = 'active')] + items.sort(lambda x,y:cmp(x[1], y[1])) + items.insert(0, ('', t(_('')))) + return DisplayList(items) + + # TODO This function will will be removed on v319 + def getEthnicity(self): + """ + This function exists because we are changing the construction of ethnicities. Until now, ethnicities options were + hand-coded but now they are a new content type. So we need to pass all patient's ethnicity values, but to do + such thing, we need to create new ethnicity types on upgrade step and edit patient ethnicity field to relate them + with its corresponding ethnicity content type. + :return: + """ + return self.getEthnicity_Obj() + + # TODO This function will be removed on v319 + def setEthnicity(self, value): + self.setEthnicity_Obj(value) + + # schemata.finalizeATCTSchema(schema, folderish=True, moveDiscussion=False) atapi.registerType(Patient, PROJECTNAME) diff --git a/bika/health/controlpanel/bika_ethnicities.py b/bika/health/controlpanel/bika_ethnicities.py new file mode 100644 index 0000000..15e9af8 --- /dev/null +++ b/bika/health/controlpanel/bika_ethnicities.py @@ -0,0 +1,81 @@ +from Products.ATContentTypes.content import schemata +from Products.Archetypes import atapi +from bika.lims.browser.bika_listing import BikaListingView +from bika.health.config import PROJECTNAME +from bika.health import bikaMessageFactory as _ +from bika.health.interfaces import IEthnicities +from plone.app.layout.globals.interfaces import IViewView +from plone.app.content.browser.interfaces import IFolderContentsView +from plone.app.folder.folder import ATFolder, ATFolderSchema +from zope.interface.declarations import implements + + +class EthnicitiesView(BikaListingView): + implements(IFolderContentsView, IViewView) + + def __init__(self, context, request): + super(EthnicitiesView, self).__init__(context, request) + self.catalog = 'bika_setup_catalog' + self.contentFilter = {'portal_type': 'Ethnicity', + 'sort_on': 'sortable_title'} + self.context_actions = {_('Add'): + {'url': 'createObject?type_name=Ethnicity', + 'icon': '++resource++bika.lims.images/add.png'}} + self.title = self.context.translate(_("Ethnicities")) + self.icon = self.portal_url + "/++resource++bika.health.images/patient.png" + self.description = "" + self.show_sort_column = False + self.show_select_row = False + self.show_select_column = True + self.pagesize = 25 + + self.columns = { + 'Title': {'title': _('Ethnicity Name'), + 'index': 'sortable_title'}, + + 'Description': {'title': _('Description'), + 'index': 'description', + 'toggle': False}, + } + + self.review_states = [ + {'id': 'default', + 'title': _('Active'), + 'contentFilter': {'inactive_state': 'active'}, + 'transitions': [{'id': 'deactivate'}, ], + 'columns': ['Title', + 'Description']}, + {'id': 'inactive', + 'title': _('Dormant'), + 'contentFilter': {'inactive_state': 'inactive'}, + 'transitions': [{'id': 'activate'}, ], + 'columns': ['Title', + 'Description']}, + {'id': 'all', + 'title': _('All'), + 'contentFilter': {}, + 'columns': ['Title', + 'Description']}, + ] + + def folderitems(self): + items = BikaListingView.folderitems(self) + for x in range(len(items)): + if not items[x].has_key('obj'): continue + obj = items[x]['obj'] + items[x]['Description'] = obj.Description() + items[x]['replace']['Title'] = "%s" % \ + (items[x]['url'], items[x]['Title']) + + return items + +schema = ATFolderSchema.copy() + + +class Ethnicities(ATFolder): + implements(IEthnicities) + displayContentsTab = False + schema = schema + +schemata.finalizeATCTSchema(schema, folderish=True, moveDiscussion=False) +atapi.registerType(Ethnicities, PROJECTNAME) diff --git a/bika/health/controlpanel/configure.zcml b/bika/health/controlpanel/configure.zcml index 50dbbf6..23fb7ac 100644 --- a/bika/health/controlpanel/configure.zcml +++ b/bika/health/controlpanel/configure.zcml @@ -116,4 +116,12 @@ layer="bika.lims.interfaces.IBikaLIMS" /> + + diff --git a/bika/health/interfaces.py b/bika/health/interfaces.py index 38ace79..6cc507f 100644 --- a/bika/health/interfaces.py +++ b/bika/health/interfaces.py @@ -59,3 +59,14 @@ class IInsuranceCompany(Interface): class IInsuranceCompanies(Interface): "" + +class IEthnicity(Interface): + """ + Ethnicity content type marker + """ + + +class IEthnicities(Interface): + """ + Ethnicities content folder marker + """ diff --git a/bika/health/overrides.zcml b/bika/health/overrides.zcml index 9569040..6a19d5f 100644 --- a/bika/health/overrides.zcml +++ b/bika/health/overrides.zcml @@ -11,7 +11,6 @@ - diff --git a/bika/health/permissions.py b/bika/health/permissions.py index a2cfc7d..7169637 100644 --- a/bika/health/permissions.py +++ b/bika/health/permissions.py @@ -20,6 +20,7 @@ AddSymptom = 'BIKA: Add Symptom' AddDrugProhibition = 'BIKA: Add DrugProhibition' AddInsuranceCompany = 'BIKA: Add InsuranceCompany' +AddEthnicity = 'BIKA: Add Ethnicity' # Add Permissions for specific types, if required ADD_CONTENT_PERMISSIONS = { @@ -32,7 +33,8 @@ 'VaccinationCenter': AddVaccinationCenter, 'Symptom': AddSymptom, 'DrugProhibition': AddDrugProhibition, - 'InsuranceCompany': AddInsuranceCompany + 'InsuranceCompany': AddInsuranceCompany, + 'Ethnicity': AddEthnicity } ManageDoctors = "BIKA: Manage Doctors" @@ -41,6 +43,7 @@ ViewSamples = "BIKA: View Samples" ViewAnalysisRequests = "BIKA: View AnalysisRequests" ViewInsuranceCompanies = "BIKA: View InsuranceCompanies" +ViewEthnicities = "BIKA: View Ethnicities" # Patient permissions ViewPatients = 'BIKA: View Patients' diff --git a/bika/health/permissions.zcml b/bika/health/permissions.zcml index 443a5b2..12b961a 100644 --- a/bika/health/permissions.zcml +++ b/bika/health/permissions.zcml @@ -23,7 +23,8 @@ - ++ + @@ -32,6 +33,7 @@ + # Patient permissions diff --git a/bika/health/profiles/default/controlpanel.xml b/bika/health/profiles/default/controlpanel.xml index 6ec1221..4adb252 100644 --- a/bika/health/profiles/default/controlpanel.xml +++ b/bika/health/profiles/default/controlpanel.xml @@ -130,4 +130,13 @@ BIKA: Manage Bika + + BIKA: Manage Bika + + diff --git a/bika/health/profiles/default/cssregistry.xml b/bika/health/profiles/default/cssregistry.xml index 93cd30e..55b399b 100644 --- a/bika/health/profiles/default/cssregistry.xml +++ b/bika/health/profiles/default/cssregistry.xml @@ -24,4 +24,11 @@ rendering="link" insert-after="*"/> + + diff --git a/bika/health/profiles/default/factorytool.xml b/bika/health/profiles/default/factorytool.xml index 45fa6a5..4734910 100644 --- a/bika/health/profiles/default/factorytool.xml +++ b/bika/health/profiles/default/factorytool.xml @@ -17,5 +17,6 @@ + diff --git a/bika/health/profiles/default/jsregistry.xml b/bika/health/profiles/default/jsregistry.xml index 493ea23..40cd028 100644 --- a/bika/health/profiles/default/jsregistry.xml +++ b/bika/health/profiles/default/jsregistry.xml @@ -78,6 +78,17 @@ inline="False" insert-after="*"/> + + - 316 + 317 profile-bika.lims:default diff --git a/bika/health/profiles/default/structure/bika_setup/.objects b/bika/health/profiles/default/structure/bika_setup/.objects index ec5b25d..08170c3 100644 --- a/bika/health/profiles/default/structure/bika_setup/.objects +++ b/bika/health/profiles/default/structure/bika_setup/.objects @@ -12,3 +12,4 @@ bika_symptoms,Symptoms bika_treatments,Treatments bika_vaccinationcenters,VaccinationCenters bika_insurancecompanies,InsuranceCompanies +bika_ethnicities,Ethnicities diff --git a/bika/health/profiles/default/structure/bika_setup/.preserve b/bika/health/profiles/default/structure/bika_setup/.preserve index 70241b9..dc8b558 100644 --- a/bika/health/profiles/default/structure/bika_setup/.preserve +++ b/bika/health/profiles/default/structure/bika_setup/.preserve @@ -12,4 +12,4 @@ bika_insurancecompanies bika_symptoms bika_treatments bika_vaccinationcenters - +bika_ethnicities diff --git a/bika/health/profiles/default/structure/bika_setup/bika_ethnicities/.properties b/bika/health/profiles/default/structure/bika_setup/bika_ethnicities/.properties new file mode 100644 index 0000000..a1f37e1 --- /dev/null +++ b/bika/health/profiles/default/structure/bika_setup/bika_ethnicities/.properties @@ -0,0 +1,3 @@ +[DEFAULT] +description = +title = Ethnicities \ No newline at end of file diff --git a/bika/health/profiles/default/types.xml b/bika/health/profiles/default/types.xml index c1d4cfa..3e2a673 100644 --- a/bika/health/profiles/default/types.xml +++ b/bika/health/profiles/default/types.xml @@ -17,6 +17,8 @@ + + diff --git a/bika/health/profiles/default/types/Ethnicities.xml b/bika/health/profiles/default/types/Ethnicities.xml new file mode 100644 index 0000000..45fb921 --- /dev/null +++ b/bika/health/profiles/default/types/Ethnicities.xml @@ -0,0 +1,26 @@ + + + Ethnicities + + ++resource++bika.health.images/patient_big.png + Ethnicities + bika.health + addEthnicities + + + False + True + + + + False + False + + + + + + diff --git a/bika/health/profiles/default/types/Ethnicity.xml b/bika/health/profiles/default/types/Ethnicity.xml new file mode 100644 index 0000000..3bc7ac9 --- /dev/null +++ b/bika/health/profiles/default/types/Ethnicity.xml @@ -0,0 +1,41 @@ + + + Ethnicity + The ethnicity of the patient + Ethnicity + ++resource++bika.health.images/patient.png + bika.health + addEthnicity + True + True + False + False + + + + + + + + + + + + + + + diff --git a/bika/health/profiles/default/workflows.xml b/bika/health/profiles/default/workflows.xml index cc963d1..caadb41 100644 --- a/bika/health/profiles/default/workflows.xml +++ b/bika/health/profiles/default/workflows.xml @@ -117,5 +117,11 @@ + + + + + + diff --git a/bika/health/setupdata/__init__.py b/bika/health/setupdata/__init__.py index 9ee9a0a..5ea8b91 100644 --- a/bika/health/setupdata/__init__.py +++ b/bika/health/setupdata/__init__.py @@ -1,6 +1,7 @@ from Products.CMFCore.utils import getToolByName from Products.CMFPlone.utils import safe_unicode, _createObjectByType from bika.lims import logger +from Products.CMFCore.utils import getToolByName from bika.lims.exportimport.dataimport import SetupDataSetList as SDL from bika.lims.exportimport.setupdata import WorksheetImporter from bika.lims.idserver import renameAfterCreation @@ -8,6 +9,7 @@ from bika.lims.utils import tmpID from pkg_resources import resource_filename from zope.interface import implements +import transaction class SetupDataSetList(SDL): @@ -261,6 +263,21 @@ def Import(self): renameAfterCreation(obj) +class Ethnicities(WorksheetImporter): + + def Import(self): + folder = self.context.bika_setup.bika_ethnicities + rows = self.get_rows(3) + for row in rows: + _id = folder.invokeFactory('Ethnicity', id=tmpID()) + obj = folder[_id] + if row.get('Title', None): + obj.edit(title=row['Title'], + description=row.get('Description', '')) + obj.unmarkCreationFlag() + renameAfterCreation(obj) + + class Patients(WorksheetImporter): def Import(self): @@ -275,12 +292,21 @@ def Import(self): raise IndexError("Primary referrer invalid: '%s'" % row['PrimaryReferrer']) client = client[0].getObject() + + # Getting an existing ethnicity + bsc = getToolByName(self.context, 'bika_setup_catalog') + ethnicity = bsc(portal_type='Ethnicity', Title=row.get('Ethnicity', '')) + if len(ethnicity) == 0: + raise IndexError("Invalid ethnicity: '%s'" % row['Ethnicity']) + ethnicity = ethnicity[0].getObject() + _id = folder.invokeFactory('Patient', id=tmpID()) obj = folder[_id] obj.unmarkCreationFlag() renameAfterCreation(obj) Fullname = (row['Firstname'] + " " + row.get('Surname', '')).strip() - obj.edit(title=Fullname, + obj.edit(PatientID=row.get('PatientID'), + title=Fullname, ClientPatientID = row.get('ClientPatientID', ''), Salutation = row.get('Salutation', ''), Firstname = row.get('Firstname', ''), @@ -291,7 +317,8 @@ def Import(self): BirthDate = row.get('BirthDate', ''), BirthDateEstimated =self.to_bool(row.get('BirthDateEstimated','False')), BirthPlace = row.get('BirthPlace', ''), - Ethnicity = row.get('Ethnicity', ''), + # TODO Ethnicity_Obj -> Ethnicity on health v319 + Ethnicity_Obj=ethnicity.UID(), Citizenship =row.get('Citizenship', ''), MothersName = row.get('MothersName', ''), CivilStatus =row.get('CivilStatus', ''), @@ -320,7 +347,13 @@ def Import(self): logger.error("Unable to load Feature %s"%row['Feature']) obj.unmarkCreationFlag() - renameAfterCreation(obj) + transaction.savepoint(optimistic=True) + if row.get('PatientID'): + # To maintain the patient spreadsheet's IDs, we cannot do a 'renameaftercreation()' + obj.aq_inner.aq_parent.manage_renameObject(obj.id, row.get('PatientID')) + else: + renameAfterCreation(obj) + class Analysis_Specifications(WorksheetImporter): @@ -347,8 +380,8 @@ def Import(self): 'keyword': service.getKeyword(), 'min': row['min'] if row['min'] else '0', 'max': row['max'] if row['max'] else '0', - 'minpanic': row['min'] if row['min'] else '0', - 'maxpanic': row['max'] if row['max'] else '0', + 'minpanic': row['minpanic'] if row['minpanic'] else '0', + 'maxpanic': row['maxpanic'] if row['maxpanic'] else '0', 'error': row['error'] if row['error'] else '0' }) # write objects. @@ -369,10 +402,33 @@ def Import(self): obj.unmarkCreationFlag() renameAfterCreation(obj) - +class Insurance_Companies(WorksheetImporter): + + def Import(self): + folder = self.context.bika_setup.bika_insurancecompanies + for row in self.get_rows(3): + obj = _createObjectByType("InsuranceCompany", folder, tmpID()) + if row.get('Name', None): + obj.edit( + Name=row.get('Name', ''), + EmailAddress=row.get('EmailAddress', ''), + Phone=row.get('Phone', ''), + Fax=row.get('Fax', ''), + TaxNumber=row.get('TaxNumber', ''), + AccountType=row.get('AccountType', {}), + AccountName=row.get('AccountName', {}), + AccountNumber=row.get('AccountNumber', ''), + BankName=row.get('BankName', ''), + BankBranch=row.get('BankBranch', ''), + ) + self.fill_contactfields(row, obj) + self.fill_addressfields(row, obj) + obj.unmarkCreationFlag() + renameAfterCreation(obj) from bika.lims.exportimport.setupdata import Setup as BaseSetup + class Setup(BaseSetup): def Import(self): @@ -387,3 +443,4 @@ def Import(self): EnablePanicAlert=self.to_bool(values.get('EnablePanicAlert', True)), EnableAnalysisRemarks=self.to_bool(values.get('EnableAnalysisRemarks', True)) ) + diff --git a/bika/health/setupdata/configure.zcml b/bika/health/setupdata/configure.zcml index 812583c..3759f1e 100644 --- a/bika/health/setupdata/configure.zcml +++ b/bika/health/setupdata/configure.zcml @@ -86,10 +86,22 @@ provides="bika.lims.interfaces.ISetupDataImporter" for="Products.CMFPlone.interfaces.IPloneSiteRoot"/> + + + + diff --git a/bika/health/setupdata/test/test.xlsx b/bika/health/setupdata/test/test.xlsx index 42b1594..bd7a2eb 100644 Binary files a/bika/health/setupdata/test/test.xlsx and b/bika/health/setupdata/test/test.xlsx differ diff --git a/bika/health/setuphandlers.py b/bika/health/setuphandlers.py index 993268a..8cbe943 100644 --- a/bika/health/setuphandlers.py +++ b/bika/health/setuphandlers.py @@ -6,6 +6,8 @@ from Products.CMFEditions.Permissions import ApplyVersionControl from Products.CMFEditions.Permissions import SaveNewVersion from bika.health import logger +from bika.lims.utils import tmpID +from bika.lims.idserver import renameAfterCreation from bika.health.permissions import AddAetiologicAgent from bika.health.permissions import ManageDoctors from bika.health.permissions import ViewPatients @@ -13,6 +15,7 @@ from bika.health.permissions import ViewSamples from bika.health.permissions import ViewAnalysisRequests from bika.health.permissions import ViewInsuranceCompanies +from bika.health.permissions import ViewEthnicities from bika.health.permissions import AddAetiologicAgent from bika.health.permissions import AddDoctor from bika.health.permissions import AddDrug @@ -23,6 +26,7 @@ from bika.health.permissions import AddTreatment from bika.health.permissions import AddVaccinationCenter from bika.health.permissions import AddInsuranceCompany +from bika.health.permissions import AddEthnicity from bika.health.permissions import EditPatient from bika.health.permissions import ManageDoctors from bika.lims.permissions import AddAnalysisRequest @@ -38,6 +42,25 @@ class Empty: pass +def setupEthnicities(bika_setup): + """ + Creates standard ethnicities + """ + ethnicities = ['Native American', 'Asian', 'Black', 'Native Hawaiian or Other Pacific Islander', 'White', + 'Hispanic or Latino'] + for ethnicityName in ethnicities: + folder = bika_setup.bika_ethnicities + # Generating a temporal object + _id = folder.invokeFactory('Ethnicity', id=tmpID()) + obj = folder[_id] + # Setting its values + obj.edit(title=ethnicityName, + description='') + obj.unmarkCreationFlag() + renameAfterCreation(obj) + logger.info("Standard ethnicities enabled") + + def setupHealthVarious(context): """ Setup Bika site structure """ @@ -71,6 +94,7 @@ def setupHealthVarious(context): 'bika_identifiertypes', 'bika_casesyndromicclassifications', 'bika_insurancecompanies', + 'bika_ethnicities', ): obj = bika_setup._getOb(obj_id) obj.unmarkCreationFlag() @@ -106,6 +130,8 @@ def setupHealthVarious(context): description="", condition="") + setupEthnicities(bika_setup) + def setupHealthGroupsAndRoles(context): @@ -158,6 +184,7 @@ def setupHealthPermissions(context): mp(AddInsuranceCompany, ['Manager', 'Owner', 'LabManager', 'LabClerk'], 1) mp(AddSymptom, ['Manager', 'Owner', 'LabManager', 'LabClerk'], 1) mp(AddDrugProhibition, ['Manager', 'Owner', 'LabManager', 'LabClerk'], 1) + mp(AddEthnicity, ['Manager', 'Owner', 'LabManager', 'LabClerk'], 1) mp(ApplyVersionControl, ['Manager', 'LabManager', 'LabClerk', 'Doctor', 'Analyst', 'Owner', 'RegulatoryInspector'], 1) mp(SaveNewVersion, ['Manager', 'LabManager', 'LabClerk', 'Doctor', 'Analyst', 'Owner', 'RegulatoryInspector'], 1) @@ -170,6 +197,7 @@ def setupHealthPermissions(context): mp(ViewSamples, ['Manager', 'LabManager', 'Owner', 'LabClerk', 'Doctor', 'RegulatoryInspector'], 1) mp(ViewAnalysisRequests, ['Manager', 'LabManager', 'Owner', 'LabClerk', 'Doctor', 'RegulatoryInspector'], 1) mp(ViewInsuranceCompanies, ['Manager', 'LabManager', 'Owner', 'LabClerk', 'Doctor', 'RegulatoryInspector'], 1) + mp(ViewEthnicities, ['Manager', 'LabManager', 'Owner', 'LabClerk', 'Doctor', 'RegulatoryInspector'], 1) # /clients folder permissions # Member role must have view permission on /clients, to see the list. @@ -316,6 +344,7 @@ def addColumn(cat, col): at.setCatalogsByType('EpidemiologicalYear', ['bika_setup_catalog', ]) at.setCatalogsByType('IdentifierType', ['bika_setup_catalog', ]) at.setCatalogsByType('CaseSyndromicClassification', ['bika_setup_catalog', ]) + at.setCatalogsByType('Ethnicity', ['bika_setup_catalog', ]) addIndex(bsc,'getGender', 'FieldIndex') addColumn(bsc,'getGender') diff --git a/bika/health/skins/bika_health/bika_health.css.dtml b/bika/health/skins/bika_health/bika_health.css.dtml index 621b655..a1ede78 100644 --- a/bika/health/skins/bika_health/bika_health.css.dtml +++ b/bika/health/skins/bika_health/bika_health.css.dtml @@ -44,53 +44,53 @@ } div.template-symptoms td input[type="text"] { - width:100%; + width:100%; } div.template-symptoms td, div.template-symptoms th { - padding: 0 5px 0px 5px; + padding: 0 5px 0px 5px; } div.template-symptoms td.Code, div.template-symptoms td.Onset { - width:10%; + width:10%; } div.template-symptoms td.Title { - width:30%; + width:30%; } div.template-symptoms td.Description { - width:45%; + width:45%; } div.template-caseprovisionaldiagnosis td input[type="text"] { - width:100%; + width:100%; } div.template-caseprovisionaldiagnosis td, div.template-caseprovisionaldiagnosis th { - padding: 0 5px 0px 5px; + padding: 0 5px 0px 5px; } div.template-caseprovisionaldiagnosis td.Code, div.template-caseprovisionaldiagnosis td.Onset { - width:10%; + width:10%; } div.template-caseprovisionaldiagnosis td.Title { - width:30%; + width:30%; } div.template-caseprovisionaldiagnosis td.Description { - width:45%; + width:45%; } .readonly-emphasize { - background: none repeat scroll 0 0 transparent; - border: medium none; - font-size: 1.8em; - font-weight: bold; + background: none repeat scroll 0 0 transparent; + border: medium none; + font-size: 1.8em; + font-weight: bold; } div.cg-comboItem :not(.template-ar_add) { - height:100%; + height:100%; } div.cg-DivItem { - height:auto; - text-align:left; + height:auto; + text-align:left; } /* OVERWRITE FROM bika.lims/bika_listing.css */ diff --git a/bika/health/skins/bika_health/bika_health_standard_analysis_request.css.dtml b/bika/health/skins/bika_health/bika_health_standard_analysis_request.css.dtml new file mode 100644 index 0000000..506d9fa --- /dev/null +++ b/bika/health/skins/bika_health/bika_health_standard_analysis_request.css.dtml @@ -0,0 +1,213 @@ +/* Health-standard AR add form */ +#content-core #health-standard-ar-add-form div.documentDescription { + border-top: 1px solid #ccc; + color: #333; + font-size: 1.8em; + font-weight: normal; + margin-top: 15px; + padding: 15px 0 8px; +} +#content-core #health-standard-ar-add-form div.content-block { + border: 1px solid #ccc; +} +#content-core #health-standard-ar-add-form div.content-block div.field { + border-bottom: 1px solid #aaa; + padding: 5px; + min-height:16px; + margin-top:-2px; +} +#content-core #health-standard-ar-add-form div.content-block div.field label { + float:left; + padding-top:2px; +} +#content-core #health-standard-ar-add-form div.content-block div.field input, +#content-core #health-standard-ar-add-form div.content-block div.field select, +#content-core #health-standard-ar-add-form div.content-block div.field div.readonly { + float: right; +} +#content-core #health-standard-ar-add-form div.content-block div.field select { + border-style: none none none solid; + border-width: medium medium medium 1px; + margin-top: -3px; + padding: 3px 0 4px 5px; + width: 61%; + background-color: #dfdfdf; +} +#content-core #health-standard-ar-add-form div.content-block div.field input:after, +#content-core #health-standard-ar-add-form div.content-block div.field select:after, +#content-core #health-standard-ar-add-form div.content-block div.field div.readonly:after{ + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; +} +#content-core #health-standard-ar-add-form div.content-block div.field input[type="text"], +#content-core #health-standard-ar-add-form div.content-block div.field div.readonly { + background-color: #dfdfdf; + border: medium none; + margin: 0 -2px 0 0; + padding: 1px 0 2px 10px; + width: 60%; +} +#content-core #health-standard-ar-add-form div.content-block div.field textarea { + background-color: #dedede; + border: medium none; + height: 32px; + margin: 5px -5px -5px; + padding: 5px; + color:#333; +} +#content-core #health-standard-ar-add-form div.content-block div.field textarea:disabled { + background-color:#fff; + color:#555; +} +#content-core #health-standard-ar-add-form div.content-block span.formHelp { + display:none; + visibility:hidden; +} +#content-core #health-standard-ar-add-form div.content-block div.field input[type="text"]:disabled, +#content-core #health-standard-ar-add-form div.content-block div.field div.readonly, +#content-core #health-standard-ar-add-form div.content-block div.field select:disabled { + background-color:#fff; + color: #555; + background-image:none; +} +#content-core #health-standard-ar-add-form div.content-block div.field input[type="checkbox"] { + margin-right: 59%; +} +#content-core #health-standard-ar-add-form div.content-block div.fieldErrorBox { + color: red; + font-size: 0.9em; + font-weight: bold; +} +#content-core #health-standard-ar-add-form div.content-block h2 { + background-color: #436976; + border: 1px solid #436976; + color: #fff; + font-size: 1.1em; + font-weight: bold; + margin: -1px -1px 0; + padding-left: 5px; + text-transform: uppercase; +} +#content-core #health-standard-ar-add-form div.content-block h3 { + background-color: #888; + color: #fff; + font-size: 1em; + padding-left: 5px; +} +#content-core #health-standard-ar-add-form div.content-block.patient-block, +#content-core #health-standard-ar-add-form div.content-block.insurance-block { + width: 49.5%; +} + +#content-core #health-standard-ar-add-form div.content-block.patient-block { + float:left; + max-height: 432px; + min-height: 432px; +} +#content-core #health-standard-ar-add-form div.content-block.insurance-block { + float: right; +} +#content-core #health-standard-ar-add-form div.content-block.patient-block div#section-signatures { + border-bottom:none; +} +#content-core #health-standard-ar-add-form div.content-block.analyses-block { + border:1px solid #fff; +} +#content-core #health-standard-ar-add-form div.content-block.analyses-block div.department-block { + display: inline-block; + width: 24.6%; + vertical-align:top; +} +#content-core #health-standard-ar-add-form div.content-block.analyses-block div.department-block h2 { + margin:0px; +} +#content-core #health-standard-ar-add-form div.content-block.analyses-block div.department-block ul { + border: 1px solid #888; + list-style: none outside none; + margin: 0; + padding: 5px; +} +#content-core #health-standard-ar-add-form div.content-block.analyses-block div.department-block ul li { + line-height:100%; +} +#content-core #health-standard-ar-add-form div.content-block.analyses-block div.department-block ul li span.keyword { + color: #888; + font-size: 0.7em; + vertical-align: top; +} +#content-core #health-standard-ar-add-form div.content-block.analyses-block div.department-block ul li span.title { + font-size: 0.85em; + line-height: 100%; + padding-left: 5px; + vertical-align: top; +} +#content-core #health-standard-ar-add-form div.clearfix { + clear: both; + padding-bottom: 10px; +} + +#content-core #health-standard-ar-add-form div.content-block.analyses-block div.department-block ul li .specs { + float:right; + display:none; +} +#content-core #health-standard-ar-add-form div.content-block.analyses-block div.department-block ul li .spec_bit { + border: 1px solid #888; + font-size: 0.8em; + margin-right: 3px; + padding-right: 1px; + text-align: right; + width: 20px; +} +#content div.ar-add-template-classic-button { + float:right; +} +#content div.ar-add-template-classic-button a { + background-color:#fff; + border: 1px solid #cdcdcd; + padding: 4px 10px; +} +#content div.ar-add-template-classic-button input { + background-color:#fff; + border: 1px solid #cdcdcd; + padding: 2px 10px 2px; + margin-top: -5.5px; +} +#content div.ar-add-template-classic-button a:hover, +#content div.ar-add-template-classic-button input:hover{ + background-color: #ddd; + color:#fff; +} +@media print { + body { + font-size: 9px; + } + #content div.ar-add-template-classic-button, + div#portal-theme{ + display: none; + } + #content-core #health-standard-ar-add-form div.content-block div.field { + border-bottom: 1px solid #aaa; + padding: 5px; + min-height:16px; + margin-top:-2px; + } + #content-core #health-standard-ar-add-form div.content-block div.field input { + font-size: 9px; + } + #content-core #health-standard-ar-add-form div.content-block div.field select { + font-size: 9px; + outline: 0; + padding-left: 1px; + } + #content-core #health-standard-ar-add-form div.content-block div.field textarea { + font-size: 8px; + } + #content-core #health-standard-ar-add-form div.analyses-block { + page-break-after: always; + } + +} \ No newline at end of file diff --git a/bika/health/static/js/bika.health.analysisrequest.add.js b/bika/health/static/js/bika.health.analysisrequest.add.js index 6897bcd..d575000 100644 --- a/bika/health/static/js/bika.health.analysisrequest.add.js +++ b/bika/health/static/js/bika.health.analysisrequest.add.js @@ -42,12 +42,18 @@ function HealthAnalysisRequestAddView() { // ClientPatientID fields change. $('[id$="_ClientPatientID"]').bind("selected paste blur change", function () { colposition = $(this).closest('td').attr('column'); + if (colposition == undefined){ + // we are on the specific health template + colposition = 0} loadPatient($(this).val(), colposition); checkClientContacts(); }); $('[id$="_Patient"]').bind("selected paste blur change", function () { colposition = $(this).closest('td').attr('column'); + if (colposition == undefined){ + // we are on the specific health template + colposition = 0} uid = $("#" + this.id + "_uid").val(); loadClientPatientID(uid, colposition); checkClientContacts(); @@ -59,6 +65,9 @@ function HealthAnalysisRequestAddView() { // See https://github.com/bikalabs/bika.health/issues/100 $('[id$="_Sample"]').bind("selected paste blur", function () { colposition = $(this).closest('td').attr('column'); + if (colposition == undefined){ + // we are on the specific health template + colposition = 0} uid = $("#" + this.id + "_uid").val(); loadPatientFromSample(uid, colposition); checkClientContacts(); @@ -250,46 +259,64 @@ function HealthAnalysisRequestAddView() { clientuid = $("#ar_" + col + "_Client_uid").val(); // Batch searches - element = $("#ar_" + col + "_Batch") - base_query = $.parseJSON($(element).attr("base_query")); - base_query['getClientUID'] = clientuid; - applyComboFilter(element, base_query); + element = $("#ar_" + col + "_Batch"); + if (element.length > 0){ + base_query = $.parseJSON($(element).attr("base_query")); + if (base_query != null) { + base_query['getClientUID'] = clientuid; + applyComboFilter(element, base_query); + } + } // Patient searches - element = $("#ar_" + col + "_Patient") - base_query = $.parseJSON($(element).attr("base_query")); - base_query['getPrimaryReferrerUID'] = clientuid; - applyComboFilter(element, base_query); + element = $("#ar_" + col + "_Patient"); + if (element.length > 0) { + base_query = $.parseJSON($(element).attr("base_query")); + if (base_query != null) { + base_query['getPrimaryReferrerUID'] = clientuid; + applyComboFilter(element, base_query); + } + } // CPID searches element = $("#ar_" + col + "_ClientPatientID") - base_query = $.parseJSON($(element).attr("base_query")); - base_query['getPrimaryReferrerUID'] = clientuid; - applyComboFilter(element, base_query); + if (element.length > 0) { + base_query = $.parseJSON($(element).attr("base_query")); + if (base_query != null) { + base_query['getPrimaryReferrerUID'] = clientuid; + applyComboFilter(element, base_query); + } + } // Contact searches element = $("#ar_" + col + "_Contact") - base_query = $.parseJSON($(element).attr("base_query")); - base_query['getParentUID'] = clientuid; - applyComboFilter(element, base_query); + if (element.length > 0) { + base_query = $.parseJSON($(element).attr("base_query")); + if (base_query != null) { + base_query['getParentUID'] = clientuid; + applyComboFilter(element, base_query); + } + } } } function applyComboFilter(element, base_query) { - $(element).attr("base_query", $.toJSON(base_query)); - options = $.parseJSON($(element).attr("combogrid_options")); - options.url = window.location.href.split("/ar_add")[0] + "/" + options.url - options.url = options.url + '?_authenticator=' + $('input[name="_authenticator"]').val(); - options.url = options.url + '&catalog_name=' + $(element).attr('catalog_name'); - options.url = options.url + '&base_query=' + $.toJSON(base_query); - options.url = options.url + '&search_query=' + $(element).attr('search_query'); - options.url = options.url + '&colModel=' + $.toJSON($.parseJSON($(element).attr('combogrid_options'))["colModel"]); - options.url = options.url + '&search_fields=' + $.toJSON($.parseJSON($(element).attr('combogrid_options'))["search_fields"]); - options.url = options.url + '&discard_empty=' + $.toJSON($.parseJSON($(element).attr('combogrid_options'))["discard_empty"]); - options['force_all'] = 'false'; - $(element).combogrid(options); - $(element).addClass("has_combogrid_widget"); - $(element).attr('search_query', '{}'); + if (element.length > 0) { + $(element).attr("base_query", $.toJSON(base_query)); + options = $.parseJSON($(element).attr("combogrid_options")); + options.url = window.location.href.split("/ar_add")[0] + "/" + options.url + options.url = options.url + '?_authenticator=' + $('input[name="_authenticator"]').val(); + options.url = options.url + '&catalog_name=' + $(element).attr('catalog_name'); + options.url = options.url + '&base_query=' + $.toJSON(base_query); + options.url = options.url + '&search_query=' + $(element).attr('search_query'); + options.url = options.url + '&colModel=' + $.toJSON($.parseJSON($(element).attr('combogrid_options'))["colModel"]); + options.url = options.url + '&search_fields=' + $.toJSON($.parseJSON($(element).attr('combogrid_options'))["search_fields"]); + options.url = options.url + '&discard_empty=' + $.toJSON($.parseJSON($(element).attr('combogrid_options'))["discard_empty"]); + options['force_all'] = 'false'; + $(element).combogrid(options); + $(element).addClass("has_combogrid_widget"); + $(element).attr('search_query', '{}'); + } } /** @@ -386,14 +413,17 @@ function HealthAnalysisRequestAddView() { // Only allow the selection of batches from this patient element = $("#ar_" + col + "_Batch") base_query = $.parseJSON($(element).attr("base_query")); - base_query['getPatientUID'] = data['PatientUID']; - applyComboFilter(element, base_query); - + if (base_query != null) { + base_query['getPatientUID'] = data['PatientUID']; + applyComboFilter(element, base_query); + } // Contact searches element = $("#ar_" + col + "_Contact") base_query = $.parseJSON($(element).attr("base_query")); - base_query['getParentUID'] = data['ClientUID']; - applyComboFilter(element, base_query); + if (base_query != null) { + base_query['getParentUID'] = data['ClientUID']; + applyComboFilter(element, base_query); + } } function resetPatientData(col) { diff --git a/bika/health/static/js/bika.health.analysisrequest.ar_add_health_standard.js b/bika/health/static/js/bika.health.analysisrequest.ar_add_health_standard.js new file mode 100644 index 0000000..88444f5 --- /dev/null +++ b/bika/health/static/js/bika.health.analysisrequest.ar_add_health_standard.js @@ -0,0 +1,659 @@ +/** + * Controller class for AnalysisRequest add view for health standard template + */ +function HealthStandardAnalysisRequestAddView() { + /** + * Correct functionality: + * ====================== + * + * AR coming from batch: + * --------------------- + * - Patient -> From batch. Fields blocked. + * - Guarantor and insurance -> From batch's patient. Fields blocked. + * - The save button is going to crate -> an AR + * + * AR coming from patient: + * ----------------------- + * - Patient -> From patient. Fields blocked. + * - Guarantor and insurance -> From the current patient. Fields blocked + * - The save button is going to crate -> a case if the checkbox is selected, an AR (related with the case.) + * + * AR coming from client: + * ---------------------- + * - Patient -> Must be chosen first! + * -- If a new patient is being created (NewPatientCheckbox == selected) -> + * - Guarantor's + insurance ready to be edited. Fields unblocked. + * - Patient fields unblocked. + * --- The save button is going to create -> a patient, a case related with the created patient if required, an AR (inside the case) + * + * -- If an old patient is selected (NewPatientCheckbox == unselected) -> + * - Guarantor + insurance from selected patient. Fields blocked. + * - Patient fields blocked. + * --- The save button is going to create -> a case if required, an AR. + * + * How it works? + * ============= + * We can came from three different positions: from a batch, from a patient or from a client. + * If we come from a batch, all the client, patient, insurance and doctor's fields will be filled out. + * If we come from a patient, the client, patient and insurance will be filled out. We can decide if we want to + * create a new batch related with the new analysis request. + * If we come from a client(insurance company), we will be able to select an existent patient or create a new one. + * In the case we've selected an existing patient, all patient and insurance's fields will be blocked and filled out. + * If the checkbox "new patient" is selected all fields will be enabled. + * + * The "GLOBAL" SAVE BUTTON: When the user has filled out all the required fields, he clicks on the save button. + * This is NOT the form's button! So after clicking this button, the JS will check if a new patient and a new case + * should be created and related each other. + * Once these objects' creation (or not) have finished, the data from the patient and the case is copied inside + * the analysis request's html form. Then, the javascript clicks on the hidden save button from the form. + * Consequently the ajaxForm is gonna create the new Analysis Request. + * + */ + + var that = this; + + // ------------------------------------------------------------------------ + // PUBLIC FUNCTIONS + // ------------------------------------------------------------------------ + + /** + * Entry-point method for StandardAnalysisRequestAddView + */ + this.load = function () { + + var datafilled = false; + var frombatch = window.location.href.search('/batches/') >= 0; + var frompatient = document.referrer.search('/patients/') >= 0; + + if (frombatch) { + // The current AR add View comes from a batch. Automatically fill + // the Client, Patient and Doctor fields and set them as readonly. + // Deactivate the option and fields to create a new patient + datafilled = true; + var batchid = window.location.href.split("/batches/")[1].split("/")[0]; + cancelPatientCreationCheckBox(); + // The fields with data should be fill out and set them as readonly. + setDataFromBatch(batchid); + // Hide and clear the CreateNewCase checkbox + $('input#CreateNewCase').prop('checked',false).prop('disabled', true) + } + else if (frompatient) { + // The current AR add View comes from a patient's AR folder view. + // Automatically fill the Client and Patient fields and set them + // as readonly. + datafilled = true; + cancelPatientCreationCheckBox(); + setDataFromPatient() + } + + if (!datafilled) { + // The current AR Add View doesn't come from a batch nor patient. + // Handle event firing when Patient or ClientPatientID fields change. + + // ClientPatientID changed + $('[id$="_ClientPatientID"]').bind("selected paste blur change", function () { + // Load the new patients data + $('input[id$="_Patient"]').trigger("change"); + }); + + // PatientFullName changed + $('[id$="_Patient"]').bind("selected paste blur change", function () { + // Set the new patient data + var patientUID = $(this).attr('uid'); + setPatientData(patientUID); + // Set the new insurance's patient. We can call the function used to fill the insurance data when the + // AR comes from a batch + setInsuranceDataFromBatch(patientUID); + }); + + // NewPatient checkbox change + var np = $('input#NewPatient'); + np.bind("click change", function () { + // The old patient's data should be uploaded/cleaned + hideShowFirstnameSurname(); + if (np.prop('checked')) { + // If we are creating a new patient we should allow to fill the insurance stuff + cleanAndEnableDisableInsuranceData(false); + } + else{ + cleanAndEnableDisableInsuranceData(true); + } + }); + } + + // Binding the triggers used on all cases + $('[id$="_Doctor"]').bind("selected paste blur change", function () { + setDoctorCode(); + }); + $('input#PatientAsGuarantor').bind("click", function() { + var cb = $('input#PatientAsGuarantor'); + if (cb.prop('checked')) { + cleanAndEnableDisableInsuranceData(true); + cb.prop('disabled', false); + cb.prop('checked', true); + } + else{ + cleanAndEnableDisableInsuranceData(false) + } + }); + + // Functions to execute at page load + hideShowFirstnameSurname(); + disableInsuranceData(); + // Unbind previous (from lims) loadAjaxSubmitHandler on submit button. + $("#analysisrequest_edit_form").ajaxFormUnbind(); + // Bind the new handler + loadAjaxSubmitHealthHandler(); + $('input#print').bind('click', function(){ + printAnalysisRequest() + }); + + }; + // ------------------------------------------------------------------------ + // PRIVATE FUNCTIONS + // ------------------------------------------------------------------------ + + function setDataFromBatch(batchid){ + /** + * It obtains the patient's data when the AR comes from the batch (case) view. + */ + $('#archetypes-fieldname-Batch').remove(); + $.ajaxSetup({async:false}); + // Get batch data + window.bika.lims.jsonapi_read({ + catalog_name:'bika_catalog', + id:batchid + }, function(data){ + // Set patient data + setPatientData(data.objects[0]['getPatientUID']); + // Set the doctor's code + $('input#DoctorsCode').val(data.objects[0]['getDoctorID']).prop('disabled', true); + // Set the insurance company and block the insurance fields if it's necessary + setInsuranceDataFromBatch(data.objects[0]['getPatientUID']); + }); + $.ajaxSetup({async:true}); + } + + function setDataFromPatient(){ + /** + * It obtains the patient data when the AR comes from the patient view. + */ + var pid = document.referrer.split("/patients/")[1].split("/")[0]; + $.ajaxSetup({async:false}); + // Get patient data + window.bika.lims.jsonapi_read({ + catalog_name:'bika_patient_catalog', + id:pid + }, function(data){ + setPatientData(data.objects[0]['UID']); + setInsuranceDataFromPatient(data.objects[0]) + }); + $.ajaxSetup({async:true}); + } + + // Patient template controller ---------------------------------------------------- + + function cancelPatientCreationCheckBox() { + /** + * If the "create a new patient" actions should be canceled, this function clear the checkbox to create + * a new patient and deactivates the possibility of interact with the checkbox. + */ + var cb = $('input#NewPatient'); + cb.prop('disabled', true).prop('checked', false); + } + + function cleanPatientData(){ + /** + * Clean all fields used by patient + */ + $('[id$="_Patient"]').val('').attr('uid',''); + $('input#Surname').val(''); + $('input#Firstname').val(''); + $("input#BirthDate").val(''); + $('input#BirthDateEstimated').prop('checked', false); + $('select#Gender').val('dk'); + $('input#BusinessPhone').val('').attr('uid',''); + $('input#HomePhone').val(''); + $('input#MobilePhone').val(''); + $('input#EmailAddress').val(''); + $('input#ar_0_ClientPatientID').val('').attr('uid',''); + $('input#PatientID').val('') + } + + function hideShowFirstnameSurname(){ + /** + * Hide/show the fields surname, first name and patient depending on the "New patient"'s checkbox state. + * This function clear all patient data too. + */ + var cb = $('input#NewPatient'); + if (cb.prop('checked')) { + $('div#archetypes-fieldname-Patient').hide(); + $('div#PatientID').hide(); + $('div#archetypes-fieldname-Surname').show(); + $('div#archetypes-fieldname-Firstname').show(); + // Hiding the client-patient reference input and showing the simple one used when creating a patient + $('input#ar_0_ClientPatientID').hide(); + $('input#ClientPatientID').show(); + + // Enable and clear all the fields + $('input#Surname').prop('disabled', false); + $('input#Firstname').prop('disabled', false); + $("input#BirthDate").prop('disabled', false); + $('input#BirthDateEstimated').prop('disabled', false).prop('checked', false); + $('select#Gender').prop('disabled', false).val('dk'); + $('input#BusinessPhone').prop('disabled', false); + $('input#HomePhone').prop('disabled', false); + $('input#MobilePhone').prop('disabled', false); + $('input#EmailAddress').prop('disabled', false); + // We should clean the old data + cleanPatientData() + } + else { + + $('div#archetypes-fieldname-Patient').show(); + $('div#PatientID').show(); + $('div#archetypes-fieldname-Surname').hide(); + $('div#archetypes-fieldname-Firstname').hide(); + // Showing the client-patient reference input and hiding the simple one + $('input#ar_0_ClientPatientID').show(); + $('input#ClientPatientID').hide().val(''); + + // Disable and clear all the fields + $("input#BirthDate").prop('disabled', true); + $('input#BirthDateEstimated').prop('disabled', true); + $('select#Gender').prop('disabled', true); + $('input#BusinessPhone').prop('disabled', true); + $('input#HomePhone').prop('disabled', true); + $('input#MobilePhone').prop('disabled', true); + $('input#EmailAddress').prop('disabled', true); + //$('input#ar_0_ClientPatientID').prop('disabled', true); + } + } + + function setPatientData(patientuid){ + /** + * It fills out the patient data that remains from the bika.analysisrequest.add.js and blocks it. + * @patientuid The patient's uid + */ + if (patientuid == ''){ + // All data patient should be cleaned because no patient has been selected + cleanPatientData(); + // Disabling the reference input client-patient uid to allow to introduce a patient from here + $('input#ar_0_ClientPatientID').prop('disabled', false); + } + $.ajaxSetup({async:false}); + window.bika.lims.jsonapi_read({ + catalog_name: 'bika_patient_catalog', + UID: patientuid + },function(dataobj){ + if (dataobj.objects.length > 0) { + var data = dataobj.objects[0]; + $("input#BirthDate").val(data['BirthDate']).prop('disabled', true); + $('input#BirthDateEstimated').prop('checked', data['BirthDateEstimated']).prop('disabled', true); + $('select#Gender').val(data['Gender']).prop('disabled', true); + $('input#BusinessPhone').val(data['BusinessPhone']).prop('disabled', true); + $('input#HomePhone').val(data['HomePhone']).prop('disabled', true); + $('input#MobilePhone').val(data['MobilePhone']).prop('disabled', true); + $('input#EmailAddress').val(data['EmailAddress']).prop('disabled', true); + $('input#ar_0_ClientPatientID').val(data['ClientPatientID']).prop('disabled', true); + $('input#PatientID').val(data['getPatientID']) + } + }); + $.ajaxSetup({async:true}); + } + + // Doctor's template controller ------------------------------------------------ + + function setDoctorCode(){ + /** + * Set the Doctor's code from the selected Referring Doctor. + */ + var doctoruid = $('[id$="_Doctor"]').attr('uid'); + $.ajaxSetup({async:false}); + window.bika.lims.jsonapi_read({ + catalog_name:'portal_catalog', + portal_type: 'Doctor', + UID:doctoruid + }, function(data){ + $('input#DoctorsCode').val(data.objects[0]['DoctorID']); + }); + $.ajaxSetup({async:true}); + } + + // Insurance's template controller -------------------------------------------------- + + function setInsuranceDataFromBatch(patientuid){ + /** + * The function checks if the patient is related with an insurance company. In the affirmative case, + * the insurance fields will be filled out and blocked + * @patientuid The patient's uid where the function will look for the insurance company. + */ + if (patientuid == ''){ + // It means that the patient fields have been cleaned of data. + cleanAndEnableDisableInsuranceData(true); + } + else { + $.ajaxSetup({async: false}); + window.bika.lims.jsonapi_read({ + catalog_name: 'bika_patient_catalog', + UID: patientuid + }, function (data) { + if (data.objects[0]['InsuranceCompany_uid'] != '') { + setInsurance(data.objects[0]['InsuranceCompany_uid']); + } + // Since data variable stores the patient fields, we can set all guarantor stuff. + setGuarantor(data.objects[0]); + }); + $.ajaxSetup({async: true}); + } + } + + function disableInsuranceData(){ + /** + * Disable all the fields related with the insurance + */ + $('input#ar_0_InsuranceCompany').prop('disabled', true); + $('input#PatientAsGuarantor').prop('disabled', true); + $('input#GuarantorID').prop('disabled', true); + $('input#GuarantorSurname').prop('disabled', true); + $('input#GuarantorFirstname').prop('disabled', true); + $('select[id="PostalAddress.country"]').prop('disabled', true); + $('select[id="PostalAddress.state"]').prop('disabled', true); + $('select[id="PostalAddress.district"]').prop('disabled', true); + $('input[id="PostalAddress.city"]').prop('disabled', true); + $('input[id="PostalAddress.zip"]').prop('disabled', true); + $('textarea[id="PostalAddress.address"]').prop('disabled', true); + $('input#GuarantorBusinessPhone').prop('disabled', true); + $('input#GuarantorHomePhone').prop('disabled', true); + $('input#GuarantorMobilePhone').prop('disabled', true); + } + + function cleanAndEnableDisableInsuranceData(state){ + /** + * Clean and enable/disable all the fields related with the insurance + * @state True/False depending on the disable state + */ + $('input#ar_0_InsuranceCompany').val('').attr('uid','').prop('disabled', state); + $('input#PatientAsGuarantor').prop('checked', false).prop('disabled', state); + $('input#GuarantorID').val('').prop('disabled', state); + $('input#GuarantorSurname').val('').prop('disabled', state); + $('input#GuarantorFirstname').val('').prop('disabled', state); + $('select[id="PostalAddress.country"]') + .val($('[id="PostalAddress.country"] option[selected="selected"]').val()).prop('disabled', state); + $('select[id="PostalAddress.state"]').val('').prop('disabled', state); + $('select[id="PostalAddress.district"]').val('').prop('disabled', state); + $('input[id="PostalAddress.city"]').val('').prop('disabled', state); + $('input[id="PostalAddress.zip"]').val('').prop('disabled', state); + $('textarea[id="PostalAddress.address"]').val('').prop('disabled', state); + $('input#GuarantorBusinessPhone').val('').prop('disabled', state); + $('input#GuarantorHomePhone').val('').prop('disabled', state); + $('input#GuarantorMobilePhone').val('').prop('disabled', state); + } + + function setInsuranceDataFromPatient(patientdata){ + /** + * The function checks if the patient is related with an insurance company. In the affirmative case, + * the insurance fields will be filled out and blocked. + * @patientuid A dictionary with the patient's data where the function will look for the insurance company. + */ + // Check if patient is related with an insurance company + if (patientdata['InsuranceCompany_uid'] != ''){ + setInsurance(patientdata['InsuranceCompany_uid']); + } + // Fill out guarantor's fields + setGuarantor(patientdata); + } + + function setInsurance(insuranceuid){ + /** + * This function fill out the insurance field and block it. + * @insuranceuid The insurance company UID + */ + if (insuranceuid != undefined) { + $.ajaxSetup({async: false}); + window.bika.lims.jsonapi_read({ + catalog_name: 'bika_setup_catalog', + content_type: 'InsuranceCompany', + UID: insuranceuid + }, function (dataobj) { + var data = dataobj.objects[0]; + $('input#ar_0_InsuranceCompany').val(data['Title']).attr('uid', data['UID']).prop('disabled', true); + }); + $.ajaxSetup({async: true}); + } + } + + function setGuarantor(patientdata) { + /** + * It fills out all guarantor's fields. + * @patientdata It's a dictionary with the patient's data + */ + // The AR comes from batch or patients view + $('input#PatientAsGuarantor').prop('checked', patientdata['PatientAsGuarantor']).prop('disabled', true); + $('input#GuarantorID').val(patientdata['GuarantorID']).prop('disabled', true); + $('input#GuarantorSurname').val(patientdata['GuarantorSurname']).prop('disabled', true); + $('input#GuarantorFirstname').val(patientdata['GuarantorFirstname']).prop('disabled', true); + $('select[id="PostalAddress.country"]').val(patientdata['PostalAddress']['country']).prop('disabled', true); + $('select[id="PostalAddress.state"]').val(patientdata['PostalAddress']['state']).prop('disabled', true); + $('select[id="PostalAddress.district"]').val(patientdata['PostalAddress']['district']).prop('disabled', true); + $('input[id="PostalAddress.city"]').val(patientdata['PostalAddress']['city']).prop('disabled', true); + $('input[id="PostalAddress.zip"]').val(patientdata['PostalAddress']['zip']).prop('disabled', true); + $('textarea[id="PostalAddress.address"]').val(patientdata['PostalAddress']['address']).prop('disabled', true); + $('input#GuarantorBusinessPhone').val(patientdata['GuarantorBusinessPhone']).prop('disabled', true); + $('input#GuarantorHomePhone').val(patientdata['GuarantorHomePhone']).prop('disabled', true); + $('input#GuarantorMobilePhone').val(patientdata['GuarantorMobilePhone']).prop('disabled', true); + } + + // Defining the health submit handler ----------------------------------------------------- + + function loadAjaxSubmitHealthHandler(){ + /** + * This functions builds the options to create the objects and binds the needed functions to create the objects + * after submit. + * Since the different objects definitions are split into different forms, on the form's submit we have to create + * every object (if it's necessary) from every form, and copy its data into the analysis request's form to create + * the analysis request with all the recently created objects. + */ + $('input#global_save_button').bind('click', function(){ + // If the Analysis Request comes form a case (batch), the fields ClientPatientID, Doctor and Patient should + // be copied from their forms to the Analysis Request form. + if ($('input#NewPatient').prop('checked')){ + // A patient should be created + createPatient(); + } + if ($('input#CreateNewCase').prop('checked')){ + // Creating a case + createCase(); + } + // Coping the patient from the patient's creation form to the analysis request's creation form + $("form#analysisrequest_patient_edit_form #archetypes-fieldname-Patient") + .clone().appendTo("form#analysisrequest_edit_form").hide(); + // Coping the doctor + $("div#archetypes-fieldname-Doctor") + .clone().appendTo("form#analysisrequest_edit_form").hide(); + // Coping the Client-Patient-ID + $("form#analysisrequest_patient_edit_form #archetypes-fieldname-ClientPatientID") + .clone().appendTo("form#analysisrequest_edit_form").hide(); + + var options = createAR(); + $("#analysisrequest_edit_form").ajaxForm(options); + // Click on analysis request form to trigger the AR creation + $('form#analysisrequest_edit_form input[name="save_button"]').click(); + }); + } + + // Creating an AR --------------------------------------------------------------------- + + function createAR(){ + /** + * This function creates an Analysis Request. + */ + var options = { + url: window.location.href.split("/portal_factory")[0] + "/analysisrequest_submit", + dataType: "json", + data: {"_authenticator": $("input[name='_authenticator']").val()}, + beforeSubmit: function() { + $("input[class~='context']").prop("disabled",true); + }, + success: function(responseText) { + var destination; + if(responseText.success !== undefined){ + if(responseText.stickers !== undefined){ + destination = window.location.href + .split("/portal_factory")[0]; + var ars = responseText.stickers; + var template = responseText.stickertemplate; + var q = "/sticker?template="+template+"&items="; + q = q + ars.join(","); + window.location.replace(destination+q); + } else { + destination = window.location.href + .split("/portal_factory")[0]; + window.location.replace(destination); + } + } else { + var msg = ""; + for(var error in responseText.errors){ + var x = error.split("."); + var e; + if (x.length == 2){ + e = x[1] + ", Column " + (+x[0]) + ": "; + } else { + e = ""; + } + msg = msg + e + responseText.errors[error] + "
"; + } + window.bika.lims.portalMessage(msg); + window.scroll(0,0); + $("input[class~='context']").prop("disabled", false); + } + }, + error: function(XMLHttpRequest, statusText) { + window.bika.lims.portalMessage(statusText); + window.scroll(0,0); + $("input[class~='context']").prop("disabled", false); + } + }; + return options; + } + // Creating a Patient ---------------------------------------------------------------- + + function createPatient(){ + /** + * This function creates a patient via ajax and jsonapi from the data introduced in the form. + */ + var request_data = { + obj_path: '/Plone/patients', + obj_type: 'Patient', + ClientPatientID: $('input#ClientPatientID').val(), + Surname: $('#Surname').val(), + Firstname: $('#Firstname').val(), + BirthDate: $('#BirthDate').val(), + BirthDateEstimated: $('#BirthDateEstimated').prop('checked'), + Gender: $('#Gender').val(), + HomePhone: $('#HomePhone').val(), + MobilePhone: $('#MobilePhone').val(), + BusinessPhone: $('#BusinessPhone').val(), + EmailAddress: $('#EmailAddress').val(), + PatientAsGuarantor: $('#PatientAsGuarantor').prop('checked'), + PrimaryReferrer: "portal_type:Client|UID:" + $('input#ar_0_Client_uid').val() + }; + if (!request_data['PatientAsGuarantor']){ + var request_data_ext = { + GuarantorID: $('#GuarantorID').val(), + GuarantorFirstname: $('#GuarantorFirstname').val(), + GuarantorSurname: $('#GuarantorSurname').val(), + PostalAddress: $.toJSON({country:$('[id="PostalAddress.country"]').val(), state:$('[id="PostalAddress.state"]').val(), + city:$('[id="PostalAddress.city"]').val(), address:$('[id="PostalAddress.address"]').val(), + zip:$('[id="PostalAddress.zip"]').val()}), + GuarantorHomePhone: $('#GuarantorHomePhone').val(), + GuarantorMobilePhone: $('#GuarantorMobilePhone').val(), + GuarantorBusinessPhone: $('#GuarantorBusinessPhone').val() + }; + $.extend(request_data,request_data_ext) + } + $.ajaxSetup({async: false}); + $.ajax({ + type: "POST", + dataType: "json", + url: window.portal_url + "/@@API/create", + data: request_data, + success: function(data){ + // Getting the case's uid + window.bika.lims.jsonapi_read({ + catalog_name: 'bika_patient_catalog', + content_type: 'Patient', + id: data['obj_id'] + }, function (dataobj) { + // After creating the new patient, we should fill the "existing patient"'s input. When we are + // creating the analysis request, this input is going to be cloned to the analysis request form, and + // consequently, the AR and the patient will be related. + $('input#ar_0_Patient').attr('uid', dataobj.objects[0]['UID']); + $('input#ar_0_Patient_uid').val(dataobj.objects[0]['UID']); + $('input#ar_0_Patient').val(dataobj.objects[0]['title']) + }); + }, + error: function(XMLHttpRequest, statusText) { + window.bika.lims.portalMessage(statusText); + window.scroll(0,0); + $("input[class~='context']").prop("disabled", false); + } + }); + $.ajaxSetup({async: true}); + } + + // Creating a batch (case) ------------------------------------------------------------ + + function createCase(){ + /** + * This function reads the data from the doctor and the patient, and consequently creates a case. + * Finally it fills the case's input inside the analysis request form. + */ + var patientuid = $('input#ar_0_Patient').attr('uid'); + var doctoruid = $('input#ar_0_Doctor').attr('uid'); + var clientuid = $('input#ar_0_Client_uid').val(); + var request_data = { + obj_path: '/Plone/batches', + obj_type: 'Batch', + Patient: "catalog_name:bika_patient_catalog|portal_type:Patient|UID:" + patientuid, + Doctor:"portal_type:Doctor|UID:" + doctoruid, + Client:"portal_type:Client|UID:" + clientuid + }; + $.ajaxSetup({async: false}); + $.ajax({ + type: "POST", + dataType: "json", + url: window.portal_url + "/@@API/create", + data: request_data, + success: function(data){ + // To obtain the case's uid + window.bika.lims.jsonapi_read({ + catalog_name: 'bika_catalog', + content_type: 'Batch', + id: data['obj_id'] + }, function (dataobj) { + // Writing the case's uid inside the analysis request creation's form. Thus when the analysis + // request form submits, it will catch the case's uid and create it. + $('form#analysisrequest_edit_form input#ar_0_Batch').attr('uid', dataobj.objects[0]['UID']); + $('form#analysisrequest_edit_form input#ar_0_Batch_uid').val(dataobj.objects[0]['UID']); + }); + // The case's input also needs their id. + $('form#analysisrequest_edit_form input#ar_0_Batch').val(data['obj_id']); + }, + error: function(XMLHttpRequest, statusText) { + window.bika.lims.portalMessage(statusText); + window.scroll(0,0); + $("input[class~='context']").prop("disabled", false); + } + }); + $.ajaxSetup({async: true}); + } + + + function printAnalysisRequest(){ + /** + * This function triggers the browser's print-page option. + */ + window.print() + } +} diff --git a/bika/health/static/js/bika.health.loader.js b/bika/health/static/js/bika.health.loader.js index 420ac93..96660f0 100644 --- a/bika/health/static/js/bika.health.loader.js +++ b/bika/health/static/js/bika.health.loader.js @@ -30,7 +30,10 @@ window.bika.health.controllers = { 'HealthPatientPublicationPrefsEditView'], ".template-ar_add #analysisrequest_edit_form": - ['HealthAnalysisRequestAddView', ], + ['HealthAnalysisRequestAddView'], + + ".template-ar_add #health-standard-ar-add-form": + ['HealthStandardAnalysisRequestAddView'], ".template-base_edit.portaltype-bikasetup": ['HealthBikaSetupEditView'], diff --git a/bika/health/static/js/bika.health.patient.js b/bika/health/static/js/bika.health.patient.js index 2bf17ba..109141b 100644 --- a/bika/health/static/js/bika.health.patient.js +++ b/bika/health/static/js/bika.health.patient.js @@ -144,6 +144,10 @@ function HealthPatientEditView() { $("input#InvoiceToInsuranceCompany").live('change',function() { checkInvoiceToInsuranceCompany(this); }); + $("#PatientAsGuarantor").live('change',function() { + hide_show_guarantor_fields(); + }); + hide_show_guarantor_fields(); } /** @@ -316,6 +320,23 @@ function HealthPatientEditView() { $(item).prop('checked', false).unbind("click"); } } + + function hide_show_guarantor_fields(){ + /** + * If the "Patient is the guarantor" checkbox is set, the guarantor's fields are going to be hidden. + * In the opposite situation, the opposite action is going to happen. + */ + var fields = $('[data-fieldname*="Guarantor"]').not('#archetypes-fieldname-PatientAsGuarantor'); + var address_widget = $('[id*="GuarantorPostalAddress"]').closest('fieldset'); + if ($("#PatientAsGuarantor").attr('checked')?true:false){ + fields.hide(); + address_widget.hide(); + } + else { + fields.show(); + address_widget.show(); + } + } } diff --git a/bika/health/upgrade/configure.zcml b/bika/health/upgrade/configure.zcml index 24addee..103309d 100644 --- a/bika/health/upgrade/configure.zcml +++ b/bika/health/upgrade/configure.zcml @@ -65,4 +65,13 @@ handler="bika.health.upgrade.to316.upgrade" sortkey="1" profile="bika.health:default"/> + + diff --git a/bika/health/upgrade/to317.py b/bika/health/upgrade/to317.py new file mode 100644 index 0000000..68a32f8 --- /dev/null +++ b/bika/health/upgrade/to317.py @@ -0,0 +1,113 @@ +from Acquisition import aq_inner +from Acquisition import aq_parent +from Products.CMFCore.utils import getToolByName +from bika.lims.utils import tmpID +from bika.lims.idserver import renameAfterCreation +from bika.health.permissions import AddEthnicity, ViewEthnicities +from bika.lims import logger + + +def upgrade(tool): + + # Adding bika.health.analysisrequest.ar_add_health_standard.js + portal = aq_parent(aq_inner(tool)) + setup = portal.portal_setup + typestool = getToolByName(portal, 'portal_types') + + # Since this new version changes the hard-coded ethnicity to a new content type ethnicity, we need to save all + # patients' ethnicities to create and load them later. Otherwise, all patient with an ethnicity defined will lost + # its ethnicity value. + + # Getting all patients brains + bpc = getToolByName(tool, 'bika_patient_catalog') + all_patients = bpc(portal_type='Patient') + # Initializing the list that will contain tuples with the actual (patientUID,ethnicity) + patient_list = [] + for patient in all_patients: + # Obtaining patient object + patient_obj = patient.getObject() + # Obtaining patient's ethnicity name and uid + pa_ethnicity = patient_obj.Schema()['Ethnicity'].get(patient_obj) + pa_uid = patient_obj.UID() + if pa_ethnicity != '': + # If the patient contains an ethnicity, it should be saved in the list + patient_list.append((pa_uid, pa_ethnicity)) + # reread jsregistry with the new data + setup.runImportStepFromProfile('profile-bika.health:default', 'jsregistry') + # Reread cssregistry to update the changes + setup.runImportStepFromProfile('profile-bika.health:default', 'cssregistry') + # Reread typeinfo to update/add the modified/added types + setup.runImportStepFromProfile('profile-bika.health:default', 'typeinfo') + # Reread factorytool to add the the new ethnicity type + setup.runImportStepFromProfile('profile-bika.health:default', 'factorytool') + # Reread workflow to add the the new ethnicity type + setup.runImportStepFromProfile('profile-bika.health:default', 'workflow') + # Reread controlpanel to add the the new ethnicity type + setup.runImportStepFromProfile('profile-bika.health:default', 'controlpanel') + + # Adding Ethnicity roles + workflow = getToolByName(portal, 'portal_workflow') + workflow.updateRoleMappings() + + # Adding Ethnicity content type + at = getToolByName(portal, 'archetype_tool') + at.setCatalogsByType('Ethnicity', ['bika_setup_catalog', ]) + # If the type is not created yet, we should create it + if not portal['bika_setup'].get('bika_ethnicities'): + typestool.constructContent(type_name="Ethnicities", + container=portal['bika_setup'], + id='bika_ethnicities', + title='Ethnicity') + obj = portal['bika_setup']['bika_ethnicities'] + obj.unmarkCreationFlag() + obj.reindexObject() + if not portal['bika_setup'].get('bika_ethnicities'): + logger.info("Ethnicities not created") + + # Define permissions for ethnicity + mp = portal.manage_permission + mp(AddEthnicity, ['Manager', 'Owner', 'LabManager', 'LabClerk'], 1) + mp(ViewEthnicities, ['Manager', 'LabManager', 'Owner', 'LabClerk', 'Doctor', 'RegulatoryInspector'], 1) + + # Creating all standard ethnicities + createEthnicities(tool) + # Relating new ethnicity contents types with patients + addPatientEthnicity(tool, patient_list) + + return True + + +def createEthnicities(context): + """ + This function creates al the standard ethnicities + :return: a list of tuples with the created ethnicities contents as: [(ethnicity_name, ethnicity_uid), (), ...] + """ + ethnicities = ['Native American', 'Asian', 'Black', 'Native Hawaiian or Other Pacific Islander', 'White', + 'Hispanic or Latino'] + for ethnicityname in ethnicities: + folder = context.bika_setup.bika_ethnicities + # Generating a temporal object + _id = folder.invokeFactory('Ethnicity', id=tmpID()) + obj = folder[_id] + # Setting its values + obj.edit(title=ethnicityname, + description='') + obj.unmarkCreationFlag() + renameAfterCreation(obj) + + +def addPatientEthnicity(context, patient_list): + """ + This function adds to the patient, its ethnicity. + :Patient_list: is a list of tuples. Each tuple contains a patient UID and a string. This string is the name of the + ethnicity that used te be related in the patient. [(patientUID, ethnicityName),(),...] + :return: Ethnicity object + """ + for patientUID,ethnicityname in patient_list: + # Getting the ethnicity uid + bsc = getToolByName(context, 'bika_setup_catalog') + bpc = getToolByName(context, 'bika_patient_catalog') + ethnicityUID= bsc(Portal_type='Ethnicity', Title=ethnicityname)[0].getObject().UID() + # Getting the patient object + patient = bpc(Portal_type='Patient', UID=patientUID)[0].getObject() + patient.setEthnicity(ethnicityUID) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c74a51a..c46ee92 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -1,4 +1,12 @@ -3.1.6 (unreleased) +3.1.7 (2015-06-09) +------------------ +HEALTH-245: Set-up data load. Patient ID conversion, alternatives +HEALTH-227: Converting Patient IDs before import +HEALTH-228: Load Setup data bugs +HEALTH-140: AR Create per path lab standard form +HEALTH-251: Add guarantor details in insurance companies + +3.1.6 (2015-02-27) ------------------ HEALTH-223: When you are adding a doctor through an overlay (add doctor button in cases), the address widgets don't work properly. HEALTH-215: Correct Navigation tree order diff --git a/setup.cfg b/setup.cfg index ed2be0a..0cf29e1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [egg_info] -tag_build = +tag_build = tag_date = false -tag_svn_revision = true +tag_svn_revision = false diff --git a/setup.py b/setup.py index 5dd8bf4..10cf7d7 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import os -version = '3.1.6' +version = '3.1.7' setup(name='bika.health', version=version, @@ -33,7 +33,7 @@ zip_safe=False, install_requires=[ 'setuptools', - 'bika.lims==3.1.7', + 'bika.lims==3.1.8', 'archetypes.schemaextender', 'collective.wtf', ],