Skip to content

Commit c2fc6dc

Browse files
authored
Merge pull request #67 from woocommerce/issue/12-payment
Payment in order details
2 parents 585ec97 + eeab9ef commit c2fc6dc

File tree

14 files changed

+752
-34
lines changed

14 files changed

+752
-34
lines changed

Podfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ PODS:
5656
- UIDeviceIdentifier (~> 0.4)
5757
- WordPressShared (~> 1.0.3)
5858
- wpxmlrpc (= 0.8.3)
59-
- WordPressShared (1.0.5):
59+
- WordPressShared (1.0.7):
6060
- CocoaLumberjack (~> 3.4)
6161
- FormatterKit/TimeIntervalFormatter (= 1.8.2)
6262
- WordPressUI (1.0.4)
@@ -126,10 +126,10 @@ SPEC CHECKSUMS:
126126
UIDeviceIdentifier: a959a6d4f51036b4180dd31fb26483a820f1cc46
127127
WordPressAuthenticator: 4c802aa18781858253daf984f873e3efe0dac7ef
128128
WordPressKit: a24baaa783c3a221f2d9a51c19318cbb27333373
129-
WordPressShared: d7fdb0ca9302cf4bc7f3ec0fbe98d0d8eb721438
129+
WordPressShared: db964b81e02ff9be1ea2ff65ca9a4d57c49e82ba
130130
WordPressUI: f2348649b63b5a9392a72b1d2f46dd1d72e80ad9
131131
wpxmlrpc: bfc572f62ce7ee897f6f38b098d2ba08732ecef4
132132

133133
PODFILE CHECKSUM: 1a514306be7bf312acfaeb1ec608a21807147f66
134134

135-
COCOAPODS: 1.5.3
135+
COCOAPODS: 1.5.2

WooCommerce/Classes/Model/Order.swift

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@ struct Order: Decodable {
1919
let billingAddress: Address
2020
let items: [OrderItem]
2121
let currency: String
22-
let totalString: String
22+
let total: String
2323
let notes: [OrderNote]?
2424
let customerID: Int
2525
let customerNote: String?
26+
let couponLines: [CouponLine]?
27+
let discountTotal: String
28+
let shippingTotal: String
29+
let totalTax: String
30+
let paymentMethod: String
31+
let paymentMethodTitle: String
2632

27-
init(identifier: Int, number: String, statusString: String, status: OrderStatus, customer: Customer?, dateCreatedString: String, dateUpdatedString: String, shippingAddress: Address, billingAddress: Address, items: [OrderItem], currency: String, totalString: String, notes: [OrderNote]?, customerID: Int, customerNote: String?) {
33+
init(identifier: Int, number: String, statusString: String, status: OrderStatus, customer: Customer?, dateCreatedString: String, dateUpdatedString: String, shippingAddress: Address, billingAddress: Address, items: [OrderItem], currency: String, total: String, notes: [OrderNote]?, customerID: Int, customerNote: String?, couponLines: [CouponLine]?, discountTotal: String, shippingTotal: String, totalTax: String, paymentMethod: String, paymentMethodTitle: String) {
2834
self.identifier = identifier
2935
self.number = number
3036
self.statusString = statusString
@@ -36,10 +42,16 @@ struct Order: Decodable {
3642
self.billingAddress = billingAddress
3743
self.items = items
3844
self.currency = currency
39-
self.totalString = totalString
45+
self.total = total
4046
self.notes = notes
4147
self.customerID = customerID
4248
self.customerNote = customerNote
49+
self.couponLines = couponLines
50+
self.discountTotal = discountTotal
51+
self.shippingTotal = shippingTotal
52+
self.totalTax = totalTax
53+
self.paymentMethod = paymentMethod
54+
self.paymentMethodTitle = paymentMethodTitle
4355
}
4456

4557
init(from decoder: Decoder) throws {
@@ -55,12 +67,18 @@ struct Order: Decodable {
5567
let billingAddress = try container.decode(Address.self, forKey: .billingAddress)
5668
let items = try container.decode([OrderItem].self, forKey: .orderItems)
5769
let currency = try container.decode(String.self, forKey: .currency)
58-
let totalString = try container.decode(String.self, forKey: .total)
70+
let total = try container.decode(String.self, forKey: .total)
5971
let notes = try container.decodeIfPresent([OrderNote].self, forKey: .notes)
6072
let customerID = try container.decode(Int.self, forKey: .customerID)
6173
let customerNote = try container.decode(String.self, forKey: .customerNote)
74+
let couponLines = try container.decodeIfPresent([CouponLine].self, forKey: .couponLines)
75+
let discountTotal = try container.decode(String.self, forKey: .discountTotal)
76+
let shippingTotal = try container.decode(String.self, forKey: .shippingTotal)
77+
let totalTax = try container.decode(String.self, forKey: .totalTax)
78+
let paymentMethod = try container.decode(String.self, forKey: .paymentMethod)
79+
let paymentMethodTitle = try container.decode(String.self, forKey: .paymentMethodTitle)
6280

63-
self.init(identifier: identifier, number: number, statusString: statusString, status: status, customer: customer, dateCreatedString: dateCreatedString, dateUpdatedString: dateUpdatedString, shippingAddress: shippingAddress, billingAddress: billingAddress, items: items, currency: currency, totalString: totalString, notes: notes, customerID: customerID, customerNote: customerNote)
81+
self.init(identifier: identifier, number: number, statusString: statusString, status: status, customer: customer, dateCreatedString: dateCreatedString, dateUpdatedString: dateUpdatedString, shippingAddress: shippingAddress, billingAddress: billingAddress, items: items, currency: currency, total: total, notes: notes, customerID: customerID, customerNote: customerNote, couponLines: couponLines, discountTotal: discountTotal, shippingTotal: shippingTotal, totalTax: totalTax, paymentMethod: paymentMethod, paymentMethodTitle: paymentMethodTitle)
6482
}
6583

6684
var currencySymbol: String {
@@ -83,12 +101,27 @@ struct Order: Decodable {
83101
case total = "total"
84102
case customerNote = "customer_note"
85103
case notes = "notes"
104+
case couponLines = "coupon_lines"
105+
case discountTotal = "discount_total"
106+
case shippingTotal = "shipping_total"
107+
case totalTax = "total_tax"
108+
case paymentMethod = "payment_method"
109+
case paymentMethodTitle = "payment_method_title"
86110
}
87111

88112
var dateCreated: Date {
89113
// TODO: use WordPressShared date helpers to convert dateCreatedString into a Date
90114
return Date()
91115
}
116+
117+
var subtotal: String {
118+
let subtotal = items.reduce(0.0) { (output, item) in
119+
let itemSubtotal = Double(item.subtotal) ?? 0.0
120+
return output + itemSubtotal
121+
}
122+
123+
return String(format: "%.2f", subtotal)
124+
}
92125
}
93126

94127
// MARK: -
@@ -407,3 +440,26 @@ extension Customer: Decodable {
407440
self.init(identifier: identifier, firstName: firstName, lastName: lastName, email: email, phone: phone, billingAddress: billingAddress, shippingAddress: shippingAddress, note: note)
408441
}
409442
}
443+
444+
struct CouponLine: Decodable {
445+
let id: Int
446+
let code: String
447+
448+
init(id: Int, code: String) {
449+
self.id = id
450+
self.code = code
451+
}
452+
453+
enum CouponLineStructKeys: String, CodingKey {
454+
case id = "id"
455+
case code = "code"
456+
}
457+
458+
init(from decoder: Decoder) throws {
459+
let container = try decoder.container(keyedBy: CouponLineStructKeys.self)
460+
let id = try container.decode(Int.self, forKey: .id)
461+
let code = try container.decode(String.self, forKey: .code)
462+
463+
self.init(id: id, code: code)
464+
}
465+
}

WooCommerce/Classes/Styles/Style.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ protocol Style {
1717
var sectionTitleColor: UIColor { get }
1818
var buttonPrimaryColor: UIColor { get }
1919
var buttonPrimaryHighlightedColor: UIColor { get }
20+
var cellSeparatorColor: UIColor { get }
2021
}
2122

2223
// MARK: - WooCommerce's Default Style
@@ -35,6 +36,7 @@ class DefaultStyle: Style {
3536
let sectionTitleColor = UIColor.darkGray
3637
let buttonPrimaryColor = UIColor(red: 0x96/255.0, green: 0x58/255.0, blue: 0x8A/255.0, alpha: 0xFF/255.0)
3738
let buttonPrimaryHighlightedColor = UIColor(red: 0x6E/255.0, green: 0x29/255.0, blue: 0x67/255.0, alpha: 0xFF/255.0)
39+
let cellSeparatorColor = UIColor.lightGray
3840
}
3941

4042

@@ -106,4 +108,8 @@ class StyleManager {
106108
static var buttonPrimaryHighlightedColor: UIColor {
107109
return active.buttonPrimaryHighlightedColor
108110
}
111+
112+
static var cellSeparatorColor: UIColor {
113+
return active.cellSeparatorColor
114+
}
109115
}

WooCommerce/Classes/ViewModels/OrderDetailsViewModel.swift

Lines changed: 99 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,106 @@ import Foundation
22
import UIKit
33

44
class OrderDetailsViewModel {
5-
let summaryTitle: String
6-
let dateCreated: String
7-
let paymentStatus: String
8-
let paymentBackgroundColor: UIColor
9-
let paymentBorderColor: CGColor
10-
let customerNote: String?
11-
let shippingAddress: String?
12-
let billingAddress: String?
13-
let shippingViewModel: ContactViewModel
14-
let billingViewModel: ContactViewModel
5+
private let order: Order
6+
private let couponLines: [CouponLine]?
157

168
init(order: Order) {
17-
summaryTitle = "#\(order.number) \(order.shippingAddress.firstName) \(order.shippingAddress.lastName)"
18-
dateCreated = String.localizedStringWithFormat(NSLocalizedString("Created %@", comment: "Order created date"), order.dateCreatedString) //FIXME: use a formatted date instead of raw timestamp
19-
paymentStatus = order.status.description
20-
paymentBackgroundColor = order.status.backgroundColor // MVVM: who should own color responsibilities? Maybe address this down the road.
21-
paymentBorderColor = order.status.borderColor // same here
22-
customerNote = order.customerNote
23-
shippingViewModel = ContactViewModel(with: order.shippingAddress, contactType: ContactType.shipping)
24-
shippingAddress = shippingViewModel.formattedAddress
25-
billingViewModel = ContactViewModel(with: order.billingAddress, contactType: ContactType.billing)
26-
billingAddress = billingViewModel.formattedAddress
9+
self.order = order
10+
self.couponLines = order.couponLines
11+
}
12+
13+
var summaryTitle: String {
14+
return "#\(order.number) \(order.shippingAddress.firstName) \(order.shippingAddress.lastName)"
15+
}
16+
17+
var dateCreated: String {
18+
return String.localizedStringWithFormat(NSLocalizedString("Created %@", comment: "Order created date"), order.dateCreatedString) //FIXME: use a formatted date instead of raw timestamp
19+
}
20+
21+
var paymentStatus: String {
22+
return order.status.description
23+
}
24+
25+
var paymentBackgroundColor: UIColor {
26+
return order.status.backgroundColor // MVVM: who should own color responsibilities? Maybe address this down the road.
27+
}
28+
29+
var paymentBorderColor: CGColor {
30+
return order.status.borderColor // same here
31+
}
32+
33+
var customerNote: String? {
34+
return order.customerNote
35+
}
36+
37+
var shippingViewModel: ContactViewModel {
38+
return ContactViewModel(with: order.shippingAddress, contactType: ContactType.shipping)
39+
}
40+
var shippingAddress: String? {
41+
return shippingViewModel.formattedAddress
42+
}
43+
44+
private(set) lazy var billingViewModel = ContactViewModel(with: order.billingAddress, contactType: ContactType.billing)
45+
private(set) lazy var billingAddress = billingViewModel.formattedAddress
46+
47+
let subtotalLabel = NSLocalizedString("Subtotal", comment: "Subtotal label for payment view")
48+
49+
var subtotalValue: String {
50+
return order.currencySymbol + order.subtotal
51+
}
52+
53+
var discountLabel: String? {
54+
return summarizeCoupons(from: couponLines)
55+
}
56+
57+
var discountValue: String? {
58+
return Double(order.discountTotal) != 0 ? "" + order.currencySymbol + order.discountTotal : nil
59+
}
60+
61+
var shippingLabel: String {
62+
return NSLocalizedString("Shipping", comment: "Shipping label for payment view")
63+
}
64+
65+
var shippingValue: String {
66+
return order.currencySymbol + order.shippingTotal
67+
}
68+
69+
var taxesLabel: String? {
70+
return Double(order.totalTax) != 0 ? NSLocalizedString("Taxes", comment: "Taxes label for payment view") : nil
71+
}
72+
73+
var taxesValue: String? {
74+
return Double(order.totalTax) != 0 ? order.currencySymbol + order.totalTax : nil
75+
}
76+
77+
var totalLabel: String {
78+
return NSLocalizedString("Total", comment: "Total label for payment view")
79+
}
80+
81+
var totalValue: String {
82+
return order.currencySymbol + order.total
83+
}
84+
85+
var paymentSummary: String {
86+
return NSLocalizedString("Payment of \(totalValue) received via \(order.paymentMethodTitle)", comment: "Payment of <currency symbol><payment total> received via (payment method title)")
87+
}
88+
89+
/// MARK: Private
90+
///
91+
private func summarizeCoupons(from lines: [CouponLine]?) -> String? {
92+
guard let couponLines = lines else {
93+
return nil
94+
}
95+
96+
let output = couponLines.reduce("") { (output, line) in
97+
let prefix = output.isEmpty ? "" : ","
98+
return output + prefix + line.code
99+
}
100+
101+
guard !output.isEmpty else {
102+
return nil
103+
}
104+
105+
return NSLocalizedString("Discount", comment: "Discount label for payment view") + " (" + output + ")"
27106
}
28107
}

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ class OrderDetailsViewController: UIViewController {
1414
}
1515

1616
var viewModel: OrderDetailsViewModel!
17-
var sectionTitles = [String]()
1817
var billingIsHidden = true
1918
private var sections = [Section]()
2019

@@ -33,6 +32,8 @@ class OrderDetailsViewController: UIViewController {
3332
}
3433

3534
func configureTableView() {
35+
tableView.estimatedRowHeight = Constants.rowHeight
36+
tableView.rowHeight = UITableViewAutomaticDimension
3637
configureSections()
3738
configureNibs()
3839
}
@@ -45,16 +46,18 @@ class OrderDetailsViewController: UIViewController {
4546
let infoRows: [Row] = billingIsHidden ? [.shippingAddress] : [.shippingAddress, .billingAddress, .billingPhone, .billingEmail]
4647
let infoSection = Section(title: NSLocalizedString("CUSTOMER INFORMATION", comment: "Customer info section title"), footer: infoFooter, rows: infoRows)
4748

49+
let paymentSection = Section(title: NSLocalizedString("PAYMENT", comment: "Payment section title"), footer: nil, rows: [.payment])
50+
4851
// FIXME: this is temporary
4952
// the API response always sends customer note data
5053
// if there is no customer note it sends an empty string
5154
// but order has customerNote as an optional property right now
5255
guard let customerNote = order.customerNote,
5356
!customerNote.isEmpty else {
54-
sections = [summarySection, infoSection]
57+
sections = [summarySection, infoSection, paymentSection]
5558
return
5659
}
57-
sections = [summarySection, customerNoteSection, infoSection]
60+
sections = [summarySection, customerNoteSection, infoSection, paymentSection]
5861
}
5962

6063
func configureNibs() {
@@ -153,6 +156,8 @@ extension OrderDetailsViewController {
153156
configure(cell, for: .billingPhone)
154157
case let cell as BillingDetailsTableViewCell where row == .billingEmail:
155158
configure(cell, for: .billingEmail)
159+
case let cell as PaymentTableViewCell:
160+
cell.configure(with: viewModel)
156161
default:
157162
fatalError("Unidentified customer info row type")
158163
}
@@ -277,6 +282,7 @@ private extension OrderDetailsViewController {
277282
case billingAddress
278283
case billingPhone
279284
case billingEmail
285+
case payment
280286

281287
var reuseIdentifier: String {
282288
switch self {
@@ -292,6 +298,8 @@ private extension OrderDetailsViewController {
292298
return BillingDetailsTableViewCell.reuseIdentifier
293299
case .billingEmail:
294300
return BillingDetailsTableViewCell.reuseIdentifier
301+
case .payment:
302+
return PaymentTableViewCell.reuseIdentifier
295303
}
296304
}
297305
}

0 commit comments

Comments
 (0)