Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added dump.rdb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"numeral": "^2.0.6",
"passport": "^0.4.0",
"passport-jwt": "^4.0.0",
"pdf-creator-node": "^2.3.4",
"pm2": "^5.1.0",
"redis": "^3.1.2",
"sequelize": "^6.6.5",
Expand Down
20 changes: 20 additions & 0 deletions src/modules/inventory/stockCorrection/controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const httpStatus = require('http-status');
const fs = require('fs');
const pdf = require('pdf-creator-node');
const catchAsync = require('@src/utils/catchAsync');
const apiServices = require('./services/apis');

Expand Down Expand Up @@ -164,6 +166,23 @@ const deleteFormRejectByToken = catchAsync(async (req, res) => {
res.status(httpStatus.OK).send({ data: stockCorrection, meta: { projectName: project.name } });
});

const generatePdf = catchAsync(async (req, res) => {
const {
currentTenantDatabase,
params: { stockCorrectionId },
} = req;
const {document, options} = await new apiServices.GeneratePdf(currentTenantDatabase, stockCorrectionId).call();

pdf.create(document, options).then((stream) => {
const file = fs.createReadStream(stream.path);
const stat = fs.statSync(stream.path);
res.setHeader('Content-Length', stat.size);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename=stock-correction.pdf`);
return file.pipe(res);
});
});

module.exports = {
findAll,
findOne,
Expand All @@ -178,4 +197,5 @@ module.exports = {
deleteFormApproveByToken,
deleteFormReject,
deleteFormRejectByToken,
generatePdf,
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ module.exports = (sequelize, DataTypes, projectCode) => {
class StockCorrectionItem extends Model {
static associate({ [projectCode]: models }) {
this.belongsTo(models.StockCorrection, { as: 'stockCorrection', onUpdate: 'CASCADE', onDelete: 'CASCADE' });
this.belongsTo(models.Allocation, { as: 'allocation', onDelete: 'RESTRICT' });

this.belongsTo(models.Item, { as: 'item', onUpdate: 'RESTRICT', onDelete: 'RESTRICT' });
}
Expand All @@ -20,27 +19,13 @@ module.exports = (sequelize, DataTypes, projectCode) => {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
initialStock: {
type: DataTypes.DECIMAL,
allowNull: true,
get() {
return parseFloat(this.getDataValue('initialStock'));
},
},
quantity: {
type: DataTypes.DECIMAL,
allowNull: false,
get() {
return parseFloat(this.getDataValue('quantity'));
},
},
finalStock: {
type: DataTypes.DECIMAL,
allowNull: true,
get() {
return parseFloat(this.getDataValue('finalStock'));
},
},
expiryDate: {
type: DataTypes.DATE,
allowNull: true,
Expand Down Expand Up @@ -69,9 +54,6 @@ module.exports = (sequelize, DataTypes, projectCode) => {
type: DataTypes.STRING,
allowNull: true,
},
allocationId: {
type: DataTypes.INTEGER,
},
},
{
hooks: {},
Expand Down
9 changes: 9 additions & 0 deletions src/modules/inventory/stockCorrection/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,13 @@ router
controller.updateForm
);

// ADD WATERMARK ON REJECTED STOCK-CORRECTION PDF
router
.route('/pdf/:stockCorrectionId')
.get(
celebrate(requestValidations.requireAuth),
celebrate(requestValidations.requireStockCorrectionId),
controller.generatePdf
);

module.exports = router;
84 changes: 84 additions & 0 deletions src/modules/inventory/stockCorrection/services/apis/GeneratePdf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const fs = require('fs');
const httpStatus = require('http-status');
const ApiError = require('@src/utils/ApiError');

class GeneratePdf {
constructor(tenantDatabase, stockCorrectionId) {
this.tenantDatabase = tenantDatabase;
this.stockCorrectionId = stockCorrectionId;
}

async call() {
let items = [];
let templatePath = 'stockCorrection';

const stockCorrection = await this.tenantDatabase.StockCorrection.findOne({
where: {
id: this.stockCorrectionId,
},
include: [
{
model: this.tenantDatabase.Form,
as: 'form',
include: [
{ model: this.tenantDatabase.User, as: 'createdByUser' },
{ model: this.tenantDatabase.User, as: 'requestApprovalToUser' },
],
},
{ model: this.tenantDatabase.Warehouse, as: 'warehouse' },
{
model: this.tenantDatabase.StockCorrectionItem,
as: 'items',
include: [
{ model: this.tenantDatabase.Item, as: 'item', include: [{ model: this.tenantDatabase.ItemUnit, as: 'units' }] },
],
},
],
});
if (!stockCorrection) {
throw new ApiError(httpStatus.NOT_FOUND, 'Stock correction is not exist');
}

const date = new Date(stockCorrection.form.date).toLocaleDateString('id', { dateStyle: 'long' });
stockCorrection.items.forEach((e) => {
let obj = {};
obj.item = e.item.name;
obj.stockDatabase = parseInt(e.item.stock);
obj.stockCorrectionQty = e.quantity;
obj.balance = parseInt(e.item.stock) - e.quantity;
items.push(obj);
});

if (stockCorrection.form.approvalStatus === 0) {
templatePath = 'stockCorrectionWatermarked';
}

const html = fs.readFileSync(`src/modules/inventory/stockCorrection/templates/${templatePath}.html`, 'utf-8');
const options = {
height: '297mm',
width: '210mm',
orientation: 'portrait',
localUrlAccess: true,
};

const document = {
html,
data: {
date,
formNumber: stockCorrection.form.number,
warehouse: stockCorrection.warehouse.name,
address: stockCorrection.warehouse.address,
phone: stockCorrection.warehouse.phone,
items,
createdBy: stockCorrection.form.createdByUser?.name,
approveBy: stockCorrection.form.requestApprovalToUser?.name,
},
path: './stock-correction.pdf',
type: 'stream',
};

return {document, options}
}
}

module.exports = GeneratePdf;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const httpStatus = require('http-status');
const ApiError = require('@src/utils/ApiError');
const tenantDatabase = require('@src/models').tenant;
const GeneratePdf = require('./GeneratePdf');

describe('Stock Correction - Generate Pdf', () => {
describe('validations', () => {
it('throw error when stock correction is not exist', async () => {
await expect(async () => {
await new GeneratePdf(tenantDatabase, (stockCorrectionId = 'invalid-id')).call();
}).rejects.toThrow(new ApiError(httpStatus.NOT_FOUND, 'Stock correction is not exist'));
});
});

describe('success', () => {
let options;
it('Pdf Generated', async () => {
({ options } = await new GeneratePdf(tenantDatabase, 1).call());
expect(options).toEqual({
height: '297mm',
width: '210mm',
orientation: 'portrait',
localUrlAccess: true,
});
});
});
});
2 changes: 2 additions & 0 deletions src/modules/inventory/stockCorrection/services/apis/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const DeleteFormRequest = require('./DeleteFormRequest');
const FindAll = require('./FindAll');
const FindOne = require('./FindOne');
const UpdateForm = require('./UpdateForm');
const GeneratePdf = require('./GeneratePdf');

module.exports = {
CreateFormRequest,
Expand All @@ -26,4 +27,5 @@ module.exports = {
FindAll,
FindOne,
UpdateForm,
GeneratePdf,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stock Correction</title>
</head>
<body>
<div style="display: flex; flex-direction: row; margin-top: 4mm">
<img
src="https://cdn.pixabay.com/photo/2014/09/19/12/43/star-452341_1280.png"
style="height: 2cm; width: 2cm; margin-left: 3mm"
/>
<div style="margin-left: 75mm; margin-top: -2cm">
<h2 style="margin: 0 0 3mm 0; color: #2296f3">Stock Correction</h2>
<h3 style="margin: 0 0 2mm 0; color: #2296f3">PT.GANESHA MANDIRI BHAKTI</h3>
<p style="margin: 0 0 2mm 0">Jln. Musi No 21 Tegalsari Surabaya</p>
<p style="margin: 0 0 2mm 0">Phone : (021)-29339383</p>
</div>
</div>
<div style="background-color: #2296f3; height: 5mm; margin: 4mm 2mm 2mm 2mm"></div>
<div style="display: flex; flex-direction: row; margin-top: 4mm">
<div style="margin-left: 3mm">
<p style="margin: 0 0 2mm 0">Date : {{date}}</p>
<p style="margin: 0 0 2mm 0">Form Number : {{formNumber}}</p>
</div>
<div style="margin-left: 75mm; margin-top: -1.25cm">
<p style="margin: 0 0 2mm 0">Warehouse : {{warehouse}}</p>
<p style="margin: 0 0 2mm 0">Addrress : {{address}}</p>
<p style="margin: 0 0 2mm 0">Phone Number : {{phone}}</p>
</div>
</div>
<div style="margin-top: 7mm">
<table style="margin: auto; border: 1px solid #ddd; width: 80%">
<tr style="background-color: #2296f3">
<th style="padding-top: 10px; padding-bottom: 10px">Item</th>
<th style="padding-top: 10px; padding-bottom: 10px">Stock Database</th>
<th style="padding-top: 10px; padding-bottom: 10px">Stock Correction</th>
<th style="padding-top: 10px; padding-bottom: 10px">Balance</th>
</tr>
<tr>
{{#each items}}
<td style="text-align: center">{{this.item}}</td>
<td style="text-align: center">{{this.stockDatabase}}</td>
<td style="text-align: center">{{this.stockCorrectionQty}}</td>
<td style="text-align: center">{{this.balance}}</td>
{{/each}}
</tr>
</table>
</div>
<div style="margin: 5cm 0 0 2cm">
<div style="text-align: center">
<h3>CreatedBy</h3>
<p style="margin-top: 2cm">{{createdBy}}</p>
</div>
<div style="margin: -34mm 0 0 80mm; text-align: center">
<h3>ApprovedBy</h3>
<p style="margin-top: 2cm">{{approveBy}}</p>
</div>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stock Correction</title>
</head>
<body>
<div style="position: fixed; top: 48%; left: 7%; transform: rotate(-45deg); opacity: 0.5; z-index: 99; font-size: 700%; color: red">Cancelled</div>
<div style="display: flex; flex-direction: row; margin-top: 4mm">
<img
src="https://cdn.pixabay.com/photo/2014/09/19/12/43/star-452341_1280.png"
style="height: 2cm; width: 2cm; margin-left: 3mm"
/>
<div style="margin-left: 75mm; margin-top: -2cm">
<h2 style="margin: 0 0 3mm 0; color: #2296f3">Stock Correction</h2>
<h3 style="margin: 0 0 2mm 0; color: #2296f3">PT.GANESHA MANDIRI BHAKTI</h3>
<p style="margin: 0 0 2mm 0">Jln. Musi No 21 Tegalsari Surabaya</p>
<p style="margin: 0 0 2mm 0">Phone : (021)-29339383</p>
</div>
</div>
<div style="background-color: #2296f3; height: 5mm; margin: 4mm 2mm 2mm 2mm"></div>
<div style="display: flex; flex-direction: row; margin-top: 4mm">
<div style="margin-left: 3mm">
<p style="margin: 0 0 2mm 0">Date : {{date}}</p>
<p style="margin: 0 0 2mm 0">Form Number : {{formNumber}}</p>
</div>
<div style="margin-left: 75mm; margin-top: -1.25cm">
<p style="margin: 0 0 2mm 0">Warehouse : {{warehouse}}</p>
<p style="margin: 0 0 2mm 0">Addrress : {{address}}</p>
<p style="margin: 0 0 2mm 0">Phone Number : {{phone}}</p>
</div>
</div>
<div style="margin-top: 7mm">
<table style="margin: auto; border: 1px solid #ddd; width: 80%">
<tr style="background-color: #2296f3">
<th style="padding-top: 10px; padding-bottom: 10px">Item</th>
<th style="padding-top: 10px; padding-bottom: 10px">Stock Database</th>
<th style="padding-top: 10px; padding-bottom: 10px">Stock Correction</th>
<th style="padding-top: 10px; padding-bottom: 10px">Balance</th>
</tr>
<tr>
{{#each items}}
<td style="text-align: center">{{this.item}}</td>
<td style="text-align: center">{{this.stockDatabase}}</td>
<td style="text-align: center">{{this.stockCorrectionQty}}</td>
<td style="text-align: center">{{this.balance}}</td>
{{/each}}
</tr>
</table>
</div>
<div style="margin: 5cm 0 0 2cm">
<div style="text-align: center">
<h3>CreatedBy</h3>
<p style="margin-top: 2cm">{{createdBy}}</p>
</div>
<div style="margin: -34mm 0 0 80mm; text-align: center">
<h3>ApprovedBy</h3>
<p style="margin-top: 2cm">{{approveBy}}</p>
</div>
</div>
</body>
</html>
Loading