feat: ability to publish and draft inventory adjustment transactions.

This commit is contained in:
a.bouhuolia
2021-01-11 21:05:23 +02:00
parent bbd4ee5962
commit 463c748717
8 changed files with 142 additions and 21 deletions

View File

@@ -16,6 +16,13 @@ export default class InventoryAdjustmentsController extends BaseController {
router() {
const router = Router();
router.post(
'/:id/publish',
[param('id').exists().isNumeric().toInt()],
this.validationResult,
this.asyncMiddleware(this.publishInventoryAdjustment.bind(this)),
this.handleServiceErrors
);
router.delete(
'/:id',
[param('id').exists().isNumeric().toInt()],
@@ -62,6 +69,7 @@ export default class InventoryAdjustmentsController extends BaseController {
.exists()
.isFloat()
.toInt(),
check('publish').default(false).isBoolean().toBoolean(),
];
}
@@ -124,6 +132,34 @@ export default class InventoryAdjustmentsController extends BaseController {
}
}
/**
* Publish the given inventory adjustment transaction.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async publishInventoryAdjustment(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { id: adjustmentId } = req.params;
try {
await this.inventoryAdjustmentService.publishInventoryAdjustment(
tenantId,
adjustmentId
);
return res.status(200).send({
id: adjustmentId,
message: 'The inventory adjustment has been published successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Retrieve the inventory adjustments paginated list.
* @param {Request} req

View File

@@ -9,6 +9,7 @@ exports.up = function(knex) {
table.string('reference_no').index();
table.string('description');
table.integer('user_id').unsigned();
table.date('published_at');
table.timestamps();
});
};

View File

@@ -1,9 +1,8 @@
type IAdjustmentTypes = 'increment' | 'decrement';
export interface IQuickInventoryAdjustmentDTO {
date: Date | string;
type: IAdjustmentTypes,
type: IAdjustmentTypes;
adjustmentAccountId: number;
reason: string;
description: string;
@@ -11,31 +10,33 @@ export interface IQuickInventoryAdjustmentDTO {
itemId: number;
quantity: number;
cost: number;
};
publish: boolean;
}
export interface IInventoryAdjustment {
id?: number,
id?: number;
date: Date | string;
adjustmentAccountId: number;
reason: string;
description: string;
referenceNo: string;
inventoryDirection?: 'IN' | 'OUT',
inventoryDirection?: 'IN' | 'OUT';
entries: IInventoryAdjustmentEntry[];
userId: number;
};
publishedAt?: Date|null;
}
export interface IInventoryAdjustmentEntry {
id?: number,
adjustmentId?: number,
index: number,
id?: number;
adjustmentId?: number;
index: number;
itemId: number;
quantity?: number;
cost?: number;
value?: number;
};
}
export interface IInventoryAdjustmentsFilter{
page: number,
pageSize: number,
};
export interface IInventoryAdjustmentsFilter {
page: number;
pageSize: number;
}

View File

@@ -20,7 +20,7 @@ export default class InventoryAdjustment extends TenantModel {
* Virtual attributes.
*/
static get virtualAttributes() {
return ['inventoryDirection'];
return ['inventoryDirection', 'isPublished'];
}
/**
@@ -30,6 +30,14 @@ export default class InventoryAdjustment extends TenantModel {
return InventoryAdjustment.getInventoryDirection(this.type);
}
/**
* Detarmines whether the adjustment is published.
* @return {boolean}
*/
get isPublished() {
return !!this.publishedAt;
}
static getInventoryDirection(type) {
const directions = {
'increment': 'IN',

View File

@@ -1,5 +1,6 @@
import { Inject, Service } from 'typedi';
import { omit } from 'lodash';
import moment from 'moment';
import {
EventDispatcher,
EventDispatcherInterface,
@@ -54,16 +55,21 @@ export default class InventoryAdjustmentService {
authorizedUser: ISystemUser
): IInventoryAdjustment {
return {
...omit(adjustmentDTO, ['quantity', 'cost', 'itemId']),
...omit(adjustmentDTO, ['quantity', 'cost', 'itemId', 'publish']),
userId: authorizedUser.id,
...(adjustmentDTO.publish
? {
publishedAt: moment().toMySqlDateTime(),
}
: {}),
entries: [
{
index: 1,
itemId: adjustmentDTO.itemId,
...('increment' === adjustmentDTO.type
? {
quantity: adjustmentDTO.quantity,
cost: adjustmentDTO.cost,
quantity: adjustmentDTO.quantity,
cost: adjustmentDTO.cost,
}
: {}),
...('decrement' === adjustmentDTO.type
@@ -212,6 +218,50 @@ export default class InventoryAdjustmentService {
);
}
/**
* Publish the inventory adjustment transaction.
* @param tenantId
* @param inventoryAdjustmentId
*/
async publishInventoryAdjustment(
tenantId: number,
inventoryAdjustmentId: number
): Promise<void> {
const { InventoryAdjustment } = this.tenancy.models(tenantId);
// Retrieve the inventory adjustment or throw not found service error.
const oldInventoryAdjustment = await this.getInventoryAdjustmentOrThrowError(
tenantId,
inventoryAdjustmentId
);
this.logger.info('[inventory_adjustment] trying to publish adjustment.', {
tenantId,
inventoryAdjustmentId,
});
// Publish the inventory adjustment transaction.
await InventoryAdjustment.query()
.findById(inventoryAdjustmentId)
.patch({
publishedAt: moment().toMySqlDateTime(),
});
// Retrieve the inventory adjustment after the modification.
const inventoryAdjustment = await InventoryAdjustment.query()
.findById(inventoryAdjustmentId)
.withGraphFetched('entries');
// Triggers `onInventoryAdjustmentDeleted` event.
await this.eventDispatcher.dispatch(
events.inventoryAdjustment.onPublished,
{
tenantId,
inventoryAdjustmentId,
inventoryAdjustment,
oldInventoryAdjustment,
}
);
}
/**
* Retrieve the inventory adjustments paginated list.
* @param {number} tenantId
@@ -246,7 +296,7 @@ export default class InventoryAdjustmentService {
async writeInventoryTransactions(
tenantId: number,
inventoryAdjustment: IInventoryAdjustment,
override: boolean = false,
override: boolean = false
): Promise<void> {
// Gets the next inventory lot number.
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);

View File

@@ -30,7 +30,7 @@ export class InventorySubscriber {
if (dependsComputeJobs.length === 0) {
this.startingDate = null;
await this.saleInvoicesCost.scheduleWriteJournalEntries(
await this.saleInvoicesCost.scheduleWriteJournalEntries(
tenantId,
startingDate
);

View File

@@ -27,6 +27,9 @@ export default class InventoryAdjustmentsSubscriber {
tenantId,
inventoryAdjustment,
}) {
// Can't continue if the inventory adjustment is not published.
if (!inventoryAdjustment.isPublished) { return; }
await this.inventoryAdjustment.writeInventoryTransactions(
tenantId,
inventoryAdjustment
@@ -39,11 +42,29 @@ export default class InventoryAdjustmentsSubscriber {
@On(events.inventoryAdjustment.onDeleted)
async handleRevertInventoryTransactionsOnceDeleted({
tenantId,
inventoryAdjustmentId
inventoryAdjustmentId,
oldInventoryTransaction,
}) {
// Can't continue if the inventory adjustment is not published.
if (!oldInventoryTransaction.isPublished) { return; }
await this.inventoryAdjustment.revertInventoryTransactions(
tenantId,
inventoryAdjustmentId,
);
}
/**
* Handles writing inventory transactions once the quick adjustment created.
*/
@On(events.inventoryAdjustment.onPublished)
async handleWriteInventoryTransactionsOncePublished({
tenantId,
inventoryAdjustment,
}) {
await this.inventoryAdjustment.writeInventoryTransactions(
tenantId,
inventoryAdjustment
)
}
}

View File

@@ -193,9 +193,13 @@ export default {
onComputeItemCostJobCompleted: 'onComputeItemCostJobCompleted'
},
/**
* Inventory adjustment service.
*/
inventoryAdjustment: {
onCreated: 'onInventoryAdjustmentCreated',
onQuickCreated: 'onInventoryAdjustmentQuickCreated',
onDeleted: 'onInventoryAdjustmentDeleted',
onPublished: 'onInventoryAdjustmentPublished',
}
}