mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
Merge branch 'develop' into draft-import-resources
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import Container, { Inject, Service } from 'typedi';
|
||||
import { Router } from 'express';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import { PlaidBankingController } from './PlaidBankingController';
|
||||
|
||||
@Service()
|
||||
export class BankingController extends BaseController {
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.use('/plaid', Container.get(PlaidBankingController).router());
|
||||
|
||||
return router;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import { PlaidApplication } from '@/services/Banking/Plaid/PlaidApplication';
|
||||
|
||||
@Service()
|
||||
export class PlaidBankingController extends BaseController {
|
||||
@Inject()
|
||||
private plaidApp: PlaidApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.post('/link-token', this.linkToken.bind(this));
|
||||
router.post('/exchange-token', this.exchangeToken.bind(this));
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Plaid link token.
|
||||
* @param {Request} req
|
||||
* @param {response} res
|
||||
* @returns {Response}
|
||||
*/
|
||||
private async linkToken(req: Request, res: Response) {
|
||||
const { tenantId } = req;
|
||||
|
||||
const linkToken = await this.plaidApp.getLinkToken(tenantId);
|
||||
|
||||
return res.status(200).send(linkToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchanges the given public token.
|
||||
* @param {Request} req
|
||||
* @param {response} res
|
||||
* @returns {Response}
|
||||
*/
|
||||
public async exchangeToken(req: Request, res: Response) {
|
||||
const { tenantId } = req;
|
||||
const { public_token, institution_id } = req.body;
|
||||
|
||||
await this.plaidApp.exchangeToken(tenantId, {
|
||||
institutionId: institution_id,
|
||||
publicToken: public_token,
|
||||
});
|
||||
return res.status(200).send({});
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,9 @@ export default class CashflowController {
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.use(Container.get(CommandCashflowTransaction).router());
|
||||
router.use(Container.get(GetCashflowTransaction).router());
|
||||
router.use(Container.get(GetCashflowAccounts).router());
|
||||
router.use(Container.get(CommandCashflowTransaction).router());
|
||||
router.use(Container.get(DeleteCashflowTransaction).router());
|
||||
|
||||
return router;
|
||||
|
||||
@@ -3,14 +3,15 @@ import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { param } from 'express-validator';
|
||||
import BaseController from '../BaseController';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import DeleteCashflowTransactionService from '../../../services/Cashflow/DeleteCashflowTransactionService';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
|
||||
import { AbilitySubject, CashflowAction } from '@/interfaces';
|
||||
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
|
||||
|
||||
@Service()
|
||||
export default class DeleteCashflowTransaction extends BaseController {
|
||||
export default class DeleteCashflowTransactionController extends BaseController {
|
||||
@Inject()
|
||||
deleteCashflowService: DeleteCashflowTransactionService;
|
||||
private cashflowApplication: CashflowApplication;
|
||||
|
||||
/**
|
||||
* Controller router.
|
||||
@@ -44,7 +45,7 @@ export default class DeleteCashflowTransaction extends BaseController {
|
||||
|
||||
try {
|
||||
const { oldCashflowTransaction } =
|
||||
await this.deleteCashflowService.deleteCashflowTransaction(
|
||||
await this.cashflowApplication.deleteTransaction(
|
||||
tenantId,
|
||||
transactionId
|
||||
);
|
||||
@@ -92,6 +93,19 @@ export default class DeleteCashflowTransaction extends BaseController {
|
||||
],
|
||||
});
|
||||
}
|
||||
if (
|
||||
error.errorType ===
|
||||
'CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED'
|
||||
) {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [
|
||||
{
|
||||
type: 'CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED',
|
||||
code: 4100,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { param, query } from 'express-validator';
|
||||
import GetCashflowAccountsService from '@/services/Cashflow/GetCashflowAccountsService';
|
||||
import { query } from 'express-validator';
|
||||
import BaseController from '../BaseController';
|
||||
import GetCashflowTransactionsService from '@/services/Cashflow/GetCashflowTransactionsService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AbilitySubject, CashflowAction } from '@/interfaces';
|
||||
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
|
||||
|
||||
@Service()
|
||||
export default class GetCashflowAccounts extends BaseController {
|
||||
@Inject()
|
||||
getCashflowAccountsService: GetCashflowAccountsService;
|
||||
|
||||
@Inject()
|
||||
getCashflowTransactionsService: GetCashflowTransactionsService;
|
||||
private cashflowApplication: CashflowApplication;
|
||||
|
||||
/**
|
||||
* Controller router.
|
||||
@@ -62,10 +58,7 @@ export default class GetCashflowAccounts extends BaseController {
|
||||
|
||||
try {
|
||||
const cashflowAccounts =
|
||||
await this.getCashflowAccountsService.getCashflowAccounts(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
await this.cashflowApplication.getCashflowAccounts(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
cashflow_accounts: this.transfromToResponse(cashflowAccounts),
|
||||
|
||||
@@ -2,15 +2,15 @@ import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { param } from 'express-validator';
|
||||
import BaseController from '../BaseController';
|
||||
import GetCashflowTransactionsService from '@/services/Cashflow/GetCashflowTransactionsService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AbilitySubject, CashflowAction } from '@/interfaces';
|
||||
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
|
||||
|
||||
@Service()
|
||||
export default class GetCashflowAccounts extends BaseController {
|
||||
@Inject()
|
||||
getCashflowTransactionsService: GetCashflowTransactionsService;
|
||||
private cashflowApplication: CashflowApplication;
|
||||
|
||||
/**
|
||||
* Controller router.
|
||||
@@ -43,11 +43,10 @@ export default class GetCashflowAccounts extends BaseController {
|
||||
const { transactionId } = req.params;
|
||||
|
||||
try {
|
||||
const cashflowTransaction =
|
||||
await this.getCashflowTransactionsService.getCashflowTransaction(
|
||||
tenantId,
|
||||
transactionId
|
||||
);
|
||||
const cashflowTransaction = await this.cashflowApplication.getTransaction(
|
||||
tenantId,
|
||||
transactionId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
cashflow_transaction: this.transfromToResponse(cashflowTransaction),
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { check } from 'express-validator';
|
||||
import { ValidationChain, check, param, query } from 'express-validator';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import BaseController from '../BaseController';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import NewCashflowTransactionService from '@/services/Cashflow/NewCashflowTransactionService';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AbilitySubject, CashflowAction } from '@/interfaces';
|
||||
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
|
||||
|
||||
@Service()
|
||||
export default class NewCashflowTransactionController extends BaseController {
|
||||
@Inject()
|
||||
private newCashflowTranscationService: NewCashflowTransactionService;
|
||||
private cashflowApplication: CashflowApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -18,6 +18,18 @@ export default class NewCashflowTransactionController extends BaseController {
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/transactions/uncategorized/:id',
|
||||
this.asyncMiddleware(this.getUncategorizedCashflowTransaction),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/transactions/:id/uncategorized',
|
||||
this.getUncategorizedTransactionsValidationSchema,
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.getUncategorizedCashflowTransactions),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/transactions',
|
||||
CheckPolicies(CashflowAction.Create, AbilitySubject.Cashflow),
|
||||
@@ -26,13 +38,72 @@ export default class NewCashflowTransactionController extends BaseController {
|
||||
this.asyncMiddleware(this.newCashflowTransaction),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/transactions/:id/uncategorize',
|
||||
this.revertCategorizedCashflowTransaction,
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/transactions/:id/categorize',
|
||||
this.categorizeCashflowTransactionValidationSchema,
|
||||
this.validationResult,
|
||||
this.categorizeCashflowTransaction,
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/transaction/:id/categorize/expense',
|
||||
this.categorizeAsExpenseValidationSchema,
|
||||
this.validationResult,
|
||||
this.categorizesCashflowTransactionAsExpense,
|
||||
this.catchServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getting uncategorized transactions validation schema.
|
||||
* @returns {ValidationChain}
|
||||
*/
|
||||
public get getUncategorizedTransactionsValidationSchema() {
|
||||
return [
|
||||
param('id').exists().isNumeric().toInt(),
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize as expense validation schema.
|
||||
*/
|
||||
public get categorizeAsExpenseValidationSchema() {
|
||||
return [
|
||||
check('expense_account_id').exists(),
|
||||
check('date').isISO8601().exists(),
|
||||
check('reference_no').optional(),
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize cashflow tranasction validation schema.
|
||||
*/
|
||||
public get categorizeCashflowTransactionValidationSchema() {
|
||||
return [
|
||||
check('date').exists().isISO8601().toDate(),
|
||||
check('credit_account_id').exists().isInt().toInt(),
|
||||
check('transaction_number').optional(),
|
||||
check('transaction_type').exists(),
|
||||
check('reference_no').optional(),
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
check('description').optional(),
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* New cashflow transaction validation schema.
|
||||
*/
|
||||
get newTransactionValidationSchema() {
|
||||
public get newTransactionValidationSchema() {
|
||||
return [
|
||||
check('date').exists().isISO8601().toDate(),
|
||||
check('reference_no').optional({ nullable: true }).trim().escape(),
|
||||
@@ -48,9 +119,7 @@ export default class NewCashflowTransactionController extends BaseController {
|
||||
check('credit_account_id').exists().isInt().toInt(),
|
||||
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('publish').default(false).isBoolean().toBoolean(),
|
||||
];
|
||||
}
|
||||
@@ -70,13 +139,12 @@ export default class NewCashflowTransactionController extends BaseController {
|
||||
const ownerContributionDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const { cashflowTransaction } =
|
||||
await this.newCashflowTranscationService.newCashflowTransaction(
|
||||
const cashflowTransaction =
|
||||
await this.cashflowApplication.createTransaction(
|
||||
tenantId,
|
||||
ownerContributionDTO,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: cashflowTransaction.id,
|
||||
message: 'New cashflow transaction has been created successfully.',
|
||||
@@ -86,11 +154,147 @@ export default class NewCashflowTransactionController extends BaseController {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Revert the categorized cashflow transaction.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private revertCategorizedCashflowTransaction = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: cashflowTransactionId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.cashflowApplication.uncategorizeTransaction(
|
||||
tenantId,
|
||||
cashflowTransactionId
|
||||
);
|
||||
return res.status(200).send({ data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Categorize the cashflow transaction.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private categorizeCashflowTransaction = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: cashflowTransactionId } = req.params;
|
||||
const cashflowTransaction = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.cashflowApplication.categorizeTransaction(
|
||||
tenantId,
|
||||
cashflowTransactionId,
|
||||
cashflowTransaction
|
||||
);
|
||||
return res.status(200).send({
|
||||
message: 'The cashflow transaction has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Categorize the transaction as expense transaction.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private categorizesCashflowTransactionAsExpense = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: cashflowTransactionId } = req.params;
|
||||
const cashflowTransaction = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.cashflowApplication.categorizeAsExpense(
|
||||
tenantId,
|
||||
cashflowTransactionId,
|
||||
cashflowTransaction
|
||||
);
|
||||
return res.status(200).send({
|
||||
message: 'The cashflow transaction has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the uncategorized cashflow transactions.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public getUncategorizedCashflowTransaction = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: transactionId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.cashflowApplication.getUncategorizedTransaction(
|
||||
tenantId,
|
||||
transactionId
|
||||
);
|
||||
return res.status(200).send({ data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the uncategorized cashflow transactions.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public getUncategorizedCashflowTransactions = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: accountId } = req.params;
|
||||
const query = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const data = await this.cashflowApplication.getUncategorizedTransactions(
|
||||
tenantId,
|
||||
accountId,
|
||||
query
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the service errors.
|
||||
* @param error
|
||||
* @param req
|
||||
* @param res
|
||||
* @param {Request} req
|
||||
* @param {res
|
||||
* @param next
|
||||
* @returns
|
||||
*/
|
||||
@@ -140,6 +344,16 @@ export default class NewCashflowTransactionController extends BaseController {
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'UNCATEGORIZED_TRANSACTION_TYPE_INVALID') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [
|
||||
{
|
||||
type: 'UNCATEGORIZED_TRANSACTION_TYPE_INVALID',
|
||||
code: 4100,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -26,27 +26,27 @@ export default class ContactsController extends BaseController {
|
||||
[...this.autocompleteQuerySchema],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.autocompleteContacts.bind(this)),
|
||||
this.dynamicListService.handlerErrorsToResponse
|
||||
this.dynamicListService.handlerErrorsToResponse,
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
[param('id').exists().isNumeric().toInt()],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.getContact.bind(this))
|
||||
this.asyncMiddleware(this.getContact.bind(this)),
|
||||
);
|
||||
router.post(
|
||||
'/:id/inactivate',
|
||||
[param('id').exists().isNumeric().toInt()],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.inactivateContact.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
this.handlerServiceErrors,
|
||||
);
|
||||
router.post(
|
||||
'/:id/activate',
|
||||
[param('id').exists().isNumeric().toInt()],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.activateContact.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
this.handlerServiceErrors,
|
||||
);
|
||||
return router;
|
||||
}
|
||||
@@ -77,7 +77,7 @@ export default class ContactsController extends BaseController {
|
||||
try {
|
||||
const contact = await this.contactsService.getContact(
|
||||
tenantId,
|
||||
contactId
|
||||
contactId,
|
||||
);
|
||||
return res.status(200).send({
|
||||
customer: this.transfromToResponse(contact),
|
||||
@@ -105,7 +105,7 @@ export default class ContactsController extends BaseController {
|
||||
try {
|
||||
const contacts = await this.contactsService.autocompleteContacts(
|
||||
tenantId,
|
||||
filter
|
||||
filter,
|
||||
);
|
||||
return res.status(200).send({ contacts });
|
||||
} catch (error) {
|
||||
@@ -153,7 +153,6 @@ export default class ContactsController extends BaseController {
|
||||
check('email')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.normalizeEmail()
|
||||
.isEmail()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('website')
|
||||
@@ -380,7 +379,7 @@ export default class ContactsController extends BaseController {
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
next: NextFunction,
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'contact_not_found') {
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { query, oneOf } from 'express-validator';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from './BaseController';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import ExchangeRatesService from '@/services/ExchangeRates/ExchangeRatesService';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { EchangeRateErrors } from '@/lib/ExchangeRate/types';
|
||||
import { ExchangeRateApplication } from '@/services/ExchangeRates/ExchangeRateApplication';
|
||||
|
||||
@Service()
|
||||
export default class ExchangeRatesController extends BaseController {
|
||||
@Inject()
|
||||
exchangeRatesService: ExchangeRatesService;
|
||||
|
||||
@Inject()
|
||||
dynamicListService: DynamicListingService;
|
||||
private exchangeRatesApp: ExchangeRateApplication;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -22,164 +19,40 @@ export default class ExchangeRatesController extends BaseController {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
[...this.exchangeRatesListSchema],
|
||||
'/latest',
|
||||
[
|
||||
oneOf([
|
||||
query('to_currency').exists().isString().isISO4217(),
|
||||
query('from_currency').exists().isString().isISO4217(),
|
||||
]),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.exchangeRates.bind(this)),
|
||||
this.dynamicListService.handlerErrorsToResponse,
|
||||
this.handleServiceError,
|
||||
);
|
||||
router.post(
|
||||
'/',
|
||||
[...this.exchangeRateDTOSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.addExchangeRate.bind(this)),
|
||||
this.handleServiceError
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
[...this.exchangeRateEditDTOSchema, ...this.exchangeRateIdSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editExchangeRate.bind(this)),
|
||||
this.handleServiceError
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
[...this.exchangeRateIdSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteExchangeRate.bind(this)),
|
||||
asyncMiddleware(this.latestExchangeRate.bind(this)),
|
||||
this.handleServiceError
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
get exchangeRatesListSchema() {
|
||||
return [
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
];
|
||||
}
|
||||
|
||||
get exchangeRateDTOSchema() {
|
||||
return [
|
||||
check('exchange_rate').exists().isNumeric().toFloat(),
|
||||
check('currency_code').exists().trim().escape(),
|
||||
check('date').exists().isISO8601(),
|
||||
];
|
||||
}
|
||||
|
||||
get exchangeRateEditDTOSchema() {
|
||||
return [check('exchange_rate').exists().isNumeric().toFloat()];
|
||||
}
|
||||
|
||||
get exchangeRateIdSchema() {
|
||||
return [param('id').isNumeric().toInt()];
|
||||
}
|
||||
|
||||
get exchangeRatesIdsSchema() {
|
||||
return [
|
||||
query('ids').isArray({ min: 2 }),
|
||||
query('ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve exchange rates.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async exchangeRates(req: Request, res: Response, next: NextFunction) {
|
||||
private async latestExchangeRate(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
const filter = {
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
filterRoles: [],
|
||||
columnSortBy: 'created_at',
|
||||
sortOrder: 'asc',
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
if (filter.stringifiedFilterRoles) {
|
||||
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
||||
}
|
||||
try {
|
||||
const exchangeRates = await this.exchangeRatesService.listExchangeRates(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({ exchange_rates: exchangeRates });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new exchange rate on the given date.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async addExchangeRate(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const exchangeRateDTO = this.matchedBodyData(req);
|
||||
const exchangeRateQuery = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const exchangeRate = await this.exchangeRatesService.newExchangeRate(
|
||||
const exchangeRate = await this.exchangeRatesApp.latest(
|
||||
tenantId,
|
||||
exchangeRateDTO
|
||||
exchangeRateQuery
|
||||
);
|
||||
return res.status(200).send({ id: exchangeRate.id });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the given exchange rate.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async editExchangeRate(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: exchangeRateId } = req.params;
|
||||
const exchangeRateDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const exchangeRate = await this.exchangeRatesService.editExchangeRate(
|
||||
tenantId,
|
||||
exchangeRateId,
|
||||
exchangeRateDTO
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: exchangeRateId,
|
||||
message: 'The exchange rate has been edited successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the given exchange rate from the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async deleteExchangeRate(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: exchangeRateId } = req.params;
|
||||
|
||||
try {
|
||||
await this.exchangeRatesService.deleteExchangeRate(
|
||||
tenantId,
|
||||
exchangeRateId
|
||||
);
|
||||
return res.status(200).send({ id: exchangeRateId });
|
||||
return res.status(200).send(exchangeRate);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -192,26 +65,56 @@ export default class ExchangeRatesController extends BaseController {
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
handleServiceError(
|
||||
private handleServiceError(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'EXCHANGE_RATE_NOT_FOUND') {
|
||||
return res.status(404).send({
|
||||
errors: [{ type: 'EXCHANGE.RATE.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'NOT_FOUND_EXCHANGE_RATES') {
|
||||
if (EchangeRateErrors.EX_RATE_INVALID_BASE_CURRENCY === error.errorType) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'EXCHANGE.RATES.IS.NOT.FOUND', code: 100 }],
|
||||
errors: [
|
||||
{
|
||||
type: EchangeRateErrors.EX_RATE_INVALID_BASE_CURRENCY,
|
||||
code: 100,
|
||||
message: 'The given base currency is invalid.',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'EXCHANGE_RATE_PERIOD_EXISTS') {
|
||||
} else if (
|
||||
EchangeRateErrors.EX_RATE_SERVICE_NOT_ALLOWED === error.errorType
|
||||
) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'EXCHANGE.RATE.PERIOD.EXISTS', code: 300 }],
|
||||
errors: [
|
||||
{
|
||||
type: EchangeRateErrors.EX_RATE_SERVICE_NOT_ALLOWED,
|
||||
code: 200,
|
||||
message: 'The service is not allowed',
|
||||
},
|
||||
],
|
||||
});
|
||||
} else if (
|
||||
EchangeRateErrors.EX_RATE_SERVICE_API_KEY_REQUIRED === error.errorType
|
||||
) {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{
|
||||
type: EchangeRateErrors.EX_RATE_SERVICE_API_KEY_REQUIRED,
|
||||
code: 300,
|
||||
message: 'The API key is required',
|
||||
},
|
||||
],
|
||||
});
|
||||
} else if (EchangeRateErrors.EX_RATE_LIMIT_EXCEEDED === error.errorType) {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{
|
||||
type: EchangeRateErrors.EX_RATE_LIMIT_EXCEEDED,
|
||||
code: 400,
|
||||
message: 'The API rate limit has been exceeded',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ export default class APAgingSummaryReportController extends BaseFinancialReportC
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF
|
||||
]);
|
||||
// Retrieves the json table format.
|
||||
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||
@@ -98,6 +99,15 @@ export default class APAgingSummaryReportController extends BaseFinancialReportC
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
return res.send(buffer);
|
||||
// Retrieves the pdf format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.APAgingSummaryApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
return res.send(pdfContent);
|
||||
// Retrieves the json format.
|
||||
} else {
|
||||
const sheet = await this.APAgingSummaryApp.sheet(tenantId, filter);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
@Service()
|
||||
export default class ARAgingSummaryReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
ARAgingSummaryApp: ARAgingSummaryApplication;
|
||||
private ARAgingSummaryApp: ARAgingSummaryApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -69,6 +69,7 @@ export default class ARAgingSummaryReportController extends BaseFinancialReportC
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF
|
||||
]);
|
||||
// Retrieves the xlsx format.
|
||||
if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||
@@ -96,6 +97,15 @@ export default class ARAgingSummaryReportController extends BaseFinancialReportC
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(buffer);
|
||||
// Retrieves the pdf format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.ARAgingSummaryApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
return res.send(pdfContent);
|
||||
// Retrieves the json format.
|
||||
} else {
|
||||
const sheet = await this.ARAgingSummaryApp.sheet(tenantId, filter);
|
||||
|
||||
@@ -101,6 +101,7 @@ export default class BalanceSheetStatementController extends BaseFinancialReport
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves the json table format.
|
||||
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE == acceptType) {
|
||||
@@ -128,6 +129,15 @@ export default class BalanceSheetStatementController extends BaseFinancialReport
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
return res.send(buffer);
|
||||
// Retrieves the pdf format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.balanceSheetApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
} else {
|
||||
const sheet = await this.balanceSheetApp.sheet(tenantId, filter);
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ export default class CashFlowController extends BaseFinancialReportController {
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF
|
||||
]);
|
||||
// Retrieves the json table format.
|
||||
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||
@@ -106,6 +107,15 @@ export default class CashFlowController extends BaseFinancialReportController {
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
return res.send(buffer);
|
||||
// Retrieves the pdf format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.cashflowSheetApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
return res.send(pdfContent);
|
||||
// Retrieves the json format.
|
||||
} else {
|
||||
const cashflow = await this.cashflowSheetApp.sheet(tenantId, filter);
|
||||
|
||||
@@ -75,6 +75,7 @@ export default class CustomerBalanceSummaryReportController extends BaseFinancia
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
|
||||
// Retrieves the xlsx format.
|
||||
@@ -109,6 +110,19 @@ export default class CustomerBalanceSummaryReportController extends BaseFinancia
|
||||
filter
|
||||
);
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the pdf format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const buffer = await this.customerBalanceSummaryApp.pdf(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': buffer.length,
|
||||
});
|
||||
return res.send(buffer);
|
||||
// Retrieves the json format.
|
||||
} else {
|
||||
const sheet = await this.customerBalanceSummaryApp.sheet(
|
||||
tenantId,
|
||||
|
||||
@@ -2,20 +2,21 @@ import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { query, ValidationChain } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import GeneralLedgerService from '@/services/FinancialStatements/GeneralLedger/GeneralLedgerService';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
import { GeneralLedgerApplication } from '@/services/FinancialStatements/GeneralLedger/GeneralLedgerApplication';
|
||||
|
||||
@Service()
|
||||
export default class GeneralLedgerReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
generalLedgetService: GeneralLedgerService;
|
||||
private generalLedgerApplication: GeneralLedgerApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
@@ -31,7 +32,7 @@ export default class GeneralLedgerReportController extends BaseFinancialReportCo
|
||||
/**
|
||||
* Validation schema.
|
||||
*/
|
||||
get validationSchema(): ValidationChain[] {
|
||||
private get validationSchema(): ValidationChain[] {
|
||||
return [
|
||||
query('from_date').optional().isISO8601(),
|
||||
query('to_date').optional().isISO8601(),
|
||||
@@ -60,21 +61,56 @@ export default class GeneralLedgerReportController extends BaseFinancialReportCo
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
*/
|
||||
async generalLedger(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
private async generalLedger(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
const accept = this.accepts(req);
|
||||
|
||||
try {
|
||||
const { data, query, meta } =
|
||||
await this.generalLedgetService.generalLedger(tenantId, filter);
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves the table format.
|
||||
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||
const table = await this.generalLedgerApplication.table(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
meta: this.transfromToResponse(meta),
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the csv format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) {
|
||||
const buffer = await this.generalLedgerApplication.csv(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(buffer);
|
||||
// Retrieves the xlsx format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||
const buffer = await this.generalLedgerApplication.xlsx(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
return res.send(buffer);
|
||||
// Retrieves the pdf format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.generalLedgerApplication.pdf(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
return res.send(pdfContent);
|
||||
// Retrieves the json format.
|
||||
} else {
|
||||
const sheet = await this.generalLedgerApplication.sheet(tenantId, filter);
|
||||
return res.status(200).send(sheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ export default class InventoryDetailsController extends BaseController {
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves the csv format.
|
||||
if (acceptType === ACCEPT_TYPE.APPLICATION_CSV) {
|
||||
@@ -127,6 +128,15 @@ export default class InventoryDetailsController extends BaseController {
|
||||
filter
|
||||
);
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the pdf format.
|
||||
} else if (acceptType === ACCEPT_TYPE.APPLICATION_PDF) {
|
||||
const buffer = await this.inventoryItemDetailsApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': buffer.length,
|
||||
});
|
||||
return res.send(buffer);
|
||||
} else {
|
||||
const sheet = await this.inventoryItemDetailsApp.sheet(
|
||||
tenantId,
|
||||
|
||||
@@ -3,14 +3,15 @@ import { query, ValidationChain } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import InventoryValuationService from '@/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetService';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { InventoryValuationSheetApplication } from '@/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetApplication';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
|
||||
@Service()
|
||||
export default class InventoryValuationReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
inventoryValuationService: InventoryValuationService;
|
||||
private inventoryValuationApp: InventoryValuationSheetApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -71,19 +72,55 @@ export default class InventoryValuationReportController extends BaseFinancialRep
|
||||
const { tenantId } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const { data, query, meta } =
|
||||
await this.inventoryValuationService.inventoryValuationSheet(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({
|
||||
meta: this.transfromToResponse(meta),
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
const accept = this.accepts(req);
|
||||
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
|
||||
// Retrieves the json table format.
|
||||
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||
const table = await this.inventoryValuationApp.table(tenantId, filter);
|
||||
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the csv format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_CSV == acceptType) {
|
||||
const buffer = await this.inventoryValuationApp.csv(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(buffer);
|
||||
// Retrieves the xslx buffer format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||
const buffer = await this.inventoryValuationApp.xlsx(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
return res.send(buffer);
|
||||
// Retrieves the pdf format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.inventoryValuationApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
return res.status(200).send(pdfContent);
|
||||
// Retrieves the json format.
|
||||
} else {
|
||||
const { data, query, meta } = await this.inventoryValuationApp.sheet(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({ meta, data, query });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,15 @@ import { Request, Response, Router, NextFunction } from 'express';
|
||||
import { castArray } from 'lodash';
|
||||
import { query, oneOf } from 'express-validator';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import JournalSheetService from '@/services/FinancialStatements/JournalSheet/JournalSheetService';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
import { JournalSheetApplication } from '@/services/FinancialStatements/JournalSheet/JournalSheetApplication';
|
||||
|
||||
@Service()
|
||||
export default class JournalSheetController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
journalService: JournalSheetService;
|
||||
private journalSheetApp: JournalSheetApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -57,28 +58,58 @@ export default class JournalSheetController extends BaseFinancialReportControlle
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
*/
|
||||
async journal(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
private async journal(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
let filter = this.matchedQueryData(req);
|
||||
|
||||
filter = {
|
||||
...filter,
|
||||
accountsIds: castArray(filter.accountsIds),
|
||||
};
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
|
||||
try {
|
||||
const { data, query, meta } = await this.journalService.journalSheet(
|
||||
tenantId,
|
||||
filter
|
||||
// Retrieves the json table format.
|
||||
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||
const table = await this.journalSheetApp.table(tenantId, filter);
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the csv format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) {
|
||||
const buffer = await this.journalSheetApp.csv(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(buffer);
|
||||
// Retrieves the xlsx format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||
const buffer = await this.journalSheetApp.xlsx(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
return res.send(buffer);
|
||||
// Retrieves the json format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.journalSheetApp.pdf(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
meta: this.transfromToResponse(meta),
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
res.send(pdfContent);
|
||||
} else {
|
||||
const sheet = await this.journalSheetApp.sheet(tenantId, filter);
|
||||
|
||||
return res.status(200).send(sheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ export default class ProfitLossSheetController extends BaseFinancialReportContro
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
try {
|
||||
// Retrieves the csv format.
|
||||
@@ -125,6 +126,14 @@ export default class ProfitLossSheetController extends BaseFinancialReportContro
|
||||
);
|
||||
return res.send(sheet);
|
||||
// Retrieves the json format.
|
||||
} else if (acceptType === ACCEPT_TYPE.APPLICATION_PDF) {
|
||||
const pdfContent = await this.profitLossSheetApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
return res.send(pdfContent);
|
||||
} else {
|
||||
const sheet = await this.profitLossSheetApp.sheet(tenantId, filter);
|
||||
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { query, ValidationChain } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import PurchasesByItemsService from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsService';
|
||||
import { PurchasesByItemsService } from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsService';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
import { PurcahsesByItemsApplication } from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsApplication';
|
||||
|
||||
@Service()
|
||||
export default class PurchasesByItemReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
purchasesByItemsService: PurchasesByItemsService;
|
||||
private purchasesByItemsApp: PurcahsesByItemsApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -63,20 +64,56 @@ export default class PurchasesByItemReportController extends BaseFinancialReport
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
*/
|
||||
async purchasesByItems(req: Request, res: Response, next: NextFunction) {
|
||||
public async purchasesByItems(req: Request, res: Response) {
|
||||
const { tenantId } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const { data, query, meta } =
|
||||
await this.purchasesByItemsService.purchasesByItems(tenantId, filter);
|
||||
return res.status(200).send({
|
||||
meta: this.transfromToResponse(meta),
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
const accept = this.accepts(req);
|
||||
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// JSON table response format.
|
||||
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||
const table = await this.purchasesByItemsApp.table(tenantId, filter);
|
||||
|
||||
return res.status(200).send(table);
|
||||
// CSV response format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) {
|
||||
const buffer = await this.purchasesByItemsApp.csv(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(buffer);
|
||||
// Xlsx response format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||
const buffer = await this.purchasesByItemsApp.xlsx(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
return res.send(buffer);
|
||||
// PDF response format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.purchasesByItemsApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
return res.send(pdfContent);
|
||||
// Json response format.
|
||||
} else {
|
||||
const sheet = await this.purchasesByItemsApp.sheet(tenantId, filter);
|
||||
|
||||
return res.status(200).send(sheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { query, ValidationChain } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import { query, ValidationChain, ValidationSchema } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import SalesByItemsReportService from '@/services/FinancialStatements/SalesByItems/SalesByItemsService';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
import { SalesByItemsApplication } from '@/services/FinancialStatements/SalesByItems/SalesByItemsApplication';
|
||||
|
||||
@Service()
|
||||
export default class SalesByItemsReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
salesByItemsService: SalesByItemsReportService;
|
||||
private salesByItemsApp: SalesByItemsApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -24,13 +24,14 @@ export default class SalesByItemsReportController extends BaseFinancialReportCon
|
||||
CheckPolicies(ReportsAction.READ_SALES_BY_ITEMS, AbilitySubject.Report),
|
||||
this.validationSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.purchasesByItems.bind(this))
|
||||
asyncMiddleware(this.salesByItems.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
private get validationSchema(): ValidationChain[] {
|
||||
return [
|
||||
@@ -60,26 +61,53 @@ export default class SalesByItemsReportController extends BaseFinancialReportCon
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
*/
|
||||
private async purchasesByItems(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
private async salesByItems(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
const accept = this.accepts(req);
|
||||
|
||||
try {
|
||||
const { data, query, meta } = await this.salesByItemsService.salesByItems(
|
||||
tenantId,
|
||||
filter
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves the csv format.
|
||||
if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) {
|
||||
const buffer = await this.salesByItemsApp.csv(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(buffer);
|
||||
// Retrieves the json table format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||
const table = await this.salesByItemsApp.table(tenantId, filter);
|
||||
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the xlsx format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||
const buffer = this.salesByItemsApp.xlsx(tenantId, filter);
|
||||
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
);
|
||||
return res.status(200).send({
|
||||
meta: this.transfromToResponse(meta),
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
return res.send(buffer);
|
||||
// Retrieves the json format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.salesByItemsApp.pdf(tenantId, filter);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
return res.send(pdfContent);
|
||||
} else {
|
||||
const sheet = await this.salesByItemsApp.sheet(tenantId, filter);
|
||||
return res.status(200).send(sheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ export default class SalesTaxLiabilitySummary extends BaseFinancialReportControl
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
|
||||
// Retrieves the json table format.
|
||||
@@ -97,6 +98,16 @@ export default class SalesTaxLiabilitySummary extends BaseFinancialReportControl
|
||||
|
||||
return res.send(buffer);
|
||||
// Retrieves the json format.
|
||||
} else if (acceptType === ACCEPT_TYPE.APPLICATION_PDF) {
|
||||
const pdfContent = await this.salesTaxLiabilitySummaryApp.pdf(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
return res.status(200).send(pdfContent);
|
||||
} else {
|
||||
const sheet = await this.salesTaxLiabilitySummaryApp.sheet(
|
||||
tenantId,
|
||||
|
||||
@@ -70,6 +70,7 @@ export default class TransactionsByCustomersReportController extends BaseFinanci
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
try {
|
||||
// Retrieves the json table format.
|
||||
@@ -103,6 +104,16 @@ export default class TransactionsByCustomersReportController extends BaseFinanci
|
||||
);
|
||||
return res.send(buffer);
|
||||
// Retrieve the json format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.transactionsByCustomersApp.pdf(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
return res.send(pdfContent);
|
||||
} else {
|
||||
const sheet = await this.transactionsByCustomersApp.sheet(
|
||||
tenantId,
|
||||
|
||||
@@ -71,6 +71,7 @@ export default class TransactionsByVendorsReportController extends BaseFinancial
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
|
||||
// Retrieves the xlsx format.
|
||||
@@ -101,6 +102,17 @@ export default class TransactionsByVendorsReportController extends BaseFinancial
|
||||
filter
|
||||
);
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the pdf format.
|
||||
} else if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.transactionsByVendorsApp.pdf(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
return res.send(pdfContent);
|
||||
// Retrieves the json format.
|
||||
} else {
|
||||
const sheet = await this.transactionsByVendorsApp.sheet(
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Request, Response, Router, NextFunction } from 'express';
|
||||
import { query, ValidationChain } from 'express-validator';
|
||||
import { castArray } from 'lodash';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import TrialBalanceSheetService from '@/services/FinancialStatements/TrialBalanceSheet/TrialBalanceSheetInjectable';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
@@ -81,6 +80,7 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves in json table format.
|
||||
if (acceptType === ACCEPT_TYPE.APPLICATION_JSON_TABLE) {
|
||||
@@ -109,6 +109,17 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(buffer);
|
||||
// Retrieves in pdf format.
|
||||
} else if (acceptType === ACCEPT_TYPE.APPLICATION_PDF) {
|
||||
const pdfContent = await this.trialBalanceSheetApp.pdf(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Retrieves in json format.
|
||||
} else {
|
||||
const { data, query, meta } = await this.trialBalanceSheetApp.sheet(
|
||||
|
||||
@@ -72,6 +72,7 @@ export default class VendorBalanceSummaryReportController extends BaseFinancialR
|
||||
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||
ACCEPT_TYPE.APPLICATION_CSV,
|
||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
|
||||
// Retrieves the csv format.
|
||||
@@ -100,6 +101,17 @@ export default class VendorBalanceSummaryReportController extends BaseFinancialR
|
||||
filter
|
||||
);
|
||||
return res.status(200).send(table);
|
||||
// Retrieves the pdf format.
|
||||
} else if (acceptType === ACCEPT_TYPE.APPLICATION_PDF) {
|
||||
const pdfContent = await this.vendorBalanceSummaryApp.pdf(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
return res.send(pdfContent);
|
||||
// Retrieves the json format.
|
||||
} else {
|
||||
const sheet = await this.vendorBalanceSummaryApp.sheet(
|
||||
|
||||
@@ -303,7 +303,7 @@ export default class BillsController extends BaseController {
|
||||
try {
|
||||
const bill = await this.billsApplication.getBill(tenantId, billId);
|
||||
|
||||
return res.status(200).send(this.transfromToResponse({ bill }));
|
||||
return res.status(200).send({ bill });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -348,14 +348,11 @@ export default class BillsController extends BaseController {
|
||||
};
|
||||
|
||||
try {
|
||||
const { bills, pagination, filterMeta } =
|
||||
await this.billsApplication.getBills(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
bills: this.transfromToResponse(bills),
|
||||
pagination: this.transfromToResponse(pagination),
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
});
|
||||
const billsWithPagination = await this.billsApplication.getBills(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send(billsWithPagination);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -563,6 +560,16 @@ export default class BillsController extends BaseController {
|
||||
errors: [{ type: 'ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND', code: 1900 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'BILL_AMOUNT_SMALLER_THAN_PAID_AMOUNT') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [
|
||||
{
|
||||
type: 'BILL_AMOUNT_SMALLER_THAN_PAID_AMOUNT',
|
||||
code: 2000,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -158,15 +158,11 @@ export default class BillsPayments extends BaseController {
|
||||
const { tenantId } = req;
|
||||
const { vendorId } = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const entries = await this.billPaymentsPages.getNewPageEntries(
|
||||
tenantId,
|
||||
vendorId
|
||||
);
|
||||
return res.status(200).send({
|
||||
entries: this.transfromToResponse(entries),
|
||||
});
|
||||
} catch (error) {}
|
||||
const entries = await this.billPaymentsPages.getNewPageEntries(
|
||||
tenantId,
|
||||
vendorId
|
||||
);
|
||||
return res.status(200).send({ entries });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,16 +179,12 @@ export default class BillsPayments extends BaseController {
|
||||
const { id: paymentReceiveId } = req.params;
|
||||
|
||||
try {
|
||||
const { billPayment, entries } =
|
||||
const billPaymentsWithEditEntries =
|
||||
await this.billPaymentsPages.getBillPaymentEditPage(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
bill_payment: this.transfromToResponse(billPayment),
|
||||
entries: this.transfromToResponse(entries),
|
||||
});
|
||||
return res.status(200).send(billPaymentsWithEditEntries);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -304,9 +296,7 @@ export default class BillsPayments extends BaseController {
|
||||
tenantId,
|
||||
billPaymentId
|
||||
);
|
||||
return res.status(200).send({
|
||||
bill_payment: this.transfromToResponse(billPayment),
|
||||
});
|
||||
return res.status(200).send({ billPayment });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -359,17 +349,12 @@ export default class BillsPayments extends BaseController {
|
||||
};
|
||||
|
||||
try {
|
||||
const { billPayments, pagination, filterMeta } =
|
||||
const billPaymentsWithPagination =
|
||||
await this.billPaymentsApplication.getBillPayments(
|
||||
tenantId,
|
||||
billPaymentsFilter
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
bill_payments: this.transfromToResponse(billPayments),
|
||||
pagination: this.transfromToResponse(pagination),
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
});
|
||||
return res.status(200).send(billPaymentsWithPagination);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -320,20 +320,19 @@ export default class VendorCreditController extends BaseController {
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { id: billId } = req.params;
|
||||
const { id: vendorCreditId } = req.params;
|
||||
const { tenantId, user } = req;
|
||||
const vendorCreditEditDTO: IVendorCreditEditDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.editVendorCreditService.editVendorCredit(
|
||||
tenantId,
|
||||
billId,
|
||||
vendorCreditEditDTO,
|
||||
user
|
||||
vendorCreditId,
|
||||
vendorCreditEditDTO
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: billId,
|
||||
id: vendorCreditId,
|
||||
message: 'The vendor credit has been edited successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import GetCreditNoteAssociatedInvoicesToApply from '@/services/CreditNotes/GetCr
|
||||
import GetCreditNoteAssociatedAppliedInvoices from '@/services/CreditNotes/GetCreditNoteAssociatedAppliedInvoices';
|
||||
import GetRefundCreditTransaction from '@/services/CreditNotes/GetRefundCreditNoteTransaction';
|
||||
import GetCreditNotePdf from '../../../services/CreditNotes/GetCreditNotePdf';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
/**
|
||||
* Credit notes controller.
|
||||
* @service
|
||||
@@ -293,7 +294,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
return [
|
||||
check('from_account_id').exists().isNumeric().toInt(),
|
||||
check('description').optional(),
|
||||
|
||||
|
||||
check('amount').exists().isNumeric().toFloat(),
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
|
||||
@@ -438,7 +439,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the payment receive details.
|
||||
* Retrieve the credit note details.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
@@ -451,38 +452,28 @@ export default class PaymentReceivesController extends BaseController {
|
||||
const { tenantId } = req;
|
||||
const { id: creditNoteId } = req.params;
|
||||
|
||||
try {
|
||||
const accept = this.accepts(req);
|
||||
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.creditNotePdf.getCreditNotePdf(
|
||||
tenantId,
|
||||
creditNoteId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
} else {
|
||||
const creditNote = await this.getCreditNoteService.getCreditNote(
|
||||
tenantId,
|
||||
creditNoteId
|
||||
);
|
||||
const ACCEPT_TYPE = {
|
||||
APPLICATION_PDF: 'application/pdf',
|
||||
APPLICATION_JSON: 'application/json',
|
||||
};
|
||||
// Response formatter.
|
||||
res.format({
|
||||
// Json content type.
|
||||
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
|
||||
return res
|
||||
.status(200)
|
||||
.send({ credit_note: this.transfromToResponse(creditNote) });
|
||||
},
|
||||
// Pdf content type.
|
||||
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
|
||||
const pdfContent = await this.creditNotePdf.getCreditNotePdf(
|
||||
tenantId,
|
||||
creditNote
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
return res.status(200).send({ creditNote });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, param, query, ValidationChain } from 'express-validator';
|
||||
import { body, check, param, query, ValidationChain } from 'express-validator';
|
||||
import {
|
||||
AbilitySubject,
|
||||
IPaymentReceiveDTO,
|
||||
PaymentReceiveAction,
|
||||
PaymentReceiveMailOptsDTO,
|
||||
} from '@/interfaces';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
@@ -13,6 +14,7 @@ import DynamicListingService from '@/services/DynamicListing/DynamicListService'
|
||||
import { PaymentReceivesApplication } from '@/services/Sales/PaymentReceives/PaymentReceivesApplication';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
|
||||
@Service()
|
||||
export default class PaymentReceivesController extends BaseController {
|
||||
@@ -117,6 +119,25 @@ export default class PaymentReceivesController extends BaseController {
|
||||
asyncMiddleware(this.deletePaymentReceive.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/mail',
|
||||
[
|
||||
...this.paymentReceiveValidation,
|
||||
body('subject').isString().optional(),
|
||||
body('from').isString().optional(),
|
||||
body('to').isString().optional(),
|
||||
body('body').isString().optional(),
|
||||
body('attach_invoice').optional().isBoolean().toBoolean(),
|
||||
],
|
||||
this.sendPaymentReceiveByMail.bind(this),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id/mail',
|
||||
[...this.paymentReceiveValidation],
|
||||
asyncMiddleware(this.getPaymentDefaultMail.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -328,17 +349,12 @@ export default class PaymentReceivesController extends BaseController {
|
||||
};
|
||||
|
||||
try {
|
||||
const { paymentReceives, pagination, filterMeta } =
|
||||
const paymentsReceivedWithPagination =
|
||||
await this.paymentReceiveApplication.getPaymentReceives(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
payment_receives: this.transfromToResponse(paymentReceives),
|
||||
pagination: this.transfromToResponse(pagination),
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
});
|
||||
return res.status(200).send(paymentsReceivedWithPagination);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -415,38 +431,34 @@ export default class PaymentReceivesController extends BaseController {
|
||||
const { tenantId } = req;
|
||||
const { id: paymentReceiveId } = req.params;
|
||||
|
||||
try {
|
||||
const accept = this.accepts(req);
|
||||
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Response in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent =
|
||||
await this.paymentReceiveApplication.getPaymentReceivePdf(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Response in json format.
|
||||
} else {
|
||||
const paymentReceive =
|
||||
await this.paymentReceiveApplication.getPaymentReceive(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
);
|
||||
|
||||
const ACCEPT_TYPE = {
|
||||
APPLICATION_PDF: 'application/pdf',
|
||||
APPLICATION_JSON: 'application/json',
|
||||
};
|
||||
res.format({
|
||||
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
|
||||
return res.status(200).send({
|
||||
payment_receive: this.transfromToResponse(paymentReceive),
|
||||
});
|
||||
},
|
||||
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
|
||||
const pdfContent =
|
||||
await this.paymentReceiveApplication.getPaymentReceivePdf(
|
||||
tenantId,
|
||||
paymentReceive
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
},
|
||||
return res.status(200).send({
|
||||
payment_receive: paymentReceive,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,7 +492,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Retrieves the sms details of the given payment receive.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
@@ -508,13 +520,73 @@ export default class PaymentReceivesController extends BaseController {
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param error
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
* Sends mail invoice of the given sale invoice.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
handleServiceErrors(
|
||||
public sendPaymentReceiveByMail = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: paymentReceiveId } = req.params;
|
||||
const paymentMailDTO: PaymentReceiveMailOptsDTO = this.matchedBodyData(
|
||||
req,
|
||||
{
|
||||
includeOptionals: false,
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
await this.paymentReceiveApplication.notifyPaymentByMail(
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
paymentMailDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
code: 200,
|
||||
message: 'The payment notification has been sent successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the default mail options of the given payment transaction.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public getPaymentDefaultMail = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: paymentReceiveId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.paymentReceiveApplication.getPaymentMailOptions(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
);
|
||||
return res.status(200).send({ data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private handleServiceErrors(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { body, check, param, query } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
AbilitySubject,
|
||||
ISaleEstimateDTO,
|
||||
SaleEstimateAction,
|
||||
SaleEstimateMailOptionsDTO,
|
||||
} from '@/interfaces';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
@@ -121,6 +122,27 @@ export default class SalesEstimatesController extends BaseController {
|
||||
this.handleServiceErrors,
|
||||
this.dynamicListService.handlerErrorsToResponse
|
||||
);
|
||||
router.post(
|
||||
'/:id/mail',
|
||||
[
|
||||
...this.validateSpecificEstimateSchema,
|
||||
body('subject').isString().optional(),
|
||||
body('from').isString().optional(),
|
||||
body('to').isString().optional(),
|
||||
body('body').isString().optional(),
|
||||
body('attach_invoice').optional().isBoolean().toBoolean(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.sendSaleEstimateMail.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id/mail',
|
||||
[...this.validateSpecificEstimateSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getSaleEstimateMail.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -312,7 +334,6 @@ export default class SalesEstimatesController extends BaseController {
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: estimateId,
|
||||
message: 'The sale estimate has been approved successfully.',
|
||||
@@ -341,7 +362,6 @@ export default class SalesEstimatesController extends BaseController {
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: estimateId,
|
||||
message: 'The sale estimate has been rejected successfully.',
|
||||
@@ -361,33 +381,30 @@ export default class SalesEstimatesController extends BaseController {
|
||||
const { id: estimateId } = req.params;
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
const accept = this.accepts(req);
|
||||
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves estimate in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
|
||||
const pdfContent = await this.saleEstimatesApplication.getSaleEstimatePdf(
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Retrieves estimates in json format.
|
||||
} else {
|
||||
const estimate = await this.saleEstimatesApplication.getSaleEstimate(
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
// Response formatter.
|
||||
res.format({
|
||||
// JSON content type.
|
||||
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
|
||||
return res.status(200).send(this.transfromToResponse({ estimate }));
|
||||
},
|
||||
// PDF content type.
|
||||
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
|
||||
const pdfContent =
|
||||
await this.saleEstimatesApplication.getSaleEstimatePdf(
|
||||
tenantId,
|
||||
estimate
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
return res.status(200).send({ estimate });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,22 +422,11 @@ export default class SalesEstimatesController extends BaseController {
|
||||
pageSize: 12,
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
try {
|
||||
const { salesEstimates, pagination, filterMeta } =
|
||||
const salesEstimatesWithPagination =
|
||||
await this.saleEstimatesApplication.getSaleEstimates(tenantId, filter);
|
||||
|
||||
res.format({
|
||||
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
|
||||
return res.status(200).send(
|
||||
this.transfromToResponse({
|
||||
salesEstimates,
|
||||
pagination,
|
||||
filterMeta,
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
return res.status(200).send(salesEstimatesWithPagination);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -478,6 +484,65 @@ export default class SalesEstimatesController extends BaseController {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send the sale estimate mail.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private sendSaleEstimateMail = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: invoiceId } = req.params;
|
||||
const saleEstimateDTO: SaleEstimateMailOptionsDTO = this.matchedBodyData(
|
||||
req,
|
||||
{
|
||||
includeOptionals: false,
|
||||
}
|
||||
);
|
||||
try {
|
||||
await this.saleEstimatesApplication.sendSaleEstimateMail(
|
||||
tenantId,
|
||||
invoiceId,
|
||||
saleEstimateDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
code: 200,
|
||||
message: 'The sale estimate mail has been sent successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the default mail options of the given sale estimate.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private getSaleEstimateMail = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: invoiceId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.saleEstimatesApplication.getSaleEstimateMail(
|
||||
tenantId,
|
||||
invoiceId
|
||||
);
|
||||
return res.status(200).send({ data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param {Error} error
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { body, check, param, query } from 'express-validator';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import BaseController from '../BaseController';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
@@ -10,14 +10,12 @@ import {
|
||||
ISaleInvoiceCreateDTO,
|
||||
SaleInvoiceAction,
|
||||
AbilitySubject,
|
||||
SendInvoiceMailDTO,
|
||||
} from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { SaleInvoiceApplication } from '@/services/Sales/Invoices/SaleInvoicesApplication';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
|
||||
const ACCEPT_TYPE = {
|
||||
APPLICATION_PDF: 'application/pdf',
|
||||
APPLICATION_JSON: 'application/json',
|
||||
};
|
||||
@Service()
|
||||
export default class SaleInvoicesController extends BaseController {
|
||||
@Inject()
|
||||
@@ -145,6 +143,48 @@ export default class SaleInvoicesController extends BaseController {
|
||||
this.handleServiceErrors,
|
||||
this.dynamicListService.handlerErrorsToResponse
|
||||
);
|
||||
router.get(
|
||||
'/:id/mail-reminder',
|
||||
this.specificSaleInvoiceValidation,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getSaleInvoiceMailReminder.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/mail-reminder',
|
||||
[
|
||||
...this.specificSaleInvoiceValidation,
|
||||
body('subject').isString().optional(),
|
||||
body('from').isString().optional(),
|
||||
body('to').isString().optional(),
|
||||
body('body').isString().optional(),
|
||||
body('attach_invoice').optional().isBoolean().toBoolean(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.sendSaleInvoiceMailReminder.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/mail',
|
||||
[
|
||||
...this.specificSaleInvoiceValidation,
|
||||
body('subject').isString().optional(),
|
||||
body('from').isString().optional(),
|
||||
body('to').isString().optional(),
|
||||
body('body').isString().optional(),
|
||||
body('attach_invoice').optional().isBoolean().toBoolean(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.sendSaleInvoiceMail.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id/mail',
|
||||
[...this.specificSaleInvoiceValidation],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getSaleInvoiceMail.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -360,7 +400,6 @@ export default class SaleInvoicesController extends BaseController {
|
||||
saleInvoiceId,
|
||||
user
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: saleInvoiceId,
|
||||
message: 'The sale invoice has been deleted successfully.',
|
||||
@@ -375,43 +414,35 @@ export default class SaleInvoicesController extends BaseController {
|
||||
* @param {Request} req - Request object.
|
||||
* @param {Response} res - Response object.
|
||||
*/
|
||||
private async getSaleInvoice(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
private async getSaleInvoice(req: Request, res: Response) {
|
||||
const { id: saleInvoiceId } = req.params;
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
const accept = this.accepts(req);
|
||||
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves invoice in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
|
||||
const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Retrieves invoice in json format.
|
||||
} else {
|
||||
const saleInvoice = await this.saleInvoiceApplication.getSaleInvoice(
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
user
|
||||
);
|
||||
// Response formatter.
|
||||
res.format({
|
||||
// JSON content type.
|
||||
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
|
||||
return res
|
||||
.status(200)
|
||||
.send(this.transfromToResponse({ saleInvoice }));
|
||||
},
|
||||
// PDF content type.
|
||||
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
|
||||
const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf(
|
||||
tenantId,
|
||||
saleInvoice
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
return res.status(200).send({ saleInvoice });
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -434,14 +465,10 @@ export default class SaleInvoicesController extends BaseController {
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
try {
|
||||
const { salesInvoices, filterMeta, pagination } =
|
||||
const salesInvoicesWithPagination =
|
||||
await this.saleInvoiceApplication.getSaleInvoices(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
sales_invoices: this.transfromToResponse(salesInvoices),
|
||||
pagination: this.transfromToResponse(pagination),
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
});
|
||||
return res.status(200).send(salesInvoicesWithPagination);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -468,9 +495,7 @@ export default class SaleInvoicesController extends BaseController {
|
||||
tenantId,
|
||||
customerId
|
||||
);
|
||||
return res.status(200).send({
|
||||
sales_invoices: this.transfromToResponse(salesInvoices),
|
||||
});
|
||||
return res.status(200).send({ salesInvoices });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -498,7 +523,6 @@ export default class SaleInvoicesController extends BaseController {
|
||||
invoiceId,
|
||||
writeoffDTO
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: saleInvoice.id,
|
||||
message: 'The given sale invoice has been written-off successfully.',
|
||||
@@ -630,6 +654,119 @@ export default class SaleInvoicesController extends BaseController {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends mail invoice of the given sale invoice.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public async sendSaleInvoiceMail(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
const { id: invoiceId } = req.params;
|
||||
const invoiceMailDTO: SendInvoiceMailDTO = this.matchedBodyData(req, {
|
||||
includeOptionals: false,
|
||||
});
|
||||
|
||||
try {
|
||||
await this.saleInvoiceApplication.sendSaleInvoiceMail(
|
||||
tenantId,
|
||||
invoiceId,
|
||||
invoiceMailDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
code: 200,
|
||||
message: 'The sale invoice mail has been sent successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreivers the sale invoice reminder options.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public async getSaleInvoiceMailReminder(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
const { id: invoiceId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.saleInvoiceApplication.getSaleInvoiceMailReminder(
|
||||
tenantId,
|
||||
invoiceId
|
||||
);
|
||||
return res.status(200).send(data);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends mail invoice of the given sale invoice.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public async sendSaleInvoiceMailReminder(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
const { id: invoiceId } = req.params;
|
||||
const invoiceMailDTO: SendInvoiceMailDTO = this.matchedBodyData(req, {
|
||||
includeOptionals: false,
|
||||
});
|
||||
try {
|
||||
await this.saleInvoiceApplication.sendSaleInvoiceMailReminder(
|
||||
tenantId,
|
||||
invoiceId,
|
||||
invoiceMailDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
code: 200,
|
||||
message: 'The sale invoice mail reminder has been sent successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default mail options of the given sale invoice.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public async getSaleInvoiceMail(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
const { id: invoiceId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.saleInvoiceApplication.getSaleInvoiceMail(
|
||||
tenantId,
|
||||
invoiceId
|
||||
);
|
||||
return res.status(200).send({ data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param {Error} error
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { body, check, param, query } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from '../BaseController';
|
||||
import { ISaleReceiptDTO } from '@/interfaces/SaleReceipt';
|
||||
import {
|
||||
ISaleReceiptDTO,
|
||||
SaleReceiptMailOpts,
|
||||
SaleReceiptMailOptsDTO,
|
||||
} from '@/interfaces/SaleReceipt';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AbilitySubject, SaleReceiptAction } from '@/interfaces';
|
||||
import { SaleReceiptApplication } from '@/services/Sales/Receipts/SaleReceiptApplication';
|
||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||
|
||||
@Service()
|
||||
export default class SalesReceiptsController extends BaseController {
|
||||
@@ -46,6 +51,27 @@ export default class SalesReceiptsController extends BaseController {
|
||||
this.saleReceiptSmsDetails,
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/mail',
|
||||
[
|
||||
...this.specificReceiptValidationSchema,
|
||||
body('subject').isString().optional(),
|
||||
body('from').isString().optional(),
|
||||
body('to').isString().optional(),
|
||||
body('body').isString().optional(),
|
||||
body('attach_receipt').optional().isBoolean().toBoolean(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.sendSaleReceiptMail.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id/mail',
|
||||
[...this.specificReceiptValidationSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getSaleReceiptMail.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
CheckPolicies(SaleReceiptAction.Edit, AbilitySubject.SaleReceipt),
|
||||
@@ -205,7 +231,6 @@ export default class SalesReceiptsController extends BaseController {
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: saleReceiptId,
|
||||
message: 'Sale receipt has been deleted successfully.',
|
||||
@@ -294,15 +319,10 @@ export default class SalesReceiptsController extends BaseController {
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
try {
|
||||
const { data, pagination, filterMeta } =
|
||||
const salesReceiptsWithPagination =
|
||||
await this.saleReceiptsApplication.getSaleReceipts(tenantId, filter);
|
||||
|
||||
const response = this.transfromToResponse({
|
||||
data,
|
||||
pagination,
|
||||
filterMeta,
|
||||
});
|
||||
return res.status(200).send(response);
|
||||
return res.status(200).send(salesReceiptsWithPagination);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -314,36 +334,34 @@ export default class SalesReceiptsController extends BaseController {
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async getSaleReceipt(req: Request, res: Response, next: NextFunction) {
|
||||
public async getSaleReceipt(req: Request, res: Response) {
|
||||
const { id: saleReceiptId } = req.params;
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
const accept = this.accepts(req);
|
||||
|
||||
const acceptType = accept.types([
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves receipt in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
|
||||
const pdfContent = await this.saleReceiptsApplication.getSaleReceiptPdf(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Retrieves receipt in json format.
|
||||
} else {
|
||||
const saleReceipt = await this.saleReceiptsApplication.getSaleReceipt(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
res.format({
|
||||
'application/json': () => {
|
||||
return res
|
||||
.status(200)
|
||||
.send(this.transfromToResponse({ saleReceipt }));
|
||||
},
|
||||
'application/pdf': async () => {
|
||||
const pdfContent =
|
||||
await this.saleReceiptsApplication.getSaleReceiptPdf(
|
||||
tenantId,
|
||||
saleReceipt
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
return res.status(200).send({ saleReceipt });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,6 +423,64 @@ export default class SalesReceiptsController extends BaseController {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends mail notification of the given sale receipt.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public sendSaleReceiptMail = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: receiptId } = req.params;
|
||||
const receiptMailDTO: SaleReceiptMailOptsDTO = this.matchedBodyData(req, {
|
||||
includeOptionals: false,
|
||||
});
|
||||
|
||||
try {
|
||||
await this.saleReceiptsApplication.sendSaleReceiptMail(
|
||||
tenantId,
|
||||
receiptId,
|
||||
receiptMailDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
code: 200,
|
||||
message:
|
||||
'The sale receipt notification via sms has been sent successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the default mail options of the given sale receipt.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public getSaleReceiptMail = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: receiptId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.saleReceiptsApplication.getSaleReceiptMail(
|
||||
tenantId,
|
||||
receiptId
|
||||
);
|
||||
return res.status(200).send({ data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param {Error} error
|
||||
|
||||
47
packages/server/src/api/controllers/Webhooks/Webhooks.ts
Normal file
47
packages/server/src/api/controllers/Webhooks/Webhooks.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Router } from 'express';
|
||||
import { PlaidApplication } from '@/services/Banking/Plaid/PlaidApplication';
|
||||
import { Request, Response } from 'express';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import BaseController from '../BaseController';
|
||||
import { PlaidWebhookTenantBootMiddleware } from '@/services/Banking/Plaid/PlaidWebhookTenantBootMiddleware';
|
||||
|
||||
@Service()
|
||||
export class Webhooks extends BaseController {
|
||||
@Inject()
|
||||
private plaidApp: PlaidApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.use(PlaidWebhookTenantBootMiddleware);
|
||||
router.post('/plaid', this.plaidWebhooks.bind(this));
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to Plaid webhooks.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @returns {Response}
|
||||
*/
|
||||
public async plaidWebhooks(req: Request, res: Response) {
|
||||
const { tenantId } = req;
|
||||
const {
|
||||
webhook_type: webhookType,
|
||||
webhook_code: webhookCode,
|
||||
item_id: plaidItemId,
|
||||
} = req.body;
|
||||
|
||||
await this.plaidApp.webhooks(
|
||||
tenantId,
|
||||
plaidItemId,
|
||||
webhookType,
|
||||
webhookCode
|
||||
);
|
||||
return res.status(200).send({ code: 200, message: 'ok' });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user