diff --git a/Modules/Sources/Networking/Model/Bookings/Booking.swift b/Modules/Sources/Networking/Model/Bookings/Booking.swift index c53bc714f8c..f537e9f749d 100644 --- a/Modules/Sources/Networking/Model/Bookings/Booking.swift +++ b/Modules/Sources/Networking/Model/Bookings/Booking.swift @@ -233,3 +233,12 @@ public enum BookingAttendanceStatus: String, CaseIterable, Codable { case noShow = "no-show" case unknown } + +/// Represents the payment status for a booking. +/// To be decoded from booking response once available. +public enum BookingPaymentStatus: String, CaseIterable, Codable { + case paid + case unpaid + case refunded + case unknown +} diff --git a/Modules/Sources/Networking/Remote/BookingsRemote.swift b/Modules/Sources/Networking/Remote/BookingsRemote.swift index f72f842dec7..d09167f68a7 100644 --- a/Modules/Sources/Networking/Remote/BookingsRemote.swift +++ b/Modules/Sources/Networking/Remote/BookingsRemote.swift @@ -38,8 +38,8 @@ public struct BookingFilters { public let resourceIDs: [Int64] public let startDateBefore: String? public let startDateAfter: String? - public let bookingStatuses: [String] public let attendanceStatuses: [String] + public let paymentStatuses: [String] public init( productIDs: [Int64] = [], @@ -47,16 +47,16 @@ public struct BookingFilters { resourceIDs: [Int64] = [], startDateBefore: String? = nil, startDateAfter: String? = nil, - bookingStatuses: [String] = [], - attendanceStatuses: [String] = [] + attendanceStatuses: [String] = [], + paymentStatuses: [String] = [] ) { self.productIDs = productIDs self.customerIDs = customerIDs self.resourceIDs = resourceIDs self.startDateBefore = startDateBefore self.startDateAfter = startDateAfter - self.bookingStatuses = bookingStatuses self.attendanceStatuses = attendanceStatuses + self.paymentStatuses = paymentStatuses } } @@ -110,13 +110,13 @@ public final class BookingsRemote: Remote, BookingsRemoteProtocol { parameters[ParameterKey.startDateAfter] = startDateAfter } - if filters.bookingStatuses.isNotEmpty { - parameters[ParameterKey.bookingStatus] = filters.bookingStatuses - } - if filters.attendanceStatuses.isNotEmpty { parameters[ParameterKey.attendanceStatus] = filters.attendanceStatuses } + + if filters.paymentStatuses.isNotEmpty { + parameters[ParameterKey.paymentStatus] = filters.paymentStatuses + } } if let searchQuery = searchQuery, !searchQuery.isEmpty { @@ -256,8 +256,8 @@ public extension BookingsRemote { static let product: String = "product" static let customer: String = "customer" static let resource: String = "resource" - static let bookingStatus: String = "booking_status" static let attendanceStatus = "attendance_status" + static let paymentStatus = "booking_status" // to be updated later when payment filtering is supported static let status: String = "status" } } diff --git a/Modules/Sources/Yosemite/Model/Bookings/StoredBookingFilters.swift b/Modules/Sources/Yosemite/Model/Bookings/StoredBookingFilters.swift index 19bc0b2da0b..dd8f178e7bf 100644 --- a/Modules/Sources/Yosemite/Model/Bookings/StoredBookingFilters.swift +++ b/Modules/Sources/Yosemite/Model/Bookings/StoredBookingFilters.swift @@ -17,7 +17,7 @@ public struct StoredBookingFilters: Codable, Equatable, GeneratedFakeable { public let teamMembers: [BookingTeamMemberFilter] public let products: [BookingProductFilter] public let attendanceStatuses: [BookingAttendanceStatus] - public let paymentStatuses: [BookingStatus] + public let paymentStatuses: [BookingPaymentStatus] public let customers: [BookingCustomerFilter] public let dateRange: BookingDateRangeFilter? public let numberOfActiveFilters: Int @@ -25,7 +25,7 @@ public struct StoredBookingFilters: Codable, Equatable, GeneratedFakeable { public init(teamMembers: [BookingTeamMemberFilter], products: [BookingProductFilter], attendanceStatuses: [BookingAttendanceStatus], - paymentStatuses: [BookingStatus], + paymentStatuses: [BookingPaymentStatus], customers: [BookingCustomerFilter], dateRange: BookingDateRangeFilter?) { self.teamMembers = teamMembers diff --git a/Modules/Sources/Yosemite/Model/Model.swift b/Modules/Sources/Yosemite/Model/Model.swift index a877cdcaa3b..e1a756e59f4 100644 --- a/Modules/Sources/Yosemite/Model/Model.swift +++ b/Modules/Sources/Yosemite/Model/Model.swift @@ -34,6 +34,7 @@ public typealias BookingPaymentInfo = Networking.BookingPaymentInfo public typealias BookingProductInfo = Networking.BookingProductInfo public typealias BookingResource = Networking.BookingResource public typealias BookingAttendanceStatus = Networking.BookingAttendanceStatus +public typealias BookingPaymentStatus = Networking.BookingPaymentStatus public typealias CreateBlazeCampaign = Networking.CreateBlazeCampaign public typealias FallibleCancelable = Hardware.FallibleCancelable public typealias CommentStatus = Networking.CommentStatus diff --git a/Modules/Sources/Yosemite/Tools/Bookings/ResultsController+FilterBookings.swift b/Modules/Sources/Yosemite/Tools/Bookings/ResultsController+FilterBookings.swift index 5c7f546c068..35b6d30e9f8 100644 --- a/Modules/Sources/Yosemite/Tools/Bookings/ResultsController+FilterBookings.swift +++ b/Modules/Sources/Yosemite/Tools/Bookings/ResultsController+FilterBookings.swift @@ -20,7 +20,8 @@ extension NSPredicate { return NSPredicate(format: "startDate > %@", date as NSDate) } - let bookingStatusesPredicate = filters.bookingStatuses.isNotEmpty ? NSPredicate(format: "statusKey IN %@", filters.bookingStatuses) : nil + // TODO: update `statusKey` to paymentStatusKey once available + let paymentStatusesPredicate = filters.paymentStatuses.isNotEmpty ? NSPredicate(format: "statusKey IN %@", filters.paymentStatuses) : nil let attendanceStatusesPredicate = filters.attendanceStatuses.isNotEmpty ? NSPredicate(format: "attendanceStatusKey IN %@", filters.attendanceStatuses) : nil @@ -32,7 +33,7 @@ extension NSPredicate { resourceIDsPredicate, startDateBeforePredicate, startDateAfterPredicate, - bookingStatusesPredicate, + paymentStatusesPredicate, attendanceStatusesPredicate ].compactMap({ $0 }) diff --git a/Modules/Tests/YosemiteTests/Stores/AppSettingsStoreTests+BookingFilters.swift b/Modules/Tests/YosemiteTests/Stores/AppSettingsStoreTests+BookingFilters.swift index 054749a4253..61deeaa5aa5 100644 --- a/Modules/Tests/YosemiteTests/Stores/AppSettingsStoreTests+BookingFilters.swift +++ b/Modules/Tests/YosemiteTests/Stores/AppSettingsStoreTests+BookingFilters.swift @@ -56,7 +56,7 @@ struct AppSettingsStoreTests_BookingFilters { teamMembers: [BookingTeamMemberFilter(resourceID: 100, name: "Team Member 1")], products: [BookingProductFilter(productID: 1, name: "Product 1")], attendanceStatuses: [.booked, .noShow], - paymentStatuses: [.confirmed, .paid], + paymentStatuses: [.unpaid, .paid], customers: [BookingCustomerFilter(customerID: 10, name: "Customer 1")], dateRange: nil ) @@ -97,7 +97,7 @@ struct AppSettingsStoreTests_BookingFilters { teamMembers: [BookingTeamMemberFilter(resourceID: 100, name: "Team Member 1")], products: [BookingProductFilter(productID: 1, name: "Product 1")], attendanceStatuses: [.booked], - paymentStatuses: [.confirmed], + paymentStatuses: [.unpaid], customers: [BookingCustomerFilter(customerID: 10, name: "Customer 1")], dateRange: nil ) @@ -106,7 +106,7 @@ struct AppSettingsStoreTests_BookingFilters { teamMembers: [BookingTeamMemberFilter(resourceID: 200, name: "Team Member 2")], products: [BookingProductFilter(productID: 2, name: "Product 2")], attendanceStatuses: [.noShow], - paymentStatuses: [.paid, .cancelled], + paymentStatuses: [.paid, .refunded], customers: [], dateRange: nil ) @@ -162,7 +162,7 @@ struct AppSettingsStoreTests_BookingFilters { teamMembers: [BookingTeamMemberFilter(resourceID: 100, name: "Team Member 1")], products: [BookingProductFilter(productID: 1, name: "Product 1")], attendanceStatuses: [], - paymentStatuses: [.confirmed], + paymentStatuses: [.unpaid], customers: [BookingCustomerFilter(customerID: 10, name: "Customer 1")], dateRange: nil ) @@ -221,7 +221,7 @@ struct AppSettingsStoreTests_BookingFilters { teamMembers: [BookingTeamMemberFilter(resourceID: 100, name: "Team Member 1")], products: [], attendanceStatuses: [.booked], - paymentStatuses: [.confirmed], + paymentStatuses: [.refunded], customers: [], dateRange: nil ) diff --git a/WooCommerce/Classes/Bookings/BookingFilters/BookingFiltersViewModel.swift b/WooCommerce/Classes/Bookings/BookingFilters/BookingFiltersViewModel.swift index 67a1021026a..ef21f0e7bc4 100644 --- a/WooCommerce/Classes/Bookings/BookingFilters/BookingFiltersViewModel.swift +++ b/WooCommerce/Classes/Bookings/BookingFilters/BookingFiltersViewModel.swift @@ -39,7 +39,7 @@ final class BookingFiltersViewModel: FilterListViewModel { let products = (productFilterViewModel.selectedValue as? MultipleFilterSelection)?.items as? [BookingProductFilter] ?? [] let customers = (customerFilterViewModel.selectedValue as? MultipleFilterSelection)?.items as? [BookingCustomerFilter] ?? [] let attendanceStatuses = (attendanceStatusFilterViewModel.selectedValue as? MultipleFilterSelection)?.items as? [BookingAttendanceStatus] ?? [] - let paymentStatuses = (paymentStatusFilterViewModel.selectedValue as? MultipleFilterSelection)?.items as? [BookingStatus] ?? [] + let paymentStatuses = (paymentStatusFilterViewModel.selectedValue as? MultipleFilterSelection)?.items as? [BookingPaymentStatus] ?? [] let dateRange = dateTimeFilterViewModel.selectedValue as? BookingDateRangeFilter let numberOfActiveFilters = filterTypeViewModels.numberOfActiveFilters @@ -78,7 +78,7 @@ final class BookingFiltersViewModel: FilterListViewModel { productFilterViewModel.selectedValue = BookingProductFilter?.none customerFilterViewModel.selectedValue = CustomerFilter?.none attendanceStatusFilterViewModel.selectedValue = BookingAttendanceStatus?.none - paymentStatusFilterViewModel.selectedValue = BookingStatus?.none + paymentStatusFilterViewModel.selectedValue = BookingPaymentStatus?.none dateTimeFilterViewModel.selectedValue = BookingDateRangeFilter?.none } @@ -89,7 +89,7 @@ final class BookingFiltersViewModel: FilterListViewModel { let teamMembers: [BookingTeamMemberFilter] let products: [BookingProductFilter] let attendanceStatuses: [BookingAttendanceStatus] - let paymentStatuses: [BookingStatus] + let paymentStatuses: [BookingPaymentStatus] let customers: [BookingCustomerFilter] let dateRange: BookingDateRangeFilter? @@ -108,7 +108,7 @@ final class BookingFiltersViewModel: FilterListViewModel { init(teamMembers: [BookingTeamMemberFilter], products: [BookingProductFilter], attendanceStatuses: [BookingAttendanceStatus], - paymentStatuses: [BookingStatus], + paymentStatuses: [BookingPaymentStatus], customers: [BookingCustomerFilter], dateRange: BookingDateRangeFilter?, numberOfActiveFilters: Int) { @@ -143,8 +143,8 @@ final class BookingFiltersViewModel: FilterListViewModel { resourceIDs: teamMembers.map { $0.resourceID }, startDateBefore: dateRange?.endDate?.ISO8601Format(), startDateAfter: dateRange?.startDate?.ISO8601Format(), - bookingStatuses: paymentStatuses.map { $0.rawValue }, - attendanceStatuses: attendanceStatuses.map { $0.rawValue } + attendanceStatuses: attendanceStatuses.map { $0.rawValue }, + paymentStatuses: paymentStatuses.map { $0.rawValue } ) } } @@ -198,12 +198,12 @@ extension BookingFiltersViewModel.BookingListFilter { listSelectorConfig: .bookingCustomers(siteID: siteID), selectedValue: MultipleFilterSelection(items: filters.customers)) case .attendanceStatus: - let options: [BookingAttendanceStatus?] = [.booked, .checkedIn, .cancelled, .noShow] + let options: [BookingAttendanceStatus?] = [.booked, .checkedIn, .noShow, .cancelled] return FilterTypeViewModel(title: title, listSelectorConfig: .multiSelectStaticOptions(options: options), selectedValue: MultipleFilterSelection(items: filters.attendanceStatuses)) case .paymentStatus: - let options: [BookingStatus?] = [.complete, .paid, .unpaid, .cancelled, .pendingConfirmation, .confirmed] + let options: [BookingPaymentStatus?] = [.paid, .unpaid, .refunded] return FilterTypeViewModel(title: title, listSelectorConfig: .multiSelectStaticOptions(options: options), selectedValue: MultipleFilterSelection(items: filters.paymentStatuses)) @@ -234,12 +234,12 @@ extension BookingAttendanceStatus: FilterType { } } -extension BookingStatus: FilterType { +extension BookingPaymentStatus: FilterType { var description: String { localizedTitle } var isActive: Bool { switch self { - case .complete, .paid, .unpaid, .cancelled, .pendingConfirmation, .confirmed: + case .paid, .unpaid, .refunded: return true case .unknown: return false diff --git a/WooCommerce/Classes/Bookings/BookingList/BookingPaymentStatus+Helpers.swift b/WooCommerce/Classes/Bookings/BookingList/BookingPaymentStatus+Helpers.swift new file mode 100644 index 00000000000..6f11b154132 --- /dev/null +++ b/WooCommerce/Classes/Bookings/BookingList/BookingPaymentStatus+Helpers.swift @@ -0,0 +1,33 @@ +import Foundation +import enum Yosemite.BookingPaymentStatus + +extension BookingPaymentStatus { + var localizedTitle: String { + switch self { + case .paid: + NSLocalizedString( + "bookingPaymentStatus.title.paid", + value: "Paid", + comment: "Status of a paid booking" + ) + case .unpaid: + NSLocalizedString( + "bookingPaymentStatus.title.unpaid", + value: "Unpaid", + comment: "Status of an unpaid booking" + ) + case .refunded: + NSLocalizedString( + "bookingPaymentStatus.title.refunded", + value: "Refunded", + comment: "Status of a refunded booking" + ) + case .unknown: + NSLocalizedString( + "bookingPaymentStatus.title.unknown", + value: "Unknown", + comment: "Status of a booking with unexpected payment status" + ) + } + } +} diff --git a/WooCommerce/Classes/ViewModels/Booking Details/BookingAttendanceStatus+Localization.swift b/WooCommerce/Classes/ViewModels/Booking Details/BookingAttendanceStatus+Localization.swift index 1ed6abad359..4264daba314 100644 --- a/WooCommerce/Classes/ViewModels/Booking Details/BookingAttendanceStatus+Localization.swift +++ b/WooCommerce/Classes/ViewModels/Booking Details/BookingAttendanceStatus+Localization.swift @@ -18,8 +18,8 @@ extension BookingAttendanceStatus { ) case .cancelled: return NSLocalizedString( - "BookingAttendanceStatus.cancelled", - value: "Cancelled", + "BookingAttendanceStatus.canceled", + value: "Canceled", comment: "Title for 'Cancelled' booking attendance status." ) case .noShow: