fix: writing inventory transactions on invoice and bill created.

This commit is contained in:
a.bouhuolia
2020-12-19 19:48:38 +02:00
parent 5f8ecddd62
commit 1e8b00d8fe
7 changed files with 145 additions and 41 deletions

View File

@@ -81,7 +81,6 @@ export default () => {
dashboard.use(EnsureConfiguredMiddleware); dashboard.use(EnsureConfiguredMiddleware);
dashboard.use(EnsureTenantIsSeeded); dashboard.use(EnsureTenantIsSeeded);
dashboard.use('/users', Container.get(Users).router()); dashboard.use('/users', Container.get(Users).router());
dashboard.use('/invite', Container.get(InviteUsers).authRouter()); dashboard.use('/invite', Container.get(InviteUsers).authRouter());
dashboard.use('/currencies', Container.get(Currencies).router()); dashboard.use('/currencies', Container.get(Currencies).router());

View File

@@ -9,6 +9,7 @@ export interface IInventoryTransaction {
rate: number, rate: number,
transactionType: string, transactionType: string,
transactionId: string, transactionId: string,
lotNumber: string,
}; };
export interface IInventoryLotCost { export interface IInventoryLotCost {

View File

@@ -1,4 +1,5 @@
import { Container, Service, Inject } from 'typedi'; import { Container, Service, Inject } from 'typedi';
import { IInventoryTransaction, IItem } from 'interfaces'
import InventoryAverageCost from 'services/Inventory/InventoryAverageCost'; import InventoryAverageCost from 'services/Inventory/InventoryAverageCost';
import InventoryCostLotTracker from 'services/Inventory/InventoryCostLotTracker'; import InventoryCostLotTracker from 'services/Inventory/InventoryCostLotTracker';
import TenancyService from 'services/Tenancy/TenancyService'; import TenancyService from 'services/Tenancy/TenancyService';
@@ -61,26 +62,29 @@ export default class InventoryService {
/** /**
* Records the inventory transactions. * Records the inventory transactions.
* @param {number} tenantId - Tenant id. * @param {number} tenantId - Tenant id.
* @param {Bill} bill * @param {Bill} bill - Bill model object.
* @param {number} billId * @param {number} billId - Bill id.
* @return {Promise<void>}
*/ */
async recordInventoryTransactions( async recordInventoryTransactions(
tenantId: number, tenantId: number,
entries: [], entries: IInventoryTransaction[],
deleteOld: boolean, deleteOld: boolean,
) { ): Promise<void> {
const { InventoryTransaction, Item } = this.tenancy.models(tenantId); const { InventoryTransaction, Item } = this.tenancy.models(tenantId);
const entriesItemsIds = entries.map((e: any) => e.item_id); // Mapping the inventory entries items ids.
const entriesItemsIds = entries.map((e: any) => e.itemId);
const inventoryItems = await Item.query() const inventoryItems = await Item.query()
.whereIn('id', entriesItemsIds) .whereIn('id', entriesItemsIds)
.where('type', 'inventory'); .where('type', 'inventory');
const inventoryItemsIds = inventoryItems.map((i: any) => i.id); // Mapping the inventory items ids.
const inventoryItemsIds = inventoryItems.map((i: IItem) => i.id);
// Filter the bill entries that have inventory items. // Filter the bill entries that have inventory items.
const inventoryEntries = entries.filter( const inventoryEntries = entries.filter(
(entry: any) => inventoryItemsIds.indexOf(entry.item_id) !== -1 (entry: IInventoryTransaction) => inventoryItemsIds.indexOf(entry.itemId) !== -1
); );
inventoryEntries.forEach(async (entry: any) => { inventoryEntries.forEach(async (entry: any) => {
if (deleteOld) { if (deleteOld) {

View File

@@ -23,6 +23,7 @@ import {
IPaginationMeta, IPaginationMeta,
IFilterMeta, IFilterMeta,
IBillsFilter, IBillsFilter,
IItemEntry,
} from 'interfaces'; } from 'interfaces';
import { ServiceError } from 'exceptions'; import { ServiceError } from 'exceptions';
import ItemsService from 'services/Items/ItemsService'; import ItemsService from 'services/Items/ItemsService';
@@ -221,7 +222,6 @@ export default class BillsService extends SalesInvoicesCost {
authorizedUser, authorizedUser,
null null
); );
// Retrieve vendor or throw not found service error. // Retrieve vendor or throw not found service error.
await this.getVendorOrThrowError(tenantId, billDTO.vendorId); await this.getVendorOrThrowError(tenantId, billDTO.vendorId);
@@ -372,29 +372,42 @@ export default class BillsService extends SalesInvoicesCost {
* @param {Bill} bill * @param {Bill} bill
* @param {number} billId * @param {number} billId
*/ */
public recordInventoryTransactions( public async recordInventoryTransactions(
tenantId: number, tenantId: number,
bill: any, bill: IBill,
billId: number,
override?: boolean override?: boolean
) { ): Promise<void> {
const inventoryTransactions = bill.entries.map((entry) => ({ const invTransactions = bill.entries.map((entry: IItemEntry) => ({
...pick(entry, ['item_id', 'quantity', 'rate']), ...pick(entry, ['itemId', 'quantity', 'rate']),
lotNumber: bill.invLotNumber, lotNumber: bill.invLotNumber,
transactionType: 'Bill', transactionType: 'Bill',
transactionId: billId, transactionId: bill.id,
direction: 'IN', direction: 'IN',
date: bill.bill_date, date: bill.billDate,
entryId: entry.id, entryId: entry.id,
})); }));
return this.inventoryService.recordInventoryTransactions( await this.inventoryService.recordInventoryTransactions(
tenantId, tenantId,
inventoryTransactions, invTransactions,
override override
); );
} }
/**
* Reverts the inventory transactions of the given bill id.
* @param {number} tenantId - Tenant id.
* @param {number} billId - Bill id.
* @return {Promise<void>}
*/
public async revertInventoryTransactions(tenantId: number, billId: number) {
await this.inventoryService.deleteInventoryTransactions(
tenantId,
billId,
'Bill',
);
}
/** /**
* Records the bill journal transactions. * Records the bill journal transactions.
* @async * @async
@@ -437,7 +450,6 @@ export default class BillsService extends SalesInvoicesCost {
Bill, Bill,
billsFilter billsFilter
); );
this.logger.info('[bills] trying to get bills data table.', { this.logger.info('[bills] trying to get bills data table.', {
tenantId, tenantId,
billsFilter, billsFilter,

View File

@@ -7,13 +7,13 @@ import {
} from 'decorators/eventDispatcher'; } from 'decorators/eventDispatcher';
import { import {
ISaleInvoice, ISaleInvoice,
ISaleInvoiceDTO,
IItemEntry, IItemEntry,
ISaleInvoiceCreateDTO,
ISaleInvoiceEditDTO,
IInventoryTransaction,
ISalesInvoicesFilter, ISalesInvoicesFilter,
IPaginationMeta, IPaginationMeta,
IFilterMeta, IFilterMeta,
ISaleInvoiceCreateDTO,
ISaleInvoiceEditDTO,
} from 'interfaces'; } from 'interfaces';
import events from 'subscribers/events'; import events from 'subscribers/events';
import JournalPoster from 'services/Accounting/JournalPoster'; import JournalPoster from 'services/Accounting/JournalPoster';
@@ -351,48 +351,68 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
.where('reference_type', 'SaleInvoice') .where('reference_type', 'SaleInvoice')
.delete(); .delete();
// Triggers `onSaleInvoiceDeleted` event.
await this.eventDispatcher.dispatch(events.saleInvoice.onDeleted, { await this.eventDispatcher.dispatch(events.saleInvoice.onDeleted, {
tenantId, tenantId,
oldSaleInvoice, oldSaleInvoice,
saleInvoiceId,
}); });
} }
/** /**
* Records the inventory transactions from the givne sale invoice input. * Records the inventory transactions from the givne sale invoice input.
* @param {SaleInvoice} saleInvoice - * @parma {number} tenantId - Tenant id.
* @param {number} saleInvoiceId - * @param {SaleInvoice} saleInvoice - Sale invoice DTO.
* @param {boolean} override - * @param {number} saleInvoiceId - Sale invoice id.
* @param {boolean} override - Allow to override old transactions.
*/ */
private recordInventoryTranscactions( public recordInventoryTranscactions(
tenantId: number, tenantId: number,
saleInvoice, saleInvoice: ISaleInvoice,
saleInvoiceId: number,
override?: boolean override?: boolean
) { ) {
this.logger.info('[sale_invoice] saving inventory transactions'); this.logger.info('[sale_invoice] saving inventory transactions');
const inventortyTransactions = saleInvoice.entries.map((entry) => ({ const invTransactions: IInventoryTransaction[] = saleInvoice.entries.map(
...pick(entry, ['item_id', 'quantity', 'rate']), (entry: IItemEntry) => ({
lotNumber: saleInvoice.invLotNumber, ...pick(entry, ['itemId', 'quantity', 'rate']),
lotNumber: 1,
transactionType: 'SaleInvoice', transactionType: 'SaleInvoice',
transactionId: saleInvoiceId, transactionId: saleInvoice.id,
direction: 'OUT', direction: 'OUT',
date: saleInvoice.invoice_date, date: saleInvoice.invoiceDate,
entryId: entry.id, entryId: entry.id,
})); })
);
return this.inventoryService.recordInventoryTransactions( return this.inventoryService.recordInventoryTransactions(
tenantId, tenantId,
inventortyTransactions, invTransactions,
override override
); );
} }
/**
* Reverting the inventory transactions once the invoice deleted.
* @param {number} tenantId - Tenant id.
* @param {number} billId - Bill id.
*/
public revertInventoryTransactions(
tenantId: number,
billId: number
): Promise<void> {
return this.inventoryService.deleteInventoryTransactions(
tenantId,
billId,
'SaleInvoice'
);
}
/** /**
* Deletes the inventory transactions. * Deletes the inventory transactions.
* @param {string} transactionType * @param {string} transactionType
* @param {number} transactionId * @param {number} transactionId
*/ */
private async revertInventoryTransactions( private async revertInventoryTransactions_(
tenantId: number, tenantId: number,
inventoryTransactions: array inventoryTransactions: array
) { ) {

View File

@@ -108,4 +108,41 @@ export default class BillSubscriber {
oldBill.vendorId oldBill.vendorId
); );
} }
/**
* Handles writing the inventory transactions once bill created.
*/
@On(events.bill.onCreated)
async handleWritingInventoryTransactions({ tenantId, bill }) {
this.logger.info('[bill] writing the inventory transactions', { tenantId });
this.billsService.recordInventoryTransactions(
tenantId,
bill,
);
}
/**
* Handles the overwriting the inventory transactions once bill edited.
*/
@On(events.bill.onEdited)
async handleOverwritingInventoryTransactions({ tenantId, bill }) {
this.logger.info('[bill] overwriting the inventory transactions.', { tenantId });
this.billsService.recordInventoryTransactions(
tenantId,
bill,
true,
);
}
/**
* Handles the reverting the inventory transactions once the bill deleted.
*/
@On(events.bill.onDeleted)
async handleRevertInventoryTransactions({ tenantId, billId }) {
this.logger.info('[bill] reverting the bill inventory transactions', { tenantId, billId });
this.billsService.revertInventoryTransactions(
tenantId,
billId,
);
}
} }

View File

@@ -4,6 +4,7 @@ import events from 'subscribers/events';
import TenancyService from 'services/Tenancy/TenancyService'; import TenancyService from 'services/Tenancy/TenancyService';
import SettingsService from 'services/Settings/SettingsService'; import SettingsService from 'services/Settings/SettingsService';
import SaleEstimateService from 'services/Sales/SalesEstimate'; import SaleEstimateService from 'services/Sales/SalesEstimate';
import SaleInvoicesService from 'services/Sales/SalesInvoices';
@EventSubscriber() @EventSubscriber()
export default class SaleInvoiceSubscriber { export default class SaleInvoiceSubscriber {
@@ -11,12 +12,14 @@ export default class SaleInvoiceSubscriber {
tenancy: TenancyService; tenancy: TenancyService;
settingsService: SettingsService; settingsService: SettingsService;
saleEstimatesService: SaleEstimateService; saleEstimatesService: SaleEstimateService;
saleInvoicesService: SaleInvoicesService;
constructor() { constructor() {
this.logger = Container.get('logger'); this.logger = Container.get('logger');
this.tenancy = Container.get(TenancyService); this.tenancy = Container.get(TenancyService);
this.settingsService = Container.get(SettingsService); this.settingsService = Container.get(SettingsService);
this.saleEstimatesService = Container.get(SaleEstimateService); this.saleEstimatesService = Container.get(SaleEstimateService);
this.saleInvoicesService = Container.get(SaleInvoicesService);
} }
/** /**
@@ -114,4 +117,32 @@ export default class SaleInvoiceSubscriber {
group: 'sales_invoices', group: 'sales_invoices',
}); });
} }
/**
* Handles the writing inventory transactions once the invoice created.
*/
@On(events.saleInvoice.onCreated)
public async handleWritingInventoryTransactions({ tenantId, saleInvoice }) {
this.logger.info('[sale_invoice] trying to write inventory transactions.', {
tenantId,
});
await this.saleInvoicesService.recordInventoryTranscactions(
tenantId,
saleInvoice,
);
}
/**
* Handles deleting the inventory transactions once the invoice deleted.
*/
@On(events.saleInvoice.onDeleted)
public async handleDeletingInventoryTransactions({ tenantId, saleInvoiceId }) {
this.logger.info('[sale_invoice] trying to revert inventory transactions.', {
tenantId, saleInvoiceId,
});
await this.saleInvoicesService.revertInventoryTransactions(
tenantId,
saleInvoiceId,
);
}
} }