mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 06:10:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehouse } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class BillActivateWarehouses {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Updates all credit note transactions with the primary warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} primaryWarehouse
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public updateBillsWithWarehouse = async (
|
||||
tenantId: number,
|
||||
primaryWarehouse: IWarehouse
|
||||
): Promise<void> => {
|
||||
const { Bill, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Updates the sale estimates with primary warehouse.
|
||||
await Bill.query().update({ warehouseId: primaryWarehouse.id });
|
||||
|
||||
// Update the sale estimates entries with primary warehouse.
|
||||
await ItemEntry.query().where('referenceType', 'Bill').update({
|
||||
warehouseId: primaryWarehouse.id,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehouse } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class CreditNotesActivateWarehouses {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Updates all credit note transactions with the primary warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} primaryWarehouse
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public updateCreditsWithWarehouse = async (
|
||||
tenantId: number,
|
||||
primaryWarehouse: IWarehouse
|
||||
): Promise<void> => {
|
||||
const { CreditNote, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Updates the sale estimates with primary warehouse.
|
||||
await CreditNote.query().update({ warehouseId: primaryWarehouse.id });
|
||||
|
||||
// Update the sale estimates entries with primary warehouse.
|
||||
await ItemEntry.query().where('referenceType', 'CreditNote').update({
|
||||
warehouseId: primaryWarehouse.id,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehouse } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class EstimatesActivateWarehouses {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} primaryWarehouse
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public updateEstimatesWithWarehouse = async (
|
||||
tenantId: number,
|
||||
primaryWarehouse: IWarehouse
|
||||
): Promise<void> => {
|
||||
const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Updates the sale estimates with primary warehouse.
|
||||
await SaleEstimate.query().update({ warehouseId: primaryWarehouse.id });
|
||||
|
||||
// Update the sale estimates entries with primary warehouse.
|
||||
await ItemEntry.query().where('referenceType', 'SaleEstimate').update({
|
||||
warehouseId: primaryWarehouse.id,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehouse } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class InventoryActivateWarehouses {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} primaryWarehouse
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public updateInventoryTransactionsWithWarehouse = async (
|
||||
tenantId: number,
|
||||
primaryWarehouse: IWarehouse
|
||||
): Promise<void> => {
|
||||
const { InventoryTransaction, InventoryCostLotTracker } =
|
||||
this.tenancy.models(tenantId);
|
||||
|
||||
// Updates the inventory transactions with primary warehouse.
|
||||
await InventoryTransaction.query().update({
|
||||
warehouseId: primaryWarehouse.id,
|
||||
});
|
||||
await InventoryCostLotTracker.query().update({
|
||||
warehouseId: primaryWarehouse.id,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehouse } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class InvoicesActivateWarehouses {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} primaryWarehouse
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public updateInvoicesWithWarehouse = async (
|
||||
tenantId: number,
|
||||
primaryWarehouse: IWarehouse
|
||||
): Promise<void> => {
|
||||
const { SaleInvoice, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Updates the sale invoices with primary warehouse.
|
||||
await SaleInvoice.query().update({ warehouseId: primaryWarehouse.id });
|
||||
|
||||
// Update the sale invoices entries with primary warehouse.
|
||||
await ItemEntry.query().where('referenceType', 'SaleInvoice').update({
|
||||
warehouseId: primaryWarehouse.id,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehouse } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class ReceiptActivateWarehouses {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Updates all sale receipts transactions with the primary warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} primaryWarehouse
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public updateReceiptsWithWarehouse = async (
|
||||
tenantId: number,
|
||||
primaryWarehouse: IWarehouse
|
||||
): Promise<void> => {
|
||||
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Updates the vendor credits transactions with primary warehouse.
|
||||
await SaleReceipt.query().update({ warehouseId: primaryWarehouse.id });
|
||||
|
||||
// Update the sale invoices entries with primary warehouse.
|
||||
await ItemEntry.query().where('referenceType', 'SaleReceipt').update({
|
||||
warehouseId: primaryWarehouse.id,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehouse } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class VendorCreditActivateWarehouses {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Updates all vendor credits transactions with the primary warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} primaryWarehouse
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public updateCreditsWithWarehouse = async (
|
||||
tenantId: number,
|
||||
primaryWarehouse: IWarehouse
|
||||
): Promise<void> => {
|
||||
const { VendorCredit, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Updates the vendor credits transactions with primary warehouse.
|
||||
await VendorCredit.query().update({ warehouseId: primaryWarehouse.id });
|
||||
|
||||
// Update the sale invoices entries with primary warehouse.
|
||||
await ItemEntry.query().where('referenceType', 'VendorCredit').update({
|
||||
warehouseId: primaryWarehouse.id,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { CreateInitialWarehouse } from './CreateInitialWarehouse';
|
||||
import { WarehousesSettings } from './WarehousesSettings';
|
||||
|
||||
import events from '@/subscribers/events';
|
||||
import { ERRORS } from './contants';
|
||||
|
||||
@Service()
|
||||
export class ActivateWarehouses {
|
||||
@Inject()
|
||||
uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
createInitialWarehouse: CreateInitialWarehouse;
|
||||
|
||||
@Inject()
|
||||
settings: WarehousesSettings;
|
||||
|
||||
/**
|
||||
* Throws error if the multi-warehouses is already activated.
|
||||
* @param {boolean} isActivated
|
||||
*/
|
||||
private throwIfWarehousesActivated = (isActivated: boolean) => {
|
||||
if (isActivated) {
|
||||
throw new ServiceError(ERRORS.MUTLI_WAREHOUSES_ALREADY_ACTIVATED);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Activates the multi-warehouses.
|
||||
*
|
||||
* - Creates a new warehouses and mark it as primary.
|
||||
* - Seed warehouses items quantity.
|
||||
* - Mutate inventory transactions with the primary warehouse.
|
||||
* --------
|
||||
* @param {number} tenantId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public activateWarehouses = (tenantId: number): Promise<void> => {
|
||||
// Retrieve whether the multi-warehouses is active.
|
||||
const isActivated = this.settings.isMultiWarehousesActive(tenantId);
|
||||
|
||||
// Throw error if the warehouses is already activated.
|
||||
this.throwIfWarehousesActivated(isActivated);
|
||||
|
||||
// Activates multi-warehouses on unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseActivate` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouse.onActivate, {
|
||||
tenantId,
|
||||
trx,
|
||||
});
|
||||
// Creates a primary warehouse on the storage..
|
||||
const primaryWarehouse =
|
||||
await this.createInitialWarehouse.createInitialWarehouse(tenantId);
|
||||
|
||||
// Marks the multi-warehouses is activated.
|
||||
this.settings.markMutliwarehoussAsActivated(tenantId);
|
||||
|
||||
// Triggers `onWarehouseActivated` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouse.onActivated, {
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
trx,
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import { IWarehousesActivatedPayload } from '@/interfaces';
|
||||
import { UpdateInventoryTransactionsWithWarehouse } from './UpdateInventoryTransactionsWithWarehouse';
|
||||
import { CreateInitialWarehousesItemsQuantity } from './CreateInitialWarehousesitemsQuantity';
|
||||
|
||||
@Service()
|
||||
export class ActivateWarehousesSubscriber {
|
||||
@Inject()
|
||||
private updateInventoryTransactionsWithWarehouse: UpdateInventoryTransactionsWithWarehouse;
|
||||
|
||||
@Inject()
|
||||
private createInitialWarehousesItemsQuantity: CreateInitialWarehousesItemsQuantity;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
attach(bus) {
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.updateInventoryTransactionsWithWarehouseOnActivating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.createInitialWarehousesItemsQuantityOnActivating
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates inventory transactiont to primary warehouse once
|
||||
* multi-warehouses activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private updateInventoryTransactionsWithWarehouseOnActivating = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.updateInventoryTransactionsWithWarehouse.run(
|
||||
tenantId,
|
||||
primaryWarehouse.id
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates initial warehouses items quantity once the multi-warehouses activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private createInitialWarehousesItemsQuantityOnActivating = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.createInitialWarehousesItemsQuantity.run(
|
||||
tenantId,
|
||||
primaryWarehouse.id
|
||||
);
|
||||
};
|
||||
}
|
||||
26
packages/server/src/services/Warehouses/CRUDWarehouse.ts
Normal file
26
packages/server/src/services/Warehouses/CRUDWarehouse.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from './contants';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
export class CRUDWarehouse {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
getWarehouseOrThrowNotFound = async (tenantId: number, warehouseId: number) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const foundWarehouse = await Warehouse.query().findById(warehouseId);
|
||||
|
||||
if (!foundWarehouse) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_NOT_FOUND);
|
||||
}
|
||||
return foundWarehouse;
|
||||
};
|
||||
|
||||
throwIfWarehouseNotFound = (warehouse) => {
|
||||
if (!warehouse) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { CreateWarehouse } from './CreateWarehouse';
|
||||
|
||||
@Service()
|
||||
export class CreateInitialWarehouse {
|
||||
@Inject()
|
||||
private createWarehouse: CreateWarehouse;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Creates a initial warehouse.
|
||||
* @param {number} tenantId
|
||||
*/
|
||||
public createInitialWarehouse = async (tenantId: number) => {
|
||||
const { __ } = this.tenancy.i18n(tenantId);
|
||||
|
||||
return this.createWarehouse.createWarehouse(tenantId, {
|
||||
name: __('warehouses.primary_warehouse'),
|
||||
code: '10001',
|
||||
primary: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import { IItem, IItemWarehouseQuantityChange } from '@/interfaces';
|
||||
import { WarehousesItemsQuantitySync } from './Integrations/WarehousesItemsQuantitySync';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class CreateInitialWarehousesItemsQuantity {
|
||||
@Inject()
|
||||
private warehousesItemsQuantitySync: WarehousesItemsQuantitySync;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieves items warehouses quantity changes of the given inventory items.
|
||||
* @param {IItem[]} items
|
||||
* @param {IWarehouse} primaryWarehouse
|
||||
* @returns {IItemWarehouseQuantityChange[]}
|
||||
*/
|
||||
private getWarehousesItemsChanges = (
|
||||
items: IItem[],
|
||||
primaryWarehouseId: number
|
||||
): IItemWarehouseQuantityChange[] => {
|
||||
return items
|
||||
.filter((item: IItem) => item.quantityOnHand)
|
||||
.map((item: IItem) => ({
|
||||
itemId: item.id,
|
||||
warehouseId: primaryWarehouseId,
|
||||
amount: item.quantityOnHand,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates initial warehouses items quantity.
|
||||
* @param {number} tenantId
|
||||
*/
|
||||
public run = async (
|
||||
tenantId: number,
|
||||
primaryWarehouseId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const { Item } = this.tenancy.models(tenantId);
|
||||
|
||||
const items = await Item.query(trx).where('type', 'Inventory');
|
||||
|
||||
const warehousesChanges = this.getWarehousesItemsChanges(
|
||||
items,
|
||||
primaryWarehouseId
|
||||
);
|
||||
await this.warehousesItemsQuantitySync.mutateWarehousesItemsQuantity(
|
||||
tenantId,
|
||||
warehousesChanges,
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
83
packages/server/src/services/Warehouses/CreateWarehouse.ts
Normal file
83
packages/server/src/services/Warehouses/CreateWarehouse.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import {
|
||||
ICreateWarehouseDTO,
|
||||
IWarehouse,
|
||||
IWarehouseCreatedPayload,
|
||||
IWarehouseCreatePayload,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import events from '@/subscribers/events';
|
||||
import { WarehouseValidator } from './WarehouseValidator';
|
||||
|
||||
@Service()
|
||||
export class CreateWarehouse {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private validator: WarehouseValidator;
|
||||
|
||||
/**
|
||||
* Authorize the warehouse before deleting.
|
||||
* @param {number} tenantId -
|
||||
* @param {ICreateWarehouseDTO} warehouseDTO -
|
||||
*/
|
||||
public authorize = async (
|
||||
tenantId: number,
|
||||
warehouseDTO: ICreateWarehouseDTO
|
||||
) => {
|
||||
if (warehouseDTO.code) {
|
||||
await this.validator.validateWarehouseCodeUnique(
|
||||
tenantId,
|
||||
warehouseDTO.code
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new warehouse on the system.
|
||||
* @param {number} tenantId
|
||||
* @param {ICreateWarehouseDTO} warehouseDTO
|
||||
*/
|
||||
public createWarehouse = async (
|
||||
tenantId: number,
|
||||
warehouseDTO: ICreateWarehouseDTO
|
||||
): Promise<IWarehouse> => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
// Authorize warehouse before creating.
|
||||
await this.authorize(tenantId, warehouseDTO);
|
||||
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseCreate` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouse.onEdit, {
|
||||
tenantId,
|
||||
warehouseDTO,
|
||||
trx,
|
||||
} as IWarehouseCreatePayload);
|
||||
|
||||
// Creates a new warehouse on the storage.
|
||||
const warehouse = await Warehouse.query(trx).insertAndFetch({
|
||||
...warehouseDTO,
|
||||
});
|
||||
// Triggers `onWarehouseCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouse.onCreated, {
|
||||
tenantId,
|
||||
warehouseDTO,
|
||||
warehouse,
|
||||
trx,
|
||||
} as IWarehouseCreatedPayload);
|
||||
|
||||
return warehouse;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class DeleteItemWarehousesQuantity {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Deletes the given item warehouses quantities.
|
||||
* @param {number} tenantId
|
||||
* @param {number} itemId
|
||||
* @param {Knex.Transaction} trx -
|
||||
*/
|
||||
public deleteItemWarehousesQuantity = async (
|
||||
tenantId: number,
|
||||
itemId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const { ItemWarehouseQuantity } = this.tenancy.models(tenantId);
|
||||
|
||||
await ItemWarehouseQuantity.query(trx).where('itemId', itemId).delete();
|
||||
};
|
||||
}
|
||||
86
packages/server/src/services/Warehouses/DeleteWarehouse.ts
Normal file
86
packages/server/src/services/Warehouses/DeleteWarehouse.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
import { IWarehouseDeletedPayload, IWarehouseDeletePayload } from '@/interfaces';
|
||||
import { CRUDWarehouse } from './CRUDWarehouse';
|
||||
import { WarehouseValidator } from './WarehouseValidator';
|
||||
import { ERRORS } from './contants';
|
||||
|
||||
@Service()
|
||||
export class DeleteWarehouse extends CRUDWarehouse {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
validator: WarehouseValidator;
|
||||
|
||||
/**
|
||||
* Validates the given warehouse before deleting.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public authorize = async (tenantId: number, warehouseId: number) => {
|
||||
await this.validator.validateWarehouseNotOnlyWarehouse(
|
||||
tenantId,
|
||||
warehouseId
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes specific warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public deleteWarehouse = async (
|
||||
tenantId: number,
|
||||
warehouseId: number
|
||||
): Promise<void> => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old warehouse or throw not found service error.
|
||||
const oldWarehouse = await Warehouse.query()
|
||||
.findById(warehouseId)
|
||||
.throwIfNotFound()
|
||||
.queryAndThrowIfHasRelations({
|
||||
type: ERRORS.WAREHOUSE_HAS_ASSOCIATED_TRANSACTIONS,
|
||||
});
|
||||
|
||||
// Validates the given warehouse before deleting.
|
||||
await this.authorize(tenantId, warehouseId);
|
||||
|
||||
// Creates a new warehouse under unit-of-work.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
const eventPayload = {
|
||||
tenantId,
|
||||
warehouseId,
|
||||
oldWarehouse,
|
||||
trx,
|
||||
} as IWarehouseDeletePayload | IWarehouseDeletedPayload;
|
||||
|
||||
// Triggers `onWarehouseCreate`.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.warehouse.onDelete,
|
||||
eventPayload
|
||||
);
|
||||
// Delets the given warehouse from the storage.
|
||||
await Warehouse.query().findById(warehouseId).delete();
|
||||
|
||||
// Triggers `onWarehouseCreated`.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.warehouse.onDeleted,
|
||||
eventPayload as IWarehouseDeletedPayload
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
82
packages/server/src/services/Warehouses/EditWarehouse.ts
Normal file
82
packages/server/src/services/Warehouses/EditWarehouse.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
import { IEditWarehouseDTO, IWarehouse } from '@/interfaces';
|
||||
import { WarehouseValidator } from './WarehouseValidator';
|
||||
|
||||
@Service()
|
||||
export class EditWarehouse {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private validator: WarehouseValidator;
|
||||
|
||||
/**
|
||||
* Authorize the warehouse before deleting.
|
||||
* @param {number} tenantId -
|
||||
* @param {ICreateWarehouseDTO} warehouseDTO -
|
||||
*/
|
||||
public authorize = async (
|
||||
tenantId: number,
|
||||
warehouseDTO: IEditWarehouseDTO,
|
||||
warehouseId: number
|
||||
) => {
|
||||
if (warehouseDTO.code) {
|
||||
await this.validator.validateWarehouseCodeUnique(
|
||||
tenantId,
|
||||
warehouseDTO.code,
|
||||
warehouseId
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits a new warehouse on the system.
|
||||
* @param {number} tenantId
|
||||
* @param {ICreateWarehouseDTO} warehouseDTO
|
||||
* @returns {Promise<IWarehouse>}
|
||||
*/
|
||||
public editWarehouse = async (
|
||||
tenantId: number,
|
||||
warehouseId: number,
|
||||
warehouseDTO: IEditWarehouseDTO
|
||||
): Promise<IWarehouse> => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
// Authorize the warehouse DTO before editing.
|
||||
await this.authorize(tenantId, warehouseDTO, warehouseId);
|
||||
|
||||
// Edits warehouse under unit-of-work.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseEdit` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouse.onEdit, {
|
||||
tenantId,
|
||||
warehouseId,
|
||||
warehouseDTO,
|
||||
trx,
|
||||
});
|
||||
// Updates the given branch on the storage.
|
||||
const warehouse = await Warehouse.query().patchAndFetchById(warehouseId, {
|
||||
...warehouseDTO,
|
||||
});
|
||||
// Triggers `onWarehouseEdited` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouse.onEdited, {
|
||||
tenantId,
|
||||
warehouse,
|
||||
warehouseDTO,
|
||||
trx,
|
||||
});
|
||||
return warehouse;
|
||||
});
|
||||
};
|
||||
}
|
||||
39
packages/server/src/services/Warehouses/EventsProvider.ts
Normal file
39
packages/server/src/services/Warehouses/EventsProvider.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
BillsActivateWarehousesSubscriber,
|
||||
CreditsActivateWarehousesSubscriber,
|
||||
InvoicesActivateWarehousesSubscriber,
|
||||
ReceiptsActivateWarehousesSubscriber,
|
||||
EstimatesActivateWarehousesSubscriber,
|
||||
InventoryActivateWarehousesSubscriber,
|
||||
VendorCreditsActivateWarehousesSubscriber,
|
||||
} from './Subscribers/Activate';
|
||||
import {
|
||||
BillWarehousesValidateSubscriber,
|
||||
CreditNoteWarehousesValidateSubscriber,
|
||||
SaleReceiptWarehousesValidateSubscriber,
|
||||
SaleEstimateWarehousesValidateSubscriber,
|
||||
SaleInvoicesWarehousesValidateSubscriber,
|
||||
VendorCreditWarehousesValidateSubscriber,
|
||||
InventoryAdjustmentWarehouseValidatorSubscriber,
|
||||
} from './Subscribers/Validators';
|
||||
import { DeleteItemWarehousesQuantitySubscriber } from './Subscribers/DeleteItemWarehousesQuantitySubscriber';
|
||||
|
||||
export default () => [
|
||||
BillsActivateWarehousesSubscriber,
|
||||
CreditsActivateWarehousesSubscriber,
|
||||
InvoicesActivateWarehousesSubscriber,
|
||||
ReceiptsActivateWarehousesSubscriber,
|
||||
EstimatesActivateWarehousesSubscriber,
|
||||
InventoryActivateWarehousesSubscriber,
|
||||
VendorCreditsActivateWarehousesSubscriber,
|
||||
|
||||
BillWarehousesValidateSubscriber,
|
||||
CreditNoteWarehousesValidateSubscriber,
|
||||
SaleReceiptWarehousesValidateSubscriber,
|
||||
SaleEstimateWarehousesValidateSubscriber,
|
||||
SaleInvoicesWarehousesValidateSubscriber,
|
||||
VendorCreditWarehousesValidateSubscriber,
|
||||
InventoryAdjustmentWarehouseValidatorSubscriber,
|
||||
|
||||
DeleteItemWarehousesQuantitySubscriber,
|
||||
];
|
||||
24
packages/server/src/services/Warehouses/GetWarehouse.ts
Normal file
24
packages/server/src/services/Warehouses/GetWarehouse.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { CRUDWarehouse } from './CRUDWarehouse';
|
||||
|
||||
@Service()
|
||||
export class GetWarehouse extends CRUDWarehouse {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieves warehouse details.
|
||||
* @param {number} tenantId
|
||||
* @returns
|
||||
*/
|
||||
public getWarehouse = async (tenantId: number, warehouseId: number) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const warehouse = await Warehouse.query().findById(warehouseId);
|
||||
|
||||
this.throwIfWarehouseNotFound(warehouse);
|
||||
|
||||
return warehouse;
|
||||
};
|
||||
}
|
||||
21
packages/server/src/services/Warehouses/GetWarehouses.ts
Normal file
21
packages/server/src/services/Warehouses/GetWarehouses.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class GetWarehouses {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieves warehouses list.
|
||||
* @param {number} tenantId
|
||||
* @returns
|
||||
*/
|
||||
public getWarehouses = async (tenantId: number) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const warehouses = await Warehouse.query().orderBy('name', 'DESC');
|
||||
|
||||
return warehouses;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { chain, difference } from 'lodash';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from './constants';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class ValidateWarehouseExistance {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Validate transaction warehouse id existance.
|
||||
* @param transDTO
|
||||
* @param entries
|
||||
*/
|
||||
public validateWarehouseIdExistance = (
|
||||
transDTO: { warehouseId?: number },
|
||||
entries: { warehouseId?: number }[] = []
|
||||
) => {
|
||||
const notAssignedWarehouseEntries = entries.filter((e) => !e.warehouseId);
|
||||
|
||||
if (notAssignedWarehouseEntries.length > 0 && !transDTO.warehouseId) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_ID_NOT_FOUND);
|
||||
}
|
||||
if (entries.length === 0 && !transDTO.warehouseId) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_ID_NOT_FOUND);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate warehouse existance.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
*/
|
||||
public validateWarehouseExistance = (
|
||||
tenantId: number,
|
||||
warehouseId: number
|
||||
) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const warehouse = Warehouse.query().findById(warehouseId);
|
||||
|
||||
if (!warehouse) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_ID_NOT_FOUND);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {{ warehouseId?: number }[]} entries
|
||||
*/
|
||||
public validateItemEntriesWarehousesExistance = async (
|
||||
tenantId: number,
|
||||
entries: { warehouseId?: number }[]
|
||||
) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const entriesWarehousesIds = chain(entries)
|
||||
.filter((e) => !!e.warehouseId)
|
||||
.map((e) => e.warehouseId)
|
||||
.uniq()
|
||||
.value();
|
||||
|
||||
const warehouses = await Warehouse.query().whereIn(
|
||||
'id',
|
||||
entriesWarehousesIds
|
||||
);
|
||||
const warehousesIds = warehouses.map((e) => e.id);
|
||||
const notFoundWarehousesIds = difference(
|
||||
entriesWarehousesIds,
|
||||
warehousesIds
|
||||
);
|
||||
if (notFoundWarehousesIds.length > 0) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_ID_NOT_FOUND);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { omit } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import { WarehousesSettings } from '../WarehousesSettings';
|
||||
|
||||
@Service()
|
||||
export class WarehouseTransactionDTOTransform {
|
||||
@Inject()
|
||||
private warehousesSettings: WarehousesSettings;
|
||||
|
||||
/**
|
||||
* Excludes DTO warehouse id when mutli-warehouses feature is inactive.
|
||||
* @param {number} tenantId
|
||||
* @returns {Promise<Omit<T, 'warehouseId'> | T>}
|
||||
*/
|
||||
private excludeDTOWarehouseIdWhenInactive = <
|
||||
T extends { warehouseId?: number }
|
||||
>(
|
||||
tenantId: number,
|
||||
DTO: T
|
||||
): Omit<T, 'warehouseId'> | T => {
|
||||
const isActive = this.warehousesSettings.isMultiWarehousesActive(tenantId);
|
||||
|
||||
return !isActive ? omit(DTO, ['warehouseId']) : DTO;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {T} DTO -
|
||||
* @returns {Omit<T, 'warehouseId'> | T}
|
||||
*/
|
||||
public transformDTO =
|
||||
<T extends { warehouseId?: number }>(tenantId: number) =>
|
||||
(DTO: T): Omit<T, 'warehouseId'> | T => {
|
||||
return this.excludeDTOWarehouseIdWhenInactive<T>(tenantId, DTO);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { ValidateWarehouseExistance } from './ValidateWarehouseExistance';
|
||||
import { WarehousesSettings } from '../WarehousesSettings';
|
||||
|
||||
interface IWarehouseTransactionDTO {
|
||||
warehouseId?: number|null;
|
||||
entries?: { warehouseId?: number|null }[];
|
||||
}
|
||||
|
||||
@Service()
|
||||
export class WarehousesDTOValidators {
|
||||
@Inject()
|
||||
private validateWarehouseExistanceService: ValidateWarehouseExistance;
|
||||
|
||||
@Inject()
|
||||
private warehousesSettings: WarehousesSettings;
|
||||
|
||||
/**
|
||||
* Validates the warehouse existance of sale invoice transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleInvoiceCreateDTO | ISaleInvoiceEditDTO} saleInvoiceDTO
|
||||
*/
|
||||
public validateDTOWarehouseExistance = async (
|
||||
tenantId: number,
|
||||
DTO: IWarehouseTransactionDTO
|
||||
) => {
|
||||
// Validates the sale invoice warehouse id existance.
|
||||
this.validateWarehouseExistanceService.validateWarehouseIdExistance(
|
||||
DTO,
|
||||
DTO.entries
|
||||
);
|
||||
// Validate the sale invoice warehouse existance on the storage.
|
||||
if (DTO.warehouseId) {
|
||||
this.validateWarehouseExistanceService.validateWarehouseExistance(
|
||||
tenantId,
|
||||
DTO.warehouseId
|
||||
);
|
||||
}
|
||||
// Validate the sale invoice entries warehouses existance on the storage.
|
||||
if (!isEmpty(DTO.entries)) {
|
||||
await this.validateWarehouseExistanceService.validateItemEntriesWarehousesExistance(
|
||||
tenantId,
|
||||
DTO.entries
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the warehouse existance of
|
||||
* @param {number} tenantId
|
||||
* @param {IWarehouseTransactionDTO} saleInvoiceDTO
|
||||
* @returns
|
||||
*/
|
||||
public validateDTOWarehouseWhenActive = async (
|
||||
tenantId: number,
|
||||
DTO: IWarehouseTransactionDTO
|
||||
): Promise<void> => {
|
||||
const isActive = this.warehousesSettings.isMultiWarehousesActive(tenantId);
|
||||
|
||||
// Can't continue if the multi-warehouses feature is inactive.
|
||||
if (!isActive) return;
|
||||
|
||||
return this.validateDTOWarehouseExistance(tenantId, DTO);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import {
|
||||
IInventoryTransaction,
|
||||
IItemWarehouseQuantityChange,
|
||||
} from '@/interfaces';
|
||||
import { set, get, chain, toPairs } from 'lodash';
|
||||
|
||||
export class WarehousesItemsQuantity {
|
||||
balanceMap: { [warehouseId: number]: { [itemId: number]: number } } = {};
|
||||
/**
|
||||
*
|
||||
* @param {number} warehouseId
|
||||
* @param {number} itemId
|
||||
* @returns {number}
|
||||
*/
|
||||
public get = (warehouseId: number, itemId: number): number => {
|
||||
return get(this.balanceMap, `${warehouseId}.${itemId}`, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} warehouseId
|
||||
* @param {number} itemId
|
||||
* @param {number} amount
|
||||
* @returns {WarehousesItemsQuantity}
|
||||
*/
|
||||
public set = (warehouseId: number, itemId: number, amount: number) => {
|
||||
if (!get(this.balanceMap, warehouseId)) {
|
||||
set(this.balanceMap, warehouseId, {});
|
||||
}
|
||||
set(this.balanceMap, `${warehouseId}.${itemId}`, amount);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} warehouseId
|
||||
* @param {number} itemId
|
||||
* @param {number} amount
|
||||
* @returns {WarehousesItemsQuantity}
|
||||
*/
|
||||
public increment = (warehouseId: number, itemId: number, amount: number) => {
|
||||
const oldAmount = this.get(warehouseId, itemId);
|
||||
|
||||
return this.set(warehouseId, itemId, oldAmount + amount);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} warehouseId
|
||||
* @param {number} itemId
|
||||
* @param {number} amount
|
||||
* @returns {WarehousesItemsQuantity}
|
||||
*/
|
||||
public decrement = (warehouseId: number, itemId: number, amount: number) => {
|
||||
const oldAmount = this.get(warehouseId, itemId);
|
||||
|
||||
return this.set(warehouseId, itemId, oldAmount - amount);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {WarehousesItemsQuantity}
|
||||
*/
|
||||
public reverse = () => {
|
||||
const collection = this.toArray();
|
||||
|
||||
collection.forEach((change) => {
|
||||
this.set(change.warehouseId, change.itemId, change.amount * -1);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {IItemWarehouseQuantityChange[]}
|
||||
*/
|
||||
public toArray = (): IItemWarehouseQuantityChange[] => {
|
||||
return chain(this.balanceMap)
|
||||
.toPairs()
|
||||
.map(([warehouseId, item]) => {
|
||||
const pairs = toPairs(item);
|
||||
|
||||
return pairs.map(([itemId, amount]) => ({
|
||||
itemId: parseInt(itemId),
|
||||
warehouseId: parseInt(warehouseId),
|
||||
amount,
|
||||
}));
|
||||
})
|
||||
.flatten()
|
||||
.value();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IInventoryTransaction[]} inventoryTransactions
|
||||
* @returns {WarehousesItemsQuantity}
|
||||
*/
|
||||
static fromInventoryTransaction = (
|
||||
inventoryTransactions: IInventoryTransaction[]
|
||||
): WarehousesItemsQuantity => {
|
||||
const warehouseTransactions = inventoryTransactions.filter(
|
||||
(transaction) => transaction.warehouseId
|
||||
);
|
||||
const warehouseItemsQuantity = new WarehousesItemsQuantity();
|
||||
|
||||
warehouseTransactions.forEach((transaction: IInventoryTransaction) => {
|
||||
const change =
|
||||
transaction.direction === 'IN'
|
||||
? warehouseItemsQuantity.increment
|
||||
: warehouseItemsQuantity.decrement;
|
||||
|
||||
change(transaction.warehouseId, transaction.itemId, transaction.quantity);
|
||||
});
|
||||
return warehouseItemsQuantity;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import events from '@/subscribers/events';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { WarehousesItemsQuantitySync } from './WarehousesItemsQuantitySync';
|
||||
import {
|
||||
IInventoryTransactionsCreatedPayload,
|
||||
IInventoryTransactionsDeletedPayload,
|
||||
} from '@/interfaces';
|
||||
import { WarehousesSettings } from '../WarehousesSettings';
|
||||
|
||||
@Service()
|
||||
export class WarehousesItemsQuantitySyncSubscriber {
|
||||
@Inject()
|
||||
private warehousesItemsQuantitySync: WarehousesItemsQuantitySync;
|
||||
|
||||
@Inject()
|
||||
private warehousesSettings: WarehousesSettings;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.inventory.onInventoryTransactionsCreated,
|
||||
this.syncWarehousesItemsQuantityOnInventoryTransCreated
|
||||
);
|
||||
bus.subscribe(
|
||||
events.inventory.onInventoryTransactionsDeleted,
|
||||
this.syncWarehousesItemsQuantityOnInventoryTransDeleted
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs warehouses items quantity once inventory transactions created.
|
||||
* @param {IInventoryTransactionsCreatedPayload}
|
||||
*/
|
||||
private syncWarehousesItemsQuantityOnInventoryTransCreated = async ({
|
||||
tenantId,
|
||||
inventoryTransactions,
|
||||
trx,
|
||||
}: IInventoryTransactionsCreatedPayload) => {
|
||||
const isActive = this.warehousesSettings.isMultiWarehousesActive(tenantId);
|
||||
|
||||
// Can't continue if the warehouses features is not active.
|
||||
if (!isActive) return;
|
||||
|
||||
await this.warehousesItemsQuantitySync.mutateWarehousesItemsQuantityFromTransactions(
|
||||
tenantId,
|
||||
inventoryTransactions,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Syncs warehouses items quantity once inventory transactions deleted.
|
||||
* @param {IInventoryTransactionsDeletedPayload}
|
||||
*/
|
||||
private syncWarehousesItemsQuantityOnInventoryTransDeleted = async ({
|
||||
tenantId,
|
||||
oldInventoryTransactions,
|
||||
trx,
|
||||
}: IInventoryTransactionsDeletedPayload) => {
|
||||
const isActive = this.warehousesSettings.isMultiWarehousesActive(tenantId);
|
||||
|
||||
// Can't continue if the warehouses feature is not active yet.
|
||||
if (!isActive) return;
|
||||
|
||||
await this.warehousesItemsQuantitySync.reverseWarehousesItemsQuantityFromTransactions(
|
||||
tenantId,
|
||||
oldInventoryTransactions,
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { omit } from 'lodash';
|
||||
import {
|
||||
IInventoryTransaction,
|
||||
IItemWarehouseQuantityChange,
|
||||
} from '@/interfaces';
|
||||
import { WarehousesItemsQuantity } from './WarehousesItemsQuantity';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class WarehousesItemsQuantitySync {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieves the reversed warehouses items quantity changes.
|
||||
* @param {IInventoryTransaction[]} inventoryTransactions
|
||||
* @returns {IItemWarehouseQuantityChange[]}
|
||||
*/
|
||||
public getReverseWarehousesItemsQuantityChanges = (
|
||||
inventoryTransactions: IInventoryTransaction[]
|
||||
): IItemWarehouseQuantityChange[] => {
|
||||
const warehouseItemsQuantity =
|
||||
WarehousesItemsQuantity.fromInventoryTransaction(inventoryTransactions);
|
||||
|
||||
return warehouseItemsQuantity.reverse().toArray();
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the warehouses items changes from the given inventory tranasctions.
|
||||
* @param {IInventoryTransaction[]} inventoryTransactions
|
||||
* @returns {IItemWarehouseQuantityChange[]}
|
||||
*/
|
||||
public getWarehousesItemsQuantityChange = (
|
||||
inventoryTransactions: IInventoryTransaction[]
|
||||
): IItemWarehouseQuantityChange[] => {
|
||||
const warehouseItemsQuantity =
|
||||
WarehousesItemsQuantity.fromInventoryTransaction(inventoryTransactions);
|
||||
|
||||
return warehouseItemsQuantity.toArray();
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutates warehouses items quantity on hand on the storage.
|
||||
* @param {number} tenantId
|
||||
* @param {IItemWarehouseQuantityChange[]} warehousesItemsQuantity
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public mutateWarehousesItemsQuantity = async (
|
||||
tenantId: number,
|
||||
warehousesItemsQuantity: IItemWarehouseQuantityChange[],
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const mutationsOpers = warehousesItemsQuantity.map(
|
||||
(change: IItemWarehouseQuantityChange) =>
|
||||
this.mutateWarehouseItemQuantity(tenantId, change, trx)
|
||||
);
|
||||
await Promise.all(mutationsOpers);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutates the warehouse item quantity.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseItemQuantity
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public mutateWarehouseItemQuantity = async (
|
||||
tenantId: number,
|
||||
warehouseItemQuantity: IItemWarehouseQuantityChange,
|
||||
trx: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const { ItemWarehouseQuantity } = this.tenancy.models(tenantId);
|
||||
|
||||
const itemWarehouseQuantity = await ItemWarehouseQuantity.query(trx)
|
||||
.where('itemId', warehouseItemQuantity.itemId)
|
||||
.where('warehouseId', warehouseItemQuantity.warehouseId)
|
||||
.first();
|
||||
|
||||
if (itemWarehouseQuantity) {
|
||||
await ItemWarehouseQuantity.changeAmount(
|
||||
{
|
||||
itemId: warehouseItemQuantity.itemId,
|
||||
warehouseId: warehouseItemQuantity.warehouseId,
|
||||
},
|
||||
'quantityOnHand',
|
||||
warehouseItemQuantity.amount,
|
||||
trx
|
||||
);
|
||||
} else {
|
||||
await ItemWarehouseQuantity.query(trx).insert({
|
||||
...omit(warehouseItemQuantity, ['amount']),
|
||||
quantityOnHand: warehouseItemQuantity.amount,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutates warehouses items quantity from inventory transactions.
|
||||
* @param {number} tenantId -
|
||||
* @param {IInventoryTransaction[]} inventoryTransactions -
|
||||
* @param {Knex.Transaction}
|
||||
*/
|
||||
public mutateWarehousesItemsQuantityFromTransactions = async (
|
||||
tenantId: number,
|
||||
inventoryTransactions: IInventoryTransaction[],
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const changes = this.getWarehousesItemsQuantityChange(
|
||||
inventoryTransactions
|
||||
);
|
||||
await this.mutateWarehousesItemsQuantity(tenantId, changes, trx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverses warehouses items quantity from inventory transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {IInventoryTransaction[]} inventoryTransactions
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public reverseWarehousesItemsQuantityFromTransactions = async (
|
||||
tenantId: number,
|
||||
inventoryTransactions: IInventoryTransaction[],
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const changes = this.getReverseWarehousesItemsQuantityChanges(
|
||||
inventoryTransactions
|
||||
);
|
||||
await this.mutateWarehousesItemsQuantity(tenantId, changes, trx);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export const ERRORS = {
|
||||
WAREHOUSE_ID_NOT_FOUND: 'WAREHOUSE_ID_NOT_FOUND',
|
||||
ITEM_ENTRY_WAREHOUSE_ID_NOT_FOUND: 'ITEM_ENTRY_WAREHOUSE_ID_NOT_FOUND',
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { GetItemWarehouseTransformer } from './GettItemWarehouseTransformer';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GetItemWarehouses {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieves the item warehouses.
|
||||
* @param {number} tenantId
|
||||
* @param {number} itemId
|
||||
* @returns
|
||||
*/
|
||||
public getItemWarehouses = async (tenantId: number, itemId: number) => {
|
||||
const { ItemWarehouseQuantity, Item } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves specific item or throw not found service error.
|
||||
const item = await Item.query().findById(itemId).throwIfNotFound();
|
||||
|
||||
const itemWarehouses = await ItemWarehouseQuantity.query()
|
||||
.where('itemId', itemId)
|
||||
.withGraphFetched('warehouse');
|
||||
|
||||
// Retrieves the transformed items warehouses.
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
itemWarehouses,
|
||||
new GetItemWarehouseTransformer()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { formatNumber } from 'utils';
|
||||
|
||||
export class GetItemWarehouseTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'warehouseId',
|
||||
'warehouseName',
|
||||
'warehouseCode',
|
||||
'quantityOnHandFormatted',
|
||||
];
|
||||
};
|
||||
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['warehouse'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Formatted sell price.
|
||||
* @param item
|
||||
* @returns {string}
|
||||
*/
|
||||
public quantityOnHandFormatted(item): string {
|
||||
return formatNumber(item.quantityOnHand, { money: false });
|
||||
}
|
||||
|
||||
public warehouseCode(item): string {
|
||||
return item.warehouse.code;
|
||||
}
|
||||
|
||||
public warehouseName(item): string {
|
||||
return item.warehouse.name;
|
||||
}
|
||||
|
||||
public warehouseId(item): number {
|
||||
return item.warehouse.id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehousesActivatedPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { BillActivateWarehouses } from '../../Activate/BillWarehousesActivate';
|
||||
|
||||
@Service()
|
||||
export class BillsActivateWarehousesSubscriber {
|
||||
@Inject()
|
||||
private billsActivateWarehouses: BillActivateWarehouses;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.updateBillsWithWarehouseOnActivated
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse once
|
||||
* multi-warehouses feature is activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private updateBillsWithWarehouseOnActivated = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.billsActivateWarehouses.updateBillsWithWarehouse(
|
||||
tenantId,
|
||||
primaryWarehouse
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehousesActivatedPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { CreditNotesActivateWarehouses } from '../../Activate/CreditNoteWarehousesActivate';
|
||||
|
||||
@Service()
|
||||
export class CreditsActivateWarehousesSubscriber {
|
||||
@Inject()
|
||||
private creditsActivateWarehouses: CreditNotesActivateWarehouses;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.updateInvoicesWithWarehouseOnActivated
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse once
|
||||
* multi-warehouses feature is activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private updateInvoicesWithWarehouseOnActivated = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.creditsActivateWarehouses.updateCreditsWithWarehouse(
|
||||
tenantId,
|
||||
primaryWarehouse
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehousesActivatedPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { EstimatesActivateWarehouses } from '../../Activate/EstimateWarehousesActivate';
|
||||
|
||||
@Service()
|
||||
export class EstimatesActivateWarehousesSubscriber {
|
||||
@Inject()
|
||||
private estimatesActivateWarehouses: EstimatesActivateWarehouses;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.updateEstimatessWithWarehouseOnActivated
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse once
|
||||
* multi-warehouses feature is activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private updateEstimatessWithWarehouseOnActivated = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.estimatesActivateWarehouses.updateEstimatesWithWarehouse(
|
||||
tenantId,
|
||||
primaryWarehouse
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehousesActivatedPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { InventoryActivateWarehouses } from '../../Activate/InventoryTransactionsWarehousesActivate';
|
||||
|
||||
@Service()
|
||||
export class InventoryActivateWarehousesSubscriber {
|
||||
@Inject()
|
||||
private inventoryActivateWarehouses: InventoryActivateWarehouses;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.updateInventoryTransactionsWithWarehouseOnActivated
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse once
|
||||
* multi-warehouses feature is activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private updateInventoryTransactionsWithWarehouseOnActivated = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.inventoryActivateWarehouses.updateInventoryTransactionsWithWarehouse(
|
||||
tenantId,
|
||||
primaryWarehouse
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehousesActivatedPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { InvoicesActivateWarehouses } from '../../Activate/InvoiceWarehousesActivate';
|
||||
|
||||
@Service()
|
||||
export class InvoicesActivateWarehousesSubscriber {
|
||||
@Inject()
|
||||
private invoicesActivateWarehouses: InvoicesActivateWarehouses;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.updateInvoicesWithWarehouseOnActivated
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse once
|
||||
* multi-warehouses feature is activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private updateInvoicesWithWarehouseOnActivated = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.invoicesActivateWarehouses.updateInvoicesWithWarehouse(
|
||||
tenantId,
|
||||
primaryWarehouse
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehousesActivatedPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { ReceiptActivateWarehouses } from '../../Activate/ReceiptWarehousesActivate';
|
||||
|
||||
@Service()
|
||||
export class ReceiptsActivateWarehousesSubscriber {
|
||||
@Inject()
|
||||
private receiptsActivateWarehouses: ReceiptActivateWarehouses;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.updateInventoryTransactionsWithWarehouseOnActivated
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all receipts transactions with the primary warehouse once
|
||||
* multi-warehouses feature is activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private updateInventoryTransactionsWithWarehouseOnActivated = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.receiptsActivateWarehouses.updateReceiptsWithWarehouse(
|
||||
tenantId,
|
||||
primaryWarehouse
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IWarehousesActivatedPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { VendorCreditActivateWarehouses } from '../../Activate/VendorCreditWarehousesActivate';
|
||||
|
||||
@Service()
|
||||
export class VendorCreditsActivateWarehousesSubscriber {
|
||||
@Inject()
|
||||
private creditsActivateWarehouses: VendorCreditActivateWarehouses;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.warehouse.onActivated,
|
||||
this.updateCreditsWithWarehouseOnActivated
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with the primary warehouse once
|
||||
* multi-warehouses feature is activated.
|
||||
* @param {IWarehousesActivatedPayload}
|
||||
*/
|
||||
private updateCreditsWithWarehouseOnActivated = async ({
|
||||
tenantId,
|
||||
primaryWarehouse,
|
||||
}: IWarehousesActivatedPayload) => {
|
||||
await this.creditsActivateWarehouses.updateCreditsWithWarehouse(
|
||||
tenantId,
|
||||
primaryWarehouse
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* eslint-disable import/extensions */
|
||||
export * from './BillWarehousesActivateSubscriber';
|
||||
export * from './CreditNoteWarehousesActivateSubscriber';
|
||||
export * from './EstimateWarehousesActivateSubscriber';
|
||||
export * from './InventoryTransactionsWarehousesActivateSubscriber';
|
||||
export * from './VendorCreditWarehousesActivateSubscriber';
|
||||
export * from './ReceiptWarehousesActivateSubscriber';
|
||||
export * from './InvoiceWarehousesActivateSubscriber';
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import { DeleteItemWarehousesQuantity } from '../DeleteItemWarehousesQuantity';
|
||||
import { IItemEventDeletingPayload } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class DeleteItemWarehousesQuantitySubscriber {
|
||||
@Inject()
|
||||
private deleteItemWarehousesQuantity: DeleteItemWarehousesQuantity;
|
||||
|
||||
/**
|
||||
* Attaches events.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.item.onDeleting,
|
||||
this.deleteItemWarehouseQuantitiesOnItemDelete
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given item warehouses quantities once the item deleting.
|
||||
* @param {IItemEventDeletingPayload} payload -
|
||||
*/
|
||||
private deleteItemWarehouseQuantitiesOnItemDelete = async ({
|
||||
tenantId,
|
||||
oldItem,
|
||||
trx,
|
||||
}: IItemEventDeletingPayload) => {
|
||||
await this.deleteItemWarehousesQuantity.deleteItemWarehousesQuantity(
|
||||
tenantId,
|
||||
oldItem.id,
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { IInventoryAdjustmentCreatingPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WarehousesDTOValidators } from '../../../Integrations/WarehousesDTOValidators';
|
||||
|
||||
@Service()
|
||||
export class InventoryAdjustmentWarehouseValidatorSubscriber {
|
||||
@Inject()
|
||||
private warehouseDTOValidator: WarehousesDTOValidators;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.inventoryAdjustment.onQuickCreating,
|
||||
this.validateAdjustmentWarehouseExistanceOnCreating
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once creating.
|
||||
* @param {IBillCreatingPayload}
|
||||
*/
|
||||
private validateAdjustmentWarehouseExistanceOnCreating = async ({
|
||||
quickAdjustmentDTO,
|
||||
tenantId,
|
||||
}: IInventoryAdjustmentCreatingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
quickAdjustmentDTO
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { IBillCreatingPayload, IBillEditingPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WarehousesDTOValidators } from '../../../Integrations/WarehousesDTOValidators';
|
||||
|
||||
@Service()
|
||||
export class BillWarehousesValidateSubscriber {
|
||||
@Inject()
|
||||
private warehouseDTOValidator: WarehousesDTOValidators;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.bill.onCreating,
|
||||
this.validateBillWarehouseExistanceOnCreating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onEditing,
|
||||
this.validateSaleEstimateWarehouseExistanceOnEditing
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once creating.
|
||||
* @param {IBillCreatingPayload}
|
||||
*/
|
||||
private validateBillWarehouseExistanceOnCreating = async ({
|
||||
billDTO,
|
||||
tenantId,
|
||||
}: IBillCreatingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
billDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once editing.
|
||||
* @param {IBillEditingPayload}
|
||||
*/
|
||||
private validateSaleEstimateWarehouseExistanceOnEditing = async ({
|
||||
tenantId,
|
||||
billDTO,
|
||||
}: IBillEditingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
billDTO
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
IVendorCreditCreatingPayload,
|
||||
IVendorCreditEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WarehousesDTOValidators } from '../../../Integrations/WarehousesDTOValidators';
|
||||
|
||||
@Service()
|
||||
export class VendorCreditWarehousesValidateSubscriber {
|
||||
@Inject()
|
||||
warehouseDTOValidator: WarehousesDTOValidators;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onCreating,
|
||||
this.validateVendorCreditWarehouseExistanceOnCreating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onEditing,
|
||||
this.validateSaleEstimateWarehouseExistanceOnEditing
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once creating.
|
||||
* @param {IVendorCreditCreatingPayload}
|
||||
*/
|
||||
private validateVendorCreditWarehouseExistanceOnCreating = async ({
|
||||
vendorCreditCreateDTO,
|
||||
tenantId,
|
||||
}: IVendorCreditCreatingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
vendorCreditCreateDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once editing.
|
||||
* @param {IVendorCreditEditingPayload}
|
||||
*/
|
||||
private validateSaleEstimateWarehouseExistanceOnEditing = async ({
|
||||
tenantId,
|
||||
vendorCreditDTO,
|
||||
}: IVendorCreditEditingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
vendorCreditDTO
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
ICreditNoteCreatingPayload,
|
||||
ICreditNoteEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WarehousesDTOValidators } from '../../../Integrations/WarehousesDTOValidators';
|
||||
|
||||
@Service()
|
||||
export class CreditNoteWarehousesValidateSubscriber {
|
||||
@Inject()
|
||||
warehouseDTOValidator: WarehousesDTOValidators;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.creditNote.onCreating,
|
||||
this.validateCreditNoteWarehouseExistanceOnCreating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.creditNote.onEditing,
|
||||
this.validateCreditNoteWarehouseExistanceOnEditing
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once creating.
|
||||
* @param {ICreditNoteCreatingPayload}
|
||||
*/
|
||||
private validateCreditNoteWarehouseExistanceOnCreating = async ({
|
||||
creditNoteDTO,
|
||||
tenantId,
|
||||
}: ICreditNoteCreatingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
creditNoteDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once editing.
|
||||
* @param {ICreditNoteEditingPayload}
|
||||
*/
|
||||
private validateCreditNoteWarehouseExistanceOnEditing = async ({
|
||||
tenantId,
|
||||
creditNoteEditDTO,
|
||||
}: ICreditNoteEditingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
creditNoteEditDTO
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
ISaleEstimateCreatingPayload,
|
||||
ISaleEstimateEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WarehousesDTOValidators } from '../../../Integrations/WarehousesDTOValidators';
|
||||
|
||||
@Service()
|
||||
export class SaleEstimateWarehousesValidateSubscriber {
|
||||
@Inject()
|
||||
warehouseDTOValidator: WarehousesDTOValidators;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.saleEstimate.onCreating,
|
||||
this.validateSaleEstimateWarehouseExistanceOnCreating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleEstimate.onEditing,
|
||||
this.validateSaleEstimateWarehouseExistanceOnEditing
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once creating.
|
||||
* @param {ISaleEstimateCreatingPayload}
|
||||
*/
|
||||
private validateSaleEstimateWarehouseExistanceOnCreating = async ({
|
||||
estimateDTO,
|
||||
tenantId,
|
||||
}: ISaleEstimateCreatingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
estimateDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once editing.
|
||||
* @param {ISaleEstimateEditingPayload}
|
||||
*/
|
||||
private validateSaleEstimateWarehouseExistanceOnEditing = async ({
|
||||
tenantId,
|
||||
estimateDTO,
|
||||
}: ISaleEstimateEditingPayload) => {
|
||||
await this.warehouseDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
estimateDTO
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
ISaleInvoiceCreatingPaylaod,
|
||||
ISaleInvoiceEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WarehousesDTOValidators } from '../../../Integrations/WarehousesDTOValidators';
|
||||
|
||||
@Service()
|
||||
export class SaleInvoicesWarehousesValidateSubscriber {
|
||||
@Inject()
|
||||
warehousesDTOValidator: WarehousesDTOValidators;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onCreating,
|
||||
this.validateSaleInvoiceWarehouseExistanceOnCreating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onEditing,
|
||||
this.validateSaleInvoiceWarehouseExistanceOnEditing
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once creating.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
*/
|
||||
private validateSaleInvoiceWarehouseExistanceOnCreating = async ({
|
||||
saleInvoiceDTO,
|
||||
tenantId,
|
||||
}: ISaleInvoiceCreatingPaylaod) => {
|
||||
await this.warehousesDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
saleInvoiceDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once editing.
|
||||
* @param {ISaleInvoiceEditingPayload}
|
||||
*/
|
||||
private validateSaleInvoiceWarehouseExistanceOnEditing = async ({
|
||||
tenantId,
|
||||
saleInvoiceDTO,
|
||||
}: ISaleInvoiceEditingPayload) => {
|
||||
await this.warehousesDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
saleInvoiceDTO
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
ISaleReceiptCreatingPayload,
|
||||
ISaleReceiptEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WarehousesDTOValidators } from '../../../Integrations/WarehousesDTOValidators';
|
||||
|
||||
@Service()
|
||||
export class SaleReceiptWarehousesValidateSubscriber {
|
||||
@Inject()
|
||||
private warehousesDTOValidator: WarehousesDTOValidators;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.saleReceipt.onCreating,
|
||||
this.validateSaleReceiptWarehouseExistanceOnCreating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleReceipt.onEditing,
|
||||
this.validateSaleReceiptWarehouseExistanceOnEditing
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once creating.
|
||||
* @param {ISaleReceiptCreatingPayload}
|
||||
*/
|
||||
private validateSaleReceiptWarehouseExistanceOnCreating = async ({
|
||||
saleReceiptDTO,
|
||||
tenantId,
|
||||
}: ISaleReceiptCreatingPayload) => {
|
||||
await this.warehousesDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
saleReceiptDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate warehouse existance of sale invoice once editing.
|
||||
* @param {ISaleReceiptEditingPayload}
|
||||
*/
|
||||
private validateSaleReceiptWarehouseExistanceOnEditing = async ({
|
||||
tenantId,
|
||||
saleReceiptDTO,
|
||||
}: ISaleReceiptEditingPayload) => {
|
||||
await this.warehousesDTOValidator.validateDTOWarehouseWhenActive(
|
||||
tenantId,
|
||||
saleReceiptDTO
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export * from './Purchases/BillWarehousesSubscriber';
|
||||
export * from './Purchases/VendorCreditWarehousesSubscriber';
|
||||
|
||||
export * from './Sales/SaleEstimateWarehousesSubscriber';
|
||||
export * from './Sales/CreditNoteWarehousesSubscriber';
|
||||
export * from './Sales/SaleInvoicesWarehousesSubscriber';
|
||||
export * from './Sales/SaleReceiptWarehousesSubscriber';
|
||||
|
||||
export * from './InventoryAdjustment/InventoryAdjustmentWarehouseValidatorSubscriber';
|
||||
@@ -0,0 +1,21 @@
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { Service, Inject } from 'typedi';
|
||||
|
||||
@Service()
|
||||
export class UpdateInventoryTransactionsWithWarehouse {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Updates all inventory transactions with primary warehouse.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} warehouseId -
|
||||
*/
|
||||
public run = async (tenantId: number, primaryWarehouseId: number) => {
|
||||
const { InventoryTransaction } = this.tenancy.models(tenantId);
|
||||
|
||||
await InventoryTransaction.query().update({
|
||||
warehouseId: primaryWarehouseId,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import { CRUDWarehouse } from './CRUDWarehouse';
|
||||
import {
|
||||
IWarehouseMarkAsPrimaryPayload,
|
||||
IWarehouseMarkedAsPrimaryPayload,
|
||||
} from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class WarehouseMarkPrimary extends CRUDWarehouse {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Marks the given warehouse as primary.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
* @returns {Promise<IWarehouse>}
|
||||
*/
|
||||
public markAsPrimary = async (tenantId: number, warehouseId: number) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const oldWarehouse = await this.getWarehouseOrThrowNotFound(
|
||||
tenantId,
|
||||
warehouseId
|
||||
);
|
||||
// Updates the branches under unit-of-work enivrement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseMarkPrimary` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouse.onMarkPrimary, {
|
||||
tenantId,
|
||||
oldWarehouse,
|
||||
trx,
|
||||
} as IWarehouseMarkAsPrimaryPayload);
|
||||
// marks all warehouses as not primary.
|
||||
await Warehouse.query(trx).update({ primary: false });
|
||||
|
||||
// Marks the particular branch as primary.
|
||||
const markedWarehouse = await Warehouse.query(trx).patchAndFetchById(
|
||||
warehouseId,
|
||||
{ primary: true }
|
||||
);
|
||||
// Triggers `onWarehouseMarkedPrimary` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouse.onMarkedPrimary, {
|
||||
tenantId,
|
||||
oldWarehouse,
|
||||
markedWarehouse,
|
||||
trx,
|
||||
} as IWarehouseMarkedAsPrimaryPayload);
|
||||
|
||||
return markedWarehouse;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ServiceError, ServiceErrors } from '@/exceptions';
|
||||
import { ERRORS } from './contants';
|
||||
|
||||
@Service()
|
||||
export class WarehouseValidator {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
*/
|
||||
public validateWarehouseNotOnlyWarehouse = async (
|
||||
tenantId: number,
|
||||
warehouseId: number
|
||||
) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const warehouses = await Warehouse.query().whereNot('id', warehouseId);
|
||||
|
||||
if (warehouses.length === 0) {
|
||||
throw new ServiceError(ERRORS.COULD_NOT_DELETE_ONLY_WAERHOUSE);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param tenantId
|
||||
* @param code
|
||||
* @param exceptWarehouseId
|
||||
*/
|
||||
public validateWarehouseCodeUnique = async (
|
||||
tenantId: number,
|
||||
code: string,
|
||||
exceptWarehouseId?: number
|
||||
) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const warehouse = await Warehouse.query()
|
||||
.onBuild((query) => {
|
||||
query.select(['id']);
|
||||
query.where('code', code);
|
||||
|
||||
if (exceptWarehouseId) {
|
||||
query.whereNot('id', exceptWarehouseId);
|
||||
}
|
||||
})
|
||||
.first();
|
||||
|
||||
if (warehouse) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_CODE_NOT_UNIQUE);
|
||||
}
|
||||
};
|
||||
}
|
||||
137
packages/server/src/services/Warehouses/WarehousesApplication.ts
Normal file
137
packages/server/src/services/Warehouses/WarehousesApplication.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { ICreateWarehouseDTO, IEditWarehouseDTO, IWarehouse } from '@/interfaces';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ActivateWarehouses } from './ActivateWarehouses';
|
||||
import { CreateWarehouse } from './CreateWarehouse';
|
||||
import { DeleteWarehouse } from './DeleteWarehouse';
|
||||
import { EditWarehouse } from './EditWarehouse';
|
||||
import { GetWarehouse } from './GetWarehouse';
|
||||
import { GetWarehouses } from './GetWarehouses';
|
||||
import { GetItemWarehouses } from './Items/GetItemWarehouses';
|
||||
import { WarehouseMarkPrimary } from './WarehouseMarkPrimary';
|
||||
|
||||
@Service()
|
||||
export class WarehousesApplication {
|
||||
@Inject()
|
||||
private createWarehouseService: CreateWarehouse;
|
||||
|
||||
@Inject()
|
||||
private editWarehouseService: EditWarehouse;
|
||||
|
||||
@Inject()
|
||||
private deleteWarehouseService: DeleteWarehouse;
|
||||
|
||||
@Inject()
|
||||
private getWarehouseService: GetWarehouse;
|
||||
|
||||
@Inject()
|
||||
private getWarehousesService: GetWarehouses;
|
||||
|
||||
@Inject()
|
||||
private activateWarehousesService: ActivateWarehouses;
|
||||
|
||||
@Inject()
|
||||
private markWarehousePrimaryService: WarehouseMarkPrimary;
|
||||
|
||||
@Inject()
|
||||
private getItemWarehousesService: GetItemWarehouses;
|
||||
|
||||
/**
|
||||
* Creates a new warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {ICreateWarehouseDTO} createWarehouseDTO
|
||||
* @returns
|
||||
*/
|
||||
public createWarehouse = (
|
||||
tenantId: number,
|
||||
createWarehouseDTO: ICreateWarehouseDTO
|
||||
) => {
|
||||
return this.createWarehouseService.createWarehouse(
|
||||
tenantId,
|
||||
createWarehouseDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits the given warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
* @param {IEditWarehouseDTO} editWarehouseDTO
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public editWarehouse = (
|
||||
tenantId: number,
|
||||
warehouseId: number,
|
||||
editWarehouseDTO: IEditWarehouseDTO
|
||||
) => {
|
||||
return this.editWarehouseService.editWarehouse(
|
||||
tenantId,
|
||||
warehouseId,
|
||||
editWarehouseDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the given warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
*/
|
||||
public deleteWarehouse = (tenantId: number, warehouseId: number) => {
|
||||
return this.deleteWarehouseService.deleteWarehouse(tenantId, warehouseId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the specific warehouse.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
* @returns
|
||||
*/
|
||||
public getWarehouse = (tenantId: number, warehouseId: number) => {
|
||||
return this.getWarehouseService.getWarehouse(tenantId, warehouseId);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @returns
|
||||
*/
|
||||
public getWarehouses = (tenantId: number) => {
|
||||
return this.getWarehousesService.getWarehouses(tenantId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Activates the warehouses feature.
|
||||
* @param {number} tenantId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public activateWarehouses = (tenantId: number) => {
|
||||
return this.activateWarehousesService.activateWarehouses(tenantId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark the given warehouse as primary.
|
||||
* @param {number} tenantId -
|
||||
* @returns {Promise<IWarehouse>}
|
||||
*/
|
||||
public markWarehousePrimary = (
|
||||
tenantId: number,
|
||||
warehouseId: number
|
||||
): Promise<IWarehouse> => {
|
||||
return this.markWarehousePrimaryService.markAsPrimary(
|
||||
tenantId,
|
||||
warehouseId
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the specific item warehouses quantity.
|
||||
* @param {number} tenantId
|
||||
* @param {number} itemId
|
||||
* @returns
|
||||
*/
|
||||
public getItemWarehouses = (
|
||||
tenantId: number,
|
||||
itemId: number
|
||||
): Promise<any> => {
|
||||
return this.getItemWarehousesService.getItemWarehouses(tenantId, itemId);
|
||||
};
|
||||
}
|
||||
36
packages/server/src/services/Warehouses/WarehousesService.ts
Normal file
36
packages/server/src/services/Warehouses/WarehousesService.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Service } from 'typedi';
|
||||
|
||||
@Service()
|
||||
export class WarehousesService {
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
*/
|
||||
getWarehouse = (tenantId: number, warehouseId: number) => {};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
*/
|
||||
getWarehouses = (tenantId: number) => {};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
*/
|
||||
deleteWarehouse = (tenantId: number, warehouseId: number) => {};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseId
|
||||
* @param {IEditWarehouseDTO} warehouseDTO
|
||||
*/
|
||||
editWarehouse = (
|
||||
tenantId: number,
|
||||
warehouseId: number,
|
||||
warehouseDTO: IEditWarehouseDTO
|
||||
) => {};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Features } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class WarehousesSettings {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Marks multi-warehouses as activated.
|
||||
*/
|
||||
public markMutliwarehoussAsActivated = (tenantId: number) => {
|
||||
const settings = this.tenancy.settings(tenantId);
|
||||
|
||||
settings.set({ group: 'features', key: Features.WAREHOUSES, value: 1 });
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines multi-warehouses is active.
|
||||
* @param {number} tenantId
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isMultiWarehousesActive = (tenantId: number) => {
|
||||
const settings = this.tenancy.settings(tenantId);
|
||||
|
||||
return settings.get({ group: 'features', key: Features.WAREHOUSES });
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
@Service()
|
||||
export class CRUDWarehouseTransfer {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
|
||||
throwIfTransferNotFound = (warehouseTransfer) => {
|
||||
if (!warehouseTransfer) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
public getWarehouseTransferOrThrowNotFound = async (
|
||||
tenantId: number,
|
||||
branchId: number
|
||||
) => {
|
||||
const { WarehouseTransfer } = this.tenancy.models(tenantId);
|
||||
|
||||
const foundTransfer = await WarehouseTransfer.query().findById(branchId);
|
||||
|
||||
if (!foundTransfer) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_NOT_FOUND);
|
||||
}
|
||||
return foundTransfer;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Inject } from 'typedi';
|
||||
import {
|
||||
ICreateWarehouseTransferDTO,
|
||||
IEditWarehouseTransferDTO,
|
||||
IItem,
|
||||
} from '@/interfaces';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from './constants';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
import { CRUDWarehouseTransfer } from './CRUDWarehouseTransfer';
|
||||
|
||||
export class CommandWarehouseTransfer extends CRUDWarehouseTransfer {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
itemsEntries: ItemsEntriesService;
|
||||
|
||||
/**
|
||||
* Validate the from/to warehouses should not be the same.
|
||||
* @param {ICreateWarehouseTransferDTO|IEditWarehouseTransferDTO} warehouseTransferDTO
|
||||
*/
|
||||
protected validateWarehouseFromToNotSame = (
|
||||
warehouseTransferDTO:
|
||||
| ICreateWarehouseTransferDTO
|
||||
| IEditWarehouseTransferDTO
|
||||
) => {
|
||||
if (
|
||||
warehouseTransferDTO.fromWarehouseId ===
|
||||
warehouseTransferDTO.toWarehouseId
|
||||
) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSES_TRANSFER_SHOULD_NOT_BE_SAME);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates entries items should be inventory.
|
||||
* @param {IItem[]} items
|
||||
* @returns {void}
|
||||
*/
|
||||
protected validateItemsShouldBeInventory = (items: IItem[]): void => {
|
||||
const nonInventoryItems = items.filter((item) => item.type !== 'inventory');
|
||||
|
||||
if (nonInventoryItems.length > 0) {
|
||||
throw new ServiceError(
|
||||
ERRORS.WAREHOUSE_TRANSFER_ITEMS_SHOULD_BE_INVENTORY
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
protected getToWarehouseOrThrow = async (
|
||||
tenantId: number,
|
||||
fromWarehouseId: number
|
||||
) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const warehouse = await Warehouse.query().findById(fromWarehouseId);
|
||||
|
||||
if (!warehouse) {
|
||||
throw new ServiceError(ERRORS.TO_WAREHOUSE_NOT_FOUND);
|
||||
}
|
||||
return warehouse;
|
||||
};
|
||||
|
||||
protected getFromWarehouseOrThrow = async (
|
||||
tenantId: number,
|
||||
fromWarehouseId: number
|
||||
) => {
|
||||
const { Warehouse } = this.tenancy.models(tenantId);
|
||||
|
||||
const warehouse = await Warehouse.query().findById(fromWarehouseId);
|
||||
|
||||
if (!warehouse) {
|
||||
throw new ServiceError(ERRORS.FROM_WAREHOUSE_NOT_FOUND);
|
||||
}
|
||||
return warehouse;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
import { Knex } from 'knex';
|
||||
import { omit, get, isNumber } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
ICreateWarehouseTransferDTO,
|
||||
IWarehouseTransfer,
|
||||
IWarehouseTransferCreate,
|
||||
IWarehouseTransferCreated,
|
||||
IWarehouseTransferEntryDTO,
|
||||
IInventoryItemCostMeta,
|
||||
} from '@/interfaces';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
import { CommandWarehouseTransfer } from './CommandWarehouseTransfer';
|
||||
import { InventoryItemCostService } from '@/services/Inventory/InventoryCostsService';
|
||||
import { WarehouseTransferAutoIncrement } from './WarehouseTransferAutoIncrement';
|
||||
|
||||
@Service()
|
||||
export class CreateWarehouseTransfer extends CommandWarehouseTransfer {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private itemsEntries: ItemsEntriesService;
|
||||
|
||||
@Inject()
|
||||
private inventoryItemCost: InventoryItemCostService;
|
||||
|
||||
@Inject()
|
||||
private autoIncrementOrders: WarehouseTransferAutoIncrement;
|
||||
|
||||
/**
|
||||
* Transformes the givne new warehouse transfer DTO to model.
|
||||
* @param {ICreateWarehouseTransferDTO} warehouseTransferDTO
|
||||
* @returns {IWarehouseTransfer}
|
||||
*/
|
||||
private transformDTOToModel = async (
|
||||
tenantId: number,
|
||||
warehouseTransferDTO: ICreateWarehouseTransferDTO
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
const entries = await this.transformEntries(
|
||||
tenantId,
|
||||
warehouseTransferDTO,
|
||||
warehouseTransferDTO.entries
|
||||
);
|
||||
// Retrieves the auto-increment the warehouse transfer number.
|
||||
const autoNextNumber =
|
||||
this.autoIncrementOrders.getNextTransferNumber(tenantId);
|
||||
|
||||
// Warehouse transfer order transaction number.
|
||||
const transactionNumber =
|
||||
warehouseTransferDTO.transactionNumber || autoNextNumber;
|
||||
|
||||
return {
|
||||
...omit(warehouseTransferDTO, ['transferDelivered', 'transferInitiated']),
|
||||
transactionNumber,
|
||||
...(warehouseTransferDTO.transferDelivered
|
||||
? {
|
||||
transferDeliveredAt: new Date(),
|
||||
}
|
||||
: {}),
|
||||
...(warehouseTransferDTO.transferDelivered ||
|
||||
warehouseTransferDTO.transferInitiated
|
||||
? {
|
||||
transferInitiatedAt: new Date(),
|
||||
}
|
||||
: {}),
|
||||
entries,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc average cost to the entry that has no cost.
|
||||
* @param {Promise<Map<number, IInventoryItemCostMeta>} inventoryItemsCostMap -
|
||||
* @param {IWarehouseTransferEntryDTO} entry -
|
||||
*/
|
||||
private transformEntryAssocAverageCost = R.curry(
|
||||
(
|
||||
inventoryItemsCostMap: Map<number, IInventoryItemCostMeta>,
|
||||
entry: IWarehouseTransferEntryDTO
|
||||
): IWarehouseTransferEntryDTO => {
|
||||
const itemValuation = inventoryItemsCostMap.get(entry.itemId);
|
||||
const itemCost = get(itemValuation, 'average', 0);
|
||||
|
||||
return isNumber(entry.cost) ? entry : R.assoc('cost', itemCost, entry);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Transformes warehouse transfer entries.
|
||||
* @param {number} tenantId
|
||||
* @param {ICreateWarehouseTransferDTO} warehouseTransferDTO
|
||||
* @param {IWarehouseTransferEntryDTO[]} entries
|
||||
* @returns {Promise<IWarehouseTransferEntryDTO[]>}
|
||||
*/
|
||||
public transformEntries = async (
|
||||
tenantId: number,
|
||||
warehouseTransferDTO: ICreateWarehouseTransferDTO,
|
||||
entries: IWarehouseTransferEntryDTO[]
|
||||
): Promise<IWarehouseTransferEntryDTO[]> => {
|
||||
const inventoryItemsIds = warehouseTransferDTO.entries.map((e) => e.itemId);
|
||||
|
||||
// Retrieves the inventory items valuation map.
|
||||
const inventoryItemsCostMap =
|
||||
await this.inventoryItemCost.getItemsInventoryValuation(
|
||||
tenantId,
|
||||
inventoryItemsIds,
|
||||
warehouseTransferDTO.date
|
||||
);
|
||||
// Assoc average cost to the entry.
|
||||
const assocAverageCost = this.transformEntryAssocAverageCost(
|
||||
inventoryItemsCostMap
|
||||
);
|
||||
return R.map(assocAverageCost)(entries);
|
||||
};
|
||||
|
||||
/**
|
||||
* Authorize warehouse transfer before creating.
|
||||
* @param {number} tenantId
|
||||
* @param {ICreateWarehouseTransferDTO} warehouseTransferDTO
|
||||
*/
|
||||
public authorize = async (
|
||||
tenantId: number,
|
||||
warehouseTransferDTO: ICreateWarehouseTransferDTO
|
||||
) => {
|
||||
// Validate warehouse from and to should not be the same.
|
||||
this.validateWarehouseFromToNotSame(warehouseTransferDTO);
|
||||
|
||||
// Retrieves the from warehouse or throw not found service error.
|
||||
const fromWarehouse = await this.getFromWarehouseOrThrow(
|
||||
tenantId,
|
||||
warehouseTransferDTO.fromWarehouseId
|
||||
);
|
||||
// Retrieves the to warehouse or throw not found service error.
|
||||
const toWarehouse = await this.getToWarehouseOrThrow(
|
||||
tenantId,
|
||||
warehouseTransferDTO.toWarehouseId
|
||||
);
|
||||
// Validates the not found entries items ids.
|
||||
const items = await this.itemsEntries.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
warehouseTransferDTO.entries
|
||||
);
|
||||
// Validate the items entries should be inventory type.
|
||||
this.validateItemsShouldBeInventory(items);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new warehouse transfer transaction.
|
||||
* @param {number} tenantId -
|
||||
* @param {ICreateWarehouseTransferDTO} warehouseDTO -
|
||||
* @returns {Promise<IWarehouseTransferCreate>}
|
||||
*/
|
||||
public createWarehouseTransfer = async (
|
||||
tenantId: number,
|
||||
warehouseTransferDTO: ICreateWarehouseTransferDTO
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
const { WarehouseTransfer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Authorize warehouse transfer before creating.
|
||||
await this.authorize(tenantId, warehouseTransferDTO);
|
||||
|
||||
// Transformes the warehouse transfer DTO to model.
|
||||
const warehouseTransferModel = await this.transformDTOToModel(
|
||||
tenantId,
|
||||
warehouseTransferDTO
|
||||
);
|
||||
// Create warehouse transfer under unit-of-work.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseTransferCreate` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouseTransfer.onCreate, {
|
||||
trx,
|
||||
warehouseTransferDTO,
|
||||
tenantId,
|
||||
} as IWarehouseTransferCreate);
|
||||
|
||||
// Stores the warehouse transfer transaction graph to the storage.
|
||||
const warehouseTransfer = await WarehouseTransfer.query(
|
||||
trx
|
||||
).upsertGraphAndFetch({
|
||||
...warehouseTransferModel,
|
||||
});
|
||||
// Triggers `onWarehouseTransferCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouseTransfer.onCreated, {
|
||||
trx,
|
||||
warehouseTransfer,
|
||||
warehouseTransferDTO,
|
||||
tenantId,
|
||||
} as IWarehouseTransferCreated);
|
||||
|
||||
return warehouseTransfer;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IWarehouseTransferDeletedPayload,
|
||||
IWarehouseTransferDeletePayload,
|
||||
} from '@/interfaces';
|
||||
import { CRUDWarehouseTransfer } from './CRUDWarehouseTransfer';
|
||||
|
||||
@Service()
|
||||
export class DeleteWarehouseTransfer extends CRUDWarehouseTransfer {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Deletes warehouse transfer transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public deleteWarehouseTransfer = async (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
): Promise<void> => {
|
||||
const { WarehouseTransfer, WarehouseTransferEntry } =
|
||||
this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the old warehouse transfer or throw not found service error.
|
||||
const oldWarehouseTransfer = await WarehouseTransfer.query()
|
||||
.findById(warehouseTransferId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Deletes the warehouse transfer under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseTransferCreate` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouseTransfer.onDelete, {
|
||||
tenantId,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
} as IWarehouseTransferDeletePayload);
|
||||
|
||||
// Delete warehouse transfer entries.
|
||||
await WarehouseTransferEntry.query(trx)
|
||||
.where('warehouseTransferId', warehouseTransferId)
|
||||
.delete();
|
||||
|
||||
// Delete warehouse transfer.
|
||||
await WarehouseTransfer.query(trx).findById(warehouseTransferId).delete();
|
||||
|
||||
// Triggers `onWarehouseTransferDeleted` event
|
||||
await this.eventPublisher.emitAsync(events.warehouseTransfer.onDeleted, {
|
||||
tenantId,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
} as IWarehouseTransferDeletedPayload);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { Knex } from 'knex';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IEditWarehouseTransferDTO,
|
||||
IWarehouseTransfer,
|
||||
IWarehouseTransferEditPayload,
|
||||
IWarehouseTransferEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import { CommandWarehouseTransfer } from './CommandWarehouseTransfer';
|
||||
|
||||
@Service()
|
||||
export class EditWarehouseTransfer extends CommandWarehouseTransfer {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Edits warehouse transfer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @param {IEditWarehouseTransferDTO} editWarehouseDTO
|
||||
* @returns {Promise<IWarehouseTransfer>}
|
||||
*/
|
||||
public editWarehouseTransfer = async (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number,
|
||||
editWarehouseDTO: IEditWarehouseTransferDTO
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
const { WarehouseTransfer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old warehouse transfer transaction.
|
||||
const oldWarehouseTransfer = await WarehouseTransfer.query()
|
||||
.findById(warehouseTransferId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Validate warehouse from and to should not be the same.
|
||||
this.validateWarehouseFromToNotSame(editWarehouseDTO);
|
||||
|
||||
// Retrieves the from warehouse or throw not found service error.
|
||||
const fromWarehouse = await this.getFromWarehouseOrThrow(
|
||||
tenantId,
|
||||
editWarehouseDTO.fromWarehouseId
|
||||
);
|
||||
// Retrieves the to warehouse or throw not found service error.
|
||||
const toWarehouse = await this.getToWarehouseOrThrow(
|
||||
tenantId,
|
||||
editWarehouseDTO.toWarehouseId
|
||||
);
|
||||
// Validates the not found entries items ids.
|
||||
const items = await this.itemsEntries.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
editWarehouseDTO.entries
|
||||
);
|
||||
// Validate the items entries should be inventory type.
|
||||
this.validateItemsShouldBeInventory(items);
|
||||
|
||||
// Edits warehouse transfer transaction under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseTransferEdit` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouseTransfer.onEdit, {
|
||||
tenantId,
|
||||
editWarehouseDTO,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
} as IWarehouseTransferEditPayload);
|
||||
|
||||
// Updates warehouse transfer graph on the storage.
|
||||
const warehouseTransfer = await WarehouseTransfer.query(
|
||||
trx
|
||||
).upsertGraphAndFetch({
|
||||
id: warehouseTransferId,
|
||||
...editWarehouseDTO,
|
||||
});
|
||||
// Triggers `onWarehouseTransferEdit` event
|
||||
await this.eventPublisher.emitAsync(events.warehouseTransfer.onEdited, {
|
||||
tenantId,
|
||||
editWarehouseDTO,
|
||||
warehouseTransfer,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
} as IWarehouseTransferEditedPayload);
|
||||
|
||||
return warehouseTransfer;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { IWarehouseTransfer } from '@/interfaces';
|
||||
import { CRUDWarehouseTransfer } from './CRUDWarehouseTransfer';
|
||||
import { WarehouseTransferTransformer } from './WarehouseTransferTransfomer';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GetWarehouseTransfer extends CRUDWarehouseTransfer {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieves the specific warehouse transfer transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @param {IEditWarehouseTransferDTO} editWarehouseDTO
|
||||
* @returns {Promise<IWarehouseTransfer>}
|
||||
*/
|
||||
public getWarehouseTransfer = async (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
const { WarehouseTransfer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old warehouse transfer transaction.
|
||||
const warehouseTransfer = await WarehouseTransfer.query()
|
||||
.findById(warehouseTransferId)
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('fromWarehouse')
|
||||
.withGraphFetched('toWarehouse');
|
||||
|
||||
this.throwIfTransferNotFound(warehouseTransfer);
|
||||
|
||||
// Retrieves the transfromed warehouse transfers.
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
new WarehouseTransferTransformer()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import * as R from 'ramda';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { IGetWarehousesTransfersFilterDTO } from '@/interfaces';
|
||||
import { WarehouseTransferTransformer } from './WarehouseTransferTransfomer';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GetWarehouseTransfers {
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Parses the sale invoice list filter DTO.
|
||||
* @param filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves warehouse transfers paginated list.
|
||||
* @param {number} tenantId
|
||||
* @param {IGetWarehousesTransfersFilterDTO} filterDTO
|
||||
* @returns {}
|
||||
*/
|
||||
public getWarehouseTransfers = async (
|
||||
tenantId: number,
|
||||
filterDTO: IGetWarehousesTransfersFilterDTO
|
||||
) => {
|
||||
const { WarehouseTransfer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses stringified filter roles.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
WarehouseTransfer,
|
||||
filter
|
||||
);
|
||||
const { results, pagination } = await WarehouseTransfer.query()
|
||||
.onBuild((query) => {
|
||||
query.withGraphFetched('entries.item');
|
||||
query.withGraphFetched('fromWarehouse');
|
||||
query.withGraphFetched('toWarehouse');
|
||||
|
||||
dynamicFilter.buildQuery()(query);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
// Retrieves the transformed warehouse transfers
|
||||
const warehousesTransfers = await this.transformer.transform(
|
||||
tenantId,
|
||||
results,
|
||||
new WarehouseTransferTransformer()
|
||||
);
|
||||
return {
|
||||
warehousesTransfers,
|
||||
pagination,
|
||||
filter,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IWarehouseTransfer,
|
||||
IWarehouseTransferEditedPayload,
|
||||
IWarehouseTransferInitiatePayload,
|
||||
} from '@/interfaces';
|
||||
import { CommandWarehouseTransfer } from './CommandWarehouseTransfer';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
@Service()
|
||||
export class InitiateWarehouseTransfer extends CommandWarehouseTransfer {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Validate the given warehouse transfer not already initiated.
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
*/
|
||||
private validateWarehouseTransferNotAlreadyInitiated = (
|
||||
warehouseTransfer: IWarehouseTransfer
|
||||
) => {
|
||||
if (warehouseTransfer.transferInitiatedAt) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_ALREADY_INITIATED);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initiate warehouse transfer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @returns {Promise<IWarehouseTransfer>}
|
||||
*/
|
||||
public initiateWarehouseTransfer = async (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
const { WarehouseTransfer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old warehouse transfer transaction.
|
||||
const oldWarehouseTransfer = await WarehouseTransfer.query()
|
||||
.findById(warehouseTransferId)
|
||||
.throwIfNotFound(warehouseTransferId);
|
||||
|
||||
// Validate the given warehouse transfer not already initiated.
|
||||
this.validateWarehouseTransferNotAlreadyInitiated(oldWarehouseTransfer);
|
||||
|
||||
// Edits warehouse transfer transaction under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseTransferInitiate` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouseTransfer.onInitiate, {
|
||||
tenantId,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
} as IWarehouseTransferInitiatePayload);
|
||||
|
||||
// Updates warehouse transfer graph on the storage.
|
||||
const warehouseTransferUpdated = await WarehouseTransfer.query(trx)
|
||||
.findById(warehouseTransferId)
|
||||
.patch({
|
||||
transferInitiatedAt: new Date(),
|
||||
});
|
||||
// Fetches the warehouse transfer with entries.
|
||||
const warehouseTransfer = await WarehouseTransfer.query(trx)
|
||||
.findById(warehouseTransferId)
|
||||
.withGraphFetched('entries');
|
||||
|
||||
// Triggers `onWarehouseTransferEdit` event
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.warehouseTransfer.onInitiated,
|
||||
{
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
} as IWarehouseTransferEditedPayload
|
||||
);
|
||||
return warehouseTransfer;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IWarehouseTransfer,
|
||||
IWarehouseTransferTransferingPayload,
|
||||
IWarehouseTransferTransferredPayload,
|
||||
} from '@/interfaces';
|
||||
import { CommandWarehouseTransfer } from './CommandWarehouseTransfer';
|
||||
import { ERRORS } from './constants';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
|
||||
@Service()
|
||||
export class TransferredWarehouseTransfer extends CommandWarehouseTransfer {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Validate the warehouse transfer not already transferred.
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
*/
|
||||
private validateWarehouseTransferNotTransferred = (
|
||||
warehouseTransfer: IWarehouseTransfer
|
||||
) => {
|
||||
if (warehouseTransfer.transferDeliveredAt) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_ALREAD_TRANSFERRED);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the warehouse transfer should be initiated.
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
*/
|
||||
private validateWarehouseTranbsferShouldInitiated = (
|
||||
warehouseTransfer: IWarehouseTransfer
|
||||
) => {
|
||||
if (!warehouseTransfer.transferInitiatedAt) {
|
||||
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_NOT_INITIATED);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Transferred warehouse transfer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @returns {Promise<IWarehouseTransfer>}
|
||||
*/
|
||||
public transferredWarehouseTransfer = async (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
const { WarehouseTransfer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old warehouse transfer transaction.
|
||||
const oldWarehouseTransfer = await WarehouseTransfer.query()
|
||||
.findById(warehouseTransferId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Validate the warehouse transfer not already transferred.
|
||||
this.validateWarehouseTransferNotTransferred(oldWarehouseTransfer);
|
||||
|
||||
// Validate the warehouse transfer should be initiated.
|
||||
this.validateWarehouseTranbsferShouldInitiated(oldWarehouseTransfer);
|
||||
|
||||
// Edits warehouse transfer transaction under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onWarehouseTransferInitiate` event.
|
||||
await this.eventPublisher.emitAsync(events.warehouseTransfer.onTransfer, {
|
||||
tenantId,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
} as IWarehouseTransferTransferingPayload);
|
||||
|
||||
// Updates warehouse transfer graph on the storage.
|
||||
const warehouseTransferUpdated = await WarehouseTransfer.query(trx)
|
||||
.findById(warehouseTransferId)
|
||||
.patch({
|
||||
transferDeliveredAt: new Date(),
|
||||
});
|
||||
// Fetches the warehouse transfer with entries.
|
||||
const warehouseTransfer = await WarehouseTransfer.query(trx)
|
||||
.findById(warehouseTransferId)
|
||||
.withGraphFetched('entries');
|
||||
|
||||
// Triggers `onWarehouseTransferEdit` event
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.warehouseTransfer.onTransferred,
|
||||
{
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
} as IWarehouseTransferTransferredPayload
|
||||
);
|
||||
return warehouseTransfer;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
import {
|
||||
ICreateWarehouseTransferDTO,
|
||||
IEditWarehouseTransferDTO,
|
||||
IGetWarehousesTransfersFilterDTO,
|
||||
IWarehouseTransfer,
|
||||
} from '@/interfaces';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { CreateWarehouseTransfer } from './CreateWarehouseTransfer';
|
||||
import { DeleteWarehouseTransfer } from './DeleteWarehouseTransfer';
|
||||
import { EditWarehouseTransfer } from './EditWarehouseTransfer';
|
||||
import { GetWarehouseTransfer } from './GetWarehouseTransfer';
|
||||
import { GetWarehouseTransfers } from './GetWarehouseTransfers';
|
||||
import { InitiateWarehouseTransfer } from './InitiateWarehouseTransfer';
|
||||
import { TransferredWarehouseTransfer } from './TransferredWarehouseTransfer';
|
||||
|
||||
@Service()
|
||||
export class WarehouseTransferApplication {
|
||||
@Inject()
|
||||
private createWarehouseTransferService: CreateWarehouseTransfer;
|
||||
|
||||
@Inject()
|
||||
private editWarehouseTransferService: EditWarehouseTransfer;
|
||||
|
||||
@Inject()
|
||||
private deleteWarehouseTransferService: DeleteWarehouseTransfer;
|
||||
|
||||
@Inject()
|
||||
private getWarehouseTransferService: GetWarehouseTransfer;
|
||||
|
||||
@Inject()
|
||||
private getWarehousesTransfersService: GetWarehouseTransfers;
|
||||
|
||||
@Inject()
|
||||
private initiateWarehouseTransferService: InitiateWarehouseTransfer;
|
||||
|
||||
@Inject()
|
||||
private transferredWarehouseTransferService: TransferredWarehouseTransfer;
|
||||
|
||||
/**
|
||||
* Creates a warehouse transfer transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {ICreateWarehouseTransferDTO} createWarehouseTransferDTO
|
||||
* @returns {}
|
||||
*/
|
||||
public createWarehouseTransfer = (
|
||||
tenantId: number,
|
||||
createWarehouseTransferDTO: ICreateWarehouseTransferDTO
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
return this.createWarehouseTransferService.createWarehouseTransfer(
|
||||
tenantId,
|
||||
createWarehouseTransferDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits warehouse transfer transaction.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} warehouseTransferId - number
|
||||
* @param {IEditWarehouseTransferDTO} editWarehouseTransferDTO
|
||||
*/
|
||||
public editWarehouseTransfer = (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number,
|
||||
editWarehouseTransferDTO: IEditWarehouseTransferDTO
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
return this.editWarehouseTransferService.editWarehouseTransfer(
|
||||
tenantId,
|
||||
warehouseTransferId,
|
||||
editWarehouseTransferDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes warehouse transfer transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public deleteWarehouseTransfer = (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
): Promise<void> => {
|
||||
return this.deleteWarehouseTransferService.deleteWarehouseTransfer(
|
||||
tenantId,
|
||||
warehouseTransferId
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves warehouse transfer transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @returns {Promise<IWarehouseTransfer>}
|
||||
*/
|
||||
public getWarehouseTransfer = (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
return this.getWarehouseTransferService.getWarehouseTransfer(
|
||||
tenantId,
|
||||
warehouseTransferId
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves warehouses trans
|
||||
* @param {number} tenantId
|
||||
* @param {IGetWarehousesTransfersFilterDTO} filterDTO
|
||||
* @returns {Promise<IWarehouseTransfer>}
|
||||
*/
|
||||
public getWarehousesTransfers = (
|
||||
tenantId: number,
|
||||
filterDTO: IGetWarehousesTransfersFilterDTO
|
||||
) => {
|
||||
return this.getWarehousesTransfersService.getWarehouseTransfers(
|
||||
tenantId,
|
||||
filterDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Marks the warehouse transfer order as transfered.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @returns {Promise<IWarehouseTransfer>}
|
||||
*/
|
||||
public transferredWarehouseTransfer = (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
return this.transferredWarehouseTransferService.transferredWarehouseTransfer(
|
||||
tenantId,
|
||||
warehouseTransferId
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Marks the warehouse transfer order as initiated.
|
||||
* @param {number} tenantId
|
||||
* @param {number} warehouseTransferId
|
||||
* @returns {Promise<IWarehouseTransfer>}
|
||||
*/
|
||||
public initiateWarehouseTransfer = (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
): Promise<IWarehouseTransfer> => {
|
||||
return this.initiateWarehouseTransferService.initiateWarehouseTransfer(
|
||||
tenantId,
|
||||
warehouseTransferId
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import AutoIncrementOrdersService from '../../Sales/AutoIncrementOrdersService';
|
||||
|
||||
@Service()
|
||||
export class WarehouseTransferAutoIncrement {
|
||||
@Inject()
|
||||
private autoIncrementOrdersService: AutoIncrementOrdersService;
|
||||
|
||||
/**
|
||||
* Retrieve the next unique invoice number.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @return {string}
|
||||
*/
|
||||
public getNextTransferNumber(tenantId: number): string {
|
||||
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||
tenantId,
|
||||
'warehouse_transfers'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the invoice next number.
|
||||
* @param {number} tenantId -
|
||||
*/
|
||||
public incrementNextTransferNumber(tenantId: number) {
|
||||
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||
tenantId,
|
||||
'warehouse_transfers'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import { IWarehouseTransferCreated } from '@/interfaces';
|
||||
import { WarehouseTransferAutoIncrement } from './WarehouseTransferAutoIncrement';
|
||||
|
||||
@Service()
|
||||
export class WarehouseTransferAutoIncrementSubscriber {
|
||||
@Inject()
|
||||
private warehouseTransferAutoIncrement: WarehouseTransferAutoIncrement;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach = (bus) => {
|
||||
bus.subscribe(
|
||||
events.warehouseTransfer.onCreated,
|
||||
this.incrementTransferAutoIncrementOnCreated
|
||||
);
|
||||
return bus;
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes inventory transactions once warehouse transfer created.
|
||||
* @param {IInventoryTransactionsCreatedPayload} -
|
||||
*/
|
||||
private incrementTransferAutoIncrementOnCreated = async ({
|
||||
tenantId,
|
||||
}: IWarehouseTransferCreated) => {
|
||||
await this.warehouseTransferAutoIncrement.incrementNextTransferNumber(
|
||||
tenantId
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IWarehouseTransferEditedPayload,
|
||||
IWarehouseTransferDeletedPayload,
|
||||
IWarehouseTransferCreated,
|
||||
IWarehouseTransferInitiatedPayload,
|
||||
IWarehouseTransferTransferredPayload,
|
||||
} from '@/interfaces';
|
||||
import { WarehouseTransferInventoryTransactions } from './WriteInventoryTransactions';
|
||||
|
||||
@Service()
|
||||
export class WarehouseTransferInventoryTransactionsSubscriber {
|
||||
@Inject()
|
||||
private warehouseTransferInventoryTransactions: WarehouseTransferInventoryTransactions;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach = (bus) => {
|
||||
bus.subscribe(
|
||||
events.warehouseTransfer.onCreated,
|
||||
this.writeInventoryTransactionsOnWarehouseTransferCreated
|
||||
);
|
||||
bus.subscribe(
|
||||
events.warehouseTransfer.onEdited,
|
||||
this.rewriteInventoryTransactionsOnWarehouseTransferEdited
|
||||
);
|
||||
bus.subscribe(
|
||||
events.warehouseTransfer.onDeleted,
|
||||
this.revertInventoryTransactionsOnWarehouseTransferDeleted
|
||||
);
|
||||
bus.subscribe(
|
||||
events.warehouseTransfer.onInitiated,
|
||||
this.writeInventoryTransactionsOnTransferInitiated
|
||||
);
|
||||
bus.subscribe(
|
||||
events.warehouseTransfer.onTransferred,
|
||||
this.writeInventoryTransactionsOnTransferred
|
||||
);
|
||||
return bus;
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes inventory transactions once warehouse transfer created.
|
||||
* @param {IInventoryTransactionsCreatedPayload} -
|
||||
*/
|
||||
private writeInventoryTransactionsOnWarehouseTransferCreated = async ({
|
||||
warehouseTransfer,
|
||||
tenantId,
|
||||
trx,
|
||||
}: IWarehouseTransferCreated) => {
|
||||
// Can't continue if the warehouse transfer is not initiated yet.
|
||||
if (!warehouseTransfer.isInitiated) return;
|
||||
|
||||
// Write all inventory transaction if warehouse transfer initiated and transferred.
|
||||
if (warehouseTransfer.isInitiated && warehouseTransfer.isTransferred) {
|
||||
await this.warehouseTransferInventoryTransactions.writeAllInventoryTransactions(
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
false,
|
||||
trx
|
||||
);
|
||||
// Write initiate inventory transaction if warehouse transfer initited and transferred yet.
|
||||
} else if (warehouseTransfer.isInitiated) {
|
||||
await this.warehouseTransferInventoryTransactions.writeInitiateInventoryTransactions(
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
false,
|
||||
trx
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Rewrite inventory transactions once warehouse transfer edited.
|
||||
* @param {IWarehouseTransferEditedPayload} -
|
||||
*/
|
||||
private rewriteInventoryTransactionsOnWarehouseTransferEdited = async ({
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
trx,
|
||||
}: IWarehouseTransferEditedPayload) => {
|
||||
// Can't continue if the warehouse transfer is not initiated yet.
|
||||
if (!warehouseTransfer.isInitiated) return;
|
||||
|
||||
// Write all inventory transaction if warehouse transfer initiated and transferred.
|
||||
if (warehouseTransfer.isInitiated && warehouseTransfer.isTransferred) {
|
||||
await this.warehouseTransferInventoryTransactions.writeAllInventoryTransactions(
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
true,
|
||||
trx
|
||||
);
|
||||
// Write initiate inventory transaction if warehouse transfer initited and transferred yet.
|
||||
} else if (warehouseTransfer.isInitiated) {
|
||||
await this.warehouseTransferInventoryTransactions.writeInitiateInventoryTransactions(
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
true,
|
||||
trx
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts inventory transactions once warehouse transfer deleted.
|
||||
* @parma {IWarehouseTransferDeletedPayload} -
|
||||
*/
|
||||
private revertInventoryTransactionsOnWarehouseTransferDeleted = async ({
|
||||
tenantId,
|
||||
oldWarehouseTransfer,
|
||||
trx,
|
||||
}: IWarehouseTransferDeletedPayload) => {
|
||||
await this.warehouseTransferInventoryTransactions.revertInventoryTransactions(
|
||||
tenantId,
|
||||
oldWarehouseTransfer.id,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write inventory transactions of warehouse transfer once the transfer initiated.
|
||||
* @param {IWarehouseTransferInitiatedPayload}
|
||||
*/
|
||||
private writeInventoryTransactionsOnTransferInitiated = async ({
|
||||
trx,
|
||||
warehouseTransfer,
|
||||
tenantId,
|
||||
}: IWarehouseTransferInitiatedPayload) => {
|
||||
await this.warehouseTransferInventoryTransactions.writeInitiateInventoryTransactions(
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
false,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write inventory transactions of warehouse transfer once the transfer completed.
|
||||
* @param {IWarehouseTransferTransferredPayload}
|
||||
*/
|
||||
private writeInventoryTransactionsOnTransferred = async ({
|
||||
trx,
|
||||
warehouseTransfer,
|
||||
tenantId,
|
||||
}: IWarehouseTransferTransferredPayload) => {
|
||||
await this.warehouseTransferInventoryTransactions.writeTransferredInventoryTransactions(
|
||||
tenantId,
|
||||
warehouseTransfer,
|
||||
false,
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
|
||||
export class WarehouseTransferItemTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['formattedQuantity', 'formattedCost', 'formattedTotal'];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entry
|
||||
* @returns
|
||||
*/
|
||||
public formattedTotal = (entry) => {
|
||||
return this.formatMoney(entry.total);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entry
|
||||
* @returns
|
||||
*/
|
||||
public formattedQuantity = (entry) => {
|
||||
return this.formatNumber(entry.quantity);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entry
|
||||
* @returns
|
||||
*/
|
||||
public formattedCost = (entry) => {
|
||||
return this.formatMoney(entry.cost);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { WarehouseTransferItemTransformer } from './WarehouseTransferItemTransformer';
|
||||
|
||||
export class WarehouseTransferTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['formattedDate', 'entries'];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param transfer
|
||||
* @returns
|
||||
*/
|
||||
protected formattedDate = (transfer) => {
|
||||
return this.formatDate(transfer.date);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected entries = (transfer) => {
|
||||
return this.item(transfer.entries, new WarehouseTransferItemTransformer());
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
IWarehouseTransfer,
|
||||
IInventoryTransaction,
|
||||
IWarehouseTransferEntry,
|
||||
} from '@/interfaces';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
|
||||
@Service()
|
||||
export class WarehouseTransferInventoryTransactions {
|
||||
@Inject()
|
||||
private inventory: InventoryService;
|
||||
|
||||
/**
|
||||
* Writes all (initiate and transfer) inventory transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
* @param {Boolean} override
|
||||
* @param {Knex.Transaction} trx - Knex transcation.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public writeAllInventoryTransactions = async (
|
||||
tenantId: number,
|
||||
warehouseTransfer: IWarehouseTransfer,
|
||||
override?: boolean,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const inventoryTransactions =
|
||||
this.getWarehouseTransferInventoryTransactions(warehouseTransfer);
|
||||
|
||||
await this.inventory.recordInventoryTransactions(
|
||||
tenantId,
|
||||
inventoryTransactions,
|
||||
override,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes initiate inventory transactions of warehouse transfer transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
* @param {boolean} override
|
||||
* @param {Knex.Transaction} trx - Knex transaction.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public writeInitiateInventoryTransactions = async (
|
||||
tenantId: number,
|
||||
warehouseTransfer: IWarehouseTransfer,
|
||||
override?: boolean,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const inventoryTransactions =
|
||||
this.getWarehouseFromTransferInventoryTransactions(warehouseTransfer);
|
||||
|
||||
await this.inventory.recordInventoryTransactions(
|
||||
tenantId,
|
||||
inventoryTransactions,
|
||||
override,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes transferred inventory transaction of warehouse transfer transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
* @param {boolean} override
|
||||
* @param {Knex.Transaction} trx - Knex transaction.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public writeTransferredInventoryTransactions = async (
|
||||
tenantId: number,
|
||||
warehouseTransfer: IWarehouseTransfer,
|
||||
override?: boolean,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
const inventoryTransactions =
|
||||
this.getWarehouseToTransferInventoryTransactions(warehouseTransfer);
|
||||
|
||||
await this.inventory.recordInventoryTransactions(
|
||||
tenantId,
|
||||
inventoryTransactions,
|
||||
override,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts warehouse transfer inventory transactions.
|
||||
* @param {number} tenatnId
|
||||
* @param {number} warehouseTransferId
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public revertInventoryTransactions = async (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
await this.inventory.deleteInventoryTransactions(
|
||||
tenantId,
|
||||
warehouseTransferId,
|
||||
'WarehouseTransfer',
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the inventory transactions of the given warehouse transfer.
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
* @returns {IInventoryTransaction[]}
|
||||
*/
|
||||
private getWarehouseFromTransferInventoryTransactions = (
|
||||
warehouseTransfer: IWarehouseTransfer
|
||||
): IInventoryTransaction[] => {
|
||||
const commonEntry = {
|
||||
date: warehouseTransfer.date,
|
||||
transactionType: 'WarehouseTransfer',
|
||||
transactionId: warehouseTransfer.id,
|
||||
};
|
||||
return warehouseTransfer.entries.map((entry: IWarehouseTransferEntry) => ({
|
||||
...commonEntry,
|
||||
entryId: entry.id,
|
||||
itemId: entry.itemId,
|
||||
quantity: entry.quantity,
|
||||
rate: entry.cost,
|
||||
direction: 'OUT',
|
||||
warehouseId: warehouseTransfer.fromWarehouseId,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
* @returns {IInventoryTransaction[]}
|
||||
*/
|
||||
private getWarehouseToTransferInventoryTransactions = (
|
||||
warehouseTransfer: IWarehouseTransfer
|
||||
): IInventoryTransaction[] => {
|
||||
const commonEntry = {
|
||||
date: warehouseTransfer.date,
|
||||
transactionType: 'WarehouseTransfer',
|
||||
transactionId: warehouseTransfer.id,
|
||||
};
|
||||
return warehouseTransfer.entries.map((entry: IWarehouseTransferEntry) => ({
|
||||
...commonEntry,
|
||||
entryId: entry.id,
|
||||
itemId: entry.itemId,
|
||||
quantity: entry.quantity,
|
||||
rate: entry.cost,
|
||||
direction: 'IN',
|
||||
warehouseId: warehouseTransfer.toWarehouseId,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IWarehouseTransfer} warehouseTransfer
|
||||
* @returns {IInventoryTransaction[]}
|
||||
*/
|
||||
private getWarehouseTransferInventoryTransactions = (
|
||||
warehouseTransfer: IWarehouseTransfer
|
||||
): IInventoryTransaction[] => {
|
||||
// Retrieve the to inventory transactions of warehouse transfer.
|
||||
const toTransactions =
|
||||
this.getWarehouseToTransferInventoryTransactions(warehouseTransfer);
|
||||
|
||||
// Retrieve the from inventory transactions of warehouse transfer.
|
||||
const fromTransactions =
|
||||
this.getWarehouseFromTransferInventoryTransactions(warehouseTransfer);
|
||||
|
||||
return [...toTransactions, ...fromTransactions];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
export const ERRORS = {
|
||||
WAREHOUSE_TRANSFER_NOT_FOUND: 'WAREHOUSE_TRANSFER_NOT_FOUND',
|
||||
WAREHOUSES_TRANSFER_SHOULD_NOT_BE_SAME:
|
||||
'WAREHOUSES_TRANSFER_SHOULD_NOT_BE_SAME',
|
||||
|
||||
FROM_WAREHOUSE_NOT_FOUND: 'FROM_WAREHOUSE_NOT_FOUND',
|
||||
TO_WAREHOUSE_NOT_FOUND: 'TO_WAREHOUSE_NOT_FOUND',
|
||||
WAREHOUSE_TRANSFER_ITEMS_SHOULD_BE_INVENTORY:
|
||||
'WAREHOUSE_TRANSFER_ITEMS_SHOULD_BE_INVENTORY',
|
||||
|
||||
WAREHOUSE_TRANSFER_ALREAD_TRANSFERRED:
|
||||
'WAREHOUSE_TRANSFER_ALREADY_TRANSFERRED',
|
||||
|
||||
WAREHOUSE_TRANSFER_ALREADY_INITIATED: 'WAREHOUSE_TRANSFER_ALREADY_INITIATED',
|
||||
WAREHOUSE_TRANSFER_NOT_INITIATED: 'WAREHOUSE_TRANSFER_NOT_INITIATED',
|
||||
};
|
||||
|
||||
// Warehouse transfers default views.
|
||||
export const DEFAULT_VIEWS = [
|
||||
{
|
||||
name: 'warehouse_transfer.view.draft.name',
|
||||
slug: 'draft',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' },
|
||||
],
|
||||
columns: [],
|
||||
},
|
||||
{
|
||||
name: 'warehouse_transfer.view.in_transit.name',
|
||||
slug: 'in-transit',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{
|
||||
index: 1,
|
||||
fieldKey: 'status',
|
||||
comparator: 'equals',
|
||||
value: 'in-transit',
|
||||
},
|
||||
],
|
||||
columns: [],
|
||||
},
|
||||
{
|
||||
name: 'warehouse_transfer.view.transferred.name',
|
||||
slug: 'transferred',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{
|
||||
index: 1,
|
||||
fieldKey: 'status',
|
||||
comparator: 'equals',
|
||||
value: 'tansferred',
|
||||
},
|
||||
],
|
||||
columns: [],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
|
||||
|
||||
export class WarehousesTransfersService {
|
||||
createWarehouseTranser = (
|
||||
tenantId: number,
|
||||
createWarehouseTransfer: ICreateWarehouseTransferDTO
|
||||
) => {};
|
||||
|
||||
editWarehouseTranser = (
|
||||
tenantId: number,
|
||||
editWarehouseTransfer: IEditWarehouseTransferDTO
|
||||
) => {};
|
||||
|
||||
deleteWarehouseTranser = (
|
||||
tenantId: number,
|
||||
warehouseTransferId: number
|
||||
) => {};
|
||||
|
||||
getWarehouseTransfer = (tenantId: number, warehouseTransferId: number) => {};
|
||||
|
||||
getWarehouseTransfers = (tenantId: number) => {};
|
||||
}
|
||||
7
packages/server/src/services/Warehouses/contants.ts
Normal file
7
packages/server/src/services/Warehouses/contants.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const ERRORS = {
|
||||
WAREHOUSE_NOT_FOUND: 'WAREHOUSE_NOT_FOUND',
|
||||
MUTLI_WAREHOUSES_ALREADY_ACTIVATED: 'MUTLI_WAREHOUSES_ALREADY_ACTIVATED',
|
||||
COULD_NOT_DELETE_ONLY_WAERHOUSE: 'COULD_NOT_DELETE_ONLY_WAERHOUSE',
|
||||
WAREHOUSE_CODE_NOT_UNIQUE: 'WAREHOUSE_CODE_NOT_UNIQUE',
|
||||
WAREHOUSE_HAS_ASSOCIATED_TRANSACTIONS: 'WAREHOUSE_HAS_ASSOCIATED_TRANSACTIONS'
|
||||
};
|
||||
Reference in New Issue
Block a user