Skip to content

Commit f230fd8

Browse files
authored
Merge pull request #226 from woocommerce/feature/217-empty-shipping
Order Details: Handle empty shipping address
2 parents 7113d20 + 42e34a2 commit f230fd8

File tree

9 files changed

+159
-43
lines changed

9 files changed

+159
-43
lines changed

Networking/Networking/Model/Address.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ public struct Address: Decodable {
1616
public let phone: String?
1717
public let email: String?
1818

19+
/// Make Address conform to Error protocol.
20+
///
21+
enum AddressParseError: Error {
22+
case missingCountry
23+
}
24+
25+
1926
/// Designated Initializer.
2027
///
2128
public init(firstName: String, lastName: String, company: String?, address1: String, address2: String?, city: String, state: String, postcode: String, country: String, phone: String?, email: String?) {
@@ -31,6 +38,30 @@ public struct Address: Decodable {
3138
self.phone = phone
3239
self.email = email
3340
}
41+
42+
public init(from decoder: Decoder) throws {
43+
let container = try decoder.container(keyedBy: CodingKeys.self)
44+
45+
let firstName = try container.decode(String.self, forKey: .firstName)
46+
let lastName = try container.decode(String.self, forKey: .lastName)
47+
let company = try container.decodeIfPresent(String.self, forKey: .company)
48+
let address1 = try container.decode(String.self, forKey: .address1)
49+
let address2 = try container.decodeIfPresent(String.self, forKey: .address2)
50+
let city = try container.decode(String.self, forKey: .city)
51+
let state = try container.decode(String.self, forKey: .state)
52+
let postcode = try container.decode(String.self, forKey: .postcode)
53+
let country = try container.decode(String.self, forKey: .country)
54+
let phone = try container.decodeIfPresent(String.self, forKey: .phone)
55+
let email = try container.decodeIfPresent(String.self, forKey: .email)
56+
57+
// Check for an empty country, because on Android that's how
58+
// we determine if the shipping address should be considered empty.
59+
if country.isEmpty {
60+
throw AddressParseError.missingCountry
61+
}
62+
63+
self.init(firstName: firstName, lastName: lastName, company: company, address1: address1, address2: address2, city: city, state: state, postcode: postcode, country: country, phone: phone, email: email)
64+
}
3465
}
3566

3667

Networking/Networking/Model/Order.swift

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public struct Order: Decodable {
2727
public let paymentMethodTitle: String
2828

2929
public let items: [OrderItem]
30-
public let billingAddress: Address
31-
public let shippingAddress: Address
30+
public let billingAddress: Address?
31+
public let shippingAddress: Address?
3232
public let coupons: [OrderCouponLine]
3333

3434
/// Order struct initializer.
@@ -52,8 +52,8 @@ public struct Order: Decodable {
5252
totalTax: String,
5353
paymentMethodTitle: String,
5454
items: [OrderItem],
55-
billingAddress: Address,
56-
shippingAddress: Address,
55+
billingAddress: Address?,
56+
shippingAddress: Address?,
5757
coupons: [OrderCouponLine]) {
5858

5959
self.siteID = siteID
@@ -116,11 +116,24 @@ public struct Order: Decodable {
116116
let paymentMethodTitle = try container.decode(String.self, forKey: .paymentMethodTitle)
117117

118118
let items = try container.decode([OrderItem].self, forKey: .items)
119-
let shippingAddress = try container.decode(Address.self, forKey: .shippingAddress)
120-
let billingAddress = try container.decode(Address.self, forKey: .billingAddress)
119+
120+
var shippingAddress: Address? = nil
121+
do {
122+
shippingAddress = try container.decodeIfPresent(Address.self, forKey: .shippingAddress)
123+
} catch {
124+
// no-op
125+
}
126+
127+
var billingAddress: Address? = nil
128+
do {
129+
billingAddress = try container.decode(Address.self, forKey: .billingAddress)
130+
} catch {
131+
billingAddress = Address(firstName: "", lastName: "", company: "", address1: "", address2: "", city: "", state: "", postcode: "", country: "", phone: "", email: "")
132+
}
133+
121134
let coupons = try container.decode([OrderCouponLine].self, forKey: .couponLines)
122135

123-
self.init(siteID: siteID, orderID: orderID, parentID: parentID, customerID: customerID, number: number, status: status, currency: currency, customerNote: customerNote, dateCreated: dateCreated, dateModified: dateModified, datePaid: datePaid, discountTotal: discountTotal, discountTax: discountTax, shippingTotal: shippingTotal, shippingTax: shippingTax, total: total, totalTax: totalTax, paymentMethodTitle: paymentMethodTitle, items: items, billingAddress: billingAddress, shippingAddress: shippingAddress, coupons: coupons) // initialize the struct
136+
self.init(siteID: siteID, orderID: orderID, parentID: parentID, customerID: customerID, number: number, status: status, currency: currency, customerNote: customerNote, dateCreated: dateCreated, dateModified: dateModified, datePaid: datePaid, discountTotal: discountTotal, discountTax: discountTax, shippingTotal: shippingTotal, shippingTax: shippingTax, total: total, totalTax: totalTax, paymentMethodTitle: paymentMethodTitle, items: items, billingAddress: billingAddress, shippingAddress: shippingAddress, coupons: coupons)
124137
}
125138
}
126139

Networking/NetworkingTests/Mapper/OrderListMapperTests.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,14 @@ class OrderListMapperTests: XCTestCase {
5555
XCTAssert(orders.count == 3)
5656

5757
let firstOrder = orders[0]
58-
let dummyAddresses = [firstOrder.billingAddress, firstOrder.shippingAddress]
58+
var dummyAddresses = [Address]()
59+
if let shippingAddress = firstOrder.shippingAddress {
60+
dummyAddresses.append(shippingAddress)
61+
}
62+
63+
if let billingAddress = firstOrder.billingAddress {
64+
dummyAddresses.append(billingAddress)
65+
}
5966

6067
for address in dummyAddresses {
6168
XCTAssertEqual(address.firstName, "Johnny")

Networking/NetworkingTests/Mapper/OrderMapperTests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class OrderMapperTests: XCTestCase {
5050
return
5151
}
5252

53-
let dummyAddresses = [order.billingAddress, order.shippingAddress]
53+
let dummyAddresses = [order.shippingAddress, order.billingAddress].compactMap({ $0 })
54+
XCTAssertEqual(dummyAddresses.count, 2)
5455

5556
for address in dummyAddresses {
5657
XCTAssertEqual(address.firstName, "Johnny")

WooCommerce/Classes/Model/Order+Woo.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,16 @@ extension Order {
1414

1515
return NSLocale(localeIdentifier: identifier).currencySymbol
1616
}
17+
18+
/// Determines if a shipping address exists.
19+
///
20+
var hasSeparateShippingDetail: Bool {
21+
return shippingAddress != nil
22+
}
23+
24+
/// FIXME: Creates an empty-string value Billing details, until i6 fix
25+
///
26+
func generateEmptyBillingAddress() -> Address {
27+
return Address(firstName: "", lastName: "", company: "", address1: "", address2: "", city: "", state: "", postcode: "", country: "", phone: "", email: "")
28+
}
1729
}

WooCommerce/Classes/ViewModels/OrderDetailsViewModel.swift

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ class OrderDetailsViewModel {
1414
self.orderStatusViewModel = OrderStatusViewModel(orderStatus: order.status)
1515
}
1616

17-
var summaryTitle: String {
18-
return "#\(order.number) \(order.shippingAddress.firstName) \(order.shippingAddress.lastName)"
17+
var summaryTitle: String? {
18+
if let billingAddress = order.billingAddress {
19+
return "#\(order.number) \(billingAddress.firstName) \(billingAddress.lastName)"
20+
}
21+
return "#\(order.number)"
1922
}
2023

2124
var summaryDateCreated: String {
@@ -60,14 +63,30 @@ class OrderDetailsViewModel {
6063
}
6164

6265
var shippingViewModel: ContactViewModel {
63-
return ContactViewModel(with: order.shippingAddress, contactType: ContactType.shipping)
66+
if let shippingAddress = order.shippingAddress {
67+
return ContactViewModel(with: shippingAddress, contactType: ContactType.shipping)
68+
}
69+
70+
if let billingAddress = order.billingAddress {
71+
return ContactViewModel(with: billingAddress, contactType: ContactType.shipping)
72+
}
73+
74+
return ContactViewModel(with: order.generateEmptyBillingAddress(), contactType: ContactType.shipping)
6475
}
6576
var shippingAddress: String? {
6677
return shippingViewModel.formattedAddress
6778
}
6879

69-
private(set) lazy var billingViewModel = ContactViewModel(with: order.billingAddress, contactType: ContactType.billing)
70-
private(set) lazy var billingAddress = billingViewModel.formattedAddress
80+
var billingViewModel: ContactViewModel? {
81+
if let billingAddress = order.billingAddress {
82+
return ContactViewModel(with: billingAddress, contactType: ContactType.billing)
83+
}
84+
return nil
85+
}
86+
87+
var billingAddress: String? {
88+
return billingViewModel?.formattedAddress
89+
}
7190

7291
let subtotalLabel = NSLocalizedString("Subtotal", comment: "Subtotal label for payment view")
7392

WooCommerce/Classes/ViewRelated/Orders/FulfillViewController.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ private extension FulfillViewController {
257257
fatalError()
258258
}
259259

260-
let address = order.shippingAddress
260+
guard let address = order.shippingAddress ?? order.billingAddress else {
261+
return
262+
}
261263

262264
cell.title = NSLocalizedString("Shipping details", comment: "Shipping title for customer info cell")
263265
cell.name = address.fullName
@@ -377,7 +379,20 @@ private extension Section {
377379

378380
let address: Section = {
379381
let title = NSLocalizedString("Customer Information", comment: "")
380-
let row = Row.address(shipping: order.shippingAddress)
382+
if let shippingAddress = order.shippingAddress {
383+
let row = Row.address(shipping: shippingAddress)
384+
385+
return Section(title: title, secondaryTitle: nil, rows: [row])
386+
}
387+
388+
if let billingAddress = order.billingAddress {
389+
let row = Row.address(shipping: billingAddress)
390+
391+
return Section(title: title, secondaryTitle: nil, rows: [row])
392+
}
393+
394+
let emptyAddress = order.generateEmptyBillingAddress()
395+
let row = Row.address(shipping: emptyAddress)
381396

382397
return Section(title: title, secondaryTitle: nil, rows: [row])
383398
}()

WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderDetailsViewController.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,12 @@ private extension OrderDetailsViewController {
245245
case let cell as CustomerInfoTableViewCell where row == .shippingAddress:
246246
cell.configure(with: viewModel.shippingViewModel)
247247
case let cell as CustomerInfoTableViewCell where row == .billingAddress:
248-
cell.configure(with: viewModel.billingViewModel)
248+
if let billingViewModel = viewModel.billingViewModel {
249+
cell.configure(with: billingViewModel)
250+
} else {
251+
let billingViewModel = ContactViewModel(with: viewModel.order.generateEmptyBillingAddress(), contactType: ContactType.billing)
252+
cell.configure(with: billingViewModel)
253+
}
249254
case let cell as BillingDetailsTableViewCell where row == .billingPhone:
250255
configure(cell, for: .billingPhone)
251256
case let cell as BillingDetailsTableViewCell where row == .billingEmail:
@@ -265,12 +270,12 @@ private extension OrderDetailsViewController {
265270

266271
private func configure(_ cell: BillingDetailsTableViewCell, for billingRow: Row) {
267272
if billingRow == .billingPhone {
268-
cell.configure(text: viewModel.billingViewModel.phoneNumber, image: Gridicon.iconOfType(.ellipsis))
273+
cell.configure(text: viewModel.billingViewModel?.phoneNumber, image: Gridicon.iconOfType(.ellipsis))
269274
cell.onTouchUp = { [weak self] in
270275
self?.phoneButtonAction()
271276
}
272277
} else if billingRow == .billingEmail {
273-
cell.configure(text: viewModel.billingViewModel.email, image: Gridicon.iconOfType(.mail))
278+
cell.configure(text: viewModel.billingViewModel?.email, image: Gridicon.iconOfType(.mail))
274279
cell.onTouchUp = { [weak self] in
275280
self?.emailButtonAction()
276281
}
@@ -471,7 +476,11 @@ extension OrderDetailsViewController: UITableViewDelegate {
471476
//
472477
extension OrderDetailsViewController: MFMessageComposeViewControllerDelegate {
473478
func sendTextMessageIfPossible() {
474-
let contactViewModel = ContactViewModel(with: viewModel.order.billingAddress, contactType: .billing)
479+
guard let billingAddress = viewModel.order.billingAddress else {
480+
return
481+
}
482+
483+
let contactViewModel = ContactViewModel(with: billingAddress, contactType: .billing)
475484
guard let phoneNumber = contactViewModel.cleanedPhoneNumber else {
476485
return
477486
}
@@ -498,10 +507,15 @@ extension OrderDetailsViewController: MFMessageComposeViewControllerDelegate {
498507
extension OrderDetailsViewController: MFMailComposeViewControllerDelegate {
499508
func sendEmailIfPossible() {
500509
if MFMailComposeViewController.canSendMail() {
501-
let contactViewModel = ContactViewModel(with: viewModel.order.billingAddress, contactType: .billing)
510+
guard let billingAddress = viewModel.order.billingAddress else {
511+
return
512+
}
513+
514+
let contactViewModel = ContactViewModel(with: billingAddress, contactType: .billing)
502515
guard let email = contactViewModel.email else {
503516
return
504517
}
518+
505519
sendEmail(to: email)
506520
}
507521
}

Yosemite/Yosemite/Model/Storage/Order+ReadOnlyConvertible.swift

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,33 @@ extension Storage.Order: ReadOnlyConvertible {
3030
totalTax = order.totalTax
3131
paymentMethodTitle = order.paymentMethodTitle
3232

33-
billingFirstName = order.billingAddress.firstName
34-
billingLastName = order.billingAddress.lastName
35-
billingCompany = order.billingAddress.company
36-
billingAddress1 = order.billingAddress.address1
37-
billingAddress2 = order.billingAddress.address2
38-
billingCity = order.billingAddress.city
39-
billingState = order.billingAddress.state
40-
billingPostcode = order.billingAddress.postcode
41-
billingCountry = order.billingAddress.country
42-
billingPhone = order.billingAddress.phone
43-
billingEmail = order.billingAddress.email
33+
if let billingAddress = order.billingAddress {
34+
billingFirstName = billingAddress.firstName
35+
billingLastName = billingAddress.lastName
36+
billingCompany = billingAddress.company
37+
billingAddress1 = billingAddress.address1
38+
billingAddress2 = billingAddress.address2
39+
billingCity = billingAddress.city
40+
billingState = billingAddress.state
41+
billingPostcode = billingAddress.postcode
42+
billingCountry = billingAddress.country
43+
billingPhone = billingAddress.phone
44+
billingEmail = billingAddress.email
45+
}
4446

45-
shippingFirstName = order.shippingAddress.firstName
46-
shippingLastName = order.shippingAddress.lastName
47-
shippingCompany = order.shippingAddress.company
48-
shippingAddress1 = order.shippingAddress.address1
49-
shippingAddress2 = order.shippingAddress.address2
50-
shippingCity = order.shippingAddress.city
51-
shippingState = order.shippingAddress.state
52-
shippingPostcode = order.shippingAddress.postcode
53-
shippingCountry = order.shippingAddress.country
54-
shippingPhone = order.shippingAddress.phone
55-
shippingEmail = order.shippingAddress.email
47+
if let shippingAddress = order.shippingAddress {
48+
shippingFirstName = shippingAddress.firstName
49+
shippingLastName = shippingAddress.lastName
50+
shippingCompany = shippingAddress.company
51+
shippingAddress1 = shippingAddress.address1
52+
shippingAddress2 = shippingAddress.address2
53+
shippingCity = shippingAddress.city
54+
shippingState = shippingAddress.state
55+
shippingPostcode = shippingAddress.postcode
56+
shippingCountry = shippingAddress.country
57+
shippingPhone = shippingAddress.phone
58+
shippingEmail = shippingAddress.email
59+
}
5660
}
5761

5862
/// Returns a ReadOnly version of the receiver.

0 commit comments

Comments
 (0)