tally-xml-tdl 1.13.2 → 1.15.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.
Files changed (56) hide show
  1. package/README.md +174 -174
  2. package/index.d.ts +15 -15
  3. package/package.json +6 -17
  4. package/src/index.js +2 -2
  5. package/src/{v13 → v15}/company.js +4 -2
  6. package/src/{v13 → v15}/core/changeType/v1/changeTypeString.js +111 -111
  7. package/src/{v13 → v15}/core/changeType/v2/changeTypeString.js +111 -111
  8. package/src/{v13 → v15}/core/changeType/v3/alterLeaf.js +45 -45
  9. package/src/{v13 → v15}/core/changeType/v3/changeTypeString.js +21 -21
  10. package/src/{v13 → v15}/core/changeType/v3/forArray/v1/index.js +17 -17
  11. package/src/{v13 → v15}/core/changeType/v3/forObject/v1/index.js +19 -19
  12. package/src/{v13 → v15}/core/changeType/v3/guards.js +26 -26
  13. package/src/{v13 → v15}/core/changeType/v3/traverse.js +47 -47
  14. package/src/{v13 → v15}/core/cleanTallyResponse.js +57 -57
  15. package/src/{v13 → v15}/index.d.ts +13 -13
  16. package/src/{v13 → v15}/main.d.ts +2 -2
  17. package/src/{v6/index.js → v15/masters.js} +5 -23
  18. package/src/v15/masters.json +78 -0
  19. package/src/v6/body.json +0 -30
  20. package/src/v6/body.xml +0 -25
  21. package/src/v6/core/buildXml.js +0 -9
  22. package/src/v6/core/cleanTallyResponse.js +0 -57
  23. package/src/v6/core/execute/executeXml.js +0 -14
  24. package/src/v6/core/execute/executeXmlAndClean.js +0 -15
  25. package/src/v6/core/execute/index.js +0 -3
  26. package/src/v6/core/index.js +0 -20
  27. package/src/v6/core/response/index.js +0 -2
  28. package/src/v6/core/response/xmlToJson.js +0 -18
  29. package/src/v6/core/transport/http.js +0 -40
  30. package/src/v6/core/transport/index.js +0 -1
  31. package/src/v6/index.d.ts +0 -8
  32. /package/src/{v13 → v15}/body.json +0 -0
  33. /package/src/{v13 → v15}/body.xml +0 -0
  34. /package/src/{v13 → v15}/core/buildXml.js +0 -0
  35. /package/src/{v13 → v15}/core/execute/executeBody.js +0 -0
  36. /package/src/{v13 → v15}/core/execute/executeXml.js +0 -0
  37. /package/src/{v13 → v15}/core/execute/executeXmlAndClean.js +0 -0
  38. /package/src/{v13 → v15}/core/execute/index.js +0 -0
  39. /package/src/{v13 → v15}/core/index.js +0 -0
  40. /package/src/{v13 → v15}/core/request/envelope.js +0 -0
  41. /package/src/{v13 → v15}/core/request/index.js +0 -0
  42. /package/src/{v13 → v15}/core/request/jsonToXml.js +0 -0
  43. /package/src/{v13 → v15}/core/response/index.js +0 -0
  44. /package/src/{v13 → v15}/core/response/xmlToJson.js +0 -0
  45. /package/src/{v13 → v15}/core/transport/http.js +0 -0
  46. /package/src/{v13 → v15}/core/transport/index.js +0 -0
  47. /package/src/{v13 → v15}/daybook/body copy.xml +0 -0
  48. /package/src/{v13 → v15}/daybook/body.xml +0 -0
  49. /package/src/{v13 → v15}/fromJson.js +0 -0
  50. /package/src/{v13 → v15}/fromXml.js +0 -0
  51. /package/src/{v13 → v15}/index.js +0 -0
  52. /package/src/{v13 → v15}/main.js +0 -0
  53. /package/src/{v13 → v15}/purc/body.json +0 -0
  54. /package/src/{v13 → v15}/purc/body.xml +0 -0
  55. /package/src/{v13 → v15}/purchases/v1.xml +0 -0
  56. /package/src/{v13 → v15}/purchases.js +0 -0
package/README.md CHANGED
@@ -1,175 +1,175 @@
1
- # tally-xml-tdl
2
-
3
- > Simple, clean TDL and XML extraction and manipulation tools for Tally.
4
-
5
- [![npm version](https://img.shields.io/npm/v/tally-xml-tdl.svg)](https://www.npmjs.com/package/tally-xml-tdl)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
-
8
- Extract data directly from **Tally Prime / Tally.ERP 9** using TDL XML requests over HTTP, and get back **clean, developer-friendly JSON** ready for modern web apps, APIs, dashboards, and databases.
9
-
10
- ---
11
-
12
- ## 📖 The Story
13
-
14
- Interfacing with Tally usually means dealing with complex, deeply nested XML envelopes, verbose TDL message syntax, and responses filled with XML parser artifacts (`@_RESERVEDNAME`, `#text`, `@_TYPE`, etc.):
15
-
16
- ```json
17
- // Traditional Tally XML-to-JSON response (cluttered & nested):
18
- {
19
- "ENVELOPE": {
20
- "BODY": {
21
- "DATA": {
22
- "COLLECTION": {
23
- "UNIT": [
24
- {
25
- "@_NAME": "Kgs",
26
- "@_RESERVEDNAME": "",
27
- "NAME": { "#text": "Kgs", "@_TYPE": "String" }
28
- }
29
- ]
30
- }
31
- }
32
- }
33
- }
34
- }
35
- ```
36
-
37
- **`tally-xml-tdl` changes that.** With version **`v6`**, it introduces a streamlined template-driven engine and two simple functions:
38
-
39
- - **`clean()`**: Strips all XML noise, flattens text nodes, removes reserved attributes, and returns plain JavaScript objects or arrays.
40
- - **`get()`**: Returns the full XML-to-JSON parsed object when you need raw envelope fidelity.
41
-
42
- ```json
43
- // With tally-xml-tdl clean():
44
- [
45
- "Kgs"
46
- ]
47
- ```
48
-
49
- ---
50
-
51
- ## 🚀 Features
52
-
53
- - 🧹 **Clean Data Out-of-the-Box**: Automatically strips Tally metadata attributes and flattens nested values.
54
- - ⚡ **Two Extraction Modes**: Choose `clean()` for clean data arrays, or `get()` for the full parsed envelope.
55
- - 📋 **Pre-configured Queries**: Built-in TDL queries for UOM, Stock Items, Stock Items with Base Units, and Ledgers.
56
- - 🏎️ **Fast & Lightweight**: Built on top of `fast-xml-parser` with minimal overhead.
57
- - 📦 **Zero-Config HTTP Transport**: Communicates directly with Tally's built-in HTTP server (`http://localhost:9000`).
58
- - 🔷 **First-Class TypeScript Support**: Full type declarations included (`.d.ts`).
59
- - 🌐 **Modern ESM**: Native ES modules with subpath exports (`tally-xml-tdl` and `tally-xml-tdl/v6`).
60
-
61
- ---
62
-
63
- ## 📦 Installation
64
-
65
- ```bash
66
- npm install tally-xml-tdl
67
- ```
68
-
69
- ### Prerequisites
70
- Make sure Tally is running and its HTTP server is enabled:
71
- - **Tally Prime / Tally.ERP 9**: Press `F12` (Configure) -> **Advanced Configuration** -> Set **Tally is acting as** to `Both` or `Server`, and note the port (default is `9000`).
72
-
73
- ---
74
-
75
- ## 💡 Quick Start
76
-
77
- ```javascript
78
- import { clean, get } from "tally-xml-tdl";
79
-
80
- // 1. Fetch clean, simplified data
81
- const items = await clean({
82
- company: "My Company Name",
83
- jsonId: "stockItemsWithBaseUnits"
84
- });
85
-
86
- console.log(items);
87
- // Output:
88
- // [
89
- // { NAME: "Item A", BASEUNITS: "Nos" },
90
- // { NAME: "Item B", BASEUNITS: "Kgs" }
91
- // ]
92
-
93
- // 2. Fetch raw parsed JSON (full fidelity)
94
- const raw = await get({
95
- company: "My Company Name",
96
- jsonId: "uom"
97
- });
98
-
99
- console.log(raw);
100
- ```
101
-
102
- ---
103
-
104
- ## 📚 API Reference
105
-
106
- ### `clean(options)`
107
- Sends the TDL query to Tally, parses the XML response, and cleans the resulting collection data.
108
-
109
- ```typescript
110
- clean<T = any>(options: V6Options): Promise<T>
111
- ```
112
-
113
- #### Options:
114
- | Option | Type | Required | Default | Description |
115
- | :--- | :--- | :---: | :--- | :--- |
116
- | `company` | `string` | **Yes** | — | Name of the active company in Tally. |
117
- | `jsonId` | `string` | **Yes** | — | ID of the pre-configured TDL query. |
118
- | `url` | `string` | No | `"http://localhost:9000"` | Tally HTTP endpoint URL. |
119
-
120
- ---
121
-
122
- ### `get(options)`
123
- Sends the TDL query to Tally and returns the complete XML response converted to a JavaScript object without stripping any metadata.
124
-
125
- ```typescript
126
- get<T = any>(options: V6Options): Promise<T>
127
- ```
128
-
129
- ---
130
-
131
- ## 🗂️ Built-in Query Catalog (`jsonId`)
132
-
133
- | `jsonId` | Target Entity | Description | Fields Fetched |
134
- | :--- | :--- | :--- | :--- |
135
- | `"uom"` | Units of Measure | List of units of measurement | `$$Alias:Name` |
136
- | `"stockItems"` | Stock Items | List of inventory stock items | `$$Alias:Name` |
137
- | `"stockItemsWithBaseUnits"` | Stock Items | Inventory stock items with their base units | `$$Alias:Name`, `BaseUnits` |
138
- | `"ledgerNames"` | Ledgers | List of accounting ledgers | `$$Alias:Name` |
139
-
140
- ---
141
-
142
- ## 🎯 Advanced Usage
143
-
144
- ### Connecting to a Remote or Custom Port
145
- If Tally is hosted on another machine or running on a non-default port:
146
-
147
- ```javascript
148
- import { clean } from "tally-xml-tdl";
149
-
150
- const data = await clean({
151
- company: "Acme Corp",
152
- jsonId: "ledgerNames",
153
- url: "http://192.168.1.100:9005"
154
- });
155
- ```
156
-
157
- ### Subpath Import
158
- You can also explicitly import from the `v6` subpath:
159
-
160
- ```javascript
161
- import { clean, get } from "tally-xml-tdl/v6";
162
- ```
163
-
164
- ### Using Namespaces
165
- ```javascript
166
- import { v6 } from "tally-xml-tdl";
167
-
168
- const data = await v6.clean({ company: "Acme Corp", jsonId: "uom" });
169
- ```
170
-
171
- ---
172
-
173
- ## 📄 License
174
-
1
+ # tally-xml-tdl
2
+
3
+ > Simple, clean TDL and XML extraction and manipulation tools for Tally.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/tally-xml-tdl.svg)](https://www.npmjs.com/package/tally-xml-tdl)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ Extract data directly from **Tally Prime / Tally.ERP 9** using TDL XML requests over HTTP, and get back **clean, developer-friendly JSON** ready for modern web apps, APIs, dashboards, and databases.
9
+
10
+ ---
11
+
12
+ ## 📖 The Story
13
+
14
+ Interfacing with Tally usually means dealing with complex, deeply nested XML envelopes, verbose TDL message syntax, and responses filled with XML parser artifacts (`@_RESERVEDNAME`, `#text`, `@_TYPE`, etc.):
15
+
16
+ ```json
17
+ // Traditional Tally XML-to-JSON response (cluttered & nested):
18
+ {
19
+ "ENVELOPE": {
20
+ "BODY": {
21
+ "DATA": {
22
+ "COLLECTION": {
23
+ "UNIT": [
24
+ {
25
+ "@_NAME": "Kgs",
26
+ "@_RESERVEDNAME": "",
27
+ "NAME": { "#text": "Kgs", "@_TYPE": "String" }
28
+ }
29
+ ]
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ **`tally-xml-tdl` changes that.** With version **`v6`**, it introduces a streamlined template-driven engine and two simple functions:
38
+
39
+ - **`clean()`**: Strips all XML noise, flattens text nodes, removes reserved attributes, and returns plain JavaScript objects or arrays.
40
+ - **`get()`**: Returns the full XML-to-JSON parsed object when you need raw envelope fidelity.
41
+
42
+ ```json
43
+ // With tally-xml-tdl clean():
44
+ [
45
+ "Kgs"
46
+ ]
47
+ ```
48
+
49
+ ---
50
+
51
+ ## 🚀 Features
52
+
53
+ - 🧹 **Clean Data Out-of-the-Box**: Automatically strips Tally metadata attributes and flattens nested values.
54
+ - ⚡ **Two Extraction Modes**: Choose `clean()` for clean data arrays, or `get()` for the full parsed envelope.
55
+ - 📋 **Pre-configured Queries**: Built-in TDL queries for UOM, Stock Items, Stock Items with Base Units, and Ledgers.
56
+ - 🏎️ **Fast & Lightweight**: Built on top of `fast-xml-parser` with minimal overhead.
57
+ - 📦 **Zero-Config HTTP Transport**: Communicates directly with Tally's built-in HTTP server (`http://localhost:9000`).
58
+ - 🔷 **First-Class TypeScript Support**: Full type declarations included (`.d.ts`).
59
+ - 🌐 **Modern ESM**: Native ES modules with subpath exports (`tally-xml-tdl` and `tally-xml-tdl/v6`).
60
+
61
+ ---
62
+
63
+ ## 📦 Installation
64
+
65
+ ```bash
66
+ npm install tally-xml-tdl
67
+ ```
68
+
69
+ ### Prerequisites
70
+ Make sure Tally is running and its HTTP server is enabled:
71
+ - **Tally Prime / Tally.ERP 9**: Press `F12` (Configure) -> **Advanced Configuration** -> Set **Tally is acting as** to `Both` or `Server`, and note the port (default is `9000`).
72
+
73
+ ---
74
+
75
+ ## 💡 Quick Start
76
+
77
+ ```javascript
78
+ import { clean, get } from "tally-xml-tdl";
79
+
80
+ // 1. Fetch clean, simplified data
81
+ const items = await clean({
82
+ company: "My Company Name",
83
+ jsonId: "stockItemsWithBaseUnits"
84
+ });
85
+
86
+ console.log(items);
87
+ // Output:
88
+ // [
89
+ // { NAME: "Item A", BASEUNITS: "Nos" },
90
+ // { NAME: "Item B", BASEUNITS: "Kgs" }
91
+ // ]
92
+
93
+ // 2. Fetch raw parsed JSON (full fidelity)
94
+ const raw = await get({
95
+ company: "My Company Name",
96
+ jsonId: "uom"
97
+ });
98
+
99
+ console.log(raw);
100
+ ```
101
+
102
+ ---
103
+
104
+ ## 📚 API Reference
105
+
106
+ ### `clean(options)`
107
+ Sends the TDL query to Tally, parses the XML response, and cleans the resulting collection data.
108
+
109
+ ```typescript
110
+ clean<T = any>(options: V6Options): Promise<T>
111
+ ```
112
+
113
+ #### Options:
114
+ | Option | Type | Required | Default | Description |
115
+ | :--- | :--- | :---: | :--- | :--- |
116
+ | `company` | `string` | **Yes** | — | Name of the active company in Tally. |
117
+ | `jsonId` | `string` | **Yes** | — | ID of the pre-configured TDL query. |
118
+ | `url` | `string` | No | `"http://localhost:9000"` | Tally HTTP endpoint URL. |
119
+
120
+ ---
121
+
122
+ ### `get(options)`
123
+ Sends the TDL query to Tally and returns the complete XML response converted to a JavaScript object without stripping any metadata.
124
+
125
+ ```typescript
126
+ get<T = any>(options: V6Options): Promise<T>
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 🗂️ Built-in Query Catalog (`jsonId`)
132
+
133
+ | `jsonId` | Target Entity | Description | Fields Fetched |
134
+ | :--- | :--- | :--- | :--- |
135
+ | `"uom"` | Units of Measure | List of units of measurement | `$$Alias:Name` |
136
+ | `"stockItems"` | Stock Items | List of inventory stock items | `$$Alias:Name` |
137
+ | `"stockItemsWithBaseUnits"` | Stock Items | Inventory stock items with their base units | `$$Alias:Name`, `BaseUnits` |
138
+ | `"ledgerNames"` | Ledgers | List of accounting ledgers | `$$Alias:Name` |
139
+
140
+ ---
141
+
142
+ ## 🎯 Advanced Usage
143
+
144
+ ### Connecting to a Remote or Custom Port
145
+ If Tally is hosted on another machine or running on a non-default port:
146
+
147
+ ```javascript
148
+ import { clean } from "tally-xml-tdl";
149
+
150
+ const data = await clean({
151
+ company: "Acme Corp",
152
+ jsonId: "ledgerNames",
153
+ url: "http://192.168.1.100:9005"
154
+ });
155
+ ```
156
+
157
+ ### Subpath Import
158
+ You can also explicitly import from the `v6` subpath:
159
+
160
+ ```javascript
161
+ import { clean, get } from "tally-xml-tdl/v6";
162
+ ```
163
+
164
+ ### Using Namespaces
165
+ ```javascript
166
+ import { v6 } from "tally-xml-tdl";
167
+
168
+ const data = await v6.clean({ company: "Acme Corp", jsonId: "uom" });
169
+ ```
170
+
171
+ ---
172
+
173
+ ## 📄 License
174
+
175
175
  [MIT](LICENSE) © [KeshavSoft](https://keshavsoft.com)
package/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
- export function company(showLog?: boolean): Promise<any>;
2
- export default company;
3
-
4
- export interface V6Options {
5
- company: string;
6
- jsonId: string;
7
- url?: string;
8
- }
9
-
10
- export function get<T = any>(options?: V6Options): Promise<T>;
11
- export function clean<T = any>(options?: V6Options): Promise<T>;
12
-
13
- export * as v6 from "./src/v6/index.js";
14
- export * as v13 from "./src/v13/main.js";
15
-
1
+ export function company(showLog?: boolean): Promise<any>;
2
+ export default company;
3
+
4
+ export interface V6Options {
5
+ company: string;
6
+ jsonId: string;
7
+ url?: string;
8
+ }
9
+
10
+ export function get<T = any>(options?: V6Options): Promise<T>;
11
+ export function clean<T = any>(options?: V6Options): Promise<T>;
12
+
13
+ export * as v6 from "./src/v6/index.js";
14
+ export * as v13 from "./src/v13/main.js";
15
+
package/package.json CHANGED
@@ -1,32 +1,21 @@
1
1
  {
2
2
  "name": "tally-xml-tdl",
3
- "version": "1.13.2",
3
+ "version": "1.15.1",
4
4
  "description": "Simple, clean TDL and XML extraction and manipulation tools for Tally",
5
5
  "type": "module",
6
- "main": "src/v13/main.js",
7
- "module": "./src/v13/main.js",
6
+ "main": "src/v15/main.js",
7
+ "module": "./src/v15/main.js",
8
8
  "types": "index.d.ts",
9
9
  "sideEffects": false,
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./index.d.ts",
13
- "import": "./src/v13/main.js",
14
- "default": "./src/v13/main.js"
15
- },
16
- "./v13": {
17
- "types": "./src/v13/main.d.ts",
18
- "import": "./src/v13/main.js",
19
- "default": "./src/v13/main.js"
20
- },
21
- "./v6": {
22
- "types": "./src/v6/index.d.ts",
23
- "import": "./src/v6/index.js",
24
- "default": "./src/v6/index.js"
13
+ "import": "./src/v15/main.js",
14
+ "default": "./src/v15/main.js"
25
15
  }
26
16
  },
27
17
  "files": [
28
- "src/v13",
29
- "src/v6",
18
+ "src/v15",
30
19
  "src/index.js",
31
20
  "index.js",
32
21
  "index.d.ts",
package/src/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export * from "./v13/main.js";
2
- export * as v13 from "./v13/main.js";
1
+ export * from "./v15/main.js";
2
+ export * as masters from "./v15/masters.js";
@@ -11,6 +11,7 @@ const body = fs.readFileSync(path.join(__dirname, "body.xml"), "utf8");
11
11
 
12
12
  const startFunc = async (showLog = false) => {
13
13
  const jsonId = "company";
14
+ // console.log("------------ : ", jsonId);
14
15
 
15
16
  const { tdlMessage } = bodyJson[jsonId];
16
17
  if (showLog) console.log("tdlMessage : ", tdlMessage);
@@ -18,11 +19,12 @@ const startFunc = async (showLog = false) => {
18
19
  const xml = buildXml(body, {
19
20
  tdlMessage
20
21
  });
22
+
21
23
  if (showLog) console.log("xml : ", xml);
24
+
22
25
  const jsonToReturn = await executeXmlAndClean(xml);
26
+
23
27
  if (showLog) console.log("jsonToReturn : ", jsonToReturn);
24
- // const firstRow = jsonToReturn[0];
25
- // const { "@_NAME": name, "@_RESERVEDNAME": reservedName } = firstRow;
26
28
 
27
29
  return jsonToReturn;
28
30
  };
@@ -1,111 +1,111 @@
1
- /**
2
- * The Story of Tally Type Conversion:
3
- *
4
- * Act 1 - The XML Artifacts:
5
- * Tally XML represents typed primitive fields with attributes such as `@_TYPE="String"`,
6
- * `@_TYPE="Date"`, or `@_TYPE="Logical"`. The XML parser converts these tags into
7
- * objects where `#text` holds the actual content and `@_TYPE` holds the data type.
8
- * Tags with empty content (e.g. <SERIALMASTER TYPE="String"/>) result in objects
9
- * containing only `@_TYPE` without a `#text` key.
10
- *
11
- * Act 2 - The String Story:
12
- * When `@_TYPE === "String"`, we extract `#text` as a clean string.
13
- * If `#text` is missing (empty element), we normalize it to an empty string `""`
14
- * rather than keeping the unparsed metadata object.
15
- *
16
- * Act 3 - The Date Story:
17
- * When `@_TYPE === "Date"`, Tally returns dates in YYYYMMDD format (e.g., 20260904).
18
- * XML parsers often parse this as a numeric literal (20260904). We safely normalize
19
- * this into a clean string representation `'20260904'` for consistent date formatting.
20
- *
21
- * Act 4 - Logical & Other Primitives:
22
- * When `@_TYPE === "Logical"` (values like 'Yes' / 'No') or other typed fields,
23
- * we unwrap `#text` to its plain scalar value while leaving nested child arrays
24
- * (e.g., inventory lines, ledger entries) intact.
25
- */
26
-
27
- /**
28
- * Normalizes a single field value based on its Tally XML type.
29
- *
30
- * @param {object} params
31
- * @param {*} params.inValue - The field value to inspect and transform.
32
- * @returns {*} The cleaned scalar or original complex structure.
33
- */
34
- const handleValue = ({ inValue }) => {
35
- const localValue = inValue;
36
-
37
- // Preserve null, undefined, primitives, and nested arrays (e.g. ALLINVENTORYENTRIES.LIST)
38
- if (!localValue || typeof localValue !== "object" || Array.isArray(localValue)) {
39
- return localValue;
40
- }
41
-
42
- // Process Tally typed objects with @_TYPE
43
- if ("@_TYPE" in localValue) {
44
- const type = localValue["@_TYPE"];
45
- const hasText = "#text" in localValue;
46
- const rawText = localValue["#text"];
47
-
48
- // Act 2 fallback: Empty tags (<FIELD TYPE="..."/>) have no #text
49
- if (!hasText) {
50
- return "";
51
- }
52
-
53
- switch (type) {
54
- // Act 2: String types
55
- case "String":
56
- return String(rawText);
57
-
58
- // Act 3: Date types (normalize numbers like 20260904 to string '20260904')
59
- case "Date":
60
- return String(rawText);
61
-
62
- // Act 4: Logical types ('Yes' / 'No')
63
- case "Logical":
64
- return String(rawText);
65
-
66
- // Other types (Number, Amount, Rate, etc.)
67
- default:
68
- return rawText;
69
- }
70
- }
71
-
72
- return localValue;
73
- };
74
-
75
- /**
76
- * Transforms an array of rows by converting Tally typed objects into clean values.
77
- *
78
- * @param {object|Array} input - Either { inArray: Array } or positional Array.
79
- * @returns {Array} Array of cleaned rows.
80
- */
81
- const changeTypeString = (input) => {
82
- // Supports both { inArray } parameter convention and direct array argument
83
- let localArray = Array.isArray(input) ? input : input?.inArray;
84
-
85
- if (!localArray) {
86
- return [];
87
- }
88
-
89
- if (!Array.isArray(localArray)) {
90
- localArray = [localArray];
91
- }
92
-
93
- const newArray = localArray.map(row => {
94
- if (!row || typeof row !== "object") {
95
- return row;
96
- }
97
-
98
- const result = {};
99
-
100
- for (const [key, value] of Object.entries(row)) {
101
- result[key] = handleValue({ inValue: value });
102
- }
103
-
104
- return result;
105
- });
106
-
107
- return newArray;
108
- };
109
-
110
- export default changeTypeString;
111
- export { changeTypeString };
1
+ /**
2
+ * The Story of Tally Type Conversion:
3
+ *
4
+ * Act 1 - The XML Artifacts:
5
+ * Tally XML represents typed primitive fields with attributes such as `@_TYPE="String"`,
6
+ * `@_TYPE="Date"`, or `@_TYPE="Logical"`. The XML parser converts these tags into
7
+ * objects where `#text` holds the actual content and `@_TYPE` holds the data type.
8
+ * Tags with empty content (e.g. <SERIALMASTER TYPE="String"/>) result in objects
9
+ * containing only `@_TYPE` without a `#text` key.
10
+ *
11
+ * Act 2 - The String Story:
12
+ * When `@_TYPE === "String"`, we extract `#text` as a clean string.
13
+ * If `#text` is missing (empty element), we normalize it to an empty string `""`
14
+ * rather than keeping the unparsed metadata object.
15
+ *
16
+ * Act 3 - The Date Story:
17
+ * When `@_TYPE === "Date"`, Tally returns dates in YYYYMMDD format (e.g., 20260904).
18
+ * XML parsers often parse this as a numeric literal (20260904). We safely normalize
19
+ * this into a clean string representation `'20260904'` for consistent date formatting.
20
+ *
21
+ * Act 4 - Logical & Other Primitives:
22
+ * When `@_TYPE === "Logical"` (values like 'Yes' / 'No') or other typed fields,
23
+ * we unwrap `#text` to its plain scalar value while leaving nested child arrays
24
+ * (e.g., inventory lines, ledger entries) intact.
25
+ */
26
+
27
+ /**
28
+ * Normalizes a single field value based on its Tally XML type.
29
+ *
30
+ * @param {object} params
31
+ * @param {*} params.inValue - The field value to inspect and transform.
32
+ * @returns {*} The cleaned scalar or original complex structure.
33
+ */
34
+ const handleValue = ({ inValue }) => {
35
+ const localValue = inValue;
36
+
37
+ // Preserve null, undefined, primitives, and nested arrays (e.g. ALLINVENTORYENTRIES.LIST)
38
+ if (!localValue || typeof localValue !== "object" || Array.isArray(localValue)) {
39
+ return localValue;
40
+ }
41
+
42
+ // Process Tally typed objects with @_TYPE
43
+ if ("@_TYPE" in localValue) {
44
+ const type = localValue["@_TYPE"];
45
+ const hasText = "#text" in localValue;
46
+ const rawText = localValue["#text"];
47
+
48
+ // Act 2 fallback: Empty tags (<FIELD TYPE="..."/>) have no #text
49
+ if (!hasText) {
50
+ return "";
51
+ }
52
+
53
+ switch (type) {
54
+ // Act 2: String types
55
+ case "String":
56
+ return String(rawText);
57
+
58
+ // Act 3: Date types (normalize numbers like 20260904 to string '20260904')
59
+ case "Date":
60
+ return String(rawText);
61
+
62
+ // Act 4: Logical types ('Yes' / 'No')
63
+ case "Logical":
64
+ return String(rawText);
65
+
66
+ // Other types (Number, Amount, Rate, etc.)
67
+ default:
68
+ return rawText;
69
+ }
70
+ }
71
+
72
+ return localValue;
73
+ };
74
+
75
+ /**
76
+ * Transforms an array of rows by converting Tally typed objects into clean values.
77
+ *
78
+ * @param {object|Array} input - Either { inArray: Array } or positional Array.
79
+ * @returns {Array} Array of cleaned rows.
80
+ */
81
+ const changeTypeString = (input) => {
82
+ // Supports both { inArray } parameter convention and direct array argument
83
+ let localArray = Array.isArray(input) ? input : input?.inArray;
84
+
85
+ if (!localArray) {
86
+ return [];
87
+ }
88
+
89
+ if (!Array.isArray(localArray)) {
90
+ localArray = [localArray];
91
+ }
92
+
93
+ const newArray = localArray.map(row => {
94
+ if (!row || typeof row !== "object") {
95
+ return row;
96
+ }
97
+
98
+ const result = {};
99
+
100
+ for (const [key, value] of Object.entries(row)) {
101
+ result[key] = handleValue({ inValue: value });
102
+ }
103
+
104
+ return result;
105
+ });
106
+
107
+ return newArray;
108
+ };
109
+
110
+ export default changeTypeString;
111
+ export { changeTypeString };