@rinse-dental/open-dental 3.2.2 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ import HttpClient from "../utils/httpClient";
2
+ import { PayPlanCharge } from "../types/payPlanChargeTypes";
3
+ export default class PayPlanCharges {
4
+ private httpClient;
5
+ constructor(httpClient: HttpClient);
6
+ /**
7
+ * Fetch all payment plan charges for a specified payment plan.
8
+ * Mirrors GET /payplancharges
9
+ * Added in version 23.2.28
10
+ *
11
+ * @see https://www.opendental.com/site/apipayplancharges.html
12
+ * @param params - Query parameters: { PayPlanNum: number } (required)
13
+ */
14
+ getPayPlanCharges(params: {
15
+ PayPlanNum: number;
16
+ }): Promise<PayPlanCharge[]>;
17
+ }
18
+ //# sourceMappingURL=payPlanCharges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payPlanCharges.d.ts","sourceRoot":"","sources":["../../src/api/payPlanCharges.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAS5D,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,CAAC,UAAU,CAAa;gBAEnB,UAAU,EAAE,UAAU;IAIlC;;;;;;;OAOG;IACU,iBAAiB,CAC5B,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAC7B,OAAO,CAAC,aAAa,EAAE,CAAC;CAoB5B"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /** Utility to remove undefined fields from objects (for API payloads) */
4
+ const clean = (obj) => {
5
+ return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== undefined));
6
+ };
7
+ class PayPlanCharges {
8
+ httpClient;
9
+ constructor(httpClient) {
10
+ this.httpClient = httpClient;
11
+ }
12
+ /**
13
+ * Fetch all payment plan charges for a specified payment plan.
14
+ * Mirrors GET /payplancharges
15
+ * Added in version 23.2.28
16
+ *
17
+ * @see https://www.opendental.com/site/apipayplancharges.html
18
+ * @param params - Query parameters: { PayPlanNum: number } (required)
19
+ */
20
+ async getPayPlanCharges(params) {
21
+ // Validate required parameter
22
+ if (!params || typeof params !== "object") {
23
+ throw new Error("Invalid parameters: getPayPlanCharges requires a params object.");
24
+ }
25
+ if (!params.PayPlanNum || typeof params.PayPlanNum !== "number") {
26
+ throw new Error("Invalid parameter: PayPlanNum is required and must be a number.");
27
+ }
28
+ const query = clean({
29
+ PayPlanNum: params.PayPlanNum,
30
+ });
31
+ return this.httpClient.get("/payplancharges", query);
32
+ }
33
+ }
34
+ exports.default = PayPlanCharges;
@@ -0,0 +1,66 @@
1
+ import HttpClient from "../utils/httpClient";
2
+ import { PayPlan, GetPayPlanByIdParams, CreateDynamicPayPlanParams, CreatePayPlanParams, ClosePayPlanParams, UpdateDynamicPayPlanParams } from "../types/payPlanTypes";
3
+ export default class PayPlans {
4
+ private httpClient;
5
+ constructor(httpClient: HttpClient);
6
+ /**
7
+ * Fetch multiple payment plans with filtering by PatNum or Guarantor.
8
+ * Mirrors GET /payplans
9
+ * @see https://www.opendental.com/site/apipayplans.html
10
+ * @param params - Query parameters: { PatNum: number } OR { Guarantor: number } (mutually exclusive, one required)
11
+ */
12
+ getPayPlans(params: {
13
+ PatNum: number;
14
+ Guarantor?: never;
15
+ } | {
16
+ Guarantor: number;
17
+ PatNum?: never;
18
+ }): Promise<PayPlan[]>;
19
+ /**
20
+ * Fetch a single payment plan by PayPlanNum.
21
+ * Mirrors GET /payplans/{PayPlanNum}
22
+ * Added in version 24.4.30
23
+ */
24
+ getPayPlan({ PayPlanNum, }: GetPayPlanByIdParams): Promise<PayPlan>;
25
+ /**
26
+ * Create a new dynamic payment plan.
27
+ * Mirrors POST /payplans/Dynamic
28
+ * Added in version 22.2.22
29
+ *
30
+ * Required:
31
+ * - PatNum
32
+ * - PayAmt OR NumberOfPayments
33
+ * - procNums and/or adjNums (at least one must have items)
34
+ *
35
+ * Note: If APR > 0, IsLocked may be required to be true (depending on preferences)
36
+ */
37
+ createDynamicPayPlan(data: CreateDynamicPayPlanParams): Promise<PayPlan>;
38
+ /**
39
+ * Create a patient payment plan (DEPRECATED as of 23.3.1).
40
+ * Mirrors POST /payplans
41
+ * Added in version 22.4.39, deprecated in 23.3.1
42
+ *
43
+ * @deprecated Use createDynamicPayPlan instead. This endpoint returns 410 Gone in newer versions.
44
+ *
45
+ * Required:
46
+ * - PatNum
47
+ * - useEstBalance OR principalAmount
48
+ * - PayAmt OR NumberOfPayments
49
+ */
50
+ createPayPlan(data: CreatePayPlanParams): Promise<PayPlan>;
51
+ /**
52
+ * Close a payment plan and charge any outstanding interest.
53
+ * Mirrors PUT /payplans/{PayPlanNum}/Close
54
+ * Added in version 22.4.39
55
+ */
56
+ closePayPlan({ PayPlanNum, }: ClosePayPlanParams): Promise<PayPlan>;
57
+ /**
58
+ * Update an existing dynamic payment plan.
59
+ * Mirrors PUT /payplans/{PayPlanNum}/Dynamic
60
+ * Added in version 25.3.31
61
+ *
62
+ * All fields except PayPlanNum are optional; send only what you intend to change.
63
+ */
64
+ updateDynamicPayPlan(data: UpdateDynamicPayPlanParams): Promise<PayPlan>;
65
+ }
66
+ //# sourceMappingURL=payPlans.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payPlans.d.ts","sourceRoot":"","sources":["../../src/api/payPlans.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EACL,OAAO,EACP,oBAAoB,EACpB,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,EAClB,0BAA0B,EAC3B,MAAM,uBAAuB,CAAC;AAS/B,MAAM,CAAC,OAAO,OAAO,QAAQ;IAC3B,OAAO,CAAC,UAAU,CAAa;gBAEnB,UAAU,EAAE,UAAU;IAIlC;;;;;OAKG;IACU,WAAW,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,KAAK,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,KAAK,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAsBnI;;;;OAIG;IACU,UAAU,CAAC,EACtB,UAAU,GACX,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IAO1C;;;;;;;;;;;OAWG;IACU,oBAAoB,CAC/B,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,OAAO,CAAC;IA4DnB;;;;;;;;;;;OAWG;IACU,aAAa,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAuDvE;;;;OAIG;IACU,YAAY,CAAC,EACxB,UAAU,GACX,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAaxC;;;;;;OAMG;IACU,oBAAoB,CAC/B,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,OAAO,CAAC;CAoCpB"}
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /** Utility to remove undefined fields from objects (for API payloads) */
4
+ const clean = (obj) => {
5
+ return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== undefined));
6
+ };
7
+ class PayPlans {
8
+ httpClient;
9
+ constructor(httpClient) {
10
+ this.httpClient = httpClient;
11
+ }
12
+ /**
13
+ * Fetch multiple payment plans with filtering by PatNum or Guarantor.
14
+ * Mirrors GET /payplans
15
+ * @see https://www.opendental.com/site/apipayplans.html
16
+ * @param params - Query parameters: { PatNum: number } OR { Guarantor: number } (mutually exclusive, one required)
17
+ */
18
+ async getPayPlans(params) {
19
+ // API requires either PatNum OR Guarantor, not both
20
+ if ('PatNum' in params && 'Guarantor' in params && params.PatNum && params.Guarantor) {
21
+ throw new Error("Invalid parameters: PatNum and Guarantor are mutually exclusive. Provide only one.");
22
+ }
23
+ if (!('PatNum' in params) && !('Guarantor' in params)) {
24
+ throw new Error("Invalid parameters: Either PatNum or Guarantor is required.");
25
+ }
26
+ const query = clean({
27
+ PatNum: 'PatNum' in params ? params.PatNum : undefined,
28
+ Guarantor: 'Guarantor' in params ? params.Guarantor : undefined,
29
+ });
30
+ return this.httpClient.get("/payplans", query);
31
+ }
32
+ /**
33
+ * Fetch a single payment plan by PayPlanNum.
34
+ * Mirrors GET /payplans/{PayPlanNum}
35
+ * Added in version 24.4.30
36
+ */
37
+ async getPayPlan({ PayPlanNum, }) {
38
+ if (!PayPlanNum || typeof PayPlanNum !== "number") {
39
+ throw new Error("Invalid parameter: PayPlanNum must be a valid number.");
40
+ }
41
+ return this.httpClient.get(`/payplans/${PayPlanNum}`);
42
+ }
43
+ /**
44
+ * Create a new dynamic payment plan.
45
+ * Mirrors POST /payplans/Dynamic
46
+ * Added in version 22.2.22
47
+ *
48
+ * Required:
49
+ * - PatNum
50
+ * - PayAmt OR NumberOfPayments
51
+ * - procNums and/or adjNums (at least one must have items)
52
+ *
53
+ * Note: If APR > 0, IsLocked may be required to be true (depending on preferences)
54
+ */
55
+ async createDynamicPayPlan(data) {
56
+ // Validate required fields
57
+ if (!data || typeof data !== "object") {
58
+ throw new Error("Invalid payload: createDynamicPayPlan requires a request body.");
59
+ }
60
+ if (!data.PatNum || typeof data.PatNum !== "number") {
61
+ throw new Error("Invalid payload: PatNum is required and must be a number.");
62
+ }
63
+ // Either PayAmt OR NumberOfPayments is required
64
+ if (!data.PayAmt && !data.NumberOfPayments) {
65
+ throw new Error("Invalid payload: Either PayAmt or NumberOfPayments is required.");
66
+ }
67
+ // At least one of procNums or adjNums must be provided and non-empty
68
+ const hasProcNums = Array.isArray(data.procNums) && data.procNums.length > 0;
69
+ const hasAdjNums = Array.isArray(data.adjNums) && data.adjNums.length > 0;
70
+ if (!hasProcNums && !hasAdjNums) {
71
+ throw new Error("Invalid payload: At least one of procNums or adjNums must be a non-empty array.");
72
+ }
73
+ // Validate APR and IsLocked relationship
74
+ if (data.APR && data.APR > 0 && data.IsLocked === false) {
75
+ console.warn("Warning: When APR > 0, IsLocked may need to be true depending on Open Dental preferences.");
76
+ }
77
+ const payload = clean({
78
+ PatNum: data.PatNum,
79
+ PayAmt: data.PayAmt,
80
+ NumberOfPayments: data.NumberOfPayments,
81
+ procNums: data.procNums,
82
+ adjNums: data.adjNums,
83
+ Guarantor: data.Guarantor,
84
+ PayPlanDate: data.PayPlanDate,
85
+ APR: data.APR,
86
+ DownPayment: data.DownPayment,
87
+ Note: data.Note,
88
+ PlanCategory: data.PlanCategory,
89
+ ChargeFrequency: data.ChargeFrequency,
90
+ DatePayPlanStart: data.DatePayPlanStart,
91
+ DateInterestStart: data.DateInterestStart,
92
+ IsLocked: data.IsLocked,
93
+ DynamicPayPlanTPOption: data.DynamicPayPlanTPOption,
94
+ });
95
+ return this.httpClient.post("/payplans/Dynamic", payload);
96
+ }
97
+ /**
98
+ * Create a patient payment plan (DEPRECATED as of 23.3.1).
99
+ * Mirrors POST /payplans
100
+ * Added in version 22.4.39, deprecated in 23.3.1
101
+ *
102
+ * @deprecated Use createDynamicPayPlan instead. This endpoint returns 410 Gone in newer versions.
103
+ *
104
+ * Required:
105
+ * - PatNum
106
+ * - useEstBalance OR principalAmount
107
+ * - PayAmt OR NumberOfPayments
108
+ */
109
+ async createPayPlan(data) {
110
+ console.warn("Warning: createPayPlan is deprecated as of version 23.3.1. Use createDynamicPayPlan instead.");
111
+ // Validate required fields
112
+ if (!data || typeof data !== "object") {
113
+ throw new Error("Invalid payload: createPayPlan requires a request body.");
114
+ }
115
+ if (!data.PatNum || typeof data.PatNum !== "number") {
116
+ throw new Error("Invalid payload: PatNum is required and must be a number.");
117
+ }
118
+ // Either useEstBalance OR principalAmount is required
119
+ if (data.useEstBalance === undefined &&
120
+ data.principalAmount === undefined) {
121
+ throw new Error("Invalid payload: Either useEstBalance or principalAmount is required.");
122
+ }
123
+ // Either PayAmt OR NumberOfPayments is required
124
+ if (!data.PayAmt && !data.NumberOfPayments) {
125
+ throw new Error("Invalid payload: Either PayAmt or NumberOfPayments is required.");
126
+ }
127
+ const payload = clean({
128
+ PatNum: data.PatNum,
129
+ useEstBalance: data.useEstBalance,
130
+ principalAmount: data.principalAmount,
131
+ PayAmt: data.PayAmt,
132
+ NumberOfPayments: data.NumberOfPayments,
133
+ Guarantor: data.Guarantor,
134
+ PayPlanDate: data.PayPlanDate,
135
+ APR: data.APR,
136
+ DownPayment: data.DownPayment,
137
+ Note: data.Note,
138
+ PlanCategory: data.PlanCategory,
139
+ ChargeFrequency: data.ChargeFrequency,
140
+ DatePayPlanStart: data.DatePayPlanStart,
141
+ DateInterestStart: data.DateInterestStart,
142
+ });
143
+ return this.httpClient.post("/payplans", payload);
144
+ }
145
+ /**
146
+ * Close a payment plan and charge any outstanding interest.
147
+ * Mirrors PUT /payplans/{PayPlanNum}/Close
148
+ * Added in version 22.4.39
149
+ */
150
+ async closePayPlan({ PayPlanNum, }) {
151
+ if (!PayPlanNum || typeof PayPlanNum !== "number") {
152
+ throw new Error("Invalid parameter: PayPlanNum is required and must be a number.");
153
+ }
154
+ return this.httpClient.put(`/payplans/${PayPlanNum}/Close`, {});
155
+ }
156
+ /**
157
+ * Update an existing dynamic payment plan.
158
+ * Mirrors PUT /payplans/{PayPlanNum}/Dynamic
159
+ * Added in version 25.3.31
160
+ *
161
+ * All fields except PayPlanNum are optional; send only what you intend to change.
162
+ */
163
+ async updateDynamicPayPlan(data) {
164
+ if (!data?.PayPlanNum || typeof data.PayPlanNum !== "number") {
165
+ throw new Error("Invalid payload: PayPlanNum is required and must be a number.");
166
+ }
167
+ // Validate APR and IsLocked relationship
168
+ if (data.APR && data.APR > 0 && data.IsLocked === false) {
169
+ console.warn("Warning: When APR > 0, IsLocked may need to be true depending on Open Dental preferences.");
170
+ }
171
+ const { PayPlanNum, ...updateFields } = data;
172
+ const payload = clean({
173
+ PayAmt: updateFields.PayAmt,
174
+ NumberOfPayments: updateFields.NumberOfPayments,
175
+ Guarantor: updateFields.Guarantor,
176
+ PayPlanDate: updateFields.PayPlanDate,
177
+ APR: updateFields.APR,
178
+ Note: updateFields.Note,
179
+ PlanCategory: updateFields.PlanCategory,
180
+ ChargeFrequency: updateFields.ChargeFrequency,
181
+ DatePayPlanStart: updateFields.DatePayPlanStart,
182
+ DateInterestStart: updateFields.DateInterestStart,
183
+ IsLocked: updateFields.IsLocked,
184
+ SheetDefNum: updateFields.SheetDefNum,
185
+ });
186
+ return this.httpClient.put(`/payplans/${PayPlanNum}/Dynamic`, payload);
187
+ }
188
+ }
189
+ exports.default = PayPlans;
@@ -12,6 +12,8 @@ import Providers from "./api/providers";
12
12
  import Operatories from "./api/operatories";
13
13
  import Payments from "./api/payments";
14
14
  import PaySplits from "./api/paySplits";
15
+ import PayPlans from "./api/payPlans";
16
+ import PayPlanCharges from "./api/payPlanCharges";
15
17
  import Definitions from "./api/definitions";
16
18
  import DiscountPlans from "./api/discountPlans";
17
19
  import DiscountPlanSubs from "./api/discountPlanSubs";
@@ -161,6 +163,14 @@ declare class OpenDental {
161
163
  * Create a new instance of the PaySplits API.
162
164
  */
163
165
  static PaySplits(): PaySplits;
166
+ /**
167
+ * Create a new instance of the PayPlans API.
168
+ */
169
+ static PayPlans(): PayPlans;
170
+ /**
171
+ * Create a new instance of the PayPlanCharges API.
172
+ */
173
+ static PayPlanCharges(): PayPlanCharges;
164
174
  /**
165
175
  * Create a new instance of the Fees API.
166
176
  */
@@ -1 +1 @@
1
- {"version":3,"file":"openDental.d.ts","sourceRoot":"","sources":["../src/openDental.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openDental.d.ts","sourceRoot":"","sources":["../src/openDental.ts"],"names":[],"mappings":"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"}
@@ -52,6 +52,8 @@ const providers_1 = __importDefault(require("./api/providers"));
52
52
  const operatories_1 = __importDefault(require("./api/operatories"));
53
53
  const payments_1 = __importDefault(require("./api/payments"));
54
54
  const paySplits_1 = __importDefault(require("./api/paySplits"));
55
+ const payPlans_1 = __importDefault(require("./api/payPlans"));
56
+ const payPlanCharges_1 = __importDefault(require("./api/payPlanCharges"));
55
57
  const definitions_1 = __importDefault(require("./api/definitions"));
56
58
  const discountPlans_1 = __importDefault(require("./api/discountPlans"));
57
59
  const discountPlanSubs_1 = __importDefault(require("./api/discountPlanSubs"));
@@ -353,6 +355,24 @@ class OpenDental {
353
355
  }
354
356
  return new paySplits_1.default(this.httpClient);
355
357
  }
358
+ /**
359
+ * Create a new instance of the PayPlans API.
360
+ */
361
+ static PayPlans() {
362
+ if (!this.httpClient) {
363
+ throw new Error("OpenDental not initialized. Call OpenDental.initialize() first.");
364
+ }
365
+ return new payPlans_1.default(this.httpClient);
366
+ }
367
+ /**
368
+ * Create a new instance of the PayPlanCharges API.
369
+ */
370
+ static PayPlanCharges() {
371
+ if (!this.httpClient) {
372
+ throw new Error("OpenDental not initialized. Call OpenDental.initialize() first.");
373
+ }
374
+ return new payPlanCharges_1.default(this.httpClient);
375
+ }
356
376
  /**
357
377
  * Create a new instance of the Fees API.
358
378
  */
@@ -0,0 +1,57 @@
1
+ /**
2
+ * PayPlanCharge types for Open Dental REST API
3
+ * Spec refs:
4
+ * - https://www.opendental.com/site/apipayplancharges.html
5
+ *
6
+ * Notes:
7
+ * - Dates are strings from the API (yyyy-MM-dd or yyyy-MM-dd HH:mm:ss).
8
+ * - PayPlanCharges represent individual charges within a payment plan
9
+ * - Each charge can be a debit (payment due) or credit (payment received)
10
+ * @see https://www.opendental.com/site/apipayplancharges.html
11
+ */
12
+ /** Charge type for payment plan charges */
13
+ export type ChargeType = "Debit" | "Credit";
14
+ /** Link type for payment plan charges */
15
+ export type LinkType = "Procedure" | "Adjustment" | "Payment" | "None" | string;
16
+ /**
17
+ * Represents a PayPlanCharge returned by the Open Dental API.
18
+ * Mirrors the response payload of GET endpoints.
19
+ */
20
+ export interface PayPlanCharge {
21
+ PayPlanChargeNum: number;
22
+ PayPlanNum: number;
23
+ Guarantor: number;
24
+ PatNum: number;
25
+ ChargeDate: string;
26
+ Principal: number;
27
+ Interest: number;
28
+ Note: string;
29
+ ProvNum: number;
30
+ ClinicNum: number;
31
+ ChargeType: ChargeType;
32
+ ProcNum: number;
33
+ SecDateTEntry: string;
34
+ SecDateTEdit: string;
35
+ StatementNum: number;
36
+ FKey: number;
37
+ LinkType: LinkType;
38
+ IsOffset: boolean;
39
+ }
40
+ /** GET /payplancharges query params */
41
+ export interface GetPayPlanChargesParams {
42
+ /** Payment plan number - Required */
43
+ PayPlanNum: number;
44
+ }
45
+ /** Minimal helper to detect "zero" dates in responses */
46
+ export declare const isZeroDate: (d?: string | null) => boolean;
47
+ /** Narrow helper for timestamps that can be zeroed by the API */
48
+ export declare const isZeroDateTime: (d?: string | null) => boolean;
49
+ /** Runtime guard for ChargeType values */
50
+ export declare const isChargeType: (s: string) => s is ChargeType;
51
+ /** Helper to determine if a charge is a debit (payment due) */
52
+ export declare const isDebitCharge: (charge: PayPlanCharge) => boolean;
53
+ /** Helper to determine if a charge is a credit (payment received) */
54
+ export declare const isCreditCharge: (charge: PayPlanCharge) => boolean;
55
+ /** Helper to calculate total charge amount (principal + interest) */
56
+ export declare const getTotalChargeAmount: (charge: PayPlanCharge) => number;
57
+ //# sourceMappingURL=payPlanChargeTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payPlanChargeTypes.d.ts","sourceRoot":"","sources":["../../src/types/payPlanChargeTypes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,2CAA2C;AAC3C,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5C,yCAAyC;AACzC,MAAM,MAAM,QAAQ,GAChB,WAAW,GACX,YAAY,GACZ,SAAS,GACT,MAAM,GACN,MAAM,CAAC;AAEX;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,uCAAuC;AACvC,MAAM,WAAW,uBAAuB;IACtC,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,yDAAyD;AACzD,eAAO,MAAM,UAAU,GAAI,IAAI,MAAM,GAAG,IAAI,KAAG,OACrB,CAAC;AAE3B,iEAAiE;AACjE,eAAO,MAAM,cAAc,GAAI,IAAI,MAAM,GAAG,IAAI,KAAG,OACjB,CAAC;AAEnC,0CAA0C;AAC1C,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,KAAG,CAAC,IAAI,UACb,CAAC;AAElC,+DAA+D;AAC/D,eAAO,MAAM,aAAa,GAAI,QAAQ,aAAa,KAAG,OACvB,CAAC;AAEhC,qEAAqE;AACrE,eAAO,MAAM,cAAc,GAAI,QAAQ,aAAa,KAAG,OACvB,CAAC;AAEjC,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,GAAI,QAAQ,aAAa,KAAG,MACzB,CAAC"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * PayPlanCharge types for Open Dental REST API
4
+ * Spec refs:
5
+ * - https://www.opendental.com/site/apipayplancharges.html
6
+ *
7
+ * Notes:
8
+ * - Dates are strings from the API (yyyy-MM-dd or yyyy-MM-dd HH:mm:ss).
9
+ * - PayPlanCharges represent individual charges within a payment plan
10
+ * - Each charge can be a debit (payment due) or credit (payment received)
11
+ * @see https://www.opendental.com/site/apipayplancharges.html
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.getTotalChargeAmount = exports.isCreditCharge = exports.isDebitCharge = exports.isChargeType = exports.isZeroDateTime = exports.isZeroDate = void 0;
15
+ /** Minimal helper to detect "zero" dates in responses */
16
+ const isZeroDate = (d) => !d || d === "0001-01-01";
17
+ exports.isZeroDate = isZeroDate;
18
+ /** Narrow helper for timestamps that can be zeroed by the API */
19
+ const isZeroDateTime = (d) => !d || d.startsWith("0001-01-01");
20
+ exports.isZeroDateTime = isZeroDateTime;
21
+ /** Runtime guard for ChargeType values */
22
+ const isChargeType = (s) => ["Debit", "Credit"].includes(s);
23
+ exports.isChargeType = isChargeType;
24
+ /** Helper to determine if a charge is a debit (payment due) */
25
+ const isDebitCharge = (charge) => charge.ChargeType === "Debit";
26
+ exports.isDebitCharge = isDebitCharge;
27
+ /** Helper to determine if a charge is a credit (payment received) */
28
+ const isCreditCharge = (charge) => charge.ChargeType === "Credit";
29
+ exports.isCreditCharge = isCreditCharge;
30
+ /** Helper to calculate total charge amount (principal + interest) */
31
+ const getTotalChargeAmount = (charge) => charge.Principal + charge.Interest;
32
+ exports.getTotalChargeAmount = getTotalChargeAmount;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * PayPlan types for Open Dental REST API
3
+ * Spec refs:
4
+ * - https://www.opendental.com/site/apipayplans.html
5
+ *
6
+ * Notes:
7
+ * - Dates are strings from the API (yyyy-MM-dd or yyyy-MM-dd HH:mm:ss).
8
+ * - Prior to 23.3.1, Open Dental users had the ability to create both Patient Payment Plans and Dynamic Payment Plans.
9
+ * - In version 23.3.1, Open Dental deprecated Patient Payment Plans, renaming them to Old Payment Plans in the UI.
10
+ * - The PayPlans POST (create) method was also deprecated at that time.
11
+ * - Dynamic Payment Plans are now referred to simply as Payment Plans in Open Dental.
12
+ * - Insurance payment plans have PlanNum and InsSubNum values greater than zero
13
+ * @see https://www.opendental.com/site/apipayplans.html
14
+ */
15
+ /** Charge frequency for payment plans */
16
+ export type ChargeFrequency = "Weekly" | "EveryOtherWeek" | "Monthly" | "Quarterly" | "OrdinalWeekday";
17
+ /** Dynamic payment plan treatment plan option */
18
+ export type DynamicPayPlanTPOption = "AwaitComplete" | "TreatAsComplete" | "None";
19
+ /**
20
+ * Represents a PayPlan returned by the Open Dental API.
21
+ * Mirrors the response payload of GET/POST/PUT endpoints.
22
+ */
23
+ export interface PayPlan {
24
+ PayPlanNum: number;
25
+ PatNum: number;
26
+ Guarantor: number;
27
+ PayPlanDate: string;
28
+ APR: number;
29
+ Note: string;
30
+ PlanNum: number;
31
+ CompletedAmt: number;
32
+ InsSubNum: number;
33
+ PaySchedule: number;
34
+ NumberOfPayments: number;
35
+ PayAmt: number;
36
+ DownPayment: number;
37
+ IsClosed: boolean;
38
+ Signature: string;
39
+ SigIsTopaz: boolean;
40
+ PlanCategory: number;
41
+ IsDynamic: boolean;
42
+ ChargeFrequency: ChargeFrequency;
43
+ DatePayPlanStart: string;
44
+ DynamicPayPlanTPOption: DynamicPayPlanTPOption;
45
+ DateInterestStart: string;
46
+ IsLocked: boolean;
47
+ SheetDefNum: number;
48
+ }
49
+ /** GET /payplans (multiple) query params */
50
+ export interface GetPayPlansParams {
51
+ /** Patient number - mutually exclusive with Guarantor */
52
+ PatNum?: number;
53
+ /** Guarantor number - mutually exclusive with PatNum */
54
+ Guarantor?: number;
55
+ }
56
+ /** GET /payplans/{PayPlanNum} - path parameter only */
57
+ export interface GetPayPlanByIdParams {
58
+ PayPlanNum: number;
59
+ }
60
+ /**
61
+ * POST /payplans/Dynamic body
62
+ * Creates a dynamic payment plan
63
+ * Required: PatNum, (PayAmt OR NumberOfPayments), (procNums OR adjNums)
64
+ */
65
+ export interface CreateDynamicPayPlanParams {
66
+ PatNum: number;
67
+ PayAmt?: number;
68
+ NumberOfPayments?: number;
69
+ procNums?: number[];
70
+ adjNums?: number[];
71
+ Guarantor?: number;
72
+ PayPlanDate?: string;
73
+ APR?: number;
74
+ DownPayment?: number;
75
+ Note?: string;
76
+ PlanCategory?: number;
77
+ ChargeFrequency?: ChargeFrequency;
78
+ DatePayPlanStart?: string;
79
+ DateInterestStart?: string;
80
+ IsLocked?: boolean;
81
+ DynamicPayPlanTPOption?: DynamicPayPlanTPOption;
82
+ }
83
+ /**
84
+ * POST /payplans body (DEPRECATED as of 23.3.1)
85
+ * Use CreateDynamicPayPlanParams instead
86
+ * Creates an old-style patient payment plan
87
+ */
88
+ export interface CreatePayPlanParams {
89
+ PatNum: number;
90
+ useEstBalance?: boolean;
91
+ principalAmount?: number;
92
+ PayAmt?: number;
93
+ NumberOfPayments?: number;
94
+ Guarantor?: number;
95
+ PayPlanDate?: string;
96
+ APR?: number;
97
+ DownPayment?: number;
98
+ Note?: string;
99
+ PlanCategory?: number;
100
+ ChargeFrequency?: ChargeFrequency;
101
+ DatePayPlanStart?: string;
102
+ DateInterestStart?: string;
103
+ }
104
+ /**
105
+ * PUT /payplans/{PayPlanNum}/Close
106
+ * Closes a payment plan and charges outstanding interest
107
+ */
108
+ export interface ClosePayPlanParams {
109
+ PayPlanNum: number;
110
+ }
111
+ /**
112
+ * PUT /payplans/{PayPlanNum}/Dynamic
113
+ * Updates a dynamic payment plan
114
+ * All fields optional except PayPlanNum
115
+ */
116
+ export interface UpdateDynamicPayPlanParams {
117
+ PayPlanNum: number;
118
+ PayAmt?: number;
119
+ NumberOfPayments?: number;
120
+ Guarantor?: number;
121
+ PayPlanDate?: string;
122
+ APR?: number;
123
+ Note?: string;
124
+ PlanCategory?: number;
125
+ ChargeFrequency?: ChargeFrequency;
126
+ DatePayPlanStart?: string;
127
+ DateInterestStart?: string;
128
+ IsLocked?: boolean;
129
+ SheetDefNum?: number;
130
+ }
131
+ /** Minimal helper to detect "zero" dates in responses */
132
+ export declare const isZeroDate: (d?: string | null) => boolean;
133
+ /** Narrow helper for timestamps that can be zeroed by the API */
134
+ export declare const isZeroDateTime: (d?: string | null) => boolean;
135
+ /** Runtime guard for ChargeFrequency values */
136
+ export declare const isChargeFrequency: (s: string) => s is ChargeFrequency;
137
+ /** Runtime guard for DynamicPayPlanTPOption values */
138
+ export declare const isDynamicPayPlanTPOption: (t: string) => t is DynamicPayPlanTPOption;
139
+ /** Helper to determine if a PayPlan is an insurance payment plan */
140
+ export declare const isInsurancePayPlan: (payPlan: PayPlan) => boolean;
141
+ /** Helper to determine if a PayPlan is a patient payment plan */
142
+ export declare const isPatientPayPlan: (payPlan: PayPlan) => boolean;
143
+ //# sourceMappingURL=payPlanTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payPlanTypes.d.ts","sourceRoot":"","sources":["../../src/types/payPlanTypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * PayPlan types for Open Dental REST API
4
+ * Spec refs:
5
+ * - https://www.opendental.com/site/apipayplans.html
6
+ *
7
+ * Notes:
8
+ * - Dates are strings from the API (yyyy-MM-dd or yyyy-MM-dd HH:mm:ss).
9
+ * - Prior to 23.3.1, Open Dental users had the ability to create both Patient Payment Plans and Dynamic Payment Plans.
10
+ * - In version 23.3.1, Open Dental deprecated Patient Payment Plans, renaming them to Old Payment Plans in the UI.
11
+ * - The PayPlans POST (create) method was also deprecated at that time.
12
+ * - Dynamic Payment Plans are now referred to simply as Payment Plans in Open Dental.
13
+ * - Insurance payment plans have PlanNum and InsSubNum values greater than zero
14
+ * @see https://www.opendental.com/site/apipayplans.html
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.isPatientPayPlan = exports.isInsurancePayPlan = exports.isDynamicPayPlanTPOption = exports.isChargeFrequency = exports.isZeroDateTime = exports.isZeroDate = void 0;
18
+ /** Minimal helper to detect "zero" dates in responses */
19
+ const isZeroDate = (d) => !d || d === "0001-01-01";
20
+ exports.isZeroDate = isZeroDate;
21
+ /** Narrow helper for timestamps that can be zeroed by the API */
22
+ const isZeroDateTime = (d) => !d || d.startsWith("0001-01-01");
23
+ exports.isZeroDateTime = isZeroDateTime;
24
+ /** Runtime guard for ChargeFrequency values */
25
+ const isChargeFrequency = (s) => ["Weekly", "EveryOtherWeek", "Monthly", "Quarterly", "OrdinalWeekday"].includes(s);
26
+ exports.isChargeFrequency = isChargeFrequency;
27
+ /** Runtime guard for DynamicPayPlanTPOption values */
28
+ const isDynamicPayPlanTPOption = (t) => ["AwaitComplete", "TreatAsComplete", "None"].includes(t);
29
+ exports.isDynamicPayPlanTPOption = isDynamicPayPlanTPOption;
30
+ /** Helper to determine if a PayPlan is an insurance payment plan */
31
+ const isInsurancePayPlan = (payPlan) => payPlan.PlanNum > 0 && payPlan.InsSubNum > 0;
32
+ exports.isInsurancePayPlan = isInsurancePayPlan;
33
+ /** Helper to determine if a PayPlan is a patient payment plan */
34
+ const isPatientPayPlan = (payPlan) => payPlan.PlanNum === 0 && payPlan.InsSubNum === 0;
35
+ exports.isPatientPayPlan = isPatientPayPlan;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rinse-dental/open-dental",
3
- "version": "3.2.2",
3
+ "version": "3.3.1",
4
4
  "description": "A TypeScript library for easily accessing Open Dental APIs.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/release.sh CHANGED
@@ -41,7 +41,7 @@ gh release create "$NEW_TAG" --title "Release $NEW_TAG" --notes "$COMMIT_MSG"
41
41
 
42
42
 
43
43
  #example ./release.sh "updated axios error return" patch
44
- #example ./release.sh "Added InsVerifies" minor
44
+ #example ./release.sh "Added Claims api" minor
45
45
  #example ./release.sh "Some commit message" major
46
46
 
47
47
  #./release.sh "Added readme" patch
@@ -0,0 +1,49 @@
1
+ // src/api/payPlanCharges.ts
2
+ import HttpClient from "../utils/httpClient";
3
+ import { PayPlanCharge } from "../types/payPlanChargeTypes";
4
+
5
+ /** Utility to remove undefined fields from objects (for API payloads) */
6
+ const clean = (obj: Record<string, any>): Record<string, any> => {
7
+ return Object.fromEntries(
8
+ Object.entries(obj).filter(([_, v]) => v !== undefined)
9
+ );
10
+ };
11
+
12
+ export default class PayPlanCharges {
13
+ private httpClient: HttpClient;
14
+
15
+ constructor(httpClient: HttpClient) {
16
+ this.httpClient = httpClient;
17
+ }
18
+
19
+ /**
20
+ * Fetch all payment plan charges for a specified payment plan.
21
+ * Mirrors GET /payplancharges
22
+ * Added in version 23.2.28
23
+ *
24
+ * @see https://www.opendental.com/site/apipayplancharges.html
25
+ * @param params - Query parameters: { PayPlanNum: number } (required)
26
+ */
27
+ public async getPayPlanCharges(
28
+ params: { PayPlanNum: number }
29
+ ): Promise<PayPlanCharge[]> {
30
+ // Validate required parameter
31
+ if (!params || typeof params !== "object") {
32
+ throw new Error(
33
+ "Invalid parameters: getPayPlanCharges requires a params object."
34
+ );
35
+ }
36
+
37
+ if (!params.PayPlanNum || typeof params.PayPlanNum !== "number") {
38
+ throw new Error(
39
+ "Invalid parameter: PayPlanNum is required and must be a number."
40
+ );
41
+ }
42
+
43
+ const query = clean({
44
+ PayPlanNum: params.PayPlanNum,
45
+ });
46
+
47
+ return this.httpClient.get<PayPlanCharge[]>("/payplancharges", query);
48
+ }
49
+ }
@@ -0,0 +1,274 @@
1
+ // src/api/payPlans.ts
2
+ import HttpClient from "../utils/httpClient";
3
+ import {
4
+ PayPlan,
5
+ GetPayPlanByIdParams,
6
+ CreateDynamicPayPlanParams,
7
+ CreatePayPlanParams,
8
+ ClosePayPlanParams,
9
+ UpdateDynamicPayPlanParams,
10
+ } from "../types/payPlanTypes";
11
+
12
+ /** Utility to remove undefined fields from objects (for API payloads) */
13
+ const clean = (obj: Record<string, any>): Record<string, any> => {
14
+ return Object.fromEntries(
15
+ Object.entries(obj).filter(([_, v]) => v !== undefined)
16
+ );
17
+ };
18
+
19
+ export default class PayPlans {
20
+ private httpClient: HttpClient;
21
+
22
+ constructor(httpClient: HttpClient) {
23
+ this.httpClient = httpClient;
24
+ }
25
+
26
+ /**
27
+ * Fetch multiple payment plans with filtering by PatNum or Guarantor.
28
+ * Mirrors GET /payplans
29
+ * @see https://www.opendental.com/site/apipayplans.html
30
+ * @param params - Query parameters: { PatNum: number } OR { Guarantor: number } (mutually exclusive, one required)
31
+ */
32
+ public async getPayPlans(params: { PatNum: number; Guarantor?: never } | { Guarantor: number; PatNum?: never }): Promise<PayPlan[]> {
33
+ // API requires either PatNum OR Guarantor, not both
34
+ if ('PatNum' in params && 'Guarantor' in params && params.PatNum && params.Guarantor) {
35
+ throw new Error(
36
+ "Invalid parameters: PatNum and Guarantor are mutually exclusive. Provide only one."
37
+ );
38
+ }
39
+
40
+ if (!('PatNum' in params) && !('Guarantor' in params)) {
41
+ throw new Error(
42
+ "Invalid parameters: Either PatNum or Guarantor is required."
43
+ );
44
+ }
45
+
46
+ const query = clean({
47
+ PatNum: 'PatNum' in params ? params.PatNum : undefined,
48
+ Guarantor: 'Guarantor' in params ? params.Guarantor : undefined,
49
+ });
50
+
51
+ return this.httpClient.get<PayPlan[]>("/payplans", query);
52
+ }
53
+
54
+ /**
55
+ * Fetch a single payment plan by PayPlanNum.
56
+ * Mirrors GET /payplans/{PayPlanNum}
57
+ * Added in version 24.4.30
58
+ */
59
+ public async getPayPlan({
60
+ PayPlanNum,
61
+ }: GetPayPlanByIdParams): Promise<PayPlan> {
62
+ if (!PayPlanNum || typeof PayPlanNum !== "number") {
63
+ throw new Error("Invalid parameter: PayPlanNum must be a valid number.");
64
+ }
65
+ return this.httpClient.get<PayPlan>(`/payplans/${PayPlanNum}`);
66
+ }
67
+
68
+ /**
69
+ * Create a new dynamic payment plan.
70
+ * Mirrors POST /payplans/Dynamic
71
+ * Added in version 22.2.22
72
+ *
73
+ * Required:
74
+ * - PatNum
75
+ * - PayAmt OR NumberOfPayments
76
+ * - procNums and/or adjNums (at least one must have items)
77
+ *
78
+ * Note: If APR > 0, IsLocked may be required to be true (depending on preferences)
79
+ */
80
+ public async createDynamicPayPlan(
81
+ data: CreateDynamicPayPlanParams
82
+ ): Promise<PayPlan> {
83
+ // Validate required fields
84
+ if (!data || typeof data !== "object") {
85
+ throw new Error(
86
+ "Invalid payload: createDynamicPayPlan requires a request body."
87
+ );
88
+ }
89
+
90
+ if (!data.PatNum || typeof data.PatNum !== "number") {
91
+ throw new Error(
92
+ "Invalid payload: PatNum is required and must be a number."
93
+ );
94
+ }
95
+
96
+ // Either PayAmt OR NumberOfPayments is required
97
+ if (!data.PayAmt && !data.NumberOfPayments) {
98
+ throw new Error(
99
+ "Invalid payload: Either PayAmt or NumberOfPayments is required."
100
+ );
101
+ }
102
+
103
+ // At least one of procNums or adjNums must be provided and non-empty
104
+ const hasProcNums = Array.isArray(data.procNums) && data.procNums.length > 0;
105
+ const hasAdjNums = Array.isArray(data.adjNums) && data.adjNums.length > 0;
106
+
107
+ if (!hasProcNums && !hasAdjNums) {
108
+ throw new Error(
109
+ "Invalid payload: At least one of procNums or adjNums must be a non-empty array."
110
+ );
111
+ }
112
+
113
+ // Validate APR and IsLocked relationship
114
+ if (data.APR && data.APR > 0 && data.IsLocked === false) {
115
+ console.warn(
116
+ "Warning: When APR > 0, IsLocked may need to be true depending on Open Dental preferences."
117
+ );
118
+ }
119
+
120
+ const payload = clean({
121
+ PatNum: data.PatNum,
122
+ PayAmt: data.PayAmt,
123
+ NumberOfPayments: data.NumberOfPayments,
124
+ procNums: data.procNums,
125
+ adjNums: data.adjNums,
126
+ Guarantor: data.Guarantor,
127
+ PayPlanDate: data.PayPlanDate,
128
+ APR: data.APR,
129
+ DownPayment: data.DownPayment,
130
+ Note: data.Note,
131
+ PlanCategory: data.PlanCategory,
132
+ ChargeFrequency: data.ChargeFrequency,
133
+ DatePayPlanStart: data.DatePayPlanStart,
134
+ DateInterestStart: data.DateInterestStart,
135
+ IsLocked: data.IsLocked,
136
+ DynamicPayPlanTPOption: data.DynamicPayPlanTPOption,
137
+ });
138
+
139
+ return this.httpClient.post<PayPlan>("/payplans/Dynamic", payload);
140
+ }
141
+
142
+ /**
143
+ * Create a patient payment plan (DEPRECATED as of 23.3.1).
144
+ * Mirrors POST /payplans
145
+ * Added in version 22.4.39, deprecated in 23.3.1
146
+ *
147
+ * @deprecated Use createDynamicPayPlan instead. This endpoint returns 410 Gone in newer versions.
148
+ *
149
+ * Required:
150
+ * - PatNum
151
+ * - useEstBalance OR principalAmount
152
+ * - PayAmt OR NumberOfPayments
153
+ */
154
+ public async createPayPlan(data: CreatePayPlanParams): Promise<PayPlan> {
155
+ console.warn(
156
+ "Warning: createPayPlan is deprecated as of version 23.3.1. Use createDynamicPayPlan instead."
157
+ );
158
+
159
+ // Validate required fields
160
+ if (!data || typeof data !== "object") {
161
+ throw new Error(
162
+ "Invalid payload: createPayPlan requires a request body."
163
+ );
164
+ }
165
+
166
+ if (!data.PatNum || typeof data.PatNum !== "number") {
167
+ throw new Error(
168
+ "Invalid payload: PatNum is required and must be a number."
169
+ );
170
+ }
171
+
172
+ // Either useEstBalance OR principalAmount is required
173
+ if (
174
+ data.useEstBalance === undefined &&
175
+ data.principalAmount === undefined
176
+ ) {
177
+ throw new Error(
178
+ "Invalid payload: Either useEstBalance or principalAmount is required."
179
+ );
180
+ }
181
+
182
+ // Either PayAmt OR NumberOfPayments is required
183
+ if (!data.PayAmt && !data.NumberOfPayments) {
184
+ throw new Error(
185
+ "Invalid payload: Either PayAmt or NumberOfPayments is required."
186
+ );
187
+ }
188
+
189
+ const payload = clean({
190
+ PatNum: data.PatNum,
191
+ useEstBalance: data.useEstBalance,
192
+ principalAmount: data.principalAmount,
193
+ PayAmt: data.PayAmt,
194
+ NumberOfPayments: data.NumberOfPayments,
195
+ Guarantor: data.Guarantor,
196
+ PayPlanDate: data.PayPlanDate,
197
+ APR: data.APR,
198
+ DownPayment: data.DownPayment,
199
+ Note: data.Note,
200
+ PlanCategory: data.PlanCategory,
201
+ ChargeFrequency: data.ChargeFrequency,
202
+ DatePayPlanStart: data.DatePayPlanStart,
203
+ DateInterestStart: data.DateInterestStart,
204
+ });
205
+
206
+ return this.httpClient.post<PayPlan>("/payplans", payload);
207
+ }
208
+
209
+ /**
210
+ * Close a payment plan and charge any outstanding interest.
211
+ * Mirrors PUT /payplans/{PayPlanNum}/Close
212
+ * Added in version 22.4.39
213
+ */
214
+ public async closePayPlan({
215
+ PayPlanNum,
216
+ }: ClosePayPlanParams): Promise<PayPlan> {
217
+ if (!PayPlanNum || typeof PayPlanNum !== "number") {
218
+ throw new Error(
219
+ "Invalid parameter: PayPlanNum is required and must be a number."
220
+ );
221
+ }
222
+
223
+ return this.httpClient.put<PayPlan>(
224
+ `/payplans/${PayPlanNum}/Close`,
225
+ {}
226
+ );
227
+ }
228
+
229
+ /**
230
+ * Update an existing dynamic payment plan.
231
+ * Mirrors PUT /payplans/{PayPlanNum}/Dynamic
232
+ * Added in version 25.3.31
233
+ *
234
+ * All fields except PayPlanNum are optional; send only what you intend to change.
235
+ */
236
+ public async updateDynamicPayPlan(
237
+ data: UpdateDynamicPayPlanParams
238
+ ): Promise<PayPlan> {
239
+ if (!data?.PayPlanNum || typeof data.PayPlanNum !== "number") {
240
+ throw new Error(
241
+ "Invalid payload: PayPlanNum is required and must be a number."
242
+ );
243
+ }
244
+
245
+ // Validate APR and IsLocked relationship
246
+ if (data.APR && data.APR > 0 && data.IsLocked === false) {
247
+ console.warn(
248
+ "Warning: When APR > 0, IsLocked may need to be true depending on Open Dental preferences."
249
+ );
250
+ }
251
+
252
+ const { PayPlanNum, ...updateFields } = data;
253
+
254
+ const payload = clean({
255
+ PayAmt: updateFields.PayAmt,
256
+ NumberOfPayments: updateFields.NumberOfPayments,
257
+ Guarantor: updateFields.Guarantor,
258
+ PayPlanDate: updateFields.PayPlanDate,
259
+ APR: updateFields.APR,
260
+ Note: updateFields.Note,
261
+ PlanCategory: updateFields.PlanCategory,
262
+ ChargeFrequency: updateFields.ChargeFrequency,
263
+ DatePayPlanStart: updateFields.DatePayPlanStart,
264
+ DateInterestStart: updateFields.DateInterestStart,
265
+ IsLocked: updateFields.IsLocked,
266
+ SheetDefNum: updateFields.SheetDefNum,
267
+ });
268
+
269
+ return this.httpClient.put<PayPlan>(
270
+ `/payplans/${PayPlanNum}/Dynamic`,
271
+ payload
272
+ );
273
+ }
274
+ }
package/src/openDental.ts CHANGED
@@ -13,6 +13,8 @@ import Providers from "./api/providers";
13
13
  import Operatories from "./api/operatories";
14
14
  import Payments from "./api/payments";
15
15
  import PaySplits from "./api/paySplits";
16
+ import PayPlans from "./api/payPlans";
17
+ import PayPlanCharges from "./api/payPlanCharges";
16
18
  import Definitions from "./api/definitions";
17
19
  import DiscountPlans from "./api/discountPlans";
18
20
  import DiscountPlanSubs from "./api/discountPlanSubs";
@@ -350,6 +352,26 @@ class OpenDental {
350
352
  return new PaySplits(this.httpClient);
351
353
  }
352
354
 
355
+ /**
356
+ * Create a new instance of the PayPlans API.
357
+ */
358
+ public static PayPlans() {
359
+ if (!this.httpClient) {
360
+ throw new Error("OpenDental not initialized. Call OpenDental.initialize() first.");
361
+ }
362
+ return new PayPlans(this.httpClient);
363
+ }
364
+
365
+ /**
366
+ * Create a new instance of the PayPlanCharges API.
367
+ */
368
+ public static PayPlanCharges() {
369
+ if (!this.httpClient) {
370
+ throw new Error("OpenDental not initialized. Call OpenDental.initialize() first.");
371
+ }
372
+ return new PayPlanCharges(this.httpClient);
373
+ }
374
+
353
375
  /**
354
376
  * Create a new instance of the Fees API.
355
377
  */
@@ -0,0 +1,77 @@
1
+ /**
2
+ * PayPlanCharge types for Open Dental REST API
3
+ * Spec refs:
4
+ * - https://www.opendental.com/site/apipayplancharges.html
5
+ *
6
+ * Notes:
7
+ * - Dates are strings from the API (yyyy-MM-dd or yyyy-MM-dd HH:mm:ss).
8
+ * - PayPlanCharges represent individual charges within a payment plan
9
+ * - Each charge can be a debit (payment due) or credit (payment received)
10
+ * @see https://www.opendental.com/site/apipayplancharges.html
11
+ */
12
+
13
+ /** Charge type for payment plan charges */
14
+ export type ChargeType = "Debit" | "Credit";
15
+
16
+ /** Link type for payment plan charges */
17
+ export type LinkType =
18
+ | "Procedure"
19
+ | "Adjustment"
20
+ | "Payment"
21
+ | "None"
22
+ | string; // Open to allow for other types
23
+
24
+ /**
25
+ * Represents a PayPlanCharge returned by the Open Dental API.
26
+ * Mirrors the response payload of GET endpoints.
27
+ */
28
+ export interface PayPlanCharge {
29
+ PayPlanChargeNum: number; // PK - Unique charge identifier
30
+ PayPlanNum: number; // FK to payplan.PayPlanNum
31
+ Guarantor: number; // FK to patient.PatNum (guarantor)
32
+ PatNum: number; // FK to patient.PatNum
33
+ ChargeDate: string; // "yyyy-MM-dd" - Date of charge
34
+ Principal: number; // Principal amount (decimal)
35
+ Interest: number; // Interest amount (decimal)
36
+ Note: string; // Optional note for the charge
37
+ ProvNum: number; // FK to provider.ProvNum
38
+ ClinicNum: number; // FK to clinic.ClinicNum
39
+ ChargeType: ChargeType; // Type of charge ("Debit" | "Credit")
40
+ ProcNum: number; // FK to procedurelog.ProcNum
41
+ SecDateTEntry: string; // "yyyy-MM-dd HH:mm:ss" - Entry timestamp
42
+ SecDateTEdit: string; // "yyyy-MM-dd HH:mm:ss" - Last edit timestamp
43
+ StatementNum: number; // FK to statement.StatementNum
44
+ FKey: number; // Foreign key reference (context-dependent)
45
+ LinkType: LinkType; // Link type (e.g., "Procedure", "Adjustment")
46
+ IsOffset: boolean; // Whether this is an offset charge
47
+ }
48
+
49
+ /** GET /payplancharges query params */
50
+ export interface GetPayPlanChargesParams {
51
+ /** Payment plan number - Required */
52
+ PayPlanNum: number;
53
+ }
54
+
55
+ /** Minimal helper to detect "zero" dates in responses */
56
+ export const isZeroDate = (d?: string | null): boolean =>
57
+ !d || d === "0001-01-01";
58
+
59
+ /** Narrow helper for timestamps that can be zeroed by the API */
60
+ export const isZeroDateTime = (d?: string | null): boolean =>
61
+ !d || d.startsWith("0001-01-01");
62
+
63
+ /** Runtime guard for ChargeType values */
64
+ export const isChargeType = (s: string): s is ChargeType =>
65
+ ["Debit", "Credit"].includes(s);
66
+
67
+ /** Helper to determine if a charge is a debit (payment due) */
68
+ export const isDebitCharge = (charge: PayPlanCharge): boolean =>
69
+ charge.ChargeType === "Debit";
70
+
71
+ /** Helper to determine if a charge is a credit (payment received) */
72
+ export const isCreditCharge = (charge: PayPlanCharge): boolean =>
73
+ charge.ChargeType === "Credit";
74
+
75
+ /** Helper to calculate total charge amount (principal + interest) */
76
+ export const getTotalChargeAmount = (charge: PayPlanCharge): number =>
77
+ charge.Principal + charge.Interest;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * PayPlan types for Open Dental REST API
3
+ * Spec refs:
4
+ * - https://www.opendental.com/site/apipayplans.html
5
+ *
6
+ * Notes:
7
+ * - Dates are strings from the API (yyyy-MM-dd or yyyy-MM-dd HH:mm:ss).
8
+ * - Prior to 23.3.1, Open Dental users had the ability to create both Patient Payment Plans and Dynamic Payment Plans.
9
+ * - In version 23.3.1, Open Dental deprecated Patient Payment Plans, renaming them to Old Payment Plans in the UI.
10
+ * - The PayPlans POST (create) method was also deprecated at that time.
11
+ * - Dynamic Payment Plans are now referred to simply as Payment Plans in Open Dental.
12
+ * - Insurance payment plans have PlanNum and InsSubNum values greater than zero
13
+ * @see https://www.opendental.com/site/apipayplans.html
14
+ */
15
+
16
+ /** Charge frequency for payment plans */
17
+ export type ChargeFrequency =
18
+ | "Weekly"
19
+ | "EveryOtherWeek"
20
+ | "Monthly"
21
+ | "Quarterly"
22
+ | "OrdinalWeekday";
23
+
24
+ /** Dynamic payment plan treatment plan option */
25
+ export type DynamicPayPlanTPOption =
26
+ | "AwaitComplete"
27
+ | "TreatAsComplete"
28
+ | "None";
29
+
30
+ /**
31
+ * Represents a PayPlan returned by the Open Dental API.
32
+ * Mirrors the response payload of GET/POST/PUT endpoints.
33
+ */
34
+ export interface PayPlan {
35
+ PayPlanNum: number; // PK
36
+ PatNum: number; // FK to patient.PatNum
37
+ Guarantor: number; // FK to patient.PatNum (guarantor)
38
+ PayPlanDate: string; // "yyyy-MM-dd" - Date of agreement
39
+ APR: number; // Annual percentage rate (decimal)
40
+ Note: string; // Payment plan note
41
+ PlanNum: number; // FK to insplan.PlanNum (0 for patient plans)
42
+ CompletedAmt: number; // Amount completed
43
+ InsSubNum: number; // FK to inssub.InsSubNum (0 for patient plans)
44
+ PaySchedule: number; // FK to schedule (if applicable)
45
+ NumberOfPayments: number; // Total number of payments
46
+ PayAmt: number; // Amount per payment
47
+ DownPayment: number; // Down payment amount
48
+ IsClosed: boolean; // Whether plan is closed
49
+ Signature: string; // Digital signature data
50
+ SigIsTopaz: boolean; // Whether signature is Topaz format
51
+ PlanCategory: number; // DefNum for plan category
52
+ IsDynamic: boolean; // Whether this is a dynamic payment plan
53
+ ChargeFrequency: ChargeFrequency; // Payment frequency
54
+ DatePayPlanStart: string; // "yyyy-MM-dd" - First payment due date
55
+ DynamicPayPlanTPOption: DynamicPayPlanTPOption; // TP option for dynamic plans
56
+ DateInterestStart: string; // "yyyy-MM-dd" - When interest starts accruing
57
+ IsLocked: boolean; // Lock status (required true if APR > 0)
58
+ SheetDefNum: number; // FK to sheetdef.SheetDefNum (custom sheet)
59
+ }
60
+
61
+ /** GET /payplans (multiple) query params */
62
+ export interface GetPayPlansParams {
63
+ /** Patient number - mutually exclusive with Guarantor */
64
+ PatNum?: number;
65
+ /** Guarantor number - mutually exclusive with PatNum */
66
+ Guarantor?: number;
67
+ }
68
+
69
+ /** GET /payplans/{PayPlanNum} - path parameter only */
70
+ export interface GetPayPlanByIdParams {
71
+ PayPlanNum: number;
72
+ }
73
+
74
+ /**
75
+ * POST /payplans/Dynamic body
76
+ * Creates a dynamic payment plan
77
+ * Required: PatNum, (PayAmt OR NumberOfPayments), (procNums OR adjNums)
78
+ */
79
+ export interface CreateDynamicPayPlanParams {
80
+ PatNum: number; // Required
81
+ PayAmt?: number; // Required if NumberOfPayments not provided
82
+ NumberOfPayments?: number; // Required if PayAmt not provided
83
+ procNums?: number[]; // Array of procedure numbers
84
+ adjNums?: number[]; // Array of adjustment numbers
85
+ Guarantor?: number; // FK to patient.PatNum
86
+ PayPlanDate?: string; // "yyyy-MM-dd" - defaults to today
87
+ APR?: number; // Annual percentage rate
88
+ DownPayment?: number; // Down payment amount
89
+ Note?: string; // Payment plan note
90
+ PlanCategory?: number; // DefNum for plan category
91
+ ChargeFrequency?: ChargeFrequency; // Payment frequency
92
+ DatePayPlanStart?: string; // "yyyy-MM-dd" - first payment due date
93
+ DateInterestStart?: string; // "yyyy-MM-dd" - when interest starts
94
+ IsLocked?: boolean; // Lock status (required true if APR > 0)
95
+ DynamicPayPlanTPOption?: DynamicPayPlanTPOption; // TP option
96
+ }
97
+
98
+ /**
99
+ * POST /payplans body (DEPRECATED as of 23.3.1)
100
+ * Use CreateDynamicPayPlanParams instead
101
+ * Creates an old-style patient payment plan
102
+ */
103
+ export interface CreatePayPlanParams {
104
+ PatNum: number; // Required
105
+ useEstBalance?: boolean; // Use estimated balance
106
+ principalAmount?: number; // Principal amount
107
+ PayAmt?: number; // Required if NumberOfPayments not provided
108
+ NumberOfPayments?: number; // Required if PayAmt not provided
109
+ Guarantor?: number; // FK to patient.PatNum
110
+ PayPlanDate?: string; // "yyyy-MM-dd"
111
+ APR?: number; // Annual percentage rate
112
+ DownPayment?: number; // Down payment amount
113
+ Note?: string; // Payment plan note
114
+ PlanCategory?: number; // DefNum for plan category
115
+ ChargeFrequency?: ChargeFrequency; // Payment frequency
116
+ DatePayPlanStart?: string; // "yyyy-MM-dd"
117
+ DateInterestStart?: string; // "yyyy-MM-dd"
118
+ }
119
+
120
+ /**
121
+ * PUT /payplans/{PayPlanNum}/Close
122
+ * Closes a payment plan and charges outstanding interest
123
+ */
124
+ export interface ClosePayPlanParams {
125
+ PayPlanNum: number; // in URL
126
+ }
127
+
128
+ /**
129
+ * PUT /payplans/{PayPlanNum}/Dynamic
130
+ * Updates a dynamic payment plan
131
+ * All fields optional except PayPlanNum
132
+ */
133
+ export interface UpdateDynamicPayPlanParams {
134
+ PayPlanNum: number; // Required (in URL)
135
+ PayAmt?: number; // Amount per payment
136
+ NumberOfPayments?: number; // Total number of payments
137
+ Guarantor?: number; // FK to patient.PatNum
138
+ PayPlanDate?: string; // "yyyy-MM-dd"
139
+ APR?: number; // Annual percentage rate
140
+ Note?: string; // Payment plan note
141
+ PlanCategory?: number; // DefNum for plan category
142
+ ChargeFrequency?: ChargeFrequency; // Payment frequency
143
+ DatePayPlanStart?: string; // "yyyy-MM-dd"
144
+ DateInterestStart?: string; // "yyyy-MM-dd"
145
+ IsLocked?: boolean; // Lock status
146
+ SheetDefNum?: number; // FK to sheetdef.SheetDefNum
147
+ }
148
+
149
+ /** Minimal helper to detect "zero" dates in responses */
150
+ export const isZeroDate = (d?: string | null): boolean =>
151
+ !d || d === "0001-01-01";
152
+
153
+ /** Narrow helper for timestamps that can be zeroed by the API */
154
+ export const isZeroDateTime = (d?: string | null): boolean =>
155
+ !d || d.startsWith("0001-01-01");
156
+
157
+ /** Runtime guard for ChargeFrequency values */
158
+ export const isChargeFrequency = (s: string): s is ChargeFrequency =>
159
+ ["Weekly", "EveryOtherWeek", "Monthly", "Quarterly", "OrdinalWeekday"].includes(s);
160
+
161
+ /** Runtime guard for DynamicPayPlanTPOption values */
162
+ export const isDynamicPayPlanTPOption = (t: string): t is DynamicPayPlanTPOption =>
163
+ ["AwaitComplete", "TreatAsComplete", "None"].includes(t);
164
+
165
+ /** Helper to determine if a PayPlan is an insurance payment plan */
166
+ export const isInsurancePayPlan = (payPlan: PayPlan): boolean =>
167
+ payPlan.PlanNum > 0 && payPlan.InsSubNum > 0;
168
+
169
+ /** Helper to determine if a PayPlan is a patient payment plan */
170
+ export const isPatientPayPlan = (payPlan: PayPlan): boolean =>
171
+ payPlan.PlanNum === 0 && payPlan.InsSubNum === 0;