diff --git a/Kickstarter.xcodeproj/project.pbxproj b/Kickstarter.xcodeproj/project.pbxproj index c129723235..59aa8c89b5 100644 --- a/Kickstarter.xcodeproj/project.pbxproj +++ b/Kickstarter.xcodeproj/project.pbxproj @@ -494,6 +494,9 @@ 60368E662AC334B9005EE9A5 /* ReportProjectFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60368E652AC334B9005EE9A5 /* ReportProjectFormView.swift */; }; 60368E6A2AC35BD9005EE9A5 /* ReportProjectFormViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60368E692AC35BD9005EE9A5 /* ReportProjectFormViewModel.swift */; }; 60368E6C2AC35BE3005EE9A5 /* ReportProjectFormViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60368E6B2AC35BE3005EE9A5 /* ReportProjectFormViewModelTests.swift */; }; + 604334D52D08CD1B00819385 /* BuildPaymentPlanEnvelope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604334D42D08CD1B00819385 /* BuildPaymentPlanEnvelope.swift */; }; + 604334DE2D08CF5E00819385 /* BuildPaymentPlanQuery.graphql in Resources */ = {isa = PBXBuildFile; fileRef = 604334DD2D08CF5E00819385 /* BuildPaymentPlanQuery.graphql */; }; + 604334DF2D08D8FD00819385 /* BuildPaymentPlanEnvelopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604334D62D08CD4D00819385 /* BuildPaymentPlanEnvelopeTests.swift */; }; 604453242BA08EEA00B8F485 /* PostCampaignPledgeRewardsSummaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604453232BA08EEA00B8F485 /* PostCampaignPledgeRewardsSummaryViewController.swift */; }; 604453262BA08F0500B8F485 /* PostCampaignPledgeRewardsSummaryDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604453252BA08F0500B8F485 /* PostCampaignPledgeRewardsSummaryDataSource.swift */; }; 604453282BA08F7000B8F485 /* PostCampaignPledgeRewardsSummaryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604453272BA08F7000B8F485 /* PostCampaignPledgeRewardsSummaryCell.swift */; }; @@ -2161,6 +2164,9 @@ 60368E652AC334B9005EE9A5 /* ReportProjectFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportProjectFormView.swift; sourceTree = ""; }; 60368E692AC35BD9005EE9A5 /* ReportProjectFormViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportProjectFormViewModel.swift; sourceTree = ""; }; 60368E6B2AC35BE3005EE9A5 /* ReportProjectFormViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportProjectFormViewModelTests.swift; sourceTree = ""; }; + 604334D42D08CD1B00819385 /* BuildPaymentPlanEnvelope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildPaymentPlanEnvelope.swift; sourceTree = ""; }; + 604334D62D08CD4D00819385 /* BuildPaymentPlanEnvelopeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildPaymentPlanEnvelopeTests.swift; sourceTree = ""; }; + 604334DD2D08CF5E00819385 /* BuildPaymentPlanQuery.graphql */ = {isa = PBXFileReference; lastKnownFileType = text; path = BuildPaymentPlanQuery.graphql; sourceTree = ""; }; 604453232BA08EEA00B8F485 /* PostCampaignPledgeRewardsSummaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCampaignPledgeRewardsSummaryViewController.swift; sourceTree = ""; }; 604453252BA08F0500B8F485 /* PostCampaignPledgeRewardsSummaryDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCampaignPledgeRewardsSummaryDataSource.swift; sourceTree = ""; }; 604453272BA08F7000B8F485 /* PostCampaignPledgeRewardsSummaryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCampaignPledgeRewardsSummaryCell.swift; sourceTree = ""; }; @@ -5891,6 +5897,7 @@ 7798B5292174F3BC008BC50D /* queries */ = { isa = PBXGroup; children = ( + 604334DD2D08CF5E00819385 /* BuildPaymentPlanQuery.graphql */, 8AC3E0882698CC1900168BF8 /* FetchAddOnsQuery.graphql */, 06566372270BA8E000CE7EDF /* FetchProjectRewardsByIdQuery.graphql */, 8A1556FE26939F4B00017845 /* FetchBackingQuery.graphql */, @@ -6925,6 +6932,8 @@ 77272D362370AB5C003B7A9C /* BackingPaymentSourceTests.swift */, 8A4E954824525EC100A578CF /* BackingState.swift */, D015877D1EEB2ED6006E7684 /* BackingTests.swift */, + 604334D42D08CD1B00819385 /* BuildPaymentPlanEnvelope.swift */, + 604334D62D08CD4D00819385 /* BuildPaymentPlanEnvelopeTests.swift */, 8A67DDB724DDB21100B4AB10 /* ErroredBackingsEnvelope.swift */, 474D731F26B46412000F63DC /* ErroredBackingsEnvelopeTests.swift */, E1A149212ACE013100F49709 /* FetchProjectsEnvelope.swift */, @@ -7714,6 +7723,7 @@ 0655C7282732FDA30087281F /* FetchRootCategories.graphql in Resources */, 19047FBB2889A16300BDD1A8 /* CreateSetupIntent.graphql in Resources */, AA7BB4632CF67E7900E2B2D4 /* UserFeaturesFragment.graphql in Resources */, + 604334DE2D08CF5E00819385 /* BuildPaymentPlanQuery.graphql in Resources */, 062868E626B999EF00EC5052 /* DeletePaymentSource.graphql in Resources */, 06C38CB926A9D13400591CED /* UpdateUserProfile.graphql in Resources */, 393E8DA52C5BE08B00AEA968 /* PPOProjectFragment.graphql in Resources */, @@ -9025,6 +9035,7 @@ D015883D1EEB2ED7006E7684 /* Route.swift in Sources */, D0158A2B1EEB30A2006E7684 /* UpdateDraftTemplates.swift in Sources */, D08510122195015F00BC418B /* PaymentSourceDeleteInput.swift in Sources */, + 604334D52D08CD1B00819385 /* BuildPaymentPlanEnvelope.swift in Sources */, D0158A531EEB3734006E7684 /* Secrets.swift in Sources */, D01588A71EEB2ED7006E7684 /* Project.MemberDataLenses.swift in Sources */, D01588C71EEB2ED7006E7684 /* RewardItemLenses.swift in Sources */, @@ -9239,6 +9250,7 @@ 19047FC12889B11F00BDD1A8 /* CreateSetupIntentMutationTests.swift in Sources */, 06232D472795EC4F00A81755 /* HTMLParserTemplates.swift in Sources */, D0D10C1E1EEB4550005EBAD0 /* UpdateTests.swift in Sources */, + 604334DF2D08D8FD00819385 /* BuildPaymentPlanEnvelopeTests.swift in Sources */, 0622C9242734778D0039AAC9 /* Category+CategoryFragmentTests.swift in Sources */, 066C0AEB26CC22180048F4D8 /* UpdateBackingMutationTemplate.swift in Sources */, D0D10C211EEB4550005EBAD0 /* User.NotificationsTests.swift in Sources */, diff --git a/KsApi/GraphAPI.swift b/KsApi/GraphAPI.swift index 27f939ddc8..e175c0a3e5 100644 --- a/KsApi/GraphAPI.swift +++ b/KsApi/GraphAPI.swift @@ -4281,7 +4281,6 @@ public enum GraphAPI { case partialRefunds_2024 case notificationBannerUpdate_2024 case multipleShipfromLocations_2024 - case separatePaymentSection case reactBackedProjects /// Auto generated constant for unknown enum values case __unknown(RawValue) @@ -4397,7 +4396,6 @@ public enum GraphAPI { case "partial_refunds_2024": self = .partialRefunds_2024 case "notification_banner_update_2024": self = .notificationBannerUpdate_2024 case "multiple_shipfrom_locations_2024": self = .multipleShipfromLocations_2024 - case "separate_payment_section": self = .separatePaymentSection case "react_backed_projects": self = .reactBackedProjects default: self = .__unknown(rawValue) } @@ -4514,7 +4512,6 @@ public enum GraphAPI { case .partialRefunds_2024: return "partial_refunds_2024" case .notificationBannerUpdate_2024: return "notification_banner_update_2024" case .multipleShipfromLocations_2024: return "multiple_shipfrom_locations_2024" - case .separatePaymentSection: return "separate_payment_section" case .reactBackedProjects: return "react_backed_projects" case .__unknown(let value): return value } @@ -4631,7 +4628,6 @@ public enum GraphAPI { case (.partialRefunds_2024, .partialRefunds_2024): return true case (.notificationBannerUpdate_2024, .notificationBannerUpdate_2024): return true case (.multipleShipfromLocations_2024, .multipleShipfromLocations_2024): return true - case (.separatePaymentSection, .separatePaymentSection): return true case (.reactBackedProjects, .reactBackedProjects): return true case (.__unknown(let lhsValue), .__unknown(let rhsValue)): return lhsValue == rhsValue default: return false @@ -4749,7 +4745,6 @@ public enum GraphAPI { .partialRefunds_2024, .notificationBannerUpdate_2024, .multipleShipfromLocations_2024, - .separatePaymentSection, .reactBackedProjects, ] } @@ -7902,6 +7897,277 @@ public enum GraphAPI { } } + public final class BuildPaymentPlanQuery: GraphQLQuery { + /// The raw GraphQL definition of this operation. + public let operationDefinition: String = + """ + query BuildPaymentPlan($slug: String!, $amount: String!) { + project(slug: $slug) { + __typename + paymentPlan(amount: $amount) { + __typename + projectIsPledgeOverTimeAllowed + amountIsPledgeOverTimeEligible + paymentIncrements { + __typename + amount { + __typename + amount + currency + } + scheduledCollection + } + } + } + } + """ + + public let operationName: String = "BuildPaymentPlan" + + public var slug: String + public var amount: String + + public init(slug: String, amount: String) { + self.slug = slug + self.amount = amount + } + + public var variables: GraphQLMap? { + return ["slug": slug, "amount": amount] + } + + public struct Data: GraphQLSelectionSet { + public static let possibleTypes: [String] = ["Query"] + + public static var selections: [GraphQLSelection] { + return [ + GraphQLField("project", arguments: ["slug": GraphQLVariable("slug")], type: .object(Project.selections)), + ] + } + + public private(set) var resultMap: ResultMap + + public init(unsafeResultMap: ResultMap) { + self.resultMap = unsafeResultMap + } + + public init(project: Project? = nil) { + self.init(unsafeResultMap: ["__typename": "Query", "project": project.flatMap { (value: Project) -> ResultMap in value.resultMap }]) + } + + /// Fetches a project given its slug or pid. + public var project: Project? { + get { + return (resultMap["project"] as? ResultMap).flatMap { Project(unsafeResultMap: $0) } + } + set { + resultMap.updateValue(newValue?.resultMap, forKey: "project") + } + } + + public struct Project: GraphQLSelectionSet { + public static let possibleTypes: [String] = ["Project"] + + public static var selections: [GraphQLSelection] { + return [ + GraphQLField("__typename", type: .nonNull(.scalar(String.self))), + GraphQLField("paymentPlan", arguments: ["amount": GraphQLVariable("amount")], type: .object(PaymentPlan.selections)), + ] + } + + public private(set) var resultMap: ResultMap + + public init(unsafeResultMap: ResultMap) { + self.resultMap = unsafeResultMap + } + + public init(paymentPlan: PaymentPlan? = nil) { + self.init(unsafeResultMap: ["__typename": "Project", "paymentPlan": paymentPlan.flatMap { (value: PaymentPlan) -> ResultMap in value.resultMap }]) + } + + public var __typename: String { + get { + return resultMap["__typename"]! as! String + } + set { + resultMap.updateValue(newValue, forKey: "__typename") + } + } + + /// Build a payment plan given a project id and amount + public var paymentPlan: PaymentPlan? { + get { + return (resultMap["paymentPlan"] as? ResultMap).flatMap { PaymentPlan(unsafeResultMap: $0) } + } + set { + resultMap.updateValue(newValue?.resultMap, forKey: "paymentPlan") + } + } + + public struct PaymentPlan: GraphQLSelectionSet { + public static let possibleTypes: [String] = ["PaymentPlan"] + + public static var selections: [GraphQLSelection] { + return [ + GraphQLField("__typename", type: .nonNull(.scalar(String.self))), + GraphQLField("projectIsPledgeOverTimeAllowed", type: .nonNull(.scalar(Bool.self))), + GraphQLField("amountIsPledgeOverTimeEligible", type: .nonNull(.scalar(Bool.self))), + GraphQLField("paymentIncrements", type: .list(.nonNull(.object(PaymentIncrement.selections)))), + ] + } + + public private(set) var resultMap: ResultMap + + public init(unsafeResultMap: ResultMap) { + self.resultMap = unsafeResultMap + } + + public init(projectIsPledgeOverTimeAllowed: Bool, amountIsPledgeOverTimeEligible: Bool, paymentIncrements: [PaymentIncrement]? = nil) { + self.init(unsafeResultMap: ["__typename": "PaymentPlan", "projectIsPledgeOverTimeAllowed": projectIsPledgeOverTimeAllowed, "amountIsPledgeOverTimeEligible": amountIsPledgeOverTimeEligible, "paymentIncrements": paymentIncrements.flatMap { (value: [PaymentIncrement]) -> [ResultMap] in value.map { (value: PaymentIncrement) -> ResultMap in value.resultMap } }]) + } + + public var __typename: String { + get { + return resultMap["__typename"]! as! String + } + set { + resultMap.updateValue(newValue, forKey: "__typename") + } + } + + /// Whether the project permits pledge over time pledges + public var projectIsPledgeOverTimeAllowed: Bool { + get { + return resultMap["projectIsPledgeOverTimeAllowed"]! as! Bool + } + set { + resultMap.updateValue(newValue, forKey: "projectIsPledgeOverTimeAllowed") + } + } + + /// Amount is enough to qualify for pledge over time, if project allows + public var amountIsPledgeOverTimeEligible: Bool { + get { + return resultMap["amountIsPledgeOverTimeEligible"]! as! Bool + } + set { + resultMap.updateValue(newValue, forKey: "amountIsPledgeOverTimeEligible") + } + } + + public var paymentIncrements: [PaymentIncrement]? { + get { + return (resultMap["paymentIncrements"] as? [ResultMap]).flatMap { (value: [ResultMap]) -> [PaymentIncrement] in value.map { (value: ResultMap) -> PaymentIncrement in PaymentIncrement(unsafeResultMap: value) } } + } + set { + resultMap.updateValue(newValue.flatMap { (value: [PaymentIncrement]) -> [ResultMap] in value.map { (value: PaymentIncrement) -> ResultMap in value.resultMap } }, forKey: "paymentIncrements") + } + } + + public struct PaymentIncrement: GraphQLSelectionSet { + public static let possibleTypes: [String] = ["PaymentIncrement"] + + public static var selections: [GraphQLSelection] { + return [ + GraphQLField("__typename", type: .nonNull(.scalar(String.self))), + GraphQLField("amount", type: .nonNull(.object(Amount.selections))), + GraphQLField("scheduledCollection", type: .nonNull(.scalar(String.self))), + ] + } + + public private(set) var resultMap: ResultMap + + public init(unsafeResultMap: ResultMap) { + self.resultMap = unsafeResultMap + } + + public init(amount: Amount, scheduledCollection: String) { + self.init(unsafeResultMap: ["__typename": "PaymentIncrement", "amount": amount.resultMap, "scheduledCollection": scheduledCollection]) + } + + public var __typename: String { + get { + return resultMap["__typename"]! as! String + } + set { + resultMap.updateValue(newValue, forKey: "__typename") + } + } + + public var amount: Amount { + get { + return Amount(unsafeResultMap: resultMap["amount"]! as! ResultMap) + } + set { + resultMap.updateValue(newValue.resultMap, forKey: "amount") + } + } + + public var scheduledCollection: String { + get { + return resultMap["scheduledCollection"]! as! String + } + set { + resultMap.updateValue(newValue, forKey: "scheduledCollection") + } + } + + public struct Amount: GraphQLSelectionSet { + public static let possibleTypes: [String] = ["Money"] + + public static var selections: [GraphQLSelection] { + return [ + GraphQLField("__typename", type: .nonNull(.scalar(String.self))), + GraphQLField("amount", type: .scalar(String.self)), + GraphQLField("currency", type: .scalar(CurrencyCode.self)), + ] + } + + public private(set) var resultMap: ResultMap + + public init(unsafeResultMap: ResultMap) { + self.resultMap = unsafeResultMap + } + + public init(amount: String? = nil, currency: CurrencyCode? = nil) { + self.init(unsafeResultMap: ["__typename": "Money", "amount": amount, "currency": currency]) + } + + public var __typename: String { + get { + return resultMap["__typename"]! as! String + } + set { + resultMap.updateValue(newValue, forKey: "__typename") + } + } + + /// Floating-point numeric value of monetary amount represented as a string + public var amount: String? { + get { + return resultMap["amount"] as? String + } + set { + resultMap.updateValue(newValue, forKey: "amount") + } + } + + /// Currency of the monetary amount + public var currency: CurrencyCode? { + get { + return resultMap["currency"] as? CurrencyCode + } + set { + resultMap.updateValue(newValue, forKey: "currency") + } + } + } + } + } + } + } + } + public final class FetchAddOnsQuery: GraphQLQuery { /// The raw GraphQL definition of this operation. public let operationDefinition: String = diff --git a/KsApi/GraphQLFiles.xcfilelist b/KsApi/GraphQLFiles.xcfilelist index c9842b0a63..7950c60822 100644 --- a/KsApi/GraphQLFiles.xcfilelist +++ b/KsApi/GraphQLFiles.xcfilelist @@ -47,6 +47,7 @@ $(PROJECT_DIR)/KsApi/queries/FetchUser.graphql $(PROJECT_DIR)/KsApi/queries/FetchProjectByIdQuery.graphql $(PROJECT_DIR)/KsApi/queries/FetchBackingQuery.graphql $(PROJECT_DIR)/KsApi/queries/FetchRootCategories.graphql +$(PROJECT_DIR)/KsApi/queries/BuildPaymentPlanQuery.graphql $(PROJECT_DIR)/KsApi/queries/ValidateCheckout.graphql $(PROJECT_DIR)/KsApi/queries/FetchCommentReplies.graphql $(PROJECT_DIR)/KsApi/queries/FetchUpdateComments.graphql diff --git a/KsApi/MockService.swift b/KsApi/MockService.swift index 8f9e0bc9f2..8931df2d10 100644 --- a/KsApi/MockService.swift +++ b/KsApi/MockService.swift @@ -20,6 +20,8 @@ fileprivate let blockUserResult: Result? + fileprivate let buildPaymentPlanResult: Result? + fileprivate let cancelBackingResult: Result? fileprivate let changeCurrencyResult: Result? @@ -234,6 +236,7 @@ addPaymentSheetPaymentSourceResult: Result? = nil, apolloClient: ApolloClientType? = nil, blockUserResult: Result? = nil, + buildPaymentPlanResult: Result? = nil, cancelBackingResult: Result? = nil, changeEmailResult: Result? = nil, changePasswordResult: Result? = nil, @@ -357,6 +360,8 @@ self.blockUserResult = blockUserResult + self.buildPaymentPlanResult = buildPaymentPlanResult + self.cancelBackingResult = cancelBackingResult self.changeEmailResult = changeEmailResult @@ -591,6 +596,19 @@ return client.performWithResult(mutation: mutation, result: self.blockUserResult) } + func buildPaymentPlan( + projectSlug: String, + pledgeAmount: String + ) -> SignalProducer { + guard let client = self.apolloClient else { + return .empty + } + + let query = GraphAPI.BuildPaymentPlanQuery(slug: projectSlug, amount: pledgeAmount) + + return client.fetchWithResult(query: query, result: self.buildPaymentPlanResult) + } + public func cancelBacking(input: CancelBackingInput) -> SignalProducer { guard let client = self.apolloClient else { diff --git a/KsApi/Service.swift b/KsApi/Service.swift index 4daa335011..159c6f3a6e 100644 --- a/KsApi/Service.swift +++ b/KsApi/Service.swift @@ -117,6 +117,18 @@ public struct Service: ServiceType { } } + public func buildPaymentPlan( + projectSlug: String, + pledgeAmount: String + ) -> SignalProducer { + return GraphQL.shared.client + .fetch(query: GraphAPI.BuildPaymentPlanQuery( + slug: projectSlug, + amount: pledgeAmount + )) + .flatMap(BuildPaymentPlanEnvelope.envelopeProducer(from:)) + } + public func cancelBacking(input: CancelBackingInput) -> SignalProducer { return GraphQL.shared.client diff --git a/KsApi/ServiceType.swift b/KsApi/ServiceType.swift index 6de008ca50..3bfb4c11d3 100644 --- a/KsApi/ServiceType.swift +++ b/KsApi/ServiceType.swift @@ -50,6 +50,10 @@ public protocol ServiceType { func triggerThirdPartyEventInput(input: TriggerThirdPartyEventInput) -> SignalProducer + /// Returns Pledge Over Time payment plan data. + func buildPaymentPlan(projectSlug: String, pledgeAmount: String) + -> SignalProducer + /// Cancels a backing func cancelBacking(input: CancelBackingInput) -> SignalProducer diff --git a/KsApi/graphql-schema.json b/KsApi/graphql-schema.json index b31fdc14b6..677aeb03f4 100644 --- a/KsApi/graphql-schema.json +++ b/KsApi/graphql-schema.json @@ -14231,12 +14231,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "separate_payment_section", - "description": null, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "react_backed_projects", "description": null, @@ -29586,22 +29580,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "readyToBeFinalized", - "description": "Whether the cart could be finalized or is missing required info", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "shouldCollectAddress", "description": "Whether or not this cart needs an address to be finalized", @@ -30319,6 +30297,54 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "checkoutRequired", + "description": "Whether there is required action for the backer to take in their pledge manager", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "checkoutState", + "description": "The state of checkout (taking into account order and cart status)", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "CheckoutStateEnum", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "checkoutVoucherAutoCompletable", + "description": "Whether or not the order would be auto-exemptable if the order had no cross-sells.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "crossSells", "description": "Add-ons selected for this order during Pledge Redemption", @@ -30375,6 +30401,22 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "finalizedAndNotExempt", + "description": "Whether or not the order was finalized but non-exempt.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "id", "description": null, @@ -30486,22 +30528,6 @@ }, "isDeprecated": false, "deprecationReason": null - }, - { - "name": "voucherAutoExemptable", - "description": "Whether or not the order would be auto-exemptable if the order had no cross-sells.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null } ], "inputFields": null, @@ -30515,6 +30541,35 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "ENUM", + "name": "CheckoutStateEnum", + "description": "The state of checkout, e.g. complete, in progress, incomplete.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "complete", + "description": "complete", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "in_progress", + "description": "in_progress", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "not_started", + "description": "not_started", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "OBJECT", "name": "OrderCrossSell", @@ -30580,6 +30635,22 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "orderable", + "description": "The orderable of the cross sell", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "UNION", + "name": "Orderable", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "orderableConfig", "description": "Configuration for the add-on", @@ -30667,6 +30738,22 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "unitPrice", + "description": "The price of one unit of the cross-sell", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, @@ -30674,6 +30761,22 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "UNION", + "name": "Orderable", + "description": "Types that can be added to an Order", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "Reward", + "ofType": null + } + ] + }, { "kind": "UNION", "name": "PaymentSource", @@ -36489,6 +36592,33 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "bulkEditStockLocationMappings", + "description": "Takes a stock location and bulk edits the location mapping", + "args": [ + { + "name": "input", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "BulkEditStockLocationMappingsInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "BulkEditStockLocationMappingsPayload", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "cancelBacking", "description": "Cancel a pledged backing", @@ -41256,6 +41386,102 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "BulkEditStockLocationMappingsInput", + "description": "Autogenerated input type of BulkEditStockLocationMappings", + "fields": null, + "inputFields": [ + { + "name": "stockLocationId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "locationIds", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + } + }, + "defaultValue": null + }, + { + "name": "clientMutationId", + "description": "A unique identifier for the client performing the mutation.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "BulkEditStockLocationMappingsPayload", + "description": "Autogenerated return type of BulkEditStockLocationMappings", + "fields": [ + { + "name": "clientMutationId", + "description": "A unique identifier for the client performing the mutation.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "stockLocation", + "description": "The updated stock location", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "StockLocation", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "CancelBackingInput", diff --git a/KsApi/models/BuildPaymentPlanEnvelope.swift b/KsApi/models/BuildPaymentPlanEnvelope.swift new file mode 100644 index 0000000000..7d93706cbd --- /dev/null +++ b/KsApi/models/BuildPaymentPlanEnvelope.swift @@ -0,0 +1,57 @@ +import Foundation +import ReactiveSwift + +public struct BuildPaymentPlanEnvelope: Decodable { + public var projectIsPledgeOverTimeAllowed: Bool + public var amountIsPledgeOverTimeEligible: Bool + public var paymentIncrements: [PaymentIncrements] +} + +public struct PaymentIncrements: Decodable { + public var amount: Amount + public var scheduledCollection: String + + public struct Amount: Decodable { + public var amount: String? + public var currency: GraphAPI.CurrencyCode.RawValue? + } +} + +extension BuildPaymentPlanEnvelope { + static func envelopeProducer(from data: GraphAPI.BuildPaymentPlanQuery.Data) + -> SignalProducer { + guard let envelope = BuildPaymentPlanEnvelope.buildPaymentPlanEnvelope(from: data) else { + return SignalProducer(error: .couldNotParseJSON) + } + return SignalProducer(value: envelope) + } + + /** + Returns a minimal `BuildPaymentPlanEnvelope` from a `GraphAPI.BuildPaymentPlanQuery.Data` + */ + static func buildPaymentPlanEnvelope( + from data: GraphAPI.BuildPaymentPlanQuery + .Data + ) -> BuildPaymentPlanEnvelope? { + guard let projectIsPledgeOverTimeAllowed = data.project?.paymentPlan?.projectIsPledgeOverTimeAllowed, + let amountIsPledgeOverTimeEligible = data.project?.paymentPlan?.amountIsPledgeOverTimeEligible, + let graphAPIPaymentIncrements = data.project?.paymentPlan?.paymentIncrements + else { + return nil + } + + let paymentIncrements = graphAPIPaymentIncrements.map { + let amount = PaymentIncrements.Amount( + amount: $0.amount.amount, + currency: $0.amount.currency?.rawValue + ) + return PaymentIncrements(amount: amount, scheduledCollection: $0.scheduledCollection) + } + + return BuildPaymentPlanEnvelope( + projectIsPledgeOverTimeAllowed: projectIsPledgeOverTimeAllowed, + amountIsPledgeOverTimeEligible: amountIsPledgeOverTimeEligible, + paymentIncrements: paymentIncrements + ) + } +} diff --git a/KsApi/models/BuildPaymentPlanEnvelopeTests.swift b/KsApi/models/BuildPaymentPlanEnvelopeTests.swift new file mode 100644 index 0000000000..47d594bd4e --- /dev/null +++ b/KsApi/models/BuildPaymentPlanEnvelopeTests.swift @@ -0,0 +1,34 @@ +@testable import KsApi +import XCTest + +final class BuildPaymentPlanEnvelopeTestsTests: XCTestCase { + func testBuildPaymentPlanEnvelopeTestsDecoding() { + let jsonString = """ + { + "projectIsPledgeOverTimeAllowed": true , + "amountIsPledgeOverTimeEligible": true, + "paymentIncrements": [ + "amount" { + "amount": "150.0", + "currency": "USD" + } + "scheduledCollection": "1234567" + ] + } + """ + + let data = Data(jsonString.utf8) + + do { + let envelope = try JSONDecoder().decode(BuildPaymentPlanEnvelope.self, from: data) + XCTAssertEqual(envelope.projectIsPledgeOverTimeAllowed, true) + XCTAssertEqual(envelope.amountIsPledgeOverTimeEligible, true) + XCTAssertEqual(envelope.paymentIncrements.count, 1) + XCTAssertEqual(envelope.paymentIncrements.first?.amount.amount, "150.0") + XCTAssertEqual(envelope.paymentIncrements.first?.amount.currency, "USD") + XCTAssertEqual(envelope.paymentIncrements.first?.scheduledCollection, "1234567") + } catch { + XCTFail("\(error)") + } + } +} diff --git a/KsApi/queries/BuildPaymentPlanQuery.graphql b/KsApi/queries/BuildPaymentPlanQuery.graphql new file mode 100644 index 0000000000..992e602a45 --- /dev/null +++ b/KsApi/queries/BuildPaymentPlanQuery.graphql @@ -0,0 +1,15 @@ +query BuildPaymentPlan($slug: String!, $amount: String!) { + project(slug: $slug) { + paymentPlan(amount: $amount) { + projectIsPledgeOverTimeAllowed + amountIsPledgeOverTimeEligible + paymentIncrements { + amount { + amount + currency + } + scheduledCollection + } + } + } +}