feat: retrieve organization subscriptions list api.

feat: subscriptions reducers.
This commit is contained in:
Ahmed Bouhuolia
2020-10-13 21:46:32 +02:00
parent d71845a4c4
commit 8b97673100
23 changed files with 289 additions and 55 deletions

View File

@@ -13,7 +13,6 @@ export default function DashboardLoadingIndicator({
<div className={classNames('bigcapital-loading', className)}> <div className={classNames('bigcapital-loading', className)}>
<div class="center"> <div class="center">
<Icon icon="bigcapital" height={37} width={214} /> <Icon icon="bigcapital" height={37} width={214} />
<span class="text">Please wait while resources loading...</span>
</div> </div>
</div> </div>
</Choose.When> </Choose.When>

View File

@@ -13,9 +13,9 @@ function EnsureOrganizationIsReady({
redirectTo = '/setup', redirectTo = '/setup',
// #withOrganizationByOrgId // #withOrganizationByOrgId
isOrganizationBuilt, isOrganizationInitialized,
}) { }) {
return (isOrganizationBuilt) ? children : ( return (isOrganizationInitialized) ? children : (
<Redirect <Redirect
to={{ pathname: redirectTo }} to={{ pathname: redirectTo }}
/> />
@@ -27,5 +27,5 @@ export default compose(
connect((state, props) => ({ connect((state, props) => ({
organizationId: props.currentOrganizationId, organizationId: props.currentOrganizationId,
})), })),
withOrganization(({ isOrganizationBuilt }) => ({ isOrganizationBuilt })), withOrganization(({ isOrganizationInitialized }) => ({ isOrganizationInitialized })),
)(EnsureOrganizationIsReady); )(EnsureOrganizationIsReady);

View File

@@ -7,6 +7,7 @@ import SetupWizardPage from 'containers/Setup/WizardSetupPage';
import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator'; import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator';
import withOrganizationActions from 'containers/Organization/withOrganizationActions'; import withOrganizationActions from 'containers/Organization/withOrganizationActions';
import withSubscriptionsActions from 'containers/Subscriptions/withSubscriptionsActions';
import { compose } from 'utils'; import { compose } from 'utils';
@@ -17,13 +18,26 @@ function DashboardPrivatePages({
// #withOrganizationActions // #withOrganizationActions
requestAllOrganizations, requestAllOrganizations,
// #withSubscriptionsActions
requestFetchSubscriptions,
}) { }) {
// Fetch all user's organizatins.
const fetchOrganizations = useQuery( const fetchOrganizations = useQuery(
['organizations'], () => requestAllOrganizations(), ['organizations'], () => requestAllOrganizations(),
); );
// Fetchs organization subscriptions.
const fetchSuscriptions = useQuery(
['susbcriptions'], () => requestFetchSubscriptions(),
{ enabled: fetchOrganizations.data },
)
return ( return (
<DashboardLoadingIndicator isLoading={fetchOrganizations.isFetching}> <DashboardLoadingIndicator isLoading={
fetchOrganizations.isFetching ||
fetchSuscriptions.isFetching
}>
<Switch> <Switch>
<Route path={'/setup'}> <Route path={'/setup'}>
<SetupWizardPage /> <SetupWizardPage />
@@ -39,4 +53,5 @@ function DashboardPrivatePages({
export default compose( export default compose(
withOrganizationActions, withOrganizationActions,
withSubscriptionsActions,
)(DashboardPrivatePages); )(DashboardPrivatePages);

View File

@@ -43,14 +43,18 @@ function SetupLeftSection({
<p className={'content__text'}> <p className={'content__text'}>
<T id={'you_have_a_bigcapital_account'} /> <T id={'you_have_a_bigcapital_account'} />
</p> </p>
<span class="content__divider"></span>
<div className={'content__organization'}> <div className={'content__organization'}>
<span class="organization-id">Your oragnization ID: <span class="id">{ currentOrganizationId }</span>,</span><br /> <span class="organization-id">
<span class="signout"><a onClick={onClickLogout} href="#"><T id={'sign_out'} /></a></span> Your oragnization ID: <span class="id">{ currentOrganizationId }</span>,
</span>
<br />
<span class="signout">
<a onClick={onClickLogout} href="#"><T id={'sign_out'} /></a>
</span>
</div> </div>
<span class="content__divider"></span>
<div className={'content__footer'}> <div className={'content__footer'}>
<div className={'content__contact-info'}> <div className={'content__contact-info'}>
<p><T id={'we_re_here_to_help'} /> {'+21892-791-8381'}</p> <p><T id={'we_re_here_to_help'} /> {'+21892-791-8381'}</p>

View File

@@ -16,16 +16,20 @@ import classNames from 'classnames';
import { TimezonePicker } from '@blueprintjs/timezone'; import { TimezonePicker } from '@blueprintjs/timezone';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { withWizard } from 'react-albus';
import { momentFormatter, tansformDateValue } from 'utils'; import { momentFormatter, tansformDateValue } from 'utils';
import { ListSelect, ErrorMessage, FieldRequiredHint } from 'components'; import { ListSelect, ErrorMessage, FieldRequiredHint } from 'components';
import { useHistory } from 'react-router-dom';
import withSettingsActions from 'containers/Settings/withSettingsActions'; import withSettingsActions from 'containers/Settings/withSettingsActions';
import withOrganizationActions from 'containers/Organization/withOrganizationActions'; import withOrganizationActions from 'containers/Organization/withOrganizationActions';
import { compose, optionsMapToArray } from 'utils'; import { compose, optionsMapToArray } from 'utils';
function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) { function SetupOrganizationForm({
requestSubmitOptions,
requestOrganizationSeed,
wizard,
}) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const [selected, setSelected] = useState(); const [selected, setSelected] = useState();
@@ -130,7 +134,7 @@ function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
name: Yup.string() name: Yup.string()
.required() .required()
.label(formatMessage({ id: 'organization_name_' })), .label(formatMessage({ id: 'organization_name_' })),
date_start: Yup.date() financial_date_start: Yup.date()
.required() .required()
.label(formatMessage({ id: 'date_start_' })), .label(formatMessage({ id: 'date_start_' })),
base_currency: Yup.string() base_currency: Yup.string()
@@ -148,7 +152,7 @@ function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
const initialValues = useMemo( const initialValues = useMemo(
() => ({ () => ({
name: '', name: '',
date_start: moment(new Date()).format('YYYY-MM-DD'), financial_date_start: moment(new Date()).format('YYYY-MM-DD'),
base_currency: '', base_currency: '',
language: '', language: '',
fiscal_year: '', fiscal_year: '',
@@ -176,7 +180,11 @@ function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
return { key: option.key, ...option, group: 'organization' }; return { key: option.key, ...option, group: 'organization' };
}); });
requestSubmitOptions({ options }) requestSubmitOptions({ options })
.then(() => {
return requestOrganizationSeed();
})
.then((response) => { .then((response) => {
wizard.next();
setSubmitting(false); setSubmitting(false);
}) })
.catch((erros) => { .catch((erros) => {
@@ -220,29 +228,29 @@ function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
const handleDateChange = useCallback( const handleDateChange = useCallback(
(date) => { (date) => {
const formatted = moment(date).format('YYYY-MM-DD'); const formatted = moment(date).format('YYYY-MM-DD');
setFieldValue('date_start', formatted); setFieldValue('financial_date_start', formatted);
}, },
[setFieldValue], [setFieldValue],
); );
return ( return (
<div className={'register-organizaton-form'}> <div className={'setup-organization'}>
<div className={'register-org-title'}> <div className={'setup-organization__title-wrap'}>
<h2> <h1>
<T id={'let_s_get_started'} /> <T id={'let_s_get_started'} />
</h2> </h1>
<p> <p class="paragraph">
<T id={'tell_the_system_a_little_bit_about_your_organization'} /> <T id={'tell_the_system_a_little_bit_about_your_organization'} />
</p> </p>
</div> </div>
<form onClick={handleSubmit}> <form class="setup-organization__form" onClick={handleSubmit}>
<h3> <h3>
<T id={'organization_details'} /> <T id={'organization_details'} />
</h3> </h3>
<FormGroup <FormGroup
label={<T id={'name'} />} label={<T id={'legal_organization_name'} />}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
className={'form-group--name'} className={'form-group--name'}
intent={errors.name && touched.name && Intent.DANGER} intent={errors.name && touched.name && Intent.DANGER}
@@ -258,15 +266,15 @@ function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
<FormGroup <FormGroup
label={<T id={'financial_starting_date'} />} label={<T id={'financial_starting_date'} />}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
intent={errors.date_start && touched.date_start && Intent.DANGER} intent={errors.financial_date_start && touched.financial_date_start && Intent.DANGER}
helperText={ helperText={
<ErrorMessage name="date_start" {...{ errors, touched }} /> <ErrorMessage name="financial_date_start" {...{ errors, touched }} />
} }
className={classNames('form-group--select-list', Classes.FILL)} className={classNames('form-group--select-list', Classes.FILL)}
> >
<DateInput <DateInput
{...momentFormatter('MMMM Do YYYY')} {...momentFormatter('MMMM Do YYYY')}
value={tansformDateValue(values.date_start)} value={tansformDateValue(values.financial_date_start)}
onChange={handleDateChange} onChange={handleDateChange}
popoverProps={{ position: Position.BOTTOM, minimal: true }} popoverProps={{ position: Position.BOTTOM, minimal: true }}
/> />
@@ -410,4 +418,5 @@ function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
export default compose( export default compose(
withSettingsActions, withSettingsActions,
withOrganizationActions, withOrganizationActions,
withWizard,
)(SetupOrganizationForm); )(SetupOrganizationForm);

View File

@@ -5,6 +5,7 @@ import { useHistory } from "react-router-dom";
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import WizardSetupSteps from './WizardSetupSteps'; import WizardSetupSteps from './WizardSetupSteps';
import withSubscriptions from 'containers/Subscriptions/withSubscriptions';
import SetupSubscriptionForm from './SetupSubscriptionForm'; import SetupSubscriptionForm from './SetupSubscriptionForm';
import SetupOrganizationForm from './SetupOrganizationForm'; import SetupOrganizationForm from './SetupOrganizationForm';
@@ -23,16 +24,18 @@ function SetupRightSection ({
// #withOrganization // #withOrganization
isOrganizationInitialized, isOrganizationInitialized,
isOrganizationSubscribed: hasSubscriptions, isOrganizationSeeded,
isOrganizationSeeded
// #withSubscriptions
isSubscriptionActive
}) { }) {
const history = useHistory(); const history = useHistory();
const handleSkip = useCallback(({ step, push }) => { const handleSkip = useCallback(({ step, push }) => {
const scenarios = [ const scenarios = [
{ condition: !hasSubscriptions, redirectTo: 'subscription' }, { condition: !isSubscriptionActive, redirectTo: 'subscription' },
{ condition: hasSubscriptions && !isOrganizationInitialized, redirectTo: 'initializing' }, { condition: isSubscriptionActive && !isOrganizationInitialized, redirectTo: 'initializing' },
{ condition: hasSubscriptions && !isOrganizationSeeded, redirectTo: 'organization' }, { condition: isSubscriptionActive && !isOrganizationSeeded, redirectTo: 'organization' },
]; ];
const scenario = scenarios.find((scenario) => scenario.condition); const scenario = scenarios.find((scenario) => scenario.condition);
@@ -40,7 +43,7 @@ function SetupRightSection ({
push(scenario.redirectTo); push(scenario.redirectTo);
} }
}, [ }, [
hasSubscriptions, isSubscriptionActive,
isOrganizationInitialized, isOrganizationInitialized,
isOrganizationSeeded, isOrganizationSeeded,
]); ]);
@@ -92,12 +95,15 @@ export default compose(
withOrganization(({ withOrganization(({
organization, organization,
isOrganizationInitialized, isOrganizationInitialized,
isOrganizationSubscribed,
isOrganizationSeeded, isOrganizationSeeded,
}) => ({ }) => ({
organization, organization,
isOrganizationInitialized, isOrganizationInitialized,
isOrganizationSubscribed,
isOrganizationSeeded, isOrganizationSeeded,
})), })),
withSubscriptions(({
isSubscriptionActive,
}) => ({
isSubscriptionActive
}), 'main'),
)(SetupRightSection); )(SetupRightSection);

View File

@@ -3,10 +3,11 @@ import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import { Button, Intent } from '@blueprintjs/core'; import { Button, Intent } from '@blueprintjs/core';
import { withWizard } from 'react-albus';
import withSubscriptionsActions from 'containers/Subscriptions/withSubscriptionsActions';
import BillingPlans from 'containers/Subscriptions/billingPlans'; import BillingPlans from 'containers/Subscriptions/billingPlans';
import BillingPeriods from 'containers/Subscriptions/billingPeriods'; import BillingPeriods from 'containers/Subscriptions/billingPeriods';
import { BillingPaymentmethod } from 'containers/Subscriptions/billingPaymentmethod'; import { BillingPaymentmethod } from 'containers/Subscriptions/billingPaymentmethod';
import withBillingActions from 'containers/Subscriptions/withBillingActions'; import withBillingActions from 'containers/Subscriptions/withBillingActions';
import { compose } from 'utils'; import { compose } from 'utils';
@@ -14,18 +15,23 @@ import { compose } from 'utils';
* Subscription step of wizard setup. * Subscription step of wizard setup.
*/ */
function SetupSubscriptionForm({ function SetupSubscriptionForm({
//#withBillingActions // #withBillingActions
requestSubmitBilling, requestSubmitBilling,
// #withWizard
wizard,
// #withSubscriptionsActions
requestFetchSubscriptions
}) { }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
plan_slug: Yup.string() plan_slug: Yup.string()
.required() .required()
.label(formatMessage({ id: 'plan_slug' })), .label(formatMessage({ id: 'plan_slug' })),
license_code: Yup.string() license_code: Yup.string()
.min(7) .min(10)
.max(7) .max(10)
.required() .required()
.label(formatMessage({ id: 'license_code_' })) .label(formatMessage({ id: 'license_code_' }))
.trim(), .trim(),
@@ -48,6 +54,10 @@ function SetupSubscriptionForm({
onSubmit: (values, { setSubmitting, setErrors }) => { onSubmit: (values, { setSubmitting, setErrors }) => {
requestSubmitBilling(values) requestSubmitBilling(values)
.then((response) => { .then((response) => {
return requestFetchSubscriptions();
})
.then(() => {
wizard.next();
setSubmitting(false); setSubmitting(false);
}) })
.catch((errors) => { .catch((errors) => {
@@ -76,4 +86,8 @@ function SetupSubscriptionForm({
); );
} }
export default compose(withBillingActions)(SetupSubscriptionForm); export default compose(
withBillingActions,
withWizard,
withSubscriptionsActions,
)(SetupSubscriptionForm);

View File

@@ -45,7 +45,7 @@ function BillingPeriods({ formik, title, selected = 1 }) {
return ( return (
<section class="billing-section"> <section class="billing-section">
<h1 className={'bg-title'}> <h1>
<T id={title} /> <T id={title} />
</h1> </h1>
<p className='paragraph'> <p className='paragraph'>

View File

@@ -62,7 +62,7 @@ function BillingPlans({ formik, title, selected = 1 }) {
return ( return (
<section class="billing-section"> <section class="billing-section">
<h1 className={'bg-title'}> <h1>
<T id={title} /> <T id={title} />
</h1> </h1>
<p className='paragraph'> <p className='paragraph'>

View File

@@ -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);
};

View File

@@ -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);

View File

@@ -765,4 +765,5 @@ export default {
something_wentwrong: 'Something went wrong.', something_wentwrong: 'Something went wrong.',
new_password: 'New password', new_password: 'New password',
license_code_: 'License code', license_code_: 'License code',
legal_organization_name: 'Legal Organization Name'
}; };

View File

@@ -26,10 +26,12 @@ import vendors from './vendors/vendors.reducer';
import paymentReceives from './PaymentReceive/paymentReceive.reducer'; import paymentReceives from './PaymentReceive/paymentReceive.reducer';
import paymentMades from './PaymentMades/paymentMade.reducer'; import paymentMades from './PaymentMades/paymentMade.reducer';
import organizations from './organizations/organizations.reducers'; import organizations from './organizations/organizations.reducers';
import subscriptions from './subscription/subscription.reducer';
export default combineReducers({ export default combineReducers({
authentication, authentication,
organizations, organizations,
subscriptions,
dashboard, dashboard,
users, users,
accounts, accounts,
@@ -47,12 +49,11 @@ export default combineReducers({
exchangeRates, exchangeRates,
globalErrors, globalErrors,
customers, customers,
salesEstimates, salesEstimates,
salesInvoices, salesInvoices,
salesReceipts, salesReceipts,
bills, bills,
vendors, vendors,
paymentReceives, paymentReceives,
paymentMades paymentMades,
}); });

View File

@@ -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); })
});

View File

@@ -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;
},
});

View File

@@ -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,
);

View File

@@ -0,0 +1,4 @@
export default {
SET_PLAN_SUBSCRIPTIONS_LIST: 'SET_PLAN_SUBSCRIPTIONS_LIST',
};

View File

@@ -25,6 +25,7 @@ import vendors from './vendors/vendors.types';
import paymentReceives from './PaymentReceive/paymentReceive.type'; import paymentReceives from './PaymentReceive/paymentReceive.type';
import paymentMades from './PaymentMades/paymentMade.type'; import paymentMades from './PaymentMades/paymentMade.type';
import organizations from './organizations/organizations.types'; import organizations from './organizations/organizations.types';
import subscription from './subscription/subscription.types';
export default { export default {
...authentication, ...authentication,
@@ -54,4 +55,5 @@ export default {
...paymentReceives, ...paymentReceives,
...paymentMades, ...paymentMades,
...organizations, ...organizations,
...subscription,
}; };

View File

@@ -20,13 +20,17 @@
h1{ h1{
font-size: 22px; font-size: 22px;
}
h1,
h3{
font-weight: 500; font-weight: 500;
color: #6d6d6d; color: #6b7382;
} }
} }
&__content{ &__content{
width: 70%; width: 70%;
padding-bottom: 40px;
} }
&__left-section { &__left-section {
@@ -68,7 +72,7 @@
&__text { &__text {
font-size: 16px; font-size: 16px;
opacity: 0.75; opacity: 0.75;
margin-bottom: 18px; margin-bottom: 10px;
} }
&__organization { &__organization {
@@ -87,7 +91,7 @@
height: 3px; height: 3px;
width: 100px; width: 100px;
background: rgba(255, 255, 255, 0.15); background: rgba(255, 255, 255, 0.15);
margin: 20px 0; margin: 10px 0;
} }
&__footer{ &__footer{
@@ -190,7 +194,54 @@
} }
} }
//Register Subscription form .setup-organization {
.register-subscription-form { 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;
}
}
} }

View File

@@ -1,12 +1,17 @@
import { Router } from 'express' import { Router, Request, Response, NextFunction } from 'express'
import { Container, Service } from 'typedi'; import { Container, Service, Inject } from 'typedi';
import JWTAuth from 'api/middleware/jwtAuth'; import JWTAuth from 'api/middleware/jwtAuth';
import TenancyMiddleware from 'api/middleware/TenancyMiddleware'; import TenancyMiddleware from 'api/middleware/TenancyMiddleware';
import AttachCurrentTenantUser from 'api/middleware/AttachCurrentTenantUser'; import AttachCurrentTenantUser from 'api/middleware/AttachCurrentTenantUser';
import PaymentViaLicenseController from 'api/controllers/Subscription/PaymentViaLicense'; import PaymentViaLicenseController from 'api/controllers/Subscription/PaymentViaLicense';
import SubscriptionService from 'services/Subscription/SubscriptionService';
import asyncMiddleware from 'api/middleware/asyncMiddleware';
@Service() @Service()
export default class SubscriptionController { export default class SubscriptionController {
@Inject()
subscriptionService: SubscriptionService;
/** /**
* Router constructor. * Router constructor.
*/ */
@@ -19,6 +24,26 @@ export default class SubscriptionController {
router.use('/license', Container.get(PaymentViaLicenseController).router()); router.use('/license', Container.get(PaymentViaLicenseController).router());
router.get('/',
asyncMiddleware(this.getSubscriptions.bind(this))
);
return router; 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);
}
}
} }

View File

@@ -25,6 +25,10 @@ export default {
type: 'string', type: 'string',
// config: true, // config: true,
}, },
{
key: 'financial_date_start',
type: 'string',
},
{ {
key: 'language', key: 'language',
type: 'string', type: 'string',

View File

@@ -100,7 +100,7 @@ export default class OrganizationService {
this.logger.info('[organization] trying to list all organizations.', { user }); this.logger.info('[organization] trying to list all organizations.', { user });
const { tenantRepository } = this.sysRepositories; const { tenantRepository } = this.sysRepositories;
const tenant = await tenantRepository.getByIdWithSubscriptions(user.tenantId); const tenant = await tenantRepository.getById(user.tenantId);
return [tenant]; return [tenant];
} }

View File

@@ -1,5 +1,5 @@
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import { Plan, Tenant } from 'system/models'; import { Plan, PlanSubscription } from 'system/models';
import Subscription from 'services/Subscription/Subscription'; import Subscription from 'services/Subscription/Subscription';
import LicensePaymentMethod from 'services/Payment/LicensePaymentMethod'; import LicensePaymentMethod from 'services/Payment/LicensePaymentMethod';
import PaymentContext from 'services/Payment'; import PaymentContext from 'services/Payment';
@@ -29,7 +29,7 @@ export default class SubscriptionService {
* @param {string} licenseCode * @param {string} licenseCode
* @return {Promise} * @return {Promise}
*/ */
async subscriptionViaLicense( public async subscriptionViaLicense(
tenantId: number, tenantId: number,
planSlug: string, planSlug: string,
paymentModel?: ILicensePaymentModel, paymentModel?: ILicensePaymentModel,
@@ -53,4 +53,15 @@ export default class SubscriptionService {
tenantId, paymentModel 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;
}
} }