88 lines
2.3 KiB
Swift
88 lines
2.3 KiB
Swift
import Foundation
|
|
|
|
/// A `Sendable` snapshot of a buyer's offer on a listing.
|
|
public struct Offer: Sendable, Equatable {
|
|
|
|
// MARK: Constants
|
|
|
|
/// The share of the asking price under which an offer is declined outright.
|
|
public static let floorShare = 0.8
|
|
|
|
/// How long an offer binds its buyer: 48 hours.
|
|
public static let lifetime: TimeInterval = 172_800
|
|
|
|
// MARK: Properties
|
|
|
|
/// The offer's identifier.
|
|
public let id: UUID
|
|
/// The identifier of the conversation the offer was made in.
|
|
public let conversationID: UUID
|
|
/// The listing offered on.
|
|
public let listing: Listing
|
|
/// The account offering.
|
|
public let buyer: User
|
|
/// The amount offered, in euro cents.
|
|
public let amountCents: Int
|
|
/// Where the offer stands; an open offer past its expiry reads as expired.
|
|
public let status: Status
|
|
/// When the offer was made.
|
|
public let createdAt: Date
|
|
/// When the offer stops binding its buyer.
|
|
public let expiresAt: Date
|
|
|
|
// MARK: Initializers
|
|
|
|
public init(
|
|
id: UUID,
|
|
conversationID: UUID,
|
|
listing: Listing,
|
|
buyer: User,
|
|
amountCents: Int,
|
|
status: Status,
|
|
createdAt: Date,
|
|
expiresAt: Date
|
|
) {
|
|
self.id = id
|
|
self.conversationID = conversationID
|
|
self.listing = listing
|
|
self.buyer = buyer
|
|
self.amountCents = amountCents
|
|
self.status = status
|
|
self.createdAt = createdAt
|
|
self.expiresAt = expiresAt
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Enumerations
|
|
|
|
public extension Offer {
|
|
/// Where an offer stands.
|
|
enum Status: String, Sendable {
|
|
/// Waiting for the seller's answer.
|
|
case open
|
|
/// Accepted: a sale record was opened.
|
|
case accepted
|
|
/// Declined by the seller.
|
|
case declined
|
|
/// Left unanswered past its expiry, or overtaken by a sale of the listing.
|
|
case expired
|
|
}
|
|
}
|
|
|
|
// MARK: - Extensions
|
|
|
|
public extension Offer {
|
|
|
|
// MARK: Methods
|
|
|
|
/// The lowest offer a listing's seller hears, in euro cents: ``floorShare`` of the asking price, rounded up to a whole euro.
|
|
/// - Parameter askingCents: the listing's asking price, in euro cents.
|
|
static func floor(
|
|
forAsking askingCents: Int
|
|
) -> Int {
|
|
Int((Double(askingCents) * floorShare / 100).rounded(.up)) * 100
|
|
}
|
|
|
|
}
|