mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
feat: ability to publish and draft inventory adjustment transactions.
This commit is contained in:
@@ -16,6 +16,13 @@ export default class InventoryAdjustmentsController extends BaseController {
|
|||||||
router() {
|
router() {
|
||||||
const router = 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(
|
router.delete(
|
||||||
'/:id',
|
'/:id',
|
||||||
[param('id').exists().isNumeric().toInt()],
|
[param('id').exists().isNumeric().toInt()],
|
||||||
@@ -62,6 +69,7 @@ export default class InventoryAdjustmentsController extends BaseController {
|
|||||||
.exists()
|
.exists()
|
||||||
.isFloat()
|
.isFloat()
|
||||||
.toInt(),
|
.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.
|
* Retrieve the inventory adjustments paginated list.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ exports.up = function(knex) {
|
|||||||
table.string('reference_no').index();
|
table.string('reference_no').index();
|
||||||
table.string('description');
|
table.string('description');
|
||||||
table.integer('user_id').unsigned();
|
table.integer('user_id').unsigned();
|
||||||
|
table.date('published_at');
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
|
|
||||||
type IAdjustmentTypes = 'increment' | 'decrement';
|
type IAdjustmentTypes = 'increment' | 'decrement';
|
||||||
|
|
||||||
export interface IQuickInventoryAdjustmentDTO {
|
export interface IQuickInventoryAdjustmentDTO {
|
||||||
date: Date | string;
|
date: Date | string;
|
||||||
type: IAdjustmentTypes,
|
type: IAdjustmentTypes;
|
||||||
adjustmentAccountId: number;
|
adjustmentAccountId: number;
|
||||||
reason: string;
|
reason: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -11,31 +10,33 @@ export interface IQuickInventoryAdjustmentDTO {
|
|||||||
itemId: number;
|
itemId: number;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
cost: number;
|
cost: number;
|
||||||
};
|
publish: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IInventoryAdjustment {
|
export interface IInventoryAdjustment {
|
||||||
id?: number,
|
id?: number;
|
||||||
date: Date | string;
|
date: Date | string;
|
||||||
adjustmentAccountId: number;
|
adjustmentAccountId: number;
|
||||||
reason: string;
|
reason: string;
|
||||||
description: string;
|
description: string;
|
||||||
referenceNo: string;
|
referenceNo: string;
|
||||||
inventoryDirection?: 'IN' | 'OUT',
|
inventoryDirection?: 'IN' | 'OUT';
|
||||||
entries: IInventoryAdjustmentEntry[];
|
entries: IInventoryAdjustmentEntry[];
|
||||||
userId: number;
|
userId: number;
|
||||||
};
|
publishedAt?: Date|null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IInventoryAdjustmentEntry {
|
export interface IInventoryAdjustmentEntry {
|
||||||
id?: number,
|
id?: number;
|
||||||
adjustmentId?: number,
|
adjustmentId?: number;
|
||||||
index: number,
|
index: number;
|
||||||
itemId: number;
|
itemId: number;
|
||||||
quantity?: number;
|
quantity?: number;
|
||||||
cost?: number;
|
cost?: number;
|
||||||
value?: number;
|
value?: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface IInventoryAdjustmentsFilter{
|
export interface IInventoryAdjustmentsFilter {
|
||||||
page: number,
|
page: number;
|
||||||
pageSize: number,
|
pageSize: number;
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default class InventoryAdjustment extends TenantModel {
|
|||||||
* Virtual attributes.
|
* Virtual attributes.
|
||||||
*/
|
*/
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
return ['inventoryDirection'];
|
return ['inventoryDirection', 'isPublished'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,6 +30,14 @@ export default class InventoryAdjustment extends TenantModel {
|
|||||||
return InventoryAdjustment.getInventoryDirection(this.type);
|
return InventoryAdjustment.getInventoryDirection(this.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the adjustment is published.
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
get isPublished() {
|
||||||
|
return !!this.publishedAt;
|
||||||
|
}
|
||||||
|
|
||||||
static getInventoryDirection(type) {
|
static getInventoryDirection(type) {
|
||||||
const directions = {
|
const directions = {
|
||||||
'increment': 'IN',
|
'increment': 'IN',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
import moment from 'moment';
|
||||||
import {
|
import {
|
||||||
EventDispatcher,
|
EventDispatcher,
|
||||||
EventDispatcherInterface,
|
EventDispatcherInterface,
|
||||||
@@ -54,16 +55,21 @@ export default class InventoryAdjustmentService {
|
|||||||
authorizedUser: ISystemUser
|
authorizedUser: ISystemUser
|
||||||
): IInventoryAdjustment {
|
): IInventoryAdjustment {
|
||||||
return {
|
return {
|
||||||
...omit(adjustmentDTO, ['quantity', 'cost', 'itemId']),
|
...omit(adjustmentDTO, ['quantity', 'cost', 'itemId', 'publish']),
|
||||||
userId: authorizedUser.id,
|
userId: authorizedUser.id,
|
||||||
|
...(adjustmentDTO.publish
|
||||||
|
? {
|
||||||
|
publishedAt: moment().toMySqlDateTime(),
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
entries: [
|
entries: [
|
||||||
{
|
{
|
||||||
index: 1,
|
index: 1,
|
||||||
itemId: adjustmentDTO.itemId,
|
itemId: adjustmentDTO.itemId,
|
||||||
...('increment' === adjustmentDTO.type
|
...('increment' === adjustmentDTO.type
|
||||||
? {
|
? {
|
||||||
quantity: adjustmentDTO.quantity,
|
quantity: adjustmentDTO.quantity,
|
||||||
cost: adjustmentDTO.cost,
|
cost: adjustmentDTO.cost,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...('decrement' === adjustmentDTO.type
|
...('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.
|
* Retrieve the inventory adjustments paginated list.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -246,7 +296,7 @@ export default class InventoryAdjustmentService {
|
|||||||
async writeInventoryTransactions(
|
async writeInventoryTransactions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
inventoryAdjustment: IInventoryAdjustment,
|
inventoryAdjustment: IInventoryAdjustment,
|
||||||
override: boolean = false,
|
override: boolean = false
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Gets the next inventory lot number.
|
// Gets the next inventory lot number.
|
||||||
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);
|
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export class InventorySubscriber {
|
|||||||
if (dependsComputeJobs.length === 0) {
|
if (dependsComputeJobs.length === 0) {
|
||||||
this.startingDate = null;
|
this.startingDate = null;
|
||||||
|
|
||||||
await this.saleInvoicesCost.scheduleWriteJournalEntries(
|
await this.saleInvoicesCost.scheduleWriteJournalEntries(
|
||||||
tenantId,
|
tenantId,
|
||||||
startingDate
|
startingDate
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ export default class InventoryAdjustmentsSubscriber {
|
|||||||
tenantId,
|
tenantId,
|
||||||
inventoryAdjustment,
|
inventoryAdjustment,
|
||||||
}) {
|
}) {
|
||||||
|
// Can't continue if the inventory adjustment is not published.
|
||||||
|
if (!inventoryAdjustment.isPublished) { return; }
|
||||||
|
|
||||||
await this.inventoryAdjustment.writeInventoryTransactions(
|
await this.inventoryAdjustment.writeInventoryTransactions(
|
||||||
tenantId,
|
tenantId,
|
||||||
inventoryAdjustment
|
inventoryAdjustment
|
||||||
@@ -39,11 +42,29 @@ export default class InventoryAdjustmentsSubscriber {
|
|||||||
@On(events.inventoryAdjustment.onDeleted)
|
@On(events.inventoryAdjustment.onDeleted)
|
||||||
async handleRevertInventoryTransactionsOnceDeleted({
|
async handleRevertInventoryTransactionsOnceDeleted({
|
||||||
tenantId,
|
tenantId,
|
||||||
inventoryAdjustmentId
|
inventoryAdjustmentId,
|
||||||
|
oldInventoryTransaction,
|
||||||
}) {
|
}) {
|
||||||
|
// Can't continue if the inventory adjustment is not published.
|
||||||
|
if (!oldInventoryTransaction.isPublished) { return; }
|
||||||
|
|
||||||
await this.inventoryAdjustment.revertInventoryTransactions(
|
await this.inventoryAdjustment.revertInventoryTransactions(
|
||||||
tenantId,
|
tenantId,
|
||||||
inventoryAdjustmentId,
|
inventoryAdjustmentId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles writing inventory transactions once the quick adjustment created.
|
||||||
|
*/
|
||||||
|
@On(events.inventoryAdjustment.onPublished)
|
||||||
|
async handleWriteInventoryTransactionsOncePublished({
|
||||||
|
tenantId,
|
||||||
|
inventoryAdjustment,
|
||||||
|
}) {
|
||||||
|
await this.inventoryAdjustment.writeInventoryTransactions(
|
||||||
|
tenantId,
|
||||||
|
inventoryAdjustment
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -193,9 +193,13 @@ export default {
|
|||||||
onComputeItemCostJobCompleted: 'onComputeItemCostJobCompleted'
|
onComputeItemCostJobCompleted: 'onComputeItemCostJobCompleted'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inventory adjustment service.
|
||||||
|
*/
|
||||||
inventoryAdjustment: {
|
inventoryAdjustment: {
|
||||||
onCreated: 'onInventoryAdjustmentCreated',
|
onCreated: 'onInventoryAdjustmentCreated',
|
||||||
onQuickCreated: 'onInventoryAdjustmentQuickCreated',
|
onQuickCreated: 'onInventoryAdjustmentQuickCreated',
|
||||||
onDeleted: 'onInventoryAdjustmentDeleted',
|
onDeleted: 'onInventoryAdjustmentDeleted',
|
||||||
|
onPublished: 'onInventoryAdjustmentPublished',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user