medusa-pos-plugin 0.0.34 → 0.0.36

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,434 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_BARCODE_TEMPLATE_HTML = void 0;
4
+ /**
5
+ * Default 2in x 3in hang-tag label. Placeholders: {{ALL_TAGS_HTML}} plus, inside
6
+ * <template id="single-tag">, {{PRODUCT_NAME}} {{OPTIONS_HTML}} {{SKU_HTML}} {{PRICE_HTML}}
7
+ * {{CONTACT_DETAILS}} {{BARCODE_SVG_ID}}. Hosts override via templates/barcode-template.html.
8
+ */
9
+ exports.DEFAULT_BARCODE_TEMPLATE_HTML = `<!DOCTYPE html>
10
+ <html>
11
+ <head>
12
+ <meta charset="UTF-8" />
13
+ <style>
14
+ /*
15
+ Hang-tag portrait 2in × 3in — compact spacing so all details fit.
16
+ Top: product + options
17
+ Mid: price, then horizontal barcode centered
18
+ Bottom: country / warehouse / contact + star
19
+ */
20
+ * { box-sizing: border-box; }
21
+
22
+ html, body {
23
+ font-family: Arial, Helvetica, sans-serif;
24
+ margin: 0;
25
+ padding: 0;
26
+ background: #fff;
27
+ }
28
+
29
+ .tags-wrapper {
30
+ display: block;
31
+ margin: 0;
32
+ padding: 0;
33
+ }
34
+
35
+ .print-sheet {
36
+ display: block;
37
+ width: 2in;
38
+ height: 3in;
39
+ margin: 0;
40
+ padding: 0;
41
+ overflow: hidden;
42
+ position: relative;
43
+ background: #fff;
44
+ page-break-after: always;
45
+ break-after: page;
46
+ page-break-inside: avoid;
47
+ break-inside: avoid;
48
+ }
49
+ .print-sheet:last-child {
50
+ page-break-after: auto;
51
+ break-after: auto;
52
+ }
53
+
54
+ .tag-container,
55
+ .tag-clip {
56
+ position: relative;
57
+ width: 2in;
58
+ height: 3in;
59
+ margin: 0;
60
+ padding: 0;
61
+ overflow: hidden;
62
+ background: #fff;
63
+ }
64
+
65
+ @media screen {
66
+ html, body { background: #e8e8e8; }
67
+ .tags-wrapper { padding: 12px; }
68
+ .print-sheet {
69
+ margin: 0 0 12px 0;
70
+ border: 1px solid #bbb;
71
+ page-break-after: auto;
72
+ break-after: auto;
73
+ }
74
+ }
75
+
76
+ @media print {
77
+ @page {
78
+ margin: 0 !important;
79
+ size: 2in 3in;
80
+ }
81
+ html, body {
82
+ margin: 0 !important;
83
+ padding: 0 !important;
84
+ width: 2in !important;
85
+ background: #fff !important;
86
+ -webkit-print-color-adjust: exact;
87
+ print-color-adjust: exact;
88
+ }
89
+ .tags-wrapper {
90
+ display: block !important;
91
+ margin: 0 !important;
92
+ padding: 0 !important;
93
+ }
94
+ .print-sheet {
95
+ display: block !important;
96
+ width: 2in !important;
97
+ height: 3in !important;
98
+ margin: 0 !important;
99
+ border: none !important;
100
+ overflow: hidden !important;
101
+ page-break-after: always !important;
102
+ break-after: page !important;
103
+ page-break-inside: avoid !important;
104
+ break-inside: avoid !important;
105
+ }
106
+ .print-sheet:last-child {
107
+ page-break-after: auto !important;
108
+ break-after: auto !important;
109
+ }
110
+ .tag-container,
111
+ .tag-clip {
112
+ width: 2in !important;
113
+ height: 3in !important;
114
+ overflow: hidden !important;
115
+ }
116
+ .mrp-crossed,
117
+ .sale-price,
118
+ .mrp-only,
119
+ .price-label {
120
+ color: #000 !important;
121
+ -webkit-print-color-adjust: exact !important;
122
+ print-color-adjust: exact !important;
123
+ }
124
+ .mrp-crossed {
125
+ text-decoration: line-through !important;
126
+ text-decoration-color: #000 !important;
127
+ }
128
+ }
129
+
130
+ .tag-layout {
131
+ display: flex;
132
+ flex-direction: column;
133
+ width: 2in;
134
+ height: 3in;
135
+ /* Extra left padding — thermal printers clip sticker left edge */
136
+ padding: 0.08in 0.08in 0.10in 0.20in;
137
+ gap: 0;
138
+ }
139
+
140
+ .tag-header {
141
+ flex: 0 0 auto;
142
+ margin: 0 0 0.035in 0;
143
+ }
144
+
145
+ .product-name {
146
+ font-size: 10px;
147
+ font-weight: 800;
148
+ text-transform: uppercase;
149
+ line-height: 1.12;
150
+ word-break: break-word;
151
+ margin: 0 0 0.025in 0;
152
+ display: block;
153
+ color: #000;
154
+ }
155
+ .option-line {
156
+ display: grid;
157
+ grid-template-columns: 0.48in auto;
158
+ column-gap: 0.08em;
159
+ align-items: baseline;
160
+ font-size: 8.5px;
161
+ font-weight: 700;
162
+ text-transform: uppercase;
163
+ line-height: 1.15;
164
+ margin: 0.01in 0 0 0;
165
+ color: #000;
166
+ }
167
+ .option-label {
168
+ font-weight: 400;
169
+ color: #333;
170
+ text-align: left;
171
+ white-space: nowrap;
172
+ }
173
+ .option-colon {
174
+ display: none;
175
+ }
176
+ .option-value {
177
+ font-weight: 700;
178
+ color: #000;
179
+ min-width: 0;
180
+ }
181
+ .sku-inline {
182
+ font-size: 7px;
183
+ color: #333;
184
+ text-transform: uppercase;
185
+ line-height: 1.1;
186
+ display: block;
187
+ margin-top: 0.02in;
188
+ }
189
+
190
+ /* Price tight under options */
191
+ .tag-price {
192
+ flex: 0 0 auto;
193
+ margin: 0 0 0.035in 0;
194
+ max-width: 100%;
195
+ }
196
+
197
+ .price-label {
198
+ font-size: 7px;
199
+ color: #000;
200
+ text-transform: uppercase;
201
+ display: block;
202
+ line-height: 1.1;
203
+ margin-top: 0.03in;
204
+ font-weight: 700;
205
+ letter-spacing: 0.01em;
206
+ }
207
+ .price-label:first-child { margin-top: 0; }
208
+
209
+ .price-row {
210
+ display: flex;
211
+ flex-direction: row;
212
+ align-items: baseline;
213
+ justify-content: flex-start;
214
+ gap: 0.12in;
215
+ margin-top: 0.02in;
216
+ }
217
+ .mrp-crossed {
218
+ font-size: 15px;
219
+ font-weight: 800;
220
+ color: #000 !important;
221
+ display: inline-block;
222
+ line-height: 1.1;
223
+ position: relative;
224
+ text-decoration: line-through;
225
+ text-decoration-color: #000;
226
+ text-decoration-thickness: 1.5px;
227
+ flex: 0 0 auto;
228
+ }
229
+ .mrp-crossed::after {
230
+ content: "";
231
+ position: absolute;
232
+ left: 0;
233
+ right: 0;
234
+ top: 50%;
235
+ border-top: 1.5px solid #000;
236
+ transform: translateY(-50%);
237
+ }
238
+ .sale-price,
239
+ .mrp-only {
240
+ font-size: 15px;
241
+ font-weight: 800;
242
+ color: #000 !important;
243
+ line-height: 1.05;
244
+ display: block;
245
+ margin-top: 0;
246
+ flex: 0 0 auto;
247
+ }
248
+ .mrp-only {
249
+ margin-top: 0.02in;
250
+ font-size: 17px;
251
+ font-weight: 900;
252
+ }
253
+ .tax-note {
254
+ font-size: 6px;
255
+ color: #000;
256
+ display: block;
257
+ line-height: 1.1;
258
+ margin-top: 0.025in;
259
+ }
260
+
261
+ /* Horizontal barcode centered across the tag width */
262
+ .tag-barcode-rail {
263
+ flex: 0 0 auto;
264
+ width: 100%;
265
+ height: 0.66in;
266
+ position: relative;
267
+ display: flex;
268
+ align-items: center;
269
+ justify-content: center;
270
+ overflow: hidden;
271
+ margin: 0.02in 0 0.05in 0;
272
+ }
273
+ .tag-barcode-rail .barcode-rotate {
274
+ display: block;
275
+ transform: none;
276
+ -webkit-transform: none;
277
+ }
278
+ .tag-barcode-rail canvas,
279
+ .tag-barcode-rail .barcode-img {
280
+ display: block;
281
+ width: 1.55in !important;
282
+ height: auto !important;
283
+ max-width: 1.55in;
284
+ max-height: 0.62in;
285
+ image-rendering: pixelated;
286
+ }
287
+
288
+ /* Footer takes remaining space so warehouse/contact stay visible */
289
+ .tag-footer {
290
+ flex: 1 1 auto;
291
+ min-height: 0;
292
+ display: flex;
293
+ flex-direction: row;
294
+ align-items: flex-start;
295
+ justify-content: space-between;
296
+ gap: 0.04in;
297
+ overflow: hidden;
298
+ margin-top: 0;
299
+ }
300
+
301
+ .tag-footer-info {
302
+ flex: 1 1 auto;
303
+ min-width: 0;
304
+ overflow: hidden;
305
+ }
306
+
307
+ .info-label {
308
+ font-size: 7px;
309
+ font-weight: 700;
310
+ text-transform: uppercase;
311
+ color: #000;
312
+ display: block;
313
+ margin-bottom: 0.01in;
314
+ line-height: 1.1;
315
+ }
316
+ .info-label-mixed {
317
+ text-transform: none;
318
+ }
319
+ .info-value {
320
+ font-size: 7px;
321
+ font-weight: 600;
322
+ color: #000;
323
+ line-height: 1.2;
324
+ display: block;
325
+ word-break: break-word;
326
+ text-transform: uppercase;
327
+ }
328
+ .info-value .contact-email {
329
+ text-transform: none;
330
+ font-weight: 600;
331
+ letter-spacing: 0;
332
+ }
333
+ .info-inline {
334
+ display: block;
335
+ font-size: 7px;
336
+ font-weight: 700;
337
+ text-transform: uppercase;
338
+ color: #000;
339
+ line-height: 1.15;
340
+ }
341
+ .info-row-address {
342
+ margin-bottom: 0;
343
+ }
344
+ .complaints-block {
345
+ /* ~1 enter gap after address (SURAT line) */
346
+ margin-top: 0.14in;
347
+ }
348
+ .complaints-title {
349
+ display: block;
350
+ font-size: 7px;
351
+ font-weight: 600;
352
+ font-style: italic;
353
+ color: #000;
354
+ line-height: 1.15;
355
+ margin-bottom: 0.01in;
356
+ text-transform: none;
357
+ }
358
+ .complaints-line {
359
+ display: block;
360
+ font-size: 7px;
361
+ font-weight: 600;
362
+ color: #000;
363
+ line-height: 1.2;
364
+ word-break: break-word;
365
+ }
366
+ .complaints-line.contact-email {
367
+ text-transform: none;
368
+ letter-spacing: 0;
369
+ }
370
+ .info-row {
371
+ display: block;
372
+ margin-bottom: 0.03in;
373
+ }
374
+ .info-row:last-child { margin-bottom: 0; }
375
+
376
+ .tag-star {
377
+ flex: 0 0 auto;
378
+ width: 0.22in;
379
+ height: 0.22in;
380
+ background: #000;
381
+ color: #fff;
382
+ display: flex;
383
+ align-items: center;
384
+ justify-content: center;
385
+ font-size: 12px;
386
+ font-weight: 900;
387
+ line-height: 1;
388
+ margin-top: 0.01in;
389
+ align-self: flex-end;
390
+ }
391
+ </style>
392
+ </head>
393
+ <body>
394
+ <div class="tags-wrapper">
395
+ {{ALL_TAGS_HTML}}
396
+ </div>
397
+
398
+ <template id="single-tag">
399
+ <div class="print-sheet">
400
+ <div class="tag-container">
401
+ <div class="tag-clip">
402
+ <div class="tag-layout">
403
+ <div class="tag-header">
404
+ <div class="product-name">{{PRODUCT_NAME}}</div>
405
+ {{OPTIONS_HTML}}
406
+ {{SKU_HTML}}
407
+ </div>
408
+
409
+ <div class="tag-price">
410
+ {{PRICE_HTML}}
411
+ <span class="tax-note">Inclusive of all taxes</span>
412
+ </div>
413
+
414
+ <div class="tag-barcode-rail">
415
+ <div class="barcode-rotate">
416
+ <canvas id="barcode-{{BARCODE_SVG_ID}}"></canvas>
417
+ </div>
418
+ </div>
419
+
420
+ <div class="tag-footer">
421
+ <div class="tag-footer-info">
422
+ {{CONTACT_DETAILS}}
423
+ </div>
424
+ <div class="tag-star" aria-hidden="true">✦</div>
425
+ </div>
426
+ </div>
427
+ </div>
428
+ </div>
429
+ </div>
430
+ </template>
431
+ </body>
432
+ </html>
433
+ `;
434
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFyY29kZS10ZW1wbGF0ZS1kZWZhdWx0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL2JhcmNvZGUtdGVtcGxhdGUtZGVmYXVsdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQTs7OztHQUlHO0FBQ1UsUUFBQSw2QkFBNkIsR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztDQXdhNUMsQ0FBQSJ9
package/README.md CHANGED
@@ -93,6 +93,36 @@ Successful POS returns set `metadata.pos_returned` / `pos_returned_at`. Cancel s
93
93
 
94
94
  **Gift card refund** issues a **new** gift card for the refund amount (`expiresAt: null`). Registered customers get `assignedCustomerId`; walk-in/guest sales leave the card unassigned. POS prints a slip with the code after confirm. Order metadata stores `pos_refund_method`, `pos_gift_card_code`, and `pos_gift_card_amount` so Order Details can reprint the slip.
95
95
 
96
+ ## Barcode labels (0.0.35+)
97
+
98
+ | Method | Path | Purpose |
99
+ |--------|------|---------|
100
+ | POST | `/admin/barcode-template` | Ready-to-print label HTML for variants |
101
+ | GET | `/admin/barcode-template` | Raw template + contact details (preview) |
102
+ | POST | `/admin/barcodes/bulk-generate` | Write deterministic EAN-13 barcodes onto variants |
103
+
104
+ **`POST /admin/barcode-template`**
105
+
106
+ ```json
107
+ { "items": [{ "variant_id": "variant_1", "quantity": 5 }], "region_id": "reg_1", "print_width": 2, "print_height": 60 }
108
+ ```
109
+
110
+ Legacy `{ "variant_ids": ["variant_1"] }` still works (one label each). Quantity is capped at 500 per variant. `region_id` is optional; without it the INR region (or the first region) is used for `calculated_price`. Returns `{ html, count, variants }`. The HTML draws CODE128 barcodes with JsBarcode (loaded from jsDelivr) and opens the print dialog. Errors: `400` (no items), `404` (no variants found), `500` with `{ error }`.
111
+
112
+ **`POST /admin/barcodes/bulk-generate`** — body `{ "variantIds": ["variant_1"] }`. Builds `Product|Variant|Option=Value|Weight=…|Width=…|Length=…|Height=…`, hashes it with SHA-256 and turns it into a 13-digit EAN with prefix `200`. The same input always gives the same barcode. Returns `{ success, message, count }`.
113
+
114
+ **Template.** The plugin bundles a 2in × 3in hang-tag template. To use your own, put `templates/barcode-template.html` in the Medusa project root; it takes priority. It must contain `{{ALL_TAGS_HTML}}` and a `<template id="single-tag">` with `{{PRODUCT_NAME}}`, `{{OPTIONS_HTML}}`, `{{SKU_HTML}}`, `{{PRICE_HTML}}`, `{{CONTACT_DETAILS}}` and a `<canvas id="barcode-{{BARCODE_SVG_ID}}">`.
115
+
116
+ **Contact block.** If the `dynamic_config` service (medusa-plugin-dynamic-config) is registered, values whose keys contain `contact-phone`, `contact-email` and `contact-address` fill the footer. Without it the footer shows only "Country Of Origin India".
117
+
118
+ POS staff access follows the existing guard: both paths are manager-write, so cashier sessions cannot POST to them (print or generate). For managers, a `region_id` in the print body must match the till's assigned region.
119
+
120
+ ## Known Issues & Fixes
121
+
122
+ ### 0.0.35 — barcode routes moved from the Chocomelon host
123
+
124
+ `/admin/barcode-template` and `/admin/barcodes/bulk-generate` used to live in the Chocomelon store (`src/api/admin/...` plus `templates/barcode-template.html`). They now ship with this plugin with the same request/response shapes, so the `medusa-pos` client needs no change. **Delete the host copies after upgrading** — Medusa project routes override plugin routes, so stale host files would keep serving the old code. EAN-13 output is unchanged; existing printed labels still scan to the same variants.
125
+
96
126
  ## Build
97
127
 
98
128
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "medusa-pos-plugin",
3
- "version": "0.0.34",
3
+ "version": "0.0.36",
4
4
  "description": "Medusa v2 POS operations: draft carts, finalize, payments, returns, catalog, refunds.",
5
5
  "author": "Medusa (https://medusajs.com)",
6
6
  "license": "MIT",