mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 06:10:31 +00:00
feat: issues in bills and payments made
This commit is contained in:
@@ -44,7 +44,8 @@ export default class BillsController extends BaseController {
|
|||||||
...this.specificBillValidationSchema,
|
...this.specificBillValidationSchema,
|
||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.editBill.bind(this))
|
asyncMiddleware(this.editBill.bind(this)),
|
||||||
|
this.handleServiceError,
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/:id', [
|
'/:id', [
|
||||||
@@ -101,10 +102,10 @@ export default class BillsController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
get billEditValidationSchema() {
|
get billEditValidationSchema() {
|
||||||
return [
|
return [
|
||||||
// check('bill_number').exists().trim().escape(),
|
check('bill_number').exists().trim().escape(),
|
||||||
check('bill_date').exists().isISO8601(),
|
check('bill_date').exists().isISO8601(),
|
||||||
check('due_date').optional().isISO8601(),
|
check('due_date').optional().isISO8601(),
|
||||||
// check('vendor_id').exists().isNumeric().toInt(),
|
check('vendor_id').exists().isNumeric().toInt(),
|
||||||
check('note').optional().trim().escape(),
|
check('note').optional().trim().escape(),
|
||||||
check('entries').isArray({ min: 1 }),
|
check('entries').isArray({ min: 1 }),
|
||||||
|
|
||||||
@@ -186,7 +187,7 @@ export default class BillsController extends BaseController {
|
|||||||
const { id: billId } = req.params;
|
const { id: billId } = req.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const bill = await this.billsService.getBillWithMetadata(tenantId, billId);
|
const bill = await this.billsService.getBill(tenantId, billId);
|
||||||
|
|
||||||
return res.status(200).send({ bill });
|
return res.status(200).send({ bill });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -286,6 +287,11 @@ export default class BillsController extends BaseController {
|
|||||||
errors: [{ type: 'ITEMS.IDS.NOT.FOUND', code: 400 }],
|
errors: [{ type: 'ITEMS.IDS.NOT.FOUND', code: 400 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (error.errorType === 'BILL_ENTRIES_IDS_NOT_FOUND') {
|
||||||
|
return res.status(400).send({
|
||||||
|
errors: [{ type: 'BILL_ENTRIES_IDS_NOT_FOUND', code: 900 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { IDynamicListFilterDTO } from "./DynamicFilter";
|
||||||
import { IItemEntry, IItemEntryDTO } from "./ItemEntry";
|
import { IItemEntry, IItemEntryDTO } from "./ItemEntry";
|
||||||
|
|
||||||
export interface IBillDTO {
|
export interface IBillDTO {
|
||||||
@@ -14,6 +15,8 @@ export interface IBillDTO {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface IBillEditDTO {
|
export interface IBillEditDTO {
|
||||||
|
vendorId: number,
|
||||||
|
billNumber: string,
|
||||||
billDate: Date,
|
billDate: Date,
|
||||||
dueDate: Date,
|
dueDate: Date,
|
||||||
referenceNo: string,
|
referenceNo: string,
|
||||||
@@ -42,7 +45,6 @@ export interface IBill {
|
|||||||
entries: IItemEntry[],
|
entries: IItemEntry[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IBillsFilter {
|
export interface IBillsFilter extends IDynamicListFilterDTO {
|
||||||
page: number,
|
stringifiedFilterRoles?: string,
|
||||||
pageSize: number,
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
export interface IItemEntry {
|
export interface IItemEntry {
|
||||||
|
id?: number,
|
||||||
|
|
||||||
referenceType: string,
|
referenceType: string,
|
||||||
referenceId: number,
|
referenceId: number,
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default class Bill extends TenantModel {
|
|||||||
to: 'items_entries.referenceId',
|
to: 'items_entries.referenceId',
|
||||||
},
|
},
|
||||||
filter(builder) {
|
filter(builder) {
|
||||||
builder.where('reference_type', 'SaleReceipt');
|
builder.where('reference_type', 'Bill');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ export default class BillPaymentsService {
|
|||||||
* - Re-insert the journal transactions and update the diff accounts balance.
|
* - Re-insert the journal transactions and update the diff accounts balance.
|
||||||
* - Update the diff vendor balance.
|
* - Update the diff vendor balance.
|
||||||
* - Update the diff bill payment amount.
|
* - Update the diff bill payment amount.
|
||||||
|
* ------
|
||||||
* @param {number} tenantId - Tenant id
|
* @param {number} tenantId - Tenant id
|
||||||
* @param {Integer} billPaymentId
|
* @param {Integer} billPaymentId
|
||||||
* @param {BillPaymentDTO} billPayment
|
* @param {BillPaymentDTO} billPayment
|
||||||
@@ -313,6 +314,7 @@ export default class BillPaymentsService {
|
|||||||
.upsertGraph({
|
.upsertGraph({
|
||||||
id: billPaymentId,
|
id: billPaymentId,
|
||||||
...omit(billPaymentObj, ['entries']),
|
...omit(billPaymentObj, ['entries']),
|
||||||
|
entries: billPaymentDTO.entries,
|
||||||
});
|
});
|
||||||
await this.eventDispatcher.dispatch(events.billPayments.onEdited);
|
await this.eventDispatcher.dispatch(events.billPayments.onEdited);
|
||||||
this.logger.info('[bill_payment] edited successfully.', { tenantId, billPaymentId, billPayment, oldPaymentMade });
|
this.logger.info('[bill_payment] edited successfully.', { tenantId, billPaymentId, billPayment, oldPaymentMade });
|
||||||
|
|||||||
@@ -133,9 +133,13 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {Function} next
|
* @param {Function} next
|
||||||
*/
|
*/
|
||||||
private async validateBillNumberExists(tenantId: number, billNumber: string) {
|
private async validateBillNumberExists(tenantId: number, billNumber: string, notBillId?: number) {
|
||||||
const { Bill } = this.tenancy.models(tenantId);
|
const { Bill } = this.tenancy.models(tenantId);
|
||||||
const foundBills = await Bill.query().where('bill_number', billNumber);
|
const foundBills = await Bill.query().where('bill_number', billNumber).onBuild((builder) => {
|
||||||
|
if (notBillId) {
|
||||||
|
builder.whereNot('id', notBillId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (foundBills.length > 0) {
|
if (foundBills.length > 0) {
|
||||||
throw new ServiceError(ERRORS.BILL_NUMBER_EXISTS);
|
throw new ServiceError(ERRORS.BILL_NUMBER_EXISTS);
|
||||||
@@ -144,13 +148,15 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the entries ids existance on the storage.
|
* Validates the entries ids existance on the storage.
|
||||||
* @param {Request} req
|
* @param {number} tenantId -
|
||||||
* @param {Response} res
|
* @param {number} billId -
|
||||||
* @param {Function} next
|
* @param {IItemEntry[]} billEntries -
|
||||||
*/
|
*/
|
||||||
async validateEntriesIdsExistance(tenantId: number, billId: number, billEntries: any) {
|
private async validateEntriesIdsExistance(tenantId: number, billId: number, billEntries: IItemEntryDTO[]) {
|
||||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||||
const entriesIds = billEntries.filter((e) => e.id).map((e) => e.id);
|
const entriesIds = billEntries
|
||||||
|
.filter((e: IItemEntry) => e.id)
|
||||||
|
.map((e: IItemEntry) => e.id);
|
||||||
|
|
||||||
const storedEntries = await ItemEntry.query()
|
const storedEntries = await ItemEntry.query()
|
||||||
.whereIn('reference_id', [billId])
|
.whereIn('reference_id', [billId])
|
||||||
@@ -170,9 +176,9 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {Function} next
|
* @param {Function} next
|
||||||
*/
|
*/
|
||||||
private async validateNonPurchasableEntriesItems(tenantId: number, billEntries: any) {
|
private async validateNonPurchasableEntriesItems(tenantId: number, billEntries: IItemEntryDTO[]) {
|
||||||
const { Item } = this.tenancy.models(tenantId);
|
const { Item } = this.tenancy.models(tenantId);
|
||||||
const itemsIds = billEntries.map((e: IItemEntry) => e.itemId);
|
const itemsIds = billEntries.map((e: IItemEntryDTO) => e.itemId);
|
||||||
|
|
||||||
const purchasbleItems = await Item.query()
|
const purchasbleItems = await Item.query()
|
||||||
.where('purchasable', true)
|
.where('purchasable', true)
|
||||||
@@ -194,7 +200,7 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
*
|
*
|
||||||
* @returns {IBill}
|
* @returns {IBill}
|
||||||
*/
|
*/
|
||||||
private async billDTOToModel(tenantId: number, billDTO: IBillDTO, oldBill?: IBill) {
|
private async billDTOToModel(tenantId: number, billDTO: IBillDTO|IBillEditDTO, oldBill?: IBill) {
|
||||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||||
let invLotNumber = oldBill?.invLotNumber;
|
let invLotNumber = oldBill?.invLotNumber;
|
||||||
|
|
||||||
@@ -293,18 +299,26 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
const oldBill = await this.getBillOrThrowError(tenantId, billId);
|
const oldBill = await this.getBillOrThrowError(tenantId, billId);
|
||||||
const billObj = this.billDTOToModel(tenantId, billDTO, oldBill);
|
const billObj = this.billDTOToModel(tenantId, billDTO, oldBill);
|
||||||
|
|
||||||
|
await this.getVendorOrThrowError(tenantId, billDTO.vendorId);
|
||||||
|
await this.validateBillNumberExists(tenantId, billDTO.billNumber, billId);
|
||||||
|
|
||||||
|
await this.validateEntriesIdsExistance(tenantId, billId, billDTO.entries);
|
||||||
|
await this.validateItemsIdsExistance(tenantId, billDTO.entries);
|
||||||
|
await this.validateNonPurchasableEntriesItems(tenantId, billDTO.entries);
|
||||||
|
|
||||||
// Update the bill transaction.
|
// Update the bill transaction.
|
||||||
const bill = await Bill.query()
|
const bill = await Bill.query().upsertGraph({
|
||||||
.upsertGraph({
|
id: billId,
|
||||||
id: billId,
|
...omit(billObj, ['entries', 'invLotNumber']),
|
||||||
...omit(billObj, ['entries', 'invLotNumber']),
|
|
||||||
entries: billDTO.entries.map((entry) => ({
|
entries: billDTO.entries.map((entry) => ({
|
||||||
reference_type: 'Bill',
|
reference_type: 'Bill',
|
||||||
...omit(entry, ['amount']),
|
...omit(entry, ['amount']),
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
// Triggers event `onBillEdited`.
|
// Triggers event `onBillEdited`.
|
||||||
await this.eventDispatcher.dispatch(events.bills.onEdited, { tenantId, billId, oldBill, bill });
|
await this.eventDispatcher.dispatch(events.bills.onEdited, { tenantId, billId, oldBill, bill });
|
||||||
|
this.logger.info('[bill] bill upserted successfully.', { tenantId, billId });
|
||||||
|
|
||||||
return bill;
|
return bill;
|
||||||
}
|
}
|
||||||
@@ -339,7 +353,7 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
* @param {Bill} bill
|
* @param {Bill} bill
|
||||||
* @param {number} billId
|
* @param {number} billId
|
||||||
*/
|
*/
|
||||||
recordInventoryTransactions(
|
public recordInventoryTransactions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
bill: any,
|
bill: any,
|
||||||
billId: number,
|
billId: number,
|
||||||
@@ -367,7 +381,7 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
* @param {IBill} bill
|
* @param {IBill} bill
|
||||||
* @param {Integer} billId
|
* @param {Integer} billId
|
||||||
*/
|
*/
|
||||||
async recordJournalTransactions(tenantId: number, bill: IBill, billId?: number) {
|
public async recordJournalTransactions(tenantId: number, bill: IBill, billId?: number) {
|
||||||
const { AccountTransaction, Item, ItemEntry } = this.tenancy.models(tenantId);
|
const { AccountTransaction, Item, ItemEntry } = this.tenancy.models(tenantId);
|
||||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
@@ -457,27 +471,21 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the given bill details with associated items entries.
|
* Retrieve the given bill details with associated items entries.
|
||||||
* @param {Integer} billId -
|
* @param {Integer} billId - Specific bill.
|
||||||
* @returns {Promise}
|
* @returns {Promise<IBill>}
|
||||||
*/
|
*/
|
||||||
getBill(tenantId: number, billId: number) {
|
public async getBill(tenantId: number, billId: number): Promise<IBill> {
|
||||||
const { Bill } = this.tenancy.models(tenantId);
|
|
||||||
return Bill.query().findById(billId).withGraphFetched('entries');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the given bill details with associated items entries.
|
|
||||||
* @param {Integer} billId -
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
getBillWithMetadata(tenantId: number, billId: number) {
|
|
||||||
const { Bill } = this.tenancy.models(tenantId);
|
const { Bill } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
return Bill.query()
|
this.logger.info('[bills] trying to fetch specific bill with metadata.', { tenantId, billId });
|
||||||
.where('id', billId)
|
const bill = await Bill.query().findById(billId)
|
||||||
.withGraphFetched('vendor')
|
.withGraphFetched('vendor')
|
||||||
.withGraphFetched('entries')
|
.withGraphFetched('entries');
|
||||||
.first();
|
|
||||||
|
if (!bill) {
|
||||||
|
throw new ServiceError(ERRORS.BILL_NOT_FOUND);
|
||||||
|
}
|
||||||
|
return bill;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -486,7 +494,7 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
* @param {IBill} bill -
|
* @param {IBill} bill -
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async scheduleComputeBillItemsCost(tenantId: number, bill) {
|
public async scheduleComputeBillItemsCost(tenantId: number, bill) {
|
||||||
const { Item } = this.tenancy.models(tenantId);
|
const { Item } = this.tenancy.models(tenantId);
|
||||||
const billItemsIds = bill.entries.map((entry) => entry.item_id);
|
const billItemsIds = bill.entries.map((entry) => entry.item_id);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user