-
+
diff --git a/client/src/containers/Subscriptions/billingPlans.js b/client/src/containers/Subscriptions/billingPlans.js
index 41aa17070..f94e14c1e 100644
--- a/client/src/containers/Subscriptions/billingPlans.js
+++ b/client/src/containers/Subscriptions/billingPlans.js
@@ -62,7 +62,7 @@ function BillingPlans({ formik, title, selected = 1 }) {
return (
-
+
diff --git a/client/src/containers/Subscriptions/withSubscriptions.js b/client/src/containers/Subscriptions/withSubscriptions.js
new file mode 100644
index 000000000..441dc0fb6
--- /dev/null
+++ b/client/src/containers/Subscriptions/withSubscriptions.js
@@ -0,0 +1,22 @@
+import { connect } from 'react-redux';
+import {
+ isSubscriptionOnTrialFactory,
+ isSubscriptionInactiveFactory,
+ isSubscriptionActiveFactory,
+} from 'store/subscription/subscription.selectors';
+
+export default (mapState, slug) => {
+ const isSubscriptionOnTrial = isSubscriptionOnTrialFactory(slug);
+ const isSubscriptionInactive = isSubscriptionInactiveFactory(slug);
+ const isSubscriptionActive = isSubscriptionActiveFactory(slug);
+
+ const mapStateToProps = (state, props) => {
+ const mapped = {
+ isSubscriptionOnTrial: isSubscriptionOnTrial(state, props),
+ isSubscriptionInactive: isSubscriptionInactive(state, props),
+ isSubscriptionActive: isSubscriptionActive(state, props),
+ };
+ return (mapState) ? mapState(mapped, state, props) : mapped;
+ };
+ return connect(mapStateToProps);
+};
\ No newline at end of file
diff --git a/client/src/containers/Subscriptions/withSubscriptionsActions.js b/client/src/containers/Subscriptions/withSubscriptionsActions.js
new file mode 100644
index 000000000..2b8ed6694
--- /dev/null
+++ b/client/src/containers/Subscriptions/withSubscriptionsActions.js
@@ -0,0 +1,10 @@
+import { connect } from 'react-redux';
+import {
+ fetchSubscriptions,
+} from 'store/subscription/subscription.actions'
+
+const mapDispatchToProps = (dispatch) => ({
+ requestFetchSubscriptions: () => dispatch(fetchSubscriptions()),
+});
+
+export default connect(null, mapDispatchToProps);
\ No newline at end of file
diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js
index 5396a8dd7..2fc8eb4c4 100644
--- a/client/src/lang/en/index.js
+++ b/client/src/lang/en/index.js
@@ -765,4 +765,5 @@ export default {
something_wentwrong: 'Something went wrong.',
new_password: 'New password',
license_code_: 'License code',
+ legal_organization_name: 'Legal Organization Name'
};
diff --git a/client/src/store/reducers.js b/client/src/store/reducers.js
index 2827e1eed..62e4188ce 100644
--- a/client/src/store/reducers.js
+++ b/client/src/store/reducers.js
@@ -26,10 +26,12 @@ import vendors from './vendors/vendors.reducer';
import paymentReceives from './PaymentReceive/paymentReceive.reducer';
import paymentMades from './PaymentMades/paymentMade.reducer';
import organizations from './organizations/organizations.reducers';
+import subscriptions from './subscription/subscription.reducer';
export default combineReducers({
authentication,
organizations,
+ subscriptions,
dashboard,
users,
accounts,
@@ -47,12 +49,11 @@ export default combineReducers({
exchangeRates,
globalErrors,
customers,
-
salesEstimates,
salesInvoices,
salesReceipts,
bills,
vendors,
paymentReceives,
- paymentMades
+ paymentMades,
});
diff --git a/client/src/store/subscription/subscription.actions.js b/client/src/store/subscription/subscription.actions.js
new file mode 100644
index 000000000..28ef9d8f2
--- /dev/null
+++ b/client/src/store/subscription/subscription.actions.js
@@ -0,0 +1,14 @@
+import ApiService from 'services/ApiService';
+import t from 'store/types';
+
+export const fetchSubscriptions = () => (dispatch) => new Promise((resolve, reject) => {
+ ApiService.get('subscription').then((response) => {
+ dispatch({
+ type: t.SET_PLAN_SUBSCRIPTIONS_LIST,
+ payload: {
+ subscriptions: response.data.subscriptions,
+ },
+ });
+ resolve(response);
+ }).catch((error) => { reject(error); })
+});
\ No newline at end of file
diff --git a/client/src/store/subscription/subscription.reducer.js b/client/src/store/subscription/subscription.reducer.js
new file mode 100644
index 000000000..ee9e20f9b
--- /dev/null
+++ b/client/src/store/subscription/subscription.reducer.js
@@ -0,0 +1,19 @@
+import { createReducer } from '@reduxjs/toolkit';
+import t from 'store/types';
+
+const initialState = {
+ data: {},
+};
+
+export default createReducer(initialState, {
+
+ [t.SET_PLAN_SUBSCRIPTIONS_LIST]: (state, action) => {
+ const { subscriptions } = action.payload;
+ const _data = {};
+
+ subscriptions.forEach((subscription) => {
+ _data[subscription.id] = subscription;
+ });
+ state.data = _data;
+ },
+});
diff --git a/client/src/store/subscription/subscription.selectors.js b/client/src/store/subscription/subscription.selectors.js
new file mode 100644
index 000000000..eb59631db
--- /dev/null
+++ b/client/src/store/subscription/subscription.selectors.js
@@ -0,0 +1,23 @@
+import { createSelector } from '@reduxjs/toolkit';
+
+const subscriptionSelector = (slug) => (state, props) => {
+ const subscriptions = Object.values(state.subscriptions.data);
+ return subscriptions.find((subscription) => subscription.slug === slug);
+};
+
+export const isSubscriptionOnTrialFactory = (slug) => createSelector(
+ subscriptionSelector(slug),
+ (subscription) => !!subscription?.on_trial,
+);
+
+export const isSubscriptionActiveFactory = (slug) => createSelector(
+ subscriptionSelector(slug),
+ (subscription) => {
+ return !!subscription?.active;
+ }
+);
+
+export const isSubscriptionInactiveFactory = (slug) => createSelector(
+ subscriptionSelector(slug),
+ (subscription) => !!subscription?.inactive,
+);
\ No newline at end of file
diff --git a/client/src/store/subscription/subscription.types.js b/client/src/store/subscription/subscription.types.js
new file mode 100644
index 000000000..6a9692684
--- /dev/null
+++ b/client/src/store/subscription/subscription.types.js
@@ -0,0 +1,4 @@
+
+export default {
+ SET_PLAN_SUBSCRIPTIONS_LIST: 'SET_PLAN_SUBSCRIPTIONS_LIST',
+};
\ No newline at end of file
diff --git a/client/src/store/types.js b/client/src/store/types.js
index 755bad8dc..3e4a32ed8 100644
--- a/client/src/store/types.js
+++ b/client/src/store/types.js
@@ -25,6 +25,7 @@ import vendors from './vendors/vendors.types';
import paymentReceives from './PaymentReceive/paymentReceive.type';
import paymentMades from './PaymentMades/paymentMade.type';
import organizations from './organizations/organizations.types';
+import subscription from './subscription/subscription.types';
export default {
...authentication,
@@ -54,4 +55,5 @@ export default {
...paymentReceives,
...paymentMades,
...organizations,
+ ...subscription,
};
diff --git a/client/src/style/pages/register-wizard-page.scss b/client/src/style/pages/register-wizard-page.scss
index 26844cd32..598ed3462 100644
--- a/client/src/style/pages/register-wizard-page.scss
+++ b/client/src/style/pages/register-wizard-page.scss
@@ -20,13 +20,17 @@
h1{
font-size: 22px;
+ }
+ h1,
+ h3{
font-weight: 500;
- color: #6d6d6d;
+ color: #6b7382;
}
}
&__content{
width: 70%;
+ padding-bottom: 40px;
}
&__left-section {
@@ -68,7 +72,7 @@
&__text {
font-size: 16px;
opacity: 0.75;
- margin-bottom: 18px;
+ margin-bottom: 10px;
}
&__organization {
@@ -87,7 +91,7 @@
height: 3px;
width: 100px;
background: rgba(255, 255, 255, 0.15);
- margin: 20px 0;
+ margin: 10px 0;
}
&__footer{
@@ -190,7 +194,54 @@
}
}
-//Register Subscription form
-.register-subscription-form {
-
-}
+.setup-organization {
+ width: 580px;
+ margin: 0 auto;
+ padding: 45px 0 20px;
+
+ &__title-wrap{
+ margin-bottom: 20px;
+
+ h1 {
+ margin-top: 0;
+ margin-bottom: 10px;
+ color: #565e6c;
+ }
+ }
+
+ &__form{
+ h3 {
+ margin-bottom: 1rem;
+ }
+ }
+
+ .bp3-form-group {
+ .bp3-input-group {
+ .bp3-input {
+ height: 38px;
+ }
+ }
+ }
+
+ .bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) {
+ width: 100%;
+ height: 38px;
+ }
+
+ .register-org-note{
+ font-size: 13px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #e1e1e1;
+ margin-bottom: 1.75rem;
+ color: #666;
+ }
+
+ .register-org-button {
+ .bp3-button {
+ background-color: #0052cc;
+ min-width: 175px;
+ height: 40px;
+ font-size: 15px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/server/src/api/controllers/Subscription/index.ts b/server/src/api/controllers/Subscription/index.ts
index 1154de6a7..1485e7249 100644
--- a/server/src/api/controllers/Subscription/index.ts
+++ b/server/src/api/controllers/Subscription/index.ts
@@ -1,12 +1,17 @@
-import { Router } from 'express'
-import { Container, Service } from 'typedi';
+import { Router, Request, Response, NextFunction } from 'express'
+import { Container, Service, Inject } from 'typedi';
import JWTAuth from 'api/middleware/jwtAuth';
import TenancyMiddleware from 'api/middleware/TenancyMiddleware';
import AttachCurrentTenantUser from 'api/middleware/AttachCurrentTenantUser';
import PaymentViaLicenseController from 'api/controllers/Subscription/PaymentViaLicense';
+import SubscriptionService from 'services/Subscription/SubscriptionService';
+import asyncMiddleware from 'api/middleware/asyncMiddleware';
@Service()
-export default class SubscriptionController {
+export default class SubscriptionController {
+ @Inject()
+ subscriptionService: SubscriptionService;
+
/**
* Router constructor.
*/
@@ -19,6 +24,26 @@ export default class SubscriptionController {
router.use('/license', Container.get(PaymentViaLicenseController).router());
+ router.get('/',
+ asyncMiddleware(this.getSubscriptions.bind(this))
+ );
return router;
}
+
+ /**
+ * Retrieve all subscriptions of the authenticated user's tenant.
+ * @param {Request} req
+ * @param {Response} res
+ * @param {NextFunction} next
+ */
+ async getSubscriptions(req: Request, res: Response, next: NextFunction) {
+ const { tenantId } = req;
+
+ try {
+ const subscriptions = await this.subscriptionService.getSubscriptions(tenantId);
+ return res.status(200).send({ subscriptions });
+ } catch (error) {
+ next(error);
+ }
+ }
}
diff --git a/server/src/data/options.js b/server/src/data/options.js
index e10fee599..727bce956 100644
--- a/server/src/data/options.js
+++ b/server/src/data/options.js
@@ -25,6 +25,10 @@ export default {
type: 'string',
// config: true,
},
+ {
+ key: 'financial_date_start',
+ type: 'string',
+ },
{
key: 'language',
type: 'string',
diff --git a/server/src/services/Organization/index.ts b/server/src/services/Organization/index.ts
index 48fde80b3..a50d67930 100644
--- a/server/src/services/Organization/index.ts
+++ b/server/src/services/Organization/index.ts
@@ -100,7 +100,7 @@ export default class OrganizationService {
this.logger.info('[organization] trying to list all organizations.', { user });
const { tenantRepository } = this.sysRepositories;
- const tenant = await tenantRepository.getByIdWithSubscriptions(user.tenantId);
+ const tenant = await tenantRepository.getById(user.tenantId);
return [tenant];
}
diff --git a/server/src/services/Subscription/SubscriptionService.ts b/server/src/services/Subscription/SubscriptionService.ts
index 695a0687c..8508b5746 100644
--- a/server/src/services/Subscription/SubscriptionService.ts
+++ b/server/src/services/Subscription/SubscriptionService.ts
@@ -1,5 +1,5 @@
import { Service, Inject } from 'typedi';
-import { Plan, Tenant } from 'system/models';
+import { Plan, PlanSubscription } from 'system/models';
import Subscription from 'services/Subscription/Subscription';
import LicensePaymentMethod from 'services/Payment/LicensePaymentMethod';
import PaymentContext from 'services/Payment';
@@ -29,7 +29,7 @@ export default class SubscriptionService {
* @param {string} licenseCode
* @return {Promise}
*/
- async subscriptionViaLicense(
+ public async subscriptionViaLicense(
tenantId: number,
planSlug: string,
paymentModel?: ILicensePaymentModel,
@@ -53,4 +53,15 @@ export default class SubscriptionService {
tenantId, paymentModel
});
}
+
+ /**
+ * Retrieve all subscription of the given tenant.
+ * @param {number} tenantId
+ */
+ public async getSubscriptions(tenantId: number) {
+ this.logger.info('[subscription] trying to get tenant subscriptions.', { tenantId });
+ const subscriptions = await PlanSubscription.query().where('tenant_id', tenantId);
+
+ return subscriptions;
+ }
}
\ No newline at end of file