fix bugs in sales and purchases API.

This commit is contained in:
Ahmed Bouhuolia
2020-08-04 23:00:15 +02:00
parent db28cd2aef
commit ad772cf247
31 changed files with 420 additions and 819 deletions

View File

@@ -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';

View File

@@ -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);

View 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]);
}
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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) => {

View File

@@ -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');