From 62bd339c994eaa07cfaa8d98d9bd49e3ea67a520 Mon Sep 17 00:00:00 2001 From: John Kleinschmidt Date: Thu, 23 Jul 2015 17:01:57 -0400 Subject: [PATCH] Added the ability to request multiple lab and imaging requests at once Resolves #49 Resolves #50 --- app/controllers/abstract-edit-controller.js | 23 +++-- app/imaging/edit/controller.js | 102 +++++++++---------- app/imaging/edit/template.hbs | 34 +++---- app/labs/edit/controller.js | 98 +++++++++--------- app/labs/edit/template.hbs | 36 +++---- app/mixins/charge-actions.js | 104 ++++++++++++-------- app/mixins/patient-submodule.js | 31 +++--- app/models/imaging.js | 3 +- app/models/lab.js | 3 +- 9 files changed, 234 insertions(+), 200 deletions(-) diff --git a/app/controllers/abstract-edit-controller.js b/app/controllers/abstract-edit-controller.js index 8e547bc0a2..003d9cc6d5 100644 --- a/app/controllers/abstract-edit-controller.js +++ b/app/controllers/abstract-edit-controller.js @@ -107,12 +107,7 @@ export default Ember.ObjectController.extend(IsUpdateDisabled, ModalHelper, User */ update: function(skipAfterUpdate) { this.beforeUpdate().then(function() { - this.get('model').save().then(function(record){ - this.updateLookupLists(); - if (!skipAfterUpdate) { - this.afterUpdate(record); - } - }.bind(this)); + this.saveModel(skipAfterUpdate); }.bind(this)); } }, @@ -130,7 +125,21 @@ export default Ember.ObjectController.extend(IsUpdateDisabled, ModalHelper, User */ beforeUpdate: function() { return Ember.RSVP.Promise.resolve(); - }, + }, + + /** + * Save the model and then (optionally) run the after update. + * @param skipAfterUpdate boolean (optional) indicating whether or not + * to skip the afterUpdate call. + */ + saveModel: function(skipAfterUpdate) { + this.get('model').save().then(function(record){ + this.updateLookupLists(); + if (!skipAfterUpdate) { + this.afterUpdate(record); + } + }.bind(this)); + }, /** * Update any new values added to a lookup list diff --git a/app/imaging/edit/controller.js b/app/imaging/edit/controller.js index 7282b589d5..a5c8f6750e 100644 --- a/app/imaging/edit/controller.js +++ b/app/imaging/edit/controller.js @@ -9,8 +9,13 @@ export default AbstractEditController.extend(ChargeActions, PatientSubmodule, { chargeRoute: 'imaging.charge', canComplete: function() { - return this.currentUserCan('complete_imaging'); - }.property(), + var imagingTypeName = this.get('selectedImagingType'); + if (Ember.isArray(imagingTypeName) && imagingTypeName.length >1) { + return false; + } else { + return this.currentUserCan('complete_imaging'); + } + }.property('selectedImagingType.[]'), actions: { completeImaging: function() { @@ -20,7 +25,42 @@ export default AbstractEditController.extend(ChargeActions, PatientSubmodule, { this.set('imagingDate', new Date()); this.send('update'); } - } + }, + + /** + * Save the imaging request(s), creating multiples when user selects multiple imaging tests. + */ + update: function() { + if (this.get('isNew')) { + var newImaging = this.get('model'), + selectedImagingType = this.get('selectedImagingType'); + if (Ember.isEmpty(this.get('status'))) { + this.set('status', 'Requested'); + } + this.set('requestedBy', newImaging.getUserName()); + this.set('requestedDate', new Date()); + if (Ember.isEmpty(selectedImagingType)) { + this.saveNewPricing(this.get('imagingTypeName'), 'Imaging','imagingType').then(function() { + this.addChildToVisit(newImaging, 'imaging', 'Imaging').then(function() { + this.saveModel(); + }.bind(this)); + }.bind(this)); + } else { + this.getSelectedPricing('selectedImagingType').then(function(pricingRecords) { + if (Ember.isArray(pricingRecords)) { + this.createMultipleRequests(pricingRecords, 'imagingType','imaging', 'Imaging'); + } else { + this.set('imagingType', pricingRecords); + this.addChildToVisit(newImaging, 'imaging', 'Imaging').then(function() { + this.saveModel(); + }.bind(this)); + } + }.bind(this)); + } + } else { + this.saveModel(); + } + } }, additionalButtons: function() { @@ -49,30 +89,12 @@ export default AbstractEditController.extend(ChargeActions, PatientSubmodule, { radiologistList: Ember.computed.alias('controllers.imaging.radiologistList'), - imagingTypeChanged: function() { - this.objectTypeChanged('imagingTypeName', 'imagingType'); - }.observes('imagingType'), - - imagingTypeNameChanged: function() { - this.objectTypeNameChanged('imagingTypeName', 'selectedImagingType'); - }.observes('imagingTypeName'), - - showCharges: function() { - var imagingType = this.get('imagingType'), - patient = this.get('patient'), - visit = this.get('visit'); - return (!Ember.isEmpty(imagingType) && !Ember.isEmpty(patient) && - !Ember.isEmpty(visit)); - }.property('imagingType','patient', 'visit'), - updateCapability: 'add_imaging', - selectedImagingTypeChanged: function() { - this.selectedObjectTypeChanged('selectedImagingType', 'imagingType'); - }.observes('selectedImagingType'), - - afterUpdate: function() { - var alertTitle, + afterUpdate: function(saveResponse, multipleRecords) { + this.updateLookupLists(); + var afterDialogAction, + alertTitle, alertMessage; if (this.get('status') === 'Completed') { alertTitle = 'Imaging Request Completed'; @@ -81,33 +103,11 @@ export default AbstractEditController.extend(ChargeActions, PatientSubmodule, { alertTitle = 'Imaging Request Saved'; alertMessage = 'The imaging request has been saved.'; } - this.saveVisitIfNeeded(alertTitle, alertMessage); - this.set('selectPatient', false); - }, - - beforeUpdate: function() { - if (!this.get('isValid')) { - return Ember.RSVP.reject(); + if (multipleRecords) { + afterDialogAction = 'allItems'; } - return new Ember.RSVP.Promise(function(resolve, reject) { - this.updateCharges().then(function() { - if (this.get('isNew')) { - var newImaging = this.get('model'); - this.set('status', 'Requested'); - this.set('requestedBy', newImaging.getUserName()); - this.set('requestedDate', new Date()); - if (this.get('newObjectType')) { - this.saveNewPricing(this.get('imagingTypeName'), 'Imaging','imagingType').then(function() { - this.addChildToVisit(newImaging, 'imaging', 'Imaging').then(resolve, reject); - }.bind(this), reject); - } else { - return this.addChildToVisit(newImaging, 'imaging', 'Imaging').then(resolve, reject); - } - } else { - resolve(); - } - }.bind(this), reject); - }.bind(this), 'beforeUpdate on imaging edit'); + this.saveVisitIfNeeded(alertTitle, alertMessage, afterDialogAction); + this.set('selectPatient', false); } }); \ No newline at end of file diff --git a/app/imaging/edit/template.hbs b/app/imaging/edit/template.hbs index 6745408d30..d8a8a6ec8d 100644 --- a/app/imaging/edit/template.hbs +++ b/app/imaging/edit/template.hbs @@ -3,37 +3,37 @@ {{patient-typeahead property="patientTypeAhead" label="Patient" content=patientList selection=selectedPatient class="required"}} {{else}} {{patient-summary patient=patient returnTo='imaging.edit' returnToContext=id disablePatientLink=isNew }} - {{/if}} -
- {{#if isNew}} + {{/if}} + {{#if isNew}} +
{{em-select class="col-xs-3 required" label="Visit" property="visit" content=patientVisits optionValuePath="content" optionLabelPath="content.visitDescription" prompt="--Add New Visit--" selected=visit }} - {{else}} +
+ {{checkbox-or-typeahead property="imagingTypeName" + label="Imaging Type" list=objectTypeList + selection=selectedImagingType + optionLabelPath='content.name' + typeAheadType='pricing' + class="required" + prompt=" " + model=model + }} + {{else}} +

{{visit.visitDate}}

- {{/if}} - {{#if isNew}} - {{select-or-typeahead property="imagingTypeName" - label="Imaging Type" list=objectTypeList - selection=selectedImagingType - optionLabelPath='content.name' - typeAheadType='pricing' - class="required col-xs-9" - prompt=" " - }} - {{else}}

{{imagingType.name}}

- {{/if}} -
+
+ {{/if}} {{#if canComplete}} {{select-or-typeahead property="radiologist" label="Radiologist" list=radiologistList diff --git a/app/labs/edit/controller.js b/app/labs/edit/controller.js index 0076626ad1..5d52b8b457 100644 --- a/app/labs/edit/controller.js +++ b/app/labs/edit/controller.js @@ -10,8 +10,13 @@ export default AbstractEditController.extend(ChargeActions, PatientSubmodule, { canComplete: function() { - return this.currentUserCan('complete_lab'); - }.property(), + var labTypeName = this.get('selectedLabType'); + if (Ember.isArray(labTypeName) && labTypeName.length >1) { + return false; + } else { + return this.currentUserCan('complete_lab'); + } + }.property('selectedLabType.[]'), actions: { completeLab: function() { @@ -21,6 +26,41 @@ export default AbstractEditController.extend(ChargeActions, PatientSubmodule, { this.set('labDate', new Date()); this.send('update'); } + }, + + /** + * Update the model and perform the before update and after update + */ + update: function() { + if (this.get('isNew')) { + var newLab = this.get('model'), + selectedLabType = this.get('selectedLabType'); + if (Ember.isEmpty(this.get('status'))) { + this.set('status', 'Requested'); + } + this.set('requestedBy', newLab.getUserName()); + this.set('requestedDate', new Date()); + if (Ember.isEmpty(selectedLabType)) { + this.saveNewPricing(this.get('labTypeName'), 'Lab', 'labType').then(function() { + this.addChildToVisit(newLab, 'labs', 'Lab').then(function() { + this.saveModel(); + }.bind(this)); + }.bind(this)); + } else { + this.getSelectedPricing('selectedLabType').then(function(pricingRecords) { + if (Ember.isArray(pricingRecords)) { + this.createMultipleRequests(pricingRecords, 'labType','labs', 'Lab'); + } else { + this.set('labType', pricingRecords); + this.addChildToVisit(newLab, 'labs', 'Lab').then(function() { + this.saveModel(); + }.bind(this)); + } + }.bind(this)); + } + } else { + this.saveModel(); + } } }, @@ -42,30 +82,11 @@ export default AbstractEditController.extend(ChargeActions, PatientSubmodule, { pricingList: null, //This gets filled in by the route - labTypeChanged: function() { - this.objectTypeChanged('labTypeName', 'labType'); - }.observes('labType'), - - labTypeNameChanged: function() { - this.objectTypeNameChanged('labTypeName', 'selectedLabType'); - }.observes('labTypeName'), - - showCharges: function() { - var labType = this.get('labType'), - patient = this.get('patient'), - visit = this.get('visit'); - return (!Ember.isEmpty(labType) && !Ember.isEmpty(patient) && - !Ember.isEmpty(visit)); - }.property('labType','patient', 'visit'), - updateCapability: 'add_lab', - selectedLabTypeChanged: function() { - this.selectedObjectTypeChanged('selectedLabType', 'labType'); - }.observes('selectedLabType'), - - afterUpdate: function() { - var alertMessage, + afterUpdate: function(saveResponse, multipleRecords) { + var afterDialogAction, + alertMessage, alertTitle; if (this.get('status') === 'Completed') { alertTitle = 'Lab Request Completed'; @@ -74,32 +95,11 @@ export default AbstractEditController.extend(ChargeActions, PatientSubmodule, { alertTitle = 'Lab Request Saved'; alertMessage = 'The lab request has been saved.'; } - this.saveVisitIfNeeded(alertTitle, alertMessage); - }, - - beforeUpdate: function() { - if (!this.get('isValid')) { - return Ember.RSVP.reject(); + if (multipleRecords) { + afterDialogAction = 'allItems'; } - return new Ember.RSVP.Promise(function(resolve, reject) { - this.updateCharges().then(function() { - if (this.get('isNew')) { - var newLab = this.get('model'); - this.set('status', 'Requested'); - this.set('requestedBy', newLab.getUserName()); - this.set('requestedDate', new Date()); - if (this.get('newObjectType')) { - this.saveNewPricing(this.get('labTypeName'), 'Lab', 'labType').then(function() { - this.addChildToVisit(newLab, 'labs', 'Lab').then(resolve, reject); - }.bind(this), reject); - } else { - return this.addChildToVisit(newLab, 'labs', 'Lab').then(resolve, reject); - } - } else { - resolve(); - } - }.bind(this), reject); - }.bind(this), 'beforeUpdate on lab edit'); + this.saveVisitIfNeeded(alertTitle, alertMessage, afterDialogAction); + this.set('selectPatient', false); } }); \ No newline at end of file diff --git a/app/labs/edit/template.hbs b/app/labs/edit/template.hbs index a9d4caafea..0b40cf1f7e 100644 --- a/app/labs/edit/template.hbs +++ b/app/labs/edit/template.hbs @@ -1,41 +1,41 @@ {{#em-form model=this submit_button=false }} - {{#if selectPatient}} {{patient-typeahead property="patientTypeAhead" label="Patient" content=patientList selection=selectedPatient class="required"}} {{else}} {{patient-summary patient=patient returnTo='labs.edit' returnToContext=id disablePatientLink=isNew }} {{/if}} -
- {{#if isNew}} + + {{#if isNew}} +
{{em-select class="col-xs-3 required" label="Visit" property="visit" content=patientVisits optionValuePath="content" optionLabelPath="content.visitDescription" prompt="--Add New Visit--" selected=visit }} - {{else}} +
+ {{checkbox-or-typeahead property="labTypeName" + label="Lab Type" list=objectTypeList + selection=selectedLabType + optionLabelPath='content.name' + typeAheadType='pricing' + class="required" + prompt=" " + model=model + }} + {{else}} +

{{visit.visitDate}}

- {{/if}} - - {{#if isNew}} - {{select-or-typeahead property="labTypeName" - label="Lab Type" list=objectTypeList - selection=selectedLabType - optionLabelPath='content.name' - typeAheadType='pricing' - class="required col-xs-6" - prompt=" " - }} - {{else}}

{{labType.name}}

- {{/if}} -
+
+ {{/if}} + {{#if canComplete}} {{em-input property="result" label="Result"}} {{/if}} diff --git a/app/mixins/charge-actions.js b/app/mixins/charge-actions.js index 14004ec4f9..f931f6c07c 100644 --- a/app/mixins/charge-actions.js +++ b/app/mixins/charge-actions.js @@ -88,29 +88,6 @@ export default Ember.Mixin.create({ }); return chargeForItem; }, - - newObjectType: false, - - objectTypeChanged: function(objectTypeNameField, typeField) { - var isNew = this.get('isNew'), - objectTypeName = this.get(objectTypeNameField), - objectType = this.get(typeField); - if (isNew) { - if(objectTypeName instanceof Object) { - this.set('newObjectType', false); - } else { - if (!Ember.isEmpty(objectType)) { - this.set('newObjectType', false); - if (objectType.get('name') !== objectTypeName) { - this.set(objectTypeNameField, objectType.get('name')); - } - } else { - this.set('newObjectType', true); - } - } - } - }, - /** * Returns object types out of the pricing list. * Used for labs and imaging where the labs and imaging types are @@ -129,15 +106,7 @@ export default Ember.Mixin.create({ } return returnList; }.property('pricingList','pricingTypeForObjectType','pricingTypeValues'), - - objectTypeNameChanged: function(objectTypeNameField, selectedField) { - var isNew = this.get('isNew'), - objectTypeName = this.get(objectTypeNameField); - if (isNew && objectTypeName instanceof Object) { - this.set(selectedField, objectTypeName); - } - }, - + organizeByType: Ember.computed.alias('pricingTypes.organizeByType'), pricingTypeList: function() { @@ -152,6 +121,51 @@ export default Ember.Mixin.create({ pricingTypeValues: Ember.computed.alias('pricingTypes.value'), + /** + * Create multiple new request records from the pricing records passed in. This function + * will also add those new records to the specified visit. + * @param {array} pricingRecords the list of pricing records to use to create request records from. + * @param {string} pricingField the name of the field on the request record to set the pricing record on. + * @param {string} visitChildName the name of the child object on the visit to add to. + * @param {string} newVisitType if a new visit needs to be created, what type of visit + * should be created. + */ + createMultipleRequests: function(pricingRecords, pricingField, visitChildName, newVisitType) { + var addPromises = [], + attributesToSave = {}, + baseModel = this.get('model'), + modelToSave, + patient = this.get('patient'), + visit = this.get('visit'); + + if (Ember.isEmpty(visit)) { + visit = this.createNewVisit(newVisitType); + } + baseModel.eachAttribute(function(name) { + attributesToSave[name] = baseModel.get(name); + }); + + pricingRecords.forEach(function(pricingRecord) { + modelToSave = this.store.createRecord(newVisitType.toLowerCase(), attributesToSave); + modelToSave.set(pricingField, pricingRecord); + modelToSave.set('patient', patient); + modelToSave.set('visit', visit); + addPromises.push(this.addChildToVisit(modelToSave, visitChildName, newVisitType)); + }.bind(this)); + + Ember.RSVP.all(addPromises).then(function(results) { + var savePromises = []; + results.forEach(function(newObject) { + console.log("Results are:", newObject); + savePromises.push(newObject.save()); + }); + Ember.RSVP.all(savePromises).then(function(saveResponse) { + this.set('visit', visit); //Make sure the visit is properly set for saving + this.afterUpdate(saveResponse, true); + }.bind(this)); + }.bind(this)); + }, + saveNewPricing: function(pricingName, pricingCategory, priceObjectToSet) { return new Ember.RSVP.Promise(function(resolve, reject) { var newPricing, @@ -172,17 +186,21 @@ export default Ember.Mixin.create({ }.bind(this), 'saveNewPricing for: '+pricingName); }, - selectedObjectTypeChanged: function(selectedField, typeField) { - var isNew = this.get('isNew'), - selectedItem = this.get(selectedField); - if (isNew) { - if (!Ember.isEmpty(selectedItem)) { - this.store.find('pricing', selectedItem._id.substr(8)).then(function(item) { - this.set(typeField, item); - }.bind(this)); - } else { - this.set(typeField); - } + getSelectedPricing: function(selectedField) { + var selectedItem = this.get(selectedField); + if (!Ember.isEmpty(selectedItem)) { + return new Ember.RSVP.Promise(function(resolve, reject) { + if (Ember.isArray(selectedItem)) { + var pricingIds = selectedItem.map(function(pricingItem) { + return pricingItem._id.substr(8); + }); + this.store.findByIds('pricing', pricingIds).then(resolve, reject); + } else { + this.store.find('pricing', selectedItem._id.substr(8)).then(resolve, reject); + } + }.bind(this)); + } else { + return Ember.RSVP.resolve(); } }, diff --git a/app/mixins/patient-submodule.js b/app/mixins/patient-submodule.js index e9f5592407..8c985fb7d6 100644 --- a/app/mixins/patient-submodule.js +++ b/app/mixins/patient-submodule.js @@ -28,24 +28,16 @@ export default Ember.Mixin.create(PatientVisits, { addChildToVisit: function(objectToAdd, childName, newVisitType) { return new Ember.RSVP.Promise(function(resolve, reject){ var childPromises = [], - patient = this.get('patient'), visit = this.get('visit'); if (Ember.isEmpty(visit)) { - visit = this.get('store').createRecord('visit', { - startDate: new Date(), - endDate: new Date(), - outPatient: true, - patient: patient, - visitType: newVisitType - }); - this.set('visit', visit); + visit = this.createNewVisit(newVisitType); } childPromises.addObjects(this.resolveVisitChildren()); Ember.RSVP.all(childPromises, 'Resolved visit children before adding new '+childName).then(function() { visit.get(childName).then(function(visitChildren) { visitChildren.addObject(objectToAdd); this.set('needToUpdateVisit', true); - resolve(); + resolve(objectToAdd); }.bind(this), reject); }.bind(this), reject); }.bind(this)); @@ -63,6 +55,19 @@ export default Ember.Mixin.create(PatientVisits, { } }.property('returnToPatient', 'returnToVisit'), + createNewVisit: function(newVisitType) { + var patient = this.get('patient'), + visit = this.get('store').createRecord('visit', { + startDate: new Date(), + endDate: new Date(), + outPatient: true, + patient: patient, + visitType: newVisitType + }); + this.set('visit', visit); + return visit; + }, + patientId: Ember.computed.alias('patient.id'), patientChanged: function() { @@ -143,14 +148,14 @@ export default Ember.Mixin.create(PatientVisits, { * @param alertTitle String the title to use on the alert. * @param alertMessage String the message to display in the alert. */ - saveVisitIfNeeded: function(alertTitle, alertMessage) { + saveVisitIfNeeded: function(alertTitle, alertMessage, alertAction) { if (this.get('needToUpdateVisit')) { this.get('visit').save().then(function() { this.set('needToUpdateVisit', false); - this.displayAlert(alertTitle, alertMessage); + this.displayAlert(alertTitle, alertMessage, alertAction); }.bind(this)); } else { - this.displayAlert(alertTitle, alertMessage); + this.displayAlert(alertTitle, alertMessage, alertAction); } }, diff --git a/app/models/imaging.js b/app/models/imaging.js index 50dc84438e..0c513c7c5d 100644 --- a/app/models/imaging.js +++ b/app/models/imaging.js @@ -31,7 +31,8 @@ export default AbstractModel.extend(DateFormat, ResultValidation, { if (object.get('isNew')) { return true; } - } + }, + message: 'Please select an imaging type' } }, patientTypeAhead: PatientValidation.patientTypeAhead, diff --git a/app/models/lab.js b/app/models/lab.js index 11bffe52ee..9c15dcf8ae 100644 --- a/app/models/lab.js +++ b/app/models/lab.js @@ -30,7 +30,8 @@ export default AbstractModel.extend(DateFormat, ResultValidation, { if (object.get('isNew')) { return true; } - } + }, + message: 'Please select a lab type' } }, patientTypeAhead: PatientValidation.patientTypeAhead,