feat: changing items quantity on hand with sales invoices and bills.

This commit is contained in:
a.bouhuolia
2020-12-23 13:11:02 +02:00
parent 395ee71c53
commit 26452d9c05
6 changed files with 202 additions and 47 deletions

View File

@@ -1,19 +1,15 @@
import { difference, map } from 'lodash';
import { Inject, Service } from 'typedi';
import {
IItemEntry,
IItemEntryDTO,
IItem,
} from 'interfaces';
import { IItemEntry, IItemEntryDTO, IItem } from 'interfaces';
import { ServiceError } from 'exceptions';
import TenancyService from 'services/Tenancy/TenancyService';
import { ItemEntry } from 'models';
import { entriesAmountDiff } from 'utils';
const ERRORS = {
ITEMS_NOT_FOUND: 'ITEMS_NOT_FOUND',
ENTRIES_IDS_NOT_FOUND: 'ENTRIES_IDS_NOT_FOUND',
NOT_PURCHASE_ABLE_ITEMS: 'NOT_PURCHASE_ABLE_ITEMS',
NOT_SELL_ABLE_ITEMS: 'NOT_SELL_ABLE_ITEMS'
NOT_SELL_ABLE_ITEMS: 'NOT_SELL_ABLE_ITEMS',
};
@Service()
@@ -23,15 +19,15 @@ export default class ItemsEntriesService {
/**
* Retrieve the inventory items entries of the reference id and type.
* @param {number} tenantId
* @param {string} referenceType
* @param {string} referenceId
* @param {number} tenantId
* @param {string} referenceType
* @param {string} referenceId
* @return {Promise<IItemEntry[]>}
*/
public async getInventoryEntries(
tenantId: number,
referenceType: string,
referenceId: number,
referenceId: number
): Promise<IItemEntry[]> {
const { Item, ItemEntry } = this.tenancy.models(tenantId);
@@ -46,7 +42,7 @@ export default class ItemsEntriesService {
// Inventory items ids.
const inventoryItemsIds = map(inventoryItems, 'id');
// Filtering the inventory items entries.
const inventoryItemsEntries = itemsEntries.filter(
(itemEntry) => inventoryItemsIds.indexOf(itemEntry.itemId) !== -1
@@ -57,13 +53,16 @@ export default class ItemsEntriesService {
/**
* Validates the entries items ids.
* @async
* @param {number} tenantId -
* @param {number} tenantId -
* @param {IItemEntryDTO} itemEntries -
*/
public async validateItemsIdsExistance(tenantId: number, itemEntries: IItemEntryDTO[]) {
public async validateItemsIdsExistance(
tenantId: number,
itemEntries: IItemEntryDTO[]
) {
const { Item } = this.tenancy.models(tenantId);
const itemsIds = itemEntries.map((e) => e.itemId);
const foundItems = await Item.query().whereIn('id', itemsIds);
const foundItemsIds = foundItems.map((item: IItem) => item.id);
@@ -74,13 +73,18 @@ export default class ItemsEntriesService {
}
}
/**
/**
* Validates the entries ids existance on the storage.
* @param {number} tenantId -
* @param {number} billId -
* @param {number} tenantId -
* @param {number} billId -
* @param {IItemEntry[]} billEntries -
*/
public async validateEntriesIdsExistance(tenantId: number, referenceId: number, referenceType: string, billEntries: IItemEntryDTO[]) {
public async validateEntriesIdsExistance(
tenantId: number,
referenceId: number,
referenceType: string,
billEntries: IItemEntryDTO[]
) {
const { ItemEntry } = this.tenancy.models(tenantId);
const entriesIds = billEntries
.filter((e: IItemEntry) => e.id)
@@ -94,18 +98,20 @@ export default class ItemsEntriesService {
const notFoundEntriesIds = difference(entriesIds, storedEntriesIds);
if (notFoundEntriesIds.length > 0) {
throw new ServiceError(ERRORS.ENTRIES_IDS_NOT_FOUND)
throw new ServiceError(ERRORS.ENTRIES_IDS_NOT_FOUND);
}
}
/**
* Validate the entries items that not purchase-able.
* Validate the entries items that not purchase-able.
*/
public async validateNonPurchasableEntriesItems(tenantId: number, itemEntries: IItemEntryDTO[]) {
public async validateNonPurchasableEntriesItems(
tenantId: number,
itemEntries: IItemEntryDTO[]
) {
const { Item } = this.tenancy.models(tenantId);
const itemsIds = itemEntries.map((e: IItemEntryDTO) => e.itemId);
const purchasbleItems = await Item.query()
.where('purchasable', true)
.whereIn('id', itemsIds);
@@ -119,12 +125,15 @@ export default class ItemsEntriesService {
}
/**
* Validate the entries items that not sell-able.
* Validate the entries items that not sell-able.
*/
public async validateNonSellableEntriesItems(tenantId: number, itemEntries: IItemEntryDTO[]) {
public async validateNonSellableEntriesItems(
tenantId: number,
itemEntries: IItemEntryDTO[]
) {
const { Item } = this.tenancy.models(tenantId);
const itemsIds = itemEntries.map((e: IItemEntryDTO) => e.itemId);
const sellableItems = await Item.query()
.where('sellable', true)
.whereIn('id', itemsIds);
@@ -136,4 +145,65 @@ export default class ItemsEntriesService {
throw new ServiceError(ERRORS.NOT_SELL_ABLE_ITEMS);
}
}
}
/**
* Changes items quantity from the given items entries the new and old onces.
* @param {number} tenantId
* @param {IItemEntry} entries - Items entries.
* @param {IItemEntry} oldEntries - Old items entries.
*/
public async changeItemsQuantity(
tenantId: number,
entries: IItemEntry[],
oldEntries?: IItemEntry[]
): Promise<void> {
const { itemRepository } = this.tenancy.repositories(tenantId);
const opers = [];
const diffEntries = entriesAmountDiff(
entries,
oldEntries,
'quantity',
'itemId'
);
diffEntries.forEach((entry: IItemEntry) => {
const changeQuantityOper = itemRepository.changeNumber(
{ id: entry.itemId, type: 'inventory' },
'quantityOnHand',
entry.quantity
);
opers.push(changeQuantityOper);
});
await Promise.all(opers);
}
/**
* Increment items quantity from the given items entries.
* @param {number} tenantId - Tenant id.
* @param {IItemEntry} entries - Items entries.
*/
public async incrementItemsEntries(
tenantId: number,
entries: IItemEntry[]
): Promise<void> {
return this.changeItemsQuantity(tenantId, entries);
}
/**
* Decrement items quantity from the given items entries.
* @param {number} tenantId - Tenant id.
* @param {IItemEntry} entries - Items entries.
*/
public async decrementItemsQuantity(
tenantId: number,
entries: IItemEntry[]
): Promise<void> {
return this.changeItemsQuantity(
tenantId,
entries.map((entry) => ({
...entry,
quantity: entry.quantity * -1,
}))
);
}
}

View File

@@ -251,6 +251,7 @@ export default class ItemsService implements IItemsService {
const storedItem = await Item.query().insertAndFetch({
...itemDTO,
active: defaultTo(itemDTO.active, 1),
quantityOnHand: itemDTO.type === 'inventory' ? 0 : null,
});
this.logger.info('[items] item inserted successfully.', {
tenantId,

View File

@@ -1,17 +1,14 @@
import { Service, Inject } from 'typedi';
import { omit, sumBy, pick, map } from 'lodash';
import { omit, sumBy, map } from 'lodash';
import moment from 'moment';
import uniqid from 'uniqid';
import {
EventDispatcher,
EventDispatcherInterface,
} from 'decorators/eventDispatcher';
import {
ISaleInvoice,
IItemEntry,
ISaleInvoiceCreateDTO,
ISaleInvoiceEditDTO,
IInventoryTransaction,
ISalesInvoicesFilter,
IPaginationMeta,
IFilterMeta,
@@ -109,7 +106,8 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
*/
async getInvoiceOrThrowError(tenantId: number, saleInvoiceId: number) {
const { SaleInvoice } = this.tenancy.models(tenantId);
const saleInvoice = await SaleInvoice.query().findById(saleInvoiceId);
const saleInvoice = await SaleInvoice.query().findById(saleInvoiceId)
.withGraphFetched('entries');
if (!saleInvoice) {
throw new ServiceError(ERRORS.SALE_INVOICE_NOT_FOUND);
@@ -355,7 +353,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
/**
* Records the inventory transactions of the given sale invoice in case
* the invoice has inventory entries only.
*
*
* @param {number} tenantId - Tenant id.
* @param {SaleInvoice} saleInvoice - Sale invoice DTO.
* @param {number} saleInvoiceId - Sale invoice id.
@@ -411,9 +409,9 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
/**
* Records the journal entries of the given sale invoice just
* in case the invoice has no inventory items entries.
*
* @param {number} tenantId -
* @param {number} saleInvoiceId
*
* @param {number} tenantId -
* @param {number} saleInvoiceId
* @param {boolean} override
* @return {Promise<void>}
*/
@@ -458,7 +456,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
// Triggers 'onInventoryTransactionsDeleted' event.
this.eventDispatcher.dispatch(
events.saleInvoice.onInventoryTransactionsDeleted,
{ tenantId, saleInvoiceId },
{ tenantId, saleInvoiceId }
);
}