av6-utils 1.0.0
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.
- package/.prettierignore +4 -0
- package/.prettierrc +6 -0
- package/README.md +234 -0
- package/dist/index.d.mts +113 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.js +363 -0
- package/dist/index.mjs +327 -0
- package/package.json +22 -0
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# AV6 Core
|
|
2
|
+
|
|
3
|
+
A comprehensive utility library for AV6 Node.js projects, providing common functionality for data operations, caching, and Excel handling.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dynamic Data Operations**: Flexible search, fetch, and CRUD operations with support for dynamic models
|
|
8
|
+
- **Caching Support**: Built-in Redis caching for improved performance
|
|
9
|
+
- **Excel Import/Export**: Seamless Excel file handling for data import and export
|
|
10
|
+
- **Pagination**: Built-in pagination for search results
|
|
11
|
+
- **DTO Mapping**: Support for Data Transfer Object mapping
|
|
12
|
+
- **Type Safety**: Written in TypeScript with comprehensive type definitions
|
|
13
|
+
- **UIN Config Management**: Unique Identification Number generation with configurable segments and automatic reset policies
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install av6-core
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Basic Setup
|
|
24
|
+
|
|
25
|
+
To use the library, you need to provide the necessary dependencies:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { commonService, Deps } from 'av6-core';
|
|
29
|
+
import { PrismaClient } from '@prisma/client';
|
|
30
|
+
import winston from 'winston';
|
|
31
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
32
|
+
|
|
33
|
+
// Initialize dependencies
|
|
34
|
+
const deps: Deps = {
|
|
35
|
+
config: {
|
|
36
|
+
REDIS_PREFIX: 'your-prefix:',
|
|
37
|
+
CACHE_KEY_NAME: 'your-cache-key'
|
|
38
|
+
},
|
|
39
|
+
mapper: {
|
|
40
|
+
dtoMapping: {}, // Your DTO mapping functions
|
|
41
|
+
mappingExport: {}, // Your export mapping functions
|
|
42
|
+
mappingImport: {}, // Your import mapping functions
|
|
43
|
+
},
|
|
44
|
+
helpers: {
|
|
45
|
+
generateErrorMessage: (type, ...variables) => {
|
|
46
|
+
// Your error message generation logic
|
|
47
|
+
},
|
|
48
|
+
ErrorHandler: class ErrorHandler extends Error {
|
|
49
|
+
// Your error handler implementation
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
logger: winston.createLogger({
|
|
53
|
+
// Your logger configuration
|
|
54
|
+
}),
|
|
55
|
+
cacheAdapter: {
|
|
56
|
+
// Your cache adapter implementation
|
|
57
|
+
},
|
|
58
|
+
requestStorage: new AsyncLocalStorage(),
|
|
59
|
+
db: new PrismaClient()
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Initialize the service
|
|
63
|
+
const service = commonService(deps);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Search Operations
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Perform a search operation
|
|
70
|
+
const searchResult = await service.search({
|
|
71
|
+
pageNo: 1,
|
|
72
|
+
pageSize: 10,
|
|
73
|
+
shortCode: 'USER',
|
|
74
|
+
searchColumns: ['name', 'email'],
|
|
75
|
+
searchText: 'john',
|
|
76
|
+
sortBy: 'createdAt',
|
|
77
|
+
sortDir: 'DESC',
|
|
78
|
+
shortCodeData: {
|
|
79
|
+
shortCode: 'USER',
|
|
80
|
+
tableName: 'user',
|
|
81
|
+
isDTO: true,
|
|
82
|
+
isCacheable: true
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Fetch Record
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Fetch a specific record
|
|
91
|
+
const record = await service.fetch({
|
|
92
|
+
shortCode: 'USER',
|
|
93
|
+
id: 123,
|
|
94
|
+
shortCodeData: {
|
|
95
|
+
shortCode: 'USER',
|
|
96
|
+
tableName: 'user',
|
|
97
|
+
isDTO: true,
|
|
98
|
+
isCacheable: true
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Excel Operations
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Import data from Excel
|
|
107
|
+
const importResult = await service.commonExcelImport({
|
|
108
|
+
shortCode: 'USER',
|
|
109
|
+
file: excelFile, // File object from upload
|
|
110
|
+
shortCodeData: {
|
|
111
|
+
shortCode: 'USER',
|
|
112
|
+
tableName: 'user'
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Export data to Excel
|
|
117
|
+
const workbook = await service.commonExcelExport({
|
|
118
|
+
shortCode: 'USER',
|
|
119
|
+
isSample: false,
|
|
120
|
+
shortCodeData: {
|
|
121
|
+
shortCode: 'USER',
|
|
122
|
+
tableName: 'user'
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### UIN Config Operations
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { uinConfigService, UinDeps, UIN_RESET_POLICY, UinShortCode } from 'av6-core';
|
|
131
|
+
|
|
132
|
+
// Initialize UIN Config dependencies
|
|
133
|
+
const uinDeps: UinDeps = {
|
|
134
|
+
config: {
|
|
135
|
+
REDIS_PREFIX: 'your-prefix:',
|
|
136
|
+
CACHE_KEY_NAME: 'your-cache-key'
|
|
137
|
+
},
|
|
138
|
+
helpers: {
|
|
139
|
+
generateErrorMessage: (type, ...variables) => {
|
|
140
|
+
// Your error message generation logic
|
|
141
|
+
},
|
|
142
|
+
ErrorHandler: class ErrorHandler extends Error {
|
|
143
|
+
// Your error handler implementation
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
logger: winston.createLogger({
|
|
147
|
+
// Your logger configuration
|
|
148
|
+
}),
|
|
149
|
+
cacheAdapter: {
|
|
150
|
+
// Your cache adapter implementation
|
|
151
|
+
},
|
|
152
|
+
requestStorage: new AsyncLocalStorage(),
|
|
153
|
+
db: new PrismaClient(),
|
|
154
|
+
prisma: PrismaNamespace,
|
|
155
|
+
shortCode: 'UIN_CONFIG',
|
|
156
|
+
cacheKey: 'uin-config'
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Initialize the UIN Config service
|
|
160
|
+
const uinService = uinConfigService(uinDeps);
|
|
161
|
+
|
|
162
|
+
// Create a new UIN Config
|
|
163
|
+
const newUinConfig = await uinService.createUINConfig({
|
|
164
|
+
shortCode: UinShortCode.INVOICE,
|
|
165
|
+
seqResetPolicy: UIN_RESET_POLICY.monthly,
|
|
166
|
+
description: 'Invoice number generator',
|
|
167
|
+
uinSegments: [
|
|
168
|
+
{ order: 1, type: 'text', value: 'INV-' },
|
|
169
|
+
{ order: 2, type: 'dateFormat', value: 'YYYYMM' },
|
|
170
|
+
{ order: 3, type: 'separator', value: '-' },
|
|
171
|
+
{ order: 4, type: 'sequenceNo', minSeqLength: 5 }
|
|
172
|
+
]
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Generate a UIN preview
|
|
176
|
+
const uinPreview = await uinService.previewUIN({
|
|
177
|
+
uinSegments: [
|
|
178
|
+
{ order: 1, type: 'text', value: 'INV-' },
|
|
179
|
+
{ order: 2, type: 'dateFormat', value: 'YYYYMM' },
|
|
180
|
+
{ order: 3, type: 'separator', value: '-' },
|
|
181
|
+
{ order: 4, type: 'sequenceNo', minSeqLength: 5 }
|
|
182
|
+
]
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Get a UIN Config by ID
|
|
186
|
+
const uinConfig = await uinService.getUINConfigById(1);
|
|
187
|
+
|
|
188
|
+
// Generate a new UIN
|
|
189
|
+
const generatedUin = await uinService.generateUIN(UinShortCode.INVOICE);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## API Reference
|
|
193
|
+
|
|
194
|
+
### Common Service
|
|
195
|
+
|
|
196
|
+
The library provides a comprehensive service for common operations:
|
|
197
|
+
|
|
198
|
+
- `search`: Search records with pagination and filtering
|
|
199
|
+
- `dropdownSearch`: Search records for dropdown components
|
|
200
|
+
- `fixedSearch`: Search with fixed criteria
|
|
201
|
+
- `fixedSearchWoPaginationService`: Search with fixed criteria without pagination
|
|
202
|
+
- `commonExcelService`: Generate Excel workbooks
|
|
203
|
+
- `fetch`: Fetch a specific record by ID
|
|
204
|
+
- `commonExcelImport`: Import data from Excel files
|
|
205
|
+
- `commonExcelExport`: Export data to Excel files
|
|
206
|
+
- `delete`: Delete a record
|
|
207
|
+
- `updateStatus`: Update the status of a record
|
|
208
|
+
- `fetchImageStream`: Fetch an image as a stream
|
|
209
|
+
|
|
210
|
+
### UIN Config Service
|
|
211
|
+
|
|
212
|
+
The library provides a service for managing Unique Identification Numbers:
|
|
213
|
+
|
|
214
|
+
- `createUINConfig`: Create a new UIN configuration
|
|
215
|
+
- `updateUINConfig`: Update an existing UIN configuration
|
|
216
|
+
- `getUINConfigById`: Retrieve a UIN configuration by ID
|
|
217
|
+
- `getAllUINConfig`: Retrieve all UIN configurations
|
|
218
|
+
- `deleteUINConfig`: Delete a UIN configuration
|
|
219
|
+
- `generateUIN`: Generate a new UIN based on a shortcode
|
|
220
|
+
- `previewUIN`: Preview a UIN based on segment configuration
|
|
221
|
+
|
|
222
|
+
### Utility Functions
|
|
223
|
+
|
|
224
|
+
The library also provides utility functions:
|
|
225
|
+
|
|
226
|
+
- `customOmit`: Omit specific keys from an object
|
|
227
|
+
- `objectTo2DArray`: Convert an object to a 2D array
|
|
228
|
+
- `toRelativeImageUrl`: Convert an absolute path to a relative image URL
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
ISC
|
|
233
|
+
|
|
234
|
+
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
declare function customOmit<T extends object, K extends keyof T>(obj: T, keys: K[]): {
|
|
2
|
+
rest: Omit<T, K>;
|
|
3
|
+
omitted: Pick<T, K>;
|
|
4
|
+
};
|
|
5
|
+
declare function getDynamicValue(obj: Record<string, any> | null | undefined, accessorKey: string): any | null;
|
|
6
|
+
declare function objectTo2DArray<T>(obj: Record<string, T>, maxCols: number): (string | T)[][];
|
|
7
|
+
declare function toNumberOrNull(value: unknown): number | null;
|
|
8
|
+
declare const getPattern: {
|
|
9
|
+
[key: string]: RegExp;
|
|
10
|
+
};
|
|
11
|
+
declare const interpolate: (template: string, vars: Record<string, unknown>) => string;
|
|
12
|
+
|
|
13
|
+
type DiscountMode = "PERCENTAGE" | "AMOUNT";
|
|
14
|
+
type AdditionalDiscountMode = "PERCENTAGE" | "AMOUNT";
|
|
15
|
+
type CoPayType = "PERCENTAGE" | "AMOUNT";
|
|
16
|
+
type TaxMethod = "NONE" | "INCLUSIVE" | "EXCLUSIVE";
|
|
17
|
+
type CoPayMode = "EXCLUSIVE-INCLUSIVE" | "PERCENTAGE-AMOUNT";
|
|
18
|
+
declare const RoundFormat: {
|
|
19
|
+
readonly ROUND: "ROUND";
|
|
20
|
+
readonly SPECIAL_ROUND: "SPECIAL_ROUND";
|
|
21
|
+
readonly TO_FIXED: "TO_FIXED";
|
|
22
|
+
readonly CEIL: "CEIL";
|
|
23
|
+
readonly FLOOR: "FLOOR";
|
|
24
|
+
readonly TRUNC: "TRUNC";
|
|
25
|
+
readonly NONE: "NONE";
|
|
26
|
+
};
|
|
27
|
+
type RoundFormat = (typeof RoundFormat)[keyof typeof RoundFormat];
|
|
28
|
+
interface ChildCalcInput {
|
|
29
|
+
qty: number;
|
|
30
|
+
rate: number;
|
|
31
|
+
otherCharge?: number;
|
|
32
|
+
addonPercentage?: number;
|
|
33
|
+
discountMode?: DiscountMode;
|
|
34
|
+
discountValue?: number;
|
|
35
|
+
taxMethod?: TaxMethod;
|
|
36
|
+
taxValue?: number;
|
|
37
|
+
/** Absolute insurer-covered at line level */
|
|
38
|
+
coPaymentType?: CoPayType;
|
|
39
|
+
coPayValue?: number;
|
|
40
|
+
coPayMethod?: "EXCLUSIVE" | "INCLUSIVE";
|
|
41
|
+
}
|
|
42
|
+
interface MasterAdditionalDiscount {
|
|
43
|
+
mode: AdditionalDiscountMode;
|
|
44
|
+
value: number;
|
|
45
|
+
coPayMode?: CoPayMode;
|
|
46
|
+
}
|
|
47
|
+
interface CalcOptions {
|
|
48
|
+
calculationMethod?: "STEP_WISE" | "FINAL_ONLY";
|
|
49
|
+
lineRound?: RoundFormat;
|
|
50
|
+
headerRound?: RoundFormat;
|
|
51
|
+
precision?: number;
|
|
52
|
+
}
|
|
53
|
+
interface ChildCalculated {
|
|
54
|
+
baseRate: number;
|
|
55
|
+
subtotalAmount: number;
|
|
56
|
+
otherChargeAmount: number;
|
|
57
|
+
discountMode: DiscountMode;
|
|
58
|
+
discountValue: number;
|
|
59
|
+
discountAmount: number;
|
|
60
|
+
taxMethod: TaxMethod;
|
|
61
|
+
taxValue: number;
|
|
62
|
+
taxAmount: number;
|
|
63
|
+
grossAmount: number;
|
|
64
|
+
netAmount: number;
|
|
65
|
+
roundOffAmount: number;
|
|
66
|
+
copayAmount: number;
|
|
67
|
+
}
|
|
68
|
+
interface MasterCalculated {
|
|
69
|
+
additionalDiscountMode: AdditionalDiscountMode;
|
|
70
|
+
additionalDiscountValue: number;
|
|
71
|
+
subtotalAmount: number;
|
|
72
|
+
otherChargeAmount: number;
|
|
73
|
+
discountTotalAmount: number;
|
|
74
|
+
taxAmount: number;
|
|
75
|
+
grossAmount: number;
|
|
76
|
+
netAmount: number;
|
|
77
|
+
roundOffAmount: number;
|
|
78
|
+
copayAmount: number;
|
|
79
|
+
}
|
|
80
|
+
interface BillingCalcResult {
|
|
81
|
+
master: MasterCalculated;
|
|
82
|
+
children: ChildCalculated[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* calculateBillingFromChildren (master-level additional discount applied AFTER child calculations)
|
|
87
|
+
* - Child: item discount -> tax (INCLUSIVE/EXCLUSIVE/NONE) -> coPay -> patient line net + line rounding
|
|
88
|
+
* - Master: sum children, compute patientRawTotal = gross - Σcopay
|
|
89
|
+
* apply master additional discount ON patientRawTotal (no propagation to children)
|
|
90
|
+
* then header rounding
|
|
91
|
+
*/
|
|
92
|
+
declare function calculateBillingFromChildren(inputs: ChildCalcInput[], masterExtra: MasterAdditionalDiscount, options?: CalcOptions): BillingCalcResult;
|
|
93
|
+
declare function calculateSingleChild(it: ChildCalcInput, coPayMode: CoPayMode, options?: CalcOptions): ChildCalculated;
|
|
94
|
+
|
|
95
|
+
interface CreateTransaction {
|
|
96
|
+
field: string;
|
|
97
|
+
changedFrom?: string | null;
|
|
98
|
+
changedTo?: string | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Compares two objects (including nested objects) by flattening them,
|
|
103
|
+
* and returns an array of differences. The returned array contains objects
|
|
104
|
+
* with the field name (underscores replaced by spaces), changedFrom, and
|
|
105
|
+
* changedTo values.
|
|
106
|
+
*
|
|
107
|
+
* @param obj1 - The first object.
|
|
108
|
+
* @param obj2 - The second object.
|
|
109
|
+
* @returns An array of DiffResult objects representing the differences.
|
|
110
|
+
*/
|
|
111
|
+
declare function findDifferences<T extends Record<string, any>>(obj1: T, obj2: T): CreateTransaction[];
|
|
112
|
+
|
|
113
|
+
export { type AdditionalDiscountMode, type BillingCalcResult, type CalcOptions, type ChildCalcInput, type ChildCalculated, type CoPayMode, type CoPayType, type DiscountMode, type MasterAdditionalDiscount, type MasterCalculated, RoundFormat, type TaxMethod, calculateBillingFromChildren, calculateSingleChild, customOmit, findDifferences, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
declare function customOmit<T extends object, K extends keyof T>(obj: T, keys: K[]): {
|
|
2
|
+
rest: Omit<T, K>;
|
|
3
|
+
omitted: Pick<T, K>;
|
|
4
|
+
};
|
|
5
|
+
declare function getDynamicValue(obj: Record<string, any> | null | undefined, accessorKey: string): any | null;
|
|
6
|
+
declare function objectTo2DArray<T>(obj: Record<string, T>, maxCols: number): (string | T)[][];
|
|
7
|
+
declare function toNumberOrNull(value: unknown): number | null;
|
|
8
|
+
declare const getPattern: {
|
|
9
|
+
[key: string]: RegExp;
|
|
10
|
+
};
|
|
11
|
+
declare const interpolate: (template: string, vars: Record<string, unknown>) => string;
|
|
12
|
+
|
|
13
|
+
type DiscountMode = "PERCENTAGE" | "AMOUNT";
|
|
14
|
+
type AdditionalDiscountMode = "PERCENTAGE" | "AMOUNT";
|
|
15
|
+
type CoPayType = "PERCENTAGE" | "AMOUNT";
|
|
16
|
+
type TaxMethod = "NONE" | "INCLUSIVE" | "EXCLUSIVE";
|
|
17
|
+
type CoPayMode = "EXCLUSIVE-INCLUSIVE" | "PERCENTAGE-AMOUNT";
|
|
18
|
+
declare const RoundFormat: {
|
|
19
|
+
readonly ROUND: "ROUND";
|
|
20
|
+
readonly SPECIAL_ROUND: "SPECIAL_ROUND";
|
|
21
|
+
readonly TO_FIXED: "TO_FIXED";
|
|
22
|
+
readonly CEIL: "CEIL";
|
|
23
|
+
readonly FLOOR: "FLOOR";
|
|
24
|
+
readonly TRUNC: "TRUNC";
|
|
25
|
+
readonly NONE: "NONE";
|
|
26
|
+
};
|
|
27
|
+
type RoundFormat = (typeof RoundFormat)[keyof typeof RoundFormat];
|
|
28
|
+
interface ChildCalcInput {
|
|
29
|
+
qty: number;
|
|
30
|
+
rate: number;
|
|
31
|
+
otherCharge?: number;
|
|
32
|
+
addonPercentage?: number;
|
|
33
|
+
discountMode?: DiscountMode;
|
|
34
|
+
discountValue?: number;
|
|
35
|
+
taxMethod?: TaxMethod;
|
|
36
|
+
taxValue?: number;
|
|
37
|
+
/** Absolute insurer-covered at line level */
|
|
38
|
+
coPaymentType?: CoPayType;
|
|
39
|
+
coPayValue?: number;
|
|
40
|
+
coPayMethod?: "EXCLUSIVE" | "INCLUSIVE";
|
|
41
|
+
}
|
|
42
|
+
interface MasterAdditionalDiscount {
|
|
43
|
+
mode: AdditionalDiscountMode;
|
|
44
|
+
value: number;
|
|
45
|
+
coPayMode?: CoPayMode;
|
|
46
|
+
}
|
|
47
|
+
interface CalcOptions {
|
|
48
|
+
calculationMethod?: "STEP_WISE" | "FINAL_ONLY";
|
|
49
|
+
lineRound?: RoundFormat;
|
|
50
|
+
headerRound?: RoundFormat;
|
|
51
|
+
precision?: number;
|
|
52
|
+
}
|
|
53
|
+
interface ChildCalculated {
|
|
54
|
+
baseRate: number;
|
|
55
|
+
subtotalAmount: number;
|
|
56
|
+
otherChargeAmount: number;
|
|
57
|
+
discountMode: DiscountMode;
|
|
58
|
+
discountValue: number;
|
|
59
|
+
discountAmount: number;
|
|
60
|
+
taxMethod: TaxMethod;
|
|
61
|
+
taxValue: number;
|
|
62
|
+
taxAmount: number;
|
|
63
|
+
grossAmount: number;
|
|
64
|
+
netAmount: number;
|
|
65
|
+
roundOffAmount: number;
|
|
66
|
+
copayAmount: number;
|
|
67
|
+
}
|
|
68
|
+
interface MasterCalculated {
|
|
69
|
+
additionalDiscountMode: AdditionalDiscountMode;
|
|
70
|
+
additionalDiscountValue: number;
|
|
71
|
+
subtotalAmount: number;
|
|
72
|
+
otherChargeAmount: number;
|
|
73
|
+
discountTotalAmount: number;
|
|
74
|
+
taxAmount: number;
|
|
75
|
+
grossAmount: number;
|
|
76
|
+
netAmount: number;
|
|
77
|
+
roundOffAmount: number;
|
|
78
|
+
copayAmount: number;
|
|
79
|
+
}
|
|
80
|
+
interface BillingCalcResult {
|
|
81
|
+
master: MasterCalculated;
|
|
82
|
+
children: ChildCalculated[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* calculateBillingFromChildren (master-level additional discount applied AFTER child calculations)
|
|
87
|
+
* - Child: item discount -> tax (INCLUSIVE/EXCLUSIVE/NONE) -> coPay -> patient line net + line rounding
|
|
88
|
+
* - Master: sum children, compute patientRawTotal = gross - Σcopay
|
|
89
|
+
* apply master additional discount ON patientRawTotal (no propagation to children)
|
|
90
|
+
* then header rounding
|
|
91
|
+
*/
|
|
92
|
+
declare function calculateBillingFromChildren(inputs: ChildCalcInput[], masterExtra: MasterAdditionalDiscount, options?: CalcOptions): BillingCalcResult;
|
|
93
|
+
declare function calculateSingleChild(it: ChildCalcInput, coPayMode: CoPayMode, options?: CalcOptions): ChildCalculated;
|
|
94
|
+
|
|
95
|
+
interface CreateTransaction {
|
|
96
|
+
field: string;
|
|
97
|
+
changedFrom?: string | null;
|
|
98
|
+
changedTo?: string | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Compares two objects (including nested objects) by flattening them,
|
|
103
|
+
* and returns an array of differences. The returned array contains objects
|
|
104
|
+
* with the field name (underscores replaced by spaces), changedFrom, and
|
|
105
|
+
* changedTo values.
|
|
106
|
+
*
|
|
107
|
+
* @param obj1 - The first object.
|
|
108
|
+
* @param obj2 - The second object.
|
|
109
|
+
* @returns An array of DiffResult objects representing the differences.
|
|
110
|
+
*/
|
|
111
|
+
declare function findDifferences<T extends Record<string, any>>(obj1: T, obj2: T): CreateTransaction[];
|
|
112
|
+
|
|
113
|
+
export { type AdditionalDiscountMode, type BillingCalcResult, type CalcOptions, type ChildCalcInput, type ChildCalculated, type CoPayMode, type CoPayType, type DiscountMode, type MasterAdditionalDiscount, type MasterCalculated, RoundFormat, type TaxMethod, calculateBillingFromChildren, calculateSingleChild, customOmit, findDifferences, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
RoundFormat: () => RoundFormat,
|
|
24
|
+
calculateBillingFromChildren: () => calculateBillingFromChildren,
|
|
25
|
+
calculateSingleChild: () => calculateSingleChild,
|
|
26
|
+
customOmit: () => customOmit,
|
|
27
|
+
findDifferences: () => findDifferences,
|
|
28
|
+
getDynamicValue: () => getDynamicValue,
|
|
29
|
+
getPattern: () => getPattern,
|
|
30
|
+
interpolate: () => interpolate,
|
|
31
|
+
objectTo2DArray: () => objectTo2DArray,
|
|
32
|
+
toNumberOrNull: () => toNumberOrNull
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/utils/helper.utils.ts
|
|
37
|
+
function customOmit(obj, keys) {
|
|
38
|
+
const rest = { ...obj };
|
|
39
|
+
const omitted = {};
|
|
40
|
+
for (const key of keys) {
|
|
41
|
+
if (key in obj) {
|
|
42
|
+
omitted[key] = obj[key];
|
|
43
|
+
delete rest[key];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { rest, omitted };
|
|
47
|
+
}
|
|
48
|
+
function getDynamicValue(obj, accessorKey) {
|
|
49
|
+
if (!accessorKey || obj == null || typeof obj !== "object") {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const keys = accessorKey.split(".");
|
|
53
|
+
let result = obj;
|
|
54
|
+
for (const key of keys) {
|
|
55
|
+
if (result == null || typeof result !== "object" || !(key in result)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
result = result[key];
|
|
59
|
+
}
|
|
60
|
+
return result === void 0 ? null : result;
|
|
61
|
+
}
|
|
62
|
+
function objectTo2DArray(obj, maxCols) {
|
|
63
|
+
if (maxCols < 2) {
|
|
64
|
+
throw new Error("maxCols must be at least 2");
|
|
65
|
+
}
|
|
66
|
+
const rows = [];
|
|
67
|
+
let currentRow = [];
|
|
68
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
69
|
+
const pair = [key, value];
|
|
70
|
+
if (currentRow.length + pair.length > maxCols) {
|
|
71
|
+
if (currentRow.length > 0) {
|
|
72
|
+
rows.push(currentRow);
|
|
73
|
+
}
|
|
74
|
+
currentRow = [];
|
|
75
|
+
}
|
|
76
|
+
currentRow.push(...pair);
|
|
77
|
+
}
|
|
78
|
+
if (currentRow.length > 0) {
|
|
79
|
+
rows.push(currentRow);
|
|
80
|
+
}
|
|
81
|
+
return rows;
|
|
82
|
+
}
|
|
83
|
+
function toNumberOrNull(value) {
|
|
84
|
+
if (typeof value === "number") {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
const converted = Number(value);
|
|
88
|
+
return isNaN(converted) ? null : converted;
|
|
89
|
+
}
|
|
90
|
+
var getPattern = {
|
|
91
|
+
stringBaseNum: /^[1-9][0-9]*$/,
|
|
92
|
+
licenseTitle: /^(?=.{3,100}$)[A-Za-z0-9][A-Za-z0-9\s\-/&,.()]*$/,
|
|
93
|
+
categoryName: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};'":\\|,.<>/?]).{3,30}$/,
|
|
94
|
+
namePattern: /^[A-Za-z\s]+$/,
|
|
95
|
+
skuPattern: /^[A-Z0-9_-]+$/i,
|
|
96
|
+
SlNoPattern: /^[A-Za-z0-9_-]+$/,
|
|
97
|
+
nameWithNumPattern: /^[A-Za-z0-9\s]+$/,
|
|
98
|
+
emailPattern: /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/,
|
|
99
|
+
// phonePattern:
|
|
100
|
+
// /^(?:\+?(\d{1,3})[\s.-]?)?(?:\(?(\d{3})\)?[\s.-]?)?(\d{3})[\s.-]?(\d{4})(?:\s*(?:x|ext)\s*(\d+))?$/,
|
|
101
|
+
phonePattern: /^\d{9}$/,
|
|
102
|
+
postalCodePattern: /^[0-9]{5}$/,
|
|
103
|
+
alphanumericPattern: /^[a-zA-Z0-9]+$/,
|
|
104
|
+
numericPattern: /^[0-9]+$/,
|
|
105
|
+
datePattern: /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/,
|
|
106
|
+
timePattern: /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/,
|
|
107
|
+
uuidPattern: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
108
|
+
passwordPattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/,
|
|
109
|
+
jsonPattern: /^(\[.+?\]|\{.+?\})$/,
|
|
110
|
+
ipPattern: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/,
|
|
111
|
+
macAddressPattern: /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/,
|
|
112
|
+
hexColorPattern: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/,
|
|
113
|
+
hexPattern: /^[0-9A-Fa-f]+$/,
|
|
114
|
+
binaryPattern: /^[01]+$/,
|
|
115
|
+
base64Pattern: /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/,
|
|
116
|
+
alphanumericDashPattern: /^[a-zA-Z0-9-]+$/,
|
|
117
|
+
alphanumericDotPattern: /^[a-zA-Z0-9.]+$/,
|
|
118
|
+
alphanumericUnderscorePattern: /^[a-zA-Z0-9_]+$/,
|
|
119
|
+
alphanumericPlusPattern: /^[a-zA-Z0-9+]+$/,
|
|
120
|
+
alphanumericSlashPattern: /^[a-zA-Z0-9/]+$/,
|
|
121
|
+
alphanumericColonPattern: /^[a-zA-Z0-9:]+$/,
|
|
122
|
+
alphanumericQuestionMarkPattern: /^[a-zA-Z0-9?]+$/,
|
|
123
|
+
alphanumericAtPattern: /^[a-zA-Z0-9@]+$/,
|
|
124
|
+
alphanumericHashPattern: /^[a-zA-Z0-9#]+$/,
|
|
125
|
+
alphanumericDollarPattern: /^[a-zA-Z0-9$]+$/,
|
|
126
|
+
alphanumericPercentPattern: /^[a-zA-Z0-9%]+$/,
|
|
127
|
+
alphanumericAmpersandPattern: /^[a-zA-Z0-9&]+$/,
|
|
128
|
+
alphanumericVerticalBarPattern: /^[a-zA-Z0-9|]+$/,
|
|
129
|
+
alphanumericTildePattern: /^[a-zA-Z0-9~]+$/,
|
|
130
|
+
alphanumericExclamationPattern: /^[a-zA-Z0-9!]+$/,
|
|
131
|
+
alphanumericAndPattern: /^[a-zA-Z0-9&]+$/,
|
|
132
|
+
alphanumericAsteriskPattern: /^[a-zA-Z0-9*]+$/,
|
|
133
|
+
imagePattern: /^.*\.(jpe?g|png|gif)$/i,
|
|
134
|
+
videoPattern: /\.(mp4|webm|ogg)$/i,
|
|
135
|
+
audioPattern: /\.(mp3|wav)$/i,
|
|
136
|
+
pdfPattern: /\.(pdf)$/i,
|
|
137
|
+
docPattern: /\.(doc|docx)$/i,
|
|
138
|
+
xlsPattern: /\.(xls|xlsx)$/i,
|
|
139
|
+
pptPattern: /\.(ppt|pptx)$/i,
|
|
140
|
+
zipPattern: /\.(zip)$/i,
|
|
141
|
+
rarPattern: /\.(rar)$/i,
|
|
142
|
+
tarPattern: /\.(tar)$/i,
|
|
143
|
+
gzipPattern: /\.(gz|gzip)$/i,
|
|
144
|
+
bz2Pattern: /\.(bz2)$/i,
|
|
145
|
+
isoPattern: /\.(iso)$/i,
|
|
146
|
+
txtPattern: /\.(txt)$/i
|
|
147
|
+
};
|
|
148
|
+
var interpolate = (template, vars) => {
|
|
149
|
+
return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (match, key) => {
|
|
150
|
+
key = key.trim();
|
|
151
|
+
return key in vars ? String(vars[key]) : "";
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/types/calculation.ts
|
|
156
|
+
var RoundFormat = {
|
|
157
|
+
ROUND: "ROUND",
|
|
158
|
+
SPECIAL_ROUND: "SPECIAL_ROUND",
|
|
159
|
+
TO_FIXED: "TO_FIXED",
|
|
160
|
+
CEIL: "CEIL",
|
|
161
|
+
FLOOR: "FLOOR",
|
|
162
|
+
TRUNC: "TRUNC",
|
|
163
|
+
NONE: "NONE"
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/utils/calculation.utils.ts
|
|
167
|
+
function applyRound(value, format, precision = 2) {
|
|
168
|
+
switch (format) {
|
|
169
|
+
case RoundFormat.NONE:
|
|
170
|
+
return value;
|
|
171
|
+
case RoundFormat.ROUND:
|
|
172
|
+
return Math.round(value);
|
|
173
|
+
case RoundFormat.SPECIAL_ROUND:
|
|
174
|
+
return value < 1 ? Math.ceil(value) : Math.round(value);
|
|
175
|
+
case RoundFormat.CEIL:
|
|
176
|
+
return Math.ceil(value);
|
|
177
|
+
case RoundFormat.FLOOR:
|
|
178
|
+
return Math.floor(value);
|
|
179
|
+
case RoundFormat.TRUNC:
|
|
180
|
+
return Math.trunc(value);
|
|
181
|
+
case RoundFormat.TO_FIXED:
|
|
182
|
+
default:
|
|
183
|
+
return Number(value.toFixed(Math.max(0, precision | 0)));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
var maybeStep = (v, stepwise, fmt, p) => stepwise ? applyRound(v, fmt, p) : v;
|
|
187
|
+
function clamp(n, min, max) {
|
|
188
|
+
return Math.max(min, Math.min(max, n));
|
|
189
|
+
}
|
|
190
|
+
var percentage = (amount, percentage2) => {
|
|
191
|
+
return amount * percentage2 / 100;
|
|
192
|
+
};
|
|
193
|
+
function calculateBillingFromChildren(inputs, masterExtra, options = {}) {
|
|
194
|
+
const stepwise = options.calculationMethod === "STEP_WISE";
|
|
195
|
+
const lineFmt = options.lineRound ?? RoundFormat.TO_FIXED;
|
|
196
|
+
const headFmt = options.headerRound ?? lineFmt;
|
|
197
|
+
const precision = options.precision ?? 2;
|
|
198
|
+
const children = inputs.map((it) => {
|
|
199
|
+
return calculateSingleChild(it, masterExtra.coPayMode ?? "PERCENTAGE-AMOUNT", options);
|
|
200
|
+
});
|
|
201
|
+
const subtotalAmount = children.reduce((a, c) => a + c.subtotalAmount, 0);
|
|
202
|
+
const otherChargeAmount = children.reduce((a, c) => a + c.otherChargeAmount, 0);
|
|
203
|
+
const itemDiscountSum = children.reduce((a, c) => a + c.discountAmount, 0);
|
|
204
|
+
const taxAmount = children.reduce((a, c) => a + c.taxAmount, 0);
|
|
205
|
+
const grossAmount = children.reduce((a, c) => a + c.grossAmount, 0);
|
|
206
|
+
const copayAmount = children.reduce((a, c) => a + c.copayAmount, 0);
|
|
207
|
+
const patientRawTotal = grossAmount - copayAmount;
|
|
208
|
+
let masterExtraApplied = 0;
|
|
209
|
+
if (masterExtra.mode === "PERCENTAGE") {
|
|
210
|
+
masterExtraApplied = percentage(patientRawTotal, masterExtra.value);
|
|
211
|
+
} else if (masterExtra.mode === "AMOUNT") {
|
|
212
|
+
masterExtraApplied = Math.min(masterExtra.value, patientRawTotal);
|
|
213
|
+
}
|
|
214
|
+
masterExtraApplied = maybeStep(masterExtraApplied, stepwise, headFmt, precision);
|
|
215
|
+
const discountTotalAmount = maybeStep(itemDiscountSum + masterExtraApplied, stepwise, headFmt, precision);
|
|
216
|
+
const patientAfterExtra = applyRound(Math.max(0, patientRawTotal - masterExtraApplied), "TO_FIXED", 2);
|
|
217
|
+
const netAmount = applyRound(patientAfterExtra, headFmt, precision);
|
|
218
|
+
const roundOffAmount = netAmount - patientAfterExtra;
|
|
219
|
+
const master = {
|
|
220
|
+
additionalDiscountMode: masterExtra.mode,
|
|
221
|
+
additionalDiscountValue: masterExtra.value,
|
|
222
|
+
subtotalAmount: applyRound(subtotalAmount, headFmt, precision),
|
|
223
|
+
otherChargeAmount: applyRound(otherChargeAmount, headFmt, precision),
|
|
224
|
+
discountTotalAmount: applyRound(discountTotalAmount, headFmt, precision),
|
|
225
|
+
// includes master extra
|
|
226
|
+
taxAmount: applyRound(taxAmount, headFmt, precision),
|
|
227
|
+
grossAmount: applyRound(grossAmount, headFmt, precision),
|
|
228
|
+
netAmount,
|
|
229
|
+
// patient payable after master extra + rounding
|
|
230
|
+
roundOffAmount: applyRound(roundOffAmount, "TO_FIXED", 2),
|
|
231
|
+
// rounded - raw
|
|
232
|
+
copayAmount: applyRound(copayAmount, headFmt, precision)
|
|
233
|
+
// Σ line copay (insurer covered)
|
|
234
|
+
};
|
|
235
|
+
return { master, children };
|
|
236
|
+
}
|
|
237
|
+
function calculateSingleChild(it, coPayMode, options = {}) {
|
|
238
|
+
const stepwise = options.calculationMethod === "STEP_WISE";
|
|
239
|
+
const lineFmt = options.lineRound ?? RoundFormat.TO_FIXED;
|
|
240
|
+
const precision = options.precision ?? 2;
|
|
241
|
+
let baseRate = it.rate;
|
|
242
|
+
if (it.addonPercentage) {
|
|
243
|
+
baseRate = baseRate + percentage(baseRate, it.addonPercentage);
|
|
244
|
+
baseRate = applyRound(baseRate, lineFmt, precision);
|
|
245
|
+
}
|
|
246
|
+
const subtotal = applyRound(it.qty * baseRate, lineFmt, precision);
|
|
247
|
+
const other = applyRound(it.otherCharge ?? 0, lineFmt, precision);
|
|
248
|
+
const basePreTax = applyRound(subtotal + other, lineFmt, precision);
|
|
249
|
+
let copayAmount = 0;
|
|
250
|
+
if (coPayMode === "PERCENTAGE-AMOUNT") {
|
|
251
|
+
const copayType = it.coPaymentType ?? "AMOUNT";
|
|
252
|
+
const copayValue = it.coPayValue ?? 0;
|
|
253
|
+
copayAmount = copayType === "PERCENTAGE" ? percentage(subtotal, copayValue) : Math.min(copayValue, subtotal);
|
|
254
|
+
}
|
|
255
|
+
const dMode = it.discountMode ?? "AMOUNT";
|
|
256
|
+
const dVal = it.discountValue ?? 0;
|
|
257
|
+
const tMethod = it.taxMethod ?? "NONE";
|
|
258
|
+
const tVal = Math.max(0, it.taxValue ?? 0);
|
|
259
|
+
let baseAfterTaxDisc = basePreTax;
|
|
260
|
+
if (tMethod === "INCLUSIVE") {
|
|
261
|
+
const inclusiveTaxMultiplier = (100 + tVal) / 100;
|
|
262
|
+
baseAfterTaxDisc = basePreTax / inclusiveTaxMultiplier;
|
|
263
|
+
}
|
|
264
|
+
baseAfterTaxDisc = maybeStep(baseAfterTaxDisc, stepwise, lineFmt, precision);
|
|
265
|
+
const inclDiff = applyRound(basePreTax - baseAfterTaxDisc, lineFmt, precision);
|
|
266
|
+
let discountValue = 0;
|
|
267
|
+
if (dMode === "AMOUNT") {
|
|
268
|
+
discountValue = dVal;
|
|
269
|
+
} else if (dMode === "PERCENTAGE") {
|
|
270
|
+
discountValue = percentage(baseAfterTaxDisc, dVal);
|
|
271
|
+
}
|
|
272
|
+
discountValue = maybeStep(discountValue, stepwise, lineFmt, precision);
|
|
273
|
+
let afterDisc = Math.max(0, baseAfterTaxDisc - discountValue);
|
|
274
|
+
afterDisc = maybeStep(afterDisc, stepwise, lineFmt, precision);
|
|
275
|
+
let calculatedTax = percentage(afterDisc, tVal);
|
|
276
|
+
calculatedTax = maybeStep(calculatedTax, stepwise, lineFmt, precision);
|
|
277
|
+
let grossAmount = afterDisc + calculatedTax;
|
|
278
|
+
grossAmount = maybeStep(grossAmount, stepwise, lineFmt, precision);
|
|
279
|
+
if (coPayMode === "EXCLUSIVE-INCLUSIVE" && it.coPayMethod === "INCLUSIVE") {
|
|
280
|
+
copayAmount = grossAmount;
|
|
281
|
+
}
|
|
282
|
+
copayAmount = applyRound(copayAmount, lineFmt, precision);
|
|
283
|
+
const copay = clamp(Math.max(0, copayAmount), 0, grossAmount);
|
|
284
|
+
const patientRaw = applyRound(grossAmount - copay, RoundFormat.TO_FIXED, 2);
|
|
285
|
+
const patientRounded = applyRound(patientRaw, lineFmt, precision);
|
|
286
|
+
const lineRoundOff = applyRound(patientRounded - patientRaw, RoundFormat.TO_FIXED, 2);
|
|
287
|
+
return {
|
|
288
|
+
// include baseRate if your ChildCalculated type has it (as in your bulk function)
|
|
289
|
+
baseRate: applyRound(baseRate, lineFmt, precision),
|
|
290
|
+
subtotalAmount: applyRound(subtotal + other - inclDiff, lineFmt, precision),
|
|
291
|
+
otherChargeAmount: other,
|
|
292
|
+
discountMode: dMode,
|
|
293
|
+
discountValue: dVal,
|
|
294
|
+
discountAmount: applyRound(discountValue, lineFmt, precision),
|
|
295
|
+
taxMethod: tMethod,
|
|
296
|
+
taxValue: tVal,
|
|
297
|
+
taxAmount: applyRound(calculatedTax, lineFmt, precision),
|
|
298
|
+
grossAmount: applyRound(grossAmount, lineFmt, precision),
|
|
299
|
+
netAmount: patientRounded,
|
|
300
|
+
roundOffAmount: lineRoundOff,
|
|
301
|
+
copayAmount: copay
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/utils/audit.utils.ts
|
|
306
|
+
function isValidDate(value) {
|
|
307
|
+
if (value instanceof Date) {
|
|
308
|
+
return !isNaN(value.getTime());
|
|
309
|
+
}
|
|
310
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
311
|
+
const parsed = new Date(value);
|
|
312
|
+
return !isNaN(parsed.getTime());
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
function flattenObject(obj, parentKey = "") {
|
|
317
|
+
const result = {};
|
|
318
|
+
for (const key of Object.keys(obj)) {
|
|
319
|
+
const raw = obj[key];
|
|
320
|
+
const newKey = parentKey ? `${parentKey}_${key}` : key;
|
|
321
|
+
if (typeof raw !== "number" && isValidDate(raw)) {
|
|
322
|
+
const date = raw instanceof Date ? raw : new Date(raw);
|
|
323
|
+
result[newKey] = date.toISOString().split("T")[0];
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
327
|
+
Object.assign(result, flattenObject(raw, newKey));
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
result[newKey] = raw;
|
|
331
|
+
}
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
function findDifferences(obj1, obj2) {
|
|
335
|
+
const flatObj1 = flattenObject(obj1);
|
|
336
|
+
const flatObj2 = flattenObject(obj2);
|
|
337
|
+
const differences = [];
|
|
338
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(flatObj1)]);
|
|
339
|
+
allKeys.forEach((key) => {
|
|
340
|
+
if (flatObj1[key] !== flatObj2[key]) {
|
|
341
|
+
differences.push({
|
|
342
|
+
// Replace underscores with spaces for a nicer field output
|
|
343
|
+
field: key.replace(/_/g, " "),
|
|
344
|
+
changedFrom: typeof flatObj1[key] !== "string" ? JSON.stringify(flatObj1[key]) : flatObj1[key],
|
|
345
|
+
changedTo: typeof flatObj2[key] !== "string" ? JSON.stringify(flatObj2[key]) : flatObj2[key]
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
return differences;
|
|
350
|
+
}
|
|
351
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
352
|
+
0 && (module.exports = {
|
|
353
|
+
RoundFormat,
|
|
354
|
+
calculateBillingFromChildren,
|
|
355
|
+
calculateSingleChild,
|
|
356
|
+
customOmit,
|
|
357
|
+
findDifferences,
|
|
358
|
+
getDynamicValue,
|
|
359
|
+
getPattern,
|
|
360
|
+
interpolate,
|
|
361
|
+
objectTo2DArray,
|
|
362
|
+
toNumberOrNull
|
|
363
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// src/utils/helper.utils.ts
|
|
2
|
+
function customOmit(obj, keys) {
|
|
3
|
+
const rest = { ...obj };
|
|
4
|
+
const omitted = {};
|
|
5
|
+
for (const key of keys) {
|
|
6
|
+
if (key in obj) {
|
|
7
|
+
omitted[key] = obj[key];
|
|
8
|
+
delete rest[key];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return { rest, omitted };
|
|
12
|
+
}
|
|
13
|
+
function getDynamicValue(obj, accessorKey) {
|
|
14
|
+
if (!accessorKey || obj == null || typeof obj !== "object") {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const keys = accessorKey.split(".");
|
|
18
|
+
let result = obj;
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
if (result == null || typeof result !== "object" || !(key in result)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
result = result[key];
|
|
24
|
+
}
|
|
25
|
+
return result === void 0 ? null : result;
|
|
26
|
+
}
|
|
27
|
+
function objectTo2DArray(obj, maxCols) {
|
|
28
|
+
if (maxCols < 2) {
|
|
29
|
+
throw new Error("maxCols must be at least 2");
|
|
30
|
+
}
|
|
31
|
+
const rows = [];
|
|
32
|
+
let currentRow = [];
|
|
33
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
34
|
+
const pair = [key, value];
|
|
35
|
+
if (currentRow.length + pair.length > maxCols) {
|
|
36
|
+
if (currentRow.length > 0) {
|
|
37
|
+
rows.push(currentRow);
|
|
38
|
+
}
|
|
39
|
+
currentRow = [];
|
|
40
|
+
}
|
|
41
|
+
currentRow.push(...pair);
|
|
42
|
+
}
|
|
43
|
+
if (currentRow.length > 0) {
|
|
44
|
+
rows.push(currentRow);
|
|
45
|
+
}
|
|
46
|
+
return rows;
|
|
47
|
+
}
|
|
48
|
+
function toNumberOrNull(value) {
|
|
49
|
+
if (typeof value === "number") {
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
const converted = Number(value);
|
|
53
|
+
return isNaN(converted) ? null : converted;
|
|
54
|
+
}
|
|
55
|
+
var getPattern = {
|
|
56
|
+
stringBaseNum: /^[1-9][0-9]*$/,
|
|
57
|
+
licenseTitle: /^(?=.{3,100}$)[A-Za-z0-9][A-Za-z0-9\s\-/&,.()]*$/,
|
|
58
|
+
categoryName: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};'":\\|,.<>/?]).{3,30}$/,
|
|
59
|
+
namePattern: /^[A-Za-z\s]+$/,
|
|
60
|
+
skuPattern: /^[A-Z0-9_-]+$/i,
|
|
61
|
+
SlNoPattern: /^[A-Za-z0-9_-]+$/,
|
|
62
|
+
nameWithNumPattern: /^[A-Za-z0-9\s]+$/,
|
|
63
|
+
emailPattern: /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/,
|
|
64
|
+
// phonePattern:
|
|
65
|
+
// /^(?:\+?(\d{1,3})[\s.-]?)?(?:\(?(\d{3})\)?[\s.-]?)?(\d{3})[\s.-]?(\d{4})(?:\s*(?:x|ext)\s*(\d+))?$/,
|
|
66
|
+
phonePattern: /^\d{9}$/,
|
|
67
|
+
postalCodePattern: /^[0-9]{5}$/,
|
|
68
|
+
alphanumericPattern: /^[a-zA-Z0-9]+$/,
|
|
69
|
+
numericPattern: /^[0-9]+$/,
|
|
70
|
+
datePattern: /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/,
|
|
71
|
+
timePattern: /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/,
|
|
72
|
+
uuidPattern: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
73
|
+
passwordPattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/,
|
|
74
|
+
jsonPattern: /^(\[.+?\]|\{.+?\})$/,
|
|
75
|
+
ipPattern: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/,
|
|
76
|
+
macAddressPattern: /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/,
|
|
77
|
+
hexColorPattern: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/,
|
|
78
|
+
hexPattern: /^[0-9A-Fa-f]+$/,
|
|
79
|
+
binaryPattern: /^[01]+$/,
|
|
80
|
+
base64Pattern: /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/,
|
|
81
|
+
alphanumericDashPattern: /^[a-zA-Z0-9-]+$/,
|
|
82
|
+
alphanumericDotPattern: /^[a-zA-Z0-9.]+$/,
|
|
83
|
+
alphanumericUnderscorePattern: /^[a-zA-Z0-9_]+$/,
|
|
84
|
+
alphanumericPlusPattern: /^[a-zA-Z0-9+]+$/,
|
|
85
|
+
alphanumericSlashPattern: /^[a-zA-Z0-9/]+$/,
|
|
86
|
+
alphanumericColonPattern: /^[a-zA-Z0-9:]+$/,
|
|
87
|
+
alphanumericQuestionMarkPattern: /^[a-zA-Z0-9?]+$/,
|
|
88
|
+
alphanumericAtPattern: /^[a-zA-Z0-9@]+$/,
|
|
89
|
+
alphanumericHashPattern: /^[a-zA-Z0-9#]+$/,
|
|
90
|
+
alphanumericDollarPattern: /^[a-zA-Z0-9$]+$/,
|
|
91
|
+
alphanumericPercentPattern: /^[a-zA-Z0-9%]+$/,
|
|
92
|
+
alphanumericAmpersandPattern: /^[a-zA-Z0-9&]+$/,
|
|
93
|
+
alphanumericVerticalBarPattern: /^[a-zA-Z0-9|]+$/,
|
|
94
|
+
alphanumericTildePattern: /^[a-zA-Z0-9~]+$/,
|
|
95
|
+
alphanumericExclamationPattern: /^[a-zA-Z0-9!]+$/,
|
|
96
|
+
alphanumericAndPattern: /^[a-zA-Z0-9&]+$/,
|
|
97
|
+
alphanumericAsteriskPattern: /^[a-zA-Z0-9*]+$/,
|
|
98
|
+
imagePattern: /^.*\.(jpe?g|png|gif)$/i,
|
|
99
|
+
videoPattern: /\.(mp4|webm|ogg)$/i,
|
|
100
|
+
audioPattern: /\.(mp3|wav)$/i,
|
|
101
|
+
pdfPattern: /\.(pdf)$/i,
|
|
102
|
+
docPattern: /\.(doc|docx)$/i,
|
|
103
|
+
xlsPattern: /\.(xls|xlsx)$/i,
|
|
104
|
+
pptPattern: /\.(ppt|pptx)$/i,
|
|
105
|
+
zipPattern: /\.(zip)$/i,
|
|
106
|
+
rarPattern: /\.(rar)$/i,
|
|
107
|
+
tarPattern: /\.(tar)$/i,
|
|
108
|
+
gzipPattern: /\.(gz|gzip)$/i,
|
|
109
|
+
bz2Pattern: /\.(bz2)$/i,
|
|
110
|
+
isoPattern: /\.(iso)$/i,
|
|
111
|
+
txtPattern: /\.(txt)$/i
|
|
112
|
+
};
|
|
113
|
+
var interpolate = (template, vars) => {
|
|
114
|
+
return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (match, key) => {
|
|
115
|
+
key = key.trim();
|
|
116
|
+
return key in vars ? String(vars[key]) : "";
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/types/calculation.ts
|
|
121
|
+
var RoundFormat = {
|
|
122
|
+
ROUND: "ROUND",
|
|
123
|
+
SPECIAL_ROUND: "SPECIAL_ROUND",
|
|
124
|
+
TO_FIXED: "TO_FIXED",
|
|
125
|
+
CEIL: "CEIL",
|
|
126
|
+
FLOOR: "FLOOR",
|
|
127
|
+
TRUNC: "TRUNC",
|
|
128
|
+
NONE: "NONE"
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/utils/calculation.utils.ts
|
|
132
|
+
function applyRound(value, format, precision = 2) {
|
|
133
|
+
switch (format) {
|
|
134
|
+
case RoundFormat.NONE:
|
|
135
|
+
return value;
|
|
136
|
+
case RoundFormat.ROUND:
|
|
137
|
+
return Math.round(value);
|
|
138
|
+
case RoundFormat.SPECIAL_ROUND:
|
|
139
|
+
return value < 1 ? Math.ceil(value) : Math.round(value);
|
|
140
|
+
case RoundFormat.CEIL:
|
|
141
|
+
return Math.ceil(value);
|
|
142
|
+
case RoundFormat.FLOOR:
|
|
143
|
+
return Math.floor(value);
|
|
144
|
+
case RoundFormat.TRUNC:
|
|
145
|
+
return Math.trunc(value);
|
|
146
|
+
case RoundFormat.TO_FIXED:
|
|
147
|
+
default:
|
|
148
|
+
return Number(value.toFixed(Math.max(0, precision | 0)));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
var maybeStep = (v, stepwise, fmt, p) => stepwise ? applyRound(v, fmt, p) : v;
|
|
152
|
+
function clamp(n, min, max) {
|
|
153
|
+
return Math.max(min, Math.min(max, n));
|
|
154
|
+
}
|
|
155
|
+
var percentage = (amount, percentage2) => {
|
|
156
|
+
return amount * percentage2 / 100;
|
|
157
|
+
};
|
|
158
|
+
function calculateBillingFromChildren(inputs, masterExtra, options = {}) {
|
|
159
|
+
const stepwise = options.calculationMethod === "STEP_WISE";
|
|
160
|
+
const lineFmt = options.lineRound ?? RoundFormat.TO_FIXED;
|
|
161
|
+
const headFmt = options.headerRound ?? lineFmt;
|
|
162
|
+
const precision = options.precision ?? 2;
|
|
163
|
+
const children = inputs.map((it) => {
|
|
164
|
+
return calculateSingleChild(it, masterExtra.coPayMode ?? "PERCENTAGE-AMOUNT", options);
|
|
165
|
+
});
|
|
166
|
+
const subtotalAmount = children.reduce((a, c) => a + c.subtotalAmount, 0);
|
|
167
|
+
const otherChargeAmount = children.reduce((a, c) => a + c.otherChargeAmount, 0);
|
|
168
|
+
const itemDiscountSum = children.reduce((a, c) => a + c.discountAmount, 0);
|
|
169
|
+
const taxAmount = children.reduce((a, c) => a + c.taxAmount, 0);
|
|
170
|
+
const grossAmount = children.reduce((a, c) => a + c.grossAmount, 0);
|
|
171
|
+
const copayAmount = children.reduce((a, c) => a + c.copayAmount, 0);
|
|
172
|
+
const patientRawTotal = grossAmount - copayAmount;
|
|
173
|
+
let masterExtraApplied = 0;
|
|
174
|
+
if (masterExtra.mode === "PERCENTAGE") {
|
|
175
|
+
masterExtraApplied = percentage(patientRawTotal, masterExtra.value);
|
|
176
|
+
} else if (masterExtra.mode === "AMOUNT") {
|
|
177
|
+
masterExtraApplied = Math.min(masterExtra.value, patientRawTotal);
|
|
178
|
+
}
|
|
179
|
+
masterExtraApplied = maybeStep(masterExtraApplied, stepwise, headFmt, precision);
|
|
180
|
+
const discountTotalAmount = maybeStep(itemDiscountSum + masterExtraApplied, stepwise, headFmt, precision);
|
|
181
|
+
const patientAfterExtra = applyRound(Math.max(0, patientRawTotal - masterExtraApplied), "TO_FIXED", 2);
|
|
182
|
+
const netAmount = applyRound(patientAfterExtra, headFmt, precision);
|
|
183
|
+
const roundOffAmount = netAmount - patientAfterExtra;
|
|
184
|
+
const master = {
|
|
185
|
+
additionalDiscountMode: masterExtra.mode,
|
|
186
|
+
additionalDiscountValue: masterExtra.value,
|
|
187
|
+
subtotalAmount: applyRound(subtotalAmount, headFmt, precision),
|
|
188
|
+
otherChargeAmount: applyRound(otherChargeAmount, headFmt, precision),
|
|
189
|
+
discountTotalAmount: applyRound(discountTotalAmount, headFmt, precision),
|
|
190
|
+
// includes master extra
|
|
191
|
+
taxAmount: applyRound(taxAmount, headFmt, precision),
|
|
192
|
+
grossAmount: applyRound(grossAmount, headFmt, precision),
|
|
193
|
+
netAmount,
|
|
194
|
+
// patient payable after master extra + rounding
|
|
195
|
+
roundOffAmount: applyRound(roundOffAmount, "TO_FIXED", 2),
|
|
196
|
+
// rounded - raw
|
|
197
|
+
copayAmount: applyRound(copayAmount, headFmt, precision)
|
|
198
|
+
// Σ line copay (insurer covered)
|
|
199
|
+
};
|
|
200
|
+
return { master, children };
|
|
201
|
+
}
|
|
202
|
+
function calculateSingleChild(it, coPayMode, options = {}) {
|
|
203
|
+
const stepwise = options.calculationMethod === "STEP_WISE";
|
|
204
|
+
const lineFmt = options.lineRound ?? RoundFormat.TO_FIXED;
|
|
205
|
+
const precision = options.precision ?? 2;
|
|
206
|
+
let baseRate = it.rate;
|
|
207
|
+
if (it.addonPercentage) {
|
|
208
|
+
baseRate = baseRate + percentage(baseRate, it.addonPercentage);
|
|
209
|
+
baseRate = applyRound(baseRate, lineFmt, precision);
|
|
210
|
+
}
|
|
211
|
+
const subtotal = applyRound(it.qty * baseRate, lineFmt, precision);
|
|
212
|
+
const other = applyRound(it.otherCharge ?? 0, lineFmt, precision);
|
|
213
|
+
const basePreTax = applyRound(subtotal + other, lineFmt, precision);
|
|
214
|
+
let copayAmount = 0;
|
|
215
|
+
if (coPayMode === "PERCENTAGE-AMOUNT") {
|
|
216
|
+
const copayType = it.coPaymentType ?? "AMOUNT";
|
|
217
|
+
const copayValue = it.coPayValue ?? 0;
|
|
218
|
+
copayAmount = copayType === "PERCENTAGE" ? percentage(subtotal, copayValue) : Math.min(copayValue, subtotal);
|
|
219
|
+
}
|
|
220
|
+
const dMode = it.discountMode ?? "AMOUNT";
|
|
221
|
+
const dVal = it.discountValue ?? 0;
|
|
222
|
+
const tMethod = it.taxMethod ?? "NONE";
|
|
223
|
+
const tVal = Math.max(0, it.taxValue ?? 0);
|
|
224
|
+
let baseAfterTaxDisc = basePreTax;
|
|
225
|
+
if (tMethod === "INCLUSIVE") {
|
|
226
|
+
const inclusiveTaxMultiplier = (100 + tVal) / 100;
|
|
227
|
+
baseAfterTaxDisc = basePreTax / inclusiveTaxMultiplier;
|
|
228
|
+
}
|
|
229
|
+
baseAfterTaxDisc = maybeStep(baseAfterTaxDisc, stepwise, lineFmt, precision);
|
|
230
|
+
const inclDiff = applyRound(basePreTax - baseAfterTaxDisc, lineFmt, precision);
|
|
231
|
+
let discountValue = 0;
|
|
232
|
+
if (dMode === "AMOUNT") {
|
|
233
|
+
discountValue = dVal;
|
|
234
|
+
} else if (dMode === "PERCENTAGE") {
|
|
235
|
+
discountValue = percentage(baseAfterTaxDisc, dVal);
|
|
236
|
+
}
|
|
237
|
+
discountValue = maybeStep(discountValue, stepwise, lineFmt, precision);
|
|
238
|
+
let afterDisc = Math.max(0, baseAfterTaxDisc - discountValue);
|
|
239
|
+
afterDisc = maybeStep(afterDisc, stepwise, lineFmt, precision);
|
|
240
|
+
let calculatedTax = percentage(afterDisc, tVal);
|
|
241
|
+
calculatedTax = maybeStep(calculatedTax, stepwise, lineFmt, precision);
|
|
242
|
+
let grossAmount = afterDisc + calculatedTax;
|
|
243
|
+
grossAmount = maybeStep(grossAmount, stepwise, lineFmt, precision);
|
|
244
|
+
if (coPayMode === "EXCLUSIVE-INCLUSIVE" && it.coPayMethod === "INCLUSIVE") {
|
|
245
|
+
copayAmount = grossAmount;
|
|
246
|
+
}
|
|
247
|
+
copayAmount = applyRound(copayAmount, lineFmt, precision);
|
|
248
|
+
const copay = clamp(Math.max(0, copayAmount), 0, grossAmount);
|
|
249
|
+
const patientRaw = applyRound(grossAmount - copay, RoundFormat.TO_FIXED, 2);
|
|
250
|
+
const patientRounded = applyRound(patientRaw, lineFmt, precision);
|
|
251
|
+
const lineRoundOff = applyRound(patientRounded - patientRaw, RoundFormat.TO_FIXED, 2);
|
|
252
|
+
return {
|
|
253
|
+
// include baseRate if your ChildCalculated type has it (as in your bulk function)
|
|
254
|
+
baseRate: applyRound(baseRate, lineFmt, precision),
|
|
255
|
+
subtotalAmount: applyRound(subtotal + other - inclDiff, lineFmt, precision),
|
|
256
|
+
otherChargeAmount: other,
|
|
257
|
+
discountMode: dMode,
|
|
258
|
+
discountValue: dVal,
|
|
259
|
+
discountAmount: applyRound(discountValue, lineFmt, precision),
|
|
260
|
+
taxMethod: tMethod,
|
|
261
|
+
taxValue: tVal,
|
|
262
|
+
taxAmount: applyRound(calculatedTax, lineFmt, precision),
|
|
263
|
+
grossAmount: applyRound(grossAmount, lineFmt, precision),
|
|
264
|
+
netAmount: patientRounded,
|
|
265
|
+
roundOffAmount: lineRoundOff,
|
|
266
|
+
copayAmount: copay
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// src/utils/audit.utils.ts
|
|
271
|
+
function isValidDate(value) {
|
|
272
|
+
if (value instanceof Date) {
|
|
273
|
+
return !isNaN(value.getTime());
|
|
274
|
+
}
|
|
275
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
276
|
+
const parsed = new Date(value);
|
|
277
|
+
return !isNaN(parsed.getTime());
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
function flattenObject(obj, parentKey = "") {
|
|
282
|
+
const result = {};
|
|
283
|
+
for (const key of Object.keys(obj)) {
|
|
284
|
+
const raw = obj[key];
|
|
285
|
+
const newKey = parentKey ? `${parentKey}_${key}` : key;
|
|
286
|
+
if (typeof raw !== "number" && isValidDate(raw)) {
|
|
287
|
+
const date = raw instanceof Date ? raw : new Date(raw);
|
|
288
|
+
result[newKey] = date.toISOString().split("T")[0];
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
292
|
+
Object.assign(result, flattenObject(raw, newKey));
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
result[newKey] = raw;
|
|
296
|
+
}
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
function findDifferences(obj1, obj2) {
|
|
300
|
+
const flatObj1 = flattenObject(obj1);
|
|
301
|
+
const flatObj2 = flattenObject(obj2);
|
|
302
|
+
const differences = [];
|
|
303
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(flatObj1)]);
|
|
304
|
+
allKeys.forEach((key) => {
|
|
305
|
+
if (flatObj1[key] !== flatObj2[key]) {
|
|
306
|
+
differences.push({
|
|
307
|
+
// Replace underscores with spaces for a nicer field output
|
|
308
|
+
field: key.replace(/_/g, " "),
|
|
309
|
+
changedFrom: typeof flatObj1[key] !== "string" ? JSON.stringify(flatObj1[key]) : flatObj1[key],
|
|
310
|
+
changedTo: typeof flatObj2[key] !== "string" ? JSON.stringify(flatObj2[key]) : flatObj2[key]
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
return differences;
|
|
315
|
+
}
|
|
316
|
+
export {
|
|
317
|
+
RoundFormat,
|
|
318
|
+
calculateBillingFromChildren,
|
|
319
|
+
calculateSingleChild,
|
|
320
|
+
customOmit,
|
|
321
|
+
findDifferences,
|
|
322
|
+
getDynamicValue,
|
|
323
|
+
getPattern,
|
|
324
|
+
interpolate,
|
|
325
|
+
objectTo2DArray,
|
|
326
|
+
toNumberOrNull
|
|
327
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "av6-utils",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"module": "dist/index.mjs",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"description": "All utility function for av6 node js projects.",
|
|
8
|
+
"author": "Aniket Sarkar",
|
|
9
|
+
"license": "ISC",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "npm run format && tsup",
|
|
12
|
+
"format": "prettier --write **/*.ts"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsup": "^8.5.0",
|
|
16
|
+
"typescript": "^5.9.2"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"prettier": "^3.6.2"
|
|
21
|
+
}
|
|
22
|
+
}
|