seven365-zyprinter 0.4.0 → 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/README.md +115 -17
- package/dist/docs.json +369 -24
- package/dist/esm/definitions.d.ts +65 -11
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +3 -0
- package/dist/esm/web.js +4 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +4 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +4 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/Plugin/ZyprintPlugin.swift +13 -0
- package/ios/Sources/Plugin/ZywellSDK.swift +462 -44
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import Foundation
|
|
9
9
|
import CoreBluetooth
|
|
10
10
|
import Network
|
|
11
|
+
import ExternalAccessory
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
@objc public class ZywellSDK: NSObject {
|
|
@@ -79,6 +80,51 @@ import Network
|
|
|
79
80
|
scanSubnet(prefix: prefix, completion: completion)
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
@objc public func discoverUSBPrinters(completion: @escaping ([[String: Any]], String?) -> Void) {
|
|
84
|
+
// Query External Accessory framework for connected accessories
|
|
85
|
+
let accessoryManager = EAAccessoryManager.shared()
|
|
86
|
+
let connectedAccessories = accessoryManager.connectedAccessories
|
|
87
|
+
|
|
88
|
+
var usbPrinters: [[String: Any]] = []
|
|
89
|
+
|
|
90
|
+
// Filter for printer accessories
|
|
91
|
+
// Note: This requires the app to declare supported accessory protocols in Info.plist
|
|
92
|
+
// under the key "UISupportedExternalAccessoryProtocols"
|
|
93
|
+
for accessory in connectedAccessories {
|
|
94
|
+
// Check if the accessory might be a printer
|
|
95
|
+
// Common printer protocol strings include manufacturer-specific identifiers
|
|
96
|
+
let isPotentialPrinter = accessory.protocolStrings.contains { protocolString in
|
|
97
|
+
protocolString.lowercased().contains("printer") ||
|
|
98
|
+
protocolString.lowercased().contains("print") ||
|
|
99
|
+
protocolString.lowercased().contains("pos")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if isPotentialPrinter || !accessory.protocolStrings.isEmpty {
|
|
103
|
+
usbPrinters.append([
|
|
104
|
+
"identifier": String(accessory.connectionID),
|
|
105
|
+
"model": "\(accessory.manufacturer) \(accessory.name)",
|
|
106
|
+
"status": accessory.isConnected ? "ready" : "offline",
|
|
107
|
+
"connectionType": "usb",
|
|
108
|
+
"manufacturer": accessory.manufacturer,
|
|
109
|
+
"modelNumber": accessory.modelNumber,
|
|
110
|
+
"serialNumber": accessory.serialNumber,
|
|
111
|
+
"firmwareRevision": accessory.firmwareRevision,
|
|
112
|
+
"hardwareRevision": accessory.hardwareRevision,
|
|
113
|
+
"protocols": accessory.protocolStrings
|
|
114
|
+
])
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Log information about USB discovery
|
|
119
|
+
if usbPrinters.isEmpty {
|
|
120
|
+
print("ZywellSDK: No USB accessories found. Note: iOS requires MFi-certified accessories with declared protocol strings in Info.plist")
|
|
121
|
+
} else {
|
|
122
|
+
print("ZywellSDK: Found \(usbPrinters.count) potential USB printer(s)")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
completion(usbPrinters, nil)
|
|
126
|
+
}
|
|
127
|
+
|
|
82
128
|
private func scanSubnet(prefix: String, completion: @escaping ([[String: Any]], String?) -> Void) {
|
|
83
129
|
let group = DispatchGroup()
|
|
84
130
|
let queue = DispatchQueue(label: "com.seven365.printer.scan", attributes: .concurrent)
|
|
@@ -323,30 +369,267 @@ import Network
|
|
|
323
369
|
// Center align (ESC a 1)
|
|
324
370
|
printData.append(Data([0x1B, 0x61, 0x01]))
|
|
325
371
|
|
|
326
|
-
//
|
|
327
|
-
if let header =
|
|
328
|
-
|
|
372
|
+
// ========== HEADER SECTION (NEW STRUCTURE) ==========
|
|
373
|
+
if let header = template["header"] as? [String: Any] {
|
|
374
|
+
// Get header formatting
|
|
375
|
+
var sizeCode: UInt8 = 0x00
|
|
376
|
+
var headerBold = false
|
|
329
377
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
378
|
+
if let size = header["size"] {
|
|
379
|
+
sizeCode = mapHeaderSizeToCode(size)
|
|
380
|
+
}
|
|
381
|
+
if let bold = header["bold"] as? Bool {
|
|
382
|
+
headerBold = bold
|
|
335
383
|
}
|
|
336
|
-
print("ZywellSDK: Header size code: \(sizeCode)")
|
|
337
384
|
|
|
338
|
-
//
|
|
339
|
-
|
|
385
|
+
// Apply bold if needed
|
|
386
|
+
if headerBold {
|
|
387
|
+
printData.append(Data([0x1B, 0x45, 0x01])) // Bold on
|
|
388
|
+
}
|
|
340
389
|
|
|
341
|
-
|
|
390
|
+
// Set font size
|
|
391
|
+
if sizeCode != 0x00 {
|
|
392
|
+
printData.append(Data([0x1D, 0x21, sizeCode]))
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Restaurant name
|
|
396
|
+
if let restaurantName = getString(header["restaurant_name"]),
|
|
397
|
+
let nameData = (restaurantName + "\n").data(using: .utf8) {
|
|
398
|
+
printData.append(nameData)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Sub header
|
|
402
|
+
if let subHeader = getString(header["sub_header"]),
|
|
403
|
+
!subHeader.isEmpty,
|
|
404
|
+
let subData = (subHeader + "\n").data(using: .utf8) {
|
|
405
|
+
printData.append(subData)
|
|
406
|
+
}
|
|
342
407
|
|
|
343
|
-
//
|
|
344
|
-
|
|
408
|
+
// GST number
|
|
409
|
+
if let gstNumber = getString(header["gst_number"]),
|
|
410
|
+
!gstNumber.isEmpty,
|
|
411
|
+
let gstData = ("GST number: \(gstNumber)\n").data(using: .utf8) {
|
|
412
|
+
printData.append(gstData)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Reset header formatting
|
|
416
|
+
if sizeCode != 0x00 {
|
|
417
|
+
printData.append(Data([0x1D, 0x21, 0x00])) // Normal size
|
|
418
|
+
}
|
|
419
|
+
if headerBold {
|
|
420
|
+
printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
|
|
421
|
+
}
|
|
345
422
|
}
|
|
346
423
|
|
|
347
424
|
// Left align (ESC a 0)
|
|
348
425
|
printData.append(Data([0x1B, 0x61, 0x00]))
|
|
349
426
|
|
|
427
|
+
// Separator
|
|
428
|
+
if let separatorData = ("--------------------------------\n").data(using: .utf8) {
|
|
429
|
+
printData.append(separatorData)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ========== ORDER INFO SECTION ==========
|
|
433
|
+
// Template type (e.g., "Kitchen Tickets")
|
|
434
|
+
if let orderType = getString(template["order_type"]),
|
|
435
|
+
let orderTypeData = (orderType + "\n").data(using: .utf8) {
|
|
436
|
+
printData.append(orderTypeData)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Table and order number with prefix
|
|
440
|
+
var orderInfoLine = ""
|
|
441
|
+
if let tableName = getString(template["table_name"]) {
|
|
442
|
+
orderInfoLine += tableName
|
|
443
|
+
}
|
|
444
|
+
if let orderNumber = getString(template["order_number"]) {
|
|
445
|
+
if !orderInfoLine.isEmpty {
|
|
446
|
+
orderInfoLine += " | "
|
|
447
|
+
}
|
|
448
|
+
orderInfoLine += orderNumber
|
|
449
|
+
}
|
|
450
|
+
if !orderInfoLine.isEmpty,
|
|
451
|
+
let orderInfoData = (orderInfoLine + "\n").data(using: .utf8) {
|
|
452
|
+
printData.append(orderInfoData)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
// Separator for items section
|
|
457
|
+
if let separatorData = ("--------------------------------\n").data(using: .utf8) {
|
|
458
|
+
printData.append(separatorData)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// ========== ITEMS SECTION (NEW STRUCTURE) ==========
|
|
462
|
+
if let kitchen = template["kitchen"] as? [[String: Any]] {
|
|
463
|
+
// Get item formatting from item object
|
|
464
|
+
var itemSizeCode: UInt8 = 0x00
|
|
465
|
+
var itemBold = false
|
|
466
|
+
|
|
467
|
+
if let item = template["item"] as? [String: Any] {
|
|
468
|
+
if let size = item["size"] {
|
|
469
|
+
itemSizeCode = mapHeaderSizeToCode(size)
|
|
470
|
+
}
|
|
471
|
+
if let bold = item["bold"] as? Bool {
|
|
472
|
+
itemBold = bold
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Apply item formatting
|
|
477
|
+
if itemBold {
|
|
478
|
+
printData.append(Data([0x1B, 0x45, 0x01])) // Bold on
|
|
479
|
+
}
|
|
480
|
+
if itemSizeCode != 0x00 {
|
|
481
|
+
printData.append(Data([0x1D, 0x21, itemSizeCode])) // Set size
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Get modifier formatting from modifier object
|
|
485
|
+
var modifierStyle = "bullet" // default
|
|
486
|
+
var modifierSizeCode: UInt8 = 0x00
|
|
487
|
+
var modifierIndent = " " // default: medium
|
|
488
|
+
|
|
489
|
+
if let modifier = template["modifier"] as? [String: Any] {
|
|
490
|
+
if let style = getString(modifier["style"]) {
|
|
491
|
+
modifierStyle = style
|
|
492
|
+
}
|
|
493
|
+
if let size = modifier["size"] {
|
|
494
|
+
modifierSizeCode = mapHeaderSizeToCode(size)
|
|
495
|
+
}
|
|
496
|
+
if let indent = getString(modifier["indent"]) {
|
|
497
|
+
switch indent.lowercased() {
|
|
498
|
+
case "small": modifierIndent = " "
|
|
499
|
+
case "medium": modifierIndent = " "
|
|
500
|
+
case "large": modifierIndent = " "
|
|
501
|
+
default: modifierIndent = " "
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
for item in kitchen {
|
|
507
|
+
var itemName = ""
|
|
508
|
+
var quantity = "1"
|
|
509
|
+
|
|
510
|
+
// Get item name
|
|
511
|
+
if let name = getString(item["name"]) {
|
|
512
|
+
itemName = name
|
|
513
|
+
} else if let menu = item["menu"] as? [String: Any],
|
|
514
|
+
let name = getString(menu["name"]) {
|
|
515
|
+
itemName = name
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Get quantity
|
|
519
|
+
if let qty = item["qty"] {
|
|
520
|
+
quantity = "x\(getString(qty) ?? "1")"
|
|
521
|
+
} else if let qty = item["quantity"] {
|
|
522
|
+
quantity = "x\(getString(qty) ?? "1")"
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Print main item line
|
|
526
|
+
let line = String(format: "%@ %@\n", itemName, quantity)
|
|
527
|
+
if let lineData = line.data(using: .utf8) {
|
|
528
|
+
printData.append(lineData)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Print modifiers
|
|
532
|
+
if let modifiers = item["modifiers"] as? [[String: Any]] {
|
|
533
|
+
// Apply modifier size if specified
|
|
534
|
+
if modifierSizeCode != 0x00 {
|
|
535
|
+
printData.append(Data([0x1D, 0x21, modifierSizeCode]))
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
for mod in modifiers {
|
|
539
|
+
if let modName = getString(mod["name"]) {
|
|
540
|
+
var prefix = ""
|
|
541
|
+
|
|
542
|
+
// Determine prefix based on style
|
|
543
|
+
switch modifierStyle.lowercased() {
|
|
544
|
+
case "dash":
|
|
545
|
+
prefix = "-"
|
|
546
|
+
case "bullet":
|
|
547
|
+
prefix = "•"
|
|
548
|
+
case "arrow":
|
|
549
|
+
prefix = "→"
|
|
550
|
+
default:
|
|
551
|
+
prefix = "•"
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Build modifier line
|
|
555
|
+
let modLine = "\(modifierIndent)\(prefix) \(modName)\n"
|
|
556
|
+
|
|
557
|
+
if let modData = modLine.data(using: .utf8) {
|
|
558
|
+
printData.append(modData)
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Reset modifier size if it was changed
|
|
564
|
+
if modifierSizeCode != 0x00 {
|
|
565
|
+
printData.append(Data([0x1D, 0x21, 0x00]))
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Reset item formatting
|
|
571
|
+
if itemSizeCode != 0x00 {
|
|
572
|
+
printData.append(Data([0x1D, 0x21, 0x00])) // Normal size
|
|
573
|
+
}
|
|
574
|
+
if itemBold {
|
|
575
|
+
printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Table number
|
|
580
|
+
if let tableNumber = getString(template["tableNumber"]),
|
|
581
|
+
let tableData = ("Table:\(tableNumber)\n").data(using: .utf8) {
|
|
582
|
+
printData.append(tableData)
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Order number and type
|
|
586
|
+
if let orderNumber = getString(template["orderNumber"]) {
|
|
587
|
+
var orderLine = "ORDER NO.: \(orderNumber)\n"
|
|
588
|
+
if let orderType = getString(template["orderType"]) {
|
|
589
|
+
orderLine += "\(orderType)\n"
|
|
590
|
+
}
|
|
591
|
+
if let orderData = orderLine.data(using: .utf8) {
|
|
592
|
+
printData.append(orderData)
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Served by
|
|
597
|
+
if let servedBy = getString(template["servedBy"]),
|
|
598
|
+
let servedData = ("Served By:\(servedBy)\n").data(using: .utf8) {
|
|
599
|
+
printData.append(servedData)
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Date/Time
|
|
603
|
+
if let dateTime = template["dateTime"] {
|
|
604
|
+
let formatter = DateFormatter()
|
|
605
|
+
formatter.dateFormat = "yyyy-MMM-dd HH:mm:ss"
|
|
606
|
+
|
|
607
|
+
var dateString = ""
|
|
608
|
+
if let date = dateTime as? Date {
|
|
609
|
+
dateString = formatter.string(from: date)
|
|
610
|
+
} else if let dateStr = getString(dateTime) {
|
|
611
|
+
dateString = dateStr
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if !dateString.isEmpty,
|
|
615
|
+
let dateData = ("Date:\(dateString)\n").data(using: .utf8) {
|
|
616
|
+
printData.append(dateData)
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Separator for items section
|
|
621
|
+
if let separatorData = ("--------------------------------\n").data(using: .utf8) {
|
|
622
|
+
printData.append(separatorData)
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Items header (ITEM, QTY, AMT)
|
|
626
|
+
if let headerData = ("ITEM\t\t\tQTY\tAMT\n").data(using: .utf8) {
|
|
627
|
+
printData.append(headerData)
|
|
628
|
+
}
|
|
629
|
+
if let separatorData = ("--------------------------------\n").data(using: .utf8) {
|
|
630
|
+
printData.append(separatorData)
|
|
631
|
+
}
|
|
632
|
+
|
|
350
633
|
// Items
|
|
351
634
|
if let kitchen = template["kitchen"] as? [[String: Any]] {
|
|
352
635
|
// Get item formatting
|
|
@@ -370,6 +653,7 @@ import Network
|
|
|
370
653
|
for item in kitchen {
|
|
371
654
|
var itemName = ""
|
|
372
655
|
var itemPrice = ""
|
|
656
|
+
var quantity = "1"
|
|
373
657
|
|
|
374
658
|
// Get name from menu object
|
|
375
659
|
if let menu = item["menu"] as? [String: Any],
|
|
@@ -377,38 +661,103 @@ import Network
|
|
|
377
661
|
itemName = name
|
|
378
662
|
}
|
|
379
663
|
|
|
380
|
-
//
|
|
664
|
+
// Get quantity
|
|
381
665
|
if let qty = item["quantity"] {
|
|
382
|
-
|
|
666
|
+
quantity = "x\(getString(qty) ?? "1")"
|
|
383
667
|
}
|
|
384
668
|
|
|
385
669
|
// Format price
|
|
386
670
|
if let price = item["total_price"] {
|
|
387
671
|
if let priceDouble = Double(getString(price) ?? "0") {
|
|
388
|
-
itemPrice = String(format: "
|
|
672
|
+
itemPrice = String(format: "%.1f", priceDouble)
|
|
389
673
|
} else {
|
|
390
|
-
itemPrice =
|
|
674
|
+
itemPrice = getString(price) ?? "0.00"
|
|
391
675
|
}
|
|
392
676
|
}
|
|
393
677
|
|
|
394
|
-
// Print main item
|
|
395
|
-
let line = String(format: "%@\t%@\n", itemName, itemPrice)
|
|
678
|
+
// Print main item line
|
|
679
|
+
let line = String(format: "%@ %@\t%@\n", itemName, quantity, itemPrice)
|
|
396
680
|
if let lineData = line.data(using: .utf8) {
|
|
397
681
|
printData.append(lineData)
|
|
398
682
|
}
|
|
399
683
|
|
|
400
684
|
// Print modifiers
|
|
401
685
|
if let modifiers = item["modifiers"] as? [[String: Any]] {
|
|
686
|
+
// Get modifier formatting options
|
|
687
|
+
var modifierStyle = "standard" // default
|
|
688
|
+
var modifierSizeCode: UInt8 = 0x00
|
|
689
|
+
var modifierIndent = " " // default: 2 spaces
|
|
690
|
+
|
|
691
|
+
if let formatting = template["formatting"] as? [String: Any] {
|
|
692
|
+
if let style = getString(formatting["modifierStyle"]) {
|
|
693
|
+
modifierStyle = style
|
|
694
|
+
}
|
|
695
|
+
if let size = formatting["modifierSize"] {
|
|
696
|
+
modifierSizeCode = mapHeaderSizeToCode(size)
|
|
697
|
+
}
|
|
698
|
+
if let indent = getString(formatting["modifierIndent"]) {
|
|
699
|
+
switch indent.lowercased() {
|
|
700
|
+
case "small": modifierIndent = " "
|
|
701
|
+
case "medium": modifierIndent = " "
|
|
702
|
+
case "large": modifierIndent = " "
|
|
703
|
+
default: modifierIndent = indent
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Apply modifier size if specified
|
|
709
|
+
if modifierSizeCode != 0x00 {
|
|
710
|
+
printData.append(Data([0x1D, 0x21, modifierSizeCode]))
|
|
711
|
+
}
|
|
712
|
+
|
|
402
713
|
for mod in modifiers {
|
|
403
714
|
if let modName = getString(mod["name"]) {
|
|
404
|
-
var modLine = "
|
|
715
|
+
var modLine = ""
|
|
716
|
+
var prefix = ""
|
|
405
717
|
|
|
406
|
-
//
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
718
|
+
// Determine prefix based on style
|
|
719
|
+
switch modifierStyle.lowercased() {
|
|
720
|
+
case "minimal":
|
|
721
|
+
prefix = "-"
|
|
722
|
+
case "bullet":
|
|
723
|
+
prefix = "•"
|
|
724
|
+
case "arrow":
|
|
725
|
+
prefix = "→"
|
|
726
|
+
case "detailed":
|
|
727
|
+
prefix = "+"
|
|
728
|
+
default: // "standard"
|
|
729
|
+
prefix = "+"
|
|
411
730
|
}
|
|
731
|
+
|
|
732
|
+
// Build modifier line based on style
|
|
733
|
+
switch modifierStyle.lowercased() {
|
|
734
|
+
case "minimal":
|
|
735
|
+
modLine = "\(modifierIndent)\(prefix) \(modName)"
|
|
736
|
+
case "bullet":
|
|
737
|
+
modLine = "\(modifierIndent)\(prefix) \(modName)"
|
|
738
|
+
case "arrow":
|
|
739
|
+
modLine = "\(modifierIndent)\(prefix) \(modName)"
|
|
740
|
+
case "detailed":
|
|
741
|
+
// Show quantity if available
|
|
742
|
+
var quantityStr = ""
|
|
743
|
+
if let qty = mod["quantity"],
|
|
744
|
+
let qtyInt = qty as? Int, qtyInt > 1 {
|
|
745
|
+
quantityStr = " (x\(qtyInt))"
|
|
746
|
+
}
|
|
747
|
+
modLine = "\(modifierIndent)\(prefix) \(modName)\(quantityStr)"
|
|
748
|
+
default: // "standard"
|
|
749
|
+
modLine = "\(modifierIndent)\(prefix) \(modName)"
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Add price if > 0 (except for minimal style)
|
|
753
|
+
if modifierStyle.lowercased() != "minimal" {
|
|
754
|
+
if let modPrice = mod["price"],
|
|
755
|
+
let priceDouble = Double(getString(modPrice) ?? "0"),
|
|
756
|
+
priceDouble > 0 {
|
|
757
|
+
modLine += String(format: "\t$%.2f", priceDouble)
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
412
761
|
modLine += "\n"
|
|
413
762
|
|
|
414
763
|
if let modData = modLine.data(using: .utf8) {
|
|
@@ -416,6 +765,11 @@ import Network
|
|
|
416
765
|
}
|
|
417
766
|
}
|
|
418
767
|
}
|
|
768
|
+
|
|
769
|
+
// Reset modifier size if it was changed
|
|
770
|
+
if modifierSizeCode != 0x00 {
|
|
771
|
+
printData.append(Data([0x1D, 0x21, 0x00]))
|
|
772
|
+
}
|
|
419
773
|
}
|
|
420
774
|
}
|
|
421
775
|
|
|
@@ -466,20 +820,51 @@ import Network
|
|
|
466
820
|
}
|
|
467
821
|
}
|
|
468
822
|
|
|
469
|
-
//
|
|
470
|
-
if let
|
|
471
|
-
|
|
472
|
-
|
|
823
|
+
// Separator
|
|
824
|
+
if let separatorData = ("--------------------------------\n").data(using: .utf8) {
|
|
825
|
+
printData.append(separatorData)
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// Subtotal, Discount, GST
|
|
829
|
+
if let subtotal = getString(template["subtotal"]) {
|
|
830
|
+
if let data = ("SUBTOTAL\t\t\t$\(subtotal)\n").data(using: .utf8) {
|
|
831
|
+
printData.append(data)
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
if let discount = getString(template["discount"]) {
|
|
836
|
+
if let discountDouble = Double(discount), discountDouble > 0 {
|
|
837
|
+
if let data = ("SAFRA Members\t\t\t-$\(discount)\n").data(using: .utf8) {
|
|
838
|
+
printData.append(data)
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if let gst = getString(template["gst"]) {
|
|
844
|
+
if let data = ("9% (Incl.) GST\t\t\t$\(gst)\n").data(using: .utf8) {
|
|
845
|
+
printData.append(data)
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// ========== TOTAL SECTION (NEW STRUCTURE) ==========
|
|
850
|
+
if let total = getString(template["total"]) {
|
|
851
|
+
// Separator
|
|
852
|
+
if let separatorData = ("--------------------------------\n").data(using: .utf8) {
|
|
853
|
+
printData.append(separatorData)
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Get total formatting from total_config object
|
|
473
857
|
var totalSizeCode: UInt8 = 0x00
|
|
474
858
|
var totalBold = false
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
859
|
+
|
|
860
|
+
if let totalConfig = template["total_config"] as? [String: Any] {
|
|
861
|
+
if let size = totalConfig["size"] {
|
|
862
|
+
totalSizeCode = mapHeaderSizeToCode(size)
|
|
863
|
+
}
|
|
864
|
+
if let bold = totalConfig["bold"] as? Bool {
|
|
865
|
+
totalBold = bold
|
|
478
866
|
}
|
|
479
|
-
totalBold = getBool(formatting["totalBold"])
|
|
480
867
|
}
|
|
481
|
-
print("ZywellSDK: Total size code: \(totalSizeCode)")
|
|
482
|
-
print("ZywellSDK: Total bold: \(totalBold)")
|
|
483
868
|
|
|
484
869
|
// Apply total formatting
|
|
485
870
|
if totalBold {
|
|
@@ -489,7 +874,9 @@ import Network
|
|
|
489
874
|
printData.append(Data([0x1D, 0x21, totalSizeCode])) // Set size
|
|
490
875
|
}
|
|
491
876
|
|
|
492
|
-
|
|
877
|
+
if let totalData = ("TOTAL\t\t\t$\(total)\n").data(using: .utf8) {
|
|
878
|
+
printData.append(totalData)
|
|
879
|
+
}
|
|
493
880
|
|
|
494
881
|
// Reset total formatting
|
|
495
882
|
if totalSizeCode != 0x00 {
|
|
@@ -500,22 +887,53 @@ import Network
|
|
|
500
887
|
}
|
|
501
888
|
}
|
|
502
889
|
|
|
503
|
-
//
|
|
504
|
-
if let
|
|
505
|
-
|
|
890
|
+
// Separator
|
|
891
|
+
if let separatorData = ("--------------------------------\n").data(using: .utf8) {
|
|
892
|
+
printData.append(separatorData)
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// Payment method
|
|
896
|
+
if let paymentMethod = getString(template["paymentMethod"]),
|
|
897
|
+
let paymentData = ("PAYMENT BY:\(paymentMethod)\t\(getString(template["total"]) ?? "")\n").data(using: .utf8) {
|
|
898
|
+
printData.append(paymentData)
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// Separator
|
|
902
|
+
if let separatorData = ("--------------------------------\n").data(using: .utf8) {
|
|
903
|
+
printData.append(separatorData)
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
// ========== FOOTER SECTION (NEW STRUCTURE) ==========
|
|
908
|
+
// Center align for footer
|
|
909
|
+
printData.append(Data([0x1B, 0x61, 0x01]))
|
|
910
|
+
|
|
911
|
+
if let footer = template["footer"] as? [String: Any] {
|
|
506
912
|
// Get footer formatting
|
|
507
913
|
var footerSizeCode: UInt8 = 0x00
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
914
|
+
var footerBold = false
|
|
915
|
+
|
|
916
|
+
if let size = footer["size"] {
|
|
917
|
+
footerSizeCode = mapHeaderSizeToCode(size)
|
|
918
|
+
}
|
|
919
|
+
if let bold = footer["bold"] as? Bool {
|
|
920
|
+
footerBold = bold
|
|
511
921
|
}
|
|
512
922
|
|
|
513
923
|
// Apply footer formatting
|
|
924
|
+
if footerBold {
|
|
925
|
+
printData.append(Data([0x1B, 0x45, 0x01])) // Bold on
|
|
926
|
+
}
|
|
514
927
|
if footerSizeCode != 0x00 {
|
|
515
928
|
printData.append(Data([0x1D, 0x21, footerSizeCode])) // Set size
|
|
516
929
|
}
|
|
517
930
|
|
|
518
|
-
|
|
931
|
+
// Footer message
|
|
932
|
+
if let message = getString(footer["message"]),
|
|
933
|
+
!message.isEmpty,
|
|
934
|
+
let messageData = (message + "\n").data(using: .utf8) {
|
|
935
|
+
printData.append(messageData)
|
|
936
|
+
}
|
|
519
937
|
|
|
520
938
|
// Reset footer formatting
|
|
521
939
|
if footerSizeCode != 0x00 {
|
package/package.json
CHANGED