mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
feat: Item validate cost, income and inventory account type.
feat: Style sales and purchases forms - 80% progress. feat: Validate purchase-able and sell-able items in invoices and bills. feat: Fix bugs in inventory FIFO/LIFO cost methods.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { check, param, query, oneOf, ValidationChain } from 'express-validator';
|
||||
import { check, param, query, ValidationChain } from 'express-validator';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||
import ItemsService from '@/services/Items/ItemsService';
|
||||
@@ -124,9 +124,6 @@ export default class ItemsController {
|
||||
|
||||
/**
|
||||
* Validate specific item params schema.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
static get validateSpecificItemSchema(): ValidationChain[] {
|
||||
return [
|
||||
@@ -135,6 +132,9 @@ export default class ItemsController {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate list query schema
|
||||
*/
|
||||
static get validateListQuerySchema() {
|
||||
return [
|
||||
query('column_sort_order').optional().isIn(['created_at', 'name', 'amount', 'sku']),
|
||||
@@ -221,16 +221,21 @@ export default class ItemsController {
|
||||
* @param {Function} next
|
||||
*/
|
||||
static async validateCostAccountExistance(req: Request, res: Response, next: Function) {
|
||||
const { Account } = req.models;
|
||||
const { Account, AccountType } = req.models;
|
||||
const item = req.body;
|
||||
|
||||
if (item.cost_account_id) {
|
||||
const foundAccount = await Account.query().findById(item.cost_account_id);
|
||||
const COGSType = await AccountType.query().findOne('key', 'cost_of_goods_sold');
|
||||
const foundAccount = await Account.query().findById(item.cost_account_id)
|
||||
|
||||
if (!foundAccount) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'COST.ACCOUNT.NOT.FOUND', code: 120 }],
|
||||
});
|
||||
} else if (foundAccount.accountTypeId !== COGSType.id) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'COST.ACCOUNT.NOT.COGS.TYPE', code: 220 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next();
|
||||
@@ -243,16 +248,21 @@ export default class ItemsController {
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
static async validateSellAccountExistance(req: Request, res: Response, next: Function) {
|
||||
const { Account } = req.models;
|
||||
const { Account, AccountType } = req.models;
|
||||
const item = req.body;
|
||||
|
||||
if (item.sell_account_id) {
|
||||
const incomeType = await AccountType.query().findOne('key', 'income');
|
||||
const foundAccount = await Account.query().findById(item.sell_account_id);
|
||||
|
||||
if (!foundAccount) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'SELL.ACCOUNT.NOT.FOUND', code: 130 }],
|
||||
});
|
||||
} else if (foundAccount.accountTypeId !== incomeType.id) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'SELL.ACCOUNT.NOT.INCOME.TYPE', code: 230 }],
|
||||
})
|
||||
}
|
||||
}
|
||||
next();
|
||||
@@ -265,16 +275,21 @@ export default class ItemsController {
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
static async validateInventoryAccountExistance(req: Request, res: Response, next: Function) {
|
||||
const { Account } = req.models;
|
||||
const { Account, AccountType } = req.models;
|
||||
const item = req.body;
|
||||
|
||||
if (item.inventory_account_id) {
|
||||
const otherAsset = await AccountType.query().findOne('key', 'other_asset');
|
||||
const foundAccount = await Account.query().findById(item.inventory_account_id);
|
||||
|
||||
if (!foundAccount) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'INVENTORY.ACCOUNT.NOT.FOUND', code: 200}],
|
||||
});
|
||||
} else if (otherAsset.id !== foundAccount.accountTypeId) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'INVENTORY.ACCOUNT.NOT.CURRENT.ASSET', code: 300 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next();
|
||||
|
||||
@@ -25,6 +25,7 @@ export default class BillsController extends BaseController {
|
||||
asyncMiddleware(this.validateVendorExistance),
|
||||
asyncMiddleware(this.validateItemsIds),
|
||||
asyncMiddleware(this.validateBillNumberExists),
|
||||
asyncMiddleware(this.validateNonPurchasableEntriesItems),
|
||||
asyncMiddleware(this.newBill)
|
||||
);
|
||||
router.post(
|
||||
@@ -35,6 +36,7 @@ export default class BillsController extends BaseController {
|
||||
asyncMiddleware(this.validateVendorExistance),
|
||||
asyncMiddleware(this.validateItemsIds),
|
||||
asyncMiddleware(this.validateEntriesIdsExistance),
|
||||
asyncMiddleware(this.validateNonPurchasableEntriesItems),
|
||||
asyncMiddleware(this.editBill)
|
||||
);
|
||||
router.get(
|
||||
@@ -201,6 +203,32 @@ export default class BillsController extends BaseController {
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the entries items that not purchase-able.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Function} next
|
||||
*/
|
||||
static async validateNonPurchasableEntriesItems(req, res, next) {
|
||||
const { Item } = req.models;
|
||||
const bill = { ...req.body };
|
||||
const itemsIds = bill.entries.map(e => e.item_id);
|
||||
|
||||
const purchasbleItems = await Item.query()
|
||||
.where('purchasable', true)
|
||||
.whereIn('id', itemsIds);
|
||||
|
||||
const purchasbleItemsIds = purchasbleItems.map((item) => item.id);
|
||||
const notPurchasableItems = difference(itemsIds, purchasbleItemsIds);
|
||||
|
||||
if (notPurchasableItems.length > 0) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'NOT.PURCHASE.ABLE.ITEMS', code: 600 }],
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new bill and records journal transactions.
|
||||
* @param {Request} req
|
||||
|
||||
@@ -11,7 +11,7 @@ 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 { Customer } from '../../../models';
|
||||
import { Customer, Item } from '../../../models';
|
||||
|
||||
export default class SaleInvoicesController {
|
||||
/**
|
||||
@@ -27,6 +27,7 @@ export default class SaleInvoicesController {
|
||||
asyncMiddleware(this.validateInvoiceCustomerExistance),
|
||||
asyncMiddleware(this.validateInvoiceNumberUnique),
|
||||
asyncMiddleware(this.validateInvoiceItemsIdsExistance),
|
||||
asyncMiddleware(this.validateNonSellableEntriesItems),
|
||||
asyncMiddleware(this.newSaleInvoice)
|
||||
);
|
||||
router.post(
|
||||
@@ -42,6 +43,7 @@ export default class SaleInvoicesController {
|
||||
asyncMiddleware(this.validateInvoiceItemsIdsExistance),
|
||||
asyncMiddleware(this.valdiateInvoiceEntriesIdsExistance),
|
||||
asyncMiddleware(this.validateEntriesIdsExistance),
|
||||
asyncMiddleware(this.validateNonSellableEntriesItems),
|
||||
asyncMiddleware(this.editSaleInvoice)
|
||||
);
|
||||
router.delete(
|
||||
@@ -257,6 +259,32 @@ export default class SaleInvoicesController {
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the entries items that not sellable.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Function} next
|
||||
*/
|
||||
static async validateNonSellableEntriesItems(req, res, next) {
|
||||
const { Item } = req.models;
|
||||
const saleInvoice = { ...req.body };
|
||||
const itemsIds = saleInvoice.entries.map(e => e.item_id);
|
||||
|
||||
const sellableItems = await Item.query()
|
||||
.where('sellable', true)
|
||||
.whereIn('id', itemsIds);
|
||||
|
||||
const sellableItemsIds = sellableItems.map((item) => item.id);
|
||||
const notSellableItems = difference(itemsIds, sellableItemsIds);
|
||||
|
||||
if (notSellableItems.length > 0) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'NOT.SELLABLE.ITEMS', code: 600 }],
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new sale invoice.
|
||||
* @param {Request} req
|
||||
|
||||
Reference in New Issue
Block a user