Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import Foundation

public struct BookingCustomerInfo: Hashable {
public let billingAddress: Address
public let note: String?

public init(billingAddress: Address) {
public init(billingAddress: Address, note: String? = nil) {
self.billingAddress = billingAddress
self.note = note
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ public struct BookingOrderInfo: Hashable {
guard let billingAddress = order.billingAddress else {
return nil
}
return BookingCustomerInfo(billingAddress: billingAddress)
return BookingCustomerInfo(
billingAddress: billingAddress,
note: order.customerNote
)
}()
self.productInfo = BookingProductInfo(name: order.items.first(where: { $0.productID == booking.productID })?.name ?? "")
self.paymentInfo = BookingPaymentInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ extension BookingCustomerInfo {
@NSManaged public var billingPhone: String?
@NSManaged public var billingPostcode: String?
@NSManaged public var billingState: String?
@NSManaged public var note: String?
@NSManaged public var orderInfo: BookingOrderInfo?

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Model 130.xcdatamodel</string>
<string>Model 131.xcdatamodel</string>
</dict>
</plist>

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extension Storage.BookingCustomerInfo: ReadOnlyConvertible {
billingPhone = customerInfo.billingAddress.phone
billingPostcode = customerInfo.billingAddress.postcode
billingState = customerInfo.billingAddress.state
note = customerInfo.note
}

public func toReadOnly() -> Yosemite.BookingCustomerInfo {
Expand All @@ -30,6 +31,6 @@ extension Storage.BookingCustomerInfo: ReadOnlyConvertible {
country: billingCountry ?? "",
phone: billingPhone,
email: billingEmail)
return .init(billingAddress: address)
return .init(billingAddress: address, note: note)
}
}
39 changes: 37 additions & 2 deletions Modules/Tests/StorageTests/CoreData/MigrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2333,6 +2333,36 @@ final class MigrationTests: XCTestCase {
// `note` should be present in `migratedBooking`
XCTAssertNotNil(migratedBooking.entity.attributesByName["note"])
}

func test_migrating_from_130_to_131_adds_note_attribute_to_bookingCustomerInfo() throws {
// Given
let sourceContainer = try startPersistentContainer("Model 130")
let sourceContext = sourceContainer.viewContext

let customerInfo = insertBookingCustomerInfo(to: sourceContext)
try sourceContext.save()

XCTAssertNil(customerInfo.entity.attributesByName["note"], "Precondition. Attribute does not exist.")

// When
let targetContainer = try migrate(sourceContainer, to: "Model 131")

// Then
let targetContext = targetContainer.viewContext
let migratedCustomerInfo = try XCTUnwrap(targetContext.first(entityName: "BookingCustomerInfo"))

// `note` should be present in `migratedCustomerInfo`
XCTAssertNotNil(migratedCustomerInfo.entity.attributesByName["note"])

let noteValue = migratedCustomerInfo.value(forKey: "note") as? String
XCTAssertNil(noteValue)

let updatedNote = "Customer note"
migratedCustomerInfo.setValue(updatedNote, forKey: "note")
try targetContext.save()

XCTAssertEqual(migratedCustomerInfo.value(forKey: "note") as? String, updatedNote)
}
}

// MARK: - Persistent Store Setup and Migrations
Expand Down Expand Up @@ -3275,7 +3305,7 @@ private extension MigrationTests {

@discardableResult
func insertBookingCustomerInfo(to context: NSManagedObjectContext) -> NSManagedObject {
context.insert(entityName: "BookingCustomerInfo", properties: [
var properties: [String: Any] = [
"billingFirstName": "John",
"billingLastName": "Doe",
"billingEmail": "[email protected]",
Expand All @@ -3284,7 +3314,12 @@ private extension MigrationTests {
"billingState": "CA",
"billingPostcode": "94102",
"billingCountry": "US"
])
]
if let entity = NSEntityDescription.entity(forEntityName: "BookingCustomerInfo", in: context),
entity.attributesByName.keys.contains("note") {
properties["note"] = "Sample note"
}
return context.insert(entityName: "BookingCustomerInfo", properties: properties)
}

@discardableResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,10 @@ private extension BookingDetailsViewModel {
headerContent.update(with: booking)

setupCustomerSectionVisibility()
if let billingAddress = booking.orderInfo?.customerInfo?.billingAddress, !billingAddress.isEmpty {
customerContent.update(with: billingAddress)
if let orderInfo = booking.orderInfo,
let customerInfo = orderInfo.customerInfo,
customerInfo.billingAddress.isEmpty == false {
customerContent.update(with: customerInfo)
}

appointmentDetailsContent.update(with: booking, resource: bookingResource)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ extension BookingDetailsViewModel {
@Published var emailText: String?
@Published var phoneText: String?
@Published var billingAddressText: String?
@Published var noteText: String?

func update(with billingAddress: Address) {
func update(with customerInfo: BookingCustomerInfo) {
let billingAddress = customerInfo.billingAddress
nameText = billingAddress.fullName
emailText = billingAddress.email ?? ""
phoneText = billingAddress.phone ?? ""
billingAddressText = formatAddress(billingAddress)
noteText = formattedNote(customerInfo.note)
}

private func formatAddress(_ address: Address) -> String {
Expand All @@ -28,5 +31,13 @@ extension BookingDetailsViewModel {
.filter { !$0.isEmpty }
.joined(separator: "\n")
}

private func formattedNote(_ note: String?) -> String? {
guard let trimmedNote = note?.trimmingCharacters(in: .whitespacesAndNewlines),
trimmedNote.isEmpty == false else {
return nil
}
return trimmedNote
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ extension BookingDetailsView {
case email(String)
case phone(String)
case billingAddress(String)
case note(String)
}

private var rows: [Row] {
Expand All @@ -29,6 +30,9 @@ extension BookingDetailsView {
if let address = content.billingAddressText, !address.isEmpty {
result.append(.billingAddress(address))
}
if let note = content.noteText, !note.isEmpty {
result.append(.note(note))
}
return result
}

Expand Down Expand Up @@ -56,6 +60,8 @@ extension BookingDetailsView {
phoneView(with: phoneText)
case .billingAddress(let billingAddressText):
billingAddressView(with: billingAddressText)
case .note(let noteText):
noteView(with: noteText)
}
}

Expand Down Expand Up @@ -136,6 +142,21 @@ extension BookingDetailsView {
}
.padding(.vertical, Layout.rowTextVerticalPadding)
}

private func noteView(with noteText: String) -> some View {
HStack {
VStack(alignment: .leading) {
Text(Localization.noteRowTitle)
.rowTextStyle()
Text(noteText)
.font(TextFont.bodyMedium)
.foregroundStyle(.secondary)
.multilineTextAlignment(.leading)
}
Spacer()
}
.padding(.vertical, Layout.rowTextVerticalPadding)
}
}
}

Expand Down Expand Up @@ -177,5 +198,11 @@ private extension BookingDetailsView.CustomerDetailsView {
value: "Billing address",
comment: "Billing address row title in customer section in booking details view."
)

static let noteRowTitle = NSLocalizedString(
"BookingDetailsView.customer.note.title",
value: "Note",
comment: "Customer note row title in customer section in booking details view."
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,47 @@ final class BookingDetailsViewModelTests: XCTestCase {
XCTAssertEqual(customerContent.billingAddressText, expectedAddress)
}

func test_customer_content_includes_customer_note() {
// Given
let billingAddress = Address.fake().copy(
firstName: "Alice",
lastName: "Johnson",
address1: "456 Main St",
city: "Springfield",
state: "IL",
postcode: "62701",
country: "US"
)
let note = "Please ring the bell twice"
let customerInfo = BookingCustomerInfo(billingAddress: billingAddress, note: note)
let orderInfo = BookingOrderInfo(
statusKey: "confirmed",
paymentInfo: nil,
customerInfo: customerInfo,
productInfo: nil
)
let booking = Booking.fake().copy(orderInfo: orderInfo)

// When
let viewModel = BookingDetailsViewModel(booking: booking, stores: storesManager)

// Then
let customerSection = viewModel.sections.first { section in
if case .customer = section.content {
return true
}
return false
}

guard let customerSection = customerSection,
case let .customer(customerContent) = customerSection.content else {
XCTFail("Customer section not found")
return
}

XCTAssertEqual(customerContent.noteText, note)
}

func test_navigation_title_includes_booking_id() {
// Given
let booking = Booking.fake().copy(bookingID: 12345)
Expand Down
Loading