mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
fix bugs in sales and purchases API.
This commit is contained in:
@@ -1,247 +0,0 @@
|
||||
import express from 'express';
|
||||
import {
|
||||
check,
|
||||
query,
|
||||
param,
|
||||
validationResult,
|
||||
} from 'express-validator';
|
||||
import { pick, difference, groupBy } from 'lodash';
|
||||
import asyncMiddleware from "@/http/middleware/asyncMiddleware";
|
||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||
import Budget from '@/models/Budget';
|
||||
import BudgetEntry from '@/models/BudgetEntry';
|
||||
import Account from '@/models/Account';
|
||||
import moment from '@/services/Moment';
|
||||
import BudgetEntriesSet from '@/collection/BudgetEntriesSet';
|
||||
import AccountType from '@/models/AccountType';
|
||||
import NestedSet from '@/collection/NestedSet';
|
||||
import { dateRangeFormat } from '@/utils';
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = express.Router();
|
||||
|
||||
router.use(JWTAuth);
|
||||
|
||||
router.post('/',
|
||||
this.newBudget.validation,
|
||||
asyncMiddleware(this.newBudget.handler));
|
||||
|
||||
router.get('/:id',
|
||||
this.getBudget.validation,
|
||||
asyncMiddleware(this.getBudget.handler));
|
||||
|
||||
router.get('/:id',
|
||||
this.deleteBudget.validation,
|
||||
asyncMiddleware(this.deleteBudget.handler));
|
||||
|
||||
router.get('/',
|
||||
this.listBudgets.validation,
|
||||
asyncMiddleware(this.listBudgets.handler));
|
||||
|
||||
return router;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve budget details of the given id.
|
||||
*/
|
||||
getBudget: {
|
||||
validation: [
|
||||
param('id').exists().isNumeric().toInt(),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
const { id } = req.params;
|
||||
const budget = await Budget.query().findById(id);
|
||||
|
||||
if (!budget) {
|
||||
return res.status(404).send({
|
||||
errors: [{ type: 'budget.not.found', code: 100 }],
|
||||
});
|
||||
}
|
||||
const accountTypes = await AccountType.query().where('balance_sheet', true);
|
||||
|
||||
const [budgetEntries, accounts] = await Promise.all([
|
||||
BudgetEntry.query().where('budget_id', budget.id),
|
||||
Account.query().whereIn('account_type_id', accountTypes.map((a) => a.id)),
|
||||
]);
|
||||
|
||||
const accountsNestedSet = new NestedSet(accounts);
|
||||
|
||||
const columns = [];
|
||||
const fromDate = moment(budget.year).startOf('year')
|
||||
.add(budget.rangeOffset, budget.rangeBy).toDate();
|
||||
|
||||
const toDate = moment(budget.year).endOf('year').toDate();
|
||||
|
||||
const dateRange = moment.range(fromDate, toDate);
|
||||
const dateRangeCollection = Array.from(dateRange.by(budget.rangeBy, {
|
||||
step: budget.rangeIncrement, excludeEnd: false, excludeStart: false,
|
||||
}));
|
||||
|
||||
dateRangeCollection.forEach((date) => {
|
||||
columns.push(date.format(dateRangeFormat(budget.rangeBy)));
|
||||
});
|
||||
const budgetEntriesSet = BudgetEntriesSet.from(budgetEntries, {
|
||||
orderSize: columns.length,
|
||||
});
|
||||
budgetEntriesSet.setZeroPlaceholder();
|
||||
budgetEntriesSet.calcTotalSummary();
|
||||
|
||||
return res.status(200).send({
|
||||
columns,
|
||||
accounts: budgetEntriesSet.toArray(),
|
||||
total: budgetEntriesSet.toArrayTotalSummary(),
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete the given budget.
|
||||
*/
|
||||
deleteBudget: {
|
||||
validation: [
|
||||
param('id').exists(),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
const budget = await Budget.query().findById(id);
|
||||
|
||||
if (!budget) {
|
||||
return res.status(404).send({
|
||||
errors: [{ type: 'budget.not.found', code: 100 }],
|
||||
});
|
||||
}
|
||||
await BudgetEntry.query().where('budget_id', budget.id).delete();
|
||||
await budget.delete();
|
||||
|
||||
return res.status(200).send();
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the new budget.
|
||||
*/
|
||||
newBudget: {
|
||||
validation: [
|
||||
check('name').exists(),
|
||||
check('fiscal_year').exists(),
|
||||
check('period').exists().isIn(['year', 'month', 'quarter', 'half-year']),
|
||||
check('accounts_type').exists().isIn(['balance_sheet', 'profit_loss']),
|
||||
check('accounts').isArray(),
|
||||
check('accounts.*.account_id').exists().isNumeric().toInt(),
|
||||
check('accounts.*.entries').exists().isArray(),
|
||||
check('accounts.*.entries.*.amount').exists().isNumeric().toFloat(),
|
||||
check('accounts.*.entries.*.order').exists().isNumeric().toInt(),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
|
||||
const form = { ...req.body };
|
||||
const submitAccountsIds = form.accounts.map((a) => a.account_id);
|
||||
const storedAccounts = await Account.query().whereIn('id', submitAccountsIds);
|
||||
const storedAccountsIds = storedAccounts.map((a) => a.id);
|
||||
|
||||
const errorReasons = [];
|
||||
const notFoundAccountsIds = difference(submitAccountsIds, storedAccountsIds);
|
||||
|
||||
if (notFoundAccountsIds.length > 0) {
|
||||
errorReasons.push({
|
||||
type: 'ACCOUNT.NOT.FOUND', code: 200, accounts: notFoundAccountsIds,
|
||||
});
|
||||
}
|
||||
if (errorReasons.length > 0) {
|
||||
return res.status(400).send({ errors: errorReasons });
|
||||
}
|
||||
// validation entries order.
|
||||
const budget = await Budget.query().insert({
|
||||
...pick(form, ['name', 'fiscal_year', 'period', 'accounts_type']),
|
||||
});
|
||||
|
||||
const promiseOpers = [];
|
||||
|
||||
form.accounts.forEach((account) => {
|
||||
account.entries.forEach((entry) => {
|
||||
const budgetEntry = BudgetEntry.query().insert({
|
||||
account_id: account.account_id,
|
||||
amount: entry.amount,
|
||||
order: entry.order,
|
||||
});
|
||||
promiseOpers.push(budgetEntry);
|
||||
});
|
||||
});
|
||||
await Promise.all(promiseOpers);
|
||||
|
||||
return res.status(200).send({ id: budget.id });
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* List of paginated budgets items.
|
||||
*/
|
||||
listBudgets: {
|
||||
validation: [
|
||||
query('year').optional(),
|
||||
query('income_statement').optional().isBoolean().toBoolean(),
|
||||
query('profit_loss').optional().isBoolean().toBoolean(),
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').isNumeric().toInt(),
|
||||
query('custom_view_id').optional().isNumeric().toInt(),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
|
||||
const filter = {
|
||||
page_size: 10,
|
||||
page: 1,
|
||||
...req.query,
|
||||
};
|
||||
const budgets = await Budget.query().runBefore((result, q) => {
|
||||
if (filter.profit_loss) {
|
||||
q.modify('filterByYear', filter.year);
|
||||
}
|
||||
if (filter.income_statement) {
|
||||
q.modify('filterByIncomeStatement', filter.income_statement);
|
||||
}
|
||||
if (filter.profit_loss) {
|
||||
q.modify('filterByProfitLoss', filter.profit_loss);
|
||||
}
|
||||
q.page(filter.page, filter.page_size);
|
||||
return result;
|
||||
});
|
||||
return res.status(200).send({
|
||||
items: budgets.items,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,122 +0,0 @@
|
||||
import express from 'express';
|
||||
import { query, validationResult } from 'express-validator';
|
||||
import moment from 'moment';
|
||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import Budget from '@/models/Budget';
|
||||
import Account from '@/models/Account';
|
||||
import AccountType from '@/models/AccountType';
|
||||
import NestedSet from '@/collection/NestedSet';
|
||||
import BudgetEntry from '@/models/BudgetEntry';
|
||||
import { dateRangeFormat } from '@/utils';
|
||||
|
||||
export default {
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = express.Router();
|
||||
|
||||
router.use(jwtAuth);
|
||||
|
||||
router.get('/budget_verses_actual/:reportId',
|
||||
this.budgetVersesActual.validation,
|
||||
asyncMiddleware(this.budgetVersesActual.handler));
|
||||
|
||||
return router;
|
||||
},
|
||||
|
||||
budgetVersesActual: {
|
||||
validation: [
|
||||
query('basis').optional().isIn(['cash', 'accural']),
|
||||
query('period').optional(),
|
||||
query('active_accounts').optional().toBoolean(),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
const { reportId } = req.params;
|
||||
const form = { ...req.body };
|
||||
const errorReasons = [];
|
||||
|
||||
const budget = await Budget.query().findById(reportId);
|
||||
|
||||
if (!budget) {
|
||||
errorReasons.push({ type: 'BUDGET_NOT_FOUND', code: 100 });
|
||||
}
|
||||
const budgetEntries = await BudgetEntry.query().where('budget_id', budget.id);
|
||||
|
||||
if (errorReasons.length > 0) {
|
||||
return res.status(400).send({ errors: errorReasons });
|
||||
}
|
||||
const accountTypes = await AccountType.query()
|
||||
.where('balance_sheet', budget.accountTypes === 'balance_sheet')
|
||||
.where('income_sheet', budget.accountTypes === 'profit_losss');
|
||||
|
||||
const accounts = await Account.query().runBefore((result, q) => {
|
||||
const accountTypesIds = accountTypes.map((t) => t.id);
|
||||
|
||||
if (accountTypesIds.length > 0) {
|
||||
q.whereIn('account_type_id', accountTypesIds);
|
||||
}
|
||||
q.where('active', form.active_accounts === true);
|
||||
q.withGraphFetched('transactions');
|
||||
});
|
||||
|
||||
// const accountsNestedSet = NestedSet.from(accounts);
|
||||
|
||||
const fromDate = moment(budget.year).startOf('year')
|
||||
.add(budget.rangeOffset, budget.rangeBy).toDate();
|
||||
|
||||
const toDate = moment(budget.year).endOf('year').toDate();
|
||||
|
||||
const dateRange = moment.range(fromDate, toDate);
|
||||
const dateRangeCollection = Array.from(dateRange.by(budget.rangeBy, {
|
||||
step: budget.rangeIncrement, excludeEnd: false, excludeStart: false,
|
||||
}));
|
||||
|
||||
// // const accounts = {
|
||||
// // assets: [
|
||||
// // {
|
||||
// // name: '',
|
||||
// // code: '',
|
||||
// // totalEntries: [
|
||||
// // {
|
||||
|
||||
// // }
|
||||
// // ],
|
||||
// // children: [
|
||||
// // {
|
||||
// // name: '',
|
||||
// // code: '',
|
||||
// // entries: [
|
||||
// // {
|
||||
|
||||
// // }
|
||||
// // ]
|
||||
// // }
|
||||
// // ]
|
||||
// // }
|
||||
// // ]
|
||||
// // }
|
||||
|
||||
return res.status(200).send({
|
||||
columns: dateRangeCollection.map(d => d.format(dateRangeFormat(budget.rangeBy))),
|
||||
// accounts: {
|
||||
// asset: [],
|
||||
// liabilities: [],
|
||||
// equaity: [],
|
||||
|
||||
// income: [],
|
||||
// expenses: [],
|
||||
// }
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
export default {
|
||||
|
||||
|
||||
router() {
|
||||
|
||||
},
|
||||
|
||||
addExchangePrice: {
|
||||
validation: {
|
||||
|
||||
},
|
||||
async handler(req, res) {
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -2,15 +2,18 @@
|
||||
import { Router } from 'express';
|
||||
import { check, param, query, ValidationChain } from 'express-validator';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import BillPaymentsService from '@/services/Purchases/BillPayments';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
import { IBillPaymentEntry, IBillPayment } from '@/interfaces/BillPayment';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing';
|
||||
|
||||
/**
|
||||
* Bills payments controller.
|
||||
* @controller
|
||||
*/
|
||||
export default class BillsPayments extends BaseController {
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -21,38 +24,43 @@ export default class BillsPayments extends BaseController {
|
||||
router.post('/', [
|
||||
...this.billPaymentSchemaValidation,
|
||||
],
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.validateBillPaymentVendorExistance),
|
||||
asyncMiddleware(this.validatePaymentAccount),
|
||||
asyncMiddleware(this.validatePaymentNumber),
|
||||
asyncMiddleware(this.validateItemsIds),
|
||||
asyncMiddleware(this.validateEntriesBillsExistance),
|
||||
asyncMiddleware(this.validateVendorsDueAmount),
|
||||
asyncMiddleware(this.createBillPayment),
|
||||
);
|
||||
router.post('/:id', [
|
||||
...this.billPaymentSchemaValidation,
|
||||
...this.specificBillPaymentValidateSchema,
|
||||
],
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.validateBillPaymentVendorExistance),
|
||||
asyncMiddleware(this.validatePaymentAccount),
|
||||
asyncMiddleware(this.validatePaymentNumber),
|
||||
asyncMiddleware(this.validateItemsIds),
|
||||
asyncMiddleware(this.validateEntriesIds),
|
||||
asyncMiddleware(this.validateEntriesBillsExistance),
|
||||
asyncMiddleware(this.validateVendorsDueAmount),
|
||||
asyncMiddleware(this.editBillPayment),
|
||||
)
|
||||
router.delete('/:id',
|
||||
this.specificBillPaymentValidateSchema,
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.validateBillPaymentExistance),
|
||||
asyncMiddleware(this.deleteBillPayment),
|
||||
);
|
||||
router.get('/:id',
|
||||
this.specificBillPaymentValidateSchema,
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.validateBillPaymentExistance),
|
||||
asyncMiddleware(this.getBillPayment),
|
||||
);
|
||||
router.get('/',
|
||||
this.listingValidationSchema,
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.getBillsPayments)
|
||||
);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -69,13 +77,8 @@ export default class BillsPayments extends BaseController {
|
||||
check('reference').optional().trim().escape(),
|
||||
|
||||
check('entries').exists().isArray({ min: 1 }),
|
||||
check('entries.*.id').optional().isNumeric().toInt(),
|
||||
check('entries.*.index').exists().isNumeric().toInt(),
|
||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||
check('entries.*.discount').optional().isNumeric().toFloat(),
|
||||
check('entries.*.description').optional().trim().escape(),
|
||||
check('entries.*.bill_id').exists().isNumeric().toInt(),
|
||||
check('entries.*.payment_amount').exists().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -97,7 +100,7 @@ export default class BillsPayments extends BaseController {
|
||||
static async validateBillPaymentVendorExistance(req: Request, res: Response, next: any ) {
|
||||
const billPayment = req.body;
|
||||
const { Vendor } = req.models;
|
||||
const isVendorExists = await Vendor.query('id', billPayment.vendor_id).first();
|
||||
const isVendorExists = await Vendor.query().findById(billPayment.vendor_id);
|
||||
|
||||
if (!isVendorExists) {
|
||||
return res.status(400).send({
|
||||
@@ -121,7 +124,7 @@ export default class BillsPayments extends BaseController {
|
||||
errors: [{ type: 'BILL.PAYMENT.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
next(req, res, next);
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,14 +135,15 @@ export default class BillsPayments extends BaseController {
|
||||
*/
|
||||
static async validatePaymentAccount(req: Request, res: Response, next: any) {
|
||||
const billPayment = { ...req.body };
|
||||
const isAccountExists = AccountsService.isAccountExists(billPayment);
|
||||
|
||||
const isAccountExists = await AccountsService.isAccountExists(
|
||||
billPayment.payment_account_id
|
||||
);
|
||||
if (!isAccountExists) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
next(req, res, next);
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,61 +153,75 @@ export default class BillsPayments extends BaseController {
|
||||
* @param {Function} res
|
||||
*/
|
||||
static async validatePaymentNumber(req: Request, res: Response, next: any) {
|
||||
const { BillPayment } = req.models;
|
||||
const billPayment = { ...req.body };
|
||||
const isNumberExists = await BillPaymentsService.isBillNoExists(billPayment);
|
||||
|
||||
if (!isNumberExists) {
|
||||
const foundBillPayment = await BillPayment.query()
|
||||
.where('payment_number', billPayment.payment_number)
|
||||
.first();
|
||||
|
||||
if (foundBillPayment) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'PAYMENT.NUMBER.NOT.UNIQUE', code: 300 }],
|
||||
});
|
||||
}
|
||||
next(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* validate entries items ids existance on the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Function} next
|
||||
*/
|
||||
static async validateItemsIds(req: Request, res: Response, next: Function) {
|
||||
const billPayment: any = { ...req.body };
|
||||
const itemsIds = billPayment.entries.map((e) => e.item_id);
|
||||
const notFoundItemsIds = await ItemsService.isItemsIdsExists(
|
||||
itemsIds
|
||||
);
|
||||
if (notFoundItemsIds.length > 0) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'ITEMS.IDS.NOT.FOUND', code: 400 }],
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the entries ids in edit bill payment.
|
||||
* Validate whether the entries bills ids exist on the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
static async validateEntriesIds(req: Request, res: Response, next: Function) {
|
||||
const { BillPaymentEntry } = req.models;
|
||||
const { id: billPaymentId } = req.params;
|
||||
const billPayment = { id: billPaymentId, ...req.body };
|
||||
static async validateEntriesBillsExistance(req: Request, res: Response, next: any) {
|
||||
const { Bill } = req.models;
|
||||
const billPayment = { ...req.body };
|
||||
const entriesBillsIds = billPayment.entries.map((e: any) => e.bill_id);
|
||||
|
||||
const entriesIds = billPayment.entries
|
||||
.filter((entry: IBillPaymentEntry) => entry.id)
|
||||
.map((entry: IBillPaymentEntry) => entry.id);
|
||||
const notFoundBillsIds = await Bill.getNotFoundBills(entriesBillsIds);
|
||||
|
||||
const storedEntries = await BillPaymentEntry.tenant().query()
|
||||
.where('bill_payment_id', billPaymentId);
|
||||
|
||||
const storedEntriesIds = storedEntries.map((entry: IBillPaymentEntry) => entry.id);
|
||||
const notFoundEntriesIds = difference(entriesIds, storedEntriesIds);
|
||||
|
||||
if (notFoundEntriesIds.length > 0) {
|
||||
if (notFoundBillsIds.length > 0) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'ENTEIES.IDS.NOT.FOUND', code: 800 }],
|
||||
errors: [{ type: 'BILLS.IDS.NOT.EXISTS', code: 600 }],
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate wether the payment amount bigger than the payable amount.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @return {void}
|
||||
*/
|
||||
static async validateVendorsDueAmount(req: Request, res: Response, next: Function) {
|
||||
const { Bill } = req.models;
|
||||
const billsIds = req.body.entries.map((entry: any) => entry.bill_id);
|
||||
const storedBills = await Bill.query()
|
||||
.whereIn('id', billsIds);
|
||||
|
||||
const storedBillsMap = new Map(
|
||||
storedBills.map((bill: any) => [bill.id, bill]),
|
||||
);
|
||||
interface invalidPaymentAmountError{
|
||||
index: number,
|
||||
due_amount: number
|
||||
};
|
||||
const hasWrongPaymentAmount: invalidPaymentAmountError[] = [];
|
||||
const { entries } = req.body;
|
||||
|
||||
entries.forEach((entry: any, index: number) => {
|
||||
const entryBill = storedBillsMap.get(entry.bill_id);
|
||||
const { dueAmount } = entryBill;
|
||||
|
||||
if (dueAmount < entry.payment_amount) {
|
||||
hasWrongPaymentAmount.push({ index, due_amount: dueAmount });
|
||||
}
|
||||
});
|
||||
if (hasWrongPaymentAmount.length > 0) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'INVALID.BILL.PAYMENT.AMOUNT', code: 400, indexes: hasWrongPaymentAmount }]
|
||||
});
|
||||
}
|
||||
next();
|
||||
@@ -297,7 +315,6 @@ export default class BillsPayments extends BaseController {
|
||||
errors: [{ type: 'BILL.PAYMENTS.RESOURCE.NOT_FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
|
||||
const viewMeta = await View.query()
|
||||
.modify('allMetadata')
|
||||
.modify('specificOrFavourite', filter.custom_view_id)
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import express from 'express';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import { PaymentReceiveEntry } from '@/models';
|
||||
import BaseController from '@/http/controllers/BaseController';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import PaymentReceiveService from '@/services/Sales/PaymentReceive';
|
||||
import PaymentReceiveService from '@/services/Sales/PaymentsReceives';
|
||||
import CustomersService from '@/services/Customers/CustomersService';
|
||||
import SaleInvoicesService from '@/services/Sales/SaleInvoice';
|
||||
import SaleInvoicesService from '@/services/Sales/SalesInvoices';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import { PaymentReceiveEntry } from '@/models';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing';
|
||||
|
||||
/**
|
||||
* Payments receives controller.
|
||||
* @controller
|
||||
*/
|
||||
export default class PaymentReceivesController extends BaseController {
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -27,10 +34,11 @@ export default class PaymentReceivesController extends BaseController {
|
||||
asyncMiddleware(this.validateDepositAccount),
|
||||
asyncMiddleware(this.validateInvoicesIDs),
|
||||
asyncMiddleware(this.validateEntriesIdsExistance),
|
||||
asyncMiddleware(this.editPaymentReceive)
|
||||
asyncMiddleware(this.validateInvoicesPaymentsAmount),
|
||||
asyncMiddleware(this.editPaymentReceive),
|
||||
);
|
||||
router.post(
|
||||
'/',
|
||||
'/',
|
||||
this.newPaymentReceiveValidation,
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.validatePaymentReceiveNoExistance),
|
||||
@@ -38,7 +46,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
asyncMiddleware(this.validateDepositAccount),
|
||||
asyncMiddleware(this.validateInvoicesIDs),
|
||||
asyncMiddleware(this.validateInvoicesPaymentsAmount),
|
||||
asyncMiddleware(this.newPaymentReceive)
|
||||
asyncMiddleware(this.newPaymentReceive),
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
@@ -58,7 +66,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
this.paymentReceiveValidation,
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.validatePaymentReceiveExistance),
|
||||
asyncMiddleware(this.deletePaymentReceive)
|
||||
asyncMiddleware(this.deletePaymentReceive),
|
||||
);
|
||||
return router;
|
||||
}
|
||||
@@ -340,7 +348,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
/**
|
||||
* Payment receive list validation schema.
|
||||
*/
|
||||
static async validatePaymentReceiveList() {
|
||||
static get validatePaymentReceiveList() {
|
||||
return [
|
||||
query('custom_view_id').optional().isNumeric().toInt(),
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
@@ -407,7 +415,8 @@ export default class PaymentReceivesController extends BaseController {
|
||||
const paymentReceives = await PaymentReceive.query().onBuild((builder) => {
|
||||
dynamicListing.buildQuery()(builder);
|
||||
return builder;
|
||||
});
|
||||
}).pagination(filter.page - 1, filter.page_size);
|
||||
|
||||
return res.status(200).send({
|
||||
payment_receives: {
|
||||
...paymentReceives,
|
||||
|
||||
@@ -103,9 +103,10 @@ export default class SalesEstimatesController extends BaseController {
|
||||
return [
|
||||
query('custom_view_id').optional().isNumeric().toInt(),
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -201,7 +202,7 @@ export default class SalesEstimatesController extends BaseController {
|
||||
.filter(e => e.id)
|
||||
.map((e) => e.id);
|
||||
|
||||
const foundEntries = await ItemEntry.query()
|
||||
const foundEntries = await ItemEntry.tenant().query()
|
||||
.whereIn('id', entriesIds)
|
||||
.where('reference_type', 'SaleInvoice')
|
||||
.where('reference_id', saleInvoiceId);
|
||||
@@ -323,7 +324,7 @@ export default class SalesEstimatesController extends BaseController {
|
||||
return res.status(400).send({ errors: errorReasons });
|
||||
}
|
||||
|
||||
const salesEstimates = await SaleEstimate.query().onBuild((query) => {
|
||||
const salesEstimates = await SaleEstimate.query().onBuild((builder) => {
|
||||
dynamicListing.buildQuery()(builder);
|
||||
return builder;
|
||||
}).pagination(filter.page - 1, filter.page_size);
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import express from 'express';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { difference } from 'lodash';
|
||||
import { ItemEntry } from '@/models';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import SaleInvoiceService from '@/services/Sales/SaleInvoice';
|
||||
import SaleInvoiceService from '@/services/Sales/SalesInvoices';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
import CustomersService from '@/services/Customers/CustomersService';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing';
|
||||
import { SaleInvoice } from '../../../models';
|
||||
import { difference } from 'lodash';
|
||||
|
||||
export default class SaleInvoicesController {
|
||||
/**
|
||||
@@ -23,6 +22,7 @@ export default class SaleInvoicesController {
|
||||
'/',
|
||||
this.saleInvoiceValidationSchema,
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.validateInvoiceCustomerExistance),
|
||||
asyncMiddleware(this.validateInvoiceNumberUnique),
|
||||
asyncMiddleware(this.validateInvoiceItemsIdsExistance),
|
||||
asyncMiddleware(this.newSaleInvoice)
|
||||
@@ -35,6 +35,7 @@ export default class SaleInvoicesController {
|
||||
],
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.validateInvoiceExistance),
|
||||
asyncMiddleware(this.validateInvoiceCustomerExistance),
|
||||
asyncMiddleware(this.validateInvoiceNumberUnique),
|
||||
asyncMiddleware(this.validateInvoiceItemsIdsExistance),
|
||||
asyncMiddleware(this.valdiateInvoiceEntriesIdsExistance),
|
||||
@@ -96,12 +97,17 @@ export default class SaleInvoicesController {
|
||||
return [param('id').exists().isNumeric().toInt()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sales invoices list validation schema.
|
||||
*/
|
||||
static get saleInvoiceListValidationSchema() {
|
||||
return [
|
||||
query('custom_view_id').optional().isNumeric().toInt(),
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -145,6 +151,7 @@ export default class SaleInvoicesController {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Validate whether sale invoice number unqiue on the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
@@ -266,8 +273,13 @@ export default class SaleInvoicesController {
|
||||
*/
|
||||
static async editSaleInvoice(req, res) {
|
||||
const { id: saleInvoiceId } = req.params;
|
||||
const saleInvoice = { ...req.body };
|
||||
|
||||
const saleInvoice = {
|
||||
...req.body,
|
||||
entries: req.body.entries.map((entry) => ({
|
||||
...entry,
|
||||
amount: ItemEntry.calcAmount(entry),
|
||||
})),
|
||||
};
|
||||
// Update the given sale invoice details.
|
||||
await SaleInvoiceService.editSaleInvoice(saleInvoiceId, saleInvoice);
|
||||
|
||||
@@ -311,6 +323,8 @@ export default class SaleInvoicesController {
|
||||
const filter = {
|
||||
filter_roles: [],
|
||||
sort_order: 'asc',
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
...req.query,
|
||||
};
|
||||
if (filter.stringified_filter_roles) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import CustomersService from '@/services/Customers/CustomersService';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
import SaleReceiptService from '@/services/Sales/SalesReceipt';
|
||||
import SaleReceiptService from '@/services/Sales/SalesReceipts';
|
||||
import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder';
|
||||
import DynamicListing from '@/services/DynamicListing/DynamicListing';
|
||||
import {
|
||||
@@ -51,7 +51,7 @@ export default class SalesReceiptsController {
|
||||
);
|
||||
router.get(
|
||||
'/',
|
||||
this.listingSalesReceipts,
|
||||
this.listSalesReceiptsValidationSchema,
|
||||
validateMiddleware,
|
||||
asyncMiddleware(this.listingSalesReceipts)
|
||||
);
|
||||
@@ -103,6 +103,8 @@ export default class SalesReceiptsController {
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -272,6 +274,7 @@ export default class SalesReceiptsController {
|
||||
sort_order: 'asc',
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
...req.query,
|
||||
};
|
||||
if (filter.stringified_filter_roles) {
|
||||
filter.filter_roles = JSON.parse(filter.stringified_filter_roles);
|
||||
@@ -312,14 +315,17 @@ export default class SalesReceiptsController {
|
||||
const salesReceipts = await SaleReceipt.query().onBuild((builder) => {
|
||||
builder.withGraphFetched('entries');
|
||||
dynamicListing.buildQuery()(builder);
|
||||
return builder;
|
||||
}).pagination(filter.page - 1, filter.page_size);
|
||||
|
||||
return res.status(200).send({
|
||||
sales_receipts: salesReceipts,
|
||||
...(viewMeta ? {
|
||||
customViewId: viewMeta.id,
|
||||
} : {}),
|
||||
sales_receipts: {
|
||||
...salesReceipts,
|
||||
...(viewMeta ? {
|
||||
view_meta: {
|
||||
customViewId: viewMeta.id,
|
||||
}
|
||||
} : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import express from 'express';
|
||||
import SalesEstimates from './SalesEstimates';
|
||||
import SalesReceipts from './SalesReceipt';
|
||||
import SalesReceipts from './SalesReceipts';
|
||||
import SalesInvoices from './SalesInvoices'
|
||||
import PaymentReceives from './PaymentReceives';
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import express from 'express';
|
||||
|
||||
export default {
|
||||
|
||||
router() {
|
||||
const router = express.Router();
|
||||
|
||||
return router;
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user