migration
This commit is contained in:
715
PediFoods/Services/ApiOrderTrackingModels.swift
Normal file
715
PediFoods/Services/ApiOrderTrackingModels.swift
Normal file
@@ -0,0 +1,715 @@
|
||||
import Foundation
|
||||
|
||||
struct AppOrderSummary: Decodable, Identifiable {
|
||||
let id: String
|
||||
let orderId: String?
|
||||
let realId: String?
|
||||
let storeId: String?
|
||||
let shortId: String?
|
||||
let total: Double?
|
||||
let status: String?
|
||||
let statusDetailed: String?
|
||||
let statusLabel: String?
|
||||
let nextAction: String?
|
||||
let paymentStatus: String?
|
||||
let paymentMethod: String?
|
||||
let deliveryType: String?
|
||||
let storeName: String?
|
||||
let storePhone: String?
|
||||
let storeLogoURL: String?
|
||||
let createdAt: String?
|
||||
let updatedAt: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case orderId
|
||||
case realId
|
||||
case storeId
|
||||
case store_id
|
||||
case shortId
|
||||
case total
|
||||
case status
|
||||
case statusDetailed
|
||||
case statusLabel
|
||||
case nextAction
|
||||
case paymentStatus
|
||||
case paymentMethod
|
||||
case deliveryType
|
||||
case storeName
|
||||
case storePhone
|
||||
case storeLogo
|
||||
case store_logo
|
||||
case logo
|
||||
case storeImage
|
||||
case store_image
|
||||
case storeImageUrl
|
||||
case logoUrl
|
||||
case date
|
||||
case createdAt
|
||||
case updatedAt
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
orderId = ApiService.decodeFlexibleString(from: container, keys: [.orderId])
|
||||
id = ApiService.decodeFlexibleString(from: container, keys: [.orderId, .realId, .id]) ?? UUID().uuidString
|
||||
realId = ApiService.decodeFlexibleString(from: container, keys: [.realId])
|
||||
storeId = ApiService.decodeFlexibleString(from: container, keys: [.storeId, .store_id])
|
||||
shortId = ApiService.decodeFlexibleString(from: container, keys: [.shortId])
|
||||
total = ApiService.decodeFlexibleDouble(from: container, keys: [.total])
|
||||
status = ApiService.decodeFlexibleString(from: container, keys: [.status])
|
||||
statusDetailed = ApiService.decodeFlexibleString(from: container, keys: [.statusDetailed])
|
||||
statusLabel = ApiService.decodeFlexibleString(from: container, keys: [.statusLabel])
|
||||
nextAction = ApiService.decodeFlexibleString(from: container, keys: [.nextAction])
|
||||
paymentStatus = ApiService.decodeFlexibleString(from: container, keys: [.paymentStatus])
|
||||
paymentMethod = ApiService.decodeFlexibleString(from: container, keys: [.paymentMethod])
|
||||
deliveryType = ApiService.decodeFlexibleString(from: container, keys: [.deliveryType])
|
||||
storeName = ApiService.decodeFlexibleString(from: container, keys: [.storeName])
|
||||
storePhone = ApiService.decodeFlexibleString(from: container, keys: [.storePhone])
|
||||
storeLogoURL = ApiService.decodeFlexibleString(from: container, keys: [.storeLogo, .store_logo, .logo, .storeImage, .store_image, .storeImageUrl, .logoUrl])
|
||||
let fallbackDate = ApiService.decodeFlexibleString(from: container, keys: [.date])
|
||||
createdAt = ApiService.decodeFlexibleString(from: container, keys: [.createdAt]) ?? fallbackDate
|
||||
updatedAt = ApiService.decodeFlexibleString(from: container, keys: [.updatedAt]) ?? fallbackDate
|
||||
}
|
||||
}
|
||||
|
||||
struct PublicOrderResult: Codable, Identifiable {
|
||||
let id: String
|
||||
let shortId: String?
|
||||
let realId: String?
|
||||
let storeId: String?
|
||||
let status: String?
|
||||
let paymentStatus: String?
|
||||
let paymentConfirmed: Bool?
|
||||
let paymentMethod: String?
|
||||
let paymentMethodCode: String?
|
||||
let paymentPayload: CreateOrderPaymentPayload?
|
||||
let payment: CreateOrderPaymentInfo?
|
||||
let nextAction: String?
|
||||
let deliveryType: String?
|
||||
let deliveryTypeLabel: String?
|
||||
let subtotal: Double?
|
||||
let deliveryFee: Double?
|
||||
let discount: Double?
|
||||
let total: Double?
|
||||
let storeName: String?
|
||||
let storePhone: String?
|
||||
let storeLogoURL: String?
|
||||
let createdAt: String?
|
||||
let updatedAt: String?
|
||||
let otp: String?
|
||||
let customerOtp: String?
|
||||
let confirmOtp: String?
|
||||
let cancellationReason: String?
|
||||
let fullAddress: String?
|
||||
let deliveryAddress: PublicOrderDeliveryAddress?
|
||||
let review: PublicOrderReview?
|
||||
let items: [PublicOrderItem]
|
||||
let timeline: [PublicOrderTimelineEvent]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case shortId
|
||||
case realId
|
||||
case storeId
|
||||
case store_id
|
||||
case status
|
||||
case paymentStatus
|
||||
case paymentConfirmed
|
||||
case paymentMethod
|
||||
case paymentMethodCode
|
||||
case paymentPayload
|
||||
case payment
|
||||
case nextAction
|
||||
case deliveryType
|
||||
case deliveryTypeLabel
|
||||
case subtotal
|
||||
case subTotal
|
||||
case itemsTotal
|
||||
case deliveryFee
|
||||
case delivery_fee
|
||||
case fee
|
||||
case discount
|
||||
case desconto
|
||||
case couponDiscount
|
||||
case total
|
||||
case storeName
|
||||
case storePhone
|
||||
case storeLogo
|
||||
case store_logo
|
||||
case logo
|
||||
case storeImage
|
||||
case store_image
|
||||
case storeImageUrl
|
||||
case logoUrl
|
||||
case createdAt
|
||||
case updatedAt
|
||||
case otp
|
||||
case customerOtp
|
||||
case confirmOtp
|
||||
case cancellationReason
|
||||
case fullAddress
|
||||
case address
|
||||
case deliveryAddress
|
||||
case delivery_address
|
||||
case customerAddress
|
||||
case customer_address
|
||||
case review
|
||||
case orderReview
|
||||
case items
|
||||
case timeline
|
||||
case history
|
||||
case orderedAt
|
||||
}
|
||||
|
||||
init(
|
||||
id: String,
|
||||
shortId: String? = nil,
|
||||
realId: String? = nil,
|
||||
storeId: String? = nil,
|
||||
status: String? = nil,
|
||||
paymentStatus: String? = nil,
|
||||
paymentConfirmed: Bool? = nil,
|
||||
paymentMethod: String? = nil,
|
||||
paymentMethodCode: String? = nil,
|
||||
paymentPayload: CreateOrderPaymentPayload? = nil,
|
||||
payment: CreateOrderPaymentInfo? = nil,
|
||||
nextAction: String? = nil,
|
||||
deliveryType: String? = nil,
|
||||
deliveryTypeLabel: String? = nil,
|
||||
subtotal: Double? = nil,
|
||||
deliveryFee: Double? = nil,
|
||||
discount: Double? = nil,
|
||||
total: Double? = nil,
|
||||
storeName: String? = nil,
|
||||
storePhone: String? = nil,
|
||||
storeLogoURL: String? = nil,
|
||||
createdAt: String? = nil,
|
||||
updatedAt: String? = nil,
|
||||
otp: String? = nil,
|
||||
customerOtp: String? = nil,
|
||||
confirmOtp: String? = nil,
|
||||
cancellationReason: String? = nil,
|
||||
fullAddress: String? = nil,
|
||||
deliveryAddress: PublicOrderDeliveryAddress? = nil,
|
||||
review: PublicOrderReview? = nil,
|
||||
items: [PublicOrderItem] = [],
|
||||
timeline: [PublicOrderTimelineEvent] = []
|
||||
) {
|
||||
self.id = id
|
||||
self.shortId = shortId
|
||||
self.realId = realId
|
||||
self.storeId = storeId
|
||||
self.status = status
|
||||
self.paymentStatus = paymentStatus
|
||||
self.paymentConfirmed = paymentConfirmed
|
||||
self.paymentMethod = paymentMethod
|
||||
self.paymentMethodCode = paymentMethodCode
|
||||
self.paymentPayload = paymentPayload
|
||||
self.payment = payment
|
||||
self.nextAction = nextAction
|
||||
self.deliveryType = deliveryType
|
||||
self.deliveryTypeLabel = deliveryTypeLabel
|
||||
self.subtotal = subtotal
|
||||
self.deliveryFee = deliveryFee
|
||||
self.discount = discount
|
||||
self.total = total
|
||||
self.storeName = storeName
|
||||
self.storePhone = storePhone
|
||||
self.storeLogoURL = storeLogoURL
|
||||
self.createdAt = createdAt
|
||||
self.updatedAt = updatedAt
|
||||
self.otp = otp
|
||||
self.customerOtp = customerOtp
|
||||
self.confirmOtp = confirmOtp
|
||||
self.cancellationReason = cancellationReason
|
||||
self.fullAddress = fullAddress
|
||||
self.deliveryAddress = deliveryAddress
|
||||
self.review = review
|
||||
self.items = items
|
||||
self.timeline = timeline
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = ApiService.decodeFlexibleString(from: container, keys: [.id]) ?? UUID().uuidString
|
||||
shortId = ApiService.decodeFlexibleString(from: container, keys: [.shortId])
|
||||
realId = ApiService.decodeFlexibleString(from: container, keys: [.realId])
|
||||
storeId = ApiService.decodeFlexibleString(from: container, keys: [.storeId, .store_id])
|
||||
status = ApiService.decodeFlexibleString(from: container, keys: [.status])
|
||||
paymentStatus = ApiService.decodeFlexibleString(from: container, keys: [.paymentStatus])
|
||||
paymentConfirmed = try? container.decode(Bool.self, forKey: .paymentConfirmed)
|
||||
paymentMethod = ApiService.decodeFlexibleString(from: container, keys: [.paymentMethod])
|
||||
paymentMethodCode = ApiService.decodeFlexibleString(from: container, keys: [.paymentMethodCode])
|
||||
nextAction = ApiService.decodeFlexibleString(from: container, keys: [.nextAction])
|
||||
payment = try? container.decode(CreateOrderPaymentInfo.self, forKey: .payment)
|
||||
if let objectPayload = try? container.decode(CreateOrderPaymentPayload.self, forKey: .paymentPayload) {
|
||||
paymentPayload = objectPayload
|
||||
} else if let stringPayload = try? container.decode(String.self, forKey: .paymentPayload) {
|
||||
paymentPayload = CreateOrderPaymentPayload(
|
||||
copyPaste: stringPayload,
|
||||
qrCodeImage: nil,
|
||||
expirationDate: nil
|
||||
)
|
||||
} else {
|
||||
paymentPayload = nil
|
||||
}
|
||||
deliveryType = ApiService.decodeFlexibleString(from: container, keys: [.deliveryType])
|
||||
deliveryTypeLabel = ApiService.decodeFlexibleString(from: container, keys: [.deliveryTypeLabel])
|
||||
subtotal = ApiService.decodeFlexibleDouble(from: container, keys: [.subtotal, .subTotal, .itemsTotal])
|
||||
deliveryFee = ApiService.decodeFlexibleDouble(from: container, keys: [.deliveryFee, .delivery_fee, .fee])
|
||||
discount = ApiService.decodeFlexibleDouble(from: container, keys: [.discount, .desconto, .couponDiscount])
|
||||
total = ApiService.decodeFlexibleDouble(from: container, keys: [.total])
|
||||
storeName = ApiService.decodeFlexibleString(from: container, keys: [.storeName])
|
||||
storePhone = ApiService.decodeFlexibleString(from: container, keys: [.storePhone])
|
||||
storeLogoURL = ApiService.decodeFlexibleString(from: container, keys: [.storeLogo, .store_logo, .logo, .storeImage, .store_image, .storeImageUrl, .logoUrl])
|
||||
createdAt = ApiService.decodeFlexibleString(from: container, keys: [.createdAt, .orderedAt])
|
||||
updatedAt = ApiService.decodeFlexibleString(from: container, keys: [.updatedAt])
|
||||
otp = ApiService.decodeFlexibleString(from: container, keys: [.otp])
|
||||
customerOtp = ApiService.decodeFlexibleString(from: container, keys: [.customerOtp])
|
||||
confirmOtp = ApiService.decodeFlexibleString(from: container, keys: [.confirmOtp])
|
||||
cancellationReason = ApiService.decodeFlexibleString(from: container, keys: [.cancellationReason])
|
||||
fullAddress = ApiService.decodeFlexibleString(from: container, keys: [.fullAddress])
|
||||
deliveryAddress = (try? container.decode(PublicOrderDeliveryAddress.self, forKey: .address))
|
||||
?? (try? container.decode(PublicOrderDeliveryAddress.self, forKey: .deliveryAddress))
|
||||
?? (try? container.decode(PublicOrderDeliveryAddress.self, forKey: .delivery_address))
|
||||
?? (try? container.decode(PublicOrderDeliveryAddress.self, forKey: .customerAddress))
|
||||
?? (try? container.decode(PublicOrderDeliveryAddress.self, forKey: .customer_address))
|
||||
review = (try? container.decode(PublicOrderReview.self, forKey: .review))
|
||||
?? (try? container.decode(PublicOrderReview.self, forKey: .orderReview))
|
||||
items = (try? container.decode([PublicOrderItem].self, forKey: .items)) ?? []
|
||||
timeline = (try? container.decode([PublicOrderTimelineEvent].self, forKey: .timeline))
|
||||
?? (try? container.decode([PublicOrderTimelineEvent].self, forKey: .history))
|
||||
?? []
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encodeIfPresent(shortId, forKey: .shortId)
|
||||
try container.encodeIfPresent(realId, forKey: .realId)
|
||||
try container.encodeIfPresent(storeId, forKey: .storeId)
|
||||
try container.encodeIfPresent(status, forKey: .status)
|
||||
try container.encodeIfPresent(paymentStatus, forKey: .paymentStatus)
|
||||
try container.encodeIfPresent(paymentConfirmed, forKey: .paymentConfirmed)
|
||||
try container.encodeIfPresent(paymentMethod, forKey: .paymentMethod)
|
||||
try container.encodeIfPresent(paymentMethodCode, forKey: .paymentMethodCode)
|
||||
try container.encodeIfPresent(paymentPayload, forKey: .paymentPayload)
|
||||
try container.encodeIfPresent(payment, forKey: .payment)
|
||||
try container.encodeIfPresent(nextAction, forKey: .nextAction)
|
||||
try container.encodeIfPresent(deliveryType, forKey: .deliveryType)
|
||||
try container.encodeIfPresent(deliveryTypeLabel, forKey: .deliveryTypeLabel)
|
||||
try container.encodeIfPresent(subtotal, forKey: .subtotal)
|
||||
try container.encodeIfPresent(deliveryFee, forKey: .deliveryFee)
|
||||
try container.encodeIfPresent(discount, forKey: .discount)
|
||||
try container.encodeIfPresent(total, forKey: .total)
|
||||
try container.encodeIfPresent(storeName, forKey: .storeName)
|
||||
try container.encodeIfPresent(storePhone, forKey: .storePhone)
|
||||
try container.encodeIfPresent(createdAt, forKey: .createdAt)
|
||||
try container.encodeIfPresent(updatedAt, forKey: .updatedAt)
|
||||
try container.encodeIfPresent(otp, forKey: .otp)
|
||||
try container.encodeIfPresent(customerOtp, forKey: .customerOtp)
|
||||
try container.encodeIfPresent(confirmOtp, forKey: .confirmOtp)
|
||||
try container.encodeIfPresent(cancellationReason, forKey: .cancellationReason)
|
||||
try container.encodeIfPresent(fullAddress, forKey: .fullAddress)
|
||||
try container.encodeIfPresent(deliveryAddress, forKey: .address)
|
||||
try container.encodeIfPresent(review, forKey: .review)
|
||||
try container.encode(items, forKey: .items)
|
||||
try container.encode(timeline, forKey: .timeline)
|
||||
}
|
||||
|
||||
var displayOtpCode: String? {
|
||||
let values = [customerOtp, otp, confirmOtp]
|
||||
for value in values {
|
||||
let trimmed = (value ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if trimmed.isEmpty == false { return trimmed }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var isInDeliveryRoute: Bool {
|
||||
let normalized = (status ?? "").uppercased()
|
||||
if normalized.contains("OUT_FOR_DELIVERY") { return true }
|
||||
if normalized.contains("EM_ROTA") { return true }
|
||||
if normalized.contains("ON_ROUTE") { return true }
|
||||
if normalized.contains("ROTA") { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
var isFinalStatus: Bool {
|
||||
let normalized = (status ?? "").uppercased()
|
||||
return normalized == "COMPLETED" || normalized == "CANCELED" || normalized == "REFUNDED"
|
||||
}
|
||||
|
||||
var isPaymentConfirmed: Bool {
|
||||
if let paymentConfirmed {
|
||||
return paymentConfirmed
|
||||
}
|
||||
|
||||
let payment = (paymentStatus ?? "").uppercased()
|
||||
let currentStatus = (status ?? "").uppercased()
|
||||
if Self.looksConfirmed(payment) || Self.looksConfirmed(currentStatus) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Fallback: alguns ambientes atualizam apenas a timeline primeiro.
|
||||
if timeline.contains(where: { event in
|
||||
let statusValue = (event.status ?? "").uppercased()
|
||||
let messageValue = (event.message ?? "").uppercased()
|
||||
return Self.looksConfirmed(statusValue) || Self.looksConfirmed(messageValue)
|
||||
}) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private static func looksConfirmed(_ value: String) -> Bool {
|
||||
if value.isEmpty { return false }
|
||||
if value.contains("PENDING") || value.contains("AWAIT") { return false }
|
||||
if value.contains("FAILED") || value.contains("ERROR") { return false }
|
||||
if value.contains("CANCEL") || value.contains("REFUND") { return false }
|
||||
|
||||
if value.contains("CONFIRM") { return true }
|
||||
if value.contains("APPROV") { return true }
|
||||
if value.contains("PAID") { return true }
|
||||
if value.contains("RECEIV") { return true }
|
||||
|
||||
return value == "SUCCESS" || value == "DONE"
|
||||
}
|
||||
}
|
||||
|
||||
struct PublicOrderDeliveryAddress: Codable {
|
||||
let label: String?
|
||||
let street: String?
|
||||
let number: String?
|
||||
let neighborhood: String?
|
||||
let city: String?
|
||||
let state: String?
|
||||
let zip: String?
|
||||
let complement: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case label
|
||||
case street
|
||||
case address
|
||||
case number
|
||||
case neighborhood
|
||||
case district
|
||||
case city
|
||||
case state
|
||||
case zip
|
||||
case zipCode
|
||||
case zipcode
|
||||
case complement
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
label = ApiService.decodeFlexibleString(from: container, keys: [.label])
|
||||
street = ApiService.decodeFlexibleString(from: container, keys: [.street, .address])
|
||||
number = ApiService.decodeFlexibleString(from: container, keys: [.number])
|
||||
neighborhood = ApiService.decodeFlexibleString(from: container, keys: [.neighborhood, .district])
|
||||
city = ApiService.decodeFlexibleString(from: container, keys: [.city])
|
||||
state = ApiService.decodeFlexibleString(from: container, keys: [.state])
|
||||
zip = ApiService.decodeFlexibleString(from: container, keys: [.zip, .zipCode, .zipcode])
|
||||
complement = ApiService.decodeFlexibleString(from: container, keys: [.complement])
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(label, forKey: .label)
|
||||
try container.encodeIfPresent(street, forKey: .street)
|
||||
try container.encodeIfPresent(number, forKey: .number)
|
||||
try container.encodeIfPresent(neighborhood, forKey: .neighborhood)
|
||||
try container.encodeIfPresent(city, forKey: .city)
|
||||
try container.encodeIfPresent(state, forKey: .state)
|
||||
try container.encodeIfPresent(zip, forKey: .zip)
|
||||
try container.encodeIfPresent(complement, forKey: .complement)
|
||||
}
|
||||
}
|
||||
|
||||
struct PublicOrderReview: Codable {
|
||||
let id: String?
|
||||
let orderId: String?
|
||||
let rate: Int?
|
||||
let message: String?
|
||||
let orderRate: Int?
|
||||
let orderComment: String?
|
||||
let orderPositiveTags: [String]?
|
||||
let orderImprovementTags: [String]?
|
||||
let deliverySentiment: String?
|
||||
let deliveryPositiveTags: [String]?
|
||||
let deliveryNegativeTags: [String]?
|
||||
let appNps: Int?
|
||||
let platform: String?
|
||||
let date: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case orderId
|
||||
case rate
|
||||
case message
|
||||
case orderRate
|
||||
case orderComment
|
||||
case orderPositiveTags
|
||||
case orderImprovementTags
|
||||
case itemFeedback
|
||||
case improvementFeedback
|
||||
case deliverySentiment
|
||||
case deliveryFeedback
|
||||
case deliveryPositiveTags
|
||||
case deliveryNegativeTags
|
||||
case appNps
|
||||
case app_nps
|
||||
case platform
|
||||
case date
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = ApiService.decodeFlexibleString(from: container, keys: [.id])
|
||||
orderId = ApiService.decodeFlexibleString(from: container, keys: [.orderId])
|
||||
rate = ApiService.decodeFlexibleInt(from: container, keys: [.rate])
|
||||
message = ApiService.decodeFlexibleString(from: container, keys: [.message])
|
||||
orderRate = ApiService.decodeFlexibleInt(from: container, keys: [.orderRate, .rate])
|
||||
orderComment = ApiService.decodeFlexibleString(from: container, keys: [.orderComment, .message])
|
||||
orderPositiveTags = Self.decodeStringList(
|
||||
from: container,
|
||||
keys: [.orderPositiveTags, .itemFeedback]
|
||||
)
|
||||
orderImprovementTags = Self.decodeStringList(
|
||||
from: container,
|
||||
keys: [.orderImprovementTags, .improvementFeedback]
|
||||
)
|
||||
deliverySentiment = ApiService.decodeFlexibleString(from: container, keys: [.deliverySentiment, .deliveryFeedback])
|
||||
deliveryPositiveTags = Self.decodeStringList(from: container, keys: [.deliveryPositiveTags])
|
||||
deliveryNegativeTags = Self.decodeStringList(from: container, keys: [.deliveryNegativeTags])
|
||||
appNps = Self.decodeNps(from: container, keys: [.appNps, .app_nps])
|
||||
platform = ApiService.decodeFlexibleString(from: container, keys: [.platform])
|
||||
date = ApiService.decodeFlexibleString(from: container, keys: [.date])
|
||||
}
|
||||
|
||||
private static func decodeStringList(
|
||||
from container: KeyedDecodingContainer<CodingKeys>,
|
||||
keys: [CodingKeys]
|
||||
) -> [String]? {
|
||||
for key in keys {
|
||||
if let list = try? container.decode([String].self, forKey: key) {
|
||||
return list
|
||||
}
|
||||
if let single = try? container.decode(String.self, forKey: key) {
|
||||
let normalized = single.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if normalized.isEmpty == false {
|
||||
return [normalized]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func decodeNps(
|
||||
from container: KeyedDecodingContainer<CodingKeys>,
|
||||
keys: [CodingKeys]
|
||||
) -> Int? {
|
||||
for key in keys {
|
||||
if let value = try? container.decode(Int.self, forKey: key) {
|
||||
return value
|
||||
}
|
||||
if let value = try? container.decode(Double.self, forKey: key) {
|
||||
return Int(value.rounded())
|
||||
}
|
||||
if let raw = try? container.decode(String.self, forKey: key) {
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if trimmed.isEmpty { continue }
|
||||
if let asInt = Int(trimmed) {
|
||||
return asInt
|
||||
}
|
||||
let normalized = trimmed.replacingOccurrences(of: ",", with: ".")
|
||||
if let asDouble = Double(normalized) {
|
||||
return Int(asDouble.rounded())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(id, forKey: .id)
|
||||
try container.encodeIfPresent(orderId, forKey: .orderId)
|
||||
try container.encodeIfPresent(rate, forKey: .rate)
|
||||
try container.encodeIfPresent(message, forKey: .message)
|
||||
try container.encodeIfPresent(orderRate, forKey: .orderRate)
|
||||
try container.encodeIfPresent(orderComment, forKey: .orderComment)
|
||||
try container.encodeIfPresent(orderPositiveTags, forKey: .orderPositiveTags)
|
||||
try container.encodeIfPresent(orderImprovementTags, forKey: .orderImprovementTags)
|
||||
try container.encodeIfPresent(deliverySentiment, forKey: .deliverySentiment)
|
||||
try container.encodeIfPresent(deliveryPositiveTags, forKey: .deliveryPositiveTags)
|
||||
try container.encodeIfPresent(deliveryNegativeTags, forKey: .deliveryNegativeTags)
|
||||
try container.encodeIfPresent(appNps, forKey: .appNps)
|
||||
try container.encodeIfPresent(platform, forKey: .platform)
|
||||
try container.encodeIfPresent(date, forKey: .date)
|
||||
}
|
||||
}
|
||||
|
||||
struct PublicOrderItem: Codable, Identifiable {
|
||||
let id: String
|
||||
let productId: String?
|
||||
let name: String?
|
||||
let qty: Int?
|
||||
let price: Double?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case productId
|
||||
case name
|
||||
case qty
|
||||
case quantity
|
||||
case price
|
||||
}
|
||||
|
||||
init(id: String = UUID().uuidString, productId: String? = nil, name: String?, qty: Int?, price: Double?) {
|
||||
self.id = id
|
||||
self.productId = productId
|
||||
self.name = name
|
||||
self.qty = qty
|
||||
self.price = price
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = (try? container.decode(String.self, forKey: .id)) ?? UUID().uuidString
|
||||
productId = try? container.decode(String.self, forKey: .productId)
|
||||
name = try? container.decode(String.self, forKey: .name)
|
||||
qty = ApiService.decodeFlexibleInt(from: container, keys: [.qty, .quantity])
|
||||
price = ApiService.decodeFlexibleDouble(from: container, keys: [.price])
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encodeIfPresent(productId, forKey: .productId)
|
||||
try container.encodeIfPresent(name, forKey: .name)
|
||||
try container.encodeIfPresent(qty, forKey: .qty)
|
||||
try container.encodeIfPresent(price, forKey: .price)
|
||||
}
|
||||
}
|
||||
|
||||
struct PublicOrderTimelineEvent: Codable, Identifiable {
|
||||
let id: String
|
||||
let status: String?
|
||||
let label: String?
|
||||
let active: Bool?
|
||||
let completed: Bool?
|
||||
let message: String?
|
||||
let time: String?
|
||||
let date: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case status
|
||||
case label
|
||||
case active
|
||||
case completed
|
||||
case message
|
||||
case event
|
||||
case time
|
||||
case date
|
||||
case createdAt
|
||||
case updatedAt
|
||||
}
|
||||
|
||||
init(
|
||||
id: String = UUID().uuidString,
|
||||
status: String?,
|
||||
label: String? = nil,
|
||||
active: Bool? = nil,
|
||||
completed: Bool? = nil,
|
||||
message: String?,
|
||||
time: String?,
|
||||
date: String? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.status = status
|
||||
self.label = label
|
||||
self.active = active
|
||||
self.completed = completed
|
||||
self.message = message
|
||||
self.time = time
|
||||
self.date = date
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = (try? container.decode(String.self, forKey: .id)) ?? UUID().uuidString
|
||||
status = try? container.decode(String.self, forKey: .status)
|
||||
label = try? container.decode(String.self, forKey: .label)
|
||||
active = try? container.decode(Bool.self, forKey: .active)
|
||||
completed = try? container.decode(Bool.self, forKey: .completed)
|
||||
message = (try? container.decode(String.self, forKey: .message))
|
||||
?? (try? container.decode(String.self, forKey: .event))
|
||||
time = (try? container.decode(String.self, forKey: .time))
|
||||
?? (try? container.decode(String.self, forKey: .createdAt))
|
||||
?? (try? container.decode(String.self, forKey: .updatedAt))
|
||||
date = try? container.decode(String.self, forKey: .date)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encodeIfPresent(status, forKey: .status)
|
||||
try container.encodeIfPresent(label, forKey: .label)
|
||||
try container.encodeIfPresent(active, forKey: .active)
|
||||
try container.encodeIfPresent(completed, forKey: .completed)
|
||||
try container.encodeIfPresent(message, forKey: .message)
|
||||
try container.encodeIfPresent(time, forKey: .time)
|
||||
try container.encodeIfPresent(date, forKey: .date)
|
||||
}
|
||||
}
|
||||
|
||||
struct OrderRealtimeUpdate: Decodable {
|
||||
let id: String?
|
||||
let shortId: String?
|
||||
let storeId: String?
|
||||
let userId: String?
|
||||
let status: String?
|
||||
let paymentStatus: String?
|
||||
let updatedAt: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case shortId
|
||||
case storeId
|
||||
case userId
|
||||
case status
|
||||
case paymentStatus
|
||||
case updatedAt
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = ApiService.decodeFlexibleString(from: container, keys: [.id])
|
||||
shortId = ApiService.decodeFlexibleString(from: container, keys: [.shortId])
|
||||
storeId = ApiService.decodeFlexibleString(from: container, keys: [.storeId])
|
||||
userId = ApiService.decodeFlexibleString(from: container, keys: [.userId])
|
||||
status = ApiService.decodeFlexibleString(from: container, keys: [.status])
|
||||
paymentStatus = ApiService.decodeFlexibleString(from: container, keys: [.paymentStatus])
|
||||
updatedAt = ApiService.decodeFlexibleString(from: container, keys: [.updatedAt])
|
||||
}
|
||||
}
|
||||
|
||||
extension CreateOrderResult {
|
||||
func asPublicOrderResult() -> PublicOrderResult {
|
||||
PublicOrderResult(
|
||||
id: id ?? UUID().uuidString,
|
||||
shortId: shortId,
|
||||
status: status,
|
||||
paymentStatus: paymentStatus,
|
||||
paymentConfirmed: paymentConfirmed,
|
||||
paymentMethod: paymentMethod,
|
||||
paymentPayload: paymentPayload,
|
||||
payment: payment
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user