diff --git a/WooCommerce/Classes/ViewRelated/Orders/FulfillViewController.swift b/WooCommerce/Classes/ViewRelated/Orders/FulfillViewController.swift index 19dfec66846..31a0b94489f 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/FulfillViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/FulfillViewController.swift @@ -1,5 +1,7 @@ import Foundation import UIKit +import CocoaLumberjack + import Yosemite import Gridicons @@ -80,6 +82,7 @@ private extension FulfillViewController { /// func setupTableView() { tableView.tableFooterView = actionView + tableView.allowsSelection = false } ///Setup: Action Button! @@ -121,8 +124,42 @@ private extension FulfillViewController { // extension FulfillViewController { + /// Whenever the Fulfillment Action is pressed, we'll mark the order as Completed, and pull back to the previous screen. + /// @IBAction func fulfillWasPressed() { -// TODO: Fill Me! + let done = updateOrderAction(siteID: order.siteID, orderID: order.orderID, status: .completed) + let undo = updateOrderAction(siteID: order.siteID, orderID: order.orderID, status: order.status) + + StoresManager.shared.dispatch(done) + + displayOrderCompleteNotice { + StoresManager.shared.dispatch(undo) + } + + navigationController?.popViewController(animated: true) + } + + /// Returns an Order Update Action that will result in the specified Order Status updated accordingly. + /// + private func updateOrderAction(siteID: Int, orderID: Int, status: OrderStatus) -> Action { + return OrderAction.updateOrder(siteID: siteID, orderID: orderID, status: status, onCompletion: { error in + guard let error = error else { + return + } + + DDLogError("⛔️ Order Update Failure: [\(orderID).status = \(status.rawValue)]. Error: \(error)") + }) + } + + /// Displays the `Order Fulfilled` Notice. Whenever the `Undo` button gets pressed, we'll execute the `onUndoAction` closure. + /// + private func displayOrderCompleteNotice(onUndoAction: @escaping () -> Void) { + let title = NSLocalizedString("Fulfillment", comment: "Fulfill Notice Title") + let message = NSLocalizedString("Order Marked as Complete!", comment: "Fulfill Notice Message") + let actionTitle = NSLocalizedString("Undo", comment: "Undo Action") + let notice = Notice(title: title, message: message, feedbackType: .success, actionTitle: actionTitle, actionHandler: onUndoAction) + + AppDelegate.shared.noticePresenter.enqueue(notice: notice) } } @@ -345,13 +382,14 @@ private extension Section { return Section(title: title, secondaryTitle: nil, rows: [row]) }() - let tracking: Section = { - let title = NSLocalizedString("Optional Tracking Information", comment: "") - let row = Row.trackingAdd - - return Section(title: title, secondaryTitle: nil, rows: [row]) - }() +// TODO: Tracking support to be added via #185 +// let tracking: Section = { +// let title = NSLocalizedString("Optional Tracking Information", comment: "") +// let row = Row.trackingAdd +// +// return Section(title: title, secondaryTitle: nil, rows: [row]) +// }() - return [products, note, address, tracking].compactMap { $0 } + return [products, note, address].compactMap { $0 } } } diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/BillingDetailsTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/BillingDetailsTableViewCell.xib index c3d50297496..e36748e1ff9 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/BillingDetailsTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/BillingDetailsTableViewCell.xib @@ -11,7 +11,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/CustomerInfoTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/CustomerInfoTableViewCell.xib index 9861ebed7e3..1fb781a31b9 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/CustomerInfoTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/CustomerInfoTableViewCell.xib @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/CustomerNoteTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/CustomerNoteTableViewCell.xib index ca35c3245af..6e0de64bf7f 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/CustomerNoteTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/CustomerNoteTableViewCell.xib @@ -1,5 +1,5 @@ - + @@ -12,7 +12,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderDetailsViewController.swift b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderDetailsViewController.swift index 1a9dd68196a..6ea070d41c4 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderDetailsViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderDetailsViewController.swift @@ -3,6 +3,7 @@ import Gridicons import Contacts import MessageUI import Yosemite +import Storage import CocoaLumberjack @@ -38,6 +39,16 @@ class OrderDetailsViewController: UIViewController { } private var sections = [Section]() + /// TODO: Replace with `ResultController` (OR) `ObjectController` ASAP + /// + private lazy var resultsController: ResultsController = { + let viewContext = CoreDataManager.global.viewContext + let predicate = NSPredicate(format: "orderID = %ld", self.viewModel.order.orderID) + let descriptor = NSSortDescriptor(key: "orderID", ascending: true) + + return ResultsController(viewContext: viewContext, matching: predicate, sortedBy: [descriptor]) + }() + // MARK: - View Lifecycle @@ -45,6 +56,7 @@ class OrderDetailsViewController: UIViewController { super.viewDidLoad() configureNavigation() configureTableView() + configureResultsController() registerTableViewCells() registerTableViewHeaderFooters() } @@ -84,6 +96,20 @@ private extension OrderDetailsViewController { navigationItem.backBarButtonItem = UIBarButtonItem(title: String(), style: .plain, target: nil, action: nil) } + /// TODO: Replace with `ResultController` (OR) `ObjectController` ASAP + /// + func configureResultsController() { + try? resultsController.performFetch() + resultsController.onDidChangeContent = { [weak self] in + guard let `self` = self, let order = self.resultsController.fetchedObjects.first else { + return + } + + self.viewModel = OrderDetailsViewModel(order: order) + self.tableView.reloadData() + } + } + /// Setup: Sections /// func reloadSections() { diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderNoteTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderNoteTableViewCell.xib index 5b774b048d8..31f4a56c272 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderNoteTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderNoteTableViewCell.xib @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/ProductDetailsTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/ProductDetailsTableViewCell.xib index 962fb9c7adf..1c78d63261d 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/ProductDetailsTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/ProductDetailsTableViewCell.xib @@ -5,80 +5,90 @@ + + - - + + - + - - + + + + + + + + + - - - - - - - - - + + - - - - - - - - - - - - - + + + + + + @@ -90,7 +100,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/ProductListTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/ProductListTableViewCell.xib index 1d03478565e..aee21ec7892 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/ProductListTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/ProductListTableViewCell.xib @@ -11,7 +11,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/SummaryTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/SummaryTableViewCell.xib index 9c3cbff1a83..581c0bf7eb3 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/SummaryTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/SummaryTableViewCell.xib @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/ReusableViews/BasicDisclosureTableViewCell.xib b/WooCommerce/Classes/ViewRelated/ReusableViews/BasicDisclosureTableViewCell.xib index 2005c210c89..cbad18277ef 100644 --- a/WooCommerce/Classes/ViewRelated/ReusableViews/BasicDisclosureTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/ReusableViews/BasicDisclosureTableViewCell.xib @@ -11,7 +11,7 @@ - + diff --git a/Yosemite/Yosemite/Actions/OrderAction.swift b/Yosemite/Yosemite/Actions/OrderAction.swift index 4d65f73a68b..cad752fc6bb 100644 --- a/Yosemite/Yosemite/Actions/OrderAction.swift +++ b/Yosemite/Yosemite/Actions/OrderAction.swift @@ -8,5 +8,5 @@ import Networking public enum OrderAction: Action { case retrieveOrders(siteID: Int, onCompletion: ([Order]?, Error?) -> Void) case retrieveOrder(siteID: Int, orderID: Int, onCompletion: (Order?, Error?) -> Void) - case updateOrder(siteID: Int, orderID: Int, status: OrderStatus, onCompletion: (Order?, Error?) -> Void) + case updateOrder(siteID: Int, orderID: Int, status: OrderStatus, onCompletion: (Error?) -> Void) } diff --git a/Yosemite/Yosemite/Stores/OrderStore.swift b/Yosemite/Yosemite/Stores/OrderStore.swift index 00e766b0417..a81eaf23af7 100644 --- a/Yosemite/Yosemite/Stores/OrderStore.swift +++ b/Yosemite/Yosemite/Stores/OrderStore.swift @@ -71,22 +71,21 @@ private extension OrderStore { /// Updates an Order with the specified Status. /// - func updateOrder(siteID: Int, orderID: Int, status: OrderStatus, onCompletion: @escaping (Order?, Error?) -> Void) { + func updateOrder(siteID: Int, orderID: Int, status: OrderStatus, onCompletion: @escaping (Error?) -> Void) { /// Optimistically update the Status let oldStatus = updateOrderStatus(orderID: orderID, status: status) let remote = OrdersRemote(network: network) - remote.updateOrder(from: siteID, orderID: orderID, status: status.description) { [weak self] (order, error) in - guard let order = order else { - - /// Revert Optimistic Update - self?.updateOrderStatus(orderID: orderID, status: oldStatus) - onCompletion(nil, error) + remote.updateOrder(from: siteID, orderID: orderID, status: status.rawValue) { [weak self] (order, error) in + guard let error = error else { + // NOTE: We're *not* actually updating the whole entity here. Reason: Prevent UI inconsistencies!! + onCompletion(nil) return } - self?.upsertStoredOrder(readOnlyOrder: order) - onCompletion(order, nil) + /// Revert Optimistic Update + self?.updateOrderStatus(orderID: orderID, status: oldStatus) + onCompletion(error) } } } @@ -108,7 +107,7 @@ extension OrderStore { } let oldStatus = OrderStatus(rawValue: order.status) - order.status = status.description + order.status = status.rawValue storage.saveIfNeeded() return oldStatus diff --git a/Yosemite/YosemiteTests/Stores/OrderStoreTests.swift b/Yosemite/YosemiteTests/Stores/OrderStoreTests.swift index 154ec0a8eeb..57f3e9a0753 100644 --- a/Yosemite/YosemiteTests/Stores/OrderStoreTests.swift +++ b/Yosemite/YosemiteTests/Stores/OrderStoreTests.swift @@ -276,10 +276,11 @@ class OrderStoreTests: XCTestCase { // Update: Expected Status is actually coming from `order.json` (Status == .processing actually!) network.simulateResponse(requestUrlSuffix: "orders/963", filename: "order") - let action = OrderAction.updateOrder(siteID: sampleSiteID, orderID: sampleOrderID, status: .processing) { (order, error) in + let action = OrderAction.updateOrder(siteID: sampleSiteID, orderID: sampleOrderID, status: .processing) { error in XCTAssertNil(error) - XCTAssertNotNil(order) - XCTAssert(order!.status == .processing) + + let storageOrder = self.storageManager.viewStorage.loadOrder(orderID: self.sampleOrderID) + XCTAssert(storageOrder?.status == OrderStatus.processing.rawValue) expectation.fulfill() } @@ -299,12 +300,11 @@ class OrderStoreTests: XCTestCase { network.removeAllSimulatedResponses() - let action = OrderAction.updateOrder(siteID: sampleSiteID, orderID: sampleOrderID, status: .processing) { (order, error) in + let action = OrderAction.updateOrder(siteID: sampleSiteID, orderID: sampleOrderID, status: .processing) { error in XCTAssertNotNil(error) - XCTAssertNil(order) - let storedOrder = self.storageManager.viewStorage.loadOrder(orderID: self.sampleOrderID) - XCTAssert(storedOrder?.status == OrderStatus.completed.description) + let storageOrder = self.storageManager.viewStorage.loadOrder(orderID: self.sampleOrderID) + XCTAssert(storageOrder?.status == OrderStatus.completed.rawValue) expectation.fulfill() }