Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be8352654e | ||
|
|
fb58ab8cc1 | ||
|
|
8da89ebe8b | ||
|
|
d43d46ebec | ||
|
|
ac3a514795 | ||
|
|
f67c63a4fa | ||
|
|
0025dcf8d4 | ||
|
|
81995dc94f | ||
|
|
3fcc70c1d8 | ||
|
|
a986c7a250 | ||
|
|
37e25a8061 | ||
|
|
cfba628465 | ||
|
|
3d200f4d7d | ||
|
|
8cab012324 | ||
|
|
7147e230de |
@@ -141,6 +141,15 @@
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mittalsam98",
|
||||
"name": "Sachin Mittal",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/42431274?v=4",
|
||||
"profile": "https://myself.vercel.app/",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -128,6 +128,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oleynikd"><img src="https://avatars.githubusercontent.com/u/3976868?v=4?s=100" width="100px;" alt="Denis"/><br /><sub><b>Denis</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aoleynikd" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://myself.vercel.app/"><img src="https://avatars.githubusercontent.com/u/42431274?v=4?s=100" width="100px;" alt="Sachin Mittal"/><br /><sub><b>Sachin Mittal</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Amittalsam98" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -103,24 +103,20 @@ export default class AccountsController extends BaseController {
|
||||
check('name')
|
||||
.exists()
|
||||
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('code')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ min: 3, max: 6 })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('currency_code').optional(),
|
||||
check('account_type')
|
||||
.exists()
|
||||
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('parent_account_id')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 })
|
||||
@@ -136,23 +132,19 @@ export default class AccountsController extends BaseController {
|
||||
check('name')
|
||||
.exists()
|
||||
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('code')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ min: 3, max: 6 })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('account_type')
|
||||
.exists()
|
||||
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('parent_account_id')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 })
|
||||
|
||||
@@ -90,27 +90,23 @@ export default class AuthenticationController extends BaseController {
|
||||
.exists()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('last_name')
|
||||
.exists()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('email')
|
||||
.exists()
|
||||
.isString()
|
||||
.isEmail()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('password')
|
||||
.exists()
|
||||
.isString()
|
||||
.isLength({ min: 6 })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
];
|
||||
}
|
||||
@@ -150,7 +146,7 @@ export default class AuthenticationController extends BaseController {
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
private get sendResetPasswordSchema(): ValidationChain[] {
|
||||
return [check('email').exists().isEmail().trim().escape()];
|
||||
return [check('email').exists().isEmail().trim()];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +154,11 @@ export default class AuthenticationController extends BaseController {
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
private async login(req: Request, res: Response, next: Function): Response {
|
||||
private async login(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: Function
|
||||
): Promise<Response | null> {
|
||||
const userDTO: ILoginDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
|
||||
@@ -43,8 +43,6 @@ export class BankingRulesController extends BaseController {
|
||||
body('assign_account_id').isInt({ min: 0 }),
|
||||
body('assign_payee').isString().optional({ nullable: true }),
|
||||
body('assign_memo').isString().optional({ nullable: true }),
|
||||
|
||||
body('recognition').isBoolean().toBoolean().optional({ nullable: true }),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,11 @@ export class ExcludeBankTransactionsController extends BaseController {
|
||||
);
|
||||
router.get(
|
||||
'/excluded',
|
||||
[],
|
||||
[
|
||||
query('account_id').optional().isNumeric().toInt(),
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
],
|
||||
this.validationResult,
|
||||
this.getExcludedBankTransactions.bind(this)
|
||||
);
|
||||
@@ -177,7 +181,7 @@ export class ExcludeBankTransactionsController extends BaseController {
|
||||
next: NextFunction
|
||||
): Promise<Response | void> {
|
||||
const { tenantId } = req;
|
||||
const filter = this.matchedBodyData(req);
|
||||
const filter = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const data =
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { NextFunction, Request, Response, Router } from 'express';
|
||||
import { query } from 'express-validator';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
|
||||
|
||||
@@ -14,7 +15,16 @@ export class RecognizedTransactionsController extends BaseController {
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get('/', this.getRecognizedTransactions.bind(this));
|
||||
router.get(
|
||||
'/',
|
||||
[
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
query('account_id').optional().isNumeric().toInt(),
|
||||
],
|
||||
this.validationResult,
|
||||
this.getRecognizedTransactions.bind(this)
|
||||
);
|
||||
router.get(
|
||||
'/transactions/:uncategorizedTransactionId',
|
||||
this.getRecognizedTransaction.bind(this)
|
||||
|
||||
@@ -112,12 +112,11 @@ export default class NewCashflowTransactionController extends BaseController {
|
||||
public get newTransactionValidationSchema() {
|
||||
return [
|
||||
check('date').exists().isISO8601().toDate(),
|
||||
check('reference_no').optional({ nullable: true }).trim().escape(),
|
||||
check('reference_no').optional({ nullable: true }).trim(),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ min: 3 })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('transaction_type').exists(),
|
||||
|
||||
check('amount').exists().isFloat().toFloat(),
|
||||
|
||||
@@ -56,7 +56,7 @@ export default class ContactsController extends BaseController {
|
||||
*/
|
||||
get autocompleteQuerySchema() {
|
||||
return [
|
||||
query('column_sort_by').optional().trim().escape(),
|
||||
query('column_sort_by').optional().trim(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
@@ -122,32 +122,27 @@ export default class ContactsController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('first_name')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('last_name')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('company_name')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('display_name')
|
||||
.exists()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('email')
|
||||
@@ -165,120 +160,101 @@ export default class ContactsController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('personal_phone')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('billing_address_1')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_2')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_city')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_country')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_email')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.isEmail()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_postcode')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_phone')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_state')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('shipping_address_1')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_2')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_city')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_country')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_email')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.isEmail()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_postcode')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_phone')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_state')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('note')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('active').optional().isBoolean().toBoolean(),
|
||||
];
|
||||
|
||||
@@ -106,11 +106,7 @@ export default class CustomersController extends ContactsController {
|
||||
*/
|
||||
get customerDTOSchema() {
|
||||
return [
|
||||
check('customer_type')
|
||||
.exists()
|
||||
.isIn(['business', 'individual'])
|
||||
.trim()
|
||||
.escape(),
|
||||
check('customer_type').exists().isIn(['business', 'individual']).trim(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -123,7 +119,6 @@ export default class CustomersController extends ContactsController {
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 3 }),
|
||||
];
|
||||
}
|
||||
@@ -133,7 +128,7 @@ export default class CustomersController extends ContactsController {
|
||||
*/
|
||||
get validateListQuerySchema() {
|
||||
return [
|
||||
query('column_sort_by').optional().trim().escape(),
|
||||
query('column_sort_by').optional().trim(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
|
||||
@@ -106,7 +106,6 @@ export default class VendorsController extends ContactsController {
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ min: 3, max: 3 }),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export default class CurrenciesController extends BaseController {
|
||||
}
|
||||
|
||||
get currencyParamSchema(): ValidationChain[] {
|
||||
return [param('currency_code').exists().trim().escape()];
|
||||
return [param('currency_code').exists().trim()];
|
||||
}
|
||||
|
||||
get listSchema(): ValidationChain[] {
|
||||
@@ -187,11 +187,13 @@ export default class CurrenciesController extends BaseController {
|
||||
}
|
||||
if (error.errorType === 'currency_code_exists') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{
|
||||
type: 'CURRENCY_CODE_EXISTS',
|
||||
message: 'The given currency code is already exists.',
|
||||
code: 200,
|
||||
}],
|
||||
errors: [
|
||||
{
|
||||
type: 'CURRENCY_CODE_EXISTS',
|
||||
message: 'The given currency code is already exists.',
|
||||
code: 200,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'CANNOT_DELETE_BASE_CURRENCY') {
|
||||
|
||||
@@ -89,7 +89,6 @@ export class ExpensesController extends BaseController {
|
||||
check('reference_no')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('payment_date').exists().isISO8601().toDate(),
|
||||
check('payment_account_id')
|
||||
@@ -123,7 +122,6 @@ export class ExpensesController extends BaseController {
|
||||
check('categories.*.description')
|
||||
.optional()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('categories.*.landed_cost').optional().isBoolean().toBoolean(),
|
||||
check('categories.*.project_id')
|
||||
@@ -144,7 +142,6 @@ export class ExpensesController extends BaseController {
|
||||
check('reference_no')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('payment_date').exists().isISO8601().toDate(),
|
||||
check('payment_account_id')
|
||||
@@ -179,7 +176,6 @@ export class ExpensesController extends BaseController {
|
||||
check('categories.*.description')
|
||||
.optional()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('categories.*.landed_cost').optional().isBoolean().toBoolean(),
|
||||
check('categories.*.project_id')
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { query } from 'express-validator';
|
||||
import BaseController from "../BaseController";
|
||||
import BaseController from '../BaseController';
|
||||
|
||||
export default class BaseFinancialReportController extends BaseController {
|
||||
|
||||
|
||||
get sheetNumberFormatValidationSchema() {
|
||||
return [
|
||||
query('number_format.precision')
|
||||
@@ -19,8 +17,7 @@ export default class BaseFinancialReportController extends BaseController {
|
||||
query('number_format.negative_format')
|
||||
.optional()
|
||||
.isIn(['parentheses', 'mines'])
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@ export default class InventoryDetailsController extends BaseController {
|
||||
query('number_format.negative_format')
|
||||
.optional()
|
||||
.isIn(['parentheses', 'mines'])
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
query('from_date').optional(),
|
||||
query('to_date').optional(),
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class JournalSheetController extends BaseFinancialReportControlle
|
||||
return [
|
||||
query('from_date').optional().isISO8601(),
|
||||
query('to_date').optional().isISO8601(),
|
||||
query('transaction_type').optional().trim().escape(),
|
||||
query('transaction_type').optional().trim(),
|
||||
query('transaction_id').optional().isInt().toInt(),
|
||||
oneOf(
|
||||
[
|
||||
|
||||
@@ -40,8 +40,7 @@ export default class TransactionsByReferenceController extends BaseController {
|
||||
query('number_format.negative_format')
|
||||
.optional()
|
||||
.isIn(['parentheses', 'mines'])
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ export default class InventoryAdjustmentsController extends BaseController {
|
||||
*/
|
||||
get validateListQuerySchema() {
|
||||
return [
|
||||
query('column_sort_by').optional().trim().escape(),
|
||||
query('column_sort_by').optional().trim(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
|
||||
@@ -25,7 +25,7 @@ export default class InviteUsersController extends BaseController {
|
||||
router.post(
|
||||
'/send',
|
||||
[
|
||||
body('email').exists().trim().escape(),
|
||||
body('email').exists().trim(),
|
||||
body('role_id').exists().isNumeric().toInt(),
|
||||
],
|
||||
this.validationResult,
|
||||
@@ -57,7 +57,7 @@ export default class InviteUsersController extends BaseController {
|
||||
);
|
||||
router.get(
|
||||
'/invited/:token',
|
||||
[param('token').exists().trim().escape()],
|
||||
[param('token').exists().trim()],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.invited.bind(this)),
|
||||
this.handleServicesError
|
||||
@@ -72,10 +72,10 @@ export default class InviteUsersController extends BaseController {
|
||||
*/
|
||||
private get inviteUserDTO() {
|
||||
return [
|
||||
check('first_name').exists().trim().escape(),
|
||||
check('last_name').exists().trim().escape(),
|
||||
check('password').exists().trim().escape().isLength({ min: 5 }),
|
||||
param('token').exists().trim().escape(),
|
||||
check('first_name').exists().trim(),
|
||||
check('last_name').exists().trim(),
|
||||
check('password').exists().trim().isLength({ min: 5 }),
|
||||
param('token').exists().trim(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -73,13 +73,11 @@ export default class ItemsCategoriesController extends BaseController {
|
||||
check('name')
|
||||
.exists()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ min: 0, max: DATATYPES_LENGTH.STRING }),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('sell_account_id')
|
||||
.optional({ nullable: true })
|
||||
@@ -101,9 +99,8 @@ export default class ItemsCategoriesController extends BaseController {
|
||||
*/
|
||||
get categoriesListValidationSchema() {
|
||||
return [
|
||||
query('column_sort_by').optional().trim().escape(),
|
||||
query('sort_order').optional().trim().escape().isIn(['desc', 'asc']),
|
||||
|
||||
query('column_sort_by').optional().trim(),
|
||||
query('sort_order').optional().trim().isIn(['desc', 'asc']),
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
];
|
||||
}
|
||||
@@ -207,14 +204,12 @@ export default class ItemsCategoriesController extends BaseController {
|
||||
};
|
||||
|
||||
try {
|
||||
const {
|
||||
itemCategories,
|
||||
filterMeta,
|
||||
} = await this.itemCategoriesService.getItemCategoriesList(
|
||||
tenantId,
|
||||
itemCategoriesFilter,
|
||||
user
|
||||
);
|
||||
const { itemCategories, filterMeta } =
|
||||
await this.itemCategoriesService.getItemCategoriesList(
|
||||
tenantId,
|
||||
itemCategoriesFilter,
|
||||
user
|
||||
);
|
||||
return res.status(200).send({
|
||||
item_categories: itemCategories,
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
|
||||
@@ -96,13 +96,11 @@ export default class ItemsController extends BaseController {
|
||||
.exists()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isIn(['service', 'non-inventory', 'inventory']),
|
||||
check('code')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
// Purchase attributes.
|
||||
check('purchasable').optional().isBoolean().toBoolean(),
|
||||
@@ -141,13 +139,11 @@ export default class ItemsController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('purchase_description')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('sell_tax_rate_id').optional({ nullable: true }).isInt().toInt(),
|
||||
check('purchase_tax_rate_id')
|
||||
@@ -162,7 +158,6 @@ export default class ItemsController extends BaseController {
|
||||
.optional()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('active').optional().isBoolean().toBoolean(),
|
||||
|
||||
@@ -184,7 +179,7 @@ export default class ItemsController extends BaseController {
|
||||
*/
|
||||
private get validateListQuerySchema() {
|
||||
return [
|
||||
query('column_sort_by').optional().trim().escape(),
|
||||
query('column_sort_by').optional().trim(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
|
||||
@@ -94,25 +94,21 @@ export default class ManualJournalsController extends BaseController {
|
||||
.optional()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('journal_type')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('reference')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
check('publish').optional().isBoolean().toBoolean(),
|
||||
@@ -163,7 +159,7 @@ export default class ManualJournalsController extends BaseController {
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
query('custom_view_id').optional().isNumeric().toInt(),
|
||||
|
||||
query('column_sort_by').optional().trim().escape(),
|
||||
query('column_sort_by').optional().trim(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
|
||||
@@ -61,15 +61,14 @@ export default class MediaController extends BaseController {
|
||||
|
||||
get uploadValidationSchema() {
|
||||
return [
|
||||
// check('attachment'),
|
||||
check('model_name').optional().trim().escape(),
|
||||
check('model_id').optional().isNumeric().toInt(),
|
||||
check('model_name').optional().trim(),
|
||||
check('model_id').optional().isNumeric(),
|
||||
];
|
||||
}
|
||||
|
||||
get linkValidationSchema() {
|
||||
return [
|
||||
check('model_name').exists().trim().escape(),
|
||||
check('model_name').exists().trim(),
|
||||
check('model_id').exists().isNumeric().toInt(),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export default class OrganizationController extends BaseController {
|
||||
private get commonOrganizationValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('name').exists().trim(),
|
||||
check('industry').optional({ nullable: true }).isString().trim().escape(),
|
||||
check('industry').optional({ nullable: true }).isString().trim(),
|
||||
check('location').exists().isString().isISO31661Alpha2(),
|
||||
check('base_currency').exists().isISO4217(),
|
||||
check('timezone').exists().isIn(moment.tz.names()),
|
||||
@@ -87,11 +87,7 @@ export default class OrganizationController extends BaseController {
|
||||
private get updateOrganizationValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
...this.commonOrganizationValidationSchema,
|
||||
check('tax_number')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape(),
|
||||
check('tax_number').optional({ nullable: true }).isString().trim(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -100,8 +100,8 @@ export default class BillsController extends BaseController {
|
||||
*/
|
||||
private get billValidationSchema() {
|
||||
return [
|
||||
check('bill_number').exists().trim().escape(),
|
||||
check('reference_no').optional().trim().escape(),
|
||||
check('bill_number').exists().trim(),
|
||||
check('reference_no').optional().trim(),
|
||||
check('bill_date').exists().isISO8601(),
|
||||
check('due_date').optional().isISO8601(),
|
||||
|
||||
@@ -112,7 +112,7 @@ export default class BillsController extends BaseController {
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
check('project_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('note').optional().trim().escape(),
|
||||
check('note').optional().trim(),
|
||||
check('open').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('is_inclusive_tax').default(false).isBoolean().toBoolean(),
|
||||
@@ -126,10 +126,7 @@ export default class BillsController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.landed_cost')
|
||||
.optional({ nullable: true })
|
||||
.isBoolean()
|
||||
@@ -141,7 +138,6 @@ export default class BillsController extends BaseController {
|
||||
check('entries.*.tax_code')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isString(),
|
||||
check('entries.*.tax_rate_id')
|
||||
.optional({ nullable: true })
|
||||
@@ -158,8 +154,8 @@ export default class BillsController extends BaseController {
|
||||
*/
|
||||
private get billEditValidationSchema() {
|
||||
return [
|
||||
check('bill_number').optional().trim().escape(),
|
||||
check('reference_no').optional().trim().escape(),
|
||||
check('bill_number').optional().trim(),
|
||||
check('reference_no').optional().trim(),
|
||||
check('bill_date').exists().isISO8601(),
|
||||
check('due_date').optional().isISO8601(),
|
||||
|
||||
@@ -170,7 +166,7 @@ export default class BillsController extends BaseController {
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
check('project_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('note').optional().trim().escape(),
|
||||
check('note').optional().trim(),
|
||||
check('open').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('entries').isArray({ min: 1 }),
|
||||
@@ -184,10 +180,7 @@ export default class BillsController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.landed_cost')
|
||||
.optional({ nullable: true })
|
||||
.isBoolean()
|
||||
@@ -222,8 +215,8 @@ export default class BillsController extends BaseController {
|
||||
|
||||
private get dueBillsListingValidationSchema() {
|
||||
return [
|
||||
query('vendor_id').optional().trim().escape(),
|
||||
query('payment_made_id').optional().trim().escape(),
|
||||
query('vendor_id').optional().trim(),
|
||||
query('payment_made_id').optional().trim(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -113,10 +113,10 @@ export default class BillsPayments extends BaseController {
|
||||
|
||||
check('amount').exists().isNumeric().toFloat(),
|
||||
check('payment_account_id').exists().isNumeric().toInt(),
|
||||
check('payment_number').optional({ nullable: true }).trim().escape(),
|
||||
check('payment_number').optional({ nullable: true }).trim(),
|
||||
check('payment_date').exists(),
|
||||
check('statement').optional().trim().escape(),
|
||||
check('reference').optional().trim().escape(),
|
||||
check('statement').optional().trim(),
|
||||
check('reference').optional().trim(),
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('entries').exists().isArray(),
|
||||
|
||||
@@ -156,13 +156,10 @@ export default class VendorCreditController extends BaseController {
|
||||
check('vendor_id').exists().isNumeric().toInt(),
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
|
||||
check('vendor_credit_number')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('reference_no').optional().trim().escape(),
|
||||
check('vendor_credit_number').optional({ nullable: true }).trim(),
|
||||
check('reference_no').optional().trim(),
|
||||
check('vendor_credit_date').exists().isISO8601().toDate(),
|
||||
check('note').optional().trim().escape(),
|
||||
check('note').optional().trim(),
|
||||
check('open').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('warehouse_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
@@ -178,10 +175,7 @@ export default class VendorCreditController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
@@ -202,13 +196,10 @@ export default class VendorCreditController extends BaseController {
|
||||
check('vendor_id').exists().isNumeric().toInt(),
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
|
||||
check('vendor_credit_number')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('reference_no').optional().trim().escape(),
|
||||
check('vendor_credit_number').optional({ nullable: true }).trim(),
|
||||
check('reference_no').optional().trim(),
|
||||
check('vendor_credit_date').exists().isISO8601().toDate(),
|
||||
check('note').optional().trim().escape(),
|
||||
check('note').optional().trim(),
|
||||
|
||||
check('warehouse_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
@@ -223,10 +214,7 @@ export default class VendorCreditController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
|
||||
@@ -18,9 +18,7 @@ export default class ResourceController extends BaseController {
|
||||
|
||||
router.get(
|
||||
'/:resource_model/meta',
|
||||
[
|
||||
param('resource_model').exists().trim().escape()
|
||||
],
|
||||
[param('resource_model').exists().trim()],
|
||||
this.asyncMiddleware(this.resourceMeta.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
@@ -48,9 +46,7 @@ export default class ResourceController extends BaseController {
|
||||
resourceModel
|
||||
);
|
||||
return res.status(200).send({
|
||||
resource_meta: this.transfromToResponse(
|
||||
resourceMeta,
|
||||
),
|
||||
resource_meta: this.transfromToResponse(resourceMeta),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
|
||||
@@ -210,9 +210,9 @@ export default class PaymentReceivesController extends BaseController {
|
||||
|
||||
check('credit_note_date').exists().isISO8601().toDate(),
|
||||
check('reference_no').optional(),
|
||||
check('credit_note_number').optional({ nullable: true }).trim().escape(),
|
||||
check('note').optional().trim().escape(),
|
||||
check('terms_conditions').optional().trim().escape(),
|
||||
check('credit_note_number').optional({ nullable: true }).trim(),
|
||||
check('note').optional().trim(),
|
||||
check('terms_conditions').optional().trim(),
|
||||
check('open').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('warehouse_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
@@ -228,10 +228,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
|
||||
@@ -154,8 +154,8 @@ export default class PaymentReceivesController extends BaseController {
|
||||
check('payment_date').exists(),
|
||||
check('reference_no').optional(),
|
||||
check('deposit_account_id').exists().isNumeric().toInt(),
|
||||
check('payment_receive_no').optional({ nullable: true }).trim().escape(),
|
||||
check('statement').optional().trim().escape(),
|
||||
check('payment_receive_no').optional({ nullable: true }).trim(),
|
||||
check('statement').optional().trim(),
|
||||
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
@@ -176,7 +176,6 @@ export default class PaymentReceivesController extends BaseController {
|
||||
private get validatePaymentReceiveList(): ValidationChain[] {
|
||||
return [
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
|
||||
query('view_slug').optional({ nullable: true }).isString().trim(),
|
||||
|
||||
query('column_sort_by').optional(),
|
||||
|
||||
@@ -155,7 +155,7 @@ export default class SalesEstimatesController extends BaseController {
|
||||
check('estimate_date').exists().isISO8601().toDate(),
|
||||
check('expiration_date').exists().isISO8601().toDate(),
|
||||
check('reference').optional(),
|
||||
check('estimate_number').optional().trim().escape(),
|
||||
check('estimate_number').optional().trim(),
|
||||
check('delivered').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
@@ -170,8 +170,7 @@ export default class SalesEstimatesController extends BaseController {
|
||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||
check('entries.*.description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('entries.*.discount')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
@@ -181,9 +180,9 @@ export default class SalesEstimatesController extends BaseController {
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
|
||||
check('note').optional().trim().escape(),
|
||||
check('terms_conditions').optional().trim().escape(),
|
||||
check('send_to_email').optional().trim().escape(),
|
||||
check('note').optional().trim(),
|
||||
check('terms_conditions').optional().trim(),
|
||||
check('send_to_email').optional().trim(),
|
||||
|
||||
check('attachments').isArray().optional(),
|
||||
check('attachments.*.key').exists().isString(),
|
||||
|
||||
@@ -200,12 +200,12 @@ export default class SaleInvoicesController extends BaseController {
|
||||
check('customer_id').exists().isNumeric().toInt(),
|
||||
check('invoice_date').exists().isISO8601().toDate(),
|
||||
check('due_date').exists().isISO8601().toDate(),
|
||||
check('invoice_no').optional().trim().escape(),
|
||||
check('reference_no').optional().trim().escape(),
|
||||
check('invoice_no').optional().trim(),
|
||||
check('reference_no').optional().trim(),
|
||||
check('delivered').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('invoice_message').optional().trim().escape(),
|
||||
check('terms_conditions').optional().trim().escape(),
|
||||
check('invoice_message').optional().trim(),
|
||||
check('terms_conditions').optional().trim(),
|
||||
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
|
||||
@@ -226,12 +226,10 @@ export default class SaleInvoicesController extends BaseController {
|
||||
.toFloat(),
|
||||
check('entries.*.description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('entries.*.tax_code')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isString(),
|
||||
check('entries.*.tax_rate_id')
|
||||
.optional({ nullable: true })
|
||||
|
||||
@@ -130,8 +130,8 @@ export default class SalesReceiptsController extends BaseController {
|
||||
|
||||
check('deposit_account_id').exists().isNumeric().toInt(),
|
||||
check('receipt_date').exists().isISO8601(),
|
||||
check('receipt_number').optional().trim().escape(),
|
||||
check('reference_no').optional().trim().escape(),
|
||||
check('receipt_number').optional().trim(),
|
||||
check('reference_no').optional().trim(),
|
||||
check('closed').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('warehouse_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
@@ -150,14 +150,13 @@ export default class SalesReceiptsController extends BaseController {
|
||||
.toInt(),
|
||||
check('entries.*.description')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape(),
|
||||
.trim(),
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
check('receipt_message').optional().trim().escape(),
|
||||
check('statement').optional().trim().escape(),
|
||||
check('receipt_message').optional().trim(),
|
||||
check('statement').optional().trim(),
|
||||
check('attachments').isArray().optional(),
|
||||
check('attachments.*.key').exists().isString(),
|
||||
];
|
||||
|
||||
@@ -52,10 +52,7 @@ export default class SettingsController extends BaseController {
|
||||
* Retrieve the application options from the storage.
|
||||
*/
|
||||
private get getSettingsSchema() {
|
||||
return [
|
||||
query('key').optional().trim().escape(),
|
||||
query('group').optional().trim().escape(),
|
||||
];
|
||||
return [query('key').optional().trim(), query('group').optional().trim()];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,7 @@ export default class ViewsController extends BaseController {
|
||||
* Custom views list validation schema.
|
||||
*/
|
||||
get viewsListSchemaValidation() {
|
||||
return [param('resource_model').exists().trim().escape()];
|
||||
return [param('resource_model').exists().trim()];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// This migration changes the precision of the tax_amount_withheld column in the bills and sales_invoices tables from 8, 2 to 13, 2.
|
||||
// This migration is necessary to allow tax_amount_withheld filed to store values bigger than 999,999.99.
|
||||
|
||||
exports.up = function(knex) {
|
||||
return knex.schema.alterTable('bills', function (table) {
|
||||
table.decimal('tax_amount_withheld', 13, 2).alter();
|
||||
}).alterTable('sales_invoices', function (table) {
|
||||
table.decimal('tax_amount_withheld', 13, 2).alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
return knex.schema.alterTable('bills', function (table) {
|
||||
table.decimal('tax_amount_withheld', 8, 2).alter();
|
||||
}).alterTable('sales_invoices', function (table) {
|
||||
table.decimal('tax_amount_withheld', 8, 2).alter();
|
||||
});
|
||||
};
|
||||
@@ -13,7 +13,9 @@ import { PaymentReceiveMailNotificationJob } from '@/services/Sales/PaymentRecei
|
||||
import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob';
|
||||
import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob';
|
||||
import { SendVerifyMailJob } from '@/services/Authentication/jobs/SendVerifyMailJob';
|
||||
import { RegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/RecognizeTransactionsJob';
|
||||
import { ReregonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RerecognizeTransactionsJob';
|
||||
import { RegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RecognizeTransactionsJob';
|
||||
import { RevertRegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RevertRecognizedTransactionsJob';
|
||||
|
||||
export default ({ agenda }: { agenda: Agenda }) => {
|
||||
new ResetPasswordMailJob(agenda);
|
||||
@@ -31,6 +33,8 @@ export default ({ agenda }: { agenda: Agenda }) => {
|
||||
new ImportDeleteExpiredFilesJobs(agenda);
|
||||
new SendVerifyMailJob(agenda);
|
||||
new RegonizeTransactionsJob(agenda);
|
||||
new ReregonizeTransactionsJob(agenda);
|
||||
new RevertRegonizeTransactionsJob(agenda);
|
||||
|
||||
agenda.start().then(() => {
|
||||
agenda.every('1 hours', 'delete-expired-imported-files', {});
|
||||
|
||||
@@ -35,7 +35,8 @@ export class RecognizeSyncedBankTranasctions extends EventSubscriber {
|
||||
runAfterTransaction(trx, async () => {
|
||||
await this.recognizeTranasctionsService.recognizeTransactions(
|
||||
tenantId,
|
||||
batch
|
||||
null,
|
||||
{ batch }
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { castArray, isEmpty } from 'lodash';
|
||||
import UncategorizedCashflowTransaction from '@/models/UncategorizedCashflowTransaction';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { transformToMapBy } from '@/utils';
|
||||
import { PromisePool } from '@supercharge/promise-pool';
|
||||
import { BankRule } from '@/models/BankRule';
|
||||
import { bankRulesMatchTransaction } from './_utils';
|
||||
import { RecognizeTransactionsCriteria } from './_types';
|
||||
|
||||
@Service()
|
||||
export class RecognizeTranasctionsService {
|
||||
@@ -48,24 +50,42 @@ export class RecognizeTranasctionsService {
|
||||
/**
|
||||
* Regonized the uncategorized transactions.
|
||||
* @param {number} tenantId -
|
||||
* @param {number|Array<number>} ruleId - The target rule id/ids.
|
||||
* @param {RecognizeTransactionsCriteria}
|
||||
* @param {Knex.Transaction} trx -
|
||||
*/
|
||||
public async recognizeTransactions(
|
||||
tenantId: number,
|
||||
batch: string = '',
|
||||
ruleId?: number | Array<number>,
|
||||
transactionsCriteria?: RecognizeTransactionsCriteria,
|
||||
trx?: Knex.Transaction
|
||||
) {
|
||||
const { UncategorizedCashflowTransaction, BankRule } =
|
||||
this.tenancy.models(tenantId);
|
||||
|
||||
const uncategorizedTranasctions =
|
||||
await UncategorizedCashflowTransaction.query().onBuild((query) => {
|
||||
query.where('recognized_transaction_id', null);
|
||||
query.where('categorized', false);
|
||||
await UncategorizedCashflowTransaction.query(trx).onBuild((query) => {
|
||||
query.modify('notRecognized');
|
||||
query.modify('notCategorized');
|
||||
|
||||
if (batch) query.where('batch', batch);
|
||||
// Filter the transactions based on the given criteria.
|
||||
if (transactionsCriteria?.batch) {
|
||||
query.where('batch', transactionsCriteria.batch);
|
||||
}
|
||||
if (transactionsCriteria?.accountId) {
|
||||
query.where('accountId', transactionsCriteria.accountId);
|
||||
}
|
||||
});
|
||||
const bankRules = await BankRule.query().withGraphFetched('conditions');
|
||||
|
||||
const bankRules = await BankRule.query(trx).onBuild((q) => {
|
||||
const rulesIds = castArray(ruleId);
|
||||
|
||||
if (!isEmpty(rulesIds)) {
|
||||
q.whereIn('id', rulesIds);
|
||||
}
|
||||
q.withGraphFetched('conditions');
|
||||
});
|
||||
|
||||
const bankRulesByAccountId = transformToMapBy(
|
||||
bankRules,
|
||||
'applyIfAccountId'
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { castArray } from 'lodash';
|
||||
import { Knex } from 'knex';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { RevertRecognizedTransactionsCriteria } from './_types';
|
||||
|
||||
@Service()
|
||||
export class RevertRecognizedTransactions {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
/**
|
||||
* Revert and unlinks the recognized transactions based on the given bank rule
|
||||
* and transactions criteria..
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number|Array<number>} bankRuleId - Bank rule id.
|
||||
* @param {RevertRecognizedTransactionsCriteria} transactionsCriteria -
|
||||
* @param {Knex.Transaction} trx - Knex transaction.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async revertRecognizedTransactions(
|
||||
tenantId: number,
|
||||
ruleId?: number | Array<number>,
|
||||
transactionsCriteria?: RevertRecognizedTransactionsCriteria,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> {
|
||||
const { UncategorizedCashflowTransaction, RecognizedBankTransaction } =
|
||||
this.tenancy.models(tenantId);
|
||||
|
||||
const rulesIds = castArray(ruleId);
|
||||
|
||||
return this.uow.withTransaction(
|
||||
tenantId,
|
||||
async (trx: Knex.Transaction) => {
|
||||
// Retrieves all the recognized transactions of the banbk rule.
|
||||
const uncategorizedTransactions =
|
||||
await UncategorizedCashflowTransaction.query(trx).onBuild((q) => {
|
||||
q.withGraphJoined('recognizedTransaction');
|
||||
q.whereNotNull('recognizedTransaction.id');
|
||||
|
||||
if (rulesIds.length > 0) {
|
||||
q.whereIn('recognizedTransaction.bankRuleId', rulesIds);
|
||||
}
|
||||
if (transactionsCriteria?.accountId) {
|
||||
q.where('accountId', transactionsCriteria.accountId);
|
||||
}
|
||||
if (transactionsCriteria?.batch) {
|
||||
q.where('batch', transactionsCriteria.batch);
|
||||
}
|
||||
});
|
||||
const uncategorizedTransactionIds = uncategorizedTransactions.map(
|
||||
(r) => r.id
|
||||
);
|
||||
// Unlink the recongized transactions out of uncategorized transactions.
|
||||
await UncategorizedCashflowTransaction.query(trx)
|
||||
.whereIn('id', uncategorizedTransactionIds)
|
||||
.patch({
|
||||
recognizedTransactionId: null,
|
||||
});
|
||||
// Delete the recognized bank transactions that assocaited to bank rule.
|
||||
await RecognizedBankTransaction.query(trx)
|
||||
.whereIn('uncategorizedTransactionId', uncategorizedTransactionIds)
|
||||
.delete();
|
||||
},
|
||||
trx
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface RevertRecognizedTransactionsCriteria {
|
||||
batch?: string;
|
||||
accountId?: number;
|
||||
}
|
||||
|
||||
|
||||
export interface RecognizeTransactionsCriteria {
|
||||
batch?: string;
|
||||
accountId?: number;
|
||||
}
|
||||
|
||||
@@ -41,14 +41,10 @@ export class TriggerRecognizedTransactions {
|
||||
*/
|
||||
private async recognizedTransactionsOnRuleCreated({
|
||||
tenantId,
|
||||
createRuleDTO,
|
||||
bankRule,
|
||||
}: IBankRuleEventCreatedPayload) {
|
||||
const payload = { tenantId };
|
||||
const payload = { tenantId, ruleId: bankRule.id };
|
||||
|
||||
// Cannot run recognition if the option is not enabled.
|
||||
if (createRuleDTO.recognition) {
|
||||
return;
|
||||
}
|
||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
||||
}
|
||||
|
||||
@@ -59,14 +55,14 @@ export class TriggerRecognizedTransactions {
|
||||
private async recognizedTransactionsOnRuleEdited({
|
||||
tenantId,
|
||||
editRuleDTO,
|
||||
ruleId,
|
||||
}: IBankRuleEventEditedPayload) {
|
||||
const payload = { tenantId };
|
||||
const payload = { tenantId, ruleId };
|
||||
|
||||
// Cannot run recognition if the option is not enabled.
|
||||
if (!editRuleDTO.recognition) {
|
||||
return;
|
||||
}
|
||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
||||
await this.agenda.now(
|
||||
'rerecognize-uncategorized-transactions-job',
|
||||
payload
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,9 +71,13 @@ export class TriggerRecognizedTransactions {
|
||||
*/
|
||||
private async recognizedTransactionsOnRuleDeleted({
|
||||
tenantId,
|
||||
ruleId,
|
||||
}: IBankRuleEventDeletedPayload) {
|
||||
const payload = { tenantId };
|
||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
||||
const payload = { tenantId, ruleId };
|
||||
await this.agenda.now(
|
||||
'revert-recognized-uncategorized-transactions-job',
|
||||
payload
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,7 +91,7 @@ export class TriggerRecognizedTransactions {
|
||||
}: IImportFileCommitedEventPayload) {
|
||||
const importFile = await Import.query().findOne({ importId });
|
||||
const batch = importFile.paramsParsed.batch;
|
||||
const payload = { tenantId, batch };
|
||||
const payload = { tenantId, transactionsCriteria: { batch } };
|
||||
|
||||
await this.agenda.now('recognize-uncategorized-transactions-job', payload);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container, { Service } from 'typedi';
|
||||
import { RecognizeTranasctionsService } from './RecognizeTranasctionsService';
|
||||
import { RecognizeTranasctionsService } from '../RecognizeTranasctionsService';
|
||||
|
||||
@Service()
|
||||
export class RegonizeTransactionsJob {
|
||||
@@ -18,11 +18,15 @@ export class RegonizeTransactionsJob {
|
||||
* Triggers sending invoice mail.
|
||||
*/
|
||||
private handler = async (job, done: Function) => {
|
||||
const { tenantId, batch } = job.attrs.data;
|
||||
const { tenantId, ruleId, transactionsCriteria } = job.attrs.data;
|
||||
const regonizeTransactions = Container.get(RecognizeTranasctionsService);
|
||||
|
||||
try {
|
||||
await regonizeTransactions.recognizeTransactions(tenantId, batch);
|
||||
await regonizeTransactions.recognizeTransactions(
|
||||
tenantId,
|
||||
ruleId,
|
||||
transactionsCriteria
|
||||
);
|
||||
done();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -0,0 +1,45 @@
|
||||
import Container, { Service } from 'typedi';
|
||||
import { RecognizeTranasctionsService } from '../RecognizeTranasctionsService';
|
||||
import { RevertRecognizedTransactions } from '../RevertRecognizedTransactions';
|
||||
|
||||
@Service()
|
||||
export class ReregonizeTransactionsJob {
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor(agenda) {
|
||||
agenda.define(
|
||||
'rerecognize-uncategorized-transactions-job',
|
||||
{ priority: 'high', concurrency: 2 },
|
||||
this.handler
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers sending invoice mail.
|
||||
*/
|
||||
private handler = async (job, done: Function) => {
|
||||
const { tenantId, ruleId, transactionsCriteria } = job.attrs.data;
|
||||
const regonizeTransactions = Container.get(RecognizeTranasctionsService);
|
||||
const revertRegonizedTransactions = Container.get(
|
||||
RevertRecognizedTransactions
|
||||
);
|
||||
|
||||
try {
|
||||
await revertRegonizedTransactions.revertRecognizedTransactions(
|
||||
tenantId,
|
||||
ruleId,
|
||||
transactionsCriteria
|
||||
);
|
||||
await regonizeTransactions.recognizeTransactions(
|
||||
tenantId,
|
||||
ruleId,
|
||||
transactionsCriteria
|
||||
);
|
||||
done();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
done(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import Container, { Service } from 'typedi';
|
||||
import { RevertRecognizedTransactions } from '../RevertRecognizedTransactions';
|
||||
|
||||
@Service()
|
||||
export class RevertRegonizeTransactionsJob {
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor(agenda) {
|
||||
agenda.define(
|
||||
'revert-recognized-uncategorized-transactions-job',
|
||||
{ priority: 'high', concurrency: 2 },
|
||||
this.handler
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers sending invoice mail.
|
||||
*/
|
||||
private handler = async (job, done: Function) => {
|
||||
const { tenantId, ruleId, transactionsCriteria } = job.attrs.data;
|
||||
const revertRegonizedTransactions = Container.get(
|
||||
RevertRecognizedTransactions
|
||||
);
|
||||
|
||||
try {
|
||||
await revertRegonizedTransactions.revertRecognizedTransactions(
|
||||
tenantId,
|
||||
ruleId,
|
||||
transactionsCriteria
|
||||
);
|
||||
done();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
done(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -62,6 +62,7 @@ export class CreateBankRuleService {
|
||||
await this.eventPublisher.emitAsync(events.bankRules.onCreated, {
|
||||
tenantId,
|
||||
createRuleDTO,
|
||||
bankRule,
|
||||
trx,
|
||||
} as IBankRuleEventCreatedPayload);
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
|
||||
@Service()
|
||||
export class UnlinkBankRuleRecognizedTransactions {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
/**
|
||||
* Unlinks the given bank rule out of recognized transactions.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} bankRuleId - Bank rule id.
|
||||
* @param {Knex.Transaction} trx - Knex transaction.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async unlinkBankRuleOutRecognizedTransactions(
|
||||
tenantId: number,
|
||||
bankRuleId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> {
|
||||
const { UncategorizedCashflowTransaction, RecognizedBankTransaction } =
|
||||
this.tenancy.models(tenantId);
|
||||
|
||||
return this.uow.withTransaction(
|
||||
tenantId,
|
||||
async (trx: Knex.Transaction) => {
|
||||
// Retrieves all the recognized transactions of the banbk rule.
|
||||
const recognizedTransactions = await RecognizedBankTransaction.query(
|
||||
trx
|
||||
).where('bankRuleId', bankRuleId);
|
||||
|
||||
const uncategorizedTransactionIds = recognizedTransactions.map(
|
||||
(r) => r.uncategorizedTransactionId
|
||||
);
|
||||
// Unlink the recongized transactions out of uncategorized transactions.
|
||||
await UncategorizedCashflowTransaction.query(trx)
|
||||
.whereIn('id', uncategorizedTransactionIds)
|
||||
.patch({
|
||||
recognizedTransactionId: null,
|
||||
});
|
||||
// Delete the recognized bank transactions that assocaited to bank rule.
|
||||
await RecognizedBankTransaction.query(trx)
|
||||
.where({ bankRuleId })
|
||||
.delete();
|
||||
},
|
||||
trx
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import { UnlinkBankRuleRecognizedTransactions } from '../UnlinkBankRuleRecognizedTransactions';
|
||||
import { IBankRuleEventDeletingPayload } from '../types';
|
||||
import { RevertRecognizedTransactions } from '../../RegonizeTranasctions/RevertRecognizedTransactions';
|
||||
|
||||
@Service()
|
||||
export class UnlinkBankRuleOnDeleteBankRule {
|
||||
@Inject()
|
||||
private unlinkBankRule: UnlinkBankRuleRecognizedTransactions;
|
||||
private revertRecognizedTransactionsService: RevertRecognizedTransactions;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -26,7 +26,7 @@ export class UnlinkBankRuleOnDeleteBankRule {
|
||||
tenantId,
|
||||
ruleId,
|
||||
}: IBankRuleEventDeletingPayload) {
|
||||
await this.unlinkBankRule.unlinkBankRuleOutRecognizedTransactions(
|
||||
await this.revertRecognizedTransactionsService.revertRecognizedTransactions(
|
||||
tenantId,
|
||||
ruleId
|
||||
);
|
||||
|
||||
@@ -30,6 +30,7 @@ export enum BankRuleApplyIfTransactionType {
|
||||
}
|
||||
|
||||
export interface IBankRule {
|
||||
id?: number;
|
||||
name: string;
|
||||
order?: number;
|
||||
applyIfAccountId: number;
|
||||
@@ -71,8 +72,6 @@ export interface IBankRuleCommonDTO {
|
||||
assignAccountId: number;
|
||||
assignPayee?: string;
|
||||
assignMemo?: string;
|
||||
|
||||
recognition?: boolean;
|
||||
}
|
||||
|
||||
export interface ICreateBankRuleDTO extends IBankRuleCommonDTO {}
|
||||
@@ -86,6 +85,7 @@ export interface IBankRuleEventCreatingPayload {
|
||||
export interface IBankRuleEventCreatedPayload {
|
||||
tenantId: number;
|
||||
createRuleDTO: ICreateBankRuleDTO;
|
||||
bankRule: IBankRule;
|
||||
trx?: Knex.Transaction;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@blueprintjs/colors": "4.1.19",
|
||||
"@blueprintjs/core": "^4.20.2",
|
||||
"@blueprintjs/datetime": "^4.4.37",
|
||||
"@blueprintjs/popover2": "^0.11.1",
|
||||
"@blueprintjs/popover2": "^1.14.11",
|
||||
"@blueprintjs/select": "^4.9.24",
|
||||
"@blueprintjs/table": "^4.10.12",
|
||||
"@blueprintjs/timezone": "^4.5.43",
|
||||
|
||||
@@ -43,6 +43,7 @@ function RecognizedTransactionsTableBoot({
|
||||
hasNextPage: hasUncategorizedTransactionsNextPage,
|
||||
} = useRecognizedBankTransactionsInfinity({
|
||||
page_size: 50,
|
||||
account_id: accountId,
|
||||
});
|
||||
// Memorized the cashflow account transactions.
|
||||
const recognizedTransactions = React.useMemo(
|
||||
|
||||
@@ -10,18 +10,16 @@ import {
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
} from '@/components';
|
||||
import { useInvoiceAggregatedTaxRates, useInvoiceTotals } from './utils';
|
||||
import { useInvoiceAggregatedTaxRates } from './utils';
|
||||
import { TaxType } from '@/interfaces/TaxRates';
|
||||
import {
|
||||
InvoiceDueAmountFormatted,
|
||||
InvoicePaidAmountFormatted,
|
||||
InvoiceSubTotalFormatted,
|
||||
InvoiceTotalFormatted,
|
||||
} from './components';
|
||||
|
||||
export function InvoiceFormFooterRight() {
|
||||
// Calculate the total due amount of invoice entries.
|
||||
const {
|
||||
formattedSubtotal,
|
||||
formattedTotal,
|
||||
formattedDueTotal,
|
||||
formattedPaymentTotal,
|
||||
} = useInvoiceTotals();
|
||||
|
||||
const {
|
||||
values: { inclusive_exclusive_tax, currency_code },
|
||||
} = useFormikContext();
|
||||
@@ -38,7 +36,7 @@ export function InvoiceFormFooterRight() {
|
||||
: 'Subtotal'}
|
||||
</>
|
||||
}
|
||||
value={formattedSubtotal}
|
||||
value={<InvoiceSubTotalFormatted />}
|
||||
/>
|
||||
{taxEntries.map((tax, index) => (
|
||||
<TotalLine
|
||||
@@ -50,18 +48,18 @@ export function InvoiceFormFooterRight() {
|
||||
))}
|
||||
<TotalLine
|
||||
title={`Total (${currency_code})`}
|
||||
value={formattedTotal}
|
||||
value={<InvoiceTotalFormatted />}
|
||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'invoice_form.label.payment_amount'} />}
|
||||
value={formattedPaymentTotal}
|
||||
value={<InvoicePaidAmountFormatted />}
|
||||
borderStyle={TotalLineBorderStyle.None}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'invoice_form.label.due_amount'} />}
|
||||
value={formattedDueTotal}
|
||||
value={<InvoiceDueAmountFormatted />}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
</InvoiceTotalLines>
|
||||
|
||||
@@ -8,7 +8,7 @@ import InvoiceFormHeaderFields from './InvoiceFormHeaderFields';
|
||||
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { PageFormBigNumber } from '@/components';
|
||||
import { useInvoiceSubtotal } from './utils';
|
||||
import { useInvoiceDueAmount } from './utils';
|
||||
|
||||
/**
|
||||
* Invoice form header section.
|
||||
@@ -32,7 +32,7 @@ function InvoiceFormBigTotal() {
|
||||
} = useFormikContext();
|
||||
|
||||
// Calculate the total due amount of invoice entries.
|
||||
const totalDueAmount = useInvoiceSubtotal();
|
||||
const totalDueAmount = useInvoiceDueAmount();
|
||||
|
||||
return (
|
||||
<PageFormBigNumber
|
||||
|
||||
@@ -4,14 +4,21 @@ import intl from 'react-intl-universal';
|
||||
import * as R from 'ramda';
|
||||
import { Button } from '@blueprintjs/core';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { ExchangeRateInputGroup } from '@/components';
|
||||
import { ExchangeRateInputGroup, FormatNumber } from '@/components';
|
||||
import { useCurrentOrganization } from '@/hooks/state';
|
||||
import { useInvoiceIsForeignCustomer, useInvoiceTotal } from './utils';
|
||||
import withSettings from '@/containers/Settings/withSettings';
|
||||
import {
|
||||
useInvoiceCurrencyCode,
|
||||
useInvoiceDueAmount,
|
||||
useInvoiceIsForeignCustomer,
|
||||
useInvoicePaidAmount,
|
||||
useInvoiceSubtotal,
|
||||
useInvoiceTotal,
|
||||
} from './utils';
|
||||
import { useUpdateEffect } from '@/hooks';
|
||||
import { transactionNumber } from '@/utils';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
import withSettings from '@/containers/Settings/withSettings';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import {
|
||||
useSyncExRateToForm,
|
||||
withExchangeRateFetchingLoading,
|
||||
@@ -109,3 +116,47 @@ export const InvoiceExchangeRateSync = R.compose(withDialogActions)(
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
*Renders the invoice formatted total.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const InvoiceTotalFormatted = () => {
|
||||
const currencyCode = useInvoiceCurrencyCode();
|
||||
const total = useInvoiceTotal();
|
||||
|
||||
return <FormatNumber value={total} currency={currencyCode} />;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the invoice formatted subtotal.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const InvoiceSubTotalFormatted = () => {
|
||||
const currencyCode = useInvoiceCurrencyCode();
|
||||
const subTotal = useInvoiceSubtotal();
|
||||
|
||||
return <FormatNumber value={subTotal} currency={currencyCode} />;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the invoice formatted due amount.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const InvoiceDueAmountFormatted = () => {
|
||||
const currencyCode = useInvoiceCurrencyCode();
|
||||
const dueAmount = useInvoiceDueAmount();
|
||||
|
||||
return <FormatNumber value={dueAmount} currency={currencyCode} />;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the invoice formatted paid amount.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const InvoicePaidAmountFormatted = () => {
|
||||
const currencyCode = useInvoiceCurrencyCode();
|
||||
const paidAmount = useInvoicePaidAmount();
|
||||
|
||||
return <FormatNumber value={paidAmount} currency={currencyCode} />;
|
||||
};
|
||||
|
||||
@@ -269,59 +269,6 @@ export const useInvoiceSubtotal = () => {
|
||||
return React.useMemo(() => getEntriesTotal(entries), [entries]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retreives the invoice totals.
|
||||
*/
|
||||
export const useInvoiceTotals = () => {
|
||||
const {
|
||||
values: { entries, currency_code: currencyCode },
|
||||
} = useFormikContext();
|
||||
|
||||
// Retrieves the invoice entries total.
|
||||
const total = React.useMemo(() => getEntriesTotal(entries), [entries]);
|
||||
|
||||
const total_ = useInvoiceTotal();
|
||||
|
||||
// Retrieves the formatted total money.
|
||||
const formattedTotal = React.useMemo(
|
||||
() => formattedAmount(total_, currencyCode),
|
||||
[total_, currencyCode],
|
||||
);
|
||||
// Retrieves the formatted subtotal.
|
||||
const formattedSubtotal = React.useMemo(
|
||||
() => formattedAmount(total, currencyCode, { money: false }),
|
||||
[total, currencyCode],
|
||||
);
|
||||
// Retrieves the payment total.
|
||||
const paymentTotal = React.useMemo(() => 0, []);
|
||||
|
||||
// Retireves the formatted payment total.
|
||||
const formattedPaymentTotal = React.useMemo(
|
||||
() => formattedAmount(paymentTotal, currencyCode),
|
||||
[paymentTotal, currencyCode],
|
||||
);
|
||||
// Retrieves the formatted due total.
|
||||
const dueTotal = React.useMemo(
|
||||
() => total - paymentTotal,
|
||||
[total, paymentTotal],
|
||||
);
|
||||
// Retrieves the formatted due total.
|
||||
const formattedDueTotal = React.useMemo(
|
||||
() => formattedAmount(dueTotal, currencyCode),
|
||||
[dueTotal, currencyCode],
|
||||
);
|
||||
|
||||
return {
|
||||
total,
|
||||
paymentTotal,
|
||||
dueTotal,
|
||||
formattedTotal,
|
||||
formattedSubtotal,
|
||||
formattedPaymentTotal,
|
||||
formattedDueTotal,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the invoice has foreign customer.
|
||||
* @returns {boolean}
|
||||
@@ -409,14 +356,25 @@ export const useInvoiceTotal = () => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the paid amount of the invoice.
|
||||
* @returns {number}
|
||||
*/
|
||||
export const useInvoicePaidAmount = () => {
|
||||
const { invoice } = useInvoiceFormContext();
|
||||
|
||||
return invoice?.payment_amount || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retreives the invoice due amount.
|
||||
* @returns {number}
|
||||
*/
|
||||
export const useInvoiceDueAmount = () => {
|
||||
const total = useInvoiceTotal();
|
||||
const paidAmount = useInvoicePaidAmount();
|
||||
|
||||
return total;
|
||||
return Math.max(total - paidAmount, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -438,3 +396,13 @@ export const useIsInvoiceTaxExclusive = () => {
|
||||
|
||||
return values.inclusive_exclusive_tax === TaxType.Exclusive;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the invoice currency code.
|
||||
* @returns {string}
|
||||
*/
|
||||
export const useInvoiceCurrencyCode = () => {
|
||||
const { values } = useFormikContext();
|
||||
|
||||
return values.currency_code;
|
||||
};
|
||||
|
||||
@@ -276,6 +276,11 @@ const onValidateExcludeUncategorizedTransaction = (queryClient) => {
|
||||
|
||||
// invalidate bank account summary.
|
||||
queryClient.invalidateQueries(BANK_QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
||||
|
||||
// Invalidate the recognized transactions.
|
||||
queryClient.invalidateQueries([
|
||||
BANK_QUERY_KEY.RECOGNIZED_BANK_TRANSACTIONS_INFINITY,
|
||||
]);
|
||||
};
|
||||
|
||||
type ExcludeUncategorizedTransactionValue = number;
|
||||
@@ -312,10 +317,6 @@ export function useExcludeUncategorizedTransaction(
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||
queryClient.invalidateQueries([
|
||||
BANK_QUERY_KEY.BANK_ACCOUNT_SUMMARY_META,
|
||||
id,
|
||||
]);
|
||||
},
|
||||
...options,
|
||||
},
|
||||
@@ -357,10 +358,6 @@ export function useUnexcludeUncategorizedTransaction(
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||
queryClient.invalidateQueries([
|
||||
BANK_QUERY_KEY.BANK_ACCOUNT_SUMMARY_META,
|
||||
id,
|
||||
]);
|
||||
},
|
||||
...options,
|
||||
},
|
||||
@@ -649,7 +646,6 @@ export function useRecognizedBankTransactionsInfinity(
|
||||
getPreviousPageParam: (firstPage) => firstPage.pagination.page - 1,
|
||||
getNextPageParam: (lastPage) => {
|
||||
const { pagination } = lastPage;
|
||||
|
||||
return pagination.total > pagination.page_size * pagination.page
|
||||
? lastPage.pagination.page + 1
|
||||
: undefined;
|
||||
|
||||
79
pnpm-lock.yaml
generated
79
pnpm-lock.yaml
generated
@@ -481,8 +481,8 @@ importers:
|
||||
specifier: ^4.4.37
|
||||
version: 4.4.37(@types/react@16.14.60)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/popover2':
|
||||
specifier: ^0.11.1
|
||||
version: 0.11.4(react-dom@18.3.1)(react@18.3.1)
|
||||
specifier: ^1.14.11
|
||||
version: 1.14.11(@types/react@16.14.60)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/select':
|
||||
specifier: ^4.9.24
|
||||
version: 4.9.24(@types/react@16.14.60)(react-dom@18.3.1)(react@18.3.1)
|
||||
@@ -3136,29 +3136,6 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/core@3.54.0(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-u2c1s6MNn0ocxhnC6CuiG5g3KV6b4cKUvSobznepA9SC3/AL1s3XOvT7DLWoHRv2B/vBOHFYEDzLw2/vlcGGZg==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
react: ^15.3.0 || 16 || 17
|
||||
react-dom: ^15.3.0 || 16 || 17
|
||||
dependencies:
|
||||
'@blueprintjs/colors': 4.1.19
|
||||
'@blueprintjs/icons': 3.33.0
|
||||
'@juggle/resize-observer': 3.4.0
|
||||
'@types/dom4': 2.0.4
|
||||
classnames: 2.5.1
|
||||
dom4: 2.1.6
|
||||
normalize.css: 8.0.1
|
||||
popper.js: 1.16.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-lifecycles-compat: 3.0.4
|
||||
react-popper: 1.3.11(react@18.3.1)
|
||||
react-transition-group: 2.9.0(react-dom@18.3.1)(react@18.3.1)
|
||||
tslib: 2.3.1
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/core@4.20.2(@types/react@16.14.60)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-5v4Nr0jozfAjiOkjY4zvt1XSpt4ldnrSaxtwo506S2cxJYfwFeMTmDshXNPFcc8L1fjZMxi0IWI2WABXzZXS6w==}
|
||||
hasBin: true
|
||||
@@ -3205,13 +3182,6 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/icons@3.33.0:
|
||||
resolution: {integrity: sha512-Q6qoSDIm0kRYQZISm59UUcDCpV3oeHulkLuh3bSlw0HhcSjvEQh2PSYbtaifM60Q4aK4PCd6bwJHg7lvF1x5fQ==}
|
||||
dependencies:
|
||||
classnames: 2.5.1
|
||||
tslib: 2.3.1
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/icons@4.16.0:
|
||||
resolution: {integrity: sha512-cyfgjUZcZCtQrXWUV8FwqYTFEzduV4a0N7yhOU38jY+cBRCLu/sDrD0Osvfk4DGRvNe4YjY7pohVLFSxpg68Uw==}
|
||||
dependencies:
|
||||
@@ -3220,23 +3190,6 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/popover2@0.11.4(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-KTietd+thBzeen9yP0WWzHJDAaqoey1H3AXvjaF94ypQQKdUaIzSmIop9Z3lJK44ynlYiI+dSzFYQbRdbpd0/w==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17
|
||||
dependencies:
|
||||
'@blueprintjs/core': 3.54.0(react-dom@18.3.1)(react@18.3.1)
|
||||
'@popperjs/core': 2.11.8
|
||||
classnames: 2.5.1
|
||||
dom4: 2.1.6
|
||||
react: 18.3.1
|
||||
react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1)(react@18.3.1)
|
||||
resize-observer-polyfill: 1.5.1
|
||||
tslib: 1.13.0
|
||||
transitivePeerDependencies:
|
||||
- react-dom
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/popover2@1.14.11(@types/react@16.14.60)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-5XAjeb2mlWjYXC0pqrNDLzHSsX85Zaiv8jixxUN9abarMUUFKGATgGF8MRsWTLAW94Gli6CB1lzVkrYkRHHf6Q==}
|
||||
peerDependencies:
|
||||
@@ -10982,12 +10935,6 @@ packages:
|
||||
utila: 0.4.0
|
||||
dev: false
|
||||
|
||||
/dom-helpers@3.4.0:
|
||||
resolution: {integrity: sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.5
|
||||
dev: false
|
||||
|
||||
/dom-helpers@5.2.1:
|
||||
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
|
||||
dependencies:
|
||||
@@ -21862,20 +21809,6 @@ packages:
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/react-transition-group@2.9.0(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==}
|
||||
peerDependencies:
|
||||
react: '>=15.0.0'
|
||||
react-dom: '>=15.0.0'
|
||||
dependencies:
|
||||
dom-helpers: 3.4.0
|
||||
loose-envify: 1.4.0
|
||||
prop-types: 15.8.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-lifecycles-compat: 3.0.4
|
||||
dev: false
|
||||
|
||||
/react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||
peerDependencies:
|
||||
@@ -24785,17 +24718,9 @@ packages:
|
||||
strip-bom: 3.0.0
|
||||
dev: true
|
||||
|
||||
/tslib@1.13.0:
|
||||
resolution: {integrity: sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==}
|
||||
dev: false
|
||||
|
||||
/tslib@1.14.1:
|
||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||
|
||||
/tslib@2.3.1:
|
||||
resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==}
|
||||
dev: false
|
||||
|
||||
/tslib@2.5.3:
|
||||
resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==}
|
||||
dev: false
|
||||
|
||||
Reference in New Issue
Block a user