diff --git a/controllers/plan.go b/controllers/plan.go index 1b05c3ee..449534bd 100644 --- a/controllers/plan.go +++ b/controllers/plan.go @@ -114,7 +114,7 @@ func (c *ApiController) GetPlan() { // @router /update-plan [post] func (c *ApiController) UpdatePlan() { id := c.Input().Get("id") - + owner := util.GetOwnerFromId(id) var plan object.Plan err := json.Unmarshal(c.Ctx.Input.RequestBody, &plan) if err != nil { @@ -122,17 +122,19 @@ func (c *ApiController) UpdatePlan() { return } if plan.Product != "" { - planId := util.GetId(plan.Owner, plan.Product) - product, err := object.GetProduct(planId) + productId := util.GetId(owner, plan.Product) + product, err := object.GetProduct(productId) if err != nil { c.ResponseError(err.Error()) return } - object.UpdateProductForPlan(&plan, product) - _, err = object.UpdateProduct(planId, product) - if err != nil { - c.ResponseError(err.Error()) - return + if product != nil { + object.UpdateProductForPlan(&plan, product) + _, err = object.UpdateProduct(productId, product) + if err != nil { + c.ResponseError(err.Error()) + return + } } } c.Data["json"] = wrapActionResponse(object.UpdatePlan(id, &plan)) diff --git a/object/plan.go b/object/plan.go index a73c6ad1..1f905068 100644 --- a/object/plan.go +++ b/object/plan.go @@ -16,6 +16,7 @@ package object import ( "fmt" + "time" "github.com/casdoor/casdoor/util" "github.com/xorm-io/core" @@ -28,10 +29,10 @@ type Plan struct { DisplayName string `xorm:"varchar(100)" json:"displayName"` Description string `xorm:"varchar(100)" json:"description"` - PricePerMonth float64 `json:"pricePerMonth"` - PricePerYear float64 `json:"pricePerYear"` + Price float64 `json:"price"` Currency string `xorm:"varchar(100)" json:"currency"` - Product string `json:"product"` // related product id + Period string `xorm:"varchar(100)" json:"period"` + Product string `xorm:"varchar(100)" json:"product"` PaymentProviders []string `xorm:"varchar(100)" json:"paymentProviders"` // payment providers for related product IsEnabled bool `json:"isEnabled"` @@ -39,10 +40,28 @@ type Plan struct { Options []string `xorm:"-" json:"options"` } +const ( + PeriodMonthly = "Monthly" + PeriodYearly = "Yearly" +) + func (plan *Plan) GetId() string { return fmt.Sprintf("%s/%s", plan.Owner, plan.Name) } +func GetDuration(period string) (startTime time.Time, endTime time.Time) { + if period == PeriodYearly { + startTime = time.Now() + endTime = startTime.AddDate(1, 0, 0) + } else if period == PeriodMonthly { + startTime = time.Now() + endTime = startTime.AddDate(0, 1, 0) + } else { + panic(fmt.Sprintf("invalid period: %s", period)) + } + return +} + func GetPlanCount(owner, field, value string) (int64, error) { session := GetSession(owner, -1, -1, field, value, "", "") return session.Count(&Plan{}) diff --git a/object/pricing.go b/object/pricing.go index d628690a..8d79eba1 100644 --- a/object/pricing.go +++ b/object/pricing.go @@ -32,12 +32,6 @@ type Pricing struct { IsEnabled bool `json:"isEnabled"` TrialDuration int `json:"trialDuration"` Application string `xorm:"varchar(100)" json:"application"` - - Submitter string `xorm:"varchar(100)" json:"submitter"` - Approver string `xorm:"varchar(100)" json:"approver"` - ApproveTime string `xorm:"varchar(100)" json:"approveTime"` - - State string `xorm:"varchar(100)" json:"state"` } func (pricing *Pricing) GetId() string { diff --git a/object/product.go b/object/product.go index 1953747a..e38d3e02 100644 --- a/object/product.go +++ b/object/product.go @@ -190,8 +190,15 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host if user.Type == "paid-user" { // Create a subscription for `paid-user` if pricingName != "" && planName != "" { - sub := NewSubscription(owner, user.Name, pricingName, planName, paymentName) - _, err := AddSubscription(sub) + plan, err := GetPlan(util.GetId(owner, planName)) + if err != nil { + return "", "", err + } + if plan == nil { + return "", "", fmt.Errorf("the plan: %s does not exist", planName) + } + sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period) + _, err = AddSubscription(sub) if err != nil { return "", "", err } @@ -267,14 +274,14 @@ func CreateProductForPlan(plan *Plan) *Product { product := &Product{ Owner: plan.Owner, Name: fmt.Sprintf("product_%v", util.GetRandomName()), - DisplayName: fmt.Sprintf("Auto Created Product for Plan %v(%v)", plan.GetId(), plan.DisplayName), + DisplayName: fmt.Sprintf("Product for Plan %v/%v/%v", plan.Name, plan.DisplayName, plan.Period), CreatedTime: plan.CreatedTime, Image: "https://cdn.casbin.org/img/casdoor-logo_1185x256.png", // TODO - Detail: fmt.Sprintf("This Product was auto created for Plan %v(%v)", plan.GetId(), plan.DisplayName), + Detail: fmt.Sprintf("This product was auto created for plan %v(%v), subscription period is %v", plan.Name, plan.DisplayName, plan.Period), Description: plan.Description, Tag: "auto_created_product_for_plan", - Price: plan.PricePerMonth, // TODO + Price: plan.Price, Currency: plan.Currency, Quantity: 999, @@ -290,9 +297,10 @@ func CreateProductForPlan(plan *Plan) *Product { } func UpdateProductForPlan(plan *Plan, product *Product) { - product.DisplayName = fmt.Sprintf("Auto Created Product for Plan %v(%v)", plan.GetId(), plan.DisplayName) - product.Detail = fmt.Sprintf("This Product was auto created for Plan %v(%v)", plan.GetId(), plan.DisplayName) - product.Price = plan.PricePerMonth // TODO - product.Providers = plan.PaymentProviders + product.Owner = plan.Owner + product.DisplayName = fmt.Sprintf("Product for Plan %v/%v/%v", plan.Name, plan.DisplayName, plan.Period) + product.Detail = fmt.Sprintf("This product was auto created for plan %v(%v), subscription period is %v", plan.Name, plan.DisplayName, plan.Period) + product.Price = plan.Price product.Currency = plan.Currency + product.Providers = plan.PaymentProviders } diff --git a/object/subscription.go b/object/subscription.go index 9fcc7315..bc271034 100644 --- a/object/subscription.go +++ b/object/subscription.go @@ -50,7 +50,7 @@ type Subscription struct { StartTime time.Time `json:"startTime"` EndTime time.Time `json:"endTime"` - Duration int `json:"duration"` + Period string `xorm:"varchar(100)" json:"period"` State SubscriptionState `xorm:"varchar(100)" json:"state"` } @@ -103,7 +103,8 @@ func (sub *Subscription) UpdateState() error { return nil } -func NewSubscription(owner, userName, pricingName, planName, paymentName string) *Subscription { +func NewSubscription(owner, userName, planName, paymentName, period string) *Subscription { + startTime, endTime := GetDuration(period) id := util.GenerateId()[:6] return &Subscription{ Owner: owner, @@ -112,13 +113,12 @@ func NewSubscription(owner, userName, pricingName, planName, paymentName string) CreatedTime: util.GetCurrentTime(), User: userName, - Pricing: pricingName, Plan: planName, Payment: paymentName, - StartTime: time.Now(), - EndTime: time.Now().AddDate(0, 0, 30), - Duration: 30, // TODO + StartTime: startTime, + EndTime: endTime, + Period: period, State: SubStatePending, // waiting for payment complete } } diff --git a/web/src/PlanEditPage.js b/web/src/PlanEditPage.js index 4abe42e6..b3ec9dd1 100644 --- a/web/src/PlanEditPage.js +++ b/web/src/PlanEditPage.js @@ -197,22 +197,29 @@ class PlanEditPage extends React.Component { - {Setting.getLabel(i18next.t("plan:Price per month"), i18next.t("plan:Price per month - Tooltip"))} : + {Setting.getLabel(i18next.t("plan:Price"), i18next.t("plan:Price - Tooltip"))} : - { - this.updatePlanField("pricePerMonth", value); + { + this.updatePlanField("price", value); }} /> - {Setting.getLabel(i18next.t("plan:Price per year"), i18next.t("plan:Price per year - Tooltip"))} : + {Setting.getLabel(i18next.t("plan:Period"), i18next.t("plan:Period - Tooltip"))} : - { - this.updatePlanField("pricePerYear", value); - }} /> + {this.updateProductField("owner", value);})}> + { + { this.updateProductField("name", e.target.value); }} /> @@ -171,7 +172,7 @@ class ProductEditPage extends React.Component { {Setting.getLabel(i18next.t("user:Tag"), i18next.t("product:Tag - Tooltip"))} : - { + { this.updateProductField("tag", e.target.value); }} /> @@ -201,7 +202,7 @@ class ProductEditPage extends React.Component { {Setting.getLabel(i18next.t("payment:Currency"), i18next.t("payment:Currency - Tooltip"))} : - { this.updateProductField("currency", value); })}> { @@ -218,7 +219,7 @@ class ProductEditPage extends React.Component { {Setting.getLabel(i18next.t("product:Price"), i18next.t("product:Price - Tooltip"))} : - { + { this.updateProductField("price", value); }} /> @@ -228,7 +229,7 @@ class ProductEditPage extends React.Component { {Setting.getLabel(i18next.t("product:Quantity"), i18next.t("product:Quantity - Tooltip"))} : - { + { this.updateProductField("quantity", value); }} /> @@ -238,7 +239,7 @@ class ProductEditPage extends React.Component { {Setting.getLabel(i18next.t("product:Sold"), i18next.t("product:Sold - Tooltip"))} : - { + { this.updateProductField("sold", value); }} /> @@ -248,7 +249,7 @@ class ProductEditPage extends React.Component { {Setting.getLabel(i18next.t("product:Payment providers"), i18next.t("product:Payment providers - Tooltip"))} : - {this.updateProductField("providers", value);})}> { this.state.providers.map((provider, index) => ) } diff --git a/web/src/ProductListPage.js b/web/src/ProductListPage.js index 25a8616a..113c6718 100644 --- a/web/src/ProductListPage.js +++ b/web/src/ProductListPage.js @@ -253,11 +253,13 @@ class ProductListPage extends BaseListPage { width: "230px", fixed: (Setting.isMobile()) ? "false" : "right", render: (text, record, index) => { + const isCreatedByPlan = record.tag === "auto_created_product_for_plan"; return (
this.deleteProduct(index)} > diff --git a/web/src/SubscriptionEditPage.js b/web/src/SubscriptionEditPage.js index 2125e4dc..a582299d 100644 --- a/web/src/SubscriptionEditPage.js +++ b/web/src/SubscriptionEditPage.js @@ -14,7 +14,7 @@ import moment from "moment"; import React from "react"; -import {Button, Card, Col, DatePicker, Input, InputNumber, Row, Select} from "antd"; +import {Button, Card, Col, DatePicker, Input, Row, Select} from "antd"; import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as PricingBackend from "./backend/PricingBackend"; import * as PlanBackend from "./backend/PlanBackend"; @@ -171,16 +171,6 @@ class SubscriptionEditPage extends React.Component { }} /> - - - {Setting.getLabel(i18next.t("subscription:Duration"), i18next.t("subscription:Duration - Tooltip"))} - - - { - this.updateSubscriptionField("duration", value); - }} /> - - {Setting.getLabel(i18next.t("subscription:Start time"), i18next.t("subscription:Start time - Tooltip"))} @@ -201,6 +191,23 @@ class SubscriptionEditPage extends React.Component { }} /> + + + {Setting.getLabel(i18next.t("plan:Period"), i18next.t("plan:Period - Tooltip"))} : + + +