mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 05:40:31 +00:00
fix bugs in sales and purchases API.
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
import express from 'express';
|
||||
import { omit } from 'lodash';
|
||||
import { check, query, validationResult, param } from 'express-validator';
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import { BillPayment, BillPaymentEntry, Vendor } from '@/models';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import ServiceItemsEntries from '../Sales/ServiceItemsEntries';
|
||||
import AccountsService from '../Accounts/AccountsService';
|
||||
import JournalPoster from '../Accounting/JournalPoster';
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { omit, sumBy, difference } from 'lodash';
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
Account,
|
||||
Bill,
|
||||
Vendor,
|
||||
InventoryTransaction,
|
||||
ItemEntry,
|
||||
Item,
|
||||
Account,
|
||||
InventoryTransaction,
|
||||
AccountTransaction,
|
||||
} from '@/models';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||
import AccountsService from '@/services/Accounts/AccountsService';
|
||||
import JournalPosterService from '@/services/Sales/JournalPosterService';
|
||||
import InventoryService from '../Inventory/Inventory';
|
||||
import { AccountTransaction } from '../../models';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
||||
|
||||
/**
|
||||
* Vendor bills services.
|
||||
@@ -65,57 +66,7 @@ export default class BillsService {
|
||||
return storedBill;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch items entries to the storage.
|
||||
*
|
||||
* @param {Array} newEntries
|
||||
* @param {Array} oldEntries
|
||||
* @param {String} referenceType
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async patchItemsEntries(newEntries, oldEntries, referenceType, billId) {
|
||||
const entriesHasIds = newEntries.filter((entry) => entry.id);
|
||||
const entriesHasNoIds = newEntries.filter((entry) => !entry.id);
|
||||
|
||||
const entriesIds = entriesHasIds.map(entry => entry.id);
|
||||
|
||||
const oldEntriesIds = oldEntries.map((e) => e.id);
|
||||
const opers = [];
|
||||
|
||||
const entriesIdsShouldDelete = difference(
|
||||
oldEntriesIds,
|
||||
entriesIds,
|
||||
);
|
||||
if (entriesIdsShouldDelete.length > 0) {
|
||||
const deleteOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.whereIn('id', entriesIdsShouldDelete)
|
||||
.delete();
|
||||
opers.push(deleteOper);
|
||||
}
|
||||
entriesHasIds.forEach((entry) => {
|
||||
const updateOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.where('id', entry.id)
|
||||
.update({
|
||||
...omit(entry, ['id']),
|
||||
});
|
||||
opers.push(updateOper);
|
||||
});
|
||||
entriesHasNoIds.forEach((entry) => {
|
||||
const insertOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.insert({
|
||||
reference_id: billId,
|
||||
reference_type: referenceType,
|
||||
...omit(entry, ['id', 'amount']),
|
||||
});
|
||||
opers.push(insertOper);
|
||||
});
|
||||
return Promise.all([...opers]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Edits details of the given bill id with associated entries.
|
||||
*
|
||||
@@ -150,8 +101,9 @@ export default class BillsService {
|
||||
.where('reference_type', 'Bill');
|
||||
|
||||
// Patch the bill entries.
|
||||
const patchEntriesOper = this.patchItemsEntries(bill.entries, storedEntries, 'Bill', billId);
|
||||
|
||||
const patchEntriesOper = HasItemsEntries.patchItemsEntries(
|
||||
bill.entries, storedEntries, 'Bill', billId,
|
||||
);
|
||||
// Record bill journal transactions.
|
||||
const recordTransactionsOper = this.recordJournalTransactions(bill, billId);
|
||||
|
||||
|
||||
56
server/src/services/Sales/HasItemsEntries.ts
Normal file
56
server/src/services/Sales/HasItemsEntries.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { difference, omit } from 'lodash';
|
||||
import { ItemEntry } from '@/models';
|
||||
|
||||
export default class HasItemEntries {
|
||||
/**
|
||||
* Patch items entries to the storage.
|
||||
*
|
||||
* @param {Array} newEntries -
|
||||
* @param {Array} oldEntries -
|
||||
* @param {String} referenceType -
|
||||
* @param {String|Number} referenceId -
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async patchItemsEntries(newEntries: Array<any>, oldEntries: Array<any>, referenceType: string, referenceId: string|number) {
|
||||
const entriesHasIds = newEntries.filter((entry) => entry.id);
|
||||
const entriesHasNoIds = newEntries.filter((entry) => !entry.id);
|
||||
const entriesIds = entriesHasIds.map(entry => entry.id);
|
||||
|
||||
const oldEntriesIds = oldEntries.map((e) => e.id);
|
||||
const excludeAttrs = ['id', 'amount'];
|
||||
const opers = [];
|
||||
|
||||
const entriesIdsShouldDelete = difference(
|
||||
oldEntriesIds,
|
||||
entriesIds,
|
||||
);
|
||||
if (entriesIdsShouldDelete.length > 0) {
|
||||
const deleteOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.whereIn('id', entriesIdsShouldDelete)
|
||||
.delete();
|
||||
opers.push(deleteOper);
|
||||
}
|
||||
entriesHasIds.forEach((entry) => {
|
||||
const updateOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.where('id', entry.id)
|
||||
.update({
|
||||
...omit(entry, excludeAttrs),
|
||||
});
|
||||
opers.push(updateOper);
|
||||
});
|
||||
entriesHasNoIds.forEach((entry) => {
|
||||
const insertOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.insert({
|
||||
reference_id: referenceId,
|
||||
reference_type: referenceType,
|
||||
...omit(entry, excludeAttrs),
|
||||
});
|
||||
opers.push(insertOper);
|
||||
});
|
||||
return Promise.all([...opers]);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { omit, sumBy, mapValues, groupBy, chain } from 'lodash';
|
||||
import moment, { updateLocale } from 'moment';
|
||||
import { omit, sumBy, chain } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
AccountTransaction,
|
||||
PaymentReceive,
|
||||
@@ -16,14 +16,18 @@ import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries';
|
||||
import PaymentReceiveEntryRepository from '@/repositories/PaymentReceiveEntryRepository';
|
||||
import CustomerRepository from '@/repositories/CustomerRepository';
|
||||
|
||||
export default class PaymentReceiveService extends JournalPosterService {
|
||||
/**
|
||||
* Payment receive service.
|
||||
* @service
|
||||
*/
|
||||
export default class PaymentReceiveService {
|
||||
/**
|
||||
* Creates a new payment receive and store it to the storage
|
||||
* with associated invoices payment and journal transactions.
|
||||
* @async
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
*/
|
||||
static async createPaymentReceive(paymentReceive) {
|
||||
static async createPaymentReceive(paymentReceive: any) {
|
||||
const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount');
|
||||
const storedPaymentReceive = await PaymentReceive.tenant()
|
||||
.query()
|
||||
@@ -31,9 +35,9 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
amount: paymentAmount,
|
||||
...omit(paymentReceive, ['entries']),
|
||||
});
|
||||
const storeOpers = [];
|
||||
const storeOpers: Array<any> = [];
|
||||
|
||||
paymentReceive.entries.forEach((entry) => {
|
||||
paymentReceive.entries.forEach((entry: any) => {
|
||||
const oper = PaymentReceiveEntry.tenant()
|
||||
.query()
|
||||
.insert({
|
||||
@@ -51,12 +55,13 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
});
|
||||
const customerIncrementOper = Customer.decrementBalance(
|
||||
paymentReceive.customer_id,
|
||||
paymentAmount
|
||||
paymentAmount,
|
||||
);
|
||||
// Records the sale invoice journal transactions.
|
||||
const recordJournalTransactions = this.recordPaymentReceiveJournalEntries({
|
||||
id: storedPaymentReceive.id,
|
||||
...paymentReceive,
|
||||
});
|
||||
id: storedPaymentReceive.id,
|
||||
...paymentReceive,
|
||||
});
|
||||
await Promise.all([
|
||||
...storeOpers,
|
||||
customerIncrementOper,
|
||||
@@ -81,9 +86,9 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
* @param {IPaymentReceive} oldPaymentReceive
|
||||
*/
|
||||
static async editPaymentReceive(
|
||||
paymentReceiveId,
|
||||
paymentReceive,
|
||||
oldPaymentReceive
|
||||
paymentReceiveId: number,
|
||||
paymentReceive: any,
|
||||
oldPaymentReceive: any
|
||||
) {
|
||||
const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount');
|
||||
// Update the payment receive transaction.
|
||||
@@ -129,14 +134,14 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
id: oldPaymentReceive.id,
|
||||
...paymentReceive,
|
||||
},
|
||||
paymentReceiveId
|
||||
paymentReceiveId,
|
||||
);
|
||||
// Increment/decrement the customer balance after calc the diff
|
||||
// between old and new value.
|
||||
const changeCustomerBalance = CustomerRepository.changeDiffBalance(
|
||||
paymentReceive.customer_id,
|
||||
oldPaymentReceive.customerId,
|
||||
paymentAmount,
|
||||
paymentAmount * -1,
|
||||
oldPaymentReceive.amount,
|
||||
);
|
||||
// Change the difference between the old and new invoice payment amount.
|
||||
@@ -164,8 +169,9 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
* - Revert the payment amount of the associated invoices.
|
||||
* @async
|
||||
* @param {Integer} paymentReceiveId
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
*/
|
||||
static async deletePaymentReceive(paymentReceiveId, paymentReceive) {
|
||||
static async deletePaymentReceive(paymentReceiveId: number, paymentReceive: any) {
|
||||
// Deletes the payment receive transaction.
|
||||
await PaymentReceive.tenant()
|
||||
.query()
|
||||
@@ -179,7 +185,7 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
.delete();
|
||||
|
||||
// Delete all associated journal transactions to payment receive transaction.
|
||||
const deleteTransactionsOper = this.deleteJournalTransactions(
|
||||
const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions(
|
||||
paymentReceiveId,
|
||||
'PaymentReceive'
|
||||
);
|
||||
@@ -190,7 +196,7 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
);
|
||||
// Revert the invoices payments amount.
|
||||
const revertInvoicesPaymentAmount = this.revertInvoicePaymentAmount(
|
||||
paymentReceive.entries.map((entry) => ({
|
||||
paymentReceive.entries.map((entry: any) => ({
|
||||
invoiceId: entry.invoiceId,
|
||||
revertAmount: entry.paymentAmount,
|
||||
}))
|
||||
@@ -206,7 +212,7 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
* Retrieve the payment receive details of the given id.
|
||||
* @param {Integer} paymentReceiveId
|
||||
*/
|
||||
static async getPaymentReceive(paymentReceiveId) {
|
||||
static async getPaymentReceive(paymentReceiveId: number) {
|
||||
const paymentReceive = await PaymentReceive.tenant()
|
||||
.query()
|
||||
.where('id', paymentReceiveId)
|
||||
@@ -219,7 +225,7 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
* Retrieve the payment receive details with associated invoices.
|
||||
* @param {Integer} paymentReceiveId
|
||||
*/
|
||||
static async getPaymentReceiveWithInvoices(paymentReceiveId) {
|
||||
static async getPaymentReceiveWithInvoices(paymentReceiveId: number) {
|
||||
return PaymentReceive.tenant()
|
||||
.query()
|
||||
.where('id', paymentReceiveId)
|
||||
@@ -231,7 +237,7 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
* Detarmines whether the payment receive exists on the storage.
|
||||
* @param {Integer} paymentReceiveId
|
||||
*/
|
||||
static async isPaymentReceiveExists(paymentReceiveId) {
|
||||
static async isPaymentReceiveExists(paymentReceiveId: number) {
|
||||
const paymentReceives = await PaymentReceive.tenant()
|
||||
.query()
|
||||
.where('id', paymentReceiveId);
|
||||
@@ -245,8 +251,8 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
* @param {Integer} paymentReceiveId - Payment receive id.
|
||||
*/
|
||||
static async isPaymentReceiveNoExists(
|
||||
paymentReceiveNumber,
|
||||
paymentReceiveId
|
||||
paymentReceiveNumber: string|number,
|
||||
paymentReceiveId: number
|
||||
) {
|
||||
const paymentReceives = await PaymentReceive.tenant()
|
||||
.query()
|
||||
@@ -261,17 +267,22 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
|
||||
/**
|
||||
* Records payment receive journal transactions.
|
||||
*
|
||||
* Invoice payment journals.
|
||||
* --------
|
||||
* - Account receivable -> Debit
|
||||
* - Payment account [current asset] -> Credit
|
||||
*
|
||||
* @async
|
||||
* @param {IPaymentReceive} paymentReceive
|
||||
* @param {Number} paymentReceiveId
|
||||
*/
|
||||
static async recordPaymentReceiveJournalEntries(
|
||||
paymentReceive,
|
||||
paymentReceiveId
|
||||
paymentReceive: any,
|
||||
paymentReceiveId?: number
|
||||
) {
|
||||
const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount');
|
||||
const formattedDate = moment(paymentReceive.payment_date).format(
|
||||
'YYYY-MM-DD'
|
||||
);
|
||||
const formattedDate = moment(paymentReceive.payment_date).format('YYYY-MM-DD');
|
||||
const receivableAccount = await AccountsService.getAccountByType(
|
||||
'accounts_receivable'
|
||||
);
|
||||
@@ -320,8 +331,8 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
* Revert the payment amount of the given invoices ids.
|
||||
* @param {Array} revertInvoices
|
||||
*/
|
||||
static async revertInvoicePaymentAmount(revertInvoices) {
|
||||
const opers = [];
|
||||
static async revertInvoicePaymentAmount(revertInvoices: any[]) {
|
||||
const opers: Promise<T>[] = [];
|
||||
|
||||
revertInvoices.forEach((revertInvoice) => {
|
||||
const { revertAmount, invoiceId } = revertInvoice;
|
||||
@@ -338,13 +349,13 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
* Saves difference changing between old and new invoice payment amount.
|
||||
* @param {Array} paymentReceiveEntries
|
||||
* @param {Array} newPaymentReceiveEntries
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
static async saveChangeInvoicePaymentAmount(
|
||||
paymentReceiveEntries,
|
||||
newPaymentReceiveEntries
|
||||
paymentReceiveEntries: [],
|
||||
newPaymentReceiveEntries: [],
|
||||
) {
|
||||
const opers = [];
|
||||
const opers: Promise<T>[] = [];
|
||||
const newEntriesTable = chain(newPaymentReceiveEntries)
|
||||
.groupBy('invoice_id')
|
||||
.mapValues((group) => (sumBy(group, 'payment_amount') || 0) * -1)
|
||||
@@ -359,7 +370,7 @@ export default class PaymentReceiveService extends JournalPosterService {
|
||||
.values()
|
||||
.value();
|
||||
|
||||
diffEntries.forEach((diffEntry) => {
|
||||
diffEntries.forEach((diffEntry: any) => {
|
||||
const oper = SaleInvoice.changePaymentAmount(
|
||||
diffEntry.invoice_id,
|
||||
diffEntry.payment_amount
|
||||
@@ -1,15 +1,15 @@
|
||||
import { omit, difference, sumBy } from 'lodash';
|
||||
import { omit, difference, sumBy, mixin } from 'lodash';
|
||||
import { SaleEstimate, ItemEntry } from '@/models';
|
||||
import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries';
|
||||
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
||||
|
||||
export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
export default class SaleEstimateService {
|
||||
/**
|
||||
* Creates a new estimate with associated entries.
|
||||
* @async
|
||||
* @param {IEstimate} estimate
|
||||
* @return {void}
|
||||
*/
|
||||
static async createEstimate(estimate) {
|
||||
static async createEstimate(estimate: any) {
|
||||
const amount = sumBy(estimate.entries, 'amount');
|
||||
const storedEstimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
@@ -17,15 +17,15 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
amount,
|
||||
...omit(estimate, ['entries']),
|
||||
});
|
||||
const storeEstimateEntriesOpers = [];
|
||||
const storeEstimateEntriesOpers: any[] = [];
|
||||
|
||||
estimate.entries.forEach((entry) => {
|
||||
estimate.entries.forEach((entry: any) => {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
.insert({
|
||||
reference_type: 'SaleEstimate',
|
||||
reference_id: storedEstimate.id,
|
||||
...omit(entry, ['total', 'amount']),
|
||||
...omit(entry, ['total', 'amount', 'id']),
|
||||
});
|
||||
storeEstimateEntriesOpers.push(oper);
|
||||
});
|
||||
@@ -40,7 +40,7 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
* @param {IEstimate} estimateId
|
||||
* @return {void}
|
||||
*/
|
||||
static async deleteEstimate(estimateId) {
|
||||
static async deleteEstimate(estimateId: number) {
|
||||
await ItemEntry.tenant()
|
||||
.query()
|
||||
.where('reference_id', estimateId)
|
||||
@@ -56,7 +56,7 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
* @param {IEstimate} estimate
|
||||
* @return {void}
|
||||
*/
|
||||
static async editEstimate(estimateId, estimate) {
|
||||
static async editEstimate(estimateId: number, estimate: any) {
|
||||
const amount = sumBy(estimate.entries, 'amount');
|
||||
const updatedEstimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
@@ -69,45 +69,12 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
.where('reference_id', estimateId)
|
||||
.where('reference_type', 'SaleEstimate');
|
||||
|
||||
const opers = [];
|
||||
const entriesHasID = estimate.entries.filter((entry) => entry.id);
|
||||
const entriesHasNoIDs = estimate.entries.filter((entry) => !entry.id);
|
||||
|
||||
const storedEntriesIds = storedEstimateEntries.map((e) => e.id);
|
||||
const formEstimateEntriesIds = entriesHasID.map((entry) => entry.id);
|
||||
|
||||
const entriesIdsShouldBeDeleted = difference(
|
||||
storedEntriesIds,
|
||||
formEstimateEntriesIds,
|
||||
const patchItemsEntries = HasItemsEntries.patchItemsEntries(
|
||||
estimate.entries, storedEstimateEntries, 'SaleEstimate', estimateId
|
||||
);
|
||||
// Deletes the given sale estimate entries ids.
|
||||
if (entriesIdsShouldBeDeleted.length > 0) {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
.whereIn('id', entriesIdsShouldBeDeleted)
|
||||
.delete();
|
||||
opers.push(oper);
|
||||
}
|
||||
// Insert the new sale estimate entries.
|
||||
entriesHasNoIDs.forEach((entry) => {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
.insert({
|
||||
reference_type: 'SaleEstimate',
|
||||
reference_id: estimateId,
|
||||
...entry,
|
||||
});
|
||||
opers.push(oper);
|
||||
});
|
||||
entriesHasID.forEach((entry) => {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
.patchAndFetchById(entry.id, {
|
||||
...omit(entry, ['id']),
|
||||
});
|
||||
opers.push(oper);
|
||||
});
|
||||
return Promise.all([...opers]);
|
||||
return Promise.all([
|
||||
patchItemsEntries,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +83,7 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
* @param {Numeric} estimateId
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static async isEstimateExists(estimateId) {
|
||||
static async isEstimateExists(estimateId: number) {
|
||||
const foundEstimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
.where('id', estimateId);
|
||||
@@ -129,7 +96,7 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
* @param {Numeric} estimateId
|
||||
* @param {IEstimate} estimate
|
||||
*/
|
||||
static async isEstimateEntriesIDsExists(estimateId, estimate) {
|
||||
static async isEstimateEntriesIDsExists(estimateId: number, estimate: any) {
|
||||
const estimateEntriesIds = estimate.entries
|
||||
.filter((e) => e.id)
|
||||
.map((e) => e.id);
|
||||
@@ -140,7 +107,7 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
.where('reference_id', estimateId)
|
||||
.where('reference_type', 'SaleEstimate');
|
||||
|
||||
const storedEstimateEntriesIds = estimateEntries.map((e) => e.id);
|
||||
const storedEstimateEntriesIds = estimateEntries.map((e: any) => e.id);
|
||||
const notFoundEntriesIDs = difference(
|
||||
estimateEntriesIds,
|
||||
storedEstimateEntriesIds
|
||||
@@ -153,7 +120,7 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
* @param {Integer} estimateId
|
||||
* @return {IEstimate}
|
||||
*/
|
||||
static async getEstimate(estimateId) {
|
||||
static async getEstimate(estimateId: number) {
|
||||
const estimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
.where('id', estimateId)
|
||||
@@ -166,7 +133,7 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
* Retrieve the estimate details with associated entries.
|
||||
* @param {Integer} estimateId
|
||||
*/
|
||||
static async getEstimateWithEntries(estimateId) {
|
||||
static async getEstimateWithEntries(estimateId: number) {
|
||||
const estimate = await SaleEstimate.tenant()
|
||||
.query()
|
||||
.where('id', estimateId)
|
||||
@@ -178,14 +145,14 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
|
||||
/**
|
||||
* Detarmines the estimate number uniqness.
|
||||
* @param {Integer} estimateNumber
|
||||
* @param {String} estimateNumber
|
||||
* @param {Integer} excludeEstimateId
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static async isEstimateNumberUnique(estimateNumber, excludeEstimateId) {
|
||||
static async isEstimateNumberUnique(estimateNumber: string, excludeEstimateId: number) {
|
||||
const foundEstimates = await SaleEstimate.tenant()
|
||||
.query()
|
||||
.onBuild((query) => {
|
||||
.onBuild((query: any) => {
|
||||
query.where('estimate_number', estimateNumber);
|
||||
|
||||
if (excludeEstimateId) {
|
||||
@@ -195,4 +162,4 @@ export default class SaleEstimateService extends ServiceItemsEntries {
|
||||
});
|
||||
return foundEstimates.length > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,26 @@ import {
|
||||
SaleInvoice,
|
||||
AccountTransaction,
|
||||
Account,
|
||||
Item,
|
||||
ItemEntry,
|
||||
Customer,
|
||||
} from '@/models';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries';
|
||||
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
||||
import CustomerRepository from '@/repositories/CustomerRepository';
|
||||
|
||||
export default class SaleInvoicesService extends ServiceItemsEntries {
|
||||
/**
|
||||
* Sales invoices service
|
||||
* @service
|
||||
*/
|
||||
export default class SaleInvoicesService {
|
||||
/**
|
||||
* Creates a new sale invoices and store it to the storage
|
||||
* with associated to entries and journal transactions.
|
||||
* @async
|
||||
* @param {ISaleInvoice}
|
||||
* @return {ISaleInvoice}
|
||||
*/
|
||||
static async createSaleInvoice(saleInvoice) {
|
||||
static async createSaleInvoice(saleInvoice: any) {
|
||||
const balance = sumBy(saleInvoice.entries, 'amount');
|
||||
const storedInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
@@ -26,9 +31,9 @@ export default class SaleInvoicesService extends ServiceItemsEntries {
|
||||
balance,
|
||||
payment_amount: 0,
|
||||
});
|
||||
const opers = [];
|
||||
const opers: Array<any> = [];
|
||||
|
||||
saleInvoice.entries.forEach((entry) => {
|
||||
saleInvoice.entries.forEach((entry: any) => {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
.insert({
|
||||
@@ -47,79 +52,56 @@ export default class SaleInvoicesService extends ServiceItemsEntries {
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the journal entries of sale invoice.
|
||||
* @param {ISaleInvoice} saleInvoice
|
||||
* @return {void}
|
||||
* Edit the given sale invoice.
|
||||
* @async
|
||||
* @param {Number} saleInvoiceId -
|
||||
* @param {ISaleInvoice} saleInvoice -
|
||||
*/
|
||||
async recordJournalEntries(saleInvoice) {
|
||||
const accountsDepGraph = await Account.depGraph().query().remember();
|
||||
const journal = new JournalPoster(accountsDepGraph);
|
||||
const receivableTotal = sumBy(saleInvoice.entries, 'total');
|
||||
static async editSaleInvoice(saleInvoiceId: number, saleInvoice: any) {
|
||||
const balance = sumBy(saleInvoice.entries, 'amount');
|
||||
const oldSaleInvoice = await SaleInvoice.tenant().query()
|
||||
.where('id', saleInvoiceId)
|
||||
.first();
|
||||
|
||||
const receivableAccount = await Account.tenant().query();
|
||||
const formattedDate = moment(saleInvoice.invoice_date).format('YYYY-MM-DD');
|
||||
|
||||
const saleItemsIds = saleInvoice.entries.map((e) => e.item_id);
|
||||
const storedInvoiceItems = await Item.tenant()
|
||||
const updatedSaleInvoices = await SaleInvoice.tenant()
|
||||
.query()
|
||||
.whereIn('id', saleItemsIds);
|
||||
|
||||
const commonJournalMeta = {
|
||||
debit: 0,
|
||||
credit: 0,
|
||||
referenceId: saleInvoice.id,
|
||||
referenceType: 'SaleInvoice',
|
||||
date: formattedDate,
|
||||
};
|
||||
const totalReceivableEntry = new journalEntry({
|
||||
...commonJournalMeta,
|
||||
debit: receivableTotal,
|
||||
account: receivableAccount.id,
|
||||
accountNormal: 'debit',
|
||||
});
|
||||
journal.debit(totalReceivableEntry);
|
||||
|
||||
saleInvoice.entries.forEach((entry) => {
|
||||
const item = {};
|
||||
const incomeEntry = JournalEntry({
|
||||
...commonJournalMeta,
|
||||
credit: entry.total,
|
||||
account: item.sellAccountId,
|
||||
accountNormal: 'credit',
|
||||
note: '',
|
||||
.where('id', saleInvoiceId)
|
||||
.update({
|
||||
balance,
|
||||
...omit(saleInvoice, ['entries']),
|
||||
});
|
||||
// Fetches the sale invoice items entries.
|
||||
const storedEntries = await ItemEntry.tenant()
|
||||
.query()
|
||||
.where('reference_id', saleInvoiceId)
|
||||
.where('reference_type', 'SaleInvoice');
|
||||
|
||||
if (item.type === 'inventory') {
|
||||
const inventoryCredit = JournalEntry({
|
||||
...commonJournalMeta,
|
||||
credit: entry.total,
|
||||
account: item.inventoryAccountId,
|
||||
accountNormal: 'credit',
|
||||
note: '',
|
||||
});
|
||||
const costEntry = JournalEntry({
|
||||
...commonJournalMeta,
|
||||
debit: entry.total,
|
||||
account: item.costAccountId,
|
||||
accountNormal: 'debit',
|
||||
note: '',
|
||||
});
|
||||
journal.debit(costEntry);
|
||||
}
|
||||
journal.credit(incomeEntry);
|
||||
});
|
||||
// Patch update the sale invoice items entries.
|
||||
const patchItemsEntriesOper = HasItemsEntries.patchItemsEntries(
|
||||
saleInvoice.entries, storedEntries, 'SaleInvoice', saleInvoiceId,
|
||||
);
|
||||
// Changes the diff customer balance between old and new amount.
|
||||
const changeCustomerBalanceOper = CustomerRepository.changeDiffBalance(
|
||||
saleInvoice.customer_id,
|
||||
oldSaleInvoice.customerId,
|
||||
balance,
|
||||
oldSaleInvoice.balance,
|
||||
);
|
||||
await Promise.all([
|
||||
journalPoster.saveEntries(),
|
||||
journalPoster.saveBalance(),
|
||||
patchItemsEntriesOper,
|
||||
changeCustomerBalanceOper,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given sale invoice with associated entries
|
||||
* and journal transactions.
|
||||
* @param {Integer} saleInvoiceId
|
||||
* @async
|
||||
* @param {Number} saleInvoiceId
|
||||
*/
|
||||
static async deleteSaleInvoice(saleInvoiceId) {
|
||||
static async deleteSaleInvoice(saleInvoiceId: number) {
|
||||
const oldSaleInvoice = await SaleInvoice.tenant().query().findById(saleInvoiceId);
|
||||
|
||||
await SaleInvoice.tenant().query().where('id', saleInvoiceId).delete();
|
||||
await ItemEntry.tenant()
|
||||
.query()
|
||||
@@ -127,6 +109,10 @@ export default class SaleInvoicesService extends ServiceItemsEntries {
|
||||
.where('reference_type', 'SaleInvoice')
|
||||
.delete();
|
||||
|
||||
const revertCustomerBalanceOper = Customer.changeBalance(
|
||||
oldSaleInvoice.customerId,
|
||||
oldSaleInvoice.balance * -1,
|
||||
);
|
||||
const invoiceTransactions = await AccountTransaction.tenant()
|
||||
.query()
|
||||
.whereIn('reference_type', ['SaleInvoice'])
|
||||
@@ -139,68 +125,29 @@ export default class SaleInvoicesService extends ServiceItemsEntries {
|
||||
journal.loadEntries(invoiceTransactions);
|
||||
journal.removeEntries();
|
||||
|
||||
await Promise.all([journal.deleteEntries(), journal.saveBalance()]);
|
||||
await Promise.all([
|
||||
journal.deleteEntries(),
|
||||
journal.saveBalance(),
|
||||
revertCustomerBalanceOper,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the given sale invoice.
|
||||
* @param {Integer} saleInvoiceId -
|
||||
* @param {ISaleInvoice} saleInvoice -
|
||||
* Records the journal entries of sale invoice.
|
||||
* @async
|
||||
* @param {ISaleInvoice} saleInvoice
|
||||
* @return {void}
|
||||
*/
|
||||
static async editSaleInvoice(saleInvoiceId, saleInvoice) {
|
||||
const updatedSaleInvoices = await SaleInvoice.tenant()
|
||||
.query()
|
||||
.where('id', saleInvoiceId)
|
||||
.update({
|
||||
...omit(saleInvoice, ['entries']),
|
||||
});
|
||||
const opers = [];
|
||||
const entriesIds = saleInvoice.entries.filter((entry) => entry.id);
|
||||
const entriesNoIds = saleInvoice.entries.filter((entry) => !entry.id);
|
||||
async recordJournalEntries(saleInvoice: any) {
|
||||
|
||||
const storedEntries = await ItemEntry.tenant()
|
||||
.query()
|
||||
.where('reference_id', saleInvoiceId)
|
||||
.where('reference_type', 'SaleInvoice');
|
||||
|
||||
const entriesIdsShouldDelete = this.entriesShouldDeleted(
|
||||
storedEntries,
|
||||
entriesIds
|
||||
);
|
||||
if (entriesIdsShouldDelete.length > 0) {
|
||||
const updateOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.whereIn('id', entriesIdsShouldDelete)
|
||||
.delete();
|
||||
opers.push(updateOper);
|
||||
}
|
||||
entriesIds.forEach((entry) => {
|
||||
const updateOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.where('id', entry.id)
|
||||
.update({
|
||||
...omit(entry, ['id']),
|
||||
});
|
||||
opers.push(updateOper);
|
||||
});
|
||||
entriesNoIds.forEach((entry) => {
|
||||
const insertOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.insert({
|
||||
reference_type: 'SaleInvoice',
|
||||
reference_id: saleInvoiceId,
|
||||
...omit(entry, ['id']),
|
||||
});
|
||||
opers.push(insertOper);
|
||||
})
|
||||
await Promise.all([...opers]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve sale invoice with associated entries.
|
||||
* @param {Integer} saleInvoiceId
|
||||
* @async
|
||||
* @param {Number} saleInvoiceId
|
||||
*/
|
||||
static async getSaleInvoiceWithEntries(saleInvoiceId) {
|
||||
static async getSaleInvoiceWithEntries(saleInvoiceId: number) {
|
||||
return SaleInvoice.tenant().query()
|
||||
.where('id', saleInvoiceId)
|
||||
.withGraphFetched('entries')
|
||||
@@ -212,7 +159,7 @@ export default class SaleInvoicesService extends ServiceItemsEntries {
|
||||
* @param {Integer} saleInvoiceId
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static async isSaleInvoiceExists(saleInvoiceId) {
|
||||
static async isSaleInvoiceExists(saleInvoiceId: number) {
|
||||
const foundSaleInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
.where('id', saleInvoiceId);
|
||||
@@ -221,10 +168,12 @@ export default class SaleInvoicesService extends ServiceItemsEntries {
|
||||
|
||||
/**
|
||||
* Detarmines the sale invoice number exists on the storage.
|
||||
* @param {Integer} saleInvoiceNumber
|
||||
* @async
|
||||
* @param {Number|String} saleInvoiceNumber
|
||||
* @param {Number} saleInvoiceId
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static async isSaleInvoiceNumberExists(saleInvoiceNumber, saleInvoiceId) {
|
||||
static async isSaleInvoiceNumberExists(saleInvoiceNumber: string|number, saleInvoiceId: number) {
|
||||
const foundSaleInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
.onBuild((query) => {
|
||||
@@ -243,7 +192,7 @@ export default class SaleInvoicesService extends ServiceItemsEntries {
|
||||
* @param {Array} invoicesIds
|
||||
* @return {Array}
|
||||
*/
|
||||
static async isInvoicesExist(invoicesIds) {
|
||||
static async isInvoicesExist(invoicesIds: Array<number>) {
|
||||
const storedInvoices = await SaleInvoice.tenant()
|
||||
.query()
|
||||
.onBuild((builder) => {
|
||||
@@ -2,19 +2,20 @@ import { omit, difference, sumBy } from 'lodash';
|
||||
import {
|
||||
SaleReceipt,
|
||||
Account,
|
||||
ItemEntry,
|
||||
} from '@/models';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import ItemEntry from '../../models/ItemEntry';
|
||||
import JournalPosterService from '@/services/Sales/JournalPosterService';
|
||||
import HasItemEntries from '@/services/Sales/HasItemsEntries';
|
||||
|
||||
export default class SalesReceipt extends JournalPosterService {
|
||||
export default class SalesReceipt {
|
||||
/**
|
||||
* Creates a new sale receipt with associated entries.
|
||||
* @async
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
* @return {Object}
|
||||
*/
|
||||
static async createSaleReceipt(saleReceipt) {
|
||||
static async createSaleReceipt(saleReceipt: any) {
|
||||
const amount = sumBy(saleReceipt.entries, 'amount');
|
||||
const storedSaleReceipt = await SaleReceipt.tenant()
|
||||
.query()
|
||||
@@ -22,9 +23,9 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
amount,
|
||||
...omit(saleReceipt, ['entries']),
|
||||
});
|
||||
const storeSaleReceiptEntriesOpers = [];
|
||||
const storeSaleReceiptEntriesOpers: Array<any> = [];
|
||||
|
||||
saleReceipt.entries.forEach((entry) => {
|
||||
saleReceipt.entries.forEach((entry: any) => {
|
||||
const oper = ItemEntry.tenant()
|
||||
.query()
|
||||
.insert({
|
||||
@@ -43,7 +44,7 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async _recordJournalTransactions(saleReceipt) {
|
||||
static async _recordJournalTransactions(saleReceipt: any) {
|
||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||
const journalPoster = new JournalPoster(accountsDepGraph);
|
||||
}
|
||||
@@ -54,7 +55,7 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
* @return {void}
|
||||
*/
|
||||
static async editSaleReceipt(saleReceiptId, saleReceipt) {
|
||||
static async editSaleReceipt(saleReceiptId: number, saleReceipt: any) {
|
||||
const amount = sumBy(saleReceipt.entries, 'amount');
|
||||
const updatedSaleReceipt = await SaleReceipt.tenant()
|
||||
.query()
|
||||
@@ -68,32 +69,11 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
.where('reference_id', saleReceiptId)
|
||||
.where('reference_type', 'SaleReceipt');
|
||||
|
||||
const storedSaleReceiptsIds = storedSaleReceiptEntries.map((e) => e.id);
|
||||
const entriesHasID = saleReceipt.entries.filter((entry) => entry.id);
|
||||
const entriesIds = entriesHasID.map((e) => e.id);
|
||||
|
||||
const opers = [];
|
||||
const entriesIdsShouldBeDeleted = difference(
|
||||
storedSaleReceiptsIds,
|
||||
entriesIds
|
||||
// Patch sale receipt items entries.
|
||||
const patchItemsEntries = HasItemEntries.patchItemsEntries(
|
||||
saleReceipt.entries, storedSaleReceiptEntries, 'SaleReceipt', saleReceiptId,
|
||||
);
|
||||
if (entriesIdsShouldBeDeleted.length > 0) {
|
||||
const deleteOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.whereIn('id', entriesIdsShouldBeDeleted)
|
||||
.where('reference_type', 'SaleReceipt')
|
||||
.delete();
|
||||
opers.push(deleteOper);
|
||||
}
|
||||
entriesHasID.forEach((entry) => {
|
||||
const updateOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.patchAndFetchById(entry.id, {
|
||||
...omit(entry, ['id']),
|
||||
});
|
||||
opers.push(updateOper);
|
||||
});
|
||||
return Promise.all([...opers]);
|
||||
return Promise.all([patchItemsEntries]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,20 +81,22 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
* @param {Integer} saleReceiptId
|
||||
* @return {void}
|
||||
*/
|
||||
static async deleteSaleReceipt(saleReceiptId) {
|
||||
await SaleReceipt.tenant().query().where('id', saleReceiptId).delete();
|
||||
await ItemEntry.tenant()
|
||||
static async deleteSaleReceipt(saleReceiptId: number) {
|
||||
const deleteSaleReceiptOper = SaleReceipt.tenant().query().where('id', saleReceiptId).delete();
|
||||
const deleteItemsEntriesOper = ItemEntry.tenant()
|
||||
.query()
|
||||
.where('reference_id', saleReceiptId)
|
||||
.where('reference_type', 'SaleReceipt')
|
||||
.delete();
|
||||
|
||||
// Delete all associated journal transactions to payment receive transaction.
|
||||
const deleteTransactionsOper = this.deleteJournalTransactions(
|
||||
const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions(
|
||||
saleReceiptId,
|
||||
'SaleReceipt'
|
||||
);
|
||||
return Promise.all([
|
||||
deleteItemsEntriesOper,
|
||||
deleteSaleReceiptOper,
|
||||
deleteTransactionsOper,
|
||||
]);
|
||||
}
|
||||
@@ -124,7 +106,7 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
* @param {Integer} saleReceiptId
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
static async isSaleReceiptExists(saleReceiptId) {
|
||||
static async isSaleReceiptExists(saleReceiptId: number) {
|
||||
const foundSaleReceipt = await SaleReceipt.tenant()
|
||||
.query()
|
||||
.where('id', saleReceiptId);
|
||||
@@ -136,7 +118,7 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
* @param {Integer} saleReceiptId
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
*/
|
||||
static async isSaleReceiptEntriesIDsExists(saleReceiptId, saleReceipt) {
|
||||
static async isSaleReceiptEntriesIDsExists(saleReceiptId: number, saleReceipt: any) {
|
||||
const entriesIDs = saleReceipt.entries
|
||||
.filter((e) => e.id)
|
||||
.map((e) => e.id);
|
||||
@@ -147,7 +129,7 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
.where('reference_id', saleReceiptId)
|
||||
.where('reference_type', 'SaleReceipt');
|
||||
|
||||
const storedEntriesIDs = storedEntries.map((e) => e.id);
|
||||
const storedEntriesIDs = storedEntries.map((e: any) => e.id);
|
||||
const notFoundEntriesIDs = difference(
|
||||
entriesIDs,
|
||||
storedEntriesIDs
|
||||
@@ -159,7 +141,7 @@ export default class SalesReceipt extends JournalPosterService {
|
||||
* Retrieve sale receipt with associated entries.
|
||||
* @param {Integer} saleReceiptId
|
||||
*/
|
||||
static async getSaleReceiptWithEntries(saleReceiptId) {
|
||||
static async getSaleReceiptWithEntries(saleReceiptId: number) {
|
||||
const saleReceipt = await SaleReceipt.tenant().query()
|
||||
.where('id', saleReceiptId)
|
||||
.withGraphFetched('entries');
|
||||
Reference in New Issue
Block a user