mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +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() {
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -30,7 +30,7 @@ export class InventorySubscriber {
|
||||
if (dependsComputeJobs.length === 0) {
|
||||
this.startingDate = null;
|
||||
|
||||
await this.saleInvoicesCost.scheduleWriteJournalEntries(
|
||||
await this.saleInvoicesCost.scheduleWriteJournalEntries(
|
||||
tenantId,
|
||||
startingDate
|
||||
);
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -193,9 +193,13 @@ export default {
|
||||
onComputeItemCostJobCompleted: 'onComputeItemCostJobCompleted'
|
||||
},
|
||||
|
||||
/**
|
||||
* Inventory adjustment service.
|
||||
*/
|
||||
inventoryAdjustment: {
|
||||
onCreated: 'onInventoryAdjustmentCreated',
|
||||
onQuickCreated: 'onInventoryAdjustmentQuickCreated',
|
||||
onDeleted: 'onInventoryAdjustmentDeleted',
|
||||
onPublished: 'onInventoryAdjustmentPublished',
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user