Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Pricing Plans to Organization Page #6602

Merged
merged 110 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from 94 commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
f4c2bac
first draft for new organization page
hotzenklotz Nov 2, 2022
4b7d509
enable access to orga page in navbar avatar menu
hotzenklotz Nov 2, 2022
ecf7944
Add pricing plan schema
frcroth Nov 2, 2022
2369483
fix navbar links to orga page
hotzenklotz Nov 2, 2022
f8c34b2
more pricing stuff
hotzenklotz Nov 2, 2022
061dc69
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Nov 2, 2022
bdf7474
orga page refactoring
hotzenklotz Nov 2, 2022
fd138fc
Use default user/storage values for different plans
frcroth Nov 3, 2022
f2a6b76
Add new model fields
frcroth Nov 3, 2022
ef4a854
Assert user count does not exceed includedUsers when joining org
frcroth Nov 3, 2022
9ddcaf5
Pass incudedStorage in MB
frcroth Nov 3, 2022
2a8fa90
fix deprecation warning
hotzenklotz Nov 3, 2022
3c6f47d
refactor orga view into sub-components
hotzenklotz Nov 3, 2022
eccaef4
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Nov 3, 2022
83fc747
adapt enum in scala
fm3 Nov 3, 2022
ca20599
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
fm3 Nov 3, 2022
27855ff
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Nov 3, 2022
74b5951
many tweaks to orga view
hotzenklotz Nov 3, 2022
aae2bf7
added modals for extending pricing plans
hotzenklotz Nov 4, 2022
1524d31
added actual user count to orga page limits
hotzenklotz Nov 4, 2022
e42416e
Insert new fields into organization table
frcroth Nov 4, 2022
44b5fbb
REVIEW fixed some TS errors: @ts-expect-error ts-migrate(2769) FIXME:…
hotzenklotz Nov 4, 2022
2307b59
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Nov 4, 2022
1e4a5ba
enforce user quota for email invites
hotzenklotz Nov 4, 2022
e3c0f2f
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Nov 4, 2022
998b3a7
map null value to max int
hotzenklotz Nov 4, 2022
31c24e6
handle infinite orga storage
hotzenklotz Nov 7, 2022
88f9647
small re-phrasing
hotzenklotz Nov 7, 2022
1d35048
enforce user limit on email invites
hotzenklotz Nov 7, 2022
be798e7
pretty + lint
hotzenklotz Nov 7, 2022
6ae9beb
Update conf/evolutions/091-pricing-plans.sql
hotzenklotz Nov 8, 2022
40ef82d
added background images for pricing plan modals
hotzenklotz Nov 8, 2022
15e1100
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Nov 8, 2022
b423e9a
added alert when plans is about to exceed
hotzenklotz Nov 8, 2022
922f4cf
show a plan expriation warning diectly on the dashboard
hotzenklotz Nov 8, 2022
7b3deaa
fix content for upgrade plan modal
hotzenklotz Nov 8, 2022
af61892
prettier
hotzenklotz Nov 8, 2022
85da260
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Nov 8, 2022
e76c0be
updated changelog
hotzenklotz Nov 8, 2022
bd73648
adapt test db
fm3 Nov 8, 2022
a533aba
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
fm3 Nov 8, 2022
b56404c
added backend routes for send out pricing plan uprgade emails
hotzenklotz Nov 9, 2022
bf48fcf
backend formatting
hotzenklotz Nov 9, 2022
064264a
connect frontend and backend prciing email routes
hotzenklotz Nov 9, 2022
cdff504
stuff
hotzenklotz Nov 10, 2022
02380ca
Update frontend/javascripts/admin/admin_rest_api.ts
hotzenklotz Nov 11, 2022
3f64e70
Update frontend/javascripts/admin/organization/upgrade_plan_modal.tsx
hotzenklotz Nov 11, 2022
9438786
Update conf/messages
hotzenklotz Nov 11, 2022
27d4d4b
Update app/views/mail/upgradePricingPlanUsers.scala.html
hotzenklotz Nov 11, 2022
2e5ef48
Update app/views/mail/upgradePricingPlanToTeam.scala.html
hotzenklotz Nov 11, 2022
6348cad
Update app/views/mail/upgradePricingPlanToPower.scala.html
hotzenklotz Nov 11, 2022
9eb2d86
Update app/views/mail/upgradePricingPlanStorage.scala.html
hotzenklotz Nov 11, 2022
c657eb0
Update app/views/mail/extendPricingPlan.scala.html
hotzenklotz Nov 11, 2022
cb094d4
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Nov 11, 2022
ad03de9
Merge branches 'pricing' and 'master' of github.com:scalableminds/web…
hotzenklotz Nov 15, 2022
4811f19
applied PR feedback and switch background images to JPEGs
hotzenklotz Nov 15, 2022
2b279e3
applied PR feedback #2
hotzenklotz Nov 15, 2022
7210f8c
PR feedback #3 / fix upgrade modals
hotzenklotz Nov 15, 2022
61bc91d
Update frontend/javascripts/admin/onboarding.tsx
hotzenklotz Nov 15, 2022
342dc8f
Update frontend/javascripts/admin/organization/organization_cards.tsx
hotzenklotz Nov 15, 2022
1663748
Update frontend/javascripts/admin/organization/pricing_plan_utils.ts
hotzenklotz Nov 15, 2022
4cf53e0
Update frontend/javascripts/admin/organization/organization_cards.tsx
hotzenklotz Nov 15, 2022
48e90e7
Update frontend/javascripts/admin/organization/pricing_plan_utils.ts
hotzenklotz Nov 15, 2022
b7d69d5
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Nov 15, 2022
664f47b
Update frontend/javascripts/admin/admin_rest_api.ts
hotzenklotz Nov 15, 2022
db461bb
PR feedback #4
hotzenklotz Nov 15, 2022
9a1ab51
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Nov 15, 2022
0c3ddd2
pretty
hotzenklotz Nov 15, 2022
ccb12d3
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Nov 15, 2022
2a7a123
PR feedback #5
hotzenklotz Nov 15, 2022
7f2c957
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Dec 6, 2022
c9d9cb3
refactored pricing upgrade emails to be a confirmation to the user
hotzenklotz Dec 7, 2022
4805fbe
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Dec 12, 2022
c85d6b7
formatting
hotzenklotz Dec 12, 2022
b5341a6
fixed warning "plan is about to expire" when it already has expired
hotzenklotz Dec 12, 2022
6161db5
fix evolution schema versioning
hotzenklotz Dec 12, 2022
e4f8664
rephrase all reference to "Free" plan to "Basic"
hotzenklotz Dec 13, 2022
0ecfcdf
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Dec 13, 2022
d5a1441
merge master
fm3 Dec 13, 2022
0b55538
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Dec 13, 2022
cdce091
Add route /pricing/status
fm3 Dec 13, 2022
7e38843
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Dec 13, 2022
c55fd45
redesigned upgrade modal to show both team power plans
hotzenklotz Dec 14, 2022
c70130e
integrated pricing plan status API
hotzenklotz Dec 14, 2022
7dd8af4
fix evolutions
hotzenklotz Dec 14, 2022
2d56f88
also respect paidUntil in exceeded checks
fm3 Dec 14, 2022
8043c68
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
fm3 Dec 14, 2022
34ff183
disable pricing plan warnings on dashboard for now
hotzenklotz Dec 14, 2022
411959d
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Dec 15, 2022
7e843b0
make linter happy
fm3 Dec 15, 2022
ef16591
linting & formatting
hotzenklotz Dec 15, 2022
92d428b
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Dec 15, 2022
52b9221
fix default orga DB
hotzenklotz Dec 15, 2022
92b0378
fixed typescript typing errors
hotzenklotz Dec 15, 2022
6c4dc97
Update frontend/javascripts/admin/organization/upgrade_plan_modal.tsx
hotzenklotz Dec 15, 2022
96998a8
applied PR feedback
hotzenklotz Dec 15, 2022
22954d3
Merge branch 'pricing' of github.com:scalableminds/webknossos into pr…
hotzenklotz Dec 15, 2022
067cf5a
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Dec 15, 2022
b3f37d2
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Dec 20, 2022
fb1ff2a
update schema version to 94
hotzenklotz Dec 20, 2022
8ec9f3b
fix CI?
hotzenklotz Dec 20, 2022
bade5ea
prevent prcing plan alarms from showing actions for unauthorized people
hotzenklotz Dec 20, 2022
b0196c9
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Dec 20, 2022
0b50790
added owner check for warnings
hotzenklotz Dec 20, 2022
f1e34ac
Merge branch 'master' of github.com:scalableminds/webknossos into pri…
hotzenklotz Jan 5, 2023
bb1f52f
added confirmation toasts on sucessful upgrade requests
hotzenklotz Jan 5, 2023
416a061
fix toast messages
hotzenklotz Jan 5, 2023
e753818
Merge branch 'master' into pricing
normanrz Jan 5, 2023
ffa7430
migration guide
fm3 Jan 5, 2023
d2a72aa
Merge branch 'master' into pricing
fm3 Jan 5, 2023
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
8 changes: 8 additions & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,16 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- The new datasets tab in the dashboard allows multi-selection of datasets so that multiple datasets can be moved to a folder at once. As in typical file explorers, CTRL + left click adds individual datasets to the current selection. Shift + left click selects a range of datasets. [#6683](https://github.com/scalableminds/webknossos/pull/6683)

### Changed
- The log viewer in the Voxelytics workflow reporting now uses a virtualized list. [#6579](https://github.com/scalableminds/webknossos/pull/6579)
- Node positions are always handled as integers. They have always been persisted as integers by the server, anyway, but the session in which a node was created handled the position as floating point in earlier versions. [#6589](https://github.com/scalableminds/webknossos/pull/6589)
- Jobs can no longer be started on datastores without workers. [#6595](https://github.com/scalableminds/webknossos/pull/6595)
- When downloading volume annotations with volume data skipped, the nml volume tag is now included anyway (but has no location attribute in this case). [#6566](https://github.com/scalableminds/webknossos/pull/6566)
- Re-phrased some backend (error) messages to improve clarity and provide helping hints. [#6616](https://github.com/scalableminds/webknossos/pull/6616)
- The layer visibility is now encoded in the sharing link. The user opening the link will see the same layers that were visible when copying the link. [#6634](https://github.com/scalableminds/webknossos/pull/6634)
- Voxelytics workflows can now be viewed by anyone with the link who is in the right organization. [#6622](https://github.com/scalableminds/webknossos/pull/6622)
- webKnossos is now able to recover from a lost webGL context. [#6663](https://github.com/scalableminds/webknossos/pull/6663)
- Bulk task creation now needs the taskTypeId, the task type summary will no longer be accepted. [#6640](https://github.com/scalableminds/webknossos/pull/6640)
- Redesigned organization page to include more infos on organization users, storage, webKnossos plan and provided opportunities to upgrade. [#6602](https://github.com/scalableminds/webknossos/pull/6602)

### Fixed
- Fixed import of N5 datasets. [#6668](https://github.com/scalableminds/webknossos/pull/6668)
Expand Down
6 changes: 6 additions & 0 deletions app/controllers/AuthenticationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class AuthenticationController @Inject()(
organization <- organizationService.findOneByInviteByNameOrDefault(
inviteBox.toOption,
organizationName)(GlobalAccessContext) ?~> Messages("organization.notFound", signUpData.organization)
_ <- organizationService
.assertUsersCanBeAdded(organization)(GlobalAccessContext, ec) ?~> "organization.users.userLimitReached"
autoActivate = inviteBox.toOption.map(_.autoActivate).getOrElse(organization.enableAutoVerify)
_ <- createUser(organization,
email,
Expand Down Expand Up @@ -331,6 +333,10 @@ class AuthenticationController @Inject()(
invite <- inviteDAO.findOneByTokenValue(inviteToken) ?~> "invite.invalidToken"
organization <- organizationDAO.findOne(invite._organization)(GlobalAccessContext) ?~> "invite.invalidToken"
_ <- userService.assertNotInOrgaYet(request.identity._multiUser, organization._id)
requestingMultiUser <- multiUserDAO.findOne(request.identity._multiUser)
_ <- Fox.runIf(!requestingMultiUser.isSuperUser)(
organizationService
.assertUsersCanBeAdded(organization)(GlobalAccessContext, ec)) ?~> "organization.users.userLimitReached"
_ <- userService.joinOrganization(request.identity, organization._id, autoActivate = invite.autoActivate)
_ = analyticsService.track(JoinOrganizationEvent(request.identity, organization))
userEmail <- userService.emailFor(request.identity)
Expand Down
19 changes: 12 additions & 7 deletions app/controllers/InitialDataController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,18 @@ Samplecountry
"""
private val organizationTeamId = ObjectId.generate
private val defaultOrganization =
Organization(ObjectId.generate,
"sample_organization",
additionalInformation,
"/assets/images/oxalis.svg",
"Sample Organization",
PricingPlan.Custom,
ObjectId.generate)
Organization(
ObjectId.generate,
"sample_organization",
additionalInformation,
"/assets/images/oxalis.svg",
"Sample Organization",
PricingPlan.Custom,
None,
None,
None,
ObjectId.generate
)
private val organizationTeam =
Team(organizationTeamId, defaultOrganization._id, "Default", isOrganizationTeam = true)
private val userId = ObjectId.generate
Expand Down
99 changes: 97 additions & 2 deletions app/controllers/OrganizationController.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package controllers

import akka.actor.ActorSystem
import com.mohiva.play.silhouette.api.Silhouette
import com.scalableminds.util.accesscontext.{DBAccessContext, GlobalAccessContext}
import com.scalableminds.util.tools.{Fox, FoxImplicits}
import javax.inject.Inject
import models.organization.{OrganizationDAO, OrganizationService}
import models.user.{InviteDAO, MultiUserDAO, UserDAO}
import models.user.{InviteDAO, MultiUserDAO, UserDAO, UserService}
import models.team.{PricingPlan}
import oxalis.security.{WkEnv, WkSilhouetteEnvironment}
import play.api.i18n.Messages
import play.api.libs.functional.syntax._
import play.api.libs.json.{JsNull, JsValue, Json, __}
import play.api.mvc.{Action, AnyContent}
import utils.WkConf
import scala.concurrent.duration._
import oxalis.mail.{DefaultMails, Send}

import scala.concurrent.ExecutionContext

Expand All @@ -22,11 +26,15 @@ class OrganizationController @Inject()(organizationDAO: OrganizationDAO,
userDAO: UserDAO,
multiUserDAO: MultiUserDAO,
wkSilhouetteEnvironment: WkSilhouetteEnvironment,
userService: UserService,
defaultMails: DefaultMails,
actorSystem: ActorSystem,
sil: Silhouette[WkEnv])(implicit ec: ExecutionContext)
extends Controller
with FoxImplicits {

private val combinedAuthenticatorService = wkSilhouetteEnvironment.combinedAuthenticatorService
private lazy val Mailer = actorSystem.actorSelection("/user/mailActor")

def organizationsIsEmpty: Action[AnyContent] = Action.async { implicit request =>
for {
Expand Down Expand Up @@ -99,7 +107,7 @@ class OrganizationController @Inject()(organizationDAO: OrganizationDAO,
organization <- organizationDAO.findOneByName(organizationName) ?~> Messages("organization.notFound",
organizationName) ~> NOT_FOUND
_ <- bool2Fox(request.identity.isAdminOf(organization._id)) ?~> "notAllowed" ~> FORBIDDEN
_ = logger.info(s"Deleting organizaion ${organization._id}")
_ = logger.info(s"Deleting organization ${organization._id}")
_ <- organizationDAO.deleteOne(organization._id)
_ <- userDAO.deleteAllWithOrganization(organization._id)
_ <- multiUserDAO.removeLastLoggedInIdentitiesWithOrga(organization._id)
Expand All @@ -111,4 +119,91 @@ class OrganizationController @Inject()(organizationDAO: OrganizationDAO,
((__ \ 'displayName).read[String] and
(__ \ 'newUserMailingList).read[String]).tupled

def sendExtendPricingPlanEmail(): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
_ <- bool2Fox(request.identity.isAdmin) ?~> Messages("organization.pricingUpgrades.notAuthorized")
organization <- organizationDAO
.findOne(request.identity._organization) ?~> Messages("organization.notFound") ~> NOT_FOUND
userEmail <- userService.emailFor(request.identity)
_ = Mailer ! Send(defaultMails.extendPricingPlanMail(request.identity, userEmail))
_ = Mailer ! Send(
defaultMails.upgradePricingPlanRequestMail(request.identity,
userEmail,
organization.displayName,
"Extend webKnossos plan by a year"))
} yield Ok
}

def sendUpgradePricingPlanEmail(requestedPlan: String): Action[AnyContent] = sil.SecuredAction.async {
implicit request =>
for {
_ <- bool2Fox(request.identity.isAdmin) ?~> Messages("organization.pricingUpgrades.notAuthorized")
organization <- organizationDAO
.findOne(request.identity._organization) ?~> Messages("organization.notFound") ~> NOT_FOUND
userEmail <- userService.emailFor(request.identity)
requestedPlan <- PricingPlan.fromString(requestedPlan)
mail = if (requestedPlan == PricingPlan.Team) {
defaultMails.upgradePricingPlanToTeamMail _
} else {
defaultMails.upgradePricingPlanToTeamMail _
}
_ = Mailer ! Send(mail(request.identity, userEmail))
_ = Mailer ! Send(
defaultMails.upgradePricingPlanRequestMail(request.identity,
userEmail,
organization.displayName,
s"Upgrade webKnossos Plan to $requestedPlan"))
} yield Ok
}

def sendUpgradePricingPlanUsersEmail(requestedUsers: Int): Action[AnyContent] =
sil.SecuredAction.async { implicit request =>
for {
_ <- bool2Fox(request.identity.isAdmin) ?~> Messages("organization.pricingUpgrades.notAuthorized")
organization <- organizationDAO.findOne(request.identity._organization) ?~> Messages("organization.notFound") ~> NOT_FOUND
userEmail <- userService.emailFor(request.identity)
_ = Mailer ! Send(defaultMails.upgradePricingPlanUsersMail(request.identity, userEmail, requestedUsers))
_ = Mailer ! Send(
defaultMails.upgradePricingPlanRequestMail(request.identity,
userEmail,
organization.displayName,
s"Purchase $requestedUsers additional users"))
} yield Ok
}

def sendUpgradePricingPlanStorageEmail(requestedStorage: Int): Action[AnyContent] =
sil.SecuredAction.async { implicit request =>
for {
_ <- bool2Fox(request.identity.isAdmin) ?~> Messages("organization.pricingUpgrades.notAuthorized")
organization <- organizationDAO.findOne(request.identity._organization) ?~> Messages("organization.notFound") ~> NOT_FOUND
userEmail <- userService.emailFor(request.identity)
_ = Mailer ! Send(defaultMails.upgradePricingPlanStorageMail(request.identity, userEmail, requestedStorage))
_ = Mailer ! Send(
defaultMails.upgradePricingPlanRequestMail(request.identity,
userEmail,
organization.displayName,
s"Purchase $requestedStorage TB additional storage"))
} yield Ok
}

def pricingStatus: Action[AnyContent] =
sil.SecuredAction.async { implicit request =>
for {
organization <- organizationDAO.findOne(request.identity._organization)
activeUserCount <- userDAO.countAllForOrganization(request.identity._organization)
// Note that this does not yet account for storage
isExceeded = organization.includedUsers.exists(userLimit => activeUserCount > userLimit) || organization.paidUntil
.exists(_.isPast)
isAlmostExceeded = (activeUserCount > 1 && organization.includedUsers.exists(userLimit =>
activeUserCount > userLimit - 2)) || organization.paidUntil.exists(paidUntil =>
(paidUntil - (6 * 7 days)).isPast)
} yield
Ok(
Json.obj(
"pricingPlan" -> organization.pricingPlan,
"isExceeded" -> isExceeded,
"isAlmostExceeded" -> isAlmostExceeded
))
}

}
18 changes: 14 additions & 4 deletions app/models/organization/Organization.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ case class Organization(
logoUrl: String,
displayName: String,
pricingPlan: PricingPlan,
paidUntil: Option[Instant],
includedUsers: Option[Int], // None means unlimited
includedStorage: Option[Long], // None means unlimited
_rootFolder: ObjectId,
newUserMailingList: String = "",
overTimeMailingList: String = "",
Expand Down Expand Up @@ -48,6 +51,9 @@ class OrganizationDAO @Inject()(sqlClient: SQLClient)(implicit ec: ExecutionCont
r.logourl,
r.displayname,
pricingPlan,
r.paiduntil.map(Instant.fromSql),
r.includedusers,
r.includedstorage,
ObjectId(r._Rootfolder),
r.newusermailinglist,
r.overtimemailinglist,
Expand Down Expand Up @@ -83,10 +89,14 @@ class OrganizationDAO @Inject()(sqlClient: SQLClient)(implicit ec: ExecutionCont

def insertOne(o: Organization): Fox[Unit] =
for {
_ <- run(
sqlu"""insert into webknossos.organizations(_id, name, additionalInformation, logoUrl, displayName, _rootFolder, newUserMailingList, overTimeMailingList, enableAutoVerify, created, isDeleted)
values(${o._id.id}, ${o.name}, ${o.additionalInformation}, ${o.logoUrl}, ${o.displayName}, ${o._rootFolder},
${o.newUserMailingList}, ${o.overTimeMailingList}, ${o.enableAutoVerify}, ${o.created}, ${o.isDeleted})
_ <- run(sqlu"""INSERT INTO webknossos.organizations
(_id, name, additionalInformation, logoUrl, displayName, _rootFolder,
newUserMailingList, overTimeMailingList, enableAutoVerify,
pricingplan, paidUntil, includedusers, includedstorage, created, isDeleted)
VALUES
(${o._id.id}, ${o.name}, ${o.additionalInformation}, ${o.logoUrl}, ${o.displayName}, ${o._rootFolder},
${o.newUserMailingList}, ${o.overTimeMailingList}, ${o.enableAutoVerify},
'#${o.pricingPlan}', ${o.paidUntil}, ${o.includedUsers}, ${o.includedStorage}, ${o.created}, ${o.isDeleted})
""")
} yield ()

Expand Down
40 changes: 30 additions & 10 deletions app/models/organization/OrganizationService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import javax.inject.Inject
import models.binary.{DataStore, DataStoreDAO}
import models.folder.{Folder, FolderDAO, FolderService}
import models.team.{PricingPlan, Team, TeamDAO}
import models.user.{Invite, MultiUserDAO, User}
import models.user.{Invite, MultiUserDAO, User, UserDAO}
import play.api.libs.json.{JsObject, Json}
import utils.{ObjectId, WkConf}

import scala.concurrent.{ExecutionContext, Future}

class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
multiUserDAO: MultiUserDAO,
userDAO: UserDAO,
teamDAO: TeamDAO,
dataStoreDAO: DataStoreDAO,
folderDAO: FolderDAO,
Expand All @@ -31,7 +32,10 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
val adminOnlyInfo = if (requestingUser.exists(_.isAdminOf(organization._id))) {
Json.obj(
"newUserMailingList" -> organization.newUserMailingList,
"pricingPlan" -> organization.pricingPlan
"pricingPlan" -> organization.pricingPlan,
"paidUntil" -> organization.paidUntil,
"includedUsers" -> organization.includedUsers,
"includedStorage" -> organization.includedStorage.map(bytes => bytes / 1000000)
)
} else Json.obj()
Fox.successful(
Expand Down Expand Up @@ -80,15 +84,22 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
.replaceAll(" ", "_")
existingOrganization <- organizationDAO.findOneByName(organizationName)(GlobalAccessContext).futureBox
_ <- bool2Fox(existingOrganization.isEmpty) ?~> "organization.name.alreadyInUse"
initialPricingPlan = if (conf.Features.isDemoInstance) PricingPlan.Basic else PricingPlan.Custom
initialPricingParameters = if (conf.Features.isDemoInstance) (PricingPlan.Basic, Some(3), Some(50000000000L))
else (PricingPlan.Custom, None, None)
organizationRootFolder = Folder(ObjectId.generate, folderService.defaultRootName)
organization = Organization(ObjectId.generate,
organizationName,
"",
"",
organizationDisplayName,
initialPricingPlan,
organizationRootFolder._id)

organization = Organization(
ObjectId.generate,
organizationName,
"",
"",
organizationDisplayName,
initialPricingParameters._1,
None,
initialPricingParameters._2,
initialPricingParameters._3,
organizationRootFolder._id
)
organizationTeam = Team(ObjectId.generate, organization._id, "Default", isOrganizationTeam = true)
_ <- folderDAO.insertAsRoot(organizationRootFolder)
_ <- organizationDAO.insertOne(organization)
Expand All @@ -109,4 +120,13 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
} yield ()
}

def assertUsersCanBeAdded(organization: Organization, usersToAddCount: Int = 1)(implicit ctx: DBAccessContext,
ec: ExecutionContext): Fox[Unit] =
for {
_ <- organizationDAO.findOne(organization._id)
userCount <- userDAO.countAllForOrganization(organization._id)
_ <- Fox.runOptional(organization.includedUsers)(includedUsers =>
bool2Fox(userCount + usersToAddCount <= includedUsers))
} yield ()

}
2 changes: 1 addition & 1 deletion app/models/team/PricingPlan.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import com.scalableminds.util.enumeration.ExtendedEnumeration

object PricingPlan extends ExtendedEnumeration {
type PricingPlan = Value
val Basic, Premium, Pilot, Custom = Value
val Basic, Team, Power, Team_Trial, Power_Trial, Custom = Value
}
2 changes: 1 addition & 1 deletion app/models/user/User.scala
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class UserDAO @Inject()(sqlClient: SQLClient)(implicit ec: ExecutionContext)
def countAllForOrganization(organizationId: ObjectId): Fox[Int] =
for {
resultList <- run(
sql"select count(_id) from #$existingCollectionName where _organization = $organizationId and not isUnlisted"
sql"select count(_id) from #$existingCollectionName where _organization = $organizationId and not isDeactivated and not isUnlisted"
.as[Int])
result <- resultList.headOption
} yield result
Expand Down
Loading