medusajs-documents-plugin 1.1.15 → 1.1.17
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,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractBaseReference = extractBaseReference;
|
|
4
|
+
exports.getDocTypePriority = getDocTypePriority;
|
|
5
|
+
exports.getDocLifecycleOrder = getDocLifecycleOrder;
|
|
6
|
+
exports.getDocTypeLabel = getDocTypeLabel;
|
|
7
|
+
exports.groupDocuments = groupDocuments;
|
|
8
|
+
/**
|
|
9
|
+
* Extracts normalized base reference by stripping known document prefixes.
|
|
10
|
+
* Examples:
|
|
11
|
+
* - "JOB-1792156525673-SNL9QH" -> "1792156525673-SNL9QH"
|
|
12
|
+
* - "RT-1792156525673-SNL9QH" -> "1792156525673-SNL9QH"
|
|
13
|
+
* - "EST-1792156525673-SNL9QH" -> "1792156525673-SNL9QH"
|
|
14
|
+
* - "RCPT-1792156525673-SNL9QH" -> "1792156525673-SNL9QH"
|
|
15
|
+
* - "INV-1001" -> "1001"
|
|
16
|
+
* - "RCPT-1001" -> "1001"
|
|
17
|
+
* - "PKG-1001" -> "1001"
|
|
18
|
+
*/
|
|
19
|
+
function extractBaseReference(displayId) {
|
|
20
|
+
if (!displayId)
|
|
21
|
+
return "";
|
|
22
|
+
const cleaned = String(displayId).trim();
|
|
23
|
+
// Strip repeated prefixes like JOB-, RT-, EST-, RCPT-, INV-, PKG-, DOC-
|
|
24
|
+
const withoutPrefix = cleaned.replace(/^(JOB-|RT-|EST-|RCPT-|INV-|PKG-|DOC-)+/i, "");
|
|
25
|
+
return withoutPrefix.trim();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Priority scoring for electing the primary document representing a pack.
|
|
29
|
+
* An Invoice is always top priority if available in the pack.
|
|
30
|
+
*/
|
|
31
|
+
function getDocTypePriority(type) {
|
|
32
|
+
switch (type?.toLowerCase()) {
|
|
33
|
+
case "invoice":
|
|
34
|
+
return 100; // Invoices always take top precedence
|
|
35
|
+
case "estimate":
|
|
36
|
+
return 80;
|
|
37
|
+
case "job_card":
|
|
38
|
+
return 70;
|
|
39
|
+
case "receipt":
|
|
40
|
+
return 60;
|
|
41
|
+
case "packing_slip":
|
|
42
|
+
return 50;
|
|
43
|
+
case "custom":
|
|
44
|
+
return 40;
|
|
45
|
+
default:
|
|
46
|
+
return 10;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Workflow lifecycle ordering for sorting related documents inside an expanded pack.
|
|
51
|
+
* 1. Job Card -> 2. Quote/Estimate -> 3. Invoice -> 4. Receipt -> 5. Packing Slip -> 6. Custom
|
|
52
|
+
*/
|
|
53
|
+
function getDocLifecycleOrder(type) {
|
|
54
|
+
switch (type?.toLowerCase()) {
|
|
55
|
+
case "job_card":
|
|
56
|
+
return 1;
|
|
57
|
+
case "estimate":
|
|
58
|
+
return 2;
|
|
59
|
+
case "invoice":
|
|
60
|
+
return 3;
|
|
61
|
+
case "receipt":
|
|
62
|
+
return 4;
|
|
63
|
+
case "packing_slip":
|
|
64
|
+
return 5;
|
|
65
|
+
case "custom":
|
|
66
|
+
return 6;
|
|
67
|
+
default:
|
|
68
|
+
return 10;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Human-friendly title or label for a document type.
|
|
73
|
+
*/
|
|
74
|
+
function getDocTypeLabel(type) {
|
|
75
|
+
switch (type?.toLowerCase()) {
|
|
76
|
+
case "invoice":
|
|
77
|
+
return "Invoice";
|
|
78
|
+
case "estimate":
|
|
79
|
+
return "Quote";
|
|
80
|
+
case "job_card":
|
|
81
|
+
return "Job Card";
|
|
82
|
+
case "receipt":
|
|
83
|
+
return "Receipt";
|
|
84
|
+
case "packing_slip":
|
|
85
|
+
return "Packing Slip";
|
|
86
|
+
case "custom":
|
|
87
|
+
return "Certificate";
|
|
88
|
+
default:
|
|
89
|
+
return type || "Document";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Groups related documents by job or order using a multi-tier correlation algorithm.
|
|
94
|
+
* 1. Ties documents by repair_ticket_id
|
|
95
|
+
* 2. Ties documents by order_id
|
|
96
|
+
* 3. Ties documents by normalized reference base suffix (e.g. 1792156525673-SNL9QH or 1001)
|
|
97
|
+
*/
|
|
98
|
+
function groupDocuments(documents) {
|
|
99
|
+
if (!documents || documents.length === 0)
|
|
100
|
+
return [];
|
|
101
|
+
// Disjoint Set Union (DSU) to cluster documents sharing any correlation key
|
|
102
|
+
const parent = new Map();
|
|
103
|
+
function find(x) {
|
|
104
|
+
if (!parent.has(x))
|
|
105
|
+
parent.set(x, x);
|
|
106
|
+
if (parent.get(x) !== x) {
|
|
107
|
+
parent.set(x, find(parent.get(x)));
|
|
108
|
+
}
|
|
109
|
+
return parent.get(x);
|
|
110
|
+
}
|
|
111
|
+
function union(x, y) {
|
|
112
|
+
const rootX = find(x);
|
|
113
|
+
const rootY = find(y);
|
|
114
|
+
if (rootX !== rootY) {
|
|
115
|
+
parent.set(rootY, rootX);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Assign keys to each document
|
|
119
|
+
const docKeys = new Map();
|
|
120
|
+
for (const doc of documents) {
|
|
121
|
+
const keys = [`doc:${doc.id}`];
|
|
122
|
+
if (doc.repair_ticket_id) {
|
|
123
|
+
keys.push(`rep:${doc.repair_ticket_id}`);
|
|
124
|
+
}
|
|
125
|
+
if (doc.order_id) {
|
|
126
|
+
keys.push(`ord:${doc.order_id}`);
|
|
127
|
+
}
|
|
128
|
+
const baseRef = extractBaseReference(doc.display_id);
|
|
129
|
+
if (baseRef && baseRef.length >= 3) {
|
|
130
|
+
// Long references like 1792156525673-SNL9QH are globally unique
|
|
131
|
+
if (baseRef.length >= 6 || /[A-Za-z]/.test(baseRef)) {
|
|
132
|
+
keys.push(`ref:${baseRef.toLowerCase()}`);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Short references like 1001 correlate within the same customer or order
|
|
136
|
+
const custKey = (doc.customer_email || doc.customer_phone || doc.customer_name || "").toLowerCase().trim();
|
|
137
|
+
keys.push(`ref:${baseRef.toLowerCase()}${custKey ? `:${custKey}` : ""}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
docKeys.set(doc.id, keys);
|
|
141
|
+
// Union all keys of this document together
|
|
142
|
+
const primaryKey = keys[0];
|
|
143
|
+
for (let i = 1; i < keys.length; i++) {
|
|
144
|
+
union(primaryKey, keys[i]);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Connect multiple documents that share any key
|
|
148
|
+
const keyToDocs = new Map();
|
|
149
|
+
for (const [docId, keys] of docKeys.entries()) {
|
|
150
|
+
for (const k of keys) {
|
|
151
|
+
if (!keyToDocs.has(k))
|
|
152
|
+
keyToDocs.set(k, []);
|
|
153
|
+
keyToDocs.get(k).push(docId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
for (const [, docIds] of keyToDocs.entries()) {
|
|
157
|
+
if (docIds.length > 1) {
|
|
158
|
+
const firstRoot = `doc:${docIds[0]}`;
|
|
159
|
+
for (let i = 1; i < docIds.length; i++) {
|
|
160
|
+
union(firstRoot, `doc:${docIds[i]}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Aggregate documents by root cluster
|
|
165
|
+
const clusters = new Map();
|
|
166
|
+
for (const doc of documents) {
|
|
167
|
+
const root = find(`doc:${doc.id}`);
|
|
168
|
+
if (!clusters.has(root))
|
|
169
|
+
clusters.set(root, []);
|
|
170
|
+
clusters.get(root).push(doc);
|
|
171
|
+
}
|
|
172
|
+
// Build DocumentGroup objects
|
|
173
|
+
const groups = [];
|
|
174
|
+
for (const [clusterId, clusterDocs] of clusters.entries()) {
|
|
175
|
+
// Sort documents in lifecycle order: job_card -> estimate -> invoice -> receipt -> packing_slip -> custom
|
|
176
|
+
clusterDocs.sort((a, b) => {
|
|
177
|
+
const orderA = getDocLifecycleOrder(a.type);
|
|
178
|
+
const orderB = getDocLifecycleOrder(b.type);
|
|
179
|
+
if (orderA !== orderB)
|
|
180
|
+
return orderA - orderB;
|
|
181
|
+
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
|
|
182
|
+
});
|
|
183
|
+
// Elect primary document: Highest priority (Invoice if available, else Estimate/Job Card)
|
|
184
|
+
let primaryDoc = clusterDocs[0];
|
|
185
|
+
let maxPriority = -1;
|
|
186
|
+
for (const d of clusterDocs) {
|
|
187
|
+
const p = getDocTypePriority(d.type);
|
|
188
|
+
if (p > maxPriority) {
|
|
189
|
+
maxPriority = p;
|
|
190
|
+
primaryDoc = d;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const hasInvoice = clusterDocs.some((d) => d.type?.toLowerCase() === "invoice");
|
|
194
|
+
// Group total amount: prefer Invoice total, then Estimate total, then Receipt total, then primaryDoc total
|
|
195
|
+
const invoiceDoc = clusterDocs.find((d) => d.type?.toLowerCase() === "invoice");
|
|
196
|
+
const estimateDoc = clusterDocs.find((d) => d.type?.toLowerCase() === "estimate");
|
|
197
|
+
const receiptDoc = clusterDocs.find((d) => d.type?.toLowerCase() === "receipt");
|
|
198
|
+
const totalAmount = Number((invoiceDoc && invoiceDoc.total_amount != null)
|
|
199
|
+
? invoiceDoc.total_amount
|
|
200
|
+
: (estimateDoc && estimateDoc.total_amount != null)
|
|
201
|
+
? estimateDoc.total_amount
|
|
202
|
+
: (receiptDoc && receiptDoc.total_amount != null)
|
|
203
|
+
? receiptDoc.total_amount
|
|
204
|
+
: primaryDoc.total_amount || 0);
|
|
205
|
+
// Group currency
|
|
206
|
+
const currencyCode = primaryDoc.currency_code || invoiceDoc?.currency_code || "kes";
|
|
207
|
+
// Summary of included types
|
|
208
|
+
const uniqueTypes = Array.from(new Set(clusterDocs.map((d) => getDocTypeLabel(d.type))));
|
|
209
|
+
const includedTypesSummary = uniqueTypes.join(" • ");
|
|
210
|
+
// Latest created_at timestamp
|
|
211
|
+
const latestCreatedAt = clusterDocs.reduce((latest, d) => {
|
|
212
|
+
return new Date(d.created_at) > new Date(latest) ? d.created_at : latest;
|
|
213
|
+
}, clusterDocs[0].created_at);
|
|
214
|
+
// Build human-friendly groupKey
|
|
215
|
+
let groupKey = primaryDoc.display_id;
|
|
216
|
+
if (primaryDoc.repair_ticket_id) {
|
|
217
|
+
groupKey = `Repair ${primaryDoc.display_id}`;
|
|
218
|
+
}
|
|
219
|
+
else if (primaryDoc.order_id) {
|
|
220
|
+
groupKey = `Order #${primaryDoc.display_id}`;
|
|
221
|
+
}
|
|
222
|
+
groups.push({
|
|
223
|
+
groupId: clusterId,
|
|
224
|
+
groupKey,
|
|
225
|
+
primaryDoc,
|
|
226
|
+
documents: clusterDocs,
|
|
227
|
+
hasInvoice,
|
|
228
|
+
totalAmount,
|
|
229
|
+
currencyCode,
|
|
230
|
+
customerName: primaryDoc.customer_name || "Valued Customer",
|
|
231
|
+
customerEmail: primaryDoc.customer_email || clusterDocs.find((d) => d.customer_email)?.customer_email,
|
|
232
|
+
customerPhone: primaryDoc.customer_phone || clusterDocs.find((d) => d.customer_phone)?.customer_phone,
|
|
233
|
+
latestCreatedAt,
|
|
234
|
+
includedTypesSummary,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
// Sort groups by latest activity (most recent first)
|
|
238
|
+
groups.sort((a, b) => new Date(b.latestCreatedAt).getTime() - new Date(a.latestCreatedAt).getTime());
|
|
239
|
+
return groups;
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG9jdW1lbnQtZ3JvdXBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy91dGlscy9kb2N1bWVudC1ncm91cGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBaURBLG9EQU1DO0FBTUQsZ0RBaUJDO0FBTUQsb0RBaUJDO0FBS0QsMENBaUJDO0FBUUQsd0NBbUtDO0FBaFFEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxTQUFnQixvQkFBb0IsQ0FBQyxTQUFrQjtJQUNyRCxJQUFJLENBQUMsU0FBUztRQUFFLE9BQU8sRUFBRSxDQUFDO0lBQzFCLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN6Qyx3RUFBd0U7SUFDeEUsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyx5Q0FBeUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNyRixPQUFPLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUM5QixDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBZ0Isa0JBQWtCLENBQUMsSUFBYTtJQUM5QyxRQUFRLElBQUksRUFBRSxXQUFXLEVBQUUsRUFBRSxDQUFDO1FBQzVCLEtBQUssU0FBUztZQUNaLE9BQU8sR0FBRyxDQUFDLENBQUMsc0NBQXNDO1FBQ3BELEtBQUssVUFBVTtZQUNiLE9BQU8sRUFBRSxDQUFDO1FBQ1osS0FBSyxVQUFVO1lBQ2IsT0FBTyxFQUFFLENBQUM7UUFDWixLQUFLLFNBQVM7WUFDWixPQUFPLEVBQUUsQ0FBQztRQUNaLEtBQUssY0FBYztZQUNqQixPQUFPLEVBQUUsQ0FBQztRQUNaLEtBQUssUUFBUTtZQUNYLE9BQU8sRUFBRSxDQUFDO1FBQ1o7WUFDRSxPQUFPLEVBQUUsQ0FBQztJQUNkLENBQUM7QUFDSCxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBZ0Isb0JBQW9CLENBQUMsSUFBYTtJQUNoRCxRQUFRLElBQUksRUFBRSxXQUFXLEVBQUUs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
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [1.1.16] - 2026-10-16
|
|
11
|
+
|
|
12
|
+
### Group & Pack Related Documents by Job / Order
|
|
13
|
+
|
|
14
|
+
- **Packed Document Rows by Job / Order**:
|
|
15
|
+
- Automatically clusters related documents belonging to the same repair or order into a single collapsed master row to dramatically save vertical space.
|
|
16
|
+
- Multi-tier correlation algorithm (`src/utils/document-grouper.ts`):
|
|
17
|
+
- Correlates by `repair_ticket_id`
|
|
18
|
+
- Correlates by `order_id`
|
|
19
|
+
- Correlates by normalized base reference number suffix (e.g. `1792156525673-SNL9QH` shared between `JOB-`, `RT-`, `EST-`, `RCPT-`, and `1001` shared between `INV-`, `RCPT-`, `PKG-`).
|
|
20
|
+
- **Primary Invoice Priority in Collapsed View**:
|
|
21
|
+
- If an **Invoice** is present in the pack, the collapsed master row prominently displays the **Invoice** display ID, amount, and payment status.
|
|
22
|
+
- If an invoice is not yet generated, primary representation gracefully falls back to Quote/Estimate or Job Card.
|
|
23
|
+
- Reference column features a count badge (e.g. `4 docs`) and an expandable chevron (`▼` / `▶`).
|
|
24
|
+
- Type column shows a clear summary of included documents: `Includes: Job Card • Quote • Invoice • Receipt`.
|
|
25
|
+
- **Inline Accordion Sub-Table & Quick Dropdown Menu**:
|
|
26
|
+
- Clicking the chevron or row expands an indented nested sub-table directly below the parent row with distinct color-coded badges for every document in the workflow:
|
|
27
|
+
- `Job Card` (orange)
|
|
28
|
+
- `Quote / Estimate` (blue)
|
|
29
|
+
- `Tax Invoice` (green / purple)
|
|
30
|
+
- `Payment Receipt` (emerald / purple)
|
|
31
|
+
- `Packing Slip` (amber / grey)
|
|
32
|
+
- Every child document includes clickable PDF view/download, status badge, amount, source, created date, and individual actions (View, Send, Push Zoho, Copy shortlink).
|
|
33
|
+
- Added a "Related Docs" quick-action dropdown menu (`Docs (4) ▼`) directly on the master row for rapid one-click access.
|
|
34
|
+
- **Pack by Job / Order Toolbar Switch**:
|
|
35
|
+
- Added a toggle switch in the Admin Hub toolbar to easily switch between the packed view (enabled by default) and the flat document list anytime.
|
|
36
|
+
- **Comprehensive Test Suite**:
|
|
37
|
+
- Added `tests/document-grouping.test.ts` validating base reference extraction, priority scoring, multi-tier clustering, and lifecycle ordering (48/48 unit tests passing).
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
10
41
|
## [1.1.14] - 2026-10-16
|
|
11
42
|
|
|
12
43
|
### Company Name Support, Shortlink 500 Fix, Duplicate Prefix Cleanup & Currency Improvements
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "medusajs-documents-plugin",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.17",
|
|
4
4
|
"description": "Comprehensive document generation and management module for Medusa v2 with Zoho Books integration, PDFKit fallback, QR codes, shortlinks, multi-channel notifications and unified admin hub.",
|
|
5
5
|
"author": "Urban Device Care <info@urbandevicecare.co.uk>",
|
|
6
6
|
"license": "MIT",
|