Skip to content

Commit 2ab030f

Browse files
authored
Merge pull request #146 from mcode/ncpdp-integration
Ncpdp integration
2 parents c7090c8 + 2f15d91 commit 2ab030f

7 files changed

Lines changed: 731 additions & 83 deletions

File tree

backend/src/database/schemas/doctorOrderSchemas.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export const orderSchema = new mongoose.Schema({
2525
total: Number,
2626
pickupDate: String,
2727
dispenseStatus: String,
28+
authorizationNumber: String,
29+
authorizationExpiration: String,
30+
denialReasonCode: String,
31+
remsNote: String,
2832
metRequirements: [
2933
{
3034
name: String,
@@ -44,4 +48,4 @@ export const orderSchema = new mongoose.Schema({
4448
// Compound index is used to prevent duplicates based off of the given parameters
4549
orderSchema.index({ simpleDrugName: 1, patientName: 1 }, { unique: true }); // schema level
4650

47-
export const doctorOrder = mongoose.model('doctorOrder', orderSchema);
51+
export const doctorOrder = mongoose.model('doctorOrder', orderSchema);

backend/src/ncpdpScriptBuilder/buildScript.v2017071.js

Lines changed: 247 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* NCPDP SCRIPT v2017071 Support */
1+
/* NCPDP SCRIPT v2017071 Support - Enhanced for Full REMS Compliance */
22
import { XMLBuilder } from 'fast-xml-parser';
33
import { v4 as uuidv4 } from 'uuid';
44

@@ -12,6 +12,9 @@ const XML_BUILDER_OPTIONS = {
1212
oneListGroup: 'true'
1313
};
1414

15+
/**
16+
* Build base NCPDP message structure
17+
*/
1518
function buildMessage(inputMessage, body) {
1619
const { Message } = inputMessage;
1720
const { Header, Body } = Message;
@@ -33,8 +36,10 @@ function buildMessage(inputMessage, body) {
3336
}
3437
},
3538
{
36-
Message:
37-
'NewRx Request Received For: ' + Body.NewRx.MedicationPrescribed.DrugDescription
39+
MessageID: Header.MessageID
40+
},
41+
{
42+
Message: 'NewRx Request Received For: ' + Body.NewRx.MedicationPrescribed.DrugDescription
3843
},
3944
{ RelatesToMessageID: Header.MessageID },
4045
{ SentTime: time.toISOString() },
@@ -49,12 +54,15 @@ function buildMessage(inputMessage, body) {
4954
return message;
5055
}
5156

57+
/**
58+
* Build NCPDP Status message (success response)
59+
*/
5260
export function buildRxStatus(newRxMessageConvertedToJSON) {
5361
const body = [
5462
{
5563
Status: [
5664
{
57-
Code: '000' // Placeholder: This is dependent on individual pharmacy
65+
Code: '000'
5866
}
5967
]
6068
}
@@ -64,13 +72,16 @@ export function buildRxStatus(newRxMessageConvertedToJSON) {
6472
return builder.build(rxStatus);
6573
}
6674

75+
/**
76+
* Build NCPDP Error message
77+
*/
6778
export function buildRxError(newRxMessageConvertedToJSON, errorMessage) {
6879
const body = [
6980
{
7081
Error: [
7182
{
72-
Code: 900, // Transaction was rejected
73-
DescriptionCode: 1000, // Unable to identify based on information submitted
83+
Code: 900,
84+
DescriptionCode: 1000,
7485
Description: errorMessage
7586
}
7687
]
@@ -81,13 +92,56 @@ export function buildRxError(newRxMessageConvertedToJSON, errorMessage) {
8192
return builder.build(rxStatus);
8293
}
8394

95+
/**
96+
* Build NCPDP RxFill message
97+
* Per NCPDP spec: Sent when medication is dispensed/picked up
98+
* Must be sent to both EHR and REMS Admin for REMS drugs
99+
*/
84100
export const buildRxFill = newRx => {
85101
const { Message } = JSON.parse(newRx.serializedJSON);
86102
const { Header, Body } = Message;
87-
console.log('Message', Message);
103+
console.log('Building RxFill per NCPDP SCRIPT');
88104
const time = new Date();
105+
106+
// Extract medication data from NewRx
107+
const medicationPrescribed = Body.NewRx.MedicationPrescribed;
108+
const drugCoded = medicationPrescribed.DrugCoded;
109+
110+
const medicationDispensed = {
111+
DrugDescription: medicationPrescribed.DrugDescription,
112+
DrugCoded: {
113+
ProductCode: drugCoded.ProductCode ? {
114+
Code: drugCoded.ProductCode.Code,
115+
Qualifier: drugCoded.ProductCode.Qualifier
116+
} : undefined,
117+
Strength: drugCoded.Strength ? {
118+
StrengthValue: drugCoded.Strength.StrengthValue,
119+
StrengthForm: drugCoded.Strength.StrengthForm,
120+
StrengthUnitOfMeasure: drugCoded.Strength.StrengthUnitOfMeasure
121+
} : undefined
122+
},
123+
Quantity: {
124+
Value: medicationPrescribed.Quantity.Value,
125+
CodeListQualifier: medicationPrescribed.Quantity.CodeListQualifier || '87',
126+
QuantityUnitOfMeasure: medicationPrescribed.Quantity.QuantityUnitOfMeasure
127+
},
128+
DaysSupply: medicationPrescribed.DaysSupply,
129+
WrittenDate: medicationPrescribed.WrittenDate,
130+
Substitutions: medicationPrescribed.Substitutions?.Substitutions ||
131+
medicationPrescribed.Substitutions || '0',
132+
NumberOfRefills: medicationPrescribed.Refills?.Quantity ||
133+
medicationPrescribed.NumberOfRefills || 0,
134+
Sig: medicationPrescribed.Sig
135+
};
136+
89137
const message = {
90138
Message: {
139+
'@@DatatypesVersion': '20170715',
140+
'@@TransportVersion': '20170715',
141+
'@@TransactionDomain': 'SCRIPT',
142+
'@@TransactionVersion': '20170715',
143+
'@@StructuresVersion': '20170715',
144+
'@@ECLVersion': '20170715',
91145
Header: [
92146
{
93147
To: {
@@ -104,6 +158,7 @@ export const buildRxFill = newRx => {
104158
{ MessageID: uuidv4() },
105159
{ RelatesToMessageID: Header.MessageID },
106160
{ SentTime: time.toISOString() },
161+
{ RxReferenceNumber: Header.MessageID },
107162
{ PrescriberOrderNumber: Header.PrescriberOrderNumber }
108163
],
109164
Body: [
@@ -117,16 +172,16 @@ export const buildRxFill = newRx => {
117172
Patient: Body.NewRx.Patient,
118173
Pharmacy: {
119174
Identification: {
120-
NCPDPID: MOCK_VALUE,
175+
NCPDPID: Header.To._ || MOCK_VALUE,
121176
NPI: MOCK_VALUE
122177
},
123-
BusinessName: Header.To._,
178+
BusinessName: Header.To._ || 'Pharmacy',
124179
Address: {
125180
AddressLine1: MOCK_VALUE,
126181
City: MOCK_VALUE,
127182
StateProvince: MOCK_VALUE,
128183
PostalCode: MOCK_VALUE,
129-
Country: MOCK_VALUE
184+
CountryCode: 'US'
130185
},
131186
CommunicationNumbers: {
132187
PrimaryTelephone: {
@@ -135,12 +190,194 @@ export const buildRxFill = newRx => {
135190
}
136191
},
137192
Prescriber: Body.NewRx.Prescriber,
193+
MedicationDispensed: medicationDispensed
194+
}
195+
}
196+
]
197+
}
198+
};
199+
const builder = new XMLBuilder(XML_BUILDER_OPTIONS);
200+
return builder.build(message);
201+
};
202+
203+
/**
204+
* Build NCPDP REMSInitiationRequest
205+
*/
206+
export const buildREMSInitiationRequest = newRx => {
207+
const { Message } = JSON.parse(newRx.serializedJSON);
208+
const { Header, Body } = Message;
209+
const time = new Date();
210+
211+
// Extract NDC from medication (prioritize NDC, fallback to other codes)
212+
const drugCoded = Body.NewRx.MedicationPrescribed.DrugCoded;
213+
const ndcCode =
214+
drugCoded?.NDC || drugCoded?.ProductCode?.Code;
215+
const humanPatient = Body.NewRx.Patient.HumanPatient;
216+
const patient = {
217+
HumanPatient: {
218+
Identification: {},
219+
Names: humanPatient.Names,
220+
GenderAndSex: humanPatient.GenderAndSex,
221+
DateOfBirth: humanPatient.DateOfBirth,
222+
Address: humanPatient.Address
223+
}
224+
};
225+
226+
const message = {
227+
Message: {
228+
'@@DatatypesVersion': '2024011',
229+
'@@TransportVersion': '2024011',
230+
'@@TransactionDomain': 'SCRIPT',
231+
'@@TransactionVersion': '2024011',
232+
'@@StructuresVersion': '2024011',
233+
'@@ECLVersion': '2024011',
234+
Header: [
235+
{
236+
To: {
237+
'#text': ndcCode,
238+
'@@Qualifier': 'ZZZ'
239+
}
240+
},
241+
{
242+
From: {
243+
'#text': Header.To._ || 'PIMS Pharmacy',
244+
'@@Qualifier': 'REMS'
245+
}
246+
},
247+
{ MessageID: uuidv4() },
248+
{ SentTime: time.toISOString() },
249+
{
250+
Security: {
251+
Sender: {
252+
SecondaryIdentification: 'PASSWORDR'
253+
}
254+
}
255+
},
256+
{
257+
SenderSoftware: {
258+
SenderSoftwareDeveloper: 'PIMS',
259+
SenderSoftwareProduct: 'PharmacySystem',
260+
SenderSoftwareVersionRelease: '1'
261+
}
262+
},
263+
{ TestMessage: 'false' }
264+
],
265+
Body: [
266+
{
267+
REMSInitiationRequest: {
268+
REMSReferenceID: uuidv4().replace(/-/g, '').substring(0, 25),
269+
Patient: patient,
270+
Pharmacy: {
271+
Identification: {
272+
NCPDPID: Header.To._ || MOCK_VALUE,
273+
NPI: MOCK_VALUE
274+
},
275+
BusinessName: Header.To._ || 'PIMS Pharmacy',
276+
CommunicationNumbers: {
277+
PrimaryTelephone: {
278+
Number: MOCK_VALUE
279+
}
280+
}
281+
},
282+
Prescriber: Body.NewRx.Prescriber,
138283
MedicationPrescribed: Body.NewRx.MedicationPrescribed
139284
}
140285
}
141286
]
142287
}
143288
};
289+
144290
const builder = new XMLBuilder(XML_BUILDER_OPTIONS);
145291
return builder.build(message);
146292
};
293+
294+
/**
295+
* Build NCPDP REMSRequest
296+
*/
297+
export const buildREMSRequest = (newRx, caseNumber) => {
298+
const { Message } = JSON.parse(newRx.serializedJSON);
299+
const { Header, Body } = Message;
300+
const time = new Date();
301+
const deadlineDate = new Date();
302+
deadlineDate.setDate(deadlineDate.getDate() + 7);
303+
304+
// Extract NDC from medication
305+
const drugCoded = Body.NewRx.MedicationPrescribed.DrugCoded;
306+
const ndcCode =
307+
drugCoded?.NDC || drugCoded?.ProductCode?.Code || '66215050130';
308+
309+
const message = {
310+
Message: {
311+
'@@DatatypesVersion': '2024011',
312+
'@@TransportVersion': '2024011',
313+
'@@TransactionDomain': 'SCRIPT',
314+
'@@TransactionVersion': '2024011',
315+
'@@StructuresVersion': '2024011',
316+
'@@ECLVersion': '2024011',
317+
Header: [
318+
{
319+
To: {
320+
'#text': ndcCode,
321+
'@@Qualifier': 'ZZZ'
322+
}
323+
},
324+
{
325+
From: {
326+
'#text': Header.To._ || 'PIMS Pharmacy',
327+
'@@Qualifier': 'REMS'
328+
}
329+
},
330+
{ MessageID: uuidv4() },
331+
{ SentTime: time.toISOString() },
332+
{
333+
Security: {
334+
Sender: {
335+
SecondaryIdentification: 'PASSWORD'
336+
}
337+
}
338+
},
339+
{
340+
SenderSoftware: {
341+
SenderSoftwareDeveloper: 'PIMS',
342+
SenderSoftwareProduct: 'PharmacySystem',
343+
SenderSoftwareVersionRelease: '1'
344+
}
345+
},
346+
{ TestMessage: 'false' }
347+
],
348+
Body: [
349+
{
350+
REMSRequest: {
351+
REMSReferenceID: uuidv4().replace(/-/g, '').substring(0, 25),
352+
Patient: Body.NewRx.Patient,
353+
Pharmacy: {
354+
Identification: {
355+
NCPDPID: Header.To._ || MOCK_VALUE,
356+
NPI: MOCK_VALUE
357+
},
358+
BusinessName: Header.To._ || 'PIMS Pharmacy',
359+
CommunicationNumbers: {
360+
PrimaryTelephone: {
361+
Number: MOCK_VALUE
362+
}
363+
}
364+
},
365+
Prescriber: Body.NewRx.Prescriber,
366+
MedicationPrescribed: Body.NewRx.MedicationPrescribed,
367+
Request: {
368+
SolicitedModel: {
369+
REMSCaseID: caseNumber,
370+
DeadlineForReply: {
371+
Date: deadlineDate.toISOString().split('T')[0]
372+
}
373+
}
374+
}
375+
}
376+
}
377+
]
378+
}
379+
};
380+
381+
const builder = new XMLBuilder(XML_BUILDER_OPTIONS);
382+
return builder.build(message);
383+
};

0 commit comments

Comments
 (0)