nuVistA/htdocs/ViewOrderDialogMedOutpt.vue

296 lines
13 KiB
Vue
Raw Normal View History

2023-04-24 23:10:19 -04:00
<template>
<div v-if="options_med && inputs" class="row">
<div class="col-12">
<div class="mb-3">
<ViewOrderableRawLookup :client="client" label="Orderable" dgnm="O RX" :query="options_med ? options_med.Medication.default.text : null" v-model="common.ORDERABLE" />
</div>
</div>
<template v-for="(instance, idx) in inputs" :key="instance">
<ViewOrderDialogMedOutptInstance :multi="inputs.length > 1" :more="idx + 1 < inputs.length" :dlgdef="dlgdef" :options_med="options_med" :options_schedule="options_schedule" :loadrsp_group="loadrsp_groups[idx]" v-model="inputs[idx]" @add="e => inputs.splice(idx + 1, 0, Object.assign({}, instance))" @remove="e => inputs.splice(idx, 1)" />
</template>
<div class="col-4">
<div class="mb-3">
<Autocomplete label="Indication" :items="options_med['Indication'].items" v-model="common.INDICATION" />
</div>
</div>
<div class="col-8">
<div class="form-floating mb-3">
<input type="text" class="form-control" placeholder=" " v-model="common.COMMENT" />
<label>Comment</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input type="number" class="form-control" placeholder=" " v-model="common.SUPPLY" />
<label>Days Supply</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input type="number" class="form-control" placeholder=" " v-model="common.QTY" />
<label>{{base_shortform ? 'Qty (in ' + base_shortform + ')' : 'Quantity'}}</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input type="number" class="form-control" placeholder=" " v-model="common.REFILLS" />
<label>Refills</label>
</div>
</div>
<div class="col-3">
<div class="form-floating mb-3">
<select class="form-select" v-model="common.PICKUP">
<option value="M">Mail</option>
<option value="W">Window</option>
</select>
<label>Pickup</label>
</div>
</div>
<div class="col-3">
<div v-if="options_fill && options_fill.Priority && options_fill.Priority.items" class="form-floating mb-3">
<select class="form-select" v-model="common.URGENCY"><option v-for="item in options_fill.Priority.items" :value="item.value">{{item.text}}</option></select>
<label>Priority</label>
</div>
</div>
<div class="col-10">
<div class="card mb-3" style="background-color: #fdfbd7;"><div class="card-body" style="font-family: monospace; white-space: pre-wrap;">{{preview}}</div></div>
</div>
<div class="col-2">
<div class="btn-group-vertical mb-3" style="width: 100%;">
<button type="button" class="btn btn-primary" @click="e => $emit('submit', output)">Submit</button>
<button type="button" class="btn btn-danger" @click="e => $emit('cancel')">Cancel</button>
</div>
</div>
</div>
</template>
<script>
import { debounce, groupByArray } from './util.mjs';
import { translate } from './numberwords.mjs';
import ViewOrderableRawLookup from './ViewOrderableRawLookup.vue';
import Autocomplete from './Autocomplete.vue';
import DateTimePicker from './DateTimePicker.vue';
import ViewOrderDialogMedOutptInstance from './ViewOrderDialogMedOutptInstance.vue';
const C_COMMON = ['ORDERABLE', 'INDICATION', 'COMMENT', 'SUPPLY', 'QTY', 'REFILLS', 'PICKUP', 'URGENCY'];
const C_DAYS = {
'': 1,
'MONTH': 30,
'WEEK': 7,
'DAY': 1,
'HOUR': 1/24,
'MINUTE': 1/24/60
};
const C_WP = { '15': true, '758': true, '763': true };
function input_dose_parse(value) {
value = (value) && (value.indexOf('&') >= 0) ? value.split('&') : [parseFloat(value) || null, value ? value.replace(/^\s*([+-]?(?:\d+|\d+\.\d+))\s*|\s+$/g, '').toUpperCase() || null : null, null, null, value, null, null, null];
value.dose = value[0];
value.unit = value[1];
value.count = value[2];
value.form = value[3];
value.text = value[4];
value.id = value[5];
value.base_dose = value[6];
value.base_unit = value[7];
return value;
}
function greatest_common_base(values, products) {
for(var i = 0; i < values.length; ++i) {
var value = values[i];
if((isNaN(value.dose)) || (isNaN(parseFloat(value.dose)))) return;
products = products.filter(x => (x.dose) && (x.unit == value.unit) && ((Math.abs((value.dose/x.dose)%1) < 0.001) || ((x.split == '1') && (Math.abs((value.dose/x.dose)%1 - 0.5) < 0.001))));
if(products.length < 1) return;
}
return products.sort((a, b) => b.dose - a.dose)[0];
}
export default {
components: {
ViewOrderableRawLookup, Autocomplete, DateTimePicker, ViewOrderDialogMedOutptInstance
},
props: {
client: Object,
ien: String,
dlgdef: Object,
bldqrsp: Object,
modelValue: Object
},
emits: [
'submit',
'cancel',
'update:modelValue'
],
data() {
return {
loadrsp: null,
loadrsp_groups: [],
inputs: null,
options_schedule: null,
options_med: null,
lastorderable: null,
common: {}
};
},
computed: {
options_med_route() {
if((this.options_med) && (this.options_med['Route'])) {
var opt, rsp, res = this.options_med['Route'].items.slice();
res.mapping = res.reduce((acc, val) => (acc[val.value] = val, acc), {});
if((this.loadrsp) && (rsp = this.loadrsp['ROUTE']) && (!res.find(x => x.value == rsp.iValue))) res.push({ value: rsp.iValue, text: rsp.eValue, abbr: rsp.eValue, sig: rsp.eValue });
return res;
} else return [];
},
input_doses() {
return this.inputs ? this.inputs.map(x => input_dose_parse(x.DOSE)) : [];
},
base_product() {
return this.options_med ? greatest_common_base(this.input_doses, this.options_med['Dispense'].items) : undefined;
},
base_form() {
try {
return this.options_med['AllDoses'].items.find(x => x.id == this.base_product.id).dosefields.split('&')[3];
} catch(ex) {}
},
base_shortform() {
try {
var dose_unit = this.base_product.dose + this.base_product.unit;
return this.options_med['Dosage'].items.find(x => x.text == dose_unit).form;
} catch(ex) {
return this.base_form;
}
},
sig() {
if((this.options_med) && (this.inputs)) {
var res = [];
var verb = this.options_med['Verb'].default;
var input_doses = this.input_doses;
var base_product = this.base_product;
var base_form = this.base_form;
for(var i = 0; i < this.inputs.length; ++i) {
var input = this.inputs[i], input_route = input.ROUTE, input_dose = input_doses[i];
var route = (this.options_med_route.find(x => x.value == input_route) || { sig: input_route }).sig;
var prep = this.options_med_route.mapping[input_route] ? this.options_med['Preposition'].default : null;
var schedule = (this.options_schedule) && (this.options_schedule[input.SCHEDULE]) ? this.options_schedule[input.SCHEDULE].text : input.SCHEDULE;
res.push((input_dose ? verb + ' ' + (base_product ? translate(input_dose.dose/base_product.dose).toUpperCase() + ' ' + (base_form || 'UNIT') + (input_dose.dose/base_product.dose >= 2 ? 'S' : '') : input_dose.text) : '') + (route ? ' ' + (prep ? prep + ' ' : '') + route : '') + (schedule ? ' ' + schedule : '') + (input.SCHTYPE == 'P' ? ' AS NEEDED' : '') + (input.DAYS ? ' FOR ' + input.DAYS : '') + (i + 1 >= this.inputs.length ? '' : (' ' + (input.CONJ == 'T' ? 'THEN' : input.CONJ == 'A' ? 'AND' : 'OR'))));
}
return res.join(' ');
}
},
preview() {
var base_product = this.base_product;
var product = base_product ? base_product.text + '\n' : this.options_med ? this.options_med['Medication'].default.text + '\n' : '';
return product + (this.sig ? this.sig + (this.common.COMMENT ? ' ' + this.common.COMMENT : '') + '\n' : '') + 'Quantity: ' + (+this.common.QTY) + ' Refills: ' + (+this.common.REFILLS) + (this.common.INDICATION ? '\nIndication: ' + this.common.INDICATION : '');
},
output() {
if((this.dlgdef) && (this.inputs)) {
var res = {};
var inputs = this.inputs.length > 0 ? this.inputs.slice() : [{}];
inputs[0] = this.common ? Object.assign({}, inputs[0], this.common) : Object.assign({}, inputs[0]);
inputs[0].SIG = this.sig;
if(this.base_product) {
inputs[0].STRENGTH = this.base_product.dose + this.base_product.unit;
inputs[0].DRUG = this.base_product.id;
}
for(var i = 0; i < inputs.length; ++i) {
var seq = i + 1, instance = inputs[i];
for(var j = 0, desc, val; j < this.dlgdef.length; ++j) {
desc = this.dlgdef[j];
if(val = instance[desc.promptID]) {
if(C_WP[desc.promptIEN]) {
res['"' + desc.promptIEN + '","' + seq + '"'] = 'ORDIALOG("WP",' + desc.promptIEN + ',' + seq + ')';
res['"WP","' + desc.promptIEN + '","' + seq + '","1","0"'] = instance[desc.promptID];
} else res['"' + desc.promptIEN + '","' + seq + '"'] = instance[desc.promptID];
}
}
}
return res;
}
}
},
created() {
this.$watch(
() => this.client,
async () => {
if(this.client) {
this.options_schedule = await this.client.ORWDPS1_SCHALL('', 0);
this.options_fill = await this.client.ORWDPS1_ODSLCT('O', '', '');
} else this.options_schedule = this.options_fill = null;
},
{ immediate: true }
);
this.$watch(
() => (this.client, this.dlgdef, this.bldqrsp, this.options_fill, {}),
async () => {
if((this.client) && (this.dlgdef) && (this.bldqrsp)) {
this.inputs = [this.dlgdef.reduce((acc, val) => (acc[val.promptID] = acc[val.promptID], acc), { ORDERABLE: this.common ? this.common.ORDERABLE : null })];
if(this.options_fill) {
if((this.options_fill.Pickup) && (this.options_fill.Pickup.default)) this.inputs.PICKUP = this.options_fill.Pickup.default.value;
if((this.options_fill.Refills) && (this.options_fill.Refills.default)) this.inputs.REFILLS = this.options_fill.Refills.default.value;
if((this.options_fill.Priority) && (this.options_fill.Priority.default)) this.inputs.URGENCY = this.options_fill.Priority.default.value;
}
this.loadrsp_groups = [];
if((this.bldqrsp.ResponseID) && (this.bldqrsp.ResponseID != '0')) {
this.loadrsp = await this.client.ORWDX_LOADRSP(this.bldqrsp.ResponseID, 0, 0);
if((this.loadrsp) && (this.loadrsp.length > 0)) {
this.loadrsp_groups = groupByArray(this.loadrsp, 'instance').sort((a, b) => a.key - b.key).map(x => x.values.reduce((acc, val) => (acc[val.promptID] = val, acc), x.values));
Object.assign(this.inputs[0], this.loadrsp_groups[0].reduce((acc, val) => (acc[val.promptID] = val.iValue != '^WP^' ? val.iValue : val.eValue, acc), {}));
for(var i = 1; i < this.loadrsp_groups.length; ++i) this.inputs.push(this.loadrsp_groups[i].reduce((acc, val) => (acc[val.promptID] = val.iValue != '^WP^' ? val.iValue : val.eValue, acc), {}));
}
}
this.common = C_COMMON.reduce((acc, val) => (acc[val] = this.inputs[0][val], acc), {});
} else {
this.inputs = this.loadrsp = null;
this.loadrsp_groups = [];
}
},
{ immediate: true }
);
this.$watch(
() => (this.client, (this.common) && (this.common.ORDERABLE), {}),
async () => {
this.options_med = (this.client) && (this.common) && (this.common.ORDERABLE) ? await this.client.ORWDPS2_OISLCT(this.common.ORDERABLE, '', '', 'Y', '') : null;
}
);
this.$watch(
() => ((this.common) && (this.common.ORDERABLE), {}),
() => {
if((this.common) && (this.common.ORDERABLE)) {
if((this.lastorderable) && (this.lastorderable != this.common.ORDERABLE)) {
for(var k in this.common) if((k != 'ORDERABLE') && (this.common.hasOwnProperty(k))) this.common[k] = null;
this.inputs = [{}];
this.loadrsp_groups = [];
}
this.lastorderable = this.common.ORDERABLE;
}
},
{ immediate: true }
);
this.$watch(
() => this.inputs ? this.inputs.map(x => x.DAYS ? parseFloat(x.DAYS)*C_DAYS[x.DAYS.replace(/^\s*([+-]?(?:\d+|\d+\.\d+))\s*|S\b|\s+$/g, '')] : 0).reduce((acc, val) => acc + val, 0) : 0,
(value) => { this.common.SUPPLY = value; },
{ immediate: true }
);
this.$watch(
() => (this.inputs) && (this.inputs.length > 0) && (this.base_product) && (this.common.SUPPLY > 0) ? this.inputs.map(x => [(x.DOSE) && (x.DOSE).indexOf('&') >= 0 ? x.DOSE.split('&')[2] : '~', x.SCHEDULE || '~', x.DAYS || '~']).reduce((acc, val) => (acc[0] += val[0] + '^', acc[1] += val[1] + '^', acc[2] += val[2] + '^', acc), ['', '', '']) : null,
debounce(async (value) => {
if(value) {
var res = await this.client.ORWDPS2_DAY2QTY(this.common.SUPPLY, value[0], value[1], value[2], '', '');
if(!isNaN(parseFloat(res))) this.common.QTY = res;
};
}, 500),
{ immediate: true }
);
this.$watch(
() => { return { dlgdef: this.dlgdef, inputs: this.inputs, common: this.common, sig: this.sig }; },
debounce(() => this.$emit('update:modelValue', this.output), 100),
{ deep: true, immediate: true }
);
}
};
</script>