diff --git a/backend/env.json b/backend/env.json
index 30e25f1..18462d2 100644
--- a/backend/env.json
+++ b/backend/env.json
@@ -54,5 +54,13 @@
"REMS_ADMIN_NCPDP": {
"type": "string",
"default": "http://localhost:8090/ncpdp/script"
+ },
+ "INTERMEDIARY_URL": {
+ "type": "string",
+ "default": "http://localhost:8090/ncpdp/script"
+ },
+ "EHR_NCPDP_URL": {
+ "type": "string",
+ "default": "|| 'http://localhost:8080/ncpdp/script'"
}
}
diff --git a/backend/src/lib/pharmacyConfig.js b/backend/src/lib/pharmacyConfig.js
new file mode 100644
index 0000000..dd3fbd7
--- /dev/null
+++ b/backend/src/lib/pharmacyConfig.js
@@ -0,0 +1,56 @@
+
+// Configuration state
+let config = {
+ useIntermediary: process.env.USE_INTERMEDIARY,
+ intermediaryUrl: process.env.INTERMEDIARY_URL,
+ remsAdminUrl: process.env.REMS_ADMIN_NCPDP,
+ ehrUrl: process.env.EHR_NCPDP_URL
+};
+
+
+
+export function getConfig() {
+ return { ...config };
+}
+
+
+export function updateConfig(newConfig) {
+ config = { ...config, ...newConfig };
+ console.log('Configuration updated:', config);
+ return { ...config };
+}
+
+/**
+ * Get the endpoint for NCPDP messages (REMS)
+ */
+export function getNCPDPEndpoint() {
+ if (config.useIntermediary) {
+ return `${config.intermediaryUrl}/ncpdp/script`;
+ }
+ return config.remsAdminUrl;
+}
+
+/**
+ * Get the endpoint for ETASU requests
+ */
+export function getETASUEndpoint() {
+ if (config.useIntermediary) {
+ return `${config.intermediaryUrl}/etasu`;
+ }
+ // Direct ETASU endpoint to REMS Admin
+ return config.remsAdminUrl.replace('/ncpdp', '/4_0_0/GuidanceResponse/$rems-etasu');
+}
+
+/**
+ * Get the endpoint for RxFill messages (to EHR)
+ * RxFill is sent to both EHR and REMS Admin
+ * If using intermediary, send to intermediary (it forwards to both)
+ * If not using intermediary, return EHR endpoint (caller must also send to REMS)
+ */
+export function getRxFillEndpoint() {
+ if (config.useIntermediary) {
+ // Intermediary handles forwarding to both EHR and REMS Admin
+ return `${config.intermediaryUrl}/ncpdp/script`;
+ }
+ return config.ehrUrl;
+}
\ No newline at end of file
diff --git a/backend/src/routes/doctorOrders.js b/backend/src/routes/doctorOrders.js
index 841ac08..cd23700 100644
--- a/backend/src/routes/doctorOrders.js
+++ b/backend/src/routes/doctorOrders.js
@@ -16,6 +16,7 @@ import {
} from '../ncpdpScriptBuilder/buildScript.v2017071.js';
import { NewRx } from '../database/schemas/newRx.js';
import { medicationRequestToRemsAdmins } from '../database/data.js';
+import { getConfig, updateConfig, getNCPDPEndpoint, getETASUEndpoint, getRxFillEndpoint } from '../lib/pharmacyConfig.js';
bpx(bodyParser);
router.use(
@@ -276,37 +277,48 @@ router.patch('/api/updateRx/:id/pickedUp', async (req, res) => {
const rxFill = buildRxFill(newRx);
console.log('Sending RxFill per NCPDP workflow');
-
- // Send to EHR
- try {
- const ehrStatus = await axios.post(env.EHR_RXFILL_URL, rxFill, {
- headers: {
- Accept: 'application/xml',
- 'Content-Type': 'application/xml'
- }
+
+ const config = getConfig();
+
+ if (config.useIntermediary) {
+ // Send to intermediary - it will forward to both EHR and REMS Admin
+ const endpoint = getRxFillEndpoint();
+ console.log(`Sending RxFill to intermediary: ${endpoint}`);
+ await axios.post(endpoint, rxFill, {
+ headers: { 'Content-Type': 'application/xml' }
});
- console.log('Sent RxFill to EHR, received status:', ehrStatus.data);
- } catch (ehrError) {
- console.log('Failed to send RxFill to EHR:', ehrError.message);
- }
-
- // Send to REMS Admin (required by NCPDP spec for REMS drugs)
- const order = await doctorOrder.findOne({ prescriberOrderNumber });
- if (isRemsDrug(order)) {
+ } else {
+ // Send to EHR
try {
- const remsAdminStatus = await axios.post(
- env.REMS_ADMIN_NCPDP,
- rxFill,
- {
- headers: {
- Accept: 'application/xml',
- 'Content-Type': 'application/xml'
- }
+ const ehrStatus = await axios.post(env.EHR_RXFILL_URL, rxFill, {
+ headers: {
+ Accept: 'application/xml',
+ 'Content-Type': 'application/xml'
}
- );
- console.log('Sent RxFill to REMS Admin, received status:', remsAdminStatus.data);
- } catch (remsError) {
- console.log('Failed to send RxFill to REMS Admin:', remsError.message);
+ });
+ console.log('Sent RxFill to EHR, received status:', ehrStatus.data);
+ } catch (ehrError) {
+ console.log('Failed to send RxFill to EHR:', ehrError.message);
+ }
+
+ // Send to REMS Admin (required by NCPDP spec for REMS drugs)
+ const order = await doctorOrder.findOne({ prescriberOrderNumber });
+ if (isRemsDrug(order)) {
+ try {
+ const remsAdminStatus = await axios.post(
+ env.REMS_ADMIN_NCPDP,
+ rxFill,
+ {
+ headers: {
+ Accept: 'application/xml',
+ 'Content-Type': 'application/xml'
+ }
+ }
+ );
+ console.log('Sent RxFill to REMS Admin, received status:', remsAdminStatus.data);
+ } catch (remsError) {
+ console.log('Failed to send RxFill to REMS Admin:', remsError.message);
+ }
}
}
} catch (error) {
@@ -482,7 +494,7 @@ const getGuidanceResponse = async order => {
try {
const response = await axios.post(etasuUrl, body, {
- headers: {
+ headers: {
'content-type': 'application/json'
}
});
@@ -516,8 +528,11 @@ const sendREMSInitiationRequest = async order => {
console.log(initiationRequest)
+ const endpoint = getNCPDPEndpoint();
+ console.log(`Sending REMSInitiationRequest to: ${endpoint}`);
+
const response = await axios.post(
- env.REMS_ADMIN_NCPDP,
+ endpoint,
initiationRequest,
{
headers: {
@@ -527,6 +542,7 @@ const sendREMSInitiationRequest = async order => {
}
);
+
const parsedResponse = await parseStringPromise(response.data, XML2JS_OPTS);
console.log('Received REMSInitiationResponse');
@@ -563,8 +579,11 @@ const sendREMSRequest = async order => {
console.log('Sending REMSRequest to REMS Admin for case:', order.caseNumber);
console.log(remsRequest)
+ const endpoint = getNCPDPEndpoint();
+ console.log(`Sending REMSRequest to: ${endpoint}`);
+
const response = await axios.post(
- env.REMS_ADMIN_NCPDP,
+ endpoint,
remsRequest,
{
headers: {
@@ -774,4 +793,25 @@ async function parseNCPDPScript(newRx) {
return order;
}
+/**
+ * Route: 'doctorOrders/api/config'
+ * Description: 'Get current pharmacy configuration'
+ */
+router.get('/api/config', async (_req, res) => {
+ const config = getConfig();
+ console.log('Returning configuration:', config);
+ res.json(config);
+});
+
+/**
+ * Route: 'doctorOrders/api/config'
+ * Description: 'Update pharmacy configuration'
+ */
+router.post('/api/config', async (req, res) => {
+ const newConfig = updateConfig(req.body);
+ console.log('Configuration updated:', newConfig);
+ res.json(newConfig);
+});
+
+
export default router;
\ No newline at end of file
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 3ee0b3d..bc188bc 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -8,6 +8,7 @@ import DoctorOrders from './views/DoctorOrders/DoctorOrders';
import Login from './views/Login/Login';
import ProtectedRoute from './components/ProtectedRoute';
import { AuthProvider } from './contexts/AuthContext';
+import ConfigToggle from './components/ConfigToggle';
import axios from 'axios';
axios.defaults.baseURL = process.env.REACT_APP_PIMS_BACKEND_URL
@@ -38,6 +39,7 @@ function App() {
+
@@ -60,4 +62,4 @@ function App() {
);
}
-export default App;
+export default App;
\ No newline at end of file
diff --git a/frontend/src/components/ConfigToggle.tsx b/frontend/src/components/ConfigToggle.tsx
new file mode 100644
index 0000000..70598c4
--- /dev/null
+++ b/frontend/src/components/ConfigToggle.tsx
@@ -0,0 +1,85 @@
+import { IconButton, Menu, MenuItem, Switch, Typography, Box, Divider } from '@mui/material';
+import SettingsIcon from '@mui/icons-material/Settings';
+import { useState, useEffect } from 'react';
+import axios from 'axios';
+
+export default function ConfigToggle() {
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [useIntermediary, setUseIntermediary] = useState(false);
+ const open = Boolean(anchorEl);
+
+ // Load config on mount
+ useEffect(() => {
+ const saved = localStorage.getItem('useIntermediary');
+ if (saved !== null) {
+ setUseIntermediary(saved === 'true');
+ }
+ }, []);
+
+ const handleClick = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
+
+ const handleToggle = async () => {
+ const newValue = !useIntermediary;
+ setUseIntermediary(newValue);
+ localStorage.setItem('useIntermediary', String(newValue));
+
+ // Update backend
+ try {
+ await axios.post('/doctorOrders/api/config', { useIntermediary: newValue });
+ console.log('Configuration updated:', newValue ? 'Using Intermediary' : 'Direct Connection');
+ } catch (error) {
+ console.error('Failed to update backend config:', error);
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+}
\ No newline at end of file