OrderHistoryEntry.swift (5822B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2026 Bohdan, Volodymyr Potuzhnyi 4 * 5 * GNU Taler is free software; you can redistribute it and/or modify it under the 6 * terms of the GNU General Public License as published by the Free Software 7 * Foundation; either version 3, or (at your option) any later version. 8 * 9 * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 import Foundation 18 19 struct Timestamp: Codable, Equatable { 20 let seconds: UInt64 21 22 var date: Date { 23 Date(timeIntervalSince1970: Double(seconds)) 24 } 25 26 init(seconds: UInt64) { 27 self.seconds = seconds 28 } 29 30 init(from decoder: Decoder) throws { 31 let container = try decoder.container(keyedBy: CodingKeys.self) 32 if let value = try? container.decode(UInt64.self, forKey: .tS) { 33 self.seconds = value 34 } else if let str = try? container.decode(String.self, forKey: .tS), str == "never" { 35 self.seconds = UInt64.max 36 } else { 37 throw DecodingError.dataCorruptedError(forKey: .tS, in: container, debugDescription: "Expected integer or \"never\" for t_s") 38 } 39 } 40 41 func encode(to encoder: Encoder) throws { 42 var container = encoder.container(keyedBy: CodingKeys.self) 43 if seconds == UInt64.max { 44 try container.encode("never", forKey: .tS) 45 } else { 46 try container.encode(seconds, forKey: .tS) 47 } 48 } 49 50 enum CodingKeys: String, CodingKey { 51 case tS = "t_s" 52 } 53 } 54 55 struct OrderHistoryEntry: Codable, Identifiable { 56 let orderId: String 57 let rowId: Int64? 58 let timestamp: Timestamp 59 let amount: Amount 60 let summary: String 61 let paid: Bool 62 let refundable: Bool 63 var refunded: Bool 64 var refundAmount: Amount? 65 var pendingRefundAmount: Amount? 66 var refundPending: Bool 67 68 var id: String { orderId } 69 70 var hasRefund: Bool { 71 refunded || (refundAmount?.isZero == false) 72 } 73 74 var hasPendingRefund: Bool { 75 refundPending || (pendingRefundAmount?.isZero == false) 76 } 77 78 enum CodingKeys: String, CodingKey { 79 case orderId = "order_id" 80 case rowId = "row_id" 81 case timestamp 82 case amount 83 case summary 84 case paid 85 case refundable 86 case refunded 87 case refundAmount = "refund_amount" 88 case refundedAmount = "refunded_amount" 89 case pendingRefundAmount = "pending_refund_amount" 90 case refundPendingAmount = "refund_pending_amount" 91 case refundPending = "refund_pending" 92 } 93 94 init( 95 orderId: String, 96 rowId: Int64? = nil, 97 timestamp: Timestamp, 98 amount: Amount, 99 summary: String, 100 paid: Bool, 101 refundable: Bool, 102 refunded: Bool = false, 103 refundAmount: Amount? = nil, 104 pendingRefundAmount: Amount? = nil, 105 refundPending: Bool = false 106 ) { 107 self.orderId = orderId 108 self.rowId = rowId 109 self.timestamp = timestamp 110 self.amount = amount 111 self.summary = summary 112 self.paid = paid 113 self.refundable = refundable 114 self.refunded = refunded 115 self.refundAmount = refundAmount 116 self.pendingRefundAmount = pendingRefundAmount 117 self.refundPending = refundPending 118 } 119 120 init(from decoder: Decoder) throws { 121 let container = try decoder.container(keyedBy: CodingKeys.self) 122 orderId = try container.decode(String.self, forKey: .orderId) 123 rowId = try container.decodeIfPresent(Int64.self, forKey: .rowId) 124 timestamp = try container.decode(Timestamp.self, forKey: .timestamp) 125 amount = try container.decode(Amount.self, forKey: .amount) 126 summary = try container.decode(String.self, forKey: .summary) 127 paid = try container.decode(Bool.self, forKey: .paid) 128 refundable = try container.decode(Bool.self, forKey: .refundable) 129 refunded = try container.decodeIfPresent(Bool.self, forKey: .refunded) ?? false 130 if let ra = try container.decodeIfPresent(Amount.self, forKey: .refundAmount) { 131 refundAmount = ra 132 } else { 133 refundAmount = try container.decodeIfPresent(Amount.self, forKey: .refundedAmount) 134 } 135 if let pra = try container.decodeIfPresent(Amount.self, forKey: .pendingRefundAmount) { 136 pendingRefundAmount = pra 137 } else { 138 pendingRefundAmount = try container.decodeIfPresent(Amount.self, forKey: .refundPendingAmount) 139 } 140 refundPending = try container.decodeIfPresent(Bool.self, forKey: .refundPending) ?? false 141 } 142 143 func encode(to encoder: Encoder) throws { 144 var container = encoder.container(keyedBy: CodingKeys.self) 145 try container.encode(orderId, forKey: .orderId) 146 try container.encodeIfPresent(rowId, forKey: .rowId) 147 try container.encode(timestamp, forKey: .timestamp) 148 try container.encode(amount, forKey: .amount) 149 try container.encode(summary, forKey: .summary) 150 try container.encode(paid, forKey: .paid) 151 try container.encode(refundable, forKey: .refundable) 152 try container.encode(refunded, forKey: .refunded) 153 try container.encodeIfPresent(refundAmount, forKey: .refundAmount) 154 try container.encodeIfPresent(pendingRefundAmount, forKey: .pendingRefundAmount) 155 try container.encode(refundPending, forKey: .refundPending) 156 } 157 } 158 159 struct OrderHistory: Codable { 160 let orders: [OrderHistoryEntry] 161 }