mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
fix: validate payment made entries ids exists.
fix: retrieve payment made and receive details.
This commit is contained in:
@@ -198,13 +198,16 @@ export default class BillsPayments extends BaseController {
|
||||
const { id: billPaymentId } = req.params;
|
||||
|
||||
try {
|
||||
const { billPayment, payableBills } = await this.billPaymentService.getBillPayment(tenantId, billPaymentId);
|
||||
const {
|
||||
billPayment,
|
||||
payableBills,
|
||||
paymentMadeBills,
|
||||
} = await this.billPaymentService.getBillPayment(tenantId, billPaymentId);
|
||||
|
||||
return res.status(200).send({
|
||||
bill_payment: {
|
||||
...this.transfromToResponse({ ...billPayment }),
|
||||
payable_bills: payableBills,
|
||||
},
|
||||
bill_payment: this.transfromToResponse({ ...billPayment }),
|
||||
payable_bills: this.transfromToResponse([ ...payableBills ]),
|
||||
payment_bills: this.transfromToResponse([ ...paymentMadeBills ]),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
|
||||
@@ -7,6 +7,7 @@ import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import PaymentReceiveService from 'services/Sales/PaymentsReceives';
|
||||
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import HasItemEntries from 'services/Sales/HasItemsEntries';
|
||||
|
||||
/**
|
||||
* Payments receives controller.
|
||||
@@ -209,10 +210,19 @@ export default class PaymentReceivesController extends BaseController {
|
||||
const { id: paymentReceiveId } = req.params;
|
||||
|
||||
try {
|
||||
const paymentReceive = await this.paymentReceiveService.getPaymentReceive(
|
||||
const {
|
||||
paymentReceive,
|
||||
receivableInvoices,
|
||||
paymentReceiveInvoices,
|
||||
} = await this.paymentReceiveService.getPaymentReceive(
|
||||
tenantId, paymentReceiveId
|
||||
);
|
||||
return res.status(200).send({ paymentReceive });
|
||||
|
||||
return res.status(200).send({
|
||||
payment_receive: this.transfromToResponse({ ...paymentReceive }),
|
||||
receivable_invoices: this.transfromToResponse([ ...receivableInvoices ]),
|
||||
payment_invoices: this.transfromToResponse([ ...paymentReceiveInvoices ]),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -314,7 +324,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
errors: [{ type: 'INVOICES_IDS_NOT_FOUND', code: 300 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'ENTRIES_IDS_NOT_FOUND') {
|
||||
if (error.errorType === 'ENTRIES_IDS_NOT_EXISTS') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'ENTRIES_IDS_NOT_FOUND', code: 300 }],
|
||||
});
|
||||
@@ -324,6 +334,12 @@ export default class PaymentReceivesController extends BaseController {
|
||||
errors: [{ type: 'CUSTOMER_NOT_FOUND', code: 300 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'INVALID_PAYMENT_AMOUNT') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'INVALID_PAYMENT_AMOUNT', code: 1000 }],
|
||||
});
|
||||
}
|
||||
console.log(error.errorType);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export default class SaleInvoicesController extends BaseController{
|
||||
check('customer_id').exists().isNumeric().toInt(),
|
||||
check('invoice_date').exists().isISO8601(),
|
||||
check('due_date').exists().isISO8601(),
|
||||
check('invoice_no').exists().trim().escape(),
|
||||
check('invoice_no').optional().trim().escape(),
|
||||
check('reference_no').optional().trim().escape(),
|
||||
check('status').exists().trim().escape(),
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mixin } from 'objection';
|
||||
import { mixin, Model } from 'objection';
|
||||
import TenantModel from 'models/TenantModel';
|
||||
|
||||
export default class BillPaymentEntry extends TenantModel {
|
||||
@@ -15,4 +15,22 @@ export default class BillPaymentEntry extends TenantModel {
|
||||
get timestamps() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const Bill = require('models/Bill');
|
||||
|
||||
return {
|
||||
bill: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Bill.default,
|
||||
join: {
|
||||
from: 'bills_payments_entries.billId',
|
||||
to: 'bills.id',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { entries, omit, sumBy, difference } from 'lodash';
|
||||
import { omit, sumBy, difference } from 'lodash';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -488,27 +488,46 @@ export default class BillPaymentsService {
|
||||
* @param {number} billPaymentId - The bill payment id.
|
||||
* @return {object}
|
||||
*/
|
||||
public async getBillPayment(tenantId: number, billPaymentId: number) {
|
||||
public async getBillPayment(tenantId: number, billPaymentId: number): Promise<{
|
||||
billPayment: IBillPayment,
|
||||
payableBills: IBill[],
|
||||
paymentMadeBills: IBill[],
|
||||
}> {
|
||||
const { BillPayment, Bill } = this.tenancy.models(tenantId);
|
||||
const billPayment = await BillPayment.query()
|
||||
.findById(billPaymentId)
|
||||
.withGraphFetched('entries')
|
||||
.withGraphFetched('entries.bill')
|
||||
.withGraphFetched('vendor')
|
||||
.withGraphFetched('paymentAccount');
|
||||
|
||||
|
||||
if (!billPayment) {
|
||||
throw new ServiceError(ERRORS.PAYMENT_MADE_NOT_FOUND);
|
||||
}
|
||||
const billsIds = billPayment.entries.map((entry) => entry.billId);
|
||||
|
||||
const payableBills = await Bill.query().onBuild((builder) => {
|
||||
const billsIds = billPayment.entries.map((entry) => entry.billId);
|
||||
// Retrieve all payable bills that assocaited to the payment made transaction.
|
||||
const payableBills = await Bill.query()
|
||||
.modify('dueBills')
|
||||
.whereNotIn('id', billsIds)
|
||||
.where('vendor_id', billPayment.vendorId)
|
||||
.orderBy('bill_date', 'ASC');
|
||||
|
||||
builder.where('vendor_id', billPayment.vendorId);
|
||||
builder.orWhereIn('id', billsIds);
|
||||
builder.orderByRaw(`FIELD(id, ${billsIds.join(', ')}) DESC`);
|
||||
builder.orderBy('bill_date', 'ASC');
|
||||
})
|
||||
return { billPayment, payableBills };
|
||||
// Retrieve all payment made assocaited bills.
|
||||
const paymentMadeBills = billPayment.entries.map((entry) => ({
|
||||
...(entry.bill),
|
||||
dueAmount: (entry.bill.dueAmount + entry.paymentAmount),
|
||||
}));
|
||||
|
||||
return {
|
||||
billPayment: {
|
||||
...billPayment,
|
||||
entries: billPayment.entries.map((entry) => ({
|
||||
...omit(entry, ['bill']),
|
||||
})),
|
||||
},
|
||||
payableBills,
|
||||
paymentMadeBills,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,8 @@ const ERRORS = {
|
||||
DEPOSIT_ACCOUNT_NOT_FOUND: 'DEPOSIT_ACCOUNT_NOT_FOUND',
|
||||
DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET_TYPE: 'DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET_TYPE',
|
||||
INVALID_PAYMENT_AMOUNT: 'INVALID_PAYMENT_AMOUNT',
|
||||
INVOICES_IDS_NOT_FOUND: 'INVOICES_IDS_NOT_FOUND'
|
||||
INVOICES_IDS_NOT_FOUND: 'INVOICES_IDS_NOT_FOUND',
|
||||
ENTRIES_IDS_NOT_EXISTS: 'ENTRIES_IDS_NOT_EXISTS'
|
||||
};
|
||||
/**
|
||||
* Payment receive service.
|
||||
@@ -180,6 +181,34 @@ export default class PaymentReceiveService {
|
||||
throw new ServiceError(ERRORS.INVALID_PAYMENT_AMOUNT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the payment receive entries IDs existance.
|
||||
* @param {number} tenantId
|
||||
* @param {number} paymentReceiveId
|
||||
* @param {IPaymentReceiveEntryDTO[]} paymentReceiveEntries
|
||||
*/
|
||||
private async validateEntriesIdsExistance(
|
||||
tenantId: number,
|
||||
paymentReceiveId: number,
|
||||
paymentReceiveEntries: IPaymentReceiveEntryDTO[],
|
||||
) {
|
||||
const { PaymentReceiveEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
const entriesIds = paymentReceiveEntries
|
||||
.filter((entry) => entry.id)
|
||||
.map((entry) => entry.id);
|
||||
|
||||
const storedEntries = await PaymentReceiveEntry.query()
|
||||
.where('payment_receive_id', paymentReceiveId);
|
||||
|
||||
const storedEntriesIds = storedEntries.map((entry: any) => entry.id);
|
||||
const notFoundEntriesIds = difference(entriesIds, storedEntriesIds);
|
||||
|
||||
if (notFoundEntriesIds.length > 0) {
|
||||
throw new ServiceError(ERRORS.ENTRIES_IDS_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new payment receive and store it to the storage
|
||||
@@ -266,9 +295,8 @@ export default class PaymentReceiveService {
|
||||
await this.customersService.getCustomerByIdOrThrowError(tenantId, paymentReceiveDTO.customerId);
|
||||
|
||||
// Validate the entries ids existance on payment receive type.
|
||||
await this.itemsEntries.validateEntriesIdsExistance(
|
||||
tenantId, paymentReceiveId, 'PaymentReceive', paymentReceiveDTO.entries
|
||||
);
|
||||
await this.validateEntriesIdsExistance(tenantId, paymentReceiveId, paymentReceiveDTO.entries);
|
||||
|
||||
// Validate payment receive invoices IDs existance and associated to the given customer id.
|
||||
await this.validateInvoicesIDsExistance(tenantId, paymentReceiveDTO.customerId, paymentReceiveDTO.entries);
|
||||
|
||||
@@ -329,27 +357,37 @@ export default class PaymentReceiveService {
|
||||
public async getPaymentReceive(
|
||||
tenantId: number,
|
||||
paymentReceiveId: number
|
||||
): Promise<{ paymentReceive: IPaymentReceive[], receivableInvoices: ISaleInvoice }> {
|
||||
): Promise<{
|
||||
paymentReceive: IPaymentReceive,
|
||||
receivableInvoices: ISaleInvoice[],
|
||||
paymentReceiveInvoices: ISaleInvoice[],
|
||||
}> {
|
||||
const { PaymentReceive, SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const paymentReceive = await PaymentReceive.query()
|
||||
.findById(paymentReceiveId)
|
||||
.withGraphFetched('entries')
|
||||
.withGraphFetched('entries.invoice')
|
||||
.withGraphFetched('customer')
|
||||
.withGraphFetched('depositAccount');
|
||||
|
||||
|
||||
if (!paymentReceive) {
|
||||
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
|
||||
}
|
||||
// Receivable open invoices.
|
||||
const receivableInvoices = await SaleInvoice.query().onBuild((builder) => {
|
||||
const invoicesIds = paymentReceive.entries.map((entry) => entry.invoiceId);
|
||||
const invoicesIds = paymentReceive.entries.map((entry) => entry.invoiceId);
|
||||
|
||||
builder.where('customer_id', paymentReceive.customerId);
|
||||
builder.orWhereIn('id', invoicesIds);
|
||||
builder.orderByRaw(`FIELD(id, ${invoicesIds.join(', ')}) DESC`);
|
||||
builder.orderBy('invoice_date', 'ASC');
|
||||
});
|
||||
return { paymentReceive, receivableInvoices };
|
||||
// Retrieves all receivable bills that associated to the payment receive transaction.
|
||||
const receivableInvoices = await SaleInvoice.query()
|
||||
.modify('dueInvoices')
|
||||
.where('customer_id', paymentReceive.customerId)
|
||||
.whereNotIn('id', invoicesIds)
|
||||
.orderBy('invoice_date', 'ASC');
|
||||
|
||||
// Retrieve all payment receive associated invoices.
|
||||
const paymentReceiveInvoices = paymentReceive.entries.map((entry) => ({
|
||||
...(entry.invoice),
|
||||
dueAmount: (entry.invoice.dueAmount + entry.paymentAmount),
|
||||
}));
|
||||
|
||||
return { paymentReceive, receivableInvoices, paymentReceiveInvoices };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user