feat: issues in bills and payments made

This commit is contained in:
Ahmed Bouhuolia
2020-10-24 14:49:32 +02:00
parent e8aa4b5d73
commit b91c452101
6 changed files with 67 additions and 47 deletions

View File

@@ -44,7 +44,8 @@ export default class BillsController extends BaseController {
...this.specificBillValidationSchema,
],
this.validationResult,
asyncMiddleware(this.editBill.bind(this))
asyncMiddleware(this.editBill.bind(this)),
this.handleServiceError,
);
router.get(
'/:id', [
@@ -101,10 +102,10 @@ export default class BillsController extends BaseController {
*/
get billEditValidationSchema() {
return [
// check('bill_number').exists().trim().escape(),
check('bill_number').exists().trim().escape(),
check('bill_date').exists().isISO8601(),
check('due_date').optional().isISO8601(),
// check('vendor_id').exists().isNumeric().toInt(),
check('vendor_id').exists().isNumeric().toInt(),
check('note').optional().trim().escape(),
check('entries').isArray({ min: 1 }),
@@ -186,7 +187,7 @@ export default class BillsController extends BaseController {
const { id: billId } = req.params;
try {
const bill = await this.billsService.getBillWithMetadata(tenantId, billId);
const bill = await this.billsService.getBill(tenantId, billId);
return res.status(200).send({ bill });
} catch (error) {
@@ -286,6 +287,11 @@ export default class BillsController extends BaseController {
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);
}

View File

@@ -1,3 +1,4 @@
import { IDynamicListFilterDTO } from "./DynamicFilter";
import { IItemEntry, IItemEntryDTO } from "./ItemEntry";
export interface IBillDTO {
@@ -14,6 +15,8 @@ export interface IBillDTO {
};
export interface IBillEditDTO {
vendorId: number,
billNumber: string,
billDate: Date,
dueDate: Date,
referenceNo: string,
@@ -42,7 +45,6 @@ export interface IBill {
entries: IItemEntry[],
};
export interface IBillsFilter {
page: number,
pageSize: number,
export interface IBillsFilter extends IDynamicListFilterDTO {
stringifiedFilterRoles?: string,
}

View File

@@ -1,6 +1,8 @@
export interface IItemEntry {
id?: number,
referenceType: string,
referenceId: number,

View File

@@ -64,7 +64,7 @@ export default class Bill extends TenantModel {
to: 'items_entries.referenceId',
},
filter(builder) {
builder.where('reference_type', 'SaleReceipt');
builder.where('reference_type', 'Bill');
},
},
};

View File

@@ -284,6 +284,7 @@ export default class BillPaymentsService {
* - Re-insert the journal transactions and update the diff accounts balance.
* - Update the diff vendor balance.
* - Update the diff bill payment amount.
* ------
* @param {number} tenantId - Tenant id
* @param {Integer} billPaymentId
* @param {BillPaymentDTO} billPayment
@@ -313,6 +314,7 @@ export default class BillPaymentsService {
.upsertGraph({
id: billPaymentId,
...omit(billPaymentObj, ['entries']),
entries: billPaymentDTO.entries,
});
await this.eventDispatcher.dispatch(events.billPayments.onEdited);
this.logger.info('[bill_payment] edited successfully.', { tenantId, billPaymentId, billPayment, oldPaymentMade });

View File

@@ -133,9 +133,13 @@ export default class BillsService extends SalesInvoicesCost {
* @param {Response} res
* @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 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) {
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.
* @param {Request} req
* @param {Response} res
* @param {Function} next
* @param {number} tenantId -
* @param {number} billId -
* @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 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()
.whereIn('reference_id', [billId])
@@ -170,9 +176,9 @@ export default class BillsService extends SalesInvoicesCost {
* @param {Response} res
* @param {Function} next
*/
private async validateNonPurchasableEntriesItems(tenantId: number, billEntries: any) {
private async validateNonPurchasableEntriesItems(tenantId: number, billEntries: IItemEntryDTO[]) {
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()
.where('purchasable', true)
@@ -194,7 +200,7 @@ export default class BillsService extends SalesInvoicesCost {
*
* @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);
let invLotNumber = oldBill?.invLotNumber;
@@ -293,18 +299,26 @@ export default class BillsService extends SalesInvoicesCost {
const oldBill = await this.getBillOrThrowError(tenantId, billId);
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.
const bill = await Bill.query()
.upsertGraph({
id: billId,
...omit(billObj, ['entries', 'invLotNumber']),
entries: billDTO.entries.map((entry) => ({
reference_type: 'Bill',
...omit(entry, ['amount']),
}))
});
const bill = await Bill.query().upsertGraph({
id: billId,
...omit(billObj, ['entries', 'invLotNumber']),
entries: billDTO.entries.map((entry) => ({
reference_type: 'Bill',
...omit(entry, ['amount']),
}))
});
// Triggers event `onBillEdited`.
await this.eventDispatcher.dispatch(events.bills.onEdited, { tenantId, billId, oldBill, bill });
this.logger.info('[bill] bill upserted successfully.', { tenantId, billId });
return bill;
}
@@ -339,7 +353,7 @@ export default class BillsService extends SalesInvoicesCost {
* @param {Bill} bill
* @param {number} billId
*/
recordInventoryTransactions(
public recordInventoryTransactions(
tenantId: number,
bill: any,
billId: number,
@@ -367,7 +381,7 @@ export default class BillsService extends SalesInvoicesCost {
* @param {IBill} bill
* @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 { accountRepository } = this.tenancy.repositories(tenantId);
@@ -457,27 +471,21 @@ export default class BillsService extends SalesInvoicesCost {
/**
* Retrieve the given bill details with associated items entries.
* @param {Integer} billId -
* @returns {Promise}
* @param {Integer} billId - Specific bill.
* @returns {Promise<IBill>}
*/
getBill(tenantId: number, billId: number) {
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) {
public async getBill(tenantId: number, billId: number): Promise<IBill> {
const { Bill } = this.tenancy.models(tenantId);
return Bill.query()
.where('id', billId)
this.logger.info('[bills] trying to fetch specific bill with metadata.', { tenantId, billId });
const bill = await Bill.query().findById(billId)
.withGraphFetched('vendor')
.withGraphFetched('entries')
.first();
.withGraphFetched('entries');
if (!bill) {
throw new ServiceError(ERRORS.BILL_NOT_FOUND);
}
return bill;
}
/**
@@ -486,7 +494,7 @@ export default class BillsService extends SalesInvoicesCost {
* @param {IBill} bill -
* @return {Promise}
*/
async scheduleComputeBillItemsCost(tenantId: number, bill) {
public async scheduleComputeBillItemsCost(tenantId: number, bill) {
const { Item } = this.tenancy.models(tenantId);
const billItemsIds = bill.entries.map((entry) => entry.item_id);