PaymentProviderEntitiesTest.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. // @ts-check
  2. const SandboxedModule = require('sandboxed-module')
  3. const { expect } = require('chai')
  4. const Errors = require('../../../../app/src/Features/Subscription/Errors')
  5. const {
  6. PaymentProviderSubscriptionChangeRequest,
  7. PaymentProviderSubscriptionUpdateRequest,
  8. PaymentProviderSubscriptionChange,
  9. PaymentProviderSubscription,
  10. PaymentProviderSubscriptionAddOnUpdate,
  11. } = require('../../../../app/src/Features/Subscription/PaymentProviderEntities')
  12. const {
  13. AI_ADD_ON_CODE,
  14. } = require('../../../../app/src/Features/Subscription/AiHelper')
  15. const SubscriptionHelper = require('../../../../app/src/Features/Subscription/SubscriptionHelper')
  16. const MODULE_PATH =
  17. '../../../../app/src/Features/Subscription/PaymentProviderEntities'
  18. describe('PaymentProviderEntities', function () {
  19. describe('PaymentProviderSubscription', function () {
  20. beforeEach(function () {
  21. this.Settings = {
  22. plans: [
  23. { planCode: 'assistant-annual', price_in_cents: 5900 },
  24. { planCode: 'cheap-plan', price_in_cents: 500 },
  25. { planCode: 'regular-plan', price_in_cents: 1000 },
  26. { planCode: 'premium-plan', price_in_cents: 2000 },
  27. {
  28. planCode: 'group_collaborator_10_enterprise',
  29. price_in_cents: 10000,
  30. },
  31. ],
  32. features: [],
  33. }
  34. this.PaymentProviderEntities = SandboxedModule.require(MODULE_PATH, {
  35. requires: {
  36. '@overleaf/settings': this.Settings,
  37. './Errors': Errors,
  38. './SubscriptionHelper': SubscriptionHelper,
  39. },
  40. })
  41. })
  42. describe('with add-ons', function () {
  43. beforeEach(function () {
  44. const {
  45. PaymentProviderSubscription,
  46. PaymentProviderSubscriptionAddOn,
  47. } = this.PaymentProviderEntities
  48. this.addOn = new PaymentProviderSubscriptionAddOn({
  49. code: 'add-on-code',
  50. name: 'My Add-On',
  51. quantity: 1,
  52. unitPrice: 2,
  53. })
  54. this.subscription = new PaymentProviderSubscription({
  55. id: 'subscription-id',
  56. userId: 'user-id',
  57. planCode: 'regular-plan',
  58. planName: 'My Plan',
  59. planPrice: 10,
  60. addOns: [this.addOn],
  61. subtotal: 10.99,
  62. taxRate: 0.2,
  63. taxAmount: 2.4,
  64. total: 14.4,
  65. currency: 'USD',
  66. })
  67. })
  68. describe('hasAddOn()', function () {
  69. it('returns true if the subscription has the given add-on', function () {
  70. expect(this.subscription.hasAddOn(this.addOn.code)).to.be.true
  71. })
  72. it("returns false if the subscription doesn't have the given add-on", function () {
  73. expect(this.subscription.hasAddOn('another-add-on')).to.be.false
  74. })
  75. })
  76. describe('getRequestForPlanChange()', function () {
  77. it('returns a change request for upgrades', function () {
  78. const { PaymentProviderSubscriptionChangeRequest } =
  79. this.PaymentProviderEntities
  80. const changeRequest = this.subscription.getRequestForPlanChange(
  81. 'premium-plan',
  82. 1,
  83. this.subscription.shouldPlanChangeAtTermEnd('premium-plan')
  84. )
  85. expect(changeRequest).to.deep.equal(
  86. new PaymentProviderSubscriptionChangeRequest({
  87. subscription: this.subscription,
  88. timeframe: 'now',
  89. planCode: 'premium-plan',
  90. })
  91. )
  92. })
  93. it('returns a change request for downgrades', function () {
  94. const { PaymentProviderSubscriptionChangeRequest } =
  95. this.PaymentProviderEntities
  96. const changeRequest = this.subscription.getRequestForPlanChange(
  97. 'cheap-plan',
  98. 1,
  99. this.subscription.shouldPlanChangeAtTermEnd('cheap-plan')
  100. )
  101. expect(changeRequest).to.deep.equal(
  102. new PaymentProviderSubscriptionChangeRequest({
  103. subscription: this.subscription,
  104. timeframe: 'term_end',
  105. planCode: 'cheap-plan',
  106. })
  107. )
  108. })
  109. it('returns a change request for downgrades while on trial', function () {
  110. const fiveDaysFromNow = new Date()
  111. fiveDaysFromNow.setDate(fiveDaysFromNow.getDate() + 5)
  112. this.subscription.trialPeriodEnd = fiveDaysFromNow
  113. const { PaymentProviderSubscriptionChangeRequest } =
  114. this.PaymentProviderEntities
  115. const changeRequest = this.subscription.getRequestForPlanChange(
  116. 'cheap-plan',
  117. 1,
  118. this.subscription.shouldPlanChangeAtTermEnd('cheap-plan')
  119. )
  120. expect(changeRequest).to.deep.equal(
  121. new PaymentProviderSubscriptionChangeRequest({
  122. subscription: this.subscription,
  123. timeframe: 'now',
  124. planCode: 'cheap-plan',
  125. })
  126. )
  127. })
  128. it('preserves the AI add-on on upgrades', function () {
  129. const { PaymentProviderSubscriptionChangeRequest } =
  130. this.PaymentProviderEntities
  131. this.addOn.code = AI_ADD_ON_CODE
  132. const changeRequest = this.subscription.getRequestForPlanChange(
  133. 'premium-plan',
  134. 1,
  135. this.subscription.shouldPlanChangeAtTermEnd('premium-plan')
  136. )
  137. expect(changeRequest).to.deep.equal(
  138. new PaymentProviderSubscriptionChangeRequest({
  139. subscription: this.subscription,
  140. timeframe: 'now',
  141. planCode: 'premium-plan',
  142. addOnUpdates: [
  143. new PaymentProviderSubscriptionAddOnUpdate({
  144. code: AI_ADD_ON_CODE,
  145. quantity: 1,
  146. }),
  147. ],
  148. })
  149. )
  150. })
  151. it('preserves the AI add-on on downgrades', function () {
  152. const { PaymentProviderSubscriptionChangeRequest } =
  153. this.PaymentProviderEntities
  154. this.addOn.code = AI_ADD_ON_CODE
  155. const changeRequest = this.subscription.getRequestForPlanChange(
  156. 'cheap-plan',
  157. 1,
  158. this.subscription.shouldPlanChangeAtTermEnd('cheap-plan')
  159. )
  160. expect(changeRequest).to.deep.equal(
  161. new PaymentProviderSubscriptionChangeRequest({
  162. subscription: this.subscription,
  163. timeframe: 'term_end',
  164. planCode: 'cheap-plan',
  165. addOnUpdates: [
  166. new PaymentProviderSubscriptionAddOnUpdate({
  167. code: AI_ADD_ON_CODE,
  168. quantity: 1,
  169. }),
  170. ],
  171. })
  172. )
  173. })
  174. it('preserves the AI add-on on upgrades from the standalone AI plan', function () {
  175. const { PaymentProviderSubscriptionChangeRequest } =
  176. this.PaymentProviderEntities
  177. this.subscription.planCode = 'assistant-annual'
  178. this.subscription.addOns = []
  179. const changeRequest = this.subscription.getRequestForPlanChange(
  180. 'cheap-plan',
  181. 1,
  182. this.subscription.shouldPlanChangeAtTermEnd('cheap-plan')
  183. )
  184. expect(changeRequest).to.deep.equal(
  185. new PaymentProviderSubscriptionChangeRequest({
  186. subscription: this.subscription,
  187. timeframe: 'now',
  188. planCode: 'cheap-plan',
  189. addOnUpdates: [
  190. new PaymentProviderSubscriptionAddOnUpdate({
  191. code: AI_ADD_ON_CODE,
  192. quantity: 1,
  193. }),
  194. ],
  195. })
  196. )
  197. })
  198. it('upgrade from individual to group plan for Stripe subscription', function () {
  199. this.subscription.service = 'stripe-uk'
  200. const { PaymentProviderSubscriptionChangeRequest } =
  201. this.PaymentProviderEntities
  202. const changeRequest = this.subscription.getRequestForPlanChange(
  203. 'group_collaborator',
  204. 10,
  205. this.subscription.shouldPlanChangeAtTermEnd(
  206. 'group_collaborator_10_enterprise'
  207. )
  208. )
  209. expect(changeRequest).to.deep.equal(
  210. new PaymentProviderSubscriptionChangeRequest({
  211. subscription: this.subscription,
  212. timeframe: 'now',
  213. planCode: 'group_collaborator',
  214. addOnUpdates: [
  215. new PaymentProviderSubscriptionAddOnUpdate({
  216. code: 'additional-license',
  217. quantity: 10,
  218. }),
  219. ],
  220. })
  221. )
  222. })
  223. it('upgrade from individual to group plan and preserves the AI add-on for Stripe subscription', function () {
  224. this.subscription.service = 'stripe-uk'
  225. const { PaymentProviderSubscriptionChangeRequest } =
  226. this.PaymentProviderEntities
  227. this.addOn.code = AI_ADD_ON_CODE
  228. const changeRequest = this.subscription.getRequestForPlanChange(
  229. 'group_collaborator',
  230. 10,
  231. this.subscription.shouldPlanChangeAtTermEnd(
  232. 'group_collaborator_10_enterprise'
  233. )
  234. )
  235. expect(changeRequest).to.deep.equal(
  236. new PaymentProviderSubscriptionChangeRequest({
  237. subscription: this.subscription,
  238. timeframe: 'now',
  239. planCode: 'group_collaborator',
  240. addOnUpdates: [
  241. new PaymentProviderSubscriptionAddOnUpdate({
  242. code: 'additional-license',
  243. quantity: 10,
  244. }),
  245. new PaymentProviderSubscriptionAddOnUpdate({
  246. code: AI_ADD_ON_CODE,
  247. quantity: 1,
  248. }),
  249. ],
  250. })
  251. )
  252. })
  253. })
  254. describe('getRequestForAddOnPurchase()', function () {
  255. it('returns a change request', function () {
  256. const {
  257. PaymentProviderSubscriptionChangeRequest,
  258. PaymentProviderSubscriptionAddOnUpdate,
  259. } = this.PaymentProviderEntities
  260. const changeRequest =
  261. this.subscription.getRequestForAddOnPurchase('another-add-on')
  262. expect(changeRequest).to.deep.equal(
  263. new PaymentProviderSubscriptionChangeRequest({
  264. subscription: this.subscription,
  265. timeframe: 'now',
  266. addOnUpdates: [
  267. new PaymentProviderSubscriptionAddOnUpdate({
  268. code: this.addOn.code,
  269. quantity: this.addOn.quantity,
  270. unitPrice: this.addOn.unitPrice,
  271. }),
  272. new PaymentProviderSubscriptionAddOnUpdate({
  273. code: 'another-add-on',
  274. quantity: 1,
  275. }),
  276. ],
  277. })
  278. )
  279. })
  280. it('returns a change request with quantity and unit price specified', function () {
  281. const {
  282. PaymentProviderSubscriptionChangeRequest,
  283. PaymentProviderSubscriptionAddOnUpdate,
  284. } = this.PaymentProviderEntities
  285. const quantity = 5
  286. const unitPrice = 10
  287. const changeRequest = this.subscription.getRequestForAddOnPurchase(
  288. 'another-add-on',
  289. quantity,
  290. unitPrice
  291. )
  292. expect(changeRequest).to.deep.equal(
  293. new PaymentProviderSubscriptionChangeRequest({
  294. subscription: this.subscription,
  295. timeframe: 'now',
  296. addOnUpdates: [
  297. new PaymentProviderSubscriptionAddOnUpdate({
  298. code: this.addOn.code,
  299. quantity: this.addOn.quantity,
  300. unitPrice: this.addOn.unitPrice,
  301. }),
  302. new PaymentProviderSubscriptionAddOnUpdate({
  303. code: 'another-add-on',
  304. quantity,
  305. unitPrice,
  306. }),
  307. ],
  308. })
  309. )
  310. })
  311. it('throws a DuplicateAddOnError if the subscription already has the add-on', function () {
  312. expect(() =>
  313. this.subscription.getRequestForAddOnPurchase(this.addOn.code)
  314. ).to.throw(Errors.DuplicateAddOnError)
  315. })
  316. })
  317. describe('getRequestForAddOnUpdate()', function () {
  318. it('returns a change request', function () {
  319. const {
  320. PaymentProviderSubscriptionChangeRequest,
  321. PaymentProviderSubscriptionAddOnUpdate,
  322. } = this.PaymentProviderEntities
  323. const newQuantity = 2
  324. const changeRequest = this.subscription.getRequestForAddOnUpdate(
  325. 'add-on-code',
  326. newQuantity
  327. )
  328. expect(changeRequest).to.deep.equal(
  329. new PaymentProviderSubscriptionChangeRequest({
  330. subscription: this.subscription,
  331. timeframe: 'now',
  332. addOnUpdates: [
  333. new PaymentProviderSubscriptionAddOnUpdate({
  334. code: this.addOn.code,
  335. quantity: newQuantity,
  336. unitPrice: this.addOn.unitPrice,
  337. }),
  338. ],
  339. })
  340. )
  341. })
  342. it("throws a AddOnNotPresentError if the subscription doesn't have the add-on", function () {
  343. expect(() =>
  344. this.subscription.getRequestForAddOnUpdate('another-add-on', 2)
  345. ).to.throw(Errors.AddOnNotPresentError)
  346. })
  347. })
  348. describe('getRequestForAddOnRemoval()', function () {
  349. it('returns a change request', function () {
  350. const changeRequest = this.subscription.getRequestForAddOnRemoval(
  351. this.addOn.code
  352. )
  353. expect(changeRequest).to.deep.equal(
  354. new PaymentProviderSubscriptionChangeRequest({
  355. subscription: this.subscription,
  356. timeframe: 'term_end',
  357. addOnUpdates: [],
  358. })
  359. )
  360. })
  361. it('returns a change request when in trial', function () {
  362. const fiveDaysFromNow = new Date()
  363. fiveDaysFromNow.setDate(fiveDaysFromNow.getDate() + 5)
  364. this.subscription.trialPeriodEnd = fiveDaysFromNow
  365. const changeRequest = this.subscription.getRequestForAddOnRemoval(
  366. this.addOn.code
  367. )
  368. expect(changeRequest).to.deep.equal(
  369. new PaymentProviderSubscriptionChangeRequest({
  370. subscription: this.subscription,
  371. timeframe: 'now',
  372. addOnUpdates: [],
  373. })
  374. )
  375. })
  376. it("throws an AddOnNotPresentError if the subscription doesn't have the add-on", function () {
  377. expect(() =>
  378. this.subscription.getRequestForAddOnRemoval('another-add-on')
  379. ).to.throw(Errors.AddOnNotPresentError)
  380. })
  381. })
  382. describe('getRequestForAddOnReactivation()', function () {
  383. it('throws an AddOnNotPresentError', function () {
  384. expect(() =>
  385. this.subscription.getRequestForAddOnReactivation(this.addOn.code)
  386. ).to.throw(Errors.AddOnNotPresentError)
  387. })
  388. })
  389. describe('getRequestForGroupPlanUpgrade()', function () {
  390. it('returns a correct change request', function () {
  391. const changeRequest =
  392. this.subscription.getRequestForGroupPlanUpgrade('test_plan_code')
  393. const addOns = [
  394. new PaymentProviderSubscriptionAddOnUpdate({
  395. code: 'add-on-code',
  396. quantity: 1,
  397. }),
  398. ]
  399. expect(changeRequest).to.deep.equal(
  400. new PaymentProviderSubscriptionChangeRequest({
  401. subscription: this.subscription,
  402. timeframe: 'now',
  403. addOnUpdates: addOns,
  404. planCode: 'test_plan_code',
  405. })
  406. )
  407. })
  408. })
  409. describe('getRequestForPoNumberAndTermsAndConditionsUpdate()', function () {
  410. it('returns a correct update request', function () {
  411. const updateRequest =
  412. this.subscription.getRequestForPoNumberAndTermsAndConditionsUpdate(
  413. 'O12345',
  414. 'T&C copy'
  415. )
  416. expect(updateRequest).to.deep.equal(
  417. new PaymentProviderSubscriptionUpdateRequest({
  418. subscription: this.subscription,
  419. poNumber: 'O12345',
  420. termsAndConditions: 'T&C copy',
  421. })
  422. )
  423. })
  424. })
  425. describe('getRequestForTermsAndConditionsUpdate()', function () {
  426. it('returns a correct update request', function () {
  427. const updateRequest =
  428. this.subscription.getRequestForTermsAndConditionsUpdate('T&C copy')
  429. expect(updateRequest).to.deep.equal(
  430. new PaymentProviderSubscriptionUpdateRequest({
  431. subscription: this.subscription,
  432. termsAndConditions: 'T&C copy',
  433. })
  434. )
  435. })
  436. })
  437. describe('with an add-on pending cancellation', function () {
  438. beforeEach(function () {
  439. this.subscription.pendingChange =
  440. new PaymentProviderSubscriptionChange({
  441. subscription: this.subscription,
  442. nextPlanCode: this.subscription.planCode,
  443. nextPlanName: this.subscription.planName,
  444. nextPlanPrice: this.subscription.planPrice,
  445. nextAddOns: [],
  446. })
  447. })
  448. describe('getRequestForAddOnReactivation()', function () {
  449. it('returns a change request', function () {
  450. const changeRequest =
  451. this.subscription.getRequestForAddOnReactivation(this.addOn.code)
  452. expect(changeRequest).to.deep.equal(
  453. new PaymentProviderSubscriptionChangeRequest({
  454. subscription: this.subscription,
  455. timeframe: 'term_end',
  456. addOnUpdates: [this.addOn.toAddOnUpdate()],
  457. })
  458. )
  459. })
  460. it('throws an AddOnNotPresentError if given the wrong add-on', function () {
  461. expect(() =>
  462. this.subscription.getRequestForAddOnReactivation('some-add-on')
  463. ).to.throw(Errors.AddOnNotPresentError)
  464. })
  465. })
  466. describe('getRequestForPlanRevert()', function () {
  467. beforeEach(function () {
  468. const { PaymentProviderSubscription } = this.PaymentProviderEntities
  469. this.subscription = new PaymentProviderSubscription({
  470. id: 'subscription-id',
  471. userId: 'user-id',
  472. planCode: 'regular-plan',
  473. planName: 'My Plan',
  474. planPrice: 10,
  475. addOns: [
  476. {
  477. addOnCode: 'addon-1',
  478. quantity: 2,
  479. unitAmountInCents: 500,
  480. },
  481. {
  482. addOnCode: 'addon-2',
  483. quantity: 1,
  484. unitAmountInCents: 600,
  485. },
  486. ],
  487. subtotal: 10.99,
  488. taxRate: 0.2,
  489. taxAmount: 2.4,
  490. total: 14.4,
  491. currency: 'USD',
  492. })
  493. })
  494. it('throws if the plan to revert to doesnt exist', function () {
  495. const invalidPlanCode = 'non-existent-plan'
  496. expect(() =>
  497. this.subscription.getRequestForPlanRevert(invalidPlanCode, null)
  498. ).to.throw('Unable to find plan in settings')
  499. })
  500. it('creates a change request with the restore point', function () {
  501. const previousPlanCode = 'cheap-plan'
  502. const previousAddOns = [
  503. { addOnCode: 'addon-1', quantity: 1, unitAmountInCents: 500 },
  504. ]
  505. const changeRequest = this.subscription.getRequestForPlanRevert(
  506. previousPlanCode,
  507. previousAddOns
  508. )
  509. expect(changeRequest).to.be.an.instanceOf(
  510. this.PaymentProviderEntities
  511. .PaymentProviderSubscriptionChangeRequest
  512. )
  513. expect(changeRequest.planCode).to.equal(previousPlanCode)
  514. expect(changeRequest.addOnUpdates).to.deep.equal([
  515. {
  516. code: 'addon-1',
  517. quantity: 1,
  518. unitPrice: 5,
  519. },
  520. ])
  521. })
  522. it('defaults to addons to an empty array to clear the addon state', function () {
  523. const previousPlanCode = 'cheap-plan'
  524. const changeRequest = this.subscription.getRequestForPlanRevert(
  525. previousPlanCode,
  526. null
  527. )
  528. expect(changeRequest.addOnUpdates).to.deep.equal([])
  529. })
  530. })
  531. })
  532. })
  533. describe('without add-ons', function () {
  534. beforeEach(function () {
  535. const { PaymentProviderSubscription } = this.PaymentProviderEntities
  536. this.subscription = new PaymentProviderSubscription({
  537. id: 'subscription-id',
  538. userId: 'user-id',
  539. planCode: 'regular-plan',
  540. planName: 'My Plan',
  541. planPrice: 10,
  542. subtotal: 10.99,
  543. taxRate: 0.2,
  544. taxAmount: 2.4,
  545. total: 14.4,
  546. currency: 'USD',
  547. })
  548. })
  549. describe('hasAddOn()', function () {
  550. it('returns false for any add-on', function () {
  551. expect(this.subscription.hasAddOn('some-add-on')).to.be.false
  552. })
  553. })
  554. describe('getRequestForAddOnPurchase()', function () {
  555. it('returns a change request', function () {
  556. const {
  557. PaymentProviderSubscriptionChangeRequest,
  558. PaymentProviderSubscriptionAddOnUpdate,
  559. } = this.PaymentProviderEntities
  560. const changeRequest =
  561. this.subscription.getRequestForAddOnPurchase('some-add-on')
  562. expect(changeRequest).to.deep.equal(
  563. new PaymentProviderSubscriptionChangeRequest({
  564. subscription: this.subscription,
  565. timeframe: 'now',
  566. addOnUpdates: [
  567. new PaymentProviderSubscriptionAddOnUpdate({
  568. code: 'some-add-on',
  569. quantity: 1,
  570. }),
  571. ],
  572. })
  573. )
  574. })
  575. })
  576. describe('getRequestForAddOnRemoval()', function () {
  577. it('throws an AddOnNotPresentError', function () {
  578. expect(() =>
  579. this.subscription.getRequestForAddOnRemoval('some-add-on')
  580. ).to.throw(Errors.AddOnNotPresentError)
  581. })
  582. })
  583. describe('getRequestForAddOnReactivation()', function () {
  584. it('throws an AddOnNotPresentError', function () {
  585. expect(() =>
  586. this.subscription.getRequestForAddOnReactivation('some-add-on')
  587. ).to.throw(Errors.AddOnNotPresentError)
  588. })
  589. })
  590. })
  591. })
  592. describe('PaymentProviderSubscriptionChange', function () {
  593. describe('constructor', function () {
  594. it('rounds the amounts when calculating the taxes', function () {
  595. const subscription = new PaymentProviderSubscription({
  596. id: 'subscription-id',
  597. userId: 'user-id',
  598. planCode: 'premium-plan',
  599. planName: 'Premium plan',
  600. planPrice: 10,
  601. subtotal: 10,
  602. taxRate: 0.15,
  603. taxAmount: 1.5,
  604. currency: 'USD',
  605. total: 11.5,
  606. periodStart: new Date(),
  607. periodEnd: new Date(),
  608. collectionMethod: 'automatic',
  609. netTerms: 0,
  610. poNumber: '012345',
  611. termsAndConditions: 'T&C copy',
  612. })
  613. const change = new PaymentProviderSubscriptionChange({
  614. subscription,
  615. nextPlanCode: 'promotional-plan',
  616. nextPlanName: 'Promotial plan',
  617. nextPlanPrice: 8.99,
  618. nextAddOns: [],
  619. })
  620. expect(change.tax).to.equal(1.35)
  621. expect(change.total).to.equal(10.34)
  622. })
  623. })
  624. })
  625. })